GameMaker Saving DS Lists with JSON

Tobey

Member
I've been reworking my save system from .ini to using json encoding. My game has a bunch of important global variables and DS lists which contain the inventory (there are arrays within that), completed puzzles and killed entities. I'm able to save and load the global variables in game but when I close the game and re-launch it, it gives me this error: "Data structure with index does not exist". I'm also getting an error ("I32 argument is array") when I try to load my inventory which I'm assuming is due to the inventory list containing arrays (each array has an item and then the amount of that item).

What I need to know:
- Should I just use .ini which I do have a working system for?
- If I should use JSON, how do I save DS lists with arrays in them and...
- how to prevent this "Data structure with index does not exist" error from occurring?

Save Script Code:
GML:
var _save = ds_list_create();
var _config = ds_list_create();
var _dungeon_id = global.dungeon_id;
var _items = global.items;
var _killed_entites = global.killed_entities;

ds_list_mark_as_map(_save, 0);
ds_list_mark_as_map(_config, 0);
ds_list_mark_as_map(_dungeon_id, 0);
ds_list_mark_as_map(_items, 0);
ds_list_mark_as_map(_killed_entites, 0);

ds_list_add(_save,
    health,
    global.max_health,
    global.defence,
    global.guilt,
    global.magic,
    global.stamina,
    global.coins,
    global.ammo,
    global.ammo_capacity,
    global.keys,
    global.destination_room,
    global.destination_x,
    global.destination_y,
    global.destination_dir,
    global.items_obtained,
    global.in_dungeon,
    global.cycle,
    global.minute,
    global.hour,
    global.day,
    global.season,
    global.clock_timer,
    global.current_song,
    global.previous_song
);

ds_list_add(_config,
    global.control_text_enabled,
    global.sfx_volume,
    global.bgm_volume,
    window_get_fullscreen()
);

var _root = ds_map_create();
ds_map_add(_root, "main", _save);
ds_map_add(_root, "config", _config);
ds_map_add(_root, "dungeon_id", _dungeon_id);
ds_map_add(_root, "items", _items);
ds_map_add(_root, "killed_entities", _killed_entites);

//ds_map_secure_save(_root, "Save.save");

var _json = json_encode(_root);

//SAVE TO FILE
var _filename = "Save.save";
var _string = _json;

var _buffer = buffer_create(string_byte_length(_string) + 1, buffer_fixed, 1);
buffer_write(_buffer, buffer_string, _string);
buffer_save(_buffer, _filename);
buffer_delete(_buffer);

ds_map_destroy(_root);
Load Script Code:
GML:
if(file_exists("Save.save")){
    var _filename = "Save.save";

    var _buffer = buffer_load(_filename);
    var _string = buffer_read(_buffer, buffer_string);
    buffer_delete(_buffer);

    var _save_file = json_decode(_string);

    var _main = _save_file[? "main"];
    var _config = _save_file[? "config"];
    var _dungeon_id = _save_file[? "dungeon_id"];
    var _items = _save_file[? "items"];
    var _killed_entites = _save_file[? "killed_entities"];
    
    show_debug_message(ds_list_find_value(_main, 0))
    
    health = ds_list_find_value(_main, 0);
    global.max_health = ds_list_find_value(_main, 1);
    global.defence = ds_list_find_value(_main, 2);
    global.guilt = ds_list_find_value(_main, 3);
    global.magic = ds_list_find_value(_main, 4);
    global.stamina = ds_list_find_value(_main, 5);
    global.coins = ds_list_find_value(_main, 6);
    global.ammo = ds_list_find_value(_main, 7);
    global.ammo_capacity = ds_list_find_value(_main, 8);
    global.keys = ds_list_find_value(_main, 9);
    global.destination_room = ds_list_find_value(_main, 10);
    global.destination_x = ds_list_find_value(_main, 11);
    global.destination_y = ds_list_find_value(_main, 12);
    global.destination_dir = ds_list_find_value(_main, 13);
    global.items_obtained = ds_list_find_value(_main, 14);
    global.in_dungeon = ds_list_find_value(_main, 15);
    global.cycle = ds_list_find_value(_main, 16);
    global.minute = ds_list_find_value(_main, 17);
    global.hour = ds_list_find_value(_main, 18);
    global.day = ds_list_find_value(_main, 19);
    global.season = ds_list_find_value(_main, 20);
    global.clock_timer = ds_list_find_value(_main, 21);
    global.current_song = ds_list_find_value(_main, 22);
    global.previous_song = ds_list_find_value(_main, 23);
    
    global.control_text_enabled = ds_list_find_value(_config, 0);
    global.sfx_volume = ds_list_find_value(_config, 1);
    global.bgm_volume = ds_list_find_value(_config, 2);
    window_set_fullscreen(ds_list_find_value(_config, 3));
    
    global.dungeon_id = _dungeon_id;
    
    ds_list_clear(global.items);
    global.items = _items;
    
    global.killed_entities = _killed_entites;
    
    if(instance_exists(obj_player)){
        obj_player.x = global.destination_x;   
        obj_player.y = global.destination_y;   
    }


} else {
    show_debug_message("File does not exist!");   
}
 
You're using ds_list_mark_as_map incorrectly. Your trying to tell the child that it has a parent, when you should be telling the parent that it has a child (in this case your map is the parent and the lists are the children. Use ds_map_add_list instead. Like this.
GML:
ds_map_add_list(_root, "main", _save);
ds_map_add_list(_root, "config", _config);
ds_map_add(_root, "dungeon_id", _dungeon_id);
ds_map_add(_root, "items", _items);
ds_map_add(_root, "killed_entities", _killed_entites);
And remove the the lists that are saying they have a map inside them.
GML:
/*ds_list_mark_as_map(_save, 0);
ds_list_mark_as_map(_config, 0);
ds_list_mark_as_map(_dungeon_id, 0);
ds_list_mark_as_map(_items, 0);
ds_list_mark_as_map(_killed_entites, 0);*/
 

Tobey

Member
That fixed the "I32 argument is array" error and I managed to get my data to properly load but I have a new issue. My inventory DS list contains arrays. Each array contains an item and quantity of that item. The problem is that these arrays are all being turned into DS lists within the parent item DS list. Is there a way to prevent this from happening?
 
Last edited:
Top