BotStrikes

Early development No public build yet · 5v5 tactical FPS · PC

Development log

What changed, measured

The public development log. No launch dates and no “soon”: if something did not move, this page says what stopped it.

Development log · updated on

Milestone by milestone, with its measurement

One entry per verified milestone: what changed, the number with its scenario, the link to the evidence, and what was tried and dropped, with the measurement that dropped it. What is not measured yet says “pending”, and what is waiting on a person says so too. No phase is declared closed here.

· F1–F6 · ADR 0017 The hash matched on Clang because we said so, not because it was checked

The project's pinned-hashes file and the evidence page claim that one million simulation steps produce the same hash on three builds: optimized GCC, GCC with sanitizers and Clang. The full verification compiled two. It now compiles the three and compares the three hashes on every run, and if it does not find Clang it fails instead of skipping the leg. All three give a59ca4f6e2299d88 for the movement corpus and f7d2406fd5b56d4d for the combat one, with 72,863 shots and 3,478 hits. In the same move, the record of the full verification of Sep 24, 2026 was published, which is where the tick, the bandwidth and the render scene the site publishes come from.

  • Clang 21.1.8, optimized GCC and GCC with sanitizers give the same two pinned hashes, over one million steps each
  • If there is no Clang, the full verification fails: before, the leg was skipped silently and the claim went unchecked
  • The record of that full verification is now published among the raw records, with the seven site figures it backs and with what it does not back
  • The whole tree still does not compile with Clang: two duplicated, unused constants in the OpenGL code break the build with warnings as errors. The two determinism probes do not pull in rendering, so the new leg runs anyway; it is written down instead of hidden

The three legs, one million steps each Movement a59ca4f6e2299d88 and combat f7d2406fd5b56d4d on optimized GCC, GCC with sanitizers and Clang 21.1.8, with 72,863 shots and 3,478 hits on all three. The two hashes are the ones the project's file has pinned and they did not move.

Tried and dropped

  • Degrading the Clang warning with a compiler wrapper so the tree would buildThe wrapper existed only so that an unused constant would stop breaking the build. Measured before deleting it: that constant entered no computation, because the rig's 18 poses come out identical with its value from the tree, with 0 and with 90 degrees, with the same fingerprint in all three cases, and the code section of the compiled object is the same before and after deleting it. It was dead code: it was deleted and no wrapper is needed any more.
  • Treating the populated scene of the dressed map as covered by that recordThe plan said the record covered the first four rows of the budget panel, and it covers three and a half: its render gate is the empty shooting range, 4.1434 ms, and the populated scene, 9.6242 ms against a 8.3333 target, comes from an ADR of the following day. The record says so in its limitations, and the list of figures without a published record still has entries.
  • Publishing the raw verification log next to the recordThe records published so far carry the file and the sha256 of their log, not the log, and that criterion stands: the log is asked for by email and that sha256 is what checks that whatever arrives is the same the figures were measured with. Changing it for one record and not for the other thirteen would make the folder less readable, not more.

· F5 · ADR 0101, 0102 The wall remembers the shot, and the body stands like a soldier

The bullet mark used to be a flat coloured cube of 1.8 cm that expired after 10 s. It is now a textured quad with alpha on the plane of the face that was hit, lit with that same wall's baked light, three material families and four shapes each: 192 alive, for the whole round, cleared when the round changes. In parallel, the gait was fixed where it matters, in the data of both rigs, and not by lowering values in the client.

  • The quad is 6.0 cm and the crater 1.8: the 1.8 was the size of the hole and was being used as the size of the whole mark
  • Acceptance criterion met: thirty shots at 40 cm leave fifteen separate holes, without the smear that had been rejected
  • Ankle to ankle standing 35.5 → 29.0 cm and toe to toe 49.1 → 34.3, baked into both rigs
  • A person still has to play it and accept it: the founder validates the stance and the marks (checkpoint H3)
  • The standing pelvis weight shift stays blocked by a human decision: 0 mm of travel
Frame cost in the worst case, reference card+0.09 ms / 8.3333

Divergence between the drawn body and its hitboxes standing 6.17 → 2.91 cm and toe 9.49 → 2.09: fairness improves with the bake, it does not get worse

Tried and dropped

  • Putting the marks' sampler into the world shaderThe frame went from 1.97 to 4.72 ms without drawing a single mark: that program is tight on registers on the reference card. The decals have their own program, and any lever that adds a sampler to the shared program has to be measured even when it draws nothing.
  • Fixing the stance by lowering three values in the clientIt left the drawn ankle about 5.4 cm inside the one used by the hitboxes, that is, the foot outside its box. Baked into the data, the standing divergence drops from 6.17 to 2.91 cm.

