KoogleApp/KoogleApp/Store/Game/ThrowPanel/Effects.cs

318 lines
12 KiB
C#

using Fluxor;
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.Win32.SafeHandles;
using System;
using System.Text.Json;
namespace KoogleApp.Store.Game.ThrowPanel
{
public class ThrowPanelEffects : IDisposable
{
private readonly ILogger<ThrowPanelEffects> _logger;
private readonly HubConnectionService _sharedHubService;
private readonly IState<ThrowPanelState> _throwPanelState;
private readonly IState<SetupState> _setupState;
private readonly SessionStorage _sessionStorage;
private readonly string _dataFilePath = "ThrowPanelState.json";
private readonly AuthenticationStateProvider _authenticationStateProvider;
private readonly IGameStatusDataService _dataService;
private readonly Lock _lock = new Lock();
private readonly IMyEventAggregator _eventAggregator;
public ThrowPanelEffects(
IMyEventAggregator eventAggregator,
ILogger<ThrowPanelEffects> logger,
HubConnectionService sharedHubService,
IState<ThrowPanelState> throwPanelState,
IState<SetupState> setupState,
AuthenticationStateProvider authenticationStateProvider,
IGameStatusDataService dataService,
SessionStorage sessionStorage)
{
_logger = logger;
_sharedHubService = sharedHubService;
_throwPanelState = throwPanelState;
_sessionStorage = sessionStorage;
_dataService = dataService;
_authenticationStateProvider = authenticationStateProvider;
_eventAggregator = eventAggregator;
}
public void Dispose()
{
//_sharedHubService?.Dispose();
}
//[EffectMethod(typeof(StartStopAction))]
[EffectMethod]
public async Task HandleStartAction(StartStopAction startStopAction, IDispatcher dispatcher)
{
ParticipantsState? participantsState = null;
SetupState? setupState = 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, []);
setupState = new SetupState(startStopAction.StartParams.ThrowMode,
startStopAction.StartParams.ThrowsPerRound, startStopAction.StartParams.Participants,
ParticipantsMode: startStopAction.StartParams.ParticipantsMode);
var username = await GetUsername();
_dataService.Initialize(new GameStatus()
{
ThrowPanelState = _throwPanelState.Value,
ParticipantsState = participantsState,
SetupState = setupState
}, username);
}
else // stop action
{
var username = await GetUsername();
await _dataService.SaveToDatabaseAndReset(new GameStatus
{
ThrowPanelState = _throwPanelState.Value with { ThrowPanelStateStatus = ThrowPanelStateStatus.GameEnd },
}, username);
dispatcher.Dispatch(new UpdateUndoRedoStateAction());
participantsState = new ParticipantsState();
}
// reset settings and all relevant states for the next game
dispatcher.Dispatch(new ThrowPanelStateChangedAction(_throwPanelState.Value, true, ParticipantsState: participantsState));
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));
return Task.CompletedTask;
}
[EffectMethod]
public Task HandleTogglePinValueAction(TogglePinValueAction stopAction, IDispatcher dispatcher)
{
dispatcher.Dispatch(new ThrowPanelStateChangedAction(_throwPanelState.Value, false, null));
return Task.CompletedTask;
}
[EffectMethod]
public Task HandleUpdatePinStateByNumberAction(UpdatePinStateByNumberAction stopAction, IDispatcher dispatcher)
{
dispatcher.Dispatch(new ThrowPanelStateChangedAction(_throwPanelState.Value, false, null));
return Task.CompletedTask;
}
[EffectMethod]
public Task HandleToggleBellAction(ToggleBellAction stopAction, IDispatcher dispatcher)
{
dispatcher.Dispatch(new ThrowPanelStateChangedAction(_throwPanelState.Value, false, 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));
}
[EffectMethod]
public async Task HandleThrowAction(ThrowAction throwAction, IDispatcher dispatcher)
{
await ShowBoardForSeconds(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));
// perform throw
dispatcher.Dispatch(new ThrowUpdateAction(throwAction.LeftSink, throwAction.RightSink));
var afterState = _throwPanelState.Value;
dispatcher.Dispatch(new GameLogicAction(beforeStates.Status?.ThrowPanelState, beforeStates.Status?.ParticipantsState, afterState, throwAction.ParticipantsState));
// save again - save the new state after the throw
dispatcher.Dispatch(new TriggerNewThrowPanelStateChangedAction(true, throwAction.ParticipantsState)); // 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)
{
dispatcher.Dispatch(new ThrowPanelStateChangedAction(_throwPanelState.Value, action.SaveToDb, action.ParticipantsState));
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
}, 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());
}
[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";
}
}
}