Skip to content

navigation: re-copy the rooms around the player on a cached getPath - #98

Merged
ResurrectedTrader merged 3 commits into
mainfrom
getpath-live-collision
Oct 8, 2026
Merged

ResurrectedTrader merged 3 commits into
mainfrom
getpath-live-collision

Conversation

@ResurrectedTrader

Copy link
Copy Markdown
Owner

d2bs: getPath reads collision straight from the rooms on every call (reference/d2bs/Map/Diablo_II/ActMap.cpp, ActMap::GetMapData -> getCollFromRoom, which reads room->pRoom1->Coll). A path asked for after a door opens goes through the door.

d2bsng before: FindPath (src/runtime/components/navigation/Pathfinder.cpp) builds a level's collision slab once and keeps it per thread until the level or the map seed changes. A code comment accepted that "doors opening / collision changes inside the same level aren't reflected". So kolbot's Pather.openDoors followed by a repath still saw the door closed.

Fix: on a cache hit, FindPath re-copies the player's room and its nearby rooms (Unit::GetRoom + Room::GetNearby) into the cached grids, primary or secondary by level, and re-applies the barricade-tower marks for those levels. On the client, collision only changes where there are units, which is around the player, and those rooms are already loaded, so the refresh adds no room data. The full-level build still happens once per level and game, which keeps the burst cost the cache was added for (e.g. Attack.getIntoPosition's fan-out) low. Navigation still depends only on contract + utils.

BuildLevelGrid is split into CopyRoomCollision / MarkBarricades helpers so the refresh uses the same copy. The test fakes gain a Unit::GetRoom stub (the test suite links Pathfinder.cpp).

Cost: each cached call now copies about 10-25 rooms of 40x40 cells (tens of KB) under one read lock - microseconds, against 1-40 ms per A* in the existing benchmarks. The benchmarks (js_tests.exe -tc="Benchmark*,Kurast*,Real*") run FindPathOnGrid on fixture grids and do not exercise this path; their numbers are unchanged (e.g. real-map walk 1.0-4.1 ms, Flayer Jungle -> Travincal 38 ms).

Restores: repathing after opening a door (kolbot Pather.openDoors, then getPath again) finds the path through the opened door.

There is no unit test: the fakes have no room model to change collision in. Not live-tested. Built Win32 Release, build.ps1 test passes, check-format clean.

🤖 Generated with Claude Code

Reference d2bs reads collision from the rooms on every getPath
(ActMap::GetMapData -> getCollFromRoom), so a path asked for after a door
opens goes through it. FindPath kept each level's collision slab for the
whole game, so a repath after Pather.openDoors still saw the door closed.

On a cache hit FindPath now re-copies the player's room and its nearby
rooms into the cached primary / secondary grids (barricade marks
included). Collision only changes where the game has units, which on the
client is around the player; those rooms are loaded, so the refresh adds
no room data and costs a handful of room copies per call.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
CopyRoomCollision and MarkBarricades become LevelGrid members. Barricades
are marked once, after every room's collision is copied, so a later room
copy can no longer overwrite a tower mark; MarkBarricades checks the
level's overlay itself.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ResurrectedTrader

Copy link
Copy Markdown
Owner Author

Done in the latest commit:

  • CopyRoomCollision and MarkBarricades are now LevelGrid members.
  • MarkBarricades takes the rooms and runs once, after every room's collision is copied, both in the full build and in the per-call refresh (grouped per grid), so a later room copy can't overwrite a tower mark. It checks the level's overlay itself.

Perf: the pathfinding benchmarks (150 timings of our A*) are within noise against main, +0.9% total, while the unchanged reference A* in the same runs swings by up to ±13%. The benchmarks time the search on prebuilt grids, so they don't exercise the per-call refresh this PR adds.

Build, tests (171/171) and format check pass.

A level's rooms do not overlap and a tower sits inside its own room, so
marking right after the room's copy is always correct. CopyRoomCollision
does both; MarkBarricades becomes a private per-room helper, and the
callers just copy rooms.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ResurrectedTrader

Copy link
Copy Markdown
Owner Author

Reworked as discussed:

  • CopyRoomCollision(room) copies the room and then marks its barricade towers; MarkBarricades(room) is a private per-room helper that returns at once on levels without the overlay.
  • That is safe per room: a level's rooms don't overlap and a tower sits inside its own room, so only re-copying that same room could overwrite its marks.
  • The build loop and the per-call refresh are now just CopyRoomCollision over their rooms.

Perf, our A* across 150 benchmark timings, main and this branch run back to back: +1.5% one round, -3.8% the next, i.e. noise (the benchmarks don't call this code).

Build, tests (171/171) and format check pass.

@ResurrectedTrader
ResurrectedTrader merged commit 7e24404 into main Oct 8, 2026
1 check passed
@ResurrectedTrader
ResurrectedTrader deleted the getpath-live-collision branch October 8, 2026 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant