GML Can't find string value...

X

XirmiX

Guest
Again... but this time I'm 100% sure it's supposed to retrieve exactly what I tell it to retrieve. Getting this 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 126) - buffer_write(load_buffer, buffer_string, client_id_load);
############################################################################################

And essentially here, I'm supposed to have the code retrieve a string value. This string value is stored upon a client joining the server, for each client, it's even generated by the server itself. And it's saved as a string:
Code:
//case for the client connecting
    case network_type_connect:
        //Identify the connecting socket
        client_socket_current = async_load[? "socket"];
       
        var client_id = "";
       
        file_id = file_text_open_read("id_picker.txt");
        var str = file_text_read_string(file_id);
        for(i = 0; i < 8; i++)
        {
            var j = string_copy(str, irandom(string_length(str)), 1);
            client_id += j;
        }
        file_text_close(file_id);
       
        socket_map[? client_socket_current] = string(client_id);
       
        client_amount = ds_map_size(socket_map);
    break;
This string is meant to be retrieved by the server from each already-joined player and sent to the currently joining player. This piece of code only runs if there are 2 or more clients on the server:
Code:
//Defining a temporary variable for the buffer number
                var buffer_type_write = -3;
               
                //Updating the amount of players that are connected in the client amount variable
                client_amount = ds_map_size(socket_map);
               
                //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)
                {
                    var client_id_this = ds_map_find_value(socket_map, client_socket_current);
                   
                    //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_s8, buffer_type_write);
                    buffer_write(send_buffer, buffer_string, client_id_this);
                    buffer_write(send_buffer, buffer_u8, client_amount);
                    buffer_write(buffer, buffer_f32, xx);
                    buffer_write(buffer, buffer_f32, yy);
                    buffer_write(buffer, buffer_f32, angle);
                    for(i = 0; i < ds_map_size(socket_map); i++)
                    {
                        socket_in_use = ds_map_exists(socket_map, i);//ds_map_find_value(socket_map, i);
                        if(i != client_socket_current)
                        {
                            network_send_packet(socket_in_use, send_buffer, buffer_get_size(send_buffer));
                        }
                    }
                    buffer_delete(send_buffer);
                    
                    //Send data to the about the existing clients to the new client
                    load_buffer = buffer_create(256, buffer_fixed, 1);
               
                    for(i = 0; i < ds_map_size(socket_map); i++)
                    {
                        //socket_in_use = ds_map_exists(socket_map, i);
                       
                        var client_id_load = ds_map_find_value(socket_map, i);
                       
                        buffer_seek(load_buffer, buffer_seek_start, 0);
                        buffer_write(load_buffer, buffer_s8, buffer_type_write);
                        buffer_write(load_buffer, buffer_string, client_id_load);
                        buffer_write(load_buffer, buffer_u8, client_amount);
                        buffer_write(load_buffer, buffer_f32, xx);
                        buffer_write(load_buffer, buffer_f32, yy);
                        buffer_write(load_buffer, buffer_f32, angle);
                        if(i != client_socket_current)
                        {
                            network_send_packet(client_socket_current, load_buffer, buffer_get_size(load_buffer));
                        }
                    }
                }
I previously had this error outside of the 2-player check thing, but had it when one player joined. That was understandable, since there were no other players, so the server had nothing to retrieve. But since after I changed the code to have this included in the 2-or-more player check statement, it shouldn't be whining about it and should execute it just fine, but for whatever reason, it's not!

Previously I had a similar problem, however, previously, my mistake was that I needed to check for a key and not the key's value. This time, I know I need to check the value, and here it's returning that it's not a string. Now that I think about it, the previous issue wasn't resolved... I simply didn't need the information that I was getting, so as soon as I changed that, everything went hey. But now that I do need it, everything goes haywire. Please help! :(
 

chamaeleon

Member
And you're 100% sure that you use the same keys when storing values in your map as you do when you look them up, and that every return value from the lookup is in fact a string and not a value that is_undefined() would return true for (the manual pages, which I shall assume you have carefully read, indicate that you should most definitely check the validity of what you get from lookup using is_undefined())? All of this, I will assume, have been verified with proper debugging?
 
W

Wraithious

Guest
It may be that you are not checking if ( file_exists( "id_picker.txt" ) && case
network_type_connect )
{
//do stuff with the file
}
If the file doesn't exist it will either crash when you try to use the file or it might do the nightside slide and return -1 which your buffer expecting a string gets angry and crashes. The only time you don't need to check is if you are writing to a file and you don't care if it exists or not (sometimes you would want to check in case you don't want to overwrite the file if it's allready there)
 
