Online Multiplayer Game

Discussion in 'Programming' started by TBryant, Jul 13, 2016.

  1. TBryant

    TBryant Guest

    hi
    After having made quit a few single player games and one split screen game, i feel as if i'm familiar enough with GameMaker to start making my 16 player online game. However i'm not actually sure on where to start with regards to the actual multiplayer part.

    Firstly, how many main player objects to i need to make just one, for all the players to use, or 16, one for each player in the game?

    Secondly, how do i code it so that a player can choose a unique name for themselves that will be saved as theirs forever.

    Finally, how do i do it so players can either choose to host a game of join a ready hosted one?
     
    james_castrello likes this.
  2. Christian

    Christian Member

    Joined:
    Jun 21, 2016
    Posts:
    61
    You need one player object for all of the players to use. Call it something like obj_client. You're not running those all on the same machine. Each player will have their own instance but they will all be obj_client. So if I'm player 1, I would be playing as obj_client(0). Then my friend in Texas would be connected as obj_client(1). Still one object, just different instances. However we would only see obj_client. You only really need obj_client and obj_server. That's it. Do not create new objects for each player. You only need one client object period. You tell the server how many clients it should support.

    How does it work? We're going to create a very basic TCP WAN server. (I can show you LAN and Bluetooth too but for simplicity and for the sake of a one sized fits all solution, we'll use WAN so anyone in the world can connect). You don't need to know about all these terms just yet, I'll explain them in Part 4. Now most people like to make a server project and a client project separate. It's a matter of preference. Keeping them separate is great for organization but personally, I hate sending two projects for one goal. So Here's what I do.
    Create an object called obj_server.

    obj_server > Create Event
    Code:
    ///Initialize our Server.
    var type = network_socket_tcp; //Creates a TCP Socket. The server can use network_socket_tcp, network_socket_udp, or network_socket_bluetooth.
    //network_socket_tcp is more reliable and stable. TCP checks for errors in the packets. TCP is a connection based protocol.
    //network_socket_udp is faster than tcp, but has a probability of dropping packets. UDP is a broadcast based protocol.
    //network_socket_bluetooth is currently not supported natively but I it's stable and fast for a broadcast based protocol.
    var port = 25653; //Will run the server on port 25653.
    max_clients = 8; //Sets our max clients to 8.
    server = network_create_server(type,port,max_clients); //Creates our server on the network and returns an id of server.
    socket = noone; //Creates a null socket, since we don't have a socket yet.
    obj_server > Game End Event
    Code:
    ///Terminates our server to free memory.
    network_destroy(server); //Destroys the server that we made in the create event. This will delete the server from the memory at the end of the game
    obj_server > Networking Event
    Code:
    ///Check for clients and data.
    var type_event = async_load[? "type"]; //Grabbing the type from async_load using an accessor, then store that value in type_event
    switch (type_event){
        case network_type_connect: //If our type_event is equal to network_type_connect. If someone tries to connect.
            //Add the client to the socket variable - When using multiple clients, use a data structure. This is a single client.
            if (socket == noone){ //If No one is connected yet.
                socket = async_load[? "socket"]; //Returns the socket from async_load using an accessor. Then store it in the socket.
            }
            break; //Break out of the switch event.
           
        case network_type_disconnect: //If someone tries to disconnect.
            //Remove the client from the socket variable
            socket = noone
            break;
           
        case network_type_data: //If we are receiving data
            //Handle the data.
            var buffer = async_load[? "buffer"]; //Create a temporary buffer.
            buffer_seek(buffer,buffer_seek_start,0) //Looks at the beginning of the buffer.
            scr_received_packet(buffer); //Executes our script scr_recieved_packet, and pass the buffer to our script.
            break;
    }
    Now we need to create a client. The client will click anywhere on the screen and it will show on the server. Create an object called obj_client
    obj_client > Create Event
    Code:
    ///Initialize our client.
    var type = network_socket_tcp; //Defines a TCP Socket For Our Client (Use UDP for LAN, but I'll cover that separately as it needs different configuration)
    var ip = get_string("Which IP address do you want to connect to?",""); //Creates an IP Address to connect to. Replace with server IP Address
    var port = 25653; //Connects to port 25653. Make sure client and server use the same port.
    socket = network_create_socket(type); //Creates a Socket using the type we defined above. Use network_create_socket_ext for LAN.
    connection = network_connect(socket,ip,port); //Creates a connection to our socket, server, and port.
    
    //Send Information To The Server.
    var size = 1024; //Has a size of 1024 bytes (1MB)
    var type = buffer_fixed; //Defines a Fixed Buffer, It does not change
    var alignment = 1; //Sets the alignment to 1, commonly used for strings.  See buffer_create
    buffer = buffer_create(size,type,alignment); //Creates our buffer.
    obj_client > Global Left Pressed
    Code:
    ///Create a click, send it to the server.
    instance_create(mouse_x,mouse_y,obj_click);
    
    //Write the click to the buffer.
    buffer_seek(buffer,buffer_seek_start,0); //Checks the beginning of the buffer
    buffer_write(buffer,buffer_u8,1); //Writes our ID to an unsigned positive 8-Bit integer (0-256) to our buffer. [Our buffer looks like: 1]
    buffer_write(buffer,buffer_u16,mouse_x); //Writes our mouse_x to an unsigned positive 16-Bit integer (0-65,535) to our buffer. [Our buffer looks like: 1, mouse_x]
    buffer_write(buffer,buffer_u16,mouse_y); //Writes our mouse_y to an unsigned positive 16-Bit integer (0-65,535) to our buffer. [Our buffer looks like: 1, mouse_x, mouse_y]
    
    //Send the buffer to the server
    //We need to tell it which socket to connect to, which buffer to use, and what buffer size we are using.
    network_send_packet(socket,buffer,buffer_tell(buffer)) //Buffer_tell is going to return the size of the buffer.
    
    obj_client > Game End
    Code:
    ///Destroys our socket and buffer to free memory
    network_destroy(socket);
    buffer_delete(buffer);
    Now we're just going to create a dummy object. This will just be a ball that shows up on the screen so we have visual feedback. Create an object called obj_click.
    obj_click > Draw Event
    Code:
    ///Draws a spot wherever we click.
    draw_set_alpha(1);
    draw_circle_colour(x,y,10,c_red,c_red,0);
    Lastly, we need to create a script that the server used in the Networking Event. Create a new script called scr_received_packet. Paste this in the script
    Code:
    ///scr_WAN_received_packet(buffer)
    var buffer = argument[0]; //Gets our input value when we execute the script [What our buffer will look like: 1,mouse_x,mouse_y]
    var message_id = buffer_read(buffer,buffer_u8); //Reads our Unsigned 8Bit Integer from our buffer. [What our buffer will look like: mouse_x,mouse_y] We remove the 1 because when a buffer reads information, it removes that id from the buffer.
    
    //You set which buffer ID you want to send information with. Maybe buffer id 1 sends mouse_x and mouse_y, id 2 sends Damage, id 3 sends health, etc.
    switch(message_id) {
        case 1: //If our message ID is equal to 1.
            var mx = buffer_read(buffer,buffer_u16); //Reads our unsigned 16 Bit Integer from our buffer and assigns it to the variable mx. [Buffer equals: mouse_y] -We deleted our mouse_x from the buffer upon reading it
            var my = buffer_read(buffer,buffer_u16); //Reads our unsigned 16 Bit Integer from our buffer and assigns it to the variable my. [Buffer equals:] -We deleted out mouse_y from the buffer upon reading it.
            //Create the click instance on our server
            instance_create(mx,my,obj_click);
            break;
    }
    Lastly, we can create a controller that determines whether or not you're joining a server or hosting a server. Let's create an object called obj_clienttype_controller.
    obj_clienttype_controller > Draw Event
    Code:
    ///Draws our instructions on the bottom of the screen
    draw_set_alpha(1);
    draw_set_color(c_black);
    draw_set_font(font_roboto);
    draw_text(0,room_height-16,"Numpad 1 = Create Server, Numpad 2 = Create Client, ESC = End Game");
    
    obj_clienttype_controller > ESC Pressed
    Code:
    ///Ends the game
    game_end();
    obj_clienttype_controller > Keypad1 Pressed
    Code:
    ///Creates the Server Object
    instance_create(0,0,obj_server);
    obj_clienttype_controller > Keypad2 Pressed
    Code:
    ///Creates the Client Object
    instance_create(0,0,obj_client);
    Now let's create a standalone executable and open it three times. Drag one window on the left, and one on the right. And the last somewhere in the middle. On the left one,go ahead and create the server. On the right one, go ahead and create the client. On the middle one, create a client. Once all of them are running. Start clicking in the client window. You should start seeing dots appear on the screen.Using the other client, start clicking around and you should see those dots on the screen too. You may notice that the clients don't see the other clients, that's because we only programmed the clients to talk to the server but we didn't tell the server to talk back to the clients. It uses almost the same methods, we just need to send information back to their sockets using for loops. I did this to show that you only need one client object for the game.

    This ends part 1. I'm writing up parts 2, 3, and 4 shortly.
     
  3. Christian

    Christian Member

    Joined:
    Jun 21, 2016
    Posts:
    61
    Side Note, if you're getting errors with font_roboto, I always create a font with the roboto font. I name it font_roboto. So if you get the error, just create a font called font_roboto. You can assign it times new roman, or arial, or whatever you want.
     
  4. Christian

    Christian Member

    Joined:
    Jun 21, 2016
    Posts:
    61
    A few more side notes. If someone is hosting your server, they need to open the port we listed above. They need to open port 25653 on their router. If you are hosting a server and running clients on the same machine. Just use the IP Address 127.0.0.1 or localhost. Those just mean that "Hey I'm running a server on this machine. Connect to this machine". This is useful if you only have one computer to test with.
     
  5. Christian

    Christian Member

    Joined:
    Jun 21, 2016
    Posts:
    61
    Part 2

    There are a couple of ways to do this. If you just don't want them to sign in again, it's pretty easy, I'll show you that shortly. However if you want to make it where they can be the only one with that username, I would recommend picking up PHP and mySQL. With PHP and mySQL, you would create a database that stores usernames and you would need to run checks to see if they exist in the database or not. You might also want to know how to make extensions for this since PHP isn't natively supported. I'll post a link to a great tutorial about it when I find the one I used for Java.

    However, I will not be getting into unique names and I will just briefly show you how to set up a username that's assigned locally on the user's computer.
    For this we are going to create an item called obj_saveandload
    obj_savenload > Create Event
    Code:
    ///Creates our default username
    username = "";
    obj_savenload >Draw
    Code:
    ///Draws our instructions
    draw_set_font(font_roboto)
    draw_set_color(c_black)
    draw_set_halign(fa_left)
    draw_text(0,0,"Press 3 to save. Press 4 to load")
    obj_savenload > Keypad3 Pressed
    Code:
    ///saves our game
    
    username = get_string("Please enter a username",""); //This will let the user type in a username.
    
    save_game(); //This will execute our script, save_game
    
    obj_savenload >Keypad4 pressed
    Code:
    ///loads game
    load_game(); //This will execute our script, load_game
    
    show_message_async("Welcome"+string(username))
    Now for organization, let's keep all of our save events in save_game and all of our load events in load_game. Lets create a script called save_game
    Paste this in there
    Code:
    ///save_game()
    //This script will save our game
    if (file_exists("Save.sav")) file_delete("Save.sav");   //If the save file exists, delete it so we can overwrite it.
    
    ini_open("Save.sav");                                   //Opens the save file for reading and writing.
    
    //-----------------------------------------------------------------------------------------------------------------
    // Information to store in our save file
    //-----------------------------------------------------------------------------------------------------------------
    //Use ini_write_real(sectionID,Key,informationToBeStored) for numbers
    //Use ini_write_string(secionID,Key,informationToBeStored) for strings
    
    //Example:
    //ini_write_real("Player","Points",score)
    //ini_write_string("Player","Name",name)
    //ini_write_string("Useless Information","What",health)
    //
    //will come out as this in the save file.
    //
    //[Player]
    //Name = Paul
    //Points = 0
    //
    //[Useless Information]
    //What = 100
    
    ini_write_string("Player","UserID",username);           //Writes our username
    //-----------------------------------------------------------------------------------------------------------------
    ini_close();                                            //Closes our ini file for reading and writing.
    
    Create a script called load_game and paste this in there.
    Code:
    ///load_game()
    //This script will load our game.
    if (file_exists("Save.sav")) {
    
    ini_open("Save.sav");//Opens our file for reading and writing
    
    //-----------------------------------------------------------------------------------------------------------------
    // Information to load from our save file
    //-----------------------------------------------------------------------------------------------------------------
    //Use ini_read_real(sectionID,Key,defaultValue) for numbers
    //Use ini_read_string(secionID,Key,defaultValue) for strings
    
    //Example: ini_read_real("Player","Points",0) will return this from the save file.
    //[Player]
    //Points = (Whatever was saved for this key).
    
    username = ini_read_string("Player","UserID","");      //Reads our Username.
    
    //-----------------------------------------------------------------------------------------------------------------
    
    ini_close();
    }
    
    That's how you save and load information I guess Part 3 was answered already so I'll write up Part 4 with definitions if you need it.
     
    Last edited: Aug 11, 2016
    james_castrello and ThunkGames like this.
  6. Christian

    Christian Member

    Joined:
    Jun 21, 2016
    Posts:
    61
  7. Christian

    Christian Member

    Joined:
    Jun 21, 2016
    Posts:
    61
    Now another solution for your unique name list, is to have some sort of master server running on a static IP address or domain name. You can skip the PHP and MySWL and use data structures or arrays (I would recommend a data structure like ds_list over an array for this type of set up). Where you run checks on the name once. You can use the same method we used for the client to talk to the server, but instead of connecting to the server,they connect to the master server. The master server would need to always be connected to the internet and would always need to be running,but you can have the master save a log of users, run a check to see if the name exists in your ds_list. And if not, let them use that name. Then after they have the name, disconnect them from the server, and they don't have to touch it again. Then you use a save file mentioned above to store a variable like "has_this_user_signed_in_before = boolean", and "username = string". This way it's locally saved and it's unique.
     
  8. Christian

    Christian Member

    Joined:
    Jun 21, 2016
    Posts:
    61
    Part 3
    Create two buttons. One button should say "Join Game", one should say "Host Game".
    When the user chooses "Host Game", create the server object.

    When the user chooses "Join Game", create the client object. The host will need to tell the client which IP Address they need to connect to. The host also needs to open the port we used earlier and have it port forwarded.Once the host has a successful connection, the client can join a game by typing in their IP address. (You should use keyboard_string as opposed to get_string as get_string should be used for debugging purposes). Then just make sure obj_server, and obj_client have persistent enabled if you want your client and server to be able to go to different rooms and stay connected.
     
  9. you are a genius!
     
  10. im having several issues

    -whenever i try to create an object i get the "what ip to connect to" prompt
    -im kind of clueless as to how to implement this as well :p
     
  11. sepehrkiller

    sepehrkiller Member

    Joined:
    Aug 2, 2017
    Posts:
    4
    Ok . Thanks for your awesome explanation. That was a big help for me.
    But what if we want the server to send packets/buffers that are recived from a client to other clients

    i mean when i click on something in my client , it will send some packets to server and then server shows it. I want the server to send it to other clients or all of the clients so those clients can show it too
    thanks again

    ( Sorry my english is a little bad )
     
  12. Neptune

    Neptune Member

    Joined:
    Jun 21, 2016
    Posts:
    1,023
    @Christian I appreciate that response a lot! Cant wait to give in an in depth review, and try it out:D
     
  13. marasovec

    marasovec Member

    Joined:
    Sep 15, 2016
    Posts:
    319
    *removed*
     
    Last edited: Aug 30, 2018
    JohnSebek likes this.
  14. Marko03970

    Marko03970 Member

    Joined:
    Mar 25, 2017
    Posts:
    14
    Christian i dont know why but at me it works only lan not online. Can u help me??
     
  15. The-any-Key

    The-any-Key Member

    Joined:
    Feb 2, 2017
    Posts:
    1,521
    Depends on the network architecture. Sometimes I use only one player object and mimic this to a remote object on the other screen. Ex obj_player and obj_multiplayer_player. But in smaller games I tend to use the same object for all players. Ex obj_player only.

    When you say "forever" you mean for the current game you play? Ex you choose a name each time you start a game with a friend?

    Host and join is to create a host or join a host. Different codes is in play with this.

    Online connection is much harder to achieve than a LAN connection.
    If you want it to work online with a lobby you can use GMnet as a framework. Tips is to check:

    GMnet got a online lobby server written in java that you can put on a online VM with open ports. This enables a online lobby and also UDP Punch that can help players connect.
    It also got UPnP that can do auto-portforward on the router (if it allow it).

    In some cases UDP Punch and UPnP is enough to make the connection. But in some cases you need more like a TURN server.
     
  16. Nabil Kabour

    Nabil Kabour Member

    Joined:
    Aug 6, 2018
    Posts:
    92
    Hey thanks for the information. Can you please show us how to have the server talk to the clients and the clients talk to each other? Thanks.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice