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..73254d7e8 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,6 +30,8 @@ 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 { @@ -36,6 +39,7 @@ public final class MovementAction implements WorldAction { private final SFVec3i blockPosition; // Corner jumps normally require you to stand closer to the block to jump private final boolean walkFewTicksNoJump; + private final PathConstraint pathConstraint; private boolean didLook; private boolean lockYRot; private boolean wasStill; @@ -88,7 +92,21 @@ public void tick(BotConnection connection) { var previousYRot = clientEntity.getYRot(); clientEntity.lookAt(EntityAnchorArgument.Anchor.EYES, targetMiddleBlock); - clientEntity.setXRot(0); + + var xRot = 0f; + var yRot = 0f; + + if (pathConstraint.yRotJitter().min() < pathConstraint.yRotJitter().max()) { + yRot = + ThreadLocalRandom.current().nextFloat((float) pathConstraint.yRotJitter().min(), (float) pathConstraint.yRotJitter().max()); + } + if (pathConstraint.xRotJitter().min() < pathConstraint.xRotJitter().max()) { + xRot = + ThreadLocalRandom.current().nextFloat((float) pathConstraint.xRotJitter().min(), (float) pathConstraint.xRotJitter().max()); + } + + clientEntity.setYRot(MathHelper.wrapDegrees(clientEntity.getYRot() + yRot)); + clientEntity.setXRot(clientEntity.getXRot() + xRot); 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..58413f843 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 yRotJitter() { + return delegate().yRotJitter(); + } + + @Override + default MinMaxProperty.DataLayout xRotJitter() { + return delegate().xRotJitter(); + } + 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..276a04488 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 yRotJitter(); + + MinMaxProperty.DataLayout xRotJitter(); } 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..b0768cd8e 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 yRotJitter; + private final MinMaxProperty.DataLayout xRotJitter; 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 yRotJitter, + MinMaxProperty.DataLayout xRotJitter) { 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.yRotJitter = yRotJitter; + this.xRotJitter = xRotJitter; } 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.Y_ROT_JITTER), + settingsSource.get(PathfindingSettings.X_ROT_JITTER) ); } @@ -213,6 +222,16 @@ public boolean disablePruning() { return disablePruning; } + @Override + public MinMaxProperty.DataLayout yRotJitter() { + return yRotJitter; + } + + @Override + public MinMaxProperty.DataLayout xRotJitter() { + return xRotJitter; + } + 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..a2b96d3cb 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 Y_ROT_JITTER = + ImmutableMinMaxProperty.builder() + .sourceType(SettingsSource.Bot.INSTANCE) + .namespace(NAMESPACE) + .key("y-rot-jitter") + .minValue(-180) + .maxValue(180) + .minEntry(ImmutableMinMaxPropertyEntry.builder() + .uiName("Y Rot Jitter Minimum") + .description("Minimum random horizontal angle offset") + .defaultValue(-25) + .build()) + .maxEntry(ImmutableMinMaxPropertyEntry.builder() + .uiName("Y Rot Jitter Maximum") + .description("Maximum random horizontal angle offset") + .defaultValue(25) + .build()) + .build(); + public static final MinMaxProperty X_ROT_JITTER = + ImmutableMinMaxProperty.builder() + .sourceType(SettingsSource.Bot.INSTANCE) + .namespace(NAMESPACE) + .key("x-rot-jitter") + .minValue(-90) + .maxValue(90) + .minEntry(ImmutableMinMaxPropertyEntry.builder() + .uiName("X Rot Jitter Minimum") + .description("Minimum random vertical angle offset") + .defaultValue(-4) + .build()) + .maxEntry(ImmutableMinMaxPropertyEntry.builder() + .uiName("X Rot 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..edcc23262 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), // yRotJitter + new MinMaxProperty.DataLayout(-4, 4) // xRotJitter ); private TestPathConstraint() {}