Complete universe model migration

This commit is contained in:
2026-04-07 14:16:59 -04:00
parent d0c6e30304
commit 6c92ab50c8
76 changed files with 2061 additions and 1072 deletions

View File

@@ -104,12 +104,12 @@ internal sealed class SimulationEngine
CreateWreck(world, "station", station.Id, station.SystemId, station.Position, station.MaxHealth * 0.12f);
world.Stations.Remove(station);
if (station.CelestialId is not null && world.Celestials.FirstOrDefault(candidate => candidate.Id == station.CelestialId) is { } celestial)
if (station.AnchorId is not null && world.Anchors.FirstOrDefault(candidate => candidate.Id == station.AnchorId) is { } anchor)
{
celestial.OccupyingStructureId = null;
anchor.OccupyingStructureId = null;
}
foreach (var claim in world.Claims.Where(candidate => candidate.CelestialId == station.CelestialId))
foreach (var claim in world.Claims.Where(candidate => candidate.AnchorId == station.AnchorId))
{
claim.Health = 0f;
claim.State = ClaimStateKinds.Destroyed;

View File

@@ -24,6 +24,7 @@ internal sealed class SimulationProjectionService
false,
events,
BuildCelestialDeltas(world),
BuildAnchorDeltas(world),
BuildNodeDeltas(world),
BuildStationDeltas(world),
BuildClaimDeltas(world),
@@ -87,26 +88,37 @@ internal sealed class SimulationProjectionService
c.Kind,
c.OrbitalAnchor,
c.LocalSpaceRadius,
c.ParentNodeId,
c.ParentAnchorId,
c.OccupyingStructureId,
c.OrbitReferenceId)).ToList(),
world.Anchors.Select(ToAnchorDelta).Select(anchor => new AnchorSnapshot(
anchor.Id,
anchor.SystemId,
anchor.Kind,
anchor.SystemPosition,
anchor.LocalSpaceRadius,
anchor.ParentAnchorId,
anchor.OccupyingStructureId,
anchor.OrbitReferenceId)).ToList(),
world.Nodes.Select(ToNodeDelta).Select(node => new ResourceNodeSnapshot(
node.Id,
node.AnchorId,
node.SystemId,
node.LocalPosition,
node.CelestialId,
node.LocalSpaceRadius,
node.SourceKind,
node.OreRemaining,
node.MaxOre,
node.ItemId)).ToList(),
node.ItemId,
node.Deposits)).ToList(),
world.Stations.Select(station => ToStationDelta(world, station)).Select(station => new StationSnapshot(
station.Id,
station.Label,
station.Category,
station.Objective,
station.SystemId,
station.AnchorId,
station.LocalPosition,
station.CelestialId,
station.Color,
station.DockedShips,
station.DockedShipIds,
@@ -127,7 +139,7 @@ internal sealed class SimulationProjectionService
claim.Id,
claim.FactionId,
claim.SystemId,
claim.CelestialId,
claim.AnchorId,
claim.State,
claim.Health,
claim.PlacedAtUtc,
@@ -136,7 +148,7 @@ internal sealed class SimulationProjectionService
site.Id,
site.FactionId,
site.SystemId,
site.CelestialId,
site.AnchorId,
site.TargetKind,
site.TargetDefinitionId,
site.BlueprintId,
@@ -180,6 +192,7 @@ internal sealed class SimulationProjectionService
ship.Purpose,
ship.Type,
ship.SystemId,
ship.AnchorId,
ship.LocalPosition,
ship.LocalVelocity,
ship.TargetLocalPosition,
@@ -196,11 +209,11 @@ internal sealed class SimulationProjectionService
ship.ControlReason,
ship.LastReplanReason,
ship.LastAccessFailureReason,
ship.CelestialId,
ship.DockedStationId,
ship.CommanderId,
ship.PolicySetId,
ship.CargoCapacity,
ship.CargoTypes,
ship.TravelSpeed,
ship.TravelSpeedUnit,
ship.Inventory,
@@ -239,6 +252,11 @@ internal sealed class SimulationProjectionService
celestial.LastDeltaSignature = BuildCelestialSignature(celestial);
}
foreach (var anchor in world.Anchors)
{
anchor.LastDeltaSignature = BuildAnchorSignature(anchor);
}
foreach (var station in world.Stations)
{
station.LastDeltaSignature = BuildStationSignature(world, station);
@@ -298,6 +316,24 @@ internal sealed class SimulationProjectionService
return deltas;
}
private static IReadOnlyList<AnchorDelta> BuildAnchorDeltas(SimulationWorld world)
{
var deltas = new List<AnchorDelta>();
foreach (var anchor in world.Anchors)
{
var signature = BuildAnchorSignature(anchor);
if (signature == anchor.LastDeltaSignature)
{
continue;
}
anchor.LastDeltaSignature = signature;
deltas.Add(ToAnchorDelta(anchor));
}
return deltas;
}
private static IReadOnlyList<CelestialDelta> BuildCelestialDeltas(SimulationWorld world)
{
var deltas = new List<CelestialDelta>();
@@ -466,17 +502,30 @@ internal sealed class SimulationProjectionService
string.Equals(c.Kind, CommanderKind.Faction, StringComparison.Ordinal));
private static string BuildNodeSignature(ResourceNodeRuntime node) =>
$"{node.SystemId}|{node.Position.X:0.###}|{node.Position.Y:0.###}|{node.Position.Z:0.###}|{node.CelestialId}|{node.OreRemaining:0.###}";
string.Join("|",
node.SystemId,
node.AnchorId,
$"{node.Position.X:0.###}",
$"{node.Position.Y:0.###}",
$"{node.Position.Z:0.###}",
$"{node.OreRemaining:0.###}",
string.Join(",",
node.Deposits
.OrderBy(deposit => deposit.Id, StringComparer.Ordinal)
.Select(deposit => $"{deposit.Id}:{deposit.Position.X:0.###}:{deposit.Position.Y:0.###}:{deposit.Position.Z:0.###}:{deposit.OreRemaining:0.###}")));
private static string BuildCelestialSignature(CelestialRuntime celestial) =>
$"{celestial.SystemId}|{celestial.Kind.ToContractValue()}|{celestial.Position.X:0.###}|{celestial.Position.Y:0.###}|{celestial.Position.Z:0.###}|{celestial.LocalSpaceRadius:0.###}|{celestial.ParentNodeId}|{celestial.OccupyingStructureId}|{celestial.OrbitReferenceId}";
$"{celestial.SystemId}|{celestial.Kind.ToContractValue()}|{celestial.Position.X:0.###}|{celestial.Position.Y:0.###}|{celestial.Position.Z:0.###}|{celestial.LocalSpaceRadius:0.###}|{celestial.ParentAnchorId}|{celestial.OccupyingStructureId}|{celestial.OrbitReferenceId}";
private static string BuildAnchorSignature(AnchorRuntime anchor) =>
$"{anchor.SystemId}|{anchor.Kind.ToContractValue()}|{anchor.Position.X:0.###}|{anchor.Position.Y:0.###}|{anchor.Position.Z:0.###}|{anchor.LocalSpaceRadius:0.###}|{anchor.ParentAnchorId}|{anchor.OccupyingStructureId}|{anchor.OrbitReferenceId}|{anchor.SourceEntityKind}|{anchor.SourceEntityId}";
private static string BuildStationSignature(SimulationWorld world, StationRuntime station)
{
var processes = ToStationActionProgressSnapshots(world, station);
return string.Join("|",
station.SystemId,
station.CelestialId ?? "none",
station.AnchorId ?? "none",
station.CommanderId ?? "none",
station.PolicySetId ?? "none",
BuildInventorySignature(station.Inventory),
@@ -495,10 +544,10 @@ internal sealed class SimulationProjectionService
}
private static string BuildClaimSignature(ClaimRuntime claim) =>
$"{claim.FactionId}|{claim.SystemId}|{claim.CelestialId}|{claim.State}|{claim.Health:0.###}|{claim.ActivatesAtUtc:O}";
$"{claim.FactionId}|{claim.SystemId}|{claim.AnchorId}|{claim.State}|{claim.Health:0.###}|{claim.ActivatesAtUtc:O}";
private static string BuildConstructionSiteSignature(ConstructionSiteRuntime site) =>
$"{site.FactionId}|{site.SystemId}|{site.CelestialId}|{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))}";
$"{site.FactionId}|{site.SystemId}|{site.AnchorId}|{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}";
@@ -552,17 +601,17 @@ internal sealed class SimulationProjectionService
string.Join(",",
ToActiveSubTaskSnapshots(ship).Select(subTask =>
$"{subTask.Id}:{subTask.Kind}:{subTask.Status}:{subTask.Progress:0.###}:{subTask.ElapsedSeconds:0.###}:{subTask.BlockingReason ?? "none"}")),
ship.SpatialState.CurrentCelestialId ?? "none",
ship.SpatialState.CurrentAnchorId ?? "none",
ship.DockedStationId ?? "none",
ship.CommanderId ?? "none",
ship.PolicySetId ?? "none",
ship.SpatialState.SpaceLayer.ToContractValue(),
ship.SpatialState.CurrentCelestialId ?? "none",
ship.SpatialState.CurrentAnchorId ?? "none",
ship.SpatialState.MovementRegime.ToContractValue(),
ship.SpatialState.DestinationNodeId ?? "none",
ship.SpatialState.DestinationAnchorId ?? "none",
ship.SpatialState.Transit?.Regime.ToContractValue() ?? "none",
ship.SpatialState.Transit?.OriginNodeId ?? "none",
ship.SpatialState.Transit?.DestinationNodeId ?? "none",
ship.SpatialState.Transit?.OriginAnchorId ?? "none",
ship.SpatialState.Transit?.DestinationAnchorId ?? "none",
ship.SpatialState.Transit?.Progress.ToString("0.###") ?? "0",
GetShipCargoAmount(ship).ToString("0.###"),
ship.Skills.Navigation.ToString(CultureInfo.InvariantCulture),
@@ -653,13 +702,33 @@ internal sealed class SimulationProjectionService
private static ResourceNodeDelta ToNodeDelta(ResourceNodeRuntime node) => new(
node.Id,
node.AnchorId,
node.SystemId,
ToDto(node.Position),
node.CelestialId,
node.LocalSpaceRadius,
node.SourceKind,
node.OreRemaining,
node.MaxOre,
node.ItemId);
node.ItemId,
node.Deposits.Select(ToResourceDepositSnapshot).ToList());
private static ResourceDepositSnapshot ToResourceDepositSnapshot(ResourceDepositRuntime deposit) => new(
deposit.Id,
deposit.NodeId,
deposit.AnchorId,
ToDto(deposit.Position),
deposit.OreRemaining,
deposit.MaxOre);
private static AnchorDelta ToAnchorDelta(AnchorRuntime anchor) => new(
anchor.Id,
anchor.SystemId,
anchor.Kind.ToContractValue(),
ToDto(anchor.Position),
anchor.LocalSpaceRadius,
anchor.ParentAnchorId,
anchor.OccupyingStructureId,
anchor.OrbitReferenceId);
private static CelestialDelta ToCelestialDelta(CelestialRuntime celestial) => new(
celestial.Id,
@@ -667,7 +736,7 @@ internal sealed class SimulationProjectionService
celestial.Kind.ToContractValue(),
ToDto(celestial.Position),
celestial.LocalSpaceRadius,
celestial.ParentNodeId,
celestial.ParentAnchorId,
celestial.OccupyingStructureId,
celestial.OrbitReferenceId);
@@ -677,8 +746,8 @@ internal sealed class SimulationProjectionService
station.Category,
station.Objective,
station.SystemId,
station.AnchorId,
ToDto(station.Position),
station.CelestialId,
station.Color,
station.DockedShipIds.Count,
station.DockedShipIds.OrderBy(id => id, StringComparer.Ordinal).ToList(),
@@ -737,7 +806,7 @@ internal sealed class SimulationProjectionService
claim.Id,
claim.FactionId,
claim.SystemId,
claim.CelestialId,
claim.AnchorId,
claim.State,
claim.Health,
claim.PlacedAtUtc,
@@ -747,7 +816,7 @@ internal sealed class SimulationProjectionService
site.Id,
site.FactionId,
site.SystemId,
site.CelestialId,
site.AnchorId,
site.TargetKind,
site.TargetDefinitionId,
site.BlueprintId,
@@ -811,6 +880,7 @@ internal sealed class SimulationProjectionService
ship.Definition.Purpose.ToDataValue(),
ship.Definition.Type.ToDataValue(),
ship.SystemId,
ship.SpatialState.CurrentAnchorId,
ToDto(ship.Position),
ToDto(ship.Velocity),
ToDto(ship.TargetPosition),
@@ -827,11 +897,16 @@ internal sealed class SimulationProjectionService
ship.ControlReason,
ship.LastReplanReason,
ship.LastAccessFailureReason,
ship.SpatialState.CurrentCelestialId,
ship.DockedStationId,
ship.CommanderId,
ship.PolicySetId,
ship.Definition.GetTotalCargoCapacity(),
ship.Definition.Cargo
.SelectMany(entry => entry.Types)
.Where(type => !string.IsNullOrWhiteSpace(type))
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(type => type, StringComparer.OrdinalIgnoreCase)
.ToList(),
ToShipTravelSpeed(ship).Speed,
ToShipTravelSpeed(ship).Unit,
@@ -880,7 +955,7 @@ internal sealed class SimulationProjectionService
order.SourceStationId,
order.DestinationStationId,
order.ItemId,
order.NodeId,
order.AnchorId,
order.ConstructionSiteId,
order.ModuleId,
order.WaitSeconds,
@@ -906,7 +981,7 @@ internal sealed class SimulationProjectionService
behavior.AreaSystemId,
behavior.TargetEntityId,
behavior.ItemId,
behavior.PreferredNodeId,
behavior.PreferredAnchorId,
behavior.PreferredConstructionSiteId,
behavior.PreferredModuleId,
behavior.TargetPosition is null ? null : ToDto(behavior.TargetPosition.Value),
@@ -929,7 +1004,7 @@ internal sealed class SimulationProjectionService
template.SourceStationId,
template.DestinationStationId,
template.ItemId,
template.NodeId,
template.AnchorId,
template.ConstructionSiteId,
template.ModuleId,
template.WaitSeconds,
@@ -1002,10 +1077,12 @@ internal sealed class SimulationProjectionService
subTask.Kind,
subTask.Status.ToContractValue(),
subTask.Summary,
subTask.TargetEntityId,
subTask.TargetSystemId,
subTask.TargetNodeId,
subTask.TargetPosition is null ? null : ToDto(subTask.TargetPosition.Value),
subTask.TargetEntityId,
subTask.TargetSystemId,
subTask.TargetAnchorId,
subTask.TargetResourceNodeId,
subTask.TargetResourceDepositId,
subTask.TargetPosition is null ? null : ToDto(subTask.TargetPosition.Value),
subTask.ItemId,
subTask.ModuleId,
subTask.Threshold,
@@ -1408,7 +1485,7 @@ internal sealed class SimulationProjectionService
claim.SourceClaimId,
claim.FactionId,
claim.SystemId,
claim.CelestialId,
claim.AnchorId,
claim.Status,
claim.ClaimKind,
claim.ClaimStrength,
@@ -1564,15 +1641,15 @@ internal sealed class SimulationProjectionService
private static ShipSpatialStateSnapshot ToShipSpatialStateSnapshot(ShipSpatialStateRuntime state) => new(
state.SpaceLayer.ToContractValue(),
state.CurrentSystemId,
state.CurrentCelestialId,
state.CurrentAnchorId,
state.LocalPosition is null ? null : ToDto(state.LocalPosition.Value),
state.SystemPosition is null ? null : ToDto(state.SystemPosition.Value),
state.MovementRegime.ToContractValue(),
state.DestinationNodeId,
state.DestinationAnchorId,
state.Transit is null ? null : new ShipTransitSnapshot(
state.Transit.Regime.ToContractValue(),
state.Transit.OriginNodeId,
state.Transit.DestinationNodeId,
state.Transit.OriginAnchorId,
state.Transit.DestinationAnchorId,
state.Transit.StartedAtUtc,
state.Transit.ArrivalDueAtUtc,
state.Transit.Progress));