Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Movement

daRPC exposes the game client’s stock movement and a narrow exact-route control surface. It does not replace or improve the client’s native planner. An external bot that needs obstacle avoidance, group-aware costs, or reliable replanning should own those decisions and submit short exact routes.

Consumer surface

NeedHTTP or SSE interface
Current map, position, walking flag, and planned routeGET /clients/{client}/status
Visible players, creatures, NPCs, and their positionsGET /clients/{client}/objects
Current group state and membersGET /clients/{client}/group
Static map bytes for an external plannerGET /maps/{map_id}/download
One cardinal step, a stock destination walk, or an exact routePOST /clients/{client}/walk
Stop the current routeDELETE /clients/{client}/walk
Ordered movement and world updatesGET /clients/{client}/events

Coordinates are zero-based. Read the current map ID, dimensions, and position from status before submitting movement.

Choosing a movement mode

One step

Submit one direction when the controller wants to drive the character one tile at a time:

{"direction":"north"}

The DLL calls the stock walk helper. A rejected step returns a command failure. If the helper accepts the step but the character never reaches the adjacent tile, walking.stopped reports obstructed.

The client predicts each step until its visual transition commits. A second direct step submitted during that transition is rejected so it cannot overlap the prediction. Submit it again after the position update or use a destination route, which the client can queue safely.

Stock destination

Submit a destination to ask the client’s built-in planner to build and execute its normal ground route:

{"destination":{"x":120,"y":85}}

This mode is intentionally vanilla. daRPC does not change native collision answers, add player or monster exclusions, retry a rejected edge, or rebuild a stalled route. Use it when the game’s ordinary shortest-path behavior is good enough. A valid tile with no native path reports no_path.

When a destination replaces a route during an active step, the DLL builds from that step’s staged destination and leaves the replacement queued. The client’s normal step-completion callback commits the staged tile and starts the queued route. Repeated replacements update that queue without starting another prediction early.

Exact route

Submit a map-tagged list of absolute tiles when an external planner owns the route:

{
  "route": {
    "map_id": 3001,
    "tiles": [
      {"x":11,"y":22},
      {"x":12,"y":22},
      {"x":12,"y":23}
    ]
  }
}

The route must:

  • contain 1 through 256 tiles;
  • start at the character’s current confirmed position, or at the staged destination of an active step;
  • stay on the stated current map and inside its dimensions;
  • use unique tiles connected by cardinal one-tile edges; and
  • pass both native collision checks at submission time.

The native self object has separate committed and staged positions during a visual step. The DLL validates an idle route against the committed position and an active-step replacement against the staged destination. The packet-confirmed position must match that effective origin. This allows an acknowledged step to finish visually without treating its older committed tile as a desynchronization.

That distinction explains most observed packet/native differences. After an acknowledgement, packet state can already be at the staged tile while the object’s committed tile remains one step behind until animation completion. This is healthy when transition_active is true and staged position matches the packet. A persistent mismatch was produced when an overlapping prediction was calculated from the older committed tile and then installed after the client committed the prior step. Exact-route deferral and direct-step rejection prevent that sequence. A server correction can create another temporary mismatch until the following authoritative position refresh completes.

Validation is transactional. Map, position, transition, tile, edge, or collision rejection leaves the route already executing in the client and all daRPC destination and walking tracking unchanged. Only a fully validated and installed route emits walking.stopped with reason replaced, updates route tracking, and emits walking.route_changed, in that order.

An accepted active-step replacement is placed in the native route vector but is not advanced immediately. The normal client step-completion callback first commits the staged tile and then advances the replacement. Repeated replacements therefore cannot start overlapping predictions.

The DLL places the validated route into the client’s native route vector and starts its normal walker. Animation, packets, acknowledgements, and pacing remain client-owned. Route injection is not a teleport and does not bypass the live step validator.

If a later exact-route edge is rejected, daRPC emits walking.obstructed, then walking.stopped with reason obstructed, and clears the exact route. It does not retry or replan.

When the server sends a confirmed position correction, daRPC stops and clears an external exact route immediately. The stock client requests its authoritative position when the correction differs from the local object, so normal recovery does not require F5. Wait for the resulting location update and replan. F5 remains a manual fallback if the client does not complete that refresh.

Resynchronizing during movement

Physical F5 and POST /clients/{client}/resync use the same synchronization path. When a scheduled refresh reaches the front of that path, daRPC clears the queued route, but it does not interrupt a step the client has already accepted. If that step’s visual transition is active, daRPC waits for its staged destination to become the committed native position before sending refresh packet 0x38. This avoids asking the server to redraw while the client still exposes the prior committed tile.

The HTTP response can arrive during this wait. client.resync marks the later packet submission. The correlated client.resync_completed means daRPC closed the refresh window after RefreshUserOK or the one-second fallback. A movement consumer should wait for the matching completion event before submitting its next walk. It does not need to measure the animation duration, poll native state, or clear and rebuild world objects.

Server-driven correction refreshes remain immediate and do not enter this deferred user-request path. See Refresh and resynchronization for coalescing, response fields, fallback behavior, and object reconciliation.

