Less boiler-plate load/save vars

Godot Version

4.3 stable

Question

I have a setup where i save through writing into a cfg file. Stupid probably, but I cant see if dict saving would be different. Maybe someone can elaborate on how to do this feasibly?

.
I start by this:

	var health
	#imagine having 200 vars here

.
to save i do this:

	var savefile = ConfigFile.new()
	savefile.set_value("data","health", health)
	#imagine having 200 vars here
	savefile.save(savefile_main)

.
to load i do this:

	var savefile = ConfigFile.new()
	savefile.load(savefile_main)
	health = savefile.get_value("data","health",100.0)
	#imagine having 200 vars here

.
ok, imagine having 200 vars to save…
I end up writing names of variables many many times. I wish i could do it in a way where i only had to sort of register an item once, and then i could iterate over the items when i save the whole bunch, when i load the whole bunch,

if only it was sane to do a str_to_var thing (which i know it isnt!)

anyone has some inputs on this?

1 Like

im not quite sure what exactly you want, but have you looked at the ResourceSaver? It allows you to saves resources, which can contain hundreds of variables, easily

1 Like

Just save them into Dictionary, like this:

# The dictionary syntax itself if you never used one:
var game_data = {
    "health": 100.0,
    "mana": 50.0,
    "level": 1,
    "position": Vector2(120,20)
    # ... more variables
}

# Saving
func save_game():
    var savefile = ConfigFile.new()
    for key in game_data.keys():
        savefile.set_value("data", key, game_data[key])
    savefile.save(savefile_main)
1 Like

If you really do have a great number of class-body variables you can use get and set along with an array of strings denoting which variables to save. Though this doesn’t leave much room for failure conditions.

const TO_SAVE = [
	"health", "money", "coins",
	"copper", "gold", "dabloons",
	"cash", "dollars", "gp",
]

func save() -> void:
	var savefile = ConfigFile.new()

	for var_name in TO_SAVE:
		savefile.set_value("data", var_name, get(var_name))

	savefile.save(savefile_main)


func load() -> void:
	var savefile = ConfigFile.new()
	savefile.load(savefile_main)

	for var_name in TO_SAVE:
		set(var_name, savefile.get_value("data", var_name)
1 Like

Like @herrspaten suggested, use resources and save with ResourceSaver.save().

There’s reasons to not use resources for data serialization, such as for networked games and shareable assets where it could open up a remote code execution vulnerability. Otherwise, use custom resources. They are great.

Example:

class_name YourData extends Resource

@export var health = 0xDEAD
@export var max_stamina = 100

# ---

# Some other place:

const data_path = "user://your_data.tres"
var your_data

func load_data():
    if ResourceLoader.exists(data_path):
        your_data = load(data_path)
    else:
        your_data = YourData.new()

func save_data():
    assert(your_data != null)
    ResourceSaver.save(your_data, data_path)

Alternative

A more advanced alternative is to use get_property_list(). Filter out your script properties. Serialize those properties to config file, json, etc. And then the inverse.

1 Like

thanks i will read into this. I’m not fully equipped to wrap my head around this

thanks alot. i will read into these things a bit.

in this case how would i then, ie, get how many, say, “coins” i have?

just inventing a strange example here:

A char says “i see you have x coins”

I want that char to then look up the value of the “coin” entry in the save data.

How would i do that?

To access the data in a dictionary you simply do this:

game_data["health"]

So for the example you used:

func check_coins(game_data: Dictionary):
    print("You have %d coins." % game_data["coins"])

Of course, in a real game scenario to not risk crashes, I would employ a safe way to check if the key exists to grab errors, like here:

func check_coins(game_data: Dictionary):
    if game_data.has("coins"):
        print("You have %d coins." % game_data["coins"])
    else:
        print("You have no coins in your game data!")
3 Likes

Ok. I will simply do this! And see if it lessens my nuisance with writing this damn vars again and again. Thanks! Solved for now. :sunglasses::sunglasses::sunglasses:

hey sorry for being a pest. I would then change a value in a key by doing this?(scavenging from multiple other posts and sites for this…):

#Adding ONE coin to the wallet
#"savedata" is the savedata dictionary
var current_coins = savedata.get["coins",0]
savedata["coins"] = current_coins + 1

#Or even better? ------->
savedata["coins"] = savedata.get("coins",0) + 1

You can just do this:

savedata["coins"] += 1
# or
savedata["coins"] = savedata["coins"] + 1

‘savedata.get[“coins”, 0]’ is not correct syntax

3 Likes

alright! cool. thats very easy. Thank you so much!

1 Like

For posterity! This works →

@export var savefile_data : String #set to "data.cfg" in editor
var data_slotname = "data"

##Dictionary holding the data!
#Values are just defaults.
var data = {
	"health": 100.0,
	"energy": 0.0,
	"damage": 0,
	"mumu": 0,
	"luck": 0.0,
	"goodwill": 0.0,
	"bla bla":0
	#keep going, keep going!
	}

.
.
Saving the entire dictionary

func save_playerdata():
	print("Savemanager - Saving all playerdata!")
	var savefile = check_savefile(savefile_playerdata)
	for key in data.keys():
		savefile.set_value(playerdata_slotname, key, data[key])
		print("Savemanager - Saving key:",key," : ",data[key])
	savefile.save(savefile_playerdata)

.
.
Loading the values from the dictionary (whatever it may contain)

func load_playerdata():
	print("Savemanager - Attempting load all playerdata!")
	var savefile = check_savefile(savefile_playerdata)
	savefile.load(savefile_playerdata)
	
	if !savefile.has_section(playerdata_slotname):
		print("Savemanager - Savefile does not have contents. Will be made via the savemanger at begin!")
		return

	for key in data:
		print("Savemanager - Loading all playerdata!")
		var value = savefile.get_value(playerdata_slotname, key)
		data[key] = value
		print("Savemanager - Loading key:",key," : ",data[key])

.
.
.
.
.
Aaaannnnd this function to ease the boiler-plating

func check_savefile(savefile_to_check):
	var savefile = ConfigFile.new()
	var err = savefile.load(savefile_to_check)
	if err != OK:
		print("Savemanager - Loading ", savefile_to_check,"failed!")
		return null
	return savefile
1 Like