From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Spottedleaf 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/PathfinderGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoal.java index b505c23c57a4b84faf5906c6295455b4720c4426..5c32cbe81c47fcb9ae347faa6fc007c5d28d79bf 100644 --- a/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoal.java +++ b/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoal.java @@ -1,10 +1,12 @@ package net.minecraft.world.entity.ai.goal; +import com.destroystokyo.paper.util.set.OptimizedSmallEnumSet; // Paper - remove streams from pathfindergoalselector import java.util.EnumSet; public abstract class PathfinderGoal { - private final EnumSet a = EnumSet.noneOf(PathfinderGoal.Type.class); + private final EnumSet a = EnumSet.noneOf(PathfinderGoal.Type.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 OptimizedSmallEnumSet goalTypes = new OptimizedSmallEnumSet<>(PathfinderGoal.Type.class); // Paper - remove streams from pathfindergoalselector public PathfinderGoal() {} @@ -28,16 +30,20 @@ public abstract class PathfinderGoal { public void e() {} public void a(EnumSet enumset) { - this.a.clear(); - this.a.addAll(enumset); + // Paper start - remove streams from pathfindergoalselector + this.goalTypes.clear(); + this.goalTypes.addAllUnchecked(enumset); + // Paper end - remove streams from pathfindergoalselector } public String toString() { return this.getClass().getSimpleName(); } - public EnumSet i() { - return this.a; + // Paper start - remove streams from pathfindergoalselector + public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet getGoalTypes() { + return this.goalTypes; + // Paper end - remove streams from pathfindergoalselector } public static enum Type { diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalSelector.java index 8c234c09a4d9ada83e36e3cdbcc1f2f5c6202f28..385cd079e264a7e66e91ab3b70b90afb59688dcd 100644 --- a/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalSelector.java +++ b/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalSelector.java @@ -1,8 +1,10 @@ package net.minecraft.world.entity.ai.goal; +import com.destroystokyo.paper.util.set.OptimizedSmallEnumSet; // Paper - remove streams from pathfindergoalselector import com.google.common.collect.Sets; import java.util.EnumMap; import java.util.EnumSet; +import java.util.Iterator; // Paper - remove streams from pathfindergoalselector import java.util.Map; import java.util.Set; import java.util.function.Supplier; @@ -28,7 +30,8 @@ public class PathfinderGoalSelector { private final Map c = new EnumMap(PathfinderGoal.Type.class); private final Set d = Sets.newLinkedHashSet(); private Set getTasks() { return d; }// Paper - OBFHELPER private final Supplier e; - private final EnumSet f = EnumSet.noneOf(PathfinderGoal.Type.class); + private final EnumSet f = EnumSet.noneOf(PathfinderGoal.Type.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 OptimizedSmallEnumSet goalTypes = new OptimizedSmallEnumSet<>(PathfinderGoal.Type.class); // Paper - remove streams from pathfindergoalselector private int g = 3;private int getTickRate() { return g; } // Paper - OBFHELPER private int curRate;private int getCurRate() { return curRate; } private void incRate() { this.curRate++; } // Paper TODO @@ -56,35 +59,38 @@ public class PathfinderGoalSelector { // Paper end public void a(PathfinderGoal pathfindergoal) { - this.d.stream().filter((pathfindergoalwrapped) -> { - return pathfindergoalwrapped.j() == pathfindergoal; - }).filter(PathfinderGoalWrapped::g).forEach(PathfinderGoalWrapped::d); - this.d.removeIf((pathfindergoalwrapped) -> { - return pathfindergoalwrapped.j() == pathfindergoal; - }); + // Paper start - remove streams from pathfindergoalselector + for (Iterator iterator = this.d.iterator(); iterator.hasNext();) { + PathfinderGoalWrapped goalWrapped = iterator.next(); + if (goalWrapped.j() != pathfindergoal) { + continue; + } + if (goalWrapped.g()) { + goalWrapped.d(); + } + iterator.remove(); + } + // Paper end - remove streams from pathfindergoalselector } + private static final PathfinderGoal.Type[] PATHFINDER_GOAL_TYPES = PathfinderGoal.Type.values(); // Paper - remove streams from pathfindergoalselector + public void doTick() { GameProfilerFiller gameprofilerfiller = (GameProfilerFiller) this.e.get(); gameprofilerfiller.enter("goalCleanup"); - this.d().filter((pathfindergoalwrapped) -> { - boolean flag; - - if (pathfindergoalwrapped.g()) { - Stream stream = pathfindergoalwrapped.i().stream(); - EnumSet enumset = this.f; - - this.f.getClass(); - if (!stream.anyMatch(enumset::contains) && pathfindergoalwrapped.b()) { - flag = false; - return flag; - } + // Paper start - remove streams from pathfindergoalselector + for (Iterator iterator = this.d.iterator(); iterator.hasNext();) { + PathfinderGoalWrapped wrappedGoal = iterator.next(); + if (!wrappedGoal.g()) { + continue; } - - flag = true; - return flag; - }).forEach(PathfinderGoal::d); + if (!this.goalTypes.hasCommonElements(wrappedGoal.getGoalTypes()) && wrappedGoal.b()) { + continue; + } + wrappedGoal.d(); + } + // Paper end - remove streams from pathfindergoalselector this.c.forEach((pathfindergoal_type, pathfindergoalwrapped) -> { if (!pathfindergoalwrapped.g()) { this.c.remove(pathfindergoal_type); @@ -93,30 +99,58 @@ public class PathfinderGoalSelector { }); gameprofilerfiller.exit(); gameprofilerfiller.enter("goalUpdate"); - this.d.stream().filter((pathfindergoalwrapped) -> { - return !pathfindergoalwrapped.g(); - }).filter((pathfindergoalwrapped) -> { - Stream stream = pathfindergoalwrapped.i().stream(); - EnumSet enumset = this.f; - - this.f.getClass(); - return stream.noneMatch(enumset::contains); - }).filter((pathfindergoalwrapped) -> { - return pathfindergoalwrapped.i().stream().allMatch((pathfindergoal_type) -> { - return ((PathfinderGoalWrapped) this.c.getOrDefault(pathfindergoal_type, PathfinderGoalSelector.b)).a(pathfindergoalwrapped); - }); - }).filter(PathfinderGoalWrapped::a).forEach((pathfindergoalwrapped) -> { - pathfindergoalwrapped.i().forEach((pathfindergoal_type) -> { - PathfinderGoalWrapped pathfindergoalwrapped1 = (PathfinderGoalWrapped) this.c.getOrDefault(pathfindergoal_type, PathfinderGoalSelector.b); - - pathfindergoalwrapped1.d(); - this.c.put(pathfindergoal_type, pathfindergoalwrapped); - }); - pathfindergoalwrapped.c(); - }); + // Paper start - remove streams from pathfindergoalselector + goal_update_loop: for (Iterator iterator = this.d.iterator(); iterator.hasNext();) { + PathfinderGoalWrapped wrappedGoal = iterator.next(); + if (wrappedGoal.g()) { + continue; + } + + OptimizedSmallEnumSet wrappedGoalSet = wrappedGoal.getGoalTypes(); + + if (this.goalTypes.hasCommonElements(wrappedGoalSet)) { + continue; + } + + long iterator1 = wrappedGoalSet.getBackingSet(); + int wrappedGoalSize = wrappedGoalSet.size(); + for (int i = 0; i < wrappedGoalSize; ++i) { + PathfinderGoal.Type type = PATHFINDER_GOAL_TYPES[Long.numberOfTrailingZeros(iterator1)]; + iterator1 ^= com.destroystokyo.paper.util.math.IntegerUtil.getTrailingBit(iterator1); + PathfinderGoalWrapped wrapped = this.c.getOrDefault(type, PathfinderGoalSelector.b); + if (!wrapped.a(wrappedGoal)) { + continue goal_update_loop; + } + } + + if (!wrappedGoal.a()) { + continue; + } + + iterator1 = wrappedGoalSet.getBackingSet(); + wrappedGoalSize = wrappedGoalSet.size(); + for (int i = 0; i < wrappedGoalSize; ++i) { + PathfinderGoal.Type type = PATHFINDER_GOAL_TYPES[Long.numberOfTrailingZeros(iterator1)]; + iterator1 ^= com.destroystokyo.paper.util.math.IntegerUtil.getTrailingBit(iterator1); + PathfinderGoalWrapped wrapped = this.c.getOrDefault(type, PathfinderGoalSelector.b); + + wrapped.d(); + this.c.put(type, wrappedGoal); + } + + wrappedGoal.c(); + } + // Paper end - remove streams from pathfindergoalselector gameprofilerfiller.exit(); gameprofilerfiller.enter("goalTick"); - this.d().forEach(PathfinderGoalWrapped::e); + // Paper start - remove streams from pathfindergoalselector + for (Iterator iterator = this.d.iterator(); iterator.hasNext();) { + PathfinderGoalWrapped wrappedGoal = iterator.next(); + if (wrappedGoal.g()) { + wrappedGoal.e(); + } + } + // Paper end - remove streams from pathfindergoalselector gameprofilerfiller.exit(); } @@ -125,11 +159,11 @@ public class PathfinderGoalSelector { } public void a(PathfinderGoal.Type pathfindergoal_type) { - this.f.add(pathfindergoal_type); + this.goalTypes.addUnchecked(pathfindergoal_type); // Paper - remove streams from pathfindergoalselector } public void b(PathfinderGoal.Type pathfindergoal_type) { - this.f.remove(pathfindergoal_type); + this.goalTypes.removeUnchecked(pathfindergoal_type); // Paper - remove streams from pathfindergoalselector } public void a(PathfinderGoal.Type pathfindergoal_type, boolean flag) { diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalWrapped.java b/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalWrapped.java index 7bb531e47668cf445083c4dedb03ccafe6a9c96b..8c8e39d35fb56aa6cf7d456adab01dff5d13a60d 100644 --- a/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalWrapped.java +++ b/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalWrapped.java @@ -59,9 +59,10 @@ public class PathfinderGoalWrapped extends PathfinderGoal { this.a.a(enumset); } - @Override - public EnumSet i() { - return this.a.i(); + // Paper start - remove streams from pathfindergoalselector + public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet getGoalTypes() { + return this.a.getGoalTypes(); + // Paper end - remove streams from pathfindergoalselector } public boolean isRunning() { return this.g(); } // Paper - OBFHELPER