This commit is contained in:
Christian Kauer 2023-12-29 14:16:05 +01:00
parent f41a373e25
commit a2e432a9c2
11 changed files with 287 additions and 29 deletions

View File

@ -1,9 +1,11 @@
using GameData.Repository; using GameData.Model;
using GameData.Repository;
using GameModel.Settings; using GameModel.Settings;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Reflection; using System.Reflection;
@ -134,17 +136,26 @@ namespace GameData
return res; return res;
} }
public async Task<T> Post<T>(object payload, string serviceUrl, bool suppressExceptions = true) /// <summary>
/// Create an item
/// https://docs.directus.io/reference/items.html#create-an-item
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="payload"></param>
/// <param name="serviceUrl"></param>
/// <param name="suppressExceptions"></param>
/// <returns></returns>
public T Post<T>(object payload, string serviceUrl, bool suppressExceptions = false)
{ {
T res = default; T res = default;
try try
{ {
var response = await client.SendAsync(GetHttpRequestMessage(HttpMethod.Post, serviceUrl, payload)); var response = client.Send(GetHttpRequestMessage(HttpMethod.Post, serviceUrl, payload));
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
{ {
using (var responseStream = await response.Content.ReadAsStreamAsync()) using (var responseStream = response.Content.ReadAsStream())
{ {
using (var streamReader = new StreamReader(responseStream)) using (var streamReader = new StreamReader(responseStream))
{ {
@ -158,7 +169,7 @@ namespace GameData
} }
else else
{ {
var errorResult = await response.Content.ReadAsStringAsync(); var errorResult = response.Content.ReadAsStringAsync().Result;
_logger.LogError("error posting to service '{service}' with code '{statuscode}' - Result: '{result}', Payload: '{payload}'", serviceUrl, response.StatusCode, errorResult, JsonConvert.SerializeObject(payload)); _logger.LogError("error posting to service '{service}' with code '{statuscode}' - Result: '{result}', Payload: '{payload}'", serviceUrl, response.StatusCode, errorResult, JsonConvert.SerializeObject(payload));
} }
} }
@ -248,7 +259,7 @@ namespace GameData
} }
else else
{ {
var errorResult = response.Content.ReadAsStringAsync(); var errorResult = response.Content.ReadAsStringAsync().Result;
_logger.LogError("error getting from service '{service}' with code '{statuscode}' - Result: '{result}'", serviceUrl, response.StatusCode, errorResult); _logger.LogError("error getting from service '{service}' with code '{statuscode}' - Result: '{result}'", serviceUrl, response.StatusCode, errorResult);
} }
} }
@ -263,5 +274,45 @@ namespace GameData
return res.Data; return res.Data;
} }
internal void Put<T>(object payload, string serviceUrl, bool suppressExceptions = false)
{
T res = default;
try
{
var response = client.Send(GetHttpRequestMessage(HttpMethod.Post, serviceUrl, payload));
if (response.IsSuccessStatusCode)
{
using (var responseStream = response.Content.ReadAsStream())
{
using (var streamReader = new StreamReader(responseStream))
{
using (var jsonTextReader = new JsonTextReader(streamReader))
{
var serializer = new JsonSerializer();
res = serializer.Deserialize<T>(jsonTextReader);
}
}
}
}
else
{
var errorResult = response.Content.ReadAsStringAsync().Result;
_logger.LogError("error posting to service '{service}' with code '{statuscode}' - Result: '{result}', Payload: '{payload}'", serviceUrl, response.StatusCode, errorResult, JsonConvert.SerializeObject(payload));
}
}
catch (Exception ex)
{
_logger.LogError(ex, "error at post");
if (!suppressExceptions)
{
throw ex;
}
}
}
} }
} }

View File

@ -5,9 +5,19 @@ namespace GameData.Dummy
{ {
public class DummyExpenseRepository : IExpenseRepository public class DummyExpenseRepository : IExpenseRepository
{ {
public DummyExpenseRepository()
{
}
public IEnumerable<Expense> GetAll() public IEnumerable<Expense> GetAll()
{ {
return IExpenseRepository.TestData; return IExpenseRepository.TestData;
} }
public void Save(MemberExpense data)
{
throw new NotImplementedException();
}
} }
} }

View File

@ -0,0 +1,13 @@
using GameModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameData.Model
{
public record GameStateDo(Guid Id, Guid GameId, string GameName, GameState GameState)
{
}
}

View File

