Skip to content
Merged

2.5.7 #1765

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ef17579
fix(shortestpath): TransportNode cost double-count + overflow
runsonmypc Apr 19, 2026
cfd16d9
chore(shortestpath): demote MoA entry/selection logs to debug
runsonmypc Apr 19, 2026
d54c2ab
chore(shortestpath): remove MoA audit temp debug plugin
runsonmypc Apr 19, 2026
1c6ded0
fix(shortestpath): inline MoA child-iteration to satisfy client-threa…
runsonmypc Apr 19, 2026
dafa91e
fix(shortestpath): honor lockedMoaRegions in pathfinder MoA filter
runsonmypc Apr 19, 2026
77a9734
fix(shortestpath): stop blacklisting MoA destinations on ambiguous ti…
runsonmypc Apr 19, 2026
4ce51ef
fix(keyboard): dispatch to canvas KeyListeners directly to stop focus…
runsonmypc Apr 19, 2026
ea3b4ec
fix(shortestpath): wait for MoA widget to close before returning from…
runsonmypc Apr 19, 2026
2088c0d
chore(threadsafety): refresh client-thread-guardrail baseline
runsonmypc Apr 20, 2026
24be767
fix(questscript): avoid league/cosmetic items with MAX_VALUE prices
May 9, 2026
792b213
Update runelite-client/src/main/java/net/runelite/client/plugins/micr…
seppulcro May 9, 2026
e8b4d32
feat(quest-helper): add quest helper start and status endpoints
sami3p May 9, 2026
c9bc49f
feat(quest-helper): add quest helper start and status endpoints
sami3p May 9, 2026
aa4b3e5
Merge pull request #1764 from seppulcro/fix/questscript-maxvalue-price
chsami May 9, 2026
f9e1b9d
refactor(walker): improve pathfinding and cancellation handling
sami3p May 9, 2026
ad3d552
Merge remote-tracking branch 'origin/development' into development
sami3p May 9, 2026
963cbc6
Merge pull request #1751 from runsonmypc/fix/transportnode-cost-overflow
chsami May 9, 2026
11a94b6
refactor(walker): enhance door interaction handling and pathfinding l…
sami3p May 9, 2026
4ee96d9
refactor(walker): enhance door interaction handling and pathfinding l…
sami3p May 9, 2026
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
66 changes: 66 additions & 0 deletions docs/F2P_WEBWALKER_HARNESS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# F2P Web Walker Harness

This harness runs live in-game webwalker regression routes for a fresh F2P account after Tutorial Island. It uses `Rs2Walker.walkWithState(...)` for both setup movement and the route under test.

The Microbot CLI may be used while investigating a failure to read state, nearby objects, nearby NPCs, screenshots, or logs. Do not use `./microbot-cli walk` or any other manual movement command to place the player at the destination.

## Run

Full fail-fast suite:

```bash
scripts/run-f2p-webwalker-harness.sh
```

Specific route:

```bash
scripts/run-f2p-webwalker-harness.sh F2P-15
```

The script compiles the client, starts RuneLite in test mode with AutoLogin enabled by `TestRunnerPlugin`, runs the hidden `F2P Web Walker Harness` plugin, and writes:

```text
~/.runelite/test-results/f2p-webwalker/result.json
```

Override the timeout or output directory:

```bash
MICROBOT_WEBWALKER_TIMEOUT_MS=2400000 \
MICROBOT_WEBWALKER_LEG_TIMEOUT_MS=300000 \
MICROBOT_WEBWALKER_OUTPUT_DIR=/tmp/f2p-webwalker \
scripts/run-f2p-webwalker-harness.sh F2P-15
```

The runner forwards route settings through `microbot.test.webwalker.*` system properties because the Gradle `runTest` task only propagates `microbot.test.*` properties into the launched client JVM.

## Agent Loop

1. Run the full suite.
2. If a route fails, inspect `result.json`, `~/.runelite/logs/client.log`, and optional observational CLI output such as `./microbot-cli state`, `objects`, `npcs`, or screenshots.
3. Understand whether the failure was setup movement or the route itself.
4. Patch the walker or supporting path data.
5. Rebuild and rerun only the failed route, for example `scripts/run-f2p-webwalker-harness.sh F2P-15`.
6. Once the failed route passes, rerun the full suite.

## Required Routes

