From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 29 Mar 2025 13:40:46 +0100 Subject: [PATCH] AsyncTargetFinding diff --git a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java index 41ee3cdc45ecc8376a2203ed588bb544ed377294..7fe2b75e8c2718851d68429380ac71203d31a58f 100644 --- a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java @@ -1,6 +1,13 @@ package net.minecraft.world.entity.ai.goal.target; import java.util.EnumSet; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nullable; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; @@ -10,15 +17,42 @@ import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.entity.ai.targeting.TargetingConditions; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.AABB; +import org.dreeam.leaf.config.modules.async.AsyncTargetFinding; public class NearestAttackableTargetGoal extends TargetGoal { private static final int DEFAULT_RANDOM_INTERVAL = 10; protected final Class targetType; protected final int randomInterval; @Nullable - protected LivingEntity target; + protected volatile LivingEntity target; protected TargetingConditions targetConditions; + // Single thread executor to prevent overwhelming the server + private static final ExecutorService TARGET_FINDER_EXECUTOR = 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 AtomicBoolean isSearching = new AtomicBoolean(false); + private final AtomicReference pendingTarget = new AtomicReference<>(null); + static { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + TARGET_FINDER_EXECUTOR.shutdown(); + TARGET_FINDER_EXECUTOR.awaitTermination(2, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { + if (!TARGET_FINDER_EXECUTOR.isTerminated()) { + TARGET_FINDER_EXECUTOR.shutdownNow(); + } + } + })); + } + public NearestAttackableTargetGoal(Mob mob, Class targetType, boolean mustSee) { this(mob, targetType, 10, mustSee, false, null); } @@ -46,8 +80,12 @@ public class NearestAttackableTargetGoal extends TargetG if (this.randomInterval > 0 && this.mob.getRandom().nextInt(this.randomInterval) != 0) { return false; } else { - this.findTarget(); - return this.target != null; + 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(); } } @@ -55,25 +93,235 @@ public class NearestAttackableTargetGoal extends TargetG return this.mob.getBoundingBox().inflate(targetDistance, targetDistance, targetDistance); } + // 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 (!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 targetType = this.targetType; + + // Start async search with immutable captured state - using submit instead of runAsync + 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 + ); + + List 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( + List 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 { + List 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; } } @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, true); // CraftBukkit - reason + 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, true); + } catch (Exception e) { + System.err.println("Error in setTarget: " + e.getMessage()); + this.target = null; + } + } super.start(); }