diff --git a/mod/src/main/java/com/soulfiremc/server/pathfinding/execution/MovementAction.java b/mod/src/main/java/com/soulfiremc/server/pathfinding/execution/MovementAction.java index 8613c1daa..f7736e144 100644 --- a/mod/src/main/java/com/soulfiremc/server/pathfinding/execution/MovementAction.java +++ b/mod/src/main/java/com/soulfiremc/server/pathfinding/execution/MovementAction.java @@ -20,6 +20,7 @@ import com.google.common.math.DoubleMath; import com.soulfiremc.server.bot.BotConnection; import com.soulfiremc.server.pathfinding.SFVec3i; +import com.soulfiremc.server.pathfinding.graph.constraint.PathConstraint; import com.soulfiremc.server.util.MathHelper; import com.soulfiremc.server.util.VectorHelper; import lombok.RequiredArgsConstructor; @@ -29,13 +30,18 @@ import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; +import java.util.concurrent.ThreadLocalRandom; + @Slf4j @RequiredArgsConstructor public final class MovementAction implements WorldAction { + private static final ThreadLocalRandom rand = ThreadLocalRandom.current(); + private static final double STEP_HEIGHT = 0.6; private final SFVec3i blockPosition; // Corner jumps normally require you to stand closer to the block to jump private final boolean walkFewTicksNoJump; + private final PathConstraint settings; private boolean didLook; private boolean lockYRot; private boolean wasStill; @@ -88,7 +94,19 @@ public void tick(BotConnection connection) { var previousYRot = clientEntity.getYRot(); clientEntity.lookAt(EntityAnchorArgument.Anchor.EYES, targetMiddleBlock); - clientEntity.setXRot(0); + + var xRotation = 0f; + var yRotation = 0f; + + if (settings.yawJitter().min() < settings.yawJitter().max()) { + yRotation = rand.nextFloat((float) settings.yawJitter().min(), (float) settings.yawJitter().max()); + } + if (settings.pitchJitter().min() < settings.pitchJitter().max()) { + xRotation = rand.nextFloat((float) settings.pitchJitter().min(), (float) settings.pitchJitter().max()); + } + + clientEntity.setYRot(clientEntity.getYRot() + yRotation); + clientEntity.setXRot(xRotation); var newYRot = clientEntity.getYRot(); var yRotDifference = Math.abs(MathHelper.wrapDegrees(newYRot - previousYRot)); diff --git a/mod/src/main/java/com/soulfiremc/server/pathfinding/execution/PathExecutor.java b/mod/src/main/java/com/soulfiremc/server/pathfinding/execution/PathExecutor.java index f71d8a3eb..88b57fe04 100644 --- a/mod/src/main/java/com/soulfiremc/server/pathfinding/execution/PathExecutor.java +++ b/mod/src/main/java/com/soulfiremc/server/pathfinding/execution/PathExecutor.java @@ -58,13 +58,13 @@ private PathExecutor( this.pathCompletionFuture = pathCompletionFuture; } - private static List repositionIfNeeded(List actions, SFVec3i from, boolean requiresRepositioning) { + private static List repositionIfNeeded(List actions, SFVec3i from, boolean requiresRepositioning, LiveRouteFinder findPath) { if (!requiresRepositioning) { return actions; } var repositionActions = new ArrayList(); - repositionActions.add(new MovementAction(from, false)); + repositionActions.add(new MovementAction(from, false, findPath.pathConstraint)); repositionActions.addAll(actions); return repositionActions; @@ -118,7 +118,7 @@ public void submitForPathCalculation(boolean isInitial) { SFHelpers.mustSupply(() -> switch (routeSearchResult.routeSearchResult()) { case RouteFinder.FoundRouteResult foundRouteResult -> () -> { - var newActions = repositionIfNeeded(foundRouteResult.actions(), routeSearchResult.start(), isInitial); + var newActions = repositionIfNeeded(foundRouteResult.actions(), routeSearchResult.start(), isInitial, this.findPath); if (newActions.isEmpty()) { log.info("We're already at the goal!"); return; @@ -133,7 +133,7 @@ public void submitForPathCalculation(boolean isInitial) { }; case RouteFinder.NoRouteFoundResult _ -> throw new IllegalStateException("No route found to the goal!"); case RouteFinder.PartialRouteResult partialRouteResult -> () -> { - var newActions = addRecalculate(repositionIfNeeded(partialRouteResult.actions(), routeSearchResult.start(), isInitial)); + var newActions = addRecalculate(repositionIfNeeded(partialRouteResult.actions(), routeSearchResult.start(), isInitial, this.findPath)); if (newActions.isEmpty()) { log.info("We're already at the goal!"); return; diff --git a/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/actions/SimpleMovement.java b/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/actions/SimpleMovement.java index 8af0d6442..77bb0befe 100644 --- a/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/actions/SimpleMovement.java +++ b/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/actions/SimpleMovement.java @@ -324,7 +324,7 @@ public List getInstructions(MinecraftGraph graph, SFVec3i nod actions.add(new BlockPlaceAction(floorBlock, blockPlaceAgainstData)); } - actions.add(new MovementAction(absoluteTargetFeetBlock, diagonal)); + actions.add(new MovementAction(absoluteTargetFeetBlock, diagonal, graph.pathConstraint())); if (interactablePassage != null) { actions.add(new InteractBlockAction(interactablePassage.block(), interactablePassage.interactFace(), false)); } diff --git a/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/constraint/DelegatePathConstraint.java b/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/constraint/DelegatePathConstraint.java index e61d874b6..3496132bb 100644 --- a/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/constraint/DelegatePathConstraint.java +++ b/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/constraint/DelegatePathConstraint.java @@ -20,6 +20,7 @@ import com.soulfiremc.server.pathfinding.SFVec3i; import com.soulfiremc.server.pathfinding.graph.DiagonalCollisionCalculator; import com.soulfiremc.server.pathfinding.graph.GraphInstructions; +import com.soulfiremc.server.settings.property.MinMaxProperty; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.state.BlockState; @@ -99,5 +100,15 @@ default boolean disablePruning() { return delegate().disablePruning(); } + @Override + default MinMaxProperty.DataLayout yawJitter() { + return delegate().yawJitter(); + } + + @Override + default MinMaxProperty.DataLayout pitchJitter() { + return delegate().pitchJitter(); + } + PathConstraint delegate(); } diff --git a/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/constraint/PathConstraint.java b/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/constraint/PathConstraint.java index 851234e2e..f09a142fc 100644 --- a/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/constraint/PathConstraint.java +++ b/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/constraint/PathConstraint.java @@ -20,6 +20,7 @@ import com.soulfiremc.server.pathfinding.SFVec3i; import com.soulfiremc.server.pathfinding.graph.DiagonalCollisionCalculator; import com.soulfiremc.server.pathfinding.graph.GraphInstructions; +import com.soulfiremc.server.settings.property.MinMaxProperty; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.state.BlockState; @@ -58,4 +59,8 @@ public interface PathConstraint { /// Returns whether pruning of the pathfinding search space is disabled. boolean disablePruning(); + + MinMaxProperty.DataLayout yawJitter(); + + MinMaxProperty.DataLayout pitchJitter(); } diff --git a/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/constraint/PathConstraintImpl.java b/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/constraint/PathConstraintImpl.java index f2ad033db..4b83c9d31 100644 --- a/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/constraint/PathConstraintImpl.java +++ b/mod/src/main/java/com/soulfiremc/server/pathfinding/graph/constraint/PathConstraintImpl.java @@ -25,6 +25,7 @@ import com.soulfiremc.server.settings.lib.BotSettingsSource; import com.soulfiremc.server.settings.lib.InstanceSettingsSource; import com.soulfiremc.server.settings.lib.SettingsSource; +import com.soulfiremc.server.settings.property.MinMaxProperty; import com.soulfiremc.server.util.SFBlockHelpers; import com.soulfiremc.server.util.SFItemHelpers; import com.soulfiremc.server.util.structs.CachedLazyObject; @@ -56,6 +57,8 @@ public final class PathConstraintImpl implements PathConstraint { private final int placeBlockPenalty; private final int expireTimeout; private final boolean disablePruning; + private final MinMaxProperty.DataLayout yawJitter; + private final MinMaxProperty.DataLayout pitchJitter; private final CachedLazyObject> unfriendlyEntities = new CachedLazyObject<>(this::getUnfriendlyEntitiesExpensive, 10, TimeUnit.SECONDS); public PathConstraintImpl( @@ -68,7 +71,9 @@ public PathConstraintImpl( int breakBlockPenalty, int placeBlockPenalty, int expireTimeout, - boolean disablePruning) { + boolean disablePruning, + MinMaxProperty.DataLayout yawJitter, + MinMaxProperty.DataLayout pitchJitter) { this.entity = entity; this.levelHeightAccessor = levelHeightAccessor; this.allowBreakingUndiggable = allowBreakingUndiggable; @@ -79,6 +84,8 @@ public PathConstraintImpl( this.placeBlockPenalty = placeBlockPenalty; this.expireTimeout = expireTimeout; this.disablePruning = disablePruning; + this.yawJitter = yawJitter; + this.pitchJitter = yawJitter; } public PathConstraintImpl(BotConnection botConnection) { @@ -103,7 +110,9 @@ public PathConstraintImpl( settingsSource.get(PathfindingSettings.BREAK_BLOCK_PENALTY), settingsSource.get(PathfindingSettings.PLACE_BLOCK_PENALTY), settingsSource.get(PathfindingSettings.EXPIRE_TIMEOUT), - settingsSource.get(PathfindingSettings.DISABLE_PRUNING) + settingsSource.get(PathfindingSettings.DISABLE_PRUNING), + settingsSource.get(PathfindingSettings.YAW_JITTER), + settingsSource.get(PathfindingSettings.PITCH_JITTER) ); } @@ -213,6 +222,16 @@ public boolean disablePruning() { return disablePruning; } + @Override + public MinMaxProperty.DataLayout yawJitter() { + return yawJitter; + } + + @Override + public MinMaxProperty.DataLayout pitchJitter() { + return pitchJitter; + } + private List getUnfriendlyEntitiesExpensive() { if (entity == null) { return List.of(); diff --git a/mod/src/main/java/com/soulfiremc/server/settings/instance/PathfindingSettings.java b/mod/src/main/java/com/soulfiremc/server/settings/instance/PathfindingSettings.java index a1113c3a8..82007ef54 100644 --- a/mod/src/main/java/com/soulfiremc/server/settings/instance/PathfindingSettings.java +++ b/mod/src/main/java/com/soulfiremc/server/settings/instance/PathfindingSettings.java @@ -19,10 +19,7 @@ import com.soulfiremc.server.settings.lib.SettingsObject; import com.soulfiremc.server.settings.lib.SettingsSource; -import com.soulfiremc.server.settings.property.BooleanProperty; -import com.soulfiremc.server.settings.property.ImmutableBooleanProperty; -import com.soulfiremc.server.settings.property.ImmutableIntProperty; -import com.soulfiremc.server.settings.property.IntProperty; +import com.soulfiremc.server.settings.property.*; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -109,4 +106,40 @@ public final class PathfindingSettings implements SettingsObject { .description("Disable periodic pruning of the pathfinding search space (may use more memory)") .defaultValue(false) .build(); + public static final MinMaxProperty YAW_JITTER = + ImmutableMinMaxProperty.builder() + .sourceType(SettingsSource.Bot.INSTANCE) + .namespace(NAMESPACE) + .key("yaw-jitter") + .minValue(-180) + .maxValue(180) + .minEntry(ImmutableMinMaxPropertyEntry.builder() + .uiName("Yaw Jitter Minimum") + .description("Minimum random horizontal angle offset") + .defaultValue(-25) + .build()) + .maxEntry(ImmutableMinMaxPropertyEntry.builder() + .uiName("Yaw Jitter Maximum") + .description("Maximum random horizontal angle offset") + .defaultValue(25) + .build()) + .build(); + public static final MinMaxProperty PITCH_JITTER = + ImmutableMinMaxProperty.builder() + .sourceType(SettingsSource.Bot.INSTANCE) + .namespace(NAMESPACE) + .key("pitch-jitter") + .minValue(-90) + .maxValue(90) + .minEntry(ImmutableMinMaxPropertyEntry.builder() + .uiName("Pitch Jitter Minimum") + .description("Minimum random vertical angle offset") + .defaultValue(-4) + .build()) + .maxEntry(ImmutableMinMaxPropertyEntry.builder() + .uiName("Pitch Jitter Maximum") + .description("Maximum random vertical angle offset") + .defaultValue(4) + .build()) + .build(); } diff --git a/mod/src/test/java/com/soulfiremc/test/utils/TestPathConstraint.java b/mod/src/test/java/com/soulfiremc/test/utils/TestPathConstraint.java index e8b061620..6f3bad499 100644 --- a/mod/src/test/java/com/soulfiremc/test/utils/TestPathConstraint.java +++ b/mod/src/test/java/com/soulfiremc/test/utils/TestPathConstraint.java @@ -20,6 +20,7 @@ import com.soulfiremc.server.pathfinding.graph.constraint.DelegatePathConstraint; import com.soulfiremc.server.pathfinding.graph.constraint.PathConstraint; import com.soulfiremc.server.pathfinding.graph.constraint.PathConstraintImpl; +import com.soulfiremc.server.settings.property.MinMaxProperty; import org.jspecify.annotations.NonNull; public final class TestPathConstraint implements DelegatePathConstraint { @@ -35,7 +36,9 @@ public final class TestPathConstraint implements DelegatePathConstraint { 2, // breakBlockPenalty 5, // placeBlockPenalty 180, // expireTimeout - false // disablePruning + false, // disablePruning + new MinMaxProperty.DataLayout(-25, 25), // yawJitter + new MinMaxProperty.DataLayout(-4, 4) // pitchJitter ); private TestPathConstraint() {}