[SDL2 Part – 12 ] Multiplayer

Introduction


A huge aspect of game programming is multiplayer. A huge share of all the popular video games today support internet in sme ways. But this introduces several new challanges. Both in the communciation over internet itself but also in what data to send, how to react to data and how to make everything synchronized. This is going to be a sub series in itself starting off with how to do the actual communication.

Packages


When you’re sending something over the Internet, it’ll get split into quite small packages. Each of these packages are usually a few kilobytes or smaller. There are actually several types of packages which get wrapped in each other. I won’t get into the details of this, but what we need to know is that there are several types of packets and they get wrapped in each other.

One of the basic packet types is the IP ( Internet Protocol ) packet. You can look at this as the package that leaves our computer and is sent out on the Internet. Every package that is sent across the Internet is of this type.

IP package

We won’t go into the details of each of these fields, the image is there just to show you how it looks. I might go into the details on a later point in time, but that is a big topic and we don’t really need to know all of that in order to do multiplayer.

The package is then sent out on the Internet and will eventually find its way to its destination PC. This is similar to sending a letter. You put the letter inside an envelope and write the destination address on it. Then the mail carrier will take care of the rest.

Actually, that isn’t entirely true because there is another step before the package leaves your computer; we need a protocol.

Protocols


Above we saw an example of in IP package.

So now that we know about packages, the next topic is protocol. A protocol is basically a set of rules about how the two parts should communicate. Most important to us, these rules dictate how to detect package loss and how to deal with them.

Why do we need protocols?


Most of the times when you send something, everything goes fine and the recipient gets the data packages he need. But things can go wrong. Let’s take an example.

Say we have sent our letter to someone. But on it’s way, the letter gets lost. But how can we know? The mail carrier doesn’t know that the letter is lost. The recipient doesn’t know you’ve sent the letter and you yourself is assuming the letter arrived at its destination without any issues.

The exact same thing could happen on the Internet ; packages can get lost, and none will know. So how can the problem be avoided? This is where protocols come in!

There are two protocols, we’ll cover, TCP and UDP. This time, I’ll only cover UDP. I’ll cover the other one, TCP, in a later post.

UDP


The most basic of the protocols we’ll look at is UDP ( User Datagram Protocol. ) Actually, UDP is so basic, there is no rules about how to deal with package loss. You have to handle that yourself.

Another issue is that there is no guarantee that the packages will be received in order. So you could get packages 1,2,3 as 3,2,1 or 3,1,2 or 2,3,1 etc.. And that’s if you do get all packages. Needless to say, using UDP can cause lots of problems. But its’ simple so we’ll start with it.

UDP is generally used for performance or simplicity reasons :

  • Video streaming
    • If a package is lost, this is just a tiny piece of the stream data and you might not even notive it
  • In cases where you just want to send a state to the server
    • Like using ping where you just get an echo back
    • If you don’t get a message back, you can retry again and again, or report an error
  • Games
    • For reducing lag
    • This means they have their own way of dealing with package loss

Addressing on the internet


On the Internet, there are two units used for addressing, the IP address and the port number. We need both of these to communicate over the Internet.

IP adress


The IP address is used to address computer. Every unit on the Internet has an IP address that refers to that unit. You can look at it as the address of a house. So when you send a letter to someone in a house, you write the address to that house. When you send a packet to a computer, you send a packet to that IP address.

Port numbers


Port numbers are used to distinguish between connections. Each connection has a separate port number tied to it. If we didn’t have port numbers, all data would go into a large buffer and you’d have no idea which of these datas where yours.

So if an IP adress refers to a house, a port could be looked at as a name.

When you set up a connection, you need both of these an IP address and a port number. Actually you need two of both since the receiver needs to know who sent the package so that it needs to know who to reply to. So all in all, we need two IPs ( out IP and destination IP ) and two port numbers ( our port numbers, destination port numbers. )

So basically, you neeed :

  • Your own IP adress and port number
  • The recipients IP address and port number

Setting up the connection


Actually, UDP is not really a connection, it’s just two parts sending data back and fort. But both parts still needs to know both IP and port number of each other. And it is common and more practical to think about it as an actual connection.

