@hypersoniclabs/helix-mcp 0.2.5 → 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,166 @@
|
|
|
1
|
+
# Character attachments — put a prop in a player's hand (held items & sockets)
|
|
2
|
+
|
|
3
|
+
Read this when a world needs a character to HOLD or WEAR something: a mug, a torch, a sword, a phone, a hat.
|
|
4
|
+
The humanoid-character system ships named skeleton sockets and a world-facing attach API — you never compute
|
|
5
|
+
bone transforms, and attachments replicate to other players automatically.
|
|
6
|
+
|
|
7
|
+
**Prerequisite — the world's INSTALLED humanoid-character must be ≥ 0.2.14** (where attachments were added).
|
|
8
|
+
On a world you did not scaffold this session, run `check_for_updates({ projectDir })` FIRST: an older install
|
|
9
|
+
shows as in-range-stale, and the fix is just `install_world_packages` + rebuild — any existing `^0.2` pin
|
|
10
|
+
already allows 0.2.14, no helix.json edit needed. For attachments to REPLICATE (not just render locally) the
|
|
11
|
+
same check also flags a stale project SDK — update and rebuild too.
|
|
12
|
+
|
|
13
|
+
## The API
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
// mp is the CharacterMultiplayer facade (standalone Character has the same three methods).
|
|
17
|
+
// The prop GLB lives in the world's own public/props/ — new URL(...) makes it absolute at runtime.
|
|
18
|
+
const mug = await mp.attach(new URL('props/mug.glb', location.href).href, 'hand_r.grip', { preset: 'mug' });
|
|
19
|
+
mp.detach(mug);
|
|
20
|
+
mp.listSockets(); // the valid socket names on this character
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- **The URL must be ABSOLUTE http(s), and `new URL('props/…', location.href).href` is how a local file in
|
|
24
|
+
`public/` becomes one** — the ref replicates verbatim and the room drops a bare `/props/…` path with the
|
|
25
|
+
whole set. There is no CDN step: bundle the prop with the world.
|
|
26
|
+
- **URL attaches replicate by default**: other players see the prop, late joiners see it too, and it cleans up
|
|
27
|
+
on detach/leave. Pass `{ replicate: false }` to keep one local. A raw `THREE.Object3D` source is always
|
|
28
|
+
local-only (it has no asset URL to send).
|
|
29
|
+
- **Cap: 8 replicated attachments per player.** Over the cap the send is skipped with a console warning.
|
|
30
|
+
- Attach from input/interaction handlers (they run only on the acting player), never from replicated-state
|
|
31
|
+
observers — the same rule as gestures and movement.
|
|
32
|
+
- Props on `head`/`camera` sockets auto-hide in first person so they never clip the camera; `hideInFp` overrides.
|
|
33
|
+
|
|
34
|
+
## Sockets — where a prop can go
|
|
35
|
+
|
|
36
|
+
`hand_r.grip` / `hand_l.grip` (held items), `head`, `head.top` (hats), `camera` (eye plane), `chest`, `back`,
|
|
37
|
+
`hips_l` / `hips_r` (holsters). Call `listSockets()` rather than assuming — the set comes from the installed
|
|
38
|
+
system version. Socket frames carry the natural orientation for a +z-authored item: `back` slings it tip-UP
|
|
39
|
+
along the spine, the hips holster it tip-DOWN, and `chest`/`head`/`head.top`/`camera` point it forward as worn.
|
|
40
|
+
|
|
41
|
+
**The grip sockets are semantic held-item frames: +z is where the item points, +y is the item's up.** You
|
|
42
|
+
reason in "the mug's spout points +z", never in bone axes.
|
|
43
|
+
|
|
44
|
+
## Prop conventions — meters, grip at origin, per-category frame
|
|
45
|
+
|
|
46
|
+
Author or pick prop GLBs with the **grip point at the origin** (where the fist closes — never the prop's
|
|
47
|
+
base) and **units in meters**. Two hard rules:
|
|
48
|
+
|
|
49
|
+
- **A prop spanning more than 2 m on any axis is rejected at load** — that is almost always a cm/mm export
|
|
50
|
+
slip. Fix the export; do not scale around it with offsets.
|
|
51
|
+
- Per-item alignment goes in `position` / `rotationEulerDeg` (socket-local, meters/degrees) or a `preset` —
|
|
52
|
+
never baked into world code as magic bone math.
|
|
53
|
+
|
|
54
|
+
**The prop's own axes are PER CATEGORY — "+z out along the tip" is only the pointed-item rule.** Each
|
|
55
|
+
preset's calibrated offsets assume the frame below; author a different frame and the preset lands wrong
|
|
56
|
+
(the classic failure: a mug modeled with its cup axis along +z attaches lying on its side, spilling).
|
|
57
|
+
|
|
58
|
+
| preset | author the GLB as | reference size |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `sword`, `torch` (long-shaft family: staff, axe, pickaxe, wand) | shaft along **+z**, blade/flame END at +z; the fist point at the origin, shaft running a little −z behind it | torch 0.53 m long |
|
|
61
|
+
| `mug` (cylinder family: glass, bottle, can) | body CENTERED on the origin, **opening UP (+y)**, handle on **−x** — NOT +z | ⌀ 0.084 × 0.10 m |
|
|
62
|
+
| `flashlight` | body wrapping the origin, **beam out +z**, head at +z | 0.17 m long |
|
|
63
|
+
| `phone` | slab centered on the origin, thin axis **y**, TOP edge toward **+z**, screen facing **+y** | 0.072 × 0.15 × 0.008 m |
|
|
64
|
+
| `pistol`, `rifle` | muzzle **+z**, grip at the origin, iron sights up **+y** | calibrated against the platform arsenal |
|
|
65
|
+
|
|
66
|
+
Pick the preset by how the item is HELD, not by shape — `torch` carries the tip up like a flame,
|
|
67
|
+
`flashlight` points it forward like a beam. Explicit offsets override the preset per field. For WEAPONS
|
|
68
|
+
there is a third layer between the two: a weapon profile's `hold` block (per-gun grip seating, resolved by
|
|
69
|
+
the attachment URL at attach time) beats the preset and loses to explicit call-site offsets — so a gun with
|
|
70
|
+
a baked seating lands right with a bare `{ preset }` attach; see `read_doc({ name: "shooter-worlds" })`. Presets work
|
|
71
|
+
in EITHER hand: attach with a preset to `hand_l.grip` and the offsets auto-mirror (position x → −x,
|
|
72
|
+
rotation `[a,b,c]` → `[a,−b,−c]`); explicit offsets are never mirrored. `inspect_gesture` /
|
|
73
|
+
`capture_gesture` apply the SAME mirror and tag the offsets line "(preset auto-mirrored for this
|
|
74
|
+
socket)", and the posed placement line reports both `dir=` (+z) and `up=` (+y) so a wrong authoring
|
|
75
|
+
frame shows numerically — a held mug should read `up` ≈ world +y.
|
|
76
|
+
|
|
77
|
+
### No asset? Build the GLB from three.js primitives — do NOT attach an in-scene Object3D
|
|
78
|
+
|
|
79
|
+
`attach()` takes a URL **by design**: the attachment replicates the asset reference, so every client must
|
|
80
|
+
fetch the same file — an Object3D you composed in the scene cannot ride the wire. When no hosted asset
|
|
81
|
+
exists, compose the prop with three.js primitives **in the canonical frame** and export a GLB once, as a
|
|
82
|
+
node script in the world repo (`scripts/make-props.mjs`, rerun after tweaks):
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
86
|
+
import * as THREE from 'three';
|
|
87
|
+
import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
|
|
88
|
+
|
|
89
|
+
// GLTFExporter's binary path uses the browser FileReader; node has Blob but not FileReader.
|
|
90
|
+
// This shim is all an untextured prop needs.
|
|
91
|
+
globalThis.FileReader = class {
|
|
92
|
+
readAsArrayBuffer(blob) {
|
|
93
|
+
blob.arrayBuffer().then((buf) => { this.result = buf; this.onloadend(); });
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const prop = new THREE.Group();
|
|
98
|
+
const ceramic = new THREE.MeshStandardMaterial({ color: 0xe8e3da, roughness: 0.85, side: THREE.DoubleSide });
|
|
99
|
+
// Body CENTERED on the origin — the origin is where the fist closes, not the prop's base.
|
|
100
|
+
// Real-world meters: a mug is ~0.08 m wide, a phone ~0.15 m tall — not 1.0.
|
|
101
|
+
prop.add(new THREE.Mesh(new THREE.CylinderGeometry(0.042, 0.042, 0.1, 24), ceramic));
|
|
102
|
+
const handle = new THREE.Mesh(new THREE.TorusGeometry(0.028, 0.007, 10, 16, Math.PI), ceramic);
|
|
103
|
+
handle.position.set(-0.042, 0, 0);
|
|
104
|
+
handle.rotation.z = Math.PI / 2;
|
|
105
|
+
prop.add(handle);
|
|
106
|
+
|
|
107
|
+
const glb = await new GLTFExporter().parseAsync(prop, { binary: true });
|
|
108
|
+
mkdirSync('public/props', { recursive: true });
|
|
109
|
+
writeFileSync('public/props/mug.glb', Buffer.from(glb));
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Frame rules while composing: the grip point at the origin (a long prop's shaft runs from slightly −z
|
|
113
|
+
through the origin out along **+z**), +y is the item's up, real-world meters. `DoubleSide` on thin or
|
|
114
|
+
open shells (a culled backface reads as a hole). Serve from `public/` and attach with an **absolute**
|
|
115
|
+
URL — `new URL('props/mug.glb', location.href).href` — a bare path is dropped with the whole set.
|
|
116
|
+
|
|
117
|
+
**Then verify with numbers, not eyes** — `inspect_gesture` with `attach` measures the exported file in
|
|
118
|
+
the grip frame. First RAW (no preset) to prove the convention:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
attachment: mug.glb → hand_r.grip
|
|
122
|
+
prop bbox (m): x -0.077…0.042 · y -0.05…0.05 · z -0.042…0.042 — meters OK (0.12 m max span)
|
|
123
|
+
extends 0.04m out along +z, … grip origin is inside the prop volume
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`meters OK` and `grip origin is inside the prop volume` are the two verdicts that must hold ("origin is
|
|
127
|
+
OUTSIDE" = you modeled the prop beside its grip point; a span near 2 m = a cm/mm export slip). Then
|
|
128
|
+
re-run with the `preset` you intend to ship and check the posed placement line reads sensibly.
|
|
129
|
+
|
|
130
|
+
## Grip finger poses — the hand closes by itself (≥ 0.2.15)
|
|
131
|
+
|
|
132
|
+
A preset also carries a calibrated FINGER pose, so the hand actually wraps the item instead of resting flat
|
|
133
|
+
under it: `mug` curls into the cylinder hold, `sword`/`torch` close the shaft fist, `flashlight` wraps the
|
|
134
|
+
barrel; `phone` deliberately keeps the neutral hand. Nothing to call — it plays on attach to a grip socket,
|
|
135
|
+
stops on detach, mirrors automatically in the left hand, and replicates through the preset name that already
|
|
136
|
+
rides the wire (older clients simply render the flat hand). Behavior worth knowing:
|
|
137
|
+
|
|
138
|
+
- **While the prop is held, the grip OWNS that hand's fingers (≥ 0.2.17):** gestures keep playing, but
|
|
139
|
+
their finger keys on the holding side are skipped — a sip cannot unwrap the hand around the mug. The
|
|
140
|
+
fingers return to gesture/animation control on detach. (On ≤ 0.2.16 the priority was reversed: a
|
|
141
|
+
finger-keying gesture temporarily unwrapped the grip.) Author finger keys freely; they apply whenever
|
|
142
|
+
that hand is not holding something.
|
|
143
|
+
- On rigs without finger bones (some imported avatars) the pose no-ops cleanly.
|
|
144
|
+
- Explicit-offset attaches without a `preset` get no finger pose — pass the preset when you want the wrap.
|
|
145
|
+
|
|
146
|
+
## Getting a prop RIGHT — measure before pixels
|
|
147
|
+
|
|
148
|
+
The gesture tools grew attachment params; the loop is the same measure-first loop as gestures:
|
|
149
|
+
|
|
150
|
+
1. `inspect_gesture` with `attach` (+ `attachSocket`/`attachPreset`/`attachOffset`/`attachRotation`): reports
|
|
151
|
+
the prop bbox **in the grip frame** ("0.06m behind the grip point", "grip origin OUTSIDE the prop volume —
|
|
152
|
+
is the grip modeled at the origin?"), a **unit verdict**, and where the prop lands at each `t` of a clip
|
|
153
|
+
("prop@hand_r.grip: between shoulder and head, points up"). Iterate offsets HERE — it is milliseconds.
|
|
154
|
+
2. `capture_gesture` with the same attach params: renders the prop in the hand across the clip (or one moment
|
|
155
|
+
with `at`) — the closing look. Trust it for how the held prop READS; go back to `inspect_gesture` for any
|
|
156
|
+
distance question.
|
|
157
|
+
|
|
158
|
+
A held pose + prop converges in 2–3 rounds this way: fix the unit verdict first, then the grip-frame bbox
|
|
159
|
+
(behind/above/out numbers), then look once.
|
|
160
|
+
|
|
161
|
+
## Multiplayer notes
|
|
162
|
+
|
|
163
|
+
Attachments are synced state (not events): the full desired set replaces on each change, replicas diff by id
|
|
164
|
+
and evict removed props, late joiners converge on the pre-existing set. Offsets are resolved on the sender and
|
|
165
|
+
travel resolved, so version skew between players cannot misplace a prop. The room enforces: ≤ 8 entries, https
|
|
166
|
+
asset URLs ≤ 512 chars, offsets ≤ 2 m — an invalid set is dropped whole (nothing partially applies).
|