From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 29 Mar 2025 13:40:46 +0100 Subject: [PATCH] Async target finding diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java index 508f3c7c1e806576f1faea7975ac9bbe73b1024b..78aee57ad8224b0728411c699d2e3844847c9c79 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -290,6 +290,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop entitiesWithScheduledTasks = java.util.concurrent.ConcurrentHashMap.newKeySet(); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run (concurrent because plugins may schedule tasks async) + @Nullable public org.dreeam.leaf.async.ai.AsyncGoalThread asyncGoalThread; // Leaf - Async target finding public static S spin(Function threadFunction) { AtomicReference atomicReference = new AtomicReference<>(); @@ -1073,6 +1074,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop players = Lists.newArrayList(); + // Leaf start - Async target finding + final List players; + { + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + this.players = Lists.newCopyOnWriteArrayList(); + } else { + this.players = Lists.newArrayList(); + } + } + // Leaf end - Async target finding public final ServerChunkCache chunkSource; private final MinecraftServer server; public final net.minecraft.world.level.storage.PrimaryLevelData serverLevelData; // CraftBukkit - type @@ -212,6 +221,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current) public boolean hasRidableMoveEvent = false; // Purpur - Ridables + public final @Nullable org.dreeam.leaf.async.ai.AsyncGoalExecutor asyncGoalExecutor; // Leaf - Async target finding @Override public @Nullable LevelChunk getChunkIfLoaded(int x, int z) { @@ -330,6 +340,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks(); } + // Leaf start - Async target finding + public final void leafMidTickTasks() { + if (this.asyncGoalExecutor != null) this.asyncGoalExecutor.midTick(); + } + // Leaf end - Async target finding + @Override public final ChunkAccess moonrise$syncLoadNonFull(final int chunkX, final int chunkZ, final net.minecraft.world.level.chunk.status.ChunkStatus status) { return this.moonrise$getChunkTaskScheduler().syncLoadNonFull(chunkX, chunkZ, status); @@ -691,6 +707,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // Paper end - rewrite chunk system this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit this.preciseTime = this.serverLevelData.getDayTime(); // Purpur - Configurable daylight cycle + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + this.asyncGoalExecutor = new org.dreeam.leaf.async.ai.AsyncGoalExecutor(server.asyncGoalThread, this); + } else { + this.asyncGoalExecutor = null; + } + // Leaf end - Async target finding } // Paper start @@ -835,12 +858,14 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe } this.moonrise$midTickTasks(); // Paper - rewrite chunk system // Gale end - Airplane - remove lambda from ticking guard - copied from guardEntityTick + this.leafMidTickTasks(); // Leaf - Async target finding } } } } ); this.tickBlockEntities(); + if (this.asyncGoalExecutor != null) this.asyncGoalExecutor.unpark(); // Leaf - Async target finding } // Paper - rewrite chunk system } @@ -1312,6 +1337,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe } // Paper end - rewrite chunk system + this.leafMidTickTasks(); // Leaf - Async target finding } private void tickBlock(BlockPos pos, Block block) { @@ -1325,6 +1351,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe } // Paper end - rewrite chunk system + this.leafMidTickTasks(); // Leaf - Async target finding } // Paper start - log detailed entity tick information diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java index 658b8af9b1db252d5664956907a5c0db538018b0..960ae487a5a88a5fe9899c16b9553cea0fbfba37 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -251,6 +251,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess protected Vec3 stuckSpeedMultiplier = Vec3.ZERO; @Nullable private Entity.RemovalReason removalReason; + private volatile boolean isRemoved = false; // Leaf - Async target finding - volatile removal check public static final float DEFAULT_BB_WIDTH = 0.6F; public static final float DEFAULT_BB_HEIGHT = 1.8F; public float moveDist; @@ -4988,7 +4989,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @Override public final boolean isRemoved() { - return this.removalReason != null; + // Leaf start - Async target finding + // Volatile removal check + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + return this.isRemoved; + } else { + return this.removalReason != null; + } + // Leaf end - Async target finding } @Nullable @@ -5008,6 +5016,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers if (this.removalReason == null) { this.removalReason = removalReason; + // Leaf start - Async target finding + // Volatile removal check + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled && this.removalReason != null) { + this.isRemoved = true; + } + // Leaf end - Async target finding } if (this.removalReason.shouldDestroy()) { @@ -5027,6 +5041,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess public void unsetRemoved() { this.removalReason = null; + // Leaf start - Async target finding + // Volatile removal check + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + this.isRemoved = false; + } + // Leaf end - Async target finding } // Paper start - Folia schedulers diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java index 77075bf43e7e2301e58aa6d9f8bfe934cce42f55..4a21420a3c7112024aba1a91bf635872afecf51a 100644 --- a/net/minecraft/world/entity/Mob.java +++ b/net/minecraft/world/entity/Mob.java @@ -135,6 +135,12 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab private float restrictRadius = -1.0F; public boolean aware = true; // CraftBukkit public int ticksSinceLastInteraction; // Purpur - Entity lifespan + // Leaf start - Async target finding + public boolean tickingTarget; + public final org.dreeam.leaf.async.ai.Waker getGoalCtx() { + return tickingTarget ? this.targetSelector.ctx : this.goalSelector.ctx; + } + // Leaf end - Async target finding protected Mob(EntityType entityType, Level level) { super(entityType, level); @@ -213,12 +219,21 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab } // Paper end - Skip AI during inactive ticks for non-aware mobs boolean isThrottled = org.dreeam.leaf.config.modules.opt.ThrottleInactiveGoalSelectorTick.enabled && _pufferfish_inactiveTickDisableCounter++ % 20 != 0; // Pufferfish - throttle inactive goal selector ticking + this.tickingTarget = false; // Leaf - Async target finding if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !isThrottled) { // Pufferfish - pass activated priroity // Pufferfish - throttle inactive goal selector ticking this.goalSelector.tick(); } + this.tickingTarget = true; // Leaf - Async target finding if (this.targetSelector.inactiveTick(this.activatedPriority, true)) { // Pufferfish - pass activated priority this.targetSelector.tick(); } + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + if (this.targetSelector.ctxGoals != null || this.goalSelector.ctxGoals != null) { + ((ServerLevel) this.level()).asyncGoalExecutor.submit(this.getId()); + } + } + // Leaf end - Async target finding } // Paper end @@ -765,17 +780,28 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab // Paper end - Allow nerfed mobs to jump and float this.sensing.tick(); int i = this.tickCount + this.getId(); + // Leaf start - Async target finding if (i % 2 != 0 && this.tickCount > 1) { + this.tickingTarget = true; if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking this.targetSelector.tickRunningGoals(false); + this.tickingTarget = false; if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking this.goalSelector.tickRunningGoals(false); } else { + this.tickingTarget = true; if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking this.targetSelector.tick(); + this.tickingTarget = false; if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking this.goalSelector.tick(); } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + if (this.targetSelector.ctxGoals != null || this.goalSelector.ctxGoals != null) { + ((ServerLevel) this.level()).asyncGoalExecutor.submit(this.getId()); + } + } + // Leaf end - Async target finding this.navigation.tick(); this.customServerAiStep((ServerLevel)this.level()); diff --git a/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java b/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java index 7651676e72fcec52d7c1f9f7d7b6f9e585015c4d..28aae1f1115c3a5dc5b3b585621ce32e4e759635 100644 --- a/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java +++ b/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java @@ -67,15 +67,24 @@ public class AvoidEntityGoal extends Goal { @Override public boolean canUse() { + // Leaf start - Async Avoid Entity Finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + if (!poll()) { + getNearestEntityAsync(); + return false; + } + } else { this.toAvoid = getServerLevel(this.mob) .getNearestEntity( this.mob.level().getEntitiesOfClass(this.avoidClass, this.mob.getBoundingBox().inflate(this.maxDist, 3.0, this.maxDist), livingEntity -> true), this.avoidEntityTargeting, this.mob, this.mob.getX(), - this.mob.getY(), + this.mob.getEyeY(), // Leaf - Async Avoid Entity Finding this.mob.getZ() ); + } + // Leaf end - Async Avoid Entity Finding if (this.toAvoid == null) { return false; } else { @@ -91,6 +100,45 @@ public class AvoidEntityGoal extends Goal { } } + // Leaf start - Async Avoid Entity Finding + private boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof LivingEntity target)) return false; + var serverLevel = getServerLevel(this.mob); + if (serverLevel == null || !target.isAlive() || !this.avoidEntityTargeting.test(serverLevel, this.mob, target)) return false; + this.toAvoid = (T) target; + return true; + } + + private void getNearestEntityAsync() { + final var mob = this.mob; + final var ctx = mob.getGoalCtx(); + if (!ctx.state) return; + final double x = mob.getX(); + final double y = mob.getEyeY(); + final double z = mob.getZ(); + final var serverLevel = getServerLevel(mob); + final var avoidClass = this.avoidClass; + final var bound = mob.getBoundingBox().inflate(this.maxDist, 3.0, this.maxDist); + ctx.wake = () -> { + try { + if (mob == null || mob.isRemoved() || !mob.isAlive() || mob.level() != serverLevel) { + return; + } + ctx.result = serverLevel.getNearestEntity( + serverLevel.getEntitiesOfClass(avoidClass, bound, livingEntity -> true), + avoidEntityTargeting, + mob, + x, + y, + z + ); + } catch (Exception e) { + org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } + }; + } + // Leaf end - Async Avoid Entity Finding + @Override public boolean canContinueToUse() { return !this.pathNav.isDone(); diff --git a/net/minecraft/world/entity/ai/goal/BegGoal.java b/net/minecraft/world/entity/ai/goal/BegGoal.java index 6f7767d1ef5ee20338a334d85ad58dab9f8c4d1b..d5a8d0fc6d28e9cd770b76a569fd73d2601c8784 100644 --- a/net/minecraft/world/entity/ai/goal/BegGoal.java +++ b/net/minecraft/world/entity/ai/goal/BegGoal.java @@ -27,8 +27,50 @@ public class BegGoal extends Goal { this.setFlags(EnumSet.of(Goal.Flag.LOOK)); } + // Leaf start - Async Target Finding + protected boolean poll() { + if (!(this.wolf.getGoalCtx().result() instanceof Player target)) return false; + if (target == null) return false; + ServerLevel serverLevel = getServerLevel(this.wolf); + if (serverLevel == null || !target.isAlive() || !playerHoldingInteresting(target)) return false; + this.player = target; + return true; + } + + private void findTargetAsync() { + final Wolf wolf = this.wolf; + final var ctx = wolf.getGoalCtx(); + if (!ctx.state) return; + final ServerLevel serverLevel = getServerLevel(wolf); + final TargetingConditions begTargeting = this.begTargeting; + ctx.wake = () -> { + try { + if (wolf.level() != serverLevel || wolf.isRemoved() || !wolf.isAlive()) { + return; + } + + var player = serverLevel.getNearestPlayer(begTargeting, wolf); + if (player != null && playerHoldingInteresting(player)) { + ctx.result = player; + } + } catch (Exception e) { + org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } + }; + } + // Leaf end - Async Target Finding + @Override public boolean canUse() { + // Leaf start - Async Target Finding + if (poll()) { + return true; + } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + findTargetAsync(); + return false; + } + // Leaf end - Async Target Finding this.player = this.level.getNearestPlayer(this.begTargeting, this.wolf); return this.player != null && this.playerHoldingInteresting(this.player); } @@ -59,10 +101,10 @@ public class BegGoal extends Goal { this.lookTime--; } - private boolean playerHoldingInteresting(Player player) { + private static boolean playerHoldingInteresting(Player player) { // Leaf start - Async Target Finding - static for (InteractionHand interactionHand : InteractionHand.values()) { ItemStack itemInHand = player.getItemInHand(interactionHand); - if (itemInHand.is(Items.BONE) || this.wolf.isFood(itemInHand)) { + if (itemInHand.is(Items.BONE) || itemInHand.is(net.minecraft.tags.ItemTags.WOLF_FOOD)) { // Leaf end - Async Target Finding return true; } } diff --git a/net/minecraft/world/entity/ai/goal/CatLieOnBedGoal.java b/net/minecraft/world/entity/ai/goal/CatLieOnBedGoal.java index 4c46cd105cde3cbcde65a02ff691c3a8edd56c76..b3205c9f35687bc37124876198ec2d657ccaa96c 100644 --- a/net/minecraft/world/entity/ai/goal/CatLieOnBedGoal.java +++ b/net/minecraft/world/entity/ai/goal/CatLieOnBedGoal.java @@ -52,6 +52,13 @@ public class CatLieOnBedGoal extends MoveToBlockGoal { @Override protected boolean isValidTarget(LevelReader level, BlockPos pos) { - return level.isEmptyBlock(pos.above()) && level.getBlockState(pos).is(BlockTags.BEDS); + return level.isEmptyBlock(pos.above()) && level.getBlockState(pos).is(BlockTags.BEDS); // Leaf - Async search block - diff on change } + + // Leaf start - Async search block + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.CatLie; + } + // Leaf end - Async search block } diff --git a/net/minecraft/world/entity/ai/goal/CatSitOnBlockGoal.java b/net/minecraft/world/entity/ai/goal/CatSitOnBlockGoal.java index 9954f49bc364969c7ccb37f4186fa2ab8710f6ae..057090e3134048e75dbaefb703e8f2d35a172a3b 100644 --- a/net/minecraft/world/entity/ai/goal/CatSitOnBlockGoal.java +++ b/net/minecraft/world/entity/ai/goal/CatSitOnBlockGoal.java @@ -44,14 +44,21 @@ public class CatSitOnBlockGoal extends MoveToBlockGoal { @Override protected boolean isValidTarget(LevelReader level, BlockPos pos) { - if (!level.isEmptyBlock(pos.above())) { + if (!level.isEmptyBlock(pos.above())) { // Leaf - Async search block - diff on change return false; } else { - BlockState blockState = level.getBlockState(pos); + BlockState blockState = level.getBlockState(pos); // Leaf - Async search block - diff on change return blockState.is(Blocks.CHEST) ? ChestBlockEntity.getOpenCount(level, pos) < 1 : blockState.is(Blocks.FURNACE) && blockState.getValue(FurnaceBlock.LIT) || blockState.is(BlockTags.BEDS, state -> state.getOptionalValue(BedBlock.PART).map(bedPart -> bedPart != BedPart.HEAD).orElse(true)); } } + + // Leaf start - Async search block + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.CatSit; + } + // Leaf end - Async search block } diff --git a/net/minecraft/world/entity/ai/goal/FollowBoatGoal.java b/net/minecraft/world/entity/ai/goal/FollowBoatGoal.java index f7dcd341444059f8fb9708e9a21b5d8ace6b9cf9..11c428833d43f867f9598093b13fe8bd6657e4ba 100644 --- a/net/minecraft/world/entity/ai/goal/FollowBoatGoal.java +++ b/net/minecraft/world/entity/ai/goal/FollowBoatGoal.java @@ -23,8 +23,54 @@ public class FollowBoatGoal extends Goal { this.mob = mob; } + // Leaf start - Async Target Finding + private boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof Player target)) return false; + var serverLevel = getServerLevel(this.mob); + return serverLevel != null && target.isAlive() && !target.isSpectator() && (Mth.abs(target.xxa) > 0.0F || Mth.abs(target.zza) > 0.0F); + } + + private void findTargetAsync() { + final PathfinderMob mob = this.mob; + final var ctx = mob.getGoalCtx(); + if (!ctx.state) return; + final var bound = mob.getBoundingBox().inflate(5.0); + final var serverLevel = getServerLevel(mob); + ctx.wake = () -> { + try { + if (mob.level() != serverLevel || mob.isRemoved() || !mob.isAlive()) { + return; + } + + List entitiesOfClass = serverLevel.getEntitiesOfClass(AbstractBoat.class, bound); + for (AbstractBoat abstractBoat : entitiesOfClass) { + Entity controllingPassenger = abstractBoat.getControllingPassenger(); + if (controllingPassenger instanceof Player player + && (Mth.abs(player.xxa) > 0.0F || Mth.abs(player.zza) > 0.0F)) { + ctx.result = player; + break; + } + } + } catch (Exception e) { + org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } + }; + } + // Leaf end - Async Target Finding @Override public boolean canUse() { + // Leaf start - Async Target Finding + if (poll()) { + return true; + } + if (this.following != null && (Mth.abs(this.following.xxa) > 0.0F || Mth.abs(this.following.zza) > 0.0F)) { + return true; + } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + findTargetAsync(); + return false; + } + // Leaf end - Async Target Finding List entitiesOfClass = this.mob.level().getEntitiesOfClass(AbstractBoat.class, this.mob.getBoundingBox().inflate(5.0)); boolean flag = false; @@ -36,7 +82,7 @@ public class FollowBoatGoal extends Goal { } } - return this.following != null && (Mth.abs(this.following.xxa) > 0.0F || Mth.abs(this.following.zza) > 0.0F) || flag; + return flag; // Leaf - Async Target Finding - move above } @Override diff --git a/net/minecraft/world/entity/ai/goal/FollowMobGoal.java b/net/minecraft/world/entity/ai/goal/FollowMobGoal.java index c9cf985173d3da9e84ce178f161e2fd5fb8ce472..911ceddb4ded974a02355c76e5d4133632ae4487 100644 --- a/net/minecraft/world/entity/ai/goal/FollowMobGoal.java +++ b/net/minecraft/world/entity/ai/goal/FollowMobGoal.java @@ -38,6 +38,15 @@ public class FollowMobGoal extends Goal { @Override public boolean canUse() { + // Leaf start - Async Follow Mob Finding + if (poll()) { + return true; + } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + getFollowingMobAsync(); + return false; + } + // Leaf end - Async Follow Mob Finding List entitiesOfClass = this.mob.level().getEntitiesOfClass(Mob.class, this.mob.getBoundingBox().inflate(this.areaSize), this.followPredicate); if (!entitiesOfClass.isEmpty()) { for (Mob mob : entitiesOfClass) { @@ -51,6 +60,43 @@ public class FollowMobGoal extends Goal { return false; } + // Leaf start - Async Follow Mob Finding + private boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof Mob target)) return false; + var serverLevel = getServerLevel(this.mob); + if (serverLevel == null || !target.isAlive() || target.isInvisible()) return false; + this.followingMob = target; + return true; + } + + private void getFollowingMobAsync() { + final var mob = this.mob; + final var ctx = this.mob.getGoalCtx(); + if (!ctx.state) return; + final var serverLevel = getServerLevel(mob); + final var bound = this.mob.getBoundingBox().inflate(this.areaSize); + final var followPredicate = this.followPredicate; + ctx.wake = () -> { + try { + if (mob == null || mob.isRemoved() || !mob.isAlive() || mob.level() != serverLevel) { + return; + } + List entitiesOfClass = serverLevel.getEntitiesOfClass(Mob.class, bound, followPredicate); + if (!entitiesOfClass.isEmpty()) { + for (final Mob follow : entitiesOfClass) { + if (!follow.isInvisible()) { + ctx.result = follow; + return; + } + } + } + } catch (Exception e) { + org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } + }; + } + // Leaf end - Async Follow Mob Finding + @Override public boolean canContinueToUse() { return this.followingMob != null && !this.navigation.isDone() && this.mob.distanceToSqr(this.followingMob) > this.stopDistance * this.stopDistance; diff --git a/net/minecraft/world/entity/ai/goal/FollowParentGoal.java b/net/minecraft/world/entity/ai/goal/FollowParentGoal.java index 3093f03d4f298bf39fec8bad2b6c22518774aea8..80f9de7b2c03c1477dae0f42b328e0100f78e58b 100644 --- a/net/minecraft/world/entity/ai/goal/FollowParentGoal.java +++ b/net/minecraft/world/entity/ai/goal/FollowParentGoal.java @@ -19,11 +19,66 @@ public class FollowParentGoal extends Goal { this.speedModifier = speedModifier; } + // Leaf start - Async Target Finding + protected boolean poll() { + if (!(this.animal.getGoalCtx().result() instanceof Animal target)) return false; + var serverLevel = getServerLevel(animal); + if (serverLevel == null || !target.isAlive() || animal.distanceToSqr(target) < 9.0) return false; + this.parent = animal; + return true; + } + + private void findTargetAsync() { + final Animal animal = this.animal; + final var ctx = animal.getGoalCtx(); + if (!ctx.state) return; + final var targetType = animal.getClass(); + final var bound = animal.getBoundingBox().inflate(8.0, 4.0, 8.0); + final var serverLevel = getServerLevel(animal); + final var pos = animal.position(); + ctx.wake = () -> { + try { + if (animal.level() != serverLevel || animal.isRemoved() || !animal.isAlive()) { + return; + } + + List entitiesOfClass = serverLevel.getEntitiesOfClass(targetType, bound); + Animal target = null; + double d = Double.MAX_VALUE; + + for (Animal animal1 : entitiesOfClass) { + if (animal1.getAge() >= 0) { + double d1 = animal1.distanceToSqr(pos); + if (!(d1 > d)) { + d = d1; + target = animal1; + } + } + } + if (target != null) { + ctx.result = target; + } + } catch (Exception e) { + org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } + }; + } + // Leaf end - Async Target Finding + @Override public boolean canUse() { if (this.animal.getAge() >= 0) { return false; } else { + // Leaf start - Async Target Finding + if (poll()) { + return true; + } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + findTargetAsync(); + return false; + } + // Leaf end - Async Target Finding List entitiesOfClass = this.animal .level() .getEntitiesOfClass((Class)this.animal.getClass(), this.animal.getBoundingBox().inflate(8.0, 4.0, 8.0)); @@ -43,6 +98,7 @@ public class FollowParentGoal extends Goal { if (animal == null) { return false; } else if (d < 9.0) { + // Leaf - Async Target Finding - diff on change return false; } else { this.parent = animal; diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java index e82e32407cec6109b9c3b0106295217f4a3f4aa2..ec56b4e8acd2dad23a94b1fe9145e6256da54493 100644 --- a/net/minecraft/world/entity/ai/goal/GoalSelector.java +++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java @@ -26,6 +26,13 @@ public class GoalSelector { private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from GoalSelector private int curRate; // Paper - EAR 2 + // Leaf start - Async target finding + public WrappedGoal @org.jetbrains.annotations.Nullable[] ctxGoals = null; + private int ctxIndex = 0; + private int ctxState = 0; + public final org.dreeam.leaf.async.ai.Waker ctx = new org.dreeam.leaf.async.ai.Waker(); + // Leaf end - Async target finding + public void addGoal(int priority, Goal goal) { this.availableGoals.add(new WrappedGoal(priority, goal)); } @@ -85,7 +92,111 @@ public class GoalSelector { return true; } + // Leaf start - Async target finding + public final boolean poll() { + if (this.ctxGoals == null || ctx.wake != null) { + return false; + } + if (ctxState == 0) { + while (ctxIndex < this.ctxGoals.length) { + WrappedGoal goal = this.ctxGoals[ctxIndex]; + // entity tempt + if (goal.isRunning() && (goalContainsAnyFlags(goal, this.goalTypes) || !goal.canContinueToUse())) { + ctx.state = false; + if (ctx.wake != null) { + return true; + } + goal.stop(); + } + + ctxIndex++; + ctx.state = true; + } + + this.lockedFlags.entrySet().removeIf(entry -> !entry.getValue().isRunning()); + + ctxIndex = 0; + ctx.state = true; + ctxState = 1; + } + if (ctxState == 1) { + while (ctxIndex < this.ctxGoals.length) { + WrappedGoal goal = this.ctxGoals[ctxIndex]; + // entity and block + if (!goal.isRunning() + && !goalContainsAnyFlags(goal, this.goalTypes) + && goalCanBeReplacedForAllFlags(goal, this.lockedFlags) + ) { + if (goal.canUse()) { + long flagIterator = goal.getFlags().getBackingSet(); + int wrappedGoalSize = goal.getFlags().size(); + for (int i = 0; i < wrappedGoalSize; ++i) { + final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; + flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator); + WrappedGoal wrappedGoal1 = this.lockedFlags.getOrDefault(flag, NO_GOAL); + wrappedGoal1.stop(); + this.lockedFlags.put(flag, goal); + } + + goal.start(); + } + ctx.state = false; + if (ctx.wake != null) { + return true; + } + } + // entity alert other + if (!ctx.state) { + switch (goal.getGoal()) { + case net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal t -> t.poll(); + case net.minecraft.world.entity.ai.goal.target.ResetUniversalAngerTargetGoal t -> t.poll(); + default -> {} + } + } + ctxIndex++; + ctx.state = true; + } + + ctxIndex = 0; + ctx.state = true; + ctxState = 2; + } + if (ctxState == 2) { + while (ctxIndex < this.ctxGoals.length) { + WrappedGoal goal = this.ctxGoals[ctxIndex]; + if (goal.isRunning()) { + goal.tick(); + } + ctxIndex++; + } + + ctxGoals = null; + ctxState = 0; + ctxIndex = 0; + ctx.state = true; + } + return false; + } + + public final void wake() { + Runnable wake = ctx.wake; + if (wake != null) { + wake.run(); + } + ctx.wake = null; + } + // Leaf end - Async target finding + public void tick() { + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + if (this.ctxGoals == null) { + this.ctxGoals = this.availableGoals.toArray(new WrappedGoal[0]); + } + return; + } + // Leaf end - Async target finding + for (WrappedGoal wrappedGoal : this.availableGoals) { if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams wrappedGoal.stop(); diff --git a/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java b/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java index be59d0c27a83b329ec3f97c029cfb9c114e22472..7c54964a5329e12a9a4a1fdb2417ccbd3f0bfae2 100644 --- a/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java +++ b/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java @@ -20,20 +20,93 @@ public class LlamaFollowCaravanGoal extends Goal { this.setFlags(EnumSet.of(Goal.Flag.MOVE)); } + // Leaf start - Async Target Finding + private @javax.annotation.Nullable Llama poll() { + if (!(this.llama.getGoalCtx().result() instanceof Llama target)) return null; + var serverLevel = getServerLevel(this.llama); + if (serverLevel == null || !target.isAlive()) return null; + return target; + } + + private void findTargetAsync() { + final Llama llama = this.llama; + final var ctx = llama.getGoalCtx(); + if (!ctx.state) return; + final var bound = llama.getBoundingBox().inflate(9.0, 4.0, 9.0); + final var serverLevel = getServerLevel(llama); + final var pos = llama.position(); + ctx.wake = () -> { + try { + if (llama.level() != serverLevel || llama.isRemoved() || !llama.isAlive()) { + return; + } + + List entities = serverLevel.getEntities(llama, bound, entity1 -> { + EntityType type = entity1.getType(); + return type == EntityType.LLAMA || type == EntityType.TRADER_LLAMA; + }); + Llama target = null; + double d = Double.MAX_VALUE; + + for (Entity entity : entities) { + Llama llama1 = (Llama) entity; + if (llama1.inCaravan() && !llama1.hasCaravanTail()) { + double d1 = llama1.distanceToSqr(pos); + if (!(d1 > d)) { + d = d1; + target = llama1; + } + } + } + + if (target == null) { + for (Entity entityx : entities) { + Llama llama1 = (Llama) entityx; + if (llama1.isLeashed() && !llama1.hasCaravanTail()) { + double d1 = llama1.distanceToSqr(pos); + if (!(d1 > d)) { + d = d1; + target = llama1; + } + } + } + } + if (target != null) { + ctx.result = target; + } + } catch (Exception e) { + org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } + }; + } + // Leaf end - Async Target Finding + @Override public boolean canUse() { if (!this.llama.level().purpurConfig.llamaJoinCaravans || !this.llama.shouldJoinCaravan) return false; // Purpur - Llama API // Purpur - Config to disable Llama caravans if (!this.llama.isLeashed() && !this.llama.inCaravan()) { + // Leaf start - Async Target Finding + Llama llama = poll(); + double d = Double.MAX_VALUE; + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + if (llama == null) { + findTargetAsync(); + return false; + } else { + d = this.llama.distanceToSqr(llama); + } + } else { + // Leaf end - Async Target Finding List entities = this.llama.level().getEntities(this.llama, this.llama.getBoundingBox().inflate(9.0, 4.0, 9.0), entity1 -> { EntityType type = entity1.getType(); return type == EntityType.LLAMA || type == EntityType.TRADER_LLAMA; }); - Llama llama = null; - double d = Double.MAX_VALUE; + // Llama llama = null; // Leaf - Async Target Finding + // double d = Double.MAX_VALUE; // Leaf - Async Target Finding for (Entity entity : entities) { Llama llama1 = (Llama)entity; - if (llama1.inCaravan() && !llama1.hasCaravanTail()) { + if (llama1.inCaravan() && !llama1.hasCaravanTail()) { // Leaf - Async Target Finding - diff on change double d1 = this.llama.distanceToSqr(llama1); if (!(d1 > d)) { d = d1; @@ -45,7 +118,7 @@ public class LlamaFollowCaravanGoal extends Goal { if (llama == null) { for (Entity entityx : entities) { Llama llama1 = (Llama)entityx; - if (llama1.isLeashed() && !llama1.hasCaravanTail()) { + if (llama1.isLeashed() && !llama1.hasCaravanTail()) { // Leaf - Async Target Finding - diff on change double d1 = this.llama.distanceToSqr(llama1); if (!(d1 > d)) { d = d1; @@ -54,6 +127,7 @@ public class LlamaFollowCaravanGoal extends Goal { } } } + } // Leaf - Async Target Finding if (llama == null) { return false; diff --git a/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java b/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java index 6463c3c9b08d6058f2843c225b08a40fc30a960b..0afad8ddb0e9d083d47b96627d999f0ee28b9446 100644 --- a/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java +++ b/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java @@ -48,32 +48,87 @@ public class LookAtPlayerGoal extends Goal { @Override public boolean canUse() { + // Leaf start - Async look finding + if (poll()) { + return true; + } if (this.mob.getRandom().nextFloat() >= this.probability) { return false; + } + if (this.mob.getTarget() != null) { + this.lookAt = this.mob.getTarget(); + } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + getLookAsync(); + return false; + } + ServerLevel serverLevel = getServerLevel(this.mob); + if (this.lookAtType == Player.class) { + this.lookAt = serverLevel.getNearestPlayer(this.lookAtContext, this.mob, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ()); } else { - if (this.mob.getTarget() != null) { - this.lookAt = this.mob.getTarget(); - } + this.lookAt = serverLevel.getNearestEntity( + this.mob + .level() + .getEntitiesOfClass(this.lookAtType, this.mob.getBoundingBox().inflate(this.lookDistance, 3.0, this.lookDistance), livingEntity -> true), + this.lookAtContext, + this.mob, + this.mob.getX(), + this.mob.getEyeY(), + this.mob.getZ() + ); + } - ServerLevel serverLevel = getServerLevel(this.mob); - if (this.lookAtType == Player.class) { - this.lookAt = serverLevel.getNearestPlayer(this.lookAtContext, this.mob, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ()); - } else { - this.lookAt = serverLevel.getNearestEntity( - this.mob - .level() - .getEntitiesOfClass(this.lookAtType, this.mob.getBoundingBox().inflate(this.lookDistance, 3.0, this.lookDistance), livingEntity -> true), - this.lookAtContext, - this.mob, - this.mob.getX(), - this.mob.getEyeY(), - this.mob.getZ() - ); - } + return this.lookAt != null; + } - return this.lookAt != null; + protected boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof LivingEntity target)) return false; + if (this.mob.getTarget() != null) { + this.lookAt = this.mob.getTarget(); + return true; } + ServerLevel serverLevel = getServerLevel(this.mob); + if (!target.isAlive() || !this.lookAtContext.test(serverLevel, this.mob, target)) return false; + this.lookAt = target; + return true; + } + + protected void getLookAsync() { + final var mob = this.mob; + final var ctx = mob.getGoalCtx(); + if (!ctx.state) return; + final double x = mob.getX(); + final double y = mob.getEyeY(); + final double z = mob.getZ(); + final var serverLevel = getServerLevel(mob); + final var bound = mob.getBoundingBox().inflate(this.lookDistance, 3.0, this.lookDistance); + final var lookAtContext = this.lookAtContext; + final var lookAtType = this.lookAtType; + ctx.wake = () -> { + try { + if (mob == null || !mob.isAlive() || mob.level() != serverLevel) { + return; + } + + if (lookAtType == Player.class) { + ctx.result = serverLevel.getNearestPlayer(lookAtContext, mob, x, y, z); + } else { + ctx.result = serverLevel.getNearestEntity( + serverLevel + .getEntitiesOfClass(lookAtType, bound, livingEntity -> true), + lookAtContext, + mob, + x, + y, + z + ); + } + } catch (Exception e) { + org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } + }; } + // Leaf end - Async look finding @Override public boolean canContinueToUse() { diff --git a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..292749c786b85f86fa5f65c49fa83539fd0603b4 100644 --- a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java +++ b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java @@ -41,8 +41,69 @@ public abstract class MoveToBlockGoal extends Goal { this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.JUMP)); } + // Leaf start - Async search block + protected boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof BlockPos blockPos1)) return false; + if (!this.mob.level().hasChunkAt(blockPos1) + || !this.mob.isWithinRestriction(blockPos1) + || !this.isValidTarget(this.mob.level(), blockPos1)) { + return false; + } + this.blockPos = blockPos1; + this.mob.movingTarget = blockPos1 == BlockPos.ZERO ? null : blockPos1; + return true; + } + + protected void getBlockAsync() { + final var mob = this.mob; + final var ctx = mob.getGoalCtx(); + if (!ctx.state) return; + final var serverLevel = getServerLevel(mob); + final TypeToCheck ty = this.typeToCheck(); + final net.minecraft.world.level.block.Block toRemove; + if (this instanceof RemoveBlockGoal removeBlockGoal) { + toRemove = removeBlockGoal.blockToRemove; + } else { + toRemove = null; + } + final int verticalSearchStart = this.verticalSearchStart; + final int searchRange = this.searchRange; + final int verticalSearchRange = this.verticalSearchRange; + final BlockPos blockPos = mob.blockPosition(); + final float restrictRadius = mob.getRestrictRadius(); + final BlockPos restrictCenter = mob.getRestrictCenter(); + ctx.wake = () -> { + try { + if (mob == null || !mob.isAlive() || mob.level() != serverLevel) { + return; + } + findNearestBlockAsync(ctx, ty, toRemove, mob, serverLevel, verticalSearchStart, searchRange, verticalSearchRange, blockPos, restrictRadius, restrictCenter); + } catch (Exception e) { + org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting block", e); + } + }; + } + + protected enum TypeToCheck { + CatLie, + CatSit, + Drowned, + FoxEat, + RaidGarden, + RemoveBlock, + Strider, + TurtleToWater, + TurtleLay, + } + // Leaf end - Async search block + @Override public boolean canUse() { + // Leaf start - Async search block + if (poll()) { + return true; + } + // Leaf end - Async search block if (this.nextStartTick > 0) { this.nextStartTick--; return false; @@ -109,6 +170,12 @@ public abstract class MoveToBlockGoal extends Goal { } protected boolean findNearestBlock() { + // Leaf start - Async search block + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchBlock) { + getBlockAsync(); + return false; + } + // Leaf end - Async search block int i = this.searchRange; int i1 = this.verticalSearchRange; BlockPos blockPos = this.mob.blockPosition(); @@ -133,5 +200,108 @@ public abstract class MoveToBlockGoal extends Goal { return false; } + // Leaf start - Async search block + protected static boolean findNearestBlockAsync( + final org.dreeam.leaf.async.ai.Waker ctx, + final TypeToCheck ty, + @org.jetbrains.annotations.Nullable final net.minecraft.world.level.block.Block toRemove, + final PathfinderMob mob, + final net.minecraft.server.level.ServerLevel serverLevel, + final int verticalSearchStart, + final int searchRange, + final int verticalSearchRange, + final BlockPos blockPos, + final float restrictRadius, + final BlockPos restrictCenter + ) { + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + for (int i2 = verticalSearchStart; i2 <= verticalSearchRange; i2 = i2 > 0 ? -i2 : 1 - i2) { + for (int i3 = 0; i3 < searchRange; i3++) { + 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); + if (!serverLevel.hasChunkAt(mutableBlockPos)) continue; + if (isWithinRestriction(restrictRadius, restrictCenter, mutableBlockPos) + && isValidTargetAsync(ty, toRemove, mob, serverLevel, mutableBlockPos)) { + ctx.result = mutableBlockPos.immutable(); + return true; + } + } + } + } + } + + return false; + } + + private static boolean isWithinRestriction(float restrictRadius, BlockPos restrictCenter, BlockPos pos) { + return restrictRadius == -1.0F || restrictCenter.distSqr(pos) < restrictRadius * restrictRadius; + } + // Leaf end - Async search block + protected abstract boolean isValidTarget(LevelReader level, BlockPos pos); + + // Leaf start - Async search block + protected abstract TypeToCheck typeToCheck(); + + private static boolean isValidTargetAsync( + TypeToCheck type, + @org.jetbrains.annotations.Nullable net.minecraft.world.level.block.Block blockToRemoveTy, + PathfinderMob mob, + LevelReader level, + BlockPos pos + ) { + switch (type) { + case CatLie -> { + return level.isEmptyBlock(pos.above()) && level.getBlockState(pos).is(net.minecraft.tags.BlockTags.BEDS); + } + case CatSit -> { + if (!level.isEmptyBlock(pos.above())) { + return false; + } else { + var blockState = level.getBlockState(pos); + return blockState.is(net.minecraft.world.level.block.Blocks.CHEST) + ? net.minecraft.world.level.block.entity.ChestBlockEntity.getOpenCount(level, pos) < 1 + : blockState.is(net.minecraft.world.level.block.Blocks.FURNACE) && blockState.getValue(net.minecraft.world.level.block.FurnaceBlock.LIT) + || blockState.is(net.minecraft.tags.BlockTags.BEDS, state -> state.getOptionalValue(net.minecraft.world.level.block.BedBlock.PART).map(bedPart -> bedPart != net.minecraft.world.level.block.state.properties.BedPart.HEAD).orElse(true)); + } + } + case Drowned -> { + BlockPos blockPos = pos.above(); + return level.isEmptyBlock(blockPos) && level.isEmptyBlock(blockPos.above()) && level.getBlockState(pos).entityCanStandOn(level, pos, mob); + } + case FoxEat -> { + var blockState = level.getBlockState(pos); + return blockState.is(net.minecraft.world.level.block.Blocks.SWEET_BERRY_BUSH) && blockState.getValue(net.minecraft.world.level.block.SweetBerryBushBlock.AGE) >= 2 || net.minecraft.world.level.block.CaveVines.hasGlowBerries(blockState); + } + case RaidGarden -> { + var blockState = level.getBlockState(pos); + if (blockState.is(net.minecraft.world.level.block.Blocks.FARMLAND)) { + blockState = level.getBlockState(pos.above()); + return blockState.getBlock() instanceof net.minecraft.world.level.block.CarrotBlock carrot && carrot.isMaxAge(blockState); + } else { + return false; + } + + } + case RemoveBlock -> { + var chunk = level.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4); // Paper - Prevent AI rules from loading chunks + return chunk != null + && chunk.getBlockState(pos).is(blockToRemoveTy) + && chunk.getBlockState(pos.above()).isAir() + && chunk.getBlockState(pos.above(2)).isAir(); + } + case Strider -> { + return level.getBlockState(pos).is(net.minecraft.world.level.block.Blocks.LAVA) && level.getBlockState(pos.above()).isPathfindable(net.minecraft.world.level.pathfinder.PathComputationType.LAND); + } + case TurtleToWater -> { + return level.getBlockState(pos).is(net.minecraft.world.level.block.Blocks.WATER); + } + case TurtleLay -> { + return level.isEmptyBlock(pos.above()) && net.minecraft.world.level.block.TurtleEggBlock.isSand(level, pos); + } + case null -> throw new IllegalStateException(); + } + // Leaf end - Async search block + } } diff --git a/net/minecraft/world/entity/ai/goal/OfferFlowerGoal.java b/net/minecraft/world/entity/ai/goal/OfferFlowerGoal.java index 4ba5f7da27f7f6842790c0093bc0f25e138fa982..1dcae83aa6b9e9318d320a17bf17a0b0eae80a60 100644 --- a/net/minecraft/world/entity/ai/goal/OfferFlowerGoal.java +++ b/net/minecraft/world/entity/ai/goal/OfferFlowerGoal.java @@ -19,10 +19,20 @@ public class OfferFlowerGoal extends Goal { @Override public boolean canUse() { + // Leaf start - Async offer flower finding if (!this.golem.level().isBrightOutside()) { return false; - } else if (this.golem.getRandom().nextInt(8000) != 0) { + } + if (poll()) { + return true; + } + if (this.golem.getRandom().nextInt(8000) != 0) { + return false; + } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + getVillagerAsync(); return false; + // Leaf end - Async offer flower finding } else { this.villager = getServerLevel(this.golem) .getNearestEntity( @@ -38,6 +48,45 @@ public class OfferFlowerGoal extends Goal { } } + + // Leaf start - Async look finding + protected boolean poll() { + if (!(this.golem.getGoalCtx().result() instanceof Villager target)) return false; + var serverLevel = getServerLevel(this.golem); + if (!target.isAlive() || !OFFER_TARGER_CONTEXT.test(serverLevel, this.golem, target)) return false; + this.villager = target; + return true; + } + + protected void getVillagerAsync() { + final var golem = this.golem; + final var ctx = golem.getGoalCtx(); + if (!ctx.state) return; + final double x = golem.getX(); + final double y = golem.getEyeY(); + final double z = golem.getZ(); + final var serverLevel = getServerLevel(golem); + final var bound = golem.getBoundingBox().inflate(6.0, 2.0, 6.0); + ctx.wake = () -> { + try { + if (golem == null || !golem.isAlive() || golem.level() != serverLevel) { + return; + } + ctx.result = serverLevel.getNearestEntity( + serverLevel.getEntitiesOfClass(Villager.class, bound, livingEntity -> true), + OFFER_TARGER_CONTEXT, + golem, + x, + y, + z + ); + } catch (Exception e) { + org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } + }; + } + // Leaf end - Async look finding + @Override public boolean canContinueToUse() { return this.tick > 0; diff --git a/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java b/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java index 084156166905562beb8d36db400bc9cab5c14be2..3eed1ac1cab130034f47da6efa33c0ad2a77a7e2 100644 --- a/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java +++ b/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java @@ -37,7 +37,14 @@ public class RemoveBlockGoal extends MoveToBlockGoal { public boolean canUse() { if (!getServerLevel(this.removerMob).purpurConfig.zombieBypassMobGriefing == !getServerLevel(this.removerMob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass to everything affected return false; - } else if (this.nextStartTick > 0) { + } + // Leaf start - async search block + if (poll()) { + this.nextStartTick = reducedTickDelay(20); + return true; + } + // Leaf end - async search block + if (this.nextStartTick > 0) { this.nextStartTick--; return false; } else if (this.findNearestBlock()) { @@ -151,8 +158,15 @@ public class RemoveBlockGoal extends MoveToBlockGoal { protected boolean isValidTarget(LevelReader level, BlockPos pos) { ChunkAccess chunk = level.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4); // Paper - Prevent AI rules from loading chunks return chunk != null - && chunk.getBlockState(pos).is(this.blockToRemove) + && chunk.getBlockState(pos).is(this.blockToRemove) // Leaf - Async search block - diff on change && chunk.getBlockState(pos.above()).isAir() && chunk.getBlockState(pos.above(2)).isAir(); } + + // Leaf start - Async search block + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.RemoveBlock; + } + // Leaf end - Async search block } diff --git a/net/minecraft/world/entity/ai/goal/TemptGoal.java b/net/minecraft/world/entity/ai/goal/TemptGoal.java index f88f618d34fb343b31de3af1a875d6633703df71..e42f77fc6d04a900fd8adb4682678f9218561aa8 100644 --- a/net/minecraft/world/entity/ai/goal/TemptGoal.java +++ b/net/minecraft/world/entity/ai/goal/TemptGoal.java @@ -36,12 +36,60 @@ public class TemptGoal extends Goal { this.targetingConditions = TEMPT_TARGETING.copy().selector((entity, level) -> this.shouldFollow(entity)); } + // Leaf start - Async Tempt Finding + private boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof Player target)) return false; + var serverLevel = getServerLevel(this.mob); + if (!target.isAlive() || !this.targetingConditions.range(this.mob.getAttributeValue(Attributes.TEMPT_RANGE)).test(serverLevel, this.mob, target)) return false; + this.player = target; + return true; + } + + private void getNearestPlayerAsync() { + final var mob = this.mob; + final var ctx = mob.getGoalCtx(); + if (!ctx.state) return; + final var serverLevel = getServerLevel(mob); + final var conditions = this.targetingConditions + .range(mob.getAttributeValue(Attributes.TEMPT_RANGE)) + .copy(); + ctx.wake = () -> { + try { + if (mob == null || mob.isRemoved() || !mob.isAlive() || mob.level() != serverLevel) { + return; + } + ctx.result = serverLevel.getNearestPlayer(conditions, mob); + } catch (Exception e) { + org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting player", e); + } + }; + } + // Leaf end - Async Tempt Finding @Override public boolean canUse() { if (this.calmDown > 0) { this.calmDown--; return false; } else { + // Leaf start - Async Tempt Finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + if (poll()) { + if (this.player != null) { + org.bukkit.event.entity.EntityTargetLivingEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(this.mob, this.player, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TEMPT); + if (event.isCancelled()) { + return false; + } + this.player = (event.getTarget() == null) ? null : ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getTarget()).getHandle(); + } + if (this.player != null) { + return true; + } + } else { + getNearestPlayerAsync(); + return false; + } + } + // Leaf end - Async Tempt Finding this.player = getServerLevel(this.mob) .getNearestPlayer(this.targetingConditions.range(this.mob.getAttributeValue(Attributes.TEMPT_RANGE)), this.mob); // CraftBukkit start diff --git a/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java index fb160a59c873d5c3f2c3d31966ca1a653f1b384d..47802c05cec6a0d8b64a592332d71259d91d5d85 100644 --- a/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java @@ -16,7 +16,7 @@ public class DefendVillageTargetGoal extends TargetGoal { private final IronGolem golem; @Nullable private LivingEntity potentialTarget; - private final TargetingConditions attackTargeting = TargetingConditions.forCombat().range(64.0); + private static final TargetingConditions attackTargeting = TargetingConditions.forCombat().range(64.0); // Leaf - Async Target Finding - static public DefendVillageTargetGoal(IronGolem golem) { super(golem, false, true); @@ -24,8 +24,58 @@ public class DefendVillageTargetGoal extends TargetGoal { this.setFlags(EnumSet.of(Goal.Flag.TARGET)); } + // Leaf start - Async Target Finding + protected boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof LivingEntity target)) return false; + ServerLevel serverLevel = getServerLevel(this.mob); + if (serverLevel == null || !target.isAlive() || !attackTargeting.test(serverLevel, golem, target)) return false; + if ((target instanceof Player player && (player.isSpectator() || player.isCreative()))) return false; + this.potentialTarget = target; + return true; + } + + private void findTargetAsync() { + final IronGolem mob = this.golem; + final var ctx = mob.getGoalCtx(); + if (!ctx.state) return; + AABB bound = this.golem.getBoundingBox().inflate(10.0, 8.0, 10.0); + final ServerLevel serverLevel = getServerLevel(mob); + ctx.wake = () -> { + try { + if (mob.level() != serverLevel || !mob.isAlive()) { + return; + } + + List nearbyEntities = serverLevel.getNearbyEntities(Villager.class, attackTargeting, mob, bound); + List nearbyPlayers = serverLevel.getNearbyPlayers(attackTargeting, mob, bound); + + for (Villager villager : nearbyEntities) { + for (Player player : nearbyPlayers) { + int playerReputation = villager.getPlayerReputation(player); + if (playerReputation <= -100) { + ctx.result = player; + break; + } + } + } + } catch (Exception e) { + org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } + }; + } + // Leaf end - Async Target Finding + @Override public boolean canUse() { + // Leaf start - Async target finding + if (poll()) { + return true; + } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + this.findTargetAsync(); + return false; + } + // Leaf end - Async target finding AABB aabb = this.golem.getBoundingBox().inflate(10.0, 8.0, 10.0); ServerLevel serverLevel = getServerLevel(this.golem); List nearbyEntities = serverLevel.getNearbyEntities(Villager.class, this.attackTargeting, this.golem, aabb); @@ -42,7 +92,7 @@ public class DefendVillageTargetGoal extends TargetGoal { } } - return this.potentialTarget != null && !(this.potentialTarget instanceof Player player1 && (player1.isSpectator() || player1.isCreative())); + return this.potentialTarget != null && !(this.potentialTarget instanceof Player player1 && (player1.isSpectator() || player1.isCreative())); // Leaf - Async target finding - diff on change } @Override diff --git a/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java index a8ec1d5f4b0fb0ff26a234235b7d8d9c4b4a2a98..24317ff50332e624a5587a0273082005ab3f36ed 100644 --- a/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java @@ -73,6 +73,56 @@ public class HurtByTargetGoal extends TargetGoal { protected void alertOthers() { double followDistance = this.getFollowDistance(); AABB aabb = AABB.unitCubeFromLowerCorner(this.mob.position()).inflate(followDistance, 10.0, followDistance); + + // Leaf start - Async alert other + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.alertOther) { + final var self = this.mob; + final var ctx = self.getGoalCtx(); + if (!ctx.state) return; + final var serverLevel = getServerLevel(self); + final var toIgnoreAlert = this.toIgnoreAlert; + ctx.wake = () -> { + try { + if (self == null || self.level() != serverLevel) { + return; + } + var toAlert = new java.util.ArrayList(); + List entitiesOfClass = serverLevel + .getEntitiesOfClass(self.getClass(), aabb, EntitySelector.NO_SPECTATORS); + for (Mob mob : entitiesOfClass) { + if (self != mob + && mob.getTarget() == null + && (!(self instanceof TamableAnimal) || ((TamableAnimal) self).getOwner() == ((TamableAnimal) mob).getOwner()) + && !mob.isAlliedTo(self.getLastHurtByMob())) { + if (toIgnoreAlert == null) { + continue; + } + + boolean flag = false; + + for (Class clazz : toIgnoreAlert) { + if (mob.getClass() == clazz) { + flag = true; + break; + } + } + + if (!flag) { + continue; + } + + toAlert.add(mob); + } + } + ctx.result = toAlert; + } catch (Exception e) { + org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception alertOthers", e); + } + }; + return; + } + // Leaf end - Async alert other + List entitiesOfClass = this.mob .level() .getEntitiesOfClass((Class)this.mob.getClass(), aabb, EntitySelector.NO_SPECTATORS); @@ -87,7 +137,7 @@ public class HurtByTargetGoal extends TargetGoal { mob = (Mob)var5.next(); if (this.mob != mob - && mob.getTarget() == null + && mob.getTarget() == null // Leaf - Async alert other - diff on change && (!(this.mob instanceof TamableAnimal) || ((TamableAnimal)this.mob).getOwner() == ((TamableAnimal)mob).getOwner()) && !mob.isAlliedTo(this.mob.getLastHurtByMob())) { if (this.toIgnoreAlert == null) { @@ -96,7 +146,7 @@ public class HurtByTargetGoal extends TargetGoal { boolean flag = false; - for (Class clazz : this.toIgnoreAlert) { + for (Class clazz : this.toIgnoreAlert) { // Leaf - Async alert other - diff on change if (mob.getClass() == clazz) { flag = true; break; @@ -113,6 +163,15 @@ public class HurtByTargetGoal extends TargetGoal { } } + // Leaf start - Async alert other + public void poll() { + if (!(this.mob.getGoalCtx().result() instanceof List toAlert)) return; + for (var livingEntity : toAlert) { + alertOther((Mob) livingEntity, this.mob.getLastHurtByMob()); + } + } + // Leaf end - Async alert other + protected void alertOther(Mob mob, LivingEntity target) { mob.setTarget(target, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_NEARBY_ENTITY); // CraftBukkit - reason } diff --git a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java index 1cd8143c938237ce035fa866d4f2ed1e2cd1ebca..719dbfb7c9f463767bbee33ecb9d764be06622a3 100644 --- a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java @@ -41,8 +41,51 @@ public class NearestAttackableTargetGoal extends TargetG this.targetConditions = TargetingConditions.forCombat().range(this.getFollowDistance()).selector(selector); } + // Leaf start - Async Target Finding + protected boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof LivingEntity target)) return false; + ServerLevel serverLevel = getServerLevel(this.mob); + if (serverLevel == null || !target.isAlive() || !this.getTargetConditions().test(serverLevel, this.mob, target)) return false; + this.target = target; + return true; + } + + private void findTargetAsync() { + final Mob mob = this.mob; + final var ctx = mob.getGoalCtx(); + if (!ctx.state) return; + final double x = mob.getX(); + final double y = mob.getEyeY(); + final double z = mob.getZ(); + final TargetingConditions targetConditions = this.getTargetConditions().copy(); + final Class targetType = this.targetType; + final AABB targetSearch = getTargetSearchArea(this.getFollowDistance()); + final ServerLevel serverLevel = getServerLevel(mob); + ctx.wake = () -> { + try { + if (mob.level() != serverLevel || mob.isRemoved() || !mob.isAlive()) { + return; + } + + if (targetType != Player.class && targetType != ServerPlayer.class) { + ctx.result = serverLevel.getNearestEntity(mob.level().getEntitiesOfClass(targetType, targetSearch, entity -> entity != null && entity != mob && entity.isAlive()), targetConditions, mob, x,y,z); + } else { + ctx.result = serverLevel.getNearestPlayer(targetConditions, mob, x, y, z); + } + } catch (Exception e) { + org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } + }; + } + // Leaf end - Async Target Finding + @Override public boolean canUse() { + // Leaf start - Async target finding + if (poll()) { + return true; + } + // Leaf end - Async target finding if (this.randomInterval > 0 && this.mob.getRandom().nextInt(this.randomInterval) != 0) { return false; } else { @@ -57,6 +100,15 @@ public class NearestAttackableTargetGoal extends TargetG protected void findTarget() { ServerLevel serverLevel = getServerLevel(this.mob); + + // Leaf start - Async Target Finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + this.findTargetAsync(); + this.target = null; + return; + } + // Leaf end - Async Target Finding + if (this.targetType != Player.class && this.targetType != ServerPlayer.class) { this.target = serverLevel.getNearestEntity( this.mob.level().getEntitiesOfClass(this.targetType, this.getTargetSearchArea(this.getFollowDistance()), entity -> true), diff --git a/net/minecraft/world/entity/ai/goal/target/NearestHealableRaiderTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/NearestHealableRaiderTargetGoal.java index 4604a603c4ddd0a9242e859aaa5a511c2d4c4f84..7274dfb52ca4a08cdebcd04294cedc73460593e5 100644 --- a/net/minecraft/world/entity/ai/goal/target/NearestHealableRaiderTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/NearestHealableRaiderTargetGoal.java @@ -23,7 +23,15 @@ public class NearestHealableRaiderTargetGoal extends Nea @Override public boolean canUse() { - if (this.cooldown > 0 || !this.mob.getRandom().nextBoolean()) { + // Leaf start - Async target finding + if (this.cooldown > 0) { + return false; + } + if (poll()) { + return ((Raider) this.mob).hasActiveRaid(); + } + if (/*this.cooldown > 0 || */ !this.mob.getRandom().nextBoolean()) { + // Leaf end - Async target finding return false; } else if (!((Raider)this.mob).hasActiveRaid()) { return false; diff --git a/net/minecraft/world/entity/ai/goal/target/NonTameRandomTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/NonTameRandomTargetGoal.java index abf57494950f55bbd75f335f26736cb9e703c197..efd2418f56c36e7850edde483a2a4906dd622441 100644 --- a/net/minecraft/world/entity/ai/goal/target/NonTameRandomTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/NonTameRandomTargetGoal.java @@ -20,6 +20,15 @@ public class NonTameRandomTargetGoal extends NearestAtta @Override public boolean canContinueToUse() { - return this.targetConditions != null ? this.targetConditions.test(getServerLevel(this.mob), this.mob, this.target) : super.canContinueToUse(); + // Leaf start - Async target finding + if (this.target == null || !this.target.isAlive() || this.target.isRemoved()) { + return false; + } + var serverLevel = getServerLevel(this.mob); + if (serverLevel == null) { + return false; + } + return this.getTargetConditions().test(serverLevel, this.mob, this.target) && super.canContinueToUse(); + // Leaf end - Async target finding } } diff --git a/net/minecraft/world/entity/ai/goal/target/ResetUniversalAngerTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/ResetUniversalAngerTargetGoal.java index 61a7bb5f1760d47a13b6aafc123bcf3dff420860..15da909b4db3d7a7532aed550208ecbb76144b37 100644 --- a/net/minecraft/world/entity/ai/goal/target/ResetUniversalAngerTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/ResetUniversalAngerTargetGoal.java @@ -37,6 +37,34 @@ public class ResetUniversalAngerTargetGoal extends G this.lastHurtByPlayerTimestamp = this.mob.getLastHurtByMobTimestamp(); this.mob.forgetCurrentTargetAndRefreshUniversalAnger(); if (this.alertOthersOfSameType) { + // Leaf start - Async alert other + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.alertOther) { + final var mob = this.mob; + final var ctx = mob.getGoalCtx(); + if (!ctx.state) return; + final var serverLevel = getServerLevel(mob); + final double followRange = this.mob.getAttributeValue(Attributes.FOLLOW_RANGE); + final AABB aabb = AABB.unitCubeFromLowerCorner(this.mob.position()).inflate(followRange, 10.0, followRange); + ctx.wake = () -> { + try { + if (mob == null || serverLevel != mob.level() || !mob.isAlive()) { + return; + } + var entities = serverLevel.getEntitiesOfClass(mob.getClass(), aabb, EntitySelector.NO_SPECTATORS); + List toStop = new java.util.ArrayList<>(entities.size()); + for (Mob entity : entities) { + if (entity != mob) { + toStop.add((NeutralMob) entity); + } + } + ctx.result = toStop; + } catch (Exception e) { + org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception alertOthers forgetCurrentTargetAndRefreshUniversalAnger", e); + } + }; + return; + } + // Leaf end - Async alert other this.getNearbyMobsOfSameType() .stream() .filter(mob -> mob != this.mob) @@ -47,7 +75,17 @@ public class ResetUniversalAngerTargetGoal extends G super.start(); } + // Leaf start - Async alert other + public void poll() { + if (!(this.mob.getGoalCtx().result() instanceof List toStop)) return; + for (var neutralMob : toStop) { + ((NeutralMob)neutralMob).forgetCurrentTargetAndRefreshUniversalAnger(); + } + } + // Leaf end - Async alert other + private List getNearbyMobsOfSameType() { + // Leaf - Async alert other - diff on change double attributeValue = this.mob.getAttributeValue(Attributes.FOLLOW_RANGE); AABB aabb = AABB.unitCubeFromLowerCorner(this.mob.position()).inflate(attributeValue, 10.0, attributeValue); return this.mob.level().getEntitiesOfClass((Class)this.mob.getClass(), aabb, EntitySelector.NO_SPECTATORS); diff --git a/net/minecraft/world/entity/ai/sensing/Sensing.java b/net/minecraft/world/entity/ai/sensing/Sensing.java index 002d3c0d8b1107a275020d5c582c37e9a5c536ee..6fa0b8defbd1d06b3bf5d9b32ffd08f32c1fb707 100644 --- a/net/minecraft/world/entity/ai/sensing/Sensing.java +++ b/net/minecraft/world/entity/ai/sensing/Sensing.java @@ -33,6 +33,17 @@ public class Sensing { } public void tick() { + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + synchronized (this) { + tickInternal(); + } + } else { + tickInternal(); + } + } + private void tickInternal() { + // Leaf end - Async target finding if (this.expiring == null) { // Gale - Petal - reduce line of sight updates this.seen.clear(); // Gale start - Petal - reduce line of sight updates @@ -63,6 +74,17 @@ public class Sensing { } public boolean hasLineOfSight(Entity entity) { + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + synchronized (this) { + return hasLineOfSightInternal(entity); + } + } else { + return hasLineOfSightInternal(entity); + } + } + private boolean hasLineOfSightInternal(Entity entity) { + // Leaf end - Async target finding int id = entity.getId(); // Gale start - Petal - reduce line of sight cache lookups - merge sets int cached = this.seen.get(id); diff --git a/net/minecraft/world/entity/animal/Fox.java b/net/minecraft/world/entity/animal/Fox.java index fcda990a5575288b39f6aa16b0ec48d2a57542e9..fb956c0bd8a877117ec33f753f6e6bb096831f78 100644 --- a/net/minecraft/world/entity/animal/Fox.java +++ b/net/minecraft/world/entity/animal/Fox.java @@ -870,6 +870,11 @@ public class Fox extends Animal { return false; } else { ServerLevel serverLevel = getServerLevel(Fox.this.level()); + // Leaf start - Async target finding + if (serverLevel == null) { + return false; + } + // Leaf end - Async target finding for (EntityReference entityReference : Fox.this.getTrustedEntities().toList()) { LivingEntity livingEntity = entityReference.getEntity(serverLevel, LivingEntity.class); @@ -877,7 +882,7 @@ public class Fox extends Animal { this.trustedLastHurt = livingEntity; this.trustedLastHurtBy = livingEntity.getLastHurtByMob(); int lastHurtByMobTimestamp = livingEntity.getLastHurtByMobTimestamp(); - return lastHurtByMobTimestamp != this.timestamp && this.canAttack(this.trustedLastHurtBy, this.targetConditions); + return lastHurtByMobTimestamp != this.timestamp && this.canAttack(this.trustedLastHurtBy, this.getTargetConditions()); // Leaf - Async target finding } } @@ -1056,7 +1061,7 @@ public class Fox extends Animal { @Override protected boolean isValidTarget(LevelReader level, BlockPos pos) { BlockState blockState = level.getBlockState(pos); - return blockState.is(Blocks.SWEET_BERRY_BUSH) && blockState.getValue(SweetBerryBushBlock.AGE) >= 2 || CaveVines.hasGlowBerries(blockState); + return blockState.is(Blocks.SWEET_BERRY_BUSH) && blockState.getValue(SweetBerryBushBlock.AGE) >= 2 || CaveVines.hasGlowBerries(blockState); // Leaf - Async search block - diff on change } @Override @@ -1120,6 +1125,13 @@ public class Fox extends Animal { Fox.this.setSitting(false); super.start(); } + + // Leaf start - Async search block + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.FoxEat; + } + // Leaf end - Async search block } class FoxFloatGoal extends FloatGoal { diff --git a/net/minecraft/world/entity/animal/Panda.java b/net/minecraft/world/entity/animal/Panda.java index 99d99d59ec0eb13dc40bc88bd70ad884bb9e2859..d0be031cedcc895b8cca8666f826628ac8ce6ab3 100644 --- a/net/minecraft/world/entity/animal/Panda.java +++ b/net/minecraft/world/entity/animal/Panda.java @@ -988,9 +988,18 @@ public class Panda extends Animal { @Override public boolean canUse() { + // Leaf start - Async look finding + if (poll()) { + return true; + } if (this.mob.getRandom().nextFloat() >= this.probability) { return false; } else { + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + getLookAsync(); + return false; + } + // Leaf end - Async look finding if (this.lookAt == null) { ServerLevel serverLevel = getServerLevel(this.mob); if (this.lookAtType == Player.class) { diff --git a/net/minecraft/world/entity/animal/Rabbit.java b/net/minecraft/world/entity/animal/Rabbit.java index 38e2698c5084f0c14c7fde5de542ed61a7cb7f1d..d96eb5a6fde72a9e7e7799624b63ec72e9b77f90 100644 --- a/net/minecraft/world/entity/animal/Rabbit.java +++ b/net/minecraft/world/entity/animal/Rabbit.java @@ -669,7 +669,13 @@ public class Rabbit extends Animal { this.wantsToRaid = this.rabbit.wantsMoreFood(); } - return super.canUse(); + // Leaf start - Async search block + if (this.wantsToRaid && !this.canRaid) { + return super.canUse(); + } else { + return false; + } + // Leaf end - Async search block } @Override @@ -712,7 +718,7 @@ public class Rabbit extends Animal { @Override protected boolean isValidTarget(LevelReader level, BlockPos pos) { BlockState blockState = level.getBlockState(pos); - if (blockState.is(Blocks.FARMLAND) && this.wantsToRaid && !this.canRaid) { + if (blockState.is(Blocks.FARMLAND) && this.wantsToRaid && !this.canRaid) { // Leaf - Async search block - diff on change blockState = level.getBlockState(pos.above()); if (blockState.getBlock() instanceof CarrotBlock && ((CarrotBlock)blockState.getBlock()).isMaxAge(blockState)) { this.canRaid = true; @@ -722,6 +728,13 @@ public class Rabbit extends Animal { return false; } + + // Leaf start - Async search block + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.RaidGarden; + } + // Leaf end - Async search block } public static enum Variant implements StringRepresentable { diff --git a/net/minecraft/world/entity/animal/Turtle.java b/net/minecraft/world/entity/animal/Turtle.java index c84f63f064a7769761f75cdedaceacde858b9b4e..9b7d059bf68ff24dddedb16ec62a9b67e3ceace9 100644 --- a/net/minecraft/world/entity/animal/Turtle.java +++ b/net/minecraft/world/entity/animal/Turtle.java @@ -482,8 +482,15 @@ public class Turtle extends Animal { @Override protected boolean isValidTarget(LevelReader level, BlockPos pos) { - return level.getBlockState(pos).is(Blocks.WATER); + return level.getBlockState(pos).is(Blocks.WATER); // Leaf - Async search block - diff on change } + + // Leaf start - Async search block + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.TurtleToWater; + } + // Leaf end - Async search block } static class TurtleLayEggGoal extends MoveToBlockGoal { @@ -537,8 +544,15 @@ public class Turtle extends Animal { @Override protected boolean isValidTarget(LevelReader level, BlockPos pos) { - return level.isEmptyBlock(pos.above()) && TurtleEggBlock.isSand(level, pos); + return level.isEmptyBlock(pos.above()) && TurtleEggBlock.isSand(level, pos); // Leaf - Async search block - diff on change + } + + // Leaf start - Async search block + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.TurtleLay; } + // Leaf end - Async search block } static class TurtleMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - Ridables diff --git a/net/minecraft/world/entity/monster/Drowned.java b/net/minecraft/world/entity/monster/Drowned.java index 2e04078664cd723e3e0c80565e4b6e6416b13901..ed790a407b4be9e2f7fcb8cf36b8d03c32864a59 100644 --- a/net/minecraft/world/entity/monster/Drowned.java +++ b/net/minecraft/world/entity/monster/Drowned.java @@ -397,7 +397,7 @@ public class Drowned extends Zombie implements RangedAttackMob { @Override protected boolean isValidTarget(LevelReader level, BlockPos pos) { BlockPos blockPos = pos.above(); - return level.isEmptyBlock(blockPos) && level.isEmptyBlock(blockPos.above()) && level.getBlockState(pos).entityCanStandOn(level, pos, this.drowned); + return level.isEmptyBlock(blockPos) && level.isEmptyBlock(blockPos.above()) && level.getBlockState(pos).entityCanStandOn(level, pos, this.drowned); // Leaf - Async search block - diff on change } @Override @@ -411,6 +411,13 @@ public class Drowned extends Zombie implements RangedAttackMob { public void stop() { super.stop(); } + + // Leaf start - Async search block + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.Drowned; + } + // Leaf end - Async search block } static class DrownedGoToWaterGoal extends Goal { diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java index e1717b5c854aa81fdd7b7e715d7c3498d9f86072..3627e7479b4deea28e268245410ec4cd48f24e9e 100644 --- a/net/minecraft/world/entity/monster/Strider.java +++ b/net/minecraft/world/entity/monster/Strider.java @@ -551,8 +551,15 @@ public class Strider extends Animal implements ItemSteerable { @Override protected boolean isValidTarget(LevelReader level, BlockPos pos) { - return level.getBlockState(pos).is(Blocks.LAVA) && level.getBlockState(pos.above()).isPathfindable(PathComputationType.LAND); + return level.getBlockState(pos).is(Blocks.LAVA) && level.getBlockState(pos.above()).isPathfindable(PathComputationType.LAND); // Leaf - Async search block - diff on change } + + // Leaf start - Async search block + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.Strider; + } + // Leaf end - Async search block } static class StriderPathNavigation extends GroundPathNavigation { diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java index 20be5c4200bfee01d62fa1103a3663b91107d5d8..5fbe3356ba9b7c4c09f87fda59971552ef6bb676 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java @@ -1525,6 +1525,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)(Level)(Object)this).moonrise$midTickTasks(); } // Paper end - rewrite chunk system + ((ServerLevel) this).leafMidTickTasks(); // Leaf - Async target finding } } this.blockEntityTickers.removeMarkedEntries(); // SparklyPaper - optimize block entity removals @@ -1788,7 +1789,11 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl @Override public List getEntities(@Nullable Entity entity, AABB boundingBox, Predicate predicate) { - List list = Lists.newArrayList(); + // Leaf start - Async target finding + // if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) + // ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel)this, boundingBox, "Cannot getEntities asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs) + // List list = Lists.newArrayList(); // Leaf - Async target finding - unused + // Leaf end - Async target finding // Paper start - rewrite chunk system final List ret = new java.util.ArrayList<>();