9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-31 04:46:38 +00:00

Replace GoalSelector#availableGoals with optimized collection (#465)

* optimize goal selector

* rebase

* rebase

---------

Co-authored-by: Taiyou06 <kaandindar21@gmail.com>
This commit is contained in:
hayanesuru
2025-08-20 04:48:46 +09:00
committed by GitHub
parent 23b7b02eee
commit 037ec255d4
2 changed files with 406 additions and 0 deletions

View File

@@ -0,0 +1,190 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: hayanesuru <hayanesuru@outlook.jp>
Date: Fri, 15 Aug 2025 15:02:27 +0900
Subject: [PATCH] optimize goal selector
diff --git a/net/minecraft/world/entity/ai/goal/Goal.java b/net/minecraft/world/entity/ai/goal/Goal.java
index f54bbe2e65b18f214266769c7a64144baafa9a58..40d53e4469a71b33949ee2bd7b01783d343d4134 100644
--- a/net/minecraft/world/entity/ai/goal/Goal.java
+++ b/net/minecraft/world/entity/ai/goal/Goal.java
@@ -100,10 +100,12 @@ public abstract class Goal {
// Paper end - Mob goal api
public static enum Flag {
+ // Leaf - optimize goal selector - diff on change
UNKNOWN_BEHAVIOR, // Paper - add UNKNOWN_BEHAVIOR
MOVE,
LOOK,
JUMP,
TARGET;
+ // Leaf - optimize goal selector - diff on change
}
}
diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java
index 653c58c7637c46c8b46a5082f671324a2221d431..f8e99ec950373fe5d3c178751bcc6e8dd9d9fb02 100644
--- a/net/minecraft/world/entity/ai/goal/GoalSelector.java
+++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java
@@ -20,10 +20,11 @@ public class GoalSelector {
}
};
private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class);
- private final Set<WrappedGoal> availableGoals = new ObjectLinkedOpenHashSet<>();
+ private final org.dreeam.leaf.util.map.BinaryGoalSet availableGoals = new org.dreeam.leaf.util.map.BinaryGoalSet(); // Leaf - optimize goal selector
private static final Goal.Flag[] GOAL_FLAG_VALUES = Goal.Flag.values(); // Paper - remove streams from GoalSelector
private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from GoalSelector
private int curRate; // Paper - EAR 2
+ private final int[] lockedPriorities = new int[GOAL_FLAG_VALUES.length]; // Leaf - optimize goal selector
public void addGoal(int priority, Goal goal) {
this.availableGoals.add(new WrappedGoal(priority, goal));
@@ -40,7 +41,12 @@ public class GoalSelector {
}
public boolean hasTasks() {
- for (WrappedGoal task : this.availableGoals) {
+ // Leaf start - optimize goal selector
+ org.dreeam.leaf.util.map.BinaryGoalSet availableGoals = this.availableGoals;
+ WrappedGoal[] elements = availableGoals.elements();
+ for (int i = 0, j = availableGoals.size(); i < j; i++) {
+ WrappedGoal task = elements[i];
+ // Leaf end - optimize goal selector
if (task.isRunning()) {
return true;
}
@@ -50,7 +56,12 @@ public class GoalSelector {
// Paper end - EAR 2
public void removeGoal(Goal goal) {
- for (WrappedGoal wrappedGoal : this.availableGoals) {
+ // Leaf start - optimize goal selector
+ org.dreeam.leaf.util.map.BinaryGoalSet availableGoals = this.availableGoals;
+ WrappedGoal[] elements = availableGoals.elements();
+ for (int i = 0, j = availableGoals.size(); i < j; i++) {
+ WrappedGoal wrappedGoal = elements[i];
+ // Leaf end - optimize goal selector
if (wrappedGoal.getGoal() == goal && wrappedGoal.isRunning()) {
wrappedGoal.stop();
}
@@ -80,37 +91,79 @@ public class GoalSelector {
}
public void tick() {
- for (WrappedGoal wrappedGoal : this.availableGoals) {
- if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams
- wrappedGoal.stop();
+ final org.dreeam.leaf.util.map.BinaryGoalSet availableGoals = this.availableGoals;
+ final WrappedGoal[] elements = availableGoals.elements();
+ final long disabled = this.goalTypes.getBackingSet();
+ final int elemSize = availableGoals.size();
+ final Map<Goal.Flag, WrappedGoal> lockedFlags = this.lockedFlags;
+ final int[] lockedPriorities = this.lockedPriorities;
+ long mask = 0L;
+
+ for (int i = 0; i < elemSize; i++) {
+ final WrappedGoal goal = elements[i];
+ if (goal.isRunning() && ((disabled & goal.goal.getFlags().getBackingSet()) != 0 || !goal.canContinueToUse())) {
+ goal.stop();
}
}
-
- this.lockedFlags.entrySet().removeIf(entry -> !entry.getValue().isRunning());
-
- for (WrappedGoal wrappedGoalx : this.availableGoals) {
- // Paper start
- if (!wrappedGoalx.isRunning() && !goalContainsAnyFlags(wrappedGoalx, this.goalTypes) && goalCanBeReplacedForAllFlags(wrappedGoalx, this.lockedFlags) && wrappedGoalx.canUse()) {
- long flagIterator = wrappedGoalx.getFlags().getBackingSet();
- int wrappedGoalSize = wrappedGoalx.getFlags().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);
- // Paper end
- WrappedGoal wrappedGoal1 = this.lockedFlags.getOrDefault(flag, NO_GOAL);
- wrappedGoal1.stop();
- this.lockedFlags.put(flag, wrappedGoalx);
+ for (int i = 0; i < GOAL_FLAG_VALUES.length; i++) {
+ final Goal.Flag flag = GOAL_FLAG_VALUES[i];
+ final WrappedGoal locked = lockedFlags.get(flag);
+ if (locked == null) {
+ lockedPriorities[i] = Integer.MAX_VALUE;
+ } else if (!locked.isRunning()) {
+ lockedFlags.remove(flag);
+ lockedPriorities[i] = Integer.MAX_VALUE;
+ } else {
+ lockedPriorities[i] = locked.isInterruptable()
+ ? locked.getPriority()
+ : Integer.MIN_VALUE;
+ mask |= (1L << i);
+ }
+ }
+ for (int i = 0; i < elemSize; i++) {
+ final WrappedGoal goal = elements[i];
+ if (goal.isRunning()) {
+ continue;
+ }
+ final long f = goal.goal.getFlags().getBackingSet();
+ final int p = goal.getPriority();
+ if ((disabled & f) != 0L
+ || (f & mask) != 0L
+ && ((f & 1L) != 0L && p >= lockedPriorities[0]
+ || (f & 2L) != 0L && p >= lockedPriorities[1]
+ || (f & 4L) != 0L && p >= lockedPriorities[2]
+ || (f & 8L) != 0L && p >= lockedPriorities[3]
+ || (f & 16L) != 0L && p >= lockedPriorities[4])) {
+ continue;
+ } else if (!goal.canUse()) {
+ continue;
+ }
+ for (long iter = f; iter != 0L; iter &= iter - 1) {
+ final int j = Long.numberOfTrailingZeros(iter);
+ final Goal.Flag flag = GOAL_FLAG_VALUES[j];
+ final WrappedGoal locked = lockedFlags.get(flag);
+ if (locked != null) {
+ locked.stop();
}
-
- wrappedGoalx.start();
+ lockedFlags.put(flag, goal);
+ lockedPriorities[j] = goal.isInterruptable()
+ ? goal.getPriority()
+ : Integer.MIN_VALUE;
+ mask |= (1L << j);
}
+ goal.start();
}
this.tickRunningGoals(true);
}
public void tickRunningGoals(boolean tickAllRunning) {
- for (WrappedGoal wrappedGoal : this.availableGoals) {
+ // Leaf start - optimize goal selector
+ org.dreeam.leaf.util.map.BinaryGoalSet availableGoals = this.availableGoals;
+ WrappedGoal[] elements = availableGoals.elements();
+ for (int i = 0, j = availableGoals.size(); i < j; i++) {
+ WrappedGoal wrappedGoal = elements[i];
+ // Leaf end - optimize goal selector
if (wrappedGoal.isRunning() && (tickAllRunning || wrappedGoal.requiresUpdateEveryTick())) {
wrappedGoal.tick();
}
diff --git a/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/net/minecraft/world/entity/ai/goal/WrappedGoal.java
index 2c2ab6a1df9d3d23773e44ce4041cc1c21b55163..f5ef2e26ff4f9207fc56abe95fe68b3f166499bd 100644
--- a/net/minecraft/world/entity/ai/goal/WrappedGoal.java
+++ b/net/minecraft/world/entity/ai/goal/WrappedGoal.java
@@ -4,7 +4,7 @@ import java.util.EnumSet;
import javax.annotation.Nullable;
public class WrappedGoal extends Goal {
- private final Goal goal;
+ public final Goal goal; // Leaf - optimize goal selector - private -> public
private final int priority;
private boolean isRunning;
@@ -70,6 +70,7 @@ public class WrappedGoal extends Goal {
@Override
public ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() { // Paper - remove streams from GoalSelector
+ // Leaf - optimize goal selector - diff on change
return this.goal.getFlags();
}

View File

@@ -0,0 +1,216 @@
package org.dreeam.leaf.util.map;
import net.minecraft.world.entity.ai.goal.WrappedGoal;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.Predicate;
public final class BinaryGoalSet extends AbstractSet<WrappedGoal> {
private static final WrappedGoal[] EMPTY_ARRAY = {};
private WrappedGoal[] a;
private int size;
private static final int DEFAULT_CAPACITY = 4;
public BinaryGoalSet() {
this.a = EMPTY_ARRAY;
}
@Override
public int size() {
return size;
}
@Override
public void clear() {
Arrays.fill(a, 0, size, null);
size = 0;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public boolean add(final WrappedGoal goal) {
Objects.requireNonNull(goal);
if (size == a.length) {
grow();
}
final int priority = goal.getPriority();
int left = 0;
int right = size;
while (left < right) {
final int mid = (left + right) >>> 1;
if (a[mid].getPriority() < priority) {
left = mid + 1;
} else {
right = mid;
}
}
int gap = left;
while (gap < size && a[gap].getPriority() == priority) {
if (a[gap] == goal) {
return false;
}
gap++;
}
if (left < size) {
System.arraycopy(a, left, a, left + 1, size - left);
}
a[left] = goal;
size++;
return true;
}
@Override
public boolean remove(final Object obj) {
if (!(obj instanceof final WrappedGoal goal)) {
return false;
}
final int priority = goal.getPriority();
int left = 0;
int right = size;
while (left < right) {
final int mid = (left + right) >>> 1;
if (a[mid].getPriority() < priority) {
left = mid + 1;
} else {
right = mid;
}
}
int gap = -1;
for (int i = left; i < size && a[i].getPriority() == priority; i++) {
if (a[i] == goal) {
gap = i;
break;
}
}
if (gap == -1) {
return false;
}
if (gap < size - 1) {
System.arraycopy(a, gap + 1, a, gap, size - gap - 1);
}
size--;
a[size] = null;
return true;
}
@Override
public boolean removeIf(@NotNull final Predicate<? super WrappedGoal> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
for (int i = size - 1; i >= 0; i--) {
final WrappedGoal e = a[i];
if (!filter.test(e)) {
continue;
}
if (i < size - 1) {
System.arraycopy(a, i + 1, a, i, size - i - 1);
}
size--;
a[size] = null;
removed = true;
}
return removed;
}
@Override
public boolean contains(final Object obj) {
if (!(obj instanceof final WrappedGoal goal)) {
return false;
}
final int priority = goal.getPriority();
int left = 0;
int right = size;
while (left < right) {
final int mid = (left + right) >>> 1;
if (a[mid].getPriority() < priority) {
left = mid + 1;
} else {
right = mid;
}
}
for (int i = left; i < size && a[i].getPriority() == priority; i++) {
if (a[i] == goal) {
return true;
}
}
return false;
}
private void grow() {
final int capacity = (a.length == 0) ? DEFAULT_CAPACITY : a.length + (a.length >> 1);
final WrappedGoal[] newArray = new WrappedGoal[capacity];
System.arraycopy(a, 0, newArray, 0, size);
a = newArray;
}
@Override
@NotNull
public Iterator<WrappedGoal> iterator() {
return new Iterator<>() {
private int cursor = 0;
private int last = -1;
@Override
public boolean hasNext() {
return cursor < size;
}
@Override
public WrappedGoal next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
last = cursor;
return a[cursor++];
}
@Override
public void remove() {
if (last == -1) {
throw new IllegalStateException();
}
System.arraycopy(a, last + 1, a, last, size - last - 1);
size--;
a[size] = null;
cursor = last;
last = -1;
}
};
}
@Override
public int hashCode() {
int result = 0;
for (int i = 0, j = size; i < j; i++) {
final WrappedGoal e = a[i];
result += e == null ? 0 : e.hashCode();
}
return result;
}
public WrappedGoal[] elements() {
return a;
}
}