Skip to content
Edit on GitHub

In-world vessels

verifiedAgainst 119c2c3 · verifiedOn 2026-09-10.

VoxelSandbox.Industry turns the headless VesselSolver into a placeable object without taking ownership of chemistry, terrain, player input, or save slots. VesselFormFactory proves a voxel footprint is clear and supported, then constructs the rendered and collidable form. VesselRuntime is the Unity bridge: it bakes optional authored catalogues, wires an atmospheric destination, and forwards Time.deltaTime. The plain-C# VesselDomain owns the fixed 20 Hz accumulator, ScienceVesselRun, gas releases, sticky failure state, and atomic checkpoint. One physical apparatus can therefore survive a reload and present a causal chemistry account without creating a World → Industry back-reference.

Symbol File Responsibility
VesselApparatusDefinition Industry/Vessels/VesselApparatusDefinition.cs:17 Authored crude-apparatus footprint, visual dimensions/colours, and placeholder physical defaults; not mutable vessel state
VesselFormFactory.TryPlace Industry/Vessels/VesselFormFactory.cs:39 Validates a clear footprint plus solid floor through IVoxelBlockWorld, then composes a procedural collider, mesh, runtime, and glow
VesselRuntime Industry/Vessels/VesselRuntime.cs:22 Thin MonoBehaviour: catalogue bake, Unity lifecycle, player-facing charge/heat/save adapters
VesselDomain Industry/Vessels/VesselDomain.cs:81 Plain-C# owner of a run, fixed cadence, environment energy, gas transfer, and failure quarantine
VesselFailureReason Industry/Vessels/VesselDomain.cs:8 One sticky result for overpressure and thermal-limit failure
VesselSaveData Industry/Vessels/VesselSaveData.cs:15 Versioned, JSON-friendly adapter for a complete checkpoint; Industry owns the DTO while World provides generic persistence
VesselThermalGlowRuntime Industry/Vessels/VesselThermalGlowRuntime.cs:15 Cached, one-way blackbody emission presentation from simulated temperature
flowchart TD
  APP["VesselApparatusDefinition\nfootprint + defaults"]
  PLACE["VesselFormFactory\nvalidate then build"]
  WORLD["IVoxelBlockWorld\nair footprint + solid floor"]
  RUNTIME["VesselRuntime\nUnity wiring"]
  DOMAIN["VesselDomain\n20 Hz run owner"]
  SOLVER["ScienceVesselRun\nVesselSolver tick"]
  AIR["AtmosphericDomainRegistry\nnamed gas receiver"]
  SAVE["VesselSaveData\ncheckpoint adapter"]
  SESSION["GameSessionRuntime\ngeneric slot bridge"]
  GLOW["VesselThermalGlowRuntime\npresentation only"]

  APP --> PLACE
  WORLD --> PLACE
  PLACE --> RUNTIME --> DOMAIN --> SOLVER
  DOMAIN --> AIR
  RUNTIME --> SAVE --> SESSION
  DOMAIN --> GLOW

Place, do not voxel-edit. VesselFormFactory.TryPlace walks every cell in the authored footprint and then the floor row below it. An occupied footprint returns FootprintCellOccupied; an air floor returns NoFloorBeneath; either failure creates nothing. Build is reserved for a harness or caller that already proved placement. The factory makes the BoxCollider the authoritative physical obstruction, removes conflicting primitive colliders, assigns the apparatus before initialization, and then adds thermal glow. Apparatus occupies space but never writes terrain.

Unity forwards; the domain decides. VesselRuntime.EnsureInitialized (VesselRuntime.cs:243) creates the domain once and is safe where Awake does not fire. Its Update (:422) only forwards Time.deltaTime; a refused tick logs and disables the component. An invalid optional catalogue produces an empty immutable table/reaction array, leaving a placeable but honestly inert vessel rather than a substitute chemistry model.

Fixed chemistry from arbitrary frame deltas. VesselDomain.TryAdvanceBySeconds (VesselDomain.cs:378) accumulates wall-clock seconds and advances whole 1 / 20-second ticks. Each tick reserves event capacity before moving material, adds optional player/environment energy, calls ScienceVesselRun, and retains accepted pressure. A short frame only accumulates; completed earlier ticks remain committed if a later tick fails. This clock is intentionally independent of Unity’s global fixed timestep.

