From cfc1ddac6d0fdd0bc588b993498d4534c9316cf5 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 16 Jun 2026 15:54:18 +0100 Subject: [PATCH] Keep orbit task targets on typed keys --- .voidbot/state/aetheria.cc | Bin 183111 -> 184038 bytes Aetheria.State.Verify/Program.cs | 90 ++++++++++++++++++ Aetheria.State/README.md | 3 + .../ServerShared/Agents/States/MoveTo.cs | 4 +- .../ServerShared/Agents/Tasks/PatrolOrbits.cs | 8 +- .../Agents/Tasks/StationTowing.cs | 2 +- Assets/Scripts/ServerShared/Zone.cs | 27 +++++- docs/aetheria-perfect-machine-map.md | 13 ++- 8 files changed, 137 insertions(+), 10 deletions(-) diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index d43378b8fc521fe45fb2e0d06971217a9bba7cf2..f42fcb0f843aee348b49022dcfeef6440520f01c 100644 GIT binary patch delta 2575 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 diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 740a20793..eef47458e 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -14,6 +14,7 @@ RequireTypedRuntimeFactionKeys(root); RequireTypedGalaxyFactionRelationships(root); RequireRuntimeCatalogKeyOnlyLookups(root); RequireTypedBehaviorBodyKeys(root); +RequireTypedOrbitTaskKeys(root); RequireTypedFactionShellLinks(root); RequireFactionKeyIdentity(root); RequireEveRuntimeBootstrap(root); @@ -856,6 +857,95 @@ static void RequireTypedBehaviorBodyKeys(string root) } } +static void RequireTypedOrbitTaskKeys(string root) +{ + var checkedFiles = new[] + { + Path.Combine(root, "Assets", "Scripts", "ServerShared", "Zone.cs"), + Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "States", "MoveTo.cs"), + Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "PatrolOrbits.cs"), + Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "StationTowing.cs") + }; + + var forbiddenSymbols = new[] + { + "public Guid Orbit { get; set; }", + "Guid[] Circuit", + "public Guid OrbitParent", + "Select(x => x.Key)" + }; + + 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( + "Orbit-targeting agent tasks and move states must stay on typed OrbitKey surfaces, with Zone owning orbit-key resolution: " + + string.Join("; ", hits)); + } + + var zoneSourcePath = Path.Combine(root, "Assets", "Scripts", "ServerShared", "Zone.cs"); + var zoneSource = File.Exists(zoneSourcePath) + ? File.ReadAllText(zoneSourcePath) + : throw new InvalidOperationException("Cannot verify orbit-key task ownership; Zone source is missing."); + var requiredZoneSymbols = new[] + { + "public static Guid ParseOrbitGuid(string orbitKey)", + "public bool TryGetOrbit(string orbitKey, out Orbit orbit)", + "public float2 GetOrbitPosition(string orbitKey)" + }; + var missingZoneSymbols = requiredZoneSymbols + .Where(symbol => !zoneSource.Contains(symbol, StringComparison.Ordinal)) + .ToArray(); + if (missingZoneSymbols.Length > 0) + { + throw new InvalidOperationException( + "Zone must own typed orbit-key parsing and resolution for agent/runtime orbit movement: " + + string.Join(", ", missingZoneSymbols)); + } + + var requiredTaskSymbols = new Dictionary + { + [Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "States", "MoveTo.cs")] = new[] + { + "public string OrbitKey { get; set; } = \"\";", + "_agent.Ship.Zone.GetOrbitPosition(OrbitKey)" + }, + [Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "PatrolOrbits.cs")] = new[] + { + "public string[] Circuit = Array.Empty();", + "public string CurrentTarget", + "patrolMoveState.OrbitKey = CurrentTarget" + }, + [Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "StationTowing.cs")] = new[] + { + "public string OrbitParentKey = \"\";" + } + }; + var missingTaskSymbols = requiredTaskSymbols + .Where(pair => !File.Exists(pair.Key) || pair.Value.Any(symbol => !File.ReadAllText(pair.Key).Contains(symbol, StringComparison.Ordinal))) + .SelectMany(pair => + { + var text = File.Exists(pair.Key) ? File.ReadAllText(pair.Key) : ""; + return pair.Value + .Where(symbol => !text.Contains(symbol, StringComparison.Ordinal)) + .Select(symbol => $"{Path.GetRelativePath(root, pair.Key)}: missing {symbol}"); + }) + .ToArray(); + if (missingTaskSymbols.Length > 0) + { + throw new InvalidOperationException( + "Orbit-targeting agent tasks must retain typed orbit keys rather than raw GUIDs: " + + string.Join("; ", missingTaskSymbols)); + } +} + static void RequireTypedFactionShellLinks(string root) { var checkedFiles = new[] diff --git a/Aetheria.State/README.md b/Aetheria.State/README.md index cb5df3399..5e04eb7b0 100644 --- a/Aetheria.State/README.md +++ b/Aetheria.State/README.md @@ -129,6 +129,9 @@ and `MiningTool` now retain typed body-key runtime state directly, while `Zone`, `Planet`, `AsteroidBelt`, and `Orbit` now own the runtime body/orbit key surfaces used for projection, and `ActionGameManager` no longer formats or resolves body/orbit keys locally while checkpointing or restoring state. +Orbit-targeting agent runtime also now retains typed orbit keys directly: +`PatrolOrbitsTask`, `MoveToOrbitState`, and `StationTowing` no longer own raw +orbit GUID targets, and `Zone` resolves orbit keys for agent orbit movement. The package runtime catalog snapshot indexes items by canonical item key; Unity gameplay, HUD, inventory, trade, ship, and zone helper lookups use `ItemKey` directly instead of deriving `ItemId` and searching by legacy GUID. diff --git a/Assets/Scripts/ServerShared/Agents/States/MoveTo.cs b/Assets/Scripts/ServerShared/Agents/States/MoveTo.cs index 12c84fc18..00008b365 100644 --- a/Assets/Scripts/ServerShared/Agents/States/MoveTo.cs +++ b/Assets/Scripts/ServerShared/Agents/States/MoveTo.cs @@ -37,8 +37,8 @@ public class MoveToEntityState : MoveToState public class MoveToOrbitState : MoveToState { - public Guid Orbit { get; set; } + public string OrbitKey { get; set; } = ""; public MoveToOrbitState(Agent agent) : base(agent) { } - protected override float2 TargetPosition => _agent.Ship.Zone.GetOrbitPosition(Orbit); + protected override float2 TargetPosition => _agent.Ship.Zone.GetOrbitPosition(OrbitKey); } diff --git a/Assets/Scripts/ServerShared/Agents/Tasks/PatrolOrbits.cs b/Assets/Scripts/ServerShared/Agents/Tasks/PatrolOrbits.cs index 653764217..461608a6d 100644 --- a/Assets/Scripts/ServerShared/Agents/Tasks/PatrolOrbits.cs +++ b/Assets/Scripts/ServerShared/Agents/Tasks/PatrolOrbits.cs @@ -3,13 +3,13 @@ using System; public class PatrolOrbitsTask : AgentTask { public override TaskType Type => TaskType.Defend; - public Guid[] Circuit; + public string[] Circuit = Array.Empty(); } public class PatrolOrbitsState : BaseState { public PatrolOrbitsTask Task; - public Guid CurrentTarget + public string CurrentTarget { get => Task.Circuit[_currentTargetIndex]; } @@ -25,7 +25,7 @@ public class PatrolOrbitsState : BaseState var patrolMoveState = new MoveToOrbitState(agent); Transitions.Add(new StateTransition(patrolMoveState, () => true, - () => patrolMoveState.Orbit = CurrentTarget)); + () => patrolMoveState.OrbitKey = CurrentTarget)); patrolMoveState.Transitions.Add(new StateTransition(this, () => patrolMoveState.Distance < 10, NextTarget)); } -} \ No newline at end of file +} diff --git a/Assets/Scripts/ServerShared/Agents/Tasks/StationTowing.cs b/Assets/Scripts/ServerShared/Agents/Tasks/StationTowing.cs index f138f74aa..dcc69098d 100644 --- a/Assets/Scripts/ServerShared/Agents/Tasks/StationTowing.cs +++ b/Assets/Scripts/ServerShared/Agents/Tasks/StationTowing.cs @@ -10,7 +10,7 @@ public class StationTowing : AgentTask public OrbitalEntity Station; - public Guid OrbitParent; + public string OrbitParentKey = ""; public float OrbitDistance; } diff --git a/Assets/Scripts/ServerShared/Zone.cs b/Assets/Scripts/ServerShared/Zone.cs index d0c6673df..6b8964705 100644 --- a/Assets/Scripts/ServerShared/Zone.cs +++ b/Assets/Scripts/ServerShared/Zone.cs @@ -100,7 +100,11 @@ public class Zone { var agent = new Minion(ship); var task = new PatrolOrbitsTask(); - task.Circuit = Orbits.OrderBy(_ => _itemManager.Random.NextFloat()).Take(4).Select(x => x.Key).ToArray(); + task.Circuit = Orbits.Values + .OrderBy(_ => _itemManager.Random.NextFloat()) + .Take(4) + .Select(orbit => orbit.OrbitKey) + .ToArray(); agent.Task = task; return agent; } @@ -115,6 +119,17 @@ public class Zone return id == Guid.Empty ? "" : $"{BodyKeyPrefix}{id:D}"; } + public static Guid ParseOrbitGuid(string orbitKey) + { + if (string.IsNullOrWhiteSpace(orbitKey)) + return Guid.Empty; + + var runtimeOrbitId = orbitKey.StartsWith(OrbitKeyPrefix, StringComparison.OrdinalIgnoreCase) + ? orbitKey.Substring(OrbitKeyPrefix.Length) + : orbitKey; + return Guid.TryParse(runtimeOrbitId, out var id) ? id : Guid.Empty; + } + public string GetBodyKey(Guid bodyId) { if (bodyId == Guid.Empty) @@ -147,6 +162,11 @@ public class Zone return AsteroidBelts.TryGetValue(ParseBodyGuid(bodyKey), out belt); } + public bool TryGetOrbit(string orbitKey, out Orbit orbit) + { + return Orbits.TryGetValue(ParseOrbitGuid(orbitKey), out orbit); + } + public void Update(float deltaTime) { _time += deltaTime; @@ -212,6 +232,11 @@ public class Zone return Orbits[orbitID].Position; } + public float2 GetOrbitPosition(string orbitKey) + { + return GetOrbitPosition(ParseOrbitGuid(orbitKey)); + } + public float2 GetOrbitVelocity(Guid orbit) { if (Orbits.ContainsKey(orbit)) diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index 7570b9587..3a50c4d8a 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -1201,6 +1201,9 @@ First Aetheria surfaces to publish: - Done: rename `OrbitalEntity.OrbitData` to `OrbitId` and delete the unused `Zone.CreateOrbit` construction-row writer; runtime entities no longer expose an orbit id through DTO-shaped naming. + - Done: move patrol/tow orbit targets onto typed orbit keys: `PatrolOrbitsTask`, + `MoveToOrbitState`, and `StationTowing` now retain keyed orbit references, + and `Zone` owns orbit-key parsing/resolution for agent orbit movement. - Done: move live asteroid-belt simulation, scanning, and mesh setup reads off `AsteroidBeltConstructionData`; `Zone`, `ResourceScanner`, and `AsteroidBeltUI` consume `AsteroidBelt` runtime asteroid/resource/orbit properties, and @@ -1363,8 +1366,10 @@ First Aetheria surfaces to publish: 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. Remaining broader simulation paths still - derive legacy GUIDs until those structures move to typed keys. + Unity body runtime boundary. Patrol/tow orbit task targets now also retain + typed orbit keys with `Zone` owning orbit-key resolution. Remaining broader + simulation paths still derive legacy GUIDs until those structures move to + typed keys. - 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 @@ -1779,6 +1784,10 @@ First Aetheria surfaces to publish: return. `Zone`, `Planet`, `AsteroidBelt`, and `Orbit` now own the runtime body/orbit key surfaces consumed by `ActionGameManager` projection, so local body/orbit key formatters may not return there either. +- `Aetheria.State.Verify` guards orbit-targeting agent runtime naming: + patrol/tow task orbit references and move-state orbit targets use `OrbitKey` + fields, and `Zone` owns orbit-key parsing/resolution instead of agent-local + raw GUID targets. - `Aetheria.State.Verify` guards temporary `Faction` shell links: geoname and boss-hull relationships use typed keys, not legacy GUID projection fields. - `Aetheria.State.Verify` guards temporary `Faction` identity: equality,