Far Signaldevlog

Making it fast, and keeping it fast

6 October 2026

Far Signal simulates basically everything, every ship is made of real parts like generators, breakers, pumps and cables, each with its own heat and wear and power draw, and a server runs all of it for every machine in its region every second while sending each player whatever they can see, so this post is about how that stays cheap.

Players one server can carry
Players one server can carry

Ships that sleep

Most ships, most of the time, have nobody anywhere near them, so they sleep, which means they aren't simulated at all and time passing costs nothing, and when someone comes close a sleeping ship wakes up and catches up to the present.

The important bit is that catching up gives exactly the same answer as if it had been simulated the whole time, with the same failures at the same moments and the same events in its history. To make that work I changed how parts fail, so instead of rolling dice every second to see if it breaks, a part works out when it'll wear out once, when it's made, and a sleeping ship can skip straight to just before its next failure. Even a loose part drifting in space cools down with nothing running, its temperature just gets worked out from when it was last touched, and a whole year passing over 10,000 sleeping ships takes about half a millisecond.

Sending less

The bigger cost is actually the network, telling every player what they can see. Things far away get sent roughly and not very often, so a ship 1,000 km off is a bit vague and sharpens up as you get closer. A ship that's coasting sends its orbit once and then your game works out where it is with the same orbital maths the server uses, so it doesn't need another update for about ten minutes unless it fires its engines. And stuff that hasn't changed doesn't get sent again, with parts, cables and panels going out in chunks and anything shared between players only being made once.

In a crowded station with 1,000 players docked, all that took the traffic from 72 KB per player per tick down to about 10 bytes.

In parallel

Each player's view gets worked out on its own, so now those views are worked out at the same time across all the server's cores, which on my test world took 2,200 players from 375 ms a tick down to 195 ms. On a one-second tick one server can now carry about 4,800 players before the CPU gives out, and the network traffic for that many would still fit comfortably down the pipe of the box it'll run on.

Before and after
Before and after

Today's profiling

After a day of piling in new features (combat, crew supplies, salvage, docking) I ran the benchmarks again and a couple of things turned up.

Moving time forward in a world where everything's asleep had got 23 times slower. The server keeps a list of busy machines, the ones warping, docked, flying a route or firing a weapon, and when combat went in, rebuilding that list started checking every part of every ship for a weapon mid-shot, even ships that were asleep, which is a bit silly when weapons only fire on ships that are awake. Skipping the sleeping ones, and refreshing the list when a ship wakes up or nods off, took a year over 10,000 sleeping ships from 3 ms back down to half a millisecond.

Awake ships had also got about 1.4 times slower, and I had a suspect, since each part's list of traits now gets searched several times a second (is it overclocked, is it a weapon, is it a warp core), so I rewrote that to search once, measured the old version against the new one and there was no difference at all, I'd just guessed wrong haha. So I built the old code side by side and bisected through a day's worth of commits, and it turned out there wasn't one culprit, every feature had just added a little bit, coming to about 0.4 ms per thousand awake ships or roughly 2% of a busy server's tick. I put the old code back, wrote it down and moved on, and it's a good reminder to actually measure before fixing anything, because fixing the suspect would've added complexity for nothing.

Faster tests

The test suite checks all of this stays exact, that sleeping and waking gives the same history, that what the network shows matches the server, that saved games load the same, and it used to take 10 minutes after every change. Building the tests with a bit of optimisation brought that down to about 2 minutes, so now it gets run before every single commit.

← all posts