How It's Made · Step 17 of 27
Seventy achievements, a fake Steam and the friend build
achievementsmultiplayersteambuilds

The stretch before you show a game to friends is mostly unglamorous work: things that make a game feel finished, and things that make it safe to hand over. On 21 and 22 October we did three of them.
Achievements that cannot change the game
An achievement system has an awkward requirement in a lockstep game. It needs to know what happened in the simulation: how many infantry you crushed, how many missiles you shot down, which ship sank. But the simulation must never depend on it, or two PCs with different achievement states could drift apart.
The rule we chose: the sim writes counters and never reads them. Event counters live on the player object (infantry crushed, buildings sold, ships sunk per ship, which support power fired and so on). They are written at the places where the event already happens, are not part of the state hash and are not saved. A watcher on the interface side reads them once a second, for this PC's seat only. If the achievement code were deleted, the game would play identically.
The first build, on 21 October, shipped 42 of the 68 achievements the writer had listed; the others were waiting for events the sim did not record. On the 22nd we added the missing counters, and 67 of the 70 now ship, with three left hidden. Mission-specific ones read the mission director's flags instead: never let a boat through, never hit the school, never lose a rocket.
We checked the claim, not only the code: the lockstep run produced the same fingerprints as before the change.
Seventy badges, drawn by a script
Every achievement needs an icon, locked and unlocked. A script draws them: seventy brick-shaped badges, two states each, 140 images, plus a contact sheet so a person can check them all at a glance. No image generator was involved. A missing icon file shows a placeholder instead of an empty square, and there is a test for it.

Testing Steam without Steam
Online play through Steam means lobbies: you create one, friends see it or are invited, they join, the match connects. Testing that on one PC with a single account is a problem.
We built the layer so that the session talks only to a small interface, a pipe that sends and receives messages. Two real pipes plug into it: one over plain UDP (a LAN or a direct address) and one over Steam's peer-to-peer sockets. A third, FakeSteam, imitates the lobby service with files on disk and loopback UDP, and is compiled out of the shipped game.
With it, two headless game instances ran the whole path: create a lobby, list it, check the version, join, connect, play. 601 state hashes and 81 commands matched on the UDP road and on the fake Steam road, and an invite-only lobby never appeared in the public list. The menus gained a host screen (friends, invite only or public), a list of games to join, a direct-IP road behind a button, chat with a mute per player, and a filter on chat text.
The real thing still has to be tested with real friends on two PCs, and our notes say so in capital letters.
The friend build, and the audits that said no
A friend build is a zip of about 8.8 GB with 1,140 files that someone can unpack and play. Before it is zipped, a script, release_audit, checks the package: the right content, nothing that should not ship, the first-launch legal screen, no leftover old names.
- Build 9 (21 October): the audit refused the first attempt, finding 28 hits of an old unit name still sitting in hint and subtitle strings for Tower Defense levels. We fixed the strings, not the ids, and the second run passed.
- Build 10 (22 October): the first attempt was refused by a new check (a build wired to a development Steam setup: now a warning in a friend build and a failure in a release build). The second attempt died because the machine ran out of memory while cooking: the editor climbed from 4.8 to 16.6 GB in ten seconds and the memory watch killed it. The third, on an idle machine, took 23 minutes and passed.
A smoke test of the packaged exe won three Tower Defense levels with the balanced bot. The log's last line is the one we like best: not verified: a played session; real two-PC Steam multiplayer from the packaged exe. Nothing uploaded.
What we would do differently
- Put the audit in before the first friend build. It caught real mistakes the first time it was used.
- Never run a package on a busy machine. Both of the failed attempts were about contention, not code.
- Decide what stays hidden before you write the list. Three achievements still have no event to hook to.
Try it yourself
When a feature has to look at a deterministic system, make the dependency one-way: it can read, the system must not know it exists. Then prove it with the hash you already have.
Next: Juice - bricks that scatter, buildings that assemble and a pixel counter that caught us (22 October 2026)
Written afterwards from our project notes and commit history; dated to when the work happened.


