Walker Transport Execution
This page documents source-level behavior and deterministic test coverage. It does not certify current game widgets, menu entries, transport animations, or landing coordinates. Those behaviors remain live-client verification pending until observed on the pinned RuneLite revision.
This guide covers execution of transport edges selected by the embedded Shortest Path plugin and converted into PluginRouteTransportEdge values. For route planning, ownership, resource downloads, and public navigation APIs, see walker.md.
Ownership and data flow
ShortestPathPlugin converts the selected upstream transport to a PluginRouteTransportEdge. PluginTransportActionResolver parses that edge and selects an executor. Each executor implements WalkerAction and is advanced only by WalkerPath; feature plugins must not tick transport actions or create another walker tick owner.
Game interactions remain owned by Api.actions.* through WalkerContext. A WalkerAction returns booleans to the path engine, while the context methods preserve InteractionResult acceptance and failure before those booleans are produced.
Transport Resolution & Handler Execution Logic Flow
Imported metadata contract
The upstream menuOption menuTarget targetID column is carried as PluginRouteTransportEdge.objectInfo. The resolver interprets it as:
<menu option> <menu target> <numeric NPC, object, or item id>
The resolver's supported action catalog matches menu options before it extracts the target. This supports multi-word actions such as Climb over; splitting on the first space would incorrectly turn over into part of the target name. Spell and home-spell rows do not require object metadata and resolve from displayInfo before object parsing.
displayInfo is transport-specific:
- fairy rings use the first three characters as the destination code;
- destination networks use the visible destination label, with a numeric prefix removed and text after a colon preferred;
- item and spell teleports use text after a colon as a secondary destination option when present;
- minigame teleports begin with the
MinigameTeleportdisplay name.
Malformed metadata fails closed: the resolver returns no action rather than inventing an ID, action, target, spell, or destination.
Dispatch matrix
TransportType | Executor | Entry behavior | Destination behavior | Arrival Radius | Stall Multiplier |
|---|---|---|---|---|---|
FAIRY_RING | FairyRingWalkerAction | Verifies the ring code; when the Lumbridge Elite Diary is incomplete, verifies or equips a Dramen/Lunar staff | Advances the existing FairyRingActions dial state one accepted action per tick | 5 | 1.5 |
QUETZAL | DestinationNetworkWalkerAction | Opens the row-defined landing-site NPC | Selects an unambiguous dialogue or visible map destination | 5 | 1.5 |
SPIRIT_TREE | DestinationNetworkWalkerAction | Opens the row-defined spirit-tree object | Selects an unambiguous dialogue or visible destination widget | 5 | 1.5 |
CHARTER_SHIP | DestinationNetworkWalkerAction | Opens the row-defined crew NPC | Selects an unambiguous dialogue or visible destination widget | 5 | 1.5 |
GNOME_GLIDER | DestinationNetworkWalkerAction | Opens the row-defined pilot NPC | Selects an unambiguous dialogue or visible destination widget | 5 | 1.5 |
TELEPORTATION_ITEM | TeleportItemWalkerAction | Prefers a matching equipped item, then inventory; uses the parsed item ID/name and action | Selects a secondary dialogue option when displayInfo contains one | 5 | 1.2 |
TELEPORTATION_SPELL, TELEPORTATION_SPELL_HOME | TeleportSpellWalkerAction | Normalizes the display spell name against Spell using ActionResolver and calls MagicActions.cast | Selects a secondary dialogue option when present | 5 | 1.2 |
CANOE | CanoeWalkerAction | Observes build and destination interfaces plus station actions | Progresses through chop, shape, float, paddle, destination selection, and wilderness confirmation from live state | 3 | 1.3 |
TELEPORTATION_MINIGAME | MinigameTeleportWalkerAction | Opens the Magic entry point | Chooses the resolved minigame and waits for observed arrival | 20 | 1.2 |
TOLL_GATE (e.g. Al-Kharid gate) | TollGateWalkerAction | Interacts with gate object (IDs 2882/2883 or Pay-fare) | Handles fee payment (coins) and dialogue prompts ("Pay 10 coins", "Can I come through?") before observing gate opening | 1 | 1.1 |
BOAT, MAGIC_CARPET, SHIP | NpcWalkerAction | Uses the row NPC ID/name and required action | Advances allowlisted travel/payment dialogue, then completes after observed destination-plane arrival within five tiles and no destination-side vessel crossing remains | 5 | 1.5 |
TRANSPORT | ImportedTransportWalkerAction | Resolves the exact row ID/name/action against live NPCs and objects; dispatches only when exactly one target kind matches | Delegates to the NPC or object executor; both or neither matching fails closed | varies | varies |
AGILITY_SHORTCUT, GRAPPLE_SHORTCUT, and remaining object-backed types | ImportedObjectWalkerAction | Uses the row object ID/name/action; transformed closed objects may match by name/action | Observes destination arrival; vessel traversal and adjacent barriers require exact arrival, other object transports allow two tiles, and shortcuts additionally fail after a bounded wait | 0/2 | 1.0 |
The action classes are package-private implementation details. Callers use Walker, NavigationActions, route options, and route snapshots rather than constructing transport actions directly.
Destination selector state machine
DestinationNetworkWalkerAction provides the common state machine for Quetzals, spirit trees, charter ships, and gnome gliders.
| State | Observation | Action | Next state |
|---|---|---|---|
| Awaiting entry | Player is not already near the destination | Interact with the exact NPC/object and row action | Selector opening |
| Selector opening | Dialogue is open | Select the normalized destination text | Destination selected |
| Selector opening | No dialogue; one matching visible widget exists | Dispatch Travel, Select, or Continue | Destination selected |
| Selector opening | No unique match exists | Wait without blind-clicking | Selector opening |
| Destination selected | Player has not arrived | Issue no duplicate selection | Destination selected |
| Any | Player is on the destination plane within five tiles and no destination-side vessel crossing remains | Report completion | Complete |
Visible-widget lookup scans loaded widget roots and dispatches only when it resolves one unique match. Zero matches wait. Multiple matches fail closed for that tick. Jagex color and formatting tags are removed before destination-label normalization. This avoids substituting a guessed packed widget ID or clicking an ambiguous label.
Pre-execution Requirement Revalidation
All transport actions implement validateRequirements(WalkerContext) which is called by WalkerPath.tickActionStep() before dispatching the action. This ensures requirements are still met at execution time (not only planning time):
- FairyRingWalkerAction: Verifies Lumbridge Elite Diary varbit or staff in equipment/inventory
- TeleportItemWalkerAction: Checks item still exists in equipment/inventory
- TeleportSpellWalkerAction: Validates spell is known and available
- CanoeWalkerAction: Confirms axe in inventory/equipment and minimum Woodcutting level
- TollGateWalkerAction: Verifies sufficient coins for toll fee
- NpcWalkerAction: Confirms the expected NPC remains resolvable, unless destination arrival is already observed
- ImportedObjectWalkerAction: Confirms the row-defined object/action, a name/action-matching transformed object, destination arrival, or an observed already-open adjacent
Openbarrier - ImportedTransportWalkerAction: Delegates to resolved delegate's validation
- DestinationNetworkWalkerAction (SpiritTree, Quetzal, Charter, Glider): Returns true (validated upstream)
If validation fails, WalkerPath increments recoveryAttempts and triggers a replan with decision requirements-changed-replan:<stepName>.
Specialized behavior
Fairy rings
The handler parses a FairyRing enum code from displayInfo. If the Lumbridge Elite Diary varbit is not complete, it checks equipment first and inventory second for a Dramen or Lunar staff. An inventory staff equips, and the next tick re-observes equipment before ring interaction continues. Dial rotation and confirmation reuse FairyRingActions, which owns the dial varbits, widget constants, last-destination optimization, and per-tick pacing.
Item teleports
The item handler searches using both the imported numeric ID and target name. It prefers equipped items because worn jewellery often exposes the desired destination directly; inventory items use the imported action plus destination, Rub, and Teleport fallbacks. After one accepted dispatch, the handler does not repeat the item interaction. If a destination dialogue appears, it selects the parsed secondary option.
Route requirements remain responsible for deciding whether charges, items, and other prerequisites make an edge eligible. The execution handler does not reconstruct upstream charge tables.
Spell teleports
The handler normalizes the text before the first colon to an uppercase underscore enum name and resolves it against Spell. Unknown spells fail closed. Casting goes through MagicActions.cast, keeping active spellbook checks, widget availability, pacing, rune checks, and structured failures centralized.
Canoes
CanoeWalkerAction derives its next operation from visible interfaces and the station's current actions rather than assuming a fixed elapsed-time sequence. It validates the minimum Woodcutting level and an inventory/equipped axe before chopping, chooses the best canoe supported by the real Woodcutting level, and handles wilderness confirmation before waiting for arrival.
Generic target resolution and object fallback
TRANSPORT metadata does not identify an ID namespace. The numeric value can refer to an NPC even though the upstream field is historically named objectInfo. ImportedTransportWalkerAction therefore queries both live target kinds using the row-defined ID, name, and action before dispatch:
- one matching NPC delegates to
NpcWalkerAction; - one matching object delegates to
ImportedObjectWalkerAction; - both matching, or neither matching, fails closed and lets
WalkerPathrecover or replan.
The Varrock-side Civitas route is the regression case:
3280 3412 0 -> 1700 3141 0
TRANSPORT
Travel Primio 12888
Primio is an NPC. The live RuneLite menu event uses NPC_FIRST_OPTION and the NPC's current scene index, while target discovery continues to use NPC ID 12888. Treating that ID as an object produces no dispatch and creates the empty-path replan loop that this resolver prevents.
ImportedObjectWalkerAction remains the executor for resolved generic objects, ordinary imported object types, and shortcuts. It first prefers the row-defined ID/name/action and may then match a transformed closed object by the same name/action without requiring the stale ID. Non-adjacent objects wait for correct-plane arrival within two tiles; they do not wait for a static ladder, portal, or lever to despawn. Vessel traversal actions require exact destination arrival. Disembark, Board, Embark, and Cross-gangplank identify vessel traversal directly; generic Cross, Walk-across, and Leave require a gangplank, mooring, or plank object name, so ordinary bridge shortcuts keep their two-tile completion radius. For an adjacent same-plane Open barrier, the live physical-door query distinguishes closed openable state from a close-only transformed open state. Once open, it queues one correlated movement request to the exact destination. The already-open bypass is not used for missing non-barrier objects.
Agility and grapple shortcuts enable the bounded-shortcut flag. If it does not observe completion after ten action ticks, the action fails and lets WalkerPath recover or replan instead of waiting indefinitely. We deliberately do not enable the bounded wait for ordinary object transports because their animation and plane-change timing can legitimately be longer.
LiveWalkerContext behavior
The live context provides the transport handlers with these guarantees:
- inventory and equipment name patterns are case-insensitive;
- NPC and object names are case-insensitive while actions are already matched case-insensitively;
- object lookup accepts either the scene object's ID or the resolved
ObjectCompositionID, covering transformed/multiloc objects; - widget destination selection must resolve one visible match;
- client state reads marshal through
ClientThreadBridgewhen needed; - all queued interactions use existing result-aware action APIs.
- dispatched walker NPC/object actions copy through Packet Utils' disabled-by-default issued-action clipboard trace, including the NPC ID and current scene index for NPC actions.
VesselDisembarkation supplies a dedicated gangplank/mooring query rather than
adding vessel verbs to the generic door policy. Barrier lookup scans the area
between the source and destination, including a one-tile margin, and accepts
only Disembark, Cross-gangplank, Walk-across, Cross, and Leave under
the vessel-name rules above. Board and Embark are embarkation actions — they
put the player onto a vessel, not off it — and are deliberately excluded from
the barrier-detection filter so charter ship dock gangplanks are never treated
as blocking doors during land-step traversal. Nearby recovery uses the same
disembarkation set and prioritizes Disembark, Cross-gangplank,
Walk-across, Cross, then Leave.
Vessel arrival and land-step recovery
NPC and destination-network transports do not report completion merely because
the player enters their normal five-tile arrival radius. When a nearby vessel
barrier still lies on the destination side, requiresDisembarkation(...) keeps
the action active. Exact destination arrival clears the requirement.
After a transport, a land route can be empty or its movement dispatch can fail
across the water gap. WalkerPath then requests one nearby disembarkation for
that land step. An accepted interaction waits up to three walker ticks for a
location change. Progress recalculates the remaining land path. No progress, a
rejected interaction, or a second request returns to the existing exhausted-step
replan path. This bound prevents a gangplank interaction from replacing a
NO_ROUTE failure with an unlimited retry loop.
The default WalkerContext.queueVisibleWidgetByText implementation returns false. This preserves compatibility for deterministic fake contexts while requiring test doubles to opt into selector behavior explicitly when a test covers a destination-map transition.
Failure and retry semantics
A handler returns false when it cannot resolve or accept its required target, item, spell, or initial interaction. WalkerPath records that failure and applies its existing retry/replan policy. Once an entry or destination selection is accepted, handlers wait for observable state instead of issuing the same action every tick.
Completion relies on player position, not merely on an accepted click. For specialized handlers and NpcWalkerAction, arrival means the player is on the same plane and within five tiles of the selected edge destination. Imported object arrival uses radius zero for adjacent barriers and radius two otherwise; source-equals-destination rows still require the defined action transition. Moving away from the source does not complete an NPC or object transport. NPC dialogue progression is limited to Can I journey on this ship?, Pay-fare, Yes, and visible continue prompts, with at most one accepted dialogue action per tick. Unknown dialogue waits under the existing route-step timeout rather than selecting an arbitrary option.
Automated validation
Use the focused resolver and canoe tests first:
.\gradlew.bat test --tests com.n3plugins.sdk.walker.PluginTransportActionResolverTest --tests com.n3plugins.sdk.walker.NpcWalkerActionTest --tests com.n3plugins.sdk.walker.CanoeWalkerActionTest --console plain
./gradlew test --tests com.n3plugins.sdk.walker.PluginTransportActionResolverTest --tests com.n3plugins.sdk.walker.NpcWalkerActionTest --tests com.n3plugins.sdk.walker.CanoeWalkerActionTest --console plain
Then run the shared walker suite for changes to context, action state, or path recovery:
.\gradlew.bat test --tests com.n3plugins.sdk.walker.* --console plain
./gradlew test --tests 'com.n3plugins.sdk.walker.*' --console plain
Documentation changes require the checks in validation.md:
./gradlew auditDocs checkDocDrift --console plain
npm --prefix docs-site run build
Live-client acceptance checklist
Static tests cannot establish current UI or in-world correctness. Before marking transport execution live-verified, record evidence for:
- one world fairy ring that requires equipping a staff and one account with the Lumbridge Elite Diary bypass;
- POH and world spirit-tree entry where available;
- a Quetzal map destination selected through Renu;
- one charter-ship and one gnome-glider map destination;
- an equipped multi-destination teleport item and an inventory teleport item;
- a direct spell teleport and a spell with a secondary destination option;
- all canoe phases, including a wilderness confirmation route;
- an agility shortcut that completes and one controlled failure that reaches the bounded retry path;
- generic object, boat, magic-carpet, and ship regression routes;
- the generic Varrock-side
Travel Primio 12888route, withNPC_FIRST_OPTION, live NPC index, and arrival near(1700,3141,0)observed after rebuilding and reloading the plugin.
Record the RuneLite version, game revision, source/destination, imported metadata, execution decisions, and observed landing point for each result. For scenarios without that evidence, report:
Live client verification: pending
Recorded live evidence
| Date | Revision | Route | Metadata | Result | Evidence boundary |
|---|---|---|---|---|---|
| 2026-07-29 | 239 | Varrock-side (3280,3412,0) to Civitas (1700,3141,0) | TRANSPORT, Travel Primio 12888 | Passed in game after the generic target-resolution fix | User-verified route completion; no Agent Server operation record or retained screenshot was supplied |
| 2026-08-31 | 240 | Any charter ship route (e.g. Brimhaven → Port Sarim) | CHARTER_SHIP, Charter Trader Crewmember 1330 | VesselDisembarkation.findBetween() changed to use selectAction() only; Board/Embark no longer register as blocking barriers during land-step traversal. boardOnlyObjectIsNotABlockingDoorBarrier test added and passing. | Static fix; live client verification pending |
This closes live acceptance for the specific Primio regression route. It does not establish live correctness for the other transports in the checklist.