GML [Solved] Buffer error upon client disconnecting

X

XirmiX

Guest
I've come to understand networking a bit better, thanks to one particularly helpful user (Tsa05). Since, I'm not sure whether the previous topic concerning networking would still be relevant, I thought I'd create this to tackle this specific issue specifically. Call me impatient, but I'd rather be trying to do something and trying to get any help I can rather than sit back and wait for it to arrive. I've been able to get as far as getting the client connecting to the server, or so that's what I think. I found out that I had not been connecting to the server at all this entire time, because buffer_send_type was not a defined variable. Then I defined the variable... and I STILL got the exact same error:
FATAL ERROR in
action number 1
of Async Event: Networking
for object obj_connection:

buffer_write argument 1 incorrect type (5) expecting a String (YYGS)
at gml_Object_obj_connection_NetworkingEvent_1 (line 38) - buffer_write(send_buffer, buffer_string, client_id); //only sending ID, because deleting ID key will subsequently delete player username

The error occurs on the server side once I switch off the client. The server tries to read a string, but instead reads a number. That doesn't make sense, because if you look at my code, you can see that I'm sending a string to the server and it's storing that string, and when the server calls back that string to write it to other clients, it should be reading a string, not a number 5, or whatever it is the error's popping up. I should mention that the error used to have 0 instead of 5 in it when my server code was sending buffers through connection code. The error was still the same in other regard, however, and happened with this exact buffer_write function.

Client Create event code
Code:
///Initializing connection socket
{
joined = false;
playerjoined = false;
playerleft = false;

message = "";

x_previous = x+1;
y_previous = y+1;
x_current = x;
y_current = y;
send_data = 0;

port = 50000;
IP = "127.0.0.1";
socket_plug = network_create_socket(network_socket_tcp);
//send_buffer = buffer_create(256, buffer_fixed, 1);
network_connect_raw(socket_plug, IP, port);
client_amount = 0;
client_data = ds_map_create();
instance_data = ds_map_create();

myclient_id = "1234567890123456";//ini_read_string("Profile Data", "password", noone);
myplayer_username = "Scorpion";

buffer_type_send = 1;
}
Client Networking event code
Code:
type = async_load[? "type"];

switch(type)
{
    //Sending data upon connection
    case network_type_data:
    switch(buffer_type_send)
    {
        //sending buffer type 1 (player enter)
        case SEND_USER_ID:
            //create a buffer with which to send information
            var send_buffer = buffer_create(256, buffer_fixed, 1);
            var buffer_type_write = SEND_USER_ID;
           
            buffer_write(send_buffer, buffer_u8, buffer_type_write);
            buffer_write(send_buffer, buffer_string, myclient_id);
            buffer_write(send_buffer, buffer_string, myplayer_username);
           
            network_send_raw(socket_plug, buffer_send, buffer_sizeof(buffer_send));
           
            message = myplayer_username + " joined";
            joined = true;
           
            my_tank = instance_create(irandom(room_width), irandom(room_height), obj_player);
           
            buffer_delete(send_buffer);
            buffer_type_send = 0;
        break;
       
        //nothing to send when for buffer type 2 (disconnecting), that would be stupid
       
        //sending buffer type 3 (equipment)
        case SEND_EQUIPMENT_DATA:
            var buffer_type_write = SEND_EQUIPMENT_DATA;
        break;
       
        //sending buffer type 4 (skins)
        case SEND_SKINS_DATA:
            var buffer_type_write = SEND_COORDINATE_DATA;
        break;
       
        //sending buffer type 5 (coordinates)
        case SEND_COORDINATE_DATA:
            var buffer_type_write = SEND_COORDINATE_DATA;
            //create a buffer with which to send information
            var send_buffer = buffer_create(256, buffer_fixed, 1);
           
            buffer_write(send_buffer, buffer_u8, buffer_type_write);
            buffer_write(send_buffer, buffer_f32, my_tank.x);
            buffer_write(send_buffer, buffer_f32, my_tank.y);
            network_send_raw(socket_plug, buffer_send, buffer_sizeof(buffer_send));
           
            buffer_delete(send_buffer);
        break;
       
        //sending buffer type 6 (angles)
        case SEND_ANGLE_DATA:
            var buffer_type_write = SEND_ANGLE_DATA;
            //create a buffer with which to send information
            var send_buffer = buffer_create(256, buffer_fixed, 1);
           
            buffer_write(send_buffer, buffer_u8, buffer_type_write);
            buffer_write(send_buffer, buffer_f32, my_tank.direction);
            network_send_raw(socket_plug, buffer_send, buffer_sizeof(buffer_send));
            buffer_delete(send_buffer);
        break;
    }
   
    buffer = async_load[? "buffer"];
    buffer_seek(buffer, buffer_seek_start, 0);
    //Identify buffer type
    var buffer_type_read = buffer_read(buffer, buffer_u8);
    switch(buffer_type_read)
    {
       
        //receiving buffer type 1 (player enter)
        case ADD_USER_ID:
            var client_id = buffer_read(buffer, buffer_string);
            var player_username = buffer_read(buffer, buffer_string);
            client_amount = buffer_read(buffer, buffer_u8);
           
            client_data[? string(client_id)] = player_username;
           
            var player_name = ds_map_find_value(client_data, client_id);
           
            var player_joined_instance = instance_create(irandom(room_width), irandom(room_height), obj_player_other);
            ds_map_add(instance_data, client_id, player_joined_instance.id);
            message = player_name + " joined";
            playerleft = true;
        break;
       
        //receiving buffer type 2 (player exit)
        case REMOVE_USER_ID:
            //reads buffers
            var client_id = buffer_read(buffer, buffer_string);
            client_amount= buffer_read(buffer, buffer_u8);
           
            var player_name = ds_map_find_value(client_data, client_id);
            message = player_name + " left";
            playerleft = true;
            //Deletes client ID from the client data list, subsequently, the client username, since client's id is its key
            ds_map_delete(client_data, client_id);
        break;
       
        //receiving buffer type 4 (skins)
        case 4:
           
        break;
       
        //receiving buffer type 5 (coordinates)
        case 5:
            var client_id = buffer_read(buffer, buffer_string);
            var xx = buffer_read(buffer, buffer_f32);
            var yy = buffer_read(buffer, buffer_f32);
            var player_instance_current = ds_map_find_value(instance_data, client_id);
            player_instance_current.x = xx;
            player_instance_current.y = yy;
        break;
       
        //receiving buffer type 6 (angles)
        case 6:
            var client_id = buffer_read(buffer, buffer_string);
            var rotation = buffer_read(buffer, buffer_f32);
            var player_instance_current = ds_map_find_value(instance_data, client_id);
            player_instance_current.direction = rotation;
        break;
       
        //unidentified case
        case UNIDENTIFIED_BUFFER:
            //show_error("A buffer received from the server was unidentified.", true);
        break;
    }
    break;
}
Client Draw event code
Code:
draw_set_color(c_white);
draw_text(16, 16, client_amount);

if joined == true
{
    draw_text(16, 2048, message);
    alarm[0] = 200;
    joined = false;
}
if playerjoined == true
{
    draw_text(16, 2048, message);
    alarm[0] = 200;
    playerjoined = false;
}
if playerleft == true
{
    draw_text(16, 2048, message);
    alarm[0] = 200;
    playerleft = false;
}
Alarm 0 event code
Code:
message = "";

Server Create event code
Code:
//declaring the type of connection (tcp)
server_type = network_socket_tcp;
//port number
port = 50000;
//max amount of clients that can connect to the server
max_players = 8;
//generating a server
server = network_create_server_raw(server_type, port, max_players);

//ds_list to hold a list of client sockets into
socket_map = ds_map_create();
//creating an integer variable, which will define how many players have joined
client_amount = ds_map_size(socket_map);
//ds_map in which to hold 
client_data = ds_map_create();

joined = false;
message = "";
Server Networking event code
Code:
///Networking
type = async_load[? "type"];

//Switch statement, because needs a list of connection types... yeah
switch(type)
{
    //case for the client connecting
    case network_type_connect:
        //Identify the connecting socket
        client_socket_current = async_load[? "socket"];
       
        //Updating the amount of players that are connected in the client amount variable
        client_amount = ds_map_size(socket_map);
       
        joined = true;
    break;

    //case for the client disconnecting
    case network_type_disconnect:
        //Identify the disconnecting socket
        client_socket_current = async_load[? "socket"];
       
        //Contained in a temporary variable, from the socket map, the ID number (a string) of the disconnecting socket
        var client_id = ds_map_find_value(socket_map, client_socket_current);
       
        //Updating the amount of players that are connected in the client amount variable
        //client_amount = ds_map_size(socket_map);
        //A temporary variable, which is needed to send amount of players connected, due to a disconnect, to other clients. Reason for this variable is quite complex...
        var client_amount_tosend = ds_map_size(socket_map) - 1;
       
        var buffer_type_write = 2;
       
        //create a buffer with which to send information
        var send_buffer = buffer_create(256, buffer_fixed, 1);
       
        //writing data to send to buffer
        buffer_write(send_buffer, buffer_u8, buffer_type_write);
        buffer_write(send_buffer, buffer_string, client_id); //only sending ID, because deleting ID key will subsequently delete player username
        buffer_write(send_buffer, buffer_u8, client_amount_tosend);
       
        //If there are now 2 or more users connected, send data about the new client to other clients
        if (ds_map_size(socket_map) >= 2)
        {
            for(i = 0; i < ds_map_size(socket_map); i++)
            {
                socket_in_use = ds_map_find_value(socket_map, i);
                network_send_raw(socket_in_use, send_buffer, buffer_sizeof(send_buffer));
            }
        }
       
        //In the client_data map, delete from client data the disconnecting client's ID along with their name (ID being the key and username being its string value)
        ds_map_delete(client_data, client_id);
       
        //Remove the disconnecting player socket from the socket list
        ds_map_delete(socket_map, client_socket_current);
       
        //Updating the amount of players that are connected in the client amount variable
        client_amount = ds_list_size(socket_map);
       
        buffer_delete(send_buffer);
    break;

    //case for receiving and sending data
    case network_type_data:
        //Identify the sender client
        client_socket_current = async_load[? "id"];
        //Identify buffer sent
        buffer = async_load[? "buffer"];
       
        //Read the buffers sent by the joining client
        buffer_seek(buffer, buffer_seek_start, 0);
        var buffer_type_read = buffer_read(buffer, buffer_u8); //Read's the initial integer sent, identifying the buffer type
        switch(buffer_type_read)
        {
            //sending buffer type 1 (player enter)
            case 1:
                var client_id = buffer_read(buffer, buffer_string); //Client's ID number
                var player_username = buffer_read(buffer, buffer_string); //Client's username
               
                //Add new player socket to the socket list
                ds_map_add(socket_map, client_socket_current, client_id);
               
                //In the client_data map, create a new key with the ID of the player, which will hold onto the user's username
                client_data[? string(client_id)] = player_username;
               
                //Defining a temporary variable for the buffer number
                var buffer_type_write = 1;
               
                //create a buffer with which to send information
                send_buffer = buffer_create(256, buffer_fixed, 1);
               
                //writing data to send to buffer
                buffer_write(send_buffer, buffer_u8, buffer_type_write);
                buffer_write(send_buffer, buffer_string, client_id);
                buffer_write(send_buffer, buffer_string, player_username);
                buffer_write(send_buffer, buffer_u8, client_amount);
               
                //If there are now 2 or more users connected, send data about the new client to other clients
                if (ds_map_size(socket_map) >= 2)
                {
                    for(i = 0; i < ds_map_size(socket_map); i++)
                    {
                        socket_in_use = ds_map_find_value(socket_map, i);
                        network_send_raw(socket_in_use, send_buffer, buffer_sizeof(send_buffer));
                    }
                }
               
                buffer_delete(send_buffer);
            break;
           
            //buffer type 2 already set to be sending upon disconnection
           
            //sending buffer type 3 (equipment)
            case 3:
                var turret = buffer_read(buffer, buffer_u8);
                var hull = buffer_read(buffer, buffer_u8);
            break;
           
            //sending buffer type 4 (skins)
            case 4:
                var turret = buffer_read(buffer, buffer_u8);
                var hull = buffer_read(buffer, buffer_u8);
            break;
           
            //sending buffer type 5 (coordinates)
            case 5:
                var client_id = ds_map_find_value(socket_map, client_socket_current); 
               
                var xx = buffer_read(buffer, buffer_f32);
                var yy = buffer_read(buffer, buffer_f32);
               
                //create a buffer with which to send information
                var send_buffer = buffer_create(256, buffer_fixed, 1);
               
                buffer_write(send_buffer, buffer_string, client_id);
                buffer_write(send_buffer, buffer_f32, xx);
                buffer_write(send_buffer, buffer_f32, yy);
                if (ds_map_size(socket_map) >= 2)
                {
                    for(i = 0; i < ds_map_size(socket_map); i++)
                    {
                        socket_in_use = ds_map_find_value(socket_map, i);
                        if(i != client_socket_current)
                        {
                            network_send_raw(socket_in_use, send_buffer, buffer_sizeof(send_buffer));
                        }
                    }
                }
               
                buffer_delete(send_buffer);
            break;
           
            //sending buffer type 6 (angles)
            case 6:
                var client_id = ds_map_find_value(socket_map, client_socket_current);
               
                var rotation = buffer_read(buffer, buffer_f32);
               
                //create a buffer with which to send information
                var send_buffer = buffer_create(256, buffer_fixed, 1);
               
                buffer_write(send_buffer, buffer_string, client_id);
                buffer_write(send_buffer, buffer_f32, rotation);
                if (ds_map_size(socket_map) >= 2)
                {
                    for(i = 0; i < ds_map_size(socket_map); i++)
                    {
                        socket_in_use = ds_map_find_value(socket_map, i);
                        if(i != client_socket_current)
                        {
                            network_send_raw(socket_in_use, send_buffer, buffer_sizeof(send_buffer));
                        }
                    }
                }
               
                buffer_delete(send_buffer);
            break;
        }
    break;
   
}
Server Draw event code
Code:
draw_set_colour(c_white);

if joined == true
{
    message = "player joined";
    draw_text(16, 16, message);
    joined = false;
}

What is going on? What am I doing wrong? I thought I comprehended what I was doing, but apparently there's something wrong that creates this error every time the client disconnects, and I'm not getting any of my text messages displayed at all! And for some reason my player object is not being spawned in either.
 

CloseRange

Member
Code:
        //writing data to send to buffer
        buffer_write(send_buffer, buffer_u8, buffer_type_write);
        buffer_write(send_buffer, buffer_string, client_id); //only sending ID, because deleting ID key will subsequently delete player username
        buffer_write(send_buffer, buffer_u8, client_amount_tosend);
You this section of code under the Server Networking Event (case network_type_disconnect)

for the client_id are you sure that it should be a string buffer? try using a u16 instead because thats where the error is occuring
 
X

XirmiX

Guest
Code:
        //writing data to send to buffer
        buffer_write(send_buffer, buffer_u8, buffer_type_write);
        buffer_write(send_buffer, buffer_string, client_id); //only sending ID, because deleting ID key will subsequently delete player username
        buffer_write(send_buffer, buffer_u8, client_amount_tosend);
You this section of code under the Server Networking Event (case network_type_disconnect)

for the client_id are you sure that it should be a string buffer? try using a u16 instead because thats where the error is occuring
Yes, that's the weird thing which I don't understand; if you check the create event of the client, you have this:
Code:
myclient_id = "1234567890123456";
that creates a variable, which will store your client ID... as a STRING. It is created as a string and it is sent as a string, so when it's saved into a ds map, why is it that when reading it, it's no longer a string? 16-digit number can't be a u16, it would beed to be u64 to stand as an integer... so I'd rather have it as a string. Anyway, the string is then sent as a string to the server:
Code:
//sending buffer type 1 (player enter)
        case SEND_USER_ID:
            //create a buffer with which to send information
            var send_buffer = buffer_create(256, buffer_fixed, 1);
            var buffer_type_write = SEND_USER_ID;
           
            buffer_write(send_buffer, buffer_u8, buffer_type_write);
            buffer_write(send_buffer, buffer_string, myclient_id);
            buffer_write(send_buffer, buffer_string, myplayer_username);
           
            network_send_raw(socket_plug, buffer_send, buffer_sizeof(buffer_send));
           
            message = myplayer_username + " joined";
            joined = true;
           
            my_tank = instance_create(irandom(room_width), irandom(room_height), obj_player);
           
            buffer_delete(send_buffer);
            buffer_type_send = 0;
        break;
