• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Questions regarding ds_lists

Hey GMC! Big question:

So, I'm making an online 2 player game. So lots of people connected to the server, but when people want to play, they get paired with only one other player. To integrate matchmaking between two players, I send the server the username of every player that presses the play button. In the server, I set a variable when reading the buffer that contains the searching player's username. I call this variable playerSearchingUsername.

I want to insert that player's username via the variable into a ds_list I've created called playerSearching (and yes I made sure to destroy it at the end of the game). So I was doing ds_list_add(playerSearching, playerSearchingUsername);

So now that the player has been added I want to check if the list has 2 or more players in it:

if ds_list_size(playerSearching) >= 2 {
//do stuff
}

if there's not more than 2 people wanting to play, then I send a response 0 to the searching client, informing them that they have been unsuccessful in joining a game and then deletes their name from the list

If the condition is true, I now want to read/extract the data from the list and grab the first two players (the one's who have been waiting in the list the longest) and pair them together as player 1 and 2, sending them into the game room. And of course, the server will have to differentiate between multiple games as to who player 1 and 2 are. How do I do this? I know this is a tall order, but it would mean the world to me if I could get some help!

And if my logic on how to do anything in the above is faulty, please let me know! Thanks in advance! :D
 
@SSJCoder I caught this error slightly before you replied, I've fixed it now, thankfully! Thanks for the response!

I successfully paired the players together! But now I have another issue. If a player disconnects, it takes the other player back to the main menu, which works the first time. But for some reason it doesn't work the second time. So the second time and all times after the first cause the player who had their opponent disconnect just sit there with no opponent. So now I'm trying to look through my disconnect code to see what the problem is
 
@SSJCoder Thanks! When I get home I'll check up on it and ensure that they are

Hey, you wouldn't mind if I kept you updated on this issue just in case I need further help, would you? I definitely don't want to be a bother. I don't imagine I'll need much help, but can't be certain
 
S

SSJCoder

Guest
@SSJCoder Thanks! When I get home I'll check up on it and ensure that they are

Hey, you wouldn't mind if I kept you updated on this issue just in case I need further help, would you? I definitely don't want to be a bother. I don't imagine I'll need much help, but can't be certain
I don't mind, just sometimes I might not be here. Other than that, ask as you will. If the question's too much of a bother (complicated questions such as AI require a solid foundation, so building that takes time), I'll let you know and why
 
@SSJCoder Thanks again! Okay, so after I pair two clients together, if one leaves, the game goes back to the main menu for the client that did not leave (perfect!), and if I restart the client that left, and then pair them again, but this time have the client that left last time be the client that stays and the client that stayed last time leaves this time, it works perfectly again! The only time it does not work is if the client that leaves happens to leave the game a second time while paired with a client that has been left behind twice by the leaving client.

I don't know why this is happening, but I think it's that the client may be storing data on the remote client that isn't getting deleted for some reason. Either that, or the server isn't properly deleting the client the second time? Here's some of my disconnect code:

Starting the search for the error within my client's searching for game code:

Code:
if obj_player.searchingForGame = false {
    var playerSearchingUsername = global.name;
   
    buffer_seek(global.buffer, buffer_seek_start, 0);
    buffer_write(global.buffer, buffer_u8, 6); // request adding to ds_list gamesearch
    buffer_write(global.buffer, buffer_string, playerSearchingUsername);
    buffer_write(global.buffer, buffer_bool, true);
    network_send_packet(obj_controller.socket, global.buffer, buffer_tell(global.buffer));

    obj_player.searchingForGame = true;
    instance_create_depth(751, 0, -1, obj_matchmaking_screen);
}
The big one, my matchmaking code:

Code:
case 6: //Add player to searching list
        var playerSearchingUsername = buffer_read(buffer, buffer_string);
        var playerStillSearching = buffer_read(buffer, buffer_bool);
        var player1Username = "";
        var player2Username = "";
        var player1Socket = 0;
        var player2Socket = 0;
        var response = 0;
        
        ds_list_add(playersSearching, playerSearchingUsername);
        ds_list_add(playersSearchingSocket, socket);
        
        if ds_list_size(playersSearching) >= 2 {
            player1Username = ds_list_find_value(playersSearching, ds_list_size(playersSearching)-1);
            ds_list_delete(playersSearching, ds_list_size(playersSearching)-1);
            player2Username = ds_list_find_value(playersSearching, ds_list_size(playersSearching)-1);
            ds_list_delete(playersSearching, ds_list_size(playersSearching)-1);
    
            player1Socket = ds_list_find_value(playersSearchingSocket, ds_list_size(playersSearchingSocket)-1);
            ds_list_delete(playersSearchingSocket, ds_list_size(playersSearchingSocket)-1);
            player2Socket = ds_list_find_value(playersSearchingSocket, ds_list_size(playersSearchingSocket)-1);
            ds_list_delete(playersSearchingSocket, ds_list_size(playersSearchingSocket)-1);
            
            if playerSearchingUsername = player1Username || playerSearchingUsername = player2Username {
                response = 1;
            }
        } else {
            if playerStillSearching = false {
                var playerUsernameInfo = ds_list_find_index(playersSearching, playerSearchingUsername);
                var playerSocketInfo = ds_list_find_index(playersSearching, socket);
                
                ds_list_delete(playersSearching, playerUsernameInfo);
                ds_list_delete(playersSearching, playerSocketInfo);
            }
        }
        
        //send response to client 1
        buffer_seek(global.buffer, buffer_seek_start, 0);
        buffer_write(global.buffer, buffer_u8, 6);
        buffer_write(global.buffer, buffer_u8, player2Socket);
        buffer_write(global.buffer, buffer_string, player2Username);
        buffer_write(global.buffer, buffer_u32, response);
        network_send_packet(player1Socket, global.buffer, buffer_tell(global.buffer));
        
        //send response to client 2
        buffer_seek(global.buffer, buffer_seek_start, 0);
        buffer_write(global.buffer, buffer_u8, 6);
        buffer_write(global.buffer, buffer_u8, player1Socket);
        buffer_write(global.buffer, buffer_string, player1Username);
        buffer_write(global.buffer, buffer_u32, response);
        network_send_packet(player2Socket, global.buffer, buffer_tell(global.buffer));
    break;
Hopping over to the client to read the response our server sent:

Code:
case 6: //get matchmaking information
        var p2Id = buffer_read(buffer, buffer_u8);
        var p2Name = buffer_read(buffer, buffer_string);
        var response = buffer_read(buffer, buffer_u8);
        var playerSearchingUsername = global.name;
        var matchmakingsearch = obj_player.searchingForGame;
        
        if matchmakingsearch = true {
            switch(response) {
                case 0: // continue searching
                    
                    buffer_seek(global.buffer, buffer_seek_start, 0);
                    buffer_write(global.buffer, buffer_u8, 6); // request adding to ds_list gamesearch
                    buffer_write(global.buffer, buffer_string, playerSearchingUsername);
                    buffer_write(global.buffer, buffer_bool, true);
                    network_send_packet(obj_controller.socket, global.buffer, buffer_tell(global.buffer));
                break;
                
                case 1: // match found!
                
                    obj_remote_player.remotePlayerName = p2Name;
                    obj_remote_player.remotePlayerId = p2Id;
                    
                    obj_player.inGame = true;
                    obj_player.searchingForGame = false;
                    room_goto(rm_game);
                    
                break;
            }
        } else {
            buffer_seek(global.buffer, buffer_seek_start, 0);
            buffer_write(global.buffer, buffer_u8, 6); // request adding to ds_list gamesearch
            buffer_write(global.buffer, buffer_string, playerSearchingUsername);
            buffer_write(global.buffer, buffer_bool, false);
            network_send_packet(obj_controller.socket, global.buffer, buffer_tell(global.buffer));
        }
            
    break;
Finished with the matchmaking stuff, now back to the server to the obj_controller.async_networking disconnect event:

Code:
case network_type_disconnect:
        var socket = ds_map_find_value(async_load, "socket");
        var index = ds_list_find_index(global.players, socket);
        
        if (index != -1) {
            ds_list_delete(global.players, index);
        }
        
        //get info on player leaving
        var playerLeavingId = 0;
        
        //var pIdl = playerLeavingId;
        
        with (obj_player) {
            if (playerSocket == socket) {
                playerLeavingId = playerIdentifier;
            }
        }
        
        //notify players
        for (var i = 0; i < ds_list_size(global.players);i++) {
            var storedPlayerSocket = ds_list_find_value(global.players, i);
            
            //send player id
            buffer_seek(global.buffer, buffer_seek_start, 0);
            buffer_write(global.buffer, buffer_u8, 5);
            buffer_write(global.buffer, buffer_u32, playerLeavingId);
            network_send_packet(storedPlayerSocket, global.buffer, buffer_tell(global.buffer));
        }
        
        with obj_player {
            if (playerIdentifier == playerLeavingId) {
                instance_destroy();
            }
        }
        
        with (obj_message) {
            instance_destroy();   
        }

        var notification = instance_create_depth(0, 0, 0, obj_message);
        notification.message = "A player has disconnected";
    break;
Now that we've sent the buffer containing the leaving player's ID to all players (it should be at least!), we'll hop over to a script in the client that is reading these buffers. If a client is currently playing against the player who's ID is = to the leaving player's ID, we're going to destroy the obj_remote_player and then head back to the main menu

Reading the buffers sent to our clients:

Code:
case 5: //get playerId (leaving)
        var pId = buffer_read(buffer, buffer_u32);
        
        with (obj_remote_player) {
            if (remotePlayerId == pId) {
                instance_destroy();
                room_goto(rm_main_menu);
            }
        }
    break;
So this works all well and good the first time. But why not subsequent times?

NOTE: My obj_remote_player is persistent and is created in the main menu. I figure that this is not the issue since the remote_player's variables should reset when the game is entered. But the obj_remote_player does not seem to get deleted the second time. Even though the remote player's client has disconnected. Any ideas? Sorry about the long message, I just really hope I can get help in getting to the bottom of this and I know that being specific can help find the little errors that trip me up :) Anyways, thanks for taking the time to help!
 
