Commit graph Aetheria/Assets/Scripts
Author SHA1 Message Date
Meta Vens
e4df73e4fe Fix F1: PersistedBehaviors as a pairs array, per Cut 0's ruling
Dictionary<int2, PersistentBehaviorData[]> has no hash-resistant comparer
under CultCache's UntrustedData security, so any run store holding an
entity threw MessagePackSerializationException on reopen, even when the
dictionary was empty. EntityPack.PersistedBehaviors (key 5, unchanged)
becomes (int2 position, PersistentBehaviorData[] data)[], matching
Equipment/CargoBays/DockingBays. Pack writes pairs; Unpack builds a local
lookup before restoring behaviors.

PersistentBehaviorData had no concrete leaf and no Union, so nothing could
ever exercise its wire format; added MarkerPersistentBehaviorData as the
one placeholder leaf so ReopenedPackKeepsPersistedBehaviors can round-trip
real data ahead of a real IPersistentBehavior implementer landing.

Tests: RunSaveTests.BarePack uses an empty pairs array.
Added ReopenedPackKeepsPersistedBehaviors (a hand-built pack with two
positions, each with one persisted behavior, round-trips through commit
and reopen) and ReopenSurvivesAGeneratedShipWithEntities (a real ship from
LoadoutGenerator against a catalog fixture, committed into a SavedZone and
reopened, pinning the defect at the layer Soul's probe found it).
MaterializedLotsSurviveSaveAndReload and CommitKeepsOnlyReachableLots go
green; 36 of 36 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 04:58:15 +02:00
Meta Vens
415f8e1152 Add the lot ledger and mint every crafted instance through it
Quality, Ingredients and Product leave CraftedItemInstance; a unit now
carries only a LotId into the run-scoped ProvenanceLedger. ItemManager
gains the one lot-minting path (CreateLot/CreateInstance(int)), GetLot,
and Brand(), which derives a maker and product from provenance instead
of a stored field. RunSave persists the ledger pruned to what the saved
zones' entities still reach; the live ledger is never pruned, so a
floor pickup collected after a save still resolves. CopyBuild is gone:
matching hardpoints now share the same lot instead of copying values.
PropertiesPanel shows the derived brand where the design's manufacturer
used to be. AetherDb's census flags duplicate (maker, design) products
and its save command checks every crafted instance's lot against a real
run store.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 04:16:23 +02:00
Meta Vens
69801bbb2c Delete the re-mint path and the dead debug menu
Cut A of docs/item-provenance-cut.md. TradeMenuDebug.cs was dead (its
script GUID appears in no scene/prefab/asset and no other .cs names the
type). ItemManager.Instantiate and CreateInstance(SimpleCommodityData,
int) re-rolled quality and dropped provenance on every load; nothing
outside EntitySerializer called them once the instantiate parameter is
gone. EntitySerializer.Unpack/Restore no longer thread an instantiate
flag, so Temperature and Armor are restored unconditionally like every
other caller already got. The starting ship and spawnturret now keep
what the generator built instead of re-minting.

Verification: Aetheria.Shared, tools/AetherDb build green against
CultLib a0813c6; tests/Aetheria.Shared.Tests 29 passed (unchanged); the
four negative greps in the map are empty. Unity compile is still owed
(the editor is open on this project; batchmode was not run per
instructions).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 03:42:12 +02:00
Meta Vens
37f513e5dc Keep the schematic underlay under the shape grid
The port dropped the legacy drawer's FlexibleSpace, so the grid's group
stretched to the inspector width and the texture was fitted there instead.
Checked by the operator in Studio on LonginusX.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 02:08:40 +02:00
Meta Vens
d90a65a908 Stop describing a preset materializer that does not exist
LoadoutGenerator.IsAvailable's comment claimed a caller passes it to
Loadouts.Materialize. No game path materializes a preset yet. The comment now
says a spawner is to pass it, and that none does yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 23:32:53 +02:00
Meta Vens
d503f076e1 Capture presets through their own cache; keep the catalog read-only
Opening the process cache's catalog writable in the editor was the wrong cut.
It made catalog read-only only by convention, so a stray catalog write in play
succeeded and a later flush could land play-mutated instances in Aetheria.cc.

- The process cache opens the catalog read-only in every build again, so the
  missing-global check runs for it as it did before db83cd29. The writable
  empty-seed exemption from ab4ced0f stays for AetherDb and fixtures.
- Loadouts.Commit(catalogPath, loadout, replace) is the only preset writer. It
  opens its own short-lived cache over the catalog, writable, commits the one
  preset conditionally on its key and disposes it. No other cache or instance
  is touched. The running game sees the preset only after the catalog reloads.
  CultLib at a0813c6 allows this: a read-only single-file store takes no lock
  and holds no handle, and the writer's commit takes the exclusive sidecar
  <path>.lock and replaces the file atomically.
- Capture refuses, writing nothing, when Aetheria.cc is missing (it never
  creates the catalog), when a preset of that name exists under another key
  (CultCache does not reject duplicate names on write), or when the key holds a
  preset and replace is not given.
- The console refuses a command with any character outside letters, digits,
  spaces and hyphens instead of stripping it, so "Mk.II" can no longer become
  "MkII". capturepreset refuses a second argument other than replace, so an
  unquoted capturepreset My Ship refuses instead of saving "My".
- The capture log line says the session sees the preset after a reload, and
  that a Studio session opened earlier overwrites captured presets on save.
- Tests: capture while the process cache holds the catalog read-only leaves it
  unchanged; a missing catalog refuses and creates nothing; a same name under a
  Studio-minted key refuses, with or without replace; a game-shaped read-only
  open missing a global throws. The existing preset tests now commit through
  the catalog path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 23:32:52 +02:00
Meta Vens
ab4ced0f53 Keep a missing catalog global loud for writable opens
Globals are owned by lifecycle, so the cache must never run without one,
however the catalog is opened. Before this, opening the catalog writable
skipped the check entirely. The editor now opens the catalog writable, so a
missing global went silent in play mode.

AetheriaStores.Open now skips the check only for a writable catalog that
has no records: a seed store being authored from nothing, as the test
fixtures do. A populated catalog is always checked, and so is a read-only
empty one. Nothing else relied on the old skip: the game has no catalog
globals, AetherDb opens the populated Aetheria.cc, and every writable test
reopen already holds its fixture global.

WritablePopulatedCatalogMissingGlobalIsLoud removes the global from a
populated catalog and checks that a writable reopen throws. It fails when
the old writable-skip condition is restored.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 23:13:22 +02:00
Meta Vens
db83cd290d Make loadouts authored catalog presets with an editor capture command
Loadouts are ship presets (variants) for spawners, not a player feature.

- Route Loadout to the catalog; the player store no longer holds it.
- Materialize takes no credits: affordability, the charge and Loadouts.Price
  are deleted, since only the player menu priced loadouts. Availability,
  key-order product choice, all-or-nothing failures and the weapon-group
  rules stay.
- Delete the InventoryPanel Save Loadout and Restore Loadout entries and
  RestoreLoadout, which also removes its docking-bay overwrite.
- The editor opens the catalog writable in the one process cache, and the
  editor-only console command capturepreset "<name>" [replace] captures the
  piloted ship. Loadouts.Commit keys a preset by name, refuses a same-name
  replace unless asked, and commits conditionally on that key. The single-file
  store then lands only that record onto the file as it is on disk. An
  unconditional commit would re-serialize every live catalog instance,
  including any that play has mutated.
- Tests: capture -> commit -> fresh reopen round-trip, explicit replace with
  no duplicate, and a preset commit that does not persist a mutated live
  Faction. The money-only tests are removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 23:09:43 +02:00
Meta Vens
0702e78cda Refuse non-weapon groups, fix the group count, and widen loadout price (Cut 10 fix)
Materialize now reports a weapon group index whose slot design has no
WeaponData, and a loadout with more groups than WeaponGroupCount. The ship
always gets exactly WeaponGroupCount groups, and a shorter loadout is padded
with empty groups. Both checks run before anything is built.

Price is summed as long, so the hull add can no longer wrap to a negative
price that pays the player. A price beyond int credits is simply unaffordable.

Deleted Capture's index >= 0 filter and its test. Every production writer of
WeaponGroups items (WeaponGroupAssignment, GenerateWeaponGroups, Unpack replay
of Pack, Materialize) groups only weapons, and TryUnequip removes weapons from
groups, so nothing produces an ungrouped index any more.

The Materialize comment now says a failed fit may already have advanced
itemManager.Random.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 22:51:37 +02:00
Meta Vens
1cd6170d7a Decide loadout affordability in Materialize and test weapon-group unequip (Cut 10 fix)
Materialize, which already charges, now also owns affordability: when credits
are below Price it lists "cannot afford <price> credits" and builds nothing.
The restore menu's separate price < Credits check is deleted; the menu still
shows Loadouts.Price and lists Materialize's failures.

Tests: one credit short builds nothing, leaves credits unchanged and lists the
failure. A fixture gun with an InstantWeapon behavior proves TryUnequip removes
the weapon and its item from every weapon group.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 22:38:16 +02:00
Meta Vens
890063b8bb Remove an unequipped weapon from its weapon groups (Cut 10 fix)
TryUnequip removed a weapon from _weapons but left it in WeaponGroups, so an
unequipped weapon could still be fired from its action-bar group and
EntitySerializer.Pack wrote -1 for it. The removal mirrors the group toggle in
WeaponGroupAssignment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 22:30:40 +02:00
Meta Vens
89667ca4e7 Offer Save Loadout only for a displayed ship (Cut 10 fix)
The option was added for any displayed entity, stations included, and a null
displayed entity threw when chosen. It is now added only when the displayed
entity is a Ship, the same conditional shape as the Restore Loadout guard.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 22:29:58 +02:00
Meta Vens
61fc863e6b Stop loadouts from saving or trusting dangling weapon-group indices (Cut 10 fix)
Entity.TryUnequip does not clear WeaponGroups, so Capture saved -1 for a
grouped item that was no longer equipped, and Materialize later threw
IndexOutOfRangeException after every other check passed. Capture now skips
group items that are not equipped. Materialize reports every weapon-group index
outside the loadout's slots as a failure before building anything, so a stale
or hand-edited record builds nothing and charges nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 22:28:24 +02:00
Meta Vens
5e5c304e37 Make loadout availability a port and move the charge into Materialize (Cut 10 fix)
Materialize takes isAvailable as a Predicate<FactionProductData>; the restore
menu passes LoadoutGenerator.IsAvailable for the docked station, so generation
and loadouts still share one availability rule. Candidate products are ordered
by record key, so "first available product" no longer depends on dictionary
order. Materialize charges Price through ref credits only when it returns a
ship; RestoreLoadout no longer subtracts on its own.

Tests: a slot that resolves but does not fit builds nothing, charges nothing and
is listed; Price is the sum of design prices and is charged on success; the
first available product in key order builds a slot, a later one is used when
the first is unavailable, and none available fails the slot.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 22:27:03 +02:00
Meta Vens
1951b39a95 Restore the schematic-shape drawer's texture, aspect and hardpoint tints (Cut 10)
On Studio 1.3.1, drawers are handed the owning record read-only as
CultInspector.Record. InspectableSchematicShapeDrawer reads the item from it
and restores what the Database Tools drawer did (d3db1730,
AetheriaInspectors.cs ShapeSchematicInspector):
- the item's schematic texture drawn under the toggle grid;
- the height derived from the texture's aspect when a texture exists;
- hull hardpoint cells tinted with HardpointData.TintColor.

The drawer never writes to the record. The comment saying these waited on
the Studio exposing the open record is gone.

Verified by the Unity batchmode compile (no error CS). The visual check is
in the operator's Studio click-through.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 22:09:38 +02:00
Meta Vens
65a007e509 Seed zones from a stable name hash instead of string.GetHashCode (Cut 10)
Closes the 8a follow-up. string.GetHashCode is stable on Mono but randomized
per process on .NET Core, so a headless sim that generates zones would get
different zones on every run.

- Extensions.StableHash folds a string's UTF-8 bytes through CultMath's pcg
  from the FNV-1a offset basis. No new CultMath API.
- ZoneGenerator seeds from `Name.StableHash() ^ pcg3d(Position).x`.
- Zone seeds from `Name.StableHash() ?? 1337u`. CultMath's Random maps
  seed 0 to its default, and the abs/Convert.ToUInt32 pair that threw on
  int.MinValue is gone.

Old runs are discarded, so a different galaxy per seed costs nothing.

Tests: StableHashIsNotProcessRandomized pins "Adrasteia" to 2427892944, the
value an independent Add-Type transcription of CultMath's pcg produced. It
fails with GetHashCode substituted back in.

Builds: headless, 15 tests pass, Unity batchmode compile reports no error CS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 22:09:37 +02:00
Meta Vens
00f62acc34 Make loadouts player-store design documents built all-or-nothing (Cut 10)
Loadout.cs owns loadouts: the document, capture, save and materialization.

- Loadout ("aetheria.loadout", player store) holds a hull design ref, and
  per slot a hull cell, a rotation and an EquippableItemData design ref,
  plus weapon groups as slot indices. It names no product, manufacturer,
  unit roll, cargo, faction, position or behaviour state.
- Loadouts.Capture records the hull design and one slot per equipped item
  (gear, cargo bays, docking bays). Entity.Equipment also holds the hull's
  own unit (MapEntity adds EquippedHull); that is Loadout.Hull, not a slot.
- Loadouts.Save is the only writer: one Commit to the player store, reusing
  the existing key for the name.
- Loadouts.Materialize is the only builder of a ship from a loadout. For
  the hull and each slot in order it resolves the design and takes the
  first product in catalog order that LoadoutGenerator.IsAvailable (now
  public) accepts, with no any-manufacturer fallback. Every failing slot is
  listed; units are built only after everything resolved, and any resolve
  or fit failure returns null with no ship, no charge and no write.
- Loadouts.Price is the hull design's price plus each slot design's price.
- InventoryPanel: Save Loadout asks for a name (default: the ship's name)
  and saves the capture. Restore Loadout, shown when docked with saved
  loadouts, lists each with its price, debits credits only after
  Materialize succeeds, then docks the ship as before; a failure opens
  "Loadout cannot be built here" with one line per failing slot.

Tests: new LoadoutTests (3): LoadoutRoundTripsThroughAFreshCache,
LoadoutHoldsNoRunOrGalaxyRefs, MissingDesignReports. Each fails with its
rule mutated out. The fixture hull is 3x3, not the spec's 2x2:
HullData.InteriorCells is Shape.Shrink(), so a 2x2 hull has no interior
cell a cargo bay can occupy.

Builds: headless, 14 tests pass, Unity batchmode compile reports no error CS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 22:06:06 +02:00
Meta Vens
95e164adce Land RunSave and the one-cache run and player lifecycle (Cut 10)
Owner: ActionGameManager composes the process's one cache. Its CultCache
getter attaches the catalog, GameData/run.cc and GameData/player.cc on
first access, in either scene, and nothing reopens it.

- RunSave (SavedGame.cs) replaces the SavedGame constructor:
  - Capture builds the live run as plain documents and writes nothing. Zone
    contents are `zone.Contents?.PackZone() ?? zone.PackedContents`, so a
    zone generated earlier but not loaded this session keeps its contents
    and its orbit and body records instead of being erased by the next save.
  - Commit is the only writer of SavedGame and SavedZone: one Commit with
    zone i at `savedzone-{i}`, SavedGame.Zones set to those refs, and every
    stored SavedZone outside that set removed. Orbits and bodies staged by
    zone generation land in the same single-file write.
  - Clear removes every run record in one Commit.
- Run begins: New Game's first statement is RunSave.Clear. Run saves:
  ActionGameManager.SaveRun at wormhole arrival and quit. Run ends: Die
  clears the run, then saves settings.
- Run exists only as the run store's SavedGame global: MainMenu enables
  Continue from it (and logs when the run store holds records but no
  SavedGame), and StartGame reads it once to pick the new-run or resume
  branch.
- PlayerSettings: the getter returns the player store's global and, on
  first launch, commits GetDefaultPlayerSettings; it is the only creator.
  SavePlayerSettings commits the instance.
- InputDisplayLayout reads its InputLayout from the catalog by the layout
  asset's name and throws when the catalog has none.

Tests: new RunSaveTests (5): RepeatedSavesKeepRecordCountConstant,
SaveRemovesZonesNoLongerInTheRun, SaveWritesOnlyTheRunStore,
ResumeThenSaveKeepsUnloadedZoneContents, ClearRemovesEveryRunRecord. Each
fails with its rule mutated out.

Builds: headless, 11 tests pass, Unity batchmode compile reports no error CS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 22:00:27 +02:00
Meta Vens
5c5c3d5e19 Delete the old run, settings, layout and loadout authorities (Cut 10)
The deletion line lands before RunSave, as the spec orders. Deleted, not
demoted:
- PlayerSettings.SavedRun (Key 1 stays unclaimed) and the SavedGame
  constructor that upserted a fresh SavedZone per zone on every save.
- ActionGameManager: the PlayerSettings.msgpack getter, file path and
  writer; the .loadout format (_loadoutPath, Loadouts, SaveLoadout and its
  default-options serializer, the commented loader); OnApplicationQuit and
  SaveState; SaveZone, which had no caller.
- MainMenu's SavedRun Continue branch and both `SavedRun = null` writes.
- InventoryPanel's Save Loadout and Restore Loadout bodies.
- InputDisplayLayout's KeyboardLayouts file reader, ParseJson,
  AssociateInputKeys, SaveLayout and the usings they alone needed.
- AetherDb's Cut 9 importer (Import.cs, legacy-census, import).
- The three-gates note on loading writes; Cut 3 made loading non-writing.
- .gitignore ignores GameData/run.cc and GameData/player.cc instead of
  PlayerSettings.msgpack.

DOES NOT BUILD in Unity: ActionGameManager, MainMenu and InputDisplayLayout
still reference the deleted members until the next commit wires RunSave,
the settings getter and the catalog layout read. Headless builds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 21:51:10 +02:00
Meta Vens
70fbaca1ed Import the legacy catalog into GameData/Aetheria.cc (Cut 9)
AetherDb gains legacy-census and a one-shot import (tools/AetherDb/Import.cs,
deleted by Cut 10). The import rewrites each legacy [tag, payload] record by
the target type's [Key] shape: slot 0 Guid becomes the record key and nil,
CultRecordRef members and DatabaseLink wrappers become D strings, the all-zero
Guid becomes nil, StatModifierData.RequireBehavior keeps the simple type name.
All 180 records land in one Commit; a second import refuses.

InputLayout moves to the catalog (Q9-1 A), with the test fixture seeding it
while the catalog is writable. GameData/*.cc is LFS, GameData/*.lock ignored.
The legacy msgpack and ansi104.json sources are removed.

Imported: SimpleCommodityData 13, CompoundCommodityData 51, GearData 25,
HullData 3, NameFile 12, Faction 12, PersonalityAttribute 3, CargoBayData 4,
DockingBayData 1, WeaponItemData 18, FactionProductData 37, InputLayout 1.
Six refs dangled already in the legacy data and are preserved as authored.

The Key(0) precondition checks only the Guid-slotted tagged types; InputLayout
is imported bare and keeps its Key(0).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 21:11:06 +02:00
Meta Vens
9bdf6ef23f Replace the Database Tools with the Studio and cut Unity over
Cut 8b. The Unity project compiles with 0 errors in 6000.3.24f1 batchmode.

Packages: manifest.json pins org.gamecult.cultlib at cultlib-unity-v1.0.57
and org.gamecult.caching.unity at caching-unity-v1.2.0, next to the 8a
CultMath tag. Aetheria.Shared.Unity references GameCult.CultLib and keeps
noEngineReferences.

The Database Tools (Assets/Scripts/CultCache/Editor) are deleted in this
commit, alongside their five ported drawers in
Assets/Scripts/Editor/CultCacheDrawers.cs, each an
[CultInspectorDrawer] ICultInspectorDrawer:
- InspectableType: popup of the attribute type's [Union] member names,
  writing the string.
- InspectableColor: float3 and float4 through ColorField.
- InspectableAnimationCurve: float4[] and BezierCurve through CurveField.
- InspectableTemperature: Kelvin, Celsius and Fahrenheit fields over one
  value.
- InspectableSchematicShape: size and cell toggles. The old drawer also
  drew the item's schematic texture and tinted hull hardpoints, which need
  the parent document. The tagged Studio hands a drawer only the value, so
  those wait on the Studio exposing the open record.

Unity call sites:
- ActionGameManager opens the catalog through AetheriaStores; Cut 10 adds
  the run and player stores.
- Refs resolve through ItemManager.GetData or Get(ref); orbit, planet and
  belt maps key by CultRecordKey; identity is compared by reference or key.
- The PlayerSettings.msgpack reader and writer, which Cut 10 deletes, pass
  CultDocumentMessagePackSerialization.OptionsFor, since the embedded run
  reaches CultMath values and refs. Keyboard layouts need no resolver and
  keep MessagePack's defaults until Cut 10.
- The input map uses the nested action to binding shape.
- PropertiesPanel reads CultInspectorRangeAttribute.
- NameTools loses its legacy name-file writer.
- Slime compares serialized settings instead of the deleted SHA1 helper.
- FieldDriver drops InspectorHeader.
- .gitignore covers the test project's build output.

Builds: Aetheria.Shared, AetherDb, Aetheria.Shared.Tests (6/6), Unity
batchmode (0 errors).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:15:20 +02:00
Meta Vens
c2026142be Pin AetheriaStores routing with tests; make PlayerSettings readable
Cut 8b. tests/Aetheria.Shared.Tests (xunit, net10.0) builds a temp-directory
catalog through AetheriaStores.Open(catalogWritable: true) per test:
- OpenThenFlushLeavesEveryFileByteIdentical: the catalog, plus run and
  player stores seeded with their globals, a SavedZone and an InputLayout.
- WeaponWrittenThroughGearHandleReloadsAsWeapon
- CatalogRefusesWrites: bytes unchanged too.
- SavedZoneNeverLandsInCatalog: no home without a run store; the snapshot
  schemas show where it landed.
- FactionIsASingletonInstance: through a ref, a ref-keyed map key, and the
  name index.
- MissingCatalogGlobalIsLoud: uses a test-only [CultGlobal] routed to the
  catalog. The game has no catalog globals yet.

The first test found a real defect. PlayerInputSettings.InputActionMap was
keyed by (string, int), and CultLib's untrusted-data MessagePack security
has no hash-resistant comparer for a tuple, so a player store could never
be read back. It is now action name to binding index to path; its two
Unity readers change in the Unity commit.

Each test was checked to fail with the rule it pins removed; see the
report.

Builds: Aetheria.Shared, AetherDb, Aetheria.Shared.Tests (6/6 pass).
Unity does not build yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:12:54 +02:00
Meta Vens
a2b54889b4 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>
2026-09-14 19:00:42 +02:00
Meta Vens
b4c06b50cb Delete the in-tree cache, JsonKnownTypes and vendored MessagePack
Cut 8b, deletes first. The old authorities are removed, not demoted:
- ServerShared/CultCache: CultCache.cs, DatabaseEntry.cs (root [Union],
  DatabaseLink, INamedEntry), RegisterResolver.cs, TypeFormatterResolver.cs,
  JsonConverters.cs, JsonKnownTypes/**.
- Assets/Plugins/MessagePack/** and System.Runtime.CompilerServices.Unsafe.dll
  (org.gamecult.cultlib ships both).
- GalaxyMapLayerData, PlayerData, GlobalSettingsAttribute.
- The inspector attributes CultLib already carries or nothing reads.

Kept and moved: MathFormatters.cs and MathResolver.cs to
ServerShared/Serialization; GetAllChildClasses, SplitCamelCase,
FormatTypeName, MaxBy and MinBy to Extensions.cs; Inspectable,
PreferredInspectorAttribute and the five Aetheria drawer attributes to
ServerShared/Attributes.cs. Aetheria.Shared.csproj drops the vendored
MessagePack include and the Unsafe package; the asmdef drops MessagePack.

Does not build: the data model moves onto CultLib in the next commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 18:42:09 +02:00
Meta Vens
2b38d2dcdd Seed zones from CultMath's PCG hash and pin CultMath 0.2.3
Cut 8a seeded ZoneGenerator's Random from the float bits of CultMath's
hash(float2), the frac(sin) shader hash. On a 100x100 position grid that
gives 1625 distinct values, where Unity.Mathematics' uint hash separated
them. CultMath 0.2.3 adds the PCG integer hashes from Jarzynski and
Olano (JCGT 2020), so the seed now uses pcg3d(Position).x, the IEEE bits
of the float2 position with z held at 0, xored with the zone-name term
as before.

- Packages/manifest.json and packages-lock.json pin
  cultmath-unity-v0.2.3 (CultLib 0f2c1f04).
- Directory.Build.props sets CultLibRevision to the same commit.

This pin also brings in 0.2.2's normalize: a zero vector now gives NaN,
as dxc does and as Unity.Mathematics did before the swap. None of Cut
8a's normalize or LookRotation edits relied on a zero-safe normalize.
They are one-to-one type conversions, and 8a removed no guard. So no
guard is added.

Verified: headless Aetheria.Shared and AetherDb builds with 0 errors.
AetherDb census, factions and hardpoint-fit are byte-identical to the
captures. Unity 6000.3.24f1 batchmode compile gives 0 errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 18:23:55 +02:00
Meta Vens
55c3fc6a49 Delete AetheriaMath in favour of CultMath
AetheriaMath predates the CultMath swap and re-implemented helpers CultMath
now owns. Callers use CultMath directly: decay (Ship, AetherDrive, Entity),
damp (VelocityConversion, FieldTester, FieldDriver), first_order_intercept
(TurretController, Combat, GuidedProjectile), quadratic_bezier (FieldDriver)
and smootherstep (Ship). Parameter order matches in every case, and those
formulas have the same float evaluation order, so behaviour is unchanged.
Galaxy's commented-out segment check names distance_to_segment.

The other overloads had no callers and are dropped without a CultMath
counterpart being needed: the double Damp overloads, FindDistanceToSegment,
GetCatmullRomPosition, GetCubicSplinePosition, Smoothstep(x), and the
Deg2Rad/Rad2Deg constants.

AetherDb census, factions and hardpoint-fit are byte-identical to the
pre-cut captures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 18:02:41 +02:00
Meta Vens
4d89c6d932 Fix the sector map label pivot after the CultMath sign swap
CultMath's sign(float) returns int, as HLSL does, so sign(x)/2+.5f became
integer division and pinned the pivot at 0.5. Scale by .5f instead so the
pivot is 0 or 1 again. The other sign( uses in Assets combine the int with
a float by multiplication or compare it against a float threshold, so their
values are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:58:53 +02:00
Meta Vens
422b6474ff Fix the float4 colour inspector for CultMath
UnityExtensions no longer has Color.ToFloat4, so MathematicsInspector's
float4 colour field converts back with the bridge's ToCultMath. This is
the only Assembly-CSharp-Editor break; Unity 6000.3.24f1 batchmode now
compiles with no error CS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:47:34 +02:00
Meta Vens
2281793154 Convert engine types through CultMath.UnityBridge
CultMath has no implicit conversions to or from Vector*, Quaternion or
Color, so Assembly-CSharp converts explicitly with ToCultMath/ToUnity at
each engine boundary: FieldDriver, GuidedProjectile(Manager),
EntityInstance, ActionGameManager, HullCollider, GridObject, ShipInstance,
ShieldManager, ItemPickup, FieldTester, PlaceUIElementWorldspace,
MapMenuInput, MapRenderer, SectorRenderer and ZoneRenderer. CultMath math
calls that were handed Unity vectors now take converted values;
Vector3.Scale in FieldDriver becomes a float3 product, and ItemPickup's
Vector3.Dot on float3s becomes dot. packages-lock.json records the
cultmath git package at f0605367 and demotes com.unity.mathematics to a
transitive dependency.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:47:33 +02:00
Meta Vens
3dc00e1225 Fix ServerShared for CultMath
CultMath's hash(float2) returns float, not uint, so ZoneGenerator folds its
bits into the zone seed with BitConverter.SingleToInt32Bits. MathResolver's
int2?[] entry used ArrayFormatter<float2?>; it now uses ArrayFormatter<int2?>.

Aetheria.Shared and AetherDb build headless with 0 errors, and AetherDb's
census, factions and hardpoint-fit output is byte-identical to the captures
taken before the swap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:40:31 +02:00
Meta Vens
6140a06a0a Swap Unity.Mathematics usings for CultMath
Every Unity.Mathematics using, alias and qualified name under Assets\Scripts
and tools now names CultMath. The nine `using static ...noise;` lines are
gone: snoise lives on CultMath's math. UnityExtensions loses float4.ToColor
and Color.ToFloat4, which duplicate the bridge's ToColor and ToCultMath; the
float3 colour pair stays because the bridge has none.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:39:12 +02:00
Meta Vens
1bad87fc35 Reference CultMath instead of Unity.Mathematics
Aetheria.Shared.csproj drops the Library\PackageCache math sources and
references CultMath.csproj under the pinned CultLibRoot. The asmdefs drop
Unity.Mathematics; Tests overrides references, so it names CultMath.dll in
precompiledReferences. The manifest drops com.unity.mathematics (Burst and
Collections still resolve it) and adds org.gamecult.cultmath at
cultmath-unity-v0.2.1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:38:54 +02:00
Meta Vens
6bd3e17690 Fix station docking bays, save-safe instance keys, and zero-weight draws
One rule now governs interior space: an empty hardpoint cell is usable, in
both UnoccupiedSpace and ItemFits. ItemFits used to refuse hardpoint cells
that UnoccupiedSpace offered, so stations failed to equip a docking bay.

CraftedItemInstance.Ingredients and Product move from keys 3 and 4 to 9 and
10, past every key the derived instances use. At 3 and 4 an older save reads
nil into a non-nullable Guid and fails to load.

WeightedRandomElements returns an empty array when every weight is zero,
instead of an array of nulls. LoadoutGenerator treats a missing Galaxy as
"every product available", the seam a fixture needs to generate loadouts
without a galaxy. ZoneGenerator skips a story station that fails to generate
and logs it, instead of throwing.

Verified: Aetheria.Shared and tools/AetherDb build headless; Unity 6000.3.24f1
batchmode compile succeeds; AetherDb doctor, station-fit and loadout pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 23:22:36 +02:00
Meta Vens
8d93b937bc Author manufacturer variation as faction products
A design lists role names; a faction product is a manufacturer's branded
build of one, carrying its name, its flavor text, and a pseudo-gaussian
quality distribution per role whose mean is the technology put in and
whose deviation is their quality control. A PerformanceStat may name the
role whose quality it reads, unset reading the item own workmanship as
before, so old saves behave unchanged. Generation selects products rather
than designs, which makes what a faction makes the availability rule.

This is Njordr shape at one dimension: role names, per-role derivation
and per-product distributions carry over when quality widens into
dimensions. No parts exist yet; the product authors the role quality.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 01:12:12 +02:00
Meta Vens
25ecb46fcd Generate stations that respect empty hardpoints and required items
UnoccupiedSpace now counts empty hardpoint cells as free, matching
ItemFits: general gear may use a hardpoint until hardpoint gear claims
it. LoadoutGenerator equips hardpoint gear before the docking bay, so the
bay cannot take the reactor's slot. Hulls, docking bays, controllers,
cargo bays and capacitors are required: if no manufacturer present in the
galaxy and known to the faction makes one, any manufacturer is used and
the database gap is logged, so a galaxy without AU still gets stations.
Manufacturer preference is allegiance / (1 + hops to headquarters); the
distance term previously never applied due to operator precedence, and
would have divided by zero in the headquarters zone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 23:23:19 +02:00
Meta Vens
97a9e7a598 Tolerate unloadable types in ServerShared reflection scans
CultCache discovers DatabaseEntry types by scanning every loaded
assembly. Outside Unity, plugin assemblies that reference UnityEngine
throw ReflectionTypeLoadException and took the whole scan down; keep the
types that do load. Also fix GetAllGenericChildClasses reading and
writing the ChildClasses cache instead of its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 23:14:34 +02:00
Meta Vens
3dcfaf9783 Delete commented-out code blocks
Git keeps the history. SimpleCommodityData records its retired
MessagePack keys so they are not reused against old data.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 22:14:58 +02:00
Meta Vens
cc7f2c8877 Remove dead code, unused plugins, and multiplayer remnants
Delete Economy.Server/Economy.Shared, LiteNetLib, network message types,
the texture-based Gravity duplicate, unreferenced scripts and agent
states, and unused plugins (MSAGL, NaughtyAttributes, AsyncAwaitUtil,
Allegorithmic, Localization samples, MrtTest, ThinkGear, QuickGraph),
plus vendored sample scenes, root zips/logs, and the stray Obsidian
folder. MainMenu logs faulted galaxy-generation tasks directly instead of
AsyncAwaitUtil's WrapErrors. Stop tracking UserSettings and the Wwise
.backup folder; ignore generated .sln/.csproj by pattern.

Game-system test scenes are kept. Playdead stays: VolumeCloudRenderer
derives from its EffectBase.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 22:14:57 +02:00
Meta Vens
e640aad65a Upgrade to Unity 6000.3.24f1
Editor-driven upgrade from 6000.1.10f1: patch-level package bumps,
importer re-serialization of legacy .meta files, and the regenerated
Input System wrapper. Verified by playing from Main Menu into ARPG.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 22:04:28 +02:00
Meta Vens
f568a7d14f Cut RethinkDB
Remove the vendored RethinkDb driver and its Linq provider, the Rethink
backing store, the already-stubbed RethinkConnection, the Rethink test
window, and the Rethink-only Remotion.Linq and Logging.Abstractions DLLs.
The database editor now works only on GameData/AetherDB.msgpack, with a
Save button replacing the dead Connect UI and broken JSON export.
RethinkTable becomes DatabaseCategory, which only groups editor entries.

Economy.Server still references Rethink; it was already unbuildable and
is handled separately.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 21:48:45 +02:00
Meta Vens
7006a6b0dd Restore the stable legacy Unity project 2026-08-10 14:57:50 +01:00
Meta Vens
04c0b77c94 Add entity-owned dynamic fog fields 2026-07-22 02:09:30 +01:00
Meta Vens
004c05d350 Keep entity fields aligned with orbital poses 2026-07-22 01:53:42 +01:00
Meta Vens
72ce713a19 Partition provider assets for demand loading 2026-07-19 21:51:49 +01:00
Meta Vens
5caa48503b Clarify tractor target and simulation authority 2026-07-19 13:14:08 +01:00
Meta Vens
83ce86c75e Restore reticle-owned weapon locking 2026-07-19 07:02:10 +01:00
Meta Vens
1e6deff644 Publish Aetheria hot state on negotiated planes 2026-07-19 03:29:53 +01:00
Meta Vens
583e16f543 Verify flow feature selection in the state smoke 2026-07-19 02:02:52 +01:00
Meta Vens
a655d99bf5 Expose smooth value flow as a daemon render option 2026-07-19 01:52:55 +01:00
Meta Vens
1eca0f50eb Let URP own gravity fog history 2026-07-18 17:16:46 +01:00