@ -16,6 +16,7 @@ namespace GameData.Repository
private ApiClient _client; private ApiClient _client;
string Url => "items/expense"; string Url => "items/expense";
List<MemberExpense> _memberExpenses = new List<MemberExpense>();
public ExpenseRepository(ILogger<ExpenseRepository> log, ApiClient apiClient) public ExpenseRepository(ILogger<ExpenseRepository> log, ApiClient apiClient)
{ {
@ -29,5 +30,10 @@ namespace GameData.Repository
{ {
return _client.Get<Expense>(Url); return _client.Get<Expense>(Url);
} }
public void Save(MemberExpense memberExpense)
{
_memberExpenses.Add(memberExpense);
}
} }
} }

View File

@ -0,0 +1,36 @@
using GameData.Model;
using GameModel;
using GameModel.Contracts;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameData.Repository
{
public class GameRepository : IGameRepository
{
readonly ILogger<GameRepository> _log;
private ApiClient _client;
string Url => "items/gamestate";
public GameRepository(ILogger<GameRepository> log, ApiClient apiClient)
{
_log = log;
_client = apiClient;
}
public GameState Load(Guid gameId)
{
throw new NotImplementedException();
}
public void Save(GameState gameState)
{
var gameStateDo = new GameStateDo(gameState.Id, gameState.GameId, gameState.GameName, gameState);
_client.Post<GameStateDo>(gameStateDo,Url);
}
}
}

View File

