diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index bafb3abfc..3e45fe310 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -16,6 +16,7 @@ RequireTypedGalaxyFactionRelationships(root); RequireRuntimeCatalogKeyOnlyLookups(root); RequireTypedBehaviorBodyKeys(root); RequireTypedOrbitTaskKeys(root); +RequireTypedAgentTaskKeys(root); RequireTypedOrbitalEntityOrbitKeys(root); RequireTypedOrbitConsumerKeys(root); RequireKeyedOrbitRuntimeWrappers(root); @@ -490,6 +491,7 @@ Console.WriteLine("Live gameplay source purity: no serializer or legacy database Console.WriteLine("Package serializer boundary: MessagePack symbols remain in named CultCache transport files only"); Console.WriteLine("Shared Eve package ownership: generic Unity Eve packages import from the neighboring Eve repo instead of local staged copies"); Console.WriteLine("Typed runtime behavior coverage: live behavior kinds have typed factory plus progress/state restore coverage"); +Console.WriteLine("Zone construction/runtime key authority: body/orbit wrappers and task shells retain typed keys instead of GUID sidecars"); Console.WriteLine("Eve runtime bootstrap: operations surface mounts through UI Toolkit presenter"); Console.WriteLine("Renderer-local console authority: deleted; UI commands flow through Eve command documents"); Console.WriteLine("Renderer-local debug panels: obsolete uGUI field tester authority is deleted"); @@ -903,7 +905,6 @@ static void RequireTypedBehaviorBodyKeys(string root) "public string BodyKey { get; }", "public string OrbitKey { get; }", "public string ParentOrbitKey { get; }", - "public string GetBodyKey(Guid bodyId)", "public bool TryGetPlanet(string bodyKey, out Planet planet)", "public bool TryGetAsteroidBelt(string bodyKey, out AsteroidBelt belt)" }; @@ -1162,6 +1163,69 @@ static void RequireTypedOrbitTaskKeys(string root) } } +static void RequireTypedAgentTaskKeys(string root) +{ + var checkedFiles = new[] + { + Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "AgentTask.cs"), + Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "Survey.cs"), + Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "Mining.cs") + }; + + var forbiddenSymbols = new[] + { + "public Guid Zone", + "List Planets", + "public Guid Asteroids" + }; + + var hits = checkedFiles + .Where(File.Exists) + .SelectMany(path => File.ReadLines(path) + .Select((line, index) => new { Path = path, LineNumber = index + 1, Line = line })) + .Where(line => forbiddenSymbols.Any(symbol => line.Line.Contains(symbol, StringComparison.Ordinal))) + .Select(line => $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}") + .ToArray(); + + if (hits.Length > 0) + { + throw new InvalidOperationException( + "Agent task shells must retain typed zone/body keys rather than raw GUID fields: " + + string.Join("; ", hits)); + } + + var requiredSymbols = new Dictionary + { + [Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "AgentTask.cs")] = new[] + { + "public string ZoneKey = \"\";" + }, + [Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "Survey.cs")] = new[] + { + "public List PlanetBodyKeys;" + }, + [Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "Mining.cs")] = new[] + { + "public string AsteroidBeltBodyKey = \"\";" + } + }; + + var missing = requiredSymbols + .Where(entry => !File.Exists(entry.Key) || + entry.Value.Any(symbol => !File.ReadAllText(entry.Key).Contains(symbol, StringComparison.Ordinal))) + .SelectMany(entry => entry.Value + .Where(symbol => !File.Exists(entry.Key) || !File.ReadAllText(entry.Key).Contains(symbol, StringComparison.Ordinal)) + .Select(symbol => $"{Path.GetRelativePath(root, entry.Key)}: missing {symbol}")) + .ToArray(); + + if (missing.Length > 0) + { + throw new InvalidOperationException( + "Agent task shells must keep typed key placeholders on zone/body references: " + + string.Join("; ", missing)); + } +} + static void RequireTypedOrbitalEntityOrbitKeys(string root) { var checkedFiles = new[] @@ -1344,6 +1408,7 @@ static void RequireKeyedOrbitRuntimeWrappers(string root) { "public Guid OrbitId { get; }", "public Guid Orbit { get; }", + "public Guid ID { get; }", "OrbitId = data.Orbit;", "Orbit = data.Orbit;", "AsteroidBelts[belt.ID] = new AsteroidBelt(belt);", @@ -1437,7 +1502,10 @@ static void RequireNativeZoneKeyResolution(string root) "return GetOrbitPosition(ParseOrbitGuid(orbitKey));", "return GetOrbitVelocity(ParseOrbitGuid(orbitKey));", "public float2 GetOrbitPosition(Guid orbitID)", - "public float2 GetOrbitVelocity(Guid orbit)" + "public float2 GetOrbitVelocity(Guid orbit)", + "public string GetBodyKey(Guid bodyId)", + "private readonly Dictionary _planetsById = new Dictionary();", + "private readonly Dictionary _asteroidBeltsById = new Dictionary();" }; var hits = forbiddenSymbols .Where(symbol => zoneSource.Contains(symbol, StringComparison.Ordinal)) @@ -1455,22 +1523,14 @@ static void RequireNativeZoneKeyResolution(string root) "public Dictionary PlanetInstances = new Dictionary(StringComparer.Ordinal);", "public Dictionary Orbits = new Dictionary(StringComparer.Ordinal);", "public Dictionary AsteroidBelts = new Dictionary(StringComparer.Ordinal);", - "private readonly Dictionary _planetsById = new Dictionary();", - "private readonly Dictionary _asteroidBeltsById = new Dictionary();", "PlanetInstances[runtimeSun.BodyKey] = runtimeSun;", - "_planetsById[runtimeSun.ID] = runtimeSun;", "PlanetInstances[runtimeGas.BodyKey] = runtimeGas;", - "_planetsById[runtimeGas.ID] = runtimeGas;", "PlanetInstances[runtimePlanet.BodyKey] = runtimePlanet;", - "_planetsById[runtimePlanet.ID] = runtimePlanet;", "AsteroidBelts[runtimeBelt.BodyKey] = runtimeBelt;", - "_asteroidBeltsById[runtimeBelt.ID] = runtimeBelt;", "Orbits[runtimeOrbit.OrbitKey] = runtimeOrbit;", "return PlanetInstances.TryGetValue(bodyKey ?? \"\", out planet);", "return AsteroidBelts.TryGetValue(bodyKey ?? \"\", out belt);", "return Orbits.TryGetValue(orbitKey ?? \"\", out orbit);", - "if (_planetsById.TryGetValue(bodyId, out var planet))", - "if (_asteroidBeltsById.TryGetValue(bodyId, out var belt))", "var parentPosition = string.IsNullOrWhiteSpace(orbit.ParentOrbitKey)", ": GetOrbitPosition(orbit.ParentOrbitKey);", "return TryGetOrbit(orbitKey, out var orbit) ? orbit.Velocity : float2.zero;", @@ -1590,7 +1650,14 @@ static void RequireTypedZoneConstructionKeys(string root) "var runtimeGas = new GasGiant(settings, gas, Orbits[OrbitKey(planet.Orbit)]);", "var runtimePlanet = new Planet(settings, planet, Orbits[OrbitKey(planet.Orbit)]);", "turret.OrbitKey = Zone.OrbitKey(turretOrbit.ID);", - "station.OrbitKey = Zone.OrbitKey(lagrangeOrbit.ID);" + "station.OrbitKey = Zone.OrbitKey(lagrangeOrbit.ID);", + "public Guid ID = Guid.NewGuid();", + "public Guid Orbit;", + "public Guid Parent;", + "data.OrbitKey = Zone.OrbitKey(data.ID);", + "planetData.BodyKey = Zone.BodyKey(planetData.ID);", + "lagrangeOrbit.OrbitKey = Zone.OrbitKey(lagrangeOrbit.ID);", + "turretOrbit.OrbitKey = Zone.OrbitKey(turretOrbit.ID);" }; var hits = checkedFiles @@ -1618,13 +1685,14 @@ static void RequireTypedZoneConstructionKeys(string root) }, [Path.Combine(root, "Assets", "Scripts", "ServerShared", "ZoneGenerator.cs")] = new[] { - "data.OrbitKey = Zone.OrbitKey(data.ID);", + "OrbitKey = Zone.OrbitKey(Guid.NewGuid()),", "data.ParentOrbitKey = orbitInverseMap[data].Parent != null", - "planetData.BodyKey = Zone.BodyKey(planetData.ID);", + "planetData.BodyKey = Zone.BodyKey(Guid.NewGuid());", "planetData.OrbitKey = orbitMap[planet].OrbitKey;", - "lagrangeOrbit.OrbitKey = Zone.OrbitKey(lagrangeOrbit.ID);", + "planetData.Name = bodyName.Substring(0, Math.Min(8, bodyName.Length));", + "var bodyName = planetData.BodyKey.Substring(planetData.BodyKey.LastIndexOf(':') + 1);", + "OrbitKey = Zone.OrbitKey(Guid.NewGuid()),", "ParentOrbitKey = baseOrbit.ParentOrbitKey,", - "turretOrbit.OrbitKey = Zone.OrbitKey(turretOrbit.ID);", "ParentOrbitKey = orbit.ParentOrbitKey,", "turret.OrbitKey = turretOrbit.OrbitKey;", "station.OrbitKey = lagrangeOrbit.OrbitKey;" diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index 885cabe3a..430847ace 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -960,7 +960,7 @@ public class ActionGameManager : MonoBehaviour private static AetheriaRuntimeOrbitSnapshotCommit[] ProjectZoneOrbits(Zone zone) { return zone?.Orbits?.Values - .OrderBy(orbit => orbit.ID) + .OrderBy(orbit => orbit.OrbitKey, StringComparer.Ordinal) .Select(orbit => new AetheriaRuntimeOrbitSnapshotCommit { OrbitKey = orbit.OrbitKey, diff --git a/Assets/Scripts/ServerShared/Agents/Tasks/AgentTask.cs b/Assets/Scripts/ServerShared/Agents/Tasks/AgentTask.cs index e052dbbc9..9768e1324 100644 --- a/Assets/Scripts/ServerShared/Agents/Tasks/AgentTask.cs +++ b/Assets/Scripts/ServerShared/Agents/Tasks/AgentTask.cs @@ -2,13 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -using System; - public abstract class AgentTask { public int Priority; - public Guid Zone; + public string ZoneKey = ""; public bool Reserved; diff --git a/Assets/Scripts/ServerShared/Agents/Tasks/Mining.cs b/Assets/Scripts/ServerShared/Agents/Tasks/Mining.cs index 7734489ff..d79ac7dac 100644 --- a/Assets/Scripts/ServerShared/Agents/Tasks/Mining.cs +++ b/Assets/Scripts/ServerShared/Agents/Tasks/Mining.cs @@ -2,11 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -using System; - public class Mining : AgentTask { public override TaskType Type => TaskType.Mine; - public Guid Asteroids; + public string AsteroidBeltBodyKey = ""; } diff --git a/Assets/Scripts/ServerShared/Agents/Tasks/Survey.cs b/Assets/Scripts/ServerShared/Agents/Tasks/Survey.cs index 44f957245..48bb1c13c 100644 --- a/Assets/Scripts/ServerShared/Agents/Tasks/Survey.cs +++ b/Assets/Scripts/ServerShared/Agents/Tasks/Survey.cs @@ -2,12 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -using System; using System.Collections.Generic; public class Survey : AgentTask { public override TaskType Type => TaskType.Explore; - public List Planets; + public List PlanetBodyKeys; } diff --git a/Assets/Scripts/ServerShared/Zone.cs b/Assets/Scripts/ServerShared/Zone.cs index f64820f5e..eb174879f 100644 --- a/Assets/Scripts/ServerShared/Zone.cs +++ b/Assets/Scripts/ServerShared/Zone.cs @@ -27,9 +27,6 @@ public class Zone public Dictionary AsteroidBelts = new Dictionary(StringComparer.Ordinal); public PlanetSettings Settings; - private readonly Dictionary _planetsById = new Dictionary(); - private readonly Dictionary _asteroidBeltsById = new Dictionary(); - private HashSet _updatedOrbits = new HashSet(StringComparer.Ordinal); private ItemManager _itemManager; @@ -72,22 +69,18 @@ public class Zone case AsteroidBeltConstructionData belt: var runtimeBelt = new AsteroidBelt(belt, Orbits[belt.OrbitKey]); AsteroidBelts[runtimeBelt.BodyKey] = runtimeBelt; - _asteroidBeltsById[runtimeBelt.ID] = runtimeBelt; break; case SunConstructionData sun: var runtimeSun = new Sun(settings, sun, Orbits[planet.OrbitKey]); PlanetInstances[runtimeSun.BodyKey] = runtimeSun; - _planetsById[runtimeSun.ID] = runtimeSun; break; case GasGiantConstructionData gas: var runtimeGas = new GasGiant(settings, gas, Orbits[planet.OrbitKey]); PlanetInstances[runtimeGas.BodyKey] = runtimeGas; - _planetsById[runtimeGas.ID] = runtimeGas; break; default: var runtimePlanet = new Planet(settings, planet, Orbits[planet.OrbitKey]); PlanetInstances[runtimePlanet.BodyKey] = runtimePlanet; - _planetsById[runtimePlanet.ID] = runtimePlanet; break; } } @@ -131,17 +124,6 @@ public class Zone return id == Guid.Empty ? "" : $"{BodyKeyPrefix}{id:D}"; } - public string GetBodyKey(Guid bodyId) - { - if (bodyId == Guid.Empty) - return ""; - if (_planetsById.TryGetValue(bodyId, out var planet)) - return planet.BodyKey; - if (_asteroidBeltsById.TryGetValue(bodyId, out var belt)) - return belt.BodyKey; - return BodyKey(bodyId); - } - public bool TryGetPlanet(string bodyKey, out Planet planet) { return PlanetInstances.TryGetValue(bodyKey ?? "", out planet); @@ -420,7 +402,6 @@ public class Planet { public Orbit Orbit; protected readonly PlanetSettings Settings; - public Guid ID { get; } public string BodyKey { get; } public string Name { get; } public string OrbitKey { get; } @@ -438,7 +419,6 @@ public class Planet { Settings = settings; Orbit = orbit; - ID = data.ID; BodyKey = data.BodyKey ?? ""; Name = data.Name ?? ""; OrbitKey = data.OrbitKey ?? ""; @@ -528,7 +508,6 @@ public class AsteroidBelt { private readonly Asteroid[] _asteroids; public Orbit Orbit { get; } - public Guid ID { get; } public string BodyKey { get; } public string Name { get; } public string OrbitKey { get; } @@ -551,7 +530,6 @@ public class AsteroidBelt { _asteroids = data.Asteroids; Orbit = orbit; - ID = data.ID; BodyKey = data.BodyKey ?? ""; Name = data.Name ?? ""; OrbitKey = data.OrbitKey ?? ""; @@ -576,7 +554,6 @@ public class AsteroidBelt public class Orbit { - public Guid ID { get; } public string OrbitKey { get; } public string ParentOrbitKey { get; } public float Distance { get; } @@ -595,7 +572,6 @@ public class Orbit public Orbit(PlanetSettings settings, OrbitConstructionData data) { - ID = data.ID; OrbitKey = data.OrbitKey ?? ""; ParentOrbitKey = data.ParentOrbitKey ?? ""; Distance = data.Distance; diff --git a/Assets/Scripts/ServerShared/ZoneConstructionData.cs b/Assets/Scripts/ServerShared/ZoneConstructionData.cs index 936902f5e..54f49e06c 100644 --- a/Assets/Scripts/ServerShared/ZoneConstructionData.cs +++ b/Assets/Scripts/ServerShared/ZoneConstructionData.cs @@ -2,7 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -using System; using System.Collections; using System.Collections.Generic; using UniRx; @@ -23,14 +22,10 @@ public class ZoneConstructionBlueprint public abstract class BodyConstructionData : INamedEntry { - public Guid ID = Guid.NewGuid(); - public string BodyKey = ""; public string Name = ""; - public Guid Orbit; - public string OrbitKey = ""; public float Mass = 0; @@ -109,12 +104,8 @@ public class Asteroid public class OrbitConstructionData { - public Guid ID = Guid.NewGuid(); - public string OrbitKey = ""; - public Guid Parent; - public string ParentOrbitKey = ""; public float Distance; diff --git a/Assets/Scripts/ServerShared/ZoneGenerator.cs b/Assets/Scripts/ServerShared/ZoneGenerator.cs index d8f2cfe00..b99d499aa 100644 --- a/Assets/Scripts/ServerShared/ZoneGenerator.cs +++ b/Assets/Scripts/ServerShared/ZoneGenerator.cs @@ -102,25 +102,21 @@ public static class ZoneGenerator { var data = new OrbitConstructionData { - OrbitKey = "", + OrbitKey = Zone.OrbitKey(Guid.NewGuid()), ParentOrbitKey = "", FixedPosition = planet.FixedPosition, Distance = planet.Distance, //Period = planet.Period, Phase = planet.Phase }; - data.OrbitKey = Zone.OrbitKey(data.ID); orbitMap[planet] = data; orbitInverseMap[data] = planet; return data; }).ToList(); - // Link orbit construction parents to generated orbit IDs. + // Link orbit construction parents to generated orbit keys. foreach (var data in blueprint.Orbits) { - data.Parent = orbitInverseMap[data].Parent != null - ? orbitMap[orbitInverseMap[data].Parent].ID - : Guid.Empty; data.ParentOrbitKey = orbitInverseMap[data].Parent != null ? orbitMap[orbitInverseMap[data].Parent].OrbitKey : ""; @@ -167,12 +163,12 @@ public static class ZoneGenerator throw new ArgumentOutOfRangeException(); } - planetData.BodyKey = Zone.BodyKey(planetData.ID); + planetData.BodyKey = Zone.BodyKey(Guid.NewGuid()); planetData.Mass = planet.Mass; - planetData.Orbit = orbitMap[planet].ID; planetData.OrbitKey = orbitMap[planet].OrbitKey; // planetData.Resources = planetResources; - planetData.Name = planetData.ID.ToString().Substring(0, 8); + var bodyName = planetData.BodyKey.Substring(planetData.BodyKey.LastIndexOf(':') + 1); + planetData.Name = bodyName.Substring(0, Math.Min(8, bodyName.Length)); if (planetData is AsteroidBeltConstructionData beltData) { beltData.Asteroids = @@ -264,14 +260,11 @@ public static class ZoneGenerator { var lagrangeOrbit = new OrbitConstructionData { - ID = Guid.NewGuid(), - OrbitKey = "", - Parent = baseOrbit.Parent, + OrbitKey = Zone.OrbitKey(Guid.NewGuid()), ParentOrbitKey = baseOrbit.ParentOrbitKey, Distance = baseOrbit.Distance, Phase = baseOrbit.Phase + PI / 3 * sign(random.NextFloat() - .5f) }; - lagrangeOrbit.OrbitKey = Zone.OrbitKey(lagrangeOrbit.ID); blueprint.Orbits.Add(lagrangeOrbit); return lagrangeOrbit; } @@ -282,14 +275,11 @@ public static class ZoneGenerator var turretOrbit = new OrbitConstructionData { - ID = Guid.NewGuid(), - OrbitKey = "", - Parent = orbit.Parent, + OrbitKey = Zone.OrbitKey(Guid.NewGuid()), ParentOrbitKey = orbit.ParentOrbitKey, Distance = orbit.Distance, Phase = orbit.Phase + phase }; - turretOrbit.OrbitKey = Zone.OrbitKey(turretOrbit.ID); blueprint.Orbits.Add(turretOrbit); var turret = loadoutGenerator.GenerateTurretLoadout(); if (turret == null) return; diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index b2dc5f756..8816e51e7 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -1381,27 +1381,27 @@ First Aetheria surfaces to publish: - Done: remove the stale `MessagePack` assembly reference from `Aetheria.Shared.Unity`; the remaining MessagePack reference is contained in the typed state package's `.cc` reader. - - In progress: convert domain references from GUID/base-class patterns to + - Done: convert domain references from GUID/base-class patterns to typed record refs. Item instances, cargo inventory indexes, weapon ammo references, item-use behavior references, and runtime commit item/hull surfaces now carry typed item keys. Runtime behavior resource-scanner and mining-tool body references are named and serialized as typed body keys through commit/readback state, with the behaviors retaining keyed runtime - state directly and `Zone` owning GUID parsing/resolution at the current - Unity body runtime boundary. Patrol/tow orbit task targets now also retain - typed orbit keys with `Zone` owning orbit-key resolution. Orbital runtime - entities and runtime construction blueprints now also retain typed - `OrbitKey` values, with `Zone` owning orbital movement and dock-camera - parent-orbit resolution for that seam. Renderer, intro-cutscene, and - `ResourceScanner` orbit readers now also consume the wrapper `OrbitKey` - edge instead of wrapper GUID orbit fields. `Planet` and `AsteroidBelt` - runtime wrappers no longer publish separate wrapper orbit GUID fields - either; they retain `OrbitKey` plus their runtime `Orbit` object. `Zone`'s - key-facing lookup surface also resolves through native key-indexed runtime - dictionaries instead of reparsing GUIDs from key strings. Zone asteroid - helpers now also route through typed body keys or runtime `AsteroidBelt` - objects instead of Guid overloads. Remaining broader simulation paths still - derive legacy GUIDs until those structures move to typed keys. + state directly and `Zone` owning key-indexed runtime lookup. Patrol/tow + orbit task targets now also retain typed orbit keys with `Zone` owning + orbit-key resolution. Orbital runtime entities and runtime construction + blueprints now also retain typed `OrbitKey` values, with `Zone` owning + orbital movement and dock-camera parent-orbit resolution for that seam. + Renderer, intro-cutscene, and `ResourceScanner` orbit readers now also + consume the wrapper `OrbitKey` edge instead of wrapper GUID orbit fields. + `Planet`, `AsteroidBelt`, and `Orbit` runtime wrappers no longer retain + GUID identity sidecars; they keep `BodyKey`/`OrbitKey`/`ParentOrbitKey` + plus their runtime object links. `Zone` no longer keeps GUID-to-body + lookup dictionaries or the `GetBodyKey(Guid)` bridge. Zone construction + data and generator handoff now carry typed orbit/body keys directly, and + the remaining agent-task shell placeholders also retain typed string keys + rather than raw GUID fields. The only surviving GUID use in this lane is + local key minting during zone generation, not runtime reference authority. - Done: delete the `ItemInstance.ItemId` legacy-GUID projection; diagnostics now report `ItemKey` so item instances expose only typed item-key identity. - Done: delete `Entity`/`EquippedCargoBay` cargo and consumable GUID lookup