9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-27 02:49:19 +00:00

Paper PR: Optimise temptation lookups (#464)

This commit is contained in:
hayanesuru
2025-08-15 18:26:08 +09:00
committed by GitHub
parent 8c2b50bdb8
commit a1e9333dd5
2 changed files with 666 additions and 0 deletions

View File

@@ -0,0 +1,484 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: okx-code <okx@okx.sh>
Date: Thu, 6 Feb 2025 19:05:00 +0100
Subject: [PATCH] Paper PR: Optimise temptation lookups
Both TemptGoal and TemptingSensor recheck their validity each tick.
For this, they iterate all online players, checking their main and
offhand items against item tags.
This logic quickly explodes as each brain/goal selector queries all
players.
This patch attempts to optimise this behaviour by lazily caching the
results of the non-entity specific checks in a single BitSet.
Such cache is valid for a single tick but can be re-used by each tempt
goal or sensor sharing the same non-entity specific selection
predicates.
Original license: GPLv3
Original project: https://github.com/PaperMC/Paper
Paper pull request: https://github.com/PaperMC/Paper/pull/11942
diff --git a/io/papermc/paper/entity/temptation/GlobalTemptationLookup.java b/io/papermc/paper/entity/temptation/GlobalTemptationLookup.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f5cdfc538ba9aa6666c019df6706015234d7bae
--- /dev/null
+++ b/io/papermc/paper/entity/temptation/GlobalTemptationLookup.java
@@ -0,0 +1,91 @@
+package io.papermc.paper.entity.temptation;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+import java.util.function.Predicate;
+import net.minecraft.tags.ItemTags;
+import net.minecraft.world.entity.animal.HappyGhast;
+import net.minecraft.world.entity.animal.armadillo.ArmadilloAi;
+import net.minecraft.world.entity.animal.axolotl.AxolotlAi;
+import net.minecraft.world.entity.animal.camel.CamelAi;
+import net.minecraft.world.entity.animal.frog.FrogAi;
+import net.minecraft.world.entity.animal.goat.GoatAi;
+import net.minecraft.world.entity.animal.sniffer.SnifferAi;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.Items;
+
+/**
+ * The tempt state lookup holds onto cached temptation flags of players in the world.
+ */
+public class GlobalTemptationLookup {
+ private static int registeredPredicateCounter = 0;
+
+ public static final TemptationPredicate BEE_FOOD = register(stack -> stack.is(ItemTags.BEE_FOOD));
+ public static final TemptationPredicate CHICKEN_FOOD = register(stack -> stack.is(ItemTags.CHICKEN_FOOD));
+ public static final TemptationPredicate COW_FOOD = register(stack -> stack.is(ItemTags.COW_FOOD));
+ public static final TemptationPredicate PANDA_FOOD = register(stack -> stack.is(ItemTags.PANDA_FOOD));
+ public static final TemptationPredicate PIG_CARROT_ON_A_STICK = register(stack -> stack.is(Items.CARROT_ON_A_STICK));
+ public static final TemptationPredicate PIG = register(stack -> stack.is(ItemTags.PIG_FOOD));
+ public static final TemptationPredicate RABBIT_FOOD = register(stack -> stack.is(ItemTags.RABBIT_FOOD));
+ public static final TemptationPredicate SHEEP_FOOD = register(stack -> stack.is(ItemTags.SHEEP_FOOD));
+ public static final TemptationPredicate TURTLE_FOOD = register(stack -> stack.is(ItemTags.TURTLE_FOOD));
+ public static final TemptationPredicate HORSE_FOOD = register(stack -> stack.is(ItemTags.HORSE_TEMPT_ITEMS));
+ public static final TemptationPredicate LLAMA_TEMPT_ITEMS = register(stack -> stack.is(ItemTags.LLAMA_TEMPT_ITEMS));
+ public static final TemptationPredicate STRIDER_TEMPT_ITEMS = register(stack -> stack.is(ItemTags.STRIDER_TEMPT_ITEMS));
+ public static final TemptationPredicate CAT_FOOD = register(stack -> stack.is(ItemTags.CAT_FOOD));
+ public static final TemptationPredicate OCELOT_FOOD = register(itemStack -> itemStack.is(ItemTags.OCELOT_FOOD));
+ public static final TemptationPredicate AXOLOTL_TEMPTATIONS = register(AxolotlAi.getTemptations());
+ public static final TemptationPredicate GOAT_TEMPTATIONS = register(GoatAi.getTemptations());
+ public static final TemptationPredicate FROG_TEMPTATIONS = register(FrogAi.getTemptations());
+ public static final TemptationPredicate CAMEL_TEMPTATIONS = register(CamelAi.getTemptations());
+ public static final TemptationPredicate ARMADILLO_TEMPTATIONS = register(ArmadilloAi.getTemptations());
+ public static final TemptationPredicate SNIFFER_TEMPTATIONS = register(SnifferAi.getTemptations());
+ public static final TemptationPredicate HAPPY_GHAST_TEMPTATIONS = register(HappyGhast.IS_FOOD);
+
+ public record TemptationPredicate(int index, Predicate<ItemStack> predicate) implements Predicate<ItemStack> {
+
+ @Override
+ public boolean test(final ItemStack itemStack) {
+ return this.predicate.test(itemStack);
+ }
+ }
+
+ public static int indexFor(final Predicate<ItemStack> predicate) {
+ return predicate instanceof final TemptationPredicate temptationPredicate ? temptationPredicate.index() : -1;
+ }
+
+ private static TemptationPredicate register(final Predicate<ItemStack> predicate) {
+ final TemptationPredicate val = new TemptationPredicate(registeredPredicateCounter, predicate);
+ registeredPredicateCounter++;
+ return val;
+ }
+
+ private final List<BitSet> precalculatedTemptItems = new ArrayList<>();
+ private final BitSet calculatedThisTick = new BitSet();
+
+ {
+ for (int i = 0; i < registeredPredicateCounter; i++) {
+ this.precalculatedTemptItems.add(new BitSet());
+ }
+ }
+
+ public void reset() {
+ for (int i = 0; i < registeredPredicateCounter; i++) {
+ this.precalculatedTemptItems.get(i).clear();
+ }
+ this.calculatedThisTick.clear();
+ }
+
+ public boolean isCalculated(final int index) {
+ return this.calculatedThisTick.get(index);
+ }
+
+ public void setCalculated(final int index) {
+ this.calculatedThisTick.set(index);
+ }
+
+ public BitSet getBitSet(final int index) {
+ return this.precalculatedTemptItems.get(index);
+ }
+}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index a474a8bdda00c1dbea30f0981e38ea6188b84c7a..f839c1b040f51e536b575ed5906f85a0a374b37f 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -936,6 +936,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
io.papermc.paper.entity.activation.ActivationRange.activateEntities(this); // Paper - EAR
+ this.globalTemptationLookup.reset(); // Paper - optimise temptation lookups - reset global cache prior to next entity tick
if (org.dreeam.leaf.config.modules.opt.OptimizeDespawn.enabled && tickRateManager.runsNormally()) { despawnMap.tick(this, this.entityTickList); } // Leaf - optimize despawn
this.entityTickList
@@ -3038,4 +3039,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.lagCompensationTick = (System.nanoTime() - MinecraftServer.SERVER_INIT) / (java.util.concurrent.TimeUnit.MILLISECONDS.toNanos(50L));
}
// Paper end - lag compensation
+
+ // Paper start - optimise temptation lookups
+ private final io.papermc.paper.entity.temptation.GlobalTemptationLookup globalTemptationLookup = new io.papermc.paper.entity.temptation.GlobalTemptationLookup(); // Paper - Optimise TemptGoal
+ public final io.papermc.paper.entity.temptation.GlobalTemptationLookup getTemptGoalLookup() {
+ return globalTemptationLookup;
+ }
+ // Paper end - optimise temptation lookups
}
diff --git a/net/minecraft/world/entity/ai/goal/TemptGoal.java b/net/minecraft/world/entity/ai/goal/TemptGoal.java
index a805c9426630c2c46db9d0dd536f1d16769395d3..0c9a355344463ea85a3b724d22d53638b1e42394 100644
--- a/net/minecraft/world/entity/ai/goal/TemptGoal.java
+++ b/net/minecraft/world/entity/ai/goal/TemptGoal.java
@@ -30,6 +30,7 @@ public class TemptGoal extends Goal {
private final Predicate<ItemStack> items;
private final boolean canScare;
private final double stopDistance;
+ private final int globalTemptationLookupIndex; // Paper - optimise temptation checks
public TemptGoal(PathfinderMob mob, double speedModifier, Predicate<ItemStack> items, boolean canScare) {
this((Mob)mob, speedModifier, items, canScare, 2.5);
@@ -40,13 +41,14 @@ public class TemptGoal extends Goal {
}
TemptGoal(Mob mob, double speedModifier, Predicate<ItemStack> items, boolean canScare, double stopDistance) {
+ this.globalTemptationLookupIndex = io.papermc.paper.entity.temptation.GlobalTemptationLookup.indexFor(items); // Paper - optimise temptation checks
this.mob = mob;
this.speedModifier = speedModifier;
this.items = items;
this.canScare = canScare;
this.stopDistance = stopDistance;
this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
- this.targetingConditions = TEMPT_TARGETING.copy().selector((entity, level) -> this.shouldFollow(entity));
+ this.targetingConditions = globalTemptationLookupIndex >= 0 ? TEMPT_TARGETING.copy() : TEMPT_TARGETING.copy().selector((entity, level) -> this.shouldFollow(entity)); // Paper - optimise temptation checks - skip selector if we have a lookup index.
}
@Override
@@ -55,8 +57,41 @@ public class TemptGoal extends Goal {
this.calmDown--;
return false;
} else {
+ // Paper start - optimise temptation lookups
+ final TargetingConditions rangeTargetingConditions = this.targetingConditions.range(this.mob.getAttributeValue(Attributes.TEMPT_RANGE));
+
+ if (this.globalTemptationLookupIndex != -1) {
+ final net.minecraft.server.level.ServerLevel level = getServerLevel(this.mob);
+ final io.papermc.paper.entity.temptation.GlobalTemptationLookup lookup = level.getTemptGoalLookup();
+ final java.util.BitSet lookupBitSet = lookup.getBitSet(this.globalTemptationLookupIndex);
+ final java.util.List<net.minecraft.server.level.ServerPlayer> players = level.players();
+ // Check if the lookup needs to be computed this tick. Do so for all players if needed.
+ if (!lookup.isCalculated(this.globalTemptationLookupIndex)) {
+ for (int i = 0; i < players.size(); i++) {
+ lookupBitSet.set(i, shouldFollow(players.get(i)));
+ }
+ lookup.setCalculated(this.globalTemptationLookupIndex);
+ }
+ double d = -1.0;
+ net.minecraft.server.level.ServerPlayer nearestPlayer = null;
+ // Only iterate over players that passed #shouldFollow either in the prior computation or another goals canUse check.
+ for (int i = lookupBitSet.nextSetBit(0); i >= 0; i = lookupBitSet.nextSetBit(i + 1)) {
+ final net.minecraft.server.level.ServerPlayer player = players.get(i);
+ if (rangeTargetingConditions.test(level, this.mob, player)) {
+ final double d1 = player.distanceToSqr(this.mob.getX(), this.mob.getY(), this.mob.getZ());
+ if (d == -1.0 || d1 < d) {
+ d = d1;
+ nearestPlayer = player;
+ }
+ }
+ }
+ this.player = nearestPlayer;
+ } else {
+ // Default case for non-optimized / non vanilla tempt goal predicates.
+ // Paper end - optimise temptation lookups
this.player = getServerLevel(this.mob)
.getNearestPlayer(this.targetingConditions.range(this.mob.getAttributeValue(Attributes.TEMPT_RANGE)), this.mob);
+ } // Paper - optimise temptation lookups
// CraftBukkit start
if (this.player != null) {
org.bukkit.event.entity.EntityTargetLivingEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(this.mob, this.player, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TEMPT);
diff --git a/net/minecraft/world/entity/ai/sensing/SensorType.java b/net/minecraft/world/entity/ai/sensing/SensorType.java
index 817bcaa1c09d9bd0b9856d06a8969ef583de6778..884398702e7810490743bbe74aa42fac9c661147 100644
--- a/net/minecraft/world/entity/ai/sensing/SensorType.java
+++ b/net/minecraft/world/entity/ai/sensing/SensorType.java
@@ -39,18 +39,18 @@ public class SensorType<U extends Sensor<?>> {
public static final SensorType<AdultSensor> NEAREST_ADULT = register("nearest_adult", AdultSensor::new);
public static final SensorType<AdultSensor> NEAREST_ADULT_ANY_TYPE = register("nearest_adult_any_type", AdultSensorAnyType::new);
public static final SensorType<AxolotlAttackablesSensor> AXOLOTL_ATTACKABLES = register("axolotl_attackables", AxolotlAttackablesSensor::new);
- public static final SensorType<TemptingSensor> AXOLOTL_TEMPTATIONS = register("axolotl_temptations", () -> new TemptingSensor(AxolotlAi.getTemptations()));
- public static final SensorType<TemptingSensor> GOAT_TEMPTATIONS = register("goat_temptations", () -> new TemptingSensor(GoatAi.getTemptations()));
- public static final SensorType<TemptingSensor> FROG_TEMPTATIONS = register("frog_temptations", () -> new TemptingSensor(FrogAi.getTemptations()));
- public static final SensorType<TemptingSensor> CAMEL_TEMPTATIONS = register("camel_temptations", () -> new TemptingSensor(CamelAi.getTemptations()));
+ public static final SensorType<TemptingSensor> AXOLOTL_TEMPTATIONS = register("axolotl_temptations", () -> new TemptingSensor(io.papermc.paper.entity.temptation.GlobalTemptationLookup.AXOLOTL_TEMPTATIONS)); // Paper - optimise temptation lookups
+ public static final SensorType<TemptingSensor> GOAT_TEMPTATIONS = register("goat_temptations", () -> new TemptingSensor(io.papermc.paper.entity.temptation.GlobalTemptationLookup.GOAT_TEMPTATIONS)); // Paper - optimise temptation lookups
+ public static final SensorType<TemptingSensor> FROG_TEMPTATIONS = register("frog_temptations", () -> new TemptingSensor(io.papermc.paper.entity.temptation.GlobalTemptationLookup.FROG_TEMPTATIONS)); // Paper - optimise temptation lookups
+ public static final SensorType<TemptingSensor> CAMEL_TEMPTATIONS = register("camel_temptations", () -> new TemptingSensor(io.papermc.paper.entity.temptation.GlobalTemptationLookup.CAMEL_TEMPTATIONS)); // Paper - optimise temptation lookups
public static final SensorType<TemptingSensor> ARMADILLO_TEMPTATIONS = register(
- "armadillo_temptations", () -> new TemptingSensor(ArmadilloAi.getTemptations())
+ "armadillo_temptations", () -> new TemptingSensor(io.papermc.paper.entity.temptation.GlobalTemptationLookup.ARMADILLO_TEMPTATIONS) // Paper - optimise temptation lookups
);
- public static final SensorType<TemptingSensor> HAPPY_GHAST_TEMPTATIONS = register("happy_ghast_temptations", () -> new TemptingSensor(HappyGhast.IS_FOOD));
+ public static final SensorType<TemptingSensor> HAPPY_GHAST_TEMPTATIONS = register("happy_ghast_temptations", () -> new TemptingSensor(io.papermc.paper.entity.temptation.GlobalTemptationLookup.HAPPY_GHAST_TEMPTATIONS)); // Paper - optimise temptation lookups
public static final SensorType<FrogAttackablesSensor> FROG_ATTACKABLES = register("frog_attackables", FrogAttackablesSensor::new);
public static final SensorType<IsInWaterSensor> IS_IN_WATER = register("is_in_water", IsInWaterSensor::new);
public static final SensorType<WardenEntitySensor> WARDEN_ENTITY_SENSOR = register("warden_entity_sensor", WardenEntitySensor::new);
- public static final SensorType<TemptingSensor> SNIFFER_TEMPTATIONS = register("sniffer_temptations", () -> new TemptingSensor(SnifferAi.getTemptations()));
+ public static final SensorType<TemptingSensor> SNIFFER_TEMPTATIONS = register("sniffer_temptations", () -> new TemptingSensor(io.papermc.paper.entity.temptation.GlobalTemptationLookup.SNIFFER_TEMPTATIONS)); // Paper - optimise temptation lookups
public static final SensorType<BreezeAttackEntitySensor> BREEZE_ATTACK_ENTITY_SENSOR = register(
"breeze_attack_entity_sensor", BreezeAttackEntitySensor::new
);
diff --git a/net/minecraft/world/entity/ai/sensing/TemptingSensor.java b/net/minecraft/world/entity/ai/sensing/TemptingSensor.java
index 5919192968d7272bbf119f248def7e96a1ea359b..a88b644c7480bfa66526c697455771eb7f86ad67 100644
--- a/net/minecraft/world/entity/ai/sensing/TemptingSensor.java
+++ b/net/minecraft/world/entity/ai/sensing/TemptingSensor.java
@@ -19,8 +19,10 @@ import net.minecraft.world.item.ItemStack;
public class TemptingSensor extends Sensor<PathfinderMob> {
private static final TargetingConditions TEMPT_TARGETING = TargetingConditions.forNonCombat().ignoreLineOfSight();
private final Predicate<ItemStack> temptations;
+ private final int globalTemptationLookupIndex; // Paper - optimise temptation lookups
public TemptingSensor(Predicate<ItemStack> temptations) {
+ this.globalTemptationLookupIndex = io.papermc.paper.entity.temptation.GlobalTemptationLookup.indexFor(temptations); // Paper - optimise temptation lookups
this.temptations = temptations;
}
@@ -28,6 +30,38 @@ public class TemptingSensor extends Sensor<PathfinderMob> {
protected void doTick(ServerLevel level, PathfinderMob entity) {
Brain<?> brain = entity.getBrain();
TargetingConditions targetingConditions = TEMPT_TARGETING.copy().range((float)entity.getAttributeValue(Attributes.TEMPT_RANGE));
+ // Paper start - optimise temptation lookups - on update, ensure below diff filters correctly
+ Player targetPlayer;
+ if (this.globalTemptationLookupIndex != -1) {
+ final io.papermc.paper.entity.temptation.GlobalTemptationLookup lookup = level.getTemptGoalLookup();
+ final java.util.BitSet lookupBitSet = lookup.getBitSet(this.globalTemptationLookupIndex);
+ final java.util.List<net.minecraft.server.level.ServerPlayer> players = level.players();
+ // Check if the lookup needs to be computed this tick. Do so for all players if needed.
+ if (!lookup.isCalculated(this.globalTemptationLookupIndex)) {
+ for (int i = 0; i < players.size(); i++) {
+ final net.minecraft.server.level.ServerPlayer serverPlayer = players.get(i);
+ lookupBitSet.set(i, net.minecraft.world.entity.EntitySelector.NO_SPECTATORS.test(serverPlayer) && this.playerHoldingTemptation(serverPlayer)); // check on update
+ }
+ lookup.setCalculated(this.globalTemptationLookupIndex);
+ }
+ double d = -1.0;
+ net.minecraft.server.level.ServerPlayer nearestPlayer = null;
+ // Only iterate over players that passed #shouldFollow either in the prior computation or another goals canUse check.
+ for (int i = lookupBitSet.nextSetBit(0); i >= 0; i = lookupBitSet.nextSetBit(i + 1)) {
+ final net.minecraft.server.level.ServerPlayer player = players.get(i);
+ if (targetingConditions.test(level, entity, player) && !entity.hasPassenger(player)) { // check on update - consider non passengers
+ final double d1 = player.distanceToSqr(entity.getX(), entity.getY(), entity.getZ());
+ if (d == -1.0 || d1 < d) {
+ d = d1;
+ nearestPlayer = player;
+ }
+ }
+ }
+ targetPlayer = nearestPlayer;
+ } else {
+ // Default case for non-optimized / non vanilla tempt goal predicates. Sorting the entire list is completely useless, but none of the vanilla logic uses this path now so
+ // less diff and easier for updates.
+ // Paper end - optimise temptation lookups
// Leaf start - Remove streams in TemptingSensor
List<net.minecraft.server.level.ServerPlayer> allPlayers = level.players();
List<Player> list = new java.util.ArrayList<>();
@@ -40,10 +74,12 @@ public class TemptingSensor extends Sensor<PathfinderMob> {
}
}
// Leaf end - Remove streams in TemptingSensor
-
- if (!list.isEmpty()) {
- list.sort(Comparator.comparingDouble(entity::distanceToSqr)); // Leaf - Remove streams in TemptingSensor
- Player player = list.get(0);
+ // Paper start - optimise temptation lookups
+ targetPlayer = list.isEmpty() ? null : list.getFirst();
+ }
+ if (targetPlayer != null) {
+ Player player = targetPlayer;
+ // Paper end - optimise temptation lookups
// CraftBukkit start
org.bukkit.event.entity.EntityTargetLivingEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(
entity, player, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TEMPT
diff --git a/net/minecraft/world/entity/animal/AbstractCow.java b/net/minecraft/world/entity/animal/AbstractCow.java
index 61e7300bbf272398b2faebf5e537d9c2ddedc6d6..832a9a56fbf870e4242d4a59ab58ed43881fdccd 100644
--- a/net/minecraft/world/entity/animal/AbstractCow.java
+++ b/net/minecraft/world/entity/animal/AbstractCow.java
@@ -40,7 +40,7 @@ public abstract class AbstractCow extends Animal {
this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new PanicGoal(this, 2.0));
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0));
- this.goalSelector.addGoal(3, new TemptGoal(this, 1.25, itemStack -> level().purpurConfig.cowFeedMushrooms > 0 && (itemStack.is(net.minecraft.world.level.block.Blocks.RED_MUSHROOM.asItem()) || itemStack.is(net.minecraft.world.level.block.Blocks.BROWN_MUSHROOM.asItem())) || itemStack.is(ItemTags.COW_FOOD), false)); // Purpur - Cows eat mushrooms
+ this.goalSelector.addGoal(3, new TemptGoal(this, 1.25, io.papermc.paper.entity.temptation.GlobalTemptationLookup.COW_FOOD, false)); // Paper - optimise temptation lookups
this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25));
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0));
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
diff --git a/net/minecraft/world/entity/animal/Bee.java b/net/minecraft/world/entity/animal/Bee.java
index eadc64f497631c048cf6a7d72e6f692763c98c6c..fb3123dc9fb8a49d2ef85bcf622a59f3cd591a19 100644
--- a/net/minecraft/world/entity/animal/Bee.java
+++ b/net/minecraft/world/entity/animal/Bee.java
@@ -240,7 +240,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
this.goalSelector.addGoal(0, new Bee.BeeAttackGoal(this, 1.4F, true));
this.goalSelector.addGoal(1, new Bee.BeeEnterHiveGoal());
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0));
- this.goalSelector.addGoal(3, new TemptGoal(this, 1.25, stack -> stack.is(ItemTags.BEE_FOOD), false));
+ this.goalSelector.addGoal(3, new TemptGoal(this, 1.25, io.papermc.paper.entity.temptation.GlobalTemptationLookup.BEE_FOOD, false)); // Paper - optimise temptation lookups
this.goalSelector.addGoal(3, new Bee.ValidateHiveGoal());
this.goalSelector.addGoal(3, new Bee.ValidateFlowerGoal());
this.beePollinateGoal = new Bee.BeePollinateGoal();
diff --git a/net/minecraft/world/entity/animal/Cat.java b/net/minecraft/world/entity/animal/Cat.java
index 14dfac12438b02ca9172a494a5730b5bf094aef1..4c0292f725845bb30782fbb6297f0e943945480c 100644
--- a/net/minecraft/world/entity/animal/Cat.java
+++ b/net/minecraft/world/entity/animal/Cat.java
@@ -148,7 +148,7 @@ public class Cat extends TamableAnimal {
@Override
protected void registerGoals() {
- this.temptGoal = new Cat.CatTemptGoal(this, 0.6, stack -> stack.is(ItemTags.CAT_FOOD), true);
+ this.temptGoal = new Cat.CatTemptGoal(this, 0.6, io.papermc.paper.entity.temptation.GlobalTemptationLookup.CAT_FOOD, true); // Paper - optimise temptation lookups
this.goalSelector.addGoal(1, new FloatGoal(this));
this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new TamableAnimal.TamableAnimalPanicGoal(1.5));
diff --git a/net/minecraft/world/entity/animal/Chicken.java b/net/minecraft/world/entity/animal/Chicken.java
index f908d57776c533b6d42b8f3cd4da088a7ec03ed1..a5821ab8eab48fc2cd29eba857c9a624eb59290d 100644
--- a/net/minecraft/world/entity/animal/Chicken.java
+++ b/net/minecraft/world/entity/animal/Chicken.java
@@ -129,7 +129,7 @@ public class Chicken extends Animal {
this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
//this.goalSelector.addGoal(1, new PanicGoal(this, 1.4)); // Purpur - Chickens can retaliate - moved down
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0));
- this.goalSelector.addGoal(3, new TemptGoal(this, 1.0, itemStack -> itemStack.is(ItemTags.CHICKEN_FOOD), false));
+ this.goalSelector.addGoal(3, new TemptGoal(this, 1.0, io.papermc.paper.entity.temptation.GlobalTemptationLookup.CHICKEN_FOOD, false)); // Paper - optimise temptation lookups
this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.1));
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0));
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
diff --git a/net/minecraft/world/entity/animal/Ocelot.java b/net/minecraft/world/entity/animal/Ocelot.java
index c83d47caf8572e7f1375829f7dcb1b3b4bbf9ed0..b30cdff64f6608f20dacf36d781b00a7ba55f093 100644
--- a/net/minecraft/world/entity/animal/Ocelot.java
+++ b/net/minecraft/world/entity/animal/Ocelot.java
@@ -139,7 +139,7 @@ public class Ocelot extends Animal {
@Override
protected void registerGoals() {
- this.temptGoal = new Ocelot.OcelotTemptGoal(this, 0.6, itemStack -> itemStack.is(ItemTags.OCELOT_FOOD), true);
+ this.temptGoal = new Ocelot.OcelotTemptGoal(this, 0.6, io.papermc.paper.entity.temptation.GlobalTemptationLookup.OCELOT_FOOD, true); // Paper - optimise temptation lookups
this.goalSelector.addGoal(1, new FloatGoal(this));
this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(3, this.temptGoal);
diff --git a/net/minecraft/world/entity/animal/Panda.java b/net/minecraft/world/entity/animal/Panda.java
index c4cfcab5be629e83c0418bcb9ef1062bc8459af9..92069cde887889e1fcb35d597445d6102512756a 100644
--- a/net/minecraft/world/entity/animal/Panda.java
+++ b/net/minecraft/world/entity/animal/Panda.java
@@ -319,7 +319,7 @@ public class Panda extends Animal {
this.goalSelector.addGoal(2, new Panda.PandaPanicGoal(this, 2.0));
this.goalSelector.addGoal(2, new Panda.PandaBreedGoal(this, 1.0));
this.goalSelector.addGoal(3, new Panda.PandaAttackGoal(this, 1.2F, true));
- this.goalSelector.addGoal(4, new TemptGoal(this, 1.0, stack -> stack.is(ItemTags.PANDA_FOOD), false));
+ this.goalSelector.addGoal(4, new TemptGoal(this, 1.0, io.papermc.paper.entity.temptation.GlobalTemptationLookup.PANDA_FOOD, false)); // Paper - optimise temptation lookups
this.goalSelector.addGoal(6, new Panda.PandaAvoidGoal<>(this, Player.class, 8.0F, 2.0, 2.0));
this.goalSelector.addGoal(6, new Panda.PandaAvoidGoal<>(this, Monster.class, 4.0F, 2.0, 2.0));
this.goalSelector.addGoal(7, new Panda.PandaSitGoal());
diff --git a/net/minecraft/world/entity/animal/Pig.java b/net/minecraft/world/entity/animal/Pig.java
index fbda6b378f9fc8fe97f4282326f86ea9633a5c98..a5ad5c232c778baa76ce023daa497e8dce3aadf2 100644
--- a/net/minecraft/world/entity/animal/Pig.java
+++ b/net/minecraft/world/entity/animal/Pig.java
@@ -118,8 +118,8 @@ public class Pig extends Animal implements ItemSteerable {
this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new PanicGoal(this, 1.25));
this.goalSelector.addGoal(3, new BreedGoal(this, 1.0));
- this.goalSelector.addGoal(4, new TemptGoal(this, 1.2, itemStack -> itemStack.is(Items.CARROT_ON_A_STICK), false));
- this.goalSelector.addGoal(4, new TemptGoal(this, 1.2, itemStack -> itemStack.is(ItemTags.PIG_FOOD), false));
+ this.goalSelector.addGoal(4, new TemptGoal(this, 1.2, io.papermc.paper.entity.temptation.GlobalTemptationLookup.PIG_CARROT_ON_A_STICK, false)); // Paper - optimise temptation lookups
+ this.goalSelector.addGoal(4, new TemptGoal(this, 1.2, io.papermc.paper.entity.temptation.GlobalTemptationLookup.PIG, false)); // Paper - optimise temptation lookups
this.goalSelector.addGoal(5, new FollowParentGoal(this, 1.1));
this.goalSelector.addGoal(6, new WaterAvoidingRandomStrollGoal(this, 1.0));
this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 6.0F));
diff --git a/net/minecraft/world/entity/animal/Rabbit.java b/net/minecraft/world/entity/animal/Rabbit.java
index c0a63a8d1a3c33d9d151aaf3131ea583f4f48588..311dea7e2231e00ff439220a47dd4449b85d0d50 100644
--- a/net/minecraft/world/entity/animal/Rabbit.java
+++ b/net/minecraft/world/entity/animal/Rabbit.java
@@ -181,7 +181,7 @@ public class Rabbit extends Animal {
this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
this.goalSelector.addGoal(1, new Rabbit.RabbitPanicGoal(this, 2.2));
this.goalSelector.addGoal(2, new BreedGoal(this, 0.8));
- this.goalSelector.addGoal(3, new TemptGoal(this, 1.0, stack -> stack.is(ItemTags.RABBIT_FOOD), false));
+ this.goalSelector.addGoal(3, new TemptGoal(this, 1.0, io.papermc.paper.entity.temptation.GlobalTemptationLookup.RABBIT_FOOD, false)); // Paper - optimise temptation lookups
this.goalSelector.addGoal(4, new Rabbit.RabbitAvoidEntityGoal<>(this, Player.class, 8.0F, 2.2, 2.2));
this.goalSelector.addGoal(4, new Rabbit.RabbitAvoidEntityGoal<>(this, Wolf.class, 10.0F, 2.2, 2.2));
this.goalSelector.addGoal(4, new Rabbit.RabbitAvoidEntityGoal<>(this, Monster.class, 4.0F, 2.2, 2.2));
diff --git a/net/minecraft/world/entity/animal/Turtle.java b/net/minecraft/world/entity/animal/Turtle.java
index 0065a43871605c8d2a8b29d1aed174aeed8be3b2..59800c502c46c82cedde0f67e3848d7ba02cc479 100644
--- a/net/minecraft/world/entity/animal/Turtle.java
+++ b/net/minecraft/world/entity/animal/Turtle.java
@@ -195,7 +195,7 @@ public class Turtle extends Animal {
this.goalSelector.addGoal(0, new Turtle.TurtlePanicGoal(this, 1.2));
this.goalSelector.addGoal(1, new Turtle.TurtleBreedGoal(this, 1.0));
this.goalSelector.addGoal(1, new Turtle.TurtleLayEggGoal(this, 1.0));
- this.goalSelector.addGoal(2, new TemptGoal(this, 1.1, itemStack -> itemStack.is(ItemTags.TURTLE_FOOD), false));
+ this.goalSelector.addGoal(2, new TemptGoal(this, 1.1, io.papermc.paper.entity.temptation.GlobalTemptationLookup.TURTLE_FOOD, false)); // Paper - optimise temptation lookups
this.goalSelector.addGoal(3, new Turtle.TurtleGoToWaterGoal(this, 1.0));
this.goalSelector.addGoal(4, new Turtle.TurtleGoHomeGoal(this, 1.0));
this.goalSelector.addGoal(7, new Turtle.TurtleTravelGoal(this, 1.0));
diff --git a/net/minecraft/world/entity/animal/horse/AbstractHorse.java b/net/minecraft/world/entity/animal/horse/AbstractHorse.java
index d3b21d7c54349045c29df09602bda9d7a7eaec52..3ce7e02b91d40170e040554cd601167b66ba602a 100644
--- a/net/minecraft/world/entity/animal/horse/AbstractHorse.java
+++ b/net/minecraft/world/entity/animal/horse/AbstractHorse.java
@@ -199,7 +199,7 @@ public abstract class AbstractHorse extends Animal implements HasCustomInventory
protected void addBehaviourGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
- this.goalSelector.addGoal(3, new TemptGoal(this, 1.25, stack -> stack.is(ItemTags.HORSE_TEMPT_ITEMS), false));
+ this.goalSelector.addGoal(3, new TemptGoal(this, 1.25, io.papermc.paper.entity.temptation.GlobalTemptationLookup.HORSE_FOOD, false)); // Paper - optimise temptation lookups
}
@Override
diff --git a/net/minecraft/world/entity/animal/horse/Llama.java b/net/minecraft/world/entity/animal/horse/Llama.java
index 455a84ffa16152137409777e1fddbab5a21bd57d..36a0dcf58dd2e7c014c2d2924941a3773f3bd3a4 100644
--- a/net/minecraft/world/entity/animal/horse/Llama.java
+++ b/net/minecraft/world/entity/animal/horse/Llama.java
@@ -222,7 +222,7 @@ public class Llama extends AbstractChestedHorse implements RangedAttackMob {
this.goalSelector.addGoal(3, new RangedAttackGoal(this, 1.25, 40, 20.0F));
this.goalSelector.addGoal(3, new PanicGoal(this, 1.2));
this.goalSelector.addGoal(4, new BreedGoal(this, 1.0));
- this.goalSelector.addGoal(5, new TemptGoal(this, 1.25, itemStack -> itemStack.is(ItemTags.LLAMA_TEMPT_ITEMS), false));
+ this.goalSelector.addGoal(5, new TemptGoal(this, 1.25, io.papermc.paper.entity.temptation.GlobalTemptationLookup.LLAMA_TEMPT_ITEMS, false)); // Paper - optimise temptation lookups
this.goalSelector.addGoal(6, new FollowParentGoal(this, 1.0));
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 0.7));
this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 6.0F));
diff --git a/net/minecraft/world/entity/animal/sheep/Sheep.java b/net/minecraft/world/entity/animal/sheep/Sheep.java
index d0b40c1186328b501e1847f29283023b81eeddc3..96a71acbed44df67c7d0da36b601c8e54159fd3f 100644
--- a/net/minecraft/world/entity/animal/sheep/Sheep.java
+++ b/net/minecraft/world/entity/animal/sheep/Sheep.java
@@ -115,7 +115,7 @@ public class Sheep extends Animal implements Shearable {
this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new PanicGoal(this, 1.25));
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0));
- this.goalSelector.addGoal(3, new TemptGoal(this, 1.1, stack -> stack.is(ItemTags.SHEEP_FOOD), false));
+ this.goalSelector.addGoal(3, new TemptGoal(this, 1.1, io.papermc.paper.entity.temptation.GlobalTemptationLookup.SHEEP_FOOD, false)); // Paper - optimise temptation lookups
this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.1));
this.goalSelector.addGoal(5, this.eatBlockGoal);
this.goalSelector.addGoal(6, new WaterAvoidingRandomStrollGoal(this, 1.0));
diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java
index 4a4f3ed5dbc6b1934b2901c3a21597ba783b1806..8fcdc4452360a1909b788eda8fe2509b0b4f6de8 100644
--- a/net/minecraft/world/entity/monster/Strider.java
+++ b/net/minecraft/world/entity/monster/Strider.java
@@ -179,7 +179,7 @@ public class Strider extends Animal implements ItemSteerable {
this.goalSelector.addGoal(1, new PanicGoal(this, 1.65));
this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0));
- this.temptGoal = new TemptGoal(this, 1.4, itemStack -> itemStack.is(ItemTags.STRIDER_TEMPT_ITEMS), false);
+ this.temptGoal = new TemptGoal(this, 1.4, io.papermc.paper.entity.temptation.GlobalTemptationLookup.STRIDER_TEMPT_ITEMS, false); // Paper - optimise temptation lookups
this.goalSelector.addGoal(3, this.temptGoal);
this.goalSelector.addGoal(4, new Strider.StriderGoToLavaGoal(this, 1.0));
this.goalSelector.addGoal(5, new FollowParentGoal(this, 1.0));