T

TimothyAllen

Guest
Code:
client_socket_current = async_load[? "socket"];
// ...
socket_map[? client_socket_current] = string(client_id);
Code:
for(i = 0; i < ds_map_size(socket_map); i++)
{
    //socket_in_use = ds_map_exists(socket_map, i);
    var client_id_load = ds_map_find_value(socket_map, i);
Why are you so sure that the socket ids being returned from the network functions are between 0 and your map size? I suspect this is your problem and the ds_map is returning an undefined value (as chamaelon stated)
 
X

XirmiX

Guest
Why are you so sure that the socket ids being returned from the network functions are between 0 and your map size? I suspect this is your problem and the ds_map is returning an undefined value (as chamaelon stated)
I was suspecting this actually. I find it odd that with the ds_map_exists as opposed to ds_map_find_value, there's no error. i must be the key position and not the key itself. Any way I can make a check for what variable is at a certain position (the position being i)?

And you're 100% sure that you use the same keys when storing values in your map as you do when you look them up, and that every return value from the lookup is in fact a string and not a value that is_undefined() would return true for (the manual pages, which I shall assume you have carefully read, indicate that you should most definitely check the validity of what you get from lookup using is_undefined())? All of this, I will assume, have been verified with proper debugging?
Yeap, it's undefined and I can only guess it's because i represents the key position and not the key name/key itself.
upload_2017-8-2_12-28-13.png

upload_2017-8-2_12-28-32.png

Not sure how I didn't get an error with ds_map_exists then. May be it can take in positions as well, but ds_map_find_value can't?
 

PNelly

Member
I was suspecting this actually. I find it odd that with the ds_map_exists as opposed to ds_map_find_value, there's no error. i must be the key position and not the key itself. Any way I can make a check for what variable is at a certain position (the position being i)?


Yeap, it's undefined and I can only guess it's because i represents the key position and not the key name/key itself.

Not sure how I didn't get an error with ds_map_exists then. May be it can take in positions as well, but ds_map_find_value can't?
ds_map_exists() returns true/false, it won't throw an error. Couple that with GML's non-static typing and you can force some pretty weird stuff to happen. Technically it's more correct to say ds_map_exists() returns 0 or 1. If you're storing the result of ds_map_exists() in a variable, and it returns "false" (meaning 0), you can then perform calculations with that value, use it as a data structure index, all sorts of things that don't make any sense. Might help explain some of the behavior.

ds_map_find_value() however, will throw and error if you try and access a key that doesn't exist.

EDIT: @TimothyAllen 's got me on the documentation. It'll return undefined instead of throwing an error, and then you will get an error if/when you try to use the undefined value incorrectly.

In regards to non-static typing, I'm pretty sure the following will run without complaint, even though it's useless and confusing:
Code:
my_list = ds_list_create(); // returns an integer id
my_bool = true;                 // true == 1
my_socket = network_socket_create(network_socket_tcp) // returns an integer id

my_strangeness = my_bool * my_list - sqrt(my_socket);

show_debug_message("the strangeness is"+string(my_strangeness));
// will probably spit out zero in a blank project
 
Last edited:
T

TimothyAllen

Guest
ds_map_find_value() however, will throw and error if you try and access a key that doesn't exist.
ds_map_find_value will return undefined when accessing a key that doesnt exist. It will not error.
 
X

XirmiX

Guest
@PNelly huh, so ds_map_exists() doesn't even return an integer, but a boolean. So, in my cases, what can I use?

What can I use instead of ds_map_exists to identify the name of a socket, while scrolling through a map with socket ids as keys?
And in another scenario, how can identify the name of a key from which to select a string, while scrolling through a map with socket ids as keys?

ds_map_find_value will return undefined when accessing a key that doesnt exist. It will not error.
Well... I got an error with that.
 

PNelly

Member
@PNelly huh, so ds_map_exists() doesn't even return an integer, but a boolean. So, in my cases, what can I use?

What can I use instead of ds_map_exists to identify the name of a socket, while scrolling through a map with socket ids as keys?
And in another scenario, how can identify the name of a key from which to select a string, while scrolling through a map with socket ids as keys?


Well... I got an error with that.
There aren't really integers or booleans per se in GM (with a small amount of wiggle room when it comes to buffers and few other things). Almost everything is treated as a floating point. See "Data Types" in the manual for more details.

another eyebrow raising example:
Code:
if(2.837) // all values >= 0.5 evaluate to "true"
     // do some stuff

if !(0.123) // all values < 0.5 evaluate to "false"
ds_map_exists() returns 0 or 1. We then treat that abstractly as true or false.

I'm not really sure what you mean by "identify the name of a socket". For a GM tcp server (what you're using), async_load[] provides you with the socket id of the client that sent the data. It's really all you need to build references to client data and access it later.

