2 Scripts to save/load 1D array as ds_list string. But why does load script fail?

P

ph101

Guest
Hi guys

Please can a more experienced GMLer roll up their sleeves and get their hands dirty with my 2 scripts for saving and loading a 1D array into an already open ini?

They are very simple -

Save loops through a 1D array and populates a temp ds_list before writing as a string with ds_list_write.
Load reads said string using ds_list_read into a temp list then loops through the length populating a 1D array.

Save works as intended, I can see a string has been made in the ini. Problem is, load is not appearing to work and I wondered if anyone could say why? I realise I should just not use arrays and purely rely on lists. I may have to if I can't fix this, but arrays are already embedded in a fair amount of my code, and really this should work...

Plus The scripts may help others.. if they worked of course...
Code:
///scr_save_1d_array(array_name, section, array_key_str)
//converts a 1d array into a list, saves the list in the open ini as a string
//assumes ini is already opened
var array_name = argument0; //name of our array to save
var array_section = argument1;
var array_key_str = argument2; //str to use as key to save a retriue3v
var length; //length of array
var temp_list_name; //temp list name
var i; //loop
var temp_list_str; //stores the list string

length = array_length_1d(array_name); //get length of 1d array
temp_list = ds_list_create(); //temp list to store array so we can write it

//output the array to a list
for (i = 0; i < length; i += 1) //global.gridWidth
  {
  ds_list_add(temp_list, array_name[i]); //add each array value to list in turn up to array length
  }

//convert the list to a str for saving in ini
temp_list_str = ds_list_write(temp_list);

//save the string as per arguments - note the INI MUST ALREADY BE OPEN
ini_write_string(array_section, array_key_str, temp_list_str);

ds_list_destroy(temp_list); //we are done with the list now, destroy
So I am saving an array called global.have_room thusly
Code:
scr_save_1d_array(global.have_room, 'general', '_1d_array_have_room');
And the load script, which fails to populate my array.

Code:
///scr_load_1d_array(array_name, section, array_key_str)
//load a previously saved str of a list and populates a 1d array with each value
//assumes ini is already opened
var new_array;
var array_section = argument0;
var array_key_str = argument1; //str to use as key to save a retriue3v
var length; //length of array
var temp_list_name; //temp list name
var i; //loop
var temp_list_str; //stores the list string

//create temp list
temp_list = ds_list_create();

//load in the encoded saved string - NOTE THE INIT IS ALREADY OPEN and is closed elsewhere
temp_list_str = ini_read_string(array_section, array_key_str, 0);

//load the str into the temp list
ds_list_read(temp_list,temp_list_str);

//get lenth of the list now it has valuesin
length = ds_list_size(temp_list);

//output the list into the stated array
for (i = 0; i < length; i += 1) //global.gridWidth
    {
    new_array[i] = ds_list_find_value(temp_list, i); //grab each value in turn and put into array
    }

ds_list_destroy(temp_list); //we are done with the list now, destroy

return new_array; //return the whole array
I'm calling the load scr to populat the array thusly

Code:
global.have_room = scr_load_1d_array('general', '_1d_array_have_room');
I tried using pointers to fill the array directly in load also to no avail so i'm really stumped. Would really appreciate some help, thank you :)
 
Last edited by a moderator:
The code looks great. Have you tried debugging it? For example, I would place a "show_message("Length: "+string(length));" right after length is set to the ds_list_size(....). Then if it shows a message with the correct length, you know it's an issue with the last part of the code.

If it is an issue with the last part, I'd look into this, from the manual:
Arrays
Arrays also have their own accessors which works in a similar way as those listed above for data structures. however array accessors have an interesting property and that is to permit you to modify an array from a script without having to copy it. When you pass an array into a script, it is passed by reference, meaning that the array itself isn't being copied into the script but rather it is simply being referenced to get the data. Normally if you then need to change the array, it would be copied to the script and then you would need to pass back the copied array for the original array to be updated. This can have costly processing overheads, and you can use the accessor instead, as that will change the original array directly without the need for it to be copied. You can see how this works in the examples below.

The syntax for arrays is:
array[@ xpos]
array[@ xpos, ypos]

After you have created your array in an instance, you can then pass it to a script by reference and use the accessor @ to change it directly. For example you would create the array and call the script like this:
array[99] = 0;
scr_Array_Populate(array);

