Jump to content

Understanding the (very) basics of game networking


Elfocrash

Recommended Posts

How do online games work?

 

It is probably very obvious to everyone but I will just recap for the sake of having everyone in the same page while reading this.

In order for players to communicate they all need to connect to a point in the internet which has the capability to receive and send back data from and to the clients. We call this point the "Server". Don't be amazed by the name. It is just a machine with specialized capabilities. For example it is in network with a lot of bandwidth to it's disposal and a good upload and download internet speed. It has a great CPU and a lot of RAM in order to process the data and the requests.

This server is running the server software of the game. If that is an MMO then it's the LoginServer and/or the GameServer while if it's something like an FPS then it is just the Server that will host this specific game.

Upon getting ingame the client will send several requests to the server over the network. These requests can be sent over either TCP or UDP (or both). 
Those requests are (almost always) encrypted byte arrays. We call the Packets.

 

TCP vs UDP

I've seen people getting confused by this but the main idea is this

TCP will guarantee that the packet was sent and received and it will send a response. This means that it is reliable but slower than UDP.

UDP will guarantee that the packet was sent BUT it won't guarantee that it was received. There is also something going on with how those requests are queued and if they are received in the correct order, but i won't get too deep into this.

 

Packets

When we are creating our client-server communication we have one thing in mind. Transfer ONLY what is needed. The less packets and individual packet size the better and more fluid experience the players get. 
So i told you that a packet is a byte array but let's look deeper into that. How is it built? How do you read the data?

For my example i will use an MMORPG as the game I am coding.

What my players want to do when they open the client is to log in. 
When the player opens the client we can automatically send a packet to the server to check if the server is up and running waiting for requests. We will call that the PingRequest packet.

Packets in game networking have specific structures. We need to have a specific structure per packet because we will need to read the packet in that very specific way in order to get the correct data back out.

Every packet starts with an identification byte (or short if you are planning to have too many packets). We call this byte the "opcode". The server will always read the first byte before moving to anything else. This will let the server know how it should read it and what it should do with it.

 

Our PingRequest packet will look like this: c
All the packet contains is it's opcode as we only need it to check if the server is up. 
The PingRequest is a c2s because the client is sending it to the server. 
We will get back a PingResponse packet which will look like this cc
What this will contains is the opcode and a byte which tells up if the gameserver is up or not. The PingResponse packet is a s2c because the server is sending it to the client.

Now let's dive deeper. The next step is logging in. This means we will need a LoginRequestpacket. The packet structure (without any fancy info like HWID) will need to me at least of this structure: cSS

This means we need the opcode and two strings for username and password.

 

Well this also brings up a question. How am I reading a packet that it's size can change on every request? That is try. A string is a set of characters but we don't know how many the user will type in. How do we work around that to read the strings without losing any data? 


Well it is actually pretty simple. We send a Null-terminated string. A null-terminated string is a character string stored as an array containing the characters and terminated with a null character \0. Essentially we are just adding the null character at the end. This then lets the packet read method know what when you have to readS() you have to read char by char until you see a null character. Then move to the next thing you have to read. Note here that Intersect doesn't do that. Instead it writes an int in front of the string with the length of the string to follow ( :'( ).

The LoginResponse then looks usually like this: cch
Again the first byte is the opcode. The second one is a true or false. If true then you can log in. If it is false then we read the next short which is the reason why our login was unsuccessful. 1 could be "Invalid password", 2 could be "You are banned" and so on.

 

This is the core of how the server and the client are communicating.

Now what you really don't want is to let the players manipulate the data they send. At the end of the day this is all traffic that they are sending and receiving on their PC. How do you counter that? Well technically speaking, you can't. Even if you encrypt the packet content (which you should do) the decryption and encryption has to take place at some point in the client itself. However even if you decrypt the contents you can't know exactly how to read the bytes because you don't know the packet structure. There are always ways to reverse engineer this information, but with every packet packet structure and encryption change slightly so you can slow them down. However on top of that you will need server side validation to things like movement (manipulating ingame xyz coordinates etc).

 

However encryption is a topic for another post.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...