Save and load visited checkpoints with config file

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By anssiko

Hi,

I’m trying to save and load visited checkpoints with config file. I’m trying to create a scalable solution, so that I can save other data as well in future. My game is a very simple platformer, so I thought config is a working solution for saving.

At the moment, saving works (for path res://, but no file appears anywhere for path user://) to some extent. Load does not. I am developing on 3.2 and osx.

Considering the code below:

  1. Why is load not working? (I’m trying to run it from _ready, and it swipes clear the config file.)

  2. Why is save not adding to the list, but swiping any previous data clear and then saving only new data when the game is run?

extends Node

const SAVE_PATH = "res://config.cfg"

var level: Node2D = null
var config = ConfigFile.new()
var visited_checkpoints := {}
var _checkpoints = {
	"SAVEDATA": {
		"Visited Checkpoints": visited_checkpoints
	}
}

func _ready():
	Events.connect("checkpoint_visited", self, "_on_Events_checkpoint_visited")
	load_checkpoints()

func _on_Events_checkpoint_visited(checkpoint_name: String) -> void:
	visited_checkpoints[level.name] = visited_checkpoints.get(level.name, [])
	visited_checkpoints[level.name].push_back(checkpoint_name)
	save_checkpoints()

func save_checkpoints():
	for section in _checkpoints.keys():
		for key in _checkpoints[section].keys():
			config.set_value(section, key, _checkpoints[section][key])
	config.save(SAVE_PATH)
	
func load_checkpoints():
	for section in _checkpoints.keys():
		for key in _checkpoints[section].keys():
			_checkpoints[section][key] = config.get_value(section,key)

Bonus question: how can I later check whether a checkpoint has been visited or not?

anssiko | 2020-03-27 09:12

You can edit your question by clicking at the three little dots in the bottom right corner and choosing “Edit”. Providing an answer to your own question that doesn’t solve the issue will decrease your question’s visibility as it won’t appear here any longer.

njamster | 2020-03-27 11:01

:bust_in_silhouette: Reply From: njamster
  1. Why is load[_checkpoints] not working? (I’m trying to run it from _ready, and it swipes clear the config file.)

First of, you don’t call config.save(SAVE_PATH). Secondly, it seems the key “Visited Checkpoints” is accessed as “VisitedCheckpoints” (without a space!) even though it is written to the config file as “Visited Checkpoints”. You can see that for yourself by using config.get_section_keys("SAVEDATA"), which will print you the array of keys in that section. Lastly, when you visit a checkpoint, you insert the information about that checkpoint in a dict called visited_checkpoints and seem to assume that this will also change the value of _checkpoints["SAVEDATA"]["VisitedCheckpoints"]. However, that’s not the case! You’re working with a copy, not a reference.

So here’s a version that should work:

extends Node

const SAVE_PATH = "res://config.cfg"

var level: Node2D = null
var config = ConfigFile.new()
var _checkpoints = {
    "SAVEDATA": {
        "VisitedCheckpoints": { }
    }
}

func _ready():
    load_checkpoints()
    Events.connect("checkpoint_visited", self, "_on_Events_checkpoint_visited")

func _on_Events_checkpoint_visited(checkpoint_name: String) -> void:
    var visited_checkpoints = _checkpoints["SAVEDATA"]["VisitedCheckpoints"]
    visited_checkpoints[level.name] = visited_checkpoints.get(level.name, [])
    visited_checkpoints[level.name].push_back(checkpoint_name)
    save_checkpoints()

func save_checkpoints():
    for section in _checkpoints.keys():
        for key in _checkpoints[section].keys():
            config.set_value(section, key, _checkpoints[section][key])
    config.save(SAVE_PATH)

func load_checkpoints():
    config.load(SAVE_PATH)
    for section in _checkpoints.keys():
        for key in _checkpoints[section].keys():
                _checkpoints[section][key] = config.get_value(section,key)
  1. Why is save[_checkpoints] not adding to the list, but swiping any previous data clear and then saving only new data when the game is run?

Now that the loading works this shouldn’t be the case anymore. Note that checkpoints will be added multiple times to the array, if they player visits them multiple times. In case you don’t want this, you’ll have to change your callback:

func _on_Events_checkpoint_visited(checkpoint_name: String) -> void:
    var visited_checkpoints = _checkpoints["SAVEDATA"]["VisitedCheckpoints"]
    visited_checkpoints[level.name] = visited_checkpoints.get(level.name, [])
    if not visited_checkpoints[level_name].has(checkpoint_name):
        visited_checkpoints[level.name].push_back(checkpoint_name)
    save_checkpoints()

Bonus question: how can I later check whether a checkpoint has been visited or not?

Just check the dictionary for the name of the checkpoint:

var visited_checkpoints = _checkpoints["SAVEDATA"]["VisitedCheckpoints"]
if visited_checkpoints[level_name].has(checkpoint_name):
    ...

Thank you for taking the time for both to explain the logic and to provide the code. It works, including the updated callback (note that level_name needed to be level.name). I’ve been a it over my head with this.

anssiko | 2020-03-28 19:30