From 52391b592ab731c5b3e10f4a69ff2a784ba850d6 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 16 Jun 2026 12:59:33 +0100 Subject: [PATCH] Expose runtime catalog relationships as typed keys --- .voidbot/state/aetheria.cc | Bin 174485 -> 177392 bytes Aetheria.State.Unity.Smoke/Program.cs | 4 +- .../AetheriaRuntimeCatalogSnapshotMapper.cs | 14 +++--- Aetheria.State.Verify/Program.cs | 11 ++++- .../Documents/AetheriaItemDefinition.cs | 5 ++ Aetheria.State/README.md | 5 +- .../Runtime/AetheriaRuntimeCatalogSnapshot.cs | 46 ++++++------------ .../Runtime/AetheriaRuntimeCatalogStore.cs | 32 ++++++++---- docs/aetheria-perfect-machine-map.md | 12 +++-- 9 files changed, 72 insertions(+), 57 deletions(-) diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index b716d486dbba295641ea437c5237418ec7a59794..242fb56b0e778001d9372d5fbbf8a8f3cc0f04b6 100644 GIT binary patch delta 2741 zcmcgu-)~e!6y^?1DWKH=g$DdFnrNimZI{x*cGYO1Af-}3TB=|*(|c!kuiTxv-kDom z9$M5GLr{#_=sXxiBmQ`hq{hVM&0yk-4@6^psKEz$Gtq?p2cEh2&fRS(KKioBo_l7_ zIp6utcjkj%*WLPS-TW75-N5cb->#m%o%s{*TOG9@(fPHBrGKY7`!Tw&7Os}YsegCm z)YkGKy8P{%+gs|}Tdb$rtdq65h0SAv5nq(K6qs@BaaN)Mrmy*2Q0#}(o@--|mWW-& zHj%{RB^*$XNS8BFc70rQspp8E&ux#mmEWyd_2&U=>s$s8QCX&eOP&jvbSrc(4}2a( z$NuZvkO#3`!fcwsggMJ^86y?C-}NYDdKwekroN2al5}O&rw%?vt5X8IOh`%`%!{Uq zXQL`coD4ohSXd;s3(2B?UA zij*jTLmU85C*L!+ZyZ;M4}O5_Y527X}gk)@rb0AQCf{Z6v5xEKxZXt?7>jCredF=A|4(;47iR`>YEBoow^d6v!cr&qU7q+0Bw4_ zH_kyf-KVzZX4HV@o@5P-njaXt_e$(o@4m-+Z1v>iaUd&j8OjgnOoU^SY!DL|r@46% zk977c3<53&yrRJm1Lm@lYCmcX)UR%^Q0Mb6B%_edkuK6qT2tFO^Qt%k7Le|>8-vrU z>6tBR{x?Tfx4Nu@>f{t^pO|c>oKJ+H8mx*R>h?5+h>~oOvB8~)g3Bsav)$Is+8-C& zOQjfb9A&q!JN6K~$K28G=Rfa2vI!DHKR`BZUk?=ol-d0E_xJ8Efpw6AMF) zp6F5>Oo~#Emg8u5JsQh0mAbcCjbHugw@|mGUVap{)(f-NCoLNW1_}d%J%joDiOZ6d z#En&F>VLmsO}B2nlJwJcvUVuHs?Z7#V5)DddRScxQODc-oK@%N(9Ml%eip4)mp-#L zY)Z(O)!_^;)MAK^S^G4P^Tq~42UTh>m~cos6AeR3pv0Prr}KY_=gKmlR5gc<*kPFN zIN2qHGn$VL9kkdT8y-!l02GMCF4cwP3NyjCiJiNY=aa07=zyKT30uk#X9-cXZcH#& z8hBGaXkLLD2BM*vi+j}+L!I3wiw5Uoy@atBG?<~LV9*)$9zzdJtgMcBMhoLIu{F5G z+6cRnm4xfirIM-T#H+Rc)A>Nmjv{QADrT@6A$UBa5khL{9cxqD;{H&tsYo8TrDfqt zC?pPHLnmsSQQ!S$_05@WWvtjN%u!m6>uB1;V8bNcw|b4iC`+T!hQbXpG;59#ZrCY& zT5yj_ib<%(*IFA@cHUZ}(1JCgu5?*TexJ9qM|3M19RiXp5!JO7z2;zQRC% zp>KyG7tsCbpPvtc=eq>mr7nc%-Zmq4D(9gs>gYxE$ol9|cWy@^m#h7gZ5RR8G`y0$j4?abv){0liM=dJ(% delta 1112 zcmaJ=%}W(g6nCy7W|ERx5>m%PO7e~0pHX3oVzv;>2v^-VbA4}e-rVWlJL*JWQV|)1 z@rX%?XwipOgu7Wri~fLUQHh({wX#<3qBHL+YtiO%=gc|h_dCDexj(*_KloU_{+yJz zclEdT=)IkNQ}?yn^m}qQAL;7MM0oMlLL{40R&t1}T)fd#5;m1+r^}9<9Hxe{xTB8_ zL6S;{GvP}_zSbzRKPbpp#| zjw22Z(ESdu#u6wm)3V}3IXFqLG&q6H}Y%qK)tMkeu?wwP@;gJ6iYfB+-Zrt`i& zF8F0;s2to;2{ubm!;fJFmC{N$Da3CE>X?6Zclcb)b3BQS#;K6(T&8N&ge0?FCUm%f zgb-3d!lS~JfCbGSqS)?x_q} z^eUqJa+7U+X5b4exJE*ur>IZcg_98xiES%$F24s?Z zwi!9jApYMm6c4sOX?3-znk6k2El7?(ni_ts)Q%Nbwf>`XP`N@HLRzhL*LC+yI?>{| z)R>_OhVxM~j(3m_b&-XVz~)^p8ANL0U|CCot>fC+^vrTqXZOU=7)(=xnk>7OBDceS zw4nK=(Hg>HAoGV%CrQ!z=a=^ zcrtWMQJW`#yUbxC_&K5N3)X$DBG?e*VA!E+d&a}%d!$QC|N2}SzFE+ol2K}O zJ9@jPo+_G82JhtyoRrrbotZ7B$@7T++hLC9+)|yj~@Z!S%;vcltwdUH&=u vwM7p+s9qy)3!$#un=cKv*9b3!do!80UXsOJsIN6s95%ip?{*bpJyX8{fH{Zr diff --git a/Aetheria.State.Unity.Smoke/Program.cs b/Aetheria.State.Unity.Smoke/Program.cs index 6c14bf3e1..915c426ff 100644 --- a/Aetheria.State.Unity.Smoke/Program.cs +++ b/Aetheria.State.Unity.Smoke/Program.cs @@ -121,7 +121,7 @@ if (!catalog.FindItemsByHardpoint(equipment.HardpointType).Any()) throw new InvalidOperationException($"Runtime catalog hardpoint lookup failed for {equipment.HardpointType}."); } -var manufactured = catalog.Items.FirstOrDefault(item => !string.IsNullOrWhiteSpace(item.ManufacturerLegacyId)) +var manufactured = catalog.Items.FirstOrDefault(item => !string.IsNullOrWhiteSpace(item.ManufacturerKey)) ?? throw new InvalidOperationException("Runtime catalog has no manufactured item."); if (string.IsNullOrWhiteSpace(manufactured.ManufacturerKey) || catalog.FindCorporation(manufactured.ManufacturerKey) == null || @@ -130,7 +130,7 @@ if (string.IsNullOrWhiteSpace(manufactured.ManufacturerKey) || throw new InvalidOperationException($"Runtime catalog typed manufacturer lookup failed for {manufactured.Name}."); } -var packageCorporation = packageCatalog.Corporations.FirstOrDefault(corporation => !string.IsNullOrWhiteSpace(corporation.GeonameFileLegacyId)); +var packageCorporation = packageCatalog.Corporations.FirstOrDefault(corporation => !string.IsNullOrWhiteSpace(corporation.GeonameFileKey)); if (packageCorporation == null) { throw new InvalidOperationException("Package catalog store did not read any corporation name-file links."); diff --git a/Aetheria.State.Unity/AetheriaRuntimeCatalogSnapshotMapper.cs b/Aetheria.State.Unity/AetheriaRuntimeCatalogSnapshotMapper.cs index 358253262..9494221df 100644 --- a/Aetheria.State.Unity/AetheriaRuntimeCatalogSnapshotMapper.cs +++ b/Aetheria.State.Unity/AetheriaRuntimeCatalogSnapshotMapper.cs @@ -16,11 +16,11 @@ internal static class AetheriaRuntimeCatalogSnapshotMapper private static AetheriaRuntimeCatalogItem FromState(AetheriaItemDefinition item) { return new AetheriaRuntimeCatalogItem( - item.LegacyId, + item.ItemKey, item.Name, item.Category, item.Description, - item.ManufacturerLegacyId, + item.ManufacturerKey, item.Price, item.Mass, item.SpecificHeat, @@ -141,23 +141,23 @@ internal static class AetheriaRuntimeCatalogSnapshotMapper private static AetheriaRuntimeCorporation FromState(AetheriaCorporation corporation) { return new AetheriaRuntimeCorporation( - corporation.LegacyId, + corporation.CorporationKey, corporation.Name, corporation.ShortName, corporation.Description, - corporation.GeonameFileLegacyId, - corporation.BossHullLegacyId, + corporation.GeonameFileKey, + corporation.BossHullItemKey, corporation.InfluenceDistance, corporation.AllegianceCount, corporation.Allegiances.Select(allegiance => new AetheriaRuntimeCorporationAllegiance( - allegiance.CorporationLegacyId, + allegiance.CorporationKey, allegiance.Weight)).ToArray()); } private static AetheriaRuntimeNameFile FromState(AetheriaNameFile nameFile) { return new AetheriaRuntimeNameFile( - nameFile.LegacyId, + nameFile.NameFileKey, nameFile.Name, nameFile.NameCount, nameFile.SampleNames, diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index b86fac070..66457109c 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -718,7 +718,16 @@ static void RequireRuntimeCatalogKeyOnlyLookups(string root) "_nameFilesByLegacyId", "FindItemByLegacyId", "FindCorporationByLegacyId", - "FindNameFileByLegacyId" + "FindNameFileByLegacyId", + "public string LegacyId { get; }", + "public string ManufacturerLegacyId", + "public string GeonameFileLegacyId", + "public string BossHullLegacyId", + "public string CorporationLegacyId", + ".ManufacturerLegacyId", + ".GeonameFileLegacyId", + ".BossHullLegacyId", + ".CorporationLegacyId" }; var hits = checkedFiles diff --git a/Aetheria.State/Documents/AetheriaItemDefinition.cs b/Aetheria.State/Documents/AetheriaItemDefinition.cs index 86836af64..92550d9ab 100644 --- a/Aetheria.State/Documents/AetheriaItemDefinition.cs +++ b/Aetheria.State/Documents/AetheriaItemDefinition.cs @@ -19,6 +19,11 @@ public sealed class AetheriaItemDefinition [CultIndex("legacyId")] public string LegacyId { get; set; } = ""; + [IgnoreMember] + public string ItemKey => string.IsNullOrWhiteSpace(LegacyId) + ? "" + : $"aetheria.item_definition:legacy:{LegacyId}"; + [Key(3)] public string Description { get; set; } = ""; diff --git a/Aetheria.State/README.md b/Aetheria.State/README.md index af0cfcff0..f706efeb1 100644 --- a/Aetheria.State/README.md +++ b/Aetheria.State/README.md @@ -129,8 +129,9 @@ 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. The Unity runtime package snapshot also no longer exposes item, corporation, or -name-file lookup by legacy ID. Canonical legacy-ID lookup remains only in -`Aetheria.State` migration/catalog inspection APIs. +name-file lookup by legacy ID, and its item/corporation/name-file DTOs no +longer publish legacy relationship fields. Canonical legacy-ID lookup remains +only in `Aetheria.State` migration/catalog inspection APIs. The shared runtime item catalog reader is also item-key-only now: its GUID index, `GetRuntimeItem(Guid)` entry point, raw GUID string fallback, `AetheriaRuntimeItemReference(Guid)` constructor, `FromLegacyId`, and reverse diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs index e020dae79..854d59d35 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs @@ -92,11 +92,11 @@ namespace GameCult.Aetheria.State.Unity public sealed class AetheriaRuntimeCatalogItem { public AetheriaRuntimeCatalogItem( - string legacyId, + string itemKey, string name, string category, string description, - string manufacturerLegacyId, + string manufacturerKey, int price, double mass, double specificHeat, @@ -141,15 +141,11 @@ namespace GameCult.Aetheria.State.Unity string simpleCommodityCategory, string compoundCommodityCategory) { - LegacyId = legacyId; - ItemKey = string.IsNullOrWhiteSpace(legacyId) ? "" : $"aetheria.item_definition:legacy:{legacyId}"; + ItemKey = itemKey; Name = name; Category = category; Description = description; - ManufacturerLegacyId = manufacturerLegacyId; - ManufacturerKey = string.IsNullOrWhiteSpace(manufacturerLegacyId) - ? "" - : $"aetheria.corporation:legacy:{manufacturerLegacyId}"; + ManufacturerKey = manufacturerKey; Price = price; Mass = mass; SpecificHeat = specificHeat; @@ -195,12 +191,10 @@ namespace GameCult.Aetheria.State.Unity CompoundCommodityCategory = compoundCommodityCategory; } - public string LegacyId { get; } public string ItemKey { get; } public string Name { get; } public string Category { get; } public string Description { get; } - public string ManufacturerLegacyId { get; } public string ManufacturerKey { get; } public int Price { get; } public double Mass { get; } @@ -449,38 +443,32 @@ namespace GameCult.Aetheria.State.Unity public sealed class AetheriaRuntimeCorporation { public AetheriaRuntimeCorporation( - string legacyId, + string corporationKey, string name, string shortName, string description, - string geonameFileLegacyId, - string bossHullLegacyId, + string geonameFileKey, + string bossHullItemKey, int influenceDistance, int allegianceCount, IReadOnlyList allegiances) { - LegacyId = legacyId; + CorporationKey = corporationKey; Name = name; ShortName = shortName; Description = description; - GeonameFileLegacyId = geonameFileLegacyId; - BossHullLegacyId = bossHullLegacyId; - CorporationKey = string.IsNullOrWhiteSpace(legacyId) ? "" : $"aetheria.corporation:legacy:{legacyId}"; - GeonameFileKey = string.IsNullOrWhiteSpace(geonameFileLegacyId) ? "" : $"aetheria.name_file:legacy:{geonameFileLegacyId}"; - BossHullItemKey = string.IsNullOrWhiteSpace(bossHullLegacyId) ? "" : $"aetheria.item_definition:legacy:{bossHullLegacyId}"; + GeonameFileKey = geonameFileKey; + BossHullItemKey = bossHullItemKey; InfluenceDistance = influenceDistance; AllegianceCount = allegianceCount; Allegiances = allegiances; } - public string LegacyId { get; } public string CorporationKey { get; } public string Name { get; } public string ShortName { get; } public string Description { get; } - public string GeonameFileLegacyId { get; } public string GeonameFileKey { get; } - public string BossHullLegacyId { get; } public string BossHullItemKey { get; } public int InfluenceDistance { get; } public int AllegianceCount { get; } @@ -489,16 +477,12 @@ namespace GameCult.Aetheria.State.Unity public sealed class AetheriaRuntimeCorporationAllegiance { - public AetheriaRuntimeCorporationAllegiance(string corporationLegacyId, double weight) + public AetheriaRuntimeCorporationAllegiance(string corporationKey, double weight) { - CorporationLegacyId = corporationLegacyId; - CorporationKey = string.IsNullOrWhiteSpace(corporationLegacyId) - ? "" - : $"aetheria.corporation:legacy:{corporationLegacyId}"; + CorporationKey = corporationKey; Weight = weight; } - public string CorporationLegacyId { get; } public string CorporationKey { get; } public double Weight { get; } } @@ -506,21 +490,19 @@ namespace GameCult.Aetheria.State.Unity public sealed class AetheriaRuntimeNameFile { public AetheriaRuntimeNameFile( - string legacyId, + string nameFileKey, string name, int nameCount, IReadOnlyList sampleNames, IReadOnlyList names) { - LegacyId = legacyId; - NameFileKey = string.IsNullOrWhiteSpace(legacyId) ? "" : $"aetheria.name_file:legacy:{legacyId}"; + NameFileKey = nameFileKey; Name = name; NameCount = nameCount; SampleNames = sampleNames; Names = names; } - public string LegacyId { get; } public string NameFileKey { get; } public string Name { get; } public int NameCount { get; } diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs index 97e850b61..5a31ccb27 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs @@ -298,11 +298,11 @@ namespace GameCult.Aetheria.State.Unity SkipRemaining(ref reader, fields, 50); return new AetheriaRuntimeCatalogItem( - legacyId, + ItemDefinitionKey(legacyId), name, category, description, - manufacturerLegacyId, + CorporationKey(manufacturerLegacyId), price, mass, specificHeat, @@ -364,12 +364,12 @@ namespace GameCult.Aetheria.State.Unity var allegiances = ReadFieldCorporationAllegiances(ref reader, fields, 11); SkipRemaining(ref reader, fields, 12); return new AetheriaRuntimeCorporation( - legacyId, + CorporationKey(legacyId), name, shortName, description, - geonameFileLegacyId, - bossHullLegacyId, + NameFileKey(geonameFileLegacyId), + ItemDefinitionKey(bossHullLegacyId), influenceDistance, allegianceCount, allegiances); @@ -385,7 +385,7 @@ namespace GameCult.Aetheria.State.Unity var sampleNames = ReadFieldStringArray(ref reader, fields, 3); var names = ReadFieldStringArray(ref reader, fields, 4); SkipRemaining(ref reader, fields, 5); - return new AetheriaRuntimeNameFile(legacyId, name, nameCount, sampleNames, names); + return new AetheriaRuntimeNameFile(NameFileKey(legacyId), name, nameCount, sampleNames, names); } private static EveSurfaceDocument ReadEveSurface(byte[] payload) @@ -1388,7 +1388,7 @@ namespace GameCult.Aetheria.State.Unity var corporationLegacyId = ReadFieldString(ref reader, allegianceFields, 0); var weight = ReadFieldDouble(ref reader, allegianceFields, 1); SkipRemaining(ref reader, allegianceFields, 2); - allegiances[allegiance] = new AetheriaRuntimeCorporationAllegiance(corporationLegacyId, weight); + allegiances[allegiance] = new AetheriaRuntimeCorporationAllegiance(CorporationKey(corporationLegacyId), weight); } return allegiances; @@ -1636,7 +1636,7 @@ namespace GameCult.Aetheria.State.Unity var itemKeyValue = ReadFieldString(ref reader, valueFields, 7); SkipRemaining(ref reader, valueFields, 8); if (string.IsNullOrWhiteSpace(itemKeyValue)) - itemKeyValue = ProjectLegacyItemKey(legacyIdValue); + itemKeyValue = ItemDefinitionKey(legacyIdValue); return new AetheriaRuntimeBehaviorValue(kind, stringValue, numberValue, boolValue, legacyIdValue, itemKeyValue, children, mapEntries); } @@ -1680,13 +1680,27 @@ namespace GameCult.Aetheria.State.Unity Array.Empty()); } - private static string ProjectLegacyItemKey(string legacyItemId) + private static string ItemDefinitionKey(string legacyItemId) { return string.IsNullOrWhiteSpace(legacyItemId) ? "" : $"aetheria.item_definition:legacy:{legacyItemId.Trim()}"; } + private static string CorporationKey(string legacyCorporationId) + { + return string.IsNullOrWhiteSpace(legacyCorporationId) + ? "" + : $"aetheria.corporation:legacy:{legacyCorporationId.Trim()}"; + } + + private static string NameFileKey(string legacyNameFileId) + { + return string.IsNullOrWhiteSpace(legacyNameFileId) + ? "" + : $"aetheria.name_file:legacy:{legacyNameFileId.Trim()}"; + } + private static string ReadString(ref MessagePackReader reader) { return reader.ReadString() ?? ""; diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index 0c91af3b0..7bc032d62 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -694,8 +694,10 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. indexes items by canonical item key, and Unity gameplay/UI/zone helper lookups resolve `ItemKey` directly rather than deriving legacy GUIDs for `FindItemByLegacyId`. The package snapshot no longer exposes item, - corporation, or name-file `Find*ByLegacyId` indexes; legacy-ID lookup remains - only on the canonical `Aetheria.State` migration/catalog inspection boundary. + corporation, or name-file `Find*ByLegacyId` indexes, and its runtime + item/corporation/name-file DTOs no longer publish legacy relationship fields; + legacy-ID lookup remains only on the canonical `Aetheria.State` + migration/catalog inspection boundary. The shared runtime item catalog reader no longer has a GUID index or `GetRuntimeItem(Guid)` entry point; item-key strings are the only lookup authority; there is no remaining `ItemInstance.Data` identity/backing field. The reader interface for this @@ -1761,8 +1763,10 @@ First Aetheria surfaces to publish: faction resolver, or `CorporationLegacyId` relationship read may return to the live `Galaxy`/`LoadoutGenerator` path. - `Aetheria.State.Verify` guards Unity runtime catalog lookup authority: - package snapshots may expose typed-key lookup only, while legacy-ID lookup - remains quarantined in canonical migration/catalog inspection APIs. + package snapshots may expose typed-key lookup only, and runtime + item/corporation/name-file DTOs may not publish legacy relationship fields; + legacy-ID lookup remains quarantined in canonical migration/catalog + inspection APIs. - `Aetheria.State.Verify` guards behavior body reference naming: resource-scanner and mining-tool runtime state surfaces use `BodyKey` fields; old `*BodyId` readback names and generic legacy reference parsers may not