The server reveals a strike's disc to every player from its launch until its blast ends #139

Owner

Tracker: #3773

Step 1 of plan #3560: the shared reveal rule and the server's side of it. The client's fog does not lift yet; steps 2 and 3 do that, and step 3 writes the changelog and the help for the whole change.

What changes

  • src/shared/nuke.ts holds BLAST_SECONDS (15 s, main's client-only NUKE_AFTER), BLAST_TICKS (450), the outer shock ring's factor OUTER_RING_REACH (1.35) and REVEAL_RADIUS (1.35 × BLAST_RADIUS ≈ 13.36 tiles). revealing(due, tick) is the window's rule, tick < due + BLAST_TICKS, and addReveals(strikes, tick, out) gives the sight sources the listed strikes lend every player at a tick.
  • The Game keeps each landed strike with its due tick until its window ends, drops it by revealing and bumps nukeRevision when it leaves. nukes() lists the landed strikes beside the strikes in flight, and reveals() gives the rule's sight sources at the Game's tick. The landed strikes are saved, so MAP_VERSION goes to 20.
  • The wire's Nukes carries landed: each landed strike's owner, tile and ticks since it landed (proto LandedMsg, src/shared/wire_pb.ts regenerated by npm run proto, codec and protocol). Every Nukes literal in the tests carries landed.
  • ServerHost.reveal stamps the rule's discs into every session's sight on every call. The host reads them once per tick, and drop() clears them beside sights. What knows, shots, hits, deaths, yields and the explored margins send then follows as for any sight.
  • Combat: engage's team vision gives every player the discs, so units may target what a disc shows.
  • src/client/render/nuke.ts reads BLAST_SECONDS for the effect's disposal and the cloud's fade, and OUTER_RING_REACH for the outer ring. The values are main's, so nothing draws differently.

Tests

  • Host (src/server/host.test.ts): a HeavyTank 11.4 tiles from the impact is in the launcher's and a third player's replicas from the first frame each gets after the launch (the Launch echo, then the next tick) through game tick due + BLAST_TICKS − 1, and in a Snapshot synced 10 ticks after the landing. Both sessions get UnitHidden for it on due + BLAST_TICKS. A tank 14.6 tiles out is in neither replica at any tick. Each session's Nukes go in flight, landed with 0 ticks since, then empty.
  • Combat (src/server/nuke.test.ts): a third player's Artillery, 14.3 tiles from an enemy HeavyTank in the disc (past its own sight and the tank's), targets the tank on every tick from the launch through due + BLAST_TICKS − 1 and fires on it. The tank is alive at due + BLAST_TICKS. From then on, for 300 ticks, the Artillery has no target and fires no shot. Without the launch it never targets or fires.
  • Rule (src/shared/nuke.test.ts): the disc at the launch tick and at due + BLAST_TICKS − 1, none at due + BLAST_TICKS.
  • Save (src/server/save.test.ts): a game saved 100 ticks after a landing restores the landed strike, and the revived and the original games end its reveal and drop it from nukes() on the same tick.
  • Codec (src/shared/codec.test.ts): a Nukes with a landed strike round-trips.

Mutation checks, each in a scratch copy of the tree:

mutant fails
revealing uses <= rule, host, combat and save tests
team vision drops the discs combat test
ServerHost.reveal drops the discs host test
the Game keeps no landed strike host, combat and save tests

The unmutated copy passes all 77 tests of those files.

Play checks

Base and head are esbuild bundles of src/ai/sim.ts and src/bench/net.ts, built from git archive trees of main d4a6158 and of the head. Math.random is seeded per match. All 42 runs went in parallel on m5 in 5 min 20 s.

The 14 seeded AI matches (small seeds 1–6 with 2 seats, medium 1–4 with 3, large 1–4 with 4) all ran to their end. 8 traces are identical to main's. These are the 8 matches in which no seat launches. The other 6 differ, and each first differs after its first launch. Each still ends for the same reason, with the same seats standing.

