Skip to content

Commit

Permalink
introduce PlayerActionName
Browse files Browse the repository at this point in the history
  • Loading branch information
Konrad Jamrozik committed Jul 6, 2024
1 parent 3fc9002 commit 8da8e97
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 33 deletions.
3 changes: 1 addition & 2 deletions src/api/ApplyPlayerActionRoute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ private static JsonHttpResult<GameSessionTurn> ApplyPlayerActionInternal(
GameSession gameSession = ApiUtils.NewGameSessionFromTurn(gameSessionTurn);
var controller = new GameSessionController(config, log, gameSession);

Contract.Assert(playerActionPayload.ActionName != GameEventType.AdvanceTimePlayerAction);
Contract.Assert(playerActionPayload.Name.ToString() != GameEventType.AdvanceTimePlayerAction);

gameSession.CurrentPlayerActionEvents.Add(playerActionPayload.Apply(controller));

Expand All @@ -65,7 +65,6 @@ private static JsonHttpResult<GameSessionTurn> ApplyPlayerActionInternal(
// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/parameter-binding?view=aspnetcore-8.0#configure-json-deserialization-options-for-an-endpoint
parsedBody =
(await req.ReadFromJsonAsync<ApplyPlayerActionRequestBody>(GameSessionTurn.JsonSerializerOptions))!;
PlayerAction.ValidateName(parsedBody.PlayerActionPayload.ActionName);
error = null;
}
else
Expand Down
19 changes: 12 additions & 7 deletions src/api/PlayerActionPayload.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// codesync: UfoGameLib.Api.PlayerActionPayload

using System.Text.Json.Serialization;
using UfoGameLib.Controller;
using UfoGameLib.Events;

Expand All @@ -15,23 +17,26 @@ namespace UfoGameLib.Api;
// Reason: used by JSON deserializer.
public class PlayerActionPayload
{
// kja introduce type: PlayerActionName
public readonly PlayerActionName Name;
// ReSharper disable MemberCanBePrivate.Global
// Reason: used by JSON deserializer.
public readonly string ActionName;
public readonly int[]? Ids;
public readonly int? TargetId;

public PlayerActionPayload(string name, int[]? ids, int? targetId) : this(new PlayerActionName(name), ids, targetId)
{
}

/// <summary>
/// Represents a player action payload. The payload is expected to be deserialized
/// from a system boundary, e.g. from a JSON string received from a POST HTTP request.
///
/// The payload can be applied to a GameSessionController. See the Apply() method.
/// </summary>
public PlayerActionPayload(string actionName, int[]? ids, int? targetId)
[JsonConstructor]
public PlayerActionPayload(PlayerActionName name, int[]? ids, int? targetId)
{
PlayerAction.ValidateName(actionName);
ActionName = actionName;
Name = name;
Ids = ids;
TargetId = targetId;
}
Expand All @@ -53,7 +58,7 @@ public PlayerActionEvent Apply(GameSessionController controller)
private Func<PlayerActionEvent> TranslatePlayerActionToControllerAction(GameSessionController controller)
// https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#property-pattern
// https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#positional-pattern
=> ActionName switch
=> Name.ToString() switch
{
GameEventType.AdvanceTimePlayerAction => () => controller.AdvanceTime().advaceTimeEvent,
nameof(BuyTransportCapacityPlayerAction) => () => controller.CurrentTurnController.BuyTransportCapacity(TargetId!.Value),
Expand All @@ -65,6 +70,6 @@ private Func<PlayerActionEvent> TranslatePlayerActionToControllerAction(GameSess
nameof(SendAgentsToTrainingPlayerAction) => () => controller.CurrentTurnController.SendAgentsToTraining(Ids!),
nameof(RecallAgentsPlayerAction) => () => controller.CurrentTurnController.RecallAgents(Ids!),
nameof(LaunchMissionPlayerAction) => () => controller.CurrentTurnController.LaunchMission(TargetId!.Value, Ids!),
_ => () => throw new ArgumentException($"Unsupported player action of '{ActionName}'")
_ => () => throw new ArgumentException($"Unsupported player action of '{Name}'")
};
}
8 changes: 1 addition & 7 deletions src/game-lib/Controller/PlayerAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,6 @@ public PlayerActionEvent Apply(GameState state, int nextEventId)
}
protected abstract (List<int>? ids, int? targetId) ApplyImpl(GameState state);

// kja get rid of ValidattName and IsValidName
public static void ValidateName(string name)
{
bool isValid = IsValidName(name);
Contract.Assert(isValid, $"The type name '{name}' is not a valid name of PlayerAction-derived class.");
}

public static bool IsValidName(string name)
{
Expand All @@ -51,4 +45,4 @@ public static bool IsValidName(string name)
bool isValid = derivedTypes.Any(t => t.Name == name);
return isValid;
}
}
}
44 changes: 44 additions & 0 deletions src/game-lib/Controller/PlayerActionName.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Collections.Immutable;
using System.Reflection;
using System.Text.Json.Serialization;
using Lib.Contracts;
using Lib.Json;