Release gas to somewhere real. Venting, relief, and rupture require both an AtmosphericDomainRegistry and a named WorldBlockPosition. The domain builds a complete gas transfer, asks the receiver to accept it, then removes exact moles and records the vent. Without a configured destination the release is rejected and gas stays in the vessel. An open vessel vents after an accepted tick and cannot pressurise. A sealed vessel may proportionally relieve to its setpoint; only after that can it rupture above rated pressure. Thermal failure is checked first and applies even while open.

Failures, observations, and rendering. FailureReason is sticky: after Overpressure or ThermalFailure, all later advances are refused. TryBuildLatestAfterAction reconstructs bounded causal evidence, while the runtime supplies observation/narration adapters and UI owns layout. VesselThermalGlowRuntime.Refresh reads temperature, updates only once the change exceeds 0.25 K, and writes renderer property blocks. It is display only, never a heat source.

Save without reversing dependencies. VesselRuntime.TryCaptureSaveRecord creates a VesselDomainCheckpoint; VesselSaveRecord.FromCheckpoint flattens stable run/domain ids, clock, charge including traces, event log, valve/seal/failure state, and active player heat. Restore rebuilds a fresh domain with saved identity and current apparatus parameters, validates the checkpoint, and performs no offline catch-up tick. GameSessionRuntime calls generic SaveGameService.TrySaveVessels<T> / TryLoadVessels<T> (SaveGameService.cs:343), so World need not import an Industry type.

  1. The domain owns simulation. Cadence, charge, failure, and checkpoints remain in VesselDomain; VesselRuntime adapts Unity and authored assets only.
  2. Placement is all-or-nothing. Gameplay calls TryPlace; every footprint and floor cell must validate before any form exists, and placement must not edit voxels.
  3. Every released mole has a named receiver. Receiver acceptance precedes charge mutation. Never add a local discard path for open, relief, or rupture gas.
  4. Failure permanently quarantines a vessel. Preserve sticky FailureReason, refusal of new ticks/heat, and restoration of the same failed state.
  5. A checkpoint restores as one state. Keep joint Chemistry/Industry validation, stable run/domain identities, and the no-offline-catch-up rule.
  6. Crude apparatus defaults are placeholders. Pressure, temperature, heat, and wall-transfer values are tuning inputs until material tiers exist, not sourced physical constants.
  7. Presentation cannot affect chemistry. Glow and after-action narration read observations; they never write temperature, charge, event, or failure state.
  • Chemistry: The chemistry solver supplies immutable inputs and transactional ticks. Industry supplies a world-owned control volume and consequences.
  • Data and Inventory: Catalogues & authoring supplies substance/reaction assets; MaterialBatchCharging converts a carried batch through the baked table all-or-nothing.
  • World and Player: Player owns interaction input and requests placement, charging, and heat. AtmosphericDomainRuntime supplies the scene-owned receiver. World remains ignorant of vessel types.
  • Persistence and UI: Persistence owns atomic slot writes through generic DTO methods. GameSessionRuntime coordinates vessel records; UI renders only the evidence-bounded account.
  • Tests: VesselDomainTests, VesselAtmosphereTransferTests, VesselDomainPersistenceTests, VesselSaveDataTests, VesselFormFactoryTests, VesselAfterActionNarrativeTests, and VesselThermalGlowRuntimeTests in Assets/_Game/Tests/EditMode/Industry/ cover these boundaries.
  • The chemistry solver — the headless transaction the domain drives.
  • Persistence — atomic slot writes and the generic DTO bridge.
  • Assemblies & boundaries — why World never references Industry back.
  • Docs/MASTERPLAN.md §29.2–29.6, §31.4, and §36.4; Docs/CHEMISTRY_FOUNDATIONS.md §28.7.

User-contributed notes

Corrections, clarifications, and practical tips for this page. Anonymous is fine — a name is optional. Basic Markdown works: **bold**, *italic*, `code`, and links.

Notes policy

Notes are lightly filtered for spam and may be edited or removed. Keep them about this page — no support requests, no personal data, nothing you would not publish. Links are limited and marked nofollow.

  1. Loading notes…