I sometimes get asked how we do the networking system in Rust + Unity. Do we use Unity’s networking system? Do we use uLink? Do we use two different projects, one for server, one for client? So here’s how we do.
Networking uses Raknet. Raknet is a tried and tested networking solution. It was bought by Oculus for some reason and made open source. This is great except that it hasn’t been updated in forever – and there’s some bugs in the repository version. The fact that these bugs exist, yet it was so widely used make me think that the open source version had to have a bunch of shit ripped out, so it hasn’t really been that tried and tested.
Raknet is coded in c++, and Rust is in C# – so I made a little wrapper library for it. The wrapper doesn’t really have to be that complicated, pretty high level. Pretty much just creating and closing a server, connecting and disconnecting, sending and receiving packets. Easy.
Something that maybe confuses a lot of people is the split between server and client. Creating two projects would have worked, but it would have been pretty complicated. I was raised on the Source engine, so sharing code and ifdef’ing it makes the most sense to me. So that’s what we do. The server is compiled with SERVER defined, the client is compiled with CLIENT defined. Then the code is peppered with this kind of stuff.
And we have one of these if we need to switch between them in the editor.
This works fine when releasing stuff but you really want to test shit in the editor. In legacy we had the ridiculous situation where if you wanted to test something you’d written you’d have to compile a server and start it up in the background, then run the client in the editor and join it. This was a fucking nightmare. The problem was that this was so ingrained in how the game was made that it was pretty much impossible to change – and was one of the big arguments for restarting.
With experimental it was a high priority to get listen servers working. How it works is it defines SERVER and CLIENT both. So the code has to be written in a way to account for this.
The game needs to include client and server entities separately. I started off making it so they’d try to share an entity and this lead to problems when releasing builds. It wasn’t showing the same behaviour because all the entity variables were automatically shared – instead of being networked. This is really important – it should try to emulate the networking 100%.
Including both sets of entities needs a bunch of care and hacks. For example, if you have a physics object you need to be careful because there’ll be 2 physics objects occupying the same space, the client and server version. You need to make it so they don’t interact with each other.
But once you get it working
And then it’s just a case of being mindful of maintaining it. Which isn’t too hard considering that if you don’t it breaks.
Server Console Window
I’ve talked about this somewhere before. How do we make our server console actually show up in a window. Unity fucks with the standard output so that when running in -batchmode the output all gets redirected, so you can’t print to the screen.
Well, through trial and error I found a way to make it work. Here’s a class that makes it work for Rust. After running that you should be able to use the System.Console commands as normal.
If you want to know how we make something else happen in Rust + Unity abuse me on twitter and I might do a blog about it.