Commit graph

265 commits

Author SHA1 Message Date
MiTHRAL
6fac050150 netcode-revival: pin rexglue to unk08=1 Live indicator fix 2026-08-28 22:03:46 -04:00
MiTHRAL
9a2b93e1d2 netcode-revival: default the local profile to Xbox Live signed-in
The Party Play gate (sub_8263E788(1)) hard-requires XamUserGetSigninState
== 2. The app's profile system forced user_live_signed_in=false (LocalProfile
live_signed_in=false), overriding rexglue's cvar default, so signin state was
1 and the option stayed greyed out. Default the profile to live-signed-in.
2026-08-28 21:49:23 -04:00
MiTHRAL
63162cf757 netcode-revival: show the Party Play gate inputs in the M menu
Adds a '-- Party Play gate --' block to the ghost dialog Status showing the
live-flag mapped state + value, the per-slot signin states and signin-info
results, link active, and title online - so we can see in-game (not stdout)
exactly what the frontend gate sees when Party Play is greyed out.
2026-08-28 21:37:39 -04:00
MiTHRAL
1eacbcffc3 netcode-revival: pin rexglue to Xam user-slot shims (Party Play gate) 2026-08-28 14:52:19 -04:00
MiTHRAL
157c9dd48b netcode-revival: pin rexglue to live-flag enforcement 2026-08-28 14:32:15 -04:00
MiTHRAL
07f7fc8b3f netcode-revival: force the game's internal 'live user' flag
The game's frontend refuses 'Xbox Live features' based on its OWN static
flag (0x83028851, read by sub_8263E788) BEFORE calling any rexglue API -
which is why no trace logged during the party attempt. The game never sets
the flag without a real Live session. The ghost network loop now forces it
to 1 every tick so the frontend gate opens and the flow reaches the traced
XNet/Xam calls. Fragile: address tied to this title's build.
2026-08-28 14:32:13 -04:00
MiTHRAL
6859fe453c netcode-revival: pin rexglue to trace build (online-path diagnostics) 2026-08-28 14:20:59 -04:00
MiTHRAL
6dbaae851f netcode-revival: pin rexglue to c42b7d5 (online title address + active link - fixes EA Nation freeze) 2026-08-28 14:13:11 -04:00
MiTHRAL
a0802cc476 netcode-revival: pin rexglue to 08d3f3c (SetRelay deadlock fix + privilege grant) 2026-08-28 13:52:22 -04:00
MiTHRAL
6f8bf05b04 netcode-revival: pin rexglue to 96801e1 (LIVE sign-in default true) 2026-08-28 13:38:12 -04:00
MiTHRAL
1d237a3a2c netcode-revival: verbose game-traffic telemetry on the relay
With --verbose, the relay logs a windowed summary of forwarded game
datagrams ('game traffic: N datagram(s) relayed to M member(s) in room
'XXX'') so the game's own RakNet netcode is observable in the relay log -
distinct from ghost pose joins. Counted per 2s window to avoid per-packet
spam.
2026-08-28 13:30:31 -04:00
MiTHRAL
dea4c3c5f4 netcode-revival: log relay sweeps + show netcode transport state in the M menu
- Relay --verbose now logs which client timed out (ip:port, session, room)
  instead of silently dropping, so '2 client(s) -> 1 client(s)' is
  explainable.
- The M menu Status block shows the netcode transport state: 'Netcode:
  relay joined / not connected' and how many sessions are discoverable,
  so the RakNet pipeline is visible without grepping logs.
- Pin rexglue to 3ec27d1 (transport receive timeout - the silent-client
  disconnect fix).
2026-08-28 13:29:08 -04:00
MiTHRAL
658d727aba netcode-revival: make room/server fields actually apply
Typing a room or relay and pressing Enter did not commit: the fields only
wrote the cvar on ImGuiInputTextFlags_EnterReturnsTrue, and Enter was flaky
in the game. Now:

- Room code commits on every keystroke (uppercase), so the relay follows the
  typed code immediately. A room change re-joins the relay on the next tick
  instead of waiting up to the 5s heartbeat.
- Relay server commits on Enter OR when the field loses focus (clicking
  away), so a swallowed Enter can't leave the old relay active; kept
  commit-on-deactivate (not per keystroke) so partial hostnames don't flap
  the connection while typing.
- Display name commits on change (same Enter flakiness).
- The network loop now splits a 'host:port' server string and uses the
  embedded port for both the relay resolve and the game's netcode transport,
  so typing '127.0.0.1:40001' works (previously it produced a broken
  'host:port:port' resolve).
2026-08-28 13:19:45 -04:00
MiTHRAL
2bc46451fc netcode-revival: document Phase 1 status + two-instance test recipe 2026-08-28 02:26:19 -04:00
MiTHRAL
dbe8afaa57 netcode-revival: ghost client feeds the game's netcode transport (Phase 1c)
- SendJoin carries the machine's virtual IP (v5) so the relay can route the
  game's UDP and answer session queries.
- While the public relay is connected, keep rexglue's XNetTransport on the
  same relay + room (idempotent; reconnects on any change), so the game's
  RakNet netcode follows the room the player joined.
2026-08-28 02:25:31 -04:00
MiTHRAL
7e86b8563a netcode-revival: pin rexglue to transport fixes (packing, heartbeat, logging) 2026-08-28 02:14:19 -04:00
MiTHRAL
3664138037 netcode-revival: pin rexglue to bf3635d (Phase 1b relay transport)
The game's RakNet UDP now routes through the relay via the rexglue
XNetTransport + XSocket integration; QosLookup discovers room members.
2026-08-28 02:11:28 -04:00
MiTHRAL
0ab155341c netcode-revival: relay game-transport + session discovery (Phase 1a)
The game's own RakNet UDP now has a path through the relay (v5 protocol):

- kTypeGame: opaque game datagrams forwarded to the destination member
  (dst_virtual_ip, or broadcast to all room members when 0); the relay
  re-stamps the sender's virtual IP so the recipient can reply. This gives
  the netcode a P2P route with the server as a pure relay/stabilizer.
- kTypeSessionQuery / kTypeSessionReply: a client asks 'who's in my room?'
  and gets the other members' session ids + virtual IPs + names - exactly
  what XNetQosLookup must hand the game's session search.
- JoinPayload now carries the client's virtual IP (10.80.0.x identity) so
  the relay can route and answer discovery.

Tested end-to-end (unicast + broadcast + room isolation + discovery).
2026-08-28 02:04:34 -04:00
MiTHRAL
bd855c5d88 netcode-revival: document the revival plan + Phase 0 progress 2026-08-28 01:58:21 -04:00
MiTHRAL
0d008a0764 netcode-revival: pin rexglue to 767bd6e (Phase 0 XNet session/address layer)
XNetGetTitleXnAddr identity, XnAddrToInAddr/ServerToInAddr virtual-IP
resolution, QosListen/QosLookup, session keys, Connect/GetConnectStatus and
getsockopt are now real in the rexglue netcode-revival branch.
2026-08-28 01:54:57 -04:00
MiTHRAL
f38185928d netcode-revival: pin rexglue to the netcode branch (395e1ce)
Foundation fixes (cvar teardown SIOF, imgui RasterizerGamma) so this branch
builds against the rexglue netcode-revival fork branch.
2026-08-28 01:48:17 -04:00
MiTHRAL
df86553b77 ghost: capture local player transform from the body item, not the UBT hook
The self-check cube (skate3_ghost_draw_local) appeared on a nearby NPC
because OnLocalSkaterTick read the world from the UpdateBoneTransforms
hook's pack-owner entity, and UpdateBoneTransforms fires for NPC skaters too
- the last writer won, often an NPC.

Local-player capture now happens in OnFrameSceneBuilt (the same per-frame
body-item heuristic the ghost models use): resolve the body item's OWNING
entity via the ctx->entity store and read its world matrix (entity+416),
falling back to the body root-bone translation. The UBT-based capture and
hook call are removed.
2026-08-28 01:20:52 -04:00
MiTHRAL
d809bbc25a windows-build.cmd: also exclude MSYS environment conversion (%LIB%)
The kernel32.lib LNK1104 persists despite MSYS2_ARG_CONV_EXCL because that
only protects command-line ARGUMENTS - MSYS/Git Bash still rewrites the
%LIB% ENVIRONMENT variable (backslashes + semicolons), so link.exe receives
POSIX paths and cannot find kernel32.lib. Set MSYS2_ENV_CONV_EXCL=* too, and
print LIB + warn when it is empty after vcvarsall.
2026-08-27 14:19:46 -04:00
MiTHRAL
81189576c0 releases: harden for Windows + one-click windows-build.cmd launcher
Addresses the known Windows build issues (see /mnt/archive/Issues.txt):
- windows-build.cmd: loads vcvarsall with a stable Windows SDK (WINSDK,
  default 10.0.26100.0) fixing kernel32.lib LNK1104; sets
  MSYS2_ARG_CONV_EXCL=* so Git Bash does not corrupt %LIB%; inits submodules
  (including the broken-pin self-heal) before building.
- release.sh: auto-detect the Title Update package (repo root or game dir);
  zip fallback to 7z or PowerShell Compress-Archive (Git Bash has no zip);
  upload JSON parsing falls back from python3/python to grep (Git Bash ships
  neither python).
2026-08-27 14:02:24 -04:00
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
eabb989ff7 ghost: log connection transitions at info by default
Some checks failed
release / build-linux (push) Has been cancelled
Connection state should be visible in the default logs for beta testers:
- 'ghost: joined <relay> room=<code> roster=N' now logs on connection
  TRANSITIONS (first join, reconnect, room change) instead of being gated
  behind skate3_ghost_debug (which spams every 5s re-JOIN HELLO).
- 'ghost: relay connection lost; reconnecting' logs at warn instead of
  debug.
2026-08-27 12:51:47 -04:00
MiTHRAL
e3a9fa0b99 releases: tolerate rexglue codegen exit crash; ship actual runtime lib name
The rexglue codegen tool segfaults/aborts during static teardown AFTER
writing its output (cvar registry SIOF + heap corruption at exit). The
generated source is complete and valid, so generate-skate3/generate-eawebkit
now wrap the invocation with bash -c that verifies the written output files
instead of the exit code - a genuine failure (unresolved calls without the
Title Update package) produces no output and is still caught. Fixes the
release build on toolchains that hit the teardown bug (also applies to CI).

release.sh: glob the actual rexglue runtime lib name (librexruntimerd.so for
relwithdebinfo vs librexruntime.so for release) so packaging ships the lib
the binary links.
2026-08-27 12:42:04 -04:00
MiTHRAL
babb887d4b ci: fix init-submodules empty-worktree check (ls -A counts .git)
A submodule with only a .git pointer looks non-empty to ls -A, so the
self-heal skipped every un-checked-out nested submodule. Check for real
worktree content instead (find excluding .git).
2026-08-27 11:53:54 -04:00
MiTHRAL
7173d7e88d releases: local build+upload script, optional Forgejo Actions workflow, docs
Clarifies that releases ship ONLY the recompiled binaries (skate3 +
librexruntime + ghost_relay + README) - never the game files; the game dump
(default.xex + EAWebkit.xex) is required at BUILD TIME for codegen only.

- tools/release/release.sh: builds + packages a per-platform release zip
  (configure -> codegen -> build -> zip), prints a sha256sum, and with
  --upload creates/updates a Forgejo Release and attaches the zip via the
  API (GITEA_TOKEN). Supports linux/windows/macos presets on their native
  hosts.
- .forgejo/workflows/release.yml: optional automation - builds on a
  self-hosted Docker runner (ubuntu-24.04 label), self-heals the broken
  nested submodule pins via tools/ci/init-submodules.sh, and publishes a
  Linux release on v* tags. Needs the game dump mounted on the runner
  (codegen only, never uploaded), SUBMODULE_KEY + RELEASE_TOKEN secrets.
- tools/ci/init-submodules.sh: recursive submodule init that self-heals
  upstream pins a fresh clone can no longer fetch (the rexglue fork pins
  thirdparty/imgui to a commit ocornut/imgui dropped; the script checks out
  the upstream default tip for any nested submodule left empty).
- docs/releases.md: what's in a release, the local path, and the optional
  CI path.
- .gitignore: keep tools/ci + tools/release, ignore local release zips.
2026-08-26 23:32:13 -04:00
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
1763b39eca ghost multiplayer: deployment docs + systemd unit + install script
- docs/ghost-multiplayer.md: the full beta guide - player usage, cvars,
  relay deployment (VPS build/install, free-tunnel option, verification),
  logging/troubleshooting and security notes.
- tools/ghost_relay.service: hardened systemd unit for the public relay
  (Restart=on-failure, NoNewPrivileges, PrivateTmp, ProtectSystem).
- tools/ghost_relay_deploy.sh: one-shot build + install + enable script for
  the VPS (GHOST_RELAY_PORT / GHOST_RELAY_VERBOSE overrides; opens the UDP
  port via ufw). Verified: the exact build line compiles and runs.
- README: multiplayer pointer under 'How Do I Play?'.
2026-08-26 22:21: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
7b949a312b submodule: repoint rexglue-sdk to the self-hosted fork
third_party/rexglue-sdk now fetches from the migrated copy on
git.mithraic.cloud (ad3laid3/rexglue-skate3, skate3-sdk-clean) instead of
the upstream GitHub fork, so the project no longer depends on the original
author's repo for its rexglue SDK.
2026-08-26 20:21:04 -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
ceefe4dc37 docs: drop the antivirus section from the readme 2026-07-24 20:20:59 +01:00
mchughalex
15db3f9adb sdk: bump for the startup build-title log line 2026-07-24 19:54:03 +01:00
mchughalex
27667a79a5 docs: explain antivirus false positives in the readme 2026-07-24 19:40:48 +01:00
mchughalex
671e74ba59 build: embed version resources in the windows binaries (SDK bump) 2026-07-24 19:40:48 +01:00
mchughalex
12e71d3172 sdk: bump for the opt-in inexact vulkan render target transfer cvar 2026-07-24 19:39:41 +01:00
mchughalex
b6c6196c3e build: raise the dev version floor to 2.0.0 2026-07-24 19:13:37 +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
49690b290b sdk: bump for present-cadence logging and the native_rhi cstddef fix 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