Skip to content

Rs2Walker: handleTransports inner loop never breaks for SHIP/NPC/BOAT, iterates entire remaining path #1760

@runsonmypc

Description

@runsonmypc

Summary

In Rs2Walker.handleTransports(...), the inner for (int i = indexOfStartPoint; i < path.size(); i++) loop has break statements after every transport-type handler except SHIP, NPC, and BOAT. As a result, when a candidate SHIP/NPC/BOAT transport is processed (or simply considered) the loop continues iterating the entire remaining path, doing per-iteration work and emitting per-iteration log.debug lines.

Where

runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java, around lines 1937–1972 on development:

if (path.get(i).equals(origin)) {
    if (transport.getType() == TransportType.SHIP || transport.getType() == TransportType.NPC || transport.getType() == TransportType.BOAT) {

        Rs2NpcModel npc = Rs2Npc.getNpc(transport.getName());

        if (Rs2Npc.canWalkTo(npc, 20) && Rs2Npc.interact(npc, transport.getAction())) {
            Rs2Player.waitForWalking();
            // ... dialogue / animation / distance waits ...
            sleepTickJitter(6);
        } else {
            Rs2Walker.walkFastCanvas(path.get(i));
            sleep(1200, 1600);
        }
        // <-- no break here, success or failure
    }

    if (transport.getType() == TransportType.CHARTER_SHIP) {
        if (handleCharterShip(transport)) {
            // ...
            break;            // <-- CHARTER_SHIP breaks
        }
    }
}

log.debug("[Walker] Handling {} transport: {} (i={}, path[i]={}, origin={})", ...);

if (transport.getType() == TransportType.POH)            { ... break; }
if (transport.getType() == TransportType.CANOE)          { ... break; }
if (transport.getType() == TransportType.SPIRIT_TREE)    { ... break; }
if (transport.getType() == TransportType.QUETZAL)        { ... break; }
if (transport.getType() == TransportType.MAGIC_CARPET)   { ... break; }
if (transport.getType() == TransportType.WILDERNESS_OBELISK)  { ... break; }
if (transport.getType() == TransportType.GNOME_GLIDER)   { ... break; }
if (transport.getType() == TransportType.FAIRY_RING)     { ... break; }
if (transport.getType() == TransportType.TELEPORTATION_MINIGAME) { ... break; }
if (transport.getType() == TransportType.TELEPORTATION_ITEM)     { ... break; }
if (transport.getType() == TransportType.TELEPORTATION_SPELL)    { ... break; }
if (transport.getType() == TransportType.SEASONAL_TRANSPORT)     { ... break; }

if (transport.getObjectId() <= 0) break;

// fallthrough: scene-object scan via Rs2GameObject.getAll(predicate, transport.getOrigin(), 10)

SHIP, NPC, BOAT are the only types whose handler is intended to be terminal but does not break. Once path.get(i).equals(origin) matches and the NPC/ship is interacted with, control falls through and i keeps advancing.

For NPC transports whose objectId > 0, the if (transport.getObjectId() <= 0) break; guard at ~line 2079 does not fire either, so each subsequent iteration also runs the Rs2GameObject.getAll(predicate, transport.getOrigin(), 10) scene scan that follows.

Symptom — log evidence

While running through the Auburn Valley NPC transport, handleTransports produces ~99 consecutive Handling NPC transport lines for a single transport candidate, all sharing the same origin, while i walks from indexOfStartPoint to path.size()-1:

[Walker] Handling NPC transport: Auburn Valley (i=388, path[i]=WorldPoint(x=1364, y=3287, plane=0), origin=WorldPoint(x=1487, y=3232, plane=0))
[Walker] Handling NPC transport: Auburn Valley (i=389, path[i]=WorldPoint(x=1364, y=3286, plane=0), origin=WorldPoint(x=1487, y=3232, plane=0))
...
[Walker] Handling NPC transport: Auburn Valley (i=486, path[i]=WorldPoint(x=1274, y=3168, plane=0), origin=WorldPoint(x=1487, y=3232, plane=0))

origin=(1487, 3232) is well behind the player's current position in the path, so path.get(i).equals(origin) is never true in this run — the loop is purely overhead. With debug logging on, this runs on the script thread and visibly delays the walker between game ticks; with debug logging off, the per-iteration Rs2GameObject.getAll(...) scan still runs.

Expected behavior

The SHIP/NPC/BOAT branch should break after a successful interaction (mirroring CHARTER_SHIP directly below it and every other terminal handler in the same loop). Additionally, the bare Rs2Npc.interact(...) failure path (else { walkFastCanvas; sleep; }) probably should also break rather than falling through into the fallback scene-object scan, since it is unrelated to the NPC interaction.

Suggested fix

Add explicit breaks to the SHIP/NPC/BOAT block, e.g.:

if (transport.getType() == TransportType.SHIP || transport.getType() == TransportType.NPC || transport.getType() == TransportType.BOAT) {
    Rs2NpcModel npc = Rs2Npc.getNpc(transport.getName());
    if (Rs2Npc.canWalkTo(npc, 20) && Rs2Npc.interact(npc, transport.getAction())) {
        // ... existing handling ...
        sleepTickJitter(6);
        break;
    } else {
        Rs2Walker.walkFastCanvas(path.get(i));
        sleep(1200, 1600);
        break;
    }
}

Reproduction

  1. Have the script walk a long route that crosses an NPC transport (e.g. the Quetzal NPCs in Varlamore: Auburn Valley, Quetzacalli Gorge).
  2. Enable DEBUG for net.runelite.client.plugins.microbot.util.walker.Rs2Walker.
  3. Observe [Walker] Handling NPC transport: <name> repeating ~path.size() - indexOfStartPoint times for a single transport on a single handleTransports invocation.

Versions

  • Repo: chsami/Microbot
  • Branch: development
  • File: runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java
  • Lines: 1925–2160 (handleTransports inner loop)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions