CNA-PLAT-012: Under Emscripten, SystemLink discovery is empty, hosting binds the fixed port 61191 and Join returns before the handshake

CNA snapshot 009d40f5  ·  Known Issues › Platform limitations  ·  source links pinned to 009d40f5

✓

Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.

The web build stubs out discovery (Find returns nothing), hosts on a fixed port only a Node.js relay can use, rebuilds a joining session as an outbound-only client and returns from Join before the server welcome; Node.js-run unit tests cover discovery and loopback handshakes, but no browser or two-process test exists.

Identifier
CNA-PLAT-012
Category
Platform limitation
Subsystem
Networking & gamer services
Status
Open
Verified against
CNA 009d40f5 (009d40f5dd085c4e674d3479675fac84b12b3e0a)
Evidence basis
Source-verified: read at TARGET, not executed
Tests touching this area
Yes: see Current tests
Affected contract
Microsoft::Xna::Framework::Net::NetworkSession Create, Find, Join and Update for NetworkSessionType::SystemLink in Emscripten builds

Expected behaviour

A program that finds a LAN session, joins it and sends its first packet behaves the same on every target, or the target states what it cannot do.

Actual behaviour at TARGET

The __EMSCRIPTEN__ branch of ENetDiscoveryService.cpp defines RegisterHost, UnregisterHost and Poll as empty functions and FindSessions as returning an empty vector, so Find always returns nothing on the web: "cannot search", not "no hosts". ENetBackend::StartHosting binds kEmscriptenHostPort (61191) instead of an OS-assigned port, because Emscripten's socket layer does not report the bound port back; only a Node.js-run relay or server can accept on it, since a browser tab cannot listen. ENetBackend::ConnectToHost replaces the session's transport state with an outbound-only client. EndJoin's bounded wait for the ServerWelcome is inside #ifndef __EMSCRIPTEN__ in NetworkSession.cpp, so Join returns while the construction-time local placeholder is still the host, and a caller that sends at once addresses itself until Update has processed the welcome. The networking demos and the process-spawning tests are excluded from Emscripten builds.

Source locations

Evidence

Checked by reading at 009d40f5; nothing was built for Emscripten or run in a browser or Node.js. The constraints come from the web platform (no raw UDP, no listening sockets in a tab) and Emscripten's socket emulation, which is why this is a platform limitation rather than a bug; the joined-before-welcome return is the one piece a web-specific wait (for example a promise-driven loop) could change. The network-sessions deep dive already records these as source-level statements.

Independently observed as a separate finding (merged): The browser build has no LAN discovery, hosts only through a Node.js relay on a fixed port, and compiles out EndJoin's wait for ServerWelcome.

Independent re-verification: Checked by reading at 009d40f5; nothing was built for Emscripten or run in a browser or Node.js during this review. The constraints come from the web platform (no raw UDP, no listening sockets in a tab) and Emscripten's socket emulation, which is why this is a platform limitation rather than a bug; the joined-before-welcome return is the one piece a web-specific wait could change. Emscripten-specific test coverage does exist in source: ENetDiscoveryServiceTests expect Find to be empty on Emscripten, ENetBackendTests and ENetHostHandleTests use fixed ports and emscripten_sleep for the SystemLink loopback and ConnectToHost cases, and UnitTests.cmake links CnaTests with -sJSPI=1 for Node.js. Only the process-spawning tests (TwoProcessLoopbackTest, the dispatcher hang test) and the networking demos are excluded, so no test joins from a second process and none runs in a browser.

Focused reproduction

No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.

Current tests

None for any __EMSCRIPTEN__ path; the process-spawning transport and dispatcher tests are compiled out there.

Regression test

A Node.js-hosted test of the Emscripten build that hosts on 61191, joins from a second Node.js process and checks that the first packet sent after the welcome reaches the host, plus a unit test that Find returns an empty collection at once.

Blast radius

Web builds that use SystemLink: no discovery, hosting only in a Node.js server build, and a join that completes asynchronously. Native builds are unaffected.

Workaround

After Join on the web, keep calling Update until the session's host is a remote gamer before sending the first packet. There is no web-side discovery replacement.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.