Add standalone POC comparing WebRTC DataChannel vs Socket.IO

Self-contained test under poc-webrtc/ that does not touch the game.
Spins up an Express + WebSocket signaling + node-datachannel server
alongside a Socket.IO server, serves a simple browser client that
runs the same game-like traffic pattern (14Hz worldUpdates, input
events, ping/pong) over either transport based on a URL flag.

Captures per-session stats to a JSONL file and ships an analyze.js
that prints a per-(transport, phase) summary of RTT percentiles,
receive rate, and seq-gap counts so the TCP-vs-UDP-style comparison
becomes quantitative rather than eyeball.

Confirms node-datachannel installs and works on this platform and
that the dual-channel (reliable + unreliable) pattern is feasible
to maintain — both prerequisites for the real integration.
This commit is contained in:
Jeena 2026-05-11 00:38:01 +00:00
parent 71e4b4e847
commit 47faae81e5
9 changed files with 2407 additions and 0 deletions

85
poc-webrtc/README.md Normal file
View file

@ -0,0 +1,85 @@
# chuck.js WebRTC POC
Isolated test of WebRTC DataChannels (UDP-like, unreliable) as an alternative
to Socket.IO (TCP) for the game's networking. Does not touch the chuck.js code.
## Run
```
cd poc-webrtc
npm install
npm start
```
Open two browser tabs:
- http://localhost:1235/?transport=webrtc
- http://localhost:1235/?transport=socketio
Use **A/D** or **←/→** to move, **Space** to jump. The blue box is yours; yellow
boxes are other connected clients (open more tabs to add players).
The right-hand panel shows live RTT, packet rate, last received sequence
number, and number of detected sequence gaps (= packets the unreliable channel
silently dropped).
Every connection also streams its stats to the server once per second. The
server writes everything to `runs/<timestamp>.jsonl` (one file per server
start). Use the **phase buttons** on the UI before/after changing network
conditions so the analyzer can group samples per phase.
After a test session, summarize with:
```
node analyze.js
```
This prints a per-(transport, phase) table of RTT percentiles, receive rate,
and seq gap deltas — making the comparison quantitative instead of eyeball.
## What to look for
Baseline on localhost (no packet loss): both transports show single-digit
millisecond RTT and zero gaps. WebRTC's RTT may be a hair higher due to DTLS
overhead. That's fine — the point of the comparison is **under loss**.
Add packet loss via Chrome DevTools (Network → "Add custom profile" with
download/upload throttling and packet loss) **or** via `tc netem` on Linux:
```
sudo tc qdisc add dev lo root netem loss 5% delay 50ms
# … run the test …
sudo tc qdisc del dev lo root
```
Expected difference under 5% loss:
| Metric | Socket.IO | WebRTC unreliable |
|-----------------------|----------------------|------------------------|
| RTT median | rises slightly | rises slightly |
| RTT 95th percentile | spikes wildly (HoL) | stays close to median |
| Seq gaps | ≈ 0 (TCP retransmits)| matches loss rate |
| Box motion smoothness | stutters | smooth (skips a frame) |
The Socket.IO tab will stutter visibly because TCP holds back all later
packets until the dropped one is retransmitted. The WebRTC tab will drop a
worldUpdate here and there but every other update arrives on time.
## What this POC does NOT include
- TURN server (only STUN — works on localhost/LAN; real internet may need
TURN for symmetric NATs)
- Reconnection / connection-loss handling
- Auth / encryption beyond WebRTC's built-in DTLS
- chuck.js's actual protocol (it mimics the *shape* — 14Hz broadcasts, input
events, ping/pong — not the message format)
## Decision after running
If WebRTC clearly outperforms under packet loss → write the integration plan
that swaps chuck.js's Socket.IO game-command stream for an unreliable
DataChannel (keeping Socket.IO or a reliable channel for join/leave/round
control).
If the difference is marginal → the reconciliation-based approach already in
the game is probably good enough, and the integration cost isn't justified.