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/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..561723eeb1dd80f7fdd9aff33f6e1c4c16def0ff 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java @@ -40,8 +40,10 @@ public final class ChunkEntitySlices { private final EntityCollectionBySection allEntities; private final EntityCollectionBySection hardCollidingEntities; - private final Reference2ObjectOpenHashMap, EntityCollectionBySection> entitiesByClass; - private final Reference2ObjectOpenHashMap, EntityCollectionBySection> entitiesByType; + // Leaf start - Async target finding + private final Reference2ObjectMap, EntityCollectionBySection> entitiesByClass; + private final Reference2ObjectMap, EntityCollectionBySection> entitiesByType; + // Leaf end - Async target finding private final EntityList entities = new EntityList(); public FullChunkStatus status; @@ -67,9 +69,15 @@ public final class ChunkEntitySlices { this.allEntities = new EntityCollectionBySection(this); this.hardCollidingEntities = new EntityCollectionBySection(this); - this.entitiesByClass = new Reference2ObjectOpenHashMap<>(); - this.entitiesByType = new Reference2ObjectOpenHashMap<>(); - + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + this.entitiesByClass = it.unimi.dsi.fastutil.objects.Reference2ObjectMaps.synchronize(new Reference2ObjectOpenHashMap<>()); + this.entitiesByType = it.unimi.dsi.fastutil.objects.Reference2ObjectMaps.synchronize(new Reference2ObjectOpenHashMap<>()); + } else { + this.entitiesByClass = new Reference2ObjectOpenHashMap<>(); + this.entitiesByType = new Reference2ObjectOpenHashMap<>(); + } + // Leaf end - Async target finding this.status = status; this.chunkData = chunkData; } @@ -248,14 +256,26 @@ public final class ChunkEntitySlices { this.hardCollidingEntities.addEntity(entity, sectionIndex); } - for (final Iterator, EntityCollectionBySection>> iterator = - this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { - final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + synchronized (this.entitiesByClass) { + for (final var entry : this.entitiesByClass.reference2ObjectEntrySet()) { + if (entry.getKey().isInstance(entity)) { + entry.getValue().addEntity(entity, sectionIndex); + } + } + } + } else { + for (final Iterator, EntityCollectionBySection>> iterator = + this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext(); ) { + final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); - if (entry.getKey().isInstance(entity)) { - entry.getValue().addEntity(entity, sectionIndex); + if (entry.getKey().isInstance(entity)) { + entry.getValue().addEntity(entity, sectionIndex); + } } } + // Leaf end - Async target finding EntityCollectionBySection byType = this.entitiesByType.get(entity.getType()); if (byType != null) { @@ -282,14 +302,27 @@ public final class ChunkEntitySlices { this.hardCollidingEntities.removeEntity(entity, sectionIndex); } - for (final Iterator, EntityCollectionBySection>> iterator = - this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { - final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + synchronized (this.entitiesByClass) { + for (final var entry : this.entitiesByClass.reference2ObjectEntrySet()) { + if (entry.getKey().isInstance(entity)) { + entry.getValue().removeEntity(entity, sectionIndex); + } + } + } + } else { + for (final Iterator, EntityCollectionBySection>> iterator = + this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) { + final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); - if (entry.getKey().isInstance(entity)) { - entry.getValue().removeEntity(entity, sectionIndex); + if (entry.getKey().isInstance(entity)) { + entry.getValue().removeEntity(entity, sectionIndex); + } } } + // Leaf end - Async target finding + final EntityCollectionBySection byType = this.entitiesByType.get(entity.getType()); byType.removeEntity(entity, sectionIndex); diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java index 24926aa7ed5c78b235659daf18b224b14beb744c..98af1ad020a003db66d7319f33d43deec315aec5 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -292,6 +292,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) public java.util.concurrent.Semaphore serverLevelTickingSemaphore = null; // SparklyPaper - parallel world ticking + @Nullable public org.dreeam.leaf.async.ai.AsyncGoalThread asyncGoalThread; // Leaf - Async target finding public static S spin(Function threadFunction) { ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system @@ -1088,6 +1089,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 @@ -218,6 +227,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current) public boolean hasRidableMoveEvent = false; // Purpur - Ridables final List realPlayers; // Leaves - skip + public final @Nullable org.dreeam.leaf.async.ai.AsyncGoalExecutor asyncGoalExecutor; // Leaf - Async target finding public LevelChunk getChunkIfLoaded(int x, int z) { return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately @@ -335,6 +345,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); @@ -711,6 +727,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe this.preciseTime = this.serverLevelData.getDayTime(); // Purpur - Configurable daylight cycle this.realPlayers = Lists.newArrayList(); // Leaves - skip this.tickExecutor = java.util.concurrent.Executors.newSingleThreadExecutor(new org.dreeam.leaf.async.world.SparklyPaperServerLevelTickExecutorThreadFactory(getWorld().getName())); // SparklyPaper - parallel world ticking + // 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 @@ -855,12 +878,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.tick(); // Leaf - Async target finding } // Paper - rewrite chunk system @@ -1329,6 +1354,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // Leaf end - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) // Paper end - rewrite chunk system + this.leafMidTickTasks(); // Leaf - Async target finding } private void tickBlock(BlockPos pos, Block block) { @@ -1345,6 +1371,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // Leaf end - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) // 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/Mob.java b/net/minecraft/world/entity/Mob.java index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..88809afe30bb970a7de8bdfd269268800516c426 100644 --- a/net/minecraft/world/entity/Mob.java +++ b/net/minecraft/world/entity/Mob.java @@ -144,6 +144,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); @@ -225,12 +231,22 @@ 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 + // Leaf start - Async target finding + boolean running = this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1; + this.tickingTarget = false; if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !isThrottled) { // Pufferfish - pass activated priroity // Pufferfish - throttle inactive goal selector ticking this.goalSelector.tick(); } + this.tickingTarget = true; if (this.targetSelector.inactiveTick(this.activatedPriority, true)) { // Pufferfish - pass activated priority this.targetSelector.tick(); } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + if (!running && (this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1)) { + ((ServerLevel) this.level()).asyncGoalExecutor.submit(this.getId()); + } + } + // Leaf end - Async target finding } // Paper end @@ -914,17 +930,29 @@ 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 + boolean running = this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1; 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 (!running && (this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1)) { + ((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 4b24bb47a53a46586a642f3fb2656b1b8b670bf2..9abb8e7b0dea2cb63dad234812d773403d0716f6 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,36 @@ 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 = () -> serverLevel.getNearestEntity( + serverLevel.getEntitiesOfClass(avoidClass, bound, livingEntity -> true), + avoidEntityTargeting, + mob, + x, + y, + z + ); + } + // 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 28ef40e8a645989ea181297069cf2bbe571f3082..d011e4735cb8fd65a39a6b7a66386375b12aca78 100644 --- a/net/minecraft/world/entity/ai/goal/BegGoal.java +++ b/net/minecraft/world/entity/ai/goal/BegGoal.java @@ -27,8 +27,43 @@ 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 = () -> { + var player = serverLevel.getNearestPlayer(begTargeting, wolf); + if (player != null && playerHoldingInteresting(player)) { + return player; + } + return null; + }; + } + // 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 +94,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 1fe4d1877ed52017d4d22ddb9b77f78e2b93dff9..92b6f87817aee8b277c07cf9138bf52aa20a82d6 100644 --- a/net/minecraft/world/entity/ai/goal/FollowBoatGoal.java +++ b/net/minecraft/world/entity/ai/goal/FollowBoatGoal.java @@ -23,8 +23,47 @@ 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 = () -> { + 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)) { + return player; + + } + } + return null; + }; + } + // 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; @@ -37,7 +76,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..c2baf746a0697559dc391b6f8c9303917e194836 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,36 @@ 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 = () -> { + List entitiesOfClass = serverLevel.getEntitiesOfClass(Mob.class, bound, followPredicate); + if (!entitiesOfClass.isEmpty()) { + for (final Mob follow : entitiesOfClass) { + if (!follow.isInvisible()) { + return follow; + } + } + } + return null; + }; + } + // 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..0a41797fd7beddce0b93d42bac6e0270169330ef 100644 --- a/net/minecraft/world/entity/ai/goal/FollowParentGoal.java +++ b/net/minecraft/world/entity/ai/goal/FollowParentGoal.java @@ -19,11 +19,56 @@ 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 = () -> { + 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; + } + } + } + return target; + }; + } + // 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 +88,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..3c24382a3cced8dcea103ccc87cb506310de8461 100644 --- a/net/minecraft/world/entity/ai/goal/GoalSelector.java +++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java @@ -26,13 +26,23 @@ 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 + private boolean availableGoalsDirty = true; + private WrappedGoal @org.jetbrains.annotations.Nullable[] ctxGoals = null; + private int ctxIndex = 0; + public int ctxState = -1; + 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)); + availableGoalsDirty = true; // Leaf - Async target finding } @VisibleForTesting public void removeAllGoals(Predicate filter) { this.availableGoals.removeIf(wrappedGoal -> filter.test(wrappedGoal.getGoal())); + availableGoalsDirty = true; // Leaf - Async target finding } // Paper start - EAR 2 @@ -63,18 +73,19 @@ public class GoalSelector { } this.availableGoals.removeIf(wrappedGoal1 -> wrappedGoal1.getGoal() == goal); + availableGoalsDirty = true; // Leaf - Async target finding } // Paper start - Perf: optimize goal types private static boolean goalContainsAnyFlags(WrappedGoal goal, ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet flags) { - return goal.getFlags().hasCommonElements(flags); + return goal.getFlags().hasCommonElements(flags); // Leaf - Async target finding - inline diff } private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map flag) { long flagIterator = goal.getFlags().getBackingSet(); int wrappedGoalSize = goal.getFlags().size(); for (int i = 0; i < wrappedGoalSize; ++i) { - final Goal.Flag flag1 = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; + final Goal.Flag flag1 = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; // Leaf - Async target finding - inline diff flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator); // Paper end - Perf: optimize goal types if (!flag.getOrDefault(flag1, NO_GOAL).canBeReplacedBy(goal)) { @@ -85,7 +96,131 @@ 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]; + var flags = goal.getFlags(); + // entity and block + if (!goal.isRunning() && !flags.hasCommonElements(this.goalTypes)) { + // inline + boolean result = true; + long flagIterator1 = flags.getBackingSet(); + int wrappedGoalSize1 = flags.size(); + for (int i1 = 0; i1 < wrappedGoalSize1; ++i1) { + final Goal.Flag flag1 = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator1)]; + flagIterator1 ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator1); + if (!this.lockedFlags.getOrDefault(flag1, NO_GOAL).canBeReplacedBy(goal)) { + result = false; + break; + } + } + if (result) { + if (goal.canUse()) { + long flagIterator = flags.getBackingSet(); + int wrappedGoalSize = flags.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++; + } + + ctxState = -1; + ctxIndex = 0; + ctx.state = true; + } + if (ctxState == 3) { + while (ctxIndex < this.ctxGoals.length) { + WrappedGoal goal = this.ctxGoals[ctxIndex]; + if (goal.isRunning() && goal.requiresUpdateEveryTick()) { + goal.tick(); + } + ctxIndex++; + } + + ctxState = -1; + ctxIndex = 0; + ctx.state = true; + } + return false; + } + // Leaf end - Async target finding + public void tick() { + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + if (ctxState == -1) { + if (availableGoalsDirty || this.ctxGoals == null) { + this.ctxGoals = this.availableGoals.toArray(new WrappedGoal[0]); + availableGoalsDirty = false; + } + ctxState = 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(); @@ -116,6 +251,18 @@ public class GoalSelector { } public void tickRunningGoals(boolean tickAllRunning) { + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + if (ctxState == -1) { + if (availableGoalsDirty || this.ctxGoals == null) { + this.ctxGoals = this.availableGoals.toArray(new WrappedGoal[0]); + availableGoalsDirty = false; + } + ctxState = tickAllRunning ? 2 : 3; + } + return; + } + // Leaf end - Async target finding for (WrappedGoal wrappedGoal : this.availableGoals) { if (wrappedGoal.isRunning() && (tickAllRunning || wrappedGoal.requiresUpdateEveryTick())) { wrappedGoal.tick(); diff --git a/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java b/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java index be59d0c27a83b329ec3f97c029cfb9c114e22472..86f041ff21cf44a0cded5744055654a0bff40c42 100644 --- a/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java +++ b/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java @@ -20,20 +20,83 @@ 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 = () -> { + 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; + } + } + } + } + return target; + }; + } + // 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 +108,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 +117,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..98c2b4a298ada4b02afa55f991791d8696702181 100644 --- a/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java +++ b/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java @@ -48,32 +48,79 @@ 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()); + 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 = () -> { + if (lookAtType == Player.class) { + return serverLevel.getNearestPlayer(lookAtContext, mob, x, y, z); } 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 serverLevel.getNearestEntity( + serverLevel + .getEntitiesOfClass(lookAtType, bound, livingEntity -> true), + lookAtContext, + mob, + x, + y, + z ); } - - return this.lookAt != null; - } + }; } + // 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..9b8453ce2bc2cafca7c670d79b40434e7c93afca 100644 --- a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java +++ b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java @@ -41,8 +41,60 @@ 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 = () -> findNearestBlockAsync(ty, toRemove, mob, serverLevel, verticalSearchStart, searchRange, verticalSearchRange, blockPos, restrictRadius, restrictCenter); + } + + 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 +161,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 +191,105 @@ public abstract class MoveToBlockGoal extends Goal { return false; } + // Leaf start - Async search block + protected static @javax.annotation.Nullable BlockPos findNearestBlockAsync( + 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)) { + return mutableBlockPos.immutable(); + } + } + } + } + } + return null; + } + + 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 3c274d917bca9de87abfb842f5f332e112a7a2d7..2491b84641443ecfb8afc3b179e1cf80691ac1bc 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().isDay()) { 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,36 @@ 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 = () -> serverLevel.getNearestEntity( + serverLevel.getEntitiesOfClass(Villager.class, bound, livingEntity -> true), + OFFER_TARGER_CONTEXT, + golem, + x, + y, + z + ); + } + // 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 c67a88c9c77ece7c85ffb169ac96da4f28291228..14d9b492ba431d534e0c6a567d0b7700b4c8a02d 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..754c379b42cf65c1d2278b474cdfbe50e9e62b34 100644 --- a/net/minecraft/world/entity/ai/goal/TemptGoal.java +++ b/net/minecraft/world/entity/ai/goal/TemptGoal.java @@ -36,12 +36,51 @@ 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 = () -> serverLevel.getNearestPlayer(conditions, mob); + } + // 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 4644f3f7af89623ca6218c0dd24bb6cd67db553b..c9750ad322ddaa9c457f0e652d87c7abf8559358 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,49 @@ 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 = () -> { + 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) { + return player; + } + } + } + return null; + }; + } + // 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); @@ -43,7 +84,7 @@ public class DefendVillageTargetGoal extends TargetGoal { } return this.potentialTarget != null - && (!(this.potentialTarget instanceof Player) || !this.potentialTarget.isSpectator() && !((Player)this.potentialTarget).isCreative()); + && (!(this.potentialTarget instanceof Player) || !this.potentialTarget.isSpectator() && !((Player)this.potentialTarget).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 25fe78116ce01eeefe5c958423734195d27302eb..e306c1cfc44878ea130d8046b31cf617aa32c3cc 100644 --- a/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java @@ -73,6 +73,46 @@ 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 = () -> { + 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())) { + continue; + } + if (toIgnoreAlert == null) { + toAlert.add(mob); + continue; + } + boolean flag = false; + for (Class clazz : toIgnoreAlert) { + if (mob.getClass() == clazz) { + flag = true; + break; + } + } + if (!flag) { + toAlert.add(mob); + } + } + return toAlert; + }; + return; + } + // Leaf end - Async alert other + List entitiesOfClass = this.mob .level() .getEntitiesOfClass((Class)this.mob.getClass(), aabb, EntitySelector.NO_SPECTATORS); @@ -87,7 +127,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 +136,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 +153,36 @@ public class HurtByTargetGoal extends TargetGoal { } } + // Leaf start - Async alert other + public void poll() { + if (!(this.mob.getGoalCtx().result() instanceof List toAlert)) return; + LivingEntity lastHurtByMob = this.mob.getLastHurtByMob(); + if (lastHurtByMob.getType() == EntityType.PLAYER && getServerLevel(this.mob).getGameRules().getBoolean(GameRules.RULE_UNIVERSAL_ANGER)) { + return; + } + for (Class clazz : this.toIgnoreDamage) { + if (clazz.isAssignableFrom(lastHurtByMob.getClass())) { + return; + } + } + if (this.mob.getTarget() == null) { + return; + } + if (!canContinueToUse()) { + return; + } + if (!this.canAttack(lastHurtByMob, HURT_BY_TARGETING)) { + return; + } + for (var obj : toAlert) { + Mob mob = (Mob) obj; + if (mob.getTarget() == null) { + alertOther(mob, lastHurtByMob); + } + } + } + // 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, true); // 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 85eae0a14f7a417dfd8c911079d05354a98e5834..f59d5c9be0eb10f5b5192442e1850900d71a31e9 100644 --- a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java @@ -41,8 +41,43 @@ 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 = () -> { + if (targetType != Player.class && targetType != ServerPlayer.class) { + return serverLevel.getNearestEntity(mob.level().getEntitiesOfClass(targetType, targetSearch, entity -> entity != null && entity != mob && entity.isAlive()), targetConditions, mob, x, y, z); + } else { + return serverLevel.getNearestPlayer(targetConditions, mob, x, y, z); + } + }; + } + // 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 +92,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..270f0b8b33aed1c54edbdb8595ce7fcc7fca2dc4 100644 --- a/net/minecraft/world/entity/ai/goal/target/ResetUniversalAngerTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/ResetUniversalAngerTargetGoal.java @@ -37,6 +37,27 @@ 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 = () -> { + 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); + } + } + return toStop; + }; + return; + } + // Leaf end - Async alert other this.getNearbyMobsOfSameType() .stream() .filter(mob -> mob != this.mob) @@ -47,7 +68,19 @@ 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) { + if (EntitySelector.NO_SPECTATORS.test((net.minecraft.world.entity.Entity) neutralMob)) { + ((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 90452f0945e761077608692877677f522d38bccd..54044ff16e0c2963b2220e4fb4932fe0802aa178 100644 --- a/net/minecraft/world/entity/animal/Fox.java +++ b/net/minecraft/world/entity/animal/Fox.java @@ -849,13 +849,18 @@ public class Fox extends Animal implements VariantHolder { 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 (UUID uuid : Fox.this.getTrustedUUIDs()) { if (serverLevel.getEntity(uuid) instanceof LivingEntity livingEntity) { 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 } } @@ -1033,7 +1038,7 @@ public class Fox extends Animal implements VariantHolder { @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 @@ -1097,6 +1102,13 @@ public class Fox extends Animal implements VariantHolder { 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 0a6a3060f3690ab2d8439d66e6fd6f0c5dc20688..d99a1f6baebdc7c4a700e3fee30d1b52f3c1ac3d 100644 --- a/net/minecraft/world/entity/animal/Panda.java +++ b/net/minecraft/world/entity/animal/Panda.java @@ -991,9 +991,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 da5b32a17283e540615373097acc511d928aeff5..cdab995c86978f0a789c85d14151abf19d89b673 100644 --- a/net/minecraft/world/entity/animal/Rabbit.java +++ b/net/minecraft/world/entity/animal/Rabbit.java @@ -642,7 +642,13 @@ public class Rabbit extends Animal implements VariantHolder { 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 @@ -685,7 +691,7 @@ public class Rabbit extends Animal implements VariantHolder { @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; @@ -695,6 +701,13 @@ public class Rabbit extends Animal implements VariantHolder { 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 10477fea8fcd70bf0c1ba4b6e1113625be690e68..c5f626dcfe724f8ed19303d305f6eb5cfcc0f0af 100644 --- a/net/minecraft/world/entity/animal/Turtle.java +++ b/net/minecraft/world/entity/animal/Turtle.java @@ -527,8 +527,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 { @@ -584,8 +591,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/animal/Wolf.java b/net/minecraft/world/entity/animal/Wolf.java index 7cb292de6b27fa4ba3c5fce526a4e939c576789f..68852158e2fd3c4ce241d722ac414d73ea3b8836 100644 --- a/net/minecraft/world/entity/animal/Wolf.java +++ b/net/minecraft/world/entity/animal/Wolf.java @@ -672,7 +672,7 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder level.getNearestPlayer(cond, enderman); + return false; + } + // Leaf end - Async Target Finding this.pendingTarget = getServerLevel(this.enderman).getNearestPlayer(this.startAggroTargetConditions.range(this.getFollowDistance()), this.enderman); return this.pendingTarget != null; } + // Leaf start - Async Target Finding + protected boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof Player player)) return false; + var serverLevel = getServerLevel(this.enderman); + if (!this.startAggroTargetConditions.range(this.getFollowDistance()).test(serverLevel, enderman, player)) return false; + this.pendingTarget = player; + return true; + } + // Leaf end - Async Target Finding + @Override public void start() { this.aggroTime = this.adjustedTickDelay(5); diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java index ae4ee948971e931e4fdc4ec2187f5182195c626c..f4fa19c6352e44a624e81dc201b1d7d710c2d9d2 100644 --- a/net/minecraft/world/entity/monster/Strider.java +++ b/net/minecraft/world/entity/monster/Strider.java @@ -570,8 +570,15 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { @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 ac7729d1caa80155697bfe0e8646e4eda5d1780e..2518ea7a9d2122ff55ab6546d21d7ed6cc933090 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java @@ -1549,6 +1549,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl } // Leaf end - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) - do not bother with condition work / make configurable // Paper end - rewrite chunk system + ((ServerLevel) this).leafMidTickTasks(); // Leaf - Async target finding } } this.blockEntityTickers.removeMarkedEntries(); // SparklyPaper - optimize block entity removals @@ -1817,9 +1818,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl @Override public List getEntities(@Nullable Entity entity, AABB boundingBox, Predicate predicate) { - 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 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<>();