Networked multiplayer now works in the playable web demo!
Crazy, right? I can barely believe it myself.
The Why
I'm building this game for desktop (Windows, Linux and hopefully MacOS1), and the web demo is only for getting early feedback.
But, I wanted to see if I could get multiplayer working in the web demo for three reasons:
- It's frickin cool.
- It'll help flush out bugs in the multiplayer implementation.
- It lowers the barrier for you to play the demo with a human: there's no need to cajole someone living with you to pick up a controller.
And hopefully that last point means you give me more feedback on multiplayer gameplay, and eventually our efforts turn this tech demo into an actually fun game, yay!
The Problem
The hard part of making a multiplayer game run in a browser is sending & receiving data between player's browsers.
Obviously browsers can send to & receive from web servers: that's how you can do your last minute Christmas shopping this weekend without being judged for your choice in pyjamas.
But sending & receiving web requests directly between browsers isn't possible, so all requests & responses have to travel via a web server.
That's really slow. 2
And really slow might be fine for your grandpa's Farmville, but it ain't gonna cut it for an action game.3
The How
Our savior wears an unusual mask: WebRTC, a browser technology that was originally designed to once and for all solve the problem of letting you skype your parents without having to call them and - for the 3rd time - walk them through how to actually install Skype on their computer.
By adding the aforementioned built-in video conferencing tech to browsers, tech companies accidentally gave us a way forward: 4
- Video conferencing wants from direct connections between call participants. 5
- We also need direct connections between all players.
- What if we pretend that our game's messages between browsers are actually messages containing video content? 6
It's so crazy, it just might work!
I can't claim much credit for that insanity insight though: I used a library called Matchbox7 which does all the hard work of wrangling WebRTC for me.8
Here it is in action:
In fact, it works browser<->browser as well as browser<->desktop, which is a good sign for cross-platform multiplayer! 9
But it ain't perfect.
The Caveats
NAT Problems
The biggest caveat is a thing called NAT. The short version is this:
- For reasons,10 you and/or your ISP might have a router ("NAT Gateway") that prevents incoming connections from reaching your computer.
- If so, the game has to do NAT Traversal, which is basically "try various hacks to let other players connect to you despite the uncooperative router".
When NAT Traversal doesn't work, then right now the game will just hang on the connecting stage even if there are players to connect to.
I'm still working on both improving the NAT Traversal11 and providing better feedback on the progress of connecting to other players, but for now it is what it is.
No Hot Join
Q: What do legacies, impressions and this game's networked multiplayer sessions all have in common?
A: You can leave them, but you can't join them.
Specifically, you can't join an in-progress game where players are already playing the game. That is very much something I want to fix, but it's a tricky problem to solve.
Very Basic Matchmaking
The process of grouping players so they can play a game together is called "matchmaking", and the matchmaking logic is super simplistic right now:
- When you start an online game, you'll be automatically connected to the next person who also starts an online game.
- If that next person doesn't ever come, you'll be stuck on the waiting screen.
Also, you can start a game with more than just 2 networked players,12 but since there's no hot joining, you have to wait for them all to join.
Lastly, you can't see how many players are "ready to start a match" right now, so it's hard to tell if you're about to be connected, suffering from connection issues, or if you're waiting in vain. Be optimistic :)
Networked Multiplayer Web Demo
Okay enough chit chat, here's where you can (potentially) play a multiplayer game with a total stranger.
- If you have trouble connecting, try a different browser: Chrome and Firefox work for me; I haven't tried Safari.
- If you find a bug, get a crash (sudden black screen) or can't get connected despite trying a different browser, please report it in Discord (or via email).
- If there are no strangers online and you have no friends to cajole, you can always open two separate browser windows.
It can take up to ten seconds to connect even if things are going well, so be patient:
Lastly, if you're staring at the white "waiting for players" screen for a long time (30+ seconds) and you know there's someone else there trying to play (because it's your other browser window, a friend, etc), try refreshing and starting over.
Refreshing over and over won't help - it'll just mean other people will be left hanging on the waiting for players screen. So please don't do that!
If Apple isn't too rude.
Actually, the slowness in such a setup comes from several things:
- the added latency of having to travel via the web server.
- the framing overhead of those requests and responses (e.g. HTTP headers, cookies, etc).
- the requests and responses are always transferred using an reliable-and-ordered protocol (historically, TCP, nowadays TCP over TLS, or perhaps QUIC) to transfer the HTTP requests and responses; using a reliable-and-ordered transport causes latency problems for fast paced games.
- and a few more things like TLS handshakes that I can't be bothered writing up.
Well, actually, maybe it would have worked okay-ish since we worked out last week that rollback networking can handle quite a bit of latency before things get weird. Still, it wouldn't be ideal.
Tech companies have since started working on WebTransport, which is basically HTTP/3's QUIC as an API as far as I can tell.
But it's unavailable in Safari (understandable, Apple is cash poor), and doesn't seem to enable peer-to-peer connections between browsers.
Still cool technology though.
And video conferencing also really benefits from unreliable-and-unordered messaging, so that is actually provided by WebRTC too - which is great because that is what we want for game network traffic too.
Well, actually we send a Session Description that asks browsers to establish a raw UDP + DTLS (encryption) + SCTP (stream multiplexing) "data channel", rather than e.g. a visual channel that would actually require packing our network data into a video codec.
Matchbox was developed by a guy called Johan Helsing for his game Cargo Space, and his game dev-blogs are worth a read too (as long as you aren't scared off by snippets of Rust code).
Cross-platform multiplayer (such as players with AMD/Intel CPUs playing with Apple Silicon players) requires "cross-platform determinism". The web build is compiled to run on Web Assembly, which is basically like compiling for different processor.
So if I can run the game on Linux and in the browser and they don't crash from getting out of sync, then that's a good sign that the game simulation is currently deterministic, and so it might work on MacOS too. (I don't own a Mac at the moment, so I can't test it myself yet!)
Or at least, it did after I fixed or worked around a couple issues in Matchbox. And after I rewrote 90% of the game's networking logic to remove various short sighted assumptions I had made.
There are only so many internet-routable IP (at least, IPv4) addresses in the world, so they cost money; with NAT you can share one internet-routable IP address across many computers.
This has since been post-hoc justified as a security benefit for consumers, much to the delight of the 99% of Internet-of-Things manufacturers who gleefully ship insecure devices to millions of customers.
Right now I am using Google's STUN servers as part of the WebRTC ICE protocol, but I still need to set up a TURN (basically a relay) server as a fallback for when STUN doesn't work.
Unfortunately it seems like browsers don't use UPnP to get routers to open ports (though to be fair, UPnP is commonly disabled in routers these days), and it seems you can't manually forward ports in your router's configuration for WebRTC.
In the advanced options, you need to change the "client count" slider from 2 to whatever number you want.