Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,48 @@ private static WalkerState processWalk(WorldPoint target, int distance) {
return processWalk(target, distance, 0);
}

private static final int MAX_RECALC_ATTEMPTS = 15;

/**
* Checks whether the walker has exceeded its maximum allowed path recalculation attempts.
* When the limit is reached, checks if the player is close enough to the target to treat
* as arrived (common with world-map clicks and quest targets on unwalkable tiles).
*
* @param attempts the current number of recalculation attempts
* @param target the destination world point
* @param distance the acceptable arrival distance
* @return ARRIVED if close enough, UNREACHABLE if too far, or null if under the limit
*/
private static WalkerState checkRecalcLimit(int attempts, WorldPoint target, int distance) {
if (attempts < MAX_RECALC_ATTEMPTS) {
return null;
}
int closeDist = Rs2Player.getWorldLocation().distanceTo(target);
if (closeDist <= Math.max(distance, 5)) {
log.info("[Walker] Exceeded max recalc but close enough (dist={}), treating as arrived", closeDist);
setTarget(null);
return WalkerState.ARRIVED;
}
log.warn("[Walker] Exceeded max recalc attempts ({}), giving up (dist={})", MAX_RECALC_ATTEMPTS, closeDist);
setTarget(null);
return WalkerState.UNREACHABLE;
}

/**
* Core walk loop that iteratively follows the calculated path to the target.
* Handles stall detection, partial path retries, and off-path recovery with a
* bounded recalculation limit to prevent infinite looping.
*
* @param target the destination world point
* @param distance acceptable distance threshold to consider arrival
* @param partialRetries number of partial-path retries already consumed
*/
private static WalkerState processWalk(WorldPoint target, int distance, int partialRetries) {
if (debug) {
return WalkerState.EXIT;
}
int recalcAttempts = 0;
while (true) {
try {
if (!Microbot.isLoggedIn()) {
setTarget(null);
Expand Down Expand Up @@ -331,6 +369,17 @@ private static WalkerState processWalk(WorldPoint target, int distance, int part

checkIfStuck();
if (isStuckTooLong()) {
// If we're adjacent to an unwalkable target tile (NPC, object, wall),
// the player is as close as they can get — treat as arrived instead of
// burning recalc attempts on a tile we can never stand on.
int distNow = Rs2Player.getWorldLocation().distanceTo(target);
LocalPoint lp = LocalPoint.fromWorld(Microbot.getClient().getTopLevelWorldView(), target);
if (distNow <= 1 && !Rs2Tile.isWalkable(lp)) {
log.info("[Walker] Adjacent to unwalkable target {} (dist={}), treating as arrived", target, distNow);
setTarget(null);
return WalkerState.ARRIVED;
}

long sinceMoved = System.currentTimeMillis() - lastMovedTimeMs;
long threshold = stallThresholdMs();
Telemetry.recordStallRecalc(sinceMoved, Rs2Player.getWorldLocation());
Expand All @@ -340,7 +389,11 @@ private static WalkerState processWalk(WorldPoint target, int distance, int part
lastMovedTimeMs = System.currentTimeMillis();
stuckCount = 0;
setTarget(target);
return processWalk(target, distance, partialRetries);
WalkerState recalcResult = checkRecalcLimit(++recalcAttempts, target, distance);
if (recalcResult != null) {
return recalcResult;
}
continue;
}
if (stuckCount > 10) {
var reachable = Rs2Tile.getReachableTilesFromTile(Rs2Player.getWorldLocation(), 5).keySet();
Expand Down Expand Up @@ -588,7 +641,8 @@ private static WalkerState processWalk(WorldPoint target, int distance, int part
log.info("[Walker] Walked partial path ({} tiles remaining), retrying from current position (attempt {}/3)",
finalDist, partialRetries + 1);
recalculatePath();
return processWalk(target, distance, partialRetries + 1);
partialRetries++;
continue;
}
log.info("[Walker] Walked partial path, exhausted retries. final distance to target: {}", finalDist);
Telemetry.recordUnreachable("partial-retries-exhausted", Rs2Player.getWorldLocation(),
Expand All @@ -597,11 +651,15 @@ private static WalkerState processWalk(WorldPoint target, int distance, int part
return WalkerState.UNREACHABLE;
} else {
if ("off-path-but-moving".equals(exitReason)) {
// Wait for the player to re-enter the path or to stop moving. Prevents a tight
// recursion loop that would spin on isNearPath() while the player is walking.
sleepUntil(() -> isNearPath() || !Rs2Player.isMoving(), 2000);
continue; // normal progress, don't count as recalc
}
// Actual recalc scenario (not-near-path, etc.)
WalkerState recalcResult2 = checkRecalcLimit(++recalcAttempts, target, distance);
if (recalcResult2 != null) {
return recalcResult2;
}
return processWalk(target, distance, partialRetries);
continue;
}
} catch (Exception ex) {
if (ex instanceof InterruptedException) {
Expand All @@ -610,9 +668,9 @@ private static WalkerState processWalk(WorldPoint target, int distance, int part
return WalkerState.EXIT;
}
log.error("Exception in Rs2Walker:", ex);
return WalkerState.EXIT;
}
}
log.info("Exiting walker: 403");
return WalkerState.EXIT;
}

public static boolean walkNextTo(GameObject target) {
Expand Down Expand Up @@ -815,7 +873,13 @@ public static boolean walkMiniMap(WorldPoint worldPoint, double zoomDistance) {


public static boolean walkMiniMap(WorldPoint worldPoint) {
return walkMiniMap(worldPoint, 5);
// Preserve the player's current minimap zoom instead of resetting it.
// Forcibly setting zoom to 5 every click is detectable bot behaviour.
Point point = Rs2MiniMap.worldToMinimap(worldPoint);
if (point == null) return false;
if (!disableWalkerUpdate && !Rs2MiniMap.isPointInsideMinimap(point)) return false;
Microbot.getMouse().click(point);
return true;
}

/**
Expand Down