Commit 7daf8144 by Jonathan Thomas

Fix concurrent and consistency issues when adding, removing and moving goals on…

Fix concurrent and consistency issues when adding, removing and moving goals on Entities. Removing local Map and utilize goalSelector directly as ground truth.
parent 4fd40686
Pipeline #12051 passed with stage
in 25 seconds
...@@ -9,16 +9,16 @@ import net.minecraft.server.world.ServerWorld; ...@@ -9,16 +9,16 @@ import net.minecraft.server.world.ServerWorld;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.*; import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/** /**
* The {@code EntityBehaviorManager} class keeps track of all Mob Entities which have * The {@code EntityBehaviorManager} class now directly interacts with the goal selectors of entities
* custom goals attached to them, and prevents the same goal from being added to an * to manage goals, while avoiding concurrent modification issues.
* entity more than once.
*/ */
public class EntityBehaviorManager { public class EntityBehaviorManager {
public static final Logger LOGGER = LoggerFactory.getLogger("creaturechat"); public static final Logger LOGGER = LoggerFactory.getLogger("creaturechat");
private static final Map<UUID, List<Goal>> entityGoals = new HashMap<>();
public static void addGoal(MobEntity entity, Goal goal, GoalPriority priority) { public static void addGoal(MobEntity entity, Goal goal, GoalPriority priority) {
if (!(entity.getWorld() instanceof ServerWorld)) { if (!(entity.getWorld() instanceof ServerWorld)) {
...@@ -26,79 +26,54 @@ public class EntityBehaviorManager { ...@@ -26,79 +26,54 @@ public class EntityBehaviorManager {
return; return;
} }
UUID entityId = entity.getUuid(); ServerPackets.serverInstance.execute(() -> {
GoalSelector goalSelector = GoalUtils.getGoalSelector(entity);
// Use removeGoal to remove any existing goal of the same type
removeGoal(entity, goal.getClass());
// Move any conflicting goals +1 in priority // First clear any existing goals of the same type to avoid duplicates
moveConflictingGoals(entity, priority); goalSelector.clear(g -> g.getClass().isAssignableFrom(goal.getClass()));
// Now that any existing goal of the same type has been removed, we can add the new goal // Handle potential priority conflicts before adding the new goal
List<Goal> goals = entityGoals.computeIfAbsent(entityId, k -> new ArrayList<>()); moveConflictingGoals(goalSelector, priority);
goals.add(goal);
// Ensure that the task is synced with the server thread // Now add the new goal at the specified priority
ServerPackets.serverInstance.execute(() -> {
GoalSelector goalSelector = GoalUtils.getGoalSelector(entity);
goalSelector.add(priority.getPriority(), goal); goalSelector.add(priority.getPriority(), goal);
LOGGER.debug("Goal of type {} added to entity UUID: {}", goal.getClass().getSimpleName(), entityId); LOGGER.debug("Goal of type {} added with priority {}", goal.getClass().getSimpleName(), priority);
}); });
} }
public static void removeGoal(MobEntity entity, Class<? extends Goal> goalClass) { public static void removeGoal(MobEntity entity, Class<? extends Goal> goalClass) {
UUID entityId = entity.getUuid();
List<Goal> goals = entityGoals.get(entityId);
if (goals != null) {
// Ensure that the task is synced with the server thread
ServerPackets.serverInstance.execute(() -> { ServerPackets.serverInstance.execute(() -> {
GoalSelector goalSelector = GoalUtils.getGoalSelector(entity); GoalSelector goalSelector = GoalUtils.getGoalSelector(entity);
goals.removeIf(goal -> { goalSelector.clear(g -> goalClass.isInstance(g));
if (goalClass.isInstance(goal)) { LOGGER.debug("All goals of type {} removed.", goalClass.getSimpleName());
goalSelector.remove(goal);
LOGGER.debug("Goal of type {} removed for entity UUID: {}", goalClass.getSimpleName(), entityId);
return true;
}
return false;
});
}); });
} }
}
private static void moveConflictingGoals(MobEntity entity, GoalPriority newGoalPriority) { public static void moveConflictingGoals(GoalSelector goalSelector, GoalPriority newGoalPriority) {
// Ensure that the task is synced with the server thread // Collect all prioritized goals currently in the selector.
ServerPackets.serverInstance.execute(() -> { List<PrioritizedGoal> sortedGoals = goalSelector.getGoals().stream()
GoalSelector goalSelector = GoalUtils.getGoalSelector(entity); .sorted(Comparator.comparingInt(PrioritizedGoal::getPriority))
.collect(Collectors.toList());
// Retrieve the existing goals
Set<PrioritizedGoal> existingGoals = new HashSet<>(goalSelector.getGoals()); // Check if there is an existing goal at the new priority level.
boolean conflictExists = sortedGoals.stream()
// Flag to check if there is a goal with the same priority as the new goal .anyMatch(pg -> pg.getPriority() == newGoalPriority.getPriority());
boolean conflictExists = existingGoals.stream()
.anyMatch(goal -> goal.getPriority() == newGoalPriority.getPriority()); // If there is a conflict, we need to shift priorities of this and all higher priorities.
if (conflictExists) {
if (!conflictExists) { int shiftPriority = newGoalPriority.getPriority();
// If there's no conflict, no need to adjust priorities for (PrioritizedGoal pg : sortedGoals) {
return; if (pg.getPriority() >= shiftPriority) {
} // Remove the goal and increment its priority.
goalSelector.remove(pg.getGoal());
// If there's a conflict, collect goals that need their priority incremented goalSelector.add(shiftPriority + 1, pg.getGoal());
List<PrioritizedGoal> goalsToModify = new ArrayList<>(); shiftPriority++; // Update the shift priority for the next possible conflict.
for (PrioritizedGoal prioritizedGoal : existingGoals) {
if (prioritizedGoal.getPriority() >= newGoalPriority.getPriority()) {
goalsToModify.add(prioritizedGoal);
} }
} }
// Increment priorities and re-add goals LOGGER.debug("Moved conflicting goals starting from priority {}", newGoalPriority);
for (PrioritizedGoal goalToModify : goalsToModify) { } else {
// Remove the original goal LOGGER.debug("No conflicting goal at priority {}, no action taken.", newGoalPriority);
goalSelector.remove(goalToModify.getGoal());
// Increment the priority and re-add the goal
goalSelector.add(goalToModify.getPriority() + 1, goalToModify.getGoal());
} }
});
} }
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment