# Neon Boxing: real-time game reference

Available now as free play in [Neon Boxing](../play/online-boxing). No token deposit, wager,
burn or payout is performed. [Token settlement](multiplayer.md) remains a separate proposal.

## The game

Two players fight in a side-view neon ring. Use footwork, stamina management, head/body attacks,
high/low guards and slips. A knockout ends the match; otherwise three 45-second rounds are
scored 10–9 to the fighter who dealt more damage, or 10–10 on equal damage. Equal total cards
after three rounds draw. Simultaneous knockouts also draw. Corner breaks last six seconds;
health recovers by 15 points, stamina resets, then a three-second countdown starts the next round.
There is no camera/video chat requirement. Practice is an explicitly labeled local sparring bot;
public matchmaking and invitations always reserve two human-controlled seats.

| Control | Keyboard | Touch |
| --- | --- | --- |
| Move | A/D or left/right arrows | Hold the arrow buttons |
| Head guard | W, up arrow or Space | Hold Guard ↑ |
| Body guard | S or down arrow | Hold Guard ↓ |
| Jab / cross | J / K | Tap Jab / Cross |
| Body hook / uppercut | L / I | Tap Body hook / Uppercut |
| Slip | Shift | Tap Slip |

Punches have wind-up, range, stamina cost and recovery. Attacks cannot stack. High guard blocks
head attacks; low guard blocks body hooks. Slips avoid head punches for a short window, with a
stamina cost and cooldown. A hit can interrupt a punch still winding up. Both fighters' impacts
are resolved before checking a knockout, so a simultaneous exchange has no seat-order advantage.

## Matchmaking and transport

Use the shipped [free lobby protocol](online-games.md) with `game: "boxing"`:
public queue, private invite, and both players explicitly Ready. The active fighter session comes
from the same platform parent, never from uploaded code. One active room per guest still applies.

After the room becomes `playing`, connect to `wss://arcade.fun/api/multiplayer/boxing/ws`.
The first frame authenticates; credentials never appear in the URL:

```json
{"type":"hello","room":"the reserved room ID","token":"the private 64-hex guest token"}
```

The service separately validates the exact Origin/host, authenticated guest, room membership and
playing state. The uploaded-games origin is rejected. A second tab taking the same seat replaces
its previous socket; the old tab stops controlling the fighter and can still recover its result.

Each subsequent frame contains a strictly increasing sequence and a bounded input:

```json
{"type":"input","seq":1,"input":{"move":1,"guard":"none","strike":"jab","slip":false}}
```

`move` is integer -1, 0 or 1; `guard` is none/high/low; `strike` is null/jab/cross/hook/uppercut;
`slip` is a boolean. Strike/slip are one-tick impulses, not automatic repeated attacks. No client
position, damage, health, stamina, clock or winner is accepted. The rate limit is 30 control
frames/second/seat. Parsed frames are at most 1 KiB; the transport rejects frames above 2 KiB.
Unrefreshed held movement/guard becomes neutral after 400 ms. The client normally sends controls
about ten times a second, including neutral heartbeats; blur or hidden tabs release held buttons.

Server physics runs at 20 Hz with integer rules. Snapshots arrive at 10 Hz as
`{type:"state", room, ack, connected, waiting, hasStarted, grace}`. `room` has the usual private
snapshot fields; its `state` is the full boxing simulation. `ack` is this seat's input sequence.
The client smooths rendering only. It cannot adjudicate combat. Recover from the latest snapshot
and acknowledgement; never replay a buffered punch automatically after reconnecting.

Both sockets must connect before the countdown advances. A dropped connection pauses simulation
for up to 15 seconds, with a cumulative absence budget of 20 seconds per fighter for that match.
The remaining connected fighter wins a forfeit when the absence limit expires. If both are absent,
initial setup fails, rules change or the server restarts, the fight is void. A scheduler stall of
more than two seconds voids instead of applying a burst of catch-up damage. Clocks do not turn a
server outage into a client-authorized win. There are at most eight simultaneous boxing rooms,
including waiting invitations, on the current single-machine deployment.

## Replay and creator tools

The authority checkpoints state and the exact inputs it applied in one transaction every 500 ms.
Results and input logs use the same private receipt boundary and 30-day retention as board rooms.
`GET /api/multiplayer/rooms/{id}/replay` returns `{match, initial, inputs}`; each input event has
a logical tick, seat, sequence and input object. Impulses are cleared after each replay tick;
movement/guard remain latched until the next recorded change. Replays do not advance during
network pauses. Different rules hashes must not be replayed using current rules.

The results screen can play/scrub that replay and download JSON. It also retains the usual local
recording, private feedback form and unchecked social permission. Health, stamina, round clocks
and bot labels are rendered into the recorded canvas. Reopening an archived match cannot borrow
another match's recording, even when the game title is the same.

- [Browser simulation](../js/boxing-rules.js): standalone deterministic `initial`, `neutral`,
  `step`, `finish` and punch definitions; importable in Node.
- `arcade/boxing.py`: independent server authority using matching integer rules.
- `arcade/boxing_service.py`: authenticated transport, presence/grace, fixed steps, bounded rings,
  atomic checkpoints, applied input replay and terminal results.
- `public/js/boxing-art.js`: canvas ring, fighters, punches, hit effects and recording HUD.
- `public/js/games/boxing.js`: parent controls, lobby, guest session, reconnect and replay viewer.
- `tests/test_boxing.py`, `test_boxing_rules.cjs`, `test_boxing_ui.cjs`: rule boundaries,
  concurrent hit resolution, complete cross-language fights and control/reconnect regressions.

This is a reviewed native adapter, not permission for uploaded code to install a backend or
access another player's guest token. Use it as an architectural reference: keep rules, inputs,
rendering, transport, replay and future funds verification separate. These free receipts do not
provide wallet authentication or authorize token settlement.
