Paper/patches/server/0403-Remove-streams-from-Mob-AI-System.patch
2021-06-16 13:12:05 -07:00

225 lines
11 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Mon, 6 Apr 2020 17:53:29 -0700
Subject: [PATCH] Remove streams from Mob AI System
The streams hurt performance and allocate tons of garbage, so
replace them with the standard iterator.
Also optimise the stream.anyMatch statement to move to a bitset
where we can replace the call with a single bitwise operation.
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
index d92ddc8a4c0f5249b7ff4f97af1ea3db413b2983..8c2ec30a35e86f2b30863045b586a67e485c624b 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
@@ -3,7 +3,8 @@ package net.minecraft.world.entity.ai.goal;
import java.util.EnumSet;
public abstract class Goal {
- private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class);
+ private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be.
+ private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
public abstract boolean canUse();
@@ -25,8 +26,10 @@ public abstract class Goal {
}
public void setFlags(EnumSet<Goal.Flag> controls) {
- this.flags.clear();
- this.flags.addAll(controls);
+ // Paper start - remove streams from pathfindergoalselector
+ this.goalTypes.clear();
+ this.goalTypes.addAllUnchecked(controls);
+ // Paper end - remove streams from pathfindergoalselector
}
@Override
@@ -34,8 +37,10 @@ public abstract class Goal {
return this.getClass().getSimpleName();
}
- public EnumSet<Goal.Flag> getFlags() {
- return this.flags;
+ // Paper start - remove streams from pathfindergoalselector
+ public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> getGoalTypes() {
+ return this.goalTypes;
+ // Paper end - remove streams from pathfindergoalselector
}
public static enum Flag {
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
index 69bf112655615337e0df3ea56b9e42fa5ff70430..faa53d08a12cc7441c670cae6d301de3f498ffe7 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
@@ -28,10 +28,12 @@ public class GoalSelector {
private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class);
public final Set<WrappedGoal> availableGoals = Sets.newLinkedHashSet();
private final Supplier<ProfilerFiller> profiler;
- private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class);
+ private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be.
+ private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
private int tickCount;
private int newGoalRate = 3;
private int curRate;
+ private static final Goal.Flag[] PATHFINDER_GOAL_TYPES = Goal.Flag.values(); // Paper - remove streams from pathfindergoalselector
public GoalSelector(Supplier<ProfilerFiller> profiler) {
this.profiler = profiler;
@@ -61,47 +63,95 @@ public class GoalSelector {
}
// Paper end
public void removeGoal(Goal goal) {
- this.availableGoals.stream().filter((wrappedGoal) -> {
- return wrappedGoal.getGoal() == goal;
- }).filter(WrappedGoal::isRunning).forEach(WrappedGoal::stop);
- this.availableGoals.removeIf((wrappedGoal) -> {
- return wrappedGoal.getGoal() == goal;
- });
+ // Paper start - remove streams from pathfindergoalselector
+ for (java.util.Iterator<WrappedGoal> iterator = this.availableGoals.iterator(); iterator.hasNext();) {
+ WrappedGoal goalWrapped = iterator.next();
+ if (goalWrapped.getGoal() != goal) {
+ continue;
+ }
+ if (goalWrapped.isRunning()) {
+ goalWrapped.stop();
+ }
+ iterator.remove();
+ }
+ // Paper end - remove streams from pathfindergoalselector
}
public void tick() {
ProfilerFiller profilerFiller = this.profiler.get();
profilerFiller.push("goalCleanup");
- this.getRunningGoals().filter((wrappedGoal) -> {
- return !wrappedGoal.isRunning() || wrappedGoal.getFlags().stream().anyMatch(this.disabledFlags::contains) || !wrappedGoal.canContinueToUse();
- }).forEach(Goal::stop);
- this.lockedFlags.forEach((flag, wrappedGoal) -> {
+ // Paper start - remove streams from pathfindergoalselector
+ for (java.util.Iterator<WrappedGoal> iterator = this.availableGoals.iterator(); iterator.hasNext();) {
+ WrappedGoal wrappedGoal = iterator.next();
if (!wrappedGoal.isRunning()) {
- this.lockedFlags.remove(flag);
+ continue;
+ }
+ if (!this.goalTypes.hasCommonElements(wrappedGoal.getGoalTypes()) && wrappedGoal.canContinueToUse()) {
+ continue;
+ }
+ wrappedGoal.stop();
+ }
+ // Paper end - remove streams from pathfindergoalselector
+ this.lockedFlags.forEach((pathfindergoal_type, pathfindergoalwrapped) -> {
+ if (!pathfindergoalwrapped.isRunning()) {
+ this.lockedFlags.remove(pathfindergoal_type);
}
});
profilerFiller.pop();
profilerFiller.push("goalUpdate");
- this.availableGoals.stream().filter((wrappedGoal) -> {
- return !wrappedGoal.isRunning();
- }).filter((wrappedGoal) -> {
- return wrappedGoal.getFlags().stream().noneMatch(this.disabledFlags::contains);
- }).filter((wrappedGoal) -> {
- return wrappedGoal.getFlags().stream().allMatch((flag) -> {
- return this.lockedFlags.getOrDefault(flag, NO_GOAL).canBeReplacedBy(wrappedGoal);
- });
- }).filter(WrappedGoal::canUse).forEach((wrappedGoal) -> {
- wrappedGoal.getFlags().forEach((flag) -> {
- WrappedGoal wrappedGoal2 = this.lockedFlags.getOrDefault(flag, NO_GOAL);
- wrappedGoal2.stop();
- this.lockedFlags.put(flag, wrappedGoal);
- });
+
+ // Paper start - remove streams from pathfindergoalselector
+ goal_update_loop: for (java.util.Iterator<WrappedGoal> iterator = this.availableGoals.iterator(); iterator.hasNext();) {
+ WrappedGoal wrappedGoal = iterator.next();
+ if (wrappedGoal.isRunning()) {
+ continue;
+ }
+
+ com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> wrappedGoalSet = wrappedGoal.getGoalTypes();
+
+ if (this.goalTypes.hasCommonElements(wrappedGoalSet)) {
+ continue;
+ }
+
+ long iterator1 = wrappedGoalSet.getBackingSet();
+ int wrappedGoalSize = wrappedGoalSet.size();
+ for (int i = 0; i < wrappedGoalSize; ++i) {
+ Goal.Flag type = PATHFINDER_GOAL_TYPES[Long.numberOfTrailingZeros(iterator1)];
+ iterator1 ^= com.destroystokyo.paper.util.math.IntegerUtil.getTrailingBit(iterator1);
+ WrappedGoal wrapped = this.lockedFlags.getOrDefault(type, GoalSelector.NO_GOAL);
+ if (!wrapped.canBeReplacedBy(wrappedGoal)) {
+ continue goal_update_loop;
+ }
+ }
+
+ if (!wrappedGoal.canUse()) {
+ continue;
+ }
+
+ iterator1 = wrappedGoalSet.getBackingSet();
+ wrappedGoalSize = wrappedGoalSet.size();
+ for (int i = 0; i < wrappedGoalSize; ++i) {
+ Goal.Flag type = PATHFINDER_GOAL_TYPES[Long.numberOfTrailingZeros(iterator1)];
+ iterator1 ^= com.destroystokyo.paper.util.math.IntegerUtil.getTrailingBit(iterator1);
+ WrappedGoal wrapped = this.lockedFlags.getOrDefault(type, GoalSelector.NO_GOAL);
+
+ wrapped.stop();
+ this.lockedFlags.put(type, wrappedGoal);
+ }
wrappedGoal.start();
- });
+ }
+ // Paper end - remove streams from pathfindergoalselector
profilerFiller.pop();
profilerFiller.push("goalTick");
- this.getRunningGoals().forEach(WrappedGoal::tick);
+ // Paper start - remove streams from pathfindergoalselector
+ for (java.util.Iterator<WrappedGoal> iterator = this.availableGoals.iterator(); iterator.hasNext();) {
+ WrappedGoal wrappedGoal = iterator.next();
+ if (wrappedGoal.isRunning()) {
+ wrappedGoal.tick();
+ }
+ }
+ // Paper end - remove streams from pathfindergoalselector
profilerFiller.pop();
}
@@ -118,11 +168,11 @@ public class GoalSelector {
}
public void disableControlFlag(Goal.Flag control) {
- this.disabledFlags.add(control);
+ this.goalTypes.addUnchecked(control); // Paper - remove streams from pathfindergoalselector
}
public void enableControlFlag(Goal.Flag control) {
- this.disabledFlags.remove(control);
+ this.goalTypes.removeUnchecked(control); // Paper - remove streams from pathfindergoalselector
}
public void setControlFlag(Goal.Flag control, boolean enabled) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
index 1e915b999f4261fb27846a0e559ea22e4b09b4db..037cc5d2b41161e040fc9b264a0dd04827c29681 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
@@ -58,9 +58,10 @@ public class WrappedGoal extends Goal {
this.goal.setFlags(controls);
}
- @Override
- public EnumSet<Goal.Flag> getFlags() {
- return this.goal.getFlags();
+ // Paper start - remove streams from pathfindergoalselector
+ public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> getGoalTypes() {
+ return this.goal.getGoalTypes();
+ // Paper end - remove streams from pathfindergoalselector
}
public boolean isRunning() {