View File

@@ -0,0 +1,182 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: hayanesuru <hayanesuru@outlook.jp>
Date: Fri, 15 Aug 2025 16:16:08 +0900
Subject: [PATCH] fix temptation lookups
diff --git a/io/papermc/paper/entity/temptation/GlobalTemptationLookup.java b/io/papermc/paper/entity/temptation/GlobalTemptationLookup.java
index 5f5cdfc538ba9aa6666c019df6706015234d7bae..e7c0fd59b4b64b3e60887ddae63e2f1a4ddd752f 100644
--- a/io/papermc/paper/entity/temptation/GlobalTemptationLookup.java
+++ b/io/papermc/paper/entity/temptation/GlobalTemptationLookup.java
@@ -1,9 +1,12 @@
package io.papermc.paper.entity.temptation;
-import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.function.Predicate;
+
+import it.unimi.dsi.fastutil.objects.ObjectArrays;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.ItemTags;
import net.minecraft.world.entity.animal.HappyGhast;
import net.minecraft.world.entity.animal.armadillo.ArmadilloAi;
@@ -24,6 +27,7 @@ public class GlobalTemptationLookup {
public static final TemptationPredicate BEE_FOOD = register(stack -> stack.is(ItemTags.BEE_FOOD));
public static final TemptationPredicate CHICKEN_FOOD = register(stack -> stack.is(ItemTags.CHICKEN_FOOD));
public static final TemptationPredicate COW_FOOD = register(stack -> stack.is(ItemTags.COW_FOOD));
+ public static final TemptationPredicate COW_FOOD_MUSHROOM = register(stack -> stack.is(Items.RED_MUSHROOM) || stack.is(Items.BROWN_MUSHROOM) || stack.is(ItemTags.COW_FOOD));
public static final TemptationPredicate PANDA_FOOD = register(stack -> stack.is(ItemTags.PANDA_FOOD));
public static final TemptationPredicate PIG_CARROT_ON_A_STICK = register(stack -> stack.is(Items.CARROT_ON_A_STICK));
public static final TemptationPredicate PIG = register(stack -> stack.is(ItemTags.PIG_FOOD));
@@ -61,20 +65,34 @@ public class GlobalTemptationLookup {
return val;
}
- private final List<BitSet> precalculatedTemptItems = new ArrayList<>();
+ private final BitSet[] precalculatedTemptItems;
private final BitSet calculatedThisTick = new BitSet();
+ private static final ServerPlayer[] EMPTY_PLAYERS = {};
+ private ServerPlayer[] players = EMPTY_PLAYERS;
{
- for (int i = 0; i < registeredPredicateCounter; i++) {
- this.precalculatedTemptItems.add(new BitSet());
+ precalculatedTemptItems = new BitSet[registeredPredicateCounter];
+ for (int i = 0; i < precalculatedTemptItems.length; i++) {
+ this.precalculatedTemptItems[i] = new BitSet();
}
}
- public void reset() {
+ public void tick(final ServerLevel world) {
for (int i = 0; i < registeredPredicateCounter; i++) {
- this.precalculatedTemptItems.get(i).clear();
+ this.precalculatedTemptItems[i].clear();
+ this.calculatedThisTick.clear();
+ }
+
+ int j = 0;
+ final ServerPlayer[] array = world.players().toArray(EMPTY_PLAYERS);
+ for (int i = 0; i < array.length; i++) {
+ final ServerPlayer p = array[i];
+ if (!p.isSpectator() && p.isAlive()) {
+ array[j] = p;
+ j++;
+ }
}
- this.calculatedThisTick.clear();
+ this.players = ObjectArrays.setLength(array, j);
}
public boolean isCalculated(final int index) {
@@ -86,6 +104,10 @@ public class GlobalTemptationLookup {
}
public BitSet getBitSet(final int index) {
- return this.precalculatedTemptItems.get(index);
+ return this.precalculatedTemptItems[index];
+ }
+
+ public ServerPlayer[] players() {
+ return this.players;
}
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index f839c1b040f51e536b575ed5906f85a0a374b37f..03cedc642cccf9d92f5755bec367f53f084c81f6 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -936,7 +936,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
io.papermc.paper.entity.activation.ActivationRange.activateEntities(this); // Paper - EAR
- this.globalTemptationLookup.reset(); // Paper - optimise temptation lookups - reset global cache prior to next entity tick
+ this.globalTemptationLookup.tick(this); // Paper - optimise temptation lookups - reset global cache prior to next entity tick
if (org.dreeam.leaf.config.modules.opt.OptimizeDespawn.enabled && tickRateManager.runsNormally()) { despawnMap.tick(this, this.entityTickList); } // Leaf - optimize despawn
this.entityTickList
diff --git a/net/minecraft/world/entity/ai/goal/TemptGoal.java b/net/minecraft/world/entity/ai/goal/TemptGoal.java
index 0c9a355344463ea85a3b724d22d53638b1e42394..7cb94eb75a1de0cef02d4953ee5896bd5e3c7abd 100644
--- a/net/minecraft/world/entity/ai/goal/TemptGoal.java
+++ b/net/minecraft/world/entity/ai/goal/TemptGoal.java
@@ -64,21 +64,25 @@ public class TemptGoal extends Goal {
final net.minecraft.server.level.ServerLevel level = getServerLevel(this.mob);
final io.papermc.paper.entity.temptation.GlobalTemptationLookup lookup = level.getTemptGoalLookup();
final java.util.BitSet lookupBitSet = lookup.getBitSet(this.globalTemptationLookupIndex);
- final java.util.List<net.minecraft.server.level.ServerPlayer> players = level.players();
+ final net.minecraft.server.level.ServerPlayer[] players = lookup.players();
// Check if the lookup needs to be computed this tick. Do so for all players if needed.
if (!lookup.isCalculated(this.globalTemptationLookupIndex)) {
- for (int i = 0; i < players.size(); i++) {
- lookupBitSet.set(i, shouldFollow(players.get(i)));
+ for (int i = 0; i < players.length; i++) {
+ lookupBitSet.set(i, shouldFollow(players[i]));
}
lookup.setCalculated(this.globalTemptationLookupIndex);
}
double d = -1.0;
net.minecraft.server.level.ServerPlayer nearestPlayer = null;
// Only iterate over players that passed #shouldFollow either in the prior computation or another goals canUse check.
+ final Mob mob = this.mob;
+ final double mobX = mob.getX();
+ final double mobY = mob.getY();
+ final double mobZ = mob.getZ();
for (int i = lookupBitSet.nextSetBit(0); i >= 0; i = lookupBitSet.nextSetBit(i + 1)) {
- final net.minecraft.server.level.ServerPlayer player = players.get(i);
- if (rangeTargetingConditions.test(level, this.mob, player)) {
- final double d1 = player.distanceToSqr(this.mob.getX(), this.mob.getY(), this.mob.getZ());
+ final net.minecraft.server.level.ServerPlayer player = players[i];
+ if (rangeTargetingConditions.test(level, mob, player)) {
+ final double d1 = player.distanceToSqr(mobX, mobY, mobZ);
if (d == -1.0 || d1 < d) {
d = d1;
nearestPlayer = player;
diff --git a/net/minecraft/world/entity/ai/sensing/TemptingSensor.java b/net/minecraft/world/entity/ai/sensing/TemptingSensor.java
index a88b644c7480bfa66526c697455771eb7f86ad67..58c22828b62323ed99b7b1a9e3c1196aaf619786 100644
--- a/net/minecraft/world/entity/ai/sensing/TemptingSensor.java
+++ b/net/minecraft/world/entity/ai/sensing/TemptingSensor.java
@@ -35,11 +35,11 @@ public class TemptingSensor extends Sensor<PathfinderMob> {
if (this.globalTemptationLookupIndex != -1) {
final io.papermc.paper.entity.temptation.GlobalTemptationLookup lookup = level.getTemptGoalLookup();
final java.util.BitSet lookupBitSet = lookup.getBitSet(this.globalTemptationLookupIndex);
- final java.util.List<net.minecraft.server.level.ServerPlayer> players = level.players();
+ final net.minecraft.server.level.ServerPlayer[] players = lookup.players();
// Check if the lookup needs to be computed this tick. Do so for all players if needed.
if (!lookup.isCalculated(this.globalTemptationLookupIndex)) {
- for (int i = 0; i < players.size(); i++) {
- final net.minecraft.server.level.ServerPlayer serverPlayer = players.get(i);
+ for (int i = 0; i < players.length; i++) {
+ final net.minecraft.server.level.ServerPlayer serverPlayer = players[i];
lookupBitSet.set(i, net.minecraft.world.entity.EntitySelector.NO_SPECTATORS.test(serverPlayer) && this.playerHoldingTemptation(serverPlayer)); // check on update
}
lookup.setCalculated(this.globalTemptationLookupIndex);
@@ -47,10 +47,13 @@ public class TemptingSensor extends Sensor<PathfinderMob> {
double d = -1.0;
net.minecraft.server.level.ServerPlayer nearestPlayer = null;
// Only iterate over players that passed #shouldFollow either in the prior computation or another goals canUse check.
+ final double entityX = entity.getX();
+ final double entityY = entity.getY();
+ final double entityZ = entity.getZ();
for (int i = lookupBitSet.nextSetBit(0); i >= 0; i = lookupBitSet.nextSetBit(i + 1)) {
- final net.minecraft.server.level.ServerPlayer player = players.get(i);
+ final net.minecraft.server.level.ServerPlayer player = players[i];
if (targetingConditions.test(level, entity, player) && !entity.hasPassenger(player)) { // check on update - consider non passengers
- final double d1 = player.distanceToSqr(entity.getX(), entity.getY(), entity.getZ());
+ final double d1 = player.distanceToSqr(entityX, entityY, entityZ);
if (d == -1.0 || d1 < d) {
d = d1;
nearestPlayer = player;
diff --git a/net/minecraft/world/entity/animal/AbstractCow.java b/net/minecraft/world/entity/animal/AbstractCow.java
index 832a9a56fbf870e4242d4a59ab58ed43881fdccd..48441154ff7cbfea714ba88ac31910d0419644a8 100644
--- a/net/minecraft/world/entity/animal/AbstractCow.java
+++ b/net/minecraft/world/entity/animal/AbstractCow.java
@@ -40,7 +40,7 @@ public abstract class AbstractCow extends Animal {
this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new PanicGoal(this, 2.0));
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0));
- this.goalSelector.addGoal(3, new TemptGoal(this, 1.25, io.papermc.paper.entity.temptation.GlobalTemptationLookup.COW_FOOD, false)); // Paper - optimise temptation lookups
+ this.goalSelector.addGoal(3, new TemptGoal(this, 1.25, level().purpurConfig.cowFeedMushrooms > 0 ? io.papermc.paper.entity.temptation.GlobalTemptationLookup.COW_FOOD_MUSHROOM : io.papermc.paper.entity.temptation.GlobalTemptationLookup.COW_FOOD, false)); // Purpur - Cows eat mushrooms // Paper - optimise temptation lookups
this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25));
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0));
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));