|Network is a Bitch|
|January 3, 2013, written by Maxim||Aril, Technical|
Remember the discussion we had about engines and how Simon and I decided to write our own for Aril? This applied to the game’s networking as well. I decided it’d be cool if I’d write my own network system. I figured it would probably be kindof difficult to implement such a thing, so I’d reserved 2 weeks of my working schedule to create this system. Lol, stupid me… 2 months later it’s almost nearing a state where I’d feel confident letting testers try it out, but my god did I run into a shit ton of problems along the way. I’ll proceed to describe the biggest obstacles I encountered.
The first mistake I made was thinking that TCP was cooler than UDP. In a nutshell TCP (Transmission Control Protocol) is a protocol that allows you to send packets of data to another computer using a virtual connection that ensures that all packets you send actually arrive at the other endpoint. For this protocol to work you are required to set up such a connection between your endpoint and the endpoint you want to send your packets to. UDP (User Datagram Protocol) is rather similar, except that it doesn’t guarantee your packets arrive. UDP does not require a connection and because it works simpler than TCP it is faster. For many games packet speed is very important. When packets take too long to arrive your game will start to lag and everyone will be unhappy. For Aril this isn’t really a problem because as a game, its setup is rather simple and it doesn’t need to send a shitload of packets. The real problem for me with the TCP was the connection. After I’d written the entire networking system for TCP, It turned out the connection part was really going to be a bottleneck for the system to work at all. So I decided to rewrite everything for UDP. This meant I had to write my own acknowledgement system, to ensure a sent packet actually arrived. What this means is that, when you want to send a message you send it every so many seconds until you receive a message from the other computer that he has received your initial message, clever ey?
The reason setting up a connection was such a pain in the ass leads us to my second problem: NAT Traversal. Aril matches are setup to work peer-to-peer, so when you play a game against someone your computer is directly connected to theirs. The problem with internet connections nowadays is that they are almost always behind a local router. This allows every household to only use one IP address while connecting all your computers to the internet (IP Addresses are rather expensive, cause they’re running out :O). Whenever you request something from the internet, your router remembers this and when the request returns your router sends the package towards your computer. This system is implemented through NAT tables, which remember outgoing calls and links them to your computer’s mac-address (which is a sortof ID for your PC). The problem with peer-to-peer connections is that someone else wants to connect to your computer without you requesting something first. When the connect package reaches your router it has no entry of you requesting it, so it decides to drop the packet (which actually works as a really good firewall against hackers and such). Seeing as this is not an unwanted connection, it’s rather shitty that you can’t hear about it.
The solution I found to this problem was a technique called Hole Punching (which I learned about from this handy dandy article). What it boils down to is that when one computer wants to connect to another you just both start spamming connection request at each other until one of them ‘punches’ through the router’s NAT table. While this works relatively well with UDP, it works like shit for TCP. This led me to redesign my system for UDP.
Most other problems I encountered had to do with the implementation of the network in the game. Making a game work on a single system is quite doable, but it’s a lot harder to distribute gameplay over 2 (or heck, even 6!) computers, this required me to do a loooot of tedious testing, because it’s also rather hard to properly debug a system such as this.
Seeing as it was that much of a hassle in the end, do I think it was stupid that I wanted to implement this system myself? Hell no! I now understand how all these systems and techniques work, I can tweak my own engine until it runs perfectly smooth and I’ll remember this knowledge until I die (or maybe until I get Alzheimer or something…). That’s the whole beauty of implementing stuff yourself. You learn so fucking much, it’s amazing!
So, what points should you take away from this? I wouldn’t want to discourage the use of TCP just because it didn’t work out for me, in general it is a lot safer to work with. The connection part is just a little bit more of a hassle. A point you can clearly take away is that NAT traversal is crap. There is no standardization of NAT implementation in routers so hole punching won’t work in every situation. In fact there is no NAT traversal method that works in every situation. So if you’re going to have to deal with it, be prepared to lose lots and lots of time!
And that’s it for all my encouraging words!
This was actually a rather long post. Would you rather read shorter pieces (with more pictures ^^)? Or do you think in-depth technical posts have their value?
Also, happy New Year and whatnot!