The script itself would have something like this:
var a = argument0; var i = 0; repeat(25)
{
i = irandom(99);
while (a != 0)
{
i = irandom(99);
}
a[@ i] = 100;
}

All this script is doing is checking the value of the array at a given position and if it is zero, it assigns a random value directly to the array.
 
P

ph101

Guest
Hi there,

Thanks for your quick reply! I took your advice and examined these variables more closely, especially length. Turns out, they were all as expected. In fact - it turns out the code works exactly as intended :p It's bug free I believe, so i'm quite happy. It was some other typo-like error leading me to believe it was not functioning, but it actually works fine.

With regards the use of pointers for arrays in script. Yes, very good to know that stuff and I could use that alternative approach - but because I'm calling the script by returning/defining the array it's not necessary here. Thanks again!
 
If you are just looking to turn the array into an easy to save and load string, instead of lopping through the array and putting each index into a matching index in the DS list, just put the entire array in index 0 of the list then use DS_list_read/write. It works fine without any hassle. I had a pair of scripts on the old forum that did that.

Roughly:
Code:
my_array[0] = "Hello world!";
my_array[1] = 9999;

temp_list[| 0] = my_array;
save_str = DS_list_write(temp_list);

// later...

DS_list_read(temp_list, save_str);
saved_array = temp_list[| 0];

Show_message(saved_array[0]);  // shows "Hello world!"
Sorry about the random caps. on my phone right now.
 
P

ph101

Guest
Wow really!? Oh well I feel dumb now! I really thought that approach would just save a reference to the array, not entire array. Good experience though. I also need to come up with an equivalent scr to save 2D arrays. I can test this - but in case you know it would help to understand if its possible before trying - would your appropach of bunging a 2D array in a list also work in your view?
 
Yes. this trick will work not only for 2d arrays, but also nested arrays. it probably works in most other DS types as well, though I've only tested grids and maps, and maps don't work. maps only save the array pointer which is useless.
 
J

jason_Evans

Guest
I am trying to implement this but I am having load problems also. I am not using arguments, though, so my end script is only () instead of the 3 arguments in the save. But it saves. It just does not load. It says
Code:
############################################################################################
FATAL ERROR in
action number 1
of Mouse Event for Left Pressed
for object ob_save_1:

local variable newArray(100001, -2147483648) not set before reading it.
 at gml_Script_sc_weaponsinv_load (line 22) - return newArray;
############################################################################################
--------------------------------------------------------------------------------------------
stack frame is
gml_Script_sc_weaponsinv_load (line 22)
called from - gml_Object_ob_save_1_LeftButtonPressed_1 (line 4) - sc_weaponsinv_load();
and my save script and load script are similar (without comments just because I wanted to see if it even worked since I cant find ANYTHING on saving 1d arrays and I have experimented with no luck for weeks).

save sc:
Code:
ini_open("tempList.ini");

var length;
length = array_length_1d(global.inventory[i]);


tempList = ds_list_create();

for (i = 0; i < length; i += 1) {
    ds_list_add(tempList, global.inventory[i]);
}

tempListString = ds_list_write(tempList);

ini_write_string("Inventory", "Weapons", tempListString);

ds_list_destroy(tempList);

ini_close();
load sc:

Code:
ini_open("tempList.ini");

var Length;
var newArray;

tempList = ds_list_create();

tempListString = ini_read_string("Inventory", "Weapons", "na");

ds_list_read(tempList, tempListString);

length = ds_list_size(tempList);

for (i = 0; i < length; i += 1) {
    newArray[i] = ds_list_find_value(tempList, i);
}

ds_list_destroy(tempList);

ini_close();

return newArray;
 
P

ph101

Guest
No...see @stainedofmind post below mine... - you don't need to loop through the whole array to save every cell like I thought, you can just put the whole array var in one spot in a list and save/load it with a few lines of code. Also, I have tosuggest - dont be like me - use lists or, more easily for inventory, Maps, which can be saved and loaded very easily and are usually more powerful and might be better suited to your inventory. Personally, for some reason I avoided them for months when I learned GM, because I was only familiar with arrays from the past, but how foolish of me because they are v easy to use and generally much more versatile..
 
Last edited by a moderator:
Top