refactor(engine): ambiguity burn-down batch 2 — SettingsResource cleared, 198 → 171
Test / test (pull_request) Successful in 28m7s
Test / test (pull_request) Successful in 28m7s
New SettingsMutation set: the per-frame settings mutators (handle_volume_keys → record_window_geometry_changes → persist_window_geometry_after_debounce) run as a deterministic chain ordered before GameMutation, so every reader already after GameMutation observes the current frame's settings transitively. The four readers outside that ordering (modal enter-speed chain, focus-ring pulse, HUD avatar, and the game plugin's pre-mutation chain) are ordered after the set explicitly. SettingsResource ambiguities: 21 → 0. Baseline ratchets 198 → 171. Refs #143 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -265,6 +265,7 @@ impl Plugin for GamePlugin {
|
||||
.ambiguous_with(NewGameRequestWriters),
|
||||
)
|
||||
.chain()
|
||||
.after(crate::settings_plugin::SettingsMutation)
|
||||
.before(GameMutation),
|
||||
)
|
||||
.init_resource::<AutoSaveTimer>()
|
||||
|
||||
@@ -473,7 +473,13 @@ impl Plugin for HudPlugin {
|
||||
apply_hud_visibility.before(LayoutSystem::UpdateOnResize),
|
||||
)
|
||||
.add_systems(Update, restore_hud_on_modal)
|
||||
.add_systems(Update, (update_hud_avatar, handle_avatar_button))
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
update_hud_avatar.after(crate::settings_plugin::SettingsMutation),
|
||||
handle_avatar_button,
|
||||
),
|
||||
)
|
||||
.add_systems(Update, update_won_previously.after(GameMutation))
|
||||
.add_systems(Update, announce_auto_complete.after(GameMutation))
|
||||
.add_systems(
|
||||
|
||||
@@ -38,7 +38,7 @@ mod tests {
|
||||
/// ordering edge — add `.before`/`.after` (order matters) or
|
||||
/// `.ambiguous_with` (provably order-independent) at the registration
|
||||
/// site. When triage lowers the real count, lower this constant too.
|
||||
const AMBIGUITY_BASELINE: usize = 198;
|
||||
const AMBIGUITY_BASELINE: usize = 171;
|
||||
|
||||
fn cluster_app() -> App {
|
||||
let mut app = App::new();
|
||||
|
||||
@@ -79,6 +79,14 @@ pub struct PendingWindowGeometry {
|
||||
#[derive(Message, Debug, Clone)]
|
||||
pub struct SettingsChangedEvent(pub Settings);
|
||||
|
||||
/// System set for the systems that mutate [`SettingsResource`] every frame
|
||||
/// (hotkeys and window-geometry persistence). Ordered before
|
||||
/// [`crate::game_plugin::GameMutation`]; readers of settings should sit
|
||||
/// after this set (directly, or transitively via `.after(GameMutation)`)
|
||||
/// so they observe the current frame's settings deterministically (#143).
|
||||
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SettingsMutation;
|
||||
|
||||
/// Marker on the root Settings panel entity.
|
||||
#[derive(Component, Debug)]
|
||||
pub struct SettingsPanel;
|
||||
@@ -372,15 +380,31 @@ impl Plugin for SettingsPlugin {
|
||||
// also runs cleanly under `MinimalPlugins` (tests).
|
||||
.add_message::<WindowResized>()
|
||||
.add_message::<WindowMoved>()
|
||||
// Settings changes land before game logic runs: the mutator
|
||||
// chain (volume keys → geometry record → geometry persist) is a
|
||||
// deterministic spine, and the whole set precedes GameMutation so
|
||||
// every reader already ordered after GameMutation sees this
|
||||
// frame's settings transitively (ambiguity burn-down, #143).
|
||||
.configure_sets(
|
||||
Update,
|
||||
SettingsMutation.before(crate::game_plugin::GameMutation),
|
||||
)
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
handle_volume_keys,
|
||||
record_window_geometry_changes,
|
||||
persist_window_geometry_after_debounce,
|
||||
)
|
||||
.chain()
|
||||
.in_set(SettingsMutation),
|
||||
)
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
toggle_settings_screen,
|
||||
scroll_settings_panel,
|
||||
crate::ui_modal::touch_scroll_panel::<SettingsPanelScrollable>,
|
||||
record_window_geometry_changes,
|
||||
persist_window_geometry_after_debounce,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ impl Plugin for UiFocusPlugin {
|
||||
clear_hud_focus_on_unhover,
|
||||
handle_focus_keys,
|
||||
update_focus_overlay,
|
||||
pulse_focus_overlay,
|
||||
pulse_focus_overlay.after(crate::settings_plugin::SettingsMutation),
|
||||
)
|
||||
.chain(),
|
||||
);
|
||||
|
||||
@@ -695,7 +695,8 @@ impl Plugin for UiModalPlugin {
|
||||
advance_modal_enter,
|
||||
paint_modal_buttons,
|
||||
)
|
||||
.chain(),
|
||||
.chain()
|
||||
.after(crate::settings_plugin::SettingsMutation),
|
||||
);
|
||||
// Click-outside-to-dismiss is independent of the open
|
||||
// animation chain — it reads `just_pressed(Left)` and runs
|
||||
|
||||
Reference in New Issue
Block a user