From 63edc90be8be406876936e9cc5334474cb88e68d Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Wed, 14 May 2025 00:41:32 +0900 Subject: [PATCH] fix async-target-finding --- .../features/0154-Async-target-finding.patch | 679 ++++++++++-------- ...tyList-implementation-to-BasicEntity.patch | 10 +- .../leaf/async/ai/AsyncGoalExecutor.java | 84 ++- .../java/org/dreeam/leaf/async/ai/VWaker.java | 8 + .../java/org/dreeam/leaf/async/ai/Waker.java | 16 +- .../modules/async/AsyncTargetFinding.java | 4 +- 6 files changed, 448 insertions(+), 353 deletions(-) create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/async/ai/VWaker.java diff --git a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch index f8dacc8d..5287746c 100644 --- a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch +++ b/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch @@ -4,6 +4,105 @@ 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..635d6aabbbe9be5c81a71d5e5bf758211deec0cf 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,8 @@ public final class ChunkEntitySlices { + + private final EntityCollectionBySection allEntities; + private final EntityCollectionBySection hardCollidingEntities; +- private final Reference2ObjectOpenHashMap, EntityCollectionBySection> entitiesByClass; +- private final Reference2ObjectOpenHashMap, EntityCollectionBySection> entitiesByType; ++ private final Reference2ObjectMap, EntityCollectionBySection> entitiesByClass; // Leaf ++ private final Reference2ObjectMap, EntityCollectionBySection> entitiesByType; // Leaf + private final EntityList entities = new EntityList(); + + public FullChunkStatus status; +@@ -67,9 +67,16 @@ public final class ChunkEntitySlices { + + this.allEntities = new EntityCollectionBySection(this); + this.hardCollidingEntities = new EntityCollectionBySection(this); +- this.entitiesByClass = new Reference2ObjectOpenHashMap<>(); +- this.entitiesByType = new Reference2ObjectOpenHashMap<>(); + ++ // Leaf start ++ 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 + this.status = status; + this.chunkData = chunkData; + } +@@ -248,14 +255,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 ++ 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 + + EntityCollectionBySection byType = this.entitiesByType.get(entity.getType()); + if (byType != null) { +@@ -282,14 +301,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 ++ 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 ++ + + 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 @@ -48,7 +147,7 @@ index 33dd16a26edd2974f04d9a868d3e58e8e3060032..eb0589b203bcf72cd24bb37f2c448c23 // Gale start - Pufferfish - SIMD support diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 9af7dafe03812d96aa477584d4147a68c240ab21..539206d55e87ec9968664305caaf475c991fe4d5 100644 +index 9af7dafe03812d96aa477584d4147a68c240ab21..e6fd46b8148e050c4807abf6c8a03e4747bc0da2 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -177,7 +177,16 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -115,7 +214,7 @@ index 9af7dafe03812d96aa477584d4147a68c240ab21..539206d55e87ec9968664305caaf475c } ); this.tickBlockEntities(); -+ if (this.asyncGoalExecutor != null) this.asyncGoalExecutor.unpark(); // Leaf - Async target finding ++ if (this.asyncGoalExecutor != null) this.asyncGoalExecutor.tick(); // Leaf - Async target finding } // Paper - rewrite chunk system @@ -206,7 +305,7 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..99362471bee4f8404f7cecd860ff3392 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..776f18287b6b431b6b33d370b124825798cf103a 100644 +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 { @@ -235,7 +334,7 @@ index 4b24bb47a53a46586a642f3fb2656b1b8b670bf2..776f18287b6b431b6b33d370b1248257 if (this.toAvoid == null) { return false; } else { -@@ -91,6 +100,45 @@ public class AvoidEntityGoal extends Goal { +@@ -91,6 +100,36 @@ public class AvoidEntityGoal extends Goal { } } @@ -258,23 +357,14 @@ index 4b24bb47a53a46586a642f3fb2656b1b8b670bf2..776f18287b6b431b6b33d370b1248257 + 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); -+ } -+ }; ++ ctx.wake = () -> serverLevel.getNearestEntity( ++ serverLevel.getEntitiesOfClass(avoidClass, bound, livingEntity -> true), ++ avoidEntityTargeting, ++ mob, ++ x, ++ y, ++ z ++ ); + } + // Leaf end - Async Avoid Entity Finding + @@ -282,10 +372,10 @@ index 4b24bb47a53a46586a642f3fb2656b1b8b670bf2..776f18287b6b431b6b33d370b1248257 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..4db188a08c1d13f77fb268ba03ac5d7fb9b71ad0 100644 +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,50 @@ public class BegGoal extends Goal { +@@ -27,8 +27,43 @@ public class BegGoal extends Goal { this.setFlags(EnumSet.of(Goal.Flag.LOOK)); } @@ -306,18 +396,11 @@ index 28ef40e8a645989ea181297069cf2bbe571f3082..4db188a08c1d13f77fb268ba03ac5d7f + 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); ++ var player = serverLevel.getNearestPlayer(begTargeting, wolf); ++ if (player != null && playerHoldingInteresting(player)) { ++ return player; + } ++ return null; + }; + } + // Leaf end - Async Target Finding @@ -336,7 +419,7 @@ index 28ef40e8a645989ea181297069cf2bbe571f3082..4db188a08c1d13f77fb268ba03ac5d7f 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 { +@@ -59,10 +94,10 @@ public class BegGoal extends Goal { this.lookTime--; } @@ -397,10 +480,10 @@ index 9954f49bc364969c7ccb37f4186fa2ab8710f6ae..057090e3134048e75dbaefb703e8f2d3 + // 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..2d2ffa9e89f4954e096c48cba96db995a92433e5 100644 +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,54 @@ public class FollowBoatGoal extends Goal { +@@ -23,8 +23,47 @@ public class FollowBoatGoal extends Goal { this.mob = mob; } @@ -418,23 +501,16 @@ index 1fe4d1877ed52017d4d22ddb9b77f78e2b93dff9..2d2ffa9e89f4954e096c48cba96db995 + 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)) { ++ return player; + -+ 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); + } ++ return null; + }; + } + // Leaf end - Async Target Finding @@ -455,7 +531,7 @@ index 1fe4d1877ed52017d4d22ddb9b77f78e2b93dff9..2d2ffa9e89f4954e096c48cba96db995 List entitiesOfClass = this.mob.level().getEntitiesOfClass(AbstractBoat.class, this.mob.getBoundingBox().inflate(5.0)); boolean flag = false; -@@ -37,7 +83,7 @@ public class FollowBoatGoal extends Goal { +@@ -37,7 +76,7 @@ public class FollowBoatGoal extends Goal { } } @@ -465,7 +541,7 @@ index 1fe4d1877ed52017d4d22ddb9b77f78e2b93dff9..2d2ffa9e89f4954e096c48cba96db995 @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 +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 { @@ -484,7 +560,7 @@ index c9cf985173d3da9e84ce178f161e2fd5fb8ce472..911ceddb4ded974a02355c76e5d41336 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 { +@@ -51,6 +60,36 @@ public class FollowMobGoal extends Goal { return false; } @@ -505,22 +581,15 @@ index c9cf985173d3da9e84ce178f161e2fd5fb8ce472..911ceddb4ded974a02355c76e5d41336 + 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; -+ } ++ List entitiesOfClass = serverLevel.getEntitiesOfClass(Mob.class, bound, followPredicate); ++ if (!entitiesOfClass.isEmpty()) { ++ for (final Mob follow : entitiesOfClass) { ++ if (!follow.isInvisible()) { ++ return follow; + } + } -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } ++ return null; + }; + } + // Leaf end - Async Follow Mob Finding @@ -529,10 +598,10 @@ index c9cf985173d3da9e84ce178f161e2fd5fb8ce472..911ceddb4ded974a02355c76e5d41336 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 +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,66 @@ public class FollowParentGoal extends Goal { +@@ -19,11 +19,56 @@ public class FollowParentGoal extends Goal { this.speedModifier = speedModifier; } @@ -554,30 +623,20 @@ index 3093f03d4f298bf39fec8bad2b6c22518774aea8..80f9de7b2c03c1477dae0f42b328e010 + 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; + -+ 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; -+ } ++ 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); + } ++ return target; + }; + } + // Leaf end - Async Target Finding @@ -599,7 +658,7 @@ index 3093f03d4f298bf39fec8bad2b6c22518774aea8..80f9de7b2c03c1477dae0f42b328e010 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 { +@@ -43,6 +88,7 @@ public class FollowParentGoal extends Goal { if (animal == null) { return false; } else if (d < 9.0) { @@ -608,7 +667,7 @@ index 3093f03d4f298bf39fec8bad2b6c22518774aea8..80f9de7b2c03c1477dae0f42b328e010 } 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 +index e82e32407cec6109b9c3b0106295217f4a3f4aa2..287b531610327c61fdc505df4ea8e538d289b7b2 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 { @@ -625,7 +684,7 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..ec56b4e8acd2dad23a94b1fe9145e625 public void addGoal(int priority, Goal goal) { this.availableGoals.add(new WrappedGoal(priority, goal)); } -@@ -85,7 +92,111 @@ public class GoalSelector { +@@ -85,7 +92,103 @@ public class GoalSelector { return true; } @@ -714,14 +773,6 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..ec56b4e8acd2dad23a94b1fe9145e625 + } + 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() { @@ -737,11 +788,27 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..ec56b4e8acd2dad23a94b1fe9145e625 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 +219,15 @@ public class GoalSelector { + } + + public void tickRunningGoals(boolean tickAllRunning) { ++ // Leaf start - Async target finding ++ if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { ++ if (this.ctxGoals == null) { ++ this.ctxState = 2; ++ this.ctxGoals = this.availableGoals.toArray(new WrappedGoal[0]); ++ } ++ 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..7c54964a5329e12a9a4a1fdb2417ccbd3f0bfae2 100644 +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,93 @@ public class LlamaFollowCaravanGoal extends Goal { +@@ -20,20 +20,83 @@ public class LlamaFollowCaravanGoal extends Goal { this.setFlags(EnumSet.of(Goal.Flag.MOVE)); } @@ -761,21 +828,28 @@ index be59d0c27a83b329ec3f97c029cfb9c114e22472..7c54964a5329e12a9a4a1fdb2417ccbd + 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; ++ } + } ++ } + -+ 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()) { ++ 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; @@ -783,25 +857,8 @@ index be59d0c27a83b329ec3f97c029cfb9c114e22472..7c54964a5329e12a9a4a1fdb2417ccbd + } + } + } -+ -+ 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); + } ++ return target; + }; + } + // Leaf end - Async Target Finding @@ -838,7 +895,7 @@ index be59d0c27a83b329ec3f97c029cfb9c114e22472..7c54964a5329e12a9a4a1fdb2417ccbd double d1 = this.llama.distanceToSqr(llama1); if (!(d1 > d)) { d = d1; -@@ -45,7 +118,7 @@ public class LlamaFollowCaravanGoal extends Goal { +@@ -45,7 +108,7 @@ public class LlamaFollowCaravanGoal extends Goal { if (llama == null) { for (Entity entityx : entities) { Llama llama1 = (Llama)entityx; @@ -847,7 +904,7 @@ index be59d0c27a83b329ec3f97c029cfb9c114e22472..7c54964a5329e12a9a4a1fdb2417ccbd double d1 = this.llama.distanceToSqr(llama1); if (!(d1 > d)) { d = d1; -@@ -54,6 +127,7 @@ public class LlamaFollowCaravanGoal extends Goal { +@@ -54,6 +117,7 @@ public class LlamaFollowCaravanGoal extends Goal { } } } @@ -856,10 +913,10 @@ index be59d0c27a83b329ec3f97c029cfb9c114e22472..7c54964a5329e12a9a4a1fdb2417ccbd 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 +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,87 @@ public class LookAtPlayerGoal extends Goal { +@@ -48,32 +48,79 @@ public class LookAtPlayerGoal extends Goal { @Override public boolean canUse() { @@ -899,28 +956,15 @@ index 6463c3c9b08d6058f2843c225b08a40fc30a960b..0afad8ddb0e9d083d47b96627d999f0e - 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; @@ -939,27 +983,31 @@ index 6463c3c9b08d6058f2843c225b08a40fc30a960b..0afad8ddb0e9d083d47b96627d999f0e + 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); -+ } ++ 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 @@ -967,10 +1015,10 @@ index 6463c3c9b08d6058f2843c225b08a40fc30a960b..0afad8ddb0e9d083d47b96627d999f0e @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 +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,69 @@ public abstract class MoveToBlockGoal extends Goal { +@@ -41,8 +41,60 @@ public abstract class MoveToBlockGoal extends Goal { this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.JUMP)); } @@ -1005,16 +1053,7 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..292749c786b85f86fa5f65c49fa83539 + 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); -+ } -+ }; ++ ctx.wake = () -> findNearestBlockAsync(ty, toRemove, mob, serverLevel, verticalSearchStart, searchRange, verticalSearchRange, blockPos, restrictRadius, restrictCenter); + } + + protected enum TypeToCheck { @@ -1040,7 +1079,7 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..292749c786b85f86fa5f65c49fa83539 if (this.nextStartTick > 0) { this.nextStartTick--; return false; -@@ -109,6 +170,12 @@ public abstract class MoveToBlockGoal extends Goal { +@@ -109,6 +161,12 @@ public abstract class MoveToBlockGoal extends Goal { } protected boolean findNearestBlock() { @@ -1053,13 +1092,12 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..292749c786b85f86fa5f65c49fa83539 int i = this.searchRange; int i1 = this.verticalSearchRange; BlockPos blockPos = this.mob.blockPosition(); -@@ -133,5 +200,108 @@ public abstract class MoveToBlockGoal extends Goal { +@@ -133,5 +191,105 @@ 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, ++ 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, @@ -1080,15 +1118,13 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..292749c786b85f86fa5f65c49fa83539 + if (!serverLevel.hasChunkAt(mutableBlockPos)) continue; + if (isWithinRestriction(restrictRadius, restrictCenter, mutableBlockPos) + && isValidTargetAsync(ty, toRemove, mob, serverLevel, mutableBlockPos)) { -+ ctx.result = mutableBlockPos.immutable(); -+ return true; ++ return mutableBlockPos.immutable(); + } + } + } + } + } -+ -+ return false; ++ return null; + } + + private static boolean isWithinRestriction(float restrictRadius, BlockPos restrictCenter, BlockPos pos) { @@ -1163,7 +1199,7 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..292749c786b85f86fa5f65c49fa83539 + } } diff --git a/net/minecraft/world/entity/ai/goal/OfferFlowerGoal.java b/net/minecraft/world/entity/ai/goal/OfferFlowerGoal.java -index 3c274d917bca9de87abfb842f5f332e112a7a2d7..e1d185b2f5e7eb7a00501ff85b4e16d3540066f4 100644 +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 { @@ -1179,16 +1215,16 @@ index 3c274d917bca9de87abfb842f5f332e112a7a2d7..e1d185b2f5e7eb7a00501ff85b4e16d3 + return true; + } + if (this.golem.getRandom().nextInt(8000) != 0) { -+ return false; + return false; + } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + getVillagerAsync(); - return false; ++ return false; + // Leaf end - Async offer flower finding } else { this.villager = getServerLevel(this.golem) .getNearestEntity( -@@ -38,6 +48,45 @@ public class OfferFlowerGoal extends Goal { +@@ -38,6 +48,36 @@ public class OfferFlowerGoal extends Goal { } } @@ -1211,23 +1247,14 @@ index 3c274d917bca9de87abfb842f5f332e112a7a2d7..e1d185b2f5e7eb7a00501ff85b4e16d3 + 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); -+ } -+ }; ++ ctx.wake = () -> serverLevel.getNearestEntity( ++ serverLevel.getEntitiesOfClass(Villager.class, bound, livingEntity -> true), ++ OFFER_TARGER_CONTEXT, ++ golem, ++ x, ++ y, ++ z ++ ); + } + // Leaf end - Async look finding + @@ -1272,10 +1299,10 @@ index c67a88c9c77ece7c85ffb169ac96da4f28291228..14d9b492ba431d534e0c6a567d0b7700 + // 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 +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,60 @@ public class TemptGoal extends Goal { +@@ -36,12 +36,51 @@ public class TemptGoal extends Goal { this.targetingConditions = TEMPT_TARGETING.copy().selector((entity, level) -> this.shouldFollow(entity)); } @@ -1296,16 +1323,7 @@ index f88f618d34fb343b31de3af1a875d6633703df71..e42f77fc6d04a900fd8adb4682678f92 + 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); -+ } -+ }; ++ ctx.wake = () -> serverLevel.getNearestPlayer(conditions, mob); + } + // Leaf end - Async Tempt Finding @Override @@ -1337,7 +1355,7 @@ index f88f618d34fb343b31de3af1a875d6633703df71..e42f77fc6d04a900fd8adb4682678f92 .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..941131fd448216afe2bf46dc6aab1d4b95c586fc 100644 +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 { @@ -1349,7 +1367,7 @@ index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..941131fd448216afe2bf46dc6aab1d4b public DefendVillageTargetGoal(IronGolem golem) { super(golem, false, true); -@@ -24,8 +24,58 @@ public class DefendVillageTargetGoal extends TargetGoal { +@@ -24,8 +24,49 @@ public class DefendVillageTargetGoal extends TargetGoal { this.setFlags(EnumSet.of(Goal.Flag.TARGET)); } @@ -1370,26 +1388,17 @@ index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..941131fd448216afe2bf46dc6aab1d4b + 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; -+ } ++ 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; + } + } -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } ++ return null; + }; + } + // Leaf end - Async Target Finding @@ -1408,7 +1417,7 @@ index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..941131fd448216afe2bf46dc6aab1d4b 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 +93,7 @@ public class DefendVillageTargetGoal extends TargetGoal { +@@ -43,7 +84,7 @@ public class DefendVillageTargetGoal extends TargetGoal { } return this.potentialTarget != null @@ -1418,10 +1427,10 @@ index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..941131fd448216afe2bf46dc6aab1d4b @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..6998e7a102b711152d78f4dc590812168dd24cd6 100644 +index 25fe78116ce01eeefe5c958423734195d27302eb..c11cc768f14a9bf29f40b86c05a37d7711702e97 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 { +@@ -73,6 +73,49 @@ public class HurtByTargetGoal extends TargetGoal { protected void alertOthers() { double followDistance = this.getFollowDistance(); AABB aabb = AABB.unitCubeFromLowerCorner(this.mob.position()).inflate(followDistance, 10.0, followDistance); @@ -1434,42 +1443,35 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..6998e7a102b711152d78f4dc59081216 + 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); ++ 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 toAlert; + }; + return; + } @@ -1478,7 +1480,7 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..6998e7a102b711152d78f4dc59081216 List entitiesOfClass = this.mob .level() .getEntitiesOfClass((Class)this.mob.getClass(), aabb, EntitySelector.NO_SPECTATORS); -@@ -87,7 +137,7 @@ public class HurtByTargetGoal extends TargetGoal { +@@ -87,7 +130,7 @@ public class HurtByTargetGoal extends TargetGoal { mob = (Mob)var5.next(); if (this.mob != mob @@ -1487,7 +1489,7 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..6998e7a102b711152d78f4dc59081216 && (!(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 { +@@ -96,7 +139,7 @@ public class HurtByTargetGoal extends TargetGoal { boolean flag = false; @@ -1496,15 +1498,30 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..6998e7a102b711152d78f4dc59081216 if (mob.getClass() == clazz) { flag = true; break; -@@ -113,6 +163,15 @@ public class HurtByTargetGoal extends TargetGoal { +@@ -113,6 +156,30 @@ 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()); ++ 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.canAttack(lastHurtByMob, HURT_BY_TARGETING)) { ++ return; ++ } ++ for (var obj : toAlert) { ++ Mob mob = (Mob) obj; ++ if (EntitySelector.NO_SPECTATORS.test(mob) && mob.getTarget() == null && mob.isAlliedTo(this.mob.getLastHurtByMob())) { ++ alertOther(mob, this.mob.getLastHurtByMob()); ++ } + } + } + // Leaf end - Async alert other @@ -1513,10 +1530,10 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..6998e7a102b711152d78f4dc59081216 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..3d0c28caa646286be79cefee4710b15f9aad928c 100644 +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,51 @@ public class NearestAttackableTargetGoal extends TargetG +@@ -41,8 +41,43 @@ public class NearestAttackableTargetGoal extends TargetG this.targetConditions = TargetingConditions.forCombat().range(this.getFollowDistance()).selector(selector); } @@ -1541,18 +1558,10 @@ index 85eae0a14f7a417dfd8c911079d05354a98e5834..3d0c28caa646286be79cefee4710b15f + 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); ++ 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); + } + }; + } @@ -1568,7 +1577,7 @@ index 85eae0a14f7a417dfd8c911079d05354a98e5834..3d0c28caa646286be79cefee4710b15f if (this.randomInterval > 0 && this.mob.getRandom().nextInt(this.randomInterval) != 0) { return false; } else { -@@ -57,6 +100,15 @@ public class NearestAttackableTargetGoal extends TargetG +@@ -57,6 +92,15 @@ public class NearestAttackableTargetGoal extends TargetG protected void findTarget() { ServerLevel serverLevel = getServerLevel(this.mob); @@ -1627,10 +1636,10 @@ index abf57494950f55bbd75f335f26736cb9e703c197..efd2418f56c36e7850edde483a2a4906 } } 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 +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,34 @@ public class ResetUniversalAngerTargetGoal extends G +@@ -37,6 +37,27 @@ public class ResetUniversalAngerTargetGoal extends G this.lastHurtByPlayerTimestamp = this.mob.getLastHurtByMobTimestamp(); this.mob.forgetCurrentTargetAndRefreshUniversalAnger(); if (this.alertOthersOfSameType) { @@ -1643,21 +1652,14 @@ index 61a7bb5f1760d47a13b6aafc123bcf3dff420860..15da909b4db3d7a7532aed550208ecbb + 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); + } -+ 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 toStop; + }; + return; + } @@ -1665,7 +1667,7 @@ index 61a7bb5f1760d47a13b6aafc123bcf3dff420860..15da909b4db3d7a7532aed550208ecbb this.getNearbyMobsOfSameType() .stream() .filter(mob -> mob != this.mob) -@@ -47,7 +75,17 @@ public class ResetUniversalAngerTargetGoal extends G +@@ -47,7 +68,19 @@ public class ResetUniversalAngerTargetGoal extends G super.start(); } @@ -1673,7 +1675,9 @@ index 61a7bb5f1760d47a13b6aafc123bcf3dff420860..15da909b4db3d7a7532aed550208ecbb + public void poll() { + if (!(this.mob.getGoalCtx().result() instanceof List toStop)) return; + for (var neutralMob : toStop) { -+ ((NeutralMob)neutralMob).forgetCurrentTargetAndRefreshUniversalAnger(); ++ if (EntitySelector.NO_SPECTATORS.test((net.minecraft.world.entity.Entity) neutralMob)) { ++ ((NeutralMob) neutralMob).forgetCurrentTargetAndRefreshUniversalAnger(); ++ } + } + } + // Leaf end - Async alert other @@ -1913,6 +1917,45 @@ index d44ed0d6a672a0b1eb0a8781e3e094096a2b753d..490c196419c94b75a540e64d513163df } static class DrownedGoToWaterGoal extends Goal { +diff --git a/net/minecraft/world/entity/monster/EnderMan.java b/net/minecraft/world/entity/monster/EnderMan.java +index c7897532163d4fdf5a82982f7d24a47dd61e3dfa..2986384389c1acd311f4367d622550830ce009c1 100644 +--- a/net/minecraft/world/entity/monster/EnderMan.java ++++ b/net/minecraft/world/entity/monster/EnderMan.java +@@ -597,10 +597,34 @@ public class EnderMan extends Monster implements NeutralMob { + + @Override + public boolean canUse() { ++ // Leaf start - Async Target Finding ++ if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { ++ if (poll()) { ++ return true; ++ } ++ final EnderMan enderman = this.enderman; ++ final var ctx = enderman.getGoalCtx(); ++ if (!ctx.state) return false; ++ final var level = getServerLevel(this.enderman); ++ final var cond = this.startAggroTargetConditions.range(this.getFollowDistance()).copy(); ++ ctx.wake = () -> 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 diff --git a/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch b/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch index 16a14fa4..3f1d986d 100644 --- a/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch +++ b/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Sakura: copy-EntityList-implementation-to-BasicEntityList 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..4311309f14c069f929ffe86bf0a91d7df3222828 100644 +index 635d6aabbbe9be5c81a71d5e5bf758211deec0cf..a45f4eb235a7823fce84948ad556c36d9fc6fdd8 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java -@@ -382,6 +382,13 @@ public final class ChunkEntitySlices { +@@ -414,6 +414,13 @@ public final class ChunkEntitySlices { private E[] storage; private int size; @@ -22,7 +22,7 @@ index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..4311309f14c069f929ffe86bf0a91d7d public BasicEntityList() { this(0); -@@ -402,6 +409,7 @@ public final class ChunkEntitySlices { +@@ -434,6 +441,7 @@ public final class ChunkEntitySlices { private void resize() { if (this.storage == me.titaniumtown.ArrayConstants.emptyEntityArray) { // Gale - JettPack - reduce array allocations this.storage = (E[])new Entity[DEFAULT_CAPACITY]; @@ -30,7 +30,7 @@ index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..4311309f14c069f929ffe86bf0a91d7d } else { this.storage = Arrays.copyOf(this.storage, this.storage.length * 2); } -@@ -415,6 +423,7 @@ public final class ChunkEntitySlices { +@@ -447,6 +455,7 @@ public final class ChunkEntitySlices { } else { this.storage[idx] = entity; } @@ -38,7 +38,7 @@ index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..4311309f14c069f929ffe86bf0a91d7d } public int indexOf(final E entity) { -@@ -430,24 +439,32 @@ public final class ChunkEntitySlices { +@@ -462,24 +471,32 @@ public final class ChunkEntitySlices { } public boolean remove(final E entity) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java index b02ad4ab..0f154e37 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java @@ -13,19 +13,19 @@ import java.util.concurrent.locks.LockSupport; public class AsyncGoalExecutor { - public static final Logger LOGGER = LogManager.getLogger("Leaf Async Goal"); - + protected static final Logger LOGGER = LogManager.getLogger("Leaf Async Goal"); protected final SpscIntQueue queue; protected final SpscIntQueue wake; + protected final SpscIntQueue submit; private final AsyncGoalThread thread; private final ServerLevel world; - private boolean dirty = false; - private long tickCount = 0L; + private long midTickCount = 0L; public AsyncGoalExecutor(AsyncGoalThread thread, ServerLevel world) { this.world = world; this.queue = new SpscIntQueue(AsyncTargetFinding.queueSize); this.wake = new SpscIntQueue(AsyncTargetFinding.queueSize); + this.submit = new SpscIntQueue(AsyncTargetFinding.queueSize); this.thread = thread; } @@ -34,24 +34,33 @@ public class AsyncGoalExecutor { if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) { return false; } - mob.goalSelector.wake(); - mob.targetSelector.wake(); + mob.goalSelector.ctx.wake(); + mob.targetSelector.ctx.wake(); return true; } public final void submit(int entityId) { - dirty = true; - if (!this.queue.send(entityId)) { - unpark(); - do { + if (!this.submit.send(entityId)) { + while (poll(entityId)) { wake(entityId); - } while (poll(entityId)); + } } } - public final void unpark() { - if (dirty) LockSupport.unpark(thread); - dirty = false; + public final void tick() { + while (true) { + OptionalInt result = this.submit.recv(); + if (result.isEmpty()) { + break; + } + int id = result.getAsInt(); + if (poll(id) && !this.queue.send(id)) { + do { + wake(id); + } while (poll(id)); + } + } + LockSupport.unpark(thread); } public final void midTick() { @@ -61,26 +70,51 @@ public class AsyncGoalExecutor { break; } int id = result.getAsInt(); - if (poll(id)) { - submit(id); + if (poll(id) && !this.queue.send(id)) { + do { + wake(id); + } while (poll(id)); } } - if ((tickCount % AsyncTargetFinding.threshold) == 0L) { - unpark(); + if (AsyncTargetFinding.threshold <= 0L || (midTickCount % AsyncTargetFinding.threshold) == 0L) { + boolean submitted = false; + while (true) { + OptionalInt result = this.submit.recv(); + if (result.isEmpty()) { + break; + } + submitted = true; + int id = result.getAsInt(); + if (poll(id) && !this.queue.send(id)) { + do { + wake(id); + } while (poll(id)); + } + } + if (submitted) { + LockSupport.unpark(thread); + } } - tickCount += 1; + + midTickCount += 1; } private boolean poll(int id) { Entity entity = this.world.getEntities().get(id); - if (entity == null || !entity.isAlive() || !(entity instanceof Mob mob)) { + if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) { return false; } - mob.tickingTarget = true; - boolean a = mob.targetSelector.poll(); - mob.tickingTarget = false; - boolean b = mob.goalSelector.poll(); - return a || b; + try { + mob.tickingTarget = true; + boolean a = mob.targetSelector.poll(); + mob.tickingTarget = false; + boolean b = mob.goalSelector.poll(); + return a || b; + } catch (Exception e) { + LOGGER.error("Exception while polling", e); + // retry + return true; + } } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/VWaker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/VWaker.java new file mode 100644 index 00000000..f664cfbf --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/VWaker.java @@ -0,0 +1,8 @@ +package org.dreeam.leaf.async.ai; + +import org.jetbrains.annotations.Nullable; + +@FunctionalInterface +public interface VWaker { + @Nullable Object wake(); +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java index 0f064e5e..37e557ef 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java @@ -5,14 +5,26 @@ import org.jetbrains.annotations.Nullable; public class Waker { @Nullable - public volatile Runnable wake = null; + public volatile VWaker wake = null; @Nullable public volatile Object result = null; - public volatile boolean state = true; + public boolean state = true; public final @Nullable Object result() { Object result = this.result; this.result = null; return result; } + + final void wake() { + final var wake = this.wake; + if (wake != null) { + try { + this.result = wake.wake(); + } catch (Exception e) { + AsyncGoalExecutor.LOGGER.error("Exception while wake", e); + } + this.wake = null; + } + } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java index 479eb133..a444f218 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java @@ -34,8 +34,6 @@ public class AsyncTargetFinding extends ConfigModules { asyncTargetFindingInitialized = true; enabled = config.getBoolean(getBasePath() + ".enabled", enabled); - // TODO - enabled = false; alertOther = config.getBoolean(getBasePath() + ".async-alert-other", true); searchBlock = config.getBoolean(getBasePath() + ".async-search-block", true); searchEntity = config.getBoolean(getBasePath() + ".async-search-entity", true); @@ -45,7 +43,7 @@ public class AsyncTargetFinding extends ConfigModules { if (queueSize <= 0) { queueSize = 4096; } - if (threshold <= 0L) { + if (threshold == 0L) { threshold = 10L; } if (!enabled) {