Give the friend's racket a sound and the duel a rematch

Two netplay-only finds from Oliver's two-phone session.

THE FRIEND'S STRIKE was silent: every other opponent is simulated locally
and cracks through gameplay's shared return path, but the friend's swing
runs on their phone alone and arrives as a 'shot' message that wrote the
ball and nothing else. netApplyShot now plays the same sample at the same
volume, pitched off the pace that was actually sent (strikeRate, read after
the ball is written). Both netplay modes, not just the death match.

THE REMATCH: the summary's only netplay door was THE CLUBHOUSE, so playing
the same friend again meant two trips through the lobby and a fresh code.
The relay cannot re-pair, so the rematch is the socket that is still open —
one 'again' frame and both phones reset the run under the decisions the room
already carries (same side, same mode, same court, host serves). netAgain
keeps G.net across restartRun(true) and replays netFresh, netStart's own
tail split out so pairing and rematch cannot drift into two courts. Applied,
never echoed; a 2 s window swallows the second frame when both tap at once;
a dead line refuses and falls through to the clubhouse.

Also: the dev server runs with SPLAT_AUTH= (server.py always said local dev
should), which the shared password had been 401-ing. audio.sfxTest is a new
test seam in netTest's shape. 146 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-09-11 21:28:31 +02:00
1 parent b53fc07dd1
commit 78946015b4
9 files changed
+189 -17

No files matched your search

+36
View File
@@ -5063,6 +5063,42 @@ Release cache version: `main.js?v=353`. Pre-release validation: 142 game tests a
menu browser checks pass. Deploy staging first, verify the served release, then promote the same
commit to production. Staging URL: `https://splatter-tennis-staging.wuwi.one`.
## Session log: VS A FRIEND — the friend's racket makes a noise, and the duel has a rematch (2026-09-11, v354)
Oliver, two finds off a two-phone session: *"im vs. a friend mode wird der ballsound des gegners
nicht abgespielt (zumindest deathmatch)"* and *"nach einem vs. a friend duell gibt es kein retry /
replay button"*. Both are netplay-only by construction, which is why neither had been seen before.
- **THE FRIEND'S STRIKE HAD NOWHERE TO COME FROM.** Every other opponent in the game is simulated
locally and cracks through gameplay's own strike line (`sfx('tennis-opponent', …)` in the shared
return path, which is why the CPU has always sounded right). The friend's swing runs on nobody's
phone but theirs — it arrives as a `shot` message and `netApplyShot` writes the ball straight
into the world — so half of every netplay rally was silent, in BOTH netplay modes (the report
hedged "at least deathmatch"; it was never mode-specific). One line at the end of `netApplyShot`,
after the ball is written so `strikeRate()` pitches off the pace that was actually sent.
`gameplay.strikeRate` is exported for it, and `audio.sfxTest` is a new test seam in the shape of
`netTest.sent`, so the suite can assert the crack instead of trusting the ear.
- **THE REMATCH.** The summary's only netplay door was THE CLUBHOUSE: playing the same person again
meant both of them walking back through the lobby and reading a fresh code aloud, for the one
mode whose whole point is the person opposite. The relay cannot re-pair (its room code is popped
on pairing, by design), so the rematch is THE SOCKET THAT IS STILL OPEN — one `again` frame, and
both phones reset the run under the decisions the room already carries: same side, same mode,
same court, host serves. `net.netAgain()` keeps `G.net` across `restartRun(true)` (the new flag
is the whole change to that function) and replays `netFresh()`, which is netStart's own tail
split out so the pairing and the rematch cannot drift into two different courts. It is applied,
never echoed (the rule every verdict in that file follows), and a 2 s window swallows the second
frame when both players tap at once. A dead line refuses, so the button falls through to the
clubhouse exactly as before.
- **The summary grows its second door in netplay** (`online` in render.js): REMATCH filled, THE
CLUBHOUSE outlined, NEXT reading ANOTHER DUEL · YOUR FRIEND IS STILL ON THE LINE. It is drawn on
the loser's BLED OUT screen too — the phone most likely to want another one.
- **`.claude/launch.json` now runs the dev server with `SPLAT_AUTH=`**, which server.py's own note
always said local dev should do. The shared password (2026-09-08) had been 401-ing the browser
pane, and a preview that cannot load is a verification round lost every session.
- Verified end to end through the real relay in two panes: pair → death match → REMATCH on the host
→ both courts come up 0-0 with full blood, the friend standing, no gore, "REMATCH — YOUR SERVE"
against "REMATCH — THEIR SERVE". 146 tests.
# OPEN — leftovers, in rough priority order
Nothing here is broken; these are known gaps and unverified risks.