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:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user