namespace UfoGameLib.Controller;

[JsonConverter(typeof(StringJsonConverter<PlayerActionName>))]
public class PlayerActionName
{
private readonly string _name;

private static readonly ImmutableList<string> ValidNames = GetValidNames();

private static ImmutableList<string> GetValidNames()
{
// Get the assembly that contains the PlayerAction class
Assembly assembly = Assembly.GetAssembly(typeof(PlayerAction))!;

// Get all types in the assembly that inherit from PlayerAction
var derivedTypes = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(PlayerAction)));

return derivedTypes.Select(t => t.Name).ToImmutableList();
}

public PlayerActionName(string name)
{
Contract.Assert(
ValidNames.Contains(name),
$"The type name '{name}' is not a valid name of PlayerAction-derived class.");
_name = name;
}

public override string ToString()
{
return $"{_name}";
}

// public static implicit operator string(PlayerActionName playerActionName)
// {
// return playerActionName._name;
// }
}
3 changes: 2 additions & 1 deletion src/game-lib/Events/GameEventType.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System.Collections.Immutable;
using System.Text.Json.Serialization;
using Lib.Contracts;
using Lib.Json;
using UfoGameLib.Controller;

namespace UfoGameLib.Events;


[JsonConverter(typeof(GameEventTypeConverter))]
[JsonConverter(typeof(StringJsonConverter<GameEventType>))]
public class GameEventType
{
private readonly string _type;
Expand Down
5 changes: 0 additions & 5 deletions src/game-lib/Events/GameEventTypeConverter.cs

This file was deleted.

19 changes: 9 additions & 10 deletions web/src/lib/api/playerActionsPayloadsProviders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,41 @@ export const playerActionsPayloadsProviders: {
} = {
// Note: currently Cap always buys 1 capacity. See PlayerActionPayload.cs in backend.
BuyTransportCapacityPlayerAction: (TargetId: number) => ({
ActionName: 'BuyTransportCapacityPlayerAction' as PlayerActionNameInTurn,
Name: 'BuyTransportCapacityPlayerAction' as PlayerActionNameInTurn,
TargetId,
}),
// Note: currently HireAgents always hires 1 agent. See PlayerActionPayload.cs in backend.
HireAgentsPlayerAction: (TargetId: number) => ({
ActionName: 'HireAgentsPlayerAction' as PlayerActionNameInTurn,
Name: 'HireAgentsPlayerAction' as PlayerActionNameInTurn,
TargetId,
}),
SackAgentsPlayerAction: (Ids: number[]) => ({
ActionName: 'SackAgentsPlayerAction' as PlayerActionNameInTurn,
Name: 'SackAgentsPlayerAction' as PlayerActionNameInTurn,
Ids,
}),
SendAgentsToGenerateIncomePlayerAction: (Ids: number[]) => ({
ActionName:
'SendAgentsToGenerateIncomePlayerAction' as PlayerActionNameInTurn,
Name: 'SendAgentsToGenerateIncomePlayerAction' as PlayerActionNameInTurn,
Ids,
}),
SendAgentsToGatherIntelPlayerAction: (Ids: number[]) => ({
ActionName: 'SendAgentsToGatherIntelPlayerAction' as PlayerActionNameInTurn,
Name: 'SendAgentsToGatherIntelPlayerAction' as PlayerActionNameInTurn,
Ids,
}),
SendAgentsToTrainingPlayerAction: (Ids: number[]) => ({
ActionName: 'SendAgentsToTrainingPlayerAction' as PlayerActionNameInTurn,
Name: 'SendAgentsToTrainingPlayerAction' as PlayerActionNameInTurn,
Ids,
}),
RecallAgentsPlayerAction: (Ids: number[]) => ({
ActionName: 'RecallAgentsPlayerAction' as PlayerActionNameInTurn,
Name: 'RecallAgentsPlayerAction' as PlayerActionNameInTurn,
Ids,
}),
LaunchMissionPlayerAction: (Ids: number[], TargetId: number) => ({
ActionName: 'LaunchMissionPlayerAction' as PlayerActionNameInTurn,
Name: 'LaunchMissionPlayerAction' as PlayerActionNameInTurn,
Ids,
TargetId,
}),
InvestIntelPlayerAction: (Ids: number[], TargetId: number) => ({
ActionName: 'InvestIntelPlayerAction' as PlayerActionNameInTurn,
Name: 'InvestIntelPlayerAction' as PlayerActionNameInTurn,
Ids,
TargetId,
}),
Expand Down
2 changes: 1 addition & 1 deletion web/src/lib/codesync/PlayerActionPayload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import type { PlayerActionNameInTurn } from './PlayerActionEvent'

export type PlayerActionPayload = {
readonly ActionName: PlayerActionNameInTurn
readonly Name: PlayerActionNameInTurn
readonly Ids?: number[]
readonly TargetId?: number
}

0 comments on commit 8da8e97

Please sign in to comment.