@jgengine/assets 0.18.1 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +158 -7
- package/package.json +2 -2
- package/skills/ce-handoff/SKILL.md +1 -1
- package/skills/jgengine/SKILL.md +7 -5
- package/skills/jgengine/api.md +118 -89
- package/skills/jgengine/capabilities.md +28 -0
- package/skills/jgengine/recipes/minimal-game.md +3 -0
- package/skills/jgengine-assets/api.md +33 -33
package/CHANGELOG.md
CHANGED
|
@@ -17,13 +17,6 @@ between (`--json` for structured output).
|
|
|
17
17
|
|
|
18
18
|
## [Unreleased]
|
|
19
19
|
|
|
20
|
-
### Fixed
|
|
21
|
-
|
|
22
|
-
- Separate SDK publication validation from external Games quality checks while retaining the full local and main-branch gates.
|
|
23
|
-
|
|
24
|
-
- Correct foot-IK endpoint recomputation after hip rotation; verify reachable targets and clamping without changing bone lengths.
|
|
25
|
-
- Restore release CI prerequisites and make catalog-loader tests independent of the external Games checkout.
|
|
26
|
-
|
|
27
20
|
<!--
|
|
28
21
|
Every PR that changes `packages/*/src` records its consumer-facing change here, so
|
|
29
22
|
the next release's notes are complete by construction. Add a bullet under the right
|
|
@@ -33,6 +26,164 @@ At publish, rename this heading to the new version and mirror the entries into
|
|
|
33
26
|
`packages/core/src/meta/changelog.ts` (the typed `CHANGELOG` export).
|
|
34
27
|
-->
|
|
35
28
|
|
|
29
|
+
## 0.19.0
|
|
30
|
+
|
|
31
|
+
### Migrate
|
|
32
|
+
|
|
33
|
+
- **Bump lockstep SDK packages to `^0.19.0`:** `@jgengine/{core,rapier,react,ws,node,sql,convex,shell,editor,assets,navbake}`. CLI `jgengine` is `0.16.0`; `@jgengine/github` is `0.6.0`.
|
|
34
|
+
- `environment({ structures: building(...) })` buildings and `city` volume buildings now block the player, walking NPCs, `syncWorldColliders` physics backends and `populateNavGridFromSolids` through the new `ctx.world.solids`. Before, they were render-only. If a game re-authored generated buildings as placed scene objects to make them solid, drop those objects. To keep a set passable, pass `building({ solid: false })` or turn off `solid buildings` on the city volume. A mock `GameContext` needs `world.solids` (`createWorldSolids()` from `@jgengine/core/world/worldSolids`).
|
|
35
|
+
- `createAircraftDynamics` and `AircraftTuning` (`physics/flightDynamics`) are deprecated. They command rotation from rates with pitch clamped, so aircraft can't loop, and mass and stall are fixed. Move to `createRigidAircraft` (`@jgengine/core/physics/aircraftDynamics`):
|
|
36
|
+
- `mass` → `massKg`, plus an `inertia` tensor (roughly `m·(span/4)²` for roll and `m·(length/4)²` for pitch and yaw).
|
|
37
|
+
- `fixedWing`: `lift`/`stallSpeed` → left and right wing `surfaces` (area, `liftSlope`, `stallAngle`, trim `incidence`), a tailplane and a fin; `controls.pitch/roll/yaw` → `control` authority on those surfaces plus `controls.<channel>.rate`; `maxThrust` → `engine.maxThrust`; `drag`/`maxSpeed` → `dragArea` and surface `cd0` (top speed is where thrust meets drag); `sideDrag` → the fin.
|
|
38
|
+
- `rotorcraft`: `hoverThrust` → `rotor.maxThrust`, `rotorResponse` → `rotor.spoolRate`, `groundEffectHeight` → `rotor.radius`; add `rotor.torque` and a `rotor.tail`, and pass `input.collective`.
|
|
39
|
+
- `vtol` has no one-block equivalent: combine `rotor`, `surfaces` and `engine`, or keep the deprecated model for now.
|
|
40
|
+
- `controls.stability` → `assists.autoLevel` or `assists.sas`; `afterburner` → `modifiers.thrustScale`; `airbrake` → `modifiers.dragScale`; `groundClearance` → `gear.height`.
|
|
41
|
+
- Euler `rotation` → step `heading`/`pitch`/`bank` and `orientation`; spawn with `aircraftAttitudeQuaternion(heading, pitch, bank)`.
|
|
42
|
+
- Tune against `measureFlight` with the `jgengine-world` recipe `flight-feel.md`.
|
|
43
|
+
- `KinematicVehicleTuning.chassis` and `.steering` are deprecated. Their heading is still commanded from steer, so a held steer at speed spins the car. For handling that matters, move to `createVehicleDynamics` (`@jgengine/core/physics/vehicleDynamics`). Map `massKg`/`engineForce`/`brakeForce`/`tireGrip`/`comHeight`/`trackWidth` to `massKg`, `powertrain: { kind: "direct", maxForce, maxPower }` or a gearbox, `brakeForce`, `front.peakGrip`, `comHeight` and `trackWidth`. Map `steering.wheelbase`/`maxAngle`/`highSpeedAngle`/`highSpeedAt` to the same `steering` fields plus a rack `rate`. Then tune against `measureHandling` with the `jgengine-world` recipe `vehicle-feel.md`. Plain `kinematicVehicle`, without those blocks, stays for karts, top-down and bumper cars.
|
|
44
|
+
- The default third-person orbit camera sits lower and closer (`initialDistance` 9 → 7, `initialHeight` 5.5 → 2.4), so the horizon sits near the top third and the sky is in frame. To keep the old framing, set `camera: { initialDistance: 9, initialHeight: 5.5 }` in `defineGame`.
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- Split-screen viewports (#1686). `defineGame({ viewports })` draws one scissored viewport per joined local seat (`splitViewports`, `resolveViewports`, `viewportPixels` in `@jgengine/core/game/viewports`); extra seats follow their entity with a chase or top-down camera and walk relative to it. `ViewportHuds` (`@jgengine/shell/camera/Viewports`) renders a HUD root per seat. `bun run drive` gains `--param key=value`; `?gamepad=N` synthetic pads now hold Start. Dev demo: `couch`.
|
|
49
|
+
- `createPerception` takes `maxStimuli` (default 256; the oldest are dropped) and adds `forget(observerId, targetId?)`. Stimuli insert in time order instead of re-sorting on every push.
|
|
50
|
+
- Decision graphs (`@jgengine/core/ai/decisionGraph`) gain `invert`, `cooldown`, `wait` and `random` nodes and `memory` selectors/sequences that resume at their running child. `createDecisionGraphRuntime(graph, actions, { onAbort, rng })` calls `onAbort` when a branch pre-empts a running action, and `random` draws from the injected `rng` only. `running()` names the running action; snapshots now carry timers, memory and cooldowns (older `{ runningPath }` saves still restore).
|
|
51
|
+
- Decision graph behaviors keep a persistent per-entity blackboard: seed it with `blackboard`, write facts from perception or game systems through `behaviorControl(ctx).blackboard(id)`, and it round-trips through `serialize`/`restore`. `thinkInterval` ticks the graph every N seconds, staggered across siblings, with the elapsed time as `dt`. `registerBehaviorActions(id, actions, { onAbort })`; `random` nodes draw from `ctx.rng`.
|
|
52
|
+
- Editor Animation workspace (#1683). The rail button opens the bottom dock's new **Graph** mode for the selected placement, showing layers, states and transitions.
|
|
53
|
+
- A scrub slider replays the graph runtime; parameter sliders and trigger buttons record presses on the timeline.
|
|
54
|
+
- The rig at the camera focus is posed from the runtime's output.
|
|
55
|
+
- Crossfade edits and **Store graph** save the graph to `marker.meta.animation.graph` as undoable edits.
|
|
56
|
+
- Supporting APIs: `parseAnimGraph` (`@jgengine/core/anim/animGraph`) validates stored graphs, `animGraphFromConfig` (`@jgengine/core/anim/locomotionGraph`) resolves the graph a config plays, and `createGraphPose` (`@jgengine/shell/render/useModelAnimation`) poses a rig from graph output.
|
|
57
|
+
- `npx jgengine recipe` adds `world-drops`, `click-target`, `ability-bar`, `hitscan-weapon`, `rolled-gear` and `enter-vehicle`, each type-checked against the SDK.
|
|
58
|
+
- `SkinnedInstances` (`@jgengine/shell/render/SkinnedInstances`) renders hundreds of animated copies of one rig, each with its own clip, time offset and speed, in one instanced draw per material. Clips are baked once to a bone-matrix texture and sampled on the GPU. Shadows follow the pose. The layout and frame lookup are pure: `planBoneTexture` and `sampleBoneTexture` (`@jgengine/core/anim/boneTexture`). Dev runner demo: `bun run drive skinned-crowd`, with `skinned-crowd-mixers` as the one-mixer-per-character baseline (#1683).
|
|
59
|
+
- Continuous haptics (#1769). `ctx.input.haptics(userId)` returns named rumble channels (`createHapticChannels`, `@jgengine/core/input/haptics`): `set` a level per tick, `pulse` a fading hit, mixed by priority so an impact ducks the engine hum. The shell drives each local seat's pad with refreshed short effects and resets it when silent; channels are local only. The `handling` demo feeds engine, road and impact channels.
|
|
60
|
+
- Local seats for couch co-op (#1686). `defineGame({ localPlayers: { maxSlots, claimPrimary? } })` and `localPlayers(ctx)` (`@jgengine/core/runtime/localPlayers`): pads hot-join their own seat on a button press, the shell spawns each through `loop.onNewPlayer(ctx, { userId, isNew: true })`, routes that pad to the seat's own `InputSnapshot` (`local(slotId).input`), and moves its entity with the walk controller. The seat table has `snapshot`/`restore`/`retune`. `?gamepad=N` injects N synthetic pads for headless checks.
|
|
61
|
+
- Action contexts rebind live input (#1769). `ActionContextStack` gains `subscribe`, `version`, `ids` and `activeAxes()`; a context may carry `axes` and serializable `shaping`. The shell re-binds keyboard, touch and gamepad when a context is pushed or popped, without remounting: keys held across the swap keep counting toward their new action, and context-only actions reach `ctx.input`. `ActionStateTracker` gains `rebind` and `actions`. The `handling` demo drives through a `driving` context (F parks it), with pad codes on every action. Recipe: `jgengine-world/recipes/controls.md`.
|
|
62
|
+
- `createNavMeshQuery` (`@jgengine/core/nav/navMesh`) caches a nav mesh's adjacency and a uniform polygon grid, then answers `findPath`, `findPolygon`, `closestPoint` and `raycast` without full-mesh scans. Paths use a binary-heap A* priced by `areaCosts` per `NavMeshData.areas` id (non-finite blocks an area), stop at `maxNodes` with a `partial` route, and are straightened by a portal funnel. `retune`/`snapshot`/`restore` cover the costs, so a door or hazard can close an area during play. `NavMeshLink` gains optional `start`/`end` landing points.
|
|
63
|
+
- `createWeaponFeedbackSignals` (`@jgengine/core/combat/weaponFeedback`) turns `WeaponHandling.fire()` shots, their recoil kick and impacts into one-tick pulses (`shot`, `kick`, `impact`) plus the frame's `recoil`, `cameraKick`, `spread` and `ads`, keyed for `createFeedbackMixer` routes to camera shake, audio and rumble. It has `snapshot`/`restore`/`reset`.
|
|
64
|
+
- `measureWeapon` now reports `spreadByShot` and `shotsToKill`, fires in a given `stance` (e.g. ADS), and computes time-to-kill at a `range`. Each projectile (`pellets` per shot) counts by the share of its spread cone the `targetRadius` covers, scaled by the game's `damageAt(range)` falloff. The recipe is `jgengine-combat` `weapon-feel.md`.
|
|
65
|
+
- `defineGame({ gamepad: { deadzone, curve, triggerDeadzone } })` sets pad feel for the shell's gamepad poll (#1769). Triggers now go through `triggerDeadzone` and the same curve as the sticks, and a trigger resting inside its deadzone no longer holds its action. `resolveGamepadFrame` takes a browser `Gamepad` directly and an optional reusable `out` frame, and the shell poll no longer allocates per frame. `gamepadFeelOptions` resolves the config with the old defaults (axial `0.12`/`0.95`, linear).
|
|
66
|
+
- `measureFlight` (`@jgengine/core/physics/handlingProbe`) flies a `createRigidAircraft` through deterministic autopilot scenarios. It reports roll rate (deg/s), sustained turn rate, stall speed, climb rate, time to 90% throttle response and hands-off hover drift, so a flight feel target can be a test.
|
|
67
|
+
- `RigidAircraftTuning.assists`: flight assists on the same actuators as the pilot, so full stick stays the pilot's.
|
|
68
|
+
- `sas` per axis (`0..1`) damps rates and holds attitude or heading hands-off with a learned trim. On a helicopter it holds heading against rotor torque.
|
|
69
|
+
- `autoLevel`, `maxAngleOfAttack` and `maxG` limiters, and `hoverHold` for helicopters.
|
|
70
|
+
- A `policy(context, command)` callback has the last word.
|
|
71
|
+
- The step adds `command` and `limited`. `RigidAircraftState` gains the hold, trim and drift integrals.
|
|
72
|
+
- T toggles assists in `bun run drive flight` and `flight-heli`.
|
|
73
|
+
- `RigidAircraftTuning.motor` makes `createRigidAircraft` a rocket.
|
|
74
|
+
- Thrust follows `thrustCurve` over burn time. `propellantKg` leaves at `massFlow`, so mass and inertia fall and acceleration rises through the burn. The nozzle `gimbal` steers on the pitch and yaw inputs.
|
|
75
|
+
- To stage, `retune` to the next stage: a new `motor` loads its propellant.
|
|
76
|
+
- The step reports `massKg` and `motor` telemetry. `RigidAircraftState` gains `burnTime` and `propellantKg`.
|
|
77
|
+
- Dev runner demo: `bun run drive flight-rocket`.
|
|
78
|
+
- Jump shape on `movement.feel`: `jumpCutFactor` (releasing jump early cuts the rise, so a tap hops), `apexGravityScale`/`apexSpeed` (hang at the peak), `fallGravityScale` (drop faster than you rose) and `landingRecoveryMs`/`landingSpeedScale` (a short slowdown and jump lockout after landing). All default to no change. `measureMovement` adds `tapJumpHeight`. Dev runner demos `walk-floaty` and `walk-weighty` differ only in these fields (#1772).
|
|
79
|
+
- `RigidAircraftTuning.rotor` turns `createRigidAircraft` into a helicopter: `input.collective` sets thrust, throttle spools the rotor, cyclic tilts the disc, and rotor drag torque yaws the body unless the `tail` rotor (pedal) cancels it. Thrust adds translational lift, drops while climbing through the disc, and grows in ground effect. The step reports `rotor` speed, thrust, torque, ground effect and translational lift. Dev runner demo: `bun run drive flight-heli`.
|
|
80
|
+
- `snapshot()`/`restore(next)` on `createGlideModel`, `createLodScheduler`, `createSimulationCuller` and `createSpatialIndex`, and `restore(state)` on `createGrappleSwing`, so gliding, rope swings, LOD throttling and the spatial hash save, load and replay bit-exactly (#1775).
|
|
81
|
+
- `snapshot()`/`restore(next)` on `createThreatTable`, `createAssistNetwork`, `createReputationLedger`, `createConcealmentSensor`, `createFreezeMonitor` and `createRecordingBuffer`, so AI aggro, faction standing and sensor dwell save, load and replay bit-exactly (#1775).
|
|
82
|
+
- Engine audio depth (#1770):
|
|
83
|
+
- `ctx.game.audio.setLoop` takes `lowpass` and `highpass` cutoffs in Hz, ramped like rate and gain.
|
|
84
|
+
- `SoundDef.doppler` pitches a retained loop by listener and emitter velocity; pass the emitter's with `setLoop({ velocity })`. The shell tracks the listener's from the camera. `dopplerRate` (`@jgengine/core/audio/doppler`) is the pure formula.
|
|
85
|
+
- `createEngineLayers` (`@jgengine/core/audio/engineLayers`): N rpm-keyed engine samples crossfaded at equal power, with on-load and off-load sets picked by smoothed `engineLoad`. `play(ctx.game.audio, ...)` drives one retained loop per layer. It has `snapshot`/`restore`/`retune`.
|
|
86
|
+
- `snapshot()`/`restore(next)` on `createFootprintGrid`, `createWallDrawTool` and `createTerraformBrush`, so build-grid claims, wall drafts and brush settings save, load and replay bit-exactly (#1775).
|
|
87
|
+
- `createInputBuffer` (`@jgengine/core/input/inputBuffer`): buffered presses, coyote time, hold duration and double tap, with `snapshot`/`restore`. The walk controller uses it for `movement.feel.jumpBufferMs` (a jump pressed just before landing fires on landing) and `movement.feel.coyoteMs` (a jump just after walking off a ledge still fires). Both default to 0, so walking is unchanged until a game sets them (#1686, #1772).
|
|
88
|
+
- `snapshot()`/`restore(next)` on `createLootRegistry`, `createToastQueue`, `createVfxInstanceStore`, `createRoundState` and `createLookChannel`, and `restore(state)` on `createDragCapture`, so loot tables, toasts, retained VFX, match rounds and look/drag input save, load and replay bit-exactly. `RoundSnapshot` gains `pendingWinner` (#1775).
|
|
89
|
+
- `measureMovement` (`@jgengine/core/movement/movementProbe`) reports a walking character's feel from the same `movement.feel` and `physics` fields a game sets: time to top speed, stop distance, turn-around time, jump height, apex and air time, and air-control reach. Deterministic, for tests (#1772).
|
|
90
|
+
- Chase camera depth (`camera.chase`, #1770):
|
|
91
|
+
- `fov.response` eases the speed FOV, and speed now comes from the followed entity's sim `velocity` when it publishes one, so FOV no longer jitters when render and sim rates differ.
|
|
92
|
+
- `distanceBySpeed`, `pitchFollow` (boom follows body pitch on slopes), `lookBackAction` (hold to look behind), and `collision` (the boom stops short of objects, terrain and walls via `ctx.scene.raycast`; on by default).
|
|
93
|
+
- `ctx.camera.kickFov(degrees)` queues a decaying FOV punch, tuned by `chase.fovKick`.
|
|
94
|
+
- `ChaseCameraTuning` accepts `view` and the new fields; `nextChaseView` (`@jgengine/core/runtime/cameraDirector`) cycles chase, hood and cockpit.
|
|
95
|
+
- `createRigidAircraft` (`@jgengine/core/physics/aircraftDynamics`): a quaternion rigid-body aircraft with an inertia tensor, lifting surfaces (area, lift slope, stall, post-stall lift, drag polar) placed on the body, rate-limited control surfaces, an engine with spool and optional gear. Loops, rolls and stalls come from the surfaces. Same contract as `vehicleDynamics`: fixed substeps, `snapshot`/`restore`/`retune`, per-tick modifiers, and telemetry for AoA, sideslip, g-load, airspeed and stall. Dev runner demo: `bun run drive flight`.
|
|
96
|
+
- Graphics settings a player can feel: an Ultra tier, a frame-rate limit (V-Sync or 30/60/120/144 cap), texture filtering (anisotropy, now applied to every loaded texture), and an FPS counter. `GraphicsProfile` gains `anisotropy`; a game that overrides tiers with `defineGame({ graphics })` can set it per tier.
|
|
97
|
+
- `postProcessing.stylize` (`outline`, `bands`, `pixelSize`) and two new looks, `look: "comic"` (ink outlines + cel bands, Borderlands-style) and `look: "retro"` (pixelated, posterized). Same models, different art style.
|
|
98
|
+
- `PhysicsBackend.applyForce(handle, force, point?)` and `applyTorque(handle, torque)` act over the next step, and `BodyDesc.linearDamping`/`angularDamping` set damping per body. Both are implemented on `PhysicsWorld` (translation only) and Rapier, and covered by the conformance suite. `createVehicleBackendLink` (`@jgengine/core/physics/vehicleBackendLink`) collides a vehicle sim with a backend world: a kinematic chassis shoves props, `clampMove` stops the car at walls and slides it along them, and `lastHit()` reports the impact.
|
|
99
|
+
- `VehicleAssistTuning.antiWheelie` and `.antiStoppie` (`0..1`) cap drive and braking below the pitch-over limit (`g·b/h` forward, `g·a/h` braking). With `suspension` they also back off as the front or rear goes light. They report through `tractionLimited` and `absActive`.
|
|
100
|
+
- Motorcycles on `createVehicleDynamics`: a `VehicleDynamicsTuning.lean` block (`maxLean`, `leanRate`, `countersteer`, `directSteerBelow`).
|
|
101
|
+
- Steer asks for a lean, capped at what grip can hold. The bar steers the balanced turn that lean needs, with a rider trim for tire slip and a countersteer tip-in.
|
|
102
|
+
- The bar steers directly at walking pace, and there is no lateral load transfer.
|
|
103
|
+
- The step adds `lean` (balance lean, `bodyRoll = -lean`) and `wheelie`/`stoppie` for all vehicles.
|
|
104
|
+
- `measureLean` (`physics/handlingProbe`) reports steady lean, time to lean and counter-lean.
|
|
105
|
+
- Dev runner demo: `bun run drive handling-bike`.
|
|
106
|
+
- `measureCourse` (`@jgengine/core/physics/handlingProbe`): understeer gradient (deg/g), skidpad g at a set radius, and the fastest clean slalom, from simple deterministic drivers. `measureHandling` adds `handbrakeRecoverySeconds`.
|
|
107
|
+
- `createWeaponHandling` (`@jgengine/core/combat/weaponHandling`): weapon feel as serializable state.
|
|
108
|
+
- Recoil: a per-shot `pattern` or flat `pitch`, a random cone from an injected `random`, a `cameraShare` split between view punch and aim, `recoverRate`/`recoverDelay`, and `adsScale`.
|
|
109
|
+
- Spread: `base`, `perShot` bloom, `max`, recovery, and stance/ADS multipliers.
|
|
110
|
+
- ADS time, plus `snapshot`/`restore`/`retune`/`reset`.
|
|
111
|
+
- `measureWeapon` reports first and tenth shot spread, burst climb, reset time, ADS time and time-to-kill.
|
|
112
|
+
- `createWeaponPresentation` (`@jgengine/core/combat/weaponPresentation`): per-weapon viewmodel hip and ADS offsets, viewmodel FOV, ADS zoom, sway from look speed, bob from the movement probe, and recoil kick, from a `createWeaponHandling` frame. `camera.weapon` hands that frame to the rigs: first person poses its viewmodel and adds the recoil to the look, and `shoulder` takes its ADS blend and recoil from the same frame. Dev demos: `weapon-handling`, `weapon-handling-shoulder`.
|
|
113
|
+
- `createFeedbackMixer` (`@jgengine/core/vfx/feedbackMixer`): declares how sim telemetry drives presentation. Routes map a signal through a piecewise `curve` with `attack`/`release` smoothing onto a named target, targets combine by `sum` or `max` over a `base`, and threshold `events` with cooldowns fire one-shots. No allocation per update, with `snapshot`/`restore`/`retune`/`reset`. `sampleFeedbackCurve` exposes the curve.
|
|
114
|
+
- `snapshotPlayerMovement(ctx, userId)` / `restorePlayerMovement(ctx, userId, snapshot)` (`@jgengine/core/movement/playerMovement`): per-player walk state (heading, velocities, jump latch, capsule controller) as a plain JSON copy, so walking characters predict and roll back like vehicles. A snapshot restored before the capsule exists is applied when it is created.
|
|
115
|
+
- `PlayerMovementConfig.feel` (`MovementFeelConfig`): `groundAcceleration`, `airAcceleration`, `groundFriction` (response rates, 1/s), `runMultiplier` and `crouchMultiplier` for the built-in walk controller and the voxel controller. They were hard-coded before, so every game walked the same. Omitted fields keep today's defaults.
|
|
116
|
+
- `createAxisShaper` (`@jgengine/core/input/axisShaper`): per-axis response shaping between `ctx.input.axis(...)` and a sim. Keys and sticks get separate profiles (deadzone, saturation, curve, rise/fall/return rates), plus an optional `scale(signal)` callback for speed-sensitive steering, with `snapshot`/`restore`/`retune`. `analogAxes(bindings, ctx.input.analog())` picks the profile per frame, and `shapeAxisValue` exposes the deadzone and curve math. The `handling` dev demo uses it for keyboard steering that eases in and self-centres.
|
|
117
|
+
- `VehicleDynamicsTuning.jump` (`speed`, `count`, `window`) with `car.jump()`, and `VehicleDynamicsTuning.air` (`pitchAccel`/`yawAccel`/`rollAccel`, `damping`, `maxRate`) with `modifiers.air` for air control, plus `applyAngularImpulse` for dodges and flips. The body angles use the true geometry of the tilt rather than a small-angle guess. `measureAir` (`physics/handlingProbe`) reports apex height and time, double-jump height and air rotation rates. Held brakes and the handbrake now grip statically at rest, so a parked car stays put on a slope.
|
|
118
|
+
- `VehicleDynamicsTuning.suspension` (`springRate`, `damperRate`, `reboundRate`, `travel`, `rideHeight`, `antiRoll`) and a `groundHeight` option on `createVehicleDynamics`. The car rides four sprung corners over terrain: heave, pitch and roll come from real springs, axle loads come from spring forces, slopes pull downhill, and the car can leave the ground and land. The step adds `airborne`, `landingSpeed`, `wheelLoads` and a terrain-relative `airOffset`, and `applyImpulse` takes a vertical `dvy`. `measureRide` (`physics/handlingProbe`) reports settle time, heave overshoot, brake dive, roll gradient and landing bounces. Without the block, behaviour is unchanged.
|
|
119
|
+
- `createKinematicVehicle` and `createAircraftDynamics` gain `snapshot()`/`restore()` (`KinematicVehicleState`, `AircraftDynamicsState`) for prediction, rollback and replay. `createAircraftDynamics` also gains `retune()`. The stateful-primitive ratchet no longer misses factories with a default parameter like `options = {}`, so 20 more handles without state-out are now tracked in its baseline.
|
|
120
|
+
- Boat sim `createBoatDynamics` (`@jgengine/core/physics/boatDynamics`) on the same tick/snapshot/restore/retune contract as the car. Drag climbs toward hull speed and a planing hull sheds it; the keel resists sliding sideways; a `rudder` needs water flowing past it (speed or prop wash) while an `outboard` swings its own thrust and turns at rest. `waterHeight(x, z, t)` for waves and `current(x, z)` for drift. `measureHandling` and `tickDrivableVehicle` take it as-is.
|
|
121
|
+
- Force-based car sim `createVehicleDynamics` (`@jgengine/core/physics/vehicleDynamics`): a two-axle model where yaw comes from tire forces, so understeer, oversteer, lift-off tuck, power slides, trail-braking and handbrake turns come out of physical knobs (mass, weight split, CoM height, per-axle Pacejka-style tires, roll-stiffness split, drive split, gearbox or direct drive, aero, ABS/TC/stability, caster self-align). Fixed substeps, `snapshot`/`restore`, `retune`, `thrust` modifier. `measureHandling` (`@jgengine/core/physics/handlingProbe`) drives any vehicle sim through fixed scenarios and returns 0–100, top speed, braking distance, lateral g, turn-in latency, sideslip, spin and lift-off numbers a test can assert. `tickDrivableVehicle` now accepts either sim. `drive --key` takes chords (`KeyW+KeyD:2000`). Dev runner demo: `bun run drive handling`.
|
|
122
|
+
- `jgengine create --2d` scaffolds a board game with no 3D scene, editor document, or models: a `board` world, `presentation: "hud"`, a store slot holding a `CellGrid`, arrow/WASD/Space bound to commands with held-key repeat, and a GameUI that draws the grid. `--3d` names the default. 3D-only flags (`--player`, `--ground`, `--scene`, `--from-scene`, `--shape`, `--no-world`, `--no-editor`) are refused with `--2d` instead of ignored.
|
|
123
|
+
- `@jgengine/core/puzzle/cellGrid` is documented public API (`cell-grid` capability): an immutable 2D board with `withCell`/`withCells`, `fullRows`/`clearRows`, `collapseColumns`, and `findRuns`. It was importable but tagged internal, so agents never found it.
|
|
124
|
+
- HUD-only games (`presentation: "hud"`) set `data-jg-capture="ready"` after their first painted frame unless a capture host owns the flag, so `shoot` and `drive` no longer time out waiting for a `<canvas>` that never exists.
|
|
125
|
+
- The built-in Graphics settings tab now exposes render scale plus ambient occlusion, bloom, depth-of-field, and SMAA toggles alongside the quality tier; picking a tier re-applies its defaults. `@jgengine/core/settings/graphicsSettings` (`readGraphicsSettings`, `applyGraphicsQuality`, `GRAPHICS_POST_STAGES`) resolves the stored choices onto the `GraphicsProfile` the shell renders with, and `SETTING_IDS` gains `graphics.renderScale` and `graphics.post.*`. Every game gets the rows in its existing settings menu with no wiring (#1688).
|
|
126
|
+
|
|
127
|
+
- Model triangle budget: the shared GLB loader counts each model's triangles as it lands and warns once per URL when one exceeds `DEFAULT_MODEL_TRIANGLE_BUDGET` (100k; retune with `setModelTriangleBudget`). `heavyModels()` lists the offenders and `debug_snapshot` reports them under `probes.heavyModels`, so a heavy prop shows up in evidence instead of only as a slow frame.
|
|
128
|
+
- `bun run drive <game> --state <name>` boots into a `capture.states` entry the same way `shoot --state` does, so a staged scene can be measured with `--rpc debug_snapshot` or probed instead of clicked together by hand.
|
|
129
|
+
- Building palettes take textured surfaces: a `BuildingPalette` part (and `BuildingKitPart.material`) may be `{ color?, maps?, repeat?, roughness?, metalness? }` with `maps` straight from `buildMaterialCatalog(...).resolve(id)!.maps`, not only a hex colour. Generated facade boxes and kit models tile the PBR maps per slot; unbound kinds keep their flat colour. `buildingSurfaceColor` / `resolveBuildingSurface` read either form.
|
|
130
|
+
- `CommandDef.parse` and `defineCommand` (`@jgengine/core/runtime/commandRunner`). `parse` turns the untrusted wire value into typed input once. The runner refuses a `null` result with `MALFORMED_COMMAND_INPUT_REASON` before `scope`, `validate` or `apply` run, so those stages only see parsed input.
|
|
131
|
+
- Bounded input readers in `@jgengine/core/runtime/commandInput`: `readInputNumber` (always finite, optional range and integer), `readInputString`, `readInputOneOf`, `readInputPoint2`, `readInputPoint3`, `readInputArray` and `isInputRecord`.
|
|
132
|
+
- `helpers.ensureJoined(ctx, { gameId, userId, serverId? })` (`@jgengine/convex/server`) joins a trusted actor through the `joinServer` path and returns its outcome, so host mutations can run commands for users with no live session. It is idempotent and pins no client session.
|
|
133
|
+
- `createTransportSessionId()` (`@jgengine/core/runtime/transport`) makes a fresh id for one client session's join/leave pair.
|
|
134
|
+
- `CommandDef.access` (`@jgengine/core/runtime/commandRunner`). `"server"` marks a command whose input carries host-decided facts. The public Convex `runCommand` mutation and WebSocket client messages refuse it with `SERVER_ONLY_COMMAND_REASON`, while `helpers.runCommand` and `host.runCommand({ trusted: true })` still run it. `GameRuntime.commandAccess(name)` reports it.
|
|
135
|
+
|
|
136
|
+
### Changed
|
|
137
|
+
|
|
138
|
+
- Graph playback no longer resets the rig's first bone to its bind position every frame. On rigs whose first bone is the hips, that flattened the walk bob in every clip. The root bone is pinned only while a `rootMotion` state is current (the new `AnimGraphOutput.rootMotion` flag), and only horizontally. Root travel now moves the entity along its facing and at the model's scale instead of in rig space, and its vertical part stays in the clip. The shell helper is `takeRootMotion` (#1683).
|
|
139
|
+
- Perception judges each sound or damage stimulus once per observer, from where the observer stood when it first observed after the stimulus. Before, every `observe` re-scored past sounds from the observer's current position, so a guard walking toward an old noise grew more certain of it. `PerceptionSnapshot` gains `heard` and `nextSeq`; older saves re-judge their stimuli once.
|
|
140
|
+
- Perception `loudness` now scales how far a sound carries (`hearingRange × loudness`) instead of only its confidence, so a gunshot is heard past normal hearing range and a footstep only close by. Loudness `1` behaves as before.
|
|
141
|
+
- `npx jgengine find` now surfaces seams that had no capability row: `vehicle-seats`, `drivable-vehicle`, `kinematic-vehicle`, `affix-roller`, `hitscan-shot`, `loot-beams`, `world-item-pickup`, `on-death-drops`, `pointer-commands`, `current-target`, `fire-input`, `global-cooldown`. `PointerConfig.moveCommand` JSDoc now says it receives the clicked entity (click-to-target).
|
|
142
|
+
- `npx jgengine find` matches whole stemmed words (`aim` no longer hits `claim`), drops stopwords, tries multi-word queries joined (`pick up` → `pickup`), returns the closest partial matches instead of "no match", and searches CLI recipes and skill recipe docs.
|
|
143
|
+
- `raycastNav` and `NavMeshQuery.raycast` step into the polygon a segment enters when it starts on or crosses a shared vertex, so a funnel path's corner-to-corner legs raycast clear.
|
|
144
|
+
- `bakeNavMesh` (`@jgengine/navbake`) now voxelizes geometry with recast: the walkable surface is eroded by `agentRadius`, cut under obstacles lower than `agentHeight`, and split at `maxSlope` and `maxClimb`, so a wall standing on a floor leaves a hole the path goes around. Call `await initNavBake()` once before baking; `navBakeReady()` reports it. Walkable triangles must wind counter-clockwise seen from above. Optional `cellSize`/`cellHeight` trade precision for speed. The editor warms the baker when a host starts.
|
|
145
|
+
- `findPath`, `closestPoint` and `raycastNav` on `NavMeshData` share the cached query: paths bend only at portal corners, stacked floors resolve by height, and raycasts walk polygon edges instead of sampling.
|
|
146
|
+
- Mouse look now asks for raw, unaccelerated mouse deltas (`requestPointerLock({ unadjustedMovement: true })`) in `createMouseLookTracker`, the first-person camera and the shoulder rig, and falls back to a plain lock where that is unsupported. The helper is `requestRawPointerLock` (`@jgengine/shell/input/pointerLock`).
|
|
147
|
+
- Foot IK (`ModelConfig.ik`) keeps each clip's swing lift and moves feet by their ground's height relative to the model origin, instead of pinning every ankle to the ground. It lowers the pelvis so the lower leg can reach, keeps soles above the ground where a clip dips below it, bends toward the animated knee, tilts planted feet to the ground normal and fades out while airborne. `ik: "auto"` finds the legs by bone name, and `pelvis`, `maxAdjust` and `alignToGround` are optional. Probes hit terrain and blocking objects. The math is `placeFeet` and `inferLegChains` (`@jgengine/core/anim/footPlacement`) (#1683, #1758).
|
|
148
|
+
- Sky and fog colors render as authored under any tone mapping. The sky dome and sky fog treat their colors as on-screen swatches and pre-invert the renderer's tone-mapping curve, so a `#cfe4f5` horizon no longer shows as grey `#a6b3c2` under the default `neutral` look (AgX). The dome now runs three's tone-mapping and color-space chunks, so it matches with or without a post chain. Looks with bloom (`cinematic`, `photoreal`) get a brighter sky that blooms more near the sun; raise `bloom.threshold` if that is too much.
|
|
149
|
+
- The sky's sun shadows lose their stair-step: one 2048 map over a 70 m box that leads the camera (was 180 m centered on it), texel-snapped, with a 6-texel PCF radius. Time-of-day, biome and `DayNightSky` suns use the same rig instead of three's default 10 m box.
|
|
150
|
+
- `jgengine create` seeds the sun lower and ahead-left of the spawn (bearing 128°, 20° up), so its glow is in the first frame.
|
|
151
|
+
- Scaffolded `scripts/shoot.mjs` and `scripts/drive.mjs` refuse a viewport that stays one flat color for 10 s instead of saving it; nothing is written and the command fails.
|
|
152
|
+
- `jgengine create` defaults to a 3D terrain world instead of an infinite flat slab. `editor.scene.json` seeds rolling hills as a terrain sculpt (deterministic per game id, flat around the spawn) and a bright `day` sky with a sun bearing and distance fog; `src/world.ts` renders them through `environment()` and `environmentContentFromDocument` and holds only the detail-shaded ground palette. Reshape the hills in the editor. `--ground flat` keeps the old `place()` slab (#1762).
|
|
153
|
+
- `GameRuntimeTransport.joinServer`/`leaveServer` take an optional `sessionId`. The Convex `joinServer`/`leaveServer` mutations and the WebSocket `join`/`leave` messages accept it (at most 128 characters). `useServerSession` and the shell's multiplayer sync send a fresh one per mount.
|
|
154
|
+
- Shared-topology `jgServerMembers` rows keep up to 16 live `sessionIds`. A leave with a `sessionId` ends only that session; the user leaves once none remain. A leave without one, or on a row without session ids, still leaves outright. Rooms topology is unchanged.
|
|
155
|
+
- The WebSocket router tracks sessions per connection and only leaves the host when no other connection of the same user holds the server.
|
|
156
|
+
- `helpers.resetPlayerProfile` (`@jgengine/convex/server`) now resets a player who has left the server but still has a profile, instead of throwing, so scheduled wipes and insolvency resets no longer abort a whole batch. A user with neither membership nor a profile still throws.
|
|
157
|
+
|
|
158
|
+
### Fixed
|
|
159
|
+
|
|
160
|
+
- The `combat-loop` recipe drained health with `stats.delta`, which never reaches the death pipeline, so copied enemies never died. It now damages through `ctx.scene.entity.effect` and a catalog `receive` map. The `loot` recipe now registers on `ctx.game.loot` and rolls from catalog `onDeath`. `KinematicVehicleTuning.camera` and `DRIVE_AXIS_BINDINGS` JSDoc now state what they actually do.
|
|
161
|
+
- Editor RPC `add_marker` and `set_marker` accept `catalogId`. It used to be dropped while the call still returned `ok`, so a headlessly authored `mob`/`boss` marker spawned nothing.
|
|
162
|
+
- Agent bridge `editor_summon` now resolves once the editor has mounted (up to 15 s). It used to return immediately, so editor verbs later in the same `drive` batch failed with "no live editor".
|
|
163
|
+
- Catalog `onDeath: { dropMode: "world" }` scattered drops with `Math.random`, so kill loot landed in different spots on replay and on each peer. The runtime now passes the world's seeded `ctx.rng` to the scatter.
|
|
164
|
+
- The chase camera no longer falls behind fast vehicles. Its spring eased the camera's world position toward a moving target, so the lag grew with speed (about 9 m extra at 220 km/h); it now eases the boom offset from the target (#1770).
|
|
165
|
+
- A `createVehicleDynamics` bike with both `lean` and `suspension` launched itself the moment it leaned: the springs read the lean as body roll across the narrow track. Springs now ignore lean, and lateral g keeps the same lag it has without springs.
|
|
166
|
+
- `@jgengine/rapier`: a body created with `mass` now weighs exactly that. It used to add `mass` on top of the collider's density-derived mass, so a 2 kg sphere of radius 0.5 weighed 2.52 kg, and every force, impulse and joint on a massed body was off by the collider's volume.
|
|
167
|
+
- `createVehicleDynamics` with `suspension` spawned and reset level even on a slope. The wheels started buried, so the springs fired the car into the air and it tumbled. It now fits pitch, roll and height to the terrain under its four wheels.
|
|
168
|
+
- A sky authored in the scene document (`editor.scene.json` `environment`, written by the editor lighting workspace) now renders in `place()` worlds. `defineGame` never passed `skyFromDocument(editorLayers)` to the renderer, so those edits were silently dropped. A game's own `backdrop.sky` still wins, and a document holding only point lights leaves the world sky alone (#1762).
|
|
169
|
+
- `jgengine create --scene starter` placed its props and goal behind the spawn, so the first frame showed empty ground and W walked away from the goal. The starter scene now sits ahead of the default +Z facing.
|
|
170
|
+
- A standalone game's `bun run desktop` failed with `jgengine: command not found` because the CLI is not a dependency; the script now runs `npx jgengine desktop`.
|
|
171
|
+
- `@jgengine/navbake` joins the published lockstep set. `@jgengine/editor@0.18.1` depended on it while nothing published it, so `bun install` on the released SDK failed. `check-release-set` (in `gate`, `check-types`, and the publish workflow) now fails when a published package depends on a workspace package outside the publish list or out of dependency order, or when the publish, version-bump, and changelog package lists disagree.
|
|
172
|
+
- Publish has been red since that change because a CLI packaging test still hardcoded the old publish list, so navbake never reached npm and `npx jgengine create` scaffolds failed to install. The test now defers the order to `check-release-set`.
|
|
173
|
+
- `jgengine-verify` was silently skipped by the skills installer for every new game: its `description` held an unquoted `: `, which is invalid YAML. `check-skills` now parses frontmatter as YAML and requires `name` to match the folder.
|
|
174
|
+
- `jgengine create --help` printed usage instead of scaffolding a game named after the first argument, and `create` rejects unknown flags rather than ignoring them. The usage lists `--player`, `--ground`, and `--scene`.
|
|
175
|
+
|
|
176
|
+
- `pickModel` / `resolveModelPlan` warn once in dev when a `ModelPick.fallbackModel` is not in the asset catalog (a typo or an undeclared pack), instead of silently shipping a fallback that can never take over.
|
|
177
|
+
|
|
178
|
+
- Volumetric clouds no longer cut through distant buildings: the proxy dome writes per-fragment depth at the raymarch's first cloud hit, so geometry between the camera and the cloud slab occludes the clouds instead of the dome radius deciding. `STUDIO_STAGE_POST` ambient occlusion is softer (radius 1.1, intensity 1.5) so tower silhouettes stop streaking.
|
|
179
|
+
|
|
180
|
+
- Separate SDK publication validation from external Games quality checks while retaining the full local and main-branch gates.
|
|
181
|
+
|
|
182
|
+
- Correct foot-IK endpoint recomputation after hip rotation; verify reachable targets and clamping without changing bone lengths.
|
|
183
|
+
- Restore release CI prerequisites and make catalog-loader tests independent of the external Games checkout.
|
|
184
|
+
|
|
185
|
+
- Closing a second tab, or a join that resolved after a remount, no longer evicts a user whose live tab still shows them joined, which made every later command fail with "Not a member of this server".
|
|
186
|
+
|
|
36
187
|
## 0.18.1
|
|
37
188
|
|
|
38
189
|
### Migrate
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jgengine/assets",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Self-generating, license-verified index of thousands of CC0 3D models for JGengine. Sources fetch from providers' own CDNs at pull time; the npm tarball ships only the typed index and CLI (no GLB bytes).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"test": "bun test src"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@jgengine/core": "^0.
|
|
38
|
+
"@jgengine/core": "^0.19.0",
|
|
39
39
|
"fflate": "^0.8.2"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
@@ -143,7 +143,7 @@ When sufficient, return a concise orientation: objective, progress, decisions, c
|
|
|
143
143
|
|
|
144
144
|
## JGengine notes
|
|
145
145
|
|
|
146
|
-
- Multi-session engine work
|
|
146
|
+
- Multi-session engine work spans GitHub issues and PRs — point at those; do not paste full plan bodies.
|
|
147
147
|
- Prefer `bun run agent:bootstrap --check` status, branch names, and PR links as state anchors.
|
|
148
148
|
- Local parallelism uses `.claude/worktrees/`; never invent `C:\tmp\…` Codex-shared trees.
|
|
149
149
|
- Durable process changes belong in skills, not only the handoff.
|
package/skills/jgengine/SKILL.md
CHANGED
|
@@ -5,7 +5,9 @@ description: Route game work to the smallest useful JGengine guidance set.
|
|
|
5
5
|
|
|
6
6
|
# JGengine intake and router
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
This skill decides what to load; it is not an engine manual. Package map, commands, layering, and license live in the JGengine README (repo-root [`README.md`](../../../README.md) in the monorepo, [jgengine.com](https://jgengine.com) from a created project); open it when you need one of those facts.
|
|
9
|
+
|
|
10
|
+
A created project installs only `jgengine`, `jgengine-editor`, `jgengine-verify`, `game-design`, and `jgengine-ui`. Run `npx jgengine skills -p --all` before loading any other domain named below. Those projects do not ship `api.md`; read exact signatures from the `.d.ts` files under `node_modules/@jgengine/<pkg>/dist/`.
|
|
9
11
|
|
|
10
12
|
## 1. Establish the target
|
|
11
13
|
|
|
@@ -20,7 +22,7 @@ Treat the pitch as a unique composition of needs, not a genre to fill in — nev
|
|
|
20
22
|
- HUD/menu states and **custom** UI art direction (compose shipped building blocks + HudTheme; no unskinned stock face)
|
|
21
23
|
- one observable completion scenario
|
|
22
24
|
|
|
23
|
-
For a new in-repo game, scaffold with `bun run new:game "<id-or-title>"` (thin script alias for `jgengine create`, auto-detects the in-repo variant). Outside this monorepo, start with `npx jgengine create`; never copy `Games/*`.
|
|
25
|
+
Pick the dimension first: `create` defaults to a 3D world (terrain, sky, player model, editor scene); a grid, card, tile, or other board game with no 3D scene takes `create --2d` (a `board` world drawn by GameUI over `@jgengine/core/puzzle/cellGrid`). For a new in-repo game, scaffold with `bun run new:game "<id-or-title>"` (thin script alias for `jgengine create`, auto-detects the in-repo variant). Outside this monorepo, start with `npx jgengine create`; never copy `Games/*`.
|
|
24
26
|
|
|
25
27
|
For an existing game asked to pick up a new engine release ("jgengine published a new version, grab it"), run `npx jgengine upgrade` in the project first: it diffs installed `@jgengine/*` versions against the latest release and prints every Migrate step and Adopt-worthy addition in between (`--json` for structured output). Bump, migrate oldest-first, then work the Adopt lists instead of reconstructing the changelog by hand.
|
|
26
28
|
|
|
@@ -46,9 +48,9 @@ World content routes editor-first: any request that adds, moves, restyles, or re
|
|
|
46
48
|
|
|
47
49
|
Within each selected domain:
|
|
48
50
|
|
|
49
|
-
0. Fastest start for a common composition: `npx jgengine recipe <name>` prints a vetted, SDK-typechecked snippet (imports + wired code)
|
|
50
|
-
1. Search
|
|
51
|
-
2. Open `api.md` only when exact signatures
|
|
51
|
+
0. Fastest start for a common composition: `npx jgengine recipe <name>` prints a vetted, SDK-typechecked snippet (imports + wired code) (`combat-loop`, `loot`, `world-drops`, `click-target`, `ability-bar`, `hitscan-weapon`, `rolled-gear`, `enter-vehicle` and more); `npx jgengine recipe` lists them. Copy that instead of reconning per-file.
|
|
52
|
+
1. Search capabilities by intent; do not read `capabilities.md` top to bottom. `npx jgengine find <intent>` searches every domain's index and every recipe at once, installed or not, and prints the primitive and its import (or the recipe to open); in the monorepo, `grep -i "<intent>" .claude/skills/*/capabilities.md` does the same. Search the moment you are tempted to hand-roll a HUD window, inventory grid, paperdoll, hotkey listener, minimap, stat bar, or character motion. A `z-index`, a `keydown` for a panel, a `<div>` inventory, or a limb bob almost certainly has a shipped drop-in (`usePanels`/`PanelHost`, `InventoryGrid`, `CharacterSheet`/`Paperdoll`, `PartMotionRig`, `EntityPreview`); games own the *skin*, not the re-derivation.
|
|
53
|
+
2. Open `api.md` (monorepo) or the package `.d.ts` only when exact signatures are needed; grep it, never read it whole.
|
|
52
54
|
3. Open the linked reference only for a deeper recipe or trap.
|
|
53
55
|
4. For how several primitives wire into a running loop, read the domain's `recipes/` — connected, genre-free walkthroughs organized by composition seam; this skill's own [recipes/minimal-game.md](recipes/minimal-game.md) is the default whole-game walkthrough. Prefer a recipe over reading a game's source; the games under `Games/*` are not templates or references, and a hybrid game is just a different composition of the same primitives.
|
|
54
56
|
|
package/skills/jgengine/api.md
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
8
8
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
9
|
-
- `VERSION` (const): const VERSION: "0.
|
|
9
|
+
- `VERSION` (const): const VERSION: "0.19.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
|
|
10
10
|
|
|
11
11
|
## @jgengine/core/authoring
|
|
12
12
|
|
|
13
|
-
- `ActionCodesMap` (type): type ActionCodesMap<TAction extends string = string, TCode extends string = string> = Record< TAction, ActionCodes<TCode> > — Maps each game action name to the input codes (hold/toggle keys, repeat rate) that trigger it.
|
|
13
|
+
- `ActionCodesMap` (type): type ActionCodesMap<TAction extends string = string, TCode extends string = string> = Record< TAction, ActionCodes<TCode> > — Maps each game action name to the input codes (hold/toggle keys, repeat rate) that trigger it. In a `defineGame({ input })`, pressing an action runs the same-named command with `{ yaw, pitch, aim }`; `repeatMs` re-fires it while held (automatic weapons).
|
|
14
14
|
- `AuthoredProvenance` (interface): interface AuthoredProvenance — A first-class entry in the scene document — the editor fully owns and persists it.
|
|
15
15
|
- `Drop` (interface): interface Drop — A resolved loot outcome — one item or currency grant with its rolled count.
|
|
16
16
|
- `GameDefinition` (interface): interface GameDefinition<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> — Fully-resolved game description produced by {@link defineGameDefinition} — assets, scene, and opted-in subsystems.
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
|
|
64
64
|
- `CommandDecodeResult` (type): type CommandDecodeResult<TInput> = | { ok: true; value: TInput } | { ok: false; reason: string } — ⚠ undocumented
|
|
65
65
|
- `CommandDecoder` (type): type CommandDecoder<TInput> = (input: unknown) => CommandDecodeResult<TInput> — Parses raw `unknown` transport input into `TInput`, rejecting anything that doesn't match the command's declared shape. Runs before `validate`/`apply`, so a malformed payload never reaches game logic.
|
|
66
|
-
- `CommandDefinition` (interface): interface CommandDefinition<TState, TInput> — ⚠ undocumented
|
|
67
|
-
- `CommandRegistry` (interface): interface CommandRegistry<TState> — ⚠ undocumented
|
|
68
|
-
- `CommandRejection` (interface): interface CommandRejection — ⚠ undocumented
|
|
66
|
+
- `CommandDefinition` (interface): interface CommandDefinition<TState, TInput> { decode?: CommandDecoder<TInput>; validate?(state: TState, input: TInput): CommandRejection | null; apply(state: TState, input: TInput): TState | void } — ⚠ undocumented
|
|
67
|
+
- `CommandRegistry` (interface): interface CommandRegistry<TState> { define<TInput>(name: string, definition: CommandDefinition<TState, TInput>): void; has(name: string): boolean; names(): string[]; run(state: TState, name: string, input: unknown): CommandResult<TState> } — ⚠ undocumented
|
|
68
|
+
- `CommandRejection` (interface): interface CommandRejection { reason: string } — ⚠ undocumented · used by `validateCommandInput` (@jgengine/ws): Validates a `runCommand` input against a declared catalog.
|
|
69
69
|
- `CommandResult` (type): type CommandResult<TState> = | { status: "applied"; state: TState } | { status: "rejected"; reason: string } | { status: "unknown-command" } — ⚠ undocumented
|
|
70
70
|
- `createCommandRegistry` (function): function createCommandRegistry<TState>(): CommandRegistry<TState> — ⚠ undocumented
|
|
71
71
|
|
|
@@ -84,28 +84,28 @@
|
|
|
84
84
|
- `DevtoolsControlKind` (type): type DevtoolsControlKind = | "slider" | "toggle" | "color" | "select" | "text" | "vec2" | "vec3" | "vec4" | "interval" | "angle" | "enum" — ⚠ undocumented
|
|
85
85
|
- `DevtoolsLogEntry` (interface): interface DevtoolsLogEntry — A single captured log line with timestamp, level, and formatted message.
|
|
86
86
|
- `DevtoolsLogLevel` (type): type DevtoolsLogLevel = "log" | "info" | "warn" | "error" — Severity level for a captured devtools log entry.
|
|
87
|
-
- `DevtoolsOverrides` (interface): interface DevtoolsOverrides — ⚠ undocumented
|
|
87
|
+
- `DevtoolsOverrides` (interface): interface DevtoolsOverrides { version: number; enabled: string[]; values: Record<string, unknown>; schemas?: Record<string, {kind: DevtoolsControlKind; schemaVersion?: number}> } — ⚠ undocumented
|
|
88
88
|
- `DevtoolsSnapshot` (interface): interface DevtoolsSnapshot — Serializable point-in-time snapshot of devtools state: frame, render, latency, logs, controls, and discovered fields.
|
|
89
89
|
- `DiscoveredEntry` (interface): interface DiscoveredEntry — A control auto-discovered by scanning an object/table, with its binding metadata and current reader.
|
|
90
|
-
- `DiscoverySkip` (interface): interface DiscoverySkip — ⚠ undocumented
|
|
90
|
+
- `DiscoverySkip` (interface): interface DiscoverySkip { readonly path: string; readonly reason: string } — ⚠ undocumented
|
|
91
91
|
- `FrameRecordSample` (interface): interface FrameRecordSample — Per-frame timing input recorded by callers: total frame time, sim time, and optional named phase durations.
|
|
92
92
|
- `FrameStats` (interface): interface FrameStats — Aggregated frame-timing statistics over the recent sampling window, including per-phase breakdown.
|
|
93
93
|
- `LONG_FRAME_MS` (const): const LONG_FRAME_MS: 33.4 — Frame duration in milliseconds above which a frame is recorded as a long-frame spike (~30fps budget).
|
|
94
94
|
- `LatencyStats` (interface): interface LatencyStats — Aggregated latency statistics (last/avg/min/max) over recorded latency samples.
|
|
95
95
|
- `LongFrameEvent` (interface): interface LongFrameEvent — Recorded spike event for a frame that exceeded the long-frame threshold, with culprit attribution.
|
|
96
|
-
- `NormalizedColor` (interface): interface NormalizedColor — ⚠ undocumented
|
|
96
|
+
- `NormalizedColor` (interface): interface NormalizedColor { readonly hex: string; readonly rgb: string; readonly alpha: number; readonly hasAlpha: boolean } — ⚠ undocumented
|
|
97
97
|
- `OVERRIDES_FORMAT_VERSION` (const): const OVERRIDES_FORMAT_VERSION: 1 — ⚠ undocumented
|
|
98
|
-
- `OverrideApplyDiagnostic` (interface): interface OverrideApplyDiagnostic — ⚠ undocumented
|
|
98
|
+
- `OverrideApplyDiagnostic` (interface): interface OverrideApplyDiagnostic { readonly id: string; readonly reason: string } — ⚠ undocumented
|
|
99
99
|
- `OverrideApplyResult` (interface): interface OverrideApplyResult — Outcome of applying a devtools overrides payload: counts of applied and skipped entries plus diagnostics.
|
|
100
100
|
- `PhaseStats` (interface): interface PhaseStats — Timing summary for a single named profiling phase across the sampling window.
|
|
101
101
|
- `RenderSample` (interface): interface RenderSample — Snapshot of renderer resource counts (draw calls, triangles, geometries, textures) for a frame.
|
|
102
|
-
- `ScanFieldMeta` (interface): interface ScanFieldMeta extends TunableOptions — ⚠ undocumented
|
|
102
|
+
- `ScanFieldMeta` (interface): interface ScanFieldMeta extends TunableOptions { kind?: DevtoolsControlKind } — ⚠ undocumented
|
|
103
103
|
- `ScanMeta` (type): type ScanMeta = Readonly<Record<string, ScanFieldMeta>> — ⚠ undocumented
|
|
104
104
|
- `Tunable` (interface): interface Tunable<T> — Typed handle to a registered control, exposing its live value and subscribe/set/reset operations.
|
|
105
105
|
- `TunableAccessor` (interface): interface TunableAccessor — Get/set accessor pair plus initial value used to bind a discovered field to a control.
|
|
106
|
-
- `TunableChoice` (interface): interface TunableChoice<T = unknown> — ⚠ undocumented
|
|
106
|
+
- `TunableChoice` (interface): interface TunableChoice<T = unknown> { readonly value: T; readonly label?: string } — ⚠ undocumented
|
|
107
107
|
- `TunableInterval` (type): type TunableInterval = { min: number; max: number } — ⚠ undocumented
|
|
108
|
-
- `TunableOptions` (interface): interface TunableOptions<T = unknown> — ⚠ undocumented
|
|
108
|
+
- `TunableOptions` (interface): interface TunableOptions<T = unknown> { min?: number; max?: number; step?: number; options?: readonly T[]; choices?: readonly TunableChoice<T>[]; label?: string; group?: string; onChange?: (value: T) => void; kind?: DevtoolsControlKind; unit?: AngleUnit; displayUnit?: AngleUnit… — ⚠ undocumented · used by `createDiscoverModule` (@jgengine/core/devtools/discover): Create the discovery subsystem that scans objects/tables for tunable fields and binds them as controls and probes.
|
|
109
109
|
- `TunableVec2` (type): type TunableVec2 = [number, number] — ⚠ undocumented
|
|
110
110
|
- `TunableVec3` (type): type TunableVec3 = [number, number, number] — ⚠ undocumented
|
|
111
111
|
- `TunableVec4` (type): type TunableVec4 = [number, number, number, number] — ⚠ undocumented
|
|
@@ -173,28 +173,28 @@
|
|
|
173
173
|
|
|
174
174
|
## @jgengine/core/devtools/transformTunables
|
|
175
175
|
|
|
176
|
-
- `TunableTransformResult` (interface): interface TunableTransformResult — ⚠ undocumented
|
|
176
|
+
- `TunableTransformResult` (interface): interface TunableTransformResult { code: string; bound: string[] } — ⚠ undocumented
|
|
177
177
|
|
|
178
178
|
## @jgengine/core/devtools/tunableSchema
|
|
179
179
|
|
|
180
180
|
- `AngleUnit` (type): type AngleUnit = "rad" | "deg" — ⚠ undocumented
|
|
181
181
|
- `CONTROL_SCHEMA_VERSION` (const): const CONTROL_SCHEMA_VERSION: 1 — ⚠ undocumented
|
|
182
182
|
- `DevtoolsControlKind` (type): type DevtoolsControlKind = | "slider" | "toggle" | "color" | "select" | "text" | "vec2" | "vec3" | "vec4" | "interval" | "angle" | "enum" — ⚠ undocumented
|
|
183
|
-
- `DevtoolsOverrides` (interface): interface DevtoolsOverrides — ⚠ undocumented
|
|
184
|
-
- `DiscoverySkip` (interface): interface DiscoverySkip — ⚠ undocumented
|
|
183
|
+
- `DevtoolsOverrides` (interface): interface DevtoolsOverrides { version: number; enabled: string[]; values: Record<string, unknown>; schemas?: Record<string, {kind: DevtoolsControlKind; schemaVersion?: number}> } — ⚠ undocumented
|
|
184
|
+
- `DiscoverySkip` (interface): interface DiscoverySkip { readonly path: string; readonly reason: string } — ⚠ undocumented
|
|
185
185
|
- `MAX_SCAN_DEPTH` (const): const MAX_SCAN_DEPTH: 5 — ⚠ undocumented
|
|
186
186
|
- `MAX_SCAN_TARGETS` (const): const MAX_SCAN_TARGETS: 512 — ⚠ undocumented
|
|
187
187
|
- `MAX_TABLE_ENTRIES` (const): const MAX_TABLE_ENTRIES: 64 — ⚠ undocumented
|
|
188
|
-
- `NormalizedColor` (interface): interface NormalizedColor — ⚠ undocumented
|
|
188
|
+
- `NormalizedColor` (interface): interface NormalizedColor { readonly hex: string; readonly rgb: string; readonly alpha: number; readonly hasAlpha: boolean } — ⚠ undocumented
|
|
189
189
|
- `OVERRIDES_FORMAT_VERSION` (const): const OVERRIDES_FORMAT_VERSION: 1 — ⚠ undocumented
|
|
190
|
-
- `OverrideApplyDiagnostic` (interface): interface OverrideApplyDiagnostic — ⚠ undocumented
|
|
191
|
-
- `OverrideParseResult` (interface): interface OverrideParseResult — ⚠ undocumented
|
|
192
|
-
- `ResolvedAxisBounds` (interface): interface ResolvedAxisBounds — ⚠ undocumented
|
|
193
|
-
- `ScanFieldMeta` (interface): interface ScanFieldMeta extends TunableOptions — ⚠ undocumented
|
|
190
|
+
- `OverrideApplyDiagnostic` (interface): interface OverrideApplyDiagnostic { readonly id: string; readonly reason: string } — ⚠ undocumented
|
|
191
|
+
- `OverrideParseResult` (interface): interface OverrideParseResult { readonly overrides: DevtoolsOverrides | null; readonly diagnostics: readonly string[] } — ⚠ undocumented
|
|
192
|
+
- `ResolvedAxisBounds` (interface): interface ResolvedAxisBounds { readonly labels: readonly string[]; readonly min: readonly number[]; readonly max: readonly number[]; readonly step: readonly number[] } — ⚠ undocumented
|
|
193
|
+
- `ScanFieldMeta` (interface): interface ScanFieldMeta extends TunableOptions { kind?: DevtoolsControlKind } — ⚠ undocumented
|
|
194
194
|
- `ScanMeta` (type): type ScanMeta = Readonly<Record<string, ScanFieldMeta>> — ⚠ undocumented
|
|
195
|
-
- `TunableChoice` (interface): interface TunableChoice<T = unknown> — ⚠ undocumented
|
|
195
|
+
- `TunableChoice` (interface): interface TunableChoice<T = unknown> { readonly value: T; readonly label?: string } — ⚠ undocumented
|
|
196
196
|
- `TunableInterval` (type): type TunableInterval = { min: number; max: number } — ⚠ undocumented
|
|
197
|
-
- `TunableOptions` (interface): interface TunableOptions<T = unknown> — ⚠ undocumented
|
|
197
|
+
- `TunableOptions` (interface): interface TunableOptions<T = unknown> { min?: number; max?: number; step?: number; options?: readonly T[]; choices?: readonly TunableChoice<T>[]; label?: string; group?: string; onChange?: (value: T) => void; kind?: DevtoolsControlKind; unit?: AngleUnit; displayUnit?: AngleUnit… — ⚠ undocumented · used by `createDiscoverModule` (@jgengine/core/devtools/discover): Create the discovery subsystem that scans objects/tables for tunable fields and binds them as controls and probes.
|
|
198
198
|
- `TunableVec2` (type): type TunableVec2 = [number, number] — ⚠ undocumented
|
|
199
199
|
- `TunableVec3` (type): type TunableVec3 = [number, number, number] — ⚠ undocumented
|
|
200
200
|
- `TunableVec4` (type): type TunableVec4 = [number, number, number, number] — ⚠ undocumented
|
|
@@ -224,18 +224,22 @@
|
|
|
224
224
|
- `subscribeUrlChange` (function): function subscribeUrlChange(listener: () => void): () => void — Fires `listener` whenever the query string may have changed out from under a flag — browser back/forward (`popstate`). Manual address-bar edits reload the page, so initial reads cover those; this keeps in-app state honest across history navigation. Returns an unsubscribe.
|
|
225
225
|
- `writeUrlParam` (function): function writeUrlParam(param: string, value: string | null): void — Sets `param` to `value`, or removes it when `value` is null, rewriting the URL in place with `history.replaceState` so the rest of the query and the hash survive and no history entry is pushed. No-ops without a DOM, and skips the write when the URL already matches.
|
|
226
226
|
|
|
227
|
+
## @jgengine/core/devtools/warnOnce
|
|
228
|
+
|
|
229
|
+
- `warnOnce` (function): function warnOnce(site: string, message: string): void — Logs a dev-mode `console.warn` once per `site`, and never in a production build. Reach for it where a permissive config would otherwise fail silently — a clipped sky dome, an ignored tint.
|
|
230
|
+
|
|
227
231
|
## @jgengine/core/meta/changelog
|
|
228
232
|
|
|
229
233
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
230
234
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
231
|
-
- `VERSION` (const): const VERSION: "0.
|
|
235
|
+
- `VERSION` (const): const VERSION: "0.19.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
|
|
232
236
|
|
|
233
237
|
## @jgengine/core/runtime/adapter
|
|
234
238
|
|
|
235
|
-
- `MultiplayerAdapterConfig` (type): type MultiplayerAdapterConfig = | { kind: "convex"; topology?: MultiplayerTopology; authority?: MultiplayerAuthority } | { kind: "ws"; topology?: MultiplayerTopology; url?: string; authority?: MultiplayerAuthority } | { kind: "socketio"; topology?: MultiplayerTopology; url?: string; authority?: Mult… — ⚠ undocumented
|
|
239
|
+
- `MultiplayerAdapterConfig` (type): type MultiplayerAdapterConfig = | { kind: "convex"; topology?: MultiplayerTopology; authority?: MultiplayerAuthority } | { kind: "ws"; topology?: MultiplayerTopology; url?: string; authority?: MultiplayerAuthority } | { kind: "socketio"; topology?: MultiplayerTopology; url?: string; authority?: Mult… — ⚠ undocumented · used by `convex`: Convex transport.
|
|
236
240
|
- `MultiplayerAuthority` (type): type MultiplayerAuthority = "server" | "client" — Where the world simulation is authoritative.
|
|
237
|
-
- `MultiplayerTopology` (type): type MultiplayerTopology = "shared" | "lobbies" | "private" — ⚠ undocumented
|
|
238
|
-
- `ServersPoolConfig` (type): type ServersPoolConfig = { minPlayersToStart?: number; adapter: MultiplayerAdapterConfig; } & ( | { topology: "shared"; maxServers?: number; slotsPerServer?: number } | { topology?: "rooms"; maxServers: number; slotsPerServer: number } ) — ⚠ undocumented
|
|
241
|
+
- `MultiplayerTopology` (type): type MultiplayerTopology = "shared" | "lobbies" | "private" — ⚠ undocumented · used by `convex`: Convex transport.
|
|
242
|
+
- `ServersPoolConfig` (type): type ServersPoolConfig = { minPlayersToStart?: number; adapter: MultiplayerAdapterConfig; } & ( | { topology: "shared"; maxServers?: number; slotsPerServer?: number } | { topology?: "rooms"; maxServers: number; slotsPerServer: number } ) — ⚠ undocumented · used by `servers`: Resolve the shared-world pool to one unbounded world; room pools keep explicit capacities.
|
|
239
243
|
- `adapterOf` (function): function adapterOf(multiplayer: unknown): MultiplayerAdapterConfig | null — ⚠ undocumented
|
|
240
244
|
- `convex` (function): function convex(config?: { topology?: MultiplayerTopology; authority?: MultiplayerAuthority }): MultiplayerAdapterConfig — Convex transport. Omitting `authority` (or passing `"client"`) is **presence-only** — prefer `convexPresence()` to name that intent explicitly. Pass `{ authority: "server" }` for a shared, host-authoritative world — see `examples/HOSTED.md`.
|
|
241
245
|
- `convexPresence` (function): function convexPresence(config?: { topology?: MultiplayerTopology }): MultiplayerAdapterConfig — Presence-only Convex transport — each client runs its own `onTick`; only presence/feeds/chat sync. Sugar for `convex({ ...config, authority: "client" })`.
|
|
@@ -255,21 +259,34 @@
|
|
|
255
259
|
|
|
256
260
|
## @jgengine/core/runtime/cameraDirector
|
|
257
261
|
|
|
258
|
-
- `
|
|
259
|
-
- `
|
|
262
|
+
- `CHASE_VIEWS` (const): const CHASE_VIEWS: readonly ChaseView[] — Default order {@link nextChaseView} cycles through.
|
|
263
|
+
- `CameraDirector` (interface): interface CameraDirector { follow(entityId: string | null): void; followedEntityId(): string | null | undefined; setCinematic(config: CinematicCameraConfig | null): void; cinematic(): CinematicCameraConfig | null; setChaseTuning(tuning: ChaseCameraTuning | null): v… — ⚠ undocumented
|
|
264
|
+
- `ChaseCameraTuning` (type): type ChaseCameraTuning = Partial< Pick< ChaseCameraConfig, | "distance" | "height" | "lookHeight" | "springDamping" | "fov" | "lead" | "bank" | "shakePerSpeed" | "velocityYaw" | "yawResponse" | "distanceBySpeed" | "pitchFollow" | "fovKick" | "lookBackAction" | "collision" | "view" > > — Runtime patch over the static `camera.chase` config — distance/height/fov retuning from gameplay events (#286.11), a whole driving-feel overlay applied only while a vehicle is piloted (#1299), or a `view` switch between chase and seat cameras.
|
|
265
|
+
- `nextChaseView` (function): function nextChaseView(current: ChaseView, views: readonly ChaseView[] = CHASE_VIEWS): ChaseView — The view after `current` in `views`, wrapping at the end; a view missing from the list starts the cycle over. Pair with `setChaseTuning` to bind a "change camera" key: `ctx.camera.setChaseTuning({ ...ctx.camera.chaseTuning(), view: nextChaseView(view) })`.
|
|
260
266
|
|
|
261
267
|
## @jgengine/core/runtime/commandInput
|
|
262
268
|
|
|
263
269
|
- `QuantityResult` (type): type QuantityResult = { ok: true; quantity: number } | { ok: false; reason: "invalid-quantity" } — Validated integer quantity or a stable input rejection.
|
|
270
|
+
- `isInputRecord` (function): function isInputRecord(value: unknown): value is Record<string, unknown> — Narrow an untrusted value to a plain object, rejecting arrays and null.
|
|
271
|
+
- `readInputArray` (function): function readInputArray<T>(value: unknown, readItem: (item: unknown) => T | null, options: { maxItems?: number } = {}): T[] | null — Read an array of at most `maxItems` entries, each accepted by `readItem`; any rejected entry rejects the whole array.
|
|
272
|
+
- `readInputNumber` (function): function readInputNumber(value: unknown, options: { min?: number; max?: number; integer?: boolean } = {}): number | null — Read a finite number inside `[min, max]`, or `null`. `NaN` and `±Infinity` are always rejected, so a coordinate can never drive an unbounded loop; `integer` also rejects fractions instead of flooring them.
|
|
273
|
+
- `readInputOneOf` (function): function readInputOneOf<const T extends string | number>(value: unknown, allowed: readonly T[]): T | null — Read one of a fixed set of values, or `null`.
|
|
274
|
+
- `readInputPoint2` (function): function readInputPoint2(value: unknown, options: { maxAbs?: number } = {}): { x: number; z: number } | null — Read an `{ x, z }` ground point whose axes are finite and within `maxAbs`, or `null`.
|
|
275
|
+
- `readInputPoint3` (function): function readInputPoint3(value: unknown, options: { maxAbs?: number } = {}): { x: number; y: number; z: number } | null — Read an `{ x, y, z }` point whose axes are finite and within `maxAbs`, or `null`.
|
|
276
|
+
- `readInputString` (function): function readInputString(value: unknown, options: { maxLength?: number } = {}): string | null — Read a non-empty string of at most `maxLength` characters, or `null`.
|
|
264
277
|
- `readQuantity` (function): function readQuantity(value: unknown, options: { min?: number; max?: number } = {}): QuantityResult — Read an untrusted whole-item quantity without coercion, truncation, or clamping.
|
|
265
278
|
|
|
266
279
|
## @jgengine/core/runtime/commandRunner
|
|
267
280
|
|
|
268
|
-
- `
|
|
281
|
+
- `CommandAccess` (type): type CommandAccess = "client" | "server" — Who may send a command: any client through the public transport, or only trusted host code.
|
|
282
|
+
- `CommandDef` (type): type CommandDef<TInput = unknown> = { /** * `"server"` when `input` carries facts only the host may decide (unlock state, prices, multipliers): * public transports then refuse it and only trusted host calls such as `helpers.runCommand` run it. * Defaults to `"client"`, so every field of `input` must… — ⚠ undocumented · used by `defineCommand`: Declare a command with a typed input: `parse` narrows the wire value once, and `scope`, `validate` and `apply` receive the parsed type.
|
|
269
283
|
- `CommandScope` (type): type CommandScope = { /** Member ids to hydrate; omit for the whole roster. */ players?: readonly string[]; /** Chunk keys to hydrate; omit for every chunk of the server, `[]` for none. */ chunkKeys?: readonly string[]; } — Which slice of a server has to be hydrated before some work runs against it. Both fields default to "everything", which costs a read per member plus one per chunk — the cost that makes a large shared world unaffordable per mutation. Narrow them when the caller knows what it will touch.
|
|
270
284
|
- `CommandScopeDefinition` (type): type CommandScopeDefinition = Omit<CommandScope, "players"> & { players?: readonly string[] | "actor" } — Declarative read scope; actor is resolved before host hydration.
|
|
271
285
|
- `CommandValidationError` (type): type CommandValidationError = { reason: string } — ⚠ undocumented
|
|
286
|
+
- `MALFORMED_COMMAND_INPUT_REASON` (const): const MALFORMED_COMMAND_INPUT_REASON: "Malformed command input" — Reason every host returns when a command's `parse` rejects its input.
|
|
272
287
|
- `RunCommandResult` (type): type RunCommandResult = | { ok: true; snapshot: GameRuntimeSnapshot } | { ok: false; reason: string } — ⚠ undocumented
|
|
288
|
+
- `SERVER_ONLY_COMMAND_REASON` (const): const SERVER_ONLY_COMMAND_REASON: "Command is server-only" — Reason a public transport returns for a {@link CommandDef} marked `access: "server"`.
|
|
289
|
+
- `defineCommand` (function): function defineCommand<TInput>(definition: CommandDef<TInput> & { parse: (input: unknown) => TInput | null }): CommandDef — Declare a command with a typed input: `parse` narrows the wire value once, and `scope`, `validate` and `apply` receive the parsed type. Returns the untyped {@link CommandDef} a runtime's command table holds.
|
|
273
290
|
|
|
274
291
|
## @jgengine/core/runtime/context/objectSlots
|
|
275
292
|
|
|
@@ -281,30 +298,30 @@
|
|
|
281
298
|
## @jgengine/core/runtime/gameContext
|
|
282
299
|
|
|
283
300
|
- `CatalogEntityRole` (type): type CatalogEntityRole = "player" | "enemy" | "hostile" | "npc" | "vehicle" — ⚠ undocumented
|
|
284
|
-
- `FloatTextInput` (interface): interface FloatTextInput — ⚠ undocumented
|
|
301
|
+
- `FloatTextInput` (interface): interface FloatTextInput { instanceId?: string; position?: [number, number, number]; text?: string; kind?: string; amount?: number; hitType?: string; element?: string; crit?: boolean; scale?: number } — ⚠ undocumented
|
|
285
302
|
- `GameAudio` (interface): interface GameAudio — Reachable audio seam on `ctx.game`: `play`, `music`, and `resume` route through the `audio.play`/`audio.music`/`audio.resume` events the shell's audio engine listens on, so game code triggers sound without importing the shell. Retained `loop`/`setLoop`/`stopLoop` add id-keyed loops with live pitch/gain control over `audio.loopStart`/`audio.loopSet`/`audio.loopStop` (#1051).
|
|
286
303
|
- `GameContext` (interface): interface GameContext — The live engine handle a game's loop, systems, commands, and UI read and mutate — entities, objects, the reactive store, opted-in `game.*` subsystems, world queries, and the sim clock. One context = one running world.
|
|
287
|
-
- `GameContextCards` (interface): interface GameContextCards — ⚠ undocumented
|
|
288
|
-
- `GameContextCommands` (interface): interface GameContextCommands — ⚠ undocumented
|
|
289
|
-
- `GameContextContent` (interface): interface GameContextContent — ⚠ undocumented
|
|
290
|
-
- `GameContextEconomy` (interface): interface GameContextEconomy — ⚠ undocumented
|
|
291
|
-
- `GameContextEntityEntry` (interface): interface GameContextEntityEntry — ⚠ undocumented
|
|
292
|
-
- `GameContextFeed` (interface): interface GameContextFeed extends Omit<GameFeed, "bind"> — ⚠ undocumented
|
|
293
|
-
- `GameContextItemEntry` (interface): interface GameContextItemEntry — ⚠ undocumented
|
|
294
|
-
- `GameContextItemUse` (interface): interface GameContextItemUse — ⚠ undocumented
|
|
295
|
-
- `GameContextLoot` (interface): interface GameContextLoot — ⚠ undocumented
|
|
304
|
+
- `GameContextCards` (interface): interface GameContextCards { pile(id: string, config?: CardPileConfig): CardPile } — ⚠ undocumented
|
|
305
|
+
- `GameContextCommands` (interface): interface GameContextCommands { define<TInput>(name: string, definition: CommandDefinition<GameContext, TInput>): void; has(name: string): boolean; names(): string[]; run(name: string, input: unknown): CommandResult<GameContext>; runAs(actorUserId: string, name: string,… — ⚠ undocumented
|
|
306
|
+
- `GameContextContent` (interface): interface GameContextContent { itemById?(itemId: string): GameContextItemEntry | null | undefined; entityById?(catalogId: string): GameContextEntityEntry | null | undefined; objectById?(catalogId: string): GameContextObjectEntry | null | undefined } — ⚠ undocumented
|
|
307
|
+
- `GameContextEconomy` (interface): interface GameContextEconomy { balance(userId: string, currencyId: string | CurrencyDefinition): number; grant(userId: string, currencyId: string | CurrencyDefinition, amount: number): void; charge(userId: string, currencyId: string | CurrencyDefinition, amount: number… — ⚠ undocumented
|
|
308
|
+
- `GameContextEntityEntry` (interface): interface GameContextEntityEntry { stats?: StatCatalog; receive?: ReceiveMap; onDeath?: OnDeathSpec; movement?: PoseAllowedStates & {walkSpeed?: number}; role?: CatalogEntityRole; verbs?: readonly ContextVerb[]; colliders?: EntityColliderSet; scale?: number } — ⚠ undocumented · used by `entityEntryFromCatalog` (@jgengine/core/editor): Turns an authored entity row into the runtime {@link GameContextEntityEntry} a spawn consumes: looks up `entityId` in the document's {@link …
|
|
309
|
+
- `GameContextFeed` (interface): interface GameContextFeed extends Omit<GameFeed, "bind"> { bind(action: keyof GameEventMap): () => void } — ⚠ undocumented
|
|
310
|
+
- `GameContextItemEntry` (interface): interface GameContextItemEntry { use?: string; weapon?: Record<string, unknown>; trade?: TradeField; rarity?: string; baseType?: string } — ⚠ undocumented
|
|
311
|
+
- `GameContextItemUse` (interface): interface GameContextItemUse { register(handlers: Record<string, ItemUseHandler<GameContext>>): void; registered(): string[]; can(input: ItemUseInput): ItemUseRejection | null; use(input: ItemUseInput): ItemUseResult<GameContext> } — ⚠ undocumented
|
|
312
|
+
- `GameContextLoot` (interface): interface GameContextLoot { register(def: LootTableDef): void; has(id: string): boolean; roll(id: string, rng?: () => number): Drop[]; grantToPlayer(userId: string, drops: Drop[], source?: string): void } — ⚠ undocumented
|
|
296
313
|
- `GameContextModels` (interface): interface GameContextModels — Per-kind render-model lookup for {@link GameContextOptions.models}; a resolved `ModelConfig` satisfies {@link ModelBodySource} structurally.
|
|
297
|
-
- `GameContextObjectEntry` (interface): interface GameContextObjectEntry — ⚠ undocumented
|
|
298
|
-
- `GameContextOptions` (interface): interface GameContextOptions<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> — ⚠ undocumented
|
|
299
|
-
- `GameContextRace` (interface): interface GameContextRace — ⚠ undocumented
|
|
300
|
-
- `GameContextTurn` (interface): interface GameContextTurn — ⚠ undocumented
|
|
301
|
-
- `GameContextWorld` (interface): interface GameContextWorld — ⚠ undocumented
|
|
302
|
-
- `HitReactionInput` (interface): interface HitReactionInput — ⚠ undocumented
|
|
314
|
+
- `GameContextObjectEntry` (interface): interface GameContextObjectEntry { proximityPrompt?: ProximityPrompt; breakable?: false | {baseBreakTime: number}; slotInventory?: InventoryLayout; verbs?: readonly ContextVerb[]; colliders?: EntityColliderSet; halfExtents?: readonly [number, number, number] } — ⚠ undocumented
|
|
315
|
+
- `GameContextOptions` (interface): interface GameContextOptions<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> { definition: GameDefinition<TAssetRef, TMultiplayer>; content: GameContextContent; player: {userId: string; isNew: boolean}; territory?: Territory; now?: () => number; occluder?: (from: EntityPosition, to: EntityPosition) => boolean; seed?… — ⚠ undocumented
|
|
316
|
+
- `GameContextRace` (interface): interface GameContextRace { state(id: string, config?: RaceStateConfig): RaceState } — ⚠ undocumented
|
|
317
|
+
- `GameContextTurn` (interface): interface GameContextTurn { loop(id: string, config?: TurnLoopConfig): TurnLoop } — ⚠ undocumented
|
|
318
|
+
- `GameContextWorld` (interface): interface GameContextWorld { ground: TerrainField; groundHeightAt(x: number, z: number): number; solids: WorldSolids } — ⚠ undocumented
|
|
319
|
+
- `HitReactionInput` (interface): interface HitReactionInput — ⚠ undocumented · used by `resolveHitReaction` (@jgengine/core/combat): Resolves hit feel (hitstop, knockback impulse, camera shake) from either a named `impactPresets` event (`resolveHitReaction("explosion", inp…
|
|
303
320
|
- `MoveTowardCommitOptions` (interface): interface MoveTowardCommitOptions extends MoveTowardOptions — Options for {@link SceneEntityContext.moveTowardCommit}: {@link MoveTowardOptions} plus an optional facing turn.
|
|
304
|
-
- `SceneEntityContext` (interface): interface SceneEntityContext — ⚠ undocumented
|
|
305
|
-
- `SceneObjectContext` (interface): interface SceneObjectContext extends ObjectStore — ⚠ undocumented
|
|
306
|
-
- `SceneWorldItemContext` (interface): interface SceneWorldItemContext —
|
|
307
|
-
- `TelegraphInput` (interface): interface TelegraphInput — ⚠ undocumented
|
|
321
|
+
- `SceneEntityContext` (interface): interface SceneEntityContext { spawn(name: string, options?: SpawnOptions): string; despawn(instanceId: string): boolean; update: EntityStore["update"]; setPose(instanceId: string, pose: EntityPose): boolean; setPoseConstraint: EntityStore["setPoseConstraint"]; get(ins… — ⚠ undocumented
|
|
322
|
+
- `SceneObjectContext` (interface): interface SceneObjectContext extends ObjectStore { catalog(instanceId: string): GameContextObjectEntry | null; raycast(input: ObjectRaycastInput): ObjectRaycastHit | null; raycastAll(input: ObjectRaycastInput): readonly ObjectRaycastHit[]; setColliders(instanceId: string, colliders: Entit… — ⚠ undocumented
|
|
323
|
+
- `SceneWorldItemContext` (interface): interface SceneWorldItemContext — `ctx.scene.worldItem`: items lying in the world — spawn, list, find the nearest, and pick up into a player's bag.
|
|
324
|
+
- `TelegraphInput` (interface): interface TelegraphInput { from: string; shape: TelegraphShape; at: [number, number, number]; dir?: number; windupMs: number; kind?: string; effect?: {effect: string; via?: EffectVia; radius?: number; falloff?: "linear" | "none"; los?: boolean;} } — ⚠ undocumented
|
|
308
325
|
- `VfxInput` (interface): interface VfxInput — Request a transient spell/ability VFX burst. The easy path is a named `preset` — `vfx({ preset: "arrow", from: caster, to: enemy })` renders a visible bolt with no color or archetype tuning; `"lightning"`, `"web"`, `"slash"`, `"shield"`, `"heal"`, `"explosion"` and the rest of {@link vfxPresets} likewise just work. `from`/`to` accept an instance id (the shell follows its live pose) or a fixed world point. Anything you also pass — `kind`, `color` (`0xRRGGBB`), `radius`, `durationMs` — overrides the preset; supply `kind` + `color` yourself for a fully custom burst with no preset. `durationMs` defaults per `kind`.
|
|
309
326
|
- `WorldItemPickupResult` (type): type WorldItemPickupResult = | { status: "ok"; record: WorldItemRecord } | { status: "rejected"; reason: string } — ⚠ undocumented
|
|
310
327
|
- `createGameContext` (function): function createGameContext<TAssetRef extends ModelAssetRef, TMultiplayer>(options: GameContextOptions<TAssetRef, TMultiplayer>): GameContext — ⚠ undocumented
|
|
@@ -312,36 +329,36 @@
|
|
|
312
329
|
## @jgengine/core/runtime/gameContextTypes
|
|
313
330
|
|
|
314
331
|
- `CatalogEntityRole` (type): type CatalogEntityRole = "player" | "enemy" | "hostile" | "npc" | "vehicle" — ⚠ undocumented
|
|
315
|
-
- `FloatTextInput` (interface): interface FloatTextInput — ⚠ undocumented
|
|
332
|
+
- `FloatTextInput` (interface): interface FloatTextInput { instanceId?: string; position?: [number, number, number]; text?: string; kind?: string; amount?: number; hitType?: string; element?: string; crit?: boolean; scale?: number } — ⚠ undocumented
|
|
316
333
|
- `GameAudio` (interface): interface GameAudio — Reachable audio seam on `ctx.game`: `play`, `music`, and `resume` route through the `audio.play`/`audio.music`/`audio.resume` events the shell's audio engine listens on, so game code triggers sound without importing the shell. Retained `loop`/`setLoop`/`stopLoop` add id-keyed loops with live pitch/gain control over `audio.loopStart`/`audio.loopSet`/`audio.loopStop` (#1051).
|
|
317
334
|
- `GameContext` (interface): interface GameContext — The live engine handle a game's loop, systems, commands, and UI read and mutate — entities, objects, the reactive store, opted-in `game.*` subsystems, world queries, and the sim clock. One context = one running world.
|
|
318
|
-
- `GameContextCards` (interface): interface GameContextCards — ⚠ undocumented
|
|
319
|
-
- `GameContextCommands` (interface): interface GameContextCommands — ⚠ undocumented
|
|
320
|
-
- `GameContextContent` (interface): interface GameContextContent — ⚠ undocumented
|
|
321
|
-
- `GameContextEconomy` (interface): interface GameContextEconomy — ⚠ undocumented
|
|
322
|
-
- `GameContextEntityEntry` (interface): interface GameContextEntityEntry — ⚠ undocumented
|
|
323
|
-
- `GameContextFeed` (interface): interface GameContextFeed extends Omit<GameFeed, "bind"> — ⚠ undocumented
|
|
324
|
-
- `GameContextItemEntry` (interface): interface GameContextItemEntry — ⚠ undocumented
|
|
325
|
-
- `GameContextItemUse` (interface): interface GameContextItemUse — ⚠ undocumented
|
|
326
|
-
- `GameContextLoot` (interface): interface GameContextLoot — ⚠ undocumented
|
|
335
|
+
- `GameContextCards` (interface): interface GameContextCards { pile(id: string, config?: CardPileConfig): CardPile } — ⚠ undocumented
|
|
336
|
+
- `GameContextCommands` (interface): interface GameContextCommands { define<TInput>(name: string, definition: CommandDefinition<GameContext, TInput>): void; has(name: string): boolean; names(): string[]; run(name: string, input: unknown): CommandResult<GameContext>; runAs(actorUserId: string, name: string,… — ⚠ undocumented
|
|
337
|
+
- `GameContextContent` (interface): interface GameContextContent { itemById?(itemId: string): GameContextItemEntry | null | undefined; entityById?(catalogId: string): GameContextEntityEntry | null | undefined; objectById?(catalogId: string): GameContextObjectEntry | null | undefined } — ⚠ undocumented
|
|
338
|
+
- `GameContextEconomy` (interface): interface GameContextEconomy { balance(userId: string, currencyId: string | CurrencyDefinition): number; grant(userId: string, currencyId: string | CurrencyDefinition, amount: number): void; charge(userId: string, currencyId: string | CurrencyDefinition, amount: number… — ⚠ undocumented
|
|
339
|
+
- `GameContextEntityEntry` (interface): interface GameContextEntityEntry { stats?: StatCatalog; receive?: ReceiveMap; onDeath?: OnDeathSpec; movement?: PoseAllowedStates & {walkSpeed?: number}; role?: CatalogEntityRole; verbs?: readonly ContextVerb[]; colliders?: EntityColliderSet; scale?: number } — ⚠ undocumented · used by `entityEntryFromCatalog` (@jgengine/core/editor): Turns an authored entity row into the runtime {@link GameContextEntityEntry} a spawn consumes: looks up `entityId` in the document's {@link …
|
|
340
|
+
- `GameContextFeed` (interface): interface GameContextFeed extends Omit<GameFeed, "bind"> { bind(action: keyof GameEventMap): () => void } — ⚠ undocumented
|
|
341
|
+
- `GameContextItemEntry` (interface): interface GameContextItemEntry { use?: string; weapon?: Record<string, unknown>; trade?: TradeField; rarity?: string; baseType?: string } — ⚠ undocumented
|
|
342
|
+
- `GameContextItemUse` (interface): interface GameContextItemUse { register(handlers: Record<string, ItemUseHandler<GameContext>>): void; registered(): string[]; can(input: ItemUseInput): ItemUseRejection | null; use(input: ItemUseInput): ItemUseResult<GameContext> } — ⚠ undocumented
|
|
343
|
+
- `GameContextLoot` (interface): interface GameContextLoot { register(def: LootTableDef): void; has(id: string): boolean; roll(id: string, rng?: () => number): Drop[]; grantToPlayer(userId: string, drops: Drop[], source?: string): void } — ⚠ undocumented
|
|
327
344
|
- `GameContextModels` (interface): interface GameContextModels — Per-kind render-model lookup for {@link GameContextOptions.models}; a resolved `ModelConfig` satisfies {@link ModelBodySource} structurally.
|
|
328
|
-
- `GameContextObjectEntry` (interface): interface GameContextObjectEntry — ⚠ undocumented
|
|
329
|
-
- `GameContextOptions` (interface): interface GameContextOptions<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> — ⚠ undocumented
|
|
330
|
-
- `GameContextRace` (interface): interface GameContextRace — ⚠ undocumented
|
|
331
|
-
- `GameContextTurn` (interface): interface GameContextTurn — ⚠ undocumented
|
|
332
|
-
- `GameContextWorld` (interface): interface GameContextWorld — ⚠ undocumented
|
|
333
|
-
- `HitReactionInput` (interface): interface HitReactionInput — ⚠ undocumented
|
|
345
|
+
- `GameContextObjectEntry` (interface): interface GameContextObjectEntry { proximityPrompt?: ProximityPrompt; breakable?: false | {baseBreakTime: number}; slotInventory?: InventoryLayout; verbs?: readonly ContextVerb[]; colliders?: EntityColliderSet; halfExtents?: readonly [number, number, number] } — ⚠ undocumented
|
|
346
|
+
- `GameContextOptions` (interface): interface GameContextOptions<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> { definition: GameDefinition<TAssetRef, TMultiplayer>; content: GameContextContent; player: {userId: string; isNew: boolean}; territory?: Territory; now?: () => number; occluder?: (from: EntityPosition, to: EntityPosition) => boolean; seed?… — ⚠ undocumented
|
|
347
|
+
- `GameContextRace` (interface): interface GameContextRace { state(id: string, config?: RaceStateConfig): RaceState } — ⚠ undocumented
|
|
348
|
+
- `GameContextTurn` (interface): interface GameContextTurn { loop(id: string, config?: TurnLoopConfig): TurnLoop } — ⚠ undocumented
|
|
349
|
+
- `GameContextWorld` (interface): interface GameContextWorld { ground: TerrainField; groundHeightAt(x: number, z: number): number; solids: WorldSolids } — ⚠ undocumented
|
|
350
|
+
- `HitReactionInput` (interface): interface HitReactionInput { from: string; to: string; config: HitReactionConfig | ImpactPresetName; power?: number } — ⚠ undocumented · used by `resolveHitReaction` (@jgengine/core/combat): Resolves hit feel (hitstop, knockback impulse, camera shake) from either a named `impactPresets` event (`resolveHitReaction("explosion", inp…
|
|
334
351
|
- `MoveTowardCommitOptions` (interface): interface MoveTowardCommitOptions extends MoveTowardOptions — Options for {@link SceneEntityContext.moveTowardCommit}: {@link MoveTowardOptions} plus an optional facing turn.
|
|
335
|
-
- `SceneEntityContext` (interface): interface SceneEntityContext — ⚠ undocumented
|
|
336
|
-
- `SceneObjectContext` (interface): interface SceneObjectContext extends ObjectStore — ⚠ undocumented
|
|
337
|
-
- `SceneWorldItemContext` (interface): interface SceneWorldItemContext —
|
|
338
|
-
- `TelegraphInput` (interface): interface TelegraphInput — ⚠ undocumented
|
|
352
|
+
- `SceneEntityContext` (interface): interface SceneEntityContext { spawn(name: string, options?: SpawnOptions): string; despawn(instanceId: string): boolean; update: EntityStore["update"]; setPose(instanceId: string, pose: EntityPose): boolean; setPoseConstraint: EntityStore["setPoseConstraint"]; get(ins… — ⚠ undocumented
|
|
353
|
+
- `SceneObjectContext` (interface): interface SceneObjectContext extends ObjectStore { catalog(instanceId: string): GameContextObjectEntry | null; raycast(input: ObjectRaycastInput): ObjectRaycastHit | null; raycastAll(input: ObjectRaycastInput): readonly ObjectRaycastHit[]; setColliders(instanceId: string, colliders: Entit… — ⚠ undocumented
|
|
354
|
+
- `SceneWorldItemContext` (interface): interface SceneWorldItemContext — `ctx.scene.worldItem`: items lying in the world — spawn, list, find the nearest, and pick up into a player's bag.
|
|
355
|
+
- `TelegraphInput` (interface): interface TelegraphInput { from: string; shape: TelegraphShape; at: [number, number, number]; dir?: number; windupMs: number; kind?: string; effect?: {effect: string; via?: EffectVia; radius?: number; falloff?: "linear" | "none"; los?: boolean;} } — ⚠ undocumented
|
|
339
356
|
- `VfxInput` (interface): interface VfxInput — Request a transient spell/ability VFX burst. The easy path is a named `preset` — `vfx({ preset: "arrow", from: caster, to: enemy })` renders a visible bolt with no color or archetype tuning; `"lightning"`, `"web"`, `"slash"`, `"shield"`, `"heal"`, `"explosion"` and the rest of {@link vfxPresets} likewise just work. `from`/`to` accept an instance id (the shell follows its live pose) or a fixed world point. Anything you also pass — `kind`, `color` (`0xRRGGBB`), `radius`, `durationMs` — overrides the preset; supply `kind` + `color` yourself for a fully custom burst with no preset. `durationMs` defaults per `kind`.
|
|
340
357
|
- `WorldItemPickupResult` (type): type WorldItemPickupResult = | { status: "ok"; record: WorldItemRecord } | { status: "rejected"; reason: string } — ⚠ undocumented
|
|
341
358
|
|
|
342
359
|
## @jgengine/core/runtime/gameRuntime
|
|
343
360
|
|
|
344
|
-
- `GameRuntime` (type): type GameRuntime = { gameId: string; topology?: "shared" | "rooms"; save: SaveConfig; /** * Whether this runtime declares `loop.onTick`. A host's tick cron reads it to skip hydrating and * persisting a server whose `tick` is a no-op by construction — the difference between a world that * costs a ful… — ⚠ undocumented
|
|
361
|
+
- `GameRuntime` (type): type GameRuntime = { gameId: string; topology?: "shared" | "rooms"; save: SaveConfig; /** * Whether this runtime declares `loop.onTick`. A host's tick cron reads it to skip hydrating and * persisting a server whose `tick` is a no-op by construction — the difference between a world that * costs a ful… — ⚠ undocumented · used by `initialPlayerState`: Seed a fresh player through the same onNewPlayer hook as joining, without hydrating or modifying a world.
|
|
345
362
|
- `GameRuntimeDefinition` (type): type GameRuntimeDefinition = { gameId: string; topology?: "shared" | "rooms"; save: SaveConfig; commands: Record<string, CommandDef>; loop?: ServerLoopHooks; } — ⚠ undocumented
|
|
346
363
|
- `HydrateInput` (type): type HydrateInput = { gameId: string; serverId: string; serverRow: RuntimeServerRow; playersByUserId: Record<string, RuntimePlayerRow>; chunksByKey: Record<string, RuntimeChunkRow>; revision?: number; /** Host wall clock for `onInit`, in ms. Defaults to `Date.now()`. */ nowMs?: number; } — ⚠ undocumented
|
|
347
364
|
- `RuntimeInitContext` (type): type RuntimeInitContext = { snapshot: GameRuntimeSnapshot; setSnapshot: (snapshot: GameRuntimeSnapshot) => void; /** * Host wall clock at the start of this call, in ms. The host already knows it, so anything keyed to * real time — a UTC date rollover, a `lastTickAt` anchor other code paths read, a s… — ⚠ undocumented
|
|
@@ -355,27 +372,27 @@
|
|
|
355
372
|
- `HeadlessCommandInterface` (interface): interface HeadlessCommandInterface — Canvas-free command/intent surface for {@link HeadlessRunner} — invoke a game's registered UI commands and read the resulting reactive state off `ctx`.
|
|
356
373
|
- `HeadlessInput` (interface): interface HeadlessInput — One step's worth of player intent handed to {@link HeadlessRunner.step} — the held-action set and pointer state the shell would otherwise publish from the browser each frame.
|
|
357
374
|
- `HeadlessRunner` (interface): interface HeadlessRunner — A renderer-free driver for a game loop: builds a {@link GameContext} from a {@link GameDefinition}, runs the init hooks, then advances the simulation one step at a time from injected input. No React, R3F, or three.js — the whole play path (time, input, `onTick`, behaviour nav, optional player movement) runs from `core` primitives alone, so a non-React host (a server tick, a test, a CLI replay) can play a real game and read its world snapshot. The shell's FrameDriver is one such driver bolted to `useFrame`; this is the same step distilled out of the render tree.
|
|
358
|
-
- `HeadlessRunnerOptions` (interface): interface HeadlessRunnerOptions<TAssetRef extends ModelAssetRef, TMultiplayer> — ⚠ undocumented
|
|
375
|
+
- `HeadlessRunnerOptions` (interface): interface HeadlessRunnerOptions<TAssetRef extends ModelAssetRef, TMultiplayer> { definition: GameDefinition<TAssetRef, TMultiplayer>; content?: GameContextContent; loop?: GameLoop<GameContext>; player?: {userId: string; isNew: boolean}; now?: () => number; maxStepSeconds?: number; playerMovement?: boolean; movement?: … — ⚠ undocumented
|
|
359
376
|
- `createHeadlessRunner` (function): function createHeadlessRunner<TAssetRef extends ModelAssetRef, TMultiplayer>(options: HeadlessRunnerOptions<TAssetRef, TMultiplayer>): HeadlessRunner — ⚠ undocumented
|
|
360
377
|
|
|
361
378
|
## @jgengine/core/runtime/hostPersistence
|
|
362
379
|
|
|
363
380
|
- `FEED_RING_LIMIT` (const): const FEED_RING_LIMIT: 20 — ⚠ undocumented
|
|
364
381
|
- `GameServerRecord` (type): type GameServerRecord = { serverId: string; gameId: string; status: GameServerStatus; mode?: string; modeConfig?: unknown; label?: string; visibility?: SessionVisibility; joinCode?: string; tags?: string[]; memberUserIds: string[]; slotsPerServer: number; save: SaveConfig; serverState: RuntimeServer… — ⚠ undocumented
|
|
365
|
-
- `GameServerStatus` (type): type GameServerStatus = "open" | "running" | "closed" — ⚠ undocumented
|
|
366
|
-
- `HostPersistence` (type): type HostPersistence = { savePlan?: (plan: ServerPersistPlan) => Promise<void>; resetScenario?: (reset: NormalizedScenarioReset) => Promise<void>; loadServer: (serverId: string) => Promise<GameServerRecord | null>; saveServer: (record: GameServerRecord) => Promise<void>; listServers: (gameId: string… — ⚠ undocumented
|
|
382
|
+
- `GameServerStatus` (type): type GameServerStatus = "open" | "running" | "closed" — ⚠ undocumented · used by `statusAfterLeave` (@jgengine/core/runtime/hostPolicy): Status after a leave: empty rooms reopen; non-empty rooms keep their current status.
|
|
383
|
+
- `HostPersistence` (type): type HostPersistence = { savePlan?: (plan: ServerPersistPlan) => Promise<void>; resetScenario?: (reset: NormalizedScenarioReset) => Promise<void>; loadServer: (serverId: string) => Promise<GameServerRecord | null>; saveServer: (record: GameServerRecord) => Promise<void>; listServers: (gameId: string… — ⚠ undocumented · used by `memoryPersistence` (@jgengine/node): Creates an in-memory `HostPersistence` implementation, useful for tests and ephemeral hosts.
|
|
367
384
|
- `LEADERBOARD_PENDING_KEY` (const): const LEADERBOARD_PENDING_KEY: "leaderboardPending" — ⚠ undocumented
|
|
368
385
|
- `LEADERBOARD_TOP_LIMIT` (const): const LEADERBOARD_TOP_LIMIT: 100 — ⚠ undocumented
|
|
369
386
|
- `LeaderboardEntry` (type): type LeaderboardEntry = { userId: string; value: number; } — ⚠ undocumented
|
|
370
387
|
- `LeaderboardIncrement` (type): type LeaderboardIncrement = { userId: string; stat: string; scope: LeaderboardScope; serverId?: string; by: number; } — ⚠ undocumented
|
|
371
|
-
- `LeaderboardRow` (type): type LeaderboardRow = { gameId: string; stat: string; scope: LeaderboardScope; serverId?: string; userId: string; value: number; updatedAt: number; } — ⚠ undocumented
|
|
388
|
+
- `LeaderboardRow` (type): type LeaderboardRow = { gameId: string; stat: string; scope: LeaderboardScope; serverId?: string; userId: string; value: number; updatedAt: number; } — ⚠ undocumented · used by `createLeaderboard` (@jgengine/core/game/leaderboard): Ranked score tracking across global, server, and per-profile scopes, with top-N queries and per-profile lookups.
|
|
372
389
|
- `OPEN_SERVER_LISTING_LIMIT` (const): const OPEN_SERVER_LISTING_LIMIT: 20 — ⚠ undocumented
|
|
373
390
|
- `OPEN_SERVER_LISTING_MAX` (const): const OPEN_SERVER_LISTING_MAX: 100 — ⚠ undocumented
|
|
374
391
|
- `PlayerProfileRecord` (type): type PlayerProfileRecord = { userId: string; gameId: string; playerState: RuntimePlayerRow; revision: number; updatedAt: number; } — ⚠ undocumented
|
|
375
392
|
- `ServerListing` (type): type ServerListing = { serverId: string; status: GameServerStatus; memberCount: number; slotsPerServer: number; mode?: string; label?: string; visibility?: SessionVisibility; joinCode?: string; tags?: string[]; updatedAt: number; } — ⚠ undocumented
|
|
376
393
|
- `ServerPersistPlan` (type): type ServerPersistPlan = { server: GameServerRecord; profiles: PlayerProfileRecord[]; chunks: WorldChunkRecord[]; deletedChunks: string[]; leaderboard: LeaderboardIncrement[]; /** * Which fields of the server row the plan actually moved. A store that pays per written field — * or whose subscribers r… — ⚠ undocumented
|
|
377
394
|
- `SessionAttributes` (type): type SessionAttributes = { label?: string; mode?: string; visibility?: SessionVisibility; joinCode?: string; tags?: string[]; } — ⚠ undocumented
|
|
378
|
-
- `SessionVisibility` (type): type SessionVisibility = "public" | "private" — ⚠ undocumented
|
|
395
|
+
- `SessionVisibility` (type): type SessionVisibility = "public" | "private" — ⚠ undocumented · used by `isAutoJoinCandidate` (@jgengine/core/runtime/hostPolicy): Auto-match candidate when no `serverId` is supplied: already a member, or a public room with free capacity.
|
|
379
396
|
- `ToServerListingOptions` (type): type ToServerListingOptions = { includeJoinCode?: boolean; } — ⚠ undocumented
|
|
380
397
|
- `WorldChunkRecord` (type): type WorldChunkRecord = { serverId: string; chunkKey: string; snapshot: RuntimeChunkRow; updatedAt: number; } — ⚠ undocumented
|
|
381
398
|
- `isSnapshotClean` (function): function isSnapshotClean(snapshot: GameRuntimeSnapshot): boolean — True when a snapshot carries no unwritten mutation — nothing for a persist to do.
|
|
@@ -432,12 +449,23 @@
|
|
|
432
449
|
## @jgengine/core/runtime/inputSnapshot
|
|
433
450
|
|
|
434
451
|
- `InputFrame` (interface): interface InputFrame — One client's input for a tick — the semantic held-action set plus pointer state, the serializable, over-the-wire counterpart of {@link InputSnapshot} the host stores per connected player.
|
|
435
|
-
- `InputSnapshot` (interface): interface InputSnapshot — ⚠ undocumented
|
|
452
|
+
- `InputSnapshot` (interface): interface InputSnapshot { rumble(userId: string, options: {strong: number; weak: number; ms: number}): Promise<boolean>; haptics(userId: string): HapticChannels; publish(held: readonly string[]): void; publishPointer(state: PointerAxisState | null): void; publishA… — ⚠ undocumented · used by `GamepadSource` (@jgengine/shell/input/gamepadSource): Poll browser gamepads and feed semantic actions into the shell tracker.
|
|
436
453
|
- `createInputSnapshot` (function): function createInputSnapshot(): InputSnapshot — ⚠ undocumented
|
|
437
454
|
|
|
455
|
+
## @jgengine/core/runtime/localPlayers
|
|
456
|
+
|
|
457
|
+
- `LocalPlayerAssignment` (interface): interface LocalPlayerAssignment — Result of {@link LocalPlayers.assign}.
|
|
458
|
+
- `LocalPlayerSlot` (interface): interface LocalPlayerSlot — One local seat on a shared screen: a stable slot id, the user id the game spawns for it, and the device driving it.
|
|
459
|
+
- `LocalPlayers` (interface): interface LocalPlayers — Seats for couch co-op: devices hot-join into slots, each slot has its own input.
|
|
460
|
+
- `LocalPlayersConfig` (interface): interface LocalPlayersConfig — Retunable seat policy.
|
|
461
|
+
- `LocalPlayersOptions` (interface): interface LocalPlayersOptions extends LocalPlayersConfig — Options for {@link createLocalPlayers}.
|
|
462
|
+
- `LocalPlayersSnapshot` (interface): interface LocalPlayersSnapshot — Serializable seat table.
|
|
463
|
+
- `createLocalPlayers` (function): function createLocalPlayers(options: LocalPlayersOptions): LocalPlayers — Seat table for local multiplayer on one screen.
|
|
464
|
+
- `localPlayers` (function): function localPlayers(ctx: GameContext): LocalPlayers — The seat table of a game context, created on first use with one seat for `ctx.player` and `ctx.input`. The shell retunes it from `defineGame({ localPlayers })` and hot-joins pads into it.
|
|
465
|
+
|
|
438
466
|
## @jgengine/core/runtime/motionIntents
|
|
439
467
|
|
|
440
|
-
- `MotionIntentBatch` (interface): interface MotionIntentBatch — ⚠ undocumented
|
|
468
|
+
- `MotionIntentBatch` (interface): interface MotionIntentBatch { impulses: readonly number[]; horizontalImpulses: readonly (readonly [number, number])[]; verticalVelocity: number | null; y: number | null } — ⚠ undocumented
|
|
441
469
|
- `MotionIntents` (interface): interface MotionIntents — Seam for game code to reach the motion the shell's FrameDriver otherwise owns privately (#162.4). Game code calls `impulse`, `pushHorizontal`, `setVerticalVelocity`, and/or `setY` from `onTick` or commands; the shell calls `takePending()` once per frame, before integrating gravity, to drain what accumulated. `setY` wins over physics for that frame; impulses add to the velocity the driver is about to integrate; a later `setVerticalVelocity` replaces that velocity outright. Horizontal pushes compose with the walk controller (#282.4): they add to its horizontal velocity and decay naturally as it re-blends toward input — knockback, dashes, explosion shoves without raw `setPose` offsets.
|
|
442
470
|
|
|
443
471
|
## @jgengine/core/runtime/objectRows
|
|
@@ -451,11 +479,11 @@
|
|
|
451
479
|
|
|
452
480
|
## @jgengine/core/runtime/persistenceScope
|
|
453
481
|
|
|
454
|
-
- `NormalizedScenarioReset` (interface): interface NormalizedScenarioReset — ⚠ undocumented
|
|
482
|
+
- `NormalizedScenarioReset` (interface): interface NormalizedScenarioReset { gameId: string; serverId: string | null; wipeChunks: boolean; wipeServerSession: boolean; resetPlayers: "run" | "none"; runFields: readonly string[] } — ⚠ undocumented
|
|
455
483
|
- `PersistenceScope` (type): type PersistenceScope = "run" | "meta" — ⚠ undocumented
|
|
456
|
-
- `ScenarioReset` (interface): interface ScenarioReset — ⚠ undocumented
|
|
457
|
-
- `ScopeSchema` (interface): interface ScopeSchema — ⚠ undocumented
|
|
458
|
-
- `ScopedState` (interface): interface ScopedState<T = Record<string, unknown>> — ⚠ undocumented
|
|
484
|
+
- `ScenarioReset` (interface): interface ScenarioReset { gameId: string; serverId?: string; wipeChunks?: boolean; wipeServerSession?: boolean; resetPlayers?: PersistenceScope | "none"; runFields?: readonly string[] } — ⚠ undocumented
|
|
485
|
+
- `ScopeSchema` (interface): interface ScopeSchema { run: readonly string[] } — ⚠ undocumented
|
|
486
|
+
- `ScopedState` (interface): interface ScopedState<T = Record<string, unknown>> { meta: Partial<T>; run: Partial<T> } — ⚠ undocumented
|
|
459
487
|
- `applyRunReset` (function): function applyRunReset(profile: PlayerProfileRecord, runFields: readonly string[], now: number): PlayerProfileRecord — ⚠ undocumented
|
|
460
488
|
- `clearRunFields` (function): function clearRunFields(player: RuntimePlayerRow, runFields: readonly string[]): RuntimePlayerRow — ⚠ undocumented
|
|
461
489
|
- `mergeScopes` (function): function mergeScopes<T extends Record<string, unknown>>(scoped: ScopedState<T>): Partial<T> — ⚠ undocumented
|
|
@@ -490,7 +518,7 @@
|
|
|
490
518
|
|
|
491
519
|
## @jgengine/core/runtime/save
|
|
492
520
|
|
|
493
|
-
- `SaveConfig` (type): type SaveConfig = | "none" | { auto: string; scope: SaveScope; } — ⚠ undocumented
|
|
521
|
+
- `SaveConfig` (type): type SaveConfig = | "none" | { auto: string; scope: SaveScope; } — ⚠ undocumented · used by `persistServerSnapshot` (@jgengine/convex): Write a snapshot back: server row, dirty player profiles, dirty chunks, and drained leaderboard increments, all under `save`.
|
|
494
522
|
- `SaveScope` (type): type SaveScope = "player" | "chunks" | "player+chunks" — ⚠ undocumented
|
|
495
523
|
|
|
496
524
|
## @jgengine/core/runtime/simContext
|
|
@@ -519,13 +547,13 @@
|
|
|
519
547
|
|
|
520
548
|
## @jgengine/core/runtime/snapshot
|
|
521
549
|
|
|
522
|
-
- `GameRuntimeSnapshot` (type): type GameRuntimeSnapshot = { version: number; gameId: string; serverId: string; server: RuntimeServerRow; players: Record<string, RuntimePlayerRow>; chunks: Record<string, RuntimeChunkRow>; revision: number; dirty: { server: boolean; players: string[]; chunks: string[]; }; } — ⚠ undocumented
|
|
550
|
+
- `GameRuntimeSnapshot` (type): type GameRuntimeSnapshot = { version: number; gameId: string; serverId: string; server: RuntimeServerRow; players: Record<string, RuntimePlayerRow>; chunks: Record<string, RuntimeChunkRow>; revision: number; dirty: { server: boolean; players: string[]; chunks: string[]; }; } — ⚠ undocumented · used by `isSnapshotClean` (@jgengine/core/runtime/hostPersistence): True when a snapshot carries no unwritten mutation — nothing for a persist to do.
|
|
523
551
|
- `RUNTIME_SNAPSHOT_VERSION` (const): const RUNTIME_SNAPSHOT_VERSION: 1 — ⚠ undocumented
|
|
524
|
-
- `RuntimeChunkRow` (type): type RuntimeChunkRow = { /** Cell key to owner id, persisted with its spatial chunk. */ territory?: Record<string, string>; territoryReceipts?: Record<string, { claimedAt: number; costPaid: number }>; chunkKey: string; objects: RuntimeObjectRow[]; entities: RuntimeEntityRow[]; flags?: Record<string,… — ⚠ undocumented
|
|
552
|
+
- `RuntimeChunkRow` (type): type RuntimeChunkRow = { /** Cell key to owner id, persisted with its spatial chunk. */ territory?: Record<string, string>; territoryReceipts?: Record<string, { claimedAt: number; costPaid: number }>; chunkKey: string; objects: RuntimeObjectRow[]; entities: RuntimeEntityRow[]; flags?: Record<string,… — ⚠ undocumented · used by `createEmptyChunkRow` (@jgengine/core/runtime/worldChunks): An empty chunk row for `chunkKey` — the starting value for a cell nothing has been placed in yet.
|
|
525
553
|
- `RuntimeEntityRow` (type): type RuntimeEntityRow = { instanceId: string; catalogId: string; position?: [number, number, number]; rotationY?: number; parentSpace?: string; group?: string; stats?: Record<string, { current: number; max: number; min?: number }>; targetInstanceId?: string | null; userId?: string; } — ⚠ undocumented
|
|
526
554
|
- `RuntimeInventorySlot` (type): type RuntimeInventorySlot = { item: string; count: number; slot?: number; } — ⚠ undocumented
|
|
527
555
|
- `RuntimeObjectRow` (type): type RuntimeObjectRow = { instanceId: string; catalogId: string; position: [number, number, number]; rotationY?: number; parentSpace?: string; /** Opaque per-instance state; the persisted name for `SceneObject.state`. */ flags?: Record<string, unknown>; /** Container contents for a catalog `slotInve… — The persisted form of a placed `SceneObject`: identity, placement, per-instance state, and slot contents. Convert with `toRuntimeObjectRow`/`fromRuntimeObjectRow` (`runtime/objectRows`).
|
|
528
|
-
- `RuntimePlayerRow` (type): type RuntimePlayerRow = { territoryOwnedCount?: number; ownedTerritoryChunkKeys?: string[]; userId: string; inventories: Record<string, RuntimeInventorySlot[]>; economy: Record<string, number>; unlocks: string[]; quests?: unknown; social?: unknown; leaderboard?: Record<string, number>; session?: Rec… — ⚠ undocumented
|
|
556
|
+
- `RuntimePlayerRow` (type): type RuntimePlayerRow = { territoryOwnedCount?: number; ownedTerritoryChunkKeys?: string[]; userId: string; inventories: Record<string, RuntimeInventorySlot[]>; economy: Record<string, number>; unlocks: string[]; quests?: unknown; social?: unknown; leaderboard?: Record<string, number>; session?: Rec… — ⚠ undocumented · used by `initialPlayerState` (@jgengine/core/runtime/gameRuntime): Seed a fresh player through the same onNewPlayer hook as joining, without hydrating or modifying a world.
|
|
529
557
|
- `RuntimeProfileRow` (type): type RuntimeProfileRow = { userId: string; gameId: string; player: RuntimePlayerRow; updatedAt: number; } — ⚠ undocumented
|
|
530
558
|
- `RuntimeServerRow` (type): type RuntimeServerRow = { entities: RuntimeEntityRow[]; objects: RuntimeObjectRow[]; session: Record<string, unknown>; feeds?: Record<string, unknown[]>; } — ⚠ undocumented
|
|
531
559
|
|
|
@@ -544,18 +572,19 @@
|
|
|
544
572
|
- `GameRuntimeFeeds` (type): type GameRuntimeFeeds = { subscribeServer: ( serverId: string, onChange: (view: GameRuntimeServerView | null) => void, ) => FeedUnsubscribe; subscribePlayer: ( args: { serverId: string }, onChange: (view: GameRuntimePlayerView | null) => void, ) => FeedUnsubscribe; subscribeFeed: ( args: { serverId:… — ⚠ undocumented
|
|
545
573
|
- `GameRuntimePlayerView` (type): type GameRuntimePlayerView = { userId: string; gameId: string; playerState: unknown; updatedAt: number; } — ⚠ undocumented
|
|
546
574
|
- `GameRuntimeServerView` (type): type GameRuntimeServerView = { serverId: string; gameId: string; revision: number; memberUserIds: string[]; serverState: unknown | WorldSyncFrame; updatedAt: number; } — ⚠ undocumented
|
|
547
|
-
- `GameRuntimeTransport` (type): type GameRuntimeTransport = { joinServer: (args: { gameId: string; serverId?: string; role?: MultiplayerRole
|
|
575
|
+
- `GameRuntimeTransport` (type): type GameRuntimeTransport = { /** * Join a server. `sessionId` names this client session (one tab, one mounted view) so the host keeps * the user a member while any of their sessions is still live. */ joinServer: (args: { gameId: string; serverId?: string; role?: MultiplayerRole; sessionId?: string … — ⚠ undocumented · used by `useServerSession` (@jgengine/react/useServerSession): Joins a host and exposes a retryable blocking state until membership is confirmed.
|
|
548
576
|
- `JoinServerOutcome` (type): type JoinServerOutcome = | (JoinServerResult & { ok: true }) | { ok: false; reason: "full" | "closed" | "unauthorized" } — Join failures are returned to the caller so the triggering surface can offer retry.
|
|
549
577
|
- `JoinServerResult` (type): type JoinServerResult = { serverId: string; isNew: boolean; resumeTicket?: ResumeTicket; } — ⚠ undocumented
|
|
550
578
|
- `LiveGameBackend` (type): type LiveGameBackend<TPresenceRow = unknown, TPresenceLocation = unknown, TGameId extends string = string> = GameBackend<TPresenceRow, TPresenceLocation, TGameId> & { presenceSync: PresenceSync; pushFeedEntry: (args: { serverId: string; action: string; entry: unknown }) => Promise<void>; chatSyncFor… — ⚠ undocumented
|
|
551
579
|
- `MultiplayerRole` (type): type MultiplayerRole = "player" | "spectator" — Connection role used for authoritative world access; spectators are read-only.
|
|
552
580
|
- `MultiplayerSession` (type): type MultiplayerSession = { gameId: string; userId: string; backend: LiveGameBackend; feedActions: string[]; } — ⚠ undocumented
|
|
553
|
-
- `PresencePoseRow` (type): type PresencePoseRow = { userId: string; /** Set when the host tracks presence per session, so one user can hold two rows. */ sessionId?: string; /** Actor class, e.g. `"player"` / `"agent"` — a host may clamp each differently. */ kind?: string; /** Display name carried on the row, so a nameplate ne… — ⚠ undocumented
|
|
581
|
+
- `PresencePoseRow` (type): type PresencePoseRow = { userId: string; /** Set when the host tracks presence per session, so one user can hold two rows. */ sessionId?: string; /** Actor class, e.g. `"player"` / `"agent"` — a host may clamp each differently. */ kind?: string; /** Display name carried on the row, so a nameplate ne… — ⚠ undocumented · used by `useShellMultiplayerSync` (@jgengine/shell/useShellMultiplayerSync): Joins the multiplayer server for the live context and wires presence, feed relay, and chat sync until teardown.
|
|
554
582
|
- `PresenceSync` (type): type PresenceSync = { subscribe: (serverId: string, onChange: (rows: PresencePoseRow[]) => void) => FeedUnsubscribe; syncPose: (serverId: string, pose: PlayerPose) => void; } — ⚠ undocumented
|
|
555
583
|
- `ResumeTicket` (type): type ResumeTicket = { userId: string; serverId: string; token: string; } — A short-lived credential allowing a disconnected client to reclaim one server membership.
|
|
556
584
|
- `RunCommandArgs` (type): type RunCommandArgs = { serverId: string; command: string; input: unknown; } — ⚠ undocumented
|
|
557
585
|
- `TransportRunCommandResult` (type): type TransportRunCommandResult = | { ok: true } | { ok: false; reason: string } — ⚠ undocumented
|
|
558
586
|
- `WorldSyncFrame` (type): type WorldSyncFrame = | { kind: "baseline"; revision: number; snapshot: WorldSnapshot } | { kind: "diff"; revision: number; diff: WorldDiff } — A revision-stamped world baseline or compact diff sent to a server-authoritative client.
|
|
587
|
+
- `createTransportSessionId` (function): function createTransportSessionId(): string — Fresh id for one client session's join/leave pair (see {@link GameRuntimeTransport.joinServer}).
|
|
559
588
|
|
|
560
589
|
## @jgengine/core/runtime/visibility
|
|
561
590
|
|
|
@@ -616,7 +645,7 @@
|
|
|
616
645
|
|
|
617
646
|
## @jgengine/shell/defineGame
|
|
618
647
|
|
|
619
|
-
- `GameConfig` (type): type GameConfig<TAssetRef extends ModelAssetRef = ModelAssetRef> = EngineFields<TAssetRef> & PresentationFields — ⚠ undocumented
|
|
648
|
+
- `GameConfig` (type): type GameConfig<TAssetRef extends ModelAssetRef = ModelAssetRef> = EngineFields<TAssetRef> & PresentationFields — ⚠ undocumented · used by `defineGame`: The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, au…
|
|
620
649
|
- `defineGame` (function): function defineGame<TAssetRef extends ModelAssetRef = ModelAssetRef>(config: GameConfig<TAssetRef>): PlayableGame — The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, authored scene) into a `PlayableGame` ready for `GameHost`. Defaults to solo/offline multiplayer; `editorLayers` auto-mounts the authored scene document.
|
|
621
650
|
|
|
622
651
|
## @jgengine/shell/gameKit
|
|
@@ -628,7 +657,7 @@
|
|
|
628
657
|
- `EditorDocument` (interface): interface EditorDocument — The full authored scene: every marker, volume, path, note, and sculpted terrain for a game.
|
|
629
658
|
- `EditorSummonModule` (interface): interface EditorSummonModule — Structural shape of the module `GameHost`'s `editor` loader resolves — `import("@jgengine/editor")` satisfies it.
|
|
630
659
|
- `GameCameraConfig` (interface): interface GameCameraConfig — Camera tuning for the shell's rig stack: pick the rig via `rig`, then tune it through its matching config block. All fields optional — the default is the third-person orbit rig.
|
|
631
|
-
- `GameConfig` (type): type GameConfig<TAssetRef extends ModelAssetRef = ModelAssetRef> = EngineFields<TAssetRef> & PresentationFields — ⚠ undocumented
|
|
660
|
+
- `GameConfig` (type): type GameConfig<TAssetRef extends ModelAssetRef = ModelAssetRef> = EngineFields<TAssetRef> & PresentationFields — ⚠ undocumented · used by `defineGame`: The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, au…
|
|
632
661
|
- `GameContext` (interface): interface GameContext — The live engine handle a game's loop, systems, commands, and UI read and mutate — entities, objects, the reactive store, opted-in `game.*` subsystems, world queries, and the sim clock. One context = one running world.
|
|
633
662
|
- `GameHost` (function): function GameHost({ playable, gameId, wsUrl, multiplayer, resolveMultiplayer, editor }: GameHostProps): React.JSX.Element — The one documented mount: resolves multiplayer for the playable and renders the shell. With the `editor` loader prop it also owns the whole editor summon (F2+E, `?mode=editor`, dev save endpoint).
|
|
634
663
|
- `GameHostProps` (interface): interface GameHostProps — Props for {@link GameHost}: the playable, optional multiplayer overrides, and the optional editor loader that enables the engine-owned F2+E summon.
|
|
@@ -12,6 +12,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
12
12
|
|
|
13
13
|
- `collectAuthoredTriggers` (function) · `import { collectAuthoredTriggers } from "@jgengine/shell/gameKit"`
|
|
14
14
|
|
|
15
|
+
## chase-view-cycle — cycle a vehicle camera between chase, hood and cockpit views at runtime
|
|
16
|
+
|
|
17
|
+
- `nextChaseView` (function) · `import { nextChaseView } from "@jgengine/core/runtime/cameraDirector"`
|
|
18
|
+
|
|
15
19
|
## client-side — input prediction with replay reconciliation
|
|
16
20
|
|
|
17
21
|
- `createPredictionBuffer` (function) · `import { createPredictionBuffer } from "@jgengine/core/runtime/prediction"`
|
|
@@ -20,6 +24,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
20
24
|
|
|
21
25
|
- `GameContextModels` (interface) · `import { GameContextModels } from "@jgengine/core/runtime/gameContext"`
|
|
22
26
|
|
|
27
|
+
## command-number — validate finite, bounded numbers and integers at command boundaries
|
|
28
|
+
|
|
29
|
+
- `readInputNumber` (function) · `import { readInputNumber } from "@jgengine/core/runtime/commandInput"`
|
|
30
|
+
|
|
31
|
+
## command-parse — parse untrusted command input once so every command stage sees typed, bounded values
|
|
32
|
+
|
|
33
|
+
- `defineCommand` (function) · `import { defineCommand } from "@jgengine/core/runtime/commandRunner"`
|
|
34
|
+
|
|
23
35
|
## command-quantity — validate bounded whole-item counts at command boundaries
|
|
24
36
|
|
|
25
37
|
- `readQuantity` (function) · `import { readQuantity } from "@jgengine/core/runtime/commandInput"`
|
|
@@ -40,6 +52,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
40
52
|
|
|
41
53
|
- `EditorCatalogDefinition` (interface) · `import { EditorCatalogDefinition } from "@jgengine/shell/gameKit"`
|
|
42
54
|
|
|
55
|
+
## fire-input — bind a key or mouse button to a named action that runs the same-named command with the aim; repeatMs auto-fires while held
|
|
56
|
+
|
|
57
|
+
- `ActionCodesMap` (type) · `import { ActionCodesMap } from "@jgengine/core/authoring"`
|
|
58
|
+
|
|
43
59
|
## game-system — declare a composable capability with its own schedule and lifecycle
|
|
44
60
|
|
|
45
61
|
- `SystemDefinition` (interface) · `import { SystemDefinition } from "@jgengine/shell/gameKit"`
|
|
@@ -96,6 +112,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
96
112
|
|
|
97
113
|
- `createInputRecorder` (function) · `import { createInputRecorder } from "@jgengine/core/runtime/inputRecorder"`
|
|
98
114
|
|
|
115
|
+
## local-players — Couch co-op seats: devices hot-join into slots, each with its own user id and input snapshot.
|
|
116
|
+
|
|
117
|
+
- `createLocalPlayers` (function) · `import { createLocalPlayers } from "@jgengine/core/runtime/localPlayers"`
|
|
118
|
+
|
|
99
119
|
## loot-table — validate a loot table definition for use with the registry
|
|
100
120
|
|
|
101
121
|
- `lootTable` (function) · `import { lootTable } from "@jgengine/core/authoring"`
|
|
@@ -178,6 +198,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
178
198
|
|
|
179
199
|
- `defineStore` (function) · `import { defineStore } from "@jgengine/shell/gameKit"`
|
|
180
200
|
|
|
201
|
+
## warn-once — emit a dev-only warning a single time per call site
|
|
202
|
+
|
|
203
|
+
- `warnOnce` (function) · `import { warnOnce } from "@jgengine/core/devtools/warnOnce"`
|
|
204
|
+
|
|
181
205
|
## world-chunk-key — map world positions to persisted chunk keys at a declared cell size
|
|
182
206
|
|
|
183
207
|
- `chunkKeyOf` (function) · `import { chunkKeyOf } from "@jgengine/core/runtime/worldChunks"`
|
|
@@ -193,3 +217,7 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
193
217
|
## world-environment — compose editor/preset-derived outdoor dressing — terrain, weather, water, structures — into a legacy environment feature
|
|
194
218
|
|
|
195
219
|
- `environment` (function) · `import { environment } from "@jgengine/shell/gameKit"`
|
|
220
|
+
|
|
221
|
+
## world-item-pickup — pick up dropped items: ctx.scene.worldItem nearestInRadius → pickup into the player's inventory
|
|
222
|
+
|
|
223
|
+
- `SceneWorldItemContext` (interface) · `import { SceneWorldItemContext } from "@jgengine/core/runtime/gameContext"`
|
|
@@ -45,6 +45,9 @@ spatial place. When the game *is* a place, declare only substrate + laws with `w
|
|
|
45
45
|
`@jgengine/core/world/place` (e.g. `{ id, ground: { mode: "flat", size: { x: Infinity, z: Infinity } }, physics: { gravity: -24 } }`,
|
|
46
46
|
or `mode: "board"` for 2D). Never seed, sky, or vegetation in `world` — the engine renders its
|
|
47
47
|
default sky until you author one in the editor, and foliage is editor scatter over the ground.
|
|
48
|
+
A created 3D game instead ships hills as a terrain sculpt in `editor.scene.json` and a `world.ts`
|
|
49
|
+
that consumes it with `environment({ sculpt })` via `environmentContentFromDocument`; the file
|
|
50
|
+
holds only the ground palette. Reshape the hills in the editor, not in code.
|
|
48
51
|
|
|
49
52
|
|
|
50
53
|
```ts
|
|
@@ -4,53 +4,53 @@
|
|
|
4
4
|
|
|
5
5
|
## @jgengine/assets
|
|
6
6
|
|
|
7
|
-
- `AssetAlias` (interface): interface AssetAlias — ⚠ undocumented
|
|
7
|
+
- `AssetAlias` (interface): interface AssetAlias { key: string; target: string } — ⚠ undocumented · used by `aliases`: Semantic keys → live catalog ids.
|
|
8
8
|
- `AssetDownload` (type): type AssetDownload = PinnedDownload | ScrapeDownload — ⚠ undocumented
|
|
9
9
|
- `AssetImportKind` (type): type AssetImportKind = "model" | "texture" | "material" | "sprite" | "spriteSheet" | "audio" | "font" | "hdri" — Supported logical kinds for user-supplied assets.
|
|
10
10
|
- `AssetImportSpec` (interface): interface AssetImportSpec — A validated description of files to ingest into a game project.
|
|
11
11
|
- `AssetKind` (type): type AssetKind = "model" | "pack" | "material" | "component" | "icon" | "sprite" | "spritePack" — ⚠ undocumented
|
|
12
|
-
- `AssetMatch` (type): type AssetMatch = | { kind: "model"; id: string; source: string; file?: string; via: "index" | "alias" | "single" } | { kind: "pack"; source: string; title: string; categories: readonly string[] } | { kind: "material"; id: string; title: string; categories: readonly string[] } | { kind: "component";… — ⚠ undocumented
|
|
12
|
+
- `AssetMatch` (type): type AssetMatch = | { kind: "model"; id: string; source: string; file?: string; via: "index" | "alias" | "single" } | { kind: "pack"; source: string; title: string; categories: readonly string[] } | { kind: "material"; id: string; title: string; categories: readonly string[] } | { kind: "component";… — ⚠ undocumented · used by `findAssets`: The ranked matches for a query — models, packs, HUD components, and icons in one list.
|
|
13
13
|
- `AssetProvenance` (interface): interface AssetProvenance — The resolved ownership of one logical asset id — the provisioning contract for that id.
|
|
14
14
|
- `AssetProvenanceKind` (type): type AssetProvenanceKind = "committed" | "provisioned" | "dangling" — How an asset id's bytes reach a clean checkout: - `committed` — resolves to a self-hosted / remote URL carried in the shipped index (a {@link SingleAsset}); no pull step, available immediately. - `provisioned` — a pack {@link IndexEntry}; the id is declared but its GLB is fetched on demand by `assets pull <source>` into the consumer's served dir. - `dangling` — nothing in the index, singles, or aliases owns the id, so no provisioning step exists and it can never resolve on a clean clone.
|
|
15
15
|
- `AssetProvider` (type): type AssetProvider = | "quaternius" | "kaykit" | "polypizza" | "itch" | "ambientcg" | "gameicons" | "custom" — ⚠ undocumented
|
|
16
16
|
- `AssetReference` (interface): interface AssetReference — A logical asset id referenced by some consumer (a game, scene, or config), for validation.
|
|
17
|
-
- `AssetSource` (interface): interface AssetSource — ⚠ undocumented
|
|
17
|
+
- `AssetSource` (interface): interface AssetSource { id: string; kind?: AssetSourceKind; provider: AssetProvider; title: string; license: string; author: string; categories: readonly string[]; download: AssetDownload; homepage?: string; mirror?: string; unpulled?: string } — ⚠ undocumented · used by `ambientcgSources`: Every ambientCG material source, generated per family (`ambientcg-grass001` … ).
|
|
18
18
|
- `AssetSourceKind` (type): type AssetSourceKind = "model" | "material" | "sprite" — What a source's archive contains: GLB models (default), one PBR material's texture maps, or a pack of individual 2D sprite/icon files (SVG/PNG).
|
|
19
19
|
- `AssetSpace` (interface): interface AssetSpace — Catalog-level asset-space metadata: how a model is authored relative to the engine, owned by the catalog entry rather than re-derived per game. Every field is optional so an unmeasured asset stays valid; {@link resolveFacingRotationY}/{@link assetUnitScale}/{@link resolveAnchorOffset} supply the documented defaults.
|
|
20
|
-
- `BuildCatalogOptions` (interface): interface BuildCatalogOptions — ⚠ undocumented
|
|
20
|
+
- `BuildCatalogOptions` (interface): interface BuildCatalogOptions { basePath?: string; sources?: readonly string[]; includeAliases?: boolean; includeSingles?: boolean; extras?: readonly CatalogExtra[] } — ⚠ undocumented · used by `buildCatalog`: Builds a game's asset catalog from the generated CC0 pack index plus singles, extras, and aliases.
|
|
21
21
|
- `BuildMaterialCatalogOptions` (interface): interface BuildMaterialCatalogOptions — Options for `buildMaterialCatalog`.
|
|
22
22
|
- `BuildSpriteCatalogOptions` (interface): interface BuildSpriteCatalogOptions — Options for `buildSpriteCatalog`.
|
|
23
23
|
- `CollisionMeshData` (interface): interface CollisionMeshData — Compact, serializable triangle collision mesh measured at asset reindex — the renderer-free source of mesh-accurate hitboxes. Positions are welded onto a 16-bit grid spanning `min`..`max` (model space) and stored base64-encoded, so an opted-in catalog asset ships its collision triangles inside the generated index without the runtime ever touching the rendered scene graph.
|
|
24
24
|
- `ExtractedMaterialMap` (interface): interface ExtractedMaterialMap — One normalized map pulled out of a material archive by `extractMaterialMaps`.
|
|
25
25
|
- `ExtractedSpriteFile` (interface): interface ExtractedSpriteFile — One SVG/PNG file pulled out of a sprite/icon-pack archive by `extractSpriteFiles`.
|
|
26
|
-
- `FindOptions` (interface): interface FindOptions — ⚠ undocumented
|
|
26
|
+
- `FindOptions` (interface): interface FindOptions { kind?: AssetKind; limit?: number } — ⚠ undocumented · used by `findAssets`: The ranked matches for a query — models, packs, HUD components, and icons in one list.
|
|
27
27
|
- `ImportSpecValidation` (type): type ImportSpecValidation = { ok: true; entry: AssetImportSpec } | { ok: false; reason: string } — Result of validating an import description.
|
|
28
|
-
- `IndexEntry` (interface): interface IndexEntry — ⚠ undocumented
|
|
28
|
+
- `IndexEntry` (interface): interface IndexEntry { id: string; source: string; categories: readonly string[]; file: string; dims?: ModelDims; collisionMesh?: CollisionMeshData; clips?: readonly string[]; space?: AssetSpace } — ⚠ undocumented · used by `entryForSpriteFile`: Builds one sprite/icon `IndexEntry` — same shape as a model entry, minus `dims`.
|
|
29
29
|
- `MATERIAL_MAP_FILES` (const): const MATERIAL_MAP_FILES: { readonly color: "color.jpg"; readonly normal: "normal.jpg"; readonly roughness: "roughness.jpg"; readonly ao: "ao.jpg"; readonly displacement: "displacement.jpg"; readonly ktx2: "material.ktx2"; } — Normalized filenames a pulled material directory contains, keyed by map role.
|
|
30
30
|
- `MaterialCatalog` (interface): interface MaterialCatalog — Resolves material ids and `material/…` aliases to `MaterialRef`s.
|
|
31
31
|
- `MaterialMapRole` (type): type MaterialMapRole = keyof typeof MATERIAL_MAP_FILES — One PBR map's role within a material, including an optional compressed KTX2 asset.
|
|
32
32
|
- `MaterialMaps` (interface): interface MaterialMaps — URLs of one material's PBR maps; `ao`/`displacement` files may be absent from a rare pack.
|
|
33
33
|
- `MaterialRef` (interface): interface MaterialRef — A resolved material: identity, attribution, and its normalized map URLs.
|
|
34
34
|
- `ModelDims` (interface): interface ModelDims — Measured horizontal footprint, footprint center, and lowest Y of a model in model space.
|
|
35
|
-
- `ModelSnippetOptions` (interface): interface ModelSnippetOptions — ⚠ undocumented
|
|
36
|
-
- `PinnedDownload` (interface): interface PinnedDownload — ⚠ undocumented
|
|
37
|
-
- `RankedMatch` (interface): interface RankedMatch — ⚠ undocumented
|
|
35
|
+
- `ModelSnippetOptions` (interface): interface ModelSnippetOptions { basePath?: string; seam?: "objectModels" | "entityModels" } — ⚠ undocumented · used by `modelWiringSnippet`: Copy-paste wiring for a pulled GLB id: resolve through the catalog, drop into a model seam.
|
|
36
|
+
- `PinnedDownload` (interface): interface PinnedDownload { url: string; sha256?: string } — ⚠ undocumented
|
|
37
|
+
- `RankedMatch` (interface): interface RankedMatch { match: AssetMatch; score: number } — ⚠ undocumented · used by `rankAssets`: Rank every catalog entry — models, packs, HUD components, icons — against one query.
|
|
38
38
|
- `ReferenceValidation` (interface): interface ReferenceValidation — The verdict for one {@link AssetReference}.
|
|
39
|
-
- `RegistryCatalog` (interface): interface RegistryCatalog — ⚠ undocumented
|
|
40
|
-
- `RegistryComponent` (interface): interface RegistryComponent — ⚠ undocumented
|
|
39
|
+
- `RegistryCatalog` (interface): interface RegistryCatalog { homepage: string; components: readonly RegistryComponent[]; icons: readonly string[] } — ⚠ undocumented
|
|
40
|
+
- `RegistryComponent` (interface): interface RegistryComponent { name: string; title: string; description: string } — ⚠ undocumented · used by `componentWiringSnippet`: Copy-paste wiring for a HUD component: the `shadcn add` command plus import + usage.
|
|
41
41
|
- `ReindexSpritesResult` (interface): interface ReindexSpritesResult — Per-source and total counts returned by `reindexSprites`.
|
|
42
42
|
- `ResolveProvenanceOptions` (interface): interface ResolveProvenanceOptions — Override the data {@link resolveProvenance} resolves against; every field defaults to the shipped catalog.
|
|
43
43
|
- `STARTER_ASSETS` (const): const STARTER_ASSETS: readonly StarterAsset[] — Flat list of every curated starter asset across themes.
|
|
44
44
|
- `STARTER_PACKS` (const): const STARTER_PACKS: Readonly<Record<StarterTheme, readonly StarterAsset[]>> — Theme → assets table for browse/docs (`STARTER_PACKS.people`, …).
|
|
45
45
|
- `STARTER_SOURCE_PACKS` (const): const STARTER_SOURCE_PACKS: readonly string[] — Source pack ids a game must pull so every starter asset URL resolves on disk.
|
|
46
46
|
- `STARTER_THEMES` (const): const STARTER_THEMES: readonly StarterTheme[] — Ordered starter theme ids: people, props, nature, urban.
|
|
47
|
-
- `ScrapeDownload` (interface): interface ScrapeDownload — ⚠ undocumented
|
|
48
|
-
- `SingleAsset` (interface): interface SingleAsset — ⚠ undocumented
|
|
47
|
+
- `ScrapeDownload` (interface): interface ScrapeDownload { scrape: string } — ⚠ undocumented
|
|
48
|
+
- `SingleAsset` (interface): interface SingleAsset { id: string; url: string; license: string; author: string; categories: readonly string[] } — ⚠ undocumented
|
|
49
49
|
- `StarterAsset` (interface): interface StarterAsset — One curated starter model: short id, theme, live catalog target, license, suggested height.
|
|
50
50
|
- `StarterTheme` (type): type StarterTheme = "people" | "props" | "nature" | "urban" — Curated drop-in starter themes for new games. Each entry maps a short semantic id (also registered as `asset:<id>`) to a live catalog GLB that is already measured (dims/anchor flow from the generated index via the alias target). Games wire them as `entityModels: { guest: "asset:person_casual" }`. Never Kenney — Quaternius / KayKit only.
|
|
51
51
|
- `ValidateAssetReferencesOptions` (interface): interface ValidateAssetReferencesOptions extends ResolveProvenanceOptions — Options for {@link validateAssetReferences}: the resolution data plus an optional on-disk `present` check.
|
|
52
52
|
- `ValidateAssetReferencesResult` (interface): interface ValidateAssetReferencesResult — The aggregate result of validating a set of references against the provisioning contract.
|
|
53
|
-
- `VerifyResult` (interface): interface VerifyResult — ⚠ undocumented
|
|
53
|
+
- `VerifyResult` (interface): interface VerifyResult { ok: boolean; errors: string[] } — ⚠ undocumented
|
|
54
54
|
- `aliases` (const): const aliases: readonly AssetAlias[] — Semantic keys → live catalog ids. Prefer these in games so re-homes only touch this table. Never point at kenney-*. Curated starter theme ids (`asset:person_casual`, `nature_tree`, …) come from {@link starterAliases}.
|
|
55
55
|
- `ambientcgSources` (const): const ambientcgSources: readonly AssetSource[] — Every ambientCG material source, generated per family (`ambientcg-grass001` … ).
|
|
56
56
|
- `buildCatalog` (function): function buildCatalog(options: BuildCatalogOptions = {}): AssetCatalog — Builds a game's asset catalog from the generated CC0 pack index plus singles, extras, and aliases. Registration order is packs → singles → {@link BuildCatalogOptions.extras | extras} → aliases: packs and singles come first, extras override them last-writer-wins, and aliases resolve last so they can target an extra. See {@link BuildCatalogOptions} for filtering and opt-outs.
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
|
|
101
101
|
## @jgengine/assets/catalogs/build
|
|
102
102
|
|
|
103
|
-
- `BuildCatalogOptions` (interface): interface BuildCatalogOptions — ⚠ undocumented
|
|
103
|
+
- `BuildCatalogOptions` (interface): interface BuildCatalogOptions { basePath?: string; sources?: readonly string[]; includeAliases?: boolean; includeSingles?: boolean; extras?: readonly CatalogExtra[] } — ⚠ undocumented · used by `buildCatalog`: Builds a game's asset catalog from the generated CC0 pack index plus singles, extras, and aliases.
|
|
104
104
|
- `CatalogExtra` (interface): interface CatalogExtra — A durable, hand-or-tool authored catalog entry that is not part of the generated pack index, singles, or aliases — the shape the editor writes into a promoted game's `src/game/assets.ts` when an asset is imported. `label` is human-facing only and is intentionally not registered into the catalog (it mirrors how a single asset's author/license live only in the source literal), so at runtime an extra resolves to exactly `{ url }`.
|
|
105
105
|
- `buildCatalog` (function): function buildCatalog(options: BuildCatalogOptions = {}): AssetCatalog — Builds a game's asset catalog from the generated CC0 pack index plus singles, extras, and aliases. Registration order is packs → singles → {@link BuildCatalogOptions.extras | extras} → aliases: packs and singles come first, extras override them last-writer-wins, and aliases resolve last so they can target an extra. See {@link BuildCatalogOptions} for filtering and opt-outs.
|
|
106
106
|
- `entryUrl` (function): function entryUrl(basePath: string, entry: IndexEntry): string — ⚠ undocumented
|
|
@@ -144,9 +144,9 @@
|
|
|
144
144
|
## @jgengine/assets/download
|
|
145
145
|
|
|
146
146
|
- `DEFAULT_RELEASE_BASE` (const): const DEFAULT_RELEASE_BASE: "https://github.com/Noisemaker111/jgengine/releases/download/packs" — Default asset mirror: this repo's own GitHub Releases, reachable from every cloud sandbox without network-policy changes (github.com is on the default allowlist). Assets live on the rolling `packs` release, one flat zip per pack named `<provider>-<packId>.zip`, kept in sync with the source catalog by `.github/workflows/mirror-assets.yml` — adding a catalog entry is the whole publishing step. Override the chain with `--mirror` / `JGENGINE_ASSETS_MIRROR`, or disable this hop with `JGENGINE_ASSETS_NO_DEFAULT_MIRROR=1`.
|
|
147
|
-
- `DownloadPackOptions` (interface): interface DownloadPackOptions — ⚠ undocumented
|
|
148
|
-
- `DownloadPackResult` (interface): interface DownloadPackResult — ⚠ undocumented
|
|
149
|
-
- `ExtractedGlb` (interface): interface ExtractedGlb — ⚠ undocumented
|
|
147
|
+
- `DownloadPackOptions` (interface): interface DownloadPackOptions { mirrorBase?: string; fetchImpl?: FetchLike } — ⚠ undocumented
|
|
148
|
+
- `DownloadPackResult` (interface): interface DownloadPackResult { archive: Uint8Array; url: string; attempted: readonly string[] } — ⚠ undocumented
|
|
149
|
+
- `ExtractedGlb` (interface): interface ExtractedGlb { file: string; bytes: Uint8Array } — ⚠ undocumented
|
|
150
150
|
- `ExtractedPack` (interface): interface ExtractedPack — What `extractGlbs` pulls out of a pack archive: the models plus the textures they reference.
|
|
151
151
|
- `ExtractedPackImage` (interface): interface ExtractedPackImage — An image a pack's models reference, shipped flat beside the `.glb` files.
|
|
152
152
|
- `ExtractedSpriteFile` (interface): interface ExtractedSpriteFile — One SVG/PNG file pulled out of a sprite/icon-pack archive by `extractSpriteFiles`.
|
|
@@ -159,9 +159,9 @@
|
|
|
159
159
|
## @jgengine/assets/find
|
|
160
160
|
|
|
161
161
|
- `AssetKind` (type): type AssetKind = "model" | "pack" | "material" | "component" | "icon" | "sprite" | "spritePack" — ⚠ undocumented
|
|
162
|
-
- `AssetMatch` (type): type AssetMatch = | { kind: "model"; id: string; source: string; file?: string; via: "index" | "alias" | "single" } | { kind: "pack"; source: string; title: string; categories: readonly string[] } | { kind: "material"; id: string; title: string; categories: readonly string[] } | { kind: "component";… — ⚠ undocumented
|
|
163
|
-
- `FindOptions` (interface): interface FindOptions — ⚠ undocumented
|
|
164
|
-
- `RankedMatch` (interface): interface RankedMatch — ⚠ undocumented
|
|
162
|
+
- `AssetMatch` (type): type AssetMatch = | { kind: "model"; id: string; source: string; file?: string; via: "index" | "alias" | "single" } | { kind: "pack"; source: string; title: string; categories: readonly string[] } | { kind: "material"; id: string; title: string; categories: readonly string[] } | { kind: "component";… — ⚠ undocumented · used by `findAssets`: The ranked matches for a query — models, packs, HUD components, and icons in one list.
|
|
163
|
+
- `FindOptions` (interface): interface FindOptions { kind?: AssetKind; limit?: number } — ⚠ undocumented · used by `findAssets`: The ranked matches for a query — models, packs, HUD components, and icons in one list.
|
|
164
|
+
- `RankedMatch` (interface): interface RankedMatch { match: AssetMatch; score: number } — ⚠ undocumented · used by `rankAssets`: Rank every catalog entry — models, packs, HUD components, icons — against one query.
|
|
165
165
|
- `findAssets` (function): function findAssets(query: string, options: FindOptions = {}): AssetMatch[] — The ranked matches for a query — models, packs, HUD components, and icons in one list.
|
|
166
166
|
- `rankAssets` (function): function rankAssets(query: string, options: FindOptions = {}): RankedMatch[] — Rank every catalog entry — models, packs, HUD components, icons — against one query.
|
|
167
167
|
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
|
|
186
186
|
## @jgengine/assets/indexGen
|
|
187
187
|
|
|
188
|
-
- `ReindexResult` (interface): interface ReindexResult — ⚠ undocumented
|
|
188
|
+
- `ReindexResult` (interface): interface ReindexResult { perSource: {source: string; count: number}[]; total: number } — ⚠ undocumented
|
|
189
189
|
- `entryForFile` (function): function entryForFile(source: AssetSource, file: string, dims?: ModelDims, collisionMesh?: CollisionMeshData, clips?: readonly string[]): IndexEntry — ⚠ undocumented
|
|
190
190
|
- `indexSourceDir` (function): function indexSourceDir(source: AssetSource, dir: string): IndexEntry[] — ⚠ undocumented
|
|
191
191
|
- `keyFromFile` (function): function keyFromFile(file: string): string — ⚠ undocumented
|
|
@@ -193,18 +193,18 @@
|
|
|
193
193
|
|
|
194
194
|
## @jgengine/assets/manifest
|
|
195
195
|
|
|
196
|
-
- `AssetAlias` (interface): interface AssetAlias — ⚠ undocumented
|
|
196
|
+
- `AssetAlias` (interface): interface AssetAlias { key: string; target: string } — ⚠ undocumented · used by `aliases` (@jgengine/assets): Semantic keys → live catalog ids.
|
|
197
197
|
- `AssetDownload` (type): type AssetDownload = PinnedDownload | ScrapeDownload — ⚠ undocumented
|
|
198
198
|
- `AssetProvider` (type): type AssetProvider = | "quaternius" | "kaykit" | "polypizza" | "itch" | "ambientcg" | "gameicons" | "custom" — ⚠ undocumented
|
|
199
|
-
- `AssetSource` (interface): interface AssetSource — ⚠ undocumented
|
|
199
|
+
- `AssetSource` (interface): interface AssetSource { id: string; kind?: AssetSourceKind; provider: AssetProvider; title: string; license: string; author: string; categories: readonly string[]; download: AssetDownload; homepage?: string; mirror?: string; unpulled?: string } — ⚠ undocumented · used by `ambientcgSources` (@jgengine/assets): Every ambientCG material source, generated per family (`ambientcg-grass001` … ).
|
|
200
200
|
- `AssetSourceKind` (type): type AssetSourceKind = "model" | "material" | "sprite" — What a source's archive contains: GLB models (default), one PBR material's texture maps, or a pack of individual 2D sprite/icon files (SVG/PNG).
|
|
201
201
|
- `AssetSpace` (interface): interface AssetSpace — Catalog-level asset-space metadata: how a model is authored relative to the engine, owned by the catalog entry rather than re-derived per game. Every field is optional so an unmeasured asset stays valid; {@link resolveFacingRotationY}/{@link assetUnitScale}/{@link resolveAnchorOffset} supply the documented defaults.
|
|
202
202
|
- `CollisionMeshData` (interface): interface CollisionMeshData — Compact, serializable triangle collision mesh measured at asset reindex — the renderer-free source of mesh-accurate hitboxes. Positions are welded onto a 16-bit grid spanning `min`..`max` (model space) and stored base64-encoded, so an opted-in catalog asset ships its collision triangles inside the generated index without the runtime ever touching the rendered scene graph.
|
|
203
|
-
- `IndexEntry` (interface): interface IndexEntry — ⚠ undocumented
|
|
203
|
+
- `IndexEntry` (interface): interface IndexEntry { id: string; source: string; categories: readonly string[]; file: string; dims?: ModelDims; collisionMesh?: CollisionMeshData; clips?: readonly string[]; space?: AssetSpace } — ⚠ undocumented · used by `entryForSpriteFile` (@jgengine/assets): Builds one sprite/icon `IndexEntry` — same shape as a model entry, minus `dims`.
|
|
204
204
|
- `ModelDims` (interface): interface ModelDims — Measured horizontal footprint, footprint center, and lowest Y of a model in model space.
|
|
205
|
-
- `PinnedDownload` (interface): interface PinnedDownload — ⚠ undocumented
|
|
206
|
-
- `ScrapeDownload` (interface): interface ScrapeDownload — ⚠ undocumented
|
|
207
|
-
- `SingleAsset` (interface): interface SingleAsset — ⚠ undocumented
|
|
205
|
+
- `PinnedDownload` (interface): interface PinnedDownload { url: string; sha256?: string } — ⚠ undocumented
|
|
206
|
+
- `ScrapeDownload` (interface): interface ScrapeDownload { scrape: string } — ⚠ undocumented
|
|
207
|
+
- `SingleAsset` (interface): interface SingleAsset { id: string; url: string; license: string; author: string; categories: readonly string[] } — ⚠ undocumented
|
|
208
208
|
- `isScrapeDownload` (function): function isScrapeDownload(download: AssetDownload): download is ScrapeDownload — ⚠ undocumented
|
|
209
209
|
|
|
210
210
|
## @jgengine/assets/materials
|
|
@@ -243,8 +243,8 @@
|
|
|
243
243
|
|
|
244
244
|
## @jgengine/assets/registry
|
|
245
245
|
|
|
246
|
-
- `RegistryCatalog` (interface): interface RegistryCatalog — ⚠ undocumented
|
|
247
|
-
- `RegistryComponent` (interface): interface RegistryComponent — ⚠ undocumented
|
|
246
|
+
- `RegistryCatalog` (interface): interface RegistryCatalog { homepage: string; components: readonly RegistryComponent[]; icons: readonly string[] } — ⚠ undocumented
|
|
247
|
+
- `RegistryComponent` (interface): interface RegistryComponent { name: string; title: string; description: string } — ⚠ undocumented · used by `componentWiringSnippet` (@jgengine/assets): Copy-paste wiring for a HUD component: the `shadcn add` command plus import + usage.
|
|
248
248
|
- `componentInstallUrl` (function): function componentInstallUrl(name: string): string — The `shadcn add` URL for a HUD component, e.g. `https://jgengine.com/r/vital-bar.json`.
|
|
249
249
|
- `registryCatalog` (const): const registryCatalog: RegistryCatalog — ⚠ undocumented
|
|
250
250
|
|
|
@@ -254,7 +254,7 @@
|
|
|
254
254
|
|
|
255
255
|
## @jgengine/assets/snippet
|
|
256
256
|
|
|
257
|
-
- `ModelSnippetOptions` (interface): interface ModelSnippetOptions — ⚠ undocumented
|
|
257
|
+
- `ModelSnippetOptions` (interface): interface ModelSnippetOptions { basePath?: string; seam?: "objectModels" | "entityModels" } — ⚠ undocumented · used by `modelWiringSnippet`: Copy-paste wiring for a pulled GLB id: resolve through the catalog, drop into a model seam.
|
|
258
258
|
- `componentWiringSnippet` (function): function componentWiringSnippet(component: RegistryComponent): string — Copy-paste wiring for a HUD component: the `shadcn add` command plus import + usage.
|
|
259
259
|
- `iconWiringSnippet` (function): function iconWiringSnippet(name: string): string — Copy-paste wiring for a HUD glyph from the registry `game-icon` catalog.
|
|
260
260
|
- `materialWiringSnippet` (function): function materialWiringSnippet(id: string, basePath = "/materials"): string — Copy-paste wiring for a pulled PBR material: resolve the map URLs through the material catalog, then apply them onto terrain or a model.
|
|
@@ -300,7 +300,7 @@
|
|
|
300
300
|
|
|
301
301
|
## @jgengine/assets/verify
|
|
302
302
|
|
|
303
|
-
- `VerifyInput` (interface): interface VerifyInput — ⚠ undocumented
|
|
304
|
-
- `VerifyResult` (interface): interface VerifyResult — ⚠ undocumented
|
|
303
|
+
- `VerifyInput` (interface): interface VerifyInput { sources: readonly AssetSource[]; singles: readonly SingleAsset[]; aliases: readonly AssetAlias[]; index: readonly IndexEntry[]; materialAliases?: readonly AssetAlias[] } — ⚠ undocumented
|
|
304
|
+
- `VerifyResult` (interface): interface VerifyResult { ok: boolean; errors: string[] } — ⚠ undocumented
|
|
305
305
|
- `verifyData` (function): function verifyData(input: VerifyInput): VerifyResult — ⚠ undocumented
|
|
306
306
|
- `verifyManifest` (function): function verifyManifest(): VerifyResult — ⚠ undocumented
|