@hypersoniclabs/helix-mcp 0.2.4 → 0.2.12
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/README.md +81 -11
- package/dist/continuumCanary.d.ts +17 -0
- package/dist/continuumCanary.js +17 -0
- package/dist/continuumCanary.js.map +1 -0
- package/dist/server.d.ts +14 -1
- package/dist/server.js +4058 -162
- package/dist/server.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/vehicleTools.d.ts +86 -0
- package/dist/vehicleTools.js +229 -0
- package/dist/vehicleTools.js.map +1 -0
- package/docs/avatar-face.md +115 -0
- package/docs/bridge.md +98 -0
- package/docs/bring-your-world.md +117 -0
- package/docs/catalog.md +69 -1
- package/docs/character-animation.md +442 -0
- package/docs/character-attachments.md +166 -0
- package/docs/character-world.md +785 -130
- package/docs/continuum.md +153 -0
- package/docs/items.md +73 -0
- package/docs/lighting-world.md +667 -0
- package/docs/locomotion-clip-spec.md +294 -0
- package/docs/manifest.md +31 -6
- package/docs/multiplayer-logic.md +460 -17
- package/docs/multiplayer-templates/chrono-orchard.md +36 -22
- package/docs/multiplayer-templates/collect-a-thon.md +28 -38
- package/docs/multiplayer-templates/collections.md +24 -24
- package/docs/multiplayer-templates/hangout.md +124 -111
- package/docs/multiplayer-templates/npc-wave.md +310 -0
- package/docs/multiplayer-templates/obby.md +13 -16
- package/docs/multiplayer-templates/persistent-progress.md +218 -0
- package/docs/multiplayer-templates/physics-bumper.md +27 -9
- package/docs/multiplayer-templates/physics-football.md +22 -8
- package/docs/multiplayer-templates/relic-bearers.md +12 -15
- package/docs/multiplayer-templates/server-motion.md +16 -19
- package/docs/multiplayer-templates/shooter-range.md +275 -0
- package/docs/multiplayer-templates/team-control.md +28 -15
- package/docs/multiplayer-templates/turn-arena.md +31 -22
- package/docs/multiplayer-templates/voice-radio.md +166 -0
- package/docs/multiplayer-templates/wave-survival.md +7 -8
- package/docs/multiplayer-templates/world-shop.md +240 -0
- package/docs/multiplayer-world.md +222 -129
- package/docs/npc-world.md +623 -0
- package/docs/publishing.md +108 -28
- package/docs/purchases.md +223 -0
- package/docs/scene-performance.md +64 -0
- package/docs/screenshots.md +140 -0
- package/docs/sdk.md +324 -5
- package/docs/shooter-worlds.md +537 -0
- package/docs/terrain.md +173 -0
- package/docs/upgrades.md +324 -0
- package/docs/vehicles.md +727 -0
- package/docs/world-inspect.md +156 -0
- package/docs/world-look.md +241 -0
- package/docs/world-recipe.md +65 -6
- package/package.json +15 -4
- package/skills/README.md +91 -0
- package/skills/helix-assets/SKILL.md +491 -0
- package/skills/helix-assets/references/asset-sources.md +143 -0
- package/skills/helix-assets/references/vault-api.md +105 -0
- package/skills/helix-avatar-qa/SKILL.md +85 -0
- package/skills/helix-avatars/SKILL.md +206 -0
- package/skills/helix-avatars/references/contract.md +166 -0
- package/skills/helix-avatars/references/dynamics.md +367 -0
- package/skills/helix-avatars/references/face.md +50 -0
- package/skills/helix-avatars/references/publish.md +76 -0
- package/skills/helix-avatars/references/qa.md +251 -0
- package/skills/helix-avatars/references/rigging.md +88 -0
- package/skills/helix-avatars/references/source-generated.md +190 -0
- package/skills/helix-avatars/references/source-model.md +90 -0
- package/skills/helix-avatars/references/source-rigid.md +90 -0
- package/skills/helix-avatars/references/source-vrm.md +61 -0
- package/skills/helix-gauntlet/SKILL.md +128 -0
- package/skills/helix-multiplayer/SKILL.md +150 -0
- package/skills/helix-multiplayer/references/dsl-capability-map.md +107 -0
- package/skills/helix-vehicles/SKILL.md +218 -0
- package/skills/helix-vehicles/references/addons.md +212 -0
- package/skills/helix-vehicles/references/appearance.md +339 -0
- package/skills/helix-vehicles/references/audio-import.md +138 -0
- package/skills/helix-vehicles/references/audio.md +580 -0
- package/skills/helix-vehicles/references/cabin.md +225 -0
- package/skills/helix-vehicles/references/host-manifest.md +174 -0
- package/skills/helix-vehicles/references/physics-from-specs.md +463 -0
- package/skills/helix-vehicles/references/publish.md +214 -0
- package/skills/helix-vehicles/references/qa.md +177 -0
- package/skills/helix-vehicles/references/reference-package.json +3481 -0
- package/skills/helix-vehicles/references/reference-package.md +69 -0
- package/skills/helix-vehicles/references/source-beamng.md +167 -0
- package/skills/helix-vehicles/references/source-concept.md +38 -0
- package/skills/helix-vehicles/references/source-model.md +100 -0
- package/skills/helix-vehicles/references/source-scratch.md +60 -0
- package/skills/helix-world-build/SKILL.md +376 -0
- package/skills/helix-world-build/references/config-gates.md +104 -0
- package/skills/helix-world-director/SKILL.md +210 -0
- package/skills/helix-world-qa/SKILL.md +371 -0
- package/skills/helix-world-qa/references/perf-budgets.md +240 -0
- package/skills/helix-world-qa/references/perf-handle.md +125 -0
- package/skills/helix-world-qa/references/visual-scorecard.md +85 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: helix-vehicles
|
|
3
|
+
description: Make, convert and publish a drivable HELIX vehicle item and its add-ons from any source — a real car by name, a BeamNG or other game mod, a downloaded model, a concept image, or from scratch — so it works on the first publish. Use for any request to create, import, fix, republish or add parts to a car, kart, truck or other land vehicle.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# HELIX Vehicles
|
|
7
|
+
|
|
8
|
+
A vehicle is a published **item**, not a world: a GLB that follows the part contract, plus a
|
|
9
|
+
`helix.vehicle-package/2` JSON with sourced physics, audio and controls, sealed into a Continuum
|
|
10
|
+
Package, visually verified, listed, and sold with a parts set. The rules are the **HELIX Vehicle
|
|
11
|
+
Contract**: `read_doc({ name: "vehicles" })`; read it once before step 3. This skill is the order
|
|
12
|
+
of work and the gates. Each step names its reference:
|
|
13
|
+
`read_skill({ name: "helix-vehicles", reference: "<name>" })`.
|
|
14
|
+
|
|
15
|
+
**The default failure** is a car that passes every structural check and is still wrong when
|
|
16
|
+
someone looks at it, drives it or opens its customizer. Every gate below reads an artifact, and
|
|
17
|
+
the last gate is your own eyes on real-runtime renders beside photos of the real car.
|
|
18
|
+
|
|
19
|
+
**Run `check_for_updates()` first.** The vehicle tools run the CLI bundled with this MCP; an old
|
|
20
|
+
MCP means an old QA harness.
|
|
21
|
+
|
|
22
|
+
**The gauntlet is mandatory: `read_skill({ name: "helix-gauntlet" })` now.** Steps 10, 11, 12 and 13 run
|
|
23
|
+
its loop: a bar of real-car photos, the fixed inspection set rendered in the real runtime (every seat
|
|
24
|
+
occupied, the driver steering left/centre/right, still hands), your own look at every sheet, a fresh blind
|
|
25
|
+
critic where you can spawn one, and no publish, version or listing without its PASS. Updating a car that
|
|
26
|
+
is already listed starts and ends with `check_listed_items({ itemIds: [<id>], rerun: true })`.
|
|
27
|
+
|
|
28
|
+
## The workflow
|
|
29
|
+
|
|
30
|
+
Keep a ledger file. For each step, record the artifact that proves the step passed.
|
|
31
|
+
|
|
32
|
+
1. **Research the real vehicle** (`physics-from-specs`). Cite dimensions, tyre sizes per axle,
|
|
33
|
+
mass and distribution, the torque curve, gearing, Cd and area, and the performance figures
|
|
34
|
+
(0–100, quarter mile, top speed, braking, lateral g; several tests each, take the median).
|
|
35
|
+
Collect reference photos at the fixed poses (front, side, rear, top, both 3/4 views, cockpit)
|
|
36
|
+
plus lamp, wheel, tail and interior details (`appearance`), and collect real recordings of the
|
|
37
|
+
car (idle, a steady high-rpm hold, the limiter, a cold start) as the **measuring reference**
|
|
38
|
+
for the audio step (`audio`). **Gate:** citations, a photo folder, a list of recordings.
|
|
39
|
+
2. **Acquire the mesh.** For a **real named car, prefer a quality licensed/CC0 base mesh of that
|
|
40
|
+
exact car** and clean it up (`source-model`) over modelling the body from scratch — the
|
|
41
|
+
from-scratch body has come out crude across three rounds. In order of preference:
|
|
42
|
+
(1) a **licensed/CC0 base mesh** of the exact car (Sketchfab, CC0, or equivalent) — first choice
|
|
43
|
+
for a real named car (`source-model`); (2) a **BeamNG or other game mod** (`source-beamng`);
|
|
44
|
+
(3) a **downloaded or ripped model** (`source-model`); (4) **from scratch** (`source-scratch`)
|
|
45
|
+
**only for a fictional or no-source car** — never the default for a real named car. Images or a
|
|
46
|
+
concept: `source-concept`, for fictional cars. Whatever the source, **check its licence/CC0 and
|
|
47
|
+
set `provenance.distributable`** (already a publish gate). Never plan around an AI-generated car
|
|
48
|
+
body: every one has failed `render.intact`. **Gate:** the source on disk, with its inventory and
|
|
49
|
+
licence.
|
|
50
|
+
3. **Separate into contract parts** (contract sections 1–5): few nodes, one material each; wheel
|
|
51
|
+
origins at the hubs, each axle's tyre at its real size; doors, lids, steering, glass and one node
|
|
52
|
+
per lamp function separate; +X left, +Y up, +Z forward, metres, tyres on Y = 0. Do not invent
|
|
53
|
+
parts the source lacks. Split every lid the real car opens (bonnet, boot, engine cover) and give
|
|
54
|
+
each indicator side a front AND a rear lens (`validate_vehicle` warns `PANEL_ROLE_*`, fails
|
|
55
|
+
`LAMP_INDICATOR_REAR_MISSING`). **Gate:** a numbers file against the real dimensions.
|
|
56
|
+
4. **Build the cabin** (`cabin`): the real seats, dash, legible gauges, steering wheel, pedals and
|
|
57
|
+
door cards, with H-points where the real seats put the hips. **Gate:** the **interior finish
|
|
58
|
+
gate** (`appearance` section 4.4) at the cockpit and dash poses beside interior photos — a
|
|
59
|
+
legible binnacle (tach/speedo/turbo), real seat shells, a real dash and door cards, and the
|
|
60
|
+
controls; a flat grey slab cabin fails. Every occupant assert from `preview_vehicle` passes — posture
|
|
61
|
+
(`torso_angle`, `knees_up`, `head_in_wings`) and the hands over time (`hands_on_wheel_steering`,
|
|
62
|
+
`hands_follow_wheel`, `hands_at_nine_and_three`, `fingers_still`, `hands_still`, `seat_rig_warnings`)
|
|
63
|
+
— and the `seat-*`, `driver-steering` and `stillness` sheets show a person sitting properly in every
|
|
64
|
+
seat with the hands on the rim turning with it. Fix the seat, footwell and wheel geometry, not a
|
|
65
|
+
number (`cabin`). Every declared seat counts, rear rows included: run
|
|
66
|
+
`check_vehicle_cabin({ target: <dir> })` after every cabin edit (seconds, offline) and clear every
|
|
67
|
+
FAIL, then confirm with `preview_vehicle({ target: <dir> })`, which boards every declared seat
|
|
68
|
+
(`cabin`, "A rear row").
|
|
69
|
+
5. **Materials and budgets** (contract sections 7–8, paint in `appearance`): baked PBR, KTX2 with
|
|
70
|
+
mips, linear `baseColorFactor`, ≤ 24 draws, ≤ 90k triangles, ≤ 48 materials, ≤ 2048 px, one
|
|
71
|
+
drawn GLB. **Gate:** the counted primitives, triangles and materials.
|
|
72
|
+
6. **Write the host manifest** (`host-manifest`) into the final GLB's
|
|
73
|
+
**`asset.extras.HELIX_vehicle`**: class and every socket position. Without it the car mints as
|
|
74
|
+
a non-host and no add-on can ever fit it. **Gate:** re-read the final GLB: `asset.extras`
|
|
75
|
+
lists every socket (`validate_vehicle` refuses a GLB without it).
|
|
76
|
+
7. **Write the package** from the reference (`get_vehicle_reference_package({ out })`, then
|
|
77
|
+
`reference-package`): replace every number with your sourced values, author
|
|
78
|
+
`simulation.land`, set `groundToBodyOrigin = wheelRadius + staticRideHeight`, declare every
|
|
79
|
+
seat, keep every specialty signal and action. **Gate:** `validate_vehicle({ vehiclePackage,
|
|
80
|
+
glb })` exits 0.
|
|
81
|
+
8. **Simulate** (`physics-from-specs`): `simulate_vehicle({ target: <dir> })` until every target
|
|
82
|
+
is met within its tolerance. The platform car runs **ungoverned**: a market or gentlemen's-agreement
|
|
83
|
+
speed governor is not physics, so `targets.topSpeedKph` is the ungoverned figure and the torque
|
|
84
|
+
curve sits on the maker's declared crank figures, with losses in `drivetrainEfficiency` and drag
|
|
85
|
+
(`physics-from-specs`, "Top speed" and "Torque"). An AWD car follows "All-wheel drive" there. A turbo car's lag is authored into the curve. It also runs the
|
|
86
|
+
**keyboard launch** (full-throttle W, A/D taps, hands-off yaw probe): a FAIL means the car spins
|
|
87
|
+
under a keyboard player; tune it only with sourced levers, or author `simulation.land.tractionControl`
|
|
88
|
+
(the real system, or a declared playability aid) (`physics-from-specs`, "Drivable on a keyboard"
|
|
89
|
+
and "Traction control"). **Gate:** the figures beside the targets, and the keyboard-launch verdict.
|
|
90
|
+
9. **Audio** (`audio`): **never generate or synthesise vehicle sound; always source real
|
|
91
|
+
recordings, from BeamNG mods as much as possible**, whatever the mesh source. Find the most
|
|
92
|
+
accurate simulator mod of that exact car (BeamNG first; Assetto Corsa/ACC, rFactor 2 or
|
|
93
|
+
Automobilista 2 where theirs is more accurate), **pick it by measuring** candidates against
|
|
94
|
+
real recordings of the car (firing-fundamental pitch track vs rpm, spectral centroid,
|
|
95
|
+
roughness, idle character, limiter cadence), and extract it with
|
|
96
|
+
`import_vehicle_audio` (`helix vehicle audio-import`). Fill a slot the best mod lacks from, in
|
|
97
|
+
order, another BeamNG mod of the same engine or family (then the BeamNG base game's stock
|
|
98
|
+
banks), another sim's mod, a real recording of the car; never another car's sound. **Every one of
|
|
99
|
+
the 31 runtime slots carries a sourced recording**, with its provenance; the only empty slots are
|
|
100
|
+
a component the real car physically lacks (`absent: "not-fitted"` plus a reason: turbo on an NA
|
|
101
|
+
car, reverse beep, clutch on an automatic, combustion slots on an EV) and `exhaustLadder`
|
|
102
|
+
`absent: "combined-with-engine"` when the mod records one combined set. Doors are required. Never rely on the platform's default sounds: the gate fails
|
|
103
|
+
a car that would fall back to one (`AUDIO_SLOT_UNSOURCED`). **Gate:** a slot table with each slot's source, the measurements that chose the mod,
|
|
104
|
+
the **rung-pitch self-check** on every rung (`audio`, "The rung-pitch self-check"), and
|
|
105
|
+
`check_vehicle_audio({ target: <dir> })` exits 0: every slot sourced, no
|
|
106
|
+
`AUDIO_SYNTHESISED`, no `AUDIO_SLOT_UNSOURCED`, no content warnings left unexplained (`audio`, "Content gates").
|
|
107
|
+
10. **Preview and accept the look — the gauntlet** (`helix-gauntlet`, `appearance`, `qa`, `cabin`):
|
|
108
|
+
`preview_vehicle({ target: <dir> })` (occupants and the inspection set are on by default) and read
|
|
109
|
+
EVERY sheet under `<out>/inspection/` beside the bar photos, then the blind critic. Then run the reference-overlay gate: compare each fixed pose with
|
|
110
|
+
the reference photo at the same pose, blend the silhouettes and read the IoU and landmarks
|
|
111
|
+
against the thresholds, and check the lamp treatment, panel relief, wheels and tail. Pass
|
|
112
|
+
`referencePaint` (the median body colour of the reference photos) so the **daylight paint
|
|
113
|
+
gate** judges hue, saturation, lightness and glare as Try Now draws the car, and
|
|
114
|
+
`referenceWheel` (a face-on close-up of the real wheel) so the **wheel-design gate** judges
|
|
115
|
+
spoke count, finish and centre cap, and confirm
|
|
116
|
+
`vehicle.lamps.each_function_lights` passes (every lamp key lights its lens). Then run
|
|
117
|
+
the **concrete material gate** (`appearance` section 4): sample the render's body sRGB beside
|
|
118
|
+
the reference photo, confirm no specular highlight blows to white, confirm the lamp pods read
|
|
119
|
+
closed in daylight (body-colour flaps, bezel lenses, or a clear cover over chrome reflectors and projector bowls; never black slats or glowing panels), and clear the
|
|
120
|
+
**interior finish gate** (`appearance` section 4.4). Fix the cabin until nothing moves. The
|
|
121
|
+
first mint locks the stance and the slot board. **Gate:** a pass on every overlay and on the
|
|
122
|
+
material gate, the pose sheet beside the photos, the clearance and cabin numbers, and a gauntlet
|
|
123
|
+
PASS in the ledger (every sheet looked at and PASS, the critic's `MATCHES` on every sheet).
|
|
124
|
+
11. **Publish** (`publish`): `publish_vehicle({ dir, dryRun: true })`, then for real;
|
|
125
|
+
`check_vehicle_host({ itemId })`; pass `visual_qa_item_hosted`;
|
|
126
|
+
`create_item_distribution` — **list the base car and price it** (section 5 of `publish`).
|
|
127
|
+
**Activate a later version only by re-running `publish_vehicle({ dir, itemId, version, reason })`
|
|
128
|
+
after visual QA passes; never `helix item set-version` on a car** (it moves the pin, not
|
|
129
|
+
`properties.vehiclePackage`, so the car keeps serving its old package).
|
|
130
|
+
A car left unlisted is not obtainable; the owner cannot buy a `NOT FOR SALE` car, so it is
|
|
131
|
+
not a shipped product. To change anything later, stage a new version of the same item.
|
|
132
|
+
If a publish tool answers NOT AVAILABLE, the mint is blocked: finish every gate and report it.
|
|
133
|
+
**Gate:** a PASS record for the exact pinned version, and a live marketplace listing with a
|
|
134
|
+
price.
|
|
135
|
+
12. **Add-ons: required** (`addons`). Ship at least a wheel set, an exhaust, an ECU, a gearbox,
|
|
136
|
+
suspension, brakes and anti-roll bars, plus a spoiler where the real car has one. The
|
|
137
|
+
performance parts are tune-only: no model, every op a `tune` (an engine part also gets a bay mesh
|
|
138
|
+
when the engine is visible from outside; `addons`). Each one: `check_vehicle_addon`
|
|
139
|
+
(it must fit and measurably change the drive), `preview_vehicle({ target, addon: [<dir>] })`
|
|
140
|
+
for any part with a model, `publish_vehicle_addon`, its own visual QA, then **list and price
|
|
141
|
+
it** (`create_item_distribution`). Every add-on must be listed for sale, not left dry
|
|
142
|
+
(`--dry-run` only, pricing "to the owner") — round 7 shipped all nine add-ons `NOT LISTED`
|
|
143
|
+
and nothing was buyable.
|
|
144
|
+
**Gate:** `check_vehicle_host({ itemId })` shows every required part fitting **and for sale**
|
|
145
|
+
(a `distributionId` and a price) and passing the web customizer's own guard (it fails, naming
|
|
146
|
+
the part, when the customizer would refuse to equip one — round 8's four tune-only parts), and
|
|
147
|
+
the customizer shows them buyable.
|
|
148
|
+
13. **Owner QA on the live item** (`qa`, `helix-gauntlet`): `preview_vehicle({ item: <id> })` on the
|
|
149
|
+
published car and `check_listed_items({ itemIds: [<id>], rerun: true })` (CURRENT and PASS); then buy it
|
|
150
|
+
with a QA account, spawn it in a hosted world with kerbs, board every seat, drive it against the
|
|
151
|
+
targets beside a control car, work the doors and lights at night, fit each add-on, listen.
|
|
152
|
+
**Gate:** live evidence per check, and the gauntlet's inspection of the LIVE car passing. Do not
|
|
153
|
+
report done before this.
|
|
154
|
+
|
|
155
|
+
## Hard rules (each one prevents a failure that already shipped)
|
|
156
|
+
|
|
157
|
+
- **Accuracy wins over a gate artefact.** Never move the car off the real one's figures to satisfy
|
|
158
|
+
a check; prove the artefact and report it (`qa`).
|
|
159
|
+
- **Only a submitted or hosted visual-QA run is a gate result.** A local pass runs no critic.
|
|
160
|
+
- **`asset.extras.HELIX_vehicle`, nowhere else.** Scene and root `extras` are ignored at mint.
|
|
161
|
+
- **Never mint a second copy, and never re-run a mint to change a model.** Stage a version of the
|
|
162
|
+
same item. Locked fields stay byte-identical, `note` included.
|
|
163
|
+
- **Wheel nodes have their origin at the hub**, or wheel add-ons render at the car's centre.
|
|
164
|
+
- **Look at add-ons on the host from several angles, including inboard and x-ray.**
|
|
165
|
+
- **Check the car parked and unboarded, not only boarded.**
|
|
166
|
+
- **Every seat occupied, every time.** A posture, hands or fingers defect blocks the listing; it is never
|
|
167
|
+
"minor". The F40 went on sale with its driver lying back, hands off the wheel and fingers twitching
|
|
168
|
+
because an owner-proxy called "reclined, knees up" non-blocking and nobody looked at the hands.
|
|
169
|
+
- **A listed car is re-checked when you touch it** (`check_listed_items`): its record can predate today's gates.
|
|
170
|
+
- **Calibrate physics to the real car, not to a source mod.** (Audio is the opposite: the sound
|
|
171
|
+
comes from the most accurate sim mod, chosen by measurement.)
|
|
172
|
+
- **Never synthesise or generate vehicle audio, and never rely on the platform's default sounds**
|
|
173
|
+
(the synthetic fallback engine, the generated default sound pack). A car ships its own sourced
|
|
174
|
+
sound for every slot; `check_vehicle_audio` and `publish_vehicle` fail an empty or
|
|
175
|
+
default-falling-back slot (`AUDIO_SLOT_UNSOURCED`) and a car without sourced engine audio.
|
|
176
|
+
- **Vehicle add-ons are not auto-sealed by `publish_item`**: use `publish_vehicle_addon`.
|
|
177
|
+
- **If you re-frame the host, republish every add-on authored in the host's frame.**
|
|
178
|
+
- **Give the car GLB an empty `vehicle_root` over every top-level node, with the lids under it.**
|
|
179
|
+
Host-frame add-ons bind to it; without it the car is never presented (`validate_vehicle`
|
|
180
|
+
HOST_BIND_*; `host-manifest`).
|
|
181
|
+
|
|
182
|
+
## Tools
|
|
183
|
+
|
|
184
|
+
- **Discovery:** `search_items` (`kind: "vehicle"`, `hostKind: "vehicle"`), `search_vault`
|
|
185
|
+
(`packageType: "vehicle" | "addon"`).
|
|
186
|
+
- **BeamNG import:** `bridge_detect`, `bridge_plan`, `bridge_import`.
|
|
187
|
+
- **Build and check (these run the CLI and return its output and exit code):**
|
|
188
|
+
`get_vehicle_reference_package`, `validate_vehicle`, `simulate_vehicle`, `check_vehicle_cabin`, `import_vehicle_audio`,
|
|
189
|
+
`check_vehicle_audio`,
|
|
190
|
+
`preview_vehicle`, `check_vehicle_addon`.
|
|
191
|
+
- **Publish:** `publish_vehicle`, `publish_vehicle_addon`,
|
|
192
|
+
`check_vehicle_host`, `visual_qa_item`, `visual_qa_item_hosted`, `visual_qa_item_status`,
|
|
193
|
+
`create_item_distribution`, `delist_item`, `check_listed_items` (re-judge listed cars against today's gates). Never hand-write a vehicle or add-on descriptor.
|
|
194
|
+
- **Audio:** `import_vehicle_audio` (extract a sim mod's audio with provenance), then
|
|
195
|
+
`check_vehicle_audio`. **Never `generate_audio` for a vehicle**, for the engine or for any
|
|
196
|
+
one-shot: vehicle sound is sourced, not generated.
|
|
197
|
+
|
|
198
|
+
Mesh work happens in Blender (headless) and `@gltf-transform/cli`; install them if missing
|
|
199
|
+
(`toktx` from KTX-Software for KTX2).
|
|
200
|
+
|
|
201
|
+
## References
|
|
202
|
+
|
|
203
|
+
| Reference | Covers |
|
|
204
|
+
| --- | --- |
|
|
205
|
+
| `physics-from-specs` | Research, every physics field from published specs, staggered tyres, turbo lag, targets and `simulate_vehicle`. |
|
|
206
|
+
| `source-beamng` | BeamNG mods: the importer, the field mapping, atlas and lamp splitting, known traps. |
|
|
207
|
+
| `source-model` | **First choice for a real named car:** download a quality CC0/licensed base mesh of that exact car, then cleanup, splitting, pivots, scale. Also downloaded/ripped models. |
|
|
208
|
+
| `source-concept` | Images, concepts and fictional cars. |
|
|
209
|
+
| `source-scratch` | Building a car from primitives in Blender to the contract — for fictional or no-source cars, **not** the default for a real named car. |
|
|
210
|
+
| `host-manifest` | `asset.extras.HELIX_vehicle`: the shape and tested export snippets. |
|
|
211
|
+
| `cabin` | Interior parts, how the runtime seats occupants, `cabinFit`, the occupant thresholds, and the interior finish gate. |
|
|
212
|
+
| `appearance` | The reference-overlay gate (fixed poses, IoU + landmark pass/fail), lamp treatment and panel relief, and the concrete paint/bloom/lamp-pod gate and the **interior finish gate** (sections 4.1–4.5). |
|
|
213
|
+
| `audio` | The hard rule (never synthesise; always source real recordings), finding and measuring the most accurate sim mod, `import_vehicle_audio`, the slot map and per-slot fallback order, provenance, the **rung-pitch self-check**, content gates, the URL and package closure. |
|
|
214
|
+
| `audio-import` | The details of `import_vehicle_audio`: BeamNG's own base-game defaults per slot, the `--map` slot-map file (several sources, trims, concats, recordings, not-fitted), Assetto Corsa specifics. |
|
|
215
|
+
| `addons` | The required parts set, kinds of part, frames, ops from source data. |
|
|
216
|
+
| `publish` | The order of calls, the slot board, Try Now and versions, visual QA, selling. |
|
|
217
|
+
| `qa` | The render and drive checks, the full visual-QA assert list, gate artefacts. |
|
|
218
|
+
| `reference-package` | What to keep or change in the reference package, and the componentBuild digest. |
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Vehicle add-ons
|
|
2
|
+
|
|
3
|
+
The contract is in section 11 of `read_doc({ name: "vehicles" })`. This reference is the method.
|
|
4
|
+
**Plan the add-on set before you finalise the host mesh.** Every node an add-on hides must exist
|
|
5
|
+
on the host as its own primitive, and every add-on authored in the host's frame breaks if the host
|
|
6
|
+
moves later.
|
|
7
|
+
|
|
8
|
+
## Required: the minimum parts set
|
|
9
|
+
|
|
10
|
+
**A car is not done until its customizer sells a parts set.** Buyers expect to change wheels and
|
|
11
|
+
tune the car; round 1's F40 shipped with "0 compatible" parts and failed the owner's check for it.
|
|
12
|
+
Every car ships at least:
|
|
13
|
+
|
|
14
|
+
| Part | Slot type | Kind |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| A wheel set (rims and tyres), more than one where the real car had factory or period options | `vehicle.wheel@1` | geometry, socket frame |
|
|
17
|
+
| Exhaust | `vehicle.exhaust@1` | geometry, host frame, hides `exhaust`, **and a tune op that changes the drive** (or a sound `mount`) — see the rule below |
|
|
18
|
+
| ECU | `vehicle.ecu@1` | tune only |
|
|
19
|
+
| Gearbox | `vehicle.transmission@1` | tune only |
|
|
20
|
+
| Suspension (coilovers) | `vehicle.suspension@1` | tune only |
|
|
21
|
+
| Brakes | `vehicle.brakes@1` | tune only |
|
|
22
|
+
| Anti-roll bars | `vehicle.arb@1` | tune only |
|
|
23
|
+
| Engine / powertrain, where the real car has a swappable engine or a well-known upgrade path (turbo kit, cam, bigger displacement) | `vehicle.engine@1` | geometry (visible engine bay) and/or tune only |
|
|
24
|
+
| Spoiler or wing, where the real car has one or a well-known aftermarket one exists | `vehicle.spoiler@1` | geometry, host frame, bound to `boot` |
|
|
25
|
+
|
|
26
|
+
**Cover the reference car's obvious upgrades.** The two upgrades a buyer of that car expects must be
|
|
27
|
+
present, or the set is incomplete:
|
|
28
|
+
|
|
29
|
+
- **Engine/powertrain** — where the real car has a swappable engine or a well-known tuning path (the
|
|
30
|
+
F40's is the twin-turbo V8), ship a `vehicle.engine@1` part (a visible engine-bay mesh and/or a
|
|
31
|
+
torque/limiter tune), sourced like every other part.
|
|
32
|
+
- **Spoiler/wing** — where the real car has one, ship a `vehicle.spoiler@1` part even when the wing is
|
|
33
|
+
part of the body mesh. Author a wing-replacement add-on that **hides the body wing** (a `hide` op on
|
|
34
|
+
the host's `wing_*` node, granted via the host's `hostGrantExtensions`) and adds its own wing mesh plus
|
|
35
|
+
aero ops. "The wing is body-mesh" is not a reason to skip the slot.
|
|
36
|
+
|
|
37
|
+
**Engine add-ons: tune-only by default; a mesh only where the buyer can see the engine.** An engine
|
|
38
|
+
upgrade is a performance part, so its tune ops are what the buyer pays for, and a tune-only engine is
|
|
39
|
+
a complete product (the live 993's Guntherwerks 4.0 is tune-only). Owners do notice an "engine
|
|
40
|
+
upgrade" that changes nothing they can see, so give it a mesh when, and only when, the stock engine is
|
|
41
|
+
visible from outside: through glass or a louvred cover (mid-engined cars such as the F40, Carrera GT or
|
|
42
|
+
NSX), in an open bay, or under a lid the car articulates (`mesh.articulation.panels[]` role `bonnet` or
|
|
43
|
+
`boot`). In that case:
|
|
44
|
+
|
|
45
|
+
- model only the parts that visibly change (turbos, intercoolers, plenum, cam covers, strut brace),
|
|
46
|
+
in the host frame, with `bind { socket: <the lid or "engine">, … }` when it sits under a moving lid;
|
|
47
|
+
- `hide` the stock node it replaces (`engine` or the named bay part) so the two never overlap;
|
|
48
|
+
- prove it with `preview_vehicle({ target, addon: [<dir>] })` from the pose that shows the bay
|
|
49
|
+
(top-down through the glass, or with the lid open). This path is in the contract but has no live
|
|
50
|
+
example yet, so do not ship it on the strength of the contract alone.
|
|
51
|
+
|
|
52
|
+
When the engine is hidden (a front-engined car with a closed bonnet that does not open), ship it
|
|
53
|
+
tune-only and say so in the description ("performance part: no visible change"), rather than adding a
|
|
54
|
+
mesh nobody can see that costs draws.
|
|
55
|
+
|
|
56
|
+
Add more where the car has them (a body kit, an exhaust note). The live 993 sells
|
|
57
|
+
13 parts across these slots. Each part's ops come from real data (below).
|
|
58
|
+
|
|
59
|
+
**An exhaust is a performance part.** A `vehicle.exhaust@1` add-on must change the drive — at least one
|
|
60
|
+
`tune` op that measurably moves a figure (power or weight: `drivetrain.engine.torqueCurve` /
|
|
61
|
+
`limiterRpm`, or `chassis.mass`) — or be sold as a sound part with a `mount { type: "sound" }`. A hide +
|
|
62
|
+
mesh with no tune op and no sound is refused by `check_vehicle_addon` (`ADDON_PERFORMANCE_NO_EFFECT`):
|
|
63
|
+
round 5 shipped a "competition triple-outlet exhaust" that changed nothing about how the car drove.
|
|
64
|
+
|
|
65
|
+
**Gate:** `check_vehicle_host({ itemId: <the car> })` lists every required slot type with at
|
|
66
|
+
least one fitting part **for sale**, and the customizer on the car's item page shows that many
|
|
67
|
+
compatible parts. Open it, fit each part, and look. It also runs the web customizer's own guard on
|
|
68
|
+
every fitting add-on's exact Package pin and **fails, naming the part and the customizer's message,
|
|
69
|
+
when the customizer would refuse to equip or save one**. "Fits" and "for sale" alone prove only
|
|
70
|
+
what the backend rail lists: round 8 sold four tune-only parts that the rail listed and every
|
|
71
|
+
older check passed while the customizer showed "could not be previewed" and disabled Save.
|
|
72
|
+
|
|
73
|
+
**A part with no `model.glb` is meshless, and the customizer equips a meshless part only when its
|
|
74
|
+
sealed definition is verified.** The root must be `helix.vehicle-addon-definition/1` (or the legacy
|
|
75
|
+
`helix.vehicle-addon/1`) with `mesh: null` and an ops array, and no op may be a `mount` of anything
|
|
76
|
+
but a sound. `check_vehicle_addon` runs that guard on the exact definition publish would seal
|
|
77
|
+
(`ADDON_CUSTOMIZER_REFUSES`; an `addon.json` `definition` override that restores a `mesh` or
|
|
78
|
+
changes the schema is the usual cause), and `helix vehicle addon check --item <id>` / `--host <id>`
|
|
79
|
+
runs it on what is already published.
|
|
80
|
+
|
|
81
|
+
The car must be an add-on host first: its GLB carries `asset.extras.HELIX_vehicle` (the
|
|
82
|
+
`host-manifest` reference) and its board has sockets, nodes and tunable paths.
|
|
83
|
+
|
|
84
|
+
## Kinds of part
|
|
85
|
+
|
|
86
|
+
| Kind | Geometry | Frame | Ops | Example (live, Porsche 993) |
|
|
87
|
+
| --- | --- | --- | --- | --- |
|
|
88
|
+
| Wheel set | Rim, tyre and disc as **one mesh per wheel**, a single mesh serving all four corners, at about 9–11k triangles | `socket`, outboard `+x` | `hide rim_*` (the host's wheel nodes) | HRE 501, Guntherwerks 5-spoke, deep-dish |
|
|
89
|
+
| Wing / spoiler | Wing mesh in the host's coordinates | `host` with `bind { socket: "boot", root: "vehicle_root", matrix: identity }`, so it follows the lid | aero `add` downforce, `mul` drag, `add` mass | RWB wing |
|
|
90
|
+
| Lip, kit, bumper | Panel mesh in the host's coordinates | `host` (with `bind` if mounted on a moving panel) | aero, mass, and `hide` of the stock panel | RWB lip |
|
|
91
|
+
| Exhaust | Exhaust mesh in the host's coordinates | `host` | `hide exhaust` **(a replacing part must hide what it replaces)**, mass | RWB exhaust |
|
|
92
|
+
| Engine | Visible engine-bay mesh, or none | `host` | torque curve `add`, limiter `add`, coast `mul`, mass | Guntherwerks 4.0 |
|
|
93
|
+
| ECU / gearbox / coilovers / sway bars / brakes | **None** (0 draws, glyph tile) | — | tune only | Stage 3 ECU, short-ratio gearbox, race coilovers |
|
|
94
|
+
| Exhaust note / blow-off | None. A sound `mount` row with WAV assets of its own package | — | `mount { type: "sound", … }` | — |
|
|
95
|
+
|
|
96
|
+
## Frames (the anchor decides where the part appears)
|
|
97
|
+
|
|
98
|
+
Declare the frame in the add-on GLB's `scene.extras.helixMeshFrame`:
|
|
99
|
+
|
|
100
|
+
- **`{ "contract": "helix.mesh-frame/1", "authoringFrame": "socket", "outboardAxis": "+x" }`**
|
|
101
|
+
- The mesh is metric and centred on the hub.
|
|
102
|
+
- Model the **left** wheel; the axle runs along X, and +X is the outboard face.
|
|
103
|
+
- The runtime turns it to face outward on the right side.
|
|
104
|
+
- The mesh anchors to the socket node's origin, which is why the host's wheel origins must be
|
|
105
|
+
at the hubs.
|
|
106
|
+
- Match the host tyre's radius: scale the add-on radially to the host's `wheelRadius`, as the
|
|
107
|
+
993 set did with a factor of 1.0274.
|
|
108
|
+
- **`{ "authoringFrame": "host", "bind": { "socket", "root", "matrix" } }`**
|
|
109
|
+
- The vertices sit in the host's final model frame.
|
|
110
|
+
- `bind` makes the part follow a moving socket. Without it, the part sits static at the root.
|
|
111
|
+
- **If the host is ever re-framed (raised, re-centred or re-scaled), shift every host-frame
|
|
112
|
+
add-on and republish it.**
|
|
113
|
+
- Seat a body part on the host by measurement, not by eye. The mod's wings floated 0.09–0.29 m
|
|
114
|
+
above the 993's stock lid and each had to be lowered by its measured minimum gap.
|
|
115
|
+
|
|
116
|
+
## Deriving the ops
|
|
117
|
+
|
|
118
|
+
- Ops address the v1 tuning tree: `drivetrain.engine.*`, `drivetrain.gearbox.*`,
|
|
119
|
+
`drivetrain.brakes.*`, `contacts.*.suspension.*`, `land.antiRollBar.*`, `chassis.aero.*` and
|
|
120
|
+
`chassis.mass`. On a car with `simulation.land` the engine and gearbox deltas are projected into
|
|
121
|
+
the v2 solver automatically. What stays inert is listed in section 11 of the contract.
|
|
122
|
+
- Use **ratios from source data**: the mod's jbeam option against its stock value, or a
|
|
123
|
+
manufacturer's tuning-kit figure against stock. Record each derivation in the package's
|
|
124
|
+
`addon-definition` root.
|
|
125
|
+
- **Limits:**
|
|
126
|
+
- `mul` and `add` only. `set` on a number is first-party only.
|
|
127
|
+
- An array op takes a scalar, so a per-gear ratio change becomes its mean.
|
|
128
|
+
- `contacts.*` is one wildcard over all four corners. When the front and rear ratios differ,
|
|
129
|
+
take the **smaller**.
|
|
130
|
+
- A `mul` on 0 does nothing; use `add`.
|
|
131
|
+
- A `mul` on a block the host lacks is refused at fit. A turbo kit on an NA car cannot ship,
|
|
132
|
+
because there is no `provides` verb.
|
|
133
|
+
- **Test the part live against stock.** A shorter gearbox made the 993 *slower* to 100 km/h
|
|
134
|
+
(5.49 → 5.60 s), because it added two 0.25 s shifts. Tuning `shiftTime ×0.6` and
|
|
135
|
+
`finalDrive ×0.92` fixed it, and both factors were chosen by sweeping live. A part whose only
|
|
136
|
+
effect is drag-limited, such as an ECU on a car that never reaches its limiter, shows nothing on
|
|
137
|
+
a 0–100 run. Say so in its description.
|
|
138
|
+
- **No add-on controls a speed limiter.** The platform car runs ungoverned and the live solver never applies
|
|
139
|
+
`physics.engine.speedLimiterKph`; the ECU slot's tune paths (`torqueCurve`, `limiterRpm`, …) have no limiter path and the
|
|
140
|
+
backend refuses one. An ECU's honest effects are torque and rev limit: say that, not "removes the speed limiter".
|
|
141
|
+
`check_vehicle_addon` measures the drive effect **ungoverned** (0–100, top speed, quarter mile), so an engine, gearbox or ECU
|
|
142
|
+
part shows its top-speed change even on a package that still carries a limiter.
|
|
143
|
+
|
|
144
|
+
## Geometry hygiene (each item made a published add-on grey, black or invisible)
|
|
145
|
+
|
|
146
|
+
- **KTX2 with mips** on every texture. New add-ons without them are refused at upload.
|
|
147
|
+
- **Every textured primitive has `TEXCOORD_0`.** Recover lost UVs from the source; never
|
|
148
|
+
synthesise an unwrap onto a borrowed atlas.
|
|
149
|
+
- **Paint parts:**
|
|
150
|
+
- Give them `extras.helixVehicleRole: "paint"` on the material, and the host must have a paint
|
|
151
|
+
role too.
|
|
152
|
+
- Inheritance copies only the host's colour, roughness and metalness, never its texture. So
|
|
153
|
+
carry a solid base colour, not a neutral paint mask.
|
|
154
|
+
- **Base-game textures** that live outside a mod must be supplied. For BeamNG, pass the
|
|
155
|
+
`beamngCommon` input. Otherwise the part ships flat grey.
|
|
156
|
+
- **No metallic 1 on tyres.** Use 0 with roughness about 0.6; rims at metallic ≤ 0.6 with lifted
|
|
157
|
+
albedo.
|
|
158
|
+
- **Rim faces must be visible from inboard.** One wheel set whose far side culled the rim faces
|
|
159
|
+
showed only a tyre ring and failed visual QA.
|
|
160
|
+
- Strip BeamNG paint-slot tags from material names before lookup (`name [SECONDARY]`). Otherwise
|
|
161
|
+
slot 2 paints the wheel faces white.
|
|
162
|
+
|
|
163
|
+
## The item
|
|
164
|
+
|
|
165
|
+
Each add-on is a directory: `addon.json` and `model.glb`. A tune-only part has **no**
|
|
166
|
+
`model.glb` and every op is a `tune`: ECU, gearbox, driveline, brakes, sway bars, coilovers or
|
|
167
|
+
steering, and optionally engine, intake, induction, exhaust or tyre compound.
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{ "title": "<part name>", "description": "<what it is>",
|
|
171
|
+
"fits": { "slotType": "vehicle.wheel@1",
|
|
172
|
+
"requires": { "sockets": ["wheel_lf","wheel_rf","wheel_lr","wheel_rr"], "nodes": ["rim_*"], "paths": [], "pointers": [], "skeleton": null },
|
|
173
|
+
"only": { "items": ["<host item id>"] } },
|
|
174
|
+
"ops": [{ "v": "hide", "nodes": ["rim_*"] }],
|
|
175
|
+
"provenance": { "kind": "authored|imported|generated", "source": "<source>" } }
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Example `fits` for a wing (`ops` is the aero and mass tunes):
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
{ "slotType": "vehicle.spoiler@1",
|
|
182
|
+
"requires": { "sockets": ["boot"], "nodes": [], "paths": ["chassis.aero.downforceCoefficient","chassis.aero.dragCoefficient","chassis.mass"], "pointers": [], "skeleton": null },
|
|
183
|
+
"only": { "items": ["<host item id>"] } }
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
At most 32 ops (64 for a clip-set slot). Then:
|
|
187
|
+
|
|
188
|
+
1. `check_vehicle_addon({ dir })` until it exits 0. It proves the anchoring (a socket-frame part
|
|
189
|
+
centred on its mount; the host's wheel origins at its hubs), that every hide target exists and
|
|
190
|
+
is granted on the host's board, that the host exposes every required tunable path, and the
|
|
191
|
+
backend's own count of hosts the part fits, and (for a part with no model) that the web
|
|
192
|
+
customizer's guard accepts the exact definition publish would seal. For `tune` ops it measures the drive effect on each
|
|
193
|
+
host with the live runtime's physics. A part that changes nothing it governs fails, and an op
|
|
194
|
+
that cancels another is named (contract section 11). Size the ops so the figure moves the way
|
|
195
|
+
the real part moves it. Then `preview_vehicle({ target: <car dir>, addon: [<dir>] })`, or
|
|
196
|
+
`{ item: <published car id>, addon: [<dir>] }`, fits the unpublished part in the real runtime
|
|
197
|
+
and checks it lands on its anchor (each wheel on its hub), not at the car centre. Look at the
|
|
198
|
+
render.
|
|
199
|
+
2. `publish_vehicle_addon({ dir })`. It runs the full check, uploads the part's model (none for a
|
|
200
|
+
tune-only part), seals its `addon` Package, makes the item from that Version (pinned, unlisted)
|
|
201
|
+
and reads back that each host lists it. A hide the host has not granted stops it: the host car
|
|
202
|
+
seals its grants (`hostGrantExtensions` in its `publish.json`, the `publish` reference).
|
|
203
|
+
3. Visual QA on each host, then distribute.
|
|
204
|
+
|
|
205
|
+
## Proof (per add-on, per host)
|
|
206
|
+
|
|
207
|
+
1. In the customizer, stage the part alone. Capture the front 3/4, rear 3/4, side, a close-up, and
|
|
208
|
+
each corner for wheels.
|
|
209
|
+
2. **Wheel copies must sit within ±1 cm of the four stock hub centres.** Measure it; a "seated"
|
|
210
|
+
flag is not proof.
|
|
211
|
+
3. Drive with it fitted. Wheel add-ons must spin and steer with the hub.
|
|
212
|
+
4. For performance parts, run a live A/B against stock, a median of 3, in the same world.
|