diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index b974bbd3d..972db1082 100644 Binary files a/.voidbot/state/aetheria.cc and b/.voidbot/state/aetheria.cc differ diff --git a/Assets/Scripts/Gameplay/ActionBarSlot.cs b/Assets/Scripts/Gameplay/ActionBarSlot.cs index 8f5c12791..0c6f29c76 100644 --- a/Assets/Scripts/Gameplay/ActionBarSlot.cs +++ b/Assets/Scripts/Gameplay/ActionBarSlot.cs @@ -99,7 +99,7 @@ public class ActionBarGearBinding : ActionBarBinding public ActionBarGearBinding(Entity entity, ActionBarSlot slot, EquippedItem item, IActivatedBehavior behavior) : base(entity, slot) { Item = item; - var data = item.EquippableItem.Data.Value as EquippableItemData; + var data = entity.ItemManager.GetData(item.EquippableItem); Behavior = behavior; Slot.QuantityRemaining.gameObject.SetActive(false); Slot.Icon.gameObject.SetActive(true); diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index d2648ebf0..923e91f57 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -485,7 +485,6 @@ public class ActionGameManager : MonoBehaviour } var runtimeItemCatalog = new AetheriaRuntimeItemCatalog(RuntimeCatalog); - RuntimeCatalogLinkBase.BindRuntimeItemCatalog(runtimeItemCatalog); ItemManager = new ItemManager( runtimeItemCatalog, Settings.GameplaySettings, @@ -666,7 +665,7 @@ public class ActionGameManager : MonoBehaviour slot.Binding = new ActionBarGearBinding(CurrentEntity, slot, equippedItemDragAction.EquippedItem, trigger); return true; case ItemInstanceDragObject itemInstanceDragAction: - if (!(itemInstanceDragAction.Item.Data.Value is ConsumableItemData consumable)) return false; + if (!(ItemManager.GetData(itemInstanceDragAction.Item) is ConsumableItemData consumable)) return false; slot.Binding = new ActionBarConsumableBinding(CurrentEntity, slot, consumable); return true; case WeaponGroupDragObject weaponGroupDragAction: diff --git a/Assets/Scripts/ServerShared/Entity.cs b/Assets/Scripts/ServerShared/Entity.cs index 64133c42d..4a0513e14 100644 --- a/Assets/Scripts/ServerShared/Entity.cs +++ b/Assets/Scripts/ServerShared/Entity.cs @@ -1089,7 +1089,7 @@ public class ConsumableItemEffect { Item = item; Entity = entity; - Data = (ConsumableItemData) item.Data.Value; + Data = entity.ItemManager.GetData(item) as ConsumableItemData; RemainingDuration = Data.Duration; Behaviors = Data.Behaviors @@ -1399,7 +1399,7 @@ public class EquippedCargoBay : EquippedItem // Check whether the given item will fit when its origin is placed at the given coordinate public bool ItemFits(ItemInstance item, int2 cargoCoord) { - var itemData = item.Data.Value; + var itemData = ItemManager.GetData(item); // Check every cell of the item's shape foreach (var i in itemData.Shape.Coordinates) { @@ -1707,4 +1707,4 @@ public class BehaviorGroup } //public IAnalogBehavior Axis; -} \ No newline at end of file +} diff --git a/Assets/Scripts/ServerShared/EntitySerializer.cs b/Assets/Scripts/ServerShared/EntitySerializer.cs index a9679f7c9..6a6081de9 100644 --- a/Assets/Scripts/ServerShared/EntitySerializer.cs +++ b/Assets/Scripts/ServerShared/EntitySerializer.cs @@ -209,7 +209,7 @@ public abstract class EntityPack { foreach (var (_, item) in t) { - var itemData = item.Data.Value; + var itemData = itemManager.GetData(item); if (item is SimpleCommodity s) _price += itemData.Price * s.Quantity; else @@ -221,7 +221,7 @@ public abstract class EntityPack { foreach (var (_, item) in t) { - var itemData = item.Data.Value; + var itemData = itemManager.GetData(item); if (item is SimpleCommodity s) _price += itemData.Price * s.Quantity; else diff --git a/Assets/Scripts/ServerShared/ItemManager.cs b/Assets/Scripts/ServerShared/ItemManager.cs index 0cd297a1b..5121425b9 100644 --- a/Assets/Scripts/ServerShared/ItemManager.cs +++ b/Assets/Scripts/ServerShared/ItemManager.cs @@ -58,6 +58,34 @@ public class ItemManager return _itemData.Get(id); } + public RuntimeCatalogLink CreateLink(ItemData item) + { + return new RuntimeCatalogLink(item); + } + + public ItemData GetData(ItemInstance item) + { + Hydrate(item); + return item?.Data?.Value; + } + + public void Hydrate(ItemInstance item) + { + if (item?.Data == null || item.Data.Value != null) + { + return; + } + + var data = _itemData.Get(item.Data.LinkID); + if (data == null) + { + _logger($"Attempted to hydrate missing item id {item.Data.LinkID}"); + return; + } + + item.Data.SetValue(data); + } + public void Log(string s) { _logger(s); @@ -65,32 +93,32 @@ public class ItemManager public SimpleCommodityData GetData(SimpleCommodity item) { - return item.Data.Value as SimpleCommodityData; + return GetData((ItemInstance)item) as SimpleCommodityData; } public CraftedItemData GetData(CraftedItemInstance item) { - return item.Data.Value as CraftedItemData; + return GetData((ItemInstance)item) as CraftedItemData; } public EquippableItemData GetData(EquippableItem item) { - return item.Data.Value as EquippableItemData; + return GetData((ItemInstance)item) as EquippableItemData; } public float GetMass(ItemInstance item) { return item switch { - CraftedItemInstance _ => item.Data.Value.Mass, - SimpleCommodity commodity => item.Data.Value.Mass * commodity.Quantity, + CraftedItemInstance _ => GetData(item).Mass, + SimpleCommodity commodity => GetData(item).Mass * commodity.Quantity, _ => 0 }; } public float GetThermalMass(ItemInstance item) { - var data = item.Data.Value; + var data = GetData(item); return item switch { CraftedItemInstance _ => data.Mass * data.SpecificHeat, @@ -128,7 +156,7 @@ public class ItemManager { var newItem = new SimpleCommodity { - Data = new RuntimeCatalogLink{LinkID = item.ID}, + Data = CreateLink(item), Quantity = count }; //ItemData.Add(newItem); @@ -141,7 +169,7 @@ public class ItemManager public ItemInstance Instantiate(ItemInstance item) { - var data = item.Data.Value; + var data = GetData(item); if(data is CraftedItemData c) { var i = CreateInstance(c); @@ -163,13 +191,13 @@ public class ItemManager { return new EquippableItem { - Data = new RuntimeCatalogLink {LinkID = item.ID}, Quality = quality, Durability = equippableItemData.Durability + Data = CreateLink(item), Quality = quality, Durability = equippableItemData.Durability }; } var newCommodity = new CompoundCommodity { - Data = new RuntimeCatalogLink{LinkID = item.ID}, + Data = CreateLink(item), Quality = quality }; return newCommodity; diff --git a/Assets/Scripts/ServerShared/RuntimeProjection/RuntimeCatalogEntry.cs b/Assets/Scripts/ServerShared/RuntimeProjection/RuntimeCatalogEntry.cs index a5e807047..fd4f059b7 100644 --- a/Assets/Scripts/ServerShared/RuntimeProjection/RuntimeCatalogEntry.cs +++ b/Assets/Scripts/ServerShared/RuntimeProjection/RuntimeCatalogEntry.cs @@ -28,29 +28,27 @@ public abstract class RuntimeCatalogEntry public class RuntimeCatalogLink : RuntimeCatalogLinkBase where T : ItemData { - public T Value => ResolveRuntimeItemCatalog(LinkID); + public T Value { get; private set; } + + public RuntimeCatalogLink() + { + } + + public RuntimeCatalogLink(T value) + { + SetValue(value); + } + + public void SetValue(T value) + { + Value = value; + LinkID = value?.ID ?? Guid.Empty; + } } public class RuntimeCatalogLinkBase { public Guid LinkID; - - private static IRuntimeItemCatalogReader Catalog { get; set; } - - protected static T ResolveRuntimeItemCatalog(Guid linkId) where T : ItemData - { - if (Catalog == null) - throw new InvalidOperationException("Runtime item catalog resolution is not bound. Open typed Aetheria runtime state before reading item object graphs."); - - return Catalog.Get(linkId); - } - - public static void BindRuntimeItemCatalog(IRuntimeItemCatalogReader catalog) - { - if (catalog == null) throw new ArgumentNullException(nameof(catalog)); - - Catalog = catalog; - } } public interface ITintInspector diff --git a/Assets/Scripts/UI/Menu/InventoryMenu.cs b/Assets/Scripts/UI/Menu/InventoryMenu.cs index 3d7f60bdd..5388e6aa9 100644 --- a/Assets/Scripts/UI/Menu/InventoryMenu.cs +++ b/Assets/Scripts/UI/Menu/InventoryMenu.cs @@ -117,7 +117,7 @@ public class InventoryMenu : MonoBehaviour _selectedPanel = panel; _selectedPosition = cargoEvent.CargoBay.Cargo[item]; _selectedItem = item; - _selectedItemData = item.Data.Value; + _selectedItemData = GameManager.ItemManager.GetData(item); foreach (var v in _selectedItemData.Shape.Coordinates) { var v2 = _selectedItemData.Shape.Rotate(v, _selectedItem.Rotation) + _selectedPosition; diff --git a/Assets/Scripts/UI/Menu/InventoryPanel.cs b/Assets/Scripts/UI/Menu/InventoryPanel.cs index dc8614cc4..724af2ccb 100644 --- a/Assets/Scripts/UI/Menu/InventoryPanel.cs +++ b/Assets/Scripts/UI/Menu/InventoryPanel.cs @@ -410,7 +410,7 @@ public class InventoryPanel : MonoBehaviour, IPointerClickHandler //Debug.Log("Entity Pointer Enter"); if (!(GameManager.DragObject is ItemDragObject itemDragObject)) return; var item = itemDragObject.Item; - var itemData = item.Data.Value; + var itemData = GameManager.ItemManager.GetData(item); if (!(item is EquippableItem equippableItem)) return; var placementPosition = v + itemDragObject.OriginCellOffset; if (entity.ItemFits(equippableItem, placementPosition)) @@ -586,7 +586,7 @@ public class InventoryPanel : MonoBehaviour, IPointerClickHandler if (item != null) { var itemPosition = cargo.Cargo[item]; - var itemData = item.Data.Value; + var itemData = GameManager.ItemManager.GetData(item); var originalOccupancy = cargo.Data.InteriorShape.Inset(itemData.Shape, itemPosition, item.Rotation); _dragCells = originalOccupancy.Coordinates .Select(v1 => Instantiate(CellInstances[v1], DragParent, true).transform).ToArray(); @@ -633,7 +633,7 @@ public class InventoryPanel : MonoBehaviour, IPointerClickHandler //Debug.Log("Inventory Pointer Enter"); if (!(GameManager.DragObject is ItemDragObject itemDragObject)) return; var item = itemDragObject.Item; - var itemData = item.Data.Value; + var itemData = GameManager.ItemManager.GetData(item); var placementPosition = v + itemDragObject.OriginCellOffset; if (cargo.ItemFits(item, placementPosition)) { @@ -851,7 +851,7 @@ public class InventoryPanel : MonoBehaviour, IPointerClickHandler return Color.white * .25f; var c = float3(1); - if (item.Data.Value is EquippableItemData equippable) + if (GameManager.ItemManager.GetData(item) is EquippableItemData equippable) c = HardpointData.GetColor(equippable.HardpointType); if(!highlight) diff --git a/Assets/Scripts/UI/Menu/SectorRenderer.cs b/Assets/Scripts/UI/Menu/SectorRenderer.cs index ad27b9a8d..6a73054c1 100644 --- a/Assets/Scripts/UI/Menu/SectorRenderer.cs +++ b/Assets/Scripts/UI/Menu/SectorRenderer.cs @@ -90,17 +90,17 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS Properties.AddProperty("Stars", () => starCount); var stationCount = zone.PackedContents.Entities - .Count(entity => ((HullData) entity.Hull.Data.Value).HullType == HullType.Station) + .Count(entity => (GameManager.ItemManager.GetData(entity.Hull) as HullData).HullType == HullType.Station) .ToString(); Properties.AddProperty("Stations", () => stationCount); var turretCount = zone.PackedContents.Entities - .Count(entity => ((HullData) entity.Hull.Data.Value).HullType == HullType.Turret) + .Count(entity => (GameManager.ItemManager.GetData(entity.Hull) as HullData).HullType == HullType.Turret) .ToString(); Properties.AddProperty("Turrets", () => turretCount); var shipCount = zone.PackedContents.Entities - .Count(entity => ((HullData) entity.Hull.Data.Value).HullType == HullType.Ship) + .Count(entity => (GameManager.ItemManager.GetData(entity.Hull) as HullData).HullType == HullType.Ship) .ToString(); Properties.AddProperty("Ships", () => shipCount); } diff --git a/Assets/Scripts/UI/Menu/TradeMenu.cs b/Assets/Scripts/UI/Menu/TradeMenu.cs index e9b0e403e..0a8d2e717 100644 --- a/Assets/Scripts/UI/Menu/TradeMenu.cs +++ b/Assets/Scripts/UI/Menu/TradeMenu.cs @@ -222,7 +222,7 @@ public class TradeMenu : MonoBehaviour data => data.Shape.Width*data.Shape.Height)); var items = Inventory.Cargo.Keys - .Select(ii=>(ii, ii.Data.Value)); + .Select(ii=>(ii, GameManager.ItemManager.GetData(ii))); if (MinimumSizeFilter.gameObject.activeSelf) items = items.Where(i => diff --git a/Assets/Scripts/UI/Properties Panel/PropertiesPanel.cs b/Assets/Scripts/UI/Properties Panel/PropertiesPanel.cs index 12c818fb7..5dd9be2af 100644 --- a/Assets/Scripts/UI/Properties Panel/PropertiesPanel.cs +++ b/Assets/Scripts/UI/Properties Panel/PropertiesPanel.cs @@ -382,7 +382,7 @@ public class PropertiesPanel : MonoBehaviour private void AddItemProperties(ItemInstance item) { - var data = item.Data.Value; + var data = GameManager.ItemManager.GetData(item); AddProperty(data.Description); @@ -442,7 +442,7 @@ public class PropertiesPanel : MonoBehaviour private string GetTitle(EquippableItem item) { - var data = item.Data.Value; + var data = GameManager.ItemManager.GetData(item); var (tier, upgrades) = GameManager.ItemManager.GetTier(item); return $"{data.Name} ({tier.Name}{new string('+', upgrades)})"; diff --git a/Assets/Scripts/Zone Display/ZoneRenderer.cs b/Assets/Scripts/Zone Display/ZoneRenderer.cs index 86d617cf9..c8174e271 100644 --- a/Assets/Scripts/Zone Display/ZoneRenderer.cs +++ b/Assets/Scripts/Zone Display/ZoneRenderer.cs @@ -584,7 +584,7 @@ public class ZoneRenderer : MonoBehaviour var itemPickup = gridObject.gameObject.GetComponent(); itemPickup.Item = item; itemPickup.ZoneRenderer = this; - itemPickup.ScanLabel.text = item.Data.Value.Name; + itemPickup.ScanLabel.text = ItemManager.GetData(item).Name; if (item is CraftedItemInstance craftedItemInstance) { var c = ItemManager.GetTier(craftedItemInstance).tier.Color.ToColor(); diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index ac5aae43c..5af24a752 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -623,6 +623,9 @@ First Aetheria surfaces to publish: Unity-side custom MessagePack resolver/formatter source. - Done: demote `PlayerSettings` and nested settings from MessagePack shapes after the legacy settings writer was disabled. + - Done: delete the process-global runtime catalog resolver from + `RuntimeCatalogLink`; item instances now carry hydrated identifiers and + `ItemManager` owns resolution through the typed runtime catalog reader. - Remaining: delete or quarantine old cache abstractions that no longer protect an invariant once catalog migration has a typed reader. @@ -656,6 +659,11 @@ First Aetheria surfaces to publish: can apply them into canonical typed settings/run/zone/entity state, and commands are cleared after application. It also proves renderer-emitted Eve commands are queued as typed command envelopes separately from state commits. +- Unity batchmode compile with Editor `6000.4.2f1` returned cleanly after the + runtime catalog resolver cut; `Logs/codex-unity-compile.log` has no compiler + error hits. +- `rg ".Data.Value|BindRuntimeItemCatalog|ResolveRuntimeItemCatalog|private static IRuntimeItemCatalogReader"` in + `Assets/Scripts` now finds only `ItemManager` checking its own hydrated value. - `Aetheria.State.Smoke` proves the provider-owned Eve command bridge drains `gamecult.eve.command.v1` envelopes, accepts advertised refresh commands, rejects unknown commands, persists `AetheriaEveCommandDrainStatus`, and exposes