Right after we released v3.5 we moved on to the last big subsystem we still have to build: multiplayer netcode. We don’t have anything playable for you yet, but we’ve done a huge amount of work and I’m going to tell you all about it.
Multiplayer Version 1 Goal
The biggest goal for a first playable multiplayer version is the ability to fly next to one another. That’s it.
Sounds simple? Let me tell you why it’s not.
Like all networked applications we have to deal with latency, meaning the time it takes for a message to go from one computer to another. Information over the internet travels fast, but it’s not instant. It can easily take 50 milliseconds or more between my computer saying I’m at a certain position, and your computer receiving that information. When you’re flying at 70 meters per second, that means in those 50 milliseconds you fly an additional 3.5 meters.
This means that if your computer shows my character at the position it just received, you’ll be seeing me 50 milliseconds in the past, 3.5 meters behind you. In turn, I will see you 3.5 meters behind me as well because the same delay happens for messages travelling from you to me.
With this setup we couldn’t fly next to one another, nor would it be clear if we ever collided with one another. What’s on your screen is very different from mine. With even just 50 ms of latency we will need to do some trickery to make these interactions possible. So let’s talk trickery.
As luck would have it, we can make good assumptions about a character’s trajectory. Apart from any sudden changes in direction because of collisions, accelerations (changes in direction) stay within a small range. This means we can try to predict a character’s motion ahead by those 50 milliseconds and end up with a reasonably accurate estimate of where she actually is. If both players apply this prediction, and it is correct enough, they can get the experience of flying next to one another.
There is a conceptual problem with this: Prediction can be wrong. I can’t actually know what your input is going to be ahead of time, so the best I can do is assume you’ll keep doing what you were doing before. If you did something completely different (e.g. I assume you’ll pitch up, but you pitch down) the prediction will be wrong. As soon as I find out where you actually are, I’ll have to fudge you back to a more correct position, and that will look strange at times. Lots of effort goes into figuring out how to do accurate prediction and smooth application of error correction.
There’s also a technical problem specific to Unity. Ideally, this is how you handle prediction errors:
- Roll back to last received valid position (from 50 ms ago)
- Instantly simulate ahead to current local time again (by 50 ms), using recorded player inputs
- Move player to this new, better position
Step two involves rewinding and simulating physics in non-realtime, which is not something you can do with Unity’s physics engine. There are two possible solutions to this problem:
- Use a different physics engine (like Bullet Physics, which Rocket League had to do)
- Create a simpler physics model and hope for the best
We’re currently going with the latter since it’s way easier to do. Implementing Bullet and making the whole game work with it would take a lot of extra time. Instead, we’re applying the simple first order Euler method to integrate motion of the character’s root bone, treating the entire ragdoll as a single point mass that responds to input and has rudimentary aerodynamics applied. The limbs will just follow along. Basically, for the correction phase it reduces the flight model to a simple paper plane with roughly the same characteristics as the wingsuit.
This solution doesn’t model collision or joint interaction at all; let’s talk about that.
At the moment we’re not sure how collision will work. Because of latency everyone is playing a slightly different game, and when collisions happen they will be different on each machine. We need to somehow reconcile this so everyone still ends up with the same result.
Luckily inter-player collisions are not at all required to work reliably for the core game to be enjoyable, but we still need to choose how to handle them.
We’ll try a naive full-ragdoll collision model first and see how it plays. If it turns into a rubber-banded nightmare (which I expect) we’ll have to try simpler collision models, like treating player geometry as one big cylinder.
Apart from synchronizing the actual game over the network, we also have to think about how we’ll actually bring people together.
We decided early on that we wanted players to connect ad-hoc, hosting the game themselves. Hosting tons of our own servers would cost a ton of money, and if they were ever to go down the game would be unplayable. Instead we’ll build a matchmaking service that connects players to one another, and then lets them simulate the game themselves. (Note: that’s not to say dedicated servers are out; they’re in. It’s to say we probably won’t host many ourselves.)
Multiplayer version 1 will come with a standard server browser system. You either host a game yourself, or you join an available one from a list. But because we’re building this service ourselves we can do cool tricks with it later, such as matching people together even when they’re playing singleplayer. Our dream is that you’ll always see other people flying, and be able to interact with them if you want to.
Oh, and why are we building this ourselves? Can’t we use Steam’s services? Well, we could, but we don’t want to depend on Steam for everything. We’ll integrate Steam so joining people from your friends list is easy, but we want the game to work if you don’t have a Steam account or (god forbid) Steam ever goes down.
For those who are interested, here’s a breakdown of the networking solutions we’ve looked at. We want several things:
- Flexible control over who is able to send to whom, and when, and how.
- Configurable quality of service per message type
- Permissive license (for making fixes, mod support)
- Actively maintained
- Console support (further down the line)
- Support for extension
- Priority, lod, culling
- Data-level compression
- Record and replay entire sessions
- Transparent ways of packing and sending actual packets (no guessing what happens)
- Open source is a plus
Battletested C++ library used by many games. Bought and open sourced by Oculus a year ago, after which it appears to have been abandoned. You can use Raknet in Unity through an auto-generated C# wrapper, but as we’re both not C++ heroes we’ve had trouble doing so, and we would certainly be ill-equiped to fix any bugs in the source if we were to run into them. Can connect peers through NAT punchthrough.
Open source C# library with same functionality as Raknet. Less battletest, but seems like a solid choice. Also has NAT punchthrough module built in.
Networking suite that comes with a lot of things out of the box, and is used by a lot of Unity games. License isn’t free, and it is closed source. Given how much trouble we’ve run into with other closed source middleware we’d rather avoid it.
Similar to uLink in terms of functionality but limits network topologies and hosting options. You can only establish peer-to-peer connections (no authoritative servers)
A new networking solution that built into Unity from version 5 onwards. Comes as a high level API for beginners built on top of a low level API. The high level API doesn’t scale to big games, but the low level API is a great starting point for building our own protocols.
Unfortunately UNET by default forces you to route all game traffic through their Relay Service, which will not be free and adds significant latency. We want to use UNET, but first we need to talk about Network Address Translation.
Network Address Translation
These days all our computers are behind routers. I won’t bother you with the details, but that means its not trivial to establish a connection between your computer and mine, we need a middleman to help.
UNET solves this problem with a Relay Server. We both connect to it, and all our game traffic passes through it. The benefit is that you get a 100% success rate in connecting players, the big drawback is that depending on where players are the latency between them is significantly increased. Effectively, if we are both in New Zealand our communication will pass through the closest relay server, which is in New York. For a fast-paced game that’s not a compromise we can make.
Games have been using a technique called NAT punchthrough for ages. It too involves a middleman, but it only helps two players shake hands, after which they talk to each other directly. In terms of latency, this is what we want.
Great, but UNET doesn’t offer a NAT punchthrough option right now. My guess is they really want to push the use of their subscription-based relay service, ugh.
To get around this limitation we’ve implemented a version of NAT punchthrough on top of the UNET low level API, and though we haven’t tested it extensively, it works as a proof of concept! With this we’ll be able to get the best of both worlds: First we try NAT punchthrough, and if that fails we can still connect people through the relay service.
(Note: if this works out we’ll probably wrap it up in an Asset Store package. Let us know if you’d be interested in that.)
If you’d like to follow our progress in more detail, check out the v3.6 Trello Board. It contains a detailed and up to date set of todo items for Frank and me.
Oculus recently released runtime and SDK version 0.7. It’s a huge step up, but unfortunately it breaks compatibility with games built with older versions of the SDK. We’re in a bit of a bind here, because as of the most recently release version of Unity (5.2.0f3), linear lighting (which Volo uses) still crashes when running in VR mode so we can’t make any new VR builds yet.
We’ll keep you updated, but know VR support is momentarily messed up.
Frank’s just returned from a one week vacation, and I’m about to leave for three weeks myself so Frank will be pushing the cart on his own for a bit. I think it’s been about 5 years since I last went on a proper vacation, and I feel I can really use one right now.
Anyway, I’ll be back October 1st, ready to rock. I can’t make any promises as to when we’ll have a network-enabled build out, but right now I’m hoping for around November 1st. We’ll keep you updated!
As always, let us know what you think about this new release! Leave a comment here, or better yet: discuss on the forums!