diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index 6a64baad9..28f846bf5 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 37d578a4b..4569254e9 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -11,6 +11,7 @@ RequireGameplaySourcePurity(root); RequirePackageSerializerBoundary(root); RequireTypedPendingCommitKeys(root); RequireTypedRuntimeFactionKeys(root); +RequireTypedGalaxyFactionRelationships(root); RequireEveRuntimeBootstrap(root); RequireNoRendererLocalConsole(root); RequireNoRendererLocalDebugPanels(root); @@ -662,6 +663,42 @@ static void RequireTypedRuntimeFactionKeys(string root) } } +static void RequireTypedGalaxyFactionRelationships(string root) +{ + var checkedFiles = new[] + { + Path.Combine(root, "Assets", "Scripts", "ServerShared", "Corporations.cs"), + Path.Combine(root, "Assets", "Scripts", "ServerShared", "Galaxy.cs"), + Path.Combine(root, "Assets", "Scripts", "ServerShared", "LoadoutGenerator.cs") + }; + + var forbiddenSymbols = new[] + { + "Dictionary Allegiance", + ".Allegiance[", + "ContainsFaction(Guid", + "ResolveFaction(Guid", + "CorporationLegacyId", + "factionsByLegacyId", + "_containedFactions" + }; + + 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( + "Galaxy faction relationships must use typed corporation keys, not legacy GUID allegiance state: " + + string.Join("; ", hits)); + } +} + static void RequireEveRuntimeBootstrap(string root) { var bootstrapPath = Path.Combine( diff --git a/Aetheria.State/README.md b/Aetheria.State/README.md index 93f2d7028..c6317494d 100644 --- a/Aetheria.State/README.md +++ b/Aetheria.State/README.md @@ -108,6 +108,9 @@ pending-command `CorporationLegacyId` fallback fields are deleted. `EntityConstructionBlueprint` also carries `FactionKey` only. Loadout restore, entity construction, run entity snapshots, and faction relationship commits no longer rebuild corporation references from legacy `Faction.ID` GUIDs. +Galaxy faction relationship projection is also typed-key-only: the old +`Faction.Allegiance` GUID dictionary, GUID containment helper, and GUID faction +resolver are deleted from the simulation shell. Run checkpoint commit DTOs carry typed `OrbitKey`, `ParentOrbitKey`, and `BodyKey` only for zone orbit/body snapshots. The old pending-command `OrbitLegacyId`, `ParentLegacyId`, and `BodyLegacyId` fallback fields are diff --git a/Assets/Scripts/ServerShared/Corporations.cs b/Assets/Scripts/ServerShared/Corporations.cs index 71c13d3e0..997196abc 100644 --- a/Assets/Scripts/ServerShared/Corporations.cs +++ b/Assets/Scripts/ServerShared/Corporations.cs @@ -40,8 +40,6 @@ public class Faction : INamedEntry [Inspectable] public int InfluenceDistance = 4; - [InspectableRangedFloat(0, 1)] - public Dictionary Allegiance = new Dictionary(); public Dictionary AllegianceByKey = new Dictionary(); [InspectableSoundBank] diff --git a/Assets/Scripts/ServerShared/Galaxy.cs b/Assets/Scripts/ServerShared/Galaxy.cs index 1e6036590..862827f85 100644 --- a/Assets/Scripts/ServerShared/Galaxy.cs +++ b/Assets/Scripts/ServerShared/Galaxy.cs @@ -31,7 +31,6 @@ public class Galaxy private Action Log { get; } public bool IsPrelude { get; } - private HashSet _containedFactions; private HashSet _containedFactionKeys; private GalaxyZone[] _exitPath; private Dictionary _nameGenerators = new Dictionary(); @@ -233,20 +232,17 @@ public class Galaxy throw new InvalidOperationException("Typed catalog has no factions for galaxy generation."); } - var factionsByLegacyId = factions.ToDictionary(faction => faction.ID.ToString("D"), StringComparer.OrdinalIgnoreCase); + var factionKeys = new HashSet( + factions + .Select(faction => faction.FactionKey) + .Where(key => !string.IsNullOrWhiteSpace(key)), + StringComparer.OrdinalIgnoreCase); for (var index = 0; index < corporations.Length; index++) { foreach (var allegiance in corporations[index].Allegiances) { - if (Guid.TryParse(allegiance.CorporationLegacyId, out var corporationId) && - corporationId != Guid.Empty && - factionsByLegacyId.ContainsKey(corporationId.ToString("D"))) - { - factions[index].Allegiance[corporationId] = (float) allegiance.Weight; - } - if (!string.IsNullOrWhiteSpace(allegiance.CorporationKey) && - factionsByLegacyId.ContainsKey(allegiance.CorporationLegacyId)) + factionKeys.Contains(allegiance.CorporationKey)) { factions[index].AllegianceByKey[allegiance.CorporationKey] = (float) allegiance.Weight; } @@ -473,12 +469,6 @@ public class Galaxy } } - public bool ContainsFaction(Guid factionID) - { - _containedFactions ??= new HashSet(Factions.Select(f => f.ID)); - return _containedFactions.Contains(factionID); - } - public bool ContainsFaction(string factionKey) { _containedFactionKeys ??= new HashSet( @@ -489,22 +479,6 @@ public class Galaxy return !string.IsNullOrWhiteSpace(factionKey) && _containedFactionKeys.Contains(factionKey); } - public Faction ResolveFaction(Guid factionID) - { - if (factionID == Guid.Empty) - { - return null; - } - - var faction = Factions.FirstOrDefault(f => f.ID == factionID); - if (faction != null) - { - return faction; - } - - return _allFactions.FirstOrDefault(f => f.ID == factionID); - } - public Faction ResolveFactionByKey(string factionKey) { if (string.IsNullOrWhiteSpace(factionKey)) diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index ed26b833b..0bbe71bbb 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -398,8 +398,10 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. - `Galaxy` generation no longer accepts a runtime item catalog reader or `ItemManager`. Sector and tutorial generation receive the package-owned typed runtime catalog. `Galaxy` projects typed corporation v2 records into temporary legacy - `Faction` DTOs, including allegiance edges, for the existing simulation shape - and resolves full name arrays from `aetheria.name_file.v2` records. The + `Faction` DTOs for the existing simulation shape, but allegiance edges are + typed-key-only through `AllegianceByKey`; the old GUID allegiance dictionary + and GUID faction lookup helpers are deleted. Galaxy resolves full name arrays + from `aetheria.name_file.v2` records. The runtime no longer opens the old `GameData/NameFile/*.msgpack` directory. - Item instances carry `AetheriaRuntimeItemReference`, a typed item key resolved by `ItemManager` against the typed runtime item catalog projected @@ -1011,6 +1013,10 @@ First Aetheria surfaces to publish: old blueprint `Faction` GUID compatibility field is deleted, and runtime entity/faction-relationship commits no longer rebuild faction keys from legacy `Faction.ID` GUIDs. + - Done: delete `Faction.Allegiance`, `Galaxy.ContainsFaction(Guid)`, and + `Galaxy.ResolveFaction(Guid)`; galaxy/loadout faction relationship logic + now uses `AllegianceByKey`, `ContainsFaction(string)`, and + `ResolveFactionByKey` only. - Done: move package and canonical manufacturer/corporation/name-file lookup to typed keys (`ManufacturerKey`, `CorporationKey`, `GeonameFileKey`, and `NameFileKey`), and move loadout manufacturer-distance weighting to @@ -1735,6 +1741,10 @@ First Aetheria surfaces to publish: - `Aetheria.State.Verify` also guards runtime faction identity ownership: construction blueprints cannot carry a legacy faction GUID, and runtime entity/faction commits cannot rebuild `FactionKey` from `Faction.ID`. +- `Aetheria.State.Verify` guards galaxy faction relationships as typed-key-only: + no `Faction.Allegiance` GUID dictionary, GUID faction containment, GUID + faction resolver, or `CorporationLegacyId` relationship read may return to + the live `Galaxy`/`LoadoutGenerator` path. - Unity batchmode compile returned cleanly after the runtime blueprint rename; live Unity source has no `EntityPack`, `ShipPack`, `OrbitalEntityPack`, `ZonePack`, `PackedContents`, `PackZone`, `EntitySerializer.Pack`, or