How it works

Same inputs, same lap.

NetRacer is built on one promise: the same inputs on the same track give the same lap on every machine, to the bit. Everything else follows from keeping it.

01Determinism

No floats. Anywhere.

The simulation is a pure function of the car's parameters, its state, the driver's input and the track. It runs on the driver's machine, again on the server to verify the run, and again for every replay and every ghost, and all of them must agree to the bit. Floating point cannot promise that across compilers and processors, so the simulation does not use it.

Every quantity is a 64-bit integer with 32 fractional bits, multiplied through 128-bit intermediates. Sines, cosines, square roots and exponentials come from integer routines written for the purpose. Overflow does not wrap: it stops the program and names both operands, and when a product could get that large the unit of the calculation changes instead. Floats exist only at the edges, when a track file is loaded and when a number is handed to the renderer.

Tick rate
200 ticks a second, each split into eight substeps for the contact solver.
Inputs on the wire
The quantised wire form of an input is the input. The client decodes it before simulating, even locally, so the local car and the server see the same numbers.
No hidden state
No clocks, no randomness, no hash-map iteration order. Parallel work is allowed only where the pieces are independent and the results are combined in a fixed order.
Parameters in code
The car's physics parameters are compiled into the game. They are not settings and not per-client.
02The network

Inputs go up. Inputs come down.

Every car is simulated only by its own driver's machine. The client streams its inputs to the server, nine bytes a tick, and the server stores them and relays them to everyone else. Each client re-drives the other cars from those inputs with the same deterministic code, so what you see of another driver is their car exactly, a few ticks behind. Nothing about a car's position ever crosses the wire, and the server runs no physics in its loop at all.

Claiming a time

When your own simulation crosses the finish, the client claims the time. A server thread re-drives that one run from the stored input stream, about half a second for a one minute lap, and compares state hashes along the way. Only a time the re-simulation reproduces is published as verified.

Divergence is a bug or a cheat

A mismatch between client and server is never smoothed over. Both sides log it as an error, the time is not published, and the run is kept for inspection. There is no interpolation and no reconciliation because there is nothing to reconcile: the same inputs give the same lap.

Cars do not collide with each other, which is what makes this possible. Your lap depends on your inputs and the track, never on what somebody else's connection did.

03The car model

One solver for everything.

The chassis and the four wheels are rigid bodies in one solver. Each wheel hangs on a strut with a spring, bump and rebound dampers, a bump stop and a droop stop. Tyres are rings of spheres whose contacts are found again every substep, and grip builds with slip up to a friction ellipse set by the surface and the load the tyre carcass has taken up, so a landing spike does not make a tyre bite like a press.

Drivetrain
A crank with its own inertia, a clutch, a sequential gearbox with an ignition cut on upshifts, a limited-slip differential at the front, centre and rear, and brake and drive torque applied at the hubs so the wheel's own moment lands on the chassis.
Air control
The wheels are flywheels. Braking or accelerating in the air pitches the car through the reaction torque on the chassis. That is the only air control there is, and it comes out of the same equations that drive the car on the ground.
Surfaces
Each surface scales grip along and across the road and adds rolling drag. Ice is a small fraction of asphalt; kerbs are a little more. The contrast between surfaces is exaggerated on purpose.
Walls
The chassis is a hull that meets the walls with the tyre's friction. A rigid rim contact stops the wheel a few centimetres in against a wall, so a wallride is a real contact, not a script.
04Built with

Rust, Fyrox, Blender, Nail.

The game
Rust. A shared crate holds the simulation, the track loader, the wire protocol and replays, with no engine dependency, so the headless server and the client run the very same code.
The client
The open-source Fyrox engine, with the renderer patched for distance fog and the sound engine patched for the car.
The tracks
Blender files. The exporter validates markers, checks the sun, triangulates, writes tangents, bakes lighting and rebuilds every block from its description.
The tests
Unit tests cover the pure simulation. Everything else is tested by running the real server and the real client with scripted drivers, because the test is the game.
This site
Written in Nail, a programming language I made that transpiles to Rust. Every page is a function that returns a string.