From 979cf14adf774edab7d8dfc7587245c247c9479a Mon Sep 17 00:00:00 2001 From: beo3000 Date: Sun, 11 Jan 2026 10:42:38 +0100 Subject: [PATCH] fix save game state 1. HandleProcessThrowResult - Neuer Effect der bei IsGameOver den State sofort speichert 2. HandleExecuteGameActionSuccess - Neuer Effect der bei IsGameOver den State sofort speichert Diese Effects laufen nach dem Reducer, daher ist der State bereits aktualisiert wenn gespeichert wird. Bei F5-Reload wird jetzt der finale Spielzustand geladen. --- src/Koogle.Web/Store/GameState/GameEffects.cs | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/Koogle.Web/Store/GameState/GameEffects.cs b/src/Koogle.Web/Store/GameState/GameEffects.cs index f587445..4fe1a58 100644 --- a/src/Koogle.Web/Store/GameState/GameEffects.cs +++ b/src/Koogle.Web/Store/GameState/GameEffects.cs @@ -468,9 +468,10 @@ public class GameEffects afterThrowState.PinsKnocked); } - // If game is over, skip save (user must confirm end via UI) + // If game is over, don't start debounce timer - the ProcessThrowResultAction effect handles saving if (isGameOver) { + _saveDebounceTimer?.Dispose(); return; } @@ -483,6 +484,30 @@ public class GameEffects Timeout.Infinite); } + /// + /// Handles ProcessThrowResultAction - saves game state immediately when game is over. + /// This effect runs after the reducer, so the state is already updated. + /// + [EffectMethod] + public async Task HandleProcessThrowResult(ProcessThrowResultAction action, IDispatcher dispatcher) + { + // Only save immediately if game is over + if (!action.IsGameOver) return; + + var state = _gameState.Value; + if (!state.ActiveGameId.HasValue) return; + + var success = await SaveCurrentStateAsync(state.ActiveGameId.Value); + if (success) + { + _logger.LogInformation("Game over state saved for game {GameId}", state.ActiveGameId.Value); + } + else + { + _logger.LogWarning("Failed to save game over state for game {GameId}", state.ActiveGameId.Value); + } + } + /// /// Handles ExecuteGameActionAction - executes game-specific action via IGameLogicService. /// @@ -538,7 +563,7 @@ public class GameEffects dispatcher); } - // If game is over, skip save (user must confirm end via UI) + // If game is over, skip debounce - the ExecuteGameActionSuccessAction effect handles saving if (result.IsGameOver) { return; @@ -564,6 +589,30 @@ public class GameEffects } } + /// + /// Handles ExecuteGameActionSuccessAction - saves game state immediately when game is over. + /// This effect runs after the reducer, so the state is already updated. + /// + [EffectMethod] + public async Task HandleExecuteGameActionSuccess(ExecuteGameActionSuccessAction action, IDispatcher dispatcher) + { + // Only save immediately if game is over + if (!action.IsGameOver) return; + + var state = _gameState.Value; + if (!state.ActiveGameId.HasValue) return; + + var success = await SaveCurrentStateAsync(state.ActiveGameId.Value); + if (success) + { + _logger.LogInformation("Game over state saved (from action) for game {GameId}", state.ActiveGameId.Value); + } + else + { + _logger.LogWarning("Failed to save game over state (from action) for game {GameId}", state.ActiveGameId.Value); + } + } + private static ThrowPanelSnapshot CreateThrowPanelSnapshot(ThrowPanelState state) { return ThrowPanelSnapshot.FromPins(