Basic multiplayer in my test app seems to be working well. It’s nearing the time to integrate everything into the main app. Still a long way to go.
The whole networking system in Scraps is a bit unusual because Unity’s physics (which I’m using extensively) can only run in real time, but I also want to retain cheat protection by having an authoritative server. I’ve never heard of anyone doing things quite the way I’m doing it, and I’ve been waiting for the moment when I realise why no-one’s done it, but it seems to be surprisingly decent so far. One day in the future, I’ll do a writeup on the hidden magic.
The thing is that really good networking has an authoritative server (so you can’t cheat) and client-side prediction (so you don’t get a delay on your commands). And lag compensation, but that’s out of scope here.
So the server controls everything that happens in the game. When you press a key to move forward, your computer tells the server that you pressed the forward button, and the server moves you forward. Then it sends back the information about where you moved. The server has the authority on everything you do, so even if you hack your game to move at double speed, the server will just snap you back when it tells you where you really are. I can do that with Unity physics, no problem. But you need to do more than that, because there’s always some latency.
If it takes 200ms to get to the server and another 200ms back, it’s going to be almost half a second after you press forward that you actually move! There are old games that work that way, but mostly these days we try to predict. When you press forward, your computer moves you forward, then when the server sends back the authoritative information about where you moved to, you can adjust if necessary. If the movement code’s exactly the same, you shouldn’t have to adjust. Bam, authoritative server, and instant response. I can still do that with Unity physics. But there’s one more thing to it.
Let’s say you’re moving along and the server sends you a message saying “you are here”. That message isn’t for where you are now, it’s for where you were 400ms ago (or whatever the latency round-trip is)! In the time between then and now, you could have done anything. You might have pressed jump. You don’t want the server to slam you back onto the ground because it hasn’t realised that you pressed jump yet.
So ideally we record all the commands that the user made since the last update from the server. Then when the server sends a new update, we can start from that moment, and replay the rest of the recorded user commands to get back to where we are now, but with any corrections from the server. Again, this is all to prevent cheating – otherwise we could just assume the client was right and tell the server where they are.
We actually need to constantly replay all the commands we’ve recorded since the last server message, every frame, until we get the next one. Now, uh oh, Unity’s physics aren’t fully deterministic, and you can’t step forward or rewind the physics anyway. Everything just runs in real time, all the time. Don’t we have to rewind time and then step forward again to do all this?
Oh yeah. And that’s the problem. You can kind of approximate Unity’s physics, and there’s a really good blog post on it here by Joe Best, but in the end he seems to come to the same conclusion as me, which is that it’d be a huge mess to try and do for vehicles (or for anything else really). A large part of that is Unity’s lack of ways to check if something will collide without, again, actually letting time pass. See the first entry in my earlier post here.
So I wrote down what I thought Scraps multiplayer must have:
- No client control lag
- Supports latency up to at least ~500ms
- Cheating prevention
- All physics can run in real time
I’m wasn’t keen for a huge job like trying to replace the entire physics system with Bullet physics or something like that. But I managed to work out a networking system different to anything I’ve seen before. I call it Vibrance. It’s semi-authoritative. So far it has a few small drawbacks and isn’t as “clean” as the above – it’s a little bit messy – but seems to work well overall. Sorry but I’m not ready to reveal the details of the implementation. That might be a mistake as someone would probably tell me immediately why it’s a terrible idea, but so far, so good.
Thanks for believing in the game so far.
Valve has some great documentation on how this stuff works in the Source engine if you’d like to know about FPS-style networking.