Commit graph

175 commits

Author SHA1 Message Date
MiTHRAL
32c5e438ed ghost multiplayer: fix wire-struct packing; set default relay to relay.mithraic.lol
- #pragma pack(1) on all protocol structs: alignas(1) set the struct
  alignment but still let the compiler pad members to their natural
  alignment - the uint16_t payload_size landed on an even offset, so
  PacketHeader was 8 bytes instead of 7 and the relay's
   guard dropped every real 7-byte packet
  (n=7 < 8). The in-process tests passed only because BuildPacket/PacketSize
  used the same inflated size on both sides. With true packing the struct
  sizes now equal the wire sizes exactly (PacketHeader=7, PosePayload=3140,
  HelloPayload=687, RoomsPayload=1266, ...), verified.
- kGhostDefaultServer -> relay.mithraic.lol (the production beta relay on
  the OVH VPS; IPv6 PTR set, IPv4 PTR still pending at OVH).
2026-08-26 23:17:08 -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
cceeac7c4f ghost multiplayer: MTU-safe chunked pose transport + pose smoothing
Phase 3: two robustness fixes for full-model ghosts.

- Wire: a full skeleton pose is ~3.1 KB, above the 1500-byte path MTU, so it
  now travels as up to 3 MTU-safe kTypePoseChunk datagrams (<=1.2 KB each,
  ~1.2 KB total with headers). The relay forwards each chunk verbatim
  (re-stamping only the chunk header session id), so its code path is
  shared with the legacy single-datagram POSE and needs no pose-body
  parsing. GhostPoseBuildChunks / GhostPoseReassembler (inline in the proto
  header, standalone-testable) tolerate out-of-order and duplicate chunks,
  drop incomplete sequences (the next frame resends), discard a partial on
  a newer seq, and special-case a header-only pose as a single chunk.
  Receive identity comes from the chunk header (the relay re-stamps it),
  not the blob, which the relay never touches.

- Smoothing: each remote ghost now holds a target skeleton (latest received
  pose) and a display skeleton chased per render query with exponential
  catch-up (time constant ~33 ms), so the 15 Hz bone stream renders as
  smooth motion instead of stepping. New/reshaped skeletons snap instead of
  blending between layouts.

Reassembly state is per-session on the network thread, bounded (64) and
cleared on prune/disable. Protocol version bumped to 2.
2026-08-26 20:46:08 -04:00
MiTHRAL
bfb504db7d ghost multiplayer: full skater skeleton pose + character-model ghosts
Phase 2: replace the placeholder-cube ghost rendering with real character
models driven by the remote player's skeleton.

- Wire: POSE now carries the sender's skater skeleton (up to 64 bones, 3
  float4 rows each, index-ordered exactly like DrawItem::bones), padded with
  identity rows so every mesh's referenced bones stay in range. The relay
  forwards POSE payloads verbatim (re-stamping only the sender session id),
  so the protocol can grow without a relay update.
- Capture: OnFrameSceneBuilt (called from BuildFrameScene pre-publish)
  snapshots the local player's body item bone palette (highest-bone-count
  skinned non-ropa non-LivingWorld char item near the camera) as the
  broadcast pose; defaults the broadcast rate to 15 Hz.
- Render: in RenderScene, each remote ghost is drawn by cloning this
  player's own character pieces (body + nearby skinned char items) and
  substituting the remote skeleton into the bone ring - same meshes,
  textures and cached GPU decodes, no mesh transfer. Ghosts without a pose
  (older peers / capture not ready) fall back to the solid-color cube, which
  skate3_ghost_draw_local still uses for the self-check.

