diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index f42fcb0f8..55687ffed 100644 Binary files a/.voidbot/state/aetheria.cc and b/.voidbot/state/aetheria.cc differ diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index eef47458e..3998f885d 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -15,6 +15,7 @@ RequireTypedGalaxyFactionRelationships(root); RequireRuntimeCatalogKeyOnlyLookups(root); RequireTypedBehaviorBodyKeys(root); RequireTypedOrbitTaskKeys(root); +RequireTypedOrbitalEntityOrbitKeys(root); RequireTypedFactionShellLinks(root); RequireFactionKeyIdentity(root); RequireEveRuntimeBootstrap(root); @@ -946,6 +947,99 @@ static void RequireTypedOrbitTaskKeys(string root) } } +static void RequireTypedOrbitalEntityOrbitKeys(string root) +{ + var checkedFiles = new[] + { + Path.Combine(root, "Assets", "Scripts", "ServerShared", "OrbitalEntity.cs"), + Path.Combine(root, "Assets", "Scripts", "ServerShared", "EntityConstructionBlueprintProjector.cs"), + Path.Combine(root, "Assets", "Scripts", "ServerShared", "LoadoutGenerator.cs"), + Path.Combine(root, "Assets", "Scripts", "ServerShared", "ZoneGenerator.cs"), + Path.Combine(root, "Assets", "Scripts", "Gameplay", "ActionGameManager.cs"), + Path.Combine(root, "Assets", "Scripts", "ServerShared", "Zone.cs") + }; + + var forbiddenSymbols = new[] + { + "public Guid OrbitId;", + "public Guid Orbit;", + "Orbit = orbital.OrbitId", + "blueprint.Orbit,", + "Zone.Orbits[orbital.OrbitId]", + "GetOrbitPosition(OrbitId)", + "GetOrbitVelocity(OrbitId)", + "new OrbitalEntity(ItemManager, null, hull, Guid.Empty" + }; + + 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( + "Orbital runtime entities and construction blueprints must retain typed OrbitKey state, with Zone owning orbit-key resolution: " + + string.Join("; ", hits)); + } + + var requiredSymbols = new Dictionary + { + [Path.Combine(root, "Assets", "Scripts", "ServerShared", "OrbitalEntity.cs")] = new[] + { + "public string OrbitKey = \"\";", + "OrbitKey = orbitKey ?? \"\";", + "Zone.GetOrbitPosition(OrbitKey)", + "Zone.GetOrbitVelocity(OrbitKey)" + }, + [Path.Combine(root, "Assets", "Scripts", "ServerShared", "EntityConstructionBlueprintProjector.cs")] = new[] + { + "OrbitKey = orbital.OrbitKey,", + "blueprint.OrbitKey", + "public string OrbitKey = \"\";" + }, + [Path.Combine(root, "Assets", "Scripts", "ServerShared", "LoadoutGenerator.cs")] = new[] + { + "new OrbitalEntity(ItemManager, null, hull, \"\", ItemManager.GameplaySettings.DefaultEntitySettings)" + }, + [Path.Combine(root, "Assets", "Scripts", "ServerShared", "ZoneGenerator.cs")] = new[] + { + "turret.OrbitKey = Zone.OrbitKey(turretOrbit.ID);", + "station.OrbitKey = Zone.OrbitKey(lagrangeOrbit.ID);" + }, + [Path.Combine(root, "Assets", "Scripts", "Gameplay", "ActionGameManager.cs")] = new[] + { + "Zone.TryGetOrbit(orbital.OrbitKey, out var orbit)", + "planet => planet.OrbitKey == parentOrbitKey" + }, + [Path.Combine(root, "Assets", "Scripts", "ServerShared", "Zone.cs")] = new[] + { + "public float2 GetOrbitVelocity(string orbitKey)" + } + }; + + var missingSymbols = requiredSymbols + .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 (missingSymbols.Length > 0) + { + throw new InvalidOperationException( + "Orbital runtime seam must stay on typed orbit keys through the entity owner, blueprint projector, and docking path: " + + string.Join("; ", missingSymbols)); + } +} + static void RequireTypedFactionShellLinks(string root) { var checkedFiles = new[] diff --git a/Aetheria.State/README.md b/Aetheria.State/README.md index 5e04eb7b0..822f744d7 100644 --- a/Aetheria.State/README.md +++ b/Aetheria.State/README.md @@ -132,6 +132,11 @@ 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. +Orbital runtime entities and their construction blueprints now follow the same +rule: `OrbitalEntity` and `OrbitalEntityConstructionBlueprint` retain +`OrbitKey`, runtime loadout generation seeds the empty value as `""`, and +`Zone` resolves orbit keys for orbital entity movement and dock camera parent +lookup. 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/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index 1d4f62561..c590e635f 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -2962,8 +2962,8 @@ public class ActionGameManager : MonoBehaviour FollowCamera.enabled = false; var orbital = (OrbitalEntity) entity; DockCamera.Follow = ZoneRenderer.EntityInstances[orbital].transform; - var parentOrbit = Zone.Orbits[orbital.OrbitId].Parent; - var parentOrbitPlanet = Zone.PlanetInstances.Values.FirstOrDefault(planet => planet.OrbitId == parentOrbit)?.ID ?? Guid.Empty; + var parentOrbitKey = Zone.TryGetOrbit(orbital.OrbitKey, out var orbit) ? orbit.ParentOrbitKey : ""; + var parentOrbitPlanet = Zone.PlanetInstances.Values.FirstOrDefault(planet => planet.OrbitKey == parentOrbitKey)?.ID ?? Guid.Empty; if (ZoneRenderer.Planets.ContainsKey(parentOrbitPlanet)) DockCamera.LookAt = ZoneRenderer.Planets[parentOrbitPlanet].Body.transform; else DockCamera.LookAt = ZoneRenderer.ZoneRoot; diff --git a/Assets/Scripts/ServerShared/EntityConstructionBlueprintProjector.cs b/Assets/Scripts/ServerShared/EntityConstructionBlueprintProjector.cs index 98f6838fc..14c61832f 100644 --- a/Assets/Scripts/ServerShared/EntityConstructionBlueprintProjector.cs +++ b/Assets/Scripts/ServerShared/EntityConstructionBlueprintProjector.cs @@ -12,7 +12,7 @@ public static class EntityConstructionBlueprintProjector if (entity is OrbitalEntity orbital) blueprint = new OrbitalEntityConstructionBlueprint { - Orbit = orbital.OrbitId, + OrbitKey = orbital.OrbitKey, SecurityLevel = orbital.SecurityLevel, SecurityRadius = orbital.SecurityRadius }; @@ -66,7 +66,7 @@ public static class EntityConstructionBlueprintProjector private static OrbitalEntity InstantiateFromBlueprint(ItemManager itemManager, Zone zone, OrbitalEntityConstructionBlueprint blueprint, bool instantiate = false) { - var entity = new OrbitalEntity(itemManager, zone, instantiate ? (EquippableItem) itemManager.Instantiate(blueprint.Hull) : blueprint.Hull, blueprint.Orbit, blueprint.Settings); + var entity = new OrbitalEntity(itemManager, zone, instantiate ? (EquippableItem) itemManager.Instantiate(blueprint.Hull) : blueprint.Hull, blueprint.OrbitKey, blueprint.Settings); Restore(itemManager, zone, blueprint, entity, instantiate); entity.SecurityLevel = blueprint.SecurityLevel; entity.SecurityRadius = blueprint.SecurityRadius; @@ -128,7 +128,7 @@ public class ShipConstructionBlueprint : EntityConstructionBlueprint public class OrbitalEntityConstructionBlueprint : EntityConstructionBlueprint { - public Guid Orbit; + public string OrbitKey = ""; public int Story = -1; public SecurityLevel SecurityLevel; public float SecurityRadius; diff --git a/Assets/Scripts/ServerShared/LoadoutGenerator.cs b/Assets/Scripts/ServerShared/LoadoutGenerator.cs index 9bf076017..ee04c898c 100644 --- a/Assets/Scripts/ServerShared/LoadoutGenerator.cs +++ b/Assets/Scripts/ServerShared/LoadoutGenerator.cs @@ -81,7 +81,7 @@ public class LoadoutGenerator return null; } var hull = ItemManager.CreateEquippableInstance(hullRow); - var entity = new OrbitalEntity(ItemManager, null, hull, Guid.Empty, ItemManager.GameplaySettings.DefaultEntitySettings); + var entity = new OrbitalEntity(ItemManager, null, hull, "", ItemManager.GameplaySettings.DefaultEntitySettings); entity.Faction = Faction; OutfitEntity(entity); return EntityConstructionBlueprintProjector.CaptureBlueprint(entity) as OrbitalEntityConstructionBlueprint; @@ -96,7 +96,7 @@ public class LoadoutGenerator return null; } var hull = ItemManager.CreateEquippableInstance(hullRow); - var entity = new OrbitalEntity(ItemManager, null, hull, Guid.Empty, ItemManager.GameplaySettings.DefaultEntitySettings); + var entity = new OrbitalEntity(ItemManager, null, hull, "", ItemManager.GameplaySettings.DefaultEntitySettings); entity.Faction = Faction; var emptyShape = entity.UnoccupiedSpace; diff --git a/Assets/Scripts/ServerShared/OrbitalEntity.cs b/Assets/Scripts/ServerShared/OrbitalEntity.cs index 853cf9fee..02a9f1144 100644 --- a/Assets/Scripts/ServerShared/OrbitalEntity.cs +++ b/Assets/Scripts/ServerShared/OrbitalEntity.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 Unity.Mathematics; @@ -11,23 +10,23 @@ using float2 = Unity.Mathematics.float2; public class OrbitalEntity : Entity { - public Guid OrbitId; + public string OrbitKey = ""; public SecurityLevel SecurityLevel; public float SecurityRadius; public LocationStory Story; public bool CanTow; - public OrbitalEntity(ItemManager itemManager, Zone zone, EquippableItem hull, Guid orbit, EntitySettings settings) : base(itemManager, zone, hull, settings) + public OrbitalEntity(ItemManager itemManager, Zone zone, EquippableItem hull, string orbitKey, EntitySettings settings) : base(itemManager, zone, hull, settings) { - OrbitId = orbit; + OrbitKey = orbitKey ?? ""; } public override void Update(float delta) { - if (OrbitId != Guid.Empty) + if (!string.IsNullOrWhiteSpace(OrbitKey)) { - Position.xz = Zone.GetOrbitPosition(OrbitId); - Velocity = Zone.GetOrbitVelocity(OrbitId); + Position.xz = Zone.GetOrbitPosition(OrbitKey); + Velocity = Zone.GetOrbitVelocity(OrbitKey); } base.Update(delta); diff --git a/Assets/Scripts/ServerShared/Zone.cs b/Assets/Scripts/ServerShared/Zone.cs index 6b8964705..393b414fb 100644 --- a/Assets/Scripts/ServerShared/Zone.cs +++ b/Assets/Scripts/ServerShared/Zone.cs @@ -244,6 +244,11 @@ public class Zone return float2.zero; } + public float2 GetOrbitVelocity(string orbitKey) + { + return GetOrbitVelocity(ParseOrbitGuid(orbitKey)); + } + public int NearestAsteroid(Guid planetDataID, float2 position) { var belt = AsteroidBelts[planetDataID]; diff --git a/Assets/Scripts/ServerShared/ZoneGenerator.cs b/Assets/Scripts/ServerShared/ZoneGenerator.cs index 36220b466..c21093d20 100644 --- a/Assets/Scripts/ServerShared/ZoneGenerator.cs +++ b/Assets/Scripts/ServerShared/ZoneGenerator.cs @@ -277,7 +277,7 @@ public static class ZoneGenerator blueprint.Orbits.Add(turretOrbit); var turret = loadoutGenerator.GenerateTurretLoadout(); if (turret == null) return; - turret.Orbit = turretOrbit.ID; + turret.OrbitKey = Zone.OrbitKey(turretOrbit.ID); blueprint.Entities.Add(turret); } @@ -297,7 +297,7 @@ public static class ZoneGenerator var orbit = selectedStationOrbits[i]; var lagrangeOrbit = CreateLagrangeOrbit(orbit); var station = GetLoadoutGenerator(story.Faction).GenerateStationLoadout(); - station.Orbit = lagrangeOrbit.ID; + station.OrbitKey = Zone.OrbitKey(lagrangeOrbit.ID); station.SecurityLevel = story.Security; station.SecurityRadius = blueprint.Radius; station.Story = i; @@ -314,7 +314,7 @@ public static class ZoneGenerator var lagrangeOrbit = CreateLagrangeOrbit(orbit); var station = GetLoadoutGenerator(nearestFaction).GenerateStationLoadout(); if (station == null) continue; - station.Orbit = lagrangeOrbit.ID; + station.OrbitKey = Zone.OrbitKey(lagrangeOrbit.ID); station.SecurityLevel = security; station.SecurityRadius = blueprint.Radius; blueprint.Entities.Add(station); diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index 3a50c4d8a..76064fdf9 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -1367,9 +1367,11 @@ First Aetheria surfaces to publish: 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. Remaining broader - simulation paths still derive legacy GUIDs until those structures move to - typed keys. + 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. 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