Refactor simulation and viewer architecture
This commit is contained in:
680
apps/backend/Simulation/SimulationEngine.Replication.cs
Normal file
680
apps/backend/Simulation/SimulationEngine.Replication.cs
Normal file
@@ -0,0 +1,680 @@
|
||||
using SpaceGame.Simulation.Api.Contracts;
|
||||
|
||||
namespace SpaceGame.Simulation.Api.Simulation;
|
||||
|
||||
public sealed partial class SimulationEngine
|
||||
{
|
||||
public WorldSnapshot BuildSnapshot(SimulationWorld world, long sequence)
|
||||
{
|
||||
PrimeDeltaBaseline(world);
|
||||
|
||||
return new WorldSnapshot(
|
||||
world.Label,
|
||||
world.Seed,
|
||||
sequence,
|
||||
world.TickIntervalMs,
|
||||
world.GeneratedAtUtc,
|
||||
world.Systems.Select(system => new SystemSnapshot(
|
||||
system.Definition.Id,
|
||||
system.Definition.Label,
|
||||
ToDto(system.Position),
|
||||
system.Definition.StarKind,
|
||||
system.Definition.StarCount,
|
||||
system.Definition.StarColor,
|
||||
system.Definition.StarSize,
|
||||
system.Definition.Planets.Select(planet => new PlanetSnapshot(
|
||||
planet.Label,
|
||||
planet.PlanetType,
|
||||
planet.Shape,
|
||||
planet.MoonCount,
|
||||
planet.OrbitRadius,
|
||||
planet.OrbitSpeed,
|
||||
planet.OrbitEccentricity,
|
||||
planet.OrbitInclination,
|
||||
planet.OrbitLongitudeOfAscendingNode,
|
||||
planet.OrbitArgumentOfPeriapsis,
|
||||
planet.OrbitPhaseAtEpoch,
|
||||
planet.Size,
|
||||
planet.Color,
|
||||
planet.HasRing)).ToList())).ToList(),
|
||||
world.SpatialNodes.Select(ToSpatialNodeDelta).Select(node => new SpatialNodeSnapshot(
|
||||
node.Id,
|
||||
node.SystemId,
|
||||
node.Kind,
|
||||
node.LocalPosition,
|
||||
node.BubbleId,
|
||||
node.ParentNodeId,
|
||||
node.OccupyingStructureId,
|
||||
node.OrbitReferenceId)).ToList(),
|
||||
world.LocalBubbles.Select(ToLocalBubbleDelta).Select(bubble => new LocalBubbleSnapshot(
|
||||
bubble.Id,
|
||||
bubble.NodeId,
|
||||
bubble.SystemId,
|
||||
bubble.Radius,
|
||||
bubble.OccupantShipIds,
|
||||
bubble.OccupantStationIds,
|
||||
bubble.OccupantClaimIds,
|
||||
bubble.OccupantConstructionSiteIds)).ToList(),
|
||||
world.Nodes.Select(ToNodeDelta).Select(node => new ResourceNodeSnapshot(
|
||||
node.Id,
|
||||
node.SystemId,
|
||||
node.LocalPosition,
|
||||
node.SourceKind,
|
||||
node.OreRemaining,
|
||||
node.MaxOre,
|
||||
node.ItemId)).ToList(),
|
||||
world.Stations.Select(ToStationDelta).Select(station => new StationSnapshot(
|
||||
station.Id,
|
||||
station.Label,
|
||||
station.Category,
|
||||
station.SystemId,
|
||||
station.LocalPosition,
|
||||
station.NodeId,
|
||||
station.BubbleId,
|
||||
station.AnchorNodeId,
|
||||
station.Color,
|
||||
station.DockedShips,
|
||||
station.DockingPads,
|
||||
station.EnergyStored,
|
||||
station.Inventory,
|
||||
station.FactionId,
|
||||
station.CommanderId,
|
||||
station.PolicySetId,
|
||||
station.Population,
|
||||
station.PopulationCapacity,
|
||||
station.WorkforceRequired,
|
||||
station.WorkforceEffectiveRatio,
|
||||
station.InstalledModules,
|
||||
station.MarketOrderIds)).ToList(),
|
||||
world.Claims.Select(ToClaimDelta).Select(claim => new ClaimSnapshot(
|
||||
claim.Id,
|
||||
claim.FactionId,
|
||||
claim.SystemId,
|
||||
claim.NodeId,
|
||||
claim.BubbleId,
|
||||
claim.State,
|
||||
claim.Health,
|
||||
claim.PlacedAtUtc,
|
||||
claim.ActivatesAtUtc)).ToList(),
|
||||
world.ConstructionSites.Select(ToConstructionSiteDelta).Select(site => new ConstructionSiteSnapshot(
|
||||
site.Id,
|
||||
site.FactionId,
|
||||
site.SystemId,
|
||||
site.NodeId,
|
||||
site.BubbleId,
|
||||
site.TargetKind,
|
||||
site.TargetDefinitionId,
|
||||
site.BlueprintId,
|
||||
site.ClaimId,
|
||||
site.StationId,
|
||||
site.State,
|
||||
site.Progress,
|
||||
site.Inventory,
|
||||
site.RequiredItems,
|
||||
site.DeliveredItems,
|
||||
site.AssignedConstructorShipIds,
|
||||
site.MarketOrderIds)).ToList(),
|
||||
world.MarketOrders.Select(ToMarketOrderDelta).Select(order => new MarketOrderSnapshot(
|
||||
order.Id,
|
||||
order.FactionId,
|
||||
order.StationId,
|
||||
order.ConstructionSiteId,
|
||||
order.Kind,
|
||||
order.ItemId,
|
||||
order.Amount,
|
||||
order.RemainingAmount,
|
||||
order.Valuation,
|
||||
order.ReserveThreshold,
|
||||
order.PolicySetId,
|
||||
order.State)).ToList(),
|
||||
world.Policies.Select(ToPolicySetDelta).Select(policy => new PolicySetSnapshot(
|
||||
policy.Id,
|
||||
policy.OwnerKind,
|
||||
policy.OwnerId,
|
||||
policy.TradeAccessPolicy,
|
||||
policy.DockingAccessPolicy,
|
||||
policy.ConstructionAccessPolicy,
|
||||
policy.OperationalRangePolicy)).ToList(),
|
||||
world.Ships.Select(ToShipDelta).Select(ship => new ShipSnapshot(
|
||||
ship.Id,
|
||||
ship.Label,
|
||||
ship.Role,
|
||||
ship.ShipClass,
|
||||
ship.SystemId,
|
||||
ship.LocalPosition,
|
||||
ship.LocalVelocity,
|
||||
ship.TargetLocalPosition,
|
||||
ship.State,
|
||||
ship.OrderKind,
|
||||
ship.DefaultBehaviorKind,
|
||||
ship.ControllerTaskKind,
|
||||
ship.NodeId,
|
||||
ship.BubbleId,
|
||||
ship.DockedStationId,
|
||||
ship.CommanderId,
|
||||
ship.PolicySetId,
|
||||
ship.CargoCapacity,
|
||||
ship.WorkerPopulation,
|
||||
ship.EnergyStored,
|
||||
ship.Inventory,
|
||||
ship.FactionId,
|
||||
ship.Health,
|
||||
ship.History,
|
||||
ship.SpatialState)).ToList(),
|
||||
world.Factions.Select(ToFactionDelta).Select(faction => new FactionSnapshot(
|
||||
faction.Id,
|
||||
faction.Label,
|
||||
faction.Color,
|
||||
faction.Credits,
|
||||
faction.PopulationTotal,
|
||||
faction.OreMined,
|
||||
faction.GoodsProduced,
|
||||
faction.ShipsBuilt,
|
||||
faction.ShipsLost,
|
||||
faction.DefaultPolicySetId)).ToList());
|
||||
}
|
||||
|
||||
public void PrimeDeltaBaseline(SimulationWorld world)
|
||||
{
|
||||
foreach (var node in world.Nodes)
|
||||
{
|
||||
node.LastDeltaSignature = BuildNodeSignature(node);
|
||||
}
|
||||
|
||||
foreach (var node in world.SpatialNodes)
|
||||
{
|
||||
node.LastDeltaSignature = BuildSpatialNodeSignature(node);
|
||||
}
|
||||
|
||||
foreach (var bubble in world.LocalBubbles)
|
||||
{
|
||||
bubble.LastDeltaSignature = BuildLocalBubbleSignature(bubble);
|
||||
}
|
||||
|
||||
foreach (var station in world.Stations)
|
||||
{
|
||||
station.LastDeltaSignature = BuildStationSignature(station);
|
||||
}
|
||||
|
||||
foreach (var claim in world.Claims)
|
||||
{
|
||||
claim.LastDeltaSignature = BuildClaimSignature(claim);
|
||||
}
|
||||
|
||||
foreach (var site in world.ConstructionSites)
|
||||
{
|
||||
site.LastDeltaSignature = BuildConstructionSiteSignature(site);
|
||||
}
|
||||
|
||||
foreach (var order in world.MarketOrders)
|
||||
{
|
||||
order.LastDeltaSignature = BuildMarketOrderSignature(order);
|
||||
}
|
||||
|
||||
foreach (var policy in world.Policies)
|
||||
{
|
||||
policy.LastDeltaSignature = BuildPolicySignature(policy);
|
||||
}
|
||||
|
||||
foreach (var ship in world.Ships)
|
||||
{
|
||||
ship.LastDeltaSignature = BuildShipSignature(ship);
|
||||
}
|
||||
|
||||
foreach (var faction in world.Factions)
|
||||
{
|
||||
faction.LastDeltaSignature = BuildFactionSignature(faction);
|
||||
}
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ResourceNodeDelta> BuildNodeDeltas(SimulationWorld world)
|
||||
{
|
||||
var deltas = new List<ResourceNodeDelta>();
|
||||
foreach (var node in world.Nodes)
|
||||
{
|
||||
var signature = BuildNodeSignature(node);
|
||||
if (signature == node.LastDeltaSignature)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
node.LastDeltaSignature = signature;
|
||||
deltas.Add(ToNodeDelta(node));
|
||||
}
|
||||
|
||||
return deltas;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<SpatialNodeDelta> BuildSpatialNodeDeltas(SimulationWorld world)
|
||||
{
|
||||
var deltas = new List<SpatialNodeDelta>();
|
||||
foreach (var node in world.SpatialNodes)
|
||||
{
|
||||
var signature = BuildSpatialNodeSignature(node);
|
||||
if (signature == node.LastDeltaSignature)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
node.LastDeltaSignature = signature;
|
||||
deltas.Add(ToSpatialNodeDelta(node));
|
||||
}
|
||||
|
||||
return deltas;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<LocalBubbleDelta> BuildLocalBubbleDeltas(SimulationWorld world)
|
||||
{
|
||||
var deltas = new List<LocalBubbleDelta>();
|
||||
foreach (var bubble in world.LocalBubbles)
|
||||
{
|
||||
var signature = BuildLocalBubbleSignature(bubble);
|
||||
if (signature == bubble.LastDeltaSignature)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bubble.LastDeltaSignature = signature;
|
||||
deltas.Add(ToLocalBubbleDelta(bubble));
|
||||
}
|
||||
|
||||
return deltas;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<StationDelta> BuildStationDeltas(SimulationWorld world)
|
||||
{
|
||||
var deltas = new List<StationDelta>();
|
||||
foreach (var station in world.Stations)
|
||||
{
|
||||
var signature = BuildStationSignature(station);
|
||||
if (signature == station.LastDeltaSignature)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
station.LastDeltaSignature = signature;
|
||||
deltas.Add(ToStationDelta(station));
|
||||
}
|
||||
|
||||
return deltas;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ClaimDelta> BuildClaimDeltas(SimulationWorld world)
|
||||
{
|
||||
var deltas = new List<ClaimDelta>();
|
||||
foreach (var claim in world.Claims)
|
||||
{
|
||||
var signature = BuildClaimSignature(claim);
|
||||
if (signature == claim.LastDeltaSignature)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
claim.LastDeltaSignature = signature;
|
||||
deltas.Add(ToClaimDelta(claim));
|
||||
}
|
||||
|
||||
return deltas;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ConstructionSiteDelta> BuildConstructionSiteDeltas(SimulationWorld world)
|
||||
{
|
||||
var deltas = new List<ConstructionSiteDelta>();
|
||||
foreach (var site in world.ConstructionSites)
|
||||
{
|
||||
var signature = BuildConstructionSiteSignature(site);
|
||||
if (signature == site.LastDeltaSignature)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
site.LastDeltaSignature = signature;
|
||||
deltas.Add(ToConstructionSiteDelta(site));
|
||||
}
|
||||
|
||||
return deltas;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<MarketOrderDelta> BuildMarketOrderDeltas(SimulationWorld world)
|
||||
{
|
||||
var deltas = new List<MarketOrderDelta>();
|
||||
foreach (var order in world.MarketOrders)
|
||||
{
|
||||
var signature = BuildMarketOrderSignature(order);
|
||||
if (signature == order.LastDeltaSignature)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
order.LastDeltaSignature = signature;
|
||||
deltas.Add(ToMarketOrderDelta(order));
|
||||
}
|
||||
|
||||
return deltas;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<PolicySetDelta> BuildPolicyDeltas(SimulationWorld world)
|
||||
{
|
||||
var deltas = new List<PolicySetDelta>();
|
||||
foreach (var policy in world.Policies)
|
||||
{
|
||||
var signature = BuildPolicySignature(policy);
|
||||
if (signature == policy.LastDeltaSignature)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
policy.LastDeltaSignature = signature;
|
||||
deltas.Add(ToPolicySetDelta(policy));
|
||||
}
|
||||
|
||||
return deltas;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ShipDelta> BuildShipDeltas(SimulationWorld world)
|
||||
{
|
||||
var deltas = new List<ShipDelta>();
|
||||
foreach (var ship in world.Ships)
|
||||
{
|
||||
var signature = BuildShipSignature(ship);
|
||||
if (signature == ship.LastDeltaSignature)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ship.LastDeltaSignature = signature;
|
||||
deltas.Add(ToShipDelta(ship));
|
||||
}
|
||||
|
||||
return deltas;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<FactionDelta> BuildFactionDeltas(SimulationWorld world)
|
||||
{
|
||||
var deltas = new List<FactionDelta>();
|
||||
foreach (var faction in world.Factions)
|
||||
{
|
||||
var signature = BuildFactionSignature(faction);
|
||||
if (signature == faction.LastDeltaSignature)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
faction.LastDeltaSignature = signature;
|
||||
deltas.Add(ToFactionDelta(faction));
|
||||
}
|
||||
|
||||
return deltas;
|
||||
}
|
||||
|
||||
private static string BuildNodeSignature(ResourceNodeRuntime node) => $"{node.SystemId}|{node.OreRemaining:0.###}";
|
||||
|
||||
private static string BuildSpatialNodeSignature(NodeRuntime node) =>
|
||||
$"{node.SystemId}|{node.Kind.ToContractValue()}|{node.Position.X:0.###}|{node.Position.Y:0.###}|{node.Position.Z:0.###}|{node.BubbleId}|{node.ParentNodeId}|{node.OccupyingStructureId}|{node.OrbitReferenceId}";
|
||||
|
||||
private static string BuildLocalBubbleSignature(LocalBubbleRuntime bubble) =>
|
||||
$"{bubble.SystemId}|{bubble.NodeId}|{bubble.Radius:0.###}|{string.Join(",", bubble.OccupantShipIds.OrderBy(id => id, StringComparer.Ordinal))}|{string.Join(",", bubble.OccupantStationIds.OrderBy(id => id, StringComparer.Ordinal))}|{string.Join(",", bubble.OccupantClaimIds.OrderBy(id => id, StringComparer.Ordinal))}|{string.Join(",", bubble.OccupantConstructionSiteIds.OrderBy(id => id, StringComparer.Ordinal))}";
|
||||
|
||||
private static string BuildStationSignature(StationRuntime station) =>
|
||||
$"{station.SystemId}|{station.NodeId}|{station.BubbleId}|{station.AnchorNodeId}|{station.CommanderId}|{station.PolicySetId}|{BuildInventorySignature(station.Inventory)}|{station.EnergyStored:0.###}|{station.DockedShipIds.Count}|{station.DockingPadAssignments.Count}|{station.Population:0.###}|{station.PopulationCapacity:0.###}|{station.WorkforceRequired:0.###}|{station.WorkforceEffectiveRatio:0.###}|{string.Join(",", station.InstalledModules.OrderBy(moduleId => moduleId, StringComparer.Ordinal))}|{string.Join(",", station.MarketOrderIds.OrderBy(orderId => orderId, StringComparer.Ordinal))}|{station.ActiveConstruction?.ModuleId ?? "none"}|{station.ActiveConstruction?.ProgressSeconds.ToString("0.###") ?? "0"}";
|
||||
|
||||
private static string BuildClaimSignature(ClaimRuntime claim) =>
|
||||
$"{claim.FactionId}|{claim.SystemId}|{claim.NodeId}|{claim.BubbleId}|{claim.State}|{claim.Health:0.###}|{claim.ActivatesAtUtc:O}";
|
||||
|
||||
private static string BuildConstructionSiteSignature(ConstructionSiteRuntime site) =>
|
||||
$"{site.FactionId}|{site.SystemId}|{site.NodeId}|{site.BubbleId}|{site.TargetKind}|{site.TargetDefinitionId}|{site.BlueprintId}|{site.ClaimId}|{site.StationId}|{site.State}|{site.Progress:0.###}|{BuildInventorySignature(site.Inventory)}|{BuildInventorySignature(site.RequiredItems)}|{BuildInventorySignature(site.DeliveredItems)}|{string.Join(",", site.AssignedConstructorShipIds.OrderBy(id => id, StringComparer.Ordinal))}|{string.Join(",", site.MarketOrderIds.OrderBy(id => id, StringComparer.Ordinal))}";
|
||||
|
||||
private static string BuildMarketOrderSignature(MarketOrderRuntime order) =>
|
||||
$"{order.FactionId}|{order.StationId}|{order.ConstructionSiteId}|{order.Kind}|{order.ItemId}|{order.Amount:0.###}|{order.RemainingAmount:0.###}|{order.Valuation:0.###}|{order.ReserveThreshold?.ToString("0.###") ?? "none"}|{order.PolicySetId}|{order.State}";
|
||||
|
||||
private static string BuildPolicySignature(PolicySetRuntime policy) =>
|
||||
$"{policy.OwnerKind}|{policy.OwnerId}|{policy.TradeAccessPolicy}|{policy.DockingAccessPolicy}|{policy.ConstructionAccessPolicy}|{policy.OperationalRangePolicy}";
|
||||
|
||||
private static string BuildShipSignature(ShipRuntime ship) =>
|
||||
string.Join("|",
|
||||
ship.SystemId,
|
||||
ship.Position.X.ToString("0.###"),
|
||||
ship.Position.Y.ToString("0.###"),
|
||||
ship.Position.Z.ToString("0.###"),
|
||||
ship.Velocity.X.ToString("0.###"),
|
||||
ship.Velocity.Y.ToString("0.###"),
|
||||
ship.Velocity.Z.ToString("0.###"),
|
||||
ship.TargetPosition.X.ToString("0.###"),
|
||||
ship.TargetPosition.Y.ToString("0.###"),
|
||||
ship.TargetPosition.Z.ToString("0.###"),
|
||||
ship.State,
|
||||
ship.Order?.Kind ?? "none",
|
||||
ship.DefaultBehavior.Kind,
|
||||
ship.ControllerTask.Kind,
|
||||
ship.SpatialState.CurrentNodeId ?? "none",
|
||||
ship.SpatialState.CurrentBubbleId ?? "none",
|
||||
ship.DockedStationId ?? "none",
|
||||
ship.CommanderId ?? "none",
|
||||
ship.PolicySetId ?? "none",
|
||||
ship.WorkerPopulation.ToString("0.###"),
|
||||
ship.SpatialState.SpaceLayer,
|
||||
ship.SpatialState.CurrentNodeId ?? "none",
|
||||
ship.SpatialState.CurrentBubbleId ?? "none",
|
||||
ship.SpatialState.MovementRegime,
|
||||
ship.SpatialState.DestinationNodeId ?? "none",
|
||||
ship.SpatialState.Transit?.Regime ?? "none",
|
||||
ship.SpatialState.Transit?.OriginNodeId ?? "none",
|
||||
ship.SpatialState.Transit?.DestinationNodeId ?? "none",
|
||||
ship.SpatialState.Transit?.Progress.ToString("0.###") ?? "0",
|
||||
GetShipCargoAmount(ship).ToString("0.###"),
|
||||
GetInventoryAmount(ship.Inventory, "fuel").ToString("0.###"),
|
||||
ship.EnergyStored.ToString("0.###"),
|
||||
ship.Health.ToString("0.###"));
|
||||
|
||||
private static string BuildInventorySignature(IReadOnlyDictionary<string, float> inventory) =>
|
||||
string.Join(",",
|
||||
inventory
|
||||
.Where(entry => entry.Value > 0.001f)
|
||||
.OrderBy(entry => entry.Key, StringComparer.Ordinal)
|
||||
.Select(entry => $"{entry.Key}:{entry.Value:0.###}"));
|
||||
|
||||
private static string BuildFactionSignature(FactionRuntime faction) =>
|
||||
$"{faction.Credits:0.###}|{faction.PopulationTotal:0.###}|{faction.OreMined:0.###}|{faction.GoodsProduced:0.###}|{faction.ShipsBuilt}|{faction.ShipsLost}|{faction.DefaultPolicySetId}";
|
||||
|
||||
private static ResourceNodeDelta ToNodeDelta(ResourceNodeRuntime node) => new(
|
||||
node.Id,
|
||||
node.SystemId,
|
||||
ToDto(node.Position),
|
||||
node.SourceKind,
|
||||
node.OreRemaining,
|
||||
node.MaxOre,
|
||||
node.ItemId);
|
||||
|
||||
private static SpatialNodeDelta ToSpatialNodeDelta(NodeRuntime node) => new(
|
||||
node.Id,
|
||||
node.SystemId,
|
||||
node.Kind.ToContractValue(),
|
||||
ToDto(node.Position),
|
||||
node.BubbleId,
|
||||
node.ParentNodeId,
|
||||
node.OccupyingStructureId,
|
||||
node.OrbitReferenceId);
|
||||
|
||||
private static LocalBubbleDelta ToLocalBubbleDelta(LocalBubbleRuntime bubble) => new(
|
||||
bubble.Id,
|
||||
bubble.NodeId,
|
||||
bubble.SystemId,
|
||||
bubble.Radius,
|
||||
bubble.OccupantShipIds.OrderBy(id => id, StringComparer.Ordinal).ToList(),
|
||||
bubble.OccupantStationIds.OrderBy(id => id, StringComparer.Ordinal).ToList(),
|
||||
bubble.OccupantClaimIds.OrderBy(id => id, StringComparer.Ordinal).ToList(),
|
||||
bubble.OccupantConstructionSiteIds.OrderBy(id => id, StringComparer.Ordinal).ToList());
|
||||
|
||||
private static StationDelta ToStationDelta(StationRuntime station) => new(
|
||||
station.Id,
|
||||
station.Definition.Label,
|
||||
station.Definition.Category,
|
||||
station.SystemId,
|
||||
ToDto(station.Position),
|
||||
station.NodeId,
|
||||
station.BubbleId,
|
||||
station.AnchorNodeId,
|
||||
station.Definition.Color,
|
||||
station.DockedShipIds.Count,
|
||||
GetDockingPadCount(station),
|
||||
station.EnergyStored,
|
||||
ToInventoryEntries(station.Inventory),
|
||||
station.FactionId,
|
||||
station.CommanderId,
|
||||
station.PolicySetId,
|
||||
station.Population,
|
||||
station.PopulationCapacity,
|
||||
station.WorkforceRequired,
|
||||
station.WorkforceEffectiveRatio,
|
||||
station.InstalledModules.OrderBy(moduleId => moduleId, StringComparer.Ordinal).ToList(),
|
||||
station.MarketOrderIds.OrderBy(orderId => orderId, StringComparer.Ordinal).ToList());
|
||||
|
||||
private static ClaimDelta ToClaimDelta(ClaimRuntime claim) => new(
|
||||
claim.Id,
|
||||
claim.FactionId,
|
||||
claim.SystemId,
|
||||
claim.NodeId,
|
||||
claim.BubbleId,
|
||||
claim.State,
|
||||
claim.Health,
|
||||
claim.PlacedAtUtc,
|
||||
claim.ActivatesAtUtc);
|
||||
|
||||
private static ConstructionSiteDelta ToConstructionSiteDelta(ConstructionSiteRuntime site) => new(
|
||||
site.Id,
|
||||
site.FactionId,
|
||||
site.SystemId,
|
||||
site.NodeId,
|
||||
site.BubbleId,
|
||||
site.TargetKind,
|
||||
site.TargetDefinitionId,
|
||||
site.BlueprintId,
|
||||
site.ClaimId,
|
||||
site.StationId,
|
||||
site.State,
|
||||
site.Progress,
|
||||
ToInventoryEntries(site.Inventory),
|
||||
ToInventoryEntries(site.RequiredItems),
|
||||
ToInventoryEntries(site.DeliveredItems),
|
||||
site.AssignedConstructorShipIds.OrderBy(id => id, StringComparer.Ordinal).ToList(),
|
||||
site.MarketOrderIds.OrderBy(id => id, StringComparer.Ordinal).ToList());
|
||||
|
||||
private static MarketOrderDelta ToMarketOrderDelta(MarketOrderRuntime order) => new(
|
||||
order.Id,
|
||||
order.FactionId,
|
||||
order.StationId,
|
||||
order.ConstructionSiteId,
|
||||
order.Kind,
|
||||
order.ItemId,
|
||||
order.Amount,
|
||||
order.RemainingAmount,
|
||||
order.Valuation,
|
||||
order.ReserveThreshold,
|
||||
order.PolicySetId,
|
||||
order.State);
|
||||
|
||||
private static PolicySetDelta ToPolicySetDelta(PolicySetRuntime policy) => new(
|
||||
policy.Id,
|
||||
policy.OwnerKind,
|
||||
policy.OwnerId,
|
||||
policy.TradeAccessPolicy,
|
||||
policy.DockingAccessPolicy,
|
||||
policy.ConstructionAccessPolicy,
|
||||
policy.OperationalRangePolicy);
|
||||
|
||||
private static ShipDelta ToShipDelta(ShipRuntime ship) => new(
|
||||
ship.Id,
|
||||
ship.Definition.Label,
|
||||
ship.Definition.Role,
|
||||
ship.Definition.ShipClass,
|
||||
ship.SystemId,
|
||||
ToDto(ship.Position),
|
||||
ToDto(ship.Velocity),
|
||||
ToDto(ship.TargetPosition),
|
||||
ship.State,
|
||||
ship.Order?.Kind,
|
||||
ship.DefaultBehavior.Kind,
|
||||
ship.ControllerTask.Kind,
|
||||
ship.SpatialState.CurrentNodeId,
|
||||
ship.SpatialState.CurrentBubbleId,
|
||||
ship.DockedStationId,
|
||||
ship.CommanderId,
|
||||
ship.PolicySetId,
|
||||
ship.Definition.CargoCapacity,
|
||||
ship.WorkerPopulation,
|
||||
ship.EnergyStored,
|
||||
ToInventoryEntries(ship.Inventory),
|
||||
ship.FactionId,
|
||||
ship.Health,
|
||||
ship.History.ToList(),
|
||||
ToShipSpatialStateSnapshot(ship.SpatialState));
|
||||
|
||||
private static IReadOnlyList<InventoryEntry> ToInventoryEntries(IReadOnlyDictionary<string, float> inventory) =>
|
||||
inventory
|
||||
.Where(entry => entry.Value > 0.001f)
|
||||
.OrderBy(entry => entry.Key, StringComparer.Ordinal)
|
||||
.Select(entry => new InventoryEntry(entry.Key, entry.Value))
|
||||
.ToList();
|
||||
|
||||
private static FactionDelta ToFactionDelta(FactionRuntime faction) => new(
|
||||
faction.Id,
|
||||
faction.Label,
|
||||
faction.Color,
|
||||
faction.Credits,
|
||||
faction.PopulationTotal,
|
||||
faction.OreMined,
|
||||
faction.GoodsProduced,
|
||||
faction.ShipsBuilt,
|
||||
faction.ShipsLost,
|
||||
faction.DefaultPolicySetId);
|
||||
|
||||
private static ShipSpatialStateSnapshot ToShipSpatialStateSnapshot(ShipSpatialStateRuntime state) => new(
|
||||
state.SpaceLayer,
|
||||
state.CurrentSystemId,
|
||||
state.CurrentNodeId,
|
||||
state.CurrentBubbleId,
|
||||
state.LocalPosition is null ? null : ToDto(state.LocalPosition.Value),
|
||||
state.SystemPosition is null ? null : ToDto(state.SystemPosition.Value),
|
||||
state.MovementRegime,
|
||||
state.DestinationNodeId,
|
||||
state.Transit is null ? null : new ShipTransitSnapshot(
|
||||
state.Transit.Regime,
|
||||
state.Transit.OriginNodeId,
|
||||
state.Transit.DestinationNodeId,
|
||||
state.Transit.StartedAtUtc,
|
||||
state.Transit.ArrivalDueAtUtc,
|
||||
state.Transit.Progress));
|
||||
|
||||
private static void EmitShipStateEvents(
|
||||
ShipRuntime ship,
|
||||
string previousState,
|
||||
string previousBehavior,
|
||||
string previousTask,
|
||||
string controllerEvent,
|
||||
ICollection<SimulationEventRecord> events)
|
||||
{
|
||||
var occurredAtUtc = DateTimeOffset.UtcNow;
|
||||
|
||||
if (previousState != ship.State)
|
||||
{
|
||||
events.Add(new SimulationEventRecord("ship", ship.Id, "state-changed", $"{ship.Definition.Label} {previousState} -> {ship.State}", occurredAtUtc));
|
||||
}
|
||||
|
||||
if (previousBehavior != ship.DefaultBehavior.Kind)
|
||||
{
|
||||
events.Add(new SimulationEventRecord("ship", ship.Id, "behavior-changed", $"{ship.Definition.Label} behavior {previousBehavior} -> {ship.DefaultBehavior.Kind}", occurredAtUtc));
|
||||
}
|
||||
|
||||
if (previousTask != ship.ControllerTask.Kind)
|
||||
{
|
||||
events.Add(new SimulationEventRecord("ship", ship.Id, "task-changed", $"{ship.Definition.Label} task {previousTask} -> {ship.ControllerTask.Kind}", occurredAtUtc));
|
||||
}
|
||||
|
||||
if (controllerEvent != "none")
|
||||
{
|
||||
events.Add(new SimulationEventRecord("ship", ship.Id, controllerEvent, $"{ship.Definition.Label} {controllerEvent}", occurredAtUtc));
|
||||
}
|
||||
}
|
||||
|
||||
private static Vector3Dto ToDto(Vector3 value) => new(value.X, value.Y, value.Z);
|
||||
}
|
||||
Reference in New Issue
Block a user