UDP Tutorial

Winsock Tutorial Using the 39DLL Extension made by 39ster adapted for extension by Serge Humphey


Two Player Pong using UDP and TCP

In this tutorial I will teach you how to use my WinSock dll to create a online game using both UDP and TCP. I will use

Pong as my example game.


Advantages of UDP

The big advantage of UDP is its speed and low bandwidth use. UDP is a lot faster then TCP and should be utilized in a game were the main packet is sent continuously. Also UDP is connectionless so there is no connecting involve. You just send the message to the right IP and port and its done.


Disadvantages of UDP

One disadvantage of using UDP is it is unreliable. There is no guarantee that all your packets will arrive. This is why we also open a TCP connection so important packets can be sent through the TCP socket. Packets like the main packet should be sent through UDP because even if it doesnt arrive, another one is sent straight after it.


Another big disadvantage is firewall issues with UDP. UDP requires a configured firewall just as if you were hosting a game with TCP. So with UDP even the clients need to have their firewall configured.


Setting up the server

To create a multiplayer game, one person needs to host the server and the other person needs to connect.

To set up a server using the dll you first need to create an object which controls the setting up of the server and accepting any new connection. What you will need to do is create a menu that has 2 buttons. The first button says "Host" and the second button should say "Connect". When the user selects host, make it execute this code "global.master = true;" and make the room change to a room called "rmWaiting". Create a new object and call it "objWait". In the create event of this object add this code:


{

    listen = dll39_tcp_listen(14804,2,true);

    if(listen <= 0)

    {

      show_message("Failed to listen on port 14804");

      game_end();

    }

}


What that piece of code does is create a listening socket that will listen for any incoming connections on port 14804. The port number can be whatever you want but I have used 14804. The second argument is the maximum allowed connections in the waiting list. This is not the maximum allowed people in the game. When someone tries to connect they are put on the waiting list until the server accepts them. The last argument is set to true because we want the listening socket to not freeze the game when you are using the dll39_tcp_accept() script and no one is has tried to connect. The script will return a socket identifier number that is bigger than 0 if we succeeded. A number <= 0 is returned if an error occurred. The next line checks if we are successfully listening on port 14804. If the socket identifier is smaller than or equal to 0 then an error occurred so we end the game.


Accepting new connections.

To accept new connections you need to create a step event for the "objWait" object we created. In the step event add this code:


{

    client = dll39_tcp_accept(listen,true);

    if(client <= 0) exit;

    global.udpsock = dll39_udp_connect(14805,true);

    global.otherplayer = client;

    global.otherip = dll39_lastin_ip();

    global.otherudpport = 14803;

    room_goto(rmGame);

}


The first line checks the waiting list to see if someone tried to connect to the listen socket. If no one connected then it returns a number smaller than 1. If someone did connect then it creates a new socket and returns its id. This socket will now be used to

send and receive data from the person who just connected. The second argument in dll39_tcp_accept means the new socket will be non-blocking. In this case when you are trying to receive a message it will not freeze the game if there is no message to be received.

The second line checks if dll39_tcp_accept() returned an error. If it did then it exits the script. Every line of code after the second line will only be executed if there was no error. The third line creates a UDP socket which will be used to send messages through the UDP protocol. The next line sets the global variable "global.otherplayer" to the socket id that tcpaccept() returned. The next line then gets the IP address of the player that was last accepted and stores it in the variable global.otherip. This will be used when sending a UDP message to the other player. The next line stores the port that the UDP socket is using on the remote computer into the variable global.otherudpport. This will be used when sending a UDP message to the other player. The last line goes to the playing room.


Connecting to the Server

To join a multiplayer game we need to connect to a server. In the room where you have the button "Host" and the button "Connect", make it so when the player clicks connect it executes this code:


{

    global.master = false;

    server = dll39_tcp_connect("127.0.0.1", 14804, true);

    if(server <= 0)

    {

      show_message("Unable to connect to server");

      game_end();

      exit;

    }

    global.otherplayer = server;

    global.udp = dll39_udp_connect(14803, true);

    global.otherip = dll39_tcpip(server);

    global.otherudpport = 14805;

    room_goto(rmGame);

}


The first line sets the global variable "master" to false. This is because we are not the server. We are the client connecting to the

server. The second line makes the actual connection to the server.

The first argument in dll39_tcp_connect is the ip address you want to connect to. If you are just testing locally use the address "127.0.0.1". The second argument is the port number to connect to. The third argument is whether to use blocking or non blocking mode. We set it true which means non-blocking. This makes it so whenever we try and send or receive a message, the game doesnt freeze until the operation finished. If dll39_tcp_connect successfully connected and was accepted by the server, the variable "server" should now contain the socket id. If an error occurred a number < 1 will be returned.


The next line checks if an error occurred. An error could be because the server doesnt exist or the server didnt accept us. If an

error did occur then the game will end.