There are two roles in a UDP “connection”.

  • A client
    • Tries to connect to a server
  • A server
    • Waits for a connection from a client

So the procedure for a “connection” will be something like this

  • Server with ip 123.123.123.123 waits for connection, listening to port 123
  • Client sends a packet to 123.123.123.123, port number 123
  • Server stores IP and port number for client
  • Server and client now knows both port number and IP of each other
  • Now the server and client can now send data back and forth

Sockets


The final part we need to cover about connections are sockets. A socket is a combination of IP address and ports that is unique on every PC for every connection. It consists of the following :

  • IP and port of the client side of the connection
  • IP and port of our the remote part of the connection
    • This part is mostly used for TCP connections, we won’t use it
  • Type of connection ( UDP, TCP, etc… )

We’ll be using sockets as a unit to keep track of a connection. You can look at scokets to the sockets you plug your electric devices to. Following that analogy, the wire would be the connection ( network cable ). So, in essence, it’s what connects your application to the Internet.


I realize this all might be a lot of information and hard to wrap your hand around it all, but it’ll get clearer when we put it to use.

SLD_net


Now that we know a tiny bit about UDP connections, let’s try to set up one ourselves. For that purpose, we need the SDL_net library. It is capable of setting up and maintaining both UDP and TCP connections. Since UDP connections are way simpler, we’ll only cover that for now.

Networking is, just like text rendering and .png loading, a separate part of SDL called SDL_net. We install it the exact same way as before :

Installation


Installing SDL2_net is done exactly like SDL2_image. Just replace SDL2_image with SDL2_net

Here’s the short version :

Linux


For Linux you can use need to install -lSDL2_net or -libSDL2_net or -SDL2_net ( the actual name might be different in different distributions. )

The linker flag is -lSDL2_net

The process is more or less identical to that of setting up SDL2_image.

If you can’t find SDL2_net in any repositories and it’s not installed by default, you might have to compile it yourself. For more information, see my blog post about setting up SDL2.

Windows


Similar to setting up SDL2 base.

The difference is that you have to download the development files for SDL2_net

And similarly add SDL2_net.lib to library includes and add SDL2_net.lib to the library flags ( where you previously added SDL2_image.lib )

And with that, it should work.

Mac


See the first part of my tutorial. Just install SDL2_net instead of SDL2

Using SDL_net to set up a connection


Setting up a connection with SDL_net is a bit more complicated than what we’ve previously seen. This is because there are a few steps, the code will be very C ( not C++ ) and there are some buffers ( raw arrays ) we need to keep track off.

We’ll be cutting out all GUI because we simply don’t need it. It will make our code shorter and it’ll be easier to display the results.

Structures of SDL_net


SDL_net contains two parts we need for out UDP connection. Let’s start with the simplest, IPAddress.

IPAdress


A simple struct with the following fields :

  • uint_32_t hostIP v4 address
  • uint16_t host – protocol port

It is used for keeping IP and port number together. Some functins takes this as one of the parameters.

UDPSocket


A pointer to a data type that holds to a pointer. Since it a pointer, it can be NULL, in which case there is no connection and we can’t send data back and forth.

UDPpacket


Our data packet. Contains the data we are sending back and forth along with some other information.

  • int channel
    • The src/dst channel of the packet
    • We won’t be using this
  • Uint8 *data
    • The packet data we’re sending
    • Can be of any length
  • int len
    • The length of the packet data
    • Used to find the end of the data in the data pointer
  • int maxlen
    • The max size of the data buffer
    • Always as large or larger than len
    • Only used for data package creation on the senders side
  • int status
    • Packet status after sending
    • Number of data sent
    • -1 on failure
  • IPaddress address
    • the source/dest address of apacket
    • For received packages this is the IP / port of the remote part.
    • For sent packages this is the IP / port to send to.

The various fields of a UDP packet is set with various function used for sending and receiving data. It might seem confusing right now, but it’ll get clearer once we get into the actual code.

Functions of SDL_net


SDLNet_Init


This function is just like SDL_Init and TTF_Init ; it initializes the SDL_net

SDLNet_UDP_Open


This function is used for creating a socket which we will use later to send data.

Parameters :

  • Uint16 port – the port we want to use. If you use 0, SDL_Net will assign a port for you.

Return value :

A valid UDPsocket, NULL on error. Remember that UDPSocket is a pointer.

As we saw earlier, UDP isn’t actually a connection. All we are doing is sending data back and forth. And all we need to do that is a socket. Now that we’ve opened this socket, we can start dealing with packages.

SDLNet_ResolveHost


As stated before, we need an IP address and port number in order to send data. The problem is that there are several ways to represent IP addresses and port numbers. The difference between them is the order in which the they are converted to binary. These orders are refereed to as little endian and big endian I won’t dive more into this, but you can read about it here.

The issue is that different system use different endian. So we need a uniform way of setting the IP address and port number. This is where SDLNet_ResolveHost comes in. What it does, is that it sets the name values of an IPAdress for us so we don’t have to think about endians at all.

Parameters :

  • IPaddress* address – a pointer to an IPAdress. Needs to be allocated / created in advance. ( In our case, we’ll use a variable and not a pointer so we don’t have to worry about this. )
  • const char* hostIP address to send to ( xxx.xxx.xxx.xxx )
  • Uint16 port – the port number to send to

Return value :

0 on success, otherwise -1. In this case, address.host will be INNADDR_NONE. This can happen if the address is invalid or leads to nowhere

SDLNet_AllocPacket


Allocates a UDP_Packet and returns a pointer to it.

Parameters :

  • int size – size of the packet in bytes. 0 is invalid.

Return value :

A valid pointer to UDPpacket, NULL on error ( such as out of memory )

The size of the packet determines how much data we get every time. It’ll never be more than this size, but it can be less. You can also expect that some packages gets mfSerged or split up into different segments. This is something we’ll need to handle.

After allocation space for a packet, we can finally fill that packet up with something. Which is kinda the point of this ordeal.

SDLNet_UDP_Send


Sends a UDPpacket

Parameters :

  • UDPsocket sock – Our socket to send data from ( the one we created with SDLNet_UDP_Open )
  • int channel – We’ll completely ignore this parameter and just set it to -1 ( all channels )
  • UDPpacket* packet – the data we want to send ( finally! )

Return value :

The number of destinations the packet was sent to. In our case, this will be 1. But it could be more. Because of this 0 is returned on errors. Anything higher than 0 means partial success ( since we were able to send to at least one destination. )

In our case, the function should always return 1 but I find it better to just check for 0.

SDLNet_AllocPacket


Now that we know how to send data, we also need to know how to recieve them.

Parameters :

  • UDPsocket sock – Our socket to receive data from ( the one we created with SDLNet_UDP_Open> )
  • UDPpacket* packet – the data we received

Return value :

The int value 1 when a packet is received, 0 when no packets where received, and -1 on errors.

Example


To make it simpler to use SDL_Net, I’ve made a helper class that takes care of everything. You’ll find an example at how to use it below.

Compile


To compile it on Linux or Mac. Simply run :

clang++ UDPExample.cpp -std=c++11 -lSDL2_net -o UDPTest

Starting the example


To use the example, you need two instance of the application. So start up two instances of it.

You’ll be asked to enter local IP. This is the IP of the computer you are sitting on. You can use 127.0.0.1 which simply means “this computer”. You can do this in both instances. You’ll also be asked to enter a local port and remote port. These needs to be opposite on the two instances ; the local port of the first one, needs to be the remote port of the other. This is because we need to know where to send it to and where to listen for data on.

Instance 1


Instance 2


( notice the difference in local and remote port on the two instances. )

Using the example


After inserting IP and port data, you’ll be presented with a simple menu :

Nothing will happen before you do one of the three options. And if your message doesn’t show up on the other instance, make sure you’ve entered ‘2’

And now you should be able to set up connections. Feel free to use the UDPConnection struct as you like.


Feel free to comment if you have anything to say or ask questions if anything is unclear. I always appreciate getting comments.

You can also email me : olevegard@headerphile.com

9 thoughts on “[SDL2 Part – 12 ] Multiplayer”

  1. Very nice tutorial! Could you possibly post the source code, since I was getting lost with the parameters I would use for a simple chat between clients. Could you do more tutorials on this? With this tutorial I was able to understand what all the functions do, but not how to implement them. Thanks a lot for these tutorials. Keep up the good work! 😀

    1. Thank you! =)

      The source code should be embedded now. If not, you can find it here : https://gist.github.com/15d8cff035162f02413b

      I do intend to do a tutorial on TCP connections, soon. But I’m currently starting a new job, so I don’t have too much spare time to write these. Feel free to ask if there’s anything else, though. I’ll be happy to help

      1. hey thanks for the link to the github code! I’m just having alittle trouble dissecting the different parts of the code but I guess I need to analyze the code longer. Thanks a lot for these tutorials. Happy 2015! I also saw High Flyer by GameGrape Studios that used SDL2. Here’s this link if you want to check it out, http://gamegrape-studios.itch.io/ Hey do you have a Skype? We should talk sometime! see you later.

        1. I think that’s completely normal. It’s a C-library so function names are short an not very descriptive. But just experiment with it on your own. Try writing your own chat client or something simple like that. If there is anything particularly you struggle with, feel free to post a comment about it.

          Give me your Skype, and I’ll add you. I don’t use it much though, so I’m rarely online.

          1. Oh Sorry I forgot to come back xD (4 months later) my Skype is djcyberslash1. I’m about to implement Online Multiplayer into one of my games so this is a great time. I just wanted to stop by since i haven’t been here in a while and see how everythings going.Are you still making these articles, they are very good! Oh yeah I wrote my own simple chat client! It was local ( It may of also been public since I set up the port and stuff) and the clients could see the servers messages but couldn’t send messages to the server for all the clients. It was a start but I haven’t worked with SDL2_Net for a while since I lost all my data (microsd cards). Anyway, Talk to you soon! 🙂

  2. Glad to see you back : )

    I have been kinda busy since I got a job way back in November. But I have gotten back into writing a little. Hopefully, I’ll have an article on TCP soon-ish. It’ll be fairly extensive and I might also update this post with some improvements.

    Regarding this part ; there is a bug in which you can only send one word at a time, I’ll update the article asap with a fix.

    I highly recommend getting git. That way, you can save your code to Github and you’ll never loose your code again. It’s also helpful for experimenting with the code, going back in time, etc. I use it for all my code. You can read more here : https://help.github.com/

  3. Hey,
    Thanks for the great tutorial and example, helps out a lot!
    I was able to get your example to work locally on my computer and it does as expected.
    However I am unsure of what I need to do if I want to talk to other computers on my network?
    Do I need to change the IP address to something else? If so what could they be?

    Thank you again 🙂

    1. Hello and thank you, I’m glad you find my tutorial helpful.

      Yes, you need different IP’s. The ip 127.0.0.1 basically means “my computer”. So in order for two different computer to communicate, both need to know the IP of the other. You can find the ip by opening your command prompt / terminal. On Windows you can start this by pressing Windows + R, then typing cmd and hitting enter.

      When you are in the command prompt / terminal type ipconfig or ifconfig on Linux/Mac. From there you can find the ip. Then you can use to IP from the other computer instead of 127.0.0.1

      This should work, but let me know if you have any other issues. 🙂

  4. Hey!! Thanks for the tutorial! It helped me too much with my multiplayer game but i am having a big trouble about the latence.. for testing my game i usually open two instance of the game, these two games can communicate perfectly but if i move the charachter in the first game, in the other game it updates with some delay…( But if i move the charachter in the second game, the first game updates perfectly..strange ). I discovered that the delay time is equal to the time i spent since opening a game and opening the other game ( they have the same souce code ). The game consists of a loop that sends data of the player 5 times (5 ints) and receive data 5 times.. so if the game 1 send 5 packets per frame, the second game, at its runtime, and with the same velocity of loading packets (5 per frame) will not reach the number of game1 packets sent. So it will load all the packets since the older to the younger and know i understand why this delay.. i don’t understand how to fix it, the two games will never run at the same time.. can you help me?

Leave a Reply

Your email address will not be published. Required fields are marked *