· F6 · ADR 0099, 0100 The wire contract is written down, and the worst case was measuring low

The server was speaking wire protocol 11 and agent API 10, but the wire documentation only reached 10 and declared 8 for the API. It is now written from the code and not from the log: the bomb block with its 18 bytes and its rules about who knows what, the use action, and the full canonical order of the snapshot with the size of every block and who receives each one. Every size was checked by actually encoding: an empty snapshot is 136 bytes.

  • The real worst case downstream for 5v5, a live AI under the motor model, is 795 bytes and 55,552 B/s with headers, not 54,400: that leaves 8,448 B/s of margin against the 64,000 cap
  • There is no budget regression: it was a count that measured low, and the test now pins both families of figures, with an objective and without one
  • What did run out of margin, and gets written down instead of hidden: the format allows up to sixteen seats, and that case would give 65,152 B/s with the bomb block, above the cap. Offering a sixteen-seat match with an objective needs a written decision and a person first
Worst case downstream per client, 5v5 with an objective55,552 B/s / 64,000

Tried and dropped

  • Raising the bandwidth cap so the bomb would fitNothing was raised or lowered: the cap is still 64,000 B/s. What was wrong was the count, which did not add the bomb's 18 bytes even though they travel in every snapshot of a match with an objective.
  • Leaving the version comment and the pending list as they wereThe header comment said 10 with the constant at 11, and the list declared clock adaptation between machines as pending, when it has been implemented and tested since v2. Both divergences were fixed in the same change.

· F3b · ADR 0098 The presentation clock draws where you aim

The client was drawing 0.80 ticks behind the newest snapshot instead of the 2 its own design asked for, and 4.7 % of frames left the remote body still. The cause: the error that governs the clock was measured after moving its target forward, so the controller read an error the size of a whole frame and the clock ran ahead. Which is why the lag was smaller the worse the machine was. Now the error is measured first and the target is dragged instead of replanted.

  • Lag 0.8006 → 1.2674 ticks and the difference between drawing and aiming 0.458 → 0.001
  • Frames below one tick 65.65 % → 20.91 %, frozen 0.93 % → 0.65 %, maximum step over the average 1.102 → 1.064
  • The interpolation buffer was not touched: it costs 7.3 ms more of seeing the others with lag, which is exactly what the design asked for
  • It still has to be played, and the percentage of frozen frames measured again with the client connected

Reproduction deterministic bench with no network: 0.800649 of lag and 65.65 % of frames below one tick, the same as the connected match

Tests 115 cases and 88,357 assertions in the network tests; quick verification 12 of 12

Tried and dropped

  • Lowering the clock's clamp ceilingFrozen frames got worse, up to 1.49 %.
  • Giving the clock controller more authorityFrozen frames rose to 3.35 %.

· F6 · ADR 0089, 0094 The genre's radar, without giving away a single enemy

The server now sends where living teammates are even when they cannot be seen, which is the genre's radar. Seat and position travel, 13 bytes, and nothing else. The rule it was decided with: if a field changes because of something happening outside it, it does not go in. That leaves out health, weapon, trigger and aim, because they change when an enemy is near: where a standing teammate looks is where the enemy he sees is. And a dead player no longer stares at the floor: they spectate through a living teammate's eyes, with that teammate's perception and without their private events.

  • It costs nothing in the worst case on the wire: a teammate is either among the visible ones or in the new block, never in both
  • Spectating a teammate when you die travels in 17 bytes and removes a measured blackout of up to 2:17 staring at the floor
  • AIs receive the same thing, on the same tick and with the same bytes, and with no perception delay: knowing where your own team is does not aim at anyone
  • That no enemy leaks is checked by the roster, the encoder and the decoder, separately
  • Pending a decision: drawing the teammate's outline through the wall costs +0.16 ms in the worst case, five covered teammates, against a margin of 0.05 ms. The cheap alternative, limiting it to the two nearest, is proposed and not applied
Covered teammate's outline, worst case, against the margin available+0.16 ms / 0.05

Tried and dropped

  • A teammate counter always present in the snapshotIt would have cost 1 byte and taken the sixteen-seat case from exactly 64,000 B/s to 64,064. Instead of lowering the budget the design was changed: the block only appears when there is something to send.

· F5 · ADR 0092 Material variety in the map at zero cost, and the sky stops banding

Three skins per district chosen with a fixed hash, families that never cross, and the physical material preserved: the bullet finds the same thing as before. Plus two image fixes that came out of a review by axes: dithering before quantising to 8 bits, and full sampling for every model skin, which was uploaded with half the configuration while the world array already carried all three.

  • Pixels with no local texture: median 39.8 % → 32.6 %, for +180 triangles out of 15,920, zero textures, zero layers and zero new draw calls
  • Props at zero degrees 16 → 4, and distinct angles 24 → 45
  • Dithering before quantising to 8 bits, cost 0.00 ms: the sky was banding one stripe every 6.7–10 px
  • Mipmaps, ×8 anisotropy and −0.5 bias when uploading each skin: the body keeps 99 % of the legible detail and loses 6 % of the noise near Nyquist, and the weapon keeps 96 %
  • A person still has to look at it in a match: the founder validates visual identity and legibility (checkpoint H3)
Cost of dithering in the frame+0.00 ms / 8.3333

Tried and dropped

  • Putting the dithering before edge antialiasingIn that order the worst channel gave 42, because the antialiasing decides edges by a luminance threshold and half a level changes its decision. In its place, afterwards, it gives 1.
  • Leaving half-configured sampling on the model skinsWithout anisotropy the weapon kept between 90 and 92 % of the legible detail, against 96 % now.

· F3b · ADR 0087, 0088 The tracer stopped lying, and the command is applied with a single piece of code

The beam the client drew at the moment of the click was not the shot the server resolved: the client does not know which bullet left until the server tells it. Now the beam is drawn from the server's answer for that same bullet. And client and server now prepare and apply the command with the same code, instead of with two similar orders of operations.

  • The honest beam does not cost a single new byte on the wire: the shot's answer was already travelling
  • Client and server prepare and apply the command with the same code, and the wire format does not change by a single byte
  • Encoding a snapshot can no longer take the server down: the function returns an optional, and the loop counts the rejection, names it and skips that snapshot
  • Until the beam appears a round trip goes by, 30–60 ms. What is immediate —muzzle flash, recoil and sound— stays immediate, but that is validated by a person playing

Bench three views of the same instant, standing and running, plus a connected match of 900 frames and six new contracts

Tried and dropped

  • Drawing the beam on the client at the moment of the clickIt missed by 10 cm standing and 8.94 m running compared with the bullet the server resolved. A beam that does not match the shot teaches you to aim wrong.
  • Keeping the old order of command preparation for compatibilityThe new test measured 283 disagreements out of 300 between what the client drew and what the server applied. With shared code: 0.

· F5 · ADR 0083, 0090 “Cinta”: three cover heights and one service entrance per site

The competitive map is two arenas joined by three routes, compact. Five low cover heights used to coexist; now there are three and no others, 1.00 / 1.60 / 2.20 m, with the band from 1.20 to 1.50 m forbidden because it cuts the hitbox exactly where the eye cannot judge it. And the defence has two entrances per site: a service entrance was added with its lintel at 2.40 m, four metres south of the one it already had.

  • 1,328 brushes, 0 faces in conflict and a playable footprint of 84 × 62 m
  • 18 % raised floor, with the bench band at 15–25 %; 14 % covered; and 0 % open floor far from any wall, which used to be 1 %
  • Eight new 1.60 m pillars, and low cover unified at exactly 1.00 m
  • Times measured with the SDK grid: defence to site 6.5 s, attack 12.3 s, rotation between sites 12.8 s and spawn to spawn 12 s
  • The founder still has to walk it and play it
Longest sightline, against the contract maximum36.2 m / 40.0

Tried and dropped

  • The 40 to 45 m rifle line the reference bench asks forIt does not fit in 84 × 62 m: the longest sightline the geometry gives is 36.2 m, and running the generator again did not move the maximum by a single metre. Getting 45 m would mean opening the map up, so it is stated instead of estimated.
  • Opening a new entrance straight through the mapAn automatic attempt left a 79.7 m sightline, twice the contract maximum, and fighting faces. Verification caught it on the first try, with three map tests red, and it was reverted: a new entrance is opened with a bend or a door.

Earlier entries

DateWhat changedNumberPhase
Camera culling closes part of the gap, and the budget still does not fitaverage 9.6097 → 9.3372 ms against an 8.3333 target: still 12.0 % overF5
Competitive combat band, and stairs that do not jumprifle armour ratio 0.5 → 0.75; eye step per tick 15 → 5 cmF5
Round-based matches with a scoreboard, and the test map stops being an arenafirst to 13, halves of 12, 1:55 round; open floor 35 % → 1 %F6