MapSquare.decode_locs reverses the loc list twice, and interaction targeting depends on it #24

Closed
opened 2026-07-27 22:32:09 +00:00 by sickday · 0 comments
Owner

Cache.MapSquare.decode_locs/1 reverses the loc list twice, and the two cancel.

locs/3 accumulates with acc ++ Enum.reverse(placed), which is in file order,
and then returns Enum.reverse(acc) — so the decoded list is in reverse file
order. World.Collision.index_locs/3 then builds each tile's list with
Map.update(acc, key, [entry], &[entry | &1]), prepending, which reverses it
again. World.Collision.locs_at/1 therefore returns file order by accident.

That ordering is load-bearing. World.object_shape/2 and toggle_door/6 both
resolve a click with Enum.find/2 over locs_at/1, so when several locs share a
tile, the order decides which one the player interacted with. Correcting either
reversal on its own — and both look like plain bugs in isolation — silently
changes interaction targeting across the world with nothing failing.

Separately, acc ++ ... inside the loop is quadratic in the number of loc ids on
a square. Squares carry a few thousand locs and the world has 1,513 of them.

Shape of a fix

Build the list in one direction, drop the compensating reverse, and pin the
resulting order with a test that decodes a known multi-loc tile and asserts
which loc a click resolves to.

`Cache.MapSquare.decode_locs/1` reverses the loc list twice, and the two cancel. `locs/3` accumulates with `acc ++ Enum.reverse(placed)`, which is in file order, and then returns `Enum.reverse(acc)` — so the decoded list is in reverse file order. `World.Collision.index_locs/3` then builds each tile's list with `Map.update(acc, key, [entry], &[entry | &1])`, prepending, which reverses it again. `World.Collision.locs_at/1` therefore returns file order by accident. That ordering is load-bearing. `World.object_shape/2` and `toggle_door/6` both resolve a click with `Enum.find/2` over `locs_at/1`, so when several locs share a tile, the order decides which one the player interacted with. Correcting either reversal on its own — and both look like plain bugs in isolation — silently changes interaction targeting across the world with nothing failing. Separately, `acc ++ ...` inside the loop is quadratic in the number of loc ids on a square. Squares carry a few thousand locs and the world has 1,513 of them. ### Shape of a fix Build the list in one direction, drop the compensating reverse, and pin the resulting order with a test that decodes a known multi-loc tile and asserts which loc a click resolves to.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Revenant/Server#24
No description provided.