On connect:
Code:
var _new_client = async_load["socket"];
var _new_client_map = ds_map_create();

ds_map_add(_new_client_map, "ip", async_load["ip"]); // just adding some stuff to the client data map by way of example
ds_map_add(_new_client_map,"port", async_load["port]);

// for example assume there is already a ds_map called client_map_of_maps
ds_map_add(client_map_of_maps, _new_client, _new_client_map); // can now use socket id as a map key, to grab another ds_map containing client data
then when receiving data:
Code:
var _client_that_sent_data = async_load["id"];
var _map_associated_with_client_that_sent_data = ds_map_find_value(client_map_of_maps, _client_that_sent_data);

// can now use ds_map_find_value(_map_associated_with_client_that_sent_data, <some key> ) to get data particular to that client
 
T

TimothyAllen

Guest
@PNelly huh, so ds_map_exists() doesn't even return an integer, but a boolean. So, in my cases, what can I use?

What can I use instead of ds_map_exists to identify the name of a socket, while scrolling through a map with socket ids as keys?
And in another scenario, how can identify the name of a key from which to select a string, while scrolling through a map with socket ids as keys?


Well... I got an error with that.
No you didnt. You got an error because you tried to used the returned value (which is likely undefined in your case) where a string is expected.
My advice is to have a list of the connected sockets in a simple ds_list and just iterate the list to send a packet to all the clients.
 
X

XirmiX

Guest
No you didnt. You got an error because you tried to used the returned value (which is likely undefined in your case) where a string is expected.
My advice is to have a list of the connected sockets in a simple ds_list and just iterate the list to send a packet to all the clients.
I have a ds_map instead of that, and the key is the socket and the value is a string, currently created by the server itself upon a client joining. Since I add the client to the socket list before I send information to other clients about it, I need to be able to identify socket names and ids and such.

I guess I could update the list after all the information is sent to all other clients, but that doesn't seem like a good idea. And still wouldn't work with ds_map_find_value-using for loop, only the ds_map_exists-using for loop.

@PNelly all I'm asking is how I would set a variable to the name of a key in a certain position (position being i, if you've read my previous code) and additionally, how I would set a variable to the value of a key in a certain position (position, again, being i). And okay, here is what I picture a ds_map to look like:

[the_ds_map]
key_name = "the_value";
key_name_2 = "the_value_2";
key_name_3 = "the_value_3";

Essentially, I imagine it as a part of an unwritten ini file, cause that makes the most sense. Now, you see for instance key_name_2? Yeah, how do I retrieve that from a ds_map? Not its position in the ds_map, not its value, but the name of the key. Moreover, if it's always a string, then I'll probably need to convert it to whatever type of a value a socket id is if it's not a string, which I believe is integer (This isn't explicitly clear on that).

On connect:
Code:
var _new_client = async_load["socket"];
var _new_client_map = ds_map_create();

