From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: MrHua269 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 index 3b1c3e84ab24b6e3a0e4d129b3617b393b6d6651..f421702fef63846bffaf55061c3f9c1bb1248bfa 100644 --- 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; } + // Shiroha - Fix region threading with entity ai data access + if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(target)) { + return false; + } + // Shiroha end 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 index e097bc1268b4050e3948413c52dea69950858458..16314f63fdf6f81819b2b660c48d0cf3025aa8c1 100644 --- a/net/minecraft/world/entity/ai/Brain.java +++ b/net/minecraft/world/entity/ai/Brain.java @@ -383,7 +383,7 @@ public class Brain { } public void tick(final ServerLevel level, final E body) { - this.forgetOutdatedMemories(); + this.forgetOutdatedMemories(body); // Shiroha - Fix region threading with entity ai data access this.tickSensors(level, body); this.startEachNonRunningBehavior(level, body); this.tickEachRunningBehavior(level, body); @@ -395,8 +395,8 @@ public class Brain { } } - private void forgetOutdatedMemories() { - this.memories.values().forEach(MemorySlot::tick); + 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 } 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 index f744ecd20dc70786311a7162b52aa4ceca0717db..231701ff41b519106e33011f555ee2c476085445 100644 --- 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 ) { + // Shiroha - Fix region threading with entity ai data access + if (!target.checkThread(walker.level())) { + return; + } + // Shiroha end 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 index 68251875edfa47ac34c64f99510998ad4bbb14b4..5eb0c950944b45b0878c9a8d60432712377eb895 100644 --- 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 + "}"; } + + // Shiroha start - Fix region threading with entity ai data access + @Override + public boolean checkThread(net.minecraft.world.level.Level currOwnedByLevel) { + return ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(currOwnedByLevel, this.blockPos); + } + // Shiroha end } diff --git a/net/minecraft/world/entity/ai/behavior/EntityTracker.java b/net/minecraft/world/entity/ai/behavior/EntityTracker.java index 3ac52b025ac3e1a3f9135b8e593a385a847afe0f..d2c68c78d0a2f334169a92171081eeb4363399e8 100644 --- 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; } + + // Shiroha start - Fix region threading with entity ai data access + @Override + public boolean checkThread(net.minecraft.world.level.Level currOwnedByLevel) { + return ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.entity); + } + // Shiroha end } diff --git a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java index b40da004b5281a041ee896ae176bfb9c4660f353..5b1baab6d52647ebec5ecb48189256b3eb4f8754 100644 --- a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java +++ b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java @@ -52,7 +52,7 @@ public class MoveToTargetSink extends Behavior { 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 { 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); } diff --git a/net/minecraft/world/entity/ai/behavior/PositionTracker.java b/net/minecraft/world/entity/ai/behavior/PositionTracker.java index ce6cf5ecfb190428e3ef9b7dd39c98e3d27a7b9d..3eea48aad910760683e30594a9c1851aa17ce88c 100644 --- 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); + + boolean checkThread(net.minecraft.world.level.Level currOwnedByLevel); // Shiroha - Fix region threading with entity ai data access } diff --git a/net/minecraft/world/entity/ai/behavior/SleepInBed.java b/net/minecraft/world/entity/ai/behavior/SleepInBed.java index 16017d819c28b077f732e2ef571eac179d24e323..2e248e4fcdec5830d83ecabf3df163117d2612c9 100644 --- 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 { return false; } + // Shiroha - Fix region threading with entity ai data access + if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(level, target.pos())) { + return false; + } + // Shiroha end Optional lastWokenMemory = brain.getMemory(MemoryModuleType.LAST_WOKEN); if (lastWokenMemory.isPresent()) { long timeSinceLastWoken = level.getGameTime() - lastWokenMemory.get(); @@ -56,7 +61,6 @@ public class SleepInBed extends Behavior { 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 index 88a89b4c72cc99dc89d3f3cc928b2dfda4125759..9f85ba205d4e7b69e56ba538c00b1be6e1a5a617 100644 --- 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 { this.timeToLive = timeToLive; } - public void tick() { + public void tick(net.minecraft.world.entity.Entity owner) { // Shiroha - Fix region threading with entity ai data access if (this.hasValue() && this.canExpire()) { if (this.hasExpired()) { this.clear(); @@ -21,6 +21,57 @@ public class MemorySlot { this.timeToLive--; } } + // Shiroha start - Fix region threading with entity ai data access + final net.minecraft.world.level.Level ownerLevel = owner.level(); + + // type: entity + if (io.nanachiyo0721.shiroha.config.modules.fixes.ForceCleanupEntityBrainMemoryConfig.enabledForEntity && this.value instanceof net.minecraft.world.entity.Entity entity) { + if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(entity)) { + this.clear(); + } + } + + // type: list of entity + if (io.nanachiyo0721.shiroha.config.modules.fixes.ForceCleanupEntityBrainMemoryConfig.enabledForEntityListed && this.value instanceof java.util.List list) { + // check first if it's matched type + if (!list.isEmpty() && list.getFirst() instanceof net.minecraft.world.entity.Entity) { + // matched, do check + for (Object entityInObject : list) { + final net.minecraft.world.entity.Entity entity = (net.minecraft.world.entity.Entity) entityInObject; + + if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(entity)) { + this.clear(); + break; + } + } + } + } + + // type: block_pos + if (io.nanachiyo0721.shiroha.config.modules.fixes.ForceCleanupEntityBrainMemoryConfig.enabledForBlockPos && this.value instanceof net.minecraft.core.BlockPos blockPos) { + if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(ownerLevel, blockPos)) { + this.clear(); + } + } + + + //type: position_tracker and walk_target + if (io.nanachiyo0721.shiroha.config.modules.fixes.ForceCleanupEntityBrainMemoryConfig.enabledForPositionTracker) { + 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(); + } + } + // Shiroha end } public static MemorySlot create() { diff --git a/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java b/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java index 5c967b55f6ad3b660e9cdf74fadd90c5ea67afb9..7af28b58afa6538aa47fc25da4f3b911942b7c5a 100644 --- a/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java +++ b/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java @@ -39,7 +39,7 @@ public class NearestVisibleLivingEntities { public Optional findClosest(final Predicate filter) { for (LivingEntity nearbyEntity : this.nearbyEntities) { - if (filter.test(nearbyEntity) && this.lineOfSightTest.test(nearbyEntity)) { + if (this.getExtraFilterForThreadCheck(filter).test(nearbyEntity) && this.lineOfSightTest.test(nearbyEntity)) { // Shiroha - Fix region threading with entity ai data access (one side is enough for thread checking) return Optional.of(nearbyEntity); } } @@ -48,24 +48,35 @@ public class NearestVisibleLivingEntities { } public Iterable findAll(final Predicate filter) { - return Iterables.filter(this.nearbyEntities, entity -> filter.test(entity) && this.lineOfSightTest.test(entity)); + return Iterables.filter(this.nearbyEntities, entity -> this.getExtraFilterForThreadCheck(filter).test(entity) && this.lineOfSightTest.test(entity)); // Shiroha - Fix region threading with entity ai data access (one side is enough for thread checking) } public Stream find(final Predicate filter) { - return this.nearbyEntities.stream().filter(entity -> filter.test(entity) && this.lineOfSightTest.test(entity)); + return this.nearbyEntities.stream().filter(entity -> this.getExtraFilterForThreadCheck(filter).test(entity) && this.lineOfSightTest.test(entity)); // Shiroha - Fix region threading with entity ai data access (one side is enough for thread checking) } public boolean contains(final LivingEntity targetEntity) { - return this.nearbyEntities.contains(targetEntity) && this.lineOfSightTest.test(targetEntity); + return this.nearbyEntities.contains(targetEntity) && this.getExtraFilterForThreadCheck(this.lineOfSightTest).test(targetEntity); // Shiroha - Fix region threading with entity ai data access } public boolean contains(final Predicate filter) { for (LivingEntity nearbyEntity : this.nearbyEntities) { - if (filter.test(nearbyEntity) && this.lineOfSightTest.test(nearbyEntity)) { + if (this.getExtraFilterForThreadCheck(filter).test(nearbyEntity) && this.lineOfSightTest.test(nearbyEntity)) { // Shiroha - Fix region threading with entity ai data access (one side is enough for thread checking) return true; } } return false; } + // Shiroha start - Fix region threading with entity ai data access + private Predicate getExtraFilterForThreadCheck(Predicate original) { + return io.nanachiyo0721.shiroha.config.modules.fixes.ForceCleanupEntityBrainMemoryConfig.enableForNearestLivingEntities ? ent -> { + if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(ent)) { + return false; + } + + return original.test(ent); + } : original; + } + // Shiroha end - Fix region threading with entity ai data access } diff --git a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java index e44814cfb6afb594456b8215bd13a92e93de2c85..d4b65f13c137495d436b7e8133e0ce09ee2e08c0 100644 --- 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); + // 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, 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 index 2ea1ec39a37899ae1510d0036aa670e758077537..3acafc2aa1219efb0d7a591b91afa42640bcd584 100644 --- 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 + // Shiroha start - Fix region threading with entity ai data access + if (io.nanachiyo0721.shiroha.config.modules.fixes.PathfindingFixesConfig.doNotPathfindToNotOwnedTargets) { + // 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; + } + } + // Shiroha end ProfilerFiller profiler = Profiler.get(); profiler.push("pathfind"); BlockPos fromPos = above ? this.mob.blockPosition().above() : this.mob.blockPosition(); @@ -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()) diff --git a/net/minecraft/world/entity/animal/allay/AllayAi.java b/net/minecraft/world/entity/animal/allay/AllayAi.java index c3667e7997551e0f5ce63bc6ee890082d1431400..edf2be5c1a48d80eaf900c782d02ea6f517fee2c 100644 --- 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 likedNoteblockPos = brain.getMemory(MemoryModuleType.LIKED_NOTEBLOCK_POSITION); if (likedNoteblockPos.isPresent()) { GlobalPos position = likedNoteblockPos.get(); + // Shiroha - Fix region threading with entity ai data access + final Level targetLevel = allay.level().getServer().getLevel(position.dimension()); + final BlockPos targetPos = position.pos(); + + // thread checks + if (targetLevel == null || !ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(targetLevel, targetPos)) { + brain.eraseMemory(MemoryModuleType.LIKED_NOTEBLOCK_POSITION); // The memory value is not being belong to current tick region anymore + return Optional.empty(); + } + // Shiroha 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 index d5394ae7ce56555ad21aeafb2d78c291c62e2988..d172b819377c20ecd12331377fdd16f757051389 100644 --- 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); + && this.getExploredPositions().noneMatch(explored -> { // Shiroha - Fix region threading with entity ai data access + // 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 + }) // Shiroha end + && Optional.ofNullable(this.getNavigation().createPath(position, 1)).map(Path::canReach).orElse(false); } private void dropSeed() {