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 Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http.Headers;
using System.Reflection;
@ -134,17 +136,26 @@ namespace GameData
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;
try
{
var response = await client.SendAsync(GetHttpRequestMessage(HttpMethod.Post, serviceUrl, payload));
var response = client.Send(GetHttpRequestMessage(HttpMethod.Post, serviceUrl, payload));
if (response.IsSuccessStatusCode)
{
using (var responseStream = await response.Content.ReadAsStreamAsync())
using (var responseStream = response.Content.ReadAsStream())
{
using (var streamReader = new StreamReader(responseStream))
{
@ -158,7 +169,7 @@ namespace GameData
}
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));
}
}
@ -248,7 +259,7 @@ namespace GameData
}
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);
}
}
@ -263,5 +274,45 @@ namespace GameData
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 DummyExpenseRepository()
{
}
public IEnumerable<Expense> GetAll()
{
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;
string Url => "items/expense";
List<MemberExpense> _memberExpenses = new List<MemberExpense>();
public ExpenseRepository(ILogger<ExpenseRepository> log, ApiClient apiClient)
{
@ -29,5 +30,10 @@ namespace GameData.Repository
{
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 ThrowHandler _th;
private ExpenseHandler _eh;
private GameState _lastState;
public GameState GameModel { get => _lastState; }
private IExpenseRepository _expenseRepository { get => _scope.Resolve<IExpenseRepository>(); }
private GameStateHandler _gameStateHandler;
public GameState GameModel { get => _gameStateHandler.GameState; }
public GameService(IContainer rootContainer = null)
{
@ -41,9 +40,9 @@ namespace GameHandler
{
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;
}
@ -65,16 +64,18 @@ namespace GameHandler
_isStarted = true;
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)
{
var gm = _gh.InitGameModel(playerIds, gameSettings);
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)
@ -83,13 +84,15 @@ namespace GameHandler
_gh = this.GetGameHandler(gameName);
_gh.GameExpenseOccured += _gh_GameExpenseOccured;
_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)
{
var expenseModel = _eh.HandleGameExenseEventArgs(e as GameExenseEventArgs, _lastState.ExpenseModel);
_lastState = _lastState with { ExpenseModel = expenseModel };
var expenseModel = _eh.HandleGameExenseEventArgs(e as GameExenseEventArgs, _gameStateHandler.ExpenseModel);
_gameStateHandler.Add(expenseModel);
//_lastState = _lastState with { ExpenseModel = expenseModel };
}
public GameState HandleThrow(PinThrow pinThrow)
@ -99,16 +102,22 @@ namespace GameHandler
throw new InvalidGameStateExcpetion("Game not started");
}
var boardStateBeforeUpdate = _lastState.ThrowState.BoardState;
var lastGameState = _gameStateHandler.GameState;
var boardStateBeforeUpdate = lastGameState.ThrowState.BoardState;
pinThrow = AutoCompletePlayerId(pinThrow);
var throwStateAfterUpdate = _th.Update(_lastState.ThrowState, pinThrow);
var gameModel = _gh.Update(pinThrow, _lastState.GameModel, boardStateBeforeUpdate, throwStateAfterUpdate);
var expenseModel = _eh.CheckThrow(throwStateAfterUpdate.BoardState, pinThrow, gameModel.PlayerIds, _lastState.ExpenseModel);
var throwStateAfterUpdate = _th.Update(lastGameState.ThrowState, pinThrow);
var gameModel = _gh.Update(pinThrow, lastGameState.GameModel, boardStateBeforeUpdate, throwStateAfterUpdate);
var expenseModel = _eh.CheckThrow(throwStateAfterUpdate.BoardState, pinThrow, gameModel.PlayerIds, _gameStateHandler.ExpenseModel);
_lastState = _lastState with { ThrowState = throwStateAfterUpdate, GameModel = gameModel, NextPlayerId = _gh.GetCurrentPlayerId(gameModel), ExpenseModel = expenseModel };
return _lastState ;
var newGameState = lastGameState with { ThrowState = throwStateAfterUpdate, GameModel = gameModel, NextPlayerId = _gh.GetCurrentPlayerId(gameModel) };
_gameStateHandler.Add(expenseModel);
_gameStateHandler.Add(newGameState);
return newGameState;
}
private PinThrow AutoCompletePlayerId(PinThrow pinThrow)
@ -123,7 +132,7 @@ namespace GameHandler
return pinThrow;
}
return pinThrow with { PlayerId = _gh.GetCurrentPlayerId(_lastState.GameModel) };
return pinThrow with { PlayerId = _gh.GetCurrentPlayerId(_gameStateHandler.GameState.GameModel) };
}
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();
void Save(MemberExpense data);
}
}

View File

@ -8,6 +8,15 @@ namespace GameModel
{
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()
{
return new ExpenseModel(Array.Empty<MemberExpense>());

View File

@ -7,11 +7,11 @@ using System.Threading.Tasks;
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 CommandLine;
using GameData;
using GameData.Dummy;
using GameData.Repository;
using GameHandler;
using GameHandler.DeathGame;
using GameHandler.Extensions;
using GameModel;
using GameModel.Contract;
using GameModel.Contracts;
using GameModel.DeathGame;
using GameModel.Settings;
using KoogleCli.Model;
@ -53,7 +55,8 @@ Autofac.IContainer Register()
RegisterLogging(builder);
builder.RegisterType<ApiClient>();
builder.RegisterType<ExpenseRepository>().As<IExpenseRepository>();
builder.RegisterType<ExpenseRepository>().As<IExpenseRepository>().InstancePerLifetimeScope();
builder.RegisterType<GameRepository>().As<IGameRepository>().InstancePerLifetimeScope();
return builder.Build();
}