diff options
Diffstat (limited to 'patches/server/1005-Optimize-GoalSelector-Goal.Flag-Set-operations.patch')
-rw-r--r-- | patches/server/1005-Optimize-GoalSelector-Goal.Flag-Set-operations.patch | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/patches/server/1005-Optimize-GoalSelector-Goal.Flag-Set-operations.patch b/patches/server/1005-Optimize-GoalSelector-Goal.Flag-Set-operations.patch new file mode 100644 index 0000000000..8e879a28af --- /dev/null +++ b/patches/server/1005-Optimize-GoalSelector-Goal.Flag-Set-operations.patch @@ -0,0 +1,180 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Mon, 6 Apr 2020 17:53:29 -0700 +Subject: [PATCH] Optimize GoalSelector Goal.Flag Set operations + +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 16f9a98b8a939e5ca7e2dc04f87134a7ed66736b..dd423302b1baa64ef86ded87a29659342e28c142 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 +@@ -4,7 +4,16 @@ import java.util.EnumSet; + import net.minecraft.util.Mth; + + 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 ++ ++ // Paper start - remove streams from pathfindergoalselector; make sure types are not empty ++ public Goal() { ++ if (this.goalTypes.size() == 0) { ++ this.goalTypes.add(Flag.UNKNOWN_BEHAVIOR); ++ } ++ } ++ // Paper end - remove streams from pathfindergoalselector + + public abstract boolean canUse(); + +@@ -30,8 +39,13 @@ 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.addAll(controls); ++ if (this.goalTypes.size() == 0) { ++ this.goalTypes.add(Flag.UNKNOWN_BEHAVIOR); ++ } ++ // Paper end - remove streams from pathfindergoalselector + } + + @Override +@@ -39,8 +53,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> getFlags() { ++ return this.goalTypes; ++ // Paper end - remove streams from pathfindergoalselector + } + + protected int adjustedTickDelay(int ticks) { +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 e5995d0db5dcfba59a873ff439601894fdacd556..676f5485a4ca9252e911213dcda8d51776b637b6 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 +@@ -30,10 +30,12 @@ public class GoalSelector { + private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class); + private 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[] GOAL_FLAG_VALUES = Goal.Flag.values(); // Paper - remove streams from pathfindergoalselector + + public GoalSelector(Supplier<ProfilerFiller> profiler) { + this.profiler = profiler; +@@ -65,26 +67,32 @@ 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; +- }); +- } +- +- private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet<Goal.Flag> controls) { +- for(Goal.Flag flag : goal.getFlags()) { +- if (controls.contains(flag)) { +- return true; ++ // 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 ++ } + +- return false; ++ private static boolean goalContainsAnyFlags(WrappedGoal goal, com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> controls) { ++ return goal.getFlags().hasCommonElements(controls); // Paper + } + + private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map<Goal.Flag, WrappedGoal> goalsByControl) { +- for(Goal.Flag flag : goal.getFlags()) { ++ // Paper start ++ long flagIterator = goal.getFlags().getBackingSet(); ++ int wrappedGoalSize = goal.getFlags().size(); ++ for (int i = 0; i < wrappedGoalSize; ++i) { ++ final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; ++ flagIterator ^= io.papermc.paper.util.IntegerUtil.getTrailingBit(flagIterator); ++ // Paper end + if (!goalsByControl.getOrDefault(flag, NO_GOAL).canBeReplacedBy(goal)) { + return false; + } +@@ -98,7 +106,7 @@ public class GoalSelector { + profilerFiller.push("goalCleanup"); + + for(WrappedGoal wrappedGoal : this.availableGoals) { +- if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.disabledFlags) || !wrappedGoal.canContinueToUse())) { ++ if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { + wrappedGoal.stop(); + } + } +@@ -116,8 +124,14 @@ public class GoalSelector { + profilerFiller.push("goalUpdate"); + + for(WrappedGoal wrappedGoal2 : this.availableGoals) { +- if (!wrappedGoal2.isRunning() && !goalContainsAnyFlags(wrappedGoal2, this.disabledFlags) && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags) && wrappedGoal2.canUse()) { +- for(Goal.Flag flag : wrappedGoal2.getFlags()) { ++ // Paper start ++ if (!wrappedGoal2.isRunning() && !goalContainsAnyFlags(wrappedGoal2, this.goalTypes) && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags) && wrappedGoal2.canUse()) { ++ long flagIterator = wrappedGoal2.getFlags().getBackingSet(); ++ int wrappedGoalSize = wrappedGoal2.getFlags().size(); ++ for (int i = 0; i < wrappedGoalSize; ++i) { ++ final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; ++ flagIterator ^= io.papermc.paper.util.IntegerUtil.getTrailingBit(flagIterator); ++ // Paper end + WrappedGoal wrappedGoal3 = this.lockedFlags.getOrDefault(flag, NO_GOAL); + wrappedGoal3.stop(); + this.lockedFlags.put(flag, wrappedGoal2); +@@ -157,11 +171,11 @@ public class GoalSelector { + } + + public void disableControlFlag(Goal.Flag control) { +- this.disabledFlags.add(control); ++ this.goalTypes.add(control); // Paper - remove streams from pathfindergoalselector + } + + public void enableControlFlag(Goal.Flag control) { +- this.disabledFlags.remove(control); ++ this.goalTypes.remove(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 6665ce5f48316e626907e6937d5ef1bc398a7ebd..51deb4455cac055ffa455e4f34aa30858d2fb448 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 +@@ -69,8 +69,10 @@ public class WrappedGoal extends Goal { + } + + @Override +- public EnumSet<Goal.Flag> getFlags() { ++ // Paper start - remove streams from pathfindergoalselector ++ public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() { + return this.goal.getFlags(); ++ // Paper end - remove streams from pathfindergoalselector + } + + public boolean isRunning() { |