match first launch on main (s, seat, tile) first differing record (s) end
medium-1 851.2, 1, 28,84 867.2 time limit, 3 standing
medium-2 691.2, 1, 63,82 846.4, after the second launch at 841.6 time limit, 3 standing
medium-3 721.6, 1, 129,77 723.2 time limit, 2 standing
large-1 672.0, 1, 191,96 673.6 time limit, 3 standing
large-2 796.8, 3, 214,117 798.4 time limit, 2 standing
large-3 721.6, 4, 154,223 723.2 time limit, 3 standing

npm run bench (seed 11, wan): battle-128 (the default case), and economy, cities and churn at 128 and 256. None of them launches a nuke. Every case is identical to main in hash, gameHash and commandHash, and in every client's raw, deflated and city bytes a second.

The Nuke yard's match is strikeSetup(11) with the yard's silo armed, its target tile 36,22 and its squad of eight round the impact. It leaves out the camera-placed edge Factories and the trees. Both players have echo sessions. From the launch through the window's end (750 ticks), the frames are:

session main head
Blue, the launcher 7 frames, 4,976 B raw, 2,800 B deflated 16 frames, 16,674 B raw, 7,600 B deflated, 13 enemy units sent
Red, the struck side 15 frames, 7,582 B raw, 3,898 B deflated 16 frames, 7,620 B raw, 3,934 B deflated

So the window adds 11,698 B raw (4,800 B deflated) to the launcher's frames over its 25 s, and 38 B raw to the struck side's, which are the landed strike in Nukes and the Nukes at the window's end.

Gates

Both on 811810b with a clean tree, on m5:

  • fast (run 498's #1, gate run 272): passed, npm test 975/975 in 28.6 s.
  • full (run 498's #2, gate run 274): passed, npm test 975/975 in 30.7 s, and test:phone 180/180 in 205 s.
Tracker: [#3773](https://sak.swipelab.com/tasks/3773) Step 1 of plan [#3560](https://sak.swipelab.com/tasks/3560): the shared reveal rule and the server's side of it. The client's fog does not lift yet; steps 2 and 3 do that, and step 3 writes the changelog and the help for the whole change. ## What changes - **src/shared/nuke.ts** holds `BLAST_SECONDS` (15 s, main's client-only `NUKE_AFTER`), `BLAST_TICKS` (450), the outer shock ring's factor `OUTER_RING_REACH` (1.35) and `REVEAL_RADIUS` (1.35 × BLAST_RADIUS ≈ 13.36 tiles). `revealing(due, tick)` is the window's rule, `tick < due + BLAST_TICKS`, and `addReveals(strikes, tick, out)` gives the sight sources the listed strikes lend every player at a tick. - **The Game** keeps each landed strike with its due tick until its window ends, drops it by `revealing` and bumps `nukeRevision` when it leaves. `nukes()` lists the landed strikes beside the strikes in flight, and `reveals()` gives the rule's sight sources at the Game's tick. The landed strikes are saved, so MAP_VERSION goes to 20. - **The wire's Nukes** carries `landed`: each landed strike's owner, tile and ticks since it landed (proto `LandedMsg`, src/shared/wire_pb.ts regenerated by `npm run proto`, codec and protocol). Every Nukes literal in the tests carries `landed`. - **ServerHost.reveal** stamps the rule's discs into every session's sight on every call. The host reads them once per tick, and `drop()` clears them beside `sights`. What `knows`, shots, hits, deaths, yields and the explored margins send then follows as for any sight. - **Combat:** `engage`'s team vision gives every player the discs, so units may target what a disc shows. - **src/client/render/nuke.ts** reads `BLAST_SECONDS` for the effect's disposal and the cloud's fade, and `OUTER_RING_REACH` for the outer ring. The values are main's, so nothing draws differently. ## Tests - **Host** (src/server/host.test.ts): a HeavyTank 11.4 tiles from the impact is in the launcher's and a third player's replicas from the first frame each gets after the launch (the Launch echo, then the next tick) through game tick `due + BLAST_TICKS − 1`, and in a Snapshot synced 10 ticks after the landing. Both sessions get UnitHidden for it on `due + BLAST_TICKS`. A tank 14.6 tiles out is in neither replica at any tick. Each session's Nukes go in flight, landed with 0 ticks since, then empty. - **Combat** (src/server/nuke.test.ts): a third player's Artillery, 14.3 tiles from an enemy HeavyTank in the disc (past its own sight and the tank's), targets the tank on every tick from the launch through `due + BLAST_TICKS − 1` and fires on it. The tank is alive at `due + BLAST_TICKS`. From then on, for 300 ticks, the Artillery has no target and fires no shot. Without the launch it never targets or fires. - **Rule** (src/shared/nuke.test.ts): the disc at the launch tick and at `due + BLAST_TICKS − 1`, none at `due + BLAST_TICKS`. - **Save** (src/server/save.test.ts): a game saved 100 ticks after a landing restores the landed strike, and the revived and the original games end its reveal and drop it from `nukes()` on the same tick. - **Codec** (src/shared/codec.test.ts): a Nukes with a landed strike round-trips. Mutation checks, each in a scratch copy of the tree: | mutant | fails | |---|---| | `revealing` uses `<=` | rule, host, combat and save tests | | team vision drops the discs | combat test | | `ServerHost.reveal` drops the discs | host test | | the Game keeps no landed strike | host, combat and save tests | The unmutated copy passes all 77 tests of those files. ## Play checks Base and head are esbuild bundles of src/ai/sim.ts and src/bench/net.ts, built from `git archive` trees of main d4a6158 and of the head. `Math.random` is seeded per match. All 42 runs went in parallel on m5 in 5 min 20 s. **The 14 seeded AI matches** (small seeds 1–6 with 2 seats, medium 1–4 with 3, large 1–4 with 4) all ran to their end. 8 traces are identical to main's. These are the 8 matches in which no seat launches. The other 6 differ, and each first differs after its first launch. Each still ends for the same reason, with the same seats standing. | match | first launch on main (s, seat, tile) | first differing record (s) | end | |---|---|---|---| | medium-1 | 851.2, 1, 28,84 | 867.2 | time limit, 3 standing | | medium-2 | 691.2, 1, 63,82 | 846.4, after the second launch at 841.6 | time limit, 3 standing | | medium-3 | 721.6, 1, 129,77 | 723.2 | time limit, 2 standing | | large-1 | 672.0, 1, 191,96 | 673.6 | time limit, 3 standing | | large-2 | 796.8, 3, 214,117 | 798.4 | time limit, 2 standing | | large-3 | 721.6, 4, 154,223 | 723.2 | time limit, 3 standing | **`npm run bench`** (seed 11, wan): battle-128 (the default case), and economy, cities and churn at 128 and 256. None of them launches a nuke. Every case is identical to main in `hash`, `gameHash` and `commandHash`, and in every client's raw, deflated and city bytes a second. **The Nuke yard's match** is `strikeSetup(11)` with the yard's silo armed, its target tile 36,22 and its squad of eight round the impact. It leaves out the camera-placed edge Factories and the trees. Both players have echo sessions. From the launch through the window's end (750 ticks), the frames are: | session | main | head | |---|---|---| | Blue, the launcher | 7 frames, 4,976 B raw, 2,800 B deflated | 16 frames, 16,674 B raw, 7,600 B deflated, 13 enemy units sent | | Red, the struck side | 15 frames, 7,582 B raw, 3,898 B deflated | 16 frames, 7,620 B raw, 3,934 B deflated | So the window adds 11,698 B raw (4,800 B deflated) to the launcher's frames over its 25 s, and 38 B raw to the struck side's, which are the landed strike in Nukes and the Nukes at the window's end. ## Gates Both on 811810b with a clean tree, on m5: - `fast` (run 498's #1, gate run 272): passed, `npm test` 975/975 in 28.6 s. - `full` (run 498's #2, gate run 274): passed, `npm test` 975/975 in 30.7 s, and `test:phone` 180/180 in 205 s.
agra merged commit 425535f46b into task/3560-a-launched-nuke-reveals-where-it-lands-t 2026-10-24 08:41:29 +03:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
agra/mini-alert!139
No description provided.