ds_map_add(_new_client_map, "ip", async_load["ip"]); // just adding some stuff to the client data map by way of example
ds_map_add(_new_client_map,"port", async_load["port]);

// for example assume there is already a ds_map called client_map_of_maps
ds_map_add(client_map_of_maps, _new_client, _new_client_map); // can now use socket id as a map key, to grab another ds_map containing client data
then when receiving data:
Code:
var _client_that_sent_data = async_load["id"];
var _map_associated_with_client_that_sent_data = ds_map_find_value(client_map_of_maps, _client_that_sent_data);

// can now use ds_map_find_value(_map_associated_with_client_that_sent_data, <some key> ) to get data particular to that client
That I might want to get onto later on. Right now I'm not wanting to store any data about players, the server has instances of its own that it can retrieve the coordinates and rotational positions of already. In fact, I might not need to have to store any ds_maps inside ds_maps as a result regardless. May be, will see, but right now the above doesn't seem to be concerning anything that the thread is really about.
 
T

TimothyAllen

Guest
I have a ds_map instead of that, and the key is the socket and the value is a string, currently created by the server itself upon a client joining. Since I add the client to the socket list before I send information to other clients about it, I need to be able to identify socket names and ids and such.
I realize that. But you have no guarantee that your socket ids will be 0, 1, 2, ..., (map_size). Your system relies on something that isnt guaranteed nor documented. You simply know that a socket id will be >= 0. Reguardless whether or not its your actual issue, I don't think its safe to make the assumption you are.

all I'm asking is how I would set a variable to the name of a key in a certain position (position being i, if you've read my previous code) and additionally, how I would set a variable to the value of a key in a certain position (position, again, being i). And okay, here is what I picture a ds_map to look like:

[the_ds_map]
key_name = "the_value";
key_name_2 = "the_value_2";
key_name_3 = "the_value_3";

Essentially, I imagine it as a part of an unwritten ini file, cause that makes the most sense. Now, you see for instance key_name_2? Yeah, how do I retrieve that from a ds_map? Not its position in the ds_map, not its value, but the name of the key.
ds_maps are not ordered. You can iterate them using the ds_map_find_first / ds_map_find_next functions, but it is going to be slow. But, If you dont have too many clients, then iterating the map probably wouldnt make much of a difference.
 
X

XirmiX

Guest
I realize that. But you have no guarantee that your socket ids will be 0, 1, 2, ..., (map_size). Your system relies on something that isnt guaranteed nor documented. You simply know that a socket id will be >= 0. Reguardless whether or not its your actual issue, I don't think its safe to make the assumption you are.
I need to be able to store them in VARIABLES and then check for those VARIABLES. I don't need to know the name of the socket, the program does, and so I need to set the code in a way that does this for it so that it can do what I want it to do.

Okay, so I don't make the assumption, even after debugging seems to secure it a bit better. Then what do I do? Nothing? That's very helpful.

ds_maps are not ordered. You can iterate them using the ds_map_find_first / ds_map_find_next functions, but it is going to be slow. But, If you dont have too many clients, then iterating the map probably wouldnt make much of a difference.
ds_maps are not ordered? Do you mean their CONTENTS are not ordered? If their contents aren't ordered then it's irrelevant. If ds_maps aren't ordered themselves, is what you mean, then I have no idea what you're talking about. I just need to get a way to store in a variable the name of a key. Do I need to use the ds_map_find_first / ds_map_find_next to do this? If so, then I'll go back and write/copy over that complex algorithm I wrote earlier that involved these two functions at some point in time, though I'd rather not have to do this. I just need to store the name of a key at a specified position (indicated with i in a for loop) in a variable!
 

chamaeleon

Member
I just need to store the name of a key at a specified position
You don't use ds_maps that way. It's not an array or a ds_list. A ds_map entry doesn't have a "position". It has a key. The statement "name of a key at a specified position" makes no sense. You can iterate over all keys and do something with the key, or its associated value by looking up the value for the given key in your iteration process. But please don't run down that road by attempting to iterate i times to get the i'th key. That would be an abuse of the concept represented by a map.
 
T

TimothyAllen

Guest
ds_maps are not ordered? Do you mean their CONTENTS are not ordered?
This will explain it better (from the manual)
There are a couple of things you should know about maps before you use them, however! Maps are not sorted in any (recognisable) way, meaning that to find a certain key you may have to iterate through the whole thing (which is very slow).
 

chamaeleon

Member
And a common corollary to maps being unordered, if you add an item to a map, it isn't entirely predictable in which order you will get something out if you iterate over the keys.

Example: A ds_map with key values between 0 and 7, with one line per addition made and the sequence in which the keys are being returned by ds_map_find_first() and ds_map_find_next()
Code:
var m = ds_map_create();

for (var i = 0; i < 8; i++)
{
    var s = "";
    ds_map_add(m, i, i);

    var k = ds_map_find_first(m);
    while (!is_undefined(k))
    {
        if (s != "") s += ", ";
        s += string(k);
        k = ds_map_find_next(m, k);
    }
    show_debug_message(s);
}
Code:
0
1, 0
2, 1, 0
2, 3, 1, 0
2, 3, 1, 4, 0
2, 3, 1, 5, 4, 0
2, 3, 1, 5, 4, 6, 0
2, 3, 1, 5, 4, 7, 6, 0
So for example, after having added 0 through 4 (line 5 above), you can see 4 is at "position" 3 (if we use 0-based offsets). After adding 5, key 4 is now in "position" 4. Positions simply are not conceptually appropriate in the context of maps.
 
Last edited:
X

XirmiX

Guest
@chamaeleon and @TimothyAllen thank you, that was helpful, I now understand better what you, some others, and by extension the manual, meant with this. I guess that complex algorithm will have to be put in because of this.
 
Top