| ID | From | To | Coverage |
|---|---:|---:|---|
| F2P-01 | `3222,3218,0` | `3208,3220,2` | Lumbridge castle stairs and plane change |
| F2P-02 | `3208,3220,2` | `3253,3266,0` | Castle exit to local outdoor area |
| F2P-03 | `3253,3266,0` | `3092,3245,0` | Lumbridge/Draynor open-world routing |
| F2P-04 | `3092,3245,0` | `3109,3168,0` | Draynor to Wizards' Tower bridge |
| F2P-05 | `3109,3168,0` | `3029,3217,0` | Bridge to Port Sarim docks |
| F2P-06 | `3029,3217,0` | `2957,3214,0` | Port Sarim to Rimmington |
| F2P-07 | `2957,3214,0` | `2946,3368,0` | Rimmington to Falador |
| F2P-08 | `2946,3368,0` | `3082,3420,0` | Falador to Barbarian Village |
| F2P-09 | `3082,3420,0` | `3093,3493,0` | Barbarian Village to Edgeville |
| F2P-10 | `3093,3493,0` | `3164,3486,0` | Edgeville to Grand Exchange |
| F2P-11 | `3164,3486,0` | `3185,3441,0` | Grand Exchange to Varrock west bank |
| F2P-12 | `3185,3441,0` | `3253,3420,0` | Varrock west-to-east city routing |
| F2P-13 | `3253,3420,0` | `3222,3218,0` | Varrock to Lumbridge long return |
| F2P-14 | `3092,3245,0` | `3109,3341,0` | Draynor Manor approach |
| F2P-15 | `3109,3341,0` | `3106,3363,0` | Draynor Manor door/object handling |
| F2P-16 | `3106,3363,0` | `3092,3245,0` | Reverse manor exit behavior |
1 change: 1 addition & 0 deletions docs/entity-guides/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Each guide lists known pitfalls when working with one specific game entity type.
| Entity | File | When to read |
|--------|------|--------------|
| Items (inventory, bank, ground, equipment, shops) | [items.md](items.md) | Any code calling `Rs2Inventory`, `Rs2Bank`, `Rs2Equipment`, `Rs2GroundItem`, `Rs2Shop`, or `Rs2DepositBox` interaction helpers, or any helper that takes a list of item names and applies a single action to all of them |
| Movement (walker, minimap, pathing) | [movement.md](movement.md) | Any code calling or modifying `Rs2Walker`, `Rs2MiniMap`, shortest-path marker handling, or minimap/canvas walk-click logic |

## Format

Expand Down
78 changes: 78 additions & 0 deletions docs/entity-guides/movement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Movement Gotchas

## 1. Do not recurse on failed minimap clicks without changing the click target

`Rs2Walker.processWalk` holds the walker lock while processing a path. If a minimap click is rejected because the calculated point is outside the minimap clip, immediately recursing with the same target can spin forever while still holding the lock. Shrink the click target toward the player or otherwise change the condition before retrying.

**Why this matters:** Quest steps that walk to a nearby object can repeatedly calculate a valid path but never move, starving other walk requests because the walker lock is never released.

**Pattern to follow:**

```java
WorldPoint clickTarget = getPointWithWallDistance(targetWp);
boolean clicked = Rs2Walker.walkMiniMap(clickTarget);
if (!clicked)
{
clicked = walkMiniMapToward(clickTarget, playerLoc, MINIMAP_REACH_EUCLIDEAN - 1);
}
```

**Where this applies:** `Rs2Walker`, `Rs2MiniMap`, and shortest-path walking loops.

**Defensive check:** When debugging stalls, compare pathfinder logs with `./microbot-cli state`. A repeating valid path with an unchanged player position usually means the click layer failed after pathing succeeded.

## 2. Probe raw path obstacles before declaring the walker stuck

Path smoothing can collapse many adjacent raw path tiles into one minimap waypoint. Some doors and gates are not represented as blocking collision in the pathfinder map, so the smoothed segment may legally cross them while hiding the exact tile the object handler needs to inspect. Run nearby raw-path door/object checks as soon as the raw path is longer than the smoothed path and the obstacle is in scene range; do not wait for `stuckCount` to increment first.

**Why this matters:** A walk from Varrock castle's upper floors toward Varrock fountain can descend correctly, then stall at the plane-1 castle door because the smoothed waypoint skips over the door tile and the normal per-segment door check never sees it.

**Pattern to follow:**

```java
if (rawPath != null && path != null && rawPath.size() > path.size()
&& handleNearbyRawPathSceneObjects(rawPath, HANDLER_RANGE)) {
doorOrTransportResult = true;
}
```

