SOLVED Saving Ds_lists not working

Gizzmo

Member
I've been having issues with creating a reliable save system for the current project I'm working on. I'm trying to save an array which holds all the players stats, like an rpg. I already have ds_lists set up but I've had this consistent problem:
Once I have the game, storing all the values into the list and load them, If I add an additions/changes to that list, it doesn't save it.

Example:
Lets say the player increases their health to 3, then saves and loads. The health is now 3 and everything works fine
However if the player increases their health to 4 or 5, then saves and loads. The health didn't save and it's still at 3.

Code in a create even that happens at the start of the game:
GML:
//Save file
save_data = ds_map_create();
file_name = "SaveData.sav";
Code for when the player saves:
GML:
//Player stats
var list_stats = ds_list_create();
for (i = 0; i < 6; i++)
    ds_list_add(list_stats,obj_menu_smithing.stats[i])
ds_map_add_list(save_data,"list_stats",list_stats)

ds_map_secure_save(save_data,file_name);
Code for when the player loads:

GML:
if !file_exists(file_name)
    exit;
    
ds_map_destroy(save_data);
save_data = ds_map_secure_load(file_name);

//Player stats
var list = ds_map_find_value(save_data,"list_stats");
for (i = 0; i < ds_list_size(list); i++)
    obj_menu_smithing.stats[i] = list[| i];
I must be missing an important part of the code, like I got to properly remove the old save to write the new one. I'm not sure so any help would be great.
 

samspade

Member
Do you know where the error comes in?
  • Does the correct data go into the list
  • Does the list get added (correctly) into the map
  • Does the data get saved out correctly?
  • What data is actually loaded in?
You can answer questions 1,2, and 4 with the debugger and 3 by inspecting the file that is saved - except that secure_save makes it not human readable, so it might be a good idea to start with json_encode so you get a human readable string and then save that out as text. Once you have the system working you can switch to secure save/load.

Also, as of 2.3.1 I would recommend using structs and arrays over maps and lists but that is (mostly) a matter of personal preference.
 

jo-thijs

Member
I've been having issues with creating a reliable save system for the current project I'm working on. I'm trying to save an array which holds all the players stats, like an rpg. I already have ds_lists set up but I've had this consistent problem:
Once I have the game, storing all the values into the list and load them, If I add an additions/changes to that list, it doesn't save it.

Example:
Lets say the player increases their health to 3, then saves and loads. The health is now 3 and everything works fine
However if the player increases their health to 4 or 5, then saves and loads. The health didn't save and it's still at 3.

Code in a create even that happens at the start of the game:
GML:
//Save file
save_data = ds_map_create();
file_name = "SaveData.sav";
Code for when the player saves:
GML:
//Player stats
var list_stats = ds_list_create();
for (i = 0; i < 6; i++)
    ds_list_add(list_stats,obj_menu_smithing.stats[i])
ds_map_add_list(save_data,"list_stats",list_stats)

ds_map_secure_save(save_data,file_name);
Code for when the player loads:

GML:
if !file_exists(file_name)
    exit;
 
ds_map_destroy(save_data);
save_data = ds_map_secure_load(file_name);

//Player stats
var list = ds_map_find_value(save_data,"list_stats");
for (i = 0; i < ds_list_size(list); i++)
    obj_menu_smithing.stats[i] = list[| i];
I must be missing an important part of the code, like I got to properly remove the old save to write the new one. I'm not sure so any help would be great.
In this line:
GML:
ds_map_add_list(save_data,"list_stats",list_stats)
you're not adding the list to the map, but you're adding the list identifier to the map.
ds_list_create doesn't return a list, but a "real" value (number) that is the identifier of the newly created list.
The first time you execute ds_list_create, it will return 0.
The second time, it will return 1 and so on.
When you close your game, the datastructures are gone and you only saved their identifiers to the file, but that's unsufficient to reconstruct them.

Analoguous to this post on reddit,
you will have to save the list too and one way to do so is by adding lists to the ds_map by ds_map_add_list converting everything to json before saving using json_stringify and converting back from json after loadin using json_parse.
 

