Skip to content

feat(v2.1.10): browser RetroAchievements deploy + Vs. DualSystem libretro present (Web Parity)#287

Merged
doublegate merged 5 commits into
mainfrom
feat/v2.1.10-web-parity
Jul 12, 2026
Merged

feat(v2.1.10): browser RetroAchievements deploy + Vs. DualSystem libretro present (Web Parity)#287
doublegate merged 5 commits into
mainfrom
feat/v2.1.10-web-parity

Conversation

@doublegate

Copy link
Copy Markdown
Owner

v2.1.10 "Web Parity" — browser RetroAchievements deploy + Vs. DualSystem libretro present

Finishes the Web Parity half of v2.1.10. All work is frontend / libretro /
infra — the deterministic #![no_std] core is untouched and byte-identical
(AccuracyCoin 141/141, visual_regression + save-state round-trip green).

1. Browser RetroAchievements — the ADR 0015 deploy carryover

The browser-RA marshalling (ra_glue.js rc_client trampoline + wasm_cheevos.rs)
has shipped since v1.7.0; the remaining ADR 0015 carryover was the deployable
auth proxy — the service that injects RA's identity User-Agent server-side
(browsers forbid scripts from setting it).

  • deploy/Dockerfile.raproxy runs the stdlib-only reference stub (non-root,
    env-configured); an ra-proxy compose service sits behind the shared Caddy
    TLS proxy, exposed at https://<DOMAIN>/ra/* (a handle_path strips /ra so
    the rcheevos path reaches upstream RA verbatim).
  • The proxy stub gained env-var configuration (RA_PROXY_BIND / RA_USER_AGENT
    / RA_UPSTREAM / RA_ALLOWED_ORIGINS / RA_ENFORCE_CASUAL) so one script serves
    both local dev (--config TOML) and the container (env only). No RA secret is
    held or committed — only the non-secret identity header is injected; the user's
    own login transits at request time.
  • wasm_cheevos.rs gains marshalling-contract tests (ACHIEVEMENT_TRIGGERED
    filtering, malformed-payload tolerance, the not-configured caveat).

Code-complete + validated (docker compose config valid, proxy env-config
verified, wasm browser-cheevos gate green). Needs live deploy to fully verify:
standing the stack on a real host, the RA-team User-Agent coordination, and a
real browser RA login + casual unlock (the un-CI-able ADR 0015 acceptance gate).
Runbook in deploy/README.md §"Browser RA live-verify checklist" +
docs/cheevos-browser.md.

2. Vs. DualSystem presentation — libretro (wasm documented-deferred)

The core already models the four Vs. DualSystem arcade boards (Emu::Dual /
VsDualSystem); this wires the libretro present path (crates/rustynes-libretro):

  • Detects cabinets with the same Emu::from_rom (NES 2.0 header Vs. type OR the
    SHA-keyed vs_db) the desktop frontend uses; the core now holds nes or
    dual (mutually exclusive).
  • Steps both consoles each retro_run and composes their two 256×240
    framebuffers into a single 512×240 XRGB8888 side-by-side image (MAIN left, SUB
    right), presented within a 512-wide max_width geometry so RetroArch draws the
    variable width without a geometry renegotiation.
  • Ports 0/1 → MAIN P1/P2, 2/3 → SUB P1/P2; MAIN audio plays (SUB APU ring drained
    to stay bounded); save states use VsDualSystem::snapshot/restore; memory maps
    expose the MAIN console. Previously a cabinet dump booted a single console that
    hangs on its absent partner.

Code-complete + builds (cargo build -p rustynes-libretro, clippy clean). A
live RetroArch run with a real cabinet dump is the maintainer's manual check.

wasm dual present is a documented deferred: the CPU compositor
(Gfx::compose_dual_into) and the core (Emu::Dual) are already cross-platform,
but the wasm ROM-load detection + un-gating the GPU present branch
(Gfx::render_dual, cfg(not(wasm))) are deferred to the v2.1.8/v2.1.9
gfx/composite rebase to avoid colliding with that concurrently-rewritten present
path (per the worktree concurrency directive). Mobile stays deferred.

3. Deploy stack coherence

deploy/ is now a coherent four-service stack (signaling + ra-proxy + caddy

  • coturn); docker compose up brings all four up. .env.example +
    deploy/README.md document the full stack, the RA proxy config, and both live-verify
    runbooks (netplay + browser RA). Not deployed live here — made deployable + documented.

