@genex-ai/cli-demo 0.62.0-dev.144 → 0.63.0-dev.146

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/dist/index.js CHANGED
@@ -12357,15 +12357,19 @@ var CONTROLLER_FILE_SETS = {
12357
12357
  ...INPUT_AND_CAMERA,
12358
12358
  NOTICE
12359
12359
  ],
12360
- // The player's VRM is written to public/assets/avatar.vrm at install time by
12360
+ // The FALLBACK VRM is written to public/assets/avatar.vrm at install time by
12361
12361
  // installOwnerAvatar (owner's avatar, or the bundled default) — so it is NOT
12362
- // a static manifest asset. animation-library.glb (the 12-clip core) still is;
12363
- // extra clips arrive via `genex controller anims` into public/assets/anims/.
12362
+ // a static manifest asset; at runtime the game loads the playing user's own
12363
+ // avatar (user.avatarUrl from the embed identity, AG-804) and this file
12364
+ // covers local dev + load failures. animation-library.glb (the 12-clip core)
12365
+ // still is a manifest asset; extra clips arrive via `genex controller anims`
12366
+ // into public/assets/anims/.
12364
12367
  assets: ["assets/animation-library.glb"],
12365
12368
  skill: "genex-threejs-character-controller",
12366
12369
  sketch: [
12367
12370
  `const physics = await PhysicsWorld.create();`,
12368
- `const { scene, vrm } = await loadVrm("./assets/avatar.vrm");`,
12371
+ `const { user } = await waitForPlayer(); // @genex-ai/embed-sdk \u2014 the player's own avatar`,
12372
+ `const { scene, vrm } = await loadVrm(user.avatarUrl ?? "./assets/avatar.vrm").catch(() => loadVrm("./assets/avatar.vrm"));`,
12369
12373
  `const clips = await loadCharacterClips(vrm); // core library + every genex-controller-anims pack`,
12370
12374
  `const character = new CharacterController(physics.world, camera, { ...characterPresets["default"].options, ...capsuleFromModel(scene), position: { x: 0, y: 2, z: 0 } });`,
12371
12375
  `character.root.add(scene); const anims = new CharacterAnimations(scene, clips);`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/cli-demo",
3
- "version": "0.62.0-dev.144",
3
+ "version": "0.63.0-dev.146",
4
4
  "description": "Set up your project's agent workspace (.claude/.codex/.cursor in the game folder), authorize, create a game project, generate AI assets, and publish (genex CLI).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,10 +19,12 @@ animation retargeting, capsule auto-fit, foot IK) and the 12-clip core
19
19
  ~1.3 MB) into `public/assets/`. Need more — swords, pistols, magic, climbing,
20
20
  swimming, emotes? Install exactly what the game uses with
21
21
  `npx genex controller anims <tags|clip names…>` (see Animations below). The
22
- default command also writes the player's avatar to `public/assets/avatar.vrm` —
22
+ default command also writes a fallback avatar to `public/assets/avatar.vrm` —
23
23
  **your** avatar when you're signed in,
24
24
  otherwise a bundled CC0 default (attribution in `src/controllers/NOTICE.md`).
25
- By default, the character plays as that VRM. The copied files are then owned by the game —
25
+ At runtime the character plays as the **visiting player's own** picked avatar
26
+ (`user.avatarUrl` from the embed identity — see the wiring below); the baked
27
+ file is only the fallback for local dev and load failures. The copied files are then owned by the game —
26
28
  edit them freely; re-running skips existing files unless `--force`. Do not write
27
29
  a character controller from scratch and do not swap in a kinematic-controller
28
30
  tutorial: this one is a real dynamic body that pushes crates, rides moving
@@ -89,12 +91,20 @@ import { FollowCamera } from "./controllers/character/follow-camera.ts";
89
91
  import { KeyboardInput } from "./controllers/character/keyboard-input.ts";
90
92
  import { loadVrm } from "./controllers/character/vrm/vrm-loader.ts";
91
93
  import { capsuleFromModel } from "./controllers/character/vrm/capsule-fit.ts";
94
+ import { waitForPlayer } from "@genex-ai/embed-sdk";
92
95
 
93
96
  const physics = await PhysicsWorld.create(); // nothing RAPIER-related may run before this resolves
94
97
 
95
- // Load the player's avatar, then every animation the game has (the bundled core
96
- // library + any packs installed by `genex controller anims`), retargeted onto the VRM.
97
- const { scene: avatar, vrm } = await loadVrm("./assets/avatar.vrm");
98
+ // Load the PLAYING user's avatar every visitor plays as the avatar THEY
99
+ // picked on their genex profile (guests get a per-session one), not the
100
+ // creator's. `user.avatarUrl` comes from the embed identity
101
+ // ($genex-threejs-embed-auth boots before this); the baked
102
+ // `./assets/avatar.vrm` is the fallback for local dev, old APIs, and load
103
+ // failures. Then load every animation the game has (the bundled core library +
104
+ // any packs installed by `genex controller anims`), retargeted onto the VRM.
105
+ const { user } = await waitForPlayer(); // from "@genex-ai/embed-sdk"
106
+ const { scene: avatar, vrm } = await loadVrm(user.avatarUrl ?? "./assets/avatar.vrm")
107
+ .catch(() => loadVrm("./assets/avatar.vrm")); // network failure → bundled fallback
98
108
  const clips = await loadCharacterClips(vrm);
99
109
 
100
110
  const fit = capsuleFromModel(avatar); // collider fits THIS avatar's bounds
@@ -266,11 +276,15 @@ and the style-matching rules live in `$genex-threejs-touch-controls`.
266
276
 
267
277
  **The local player is physics-authoritative; remote players are interpolated
268
278
  visuals only.** Exactly one `CharacterController` exists — yours. For every
269
- remote player: create a plain mesh (or the same GLB), move it with the
270
- interpolator from `$genex-threejs-multiplayer`, and **never** create a rigid
271
- body, a `CharacterController`, or any physics for it. Simulating remote
272
- players' physics locally guarantees divergence every client would compute a
273
- different world.
279
+ remote player: load **that player's own avatar**
280
+ `loadVrm(p.avatarUrl || "./assets/avatar.vrm")` (the multiplayer SDK's
281
+ `player.avatarUrl` is their verified profile pick; fall back to the baked file
282
+ when it's empty or fails to load, and `VRMUtils.deepDispose` the model when
283
+ they leave) — move it with the interpolator from `$genex-threejs-multiplayer`,
284
+ and **never** create a rigid body, a `CharacterController`, or any physics for
285
+ it. Simulating remote players' physics locally guarantees divergence — every
286
+ client would compute a different world. Never render every remote with your
287
+ own avatar file: players picked their looks, show them.
274
288
 
275
289
  - Publish your own `currPos` + `currQuat` as a four-number quaternion on the fixed 10–20 Hz tick,
276
290
  not per frame. Never reduce multiplayer rotation to scalar yaw.
@@ -10,8 +10,10 @@ clips only on the exact matching generated rig revision.
10
10
 
11
11
  `npx genex controller character` sets the game up to play as a **VRM avatar**:
12
12
 
13
- - `public/assets/avatar.vrm` — the player's avatar (yours when signed in, else a
14
- bundled CC0 default). Always present; always one path.
13
+ - `public/assets/avatar.vrm` — the FALLBACK avatar (yours when signed in, else a
14
+ bundled CC0 default). Always present; always one path. At runtime the game
15
+ loads the visiting player's own picked avatar instead (`user.avatarUrl` from
16
+ the embed identity) — this file covers local dev and load failures.
15
17
  - `public/assets/animation-library.glb` (~1.3 MB) — the 12-clip core
16
18
  (idle/walk/jog/sprint, the jump trio, crouch idle+move, hit, death, interact)
17
19
  on a shared Quaternius rig (provenance in `src/controllers/NOTICE.md`).
@@ -35,8 +37,13 @@ import { capsuleFromModel } from "./controllers/character/vrm/capsule-fit.ts";
35
37
  import { CharacterController } from "./controllers/character/character-controller.ts";
36
38
  import { CharacterAnimations } from "./controllers/character/character-animations.ts";
37
39
  import { characterPresets } from "./controllers/character/presets.ts";
40
+ import { waitForPlayer } from "@genex-ai/embed-sdk";
38
41
 
39
- const { scene, vrm } = await loadVrm("./assets/avatar.vrm");
42
+ // The playing user's OWN avatar (their profile pick; per-session for guests)
43
+ // the baked file is only the local-dev / failure fallback.
44
+ const { user } = await waitForPlayer();
45
+ const { scene, vrm } = await loadVrm(user.avatarUrl ?? "./assets/avatar.vrm")
46
+ .catch(() => loadVrm("./assets/avatar.vrm"));
40
47
  const clips = await loadCharacterClips(vrm); // core + every installed pack, retargeted
41
48
 
42
49
  // capsuleFromModel derives the collider from the avatar's bounds — no manual
@@ -365,8 +372,11 @@ resolves correctly (a missing flag reads as not-crouched).
365
372
 
366
373
  The mixer crossfades exactly as it does locally, so remote players animate
367
374
  correctly without simulating anything. Use the same visual lane as the owner:
368
- `loadVrm` + retargeted UAL clips for a VRM game (and call `vrm.update(delta)`
369
- per remote), or `loadMeshyCharacter` for a Meshy game. Relay one-shot events (punch,
375
+ for a VRM game load **that remote player's own avatar**
376
+ `loadVrm(p.avatarUrl || "./assets/avatar.vrm")` (the multiplayer SDK's verified
377
+ per-player field; empty means fall back) + retargeted UAL clips, call
378
+ `vrm.update(delta)` per remote, and `VRMUtils.deepDispose` the model on leave —
379
+ or `loadMeshyCharacter` for a Meshy game. Relay one-shot events (punch,
370
380
  hit, validated planar-action start) alongside the flags and call the matching
371
381
  `remoteAnims.playOneShot(...)` on receipt. Never run `MotionActionDriver` for a
372
382
  remote: its smoothed owner-authored transform is the sole movement authority.
@@ -78,6 +78,10 @@ import { waitForPlayer, waitForAuth, getColyseusAuth, getEmbedToken } from "@gen
78
78
  // everything wants.
79
79
  const { user, guest } = await waitForPlayer();
80
80
  // user.id / user.name — real account identity, or guest:<id> / "Guest-1234"
81
+ // user.avatarUrl — the player's OWN VRM avatar (profile pick; per-session for
82
+ // guests). Load it for the local player's visual in a VRM-lane game, with the
83
+ // baked ./assets/avatar.vrm as the fallback (absent on old APIs / local test
84
+ // mode). Peers see each other's via the multiplayer SDK's player.avatarUrl.
81
85
 
82
86
  // ACCOUNT gate — resolves ONLY for signed-in players (stays pending for
83
87
  // guests; resolves later if they sign in mid-game). Use ONLY for /state
@@ -46,7 +46,7 @@ npm i @genex-ai/multiplayer@^0.11.0
46
46
  > landed in 0.10; confirmed object controls, snaps, host-tick teardown, and reconnect rebasing
47
47
  > in 0.9. An older resolve does not have those.
48
48
 
49
- This skill targets `@genex-ai/multiplayer` **≥ 0.11.0** (`objects`/`host` since 0.4;
49
+ This skill targets `@genex-ai/multiplayer` **≥ 0.12.0** (`objects`/`host` since 0.4; verified per-player `avatarUrl` since 0.12;
50
50
  `matchmake()` since 0.5; private lobbies since 0.7; auto-reconnect + `inputs`/`onHostTick`
51
51
  since 0.8; soft ownership handoff since 0.8.4; confirmed controls, snap epochs, and host-tick
52
52
  lifecycle guarantees since 0.9; regional relay selection via `getColyseusUrls()` since 0.10;
@@ -519,6 +519,7 @@ of five kinds — put each on its channel and the game just works:
519
519
  | Slow agreed facts (score, round, wave, seed) | `shared` | the **host** (`isHost`) |
520
520
  | One-off actions (shot, emote, hit, chat) | `send` + `on` | whoever did it |
521
521
  | Discrete per-player values (hp, ammo, flags) | in `me.set`, read via `stateRaw` | you |
522
+ | Which avatar MODEL a player is (VRM look) | already on `players` as `p.avatarUrl` — sync nothing | the **relay** (verified identity) |
522
523
 
523
524
  Getting the channel right is the whole game. A ball on `shared` stutters (not smoothed) and
524
525
  fights (many writers). A ball on `objects` glides and has one owner. That's the difference.
@@ -531,9 +532,11 @@ fights (many writers). A ball on `objects` glides and has one owner. That's the
531
532
  - `room.me.snap(state)` — respawn/teleport/mode edge. Publishes a discontinuity epoch so remotes
532
533
  hard-reseed instead of interpolating from the old pose. Never use for ordinary movement.
533
534
  - `room.players` — fresh `Map` each read, **includes you** (skip `id === room.id`). Each value is
534
- `{ id, name, connected, state, stateRaw }`: `state` is auto-smoothed (remotes) / live (you);
535
+ `{ id, name, avatarUrl, connected, state, stateRaw }`: `state` is auto-smoothed (remotes) / live (you);
535
536
  `stateRaw` is the raw latest (hit-tests, discrete values). A reconnect-grace seat remains in this
536
- map with `connected: false`.
537
+ map with `connected: false`. `avatarUrl` is that player's verified VRM pick (server-set, `''`
538
+ when unknown) — in a VRM-lane game render each remote with
539
+ `loadVrm(p.avatarUrl || "./assets/avatar.vrm")`; never publish avatar URLs through `me.set`.
537
540
  - `room.activePlayers` — the connected-only subset of `room.players`; use its size for live quorum.
538
541
  - `room.objects` — shared objects nobody owns until claimed (a ball, an NPC):
539
542
  - `claim(id)` — **legacy** optimistic request. It flips local ownership immediately and is corrected
@@ -274,8 +274,12 @@ room.me.set({
274
274
  });
275
275
 
276
276
  // Remote players: a VISUAL-ONLY avatar — NO Rapier body, NO controller instance for remotes.
277
- // Position/rotation from smoothed state; animation from the synced flags via the avatar's own
278
- // update(flags, dt). The character-controller skill's animations reference owns the flag set.
277
+ // Build each remote's visual from THEIR OWN model: in a VRM-lane game that is
278
+ // loadVrm(pl.avatarUrl || "./assets/avatar.vrm") the verified per-player pick the relay
279
+ // replicates ('' = unknown → fall back; deepDispose the model on 'leave'). Never reuse your
280
+ // own avatar file for every remote. Position/rotation from smoothed state; animation from the
281
+ // synced flags via the avatar's own update(flags, dt). The character-controller skill's
282
+ // animations reference owns the flag set.
279
283
  const pl = room.players.get(id)!;
280
284
  remoteAvatar.group.position.set(pl.state.x, pl.state.y, pl.state.z);
281
285
  remoteAvatar.group.quaternion.fromArray(pl.state.q);