S

SSJCoder

Guest
Code:
var playerUsernameInfo = ds_list_find_index(playersSearching, playerSearchingUsername);
var playerSocketInfo = ds_list_find_index( *** playersSearching, socket);

ds_list_delete(playersSearching, playerUsernameInfo);
ds_list_delete( *** playersSearching, playerSocketInfo);
You're deleting from the wrong list, "playersSearching", rather than "playersSearchingSocket"


Code:
*** ds_list_add(playersSearching, playerSearchingUsername);
*** ds_list_add(playersSearchingSocket, socket);
You're adding them to the list every time they send a request, rather than checking if they already are on the queue


Code:
player1Username = ds_list_find_value(playersSearching, *** ds_list_size(playersSearching)-1);
You're matching up the players who last joined the queue, rather than the first.
If 100 players join, the ones who joined last (most recently) will be matched up first, and the person at the beginning of the queue will be left behind until all other players have found a match.

Notice that ds_list_add() adds to the end, rather than the beginning.

A lot of your code should be simplified, there are plenty of things rewritten many times that don't need to be, which causes your code to be harder to work with.


Here's an example
Code:
if obj_player.searchingForGame = false {
   var playerSearchingUsername = global.name;
 
   buffer_seek(global.buffer, buffer_seek_start, 0);
   buffer_write(global.buffer, buffer_u8, 6); // request adding to ds_list gamesearch
   buffer_write(global.buffer, buffer_string, playerSearchingUsername);
   buffer_write(global.buffer, buffer_bool, true);
   network_send_packet(obj_controller.socket, global.buffer, buffer_tell(global.buffer));

   obj_player.searchingForGame = true;
   instance_create_depth(751, 0, -1, obj_matchmaking_screen);
}

