Walker & shortest-path: live-collision store, Rs2Walker decomposition, and unified obstacle model - #1824
Conversation
Merge development into main after successful CI.
Promote development to main.
Promote development with RuneLite startup and ground-item looting fixes.
Merge development into main for RuneLite 1.12.31
feat: improve plugin hub UI
Development
Release 2.6.12
Merge development into main for RuneLite 1.12.32
Release Microbot 2.6.14 on RuneLite 1.12.32
Release Microbot 2.6.15 on RuneLite 1.12.33
…lker Squash of the full walker/shortest-path effort from PluginTesting, applied cleanly onto Fix-The-Walker (which had no walker changes since the shared ancestor). Walker/shortestpath scope only — no unrelated plugin changes (questhelper, woodcutting, aiohunting, aiofishing, agentserver, etc.) are included. Includes: - P0: version-stamped, self-filling live-collision disk store; regenerated collision-map + blocked-edges/transports/restrictions data. - P1: decomposed the Rs2Walker monolith behind a headless test harness — state/WalkerRouteState, recovery/RouteRecovery, geometry/WalkerPathGeometry (pure, unit-tested route/recovery/geometry decisions). - P2: unified obstacle model (obstacle/: PlannedEdge, ObstacleResolution, ObstacleResolver/Registry, Mineable/Transport resolvers, LiveScene) with the recovery dispatch cutover; rockfall fully migrated end-to-end with legacy handlers deleted. - Door decision/detection layer fully harnessed (classifier, geometry, ahead resolver, probe) — 29 headless tests. - Recovery fixes: take agility shortcuts/transports by stepping onto the origin. - Regenerated client-thread guardrail baseline for the new walker code. 68 files. Full unit suite green on this branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThe pull request adds live collision capture, overlay, persistence, route validation, learned blocked-edge storage, and transport-refresh verification updates. It refactors walker state, recovery geometry, path access, door probing, and obstacle dispatch through new helper modules and resolver contracts. It also adds Motherlode Mine navigation, toll-gate handling, developer configuration, documentation, and extensive unit and regression tests. Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java (1)
5498-5522: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winQuest-locked door can still be learned as a blocked edge, contradicting the comment right below it.
In
handleDoorsthe quest-lock branch (Line 5498) blacklists, clicks continue, refreshes and recalculates, but does not return — execution falls straight into theif (!traversed)block at Line 5509. A quest-locked interact typically leaves the player standing still (posBefore.equals(posAfter)), soshouldBlacklistDoorAfterWrongTraversalbails on thestart.equals(end)check most of the time — but any player displacement during the dialogue satisfies it and the edge gets persisted vialearnBlockedEdge, which the comment at Lines 5514-5517 explicitly says must not happen for quest/skill-locked doors.tryHandleDoorObjecthandles the same case consistently by returning immediately after the quest-lock branch (Line 5651).Proposed fix
sessionBlacklistedDoors.add(probe); Rs2Dialogue.clickContinue(); if (Rs2PathApi.getPathfinderConfig() != null) { Rs2PathApi.getPathfinderConfig().refresh(); } recalculatePath(); + return false; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java` around lines 5498 - 5522, Update the quest/stat-locked branch in handleDoors, identified by isQuestLockedDoorDialogue(), to return immediately after blacklisting, refreshing restrictions, and recalculating the path. Prevent execution from reaching the subsequent !traversed wrong-traversal handling and learnBlockedEdge call, matching the existing behavior in tryHandleDoorObject.
🧹 Nitpick comments (2)
runelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/door/Rs2DoorGeometryTest.java (1)
85-93: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThe test name promises more than the assertions check.
interactionWithinRangeUsesNearestOfProbeAndEndpointsnever exercises the "nearest" part: the first case supplies only a probe (both endpoints null), and the second puts probe and both endpoints out of range. A far probe with a near endpoint would be the case that actually pins the min-of-all-candidates behavior.♻️ Add the discriminating case
// everything 10 tiles away -> out of range. assertFalse(Rs2DoorGeometry.isDoorInteractionWithinRange(null, wp(3210, 3200), wp(3210, 3201), wp(3211, 3200), player, 2)); + // far probe but a near endpoint -> the nearest candidate wins. + assertTrue(Rs2DoorGeometry.isDoorInteractionWithinRange(null, wp(3210, 3200), wp(3201, 3200), + null, player, 2));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@runelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/door/Rs2DoorGeometryTest.java` around lines 85 - 93, Update interactionWithinRangeUsesNearestOfProbeAndEndpoints to add a discriminating assertion where the probe is out of range but one endpoint is within range, verifying the method returns true based on the nearest candidate. Keep the existing probe-only in-range and all-candidates-out-of-range assertions.runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.java (1)
674-677: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueOnly
lastLiveCaptureBaseXis invalidated here.
resetLearnedCollision()(Line 634-635) invalidates both axes; this transition path resets only X. Harmless today becausebaseChangedORs the two comparisons, but the asymmetry is a trap if the condition ever becomes an AND.♻️ Proposed tweak
overlay.setEnabled(enabled); lastLiveCaptureBaseX = Integer.MIN_VALUE; // force a capture on enable, drop snapshot on disable + lastLiveCaptureBaseY = Integer.MIN_VALUE; liveCollisionDirty = enabled;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.java` around lines 674 - 677, Update the enable/disable transition near overlay.setEnabled to invalidate both live-capture base-axis values, matching resetLearnedCollision(), rather than resetting only lastLiveCaptureBaseX; preserve the existing dirty and validation state updates.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/live/LiveCollisionPersistence.java`:
- Around line 165-173: Update LiveCollisionPersistence#shutdown so it does not
await io termination on the plugin shutdown path; after initiating executor
shutdown, return once pending writes are queued, and move any termination
waiting to a separate worker or non-shutdown thread. Preserve the final persist
call from ShortestPathPlugin#shutDown and ensure queued writes can still
complete.
In
`@runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/PathfinderConfig.java`:
- Around line 1876-1883: Update the quests component calculation in the hash
decomposition near questStateHashCode to include CLIENT_OF_KOUREND using the
same inclusion and ordering rules as computeTransportRefreshVerificationHash,
including its explicit fallback when absent from sortedQuestIds. Keep the
component aligned with the full verification hash so changes to that quest state
appear in the mismatch details.
In
`@runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/WEBWALKER_IMPROVEMENT_PLAN.md`:
- Around line 12-14: Reconcile the conflicting status entries for items `#11`, `#3`,
and `#2` throughout WEBWALKER_IMPROVEMENT_PLAN.md. Choose one authoritative status
for each item, then update the audit entries, table rows, gap descriptions, and
NEXT-stage list so every reference consistently reflects that status; preserve
the surrounding facade migration plan.
In
`@runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/door/Rs2DoorClassifier.java`:
- Around line 124-136: Update getDoorAction so the priority lookup inside the
min comparator lowercases each dact before comparing it with the lowercased act,
matching the case-insensitive filter behavior and preventing valid actions from
falling back to indexOf("").
- Around line 116-121: Update isDoorComposition to replace the direct
comp.getName().equals("null") check with the existing null-safe name helper,
preserving the current rejection of compositions whose normalized name is “null”
while safely handling null, whitespace, and case differences.
In
`@runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/obstacle/Rs2ObstacleHandler.java`:
- Around line 74-75: Update Rs2ObstacleHandler’s instance handling so being in
an instance skips only the MOTHERLODE_MINE_REGION checks and continues rockfall
lookup. Remove the early NOT_APPLICABLE return and ensure every candidate/loop
region gate, including getRegionID() == MOTHERLODE_MINE_REGION, permits
instances while retaining the existing region restriction outside instances.
In
`@runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/recovery/RouteRecovery.java`:
- Around line 63-68: Update the candidate-selection logic in RouteRecovery to
remove the unreachable idx == bestIdx distance tie-break and the now-unused
bestDistFromPlayer state, since monotonically increasing idx already selects the
last qualifying index. Also remove the corresponding “ties break toward the tile
nearer the player” javadoc sentence so the documentation matches the retained
behavior.
In
`@runelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/door/Rs2DoorProbeTest.java`:
- Around line 48-53: Update the transport fixture in
genuineTransportIsNotDoorLike to use the real ladder action "Climb" instead of
"Examine", keeping the existing transport type, names, and assertion unchanged.
In
`@runelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/geometry/WalkerPathGeometryTest.java`:
- Around line 49-61: Update
anchorForwardIndexDoesNotSnapBehindTheAnchorOnASwitchback so the player is
offset from path.get(34) while remaining Euclidean-near the outbound leg,
ensuring the test exercises the backward-snap guard rather than an exact-anchor
match. Replace the qualified boolean assertion with assertTrue and add the
required static org.junit.Assert.assertTrue import, preserving the requirement
that the returned index is at least anchor.
---
Outside diff comments:
In
`@runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java`:
- Around line 5498-5522: Update the quest/stat-locked branch in handleDoors,
identified by isQuestLockedDoorDialogue(), to return immediately after
blacklisting, refreshing restrictions, and recalculating the path. Prevent
execution from reaching the subsequent !traversed wrong-traversal handling and
learnBlockedEdge call, matching the existing behavior in tryHandleDoorObject.
---
Nitpick comments:
In
`@runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.java`:
- Around line 674-677: Update the enable/disable transition near
overlay.setEnabled to invalidate both live-capture base-axis values, matching
resetLearnedCollision(), rather than resetting only lastLiveCaptureBaseX;
preserve the existing dirty and validation state updates.
In
`@runelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/door/Rs2DoorGeometryTest.java`:
- Around line 85-93: Update interactionWithinRangeUsesNearestOfProbeAndEndpoints
to add a discriminating assertion where the probe is out of range but one
endpoint is within range, verifying the method returns true based on the nearest
candidate. Keep the existing probe-only in-range and all-candidates-out-of-range
assertions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: b2d07aeb-2041-45ad-872c-05a86480555f
⛔ Files ignored due to path filters (6)
runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/blocked_edges.tsvis excluded by!**/*.tsvrunelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/collision-map.zipis excluded by!**/*.ziprunelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/restrictions.tsvis excluded by!**/*.tsvrunelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/spirit_trees.tsvis excluded by!**/*.tsvrunelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/teleportation_items.tsvis excluded by!**/*.tsvrunelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/transports.tsvis excluded by!**/*.tsv
📒 Files selected for processing (63)
docs/walker-audit.mddocs/walker-p2-unification.mdrunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathConfig.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPanel.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/UPSTREAM_COMPARISON.mdrunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/WEBWALKER_IMPROVEMENT_PLAN.mdrunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/CollisionMap.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/LearnedBlockedEdges.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/Pathfinder.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/PathfinderConfig.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/SplitFlagMap.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/live/LiveCollisionCapture.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/live/LiveCollisionDoorMask.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/live/LiveCollisionOverlay.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/live/LiveCollisionPersistence.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/live/LiveCollisionRegion.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/live/LiveCollisionRegions.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/live/LiveCollisionSnapshot.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/live/LiveCollisionView.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/live/LiveEdgeSource.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/live/LiveRouteValidator.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2PathApi.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/WebWalkLog.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/banking/Rs2WalkerBankingPlanner.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/door/DoorProbeContext.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/door/Rs2DoorClassifier.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/door/Rs2DoorDetection.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/door/Rs2DoorGeometry.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/door/Rs2DoorProbe.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/geometry/WalkerPathGeometry.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/lifecycle/Rs2WalkerLifecycleRuntime.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/obstacle/LiveScene.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/obstacle/MineableResolver.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/obstacle/ObstacleRegistry.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/obstacle/ObstacleResolution.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/obstacle/ObstacleResolver.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/obstacle/PlannedEdge.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/obstacle/Rs2LiveScene.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/obstacle/Rs2ObstacleHandler.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/obstacle/TransportResolver.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/obstacle/WalkerActions.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/recovery/RouteRecovery.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/state/WalkerRouteState.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/shortestpath/LiveCollisionTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/shortestpath/RouteClickTargetRegressionTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathCoreTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/LearnedBlockedEdgesTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/PathfinderConfigTransportRefreshHashTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/Rs2WalkerUnitTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/TargetWalkabilityPreflightTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/banking/BankedTransportItemPlanningTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/door/Rs2DoorAheadResolverTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/door/Rs2DoorClassifierTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/door/Rs2DoorGeometryTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/door/Rs2DoorProbeTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/geometry/WalkerPathGeometryTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/obstacle/MineableResolverTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/obstacle/ObstacleRegistryTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/obstacle/TransportResolverTest.javarunelite-client/src/test/java/net/runelite/client/plugins/microbot/util/walker/recovery/RouteRecoveryTest.javarunelite-client/src/test/resources/threadsafety/client-thread-guardrail-baseline.txt
- PathfinderConfig: include CLIENT_OF_KOUREND in the decomposed quests hash
component with the same absent-from-list fallback as the full verification hash,
so verify-miss detail stays aligned.
- Rs2DoorClassifier.getDoorAction: lowercase dact in the priority-lookup comparator
(matching the case-insensitive filter) so valid actions don't fall back to indexOf("").
- Rs2DoorClassifier.isDoorComposition: use the null-safe name helper instead of
comp.getName().equals("null") (no NPE; handles null/whitespace/case).
- Rs2ObstacleHandler: in instances, skip the MLM-region gates and rely on the
rockfall object-id check (aligns code with its comment); non-instance unchanged.
- RouteRecovery.findForwardRecoveryIndex: drop the unreachable idx==bestIdx tie-break
and unused bestDistFromPlayer (monotonic idx already picks the last qualifying).
- Rs2Walker: return after the quest-locked door reroute so such doors are never
learnBlockedEdge'd (matches tryHandleDoorObject).
- ShortestPathPlugin: invalidate both live-capture base-axis values on enable/disable.
- Tests: real ladder action in Rs2DoorProbeTest; offset player + assertTrue in the
WalkerPathGeometry switchback test; discriminating endpoint-in-range assertion in
Rs2DoorGeometryTest.
Full unit suite green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
addressed PR review findings |
…s / camping the goal Sync of the walker fixes from the Clock Tower door-route debugging (squash of six PluginTesting commits: 7e9095ed9c, b317648ec8, e890d06579, 2bf709735b, a3bfd07e6e, e8ed93f5d6). Walker/shortest-path scope only. Live-traced across four walks; each layer was verified against the next trace: - Door-recovery suppression no longer strands the player: the suppress branch walks to the furthest REACHABLE route tile before the blocked edge, and the idle nudge is held off while suppression is active (its door-blind forward click was pulling the player around buildings). - Route-blocked scan gate: forward click selection stops at the near side of a closed door/wall ON the route (player-origin BFS, along-route step metric — immune to the switch-back false negatives that historically justified no gate). Lives in a gated live-only variant; the pure selection unit tests exercise stays ungated. - Anchor fold-jump protection: rawPathForwardAnchorIndex prefers walk-connected window tiles, so a route tail folding back beside the start (goal 3 tiles away through a wall) can no longer capture the anchor and pull selection to the goal. - Stale recovery clicks are preempted: the seconds-long recovery pass re-checks door-settling/moving right before clicking, so it cannot cancel an in-flight door-open; walled-target guard widened to the full recovery click radius (unreachable Euclidean-near target => replan, never click through the wall). - Anti-goal-camping: recovery rewinds to the EARLIEST unreachable route tile (the real frontier — where the door is) instead of the Euclidean-near goal, and the interim target is cleared at walk start so script-restarted walks stop yielding to the previous route's objective. +11 headless tests (route-blocked gate, fold anchor, Clock Tower fold shape). Client-thread guardrail baseline regenerated (Rs2Walker lambda renumbering only). Full unit suite green on this branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Consolidates the walker / shortest-path work into a single, reviewable change. It
moves the walker off a brittle static-only collision model, breaks the ~11.5k-line
Rs2Walkermonolith into tested, single-responsibility pieces behind a headless testharness, and unifies runtime obstacle handling
What's included
collision store so areas the static map falsely locks (e.g. Motherlode Mine,
polar kebbits) become walkable. Regenerated collision-map + blocked-edges /
transports / restrictions data.
logic out of
Rs2Walkerintostate/WalkerRouteState,recovery/RouteRecovery,and
geometry/WalkerPathGeometry. Recovery/route/geometry decisions are nowverifiable in milliseconds instead of only via a live walk.
obstacle/package (PlannedEdge,ObstacleResolution,ObstacleResolver/Registry,MineableResolver,TransportResolver,LiveScene). The recovery block's per-obstacle special casescollapse into one
resolveRecoveryObstacle(...)dispatch. Rockfall is fullymigrated end-to-end and the legacy handlers are deleted.
ahead-resolver probes, and the catalog-transport gate are now covered by 29
headless tests.
origin (fixes "clicks the far bank of a stepping stone" and skipped shortcuts).
Behavior changes
The recovery obstacle dispatch cutover changes how the walker resolves a blocked
frontier (rockfall mining + reachable transport/shortcut origins now go through the
unified path). It was live-verified — walked a door route, an MLM rockfall, and
the River Lum stepping stones with no regressions.
Testing
Notes for reviewers
Large diff (~68 files) but strangler-style: most of
Rs2Walkerstays intact; purelogic was lifted out behind same-signature wrappers, so existing callers and tests
are untouched. Docs:
docs/walker-audit.md,docs/walker-p2-unification.md.