Skip to content

fix(netplay): correct room-list and socket URLs behind a reverse proxy#3839

Merged
gantoine merged 4 commits into
masterfrom
fix/netplay-malformed-polling-url
Jul 20, 2026
Merged

fix(netplay): correct room-list and socket URLs behind a reverse proxy#3839
gantoine merged 4 commits into
masterfrom
fix/netplay-malformed-polling-url

Conversation

@gantoine

Copy link
Copy Markdown
Member

Description

Fixes #3799 — behind a reverse proxy / custom domain, the netplay room list is always empty and Create/Join Room do nothing.

Root cause

RomM's netplay overrides in Player.vue targeted an outdated EmulatorJS API (the netplay functions moved from the emulator object onto the netplay instance in the nightly build). On the current nightly build those overrides are dead code, so the emulator's own URL logic runs instead:

  • getOpenRooms() builds the request from netplay.url, which RomM set to window.location.host (a bare host, no scheme). The browser resolves that relative to the current SPA route, producing https://<domain>/<route>/<domain>/list?... — the domain duplicated inside the path, exactly as in the report. nginx's SPA fallback (try_files ... /index.html) then answers with index.html (200, text/html), JSON.parse fails, the error is swallowed, and the room list is always empty.
  • The socket connects with io(netplay.url) using Socket.IO's default path instead of RomM's mounted /netplay/socket.io, so creating/joining a room silently does nothing.

Changes (frontend/src/views/Player/EmulatorJS/Player.vue)

  1. EJS_netplayServer: window.location.hostwindow.location.origin (scheme included, so io() has an unambiguous absolute server).
  2. Override netplay.getOpenRooms on the actual netplay instance to hit /api/netplay/list?game_id=... with a root-relative path (correct on any domain / proxy). The /api/netplay/list endpoint already exists in the backend.
  3. Wrap the EmulatorJS-bundled global io (socket.io is bundled UMD onto window.io, and netplay's io(this.url) uses that global) to inject path: "/netplay/socket.io". The wrap is idempotent and only affects EmulatorJS — RomM's own app socket imports the client module rather than using the global.

No backend changes required.

AI assistance disclosure

This change was investigated and implemented with AI assistance (Claude Code): root-cause analysis of the EmulatorJS nightly netplay source, the fix, and this description. All changes were reviewed by a human.

Checklist

  • I've tested the changes locally
  • I've updated relevant comments
  • I've assigned reviewers for this PR
  • I've added unit tests that cover the changes

Note on testing: typecheck and trunk fmt/trunk check pass clean. End-to-end netplay could not be exercised here (it requires the live nightly CDN build, STUN/TURN, and a custom domain). Please smoke-test behind a reverse proxy: confirm the polling request in DevTools hits /api/netplay/list?... and returns JSON, and that Create/Join rooms connect over /netplay/socket.io.

Known fragility: netplay pins EmulatorJS to nightly, so this class of breakage can recur whenever upstream refactors the netplay API.

RomM's netplay overrides targeted an outdated EmulatorJS API, so on the
nightly build they were dead code and the emulator's own URL logic ran.
That built the room-list request from `netplay.url` (the page host with no
scheme), which resolved relative to the SPA route and duplicated the domain
inside the path. nginx's SPA fallback then answered with index.html, so the
room list was always empty. The socket also used the default socket.io path
instead of RomM's mounted /netplay/socket.io.

- Set EJS_netplayServer to window.location.origin (scheme included).
- Override netplay.getOpenRooms on the current netplay instance to hit
  /api/netplay/list with a root-relative path.
- Wrap the EmulatorJS-bundled global io to inject the /netplay/socket.io path.

Fixes #3799

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 19, 2026 22:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@greptile-apps

greptile-apps Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR updates EmulatorJS netplay URLs for reverse-proxy deployments. The main changes are:

  • Uses the full page origin as the netplay server.
  • Fetches rooms from RomM's root-relative API endpoint.
  • Wraps the bundled Socket.IO client with RomM's netplay socket path.

Confidence Score: 4/5

The netplay override timing needs to be fixed before merging.

  • Startup waits leave the original room-list and socket behavior active.
  • Polling or Create/Join activity during that interval can still use the wrong endpoints.

frontend/src/views/Player/EmulatorJS/Player.vue

Important Files Changed

Filename Overview
frontend/src/views/Player/EmulatorJS/Player.vue Corrects netplay room and socket URLs, but installs the overrides after asynchronous startup work.

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
frontend/src/views/Player/EmulatorJS/Player.vue:478
**Overrides Install After Startup Waits**

These overrides run near the end of the async `EJS_onGameStart` callback, after its save and game-manager waits. If room polling or a Create/Join action starts while that callback is suspended, EmulatorJS still uses its original room URL and default Socket.IO path, so the room list remains empty or the socket connection fails. Install both overrides synchronously before the callback's first wait.

Reviews (1): Last reviewed commit: "fix(netplay): correct room-list and sock..." | Re-trigger Greptile

Comment thread frontend/src/views/Player/EmulatorJS/Player.vue Outdated
Move the getOpenRooms and io-wrap overrides to the top of EJS_onGameStart so
they are installed synchronously, before room polling or a Create/Join action
can start, rather than at the tail of the callback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gantoine gantoine added the allow-v1-changes Allow changes to V1 UI label Jul 19, 2026
@gantoine
gantoine merged commit ed4baf1 into master Jul 20, 2026
13 of 14 checks passed
@gantoine
gantoine deleted the fix/netplay-malformed-polling-url branch July 20, 2026 00:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

allow-v1-changes Allow changes to V1 UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Netplay creates a malformed polling request

2 participants