feat: add @webgamekit/multiplayer-p2p package#62
Merged
Conversation
Client-side Socket.IO wrapper for sharing 3D scene state between players. Based on the cnotv/multi-game reference implementation. Public API: - multiplayerCreate(url, config?) — connect to Socket.IO server - multiplayerDestroy(session) — disconnect - multiplayerSendPosition(session, position, rotation) — throttled (30 ms default) - multiplayerOnPlayers(session, callback) — subscribe to player list (user:list) - multiplayerCollectCoin(session, coinId) — emit coin:collected - multiplayerOnCoinCollected(session, callback) — receive coin:collected events - multiplayerOnCoinsSync(session, callback) — receive initial coin:list Socket events match cnotv/multi-game server: user:updated, user:list, coin:collected, coin:list. socket.io-client is a peer dependency. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
✅ Deploy Preview for cnotv-generative-art ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
…tiplayer-p2p packages Closes #61 - Rename `@webgamekit/multiplayer` → `@webgamekit/multiplayer-client` (Socket.IO client) - Add `@webgamekit/multiplayer-server` (Socket.IO server — Node.js only) - Add `@webgamekit/multiplayer-p2p` (WebRTC via Trystero + NOSTR signaling, fully serverless) - All three packages expose generic typed data channels (multiplayerClient*/p2p* prefix) - Add `MultiplayerClient` and `MultiplayerP2P` experiment views - Register multiplayer packages in vite aliases and root deps - Add multiplayer packages to ESLint utility-files override Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add p2pOnPeerJoin and p2pOnPeerLeave to expose Trystero's peer lifecycle events. Fix session.peerId to use Trystero's selfId instead of the first remote peer's ID. Update MultiplayerP2P view to add peers to the list on join and remove on leave, rather than waiting for their first position broadcast. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add overflow-y: auto and max-height: 100% to .sheet-content so it becomes the scroll container. Previously pointer-events: none on .panel-container prevented touch-scroll from reaching the panel. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…m Vite pre-bundling Test mock now simulates actual peer join/leave by calling registered callbacks — previously tests only verified registration, not invocation. Exclude trystero/nostr from Vite optimizeDeps to prevent it from being pre-bundled in Node context where crypto.subtle is unavailable, which caused "Cannot read properties of undefined (reading 'digest')" at runtime. Also remove duplicate overflow-y: auto from .sheet-content (caused double scrollbar on desktop). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The room simulation routes sends between sessions and fires onPeerJoin/ onPeerLeave across peers, catching cross-peer bugs that unit tests miss. Also adds journey/multiplayer-p2p.md documenting the DataPayload constraint, moduleResolution: Bundler, crypto.subtle Vite issue, peer visibility bug, and the integration testing strategy. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Log room join/leave, selfId, peer join/leave events, and position send/receive so runtime failures are visible in the browser console. Fix unit test mock to export selfId (required after room.ts started importing it from trystero/nostr). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
trystero/nostr uses crypto.subtle.importKey for NOSTR event signing, which requires a secure context (HTTPS or localhost). The app is served on custom hostnames without HTTPS, so crypto.subtle is undefined. @trystero-p2p/torrent uses BitTorrent WebSocket trackers for signaling and has no crypto.subtle dependency, working on any origin. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All Trystero strategies require crypto.subtle (HTTPS or localhost only). Add p2pIsSupported() pre-flight check that logs the origin and returns false when crypto.subtle is unavailable, so p2pJoin throws a descriptive error instead of crashing async inside joinRoom. The view now checks p2pIsSupported on mount and shows a banner explaining the HTTPS requirement instead of silently failing. Add more debug logs: isSecureContext, origin, crypto.subtle availability, and joinRoom entry/exit points. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The torrent strategy WebSocket trackers (tracker.webtorrent.dev, tracker.btorrent.xyz) fail to connect, so peers can never discover each other. The original switch to torrent was to avoid crypto.subtle, but that is now resolved by the p2pIsSupported() HTTPS check. trystero/nostr uses NOSTR relays which are more reliable for signaling. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
onPeerJoin only fires for peers who join after you register the callback. When tab A joins after tab B is already in the room, B is invisible to A until a third peer joins and triggers a new handshake. Fix: call p2pGetPeerIds (wraps room.getPeers()) immediately after setting up onPeerJoin, and add any already-connected peers to the list. Add integration test covering the late-joiner scenario. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2 tasks
Documents all bugs encountered during P2P implementation: DataPayload constraint, TypeScript module resolution, Vite pre-bundling crypto crash, crypto.subtle on plain HTTP, torrent tracker failures, peer visibility issues, late-joiner race condition, and testing strategy. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add p2pSendAction/p2pOnAction to multiplayer-p2p for broadcasting animation names - Add PlayerAction type to package exports - Rewrite MultiplayerP2P view: canvas-only, auto-join on mount, WASD controls - Load stickboy.glb for local player (white) and remote peers (orange tint) - Animation buttons: wave (1), attack (2), jump (3), talk (4), sit (5), pick (6), death (7) - Keyboard 1-7 + gamepad buttons trigger animations and broadcast to all peers - Remote peers play received animation via playActionTimeline - Mobile: left faux-pad for movement, right button pad for actions - Integration tests cover action broadcast and self-exclusion Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…on resize - Set PLAYER_Y_OFFSET to 0 so models sit on ground surface (y=-0.5) - Spawn remote peers at staggered positions using index-based spread (alternating left/right, increasing distance) instead of all at [5,0,0] - Remove window resize listener — getTools already handles canvas/camera resize internally; re-calling init was recreating the entire scene Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ount
Root cause of lag: makeAction('pos') was called every frame (60fps) inside
the animation loop, creating a new Trystero action channel each time.
Fixed by caching [send] per session+channel via WeakMap — makeAction is now
called at most once per channel per session.
Also adds:
- ControlsLogger HUD showing player count and keyboard bindings
- FPS + peer count log every 2s (console.warn) to surface performance issues
- makeAction call counter log to confirm caching works in production
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…on send The previous debounce implementation cancelled the timer on every frame call at 60fps, so position was never actually broadcast while moving (timer always reset before firing). Replaced with a proper leading+trailing throttle: - Sends immediately on the first call if throttle window has elapsed - Schedules one trailing send for the remaining window if called mid-throttle Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…, and input mapping
- Change timeline frequency from 60 to 2 (fires 30x/sec instead of 1x/sec)
- Fix Euler serialization bug: use plain {x,y,z} objects for broadcast
- Fix getGround position mutation that compounded on HMR reloads
- Swap up/down input mappings for keyboard, gamepad, and faux-pad
- Remove spawn slot logic, simplify peer spawning
- Remove all debug logs from p2p packages
- Document frequency bug and design decisions in journey docs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d collapsible HUD - Track remote peer movement to play walk/idle animations accordingly - Slow down idle animation speed (5) vs walk (20) - Allow movement during all blocking actions (wave, attack, etc.) - Make ControlsLogger collapsible on mobile with +/− toggle Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #61
Summary
@webgamekit/multiplayer-p2p— WebRTC P2P via Trystero (NOSTR signaling, no server required)MultiplayerP2Pexperiment: canvas-only scene with stickboy.glb avatars, auto-join on loadp2pSendAction/p2pOnActionfor broadcasting and receiving animations across peersKey Changes
MultiplayerP2P.vuenow loadsstickboy.glbfor local and remote players (orange tint for remotes)p2pGetPeerIds()+onPeerJoincombo fixes late-joiner race condition (peers who joined before you)p2pIsSupported()pre-flight guard — skips P2P init gracefully on plain HTTPTest Plan
pnpm test:unit— 758 tests pass (9 new integration tests for P2P including action sync)pnpm lint— no errors/experiments/MultiplayerP2P— stickboy loads on a green ground, camera follows🤖 Generated with Claude Code