Cancelling movement

DELETE /clients/{client}/walk resets the stock route, clears route telemetry, and emits walking.stopped with reason cancelled when a walk was active. The direct CLI equivalent is:

darpc walk --pid <pid> cancel

The reset cannot revoke a step the client has already accepted. A final location.changed can therefore arrive after cancellation. Replan from the latest confirmed position rather than the position reported by the cancel response.

Replacing an active walk with a destination or route emits reason replaced. A direct step submitted during an active visual transition is rejected and leaves the current movement intact. Turning while a walk is active emits reason cancelled.

Cancelling a queued command through DELETE /clients/{client}/commands/{command_id} is different. It prevents a command that has not begun from executing; it does not stop an already active route.

  1. Read status, objects, and group state. Download and cache the raw map when the map ID changes.
  2. Build the planner’s own cost field. Static map collision can be combined with temporary dynamic costs from visible objects.
  3. Plan from the latest confirmed position. A controller can treat creature tiles and nearby safety margins as blocked or expensive, prefer proximity to group members, and give unrelated players a smaller cost. Those policies belong to the controller because they depend on its goal and risk tolerance.
  4. Submit a short exact-route prefix. Short prefixes reduce the amount of work invalidated when an object moves.
  5. Replace the saved route whenever walking.route_changed arrives. Update the start position from location.changed.
  6. On a terminal event, apply the reason-specific recovery below. Never wait indefinitely for the same route to recover itself.

A useful starting segment length is 4 through 16 edges. The best value depends on map density and how quickly the controller receives object updates.

Action source

Movement, turns, and planned routes expose a tagged source:

{ "kind": "unknown" }
{ "kind": "client" }
{ "kind": "command", "command_id": 41 }

command means the action occurred while the DLL executed that exact daRPC command. client means it originated inside the game client outside command execution. This includes physical keyboard or mouse input, synthetic Windows input, native client behavior, and other injected tools, so it must not be treated as proof of a human action. unknown is used when observation began after an action was already active or the origin could not be retained.

Status exposes character.movement_source. It is null while idle and carries the retained source for an active movement episode. A walking.stopped source describes the movement episode that ended, not necessarily the action that caused it to stop. For example, turning can cancel command-originated movement, but the stop event still identifies that movement command.

Stop reasons

walking.stopped contains:

walking.stopped {
    observation: EventObservation,
    source: ActionSource,
    current: TilePosition,
    destination: TilePosition?,
    reached_destination: bool?,
    reason: completed | obstructed | replaced | cancelled | position_corrected,
}
ReasonMeaningSuggested controller action
completedThe observed walk ended normally. If a destination is known, reached_destination says whether the final tile matches it.Confirm the current position and submit the next segment if needed.
obstructedThe walk ended before reaching its known destination, including a rejected edge or an accepted direct step that made no progress.Penalize walking.obstructed.attempted when that event is present, otherwise use destination, then replan from current.
replacedA different route or movement command superseded this walk.Track the replacement command and discard the old plan.
cancelledMovement was explicitly reset or cancelled.Stop unless the controller deliberately requested cancellation as part of replanning.
position_correctedThe server corrected the character position while walking.Discard the route and reread status before planning again.

destination and reached_destination can be null for movement initiated inside the game when no reliable destination was observed.

Route and obstruction events

planned_route in status is the latest observed client route:

planned_route {
    source: ActionSource,
    generation: u32,
    tiles: Vec<TilePosition>,
}

walking.route_changed carries the same fields. Tiles are absolute and ordered from the current tile toward the goal. A new native build advances the generation. Confirmed movement consumes tiles from the front without changing the generation. An empty tile list is the authoritative cleared route.

walking.obstructed reports:

walking.obstructed {
    observation: EventObservation,
    source: ActionSource,
    map_id: u32,
    current: TilePosition,
    attempted: TilePosition,
    direction: north | east | south | west,
    destination: TilePosition?,
    mode: direct | native_route | exact_route | pursuit,
}

The DLL reports the rejection but does not modify native routes or pursuits. Only a failed externally installed exact route is reset automatically.

Stream recovery and map changes

SSE is ordered per client. If the consumer receives stream.resync_required, it must reread every resource it uses before planning again. At minimum, reread status, objects, and group state.

Never submit one exact route across two maps. End the first segment on the warp tile, wait for the atomic location.changed event containing the new map and entry position, refresh the map and world inputs, and plan a new segment.

Command completion

The HTTP command response reports whether the main-thread operation completed, failed, or remains queued. It does not prove that a multi-step walk later reached its destination. Use ordered location, route, obstruction, and stopped events for the movement outcome.

See Web API for command status and timeout behavior, and Events for stream ordering and recovery.

An exact-route invalid_state response includes diagnostics with the route, packet, and native map IDs; packet, committed native, and staged positions; transition-active state; current route mode; and current destination. Missing values are null. Use the reason field to distinguish map transition, unavailable native state, map mismatch, position mismatch, and unavailable map dimensions. A rejected replacement has not changed the route reported by these fields.