Saving a Dictionary to a file overwrites instead of appends, or just doesn't save at all.

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

Hello, I normally don’t like to ask, but I’m hoping someone can help me make sense of this as I’m new to Godot. I’ve been pouring over tutorials, documentation, and forums all day with no luck.
The gist of it:
I’m trying to work out a save feature that I can save a main dictionary containing sub dictionaries with a bunch of variables from user input in one scene, and then load each sub dictionary up in another scene as a clickable button. (Think like a character creator, and then when finished you could click the character you made to start the game from a list of your characters.)

The problem I’m having is that every entry time I save the data, it overwrites what was there before, meaning only ONE key:sub dictionary ever shows up.
I have tried to use seek_end()as some other people have posted, but when I do that, it simply doesn’t save it, and when you back out of the character creator after hitting the save button, nothing has changed.
Can anyone help me shine a light on what I’m missing? I’m pretty sure the problem lies somewhere in how it’s being saved, and when I tried to error check the store_var() method, I get “Null” back instead of OK.
This is my main Save Code (after organizing the dictionaries)

func save_entry(playerdata):
var save_file = File.new()
if save_file.file_exists("user://PlayerData.dat"):
	save_file.open("user://PlayerData.dat",File.READ_WRITE)
else:
	save_file.open("user://PlayerData.dat",File.WRITE)
	
save_file.seek_end()
print(playerdata) #Just to test and make sure the data is organized right
var err = save_file.store_var(playerdata)
if err == OK : print("Saved Successfully") 
else: print(err) #This just gives me "Null"
save_file.close()

and then on my scene that contains the list, it’s like this:

func load_to_list(): 
var fileob = File.new()
if fileob.file_exists(loadfile): #Loadfile is a var with the filepath
	fileob.open(loadfile, File.READ)
	var playerlistdata : Dictionary = fileob.get_var()
	fileob.close()
	var keylist = playerlistdata.keys()
	print(listsize) #just to confirm only 1 shows up
	
	for char in keylist:
		var button_instance = newbutton.instance()
		get_node("ScrollContainer/Char List Container").add_child(button_instance)
		button_instance.text = char
		button_instance.next_scene_path = myplayerinfo
		
else:
	print("No data file found.")
	
func _ready():
    load_to_list()

Any help is appreciated. Spent a huge amount of time spinning my wheels on this.

Have you tried something like save_file.seek_end(-1) to move the cursor right before the end? Also, in the following code snippet:

if save_file.file_exists("user://PlayerData.dat"):
    save_file.open("user://PlayerData.dat",File.READ_WRITE)
else:
    save_file.open("user://PlantData.dat",File.WRITE)

Why is the part after the else statement is saving it to the file “PlantData.dat”?

Ertain | 2021-02-16 21:44

Thanks for your reply.
I had not tried adding the -1 to seek_end(), but I just did now and there was no change.
As for the other part, that’s just a typo. Sorry, lol.

Ninjazz | 2021-02-17 02:02

:bust_in_silhouette: Reply From: Error7Studios

I was able to get this working using var2str() and str2var().
I couldn’t get it to work using store_var().

extends Node

const SAVE_FILE_PATH := "res://SaveData.txt"

func clear_save_file() -> void:
	var dir := Directory.new()
	if dir.file_exists(SAVE_FILE_PATH):
		dir.remove(SAVE_FILE_PATH) # delete the old save file
	open_save_file(File.new()) # create new save file

func open_save_file(file: File) -> void:
	assert(file)
	assert(!file.is_open())
	var open_mode: int = file.READ_WRITE if file.file_exists(SAVE_FILE_PATH) else file.WRITE
	var open_error: int = file.open(SAVE_FILE_PATH, open_mode)
	assert(open_error == OK, str("Error opening file at: ", SAVE_FILE_PATH))
	assert(file.is_open())

func set_save_file_dict(dict: Dictionary) -> void:
	var file := File.new()
	clear_save_file()
	open_save_file(file)
	var var_as_str := var2str(dict)
	file.store_string(var_as_str)
	file.close()

func get_save_file_dict() -> Dictionary:
	var file := File.new()
	open_save_file(file)
	var file_text := file.get_as_text()
	assert(!file_text.empty(), "File is empty")
	file.close()
	var file_text_as_var = str2var(file_text)
	assert(typeof(file_text_as_var) == TYPE_DICTIONARY)
	var dict: Dictionary = file_text_as_var
	assert(!dict.empty())
	return dict

func append_save_file_dict(append_dict: Dictionary) -> void:
	assert(!append_dict.empty())
	var save_file_dict := get_save_file_dict()
	var saved_keys := save_file_dict.keys()
	var append_keys := append_dict.keys()
	var append_values := append_dict.values()

	for i in append_dict.size():
		var append_key = append_keys[i]
		var append_value = append_values[i]
		assert(!saved_keys.has(append_key), str("Key already exists in save file dict: ", append_key))
		save_file_dict[append_key] = append_value

	set_save_file_dict(save_file_dict)

func _ready():
	set_save_file_dict({A = 1, B = 2, C = 3})
	print(get_save_file_dict())
	append_save_file_dict({X = 7, Y = 8, Z = 9})
	print(get_save_file_dict())

Prints:

{A:1, B:2, C:3}
{A:1, B:2, C:3, X:7, Y:8, Z:9}
:bust_in_silhouette: Reply From: fagnerln

I don’t have much experience with files, but I think that you want an array of dictionaries?

Load the variable (array), append the dictionary, overwrite the file.