Once upon a time, back in 2013, I decided to do netcode. I didn’t plan for greatness and certainly didn’t think that I would make a career out of it - what happened was that me and a friend ran out of games to play so I decided to make a system that would make it easy enough to prototype ideas and generally mess around.
The result was a ~200 line deterministic lockstep system for GameMaker 8.1. I also didn’t know that there was a name for this approach at the time - I just figured that this was what emulators have been doing since you could load both “romhacks” and homebrew games in them.
We proceeded to make a dozen or so variously experimental game prototypes - like this game about racing to the next numbered tile:
Or some first-person tests and lag compensation techniques:
Or this online adaptation of a game by orange08 that I can’t find any trace of now:
The only one of these prototypes that actually survived to release was Color Ninjas:
Color Ninjas is a game about random weapons, power-ups, and getting the most of the play field in your color before having a final showdown with your opponent. It has been most commonly compared to Splatoon, though it predates it. I have a certain suspicion that the game doesn’t work well on on modern systems.
After Color Ninjas I got distracted with making Spelunky SD, an online multiplayer for Spelunky Classic. With it, I vastly underestimated both the amount of work it would take to make the mod, and how popular it would be - this is how a lot of people came to know that I do netcode.
After Spelunky SD, I rewrote the system in Haxe instead of plain GML (to have type-checking and be able to strip unneeded bits on compile) and then the pattern would follow: any time I would work on a project with deterministic netcode, I would bring in this system and add any missing bits to it, often hidden behind a compilation flag. Over years it has grown a number of features this way, such as:
Steamworks support
Support for all 3 console SDKs
GOG/Galaxy SDK support
Discord Game SDK support
(RIP, but also good riddance)A C# version
(here’s a hot tip for trying to do deterministic netcode in Unity games: don’t)A HaxeFlixel version for a project that never even came out
An extremely unnecessary input system with support for expressions like
gp1bt1 || gp1y < 0
A reliability layer for UDP-like protocols
Rollback layer logic
I did not plan in advance for most of these, so a lot of the code looked… not very good.
Say, GOG and Discord SDKs are similar enough in their function to Steamworks, so they are just compilation flags and variables that switch up the used functions in the regular Steamworks logic;
Console code was grouped together (because it started with a console) even though matchmaking SDKs are vastly different on each of the three;
Finally, some portions of the codebase had very long chains of #if
..#elseif
..#end
preprocessors because a game might use several SDKs, sometimes even at once (thank you, Discord “cross-play”).
Anyway, this spring I finally got fed up with all of this nonsense, and rewrote the system to have a concept of a “backend”. A backend defines how lobbies are created, how packets are sent, how and where from we fetch lobbies and set their metadata, and so on. So we have a UDP backend, a Steam backend, a GOG backend, and so on.
On rollback netcode
Surprisingly or unsurprisingly, the actual “logic” part of rollback netcode is pretty compact - just a few hundred lines total, half of which are comments and a rather boring algorithm that tries to prevent individual players from getting ahead of the rest (which causes more rollbacks for them than the rest) by communicating average number of rollbacks per frame between players.
The difficulty is, was, and always will be in making a game work with rollback - to have fast state loading and saving, to optimize game logic to the point where your game can survive doing 7 game logic steps in one frame, and to debug all of this.
That’s why GGPO source code being released didn’t magically lead to every fighting game getting rollback netcode - sure, it is nice to have some point of reference if you’ve never implemented these things yourself, but it’s only a small part of work it takes.
On weirder netcode
Apart of conventional means of lag compensation, I have also experimented with the unconventional.
Suppose you have a game with ranged combat, perhaps something like TowerFall. Instead of predicting remote player inputs and briefly teleporting things around when predictions fail, you could make your local player exist in the future (<delay> frames into the future, to be specific) and then figure out whether they’ve ran into a projectile based on where things were <delay> frames ago (because this is a deterministic simulation and you do know).
Advantages of this system include having consistent, predictable results without the usual refactoring that goes into making rollback work.
Disadvantages of this system include being hard to debug and a bit of a headache in general - me and a friend have experimented with it at various points, but never ended up shipping a project with it as adding even a relatively normal feature (like a moving platform) can cost you an evening or two to test and debug.
On C#
Haxe can generate C# code out of box, but the output is rather… verbose:
For Samurai Gunn 2’s ongoing rewrite, we have established that it would be rather nice to have standards, so I wrote a much smaller, custom C# generator that produces code that looks not too different from what you would write by hand:
Its API coverage is rather minuscule and there are limitations, but you can grab it from GitHub and play around with it.
On tutorials
Time to time people ask me if I can write more tutorials about online multiplayer, or even make a video course about doing netcode for games.
I like my text tutorials - you can quickly skim through them, and I can revise them however I want - there are tutorials on my blog that are now a decade old, and I can come back to them to tweak some wording and or update the code. That is not an easy thing to do with a video.
And yet I’m slow at writing. Anything medium-length like “Preparing your game for deterministic netcode” or most of my newsletters can sit in drafts for weeks because I keep trimming the post or figuring out a better way to explain something. How do people write a post every week, let alone write for a living? I may never understand.
On future
There’s always more to do!
I want to tweak a few other things in the codebase, and make the shared [de-]serialization code (akin to what you can find in GGMR) use variable_get_hash and other cool new GameMaker tricks, and do netcode for another game, and I’ve been also itching to try adding rollback into one of my older mods (perhaps Super Crate Box Together?)…
Thanks for reading!
Glad to hear your journey in netcode! Just a question, why is it harder to make a deterministic netcode in unity vs in gamemaker?