diff --git a/leaf-server/minecraft-patches/features/0294-configurable-goal-update-interval.patch b/leaf-server/minecraft-patches/features/0294-configurable-goal-update-interval.patch new file mode 100644 index 000000000..7f370dfa5 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0294-configurable-goal-update-interval.patch @@ -0,0 +1,84 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Mon, 19 Jan 2026 14:08:30 +0900 +Subject: [PATCH] configurable goal update interval + +The alternative solution of Pufferfish's Throttle-goal-selector-during-inactive-ticking.patch + +diff --git a/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java b/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java +index 526e9538d617ae4db85f0b85533cc8c3c85566a2..60a0bb67330f8231eee89c55272041479c80e58e 100644 +--- a/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java ++++ b/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java +@@ -63,8 +63,10 @@ public class AvoidEntityGoal extends Goal { + this(mob, avoidClass, livingEntity -> true, maxDist, walkSpeedModifier, sprintSpeedModifier, predicateOnAvoidEntity); + } + ++ private final int leaf$internal = Math.max(1, reducedTickDelay(org.dreeam.leaf.config.modules.opt.EntityGoal.chanceAvoidEntity)); private int leaf$counter; // Leaf - configurable goal update interval + @Override + public boolean canUse() { ++ leaf$counter++; if (leaf$counter < leaf$internal) { return false; } else { leaf$counter = this.mob.random.nextInt(leaf$internal); } // Leaf - configurable goal update interval + this.toAvoid = getServerLevel(this.mob) + .getNearestEntity( + this.mob.level().getEntitiesOfClass(this.avoidClass, this.mob.getBoundingBox().inflate(this.maxDist, 3.0, this.maxDist), livingEntity -> true), +diff --git a/net/minecraft/world/entity/ai/goal/FollowParentGoal.java b/net/minecraft/world/entity/ai/goal/FollowParentGoal.java +index 3b158592d97f5ee2ad10b1da75101cb1669871a3..0d5b565dc7c8d5dd1394b31b388b7d39abaa935b 100644 +--- a/net/minecraft/world/entity/ai/goal/FollowParentGoal.java ++++ b/net/minecraft/world/entity/ai/goal/FollowParentGoal.java +@@ -18,8 +18,10 @@ public class FollowParentGoal extends Goal { + this.speedModifier = speedModifier; + } + ++ private final int leaf$internal = Math.max(1, reducedTickDelay(org.dreeam.leaf.config.modules.opt.EntityGoal.chanceFollowParent)); private int leaf$counter; // Leaf - configurable goal update interval + @Override + public boolean canUse() { ++ leaf$counter++; if (leaf$counter < leaf$internal) { return false; } else { leaf$counter = this.animal.random.nextInt(leaf$internal); } // Leaf - configurable goal update interval + if (this.animal.getAge() >= 0) { + return false; + } else { +diff --git a/net/minecraft/world/entity/ai/goal/TemptGoal.java b/net/minecraft/world/entity/ai/goal/TemptGoal.java +index db3f5164170a29570a2b050328b34c918b526a2a..c2c96dc8e023d6b949cd35a2bca83eca583a4460 100644 +--- a/net/minecraft/world/entity/ai/goal/TemptGoal.java ++++ b/net/minecraft/world/entity/ai/goal/TemptGoal.java +@@ -50,12 +50,14 @@ public class TemptGoal extends Goal { + 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. + } + ++ private final int leaf$internal = Math.max(1, reducedTickDelay(org.dreeam.leaf.config.modules.opt.EntityGoal.chanceTempt)); private int leaf$counter; // Leaf - configurable goal update interval + @Override + public boolean canUse() { + if (this.calmDown > 0) { + this.calmDown--; + return false; + } else { ++ if (!isRunning()) { leaf$counter++; if (leaf$counter < leaf$internal) { return false; } else { leaf$counter = this.mob.random.nextInt(leaf$internal); } } // Leaf - configurable goal update interval + // Paper start - optimise temptation lookups + final TargetingConditions rangeTargetingConditions = this.targetingConditions.range(this.mob.getAttributeValue(Attributes.TEMPT_RANGE)); + +diff --git a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java +index e8dc0eca01cf1a4eb1fe2a799e4dcbe391843318..bfa2781704a7b1ed4f000b8c5c5ef31d55924bd0 100644 +--- a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java ++++ b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java +@@ -35,7 +35,7 @@ public class NearestAttackableTargetGoal extends TargetG + ) { + super(mob, mustSee, mustReach); + this.targetType = targetType; +- this.randomInterval = reducedTickDelay(interval); ++ this.randomInterval = org.dreeam.leaf.config.modules.opt.EntityGoal.chanceTarget > 0 && interval <= 10 ? reducedTickDelay(org.dreeam.leaf.config.modules.opt.EntityGoal.chanceTarget) : reducedTickDelay(interval); // Leaf - configurable goal update interval + this.setFlags(EnumSet.of(Goal.Flag.TARGET)); + this.targetConditions = TargetingConditions.forCombat().range(this.getFollowDistance()).selector(selector); + } +diff --git a/net/minecraft/world/entity/monster/EnderMan.java b/net/minecraft/world/entity/monster/EnderMan.java +index 809933cf4eea9dcedae4195cfecfe392973e96b6..e3901f14d7414b03df30db8c877085769cc771f0 100644 +--- a/net/minecraft/world/entity/monster/EnderMan.java ++++ b/net/minecraft/world/entity/monster/EnderMan.java +@@ -587,8 +587,10 @@ public class EnderMan extends Monster implements NeutralMob { + this.startAggroTargetConditions = TargetingConditions.forCombat().range(this.getFollowDistance()).selector(this.isAngerInducing); + } + ++ private final int leaf$internal = Math.max(1, reducedTickDelay(org.dreeam.leaf.config.modules.opt.EntityGoal.chanceEndermanLookForPlayer)); private int leaf$counter; // Leaf - configurable goal update interval + @Override + public boolean canUse() { ++ leaf$counter++; if (leaf$counter < leaf$internal) { return false; } else { leaf$counter = this.mob.random.nextInt(leaf$internal); } // Leaf - configurable goal update interval + this.pendingTarget = getServerLevel(this.enderman).getNearestPlayer(this.startAggroTargetConditions.range(this.getFollowDistance()), this.enderman); + return this.pendingTarget != null; + } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/EntityGoal.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/EntityGoal.java new file mode 100644 index 000000000..c2a05201f --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/EntityGoal.java @@ -0,0 +1,49 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class EntityGoal extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName() + ".entity-goal"; + } + + public static int chanceTarget = -1; // only all <= 10 + public static int chanceFollowParent = -1; + public static int chanceAvoidEntity = -1; + public static int chanceTempt = -1; + public static int chanceEndermanLookForPlayer = -1; + + @Override + public void onLoaded() { + String path = getBasePath() + ".start-tick-chance"; + chanceTarget = config.getInt(path + ".nearest-attackable-target", -1); + chanceFollowParent = config.getInt(path + ".follow-parent", -1); + chanceAvoidEntity = config.getInt(path + ".avoid-entity", -1); + chanceTempt = config.getInt(path + ".temptation", -1); + chanceEndermanLookForPlayer = config.getInt(path + ".enderman-look-for-player", -1); + + // expect nearest_attackable_target + if (chanceFollowParent < 1) { + chanceFollowParent = 1; + } else { + chanceFollowParent *= 2; + } + if (chanceAvoidEntity < 1) { + chanceAvoidEntity = 1; + } else { + chanceAvoidEntity *= 2; + } + if (chanceTempt < 1) { + chanceTempt = 1; + } else { + chanceTempt *= 2; + } + if (chanceEndermanLookForPlayer < 1) { + chanceEndermanLookForPlayer = 1; + } else { + chanceEndermanLookForPlayer *= 2; + } + } +}