Gizzmo

Member
Is there a way to do this on a version before 2.3.1?
I currently am on version 2.3.0, any attempts to update make my project broken in many ways.

Is there a recommended tutorial on json saving before 2.3.1?
 

Gizzmo

Member
I have this one which covers both the 2.2 and 2.3 versions.

There's also this one, which covers the basics:

Json stuff is pretty alien to me so should I just follow along the 3 parts videos on Importing and Exporting Json files and make my save system be all done with Json files?
I really appreciate your help so far.
 

samspade

Member
It's up to you. Saving is always going to be somewhat complicated. It's also unique to your game. But I think that learning how to use JSON formatting is pretty useful for a lot of things.
 

Gizzmo

Member
It's up to you. Saving is always going to be somewhat complicated. It's also unique to your game. But I think that learning how to use JSON formatting is pretty useful for a lot of things.
So I had a go following along with your tutorials and trying to implement them into my project, but I'm stuck when it comes to importing the JSON data into my project as a loading feature.
The goal is to save a variable called "current_area" and then for it be loaded back in.

Saving Code:
GML:
json_export_map = ds_map_create();

json_export_map[? "current_area"] = current_area;

json_export_string = json_encode(json_export_map);

export_json("json_export.txt",json_export_map);
ds_map_destroy(json_export_map);

//json_export_map = import_json("json_export.txt");
//ds_map_destroy(json_export_map);

show_debug_message("Done");
Loading Code:
GML:
json_example_1 = import_json("json_export.txt");
ds_map_destroy(json_example_1);

audio_pause_sound(global.music); //Turn off music
start = 1;
room_goto(current_area);
I'm just unsure how to access the variable saved inside of the DS_map, if I have to do that at all.
I've also got the scripts you recommended in the video.
GML:
function import_json(_file_name) {
    if (file_exists(_file_name)) {
        var _file, _json_string;
        _file = file_text_open_read(_file_name);
        _json_string = "";
        while (!file_text_eof(_file)) {
            _json_string += file_text_read_string(_file);
            file_text_readln(_file);
        }
        file_text_close(_file);
        return json_decode(_json_string);
    }
    return undefined;
}

function export_json(_file_name, _map) {
    var _file = file_text_open_write(_file_name);
    file_text_write_string(_file, json_encode(_map));
    file_text_close(_file);
}

function ds_list_add_list(_id, _list) {
    ds_list_add(_id, _list);
    ds_list_mark_as_list(_id, last_list_index(_id));
}

function ds_list_add_map(_id, _map) {
    ds_list_add(_id, _map);
    ds_list_mark_as_list(_id, last_list_index(_id));
}

function last_list_index(_id) {
    return ds_list_size(_id) -1;
}
 

FrostyCat

Member
The variable assignments won't be done automatically for you at loading time, you have to specify that before removing the map.
GML:
json_example_1 = import_json("json_export.txt");
current_area = json_example_1[? "current_area"];
ds_map_destroy(json_example_1);
And if you do not want to run into major problems with resource ID shifts like this in the future, you should save and load resource names instead. In your case, json_export_map[? "current_area"] = room_get_name(current_area); on the saving side and current_area = asset_get_index(json_example_1[? "current_area"]); on the loading side.
 

samspade

Member
@FrostyCat beat me to it, which is nice since the import function I'm using is his to begin with, but yes, once you load in the data you must access it and use it BEFORE destroying it. It is difficult (probably not impossible) to be generic about how you use the data. It will almost always be faster to do it in a project specific way.

There's an example project in the play list that covers a few uses if you want additional examples. Source code for that project as well: https://forum.yoyogames.com/index.php?threads/json-and-gamemaker-studio-2.78316/. For the source code check the github link in the videos themselves -there's two repos one for the JSON stuff and one for the CYOA example. But again, it is just an example, you need to customize it for the type of data you're saving and loading in your project.
 
Top