If no error occurs then the global variable "otherplayer" now becomes the socket id that the variable "server" has. Then it opens a UDP socket on the port 14803 and sets it to non-blocking mode. The next line retrieves the IP address of the server and stores it in the variable global.otherip which will be used when sending UDP messages. The next line then stores the port number that the host is using for its UDP socket and stores it in global.otherudpport. Then the game will start when we use room_goto().


Sending and receiving messages

For our game to work we need to know the y position of the paddle that the other player controls and the client needs to know the x, y

position of the ball which will be controlled by the server.


Sending

In the paddle which is controlled by you, you must send your Y co-ordinate to the other player so they can draw your paddle in the

right position. To do this, put this code in both the keyboard UP event and the keyboard DOWN event:


{

    dll39_buffer_clear(0);

    dll39_write_byte(0,0);

    dll39_write_short(y,0);

    dll39_message_send(global.udpsock,global.otherip,global.otherudpport,0);

}


The first line clears the internal buffer of any data. This is used in case there is data already in the buffer. The second line writes the byte

that represents the Message Id. In this game the message id 0 will indicate the message containing the Y position.

The next line writes the actual Y position to the buffer. We have used the data type "short" because a short can be any number between

-32000 to +32000. A short utilizes 2 bytes. If we had used one byte to represent the Y position and the Y position is bigger than 255 it will end up being not what you wanted. The last line sends all the data in the internal buffer to the other player through the UDP socket. The second argument in sendmessage is the IP of the person to send the message to. The third argument is the remote port to send it to. In this case the data is the Message Id and the 2 bytes used for the Y position.


Now we need to send the x, y position of the ball to the other player if we are the server. To do this put in the step event of the ball object:


{

    if(!global.master) exit;

    dll39_buffer_clear(0);

    dll39_write_byte(1,0);

    dll39_write_short(x,0);

    dll39_write_short(y,0);

    dll39_message_send(global.udpsock,global.otherip,global.otherudpport,0);

}


The first line checks to see if we are the server. If we are not the server then exit the script and do not execute the code below.

If however we are the server then first clear the internal buffer. Now write the Message Id "1" which will indicate the message is the balls position. Now write a short which represents the x co-ordinate and write another short which represents the y co-ordinate.

Now just send the message to the other player through the UDP socket using their IP and remote port.


Sending Chat Messages

Below is the code we use for sending strings:


{

    dll39_buffer_clear(0);

    dll39_write_byte(2,0);     //chat message id

    dll39_write_string("Hello other player",0);

    dll39_message_send(global.otherplayer,0,0,0);

}


The second line is the ID we will use for chat messages. The third line is used to write the chat string to the buffer. The last line sends the message over the TCP port because we want to make sure it arrives.


Receiving Messages

In the paddle that you do not control...the one which is controlled by the other player, put this code in the step event:


{

    var size;

    while (true)

    {

      size = dll39_message_receive(global.udpsock,0,0);   //try receive a message on the udp socket

      if(size <= 0) size = dll39_message_receive(global.otherplayer,0,0); //if no udp message try receive a tcp message

      if(size < 0) break;

      if(size == 0)

      {

        show_message("The other player left the game");

        game_end();

         break; /* The game_end() function will not end the loop itself */

      }

      messageid = dll39_read_byte(0);

      switch(messageid)

      {

        case 0:

          y = dll39_read_short(0);

        break;

       

        case 1:

          objBall.x = dll39_read_short(0);

          objBall.y = dll39_read_short(0);

        break;

   

      case 2:

        chatmessage = dll39_read_string(0);

        show_message(chatmessage);

      break;

      }

    }

}


First this creates an infinite loop using 'while (true)'. The first line of the loop receives any messages from the other player that was sent to the UDP sockets and sets the variable "size" to how much bytes we received. The second line checks to see if no message was received. If no message was received then try and receive a TCP message. If no TCP message was received then exit the loop. The next line checks to see if the other player disconnected from the game. If the message size is zero, that means the player left the game.

If the player did leave, then end the game. If we did receive a message then all data from the message is placed inside the internal buffer. We can now use the buffer scripts to return the data from the message. The first part returns the message ID (where it says messageid = dll39_read_byte(0);. After that it uses the switch() statement to check what the ID equals. If the ID is 0 then this means the message is for the other players y co-ordinate. We simply use readshort() to return the y position. Remember because we wrote the Y position as a short, we now must read the Y position using a short.


If the Message ID equals 1, which represents the balls x, y position then use dll39_read_short(0) to set the balls x position to the right place and dll39_read_short(0) again to set the balls y position in the right place.


If the Message ID equals 2, which represents a chat message then copy the chat string to the variable chatmessage using “dll39_read_string(0) then display the chat message.



Closing the sockets

When finished with the dll you should close all sockets opened during the game. To do this just use dll39_socket_close(socketid) on all the sockets that were used. Dont forget to also close the listening socket.


Copyright 2008, Serge Humphrey

This help file has been generated by the freeware version of HelpNDoc