Could be rewritten:
Code:
if ( obj_player.searchingForGame == false )
{
    // request a game (note that I put that grouped code into a function)
    sendGameRequest();
 
    // declare we're searching for a game
    obj_player.searchingForGame = true;
 
    // create match making screen
    instance_create_depth( 751, 0, -1, obj_matchmaking_screen );
}

(script) sendGameRequest
Code:
// sendGameRequest ();

// write to buffer
buffer_seek( global.buffer, buffer_seek_start, 0 );
buffer_write( global.buffer, buffer_u8, 6 );
buffer_write( global.buffer, buffer_string, global.name );
buffer_write( global.buffer, buffer_bool, true );

// send buffer
network_send_packet( obj_controller.socket, global.buffer, buffer_tell( global.buffer ) );
Larger projects need you to always simplify code, which makes it a lot easier to build onto them and to debug problems you're having.
 
Last edited:
@SSJCoder Okay, still not functioning properly. Update on something interesting that happens now though!

In my client, when it receives news of a socket disconnecting, I decided to write a notification message.

So here's the code.

Code:
case 5: //get playerId (leaving)
        var pId = buffer_read(buffer, buffer_u32);
        
        scr_showNotification("Player has gone offline");
        
        if (obj_remote_player.remotePlayerId == pId) {
            with (obj_remote_player) {
                instance_destroy();
            }
            room_goto(rm_main_menu);
        }
    break;
So, the first time, when the game works properly, obj_remote_player is destroyed and then the room is changed to the main menu. However, when I do it the second time, the object is not destroyed and the room does not change. The message "Player has gone offline" does show up, however. SO this means that the var pId does not equal the remotePlayerId the second time, but it does the first time.

