Move the ServerShared data model onto CultLib's CultCache

Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.

Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.

References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.

AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].

Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.

Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-14 19:00:42 +02:00
commit a2b54889b4
50 changed files with 537 additions and 542 deletions

View file

@ -6,7 +6,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using JsonKnownTypes;
using GameCult.Caching;
using MessagePack;
using Newtonsoft.Json;
using CultMath;
@ -268,17 +268,17 @@ public class Shape
}
[MessagePackObject, JsonObject(MemberSerialization.OptIn), JsonConverter(typeof(JsonKnownTypesConverter<ItemData>))]
public abstract class ItemData : DatabaseEntry, INamedEntry
[JsonObject(MemberSerialization.OptIn)]
public abstract class ItemData
{
[Inspectable, JsonProperty("name"), Key(1)]
[Inspectable, CultName, JsonProperty("name"), Key(1)]
public string Name;
[InspectableText, JsonProperty("description"), Key(2)]
[Inspectable, CultInspectorTextArea, JsonProperty("description"), Key(2)]
public string Description;
[InspectableDatabaseLink(typeof(Faction)), JsonProperty("creator"), Key(3)]
public Guid Manufacturer;
[Inspectable, JsonProperty("creator"), Key(3)]
public CultRecordRef<Faction> Manufacturer;
[Inspectable, JsonProperty("mass"), Key(4)]
public float Mass;
@ -295,16 +295,9 @@ public abstract class ItemData : DatabaseEntry, INamedEntry
[Inspectable, JsonProperty("price"), Key(8)]
public int Price = 0;
[IgnoreMember] public string EntryName
{
get => Name;
set => Name = value;
}
}
[DatabaseCategory("Items"), Inspectable, MessagePackObject]
[CultDocument("aetheria.simplecommoditydata", "1"), Inspectable, MessagePackObject]
public class SimpleCommodityData : ItemData
{
// MessagePack keys 6, 7, 8 and 11 belonged to removed resource-distribution fields; do not reuse them.
@ -316,7 +309,7 @@ public class SimpleCommodityData : ItemData
public SimpleCommodityCategory Category;
}
[MessagePackObject, JsonObject(MemberSerialization.OptIn), JsonConverter(typeof(JsonKnownTypesConverter<CraftedItemData>))]
[JsonObject(MemberSerialization.OptIn)]
public abstract class CraftedItemData : ItemData
{
// The named slots this design is assembled from. A stat may read the quality of the part filling one.
@ -334,17 +327,17 @@ public class ItemRole
public string Name;
}
[DatabaseCategory("Items"), Inspectable, MessagePackObject]
[CultDocument("aetheria.compoundcommoditydata", "1"), Inspectable, MessagePackObject]
public class CompoundCommodityData : CraftedItemData
{
[InspectableDatabaseLink(typeof(PersonalityAttribute)), JsonProperty("demandProfile"), Key(10)]
public Dictionary<Guid, float> DemandProfile = new Dictionary<Guid, float>();
[Inspectable, CultReference(typeof(PersonalityAttribute), many: true), JsonProperty("demandProfile"), Key(10)]
public Dictionary<CultRecordRef<PersonalityAttribute>, float> DemandProfile = new Dictionary<CultRecordRef<PersonalityAttribute>, float>();
[Inspectable, JsonProperty("category"), Key(11)]
public CompoundCommodityCategory Category;
}
[DatabaseCategory("Items"), Inspectable, MessagePackObject]
[CultDocument("aetheria.consumableitemdata", "1"), Inspectable, MessagePackObject]
public class ConsumableItemData : CraftedItemData
{
[Inspectable, JsonProperty("behaviors"), Key(10)]
@ -356,7 +349,7 @@ public class ConsumableItemData : CraftedItemData
[Inspectable, JsonProperty("duration"), Key(12)]
public float Duration;
[InspectableTexture, JsonProperty("icon"), Key(13)]
[Inspectable, CultInspectorAssetPath, JsonProperty("icon"), Key(13)]
public string Icon;
[Inspectable, JsonProperty("effectiveness"), Key(14)]
@ -366,7 +359,7 @@ public class ConsumableItemData : CraftedItemData
[JsonObject(MemberSerialization.OptIn)]
public abstract class EquippableItemData : CraftedItemData
{
[InspectableTexture, JsonProperty("schematic"), Key(10)]
[Inspectable, CultInspectorAssetPath, JsonProperty("schematic"), Key(10)]
public string Schematic;
[Inspectable, JsonProperty("behaviors"), Key(11)]
@ -396,10 +389,10 @@ public abstract class EquippableItemData : CraftedItemData
// [Inspectable, JsonProperty("sfx"), Key(19)]
// public string SoundEffectTrigger;
[InspectableTexture, JsonProperty("actionIcon"), Key(20)]
[Inspectable, CultInspectorAssetPath, JsonProperty("actionIcon"), Key(20)]
public string ActionBarIcon;
[InspectableSoundBank, JsonProperty("soundBank"), Key(21)]
[Inspectable, JsonProperty("soundBank"), Key(21)]
public uint SoundBank;
[Inspectable, JsonProperty("audioStats"), Key(22)]
@ -446,14 +439,14 @@ public abstract class EquippableItemData : CraftedItemData
[Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
public class AudioStat
{
[InspectableAudioParameter, JsonProperty("parameter"), Key(0)]
[Inspectable, JsonProperty("parameter"), Key(0)]
public uint Parameter;
[Inspectable, JsonProperty("stat"), Key(1)]
public PerformanceStat Stat = new PerformanceStat();
}
[DatabaseCategory("Items"), Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
[CultDocument("aetheria.geardata", "1"), Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
public class GearData : EquippableItemData
{
[Inspectable, JsonProperty("hardpointType"), Key(23)]
@ -462,7 +455,7 @@ public class GearData : EquippableItemData
[IgnoreMember] public override HardpointType HardpointType => Hardpoint;
}
[DatabaseCategory("Items"), Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
[CultDocument("aetheria.cargobaydata", "1"), Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
public class CargoBayData : EquippableItemData
{
[Inspectable, JsonProperty("interiorShape"), Key(24)]
@ -471,14 +464,14 @@ public class CargoBayData : EquippableItemData
[IgnoreMember] public override HardpointType HardpointType => HardpointType.Tool;
}
[DatabaseCategory("Items"), Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
[CultDocument("aetheria.dockingbaydata", "1"), Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
public class DockingBayData : CargoBayData
{
[Inspectable, JsonProperty("maxSize"), Key(25)]
public int2 MaxSize;
}
[DatabaseCategory("Items"), MessagePackObject, JsonObject(MemberSerialization.OptIn)]
[CultDocument("aetheria.weaponitemdata", "1"), MessagePackObject, JsonObject(MemberSerialization.OptIn)]
public class WeaponItemData : GearData
{
[Inspectable, JsonProperty("range"), Key(24)]
@ -497,13 +490,13 @@ public class WeaponItemData : GearData
public WeaponModifiers WeaponModifiers;
}
[DatabaseCategory("Items"), Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
[CultDocument("aetheria.hulldata", "1"), Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
public class HullData : EquippableItemData
{
[Inspectable, JsonProperty("hardpoints"), Key(23)]
public List<HardpointData> Hardpoints = new List<HardpointData>();
[InspectablePrefab, JsonProperty("prefab"), Key(24)]
[Inspectable, CultInspectorAssetPath, JsonProperty("prefab"), Key(24)]
public string Prefab;
[Inspectable, JsonProperty("hullType"), Key(25)]
@ -541,7 +534,7 @@ public class HullData : EquippableItemData
}
[Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
public class HardpointData : ITintInspector
public class HardpointData
{
[Inspectable, JsonProperty("type"), Key(0)] public HardpointType Type;
[Inspectable, JsonProperty("position"), Key(1)] public int2 Position;
@ -628,10 +621,10 @@ public class PerformanceStat
}
}
[DatabaseCategory("Items"), Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
public class PersonalityAttribute : DatabaseEntry, INamedEntry
[CultDocument("aetheria.personalityattribute", "1"), Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
public class PersonalityAttribute
{
[Inspectable, JsonProperty("name"), Key(1)]
[Inspectable, CultName, JsonProperty("name"), Key(1)]
public string Name;
[Inspectable, JsonProperty("low"), Key(2)]
@ -640,9 +633,4 @@ public class PersonalityAttribute : DatabaseEntry, INamedEntry
[Inspectable, JsonProperty("high"), Key(3)]
public string HighName;
[IgnoreMember] public string EntryName
{
get => Name;
set => Name = value;
}
}