Commit graph

5 commits

Author SHA1 Message Date
MiTHRAL
4ec504ac6c ghost: fix auto-assigned room code flickering + copy button
When the relay auto-assigns a room, the code lives in the connection state
while the skate3_ghost_room cvar stays empty. The network loop was
overwriting the active room with the empty cvar every 25 ms, making the code
flicker in and out of the menu, and the Copy button copied the empty cvar.

- Loop: only sync the cvar into the active room when it is non-empty;
  preserve the server-assigned code otherwise.
- Menu: Copy grabs the ACTIVE room from GetStatus() (works for auto-assigned
  rooms, with a brief 'copied' hint); the room field now surfaces the
  auto-assigned code instead of staying blank.
2026-08-27 12:56:22 -04:00
MiTHRAL
d7c9cbcb70 ghost multiplayer: lobby browser (LIST/ROOMS)
The M menu now has a room browser: a Refresh button asks the relay for its
room list, and the list (room code, player count, up to 4 member names) is
shown as clickable rows - clicking one sets the room code and joins on the
next heartbeat.

- Protocol: kTypeList (client -> relay, empty payload) and kTypeRooms
  (relay -> client, RoomsPayload: up to 16 rooms, code + count + names).
  LIST works from unjoined addresses so the browser can query before
  joining; a joined client's LIST is answered too. Version bumped to 4.
- Relay: BuildRoomList aggregates the client table by room (refused clients
  excluded), most-populated first, capped at kGhostMaxRooms.
- Client: RequestRooms()/GetRooms() - the network thread sends LIST to the
  best current peer (public relay, LAN peer, or the local host when
  hosting) and stores the reply under the shared lock; ROOMS replies from
  any other address are ignored. The list is cleared on disable.
- Dialog: CollapsingHeader 'Browse rooms (lobby)' with refresh + selectable
  rows.

New relay test (ghost_relay_test6) covers the lobby path; all suites pass.
2026-08-26 22:19:34 -04:00
MiTHRAL
7a48eb9f35 ghost multiplayer: beta hardening - version handshake, crash-proofing, logging, persistence
Production-readiness pass toward 1.0:

- Protocol handshake: JOIN carries kGhostVersion; a mismatched client is
  REFUSED by the relay (HELLO session 0) and its poses are never forwarded,
  so two builds can never silently corrupt each other's pose stream. The
  client surfaces the refusal in the menu and skips refused LAN hosts during
  discovery.
- Crash-proofing: every received payload is bounds-checked against its
  packet size before parsing (HELLO / PoseChunk / JOIN); both the client
  network thread and the relay thread are exception-guarded (a throw logs
  and keeps the loop alive instead of hitting std::terminate). The relay no
  longer creates client records for unknown-address POSE/LEAVE floods and
  enforces a hard client cap (--max-clients, default 64).
- Logging: rate-limited error logging throughout (send failures, resolve
  failures, refusals, thread exceptions, client-cap drops). The relay core
  uses a build-flag shim (REXLOG in-game, a tiny {} formatter in the
  dependency-free standalone), with --verbose join/leave lines for the
  server. Game crashes themselves are already handled by the SDK's crash
  handlers + file logs; this layer now avoids triggering them and explains
  itself when it can't connect.
- Settings persistence: server/room/name/port/rate save into the app's cvar
  config on change, so a player's multiplayer setup survives a restart.
- Menu: status now shows protocol refusals and the last error.

Tests updated for the handshake (version-refusal case added); all suites
pass.
2026-08-26 22:07:09 -04:00
MiTHRAL
36b5169e27 ghost multiplayer: cloth on ghosts + player nameplates
Phase 4:

- Cloth: remote ghosts now wear this player's cloth garments (tee/pants).
  The local player's rigid character.cloth_ropa items (CPU-sim deformed
  root-local VB) are cloned per ghost and drawn with the REMOTE entity world
  from the pose, so each ghost is draped in its own root frame. No mesh or
  cloth data is transmitted; the shader already applies item.world to the
  rigid cloth path (zero blend weights). Sim-inactive (skinned) garments and
  garments not near the local body are skipped.

- Nameplates: display names ride the wire. JOIN carries a display name
  (skate3_ghost_name cvar, default the OS user name); the relay stores it
  per client and echoes {session_id, name} pairs in the HELLO roster
  (RosterEntry). Clients re-JOIN every 5 s so the roster/names stay current
  as members join and leave. A GhostNameplateOverlay (always registered,
  draws only while enabled) projects each ghost's entity origin +1.75 m
  through the published camera and draws a colored name label above it.

  Uses skate3::native_scene::GetPublishedCamera (new thread-safe snapshot of
  the published view-proj/cam) so the UI thread can project world points.

- GhostDialog gains a display-name field. Protocol version bumped to 3.
2026-08-26 21:17:43 -04:00
MiTHRAL
269fbcee32 Add ghost multiplayer: UDP relay + pose broadcasting + placeholder cubes
Bolt-on multiplayer layer confined to src/native and hand-written code (no
codegen output touched):

- GhostRelay core (src/native/skate3_native_ghost_relay.*): small UDP relay
  matching clients into rooms by a shareable code; forwards each client's
  pose to the other room members. Used by both the standalone binary and
  the in-game LAN host fallback.
- Standalone server (tools/ghost_relay_main.cpp, ghost_relay target): runs
  on a public VPS or behind a free tunnel; clients connect OUTBOUND, so no
  port forwarding is needed on any client.
- In-game module (skate3_native_ghost.*): captures the local player's world
  transform at Sk8::UpdateBoneTransforms exit (pack-owner entity +416,
  verified layout) and broadcasts it at a fixed rate; auto-connects to the
  default public relay, falls back to LAN auto-host + UDP broadcast
  discovery when unreachable.
- Renderer: draws a solid-color cube per remote player in RenderScene via
  the existing scene PSO tint.a>0 branch (no new shader/SPIR-V).
- UI: GhostDialog (M key) with enable toggle, room code, relay override,
  status readout. Off by default.

Wire protocol in skate3_native_ghost_proto.h (JOIN/HELLO/POSE/PING/LEAVE).
2026-08-26 20:12:45 -04:00