Commit graph Aetheria/Assets/Scripts/ServerShared/ShipModCatalog.cs
Author SHA1 Message Date
soul
5584c0d7c8 Merge eureka/aetheria-release-loadout-commit: AetherDb loadout-commit with --boss-of; Loadouts.Commit accepts package designs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	tools/AetherDb/Program.cs
2026-10-23 04:27:12 +02:00
soul
7f1337167d Loadouts.Commit stops refusing package designs (ruling catalog-is-a-package-stack)
The catalog is a stack of packages, the shipped content included, so a Loadout may
name any design the stack holds. The mod-design refusal (from 54917bb3, backed by no
ruling) is deleted, and its test now pins that a package design commits under its key.
The IsModKey comment no longer claims the shipped catalog never references a mod.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 01:42:49 +02:00
soul
d2f4b2c347 A bad package station kind excludes the package; AetherDb opens a catalog with a kindless station hull, names it, and station-kinds hull sets it; the raw opener is gone
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 01:21:04 +02:00
Meta Vens
75df9a8421 RemovePackage: drop the dot-name clause the trailing-dot rule already covers
'.', '..' and '...' all end in a dot; one rule decides, not two.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 23:16:05 +02:00
Meta Vens
b76e212d60 RemovePackage: containment under Mods and RemovedMods, name round-trip, no links; Remove dialog keeps other buttons
Refuses names the filesystem rewrites (dot names, trailing dot or space, device names, stream colon), anything that does not
resolve to a strict child of the mods folder under the same name, and link packages. A fault with no known package names
(all mods), so no Remove button. A removal drops only its own button and the dialog returns to the rest.
Unity compile not yet run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 22:49:44 +02:00
Meta Vens
e04e247820 ShipModCatalog.RemovePackage: the one uninstall moves a mod aside to GameData/RemovedMods
Presence under Mods is the only enablement; removal is a move, never a delete,
and moving a package back reinstalls it. Refuses names that are not a package
directory directly under the mods root. Not yet built or run; verify runs at
the end of the cut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 21:47:38 +02:00
Meta Vens
3f29d803eb Bind refuses a mod Assemble would throw on: meshless map-icon and hull-collider, non-positive shield scale
Compose is the one owner of whether a mod hull is available. Bind now reads
the node scale and matrix in ReadNodeIds and refuses what Assemble refuses.
Not yet built or run; the verify job runs at the end of the cut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 21:37:51 +02:00
Meta Vens
27094e8859 Merge cut vector-rows-record (Soul verdict cut-vector-rows-record.s3)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 01:21:40 +02:00
Meta Vens
27ecd1f9c0 RigChains owns what drives a joint; the judge refuses two actuators on a joint and a reach through a driven joint
RigChains.Actuates/Drives/Actuator are the one definition of actuator kinds; ValidateRig, BindRig and ShipModPlan consume
it, and the hand-derived gun-versus-row check and the chain stop are deleted. A nested actuator is now refused, as ruling
one-joint-one-actuator says. Findings nested-actuators-accepted and actuator-kinds-spelled-in-four-places.
Builds; the tests of the previous batch that pinned nesting are red until the next commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:31:14 +02:00
Meta Vens
943c492bb8 Packages carry their products: ship.cc homes FactionProductData at mod-product:<id>:<maker key>
ShipAuthoringStore.Load/Read return (Hull, Visual, Products); Open homes the third kind;
WriteProduct/DeleteProduct are the only product writers; ValidateProducts and the ship
top-speed rule; compose checks makers, keys and product names; ship-authoring product and
price commands. Unbuilt until the verify job (Python, pin test and docs follow in the next commit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:54:30 +02:00
Meta Vens
b1f4a2aa08 Vectored rows recorded: HardpointData.Row, ThrusterData.Slew, the judge's row rules
Thruster hardpoints state a gimbal arc in FiringArc and join a row by Row (key 7);
the thruster design carries its slew (key 6). The judge refuses a Row on a
non-thruster, rows whose members disagree on Rotation or FiringArc, and a fixed
thruster, a mixed or split row, or a joint too short for the arc on a joint. Bind
treats a thruster emitter's joint as it treats a gun mount's. Not yet verified:
this commit has not been built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:38:29 +02:00
Meta Vens
2be1f8efa2 Merge origin/master into eureka/aetheria-release-mods-in-tree
AetheriaStores.Open keeps both: the registry parameter and the missing-catalog refusal.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-10 13:35:39 +02:00
Meta Vens
1047698568 Missing ship.cc fails the fixture, not the game's compose
The game treats a directory without ship.cc as not a package (TheBootCatalogIsTheShippedOneUntilAModIsInstalled), so da4d8c6c's
Compose exclusion broke that test. Reverts the Compose and ResolveCatalog change and moves the check to the one place that
holds the first-party tree to a stricter rule: TestCatalog.Resolve throws, naming each directory under GameData/Mods that
holds no ship.cc (missing-ship-cc-skipped). The GLB case change and the registry parameters stand.

Builds; not yet run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 07:35:08 +02:00
Meta Vens
da4d8c6c96 Fixture boots the game's catalog under one scoped registry; missing ship.cc is an exclusion; GLB extension is lowercase only
AetheriaStores.Open, ShipModCatalog.Compose and ResolveCatalog take an optional registry (the game passes none and keeps the
process-wide one). TestCatalog.Registry() is the shipped assembly's registry, so the test assembly's TestCatalogGlobal is no
longer a catalog global the real catalog must hold (fixture-fails-on-real-package). TestCatalog.ForRepo(root) is the one
binding Repo uses, pinned by a test over a real catalog plus a package (repo-wiring-unpinned). A package directory without
ship.cc is excluded with a reason, in Compose and so in ResolveCatalog's pre-check (missing-ship-cc-skipped). The loader takes
only a lowercase .glb, matching the case-sensitive LFS rule (lfs-glb-case).

All commits build; not yet run (verification job follows).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:50:18 +02:00
Meta Vens
9e516d06d9 Rig in the visual record: ShipAuthoring.Joints, ShipAnchor.Joint, reach check, Bind and ShipModPlan.Rigs
Deletes the articulation anchor role. Adds ShipJoint (key 5 Joints), ShipAnchor.Joint (key 5),
the judge's joint-shape, mount-on-joint and yaw-reach checks over RigChains, Bind's GLB checks
(ReadNodeIds returns the nearest tagged ancestor), and ShipModPlan.Rigs. Presentation only:
arc stays HardpointData.FiringArc and traverse WeaponData.Tracking (ruling mount-arc-gun-traverse).
Not yet built or run when committed; the verification commit follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 05:23:02 +02:00
Meta Vens
f5743cbad2 Ship packages: a thruster or radiator anchor on a mesh-less GLB node is refused at bind
ReadNodeIds is the one GLB parse and now records, per aetheria.id node, whether it carries a mesh. Bind refuses a
thruster-emitter or radiator-mesh anchor whose node has none, naming the ship and anchor (ruling
thrusters-radiators-are-meshes), so Compose and validate refuse such a package before the catalog sees it. AetherDb
'ship-authoring validate' judges the GLB when it exists (next commit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:07:08 +02:00
Meta Vens
34319b0df1 Ship S2 fixes: RequireDesigns refuses nulls explicitly, and a failing Compose falls back to the shipped catalog
- RequireDesigns names the null (entity, item, zone record, lot, lot ledger) instead of catching NullReferenceException.
- ResolveCatalog reports a Compose-level failure as an exclusion covering all mods and boots the shipped catalog.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 10:06:16 +02:00
Meta Vens
54917bb39d Ship S2 fixes: structural anchors are not mounts, mod presets refused, one bad mod never bricks the boot
- Validate: a hardpoint's anchor role is fixed by its type (thruster-emitter, radiator-mesh, else articulation); ShipModPlan's
  duplicate role check is deleted.
- Loadouts.Commit refuses a preset naming a mod design (mod-hull:/mod-ship:), so the shipped catalog never references a mod.
- Compose quarantines each package: a bad package is excluded and named, a collision excludes every package involved; the
  game boots on the rest (or the shipped catalog) and the main menu lists the exclusions.
- RequireDesigns refuses a save with a null item or lot instead of throwing NullReferenceException.
- Tests: per structural role, muzzle id ties, every slot/zone/lot in RequireDesigns, listing order, exclusion cases.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 08:30:52 +02:00
Meta Vens
460bb85ed0 Ship S2: the game composes and loads mod ships
Boot opens the derived catalog when GameData/Mods holds packages
(ShipModCatalog.ResolveCatalog), preloads each hull's GLB into an assembled
inactive prototype (ShipModShips), and LoadEntity clones it when the hull names
a Visual. ShipModPlan decides which anchor fills which ShipInstance slot; a
ShipModTemplate prefab carries the shared effects. Continue refuses a run that
names a design the catalog no longer holds (RunSave.RequireDesigns).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 06:43:52 +02:00
Meta Vens
f470c1b775 Ship S1 fixes: refuse the retired embedded hull, one body per hull, closed validator gaps
Load reads the raw ship authoring payload and refuses a non-nil retired key 1 (the
pre-S1 embedded hull) naming the old shape, so a stale hull is no longer silently
ignored; ship_cc.py refuses the same file on read and therefore on every save.

A hull naming both a Unity prefab and a visual is refused by one predicate
(ShipAuthoringStore.RequireOneBody), called by Validate and by Compose over every hull
in the derived catalog, shipped ones included; a whitespace-only Prefab is unset. This
replaces the mod-ship-only "cannot name a Unity prefab" check.

The validator also refuses non-finite or negative hardpoint Armor and FiringArc, an
undefined HardpointType or ItemRotation, and an anchor Role outside the eight the
schema documents.

Load wraps MessagePack decode failures with the file path. Python replace_layout
refuses non-boolean layout and hardpoint-footprint cells.

Tests: exact Visual key comparison, HullData.Visual at key 32, a save refreshing
stored_at, plus one case per new refusal. HullData.Visual is now [Inspectable] like
Prefab (Unity editor only; not compiled here).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 06:18:20 +02:00
Meta Vens
1702e0549f Ship S1: one hull record, and a hull names its visual
A mod ship file now holds a HullData at mod-hull:<id> and the ShipAuthoring
visual package at mod-ship:<id>. HullData.Visual (key 32) is the typed ref
between them; ShipAuthoring.Hull (key 1) is retired.

- One joint validator over (hull, visual) is the only semantic check; Load
  owns the file structure (one hull, one visual, deterministic keys).
- Compose copies the mod store's records verbatim, so the derived catalog
  holds one hull per mod ship.
- ship_cc.py edits the hull record's slots directly; the body[HULL_SLOT]
  indirection is gone.
- The package fixture is generated from ShipAuthoringTests.Fixture() (glTF
  scene included) and exported when AETHERIA_SHIP_FIXTURE_DIR is set.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 03:06:43 +02:00
Meta Vens
1987009ba1 Pin Compose, ReadPackage and ReadNodeIds; refuse name collisions (S-Soul F2, F6)
Compose, ReadPackage and ReadNodeIds had no test. ShipModCatalogTests now
covers the derived catalog, the atomic replace of an existing one, every
failure path leaving the previous derived file byte-identical, the unsafe
source/output refusals, package binding (directory name, GLB existence and
extension, anchor node presence), and the GLB reader (each header guard, the
JSON chunk boundary, node index accounting, duplicate ids).

Writing them exposed three defects in Compose, fixed here:

- F6: AetheriaStores.CatalogTypes routes ShipAuthoring to the shipped
  catalog, and CultCache indexes names per concrete type with last writer
  winning while GetByName over a base type throws on a cross-subtype match.
  Compose checked keys only, so a mod hull named like a shipped item, or like
  another mod's hull, silently shadowed it or broke lookups. It now refuses
  a hull name matching any catalog ItemData and a ship ID matching an
  existing ShipAuthoring, naming the ship.
- The backing store writes a .lock file beside every file it opens, so each
  Compose left <output>.<guid>.tmp.lock behind, success or failure, once per
  boot. The working copy now lives in its own directory that is deleted whole.
- Deleted: the duplicate-ID check (ReadPackage already pins ID to directory
  name, so it could not fire) and ReadPackage's containment StartsWith
  (Validate already refuses rooted and `..` model paths on the same Read).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:47:13 +02:00
Meta Vens
24c2964046 Preview typed mod ship visuals and captured LineArt in Unity 2026-09-26 19:10:44 +02:00
Meta Vens
4ba4bbaf2e Compose validated mod ships into a derived catalog 2026-09-26 18:30:02 +02:00