Skip to content

Commit

Permalink
add support for round-trip serialization of Factions by reference ins…
Browse files Browse the repository at this point in the history
…ide MissionSites
  • Loading branch information
Konrad Jamrozik committed Jun 13, 2024
1 parent e4e2916 commit 580d452
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 3 deletions.
11 changes: 10 additions & 1 deletion src/game-lib-tests/GameStateFixtures.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Lib.Data;
using UfoGameLib.Model;
using UfoGameLib.State;

Expand All @@ -12,7 +13,15 @@ public static GameState Get()
{
var agent = new Agent(id: i, turnHired: 1, Agent.AgentState.Available);
var terminatedAgent = new Agent(id: 100 + i, turnHired: 1, Agent.AgentState.Terminated);
var missionSite = new MissionSite(id: i, difficulty: 1, turnAppeared: 1, expiresIn: 1);
bool isExpired = i % 2 == 0;
var missionSite = new MissionSite(
id: i,
gs.Factions[i % gs.Factions.Count],
difficulty: 1,
turnAppeared: 1,
expiresIn: isExpired ? null : 1,
turnDeactivated: isExpired ? 2 : null,
expired: isExpired);
var mission = new Mission(id: i, missionSite, agentsSent: 1);
gs.Assets.Agents.Add(agent);
gs.TerminatedAgents.Add(terminatedAgent);
Expand Down
24 changes: 24 additions & 0 deletions src/game-lib-tests/GameStateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,28 @@ public void DeserializeGameStateFromInitialGameStateJsonString()

new JsonDiffAssertion(baseline: initialJsonString, target: deserializedJsonString).Assert();
}

/// <summary>
/// Use this test to exercise correct behavior of serialization and deserialization of GameState with
/// GameState.StateJsonSerializerOptions which most importantly exercise
/// UfoGameLib.State.GameStateJsonConverter.
///
/// This test DOES NOT assert hat data structures have not been duplicated in the serialization.
/// Inspect the resulting JSON in debugger to verify this.
/// </summary>
[Test]
public void RoundTripSerializationOnNontrivialGameState()
{
GameState gameState = GameStateFixtures.Get();

// Act: serialize using GameState.StateJsonSerializerOptions
string gameStateAsJsonString = gameState.ToJsonString();

// Act: deserialize using GameState.StateJsonSerializerOptions
GameState deserializedGameState = JsonSerializer.Deserialize<GameState>(gameStateAsJsonString, Opts)!;

string deserializedJsonString = deserializedGameState.ToJsonString();

new JsonDiffAssertion(baseline: gameStateAsJsonString, target: deserializedJsonString).Assert();
}
}
3 changes: 2 additions & 1 deletion src/game-lib/Controller/TimeAdvancementController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ private void CreateMissionSites(GameState state)
int siteId = state.NextMissionSiteId;
(int difficulty, int difficultyFromTurn, int roll) =
Ruleset.RollMissionSiteDifficulty(state.Timeline.CurrentTurn, _randomGen);
var site = new MissionSite(siteId, difficulty, turnAppeared: state.Timeline.CurrentTurn, expiresIn: 3);
// kja inject appropriate faction instead of state.Faction[0]
var site = new MissionSite(siteId, state.Factions[0], difficulty, turnAppeared: state.Timeline.CurrentTurn, expiresIn: 3);
state.MissionSites.Add(site);
_log.Info($"Add {site.LogString} : " +
$"difficulty: {difficulty,3}, " +
Expand Down
3 changes: 3 additions & 0 deletions src/game-lib/Model/MissionSite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class MissionSite : IIdentifiable
{
public int Id { get; }

public readonly Faction Faction;
public readonly int Difficulty;
public readonly int TurnAppeared;

Expand All @@ -30,6 +31,7 @@ public class MissionSite : IIdentifiable
[JsonConstructor]
public MissionSite(
int id,
Faction faction,
int difficulty,
int turnAppeared,
int? expiresIn,
Expand All @@ -39,6 +41,7 @@ public MissionSite(
Contract.Assert(Difficulty >= 0);
Contract.Assert(ExpiresIn == null || ExpiresIn >= 0);
Id = id;
Faction = faction;
Difficulty = difficulty;
TurnAppeared = turnAppeared;
TurnDeactivated = turnDeactivated;
Expand Down
22 changes: 21 additions & 1 deletion src/game-lib/State/GameStateJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ public override void Write(Utf8JsonWriter writer, GameState value, JsonSerialize
objJsonArrayName: nameof(Assets.Agents),
propName: nameof(Agent.CurrentMission));

ReplaceArrayObjectsPropertiesWithRefs(
parent: gameStateNode,
objJsonArrayName: nameof(GameState.MissionSites),
propName: nameof(MissionSite.Faction));

gameStateNode.WriteTo(writer, SerializationOptions);
}

Expand All @@ -112,7 +117,22 @@ public override GameState Read(ref Utf8JsonReader reader, Type typeToConvert, Js
int updateCount = DeserializeInt(gameStateNode, nameof(GameState.UpdateCount));
Factions factions = Deserialize<Factions>(gameStateNode);
Timeline timeline = Deserialize<Timeline>(gameStateNode);
MissionSites missionSites = Deserialize<MissionSites>(gameStateNode);

var missionSites = new MissionSites(
DeserializeObjArrayWithDepRefProps(
objJsonArray: JsonArray(gameStateNode, nameof(GameState.MissionSites)),
depRefPropName: nameof(MissionSite.Faction),
deps: factions,
(missionSiteObj, faction)
=> new MissionSite(
id: DeserializeInt(missionSiteObj, nameof(MissionSite.Id)),
faction: faction!,
difficulty: DeserializeInt(missionSiteObj, nameof(MissionSite.Difficulty)),
turnAppeared: DeserializeInt(missionSiteObj, nameof(MissionSite.TurnAppeared)),
expiresIn: DeserializeIntOrNull(missionSiteObj, nameof(MissionSite.ExpiresIn)),
turnDeactivated: DeserializeIntOrNull(missionSiteObj, nameof(MissionSite.TurnDeactivated)),
expired: DeserializeBool(missionSiteObj, nameof(MissionSite.Expired)))));

Agents terminatedAgents =
Deserialize<List<Agent>>(gameStateNode, nameof(GameState.TerminatedAgents)).ToAgents(terminated: true);

Expand Down
3 changes: 3 additions & 0 deletions src/lib/Json/JsonConverterSupportingReferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ protected int Id(JsonNode node)
protected int DeserializeInt(JsonNode node, string propName)
=> node[propName]!.GetValue<int>();

protected int? DeserializeIntOrNull(JsonNode node, string propName)
=> node[propName]?.GetValue<int>();

protected int? DeserializeNullableInt(JsonNode node, string propName)
{
JsonNode? propVal = node[propName];
Expand Down

0 comments on commit 580d452

Please sign in to comment.