@ -27,10 +27,9 @@ namespace GameHandler
private IGameHandler _gh; private IGameHandler _gh;
private ThrowHandler _th; private ThrowHandler _th;
private ExpenseHandler _eh; private ExpenseHandler _eh;
private GameState _lastState; private GameStateHandler _gameStateHandler;
public GameState GameModel { get => _lastState; }
public GameState GameModel { get => _gameStateHandler.GameState; }
private IExpenseRepository _expenseRepository { get => _scope.Resolve<IExpenseRepository>(); }
public GameService(IContainer rootContainer = null) public GameService(IContainer rootContainer = null)
{ {
@ -41,9 +40,9 @@ namespace GameHandler
{ {
get get
{ {
if (_lastState != null) if (_gameStateHandler.GameState != null)
{ {
return _lastState.ThrowState.BoardState.ThrowMode == ThrowMode.Decrease ? "Abräumen" : "Volle"; return _gameStateHandler.GameState.ThrowState.BoardState.ThrowMode == ThrowMode.Decrease ? "Abräumen" : "Volle";
} }
return string.Empty; return string.Empty;
} }
@ -65,16 +64,18 @@ namespace GameHandler
_isStarted = true; _isStarted = true;
InitGameServiceBehaviour(gameName); InitGameServiceBehaviour(gameName);
_lastState = InitGameState(playerIds, gameSettings, gameName); var state = InitGameState(playerIds, gameSettings, gameName);
_gameStateHandler.Add(state);
_gameStateHandler.Add(ExpenseModel.Create());
return _lastState; return state;
} }
private GameState InitGameState(int[] playerIds, IGameSettings gameSettings, string gameName) private GameState InitGameState(int[] playerIds, IGameSettings gameSettings, string gameName)
{ {
var gm = _gh.InitGameModel(playerIds, gameSettings); var gm = _gh.InitGameModel(playerIds, gameSettings);
var throwState = ThrowState.Create(_gh.ThrowMode(), _gh.ThrowsPerRount()); var throwState = ThrowState.Create(_gh.ThrowMode(), _gh.ThrowsPerRount());
return GameState.Create(gameName, throwState, _gh.GetCurrentPlayerId(gm), gm, ExpenseModel.Create()); return GameState.Create(Guid.NewGuid(), gameName, throwState, _gh.GetCurrentPlayerId(gm), gm);
} }
private void InitGameServiceBehaviour(string gameName) private void InitGameServiceBehaviour(string gameName)
@ -83,13 +84,15 @@ namespace GameHandler
_gh = this.GetGameHandler(gameName); _gh = this.GetGameHandler(gameName);
_gh.GameExpenseOccured += _gh_GameExpenseOccured; _gh.GameExpenseOccured += _gh_GameExpenseOccured;
_th = new ThrowHandler(); _th = new ThrowHandler();
_eh = new ExpenseHandler(_expenseRepository); _eh = new ExpenseHandler(_scope.Resolve<IExpenseRepository>());
_gameStateHandler = new GameStateHandler(_scope.Resolve<IGameRepository>(), _scope.Resolve<IExpenseRepository>());
} }
private void _gh_GameExpenseOccured(object? sender, EventArgs e) private void _gh_GameExpenseOccured(object? sender, EventArgs e)
{ {
var expenseModel = _eh.HandleGameExenseEventArgs(e as GameExenseEventArgs, _lastState.ExpenseModel); var expenseModel = _eh.HandleGameExenseEventArgs(e as GameExenseEventArgs, _gameStateHandler.ExpenseModel);
_lastState = _lastState with { ExpenseModel = expenseModel }; _gameStateHandler.Add(expenseModel);
//_lastState = _lastState with { ExpenseModel = expenseModel };
} }
public GameState HandleThrow(PinThrow pinThrow) public GameState HandleThrow(PinThrow pinThrow)
@ -99,16 +102,22 @@ namespace GameHandler
throw new InvalidGameStateExcpetion("Game not started"); throw new InvalidGameStateExcpetion("Game not started");
} }
var boardStateBeforeUpdate = _lastState.ThrowState.BoardState; var lastGameState = _gameStateHandler.GameState;
var boardStateBeforeUpdate = lastGameState.ThrowState.BoardState;
pinThrow = AutoCompletePlayerId(pinThrow); pinThrow = AutoCompletePlayerId(pinThrow);
var throwStateAfterUpdate = _th.Update(_lastState.ThrowState, pinThrow); var throwStateAfterUpdate = _th.Update(lastGameState.ThrowState, pinThrow);
var gameModel = _gh.Update(pinThrow, _lastState.GameModel, boardStateBeforeUpdate, throwStateAfterUpdate); var gameModel = _gh.Update(pinThrow, lastGameState.GameModel, boardStateBeforeUpdate, throwStateAfterUpdate);
var expenseModel = _eh.CheckThrow(throwStateAfterUpdate.BoardState, pinThrow, gameModel.PlayerIds, _lastState.ExpenseModel); var expenseModel = _eh.CheckThrow(throwStateAfterUpdate.BoardState, pinThrow, gameModel.PlayerIds, _gameStateHandler.ExpenseModel);
_lastState = _lastState with { ThrowState = throwStateAfterUpdate, GameModel = gameModel, NextPlayerId = _gh.GetCurrentPlayerId(gameModel), ExpenseModel = expenseModel }; var newGameState = lastGameState with { ThrowState = throwStateAfterUpdate, GameModel = gameModel, NextPlayerId = _gh.GetCurrentPlayerId(gameModel) };
return _lastState ;
_gameStateHandler.Add(expenseModel);
_gameStateHandler.Add(newGameState);
return newGameState;
} }
private PinThrow AutoCompletePlayerId(PinThrow pinThrow) private PinThrow AutoCompletePlayerId(PinThrow pinThrow)
@ -123,7 +132,7 @@ namespace GameHandler
return pinThrow; return pinThrow;
} }
return pinThrow with { PlayerId = _gh.GetCurrentPlayerId(_lastState.GameModel) }; return pinThrow with { PlayerId = _gh.GetCurrentPlayerId(_gameStateHandler.GameState.GameModel) };
} }
public void Stop() public void Stop()

View File

@ -0,0 +1,120 @@
using GameModel;
using GameModel.Contract;
using GameModel.Contracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameHandler
{
class GameStateHandlerItem<T>
{
internal Guid GameStateId;
internal bool Saved;
internal T Data;
public GameStateHandlerItem(Guid gameStateId, T data, bool saved)
{
GameStateId = gameStateId;
Saved = saved;
Data = data;
}
}
public class GameStateHandler
{
private Guid _gameStateId;
private List<GameStateHandlerItem<GameState>> gameStates = new List<GameStateHandlerItem<GameState>>();
private List<GameStateHandlerItem<ExpenseModel>> expenseModels = new List<GameStateHandlerItem<ExpenseModel>>();
private List<GameStateHandlerItem<MemberExpense>> memberExpenses = new List<GameStateHandlerItem<MemberExpense>>();
private IGameRepository _gameRepository;
private IExpenseRepository _expenseRepository;
public GameStateHandler(IGameRepository gameRepository, IExpenseRepository expenseRepository)
{
_gameRepository = gameRepository;
_expenseRepository = expenseRepository;
}
public ExpenseModel ExpenseModel
{
get
{
var res = expenseModels.LastOrDefault();
if (res != null)
{
return res.Data;
}
return null;
}
}
public GameState GameState
{
get
{
var res = gameStates.LastOrDefault();
if (res != null)
{
return res.Data;
}
return null;
}
}
internal void Add(ExpenseModel expenseModel)
{
if (ExpenseModel != null)
{
if (!expenseModel.Equals(ExpenseModel))
{
expenseModels.Add(new GameStateHandlerItem<ExpenseModel>(_gameStateId, expenseModel, false));
Save();
}
}
else
{
expenseModels.Add(new GameStateHandlerItem<ExpenseModel>(_gameStateId, expenseModel, false));
Save();
}
foreach(var memberExpense in expenseModel.MemberExpenses.Where(_ => memberExpenses.Any(me => me.Data.Id.Equals(_.Id))))
{
memberExpenses.Add(new GameStateHandlerItem<MemberExpense>(_gameStateId,memberExpense,false));
}
}
internal void Add(GameState newState)
{
if (!newState.Equals(GameState))
{
_gameStateId = Guid.NewGuid();
gameStates.Add(new GameStateHandlerItem<GameState>(_gameStateId, newState, false));
Save();
}
}
private void Save()
{
foreach (var item in gameStates.Where(_ => !_.Saved))
{
_gameRepository.Save(item.Data);
item.Saved = true;
}
foreach (var item in memberExpenses)
{
_expenseRepository.Save(item.Data);
item.Saved = true;
}
}
public void Undo()
{
}
}
}