**Where this applies:** `Rs2Walker`, `PathSmoother`, and shortest-path obstacle handling.

**Defensive check:** When a path stalls beside a visible door while the pathfinder reports a complete route, compare raw and smoothed path lengths; if the raw path is longer, verify nearby raw-path obstacle probing happens before stall recovery.

## 3. Match wall doors by crossed edge, not nearby tile

Wall-object doors block the edge between the wall object's tile and the neighboring tile indicated by its orientation. Raw-path segment probes must only treat a wall door as relevant when the path segment actually transitions across that edge. Do not match a wall door merely because the path starts on, ends on, or passes near one side of the door.

**Why this matters:** At Draynor Manor's east/back door, the player can stand on the south-side door tile and need to walk southwest into the room. A broad "door near segment" match repeatedly re-opens the back door instead of allowing the next minimap walk step to run.

**Pattern to follow:**

```java
WorldPoint doorTile = wall.getWorldLocation();
WorldPoint blockedNeighbor = getWallDoorNeighborPoint(wall.getOrientationA(), doorTile);
return isDoorEdgeTransition(previousPathTile, nextPathTile, doorTile, blockedNeighbor);
```

**Where this applies:** `Rs2Walker.handleNearbyRawPathSceneObjects`, `Rs2Walker.findDoorNearSegment`, and any wall-door probe that uses `WallObject.getOrientationA()`.

**Defensive check:** Add a unit test for a path starting on the door's blocked-neighbor tile and moving away from the door; it must return false.

## 4. Do not raw-probe doors while the player is already moving

Raw-path scene-object probing is a recovery aid for smoothed paths that hide nearby obstacles. Once a door interaction has started movement, let that movement settle or reach the door edge before probing again. Re-running raw probes while the player is still moving can repeatedly interact with the same door and prevent the normal minimap/path step from taking over.

**Why this matters:** When leaving Draynor Manor through the east/back door, the walker can click the door, start moving toward it, then immediately re-enter raw-path probing and click the same door again instead of continuing through the path outside.

**Pattern to follow:**

```java
if (Rs2Player.isMoving()) {
return false;
}
waitForDoorInteractionProgress(fromWp, toWp);
```

**Where this applies:** `Rs2Walker.handleNearbyRawPathSceneObjects`, door handlers that call `Rs2GameObject.interact`, and any recovery logic that recurses into `processWalk`.

**Defensive check:** In live testing, a door should produce one interaction followed by movement/path progress, not repeated `Raw path door handler resolved obstacle` messages every tick while the player is moving.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ project.build.group=net.runelite
project.build.version=1.12.26.2

glslang.path=
microbot.version=2.5.6
microbot.version=2.5.7
microbot.commit.sha=nogit
microbot.repo.url=http://138.201.81.246:8081/repository/microbot-snapshot/
microbot.repo.username=
Expand Down
41 changes: 37 additions & 4 deletions microbot-cli
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,10 @@ Ground Items:
ground-items pickup <name> Pick up nearest ground item

Movement:
walk <x> <y> [plane] [--wait] [--timeout SECONDS]
walk <x> <y> [plane] [--wait] [--timeout SECONDS] [--distance TILES]
Walk to coordinates (non-blocking by default;
--wait blocks until arrival or timeout, default 30s)
--wait blocks until arrival or timeout, default 30s;
--distance defaults to 0 for exact coordinates)

Banking:
bank Bank status (open/closed, items)
Expand Down Expand Up @@ -104,6 +105,10 @@ Scripts:
scripts results --class <className> Get test results
scripts results post --class <c> --data <json> Submit test results

Quest Helper:
quest-helper status Current selected quest helper state
quest-helper start --name <questName> Select/start a quest helper

Keyboard:
keyboard type <text> Type text into the game
keyboard enter Press Enter key
Expand Down Expand Up @@ -364,12 +369,13 @@ case "$1" in
;;

