diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index 1079ad251..29aeb6907 100644 Binary files a/.voidbot/state/aetheria.cc and b/.voidbot/state/aetheria.cc differ diff --git a/Aetheria.State.Unity.Smoke/Program.cs b/Aetheria.State.Unity.Smoke/Program.cs index 1269b6076..00abdd28c 100644 --- a/Aetheria.State.Unity.Smoke/Program.cs +++ b/Aetheria.State.Unity.Smoke/Program.cs @@ -273,7 +273,24 @@ try } } }, - WeaponGroups = new[] { new[] { 0 } } + WeaponGroups = new[] { new[] { 0 } }, + StatGrids = new[] + { + new AetheriaRuntimeEntityStatGridCommit + { + Name = "temperature", + Width = 2, + Height = 1, + Values = new[] { 280.0, 281.0 } + }, + new AetheriaRuntimeEntityStatGridCommit + { + Name = "hull_conductivity_x", + Width = 2, + Height = 1, + Values = new[] { 1.0, 0.0 } + } + } } } } @@ -325,6 +342,9 @@ try zone.Bodies[0].Asteroids.Length == 1 && entity?.Equipment.Length == 1 && entity.WeaponGroups.Length == 1 && + entity.StatGrids.Length == 2 && + entity.StatGrids.Any(grid => grid.Name == "temperature" && grid.Values.Length == 2 && grid.Values[1] == 281.0) && + entity.StatGrids.Any(grid => grid.Name == "hull_conductivity_x" && grid.Values.Length == 2 && grid.Values[0] == 1.0) && AetheriaRuntimeStateCommitLog.ReadPending(commitSmokeStatePath).Count == 0; } diff --git a/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs b/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs index c234a9b47..19494c2df 100644 --- a/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs +++ b/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs @@ -424,10 +424,25 @@ public static class AetheriaRuntimeCommitLogApplier .ToArray(), WeaponGroups = ToIntArrayArray(entity.WeaponGroups) .Select(group => new AetheriaWeaponGroupSnapshot { EquipmentIndices = group }) - .ToArray() + .ToArray(), + StatGrids = ToEntityStatGrids(entity.StatGrids) }; } + private static AetheriaEntityStatGrid[] ToEntityStatGrids( + IReadOnlyList? grids) + { + return (grids ?? Array.Empty()) + .Select(grid => new AetheriaEntityStatGrid + { + Name = grid.Name ?? "", + Width = grid.Width, + Height = grid.Height, + Values = (grid.Values ?? Array.Empty()).ToArray() + }) + .ToArray(); + } + private static int[] ToIntArray(IReadOnlyList? values) { return (values ?? Array.Empty()).ToArray(); diff --git a/Aetheria.State/README.md b/Aetheria.State/README.md index 5ca0a7baf..3c8102e49 100644 --- a/Aetheria.State/README.md +++ b/Aetheria.State/README.md @@ -21,7 +21,8 @@ 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. +parameters. Entity checkpoints include typed simulation stat grids for +temperature, thermal mass, armor, max armor, and hull-conductivity axes. 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 @@ -120,9 +121,12 @@ 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. 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. +longer the only surface carrying celestial graph facts. Entity checkpoints +persist hull simulation grids into `AetheriaEntitySnapshot`, so +`RuntimeEntityBlueprint` is no longer the only surface carrying those live +values. Broader runtime object graphs, typed behavior factory construction, and +behavior-private 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 @@ -170,8 +174,8 @@ 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 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. +preserve equipment slots, weapon groups, and simulation 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 9a16353bc..bb566e9f1 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -655,7 +655,67 @@ public class ActionGameManager : MonoBehaviour .ToArray() ?? Array.Empty(), WeaponGroups = entity.WeaponGroups? .Select(group => (IReadOnlyList)group.items.Select(item => entity.Equipment.IndexOf(item)).Where(index => index >= 0).ToArray()) - .ToArray() ?? Array.Empty>() + .ToArray() ?? Array.Empty>(), + StatGrids = ProjectEntityStatGrids(entity) + }; + } + + private static AetheriaRuntimeEntityStatGridCommit[] ProjectEntityStatGrids(Entity entity) + { + return new[] + { + ProjectFloatGrid("temperature", entity.Temperature), + ProjectFloatGrid("thermal_mass", entity.ThermalMass), + ProjectFloatGrid("armor", entity.Armor), + ProjectFloatGrid("max_armor", entity.MaxArmor), + ProjectBool2Grid("hull_conductivity_x", entity.HullConductivity, axis: 0), + ProjectBool2Grid("hull_conductivity_y", entity.HullConductivity, axis: 1) + } + .Where(grid => grid != null) + .ToArray(); + } + + private static AetheriaRuntimeEntityStatGridCommit ProjectFloatGrid(string name, float[,] values) + { + if (values == null) + return null; + + var width = values.GetLength(0); + var height = values.GetLength(1); + var flattened = new double[width * height]; + var index = 0; + for (var y = 0; y < height; y++) + for (var x = 0; x < width; x++) + flattened[index++] = values[x, y]; + + return new AetheriaRuntimeEntityStatGridCommit + { + Name = name, + Width = width, + Height = height, + Values = flattened + }; + } + + private static AetheriaRuntimeEntityStatGridCommit ProjectBool2Grid(string name, bool2[,] values, int axis) + { + if (values == null) + return null; + + var width = values.GetLength(0); + var height = values.GetLength(1); + var flattened = new double[width * height]; + var index = 0; + for (var y = 0; y < height; y++) + for (var x = 0; x < width; x++) + flattened[index++] = (axis == 0 ? values[x, y].x : values[x, y].y) ? 1.0 : 0.0; + + return new AetheriaRuntimeEntityStatGridCommit + { + Name = name, + Width = width, + Height = height, + Values = flattened }; } diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs index c9d37c630..6684fd028 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs @@ -525,5 +525,24 @@ namespace GameCult.Aetheria.State.Unity [Key(14)] public IReadOnlyList> WeaponGroups { get; set; } = Array.Empty>(); + + [Key(15)] + public IReadOnlyList StatGrids { get; set; } = Array.Empty(); + } + + [MessagePackObject] + public sealed class AetheriaRuntimeEntityStatGridCommit + { + [Key(0)] + public string Name { get; set; } = ""; + + [Key(1)] + public int Width { get; set; } + + [Key(2)] + public int Height { get; set; } + + [Key(3)] + public IReadOnlyList Values { get; set; } = Array.Empty(); } } diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index def2d152c..4997c0e09 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -135,6 +135,10 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. 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. +- Run checkpoint entity snapshots include typed simulation stat grids for + temperature, thermal mass, armor, max armor, and hull-conductivity axes. + `RuntimeEntityBlueprint` still exists for construction/loadout projection, + but those live hull grids no longer survive only as blueprint fields. - `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 @@ -738,9 +742,13 @@ First Aetheria surfaces to publish: - 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`. + - Done: extend run checkpoint commits and smoke coverage to carry typed + entity simulation stat grids into `AetheriaEntitySnapshot`, so + temperature/armor/conductivity no longer live only in runtime blueprints. - 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. + typed behavior factory construction, behavior-private simulation state, + and any catalog fields not covered by the stable scalar/fingerprint/ + payload pass. 4. Runtime cutover - Done: add a Unity-facing typed catalog read facade and smoke proving it can @@ -1130,9 +1138,9 @@ First Aetheria surfaces to publish: 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. 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. + includes orbit/body rows for generated celestial state, and the entity smoke + includes typed simulation stat grids. 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