Known Phase-2 limitations (documented in code): ghosts render with this
player's own character model/outfit, cloth (ropa) garments are skipped (no
remote cloth sim), and hair/alpha pieces render opaque. All confined to
src/native and the hand-written scene hooks.
2026-08-26 20:16:42 -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
mchughalex
f6e0ae87fd render: declare copy-source usage on the readback source textures 2026-07-24 21:49:05 +01:00
mchughalex
d1a3e3d255 installer: read the ISO install directory before the wizard moves the paths (SDK bump) 2026-07-24 20:47:39 +01:00
mchughalex
671e74ba59 build: embed version resources in the windows binaries (SDK bump) 2026-07-24 19:40:48 +01:00
mchughalex
ebe14fcd1f render: recover streaming-revoked guest read faults on linux and macos 2026-07-24 19:03:31 +01:00
mchughalex
a37e74fb4d render: derive the auto frame cap from a refresh-scaled margin (SDK bump) 2026-07-24 13:38:02 +01:00
mchughalex
a16314df0c capture: place sort-list batched piece clones with their instance matrix 2026-07-24 13:38:02 +01:00
mchughalex
14acde031b render: key rigid pose rings by instance identity and make their mispair gates rotation-aware 2026-07-24 13:38:02 +01:00
mchughalex
020af8ae90 capture: serve rigid instance worlds from the guest mesh context owner matrix 2026-07-24 13:38:02 +01:00
mchughalex
89360e53fc capture: pair deferred rigid clone worlds by identity instead of submission order 2026-07-24 13:38:02 +01:00
mchughalex
186c7de6f4 render: stand the static sun shadows down in venues with no sane world lighting capture 2026-07-24 13:38:02 +01:00
mchughalex
c9e501e298 render: pin the guest auto-exposure at its zone max while luminance readbacks are unavailable 2026-07-24 13:38:01 +01:00
mchughalex
5e52ef82e4 render: never adopt a material's own surface map as streamed ad art 2026-07-24 13:38:01 +01:00
mchughalex
587f5dc17f src: include cstddef in headers that use size_t 2026-07-24 13:38:01 +01:00
mchughalex
b046a436c5 video: use the 2x first-run render scale on macOS too 2026-07-24 13:38:01 +01:00
mchughalex
414015f525 macos: name the app bundle and window title skate3recomp 2026-07-24 13:38:01 +01:00
mchughalex
7329af15a8 render: default the frame cap to Auto (display refresh - 4) 2026-07-24 13:38:01 +01:00
mchughalex
7b0d690cf3 render: skip rebuilding occlusion-culled statics on most frames 2026-07-24 13:38:01 +01:00
mchughalex
8f8642aec2 render: time the submit-record loop and pre-size per-frame bookkeeping 2026-07-24 13:38:01 +01:00
mchughalex
2aef1222a0 render: skip guest draw-list dispatch for occlusion-culled statics 2026-07-24 13:38:01 +01:00
mchughalex
73c19c8cd3 render: occlusion-cull statics hidden behind rendered geometry 2026-07-24 13:38:01 +01:00
mchughalex
13447a041f render: profile the per-item CPU pipeline with occlusion-grid visibility attribution 2026-07-24 13:38:01 +01:00
mchughalex
70a7c55183 app: unload stream-probe cells before map transitions tear them down 2026-07-24 13:38:01 +01:00
mchughalex
87b27dc2a9 app: keep the stream-probe fan out of map loads 2026-07-24 13:38:01 +01:00
mchughalex
c0ba909c86 render: accumulate the static world-shadow map incrementally (SDK bump) 2026-07-24 13:38:01 +01:00
mchughalex
ceb98c34ec render: march sun shafts at quarter res with reach-proportional steps 2026-07-24 13:38:01 +01:00
mchughalex
4d616e4d45 render: mark the native scene passes for GPU-time attribution (SDK bump) 2026-07-24 13:38:01 +01:00
mchughalex
2b43fb2474 macos: self-contained application bundle with user files beside it (SDK bump) 2026-07-21 11:40:02 +01:00
mchughalex
25c68a89d7 ui: guard the settings-menu gamepad poll against runtime teardown 2026-07-21 10:50:13 +01:00
mchughalex
9fb6e5505f render: widen the content-store count caps now that byte budgets bound VRAM 2026-07-21 10:34:23 +01:00
mchughalex
0224fdf74e render: budget the content stores by GPU bytes and LRU-evict the cube cache 2026-07-21 10:21:37 +01:00
mchughalex
7fa9585285 render: compile the showcase gates out of the standard shader variants 2026-07-21 09:50:17 +01:00
mchughalex
137d97c190 render: keep showcase staging fully inert while no showcase frame is live 2026-07-21 09:31:00 +01:00
mchughalex
f47de1e596 render: record the captured cascade rows in the scene ring 2026-07-21 08:35:36 +01:00
mchughalex
b1cbbc263d render: swap unfixed caster-bank publishes for the last main-view capture 2026-07-21 08:27:09 +01:00
mchughalex
bb5d99ed10 render: record the shadow-atlas outcome in the scene ring 2026-07-21 08:18:41 +01:00
mchughalex
c03ef926eb render: hold shadow-caster parity through short ortho-capture gaps 2026-07-21 08:11:38 +01:00
mchughalex
5218b5f94c render: hold the static sun axis and cached map through single-frame capture outliers 2026-07-21 08:02:37 +01:00
mchughalex
b6d9442b45 render: add a Vulkan-only static sun receiver bias margin for toolchain rounding differences 2026-07-21 07:57:12 +01:00
mchughalex
fbcffc1445 render: fill one-to-two frame dyn publish gaps with the previous capture 2026-07-21 06:19:40 +01:00
mchughalex
d3f230b064 render: double the texture store cap now that entries carry no staging 2026-07-21 06:19:40 +01:00
mchughalex
3d09c764f8 render: stamp the texture-store LRU clock at commit so fresh decodes survive the eviction latch 2026-07-21 06:06:18 +01:00
mchughalex
2f803421e7 render: scale store-eviction idle guards to wall clock and log LRU latch transitions 2026-07-21 05:51:31 +01:00
mchughalex
f6d0136168 render: demote per-frame capture diagnostics to debug, align example config with texture-load defaults 2026-07-21 05:24:40 +01:00