405 lines
16 KiB
C#
405 lines
16 KiB
C#
using Fluxor;
|
|
using KoogleApp.Games;
|
|
using KoogleApp.Games.Training;
|
|
using KoogleApp.Hub;
|
|
using KoogleApp.Model;
|
|
using KoogleApp.Model.EventMessages;
|
|
using KoogleApp.Model.Framework;
|
|
using KoogleApp.Services;
|
|
using KoogleApp.Store.Game.Participants;
|
|
using KoogleApp.Store.Game.Setup;
|
|
using KoogleApp.Store.Game.ThrowTimer;
|
|
using KoogleApp.Store.Game.UndoRedo;
|
|
using Microsoft.AspNetCore.Components.Authorization;
|
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
using Microsoft.Win32.SafeHandles;
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Text.Json;
|
|
|
|
namespace KoogleApp.Store.Game.ThrowPanel
|
|
{
|
|
public class ThrowPanelEffects(
|
|
IMyEventAggregator eventAggregator,
|
|
ILogger<ThrowPanelEffects> logger,
|
|
HubConnectionService sharedHubService,
|
|
IState<ThrowPanelState> throwPanelState,
|
|
IState<SetupState> setupState,
|
|
AuthenticationStateProvider authenticationStateProvider,
|
|
IGameStatusDataService dataService,
|
|
IGameServiceFactory gameServiceFactory,
|
|
SessionStorage sessionStorage)
|
|
: IDisposable
|
|
{
|
|
private readonly string _dataFilePath = "ThrowPanelState.json";
|
|
private readonly Lock _lock = new Lock();
|
|
|
|
public void Dispose()
|
|
{
|
|
//_sharedHubService?.Dispose();
|
|
}
|
|
|
|
[EffectMethod]
|
|
public async Task HandleStartAction(StartStopAction startStopAction, IDispatcher dispatcher)
|
|
{
|
|
ParticipantsState? participantsState = null;
|
|
|
|
if (startStopAction.StartParams != null) // start action
|
|
{
|
|
dispatcher.Dispatch(new SetParticipatingPlayersAction(startStopAction.StartParams.Participants));
|
|
|
|
// here we just want to pass the participants with ThrowPanelStateChangedAction - therefore we create a copy of ParticipantsState - I think this is valid just for the startup of a new game
|
|
participantsState = new ParticipantsState(startStopAction.StartParams.Participants, []);
|
|
|
|
|
|
var gameService = gameServiceFactory.GetService(startStopAction.StartParams.KnownGameType);
|
|
var gameModel = gameService.InitGameModel(startStopAction.StartParams, dispatcher);
|
|
|
|
dispatcher.Dispatch(new GameModelChangedAction(gameModel.ConvertToState()));
|
|
|
|
|
|
var username = await GetUsername();
|
|
dataService.UpdateData(new GameStatus()
|
|
{
|
|
ThrowPanelState = throwPanelState.Value,
|
|
ParticipantsState = participantsState,
|
|
SetupModel = startStopAction.StartParams,
|
|
GameState = gameModel.ConvertToState(),
|
|
}, username);
|
|
|
|
|
|
}
|
|
else // stop action
|
|
{
|
|
var gameModel = GameService.FinalizeGameModel();
|
|
_gameService = null;
|
|
|
|
var username = await GetUsername();
|
|
await dataService.SaveToDatabaseAndReset(new GameStatus
|
|
{
|
|
ThrowPanelState = throwPanelState.Value with { ThrowPanelStateStatus = ThrowPanelStateStatus.GameEnd },
|
|
GameState = gameModel?.ConvertToState()
|
|
}, username);
|
|
dispatcher.Dispatch(new UpdateUndoRedoStateAction());
|
|
|
|
|
|
|
|
participantsState = new ParticipantsState();
|
|
dispatcher.Dispatch(new ThrowPanelStateChangedAction(throwPanelState.Value, true,
|
|
ParticipantsState: participantsState, GameState: gameModel?.ConvertToState()));
|
|
|
|
dispatcher.Dispatch(new ResetSetupStateAction());
|
|
}
|
|
|
|
// reset settings and all relevant states for the next game
|
|
|
|
dispatcher.Dispatch(new ParticipantsStateChangedAction(participantsState));
|
|
//dispatcher.Dispatch(new ResetSetupStateAction()); // TODO: not reduced
|
|
}
|
|
|
|
[EffectMethod]
|
|
public Task HandleToggleAllPinsAction(ToggleAllPinsAction stopAction, IDispatcher dispatcher)
|
|
{
|
|
dispatcher.Dispatch(new ThrowPanelStateChangedAction(throwPanelState.Value,
|
|
false, null, null));
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
[EffectMethod]
|
|
public Task HandleTogglePinValueAction(TogglePinValueAction stopAction, IDispatcher dispatcher)
|
|
{
|
|
dispatcher.Dispatch(new ThrowPanelStateChangedAction(throwPanelState.Value,
|
|
false, null, null));
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
[EffectMethod]
|
|
public Task HandleUpdatePinStateByNumberAction(UpdatePinStateByNumberAction stopAction, IDispatcher dispatcher)
|
|
{
|
|
dispatcher.Dispatch(new ThrowPanelStateChangedAction(throwPanelState.Value,
|
|
false, null, null));
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
[EffectMethod]
|
|
public Task HandleToggleBellAction(ToggleBellAction stopAction, IDispatcher dispatcher)
|
|
{
|
|
dispatcher.Dispatch(new ThrowPanelStateChangedAction(throwPanelState.Value,
|
|
false, null, null));
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
//ThrowBeforeUpdateAction
|
|
//ThrowUpdateAction
|
|
//ThrowAfterUpdate
|
|
[EffectMethod]
|
|
public async Task HandleEnsureBeforeThrowStatusAction(EnsureBeforeThrowStatusAction throwAction, IDispatcher dispatcher)
|
|
{
|
|
dispatcher.Dispatch(new ThrowPanelStateChangedAction(throwPanelState.Value, true,
|
|
throwAction.ParticipantsState, throwAction.GameState));
|
|
}
|
|
|
|
private IGameService? _gameService = null;
|
|
|
|
private IGameService? GameService
|
|
{
|
|
get
|
|
{
|
|
if (_gameService == null)
|
|
{
|
|
_gameService = gameServiceFactory.GetService(dataService.StartParams.KnownGameType);
|
|
|
|
}
|
|
return _gameService;
|
|
}
|
|
}
|
|
|
|
[EffectMethod]
|
|
public async Task HandleThrowAction(ThrowAction throwAction, IDispatcher dispatcher)
|
|
{
|
|
var beforeStates = dataService.GetCurrentData();
|
|
|
|
// change ThrowStatus and save - here we need to save the old state, before it is changed by ThrowUpdateAction, so that undo/redo works correctly
|
|
dispatcher.Dispatch(new EnsureBeforeThrowStatusAction(throwAction.ParticipantsState, beforeStates.Status.GameState));
|
|
|
|
// perform throw - we will get the new state after resetting all pins for the next throw.
|
|
// For the calculation of the game-progress in HandleThrow nextState if normally not useful
|
|
var nextState = beforeStates.Status.ThrowPanelState.CalculateNextPanelState();
|
|
//dispatcher.Dispatch(new ThrowUpdateAction(nextState));
|
|
|
|
var generalGameProgress = new GameProgress(
|
|
BeforeThrowState: beforeStates.Status?.ThrowPanelState,
|
|
BeforeParticipantsState: throwAction.ParticipantsState,
|
|
AfterThrowState: throwPanelState.Value,
|
|
AfterParticipantsState: throwAction.ParticipantsState,
|
|
StartParams: dataService.StartParams,
|
|
GameModel: beforeStates.Status.GameState.ConvertToModel(),
|
|
NextThrowState: nextState);
|
|
|
|
dispatcher.Dispatch(generalGameProgress); // currently unused
|
|
|
|
//var participants = throwAction.ParticipantsState;
|
|
|
|
//var gameSpecificProgress = generalGameProgress;
|
|
var gameSpecificProgress = GameService.HandleThrow(generalGameProgress, dispatcher);
|
|
|
|
//foreach (var action in actions)
|
|
//{
|
|
// if (action is SelectedNextPlayerIdsAction)
|
|
// {
|
|
// participants = ((SelectedNextPlayerIdsAction)action).ParticipantsState;
|
|
// }
|
|
// dispatcher.Dispatch(action);
|
|
//}
|
|
var gameModelState = gameSpecificProgress.GameModel.ConvertToState();
|
|
dispatcher.Dispatch(new GameModelChangedAction(gameModelState));
|
|
|
|
await ShowBoardForSeconds(dispatcher);
|
|
|
|
|
|
// save the new state after the throw
|
|
dispatcher.Dispatch(new TriggerNewThrowPanelStateChangedAction(true,
|
|
gameSpecificProgress.AfterParticipantsState, gameModelState,
|
|
gameSpecificProgress.NextThrowState)); // this will trigger ThrowPanelStateChangedAction with the new state (not current _throwPanelState.Value)
|
|
}
|
|
|
|
[EffectMethod]
|
|
public async Task HandleGameButtonAction(GameButtonAction action, IDispatcher dispatcher)
|
|
{
|
|
var beforeStates = dataService.GetCurrentData();
|
|
|
|
var beforeProgress = new GameProgress(
|
|
BeforeThrowState: beforeStates.Status?.ThrowPanelState,
|
|
BeforeParticipantsState: beforeStates.Status.ParticipantsState,
|
|
AfterThrowState: throwPanelState.Value,
|
|
AfterParticipantsState: beforeStates.Status.ParticipantsState,
|
|
StartParams: dataService.StartParams,
|
|
GameModel: beforeStates.Status.GameState.ConvertToModel(),
|
|
NextThrowState: null);
|
|
var afterProgress = GameService.HandleGameButton(action.CallbackName, beforeProgress, dispatcher);
|
|
|
|
dispatcher.Dispatch(new TriggerNewThrowPanelStateChangedAction(true,
|
|
afterProgress.AfterParticipantsState, afterProgress.GameModel.ConvertToState(),
|
|
afterProgress.NextThrowState)); // this will trigger ThrowPanelStateChangedAction with the new state (not current _throwPanelState.Value)
|
|
}
|
|
|
|
private async Task ShowBoardForSeconds(IDispatcher dispatcher)
|
|
{
|
|
dispatcher.Dispatch(new StartTimerAction(5));
|
|
await eventAggregator.PublishAsync(new GameViewChangedMessage(GameView.Board) { Scope = EventScope.Circuit });
|
|
}
|
|
|
|
[EffectMethod]
|
|
public Task HandleTriggerNewThrowPanelStateChangedAction(TriggerNewThrowPanelStateChangedAction action,
|
|
IDispatcher dispatcher)
|
|
{
|
|
if (action.NextModifiedThrowPanelState != null)
|
|
{
|
|
// Standardverhalten der Bahn wurde überschrieben, z.B. Bild gestellt oder sonstiges aus der game-spezifischen Logik
|
|
dispatcher.Dispatch(new OverwriteThrowPanelStateAction(action.NextModifiedThrowPanelState));
|
|
dispatcher.Dispatch(new ThrowPanelStateChangedAction(action.NextModifiedThrowPanelState, action.SaveToDb,
|
|
action.ParticipantsState, action.GameState));
|
|
}
|
|
else
|
|
{
|
|
dispatcher.Dispatch(new ThrowPanelStateChangedAction(throwPanelState.Value, action.SaveToDb,
|
|
action.ParticipantsState, action.GameState));
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
[EffectMethod]
|
|
public async Task HandleConnectToHubAction(ConnectToHubAction action, IDispatcher dispatcher)
|
|
{
|
|
try
|
|
{
|
|
await sharedHubService.ConnectToHub(dispatcher);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Failed to connect to hub");
|
|
//dispatcher.Dispatch(new HubConnectionFailedAction(ex.Message));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[EffectMethod]
|
|
public async Task HandelThrowPanelStateChangedAction(ThrowPanelStateChangedAction action,
|
|
IDispatcher dispatcher)
|
|
{
|
|
dispatcher.Dispatch(new SaveThrowPanelStateAction(action.State));
|
|
|
|
dispatcher.Dispatch(new BroadcastThrowPanelStateAction(action.State));
|
|
|
|
if (action.SaveToDb)
|
|
{
|
|
var username = await GetUsername();
|
|
dataService.UpdateData(new GameStatus
|
|
{
|
|
ThrowPanelState = action.State,
|
|
ParticipantsState = action.ParticipantsState,
|
|
GameState = action.GameState
|
|
}, username);
|
|
|
|
// here we need to invalidate _dataFilePath and _sessionStorage, because with a new throw, state data must be provided by dataservice
|
|
DeleteAllSessionsFile(dispatcher);
|
|
|
|
dispatcher.Dispatch(new UpdateUndoRedoStateAction());
|
|
}
|
|
}
|
|
|
|
private static void DeleteAllSessionsFile(IDispatcher dispatcher)
|
|
{
|
|
dispatcher.Dispatch(new DeleteThrowPanelSessionAction());
|
|
dispatcher.Dispatch(new DeleteParticipantsSessionAction());
|
|
}
|
|
|
|
[EffectMethod]
|
|
public async Task HandleDeleteThrowPanelSessionAction(DeleteThrowPanelSessionAction allStatesFromSessionAction, IDispatcher dispatcher)
|
|
{
|
|
await sessionStorage.SetThrowPanelStateAsync(null);
|
|
|
|
if (File.Exists(_dataFilePath))
|
|
{
|
|
await Task.Delay(100);
|
|
try
|
|
{
|
|
File.Delete(_dataFilePath);
|
|
}
|
|
catch (Exception)
|
|
{ }
|
|
}
|
|
}
|
|
|
|
[EffectMethod]
|
|
public async Task HandelLoadAllStatesFromSessionAction(LoadAllStatesFromSessionAction allStatesFromSessionAction, IDispatcher dispatcher)
|
|
{
|
|
dispatcher.Dispatch(new LoadThrowPanelStatesFromSessionAction());
|
|
dispatcher.Dispatch(new LoadParticipantsStatesFromSessionAction());
|
|
dispatcher.Dispatch(new LoadSetupStatesFromSessionAction());
|
|
dispatcher.Dispatch(new LoadGameStatesFromDataServiceAction());
|
|
}
|
|
|
|
[EffectMethod]
|
|
public async Task HandelLoadThrowPanelStatesFromSessionAction(LoadThrowPanelStatesFromSessionAction allStatesFromSessionAction, IDispatcher dispatcher)
|
|
{
|
|
var state = await sessionStorage.GetThrowPanelStateAsync();
|
|
|
|
if (state == null)
|
|
{
|
|
try
|
|
{
|
|
if (File.Exists(_dataFilePath))
|
|
{
|
|
var json = await File.ReadAllTextAsync(_dataFilePath);
|
|
state = JsonSerializer.Deserialize<ThrowPanelState>(json);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
// TODO error handling
|
|
}
|
|
}
|
|
|
|
if (state == null)
|
|
{
|
|
var data = dataService.GetCurrentData();
|
|
if (data.Status != null)
|
|
{
|
|
state = data.Status.ThrowPanelState;
|
|
}
|
|
|
|
|
|
dispatcher.Dispatch(new UpdateUndoRedoStateAction());
|
|
}
|
|
|
|
if (state != null)
|
|
{
|
|
dispatcher.Dispatch(new ThrowPanelStateLoadedAction(state));
|
|
}
|
|
}
|
|
|
|
[EffectMethod]
|
|
public async Task HandelSaveThrowPanelStateAction(SaveThrowPanelStateAction action, IDispatcher dispatcher)
|
|
{
|
|
await sessionStorage.SetThrowPanelStateAsync(action.State);
|
|
|
|
try
|
|
{
|
|
var json = JsonSerializer.Serialize(action.State, new JsonSerializerOptions
|
|
{
|
|
WriteIndented = true
|
|
});
|
|
await File.WriteAllTextAsync(_dataFilePath, json);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// TODO error handling
|
|
}
|
|
}
|
|
|
|
[EffectMethod]
|
|
public async Task HandelBroadcastThrowPanelStateAction(BroadcastThrowPanelStateAction action, IDispatcher dispatcher)
|
|
{
|
|
await sharedHubService.BroadcastThrowPanelState(action.State, dispatcher);
|
|
}
|
|
|
|
|
|
private async Task<string> GetUsername()
|
|
{
|
|
var authState = await authenticationStateProvider.GetAuthenticationStateAsync();
|
|
var user = authState.User;
|
|
var isAuthenticated = user.Identity?.IsAuthenticated ?? false;
|
|
return isAuthenticated ? user.Identity?.Name : "Gast";
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
}
|