walk)
[[ $# -lt 3 ]] && { echo '{"error":"Usage: microbot-cli walk <x> <y> [plane] [--wait] [--timeout SECONDS]"}'; exit 1; }
[[ $# -lt 3 ]] && { echo '{"error":"Usage: microbot-cli walk <x> <y> [plane] [--wait] [--timeout SECONDS] [--distance TILES]"}'; exit 1; }
walk_x="$2"
walk_y="$3"
walk_plane=0
walk_wait="false"
walk_timeout=30
walk_distance=0
shift 3
if [[ $# -gt 0 ]] && [[ "$1" != --* ]]; then
walk_plane="$1"
Expand All @@ -379,10 +385,11 @@ case "$1" in
case "$1" in
--wait) walk_wait="true"; shift ;;
--timeout) walk_timeout="$2"; shift 2 ;;
--distance) walk_distance="$2"; shift 2 ;;
*) shift ;;
esac
done
walk_body="{\"x\":${walk_x},\"y\":${walk_y},\"plane\":${walk_plane},\"wait\":${walk_wait},\"timeout\":${walk_timeout}}"
walk_body="{\"x\":${walk_x},\"y\":${walk_y},\"plane\":${walk_plane},\"wait\":${walk_wait},\"timeout\":${walk_timeout},\"distance\":${walk_distance}}"
if [[ "$walk_wait" == "true" ]]; then
curl_timeout=$((walk_timeout + 5))
curl -sf --max-time "$curl_timeout" "${AUTH_HEADER_ARGS[@]}" -X POST -H "Content-Type: application/json" -d "$walk_body" "${BASE}/walk" 2>/dev/null \
Expand Down Expand Up @@ -600,6 +607,32 @@ case "$1" in
esac
;;

quest-helper)
[[ $# -lt 2 ]] && { echo '{"error":"Usage: microbot-cli quest-helper status | start --name <questName>"}'; exit 1; }
case "$2" in
status)
do_get "${BASE}/quest-helper/status"
;;
start)
shift 2
name=""
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
*) shift ;;
esac
done
if [[ -z "$name" ]]; then
echo '{"error":"Usage: microbot-cli quest-helper start --name <questName>"}'
exit 1
fi
escaped=$(json_escape "$name")
do_post "${BASE}/quest-helper/start" "{\"name\":\"${escaped}\"}"
;;
*) echo '{"error":"Usage: microbot-cli quest-helper status | start --name <questName>"}'; exit 1 ;;
esac
;;

keyboard)
[[ $# -lt 2 ]] && { echo '{"error":"Usage: microbot-cli keyboard type <text> | enter | escape | backspace"}'; exit 1; }
case "$2" in
Expand Down
9 changes: 7 additions & 2 deletions runelite-client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ tasks.register<JavaExec>("run") {
mainClass.set("net.runelite.client.RuneLite")

jvmArgs(
"-Dfile.encoding=UTF-8"
"-Dfile.encoding=UTF-8",
"-ea"
)
}

Expand Down Expand Up @@ -91,6 +92,7 @@ tasks.register<JavaExec>("runDebug") {
// same JVM args you need normally
jvmArgs(
"-Dfile.encoding=UTF-8",
"-ea",
// JDWP agent for debugger
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005"
)
Expand All @@ -103,7 +105,10 @@ tasks.register<JavaExec>("runTest") {
classpath = sourceSets.main.get().runtimeClasspath
mainClass.set("net.runelite.client.RuneLite")

jvmArgs("-Dfile.encoding=UTF-8")
jvmArgs(
"-Dfile.encoding=UTF-8",
"-ea"
)

System.getProperties()
.filter { it.key.toString().startsWith("microbot.test.") }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ public BlockingEventManager()
// single-threaded executor for running event.execute()
this.blockingExecutor = Executors.newSingleThreadExecutor(threadFactory);

// scheduler for periodic validate() calls
startLoop();

// pre-register core events
blockingEvents.add(new WelcomeScreenEvent());
blockingEvents.add(new DisableLevelUpInterfaceEvent());
Expand All @@ -68,6 +65,13 @@ public BlockingEventManager()
sortBlockingEvents();
}

public synchronized void start() {
if (loopFuture != null && !loopFuture.isCancelled() && !loopFuture.isDone()) {
return;
}
startLoop();
}

public void shutdown() {
if (loopFuture != null) loopFuture.cancel(true);
scheduler.shutdownNow();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ protected void startUp() throws AWTException
);

Microbot.pauseAllScripts.set(false);
Microbot.getBlockingEventManager().start();

MicrobotPluginListPanel pluginListPanel = pluginListPanelProvider.get();
pluginListPanel.addFakePlugin(new MicrobotPluginConfigurationDescriptor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ private List<AgentHandler> buildHandlers(int maxResults) {
new WidgetInvokeHandler(gson),
new SettingsHandler(gson),
new KeyboardHandler(gson),
new QuestHelperHandler(gson),
new StateMachineDebugHandler(gson),
new ProfileHandler(gson),
new DynamicScriptDeployHandler(gson)
Expand Down
Loading
Loading