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() {
