mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-19 15:09:25 +00:00
320 lines
14 KiB
Diff
320 lines
14 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Taiyou06 <kaandindar21@gmail.com>
|
|
Date: Sat, 29 Mar 2025 13:40:46 +0100
|
|
Subject: [PATCH] Async Target Finding
|
|
|
|
|
|
diff --git a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
|
|
index 024792900c8ab716e91ef512d2da22548075044d..7f256793232cfa9666728223cb9964e49ff8b6ba 100644
|
|
--- a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
|
|
+++ b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
|
|
@@ -16,9 +16,37 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> extends TargetG
|
|
protected final Class<T> targetType;
|
|
protected final int randomInterval;
|
|
@Nullable
|
|
- protected LivingEntity target;
|
|
+ protected volatile LivingEntity target; // Leaf - Async Target Finding
|
|
protected TargetingConditions targetConditions;
|
|
|
|
+ // Leaf start - Async Target Finding
|
|
+ // Single thread executor to prevent overwhelming the server
|
|
+ private static final java.util.concurrent.ExecutorService TARGET_FINDER_EXECUTOR = java.util.concurrent.Executors.newSingleThreadExecutor(r -> {
|
|
+ Thread thread = new Thread(r, "Leaf - Target-Finder-Thread");
|
|
+ thread.setDaemon(true);
|
|
+ thread.setPriority(Thread.MIN_PRIORITY); // Lower priority to avoid competing with main thread
|
|
+ return thread;
|
|
+ });
|
|
+
|
|
+ // Flag to track if a search is in progress
|
|
+ private final java.util.concurrent.atomic.AtomicBoolean isSearching = new java.util.concurrent.atomic.AtomicBoolean(false);
|
|
+ private final java.util.concurrent.atomic.AtomicReference<LivingEntity> pendingTarget = new java.util.concurrent.atomic.AtomicReference<>(null);
|
|
+ static {
|
|
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
|
+ try {
|
|
+ TARGET_FINDER_EXECUTOR.shutdown();
|
|
+ TARGET_FINDER_EXECUTOR.awaitTermination(2, java.util.concurrent.TimeUnit.SECONDS);
|
|
+ } catch (InterruptedException e) {
|
|
+ Thread.currentThread().interrupt();
|
|
+ } finally {
|
|
+ if (!TARGET_FINDER_EXECUTOR.isTerminated()) {
|
|
+ TARGET_FINDER_EXECUTOR.shutdownNow();
|
|
+ }
|
|
+ }
|
|
+ }));
|
|
+ }
|
|
+ // Leaf end - Async Target Finding
|
|
+
|
|
public NearestAttackableTargetGoal(Mob mob, Class<T> targetType, boolean mustSee) {
|
|
this(mob, targetType, 10, mustSee, false, null);
|
|
}
|
|
@@ -46,8 +74,14 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> extends TargetG
|
|
if (this.randomInterval > 0 && this.mob.getRandom().nextInt(this.randomInterval) != 0) {
|
|
return false;
|
|
} else {
|
|
- this.findTarget();
|
|
- return this.target != null;
|
|
+ // Leaf start - Async Target Finding
|
|
+ findTarget();
|
|
+ LivingEntity pending = pendingTarget.getAndSet(null);
|
|
+ if (pending != null && !pending.isRemoved() && pending.isAlive()) {
|
|
+ this.target = pending;
|
|
+ }
|
|
+ return this.target != null && this.target.isAlive() && !this.target.isRemoved();
|
|
+ // Leaf end - Async Target Finding
|
|
}
|
|
}
|
|
|
|
@@ -55,25 +89,239 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> extends TargetG
|
|
return this.mob.getBoundingBox().inflate(targetDistance, targetDistance, targetDistance);
|
|
}
|
|
|
|
+ // Leaf start - Async Target Finding
|
|
+ // Async find target implementation with safer entity handling
|
|
protected void findTarget() {
|
|
- ServerLevel serverLevel = getServerLevel(this.mob);
|
|
- 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),
|
|
- this.getTargetConditions(),
|
|
- this.mob,
|
|
- this.mob.getX(),
|
|
- this.mob.getEyeY(),
|
|
- this.mob.getZ()
|
|
- );
|
|
- } else {
|
|
- this.target = serverLevel.getNearestPlayer(this.getTargetConditions(), this.mob, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ());
|
|
+ // If async is disabled or we're already searching, use sync method
|
|
+ if (!org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled || !isSearching.compareAndSet(false, true)) {
|
|
+ findTargetSync();
|
|
+ return;
|
|
}
|
|
+
|
|
+ // Capture mutable state to avoid race conditions
|
|
+ final Mob mob = this.mob;
|
|
+
|
|
+ // Safety check
|
|
+ if (mob == null || mob.isRemoved() || !mob.isAlive()) {
|
|
+ isSearching.set(false);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ final double x = mob.getX();
|
|
+ final double y = mob.getEyeY();
|
|
+ final double z = mob.getZ();
|
|
+ final double followDistance = this.getFollowDistance();
|
|
+ final TargetingConditions targetConditions = this.getTargetConditions();
|
|
+ final Class<T> targetType = this.targetType;
|
|
+
|
|
+ // Start async search with immutable captured state - using submit instead of runAsync
|
|
+ java.util.concurrent.CompletableFuture.supplyAsync(() -> {
|
|
+ try {
|
|
+ ServerLevel serverLevel = getServerLevel(mob);
|
|
+ if (serverLevel == null) {
|
|
+ return null;
|
|
+ }
|
|
+ if (mob.isRemoved() || !mob.isAlive()) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ if (targetType != Player.class && targetType != ServerPlayer.class) {
|
|
+ AABB searchArea = new AABB(
|
|
+ x - followDistance, y - followDistance, z - followDistance,
|
|
+ x + followDistance, y + followDistance, z + followDistance
|
|
+ );
|
|
+
|
|
+ java.util.List<T> entities = null;
|
|
+ try {
|
|
+ entities = mob.level().getEntitiesOfClass(targetType, searchArea, entity -> true);
|
|
+ } catch (Exception e) {
|
|
+ System.err.println("Error getting entities: " + e.getMessage());
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ if (entities != null && !entities.isEmpty()) {
|
|
+ return findNearestEntitySafely(entities, targetConditions, mob, x, y, z, serverLevel);
|
|
+ }
|
|
+ } else {
|
|
+ return findNearestPlayerSafely(targetConditions, mob, x, y, z, serverLevel);
|
|
+ }
|
|
+ } catch (Exception e) {
|
|
+ System.err.println("Error finding entities in async target finder: " + e.getMessage());
|
|
+ }
|
|
+
|
|
+ return null;
|
|
+ } catch (Exception e) {
|
|
+ System.err.println("Error during async target finding: " + e.getMessage());
|
|
+ return null;
|
|
+ } finally {
|
|
+ isSearching.set(false);
|
|
+ }
|
|
+ }, TARGET_FINDER_EXECUTOR).thenAccept(result -> {
|
|
+ if (result != null && result.isAlive() && !result.isRemoved()) {
|
|
+ pendingTarget.set(result);
|
|
+ }
|
|
+ });
|
|
}
|
|
|
|
+ @Nullable
|
|
+ private LivingEntity findNearestEntitySafely(
|
|
+ java.util.List<? extends LivingEntity> entities,
|
|
+ TargetingConditions conditions,
|
|
+ Mob source,
|
|
+ double x,
|
|
+ double y,
|
|
+ double z,
|
|
+ ServerLevel level) {
|
|
+
|
|
+ if (entities == null || entities.isEmpty() || level == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ double closestDistSq = -1.0;
|
|
+ LivingEntity closest = null;
|
|
+
|
|
+ for (int i = 0; i < entities.size(); i++) {
|
|
+ try {
|
|
+ LivingEntity entity = entities.get(i);
|
|
+ if (entity == null || entity.isRemoved() || !entity.isAlive()) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (conditions.test(level, source, entity)) {
|
|
+ double dx = entity.getX() - x;
|
|
+ double dy = entity.getY() - y;
|
|
+ double dz = entity.getZ() - z;
|
|
+ double distSq = dx * dx + dy * dy + dz * dz;
|
|
+
|
|
+ if (closestDistSq == -1.0 || distSq < closestDistSq) {
|
|
+ closestDistSq = distSq;
|
|
+ closest = entity;
|
|
+ }
|
|
+ }
|
|
+ } catch (IndexOutOfBoundsException e) {
|
|
+ break;
|
|
+ } catch (Exception e) {
|
|
+ System.err.println("Error processing entity in findNearestEntitySafely: " + e.getMessage());
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return closest;
|
|
+ } catch (Exception e) {
|
|
+ System.err.println("Error in findNearestEntitySafely: " + e.getMessage());
|
|
+ return null;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Nullable
|
|
+ private Player findNearestPlayerSafely(
|
|
+ TargetingConditions conditions,
|
|
+ Mob source,
|
|
+ double x,
|
|
+ double y,
|
|
+ double z,
|
|
+ ServerLevel level) {
|
|
+
|
|
+ if (level == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ java.util.List<? extends Player> players = level.players();
|
|
+ if (players == null || players.isEmpty()) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ double closestDistSq = -1.0;
|
|
+ Player closest = null;
|
|
+
|
|
+ for (int i = 0; i < players.size(); i++) {
|
|
+ try {
|
|
+ Player player = players.get(i);
|
|
+ if (player == null || player.isRemoved() || !player.isAlive()) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (conditions.test(level, source, player)) {
|
|
+ double dx = player.getX() - x;
|
|
+ double dy = player.getY() - y;
|
|
+ double dz = player.getZ() - z;
|
|
+ double distSq = dx * dx + dy * dy + dz * dz;
|
|
+
|
|
+ if (closestDistSq == -1.0 || distSq < closestDistSq) {
|
|
+ closestDistSq = distSq;
|
|
+ closest = player;
|
|
+ }
|
|
+ }
|
|
+ } catch (IndexOutOfBoundsException e) {
|
|
+ break;
|
|
+ } catch (Exception e) {
|
|
+ System.err.println("Error processing player in findNearestPlayerSafely: " + e.getMessage());
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return closest;
|
|
+ } catch (Exception e) {
|
|
+ System.err.println("Error in findNearestPlayerSafely: " + e.getMessage());
|
|
+ return null;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Synchronous fallback method
|
|
+ private void findTargetSync() {
|
|
+ try {
|
|
+ ServerLevel serverLevel = getServerLevel(this.mob);
|
|
+ if (serverLevel == null) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (this.targetType != Player.class && this.targetType != ServerPlayer.class) {
|
|
+ try {
|
|
+ this.target = serverLevel.getNearestEntity(
|
|
+ this.mob.level().getEntitiesOfClass(this.targetType, this.getTargetSearchArea(this.getFollowDistance()), entity -> true),
|
|
+ this.getTargetConditions(),
|
|
+ this.mob,
|
|
+ this.mob.getX(),
|
|
+ this.mob.getEyeY(),
|
|
+ this.mob.getZ()
|
|
+ );
|
|
+ } catch (Exception e) {
|
|
+ System.err.println("Error in sync entity finding: " + e.getMessage());
|
|
+ this.target = null;
|
|
+ }
|
|
+ } else {
|
|
+ try {
|
|
+ this.target = serverLevel.getNearestPlayer(this.getTargetConditions(), this.mob, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ());
|
|
+ } catch (Exception e) {
|
|
+ System.err.println("Error in sync player finding: " + e.getMessage());
|
|
+ this.target = null;
|
|
+ }
|
|
+ }
|
|
+ } catch (Exception e) {
|
|
+ System.err.println("Error in findTargetSync: " + e.getMessage());
|
|
+ this.target = null;
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - Async Target Finding
|
|
+
|
|
@Override
|
|
public void start() {
|
|
- this.mob.setTarget(this.target, this.target instanceof ServerPlayer ? org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER : org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY); // CraftBukkit - reason
|
|
+ // Leaf start - Async Target Finding
|
|
+ LivingEntity targetEntity = this.target;
|
|
+ if (targetEntity != null && !targetEntity.isRemoved() && targetEntity.isAlive()) {
|
|
+ try {
|
|
+ this.mob.setTarget(targetEntity, targetEntity instanceof ServerPlayer ?
|
|
+ org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER :
|
|
+ org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY);
|
|
+ } catch (Exception e) {
|
|
+ System.err.println("Error in setTarget: " + e.getMessage());
|
|
+ this.target = null;
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - Async Target Finding
|
|
super.start();
|
|
}
|
|
|