From 92b2e31577ccdb488a457978c8183323d9485c99 Mon Sep 17 00:00:00 2001 From: grom Date: Wed, 21 Oct 2026 21:37:12 -0600 Subject: [PATCH] app: order the zone events after the world-map feed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `feed_zone_events` fires `ZONE_CHANGED`, `ZONE_CHANGED_NEW_AREA` and `MINIMAP_ZONE_CHANGED` inline into the VM. pfQuest answers all three by calling `SetMapToCurrentZone()` whenever the map is closed (`pfQuest/map.lua:1466`), and that verb reads `worldmap.player_zone` — which `ui_world_map::feed_world_map` publishes, in a system that was ordered only `.after(UiInput)`, exactly as this one was. Nothing ordered the two against each other. Run the wrong way round, `SetMapToCurrentZone()` reselects the zone the player has just left, because the feed has not published the new one yet. Every map-derived overlay then stays a zone behind until something else reselects — opening the world map, say, whose `OnShow` calls the same verb a frame later when the feed has caught up. There is nothing to order in the reference: `SetMapToCurrentZone` calls the resolver `0x4a6650`, which reads the player's current area live. Reading a per-frame feed instead is ours, so the ordering is ours to state. Stated as a set rather than a bare `.after`, since the next consumer of the published zone will owe the same edge. Found while chasing grom's report that pfQuest's minimap icons do not re-seat on a zone change until the world map is opened — which is this shape. Not confirmed as that bug's cause: it is an unpinned order, so which way it fell before this commit is not something the logs record. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn --- crates/benilla-app/src/area.rs | 9 ++++++++- crates/benilla-app/src/ui_world_map.rs | 12 +++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/crates/benilla-app/src/area.rs b/crates/benilla-app/src/area.rs index 2f49d4f6..1d9dfab5 100644 --- a/crates/benilla-app/src/area.rs +++ b/crates/benilla-app/src/area.rs @@ -261,7 +261,14 @@ impl Plugin for AreaPlugin { // spurious splash. feed_zone_events .after(crate::ui_script::UiInput) - .after(benilla_world::terrain_stream::AreaAuthoritySet), + .after(benilla_world::terrain_stream::AreaAuthoritySet) + // `ZONE_CHANGED*` is the signal pfQuest answers with `SetMapToCurrentZone()` + // (`pfQuest/map.lua:1466`), and that verb reads the zone + // `ui_world_map::feed_world_map` publishes. Unordered, the two could run + // either way round, and the wrong way reselects the zone the player just + // left — leaving every map-derived overlay a zone behind until something + // else reselects. See `WorldMapFeedSet`. + .after(crate::ui_world_map::WorldMapFeedSet), ); } } diff --git a/crates/benilla-app/src/ui_world_map.rs b/crates/benilla-app/src/ui_world_map.rs index 86a36545..662bdce9 100644 --- a/crates/benilla-app/src/ui_world_map.rs +++ b/crates/benilla-app/src/ui_world_map.rs @@ -878,6 +878,16 @@ fn dev_map_jump( }); } +/// The set of [`feed_world_map`], which publishes the player's `(continent, zone)` into the VM. +/// +/// Anything that fires a Lua event an addon answers by calling `SetMapToCurrentZone()` must order +/// **after** this set, because that verb reads the zone this feed publishes +/// (`benilla_ui::script::worldmap`'s `player_zone`) and a stale read reselects the zone the player +/// just left. In the reference there is nothing to order: `SetMapToCurrentZone` calls the resolver +/// `0x4a6650`, which reads the player's current area live, where ours reads a per-frame feed. +#[derive(SystemSet, Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub(crate) struct WorldMapFeedSet; + /// The world-map data feed. pub(crate) struct WorldMapUiPlugin; @@ -887,7 +897,7 @@ impl Plugin for WorldMapUiPlugin { Update, ( // After the script tick: a selection changed this tick projects on the next. - feed_world_map.after(UiInput), + feed_world_map.after(UiInput).in_set(WorldMapFeedSet), // After the script tick, so the jump hit-tests this tick's frame rects. dev_map_jump.after(UiInput), ),