View File

@ -18,5 +18,6 @@ namespace GameModel.Contract
}; };
IEnumerable<Expense> GetAll(); IEnumerable<Expense> GetAll();
void Save(MemberExpense data);
} }
} }

View File

@ -8,6 +8,15 @@ namespace GameModel
{ {
public record ExpenseModel(MemberExpense[] MemberExpenses) public record ExpenseModel(MemberExpense[] MemberExpenses)
{ {
public virtual bool Equals(ExpenseModel other)
{
if (ReferenceEquals(null, other)) return false;
return MemberExpenses.SequenceEqual(other.MemberExpenses);
}
public override int GetHashCode() => MemberExpenses.GetHashCode();
public static ExpenseModel Create() public static ExpenseModel Create()
{ {
return new ExpenseModel(Array.Empty<MemberExpense>()); return new ExpenseModel(Array.Empty<MemberExpense>());

View File

@ -7,11 +7,11 @@ using System.Threading.Tasks;
namespace GameModel namespace GameModel
{ {
public record GameState(Guid GameId, string GameName, ThrowState ThrowState, int NextPlayerId, IGameModel GameModel, ExpenseModel ExpenseModel) public record GameState(Guid Id, Guid GameId, string GameName, ThrowState ThrowState, int NextPlayerId, IGameModel GameModel)
{ {
public static GameState Create(string gameName, ThrowState throwState, int nextPlayerId, IGameModel gameModel, ExpenseModel expenseModel) public static GameState Create(Guid gameId, string gameName, ThrowState throwState, int nextPlayerId, IGameModel gameModel)
{ {
return new GameState(Guid.NewGuid(), gameName, throwState, nextPlayerId, gameModel, expenseModel); return new GameState(Guid.NewGuid(), gameId, gameName, throwState, nextPlayerId, gameModel);
} }
} }
} }

View File

@ -4,12 +4,14 @@ using Autofac.Core;
using AutofacSerilogIntegration; using AutofacSerilogIntegration;
using CommandLine; using CommandLine;
using GameData; using GameData;
using GameData.Dummy;
using GameData.Repository; using GameData.Repository;
using GameHandler; using GameHandler;
using GameHandler.DeathGame; using GameHandler.DeathGame;
using GameHandler.Extensions; using GameHandler.Extensions;
using GameModel; using GameModel;
using GameModel.Contract; using GameModel.Contract;
using GameModel.Contracts;
using GameModel.DeathGame; using GameModel.DeathGame;
using GameModel.Settings; using GameModel.Settings;
using KoogleCli.Model; using KoogleCli.Model;
@ -53,7 +55,8 @@ Autofac.IContainer Register()
RegisterLogging(builder); RegisterLogging(builder);
builder.RegisterType<ApiClient>(); builder.RegisterType<ApiClient>();
builder.RegisterType<ExpenseRepository>().As<IExpenseRepository>(); builder.RegisterType<ExpenseRepository>().As<IExpenseRepository>().InstancePerLifetimeScope();
builder.RegisterType<GameRepository>().As<IGameRepository>().InstancePerLifetimeScope();
return builder.Build(); return builder.Build();
} }