FileAccess doesn't save current updated variable only the original node?!

Godot Version

4.5.1

Question

So looking at the docs, I should be able to save a dictionary to with FileAccess.store_var. Below are two save and load functions taken from the docs.

So I take my player node and put it in a dictionary. It has a variable named pc_name which is “Jack” and another variable named pc_cat which is “Meow”. I then save the dictionary with FileAccess.store_var and open it with FileAccess.get_var. It seems to save the dictionary and load it.

	var d = {}
	player.pc_name = "Ruth"
	player.pc_cat = "Woof"
	d["player"] = $Player
	prints(d.player.pc_name, d.player.pc_cat, player.pc_name, player.pc_cat)

	save_to_file(d)

	var d2 = load_from_file()
	var p2 = d2.player
	prints(p2.pc_name, p2.pc_cat, player.pc_name, player.pc_cat)

func save_to_file(content):
	var file = FileAccess.open("user://save_game.dat", FileAccess.WRITE)
	file.store_var(content, true)

func load_from_file():
	var file = FileAccess.open("user://save_game.dat", FileAccess.READ)
	var content = file.get_var(true)
	return content

However, the output is:

Ruth Woof Ruth Woof
Jack Meow Ruth Woof

So, even though the variable is saved as “Ruth” and “Woof” it loads as “Jack” and “Meow”. Crazy stuff! Anybody else run into this bug?

1 Like

When you are done loading or saving the file you should explicitly close it.

func save_to_file(content):
	var file = FileAccess.open("user://save_game.dat", FileAccess.WRITE)
	file.store_var(content, true)
	file.close()

Otherwise if you save it and then immediately load it, Windows or whatever OS you are using may return your old file. (OS cache systems and flushing timings, something like that, meant to speed up IO)

1 Like

This is not a bug at all, but expected behavior.
Doing $Player is just a reference to a node. But you are modifying the player variable. These are two different things. When you load, you simply load the Player node with default values. You’re not actually saving the player instance that you modified.

1 Like

When a FileAccess object goes out of scope, it is automatically flushed and closed. This should not affect anything.

This is the correct answer, but for the wrong reasons. The code above correctly modifies the properties, correctly stores the node in the file, and correctly loads the node from the file.

The reason the name change doesn’t get saved is because Godot will not serialize custom properties unless you specifically tell it to. It will correctly save the Node, all the built-in properties you modify, as well as the path to the script attached. However, it will not save custom properties declared in the script. As such, when it loads the Node from the file, those will revert to default values.

If you want to modify what properties get serialized from a script, you can do so using Node._get_property_list(), although that’s a bit advanced for most use cases. In general, you can simply @export any property you want saved:

# This won't save the properties to the file

class_name Player extends Node

var pc_name: String = "Jack"
var pc_cat: String = "Meow"
# This will

class_name Player extends Node

@export var pc_name: String = "Jack"
@export var pc_cat: String = "Meow"

See this: Saving games — Godot Engine (stable) documentation in English

2 Likes

Just a small addendum here; you can use @export_storage to force saving of properties without making them editable in the inspector.

4 Likes

@zigg3c
That’s actually really interesting. I would never have thought of that.

@normalized
That’s very interesting too. I wasn’t aware of that property flag.

Nothing to add, just wanted to note how fascinating that was!

1 Like

Thanks for the feedback gang. Since I posted that, I started reading up on the get_property flags which seem like a big pain. Glad it’s as simple as @export var.

For anybody else reading this, @export var any variables you want to save, store them in a dictionary, and use the above functions to save and load the dictionary. It seems like the simplest way to implement a save system in Godot.

John

1 Like