Implement deterministic simulation spine
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
using SideScrollerGame.Sim.Math;
|
||||
using SideScrollerGame.Sim.Runtime;
|
||||
|
||||
namespace SideScrollerGame.Sim.Serialization;
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
internal static class SimulationStateSerializer
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
private sealed record SimulationStateDocument
|
||||
{
|
||||
public int Version { get; init; }
|
||||
|
||||
public int Tick { get; init; }
|
||||
|
||||
public int Seed { get; init; }
|
||||
|
||||
public ulong RandomState { get; init; }
|
||||
|
||||
public ulong LastRandomValue { get; init; }
|
||||
|
||||
public ImmutableArray<PlayerStateDocument> Players { get; init; }
|
||||
}
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
private sealed record PlayerStateDocument
|
||||
{
|
||||
public int PlayerId { get; init; }
|
||||
|
||||
public int PositionX { get; init; }
|
||||
|
||||
public int PositionY { get; init; }
|
||||
|
||||
public sbyte MoveAxisX { get; init; }
|
||||
|
||||
public sbyte MoveAxisY { get; init; }
|
||||
|
||||
public short AimAxisX { get; init; }
|
||||
|
||||
public short AimAxisY { get; init; }
|
||||
|
||||
public int SelectedWeaponSlot { get; init; }
|
||||
|
||||
public int ButtonMask { get; init; }
|
||||
}
|
||||
|
||||
public static byte[] Serialize(SimulationState state)
|
||||
{
|
||||
List<PlayerStateDocument> players = new(state.Players.Length);
|
||||
foreach (var player in state.Players)
|
||||
{
|
||||
players.Add(new()
|
||||
{
|
||||
PlayerId = player.PlayerId.Value,
|
||||
PositionX = player.Position.m_X.m_Value,
|
||||
PositionY = player.Position.m_Y.m_Value,
|
||||
MoveAxisX = player.MoveAxisX,
|
||||
MoveAxisY = player.MoveAxisY,
|
||||
AimAxisX = player.AimAxisX,
|
||||
AimAxisY = player.AimAxisY,
|
||||
SelectedWeaponSlot = player.SelectedWeaponSlot,
|
||||
ButtonMask = player.ButtonMask
|
||||
});
|
||||
}
|
||||
|
||||
return JsonSerializer.SerializeToUtf8Bytes(new SimulationStateDocument
|
||||
{
|
||||
Version = SimulationDefaults.StateFormatVersion,
|
||||
Tick = state.Tick,
|
||||
Seed = state.Seed,
|
||||
RandomState = state.RandomState,
|
||||
LastRandomValue = state.LastRandomValue,
|
||||
Players = players.ToImmutableArray()
|
||||
});
|
||||
}
|
||||
|
||||
public static SimulationState Deserialize(byte[] data)
|
||||
{
|
||||
var document = JsonSerializer.Deserialize<SimulationStateDocument>(data) ?? throw new InvalidOperationException("Simulation state payload was empty.");
|
||||
if (document.Version != SimulationDefaults.StateFormatVersion)
|
||||
throw new NotSupportedException($"Unsupported simulation state version {document.Version}.");
|
||||
|
||||
var players = ImmutableArray.CreateBuilder<PlayerState>(document.Players.Length);
|
||||
foreach (var player in document.Players)
|
||||
{
|
||||
players.Add(new(new(player.PlayerId), new(new() { m_Value = player.PositionX }, new FixPoint16 { m_Value = player.PositionY }), player.MoveAxisX, player.MoveAxisY, player.AimAxisX, player.AimAxisY, player.SelectedWeaponSlot, player.ButtonMask));
|
||||
}
|
||||
|
||||
return new(document.Tick, document.Seed, document.RandomState, document.LastRandomValue, players.MoveToImmutable());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user