It is then stored by the server, as a string, into a DS map:
Code:
//case for receiving and sending data
   case network_type_data:
       //Identify the sender client
       client_socket_current = async_load[? "id"];
       //Identify buffer sent
       buffer = async_load[? "buffer"];
     
       //Read the buffers sent by the joining client
       buffer_seek(buffer, buffer_seek_start, 0);
       var buffer_type_read = buffer_read(buffer, buffer_u8); //Read's the initial integer sent, identifying the buffer type
       switch(buffer_type_read)
       {
           //sending buffer type 1 (player enter)
           case 1:
               var client_id = buffer_read(buffer, buffer_string); //Client's ID number
               var player_username = buffer_read(buffer, buffer_string); //Client's username
             
               //Add new player socket to the socket list
               ds_map_add(socket_map, client_socket_current, client_id);
             
               //In the client_data map, create a new key with the ID of the player, which will hold onto the user's username
               client_data[? string(client_id)] = player_username;
And once, say, the player disconnects, the server reads it and then writes it as a string to be sent off to other players:
Code:
//case for the client disconnecting
   case network_type_disconnect:
       //Identify the disconnecting socket
       client_socket_current = async_load[? "socket"];
     
       //Contained in a temporary variable, from the socket map, the ID number (a string) of the disconnecting socket
       var client_id = ds_map_find_value(socket_map, client_socket_current);
     
       //Updating the amount of players that are connected in the client amount variable
       //client_amount = ds_map_size(socket_map);
       //A temporary variable, which is needed to send amount of players connected, due to a disconnect, to other clients. Reason for this variable is quite complex...
       var client_amount_tosend = ds_map_size(socket_map) - 1;
     
       var buffer_type_write = 2;
     
       //create a buffer with which to send information
       var send_buffer = buffer_create(256, buffer_fixed, 1);
     
       //writing data to send to buffer
       buffer_write(send_buffer, buffer_u8, buffer_type_write);
       buffer_write(send_buffer, buffer_string, client_id); //only sending ID, because deleting ID key will subsequently delete player username
       buffer_write(send_buffer, buffer_u8, client_amount_tosend);
     
       //If there are now 2 or more users connected, send data about the new client to other clients
       if (ds_map_size(socket_map) >= 2)
       {
           for(i = 0; i < ds_map_size(socket_map); i++)
           {
               socket_in_use = ds_map_find_value(socket_map, i);
               network_send_raw(socket_in_use, send_buffer, buffer_sizeof(send_buffer));
           }
       }
     
       //In the client_data map, delete from client data the disconnecting client's ID along with their name (ID being the key and username being its string value)
       ds_map_delete(client_data, client_id);
     
       //Remove the disconnecting player socket from the socket list
       ds_map_delete(socket_map, client_socket_current);
     
       //Updating the amount of players that are connected in the client amount variable
       client_amount = ds_list_size(socket_map);
     
       buffer_delete(send_buffer);
Does it need to be read as a string as well? I mean, it stores the value within a key of the respective map as a string, so wouldn't it instantly know to read it as a string as well?
 

PNelly

Member
Since your client and server are both written in GMS I'd recommend switching from the "raw" tcp functions to the "regular" functions. The raw functions might split or combine packets at a lower level without telling you about it, and when your receive them you have to detect for it and re-split or reassemble them accordingly. Probably not the cause of your problem though.

Getting the new client id, storing it, and retrieving it don't seem to point to anything obvious. Have you checked to see what's actually in there? The debugger or a show_debug_message on the value of client_id before you try to write it into the buffer will probably yield a clue about what's going on.
 
X

XirmiX

Guest
Since your client and server are both written in GMS I'd recommend switching from the "raw" tcp functions to the "regular" functions. The raw functions might split or combine packets at a lower level without telling you about it, and when your receive them you have to detect for it and re-split or reassemble them accordingly. Probably not the cause of your problem though.

Getting the new client id, storing it, and retrieving it don't seem to point to anything obvious. Have you checked to see what's actually in there? The debugger or a show_debug_message on the value of client_id before you try to write it into the buffer will probably yield a clue about what's going on.
Placing anything anywhere won't display anything anywhere for some reason. Like, if I place:
Code:
var mycurrent_id = ds_map_find_value(socket_map, client_socket_current);
            show_debug_message(current_id);
Code:
//sending buffer type 1 (player enter)
        case SEND_USER_ID:
            //create a buffer with which to send information
            var send_buffer = buffer_create(256, buffer_fixed, 1);
            var buffer_type_write = SEND_USER_ID;
        
            buffer_write(send_buffer, buffer_u8, buffer_type_write);
            buffer_write(send_buffer, buffer_string, myclient_id);
            buffer_write(send_buffer, buffer_string, myplayer_username);
        
            var mycurrent_id = ds_map_find_value(socket_map, client_socket_current);
            show_debug_message(current_id);
        
            network_send_raw(socket_plug, buffer_send, buffer_sizeof(buffer_send));
        
            message = myplayer_username + " joined";
            joined = true;
        
            my_tank = instance_create(irandom(room_width), irandom(room_height), obj_player);
        
            buffer_delete(send_buffer);
            buffer_type_send = 0;
        break;
And the same is the case for the server.

It won't do anything in terms of logs. As for running things in the debug mode... I run my server, the server window opens and the debugger opens. As for when I run the client... the client doesn't open up AT ALL! It just opens up the server window and that's it... Only after like a minute or so the client actually opens up, but not with its own debugger window, not sure if it's supposed to happen. I have not got much knowledge over the debugger, though I've looked at some tutorials, but because when I'm trying to do what's in the tutorials doesn't happen for me when I do the same thing, I just get confused. Running the server and client in debugger mode stop any errors from occurring... weird...

At this point, I'm at a loss for what's happening completely. I don't know what is happening and why and it just doesn't make sense. But apparently, according to the logs (if logs are supposed to show the information that is being sent and received), nothing is being send or received, you just get notified of joining and leaving and that's it, although I think the logs aren't supposed to show any of that, which for me is frustrating.

Moreover, the server shows that it pauses while you tab it out. I know there's some way to change this.

Apparently this happens as well (server log):
Error (0x 2740): Can't bind port
Setting SO_REUSEADDR and retrying...Error (0x 271d): Can't bind port even with SO_REUSEADDR
cannot bind port Total memory used = 378200(0x0005c558) bytes

The port I'm using is 50000. What, is that occupied now for some reason? How am I supposed to know that and how am I supposed to tell the game to choose a free port... actually this won't do me any good unless I have a website and stuff for this...

This just doesn't make any sense anymore. I am at a loss.

As for the raw thing, I set it to raw because I plan to use a web site for getting server info on a website once I get to it.

Errors aren't popping up anymore and I don't know why, since I set everything back to as it was, but the bugs of player character not spawning in are still there, despite the fact that I have everything in for it to spawn correctly.
 

PNelly

Member
Code:
var mycurrent_id = ds_map_find_value(socket_map, client_socket_current);
           show_debug_message(current_id);
Shouldn't that be
Code:
show_debug_message( string(mycurrent_id));
I don't see the variable "current_id" anywhere else.

The "Can't bind port" error is most likely to be caused by accidentally trying to bind multiple sockets to the same port. Perhaps you have your server and client both trying to use 50000? Or the port wasn't properly freed up when it should have been, you have network_destroy() in place for the server and client shutdown?

As for the raw thing, I set it to raw because I plan to use a web site for getting server info on a website once I get to it.
If you need some other, non GMS application to plug into your server at a later time you'd probably want a separate TCP server socket just for that application that uses "raw". The possible restructuring of TCP packets in the background from the raw functions can cause problems with games if the receiving machine isn't equipped to handle them.
 
X

XirmiX

Guest
Code:
var mycurrent_id = ds_map_find_value(socket_map, client_socket_current);
           show_debug_message(current_id);
Shouldn't that be
Code:
show_debug_message( string(mycurrent_id));
I don't see the variable "current_id" anywhere else.
Yes, it's supposed to be that... but I have that now and still, nothing happens. Or, at least, I don't know what's really supposed to happen. Now, even though basically no code was changed and the two projects were launched just an hour or two later, instead of the client launching later anomaly, I get one where the debugged goes hay-wire when I launch the client in debug mode after I had launched server in debug mode, as code from elsewhere is shown to be in the wrong places. Like, for some reason, the create event code is shown to be in draw event in the debugger of the client, and two scripts, which I'm not using, are shown to be the codes of create and networking event for it, for some reason.

The "Can't bind port" error is most likely to be caused by accidentally trying to bind multiple sockets to the same port. Perhaps you have your server and client both trying to use 50000?
Yes... Aren't they supposed to use the same port for the sake of connection? That's what like every tutorial for networking in GML out there does.


If you need some other, non GMS application to plug into your server at a later time you'd probably want a separate TCP server socket just for that application that uses "raw". The possible restructuring of TCP packets in the background from the raw functions can cause problems with games if the receiving machine isn't equipped to handle them.
I have no idea what you're talking about here. I plan to use UDP for server listing, because why would you have lag/crash in having servers listed just because one server in the list is having trouble connecting?
 

PNelly

Member
" Aren't they supposed to use the same port for the sake of connection?"

If you're server is running on port p, all that's important is that the client connects to port p, as in network_connect(socket,url,port). The client can create their network socket on any port whatsoever, so long as they attempt to connect to the correct port on the machine running the server. Since it's common for people to test networked applications by running the server and client(s) on the same machine and connecting to local host (127.0.0.1), if you try to put the server socket and the client socket on the same port you can run into issues. In my own projects I'll have the clients select ports in the ephemeral range (49152 to 65535) at random until their successfully able to create a network socket. They'll then connect to whatever fixed port the server is running on.

"I have no idea what you're talking about here. I plan to use UDP for server listing, because why would you have lag/crash in having servers listed just because one server in the list is having trouble connecting?"

What I'm trying to tell you is it's probably best to avoid the raw functions when in-game data is being exchanged. If the game host and game client(s) are all GMS applications there's no reason to introduce the extra complexities that can arise when using raw. If however, you need at some point to connect your server to some other application that's written in java or php or whatever, then you'll need to either use raw for that process or build GM's packet organization features into the external application.

"Yes, it's supposed to be that"

The error you refer to in your first post "incorrect type (5) expecting a String", occurs at "buffer_write(send_buffer, buffer_string, client_id);". So, I suggest either putting in a show debug message for the value of client_id, or stepping through with the debugger, to double check it actually contains a value that makes sense. Since that line is causing an error, it sounds like it probably doesn't. I'm not sure how putting a show debug message for a variable with a different name helps with that, unless they're supposed to contain the same value and I'm missing that reading your code.
 
X

XirmiX

Guest
" Aren't they supposed to use the same port for the sake of connection?"

If you're server is running on port p, all that's important is that the client connects to port p, as in network_connect(socket,url,port). The client can create their network socket on any port whatsoever, so long as they attempt to connect to the correct port on the machine running the server. Since it's common for people to test networked applications by running the server and client(s) on the same machine and connecting to local host (127.0.0.1), if you try to put the server socket and the client socket on the same port you can run into issues. In my own projects I'll have the clients select ports in the ephemeral range (49152 to 65535) at random until their successfully able to create a network socket. They'll then connect to whatever fixed port the server is running on.
Oh, well, yes, connecting to a port is what I meant. I guess a better approach would be like that of yours, where the server checks whether a port is free and then uses that port, and then the client looks through the same ports, looking through, although I have no idea how either of these could be done without the client connecting to something that it shouldn't and the server actually checking whether any ports (of the values allowed, I can work with irandom in and of itself) are free. Also, I was curious about this whole 127.0.0.1 IP address. I'm guessing it's just local then. How would I make a server global then? Take the host's IP address? But how would I do that?

"I have no idea what you're talking about here. I plan to use UDP for server listing, because why would you have lag/crash in having servers listed just because one server in the list is having trouble connecting?"

What I'm trying to tell you is it's probably best to avoid the raw functions when in-game data is being exchanged. If the game host and game client(s) are all GMS applications there's no reason to introduce the extra complexities that can arise when using raw. If however, you need at some point to connect your server to some other application that's written in java or php or whatever, then you'll need to either use raw for that process or build GM's packet organization features into the external application.
So,what you're saying is, since I'll be making two connections, for the TCP connection between the server and the client, it's okay to not use raw, while for the UDP I would need to do so if I'm making a site connection with it?

" Aren't they supposed to use the same port for the sake of connection?"

"Yes, it's supposed to be that"

The error you refer to in your first post "incorrect type (5) expecting a String", occurs at "buffer_write(send_buffer, buffer_string, client_id);". So, I suggest either putting in a show debug message for the value of client_id, or stepping through with the debugger, to double check it actually contains a value that makes sense. Since that line is causing an error, it sounds like it probably doesn't. I'm not sure how putting a show debug message for a variable with a different name helps with that, unless they're supposed to contain the same value and I'm missing that reading your code.
Thaaat was a mistake on my side, and as I said, I fixed that and I'm still getting the same, so imagine that it's the same variable. I mentioned in another thread that this error keeps happening, from what I can understand, due to the server not being able to store a socket ID as a string key in a ds_map (not the value, but the key):

Code:
ds_map_add(socket_map, client_socket_current, client_id);
Is... that a huge problem? Because I can't not use the socket as the key and use the ID as the key instead, because then I would come to a point where the client needs to send their 16-digit ID I created UPON DISCONNECTION and that's ridiculous; you can't send information (buffer information) while disconnecting, since it's loss of connection.
 

PNelly

Member
- "I guess a better approach would be like that of yours, where the server checks whether a port is free and then uses that port"

That'll depend on what you have in mind for how the online portion of your game will work. If your intention is for a couple buddies to text each other their IP addresses and port numbers that'll work fine (and they'll need to be comfortable with port forwarding), but if you want Joe Blow in Madagascar to be able to easily find partners/opponents on the internet for your game without any tech savvy, then your server will need to be running with a globally route-able IP address(es) and port(s) that the client applications are aware of before hand, so that they don't require any input from the user.

- "Also, I was curious about this whole 127.0.0.1 IP address. I'm guessing it's just local then."

Your machine refers to itself as 127.0.0.1, which is really handy for testing. Naturally, yes that is just local.

- "How would I make a server global then? Take the host's IP address? But how would I do that?""

I started typing and realized the answer to that question invites a very, very long answer. To try and be succinct, it'll vary between implementations, but you'll need at least one dedicated server with a static, globally routeable IP address. Even then, if the actual games are going to take place in a peer to peer manner between machines rather than on the dedicated server itself you won't get very far using TCP unless you're okay with making your audience port forward.

- "So,what you're saying is, since I'll be making two connections, for the TCP connection between the server and the client, it's okay to not use raw, while for the UDP I would need to do so if I'm making a site connection with it?"

What I'm saying is that if you have two programs built with GMS communicating with each other it is in your best interest not to use raw. Raw exists is as an aid to people who need their GMS application to communicate with another application not made using GMS.

Communicating with a website is a different animal. You communicate with a website/webserver by using HTTP requests. HTTP is built on top of TCP. UDP doesn't really factor into the issue, and GMS supports all HTTP methods anyhow.

- "from what I can understand, due to the server not being able to store a socket ID as a string key in a ds_map"

Key and value for ds_map can be reals or strings, you can see for yourself in the manual. The error says "expecting a string", which means you told it you were going to give it a string, and at runtime it was given a number or undefined instead, leading me to believe client_id does not contain the value you think it does.
 
X

XirmiX

Guest
- "from what I can understand, due to the server not being able to store a socket ID as a string key in a ds_map"

Key and value for ds_map can be reals or strings, you can see for yourself in the manual. The error says "expecting a string", which means you told it you were going to give it a string, and at runtime it was given a number or undefined instead, leading me to believe client_id does not contain the value you think it does.
But I'm telling it that it's a string in every single piece of code, look:

///Initializing connection socket
{
joined = false;
playerjoined = false;
playerleft = false;

message = "";

x_previous = x+1;
y_previous = y+1;
x_current = x;
y_current = y;
send_data = 0;

port = 50000;
IP = "127.0.0.1";
socket_plug = network_create_socket(network_socket_tcp);
//send_buffer = buffer_create(256, buffer_fixed, 1);
network_connect_raw(socket_plug, IP, port);
client_amount = 0;
client_data = ds_map_create();
instance_data = ds_map_create();

myclient_id = "1234567890123456";//ini_read_string("Profile Data", "password", noone);
myplayer_username = "Scorpion";

buffer_type_send = 1;
}

type = async_load[? "type"];

switch(type)
{
//Sending data upon connection
case network_type_data:
switch(buffer_type_send)
{
//sending buffer type 1 (player enter)
case SEND_USER_ID:
//create a buffer with which to send information
var send_buffer = buffer_create(256, buffer_fixed, 1);
var buffer_type_write = SEND_USER_ID;

buffer_write(send_buffer, buffer_u8, buffer_type_write);
buffer_write(send_buffer, buffer_string, myclient_id);
buffer_write(send_buffer, buffer_string, myplayer_username);

network_send_raw(socket_plug, buffer_send, buffer_sizeof(buffer_send));

message = myplayer_username + " joined";
joined = true;

my_tank = instance_create(irandom(room_width), irandom(room_height), obj_player);

buffer_delete(send_buffer);
buffer_type_send = 0;
break;

//case for receiving and sending data
case network_type_data:
//Identify the sender client
client_socket_current = async_load[? "id"];
//Identify buffer sent
buffer = async_load[? "buffer"];

//Read the buffers sent by the joining client
buffer_seek(buffer, buffer_seek_start, 0);
var buffer_type_read = buffer_read(buffer, buffer_u8); //Read's the initial integer sent, identifying the buffer type
switch(buffer_type_read)
{
//sending buffer type 1 (player enter)
case 1:
var client_id = buffer_read(buffer, buffer_string); //Client's ID number
var player_username = buffer_read(buffer, buffer_string); //Client's username

//Add new player socket to the socket list
ds_map_add(socket_map, client_socket_current, client_id);

//In the client_data map, create a new key with the ID of the player, which will hold onto the user's username
client_data[? string(client_id)] = player_username;

//Defining a temporary variable for the buffer number
var buffer_type_write = 1;

//create a buffer with which to send information
send_buffer = buffer_create(256, buffer_fixed, 1);

//writing data to send to buffer
buffer_write(send_buffer, buffer_u8, buffer_type_write);
buffer_write(send_buffer, buffer_string, client_id);
buffer_write(send_buffer, buffer_string, player_username);
buffer_write(send_buffer, buffer_u8, client_amount);

//If there are now 2 or more users connected, send data about the new client to other clients
if (ds_map_size(socket_map) >= 2)
{
for(i = 0; i < ds_map_size(socket_map); i++)
{
socket_in_use = ds_map_find_value(socket_map, i);
network_send_raw(socket_in_use, send_buffer, buffer_sizeof(send_buffer));
}
}

buffer_delete(send_buffer);
break;

//case for the client disconnecting
case network_type_disconnect:
//Identify the disconnecting socket
client_socket_current = async_load[? "socket"];

//Contained in a temporary variable, from the socket map, the ID number (a string) of the disconnecting socket
var client_id = ds_map_find_value(socket_map, client_socket_current);

//Updating the amount of players that are connected in the client amount variable
//client_amount = ds_map_size(socket_map);
//A temporary variable, which is needed to send amount of players connected, due to a disconnect, to other clients. Reason for this variable is quite complex...
var client_amount_tosend = ds_map_size(socket_map) - 1;

var buffer_type_write = 2;

//create a buffer with which to send information
var send_buffer = buffer_create(256, buffer_fixed, 1);

//writing data to send to buffer
buffer_write(send_buffer, buffer_u8, buffer_type_write);
buffer_write(send_buffer, buffer_string, client_id); //only sending ID, because deleting ID key will subsequently delete player username
buffer_write(send_buffer, buffer_u8, client_amount_tosend);

//If there are now 2 or more users connected, send data about the new client to other clients
if (ds_map_size(socket_map) >= 2)
{
for(i = 0; i < ds_map_size(socket_map); i++)
{
socket_in_use = ds_map_find_value(socket_map, i);
network_send_raw(socket_in_use, send_buffer, buffer_sizeof(send_buffer));
}
}

//In the client_data map, delete from client data the disconnecting client's ID along with their name (ID being the key and username being its string value)
ds_map_delete(client_data, client_id);

//Remove the disconnecting player socket from the socket list
ds_map_delete(socket_map, client_socket_current);

//Updating the amount of players that are connected in the client amount variable
client_amount = ds_list_size(socket_map);

buffer_delete(send_buffer);
break;
In every single piece of code that client_id and socket_map is mentioned it's ALWAYS a string, so the program is telling me weird lies for some reason.
 
X

XirmiX

Guest
@PNelly could it perhaps be that I'm using ds maps wrong? Though I'm pretty sure I understand them. Perhaps I'm storing sockets in a way that the program could not identify them later or something like that? I'm really not sure. If it's not that, then this whole thing doesn't make any sense to me.

I should mention that it seems as if the client and the server connect and disconnect from one another, but never really do anything else. For instance, I have a different version of this code where I've furthered the code to have players spawn and be able to see each other and such, but your playable character doesn't even spawn and appear anywhere on your screen, even though I've defined the x to be irandom(room_width) and y to be irandom(room_height) and yes, I'm spawning the right object, with the command instance_create being declared as a variable, so that I could track the id of the instance.
 

Tsa05

Member
Hi there,

Have you gotten the debugger to run in your server yet? This is basically the only way that it's going to be clear what the issue is. I know that your client_id is a string, and is written as a string, sent as a string, and read as a string, but it's possible that it isn't a string after all of that for several reasons:

1) What if you try to set client_id equal to a piece of data from your buffer, and there's an error? For example, what if your buffer was written like this:
[buffer_u8 | buffer_u8 | buffer_u8 | buffer_string]
And then what if you tried to read u8, u8, string?
I'm not saying that this is what you've done--I think that your reads and writes line up...but it's a hypothetical example. Do you see, though, how a minor error in code like that would cause the 3rd read operation to produce an unexpected result?

2) What if the key you're searching for doesn't exist in a map? I suspect this is the issue in your case. If, due to an error we haven't caught elsewhere, the "key" you're looking up never actually made it into the ds_map, then the result of looking for it is not a string. The result it undefined, which is a special value in GameMaker (and is not a string).

This is why the debugger is so important in this case. The debugger can show you the data stored in your map, so you can see exactly at what part of your program client_id ceases to be the string you meant it to be.
 
X

XirmiX

Guest
@Tsa05 well, if you mean I'm running the server in debug mode (F6) then yes. If you mean something like the debugger displaying a message, then no, it doesn't give me anything.

1) This one's probably not the case indeed. I tried to set up a system where a new buffer is created to write files to each time and then deleted after the information is sent and the same thing kept happening.

2) But how can it not exist if I told it... well, to exist? ds_map_add(socket_map, client_socket_current, client_id) does exactly that, no? So how can it turn out to be undefined?

Well, you say the debugger is good, but the debugger is noping me every chance it gets. Where would it display messages? You said the log (the black bar where compiling also happens) is where the messages should be seen. They're not there, despite me inputting the code you mentioned to display the messages. The debugger highlights the code that is causing the error (which the error itself tells, as we know) and just tells the values of the variables in use upon the error occurring, which isn't helpful. I don't know what I'm supposed to do with the debugger now, if anything is possible.
 

Tsa05

Member
Anything that you output using show_debug_message appears in the Output box of the GameMaker IDE. In the screenshot below, I'm using GMS version 2, but the output box is in the same place by default in GMS1:

You can see the message Hi There listed among the output.


You might also benefit from being able to see your data in the debugger. If you haven't already got yours set up conveniently, here's a common way people do it.

First, make sure that the debugger has enough "window panes" to show you the info you need. Here's how to add a blank pane beside the usual code one:


Second, set the pane to show the kind of data that you want. For general monitoring, you'd want All Instances.


Third, while your game is running, you have to Turn On Real-time Monitoring, and also, you have to modify the display for things like Ds_Maps


Remember, in the other thread, I talked about how things like maps, lists, and sockets in GameMaker have a "reference number?' By default, that number is what is shown. When you right-click and change the display for that value to ds_map, GameMaker shows you the actual values in the map instead of the reference number.

Once that's all set up and working, you may find that some of your values aren't what you think they should be. If so, we can talk about setting the debugger to pause at certain spots, to see where the data goes wrong.
 
X

XirmiX

Guest
Okay, oddly enough, the error doesn't occur anymore at all. It occurred the last time I checked yesterday, without me changing any code. It's like, one time it will make the client open up quickly with the error occurring upon disconnection, the other time the client will open up slower and without any errors upon its disconnection. I just restarted my computer and the error occurs again, with the client opening up instantly. Apparently it's due to sometimes not being able to use the port 50000, which is annoying!

Regardless, restarted my computer, have the error going up again... Followed your instructions, setting socket_map to a ds_map in the debugger. So, I have no idea what to do with this or how this tells me anything, but this is what I have:
upload_2017-7-13_16-2-42.png

Apparently the socket map doesn't get any values put into it upon a client connecting:
upload_2017-7-13_16-2-51.png

upload_2017-7-13_16-2-58.png

So, now what? So, nothing gets put into the ds_map, even though there's code for it to happen, which is this:

//case for the client disconnecting
case network_type_disconnect:
//Identify the disconnecting socket
client_socket_current = async_load[? "socket"];

//Contained in a temporary variable, from the socket map, the ID number (a string) of the disconnecting socket
var client_id = ds_map_find_value(socket_map, client_socket_current);

//Updating the amount of players that are connected in the client amount variable
//client_amount = ds_map_size(socket_map);
//A temporary variable, which is needed to send amount of players connected, due to a disconnect, to other clients. Reason for this variable is quite complex...
var client_amount_tosend = ds_map_size(socket_map) - 1;

var buffer_type_write = 2;

//create a buffer with which to send information
var send_buffer = buffer_create(256, buffer_fixed, 1);

//writing data to send to buffer
buffer_write(send_buffer, buffer_u8, buffer_type_write);
buffer_write(send_buffer, buffer_string, client_id); //only sending ID, because deleting ID key will subsequently delete player username
buffer_write(send_buffer, buffer_u8, client_amount_tosend);

//If there are now 2 or more users connected, send data about the new client to other clients
if (ds_map_size(socket_map) >= 2)
{
for(i = 0; i < ds_map_size(socket_map); i++)
{
socket_in_use = ds_map_find_value(socket_map, i);
network_send_raw(socket_in_use, send_buffer, buffer_sizeof(send_buffer));
}
}

My guess is that I've written something wrong or it can't be stored this way (socket can't be a key value) or something. So, question is, how is the right way?

Also I'm using GMS 1.4. I plan to move on to GMS 2, but I want to make this game on 1.4 version.
 

chamaeleon

Member
So, now what? So, nothing gets put into the ds_map, even though there's code for it to happen, which is this:
As far as I can tell not a single line in that code block stores anything in ds_map. A number of calls to get something from it, but no store operations.
 
X

XirmiX

Guest
@chamaeleon I gave the wrong code there... I feel like we're going in circles because we're all getting confused due to this all being a huge. Looking back at the code, the key is the socket, the client_id is the string ID declared in client create event.
;[/COLOR][/B]
//Identify buffer sent
buffer = async_load[? "buffer"];

//Read the buffers sent by the joining client
buffer_seek(buffer, buffer_seek_start, 0);
var buffer_type_read = buffer_read(buffer, buffer_u8); //Read's the initial integer sent, identifying the buffer type
switch(buffer_type_read)
{
//sending buffer type 1 (player enter)
case 1:
var client_id = buffer_read(buffer, buffer_string); //Client's ID number
var player_username = buffer_read(buffer, buffer_string); //Client's username

//Add new player socket to the socket list
ds_map_add(socket_map, client_socket_current, client_id);

//In the client_data map, create a new key with the ID of the player, which will hold onto the user's username
client_data[? string(client_id)] = player_username;

//Defining a temporary variable for the buffer number
var buffer_type_write = 1;

//create a buffer with which to send information
send_buffer = buffer_create(256, buffer_fixed, 1);

//writing data to send to buffer
buffer_write(send_buffer, buffer_u8, buffer_type_write);
buffer_write(send_buffer, buffer_string, client_id);
buffer_write(send_buffer, buffer_string, player_username);
buffer_write(send_buffer, buffer_u8, client_amount);

//If there are now 2 or more users connected, send data about the new client to other clients
if (ds_map_size(socket_map) >= 2)
{
for(i = 0; i < ds_map_size(socket_map); i++)
{
socket_in_use = ds_map_find_value(socket_map, i);
network_send_raw(socket_in_use, send_buffer, buffer_sizeof(send_buffer));
}
}

buffer_delete(send_buffer);
break;

The above code is below the following code in the program itself that I have (so, it's connect, then disconnect and then only data). But, since Create and Data are both carried out, or SHOULD be carried out regardless of the Disconnect code being in the middle, there should be no problems. The Disconnect here, as seen below, on the server side, will call back the socket_map and its value
;[/COLOR][/B]

//Contained in a temporary variable, from the socket map, the ID number (a string) of the disconnecting socket
var client_id = ds_map_find_value(socket_map, client_socket_current);

//Updating the amount of players that are connected in the client amount variable
//client_amount = ds_map_size(socket_map);
//A temporary variable, which is needed to send amount of players connected, due to a disconnect, to other clients. Reason for this variable is quite complex...
var client_amount_tosend = ds_map_size(socket_map) - 1;

var buffer_type_write = 2;

//create a buffer with which to send information
var send_buffer = buffer_create(256, buffer_fixed, 1);

//writing data to send to buffer
buffer_write(send_buffer, buffer_u8, buffer_type_write);
show_debug_message("client_id=" + string(client_id));
buffer_write(send_buffer, buffer_string, client_id); //only sending ID, because deleting ID key will subsequently delete player username
buffer_write(send_buffer, buffer_u8, client_amount_tosend);

//If there are now 2 or more users connected, send data about the new client to other clients
if (ds_map_size(socket_map) >= 2)
{
for(i = 0; i < ds_map_size(socket_map); i++)
{
socket_in_use = ds_map_find_value(socket_map, i);
network_send_raw(socket_in_use, send_buffer, buffer_sizeof(send_buffer));
}
}

//In the client_data map, delete from client data the disconnecting client's ID along with their name (ID being the key and username being its string value)
ds_map_delete(client_data, client_id);

//Remove the disconnecting player socket from the socket list
ds_map_delete(socket_map, client_socket_current);

//Updating the amount of players that are connected in the client amount variable
client_amount = ds_list_size(socket_map);

buffer_delete(send_buffer);
break;

So what is wrong here, I don't understand!

If we can't find the fault here, perhaps I can re-write the code in relation to maps. In which case, could someone tell me how YOU would declare a string value (on client side), send it to the server, which would then be stored as a value in a ds_map (mine's called socket_map) under a key that is the socket itself. The socket is the problem, I can't retrieve or store a value of it, I think. Why? I NEEED to, somehow, because otherwise, there is no way the client can disconnect!
 

chamaeleon

Member
Looking at your debug window screenshots, I see socket_map listed with a value of 0. Maybe you can run again, get to that same view, right-click on socket_map and select display as ds_map, and see if it has a size greater than zero, and what its content is.
 

Tsa05

Member
You have:
Code:
ds_map_add(socket_map, client_socket_current, client_id);
whenever the server receives a message with a buffer u8 of 1 at the start.

Since your socket map is empty, this would seem to indicate that a buffer with u8 of 1 at the start is never received by your server. This must be where the problem is. The error upon disconnect is actually a distraction, and not the true error.
I could make the error disappear easily with one line --
Code:
if(!is_undefined(client_id)){
     // Your disconnect code here
}
That will make the error go away (because it will no longer attempt to send to a socket if no socket is found in the map).
But it will not solve the underlying problem, which appears to be that the message with client ID is never received by your server in the first place.

It might be worthwhile to run your client in debug mode now, and to see whether it is actually sending information to the server properly after it connects.
 
X

XirmiX

Guest
Looking at your debug window screenshots, I see socket_map listed with a value of 0. Maybe you can run again, get to that same view, right-click on socket_map and select display as ds_map, and see if it has a size greater than zero, and what its content is.
Yes... it's the same with ds_map open as is with closed for both before and after the client connects.
 
X

XirmiX

Guest
You have:
Code:
ds_map_add(socket_map, client_socket_current, client_id);
whenever the server receives a message with a buffer u8 of 1 at the start.

Since your socket map is empty, this would seem to indicate that a buffer with u8 of 1 at the start is never received by your server. This must be where the problem is. The error upon disconnect is actually a distraction, and not the true error.
I could make the error disappear easily with one line --
Code:
if(!is_undefined(client_id)){
     // Your disconnect code here
}
That will make the error go away (because it will no longer attempt to send to a socket if no socket is found in the map).
But it will not solve the underlying problem, which appears to be that the message with client ID is never received by your server in the first place.

It might be worthwhile to run your client in debug mode now, and to see whether it is actually sending information to the server properly after it connects.
Thought so... but it doesn't. Make. Sense! Here, the code and the debugger contradict each other; the buffer is said to be empty for the client (both server and the client have send_buffer as the buffers they send, so it's the same, don't think we need to worry about that), but for some reason, it says that the send buffer is empty... Why?! Why can't it write to the buffer?
upload_2017-7-13_20-37-34.png
 

Tsa05

Member
Separate issue. Buffers are not ds_maps, they are buffers.
Remember, all "structures" internal to GameMaker are referenced by number. That means there's a socket number 0, a ds_map number zero, a buffer number 0...
Your send_buffer is referring to buffer number 0. When you tell the debugger to show a ds_map with that number, it shows you ds_map number 0 instead of the buffer. In other words, don't worry about it.

Let me show you the minimal code required to get a client to connect to a server:
Code:
server = network_create_server_raw(network_socket_tcp, 6510, 8);
client = network_create_socket_ext(network_socket_tcp, 6520);
var result = network_connect_raw(client, "127.0.0.1", 6510);

show_debug_message( "result = "+string(result) );
That's it. Using no other events or code, this code connects the server and client (which, in this case, are actually within the same program).
If I make a new game, insert this code into the create event of a new object, put the object into a room, and run the game... The output console says:
Code:
result = 0
Pause event has been registered for this frame
Client(2) Connected: 127.0.0.1
Since negative numbers indicate no connection, and zero or above indicate a successful connection, you can see that my extremely simple program has successfully connected to itself.

I want to make sure that each of the parts of the code above are clear, because it looks as though you are setting up a server and writing the buffer correctly but the data is not going from one to the other properly.

A socket, in networking, allows a program to listen to traffic coming in via a certain specific port on your network. (But in GameMaker, it's actually just a number referring to the existence of a socket)

In my code above, server is equal to the reference number for my server socket.
The server socket is listening for TCP traffic on port number 6510. Whenever any data arrives at port number 6510, the server socket is aware of this.

The client must use a different socket. Each aspect of the game (server and client) has its own socket for listening to network stuff. The 2 different sockets can be in 2 separate programs, as you have, or in one program, as I have.
In my code, client is the socket created to send data to the server. It must listen to a port on the computer for traffic related to client things, so it needs to have a port assigned.

Port 6510 is already being listened to by the server socket, so I don't want the client socket listening in on the same one. If I did that, it would be very confusing to deal with the incoming traffic, since the server and the client would both hear all messages destined for the server. So the client socket is set up to listen on port 6520.

Finally, since I have a server socket listening for incoming connections on port 6510 and a client socket ready to send out messages on port 6520, I'm ready to try to connect the two.

I send a message via the client socket...but I sent it to the server's port!!!!! That's going to be pretty important.

The server socket only hears things on the port is was set to listen to--that's 6510. So if I send a connection request to 6520, the server socket won't hear it. And I must send the request using the client socket. If I don't use the client socket to send the request, then I will never know if the connection was successful or not because of the secrets of TCP

- When you send a connection request via TCP, you send something we call a synchronize, or SYN packet. Basically, "Hi, I wanna connect."
- The server, if it's been set up to recognize the TCP protocol, sees this formatted packet and replies with a syncronize acknowledgement packet--an ACK packet, also called a SYN-ACK. Basically, "Ok, sure you can connect."
- The client sends an ACK packet acknowledgement packet--an ACK-ACK. Basically, "Thanks, now I know that you know that we're connected."

This 3-message exchange happens within a few milliseconds when you do the network_connect function.
Sending the SYN over the client socket ensures that the server's ACK reply will be addressed to the client socket's IP address and port.
Sending the SYN to the the port that the server socket is listening to ensures that the server hears the request.
So, we send via the socket attached to port 6520, towards port 6510.

Can you make sure that your client program is using its own socket to send, but is using the server's port as a destination for the message? Can you confirm that both programs are operating on different ports?
 
X

XirmiX

Guest
@Tsa05 in every single tutorial I've seen, everyone has been using network_connect instead of network_connect_ext. What's the difference, and why doesn't network_connect need a port to be used defined? Either way, I'm still getting the same error, regardless of whether I have defined a port to use for the client or not. And yes, I'm using a different port for client; essentially like you have done, with the server using a port (50000) client connecting to the server port (50000) and the client using a different port (4990). And nope, I'm not getting the "port is already in use" issue here, neither for the server or the client. Although, I have two separate projects as server and client, as mentioned before.

So, something else is making the client not send data through to the server, or at least, making the client not write anything in its buffers. But what and why?
 

Tsa05

Member
The difference is 12 bytes and some headaches.

The thing to sort of keep in mind about ports is that multiple programs can listen in on any one port. When you create a socket, your programming is just telling the computer "Hey, when traffic comes in on a certain port, send me a copy, ok?" Multiple programs on your computer can do this, so when data comes in on a certain port, the computer just passes the data along to whichever programs are on the "call me for this port" list.

So, this can get a little weird when 2 programs--which might be totally unaware of each other and programmed by totally different people--happen to choose the same port number to listen in on. Sometimes, this is actually intentional. For example, maybe you've got 2 different web browsers on your computer. Well, all http web traffic travels to port 80 by default. So if you have 2 web browsers open at once, they have to both be listening to port 80.

The logical next question is something like... "Say I have Google Chrome browser open to yoyogames.com. Then I load Internet Explorer and load madewithunity.com. Why doesn't Chrome also switch to showing madewithunity.com?"

Weirdly enough, it could. But the short answer is "a lot of programming was done to prevent it." Since both browsers are listening on port 80, someone had to specifically program them to keep track of when to expect a new web page to be delivered (like after clicking a link, etc) and also which page to expect. When data comes over port 80, each browser looks at what has arrived, and it doesn't fit the browser's expectations, then that browser ignores the info.

So, if you do the raw type of data, then you're sending and receiving only the info in your buffer. It's up to you, then, to anticipate what kinds of other "odd" things might happen, and to program your game to account for that. For example, what would happen if Skype messenger receives an html web page instead of a text message? Will it crash?

What if your game receives a message with a buffer_string in the first position, instead of a u8? You've already seen how a single line of server code that relies on a piece of data that didn't get properly stored can cause your server to crash. Imagine if some other game or something on your computer started using port 50000 while your server was running? All kinds of randomly formatted stuff from someone else's program would start causing networking events to occur in your game. You'd have to program your game to recognize when the data is meant for you, and when it's not.

The non-raw functions already do this. They put a 12-byte chunk of info at the front of your buffer automatically when you send it. When the data is received by a non-raw-mode GameMaker server, GameMaker checks the first 12 bytes to see if they match the special identifier code that means "this is from a GameMaker game." If it finds that identifier, it chops the 12 bytes off, and then triggers the network async event. If the 12 bytes are something it doesn't understand, then it assumes the data is meant for someone else and ignores it.

Programming-wise, the rest of your stuff is identical--the adding and removing of the header info happens automatically as the data is sent or received. It's just there to help GameMaker servers filter out unwanted data. The raw functions exist in case you want to write your own special filtering-out code, and want your GameMaker game to connect to a server that isn't made with GameMaker.

Complicated answer for a simple difference.

As for tracking the data issue, I recommend adding break points to your code at places where things are *supposed* to happen, then running in debug mode. You can click on a line and press F9 to set a breakpoint (and F9 again to remove it). When you run in debug mode, GameMaker will pause your game when it hits a line with a breakpoint, and then you can use the controls at the top of the debugger to resume at normal speed or to step forward one line at a time.

For example, if you're wondering whether the client is sending the data, place a breakpoint on the line that sends the buffer. Run the client in debug mode and see if your code pauses at that line. If it does, then you know the command to send data was reached. But if your game runs without saying "Paused" in the debugger, then you know that this line was never executed.

You should be able to put breakpoints at a few key places (where data should be sent, received, or interpreted) and see what your debugger does. This should help you track down the place where something isn't right, then to determine what condition is preventing it.
 
X

XirmiX

Guest
@Tsa05 to summarise what you wrote; you recommend I don't use raw and want me to figure out why information isn't being stored and received by the server.

Okay... but what is the difference between network_connect and network_connect_ext, as in, how and when would each be necessary to use?

As for the issue... I thought I already told you that I figured out the issue, and I simply don't know how to fix it. The buffer_write commands in the connect section of the client don't write anything to the actual buffer, despite me telling them to do so. Debugging more in-depth just to find out the same thing I already know isn't going to tell me how to fix this issue, is it? Again, the issue is that upon connection, client doesn't write anything to the buffers. Why, I don't know, because I have written code that tells it to write to buffers. Perhaps I'm missing something and don't really know how to write buffers, or I need something else. Any thoughts? Perhaps you could give me an example of how you would create a buffer and write to it from client to be sent to the server and for the server to then read the information and store it?

EDIT: oh, and your suggestion wouldn't work, because the debugger opens only when you run the program, but you would need to set up break points before running the program, because the information is sent to the server initially as soon as the client joins, so this is impossible to do!
 
Last edited by a moderator:

Tsa05

Member
The difference between network_connect and network_connect_ext is that you made up the second one--there's no such function.
As far as the difference between network_connect and network_connect_raw, that was the entire subject of my post. You use them in exactly the same manner. The difference is that network_connect does some things in the background to try to filter out accidental data leaking in from other games on the same port. The _raw function does not. You can run your game just the same, but it might be interfered with by other network traffic unless you do more to prevent that.

The problem is likely not what you are suggesting, specifically "the client don't write anything to the actual buffer, despite me telling them to do so."

You are suggesting that the buffer_write() function does not put data into the buffer. If that was the problem, then everyone using GameMaker would be experiencing the same issue. I can confirm that buffer_write does work. Instead, the issue must be that the data you are writing is not what you think it is, or, after the data is written, it is not sent to the right place, or, after it is sent it is not received properly. These scenarios are far more likely than buffer_write() simply not writing.

I propose a much simpler test in either one of your programs:
Code:
var my_buffer = buffer_create(1, buffer_grow, 1);
buffer_seek(my_buffer, buffer_seek_start, 0);
buffer_write(my_buffer, buffer_u8, 5);
buffer_write(my_buffer, buffer_string, "Testing");
buffer_write(my_buffer, buffer_u16, 12345);

buffer_seek(my_buffer, buffer_seek_start, 0);
show_debug_message(string( buffer_read(my_buffer, buffer_u8)));
show_debug_message(string( buffer_read(my_buffer, buffer_string)));
show_debug_message(string( buffer_read(my_buffer, buffer_u16)));
Does this print 3 values to the output console? If so, then...buffer_write is working.

Code:
you would need to set up break points before running the program
Incorrect, and incorrect though. You CAN set up breakpoints before running your code. As I wrote, place the cursor on a line of code and press F9. The breakpoint is set. When you run your code, the breakpoint will be there. But also, you can set breakpoints during runtime if needed. So, you can have a breakpoint set before running the game, and then while the game is paused due to the breakpoint, you can add more breakpoints in the debugger code window.
 
X

XirmiX

Guest
The difference between network_connect and network_connect_ext is that you made up the second one--there's no such function.
As far as the difference between network_connect and network_connect_raw, that was the entire subject of my post. You use them in exactly the same manner. The difference is that network_connect does some things in the background to try to filter out accidental data leaking in from other games on the same port. The _raw function does not. You can run your game just the same, but it might be interfered with by other network traffic unless you do more to prevent that.

The problem is likely not what you are suggesting, specifically "the client don't write anything to the actual buffer, despite me telling them to do so."
I meant network_create_socket and network_create_socket_ext. What's the difference between those two?

And yeap, I've changed all of the raws to just normal GM socket/server.

You are suggesting that the buffer_write() function does not put data into the buffer. If that was the problem, then everyone using GameMaker would be experiencing the same issue. I can confirm that buffer_write does work. Instead, the issue must be that the data you are writing is not what you think it is, or, after the data is written, it is not sent to the right place, or, after it is sent it is not received properly. These scenarios are far more likely than buffer_write() simply not writing.

I propose a much simpler test in either one of your programs:
Code:
var my_buffer = buffer_create(1, buffer_grow, 1);
buffer_seek(my_buffer, buffer_seek_start, 0);
buffer_write(my_buffer, buffer_u8, 5);
buffer_write(my_buffer, buffer_string, "Testing");
buffer_write(my_buffer, buffer_u16, 12345);

buffer_seek(my_buffer, buffer_seek_start, 0);
show_debug_message(string( buffer_read(my_buffer, buffer_u8)));
show_debug_message(string( buffer_read(my_buffer, buffer_string)));
show_debug_message(string( buffer_read(my_buffer, buffer_u16)));
Does this print 3 values to the output console? If so, then...buffer_write is working.
Well, if you mean to put the first bit (first 5 lines from your code) in the client, indenting all the other initial sending code and then on the server doing the same for when receiving the data for case 1, then... no, nothing happens. No text messages, nothing. And I'm running the server in debug mode here.

Code:
you would need to set up break points before running the program
Incorrect, and incorrect though. You CAN set up breakpoints before running your code. As I wrote, place the cursor on a line of code and press F9. The breakpoint is set. When you run your code, the breakpoint will be there. But also, you can set breakpoints during runtime if needed. So, you can have a breakpoint set before running the game, and then while the game is paused due to the breakpoint, you can add more breakpoints in the debugger code window.
Oh, that's what you meant by code and F9, okay, got it... though doing so in network_type_connect within client crashes the client and disconnects the debugger, when running the client in debug mode. I put break points through all of the buffer_sends and also at network_send_packet.
 

PNelly

Member
@Tsa05 I appreciate you jumping in here, I'm starting to wonder if this thread might turn up in a lot of networking searches of the forum ;)

@XirmiX

"I meant network_create_socket and network_create_socket_ext. What's the difference between those two?"

_ext lets you choose which port the new socket is bound to, the other one does not. I don't know how network_create_socket() decides which port to use, but like I said above the port number being used by a client isn't really important.

"Well, if you mean to put the first bit (first 5 lines from your code) in the client, indenting all the other initial sending code and then on the server doing the same for when receiving the data for case 1, then... no, nothing happens. No text messages, nothing. And I'm running the server in debug mode here."

He's suggesting that you copy and paste that example into a code block so you can see for yourself that buffer_write works. That code just writes stuff into a buffer and immediately reads it back out and prints the values read to the console, no networking involved. Although I think he may have had a small typo, when you run that it should print "5 , Testing, 12345" on separate console lines.

"Oh, that's what you meant by code and F9, okay, got it... though doing so in network_type_connect within client crashes the client and disconnects the debugger, when running the client in debug mode. I put break points through all of the buffer_sends and also at network_send_packet."

Breakpoints "freeze" your program to give you a chance to inspect the value of variables etc and make sure things are actually progressing as you expect them to. Putting breakpoints in and around the connection handshake is likely going to blow it up because neither program is going to know how to deal with the connection process being stopped in place. Likewise, I wouldn't be surprised if breakpoints cause a lot of disruption to packet transmission.

I've never actually used the debugger to try and inspect buffer contents, I might go try it but unless it knew to read specific data types in a specific order I don't know how much useful information you would get from that. You might do something similar to Tsa05's example above - when your server/client writes a buffer, before sending try and read that buffer with the format you expect on the receiving end and print the contents to see if it actually contains what you think it does.
 
X

XirmiX

Guest
The issue here is similar in nature to one that I stumbled upon here. Although, what the creator of the thread did had was something I have already done, but for me it's not fixed.

The debugger won't really help, since it disconnects from the client as soon as you launch the client with previously set break points. I don't think the messaging will do anything either, since I'm not receiving any messages anywhere in the log regardless of what I do. Or if I am getting them somewhere, then I have no idea where exactly.

To be frank, I tried to rewrite this code a few days ago and... similar issues seem to occur. GM really doesn't like something about me using maps to store, read and send value data of and check keys for.
 
X

XirmiX

Guest
@Tsa05 and PNelly so, am I just stuck in something that can't be fixed or something? I've found that I can send, receive and store integer numbers just fine. Perhaps there's something else in terms of strings that I'm missing? Or perhaps it has to do with sockets themselves, or ds_maps, or a combination of the three.

May be it's a better and simpler idea to re-do these ds_maps, so from there I could get an idea of what to do, so, would you guys be able to provide examples? Such that, you send a string value from a client, which is then stored by the server in a ds_map, along with the socket id that sent the string, in such a way that the server can then retrieve the socket and id values to delete them from the ds_map when a player disconnects.

Speaking of which, what type of a value/key needs to be read as in order to be compared to, say client_socket_current (which is asynch_load[? "socket"], which is the socket in question)?
 

PNelly

Member
"am I just stuck in something that can't be fixed or something?"

I cannot say no emphatically enough to this question. It's not uncommon for inexperienced users to entertain this idea when they get frustrated, but it's just not the way this stuff works. I say this in part for other people that might read this thread, that whatever you're working on, it's quite unlikely your problem needs to be added to this list: https://en.wikipedia.org/wiki/List_of_undecidable_problems. A more immediately applicable way to think about might be that if anyone has ever built something remotely similar to what you would like to build, in part or in whole, then it can be done. It just might not be easy.

That being said, sometimes bugs exist within GMS or other tools that will hold people back from accomplishing a specific goal. It may have already been fixed, but this is a good example: https://forum.yoyogames.com/index.p...s-not-return-useful-information-in-tcp.24754/.

Lots of multiplayer projects have been built with GMS, I've made a couple myself. I assure you that all the components you need are in working order. I'll get off my soapbox now.


"I've found that I can send, receive and store integer numbers just fine. Perhaps there's something else in terms of strings that I'm missing? Or perhaps it has to do with sockets themselves, or ds_maps, or a combination of the three."

That's most likely the case, which is why @Tsa05 and I have been encouraging you to use the debugger and print statements to try and gather some clues about where the bug(s) actually is/are. It's clear enough it's not where the error gets thrown.

However, I just noticed a few things in your client code you should have a look at:
Code:
var send_buffer = buffer_create(256, buffer_fixed, 1);
           var buffer_type_write = SEND_USER_ID;
        
           buffer_write(send_buffer, buffer_u8, buffer_type_write);
           buffer_write(send_buffer, buffer_string, myclient_id);
           buffer_write(send_buffer, buffer_string, myplayer_username);
        
           network_send_raw(socket_plug, buffer_send, buffer_sizeof(buffer_send));
First, it's probably just a good practice to use buffer_seek() even though "send_buffer" was just created.
Second, you declare "var send_buffer", and then refer to a "buffer_send" a few lines later. See the difference?
Third, your use of buffer_sizeof() in the last line is wrong. buffer_sizeof() is used to get the number of bytes occupied by each data type constant that's usable with buffers (buffer_u8, buffer_s32, etc...). You need to be using buffer_get_size() to get the total number of bytes in a buffer.


"May be it's a better and simpler idea to re-do these ds_maps, so from there I could get an idea of what to do, so, would you guys be able to provide examples?"

Just my personal opinion, but I usually only scrap something and re-do it when I realize that I simply went too far without enough incremental testing (meaning I have no idea what broken stuff I put in there while I was building it), or a wayyyy better solution dawns on me after I've already got it working. As far as examples, it depends on the complexity of what you're asking for.


"Speaking of which, what type of a value/key needs to be read as in order to be compared to, say client_socket_current (which is asynch_load[? "socket"], which is the socket in question)?"

I'm not sure I understand. The socket id's are generated as integers, so if you want to make comparisons to a socket id you need to use a number, if that's what you mean.
 
X

XirmiX

Guest
"I've found that I can send, receive and store integer numbers just fine. Perhaps there's something else in terms of strings that I'm missing? Or perhaps it has to do with sockets themselves, or ds_maps, or a combination of the three."

That's most likely the case, which is why @Tsa05 and I have been encouraging you to use the debugger and print statements to try and gather some clues about where the bug(s) actually is/are. It's clear enough it's not where the error gets thrown.
I've said previously that debugging isn't doing anything for me; no messages are shown, and as far as I can tell, in terms of the buffer values, the client doesn't write anything to the buffer upon creation and we don't know why. Like, what do you want me to do with the debugger? It's not doing anything for me, nothing that would indicate the problem exactly.

EDIT: Also, it's most likely an issue with the client here not sending stuff to the server, because when I told the client to spawn an object anywhere in the room and write some text on screen within network_type_connect, nothing was shown; no player character (which has a triangular sprite and which I found to be working without networking just fine), no text, nothing. When it came to text, all I got was a zero. Something with the network_type_connect for the client isn't right... I can use this function for the client too, right, so when it connects and such? If not, then I guess I've found my problem, which is stupid, like come on! Well, this code doesn't have the spawning stuff and a more-built version did, but still.

ANOTHER EDIT: Noooope. Agh!

However, I just noticed a few things in your client code you should have a look at:
Code:
var send_buffer = buffer_create(256, buffer_fixed, 1);
           var buffer_type_write = SEND_USER_ID;
    
           buffer_write(send_buffer, buffer_u8, buffer_type_write);
           buffer_write(send_buffer, buffer_string, myclient_id);
           buffer_write(send_buffer, buffer_string, myplayer_username);
    
           network_send_raw(socket_plug, buffer_send, buffer_sizeof(buffer_send));
First, it's probably just a good practice to use buffer_seek() even though "send_buffer" was just created.
Second, you declare "var send_buffer", and then refer to a "buffer_send" a few lines later. See the difference?
Third, your use of buffer_sizeof() in the last line is wrong. buffer_sizeof() is used to get the number of bytes occupied by each data type constant that's usable with buffers (buffer_u8, buffer_s32, etc...). You need to be using buffer_get_size() to get the total number of bytes in a buffer.
Fixed, fixed and fixed... still getting the same bloody error :(

"May be it's a better and simpler idea to re-do these ds_maps, so from there I could get an idea of what to do, so, would you guys be able to provide examples?"

Just my personal opinion, but I usually only scrap something and re-do it when I realize that I simply went too far without enough incremental testing (meaning I have no idea what broken stuff I put in there while I was building it), or a wayyyy better solution dawns on me after I've already got it working. As far as examples, it depends on the complexity of what you're asking for.
It's not difficult for me to re-do the ds_map and all of the sending and storing of the string id and the socket number. Just give me some example and then may be I can follow it and it will actually work. Please. May be then I'll understand what the problem was as well. I'm sure you both have worked with ds_maps and sending/receiving of their data, string data and storing/retrieving of said data. Because that's what I'm working with here, just pleeease something in terms of that which works? Then I can apply my stuff to it and all should be fine.

Sorry, I'm simply desperate, because it's frustrating to be stuck on something that doesn't make sense and preventing me from creating the rest of the game. How long have I had this issue for? 2 weeks now?

"Speaking of which, what type of a value/key needs to be read as in order to be compared to, say client_socket_current (which is asynch_load[? "socket"], which is the socket in question)?"

I'm not sure I understand. The socket id's are generated as integers, so if you want to make comparisons to a socket id you need to use a number, if that's what you mean.
Yes, that is what I was asking for... although that doesn't help me because... wait... I just realized something... not in terms of storing and sending or such, but retrieving... I need a function that asks what the key of a certain ds_map itself is; not the value of a given key, but the key itself, because if the error were to be fixed, the game would be comparing the socket with the string ID of a client, which is not what I want. I want to compare the key itself. So far, it seems like there is no such a function, why the hell? ds_map_find_value will find the value of a said key in a given map, but I need something that will find the key name itself in a given map. How do I do this? I need to be able to do this.
 
Last edited by a moderator:
X

XirmiX

Guest
Have I created a mess with this? Goddamit, I just wanted something that could work. The debugger doesn't tell me what's wrong. Nothing else can tell me what's wrong. What can I do?
 

PNelly

Member
"what do you want me to do with the debugger"

You don't have to use the debugger. What you do have to do is put some effort in to build a bread crumb trail that will lead you to the source of the problem. The debugger is just one tool that can help you do that. You can print items of interest to the compile window, you can use popup dialogues, you can use "draw_text()", you can write text files with lots of output if you feel like that's what you need. The world is your oyster. Networking is hard, and requires a lot debugging savvy, which can only be gained from doing lots of debugging. Forum members can only help if they have enough information to pick apart what's going on. To get answers to questions rooted in complex topics, question askers have to be willing to gather a sufficient amount of information.

"Fixed, fixed and fixed... still getting the same bloody error"

Indeed. Your original error is on the server side upon client disconnect, and those issues were in your client code, they don't have to be related.

re: Examples

I'm considering putting together a basic networking tutorial at some point. There seem to be very few quality resources around that people new to the topic can refer to. It takes a lot of time though and I'm spread thin as it is. We will see.

re: Maps

"So far, it seems like there is no such a function"

Not so. You chose the key and put in the map, therefore you know the key. There are good ways and bad ways to store and retrieve keys though. The manual provides ds_map_find_first() and ds_map_find_next(). I suggest you stay away from these, they're horrifically slow and the problem compounds as the maps get larger. There are plenty of ways but I like to store the keys in a list so I can iterate through the map in linear time:

Code:
map_of_stuff = ds_map_create();
keys_to_map_of_stuff = ds_list_create();
thing = some_number;
key = "key_to_thing";
ds_map_add(map_of_stuff, key, thing);
ds_list_add(keys_to_map_of_stuff, key);
Iterating:

Code:
var _idx, _key, _value;
_num = ds_list_size(keys_to_map_of_stuff);
for(_idx=0;_idx<_num;_idx++){
     _key = keys_to_map_of_stuff[| _idx];   // if you haven't seen these before go to the manual and look up "accessors". 
     _value = map_of_stuff[? _key];       // they do the same thing as ds_list/map add, find value, replace, etc...
     // Do something with value
}
You can verify whether a certain key/value pair is present in a map by using ds_map_exists().
 
X

XirmiX

Guest
"what do you want me to do with the debugger"

You don't have to use the debugger. What you do have to do is put some effort in to build a bread crumb trail that will lead you to the source of the problem. The debugger is just one tool that can help you do that. You can print items of interest to the compile window, you can use popup dialogues, you can use "draw_text()", you can write text files with lots of output if you feel like that's what you need. The world is your oyster. Networking is hard, and requires a lot debugging savvy, which can only be gained from doing lots of debugging. Forum members can only help if they have enough information to pick apart what's going on. To get answers to questions rooted in complex topics, question askers have to be willing to gather a sufficient amount of information.
Yes, I would try that if the message commands actually worked, which they don't, just like instance spawning and so forth, for some reason.


"Fixed, fixed and fixed... still getting the same bloody error"

Indeed. Your original error is on the server side upon client disconnect, and those issues were in your client code, they don't have to be related.

re: Examples

I'm considering putting together a basic networking tutorial at some point. There seem to be very few quality resources around that people new to the topic can refer to. It takes a lot of time though and I'm spread thin as it is. We will see.
That would be extremely helpful if you did that. So far, I've found the tutorial made by RealTutsGML on youtube to be the the most useful for me, but then I hit a wall again with it, like with other tutorials, but it was the most comprehendable. My reason for hitting the wall; I had server and client as separate projects and at some point, he calls a server object on the client side (oServerClient object), which can't be done if you're working with two projects. So, if you do look into making a decent tutorial, I'd advise taking what he's done as a starter. He's went a step further with stuff like putting all the networking codes in scripts, which I avoided myself to not make it any more complicated, even though it might be easier to work with if you have it working smoothly. I also couldn't really understand the way he stored the socket info in maps/lists, so more info on that would be useful and would probably solve my issue.

re: Maps

"So far, it seems like there is no such a function"

Not so. You chose the key and put in the map, therefore you know the key. There are good ways and bad ways to store and retrieve keys though. The manual provides ds_map_find_first() and ds_map_find_next(). I suggest you stay away from these, they're horrifically slow and the problem compounds as the maps get larger. There are plenty of ways but I like to store the keys in a list so I can iterate through the map in linear time:

Code:
map_of_stuff = ds_map_create();
keys_to_map_of_stuff = ds_list_create();
thing = some_number;
key = "key_to_thing";
ds_map_add(map_of_stuff, key, thing);
ds_list_add(keys_to_map_of_stuff, key);
Iterating:

Code:
var _idx, _key, _value;
_num = ds_list_size(keys_to_map_of_stuff);
for(_idx=0;_idx<_num;_idx++){
     _key = keys_to_map_of_stuff[| _idx];   // if you haven't seen these before go to the manual and look up "accessors".
     _value = map_of_stuff[? _key];       // they do the same thing as ds_list/map add, find value, replace, etc...
     // Do something with value
}
You can verify whether a certain key/value pair is present in a map by using ds_map_exists().
It would be useful if there was a function ds_map_find(); which would let you get the name of the key specified in the specified ds_map, but such a function does not exist, and yeap, the ones that do, and those being the ones you mentioned, are really, really bad. Why is there no ds_map_find() function?

I can't simply use a list to store sockets, since I'll need to reference sockets to delete all kinds of information about the exiting hosts, so I need to use a ds_map, but finding ds_map keys have quite 💩💩💩💩ty functions for doing so, so I'm stuck. There's only an easy way to retrieve a value of a key, and not the key itself. May be there's an extension I can download, or perhaps attempt to create myself to make such a function exist so I can get what I need? I really don't know at this point. Though I don't think the socket identification is the problem with my error, since I'm trying to send the player's generated string id, which is declared in the client's create event where the error occurs.

And again, using debugger, I found out that a string isn't being even written to the buffer upon connection, so I'm starting to think there might be some issue in terms of sending strings on GML. I think so far, the tutorials I've followed, have only sent integer values. May be there is something more that I need to send a string? Oh, and, just in case, what is the difference between text and a string? Cause you can send a string and a text value, with string having an extra zero at the end, but not the text. What's the difference and why do two different things that are essentially the same thing even exist?
 
X

XirmiX

Guest
@PNelly @Tsa05 Okay, so here's what's going on. I somehow, SOMEHOW got the messages working. The debug messages show in the log now (for the server) THANK GOD!

But, what happens for me now is that the client crashes and loses connection with the debugger, and then after like half a minute it actually reconnects with the debugger and starts working why. No idea why, but whatever. Anyway, it seems that when running the server, debug mode or not, I get the debug log message that I placed in its client disconnect code. Here's the server's client disconnection code (along with its client connection code):

Code:
///Networking
type = async_load[? "type"];

//Switch statement, because needs a list of connection types... yeah
switch(type)
{
    //case for the client connecting
    case network_type_connect:
        //Identify the connecting socket
        client_socket_current = async_load[? "socket"];
     
        //Updating the amount of players that are connected in the client amount variable
        client_amount += 1;
     
        joined = true;
    break;

    //case for the client disconnecting
    case network_type_disconnect:
        //Identify the disconnecting socket
        client_socket_current = async_load[? "socket"];
     
        //Contained in a temporary variable, from the socket map, the ID number (a string) of the disconnecting socket
        var client_id = ds_map_find_value(socket_map, string(client_socket_current));
     
        //Updating the amount of players that are connected in the client amount variable
        //client_amount = ds_map_size(socket_map);
        //A temporary variable, which is needed to send amount of players connected, due to a disconnect, to other clients. Reason for this variable is quite complex...
        var client_amount_tosend = ds_map_size(socket_map) - 1;
     
        var buffer_type_write = 2;
     
        //create a buffer with which to send information
        var send_buffer = buffer_create(256, buffer_fixed, 1);
     
     
     
        //writing data to send to buffer
        buffer_seek(send_buffer, buffer_seek_start, 0);
        buffer_write(send_buffer, buffer_u8, buffer_type_write);
        show_debug_message("client_id=" + string(client_id));
        buffer_write(send_buffer, buffer_string, client_id); //only sending ID, because deleting ID key will subsequently delete player username
        buffer_write(send_buffer, buffer_u8, client_amount_tosend);
     
        //If there are now 2 or more users connected, send data about the new client to other clients
        if (ds_map_size(socket_map) >= 2)
        {
            for(i = 0; i < ds_map_size(socket_map); i++)
            {
                socket_in_use = ds_map_find_value(socket_map, i);
                network_send_packet(socket_in_use, send_buffer, buffer_get_size(send_buffer));
            }
        }
     
        //In the client_data map, delete from client data the disconnecting client's ID along with their name (ID being the key and username being its string value)
        ds_map_delete(client_data, client_id);
     
        //Remove the disconnecting player socket from the socket list
        ds_map_delete(socket_map, client_socket_current);
     
        //Updating the amount of players that are connected in the client amount variable
        client_amount = ds_list_size(socket_map);
     
        buffer_delete(send_buffer);
    break;

Below the above, is the code for getting client's data and sending it to other clients:
Code:
//case for receiving and sending data
    case network_type_data:
        //Identify the sender client
        client_socket_current = async_load[? "id"];
        //Identify buffer sent
        buffer = async_load[? "buffer"];
     
        //Read the buffers sent by the joining client
        buffer_seek(buffer, buffer_seek_start, 0);
        var buffer_type_read = buffer_read(buffer, buffer_u8); //Read's the initial integer sent, identifying the buffer type
        switch(buffer_type_read)
        {
            //sending buffer type 1 (player enter)
            case 1:
                /*
                buffer_seek(my_buffer, buffer_seek_start, 0);
                show_debug_message(string( buffer_read(my_buffer, buffer_u8)));
                show_debug_message(string( buffer_read(my_buffer, buffer_string)));
                show_debug_message(string( buffer_read(my_buffer, buffer_u16)));
                */
                buffer_seek(buffer, buffer_seek_start, 0);
                var client_id = buffer_read(buffer, buffer_string); //Client's ID number
                var player_username = buffer_read(buffer, buffer_string); //Client's username
             
                //Add new player socket to the socket list
                ds_map_add(socket_map, client_socket_current, client_id);
             
                //In the client_data map, create a new key with the ID of the player, which will hold onto the user's username
                client_data[? string(client_id)] = player_username;
             
                //Defining a temporary variable for the buffer number
                var buffer_type_write = 1;
             
                //create a buffer with which to send information
                send_buffer = buffer_create(256, buffer_fixed, 1);
             
                //writing data to send to buffer
                buffer_seek(send_buffer, buffer_seek_start, 0);
                buffer_write(send_buffer, buffer_u8, buffer_type_write);
                buffer_write(send_buffer, buffer_string, client_id);
                buffer_write(send_buffer, buffer_string, player_username);
                buffer_write(send_buffer, buffer_u8, client_amount);
             
                //If there are now 2 or more users connected, send data about the new client to other clients
                if (ds_map_size(socket_map) >= 2)
                {
                    for(i = 0; i < ds_map_size(socket_map); i++)
                    {
                        socket_in_use = ds_map_find_value(socket_map, i);
                        network_send_packet(socket_in_use, send_buffer, buffer_get_size(send_buffer));
                    }
                }
             
                buffer_delete(send_buffer);
            break;

Meanwhile, client... doesn't display any messages for me, debug or not, which makes me conclude that the client doesn't even send anything to the server. If it did, the debug messages would be displayed, which they are not. Here's the switch statement code, along with some code at the top before, just in case:
Code:
type = async_load[? "type"];

switch(type)
{
    //Sending data upon connection
    /*
    var my_buffer = buffer_create(1, buffer_grow, 1);
 
    buffer_seek(send_buffer, buffer_seek_start, 0);
    buffer_write(send_buffer, buffer_u8, 1);
    buffer_write(send_buffer, buffer_string, "Testing");
    buffer_write(send_buffer, buffer_u16, 12345);
    */
 
    //Sending and receiving data
    case network_type_data:
    buffer = async_load[? "buffer"];
 
    switch(initialize)
    {
        case 1:
            buffer_seek(send_buffer, buffer_seek_start, 0);
            buffer_write(send_buffer, buffer_u8, buffer_type_write);
            show_debug_message("this client's ID" = myclient_id);
            show_debug_message("this client's username" = myclient_id);
            buffer_write(send_buffer, buffer_string, myclient_id);
            buffer_write(send_buffer, buffer_string, myplayer_username);
         
            network_send_packet(socket_plug, send_buffer, buffer_get_size(send_buffer));
            //network_send_raw(socket_plug, my_buffer, buffer_sizeof(my_buffer));
         
            //message = myplayer_username + " joined";
            //joined = true;
            initialize = false;
        break;
    }

The switch statement is supposed to be triggered by a variable in create event, which is set to 1, so the case 1 in the switch statement should run, but it doesn't. Here's the client's create event code:
Code:
///Initializing connection socket
{
joined = false;
playerjoined = false;
playerleft = false;

//message = "";

x_previous = x+1;
y_previous = y+1;
x_current = x;
y_current = y;
send_data = 0;

port = 50000;
IP = "127.0.0.1";
socket_plug = network_create_socket_ext(network_socket_tcp, 4990);
send_buffer = buffer_create(256, buffer_fixed, 1);
network_connect(socket_plug, IP, port);
client_amount = 0;
client_data = ds_map_create();

myclient_id = "1234567890123456";//ini_read_string("Profile Data", "password", noone);
myplayer_username = "Scorpion";

initialize = 1;
}

I know that this is only one part of the issue, since I can't get key ids in any way, other than inefficient way (why, yoyo games, why?!), but it's the first part of the issue, it seems. So, any idea why this might be happening? I'll also give the server's create event code, just in case:

Code:
//declaring the type of connection (tcp)
server_type = network_socket_tcp;
//port number
port = 50000;
//max amount of clients that can connect to the server
max_players = 8;
//generating a server
server = network_create_server(server_type, port, max_players);

//ds_list to hold a list of client sockets into
socket_map = ds_map_create();
//creating an integer variable, which will define how many players have joined
client_amount = 0;
//ds_map in which to hold
client_data = ds_map_create();


//create a buffer with which to send information
//send_buffer = buffer_create(256, buffer_fixed, 1);

I would highly suggest putting this code in GMS, just to be able to read it easier, btw, but up to you. Keeping all the commented stuff in here, in case, for whatever reason it's relevant.
 
Last edited by a moderator:
X

XirmiX

Guest
@PNelly and @Tsa05 yes!!! The client networking code is not being executed for some reason! But the create event code IS! I placed the creation of a player object in create event and it spawned! Where as if I place it in networking event it doesn't. It's all to do with the client's networking event! The fault is in the client's networking event not being executed... But why? I don't understand! If you need, I can give you all of the current code I have. I know what is happening but I don't understand why, please help me out on this, I think I'm SO close to resolving this problem and then being able to move on and start building the game!
 
X

XirmiX

Guest
I have solved the issue(s). The issues were:

* Client was not sending any information to the server, because APPARENTLY the server needs to let the client know that it's connected first (i.e. needs to send even an 8-bit integer, for example) before the client can start sending data properly to the server. Seems a bit unnecessarily bull💩💩💩💩, but then what do I know. At least it works now.
* The server was resetting the reading of the sent buffer twice.
* The server was taking the value of a ds_map's key and not the key name itself to send to the clients. Solved using ds_map_find_first and ds_map_find_next. A pretty complex algorithm is what I needed to use, because ds_map_find (finding a specific key from a ds_map) is not a feature that exists in game maker. And I'm still annoyed by that, because the issue could have been solved much easier and I wouldn't have had to write a complex algorithm to fix this issue. Yoyo games pls!

EDIT: now that I think about it, the third thing might have been me just being a bit of an idiot... what are other clients going to do with your socket number? It's the ID they need to kno... goddamit! The server needs to delete the key after, that's why I needed to know the key! The issue might still be lingering around, but I will see, now that the client is actually sending data to the server.
 
Last edited by a moderator:
Top