fix(multi): resolve 26 bugs found in comprehensive codebase review
Build and Deploy / build-and-push (push) Successful in 3m40s

Core fixes (issues #12, #13, #22):
- #12: undo now preserves score delta instead of restoring snapshot score
- #13: take_from_foundation defaults to false (non-standard house rule)
- #22: check_win validates full suit sequence, not just card count

Engine fixes:
- #8:  replay keyboard input guard against non-replay state
- #9:  help modal scrims.is_empty() guard added
- #10: settings modal scrims.is_empty() guard added
- #11: sync_plugin builds payload at poll time (not task-spawn time)
- #14: server replay mode case-sensitivity fix ("Classic")
- #15: play_by_seed_plugin confirmed flag set to true on launch
- #16: replay back-step debounce via Local<bool> + StateChangedEvent;
       register StateChangedEvent in ReplayOverlayPlugin (fixes 52 tests)
- #17: time-attack timer ignores win-summary overlay
- #18: HUD dropdown glyphs U+25BE → U+2193 (FiraMono-safe arrow)
- #19: theme plugin applies immediate visual update on A→B→A switch
- #20: SyncAuthError / SyncBusyOverlay split into separate entities so
       auth errors are visible after busy overlay is hidden
- #21: handle_forfeit ordered before update_stats_on_new_game
- #23: server merge uses correct avg_time_seconds and games_lost math
- #24: win_summary migrated to ModalScrim pattern
- #25: card_animation apply_deferred between animation systems
- #26: cursor_plugin HashMap access uses .get() with fallback
- #27: auto_complete mid-sequence deactivation guard
- #28: feedback_anim SettleAnim ordered before FoundationFlourish
- #29: achievement_plugin iterates all win events; adds scrims guard
- #30: leaderboard modal scrims.is_empty() guard added
- #31: server auth tmp file cleanup on rename failure
- #32: sync_setup modal scrims.is_empty() guard added
- #33: font_plugin uses match fallback; TokioRuntimeResource graceful
       current-thread fallback on runtime init failure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-05-19 13:14:47 -07:00
parent 6d061d23a1
commit 7840ef9eb2
24 changed files with 374 additions and 171 deletions
+20
View File
@@ -182,12 +182,16 @@ fn sync_card_image_set_with_active_theme(
mut events: MessageReader<AssetEvent<CardTheme>>,
active: Option<Res<ActiveTheme>>,
themes: Res<Assets<CardTheme>>,
asset_server: Option<Res<AssetServer>>,
mut card_image_set: Option<ResMut<CardImageSet>>,
mut state_events: MessageWriter<StateChangedEvent>,
) {
let Some(active) = active else { return };
let active_id = active.0.id();
let mut should_sync = false;
// Consume asset events — covers the normal first-load path.
for ev in events.read() {
let id = match ev {
AssetEvent::LoadedWithDependencies { id }
@@ -198,6 +202,22 @@ fn sync_card_image_set_with_active_theme(
should_sync = true;
}
}
// A→B→A switch: Bevy does not re-fire LoadedWithDependencies for a
// handle whose asset is already cached. Detect this by checking that
// `ActiveTheme` itself changed this frame (the resource was just
// replaced by `react_to_settings_theme_change`) and the underlying
// asset is already fully loaded. If so, sync immediately rather than
// waiting for an event that will never arrive.
if !should_sync
&& active.is_changed()
&& asset_server
.as_ref()
.is_some_and(|as_| as_.is_loaded_with_dependencies(active.0.id()))
{
should_sync = true;
}
if !should_sync {
return;
}