Gates

  • cargo fmt --all --check ✅ · cargo clippy --workspace --all-targets -D warnings
  • clippy retroachievements ✅ · wasm32 (wasm-winit, wasm-canvas, browser-cheevos) ✅
  • cargo build -p rustynes-libretro ✅ · rustdoc -D warnings ✅ · markdownlint (v0.39.0) ✅
  • AccuracyCoin 141/141 ✅ · visual_regression (9) ✅ · save-state round-trip ✅ — core byte-identical
  • docker compose config valid ✅ · proxy py_compile + env-config ✅

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings July 12, 2026 04:08
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements the “Web Parity” deliverables for the WebAssembly/frontend deployment stack and the libretro wrapper: adds a deployable browser RetroAchievements auth-proxy service (env-configured) and wires Vs. DualSystem dual-screen presentation into the libretro core while updating documentation and changelog entries to match.

Changes:

  • Add env-driven configuration support to the browser RA auth proxy stub and ship it as a first-class deploy/ compose service behind Caddy at /ra/*.
  • Implement Vs. DualSystem detection and dual-framebuffer side-by-side (512×240) presentation in rustynes-libretro, including input/audio/state/memory-map behavior.
  • Add/extend documentation and changelog coverage for the deploy stack and the libretro dual presentation; add wasm RA marshalling-contract tests.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
scripts/cheevos/auth_proxy_stub.py Adds env-var override configuration so the same proxy stub works for local dev and container deployment.
crates/rustynes-libretro/src/lib.rs Adds Emu::from_rom-based DualSystem detection plus dual present/serialize/memory-map paths (512×240 composed output).
crates/rustynes-frontend/src/wasm_cheevos.rs Adds browser RA marshalling-contract unit tests (unlock filtering, malformed payload tolerance, “not configured” caveat).
deploy/Dockerfile.raproxy Introduces a container image for the RA auth proxy stub (non-root, env-configured).
deploy/docker-compose.yml Adds the ra-proxy service and wires it behind Caddy alongside signaling and coturn.
deploy/Caddyfile Proxies /ra/* to the RA proxy service (in addition to the existing signaling proxying).
deploy/.env.example Documents required RA proxy environment variables (User-Agent, CORS allowlist, upstream, casual enforcement).
deploy/README.md Expands deploy bundle docs to include the RA proxy and a manual live-verify checklist.
docs/cheevos-browser.md Updates browser RA docs to reflect deployable auth-proxy service + env-driven configuration.
docs/libretro/advanced_features.md Documents libretro Vs. DualSystem two-screen presentation behavior and constraints.
docs/frontend.md Notes libretro dual present parity and documents wasm/mobile deferrals for dual present.
CHANGELOG.md Adds Unreleased bullets describing libretro dual present and the RA proxy deploy stack work.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/rustynes-frontend/src/wasm_cheevos.rs Outdated
Comment thread crates/rustynes-libretro/src/lib.rs
Comment thread scripts/cheevos/auth_proxy_stub.py
doublegate and others added 4 commits July 12, 2026 04:31
… Web Parity)

Wire the core's existing Vs. DualSystem support (Emu::Dual / VsDualSystem — the four cross-wired arcade boards) through the libretro present path, mirroring the desktop frontend's emu.dual branch. Previously the wrapper only built a single Nes, so a DualSystem dump would boot one console that hangs on its absent partner.

Detection reuses Emu::from_rom (NES 2.0 header Vs. type OR SHA-keyed vs_db). The core holds nes OR dual (mutually exclusive). run_dual steps both consoles, composes their two 256x240 RGBA framebuffers into one 512x240 XRGB8888 side-by-side image (MAIN left, SUB right), and presents it; max_width in the AV info is 512 so RetroArch honours the per-frame width without a geometry renegotiation. Ports 0/1 drive MAIN P1/P2, 2/3 drive SUB P1/P2. Only MAIN audio plays; the SUB APU ring is drained to stay bounded. Save states use snapshot/restore; memory maps expose MAIN. Shared blit + audio helpers; hot buffers pre-sized to 512x240. The deterministic no_std core is untouched and byte-identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…, v2.1.10)

The browser-RA marshalling (ra_glue.js + wasm_cheevos.rs) has shipped since v1.7.0; the remaining ADR 0015 carryover was the DEPLOY half — the auth proxy that injects RA's identity User-Agent server-side. Adds deploy/Dockerfile.raproxy + an ra-proxy compose service behind Caddy at https://<DOMAIN>/ra/*, env-configured (no committed secret; the proxy holds no RA secret). The stub gained env-var config so one script serves local dev and the container. .env.example + deploy/README.md document the four-service stack + a browser-RA live-verify runbook. wasm_cheevos.rs gains marshalling-contract tests. docs/cheevos-browser.md records the deployable proxy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Document the v2.1.10 Web Parity work: the libretro Vs. DualSystem side-by-side present (docs/libretro/advanced_features.md + docs/frontend.md present-path parity note), and record the wasm desktop-style dual present as a documented deferred (cross-platform compositor + core already exist; the GPU present un-gate + wasm load-detection defer to the v2.1.8/v2.1.9 gfx rebase). CHANGELOG [Unreleased] gains the libretro-dual + browser-RA-deploy Added entries and the wasm-dual Deferred entry, explicit about code-complete vs needs-live-deploy-to-verify.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-defer rationale

The rebase onto origin/main (through v2.1.9) correctly relocated the v2.1.6-2.1.9 entries into their released [2.1.x] sections and left [Unreleased] empty; re-add ONLY the v2.1.10 Web Parity entry (libretro Vs. DualSystem present + browser-RA deploy Added, wasm dual Deferred). Update the docs/frontend.md wasm-defer note to the accurate post-rebase rationale: the concurrency reason is resolved, so the deferral now rests on the real basis — enabling wasm dual present is a multi-site change to the common wasm present hot-path (separate wasm ROM-load path, un-gate present_dual/dual_mode + Gfx::render_dual) for a niche feature that cannot be runtime-verified in CI (no headless browser GPU present); the libretro core delivers Vs. DualSystem for RetroArch now.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@doublegate doublegate force-pushed the feat/v2.1.10-web-parity branch from 11b1d9d to 345f2f1 Compare July 12, 2026 08:34
…, assert cleanup

Three Copilot review points on #287:

1. libretro push_audio: audio_buffer had capacity 4096 but pushes up to 2x the mono drain (8192 stereo samples), so it could reallocate on the hot path. Bumped the pre-alloc to 4096*2 (Default + retro_core!) and added a reserve(produced*2) belt-and-suspenders guard so the no-alloc guarantee stays true even if audio_float_buffer is later grown.

2. auth_proxy_stub.py: a non-numeric RA_PROXY_BIND (or config bind) port raised a raw ValueError traceback. Added _parse_port() that fails fast with a clear, actionable message (and an out-of-range 1-65535 check) for both the file-config and env paths — clean exit for container deploys, not a stack trace.

3. wasm_cheevos.rs: removed a stray trailing comma in a single-condition assert!(). (assert!(expr,) is actually valid Rust — the macro accepts an optional trailing comma — but the cleaner form removes any doubt.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@doublegate doublegate merged commit 5f495de into main Jul 12, 2026
21 checks passed
@doublegate doublegate deleted the feat/v2.1.10-web-parity branch July 12, 2026 13:21
doublegate added a commit that referenced this pull request Jul 12, 2026
…#295)

Cuts the v2.1.10 release — the creator-tools-and-web-parity step of the
v2.1.5 → v2.2.0 "deepen the existing project" run. Bumps the workspace version
2.1.9 → 2.1.10, renames CHANGELOG `[Unreleased]` → `[2.1.10]`, refreshes
Cargo.lock, syncs the README badge / current-release paragraph / BibTeX, and
authors `.github/release-notes/v2.1.10.md`.

v2.1.10 (PRs #287, #288) deepens content-creation and closes the deferred browser
gaps, on the byte-identical deterministic core (AccuracyCoin 141/141,
visual_regression byte-identical, save-state / TAS / netplay replay bit-identical):

- #288 — TAStudio greenzone / force-greenzone caching + Lua API breadth (full
  lifecycle callbacks, memory read-write with *Debug variants, HUD drawing,
  example library, wasm piccolo parity). Two review-surfaced greenzone
  anchor-bookkeeping bugs fixed with new regression tests.
- #287 — browser RetroAchievements (wasm trampoline + deployable auth-proxy,
  ADR 0015; live login is a documented deploy-to-verify step) + Vs. DualSystem
  presentation in the libretro core. Vs. DualSystem wasm present honestly
  deferred. A real rustynes-libretro push_audio realloc bug fixed en route.

No default-build behavior change.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants