Contre-Frappe
a Counter-Strike-style shooter, written from scratch in Rust
Client, authoritative dedicated server, bots, GOTV, map editor and launcher, one engine codebase, still in progress.
- Everything, engine, netcode, bots, tooling, ops
- Rust · Bevy · lightyear · Tauri · Fastify · Kubernetes
- In progress, playable solo against bots
The problem
I wanted to understand what a competitive shooter is actually made of, not by reading about it but by building one: movement that feels like Counter-Strike, a server that has the last word, opponents worth playing against, and the machinery around the game (updates, accounts, match results, spectating) that a real game cannot ship without.
It started in late 2024 as a small Bevy experiment, was restarted twice, and became this: a Cargo workspace of 29 crates, roughly a hundred thousand lines of Rust, close to a thousand tests, and a rule that every pull request comes with an entry in the project journal.
What I built
A layered workspace where each crate only depends on the layers below it: shared types and map formats at the bottom; physics, weapons, rounds and skeletons above; player, combat, economy and network core; then bot AI, effects, spatial sound, spectator, demos and voice; and at the top the binaries: the client, a headless dedicated server of about two hundred lines, an editor, and a small MCP server that lets an assistant edit maps.
The network is a bridge between Bevy events and messages, so the HUD, sound and effects never know whether they are online. The client predicts its own player and interpolates the others at 64 ticks per second; lag compensation keeps ~300 ms of position history per player and rewinds hit tests to what the shooter actually saw. Around the game: a Tauri launcher with Steam sign-in and channel-based updates, a Fastify backend that registers servers, records rounds and spawns match servers on demand, and a GOTV relay that replays ticks with a 90-second delay to spectators and a web viewer.
The bots got the most engineering discipline: a measurement harness first (bot-only soak runs, metrics, a written summary), then four phases each published with before/after numbers: blockages per round from 28 to 5, accuracy from 3 to 20 %, defuses from none to 44 %, with difficulty profiles that actually order the results.
What I learned
That "one game logic, three network modes" is worth every constraint it imposes: solo play adds no networking plugin at all, and the predicted systems on the client call exactly the physics functions the server runs. That an engine's small traps (an HDR camera never cleared, an audio listener on the wrong camera, hitboxes at Mixamo's 0.01 scale) cost more evenings than the netcode did. And that measuring before changing (a harness before a refactor, numbers before a claim) is the only reason the bots got better instead of different.
“The same physics function runs on the client and on the server; that is what makes prediction converge.”Homelab GitOps