Which means, my issue MUST be an issue with the following code, the one sending the buffer_u32 to the client, correct?

Here's the code:

Code:
case network_type_disconnect:
        var socket = ds_map_find_value(async_load, "socket");
        var index = ds_list_find_index(global.players, socket);
        
        if (index != -1) {
            ds_list_delete(global.players, index);
        }
        
        //get info on player leaving
        var playerLeavingId = 0;
        
        //var pIdl = playerLeavingId;
        
        with (obj_player) {
            if (playerSocket == socket) {
                playerLeavingId = playerIdentifier;
            }
        }
        
        //notify players (I THINK THE ERROR IS IN HERE)
        for (var i = 0; i < ds_list_size(global.players);i++) {
            var storedPlayerSocket = ds_list_find_value(global.players, i);
            
            //send player id (THIS IS WHERE I SEND THE ID TO THE CLIENT)
            buffer_seek(global.buffer, buffer_seek_start, 0);
            buffer_write(global.buffer, buffer_u8, 5);
            buffer_write(global.buffer, buffer_u32, playerLeavingId);
            network_send_packet(storedPlayerSocket, global.buffer, buffer_tell(global.buffer));
        }
        
        with obj_player {
            if (playerIdentifier == playerLeavingId) {
                instance_destroy();
            }
        }
        
        with (obj_message) {
            instance_destroy();   
        }

        var notification = instance_create_depth(0, 0, 0, obj_message);
        notification.message = "A player has disconnected";
    break;
If needed, I can send info on the ds_list global.players. Also, in the server, obj_player is created each time a client connects and their playerSocket is set to the socket when it is created. Thus when someone disconnects, we check to see if the obj_player.playerSocket = the current socket leaving the game. If so, we set the playerLeavingId to the playerIdentifier. The playerIdentifier is simply the socket count, which is increased each time someone connects. Let me know if I need to clarify anything or send more information/code. Still can't find the problem, but I think we're narrowing it down
 
S

SSJCoder

Guest
Still can't find the problem, but I think we're narrowing it down
Note that I didn't give you a solution, I just pointed out a few errors, and problems with your algorithm/code (which may or may not have solved your problem).

Code:
*** ds_list_add(playersSearching, playerSearchingUsername);
*** ds_list_add(playersSearchingSocket, socket);
You're adding them to the list every time they send a request, rather than checking if they already are on the queue
A lot of your code should be simplified, there are plenty of things rewritten many times that don't need to be, which causes your code to be harder to work with.
The first abstraction that needs to happen here is the networking. I'll dig up a system for you, to show you the basics.
 
@SSJCoder Hey I figured out the problem! It works now! Unfortunately, I cannot seem to figure out why my draw GUI event isn't working... I'll look into it because I don't see any reason why it shouldn't
 
S

SSJCoder

Guest
Here's a simplified example of a network interface, where connections are handled pretty well. It's not designed to have more than 1 client connect at a time, and it always connects to "127.0.0.1".

It's a good start if you want to get into networking =)

https://ufile.io/0c4wl
(GMS 2 Project File)
 
Last edited:
@SSJCoder I would really appreciate that! :D thanks! If this game gets far enough to actually be released to the public, I'm definitely going to put your name in the credits for the help you've offered :)
 
S

SSJCoder

Guest
@SSJCoder I would really appreciate that! :D thanks! If this game gets far enough to actually be released to the public, I'm definitely going to put your name in the credits for the help you've offered :)
Alright, I'll do it ASAP (since the jam is on right away, and ends Sunday, I'll build it for you if I can before then)
 
@SSJCoder Also, about getting over that client limit, how would you go about doing that? Cause the limit is only 1,014, which is small since I'm looking to make a mobile app capable of holding hopefully a few thousand just in case. Not expecting that many players tbh, but it's better to be prepared

Plus if I can learn how, then that would be a great knowledge to have for future use
 
@SSJCoder I meant the prompt for the jam. Usually game jams have a theme or prompt to encourage devs to think outside the box, not all do though, so I can understand the confusion :)

I basically was asking what kind of game are you making for the jam?
 
S

SSJCoder

Guest
@SSJCoder I meant the prompt for the jam. Usually game jams have a theme or prompt to encourage devs to think outside the box, not all do though, so I can understand the confusion :)

I basically was asking what kind of game are you making for the jam?
The theme is 'Two Sides of the Same Coin', and the project I'm making involves a warrior who fights during the day/night, and has powers based on the light/darkness.
 
Top