From dd21e287f4ad22bf2a40b3da483ab07a5d0c65d6 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 14 Jun 2026 12:52:48 +0100 Subject: [PATCH] Persist zone celestial state in run checkpoints --- .voidbot/state/aetheria.cc | Bin 85751 -> 85954 bytes Aetheria.State.Smoke/Program.cs | 50 ++++- Aetheria.State.Unity.Smoke/Program.cs | 53 ++++++ .../AetheriaRuntimeCommitLogApplier.cs | 129 ++++++++++++- .../AetheriaRuntimeStateDocuments.cs | 160 ++++++++++++++++ Aetheria.State/README.md | 20 +- Assets/Scripts/Gameplay/ActionGameManager.cs | 129 +++++++++++++ .../AetheriaRuntimeStateCommitDocument.cs | 175 ++++++++++++++++++ docs/aetheria-perfect-machine-map.md | 16 +- 9 files changed, 719 insertions(+), 13 deletions(-) diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index ca672f540e5b8ff2f8c47033bd62a66e8bdc96ec..1079ad251f8829cbe2a58e9290030e983bb3664a 100644 GIT binary patch delta 2077 zcmZuyO>7%Q6xQUYq%>-SN=n*9WRRexs=bb5J5CGDfB8A2X_8u%q6lfcJGQ6Uo!QRJ zCUGwosyLMkEYhA32YxD~3W^Zwu@s3Lf^&O8a6{^$IrWBkv%3yXnu}$R-@Ny|?|t8! zXP@`}@FXKon*Qk4 z*`E7nd(d+6b&F8lppud7O~Gl_rY=>Q5jU%wK6SE66HTN18MN+JR|1ah2DLYQ!MMgI z9xG0K)eu@?F1D~wrD96sI^|TtMF$I6Wja?CP7~LN4PkIo;5av)O)IZKqE%DJ`r390 z^B$w$!aNzi2K6-asQG)&4X7_o)i@z0+`$JY^(l84uVc9vtRw8eqg*QshZmoWwy+1d zF?8-Fh@QiOmke?hSDF(JqbdWDwj0FR6BO?+z@bkh!4sexdyWt7PgfiE~blXr@r8OZb)bM=flDGupz&TU6 zLir|>f}4>*Z_vc7Qq-%u&FuVYgGj$aLMbeu$%|2Q!Lw~Fkad$nA)5q>;_&zIMEKz8 zp%w=E4Qjyd6MKWyDcr767!${|xwg!JmA!=#w4)sSsg9-zq1j@oh5|R=gXn5nNm*@U|C(r@p1eF?z zo4NZ9yd~rY1~&z+VOUTEPz47<$p#>*#%l-9A=Vq+)=7+ zZ8IFEv8kMF>%In)!e3&0k5chRP^kGG?B7Y5}Rm9Eg5 z`YhpT))AUh4a#Gn3u`^xxjl%?=ZR>njD|mbb~Jo%YA9NoK?Bj#Y1AA2IgQG1K00%S zfTbL~#dIU(GDf?vn`mqXZMWV|gWD~q6vy(9g`cG9xNDM2(@lE@`TeUP)pqi(m}Q-f zuV>?BVY#CIm05*%OsVk5RR3xvNnmydz&>Dk#GObA#({UpESr1=E0L_K*nr@{z1ECg zJ&jHdm5=HcD delta 1894 zcmZWqO=uid9B0y`O)9oM)EJ}I=SK*^>})pKbln7F*CdK!Nt!(fQs};&`R~rtnfJ!` z-Xx}iK?+6iIK>e}1dAV_(B6u@SOpPpxk-8v4|)|Zder}WGn>!b%zxhdfBZiF^W$d& z|9(1f`^&z;nW?#{8F!|VU08M=PJinAaHxn(*Rsa4Gn|TJS9_tEsXR2;|3u%y-D?y5 zcP9FsOVzP51s#B7v?LR*Sp+2oN-@DpN>hz*G2@U`#+}Pss!<$W%TG4*gM=v(vUMPO zGX|g7YmiqD{96avdZQ8(b0WjF{vG6V7;-w7ewT(h*)QU1$Jgg^jB1(dXn*1rztX^s5_Y!NpbOk~h=UzviAeB-^p*KUJc)P!36d_5*IsH8 zFGwV}a_G=d0iMw`6qrV!ijcj|J}GEW7*YY1ux4?=Tw$>p+xWXYgvBMII`rXYj} zwA{26b~V<Db|^fDxp4VMKgu9xt_F~?V}Kny$PP7GxtF!OjJ;ofJgXNP6LjQ8E6+= zf^O=0LAmLQ@f@SnnI4&#z?RG^=!taKE_;GBrSE-rW_EUE&kRS5U?x43 z04I}vdTzLqesXDeem?KUE}fnutPRiwRwIT((_cO>+Eps15H%0da&7h1`HM8()$`#> z*32ki1UO%CX0`26`7IcAV5T+bs2k~jKcCA!XgFt5aeW{=*>Gy3cUO@lWRP~vZqWOf z%vi&D=e&F9`<~Sd-as~(4oIakYyGR-Yd0lyc2={u8_swt7EYvB7KYQ`{&}*r^GuQG za01jE>)?hp6BO5dbA9CW!8Ld}egCiFwE6k-V`D|8+)6pENo=RLo6Pz$`{6}r^g;9U zskrs?_3CuBmi_HIBd02J`-9o3Md#7_*SJgVD!o;}+w9*=lRcmkj4_0TcY5)KL diff --git a/Aetheria.State.Smoke/Program.cs b/Aetheria.State.Smoke/Program.cs index e15851315..840bb3476 100644 --- a/Aetheria.State.Smoke/Program.cs +++ b/Aetheria.State.Smoke/Program.cs @@ -235,7 +235,47 @@ await using (var node = await AetheriaStateNode.OpenAsync(statePath, "aetheria-s AdjacentZoneIndices = [1], FactionIndices = [0], OwnerFactionIndex = 0, - EntityKeys = [entityKey.ToString()] + EntityKeys = [entityKey.ToString()], + Orbits = + [ + new AetheriaOrbitSnapshot + { + OrbitId = "smoke:orbit", + ParentId = "smoke:parent-orbit", + Distance = 100, + Phase = 0.25, + FixedPosition = new AetheriaVector2 { X = 5, Y = -6 } + } + ], + Bodies = + [ + new AetheriaBodySnapshot + { + BodyId = "smoke:body", + Kind = "asteroid_belt", + Name = "Smoke Belt", + OrbitId = "smoke:orbit", + Mass = 42, + Resources = + [ + new AetheriaBodyResource + { + ItemKey = AetheriaCatalogKeys.ItemDefinitionFromLegacyId(itemLegacyId).ToString(), + Amount = 3.5 + } + ], + Asteroids = + [ + new AetheriaAsteroidSnapshot + { + Distance = 7, + Phase = 0.75, + Size = 2, + RotationSpeed = 0.5 + } + ] + } + ] }); await node.PutRunStateAsync(runKey, new AetheriaRunState @@ -410,7 +450,13 @@ await using (var reopened = await AetheriaStateNode.OpenAsync(statePath, "aether if (zoneState?.EntityKeys.Length != 1 || zoneState.EntityKeys[0] != entityKey.ToString() || zoneState.Position.X != 4.0 || - zoneState.OwnerFactionIndex != 0) + zoneState.OwnerFactionIndex != 0 || + zoneState.Orbits.Length != 1 || + zoneState.Orbits[0].OrbitId != "smoke:orbit" || + zoneState.Bodies.Length != 1 || + zoneState.Bodies[0].Kind != "asteroid_belt" || + zoneState.Bodies[0].Resources.Length != 1 || + zoneState.Bodies[0].Asteroids.Length != 1) { throw new InvalidOperationException("Zone state did not survive flush/reopen."); } diff --git a/Aetheria.State.Unity.Smoke/Program.cs b/Aetheria.State.Unity.Smoke/Program.cs index 68558ba05..1269b6076 100644 --- a/Aetheria.State.Unity.Smoke/Program.cs +++ b/Aetheria.State.Unity.Smoke/Program.cs @@ -201,6 +201,51 @@ try PositionY = -4.0, AdjacentZoneIndices = new[] { 1 }, OwnerFactionIndex = 0, + Orbits = new[] + { + new AetheriaRuntimeOrbitSnapshotCommit + { + OrbitLegacyId = "smoke:orbit", + ParentLegacyId = "smoke:parent-orbit", + Distance = 100.0, + Phase = 0.25, + FixedPositionX = 5.0, + FixedPositionY = -6.0 + } + }, + Bodies = new[] + { + new AetheriaRuntimeBodySnapshotCommit + { + BodyLegacyId = "smoke:body", + Kind = "asteroid_belt", + Name = "Smoke Belt", + OrbitLegacyId = "smoke:orbit", + Mass = 42.0, + Resources = new[] + { + new AetheriaRuntimeBodyResourceCommit + { + ItemDefinitionLegacyId = "smoke:ore", + Amount = 3.5 + } + }, + BodyRadiusMultiplier = 1.25, + GravityRadiusMultiplier = 2.0, + GravityDepthMultiplier = 0.5, + GravityDepthExponent = 12.0, + Asteroids = new[] + { + new AetheriaRuntimeAsteroidCommit + { + Distance = 7.0, + Phase = 0.75, + Size = 2.0, + RotationSpeed = 0.5 + } + } + } + }, Entities = new[] { new AetheriaRuntimeEntitySnapshotCommit @@ -270,6 +315,14 @@ try run.FactionRelationships[0].Relationship == "Friendly" && run.FactionRelationships[0].Standing == 3 && zone?.EntityKeys.Length == 1 && + zone.Orbits.Length == 1 && + zone.Orbits[0].OrbitId == "smoke:orbit" && + zone.Orbits[0].FixedPosition.X == 5.0 && + zone.Bodies.Length == 1 && + zone.Bodies[0].Kind == "asteroid_belt" && + zone.Bodies[0].Resources.Length == 1 && + zone.Bodies[0].Resources[0].ItemKey == "aetheria.item_definition:legacy:smoke:ore" && + zone.Bodies[0].Asteroids.Length == 1 && entity?.Equipment.Length == 1 && entity.WeaponGroups.Length == 1 && AetheriaRuntimeStateCommitLog.ReadPending(commitSmokeStatePath).Count == 0; diff --git a/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs b/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs index 243aba673..c234a9b47 100644 --- a/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs +++ b/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs @@ -256,7 +256,9 @@ public static class AetheriaRuntimeCommitLogApplier AdjacentZoneIndices = ToIntArray(zone.AdjacentZoneIndices), FactionIndices = ToIntArray(zone.FactionIndices), OwnerFactionIndex = zone.OwnerFactionIndex, - EntityKeys = entityKeys.ToArray() + EntityKeys = entityKeys.ToArray(), + Orbits = ToOrbitSnapshots(zone.Orbits), + Bodies = ToBodySnapshots(zone.Bodies) }) .ConfigureAwait(false); zoneKeys.Add(zoneKey.ToString()); @@ -267,6 +269,131 @@ public static class AetheriaRuntimeCommitLogApplier await node.PutRunStateAsync(RunKey(run.RunId), run).ConfigureAwait(false); } + private static AetheriaOrbitSnapshot[] ToOrbitSnapshots( + IReadOnlyList? orbits) + { + return (orbits ?? Array.Empty()) + .Select(orbit => new AetheriaOrbitSnapshot + { + OrbitId = orbit.OrbitLegacyId ?? "", + ParentId = orbit.ParentLegacyId ?? "", + Distance = orbit.Distance, + Phase = orbit.Phase, + FixedPosition = new AetheriaVector2 + { + X = orbit.FixedPositionX, + Y = orbit.FixedPositionY + } + }) + .ToArray(); + } + + private static AetheriaBodySnapshot[] ToBodySnapshots( + IReadOnlyList? bodies) + { + return (bodies ?? Array.Empty()) + .Select(body => new AetheriaBodySnapshot + { + BodyId = body.BodyLegacyId ?? "", + Kind = body.Kind ?? "", + Name = body.Name ?? "", + OrbitId = body.OrbitLegacyId ?? "", + Mass = body.Mass, + Resources = ToBodyResources(body.Resources), + BodyRadiusMultiplier = body.BodyRadiusMultiplier, + GravityRadiusMultiplier = body.GravityRadiusMultiplier, + GravityDepthMultiplier = body.GravityDepthMultiplier, + GravityDepthExponent = body.GravityDepthExponent, + Asteroids = ToAsteroidSnapshots(body.Asteroids), + GasGiantVisual = ToGasGiantVisual(body.GasGiantVisual), + SunVisual = ToSunVisual(body.SunVisual) + }) + .ToArray(); + } + + private static AetheriaBodyResource[] ToBodyResources( + IReadOnlyList? resources) + { + return (resources ?? Array.Empty()) + .Select(resource => new AetheriaBodyResource + { + ItemKey = ReferenceKey("aetheria.item_definition", resource.ItemDefinitionLegacyId ?? ""), + Amount = resource.Amount + }) + .ToArray(); + } + + private static AetheriaAsteroidSnapshot[] ToAsteroidSnapshots( + IReadOnlyList? asteroids) + { + return (asteroids ?? Array.Empty()) + .Select(asteroid => new AetheriaAsteroidSnapshot + { + Distance = asteroid.Distance, + Phase = asteroid.Phase, + Size = asteroid.Size, + RotationSpeed = asteroid.RotationSpeed + }) + .ToArray(); + } + + private static AetheriaGasGiantVisualState ToGasGiantVisual(AetheriaRuntimeGasGiantVisualCommit? visual) + { + if (visual == null) + return new AetheriaGasGiantVisualState(); + + return new AetheriaGasGiantVisualState + { + FirstOffsetDomainRotationSpeed = visual.FirstOffsetDomainRotationSpeed, + FirstOffsetRotationSpeed = visual.FirstOffsetRotationSpeed, + SecondOffsetDomainRotationSpeed = visual.SecondOffsetDomainRotationSpeed, + SecondOffsetRotationSpeed = visual.SecondOffsetRotationSpeed, + AlbedoRotationSpeed = visual.AlbedoRotationSpeed, + WaveRadiusMultiplier = visual.WaveRadiusMultiplier, + WaveDepthMultiplier = visual.WaveDepthMultiplier, + WaveDepthExponent = visual.WaveDepthExponent, + WaveSpeedMultiplier = visual.WaveSpeedMultiplier, + MaterialOverrides = (visual.MaterialOverrides ?? Array.Empty()).ToArray(), + Colors = (visual.Colors ?? Array.Empty()) + .Select(ToColor) + .ToArray() + }; + } + + private static AetheriaSunVisualState ToSunVisual(AetheriaRuntimeSunVisualCommit? visual) + { + if (visual == null) + return new AetheriaSunVisualState(); + + return new AetheriaSunVisualState + { + LightColor = new AetheriaVector3 + { + X = visual.LightColorX, + Y = visual.LightColorY, + Z = visual.LightColorZ + }, + FogTintColor = new AetheriaVector3 + { + X = visual.FogTintColorX, + Y = visual.FogTintColorY, + Z = visual.FogTintColorZ + }, + LightRadiusMultiplier = visual.LightRadiusMultiplier + }; + } + + private static AetheriaColor ToColor(AetheriaRuntimeColorCommit color) + { + return new AetheriaColor + { + X = color.X, + Y = color.Y, + Z = color.Z, + W = color.W + }; + } + private static AetheriaEntitySnapshot ToEntitySnapshot( AetheriaRuntimeEntitySnapshotCommit entity, string runId, diff --git a/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs b/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs index bf438db3b..38a679598 100644 --- a/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs +++ b/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs @@ -250,6 +250,166 @@ public sealed class AetheriaZoneState [Key(5)] [CultReference(typeof(AetheriaEntitySnapshot), many: true)] public string[] EntityKeys { get; set; } = []; + + [Key(6)] + public AetheriaOrbitSnapshot[] Orbits { get; set; } = []; + + [Key(7)] + public AetheriaBodySnapshot[] Bodies { get; set; } = []; +} + +[MessagePackObject] +public sealed class AetheriaOrbitSnapshot +{ + [Key(0)] + public string OrbitId { get; set; } = ""; + + [Key(1)] + public string ParentId { get; set; } = ""; + + [Key(2)] + public double Distance { get; set; } + + [Key(3)] + public double Phase { get; set; } + + [Key(4)] + public AetheriaVector2 FixedPosition { get; set; } = new(); +} + +[MessagePackObject] +public sealed class AetheriaBodySnapshot +{ + [Key(0)] + public string BodyId { get; set; } = ""; + + [Key(1)] + public string Kind { get; set; } = ""; + + [Key(2)] + public string Name { get; set; } = ""; + + [Key(3)] + public string OrbitId { get; set; } = ""; + + [Key(4)] + public double Mass { get; set; } + + [Key(5)] + public AetheriaBodyResource[] Resources { get; set; } = []; + + [Key(6)] + public double BodyRadiusMultiplier { get; set; } = 1.0; + + [Key(7)] + public double GravityRadiusMultiplier { get; set; } = 1.0; + + [Key(8)] + public double GravityDepthMultiplier { get; set; } = 1.0; + + [Key(9)] + public double GravityDepthExponent { get; set; } = 16.0; + + [Key(10)] + public AetheriaAsteroidSnapshot[] Asteroids { get; set; } = []; + + [Key(11)] + public AetheriaGasGiantVisualState GasGiantVisual { get; set; } = new(); + + [Key(12)] + public AetheriaSunVisualState SunVisual { get; set; } = new(); +} + +[MessagePackObject] +public sealed class AetheriaBodyResource +{ + [Key(0)] + public string ItemKey { get; set; } = ""; + + [Key(1)] + public double Amount { get; set; } +} + +[MessagePackObject] +public sealed class AetheriaAsteroidSnapshot +{ + [Key(0)] + public double Distance { get; set; } + + [Key(1)] + public double Phase { get; set; } + + [Key(2)] + public double Size { get; set; } + + [Key(3)] + public double RotationSpeed { get; set; } +} + +[MessagePackObject] +public sealed class AetheriaGasGiantVisualState +{ + [Key(0)] + public double FirstOffsetDomainRotationSpeed { get; set; } = 1.0; + + [Key(1)] + public double FirstOffsetRotationSpeed { get; set; } = 1.0; + + [Key(2)] + public double SecondOffsetDomainRotationSpeed { get; set; } = 1.0; + + [Key(3)] + public double SecondOffsetRotationSpeed { get; set; } = 1.0; + + [Key(4)] + public double AlbedoRotationSpeed { get; set; } = 1.0; + + [Key(5)] + public double WaveRadiusMultiplier { get; set; } = 1.0; + + [Key(6)] + public double WaveDepthMultiplier { get; set; } = 1.0; + + [Key(7)] + public double WaveDepthExponent { get; set; } = 8.0; + + [Key(8)] + public double WaveSpeedMultiplier { get; set; } = 8.0; + + [Key(9)] + public string[] MaterialOverrides { get; set; } = []; + + [Key(10)] + public AetheriaColor[] Colors { get; set; } = []; +} + +[MessagePackObject] +public sealed class AetheriaSunVisualState +{ + [Key(0)] + public AetheriaVector3 LightColor { get; set; } = new(); + + [Key(1)] + public AetheriaVector3 FogTintColor { get; set; } = new(); + + [Key(2)] + public double LightRadiusMultiplier { get; set; } = 1.0; +} + +[MessagePackObject] +public sealed class AetheriaColor +{ + [Key(0)] + public double X { get; set; } + + [Key(1)] + public double Y { get; set; } + + [Key(2)] + public double Z { get; set; } + + [Key(3)] + public double W { get; set; } } [CultDocument("aetheria.entity_snapshot", "aetheria.entity_snapshot.v1")] diff --git a/Aetheria.State/README.md b/Aetheria.State/README.md index f785f6193..5ca0a7baf 100644 --- a/Aetheria.State/README.md +++ b/Aetheria.State/README.md @@ -18,6 +18,10 @@ include current action-bar bindings as stable typed targets: input control path, binding kind, item definition reference, equipment index, behavior index, or weapon-group index. They also include current faction relationship rows as typed corporation references plus relationship tokens and numeric standings. +Zone checkpoints include typed orbit and body rows for generated celestial +state: orbit IDs, parent IDs, distance, phase, fixed positions, body kind, +resources, gravity/body multipliers, asteroid entries, and gas/sun visual +parameters. Player settings include player name, tutorial flag, story-file hash cursors, gameplay formatting, graphics preferences, input binding overrides, and action-bar inputs. Do not preserve `SavedGame`, Unity @@ -114,9 +118,11 @@ allegiance count, full allegiance edges, and music bank IDs. Corporation documents are `aetheria.corporation.v2` records because runtime generation needs actual allegiance weights, not only display counts. Runtime checkpoints now persist session faction relationships into typed run state, and runtime -sessions publish typed state-host liveness, but broader runtime object graphs, -typed behavior factory construction, and simulation state remain legacy until -dedicated typed runtime documents exist. +sessions publish typed state-host liveness. Zone checkpoints persist generated +orbit/body rows into `AetheriaZoneState`, so `RuntimeZoneBlueprint` is no +longer the only surface carrying celestial graph facts. Broader runtime object +graphs, typed behavior factory construction, and simulation state remain legacy +until dedicated typed runtime documents exist. Typed name-file documents are `aetheria.name_file.v2` records. They carry the legacy ID, display name, count, compact sample names for surfaces, and the full name array needed to move `Galaxy`/Markov name generation off legacy @@ -162,10 +168,10 @@ corporation records. typed loadout template in addition to world, catalog, run state, zone state, and an entity snapshot. It also writes and reopens `AetheriaRuntimeSession` and checks that the Eve provider advertises the runtime-session schema. The run -smoke proves a typed run can reference a typed zone, the zone can reference -typed entity snapshots, and entity snapshots can preserve equipment slots, -weapon groups, and stat grids without reviving `PlayerSettings.msgpack`, -`.loadout`, or `.zone` serialization. +smoke proves a typed run can reference a typed zone, the zone can preserve +orbit/body rows and reference typed entity snapshots, and entity snapshots can +preserve equipment slots, weapon groups, and stat grids without reviving +`PlayerSettings.msgpack`, `.loadout`, or `.zone` serialization. `Aetheria.State.Verify` opens a materialized state file and checks that the typed migration ledger matches the actual item, corporation, and name-file diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index 6c365da27..9a16353bc 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -502,12 +502,136 @@ public class ActionGameManager : MonoBehaviour .OrderBy(index => index) .ToArray() ?? Array.Empty(), OwnerFactionIndex = FactionIndex(galaxyZone?.Owner), + Orbits = ProjectZoneOrbits(zone.Blueprint), + Bodies = ProjectZoneBodies(zone.Blueprint), Entities = zone.Entities .Select((entity, index) => ProjectEntitySnapshot(zone, entity, index)) .ToArray() }; } + private static AetheriaRuntimeOrbitSnapshotCommit[] ProjectZoneOrbits(RuntimeZoneBlueprint blueprint) + { + return blueprint?.Orbits? + .OrderBy(orbit => orbit.ID) + .Select(orbit => new AetheriaRuntimeOrbitSnapshotCommit + { + OrbitLegacyId = LegacyId(orbit.ID), + ParentLegacyId = LegacyId(orbit.Parent), + Distance = orbit.Distance, + Phase = orbit.Phase, + FixedPositionX = orbit.FixedPosition.x, + FixedPositionY = orbit.FixedPosition.y + }) + .ToArray() ?? Array.Empty(); + } + + private static AetheriaRuntimeBodySnapshotCommit[] ProjectZoneBodies(RuntimeZoneBlueprint blueprint) + { + return blueprint?.Planets? + .OrderBy(body => body.ID) + .Select(ProjectZoneBody) + .ToArray() ?? Array.Empty(); + } + + private static AetheriaRuntimeBodySnapshotCommit ProjectZoneBody(BodyData body) + { + return new AetheriaRuntimeBodySnapshotCommit + { + BodyLegacyId = LegacyId(body.ID), + Kind = BodyKind(body), + Name = body.Name ?? "", + OrbitLegacyId = LegacyId(body.Orbit), + Mass = body.Mass, + Resources = body.Resources? + .OrderBy(pair => pair.Key) + .Select(pair => new AetheriaRuntimeBodyResourceCommit + { + ItemDefinitionLegacyId = LegacyId(pair.Key), + Amount = pair.Value + }) + .ToArray() ?? Array.Empty(), + BodyRadiusMultiplier = body.BodyRadiusMultiplier, + GravityRadiusMultiplier = body.GravityRadiusMultiplier, + GravityDepthMultiplier = body.GravityDepthMultiplier, + GravityDepthExponent = body.GravityDepthExponent, + Asteroids = body is AsteroidBeltData belt + ? ProjectAsteroids(belt.Asteroids) + : Array.Empty(), + GasGiantVisual = body is GasGiantData gas + ? ProjectGasGiantVisual(gas) + : new AetheriaRuntimeGasGiantVisualCommit(), + SunVisual = body is SunData sun + ? ProjectSunVisual(sun) + : new AetheriaRuntimeSunVisualCommit() + }; + } + + private static AetheriaRuntimeAsteroidCommit[] ProjectAsteroids(Asteroid[] asteroids) + { + return asteroids? + .Select(asteroid => new AetheriaRuntimeAsteroidCommit + { + Distance = asteroid.Distance, + Phase = asteroid.Phase, + Size = asteroid.Size, + RotationSpeed = asteroid.RotationSpeed + }) + .ToArray() ?? Array.Empty(); + } + + private static AetheriaRuntimeGasGiantVisualCommit ProjectGasGiantVisual(GasGiantData gas) + { + return new AetheriaRuntimeGasGiantVisualCommit + { + FirstOffsetDomainRotationSpeed = gas.FirstOffsetDomainRotationSpeed, + FirstOffsetRotationSpeed = gas.FirstOffsetRotationSpeed, + SecondOffsetDomainRotationSpeed = gas.SecondOffsetDomainRotationSpeed, + SecondOffsetRotationSpeed = gas.SecondOffsetRotationSpeed, + AlbedoRotationSpeed = gas.AlbedoRotationSpeed, + WaveRadiusMultiplier = gas.WaveRadiusMultiplier, + WaveDepthMultiplier = gas.WaveDepthMultiplier, + WaveDepthExponent = gas.WaveDepthExponent, + WaveSpeedMultiplier = gas.WaveSpeedMultiplier, + MaterialOverrides = gas.MaterialOverrides?.ToArray() ?? Array.Empty(), + Colors = gas.Colors? + .Select(color => new AetheriaRuntimeColorCommit + { + X = color.x, + Y = color.y, + Z = color.z, + W = color.w + }) + .ToArray() ?? Array.Empty() + }; + } + + private static AetheriaRuntimeSunVisualCommit ProjectSunVisual(SunData sun) + { + return new AetheriaRuntimeSunVisualCommit + { + LightColorX = sun.LightColor.x, + LightColorY = sun.LightColor.y, + LightColorZ = sun.LightColor.z, + FogTintColorX = sun.FogTintColor.x, + FogTintColorY = sun.FogTintColor.y, + FogTintColorZ = sun.FogTintColor.z, + LightRadiusMultiplier = sun.LightRadiusMultiplier + }; + } + + private static string BodyKind(BodyData body) + { + return body switch + { + SunData => "sun", + GasGiantData => "gas_giant", + AsteroidBeltData => "asteroid_belt", + PlanetData => "planet", + _ => "body" + }; + } + private AetheriaRuntimeEntitySnapshotCommit ProjectEntitySnapshot(Zone zone, Entity entity, int entityIndex) { return new AetheriaRuntimeEntitySnapshotCommit @@ -547,6 +671,11 @@ public class ActionGameManager : MonoBehaviour .ToArray() ?? Array.Empty(); } + private static string LegacyId(Guid id) + { + return id == Guid.Empty ? "" : id.ToString("D"); + } + private static int ZoneIndex(GalaxyZone zone) { return CurrentGalaxy?.Zones == null || zone == null ? -1 : Array.IndexOf(CurrentGalaxy.Zones, zone); diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs index e3acfc375..c9d37c630 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs @@ -301,6 +301,181 @@ namespace GameCult.Aetheria.State.Unity [Key(7)] public IReadOnlyList Entities { get; set; } = Array.Empty(); + + [Key(8)] + public IReadOnlyList Orbits { get; set; } = Array.Empty(); + + [Key(9)] + public IReadOnlyList Bodies { get; set; } = Array.Empty(); + } + + [MessagePackObject] + public sealed class AetheriaRuntimeOrbitSnapshotCommit + { + [Key(0)] + public string OrbitLegacyId { get; set; } = ""; + + [Key(1)] + public string ParentLegacyId { get; set; } = ""; + + [Key(2)] + public double Distance { get; set; } + + [Key(3)] + public double Phase { get; set; } + + [Key(4)] + public double FixedPositionX { get; set; } + + [Key(5)] + public double FixedPositionY { get; set; } + } + + [MessagePackObject] + public sealed class AetheriaRuntimeBodySnapshotCommit + { + [Key(0)] + public string BodyLegacyId { get; set; } = ""; + + [Key(1)] + public string Kind { get; set; } = ""; + + [Key(2)] + public string Name { get; set; } = ""; + + [Key(3)] + public string OrbitLegacyId { get; set; } = ""; + + [Key(4)] + public double Mass { get; set; } + + [Key(5)] + public IReadOnlyList Resources { get; set; } = Array.Empty(); + + [Key(6)] + public double BodyRadiusMultiplier { get; set; } = 1.0; + + [Key(7)] + public double GravityRadiusMultiplier { get; set; } = 1.0; + + [Key(8)] + public double GravityDepthMultiplier { get; set; } = 1.0; + + [Key(9)] + public double GravityDepthExponent { get; set; } = 16.0; + + [Key(10)] + public IReadOnlyList Asteroids { get; set; } = Array.Empty(); + + [Key(11)] + public AetheriaRuntimeGasGiantVisualCommit GasGiantVisual { get; set; } = new AetheriaRuntimeGasGiantVisualCommit(); + + [Key(12)] + public AetheriaRuntimeSunVisualCommit SunVisual { get; set; } = new AetheriaRuntimeSunVisualCommit(); + } + + [MessagePackObject] + public sealed class AetheriaRuntimeBodyResourceCommit + { + [Key(0)] + public string ItemDefinitionLegacyId { get; set; } = ""; + + [Key(1)] + public double Amount { get; set; } + } + + [MessagePackObject] + public sealed class AetheriaRuntimeAsteroidCommit + { + [Key(0)] + public double Distance { get; set; } + + [Key(1)] + public double Phase { get; set; } + + [Key(2)] + public double Size { get; set; } + + [Key(3)] + public double RotationSpeed { get; set; } + } + + [MessagePackObject] + public sealed class AetheriaRuntimeGasGiantVisualCommit + { + [Key(0)] + public double FirstOffsetDomainRotationSpeed { get; set; } = 1.0; + + [Key(1)] + public double FirstOffsetRotationSpeed { get; set; } = 1.0; + + [Key(2)] + public double SecondOffsetDomainRotationSpeed { get; set; } = 1.0; + + [Key(3)] + public double SecondOffsetRotationSpeed { get; set; } = 1.0; + + [Key(4)] + public double AlbedoRotationSpeed { get; set; } = 1.0; + + [Key(5)] + public double WaveRadiusMultiplier { get; set; } = 1.0; + + [Key(6)] + public double WaveDepthMultiplier { get; set; } = 1.0; + + [Key(7)] + public double WaveDepthExponent { get; set; } = 8.0; + + [Key(8)] + public double WaveSpeedMultiplier { get; set; } = 8.0; + + [Key(9)] + public IReadOnlyList MaterialOverrides { get; set; } = Array.Empty(); + + [Key(10)] + public IReadOnlyList Colors { get; set; } = Array.Empty(); + } + + [MessagePackObject] + public sealed class AetheriaRuntimeSunVisualCommit + { + [Key(0)] + public double LightColorX { get; set; } + + [Key(1)] + public double LightColorY { get; set; } + + [Key(2)] + public double LightColorZ { get; set; } + + [Key(3)] + public double FogTintColorX { get; set; } + + [Key(4)] + public double FogTintColorY { get; set; } + + [Key(5)] + public double FogTintColorZ { get; set; } + + [Key(6)] + public double LightRadiusMultiplier { get; set; } = 1.0; + } + + [MessagePackObject] + public sealed class AetheriaRuntimeColorCommit + { + [Key(0)] + public double X { get; set; } + + [Key(1)] + public double Y { get; set; } + + [Key(2)] + public double Z { get; set; } + + [Key(3)] + public double W { get; set; } } [MessagePackObject] diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index 7cff23f5a..def2d152c 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -129,6 +129,12 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. hydrating `HullData.Shape` or `ItemData.Price`. Runtime blueprints no longer capture or restore behavior-private `PersistentBehaviorData` blobs; that dead save-shape hook and its `IPersistentBehavior` interface are deleted. +- Run checkpoint zone snapshots include typed orbit and body rows. Orbit IDs, + parent IDs, distance, phase, fixed positions, body kind/name/orbit, + resources, gravity/body multipliers, asteroid belt entries, and gas/sun + visual parameters now survive through the typed pending commit lane into + `AetheriaZoneState`. `RuntimeZoneBlueprint` feeds this projector, but it is + no longer the only place generated celestial graph facts can live. - `Aetheria.State` now defines `AetheriaLoadoutTemplate` as the typed Verse replacement for bespoke `.loadout` files. It stores structured hull, equipment, cargo bay, docking bay, child-entity, assignment, and weapon-group @@ -729,6 +735,9 @@ First Aetheria surfaces to publish: node ports, provider advertisement, server heartbeat publication, and smoke coverage; daemon liveness is no longer only console output or incidental drain status. + - Done: extend run checkpoint commits and smoke coverage to carry typed + zone orbit/body rows into `AetheriaZoneState`, so generated celestial + graph facts no longer live only inside `RuntimeZoneBlueprint`. - Remaining: add typed documents/mappers for runtime object graphs, typed behavior factory construction, simulation state, and any catalog fields not covered by the stable scalar/fingerprint/payload pass. @@ -1120,9 +1129,10 @@ First Aetheria surfaces to publish: - CultCache smoke proves write, flush, reopen, query, and typed reference resolution, including full player settings as the `PlayerSettings.msgpack` replacement, loadout templates as the `.loadout` replacement, and a run -> - zone -> entity snapshot graph as the `.zone` replacement. It also proves - `aetheria.runtime_session.v1` is advertised and survives reopen as the - typed daemon-session signal. + zone -> entity snapshot graph as the `.zone` replacement. The zone smoke + includes orbit/body rows for generated celestial state. It also proves + `aetheria.runtime_session.v1` is advertised and survives reopen as the typed + daemon-session signal. - CultMesh smoke proves node start, typed put/get, subscription, flush, and reopen. - Eve surface smoke proves provider-owned surface documents are generated from