681 lines
26 KiB
C#
681 lines
26 KiB
C#
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);
|
|
}
|