How do i detect error/null on FileAcess

How do I detect error/null on FileAcess, godot 4.5.1 stable

my code is on spanish and i translated id badly sorry

Ok, I´m working on in-game settings… How do I detect that a variable called with “get_var” returned null and delete it as a result?

func LoadSettings():
	if FileAccess.file_exists("user://.file"):
		var file= FileAccess.open("user://.file", FileAccess.READ)
		languague = file.get_var(languague)
		sensi = file.get_var(sensi)
		fullscreen = file.get_var(fullscreen)
		quality= file.get_var(quality)
		sound = file.get_var(sound)

		if file== null:
			print("Error: theres a error or null on a varriable; deleting it because is broken lol")

			DirAccess.remove_absolute(ProjectSettings.globalize_path("user://.file"))
			return
		shadows = archivo.get_var(shadows)

		# -> ok the end
1 Like

Maybe you should use a ConfigFile for easier reading with defaults and easier writing if a player must change something by hand.

Currently you would have to check each variable as you run get_var, or you could check file.eof_reached() between each variable.

1 Like

Yes… but how do I fix a broken line called by “get_var” or how do I fix if calling a variable returned null…

As a result I should delete the file or write a value

I’ve moved the file checks to the start of the function. You could do something like this, but it has inherent flaws:

func LoadSettings():
	if not FileAccess.file_exists("user://.file"):
		# Handle missing file
		return

	var file = FileAccess.open("user://.file", FileAccess.READ) # This can fail
	if not file:
		# Handle open error
		return

	# Here we know the file exists, and we could open it
	languague = file.get_var(languague)
	sensi = file.get_var(sensi)
	fullscreen = file.get_var(fullscreen)
	quality= file.get_var(quality)
	sound = file.get_var(sound)

	for item in [language, sensi, fullscreen, quality, sound]:
		if not item:
			# Handle null case, but which one failed?

FileAccess.get_var() will error and return null if it cannot return a valid Variant, however you don’t have an easy way to check which call failed without modifying the structure of the code a bit.

You could use ConfigFile for this, as gertkeno said, and that would be better. If you don’t want to, however, you should consider storing a single Dictionary inside the file, as opposed to multiple Variants.

enum Option {
	LANGUAGE,
	SENSI,
	FULLSCREEN,
	QUALITY,
	SOUND,
}

# Default values. This is what you store with SaveSettings()
var option_value: Dictionary[Option, int] = {
	Option.LANGUAGE: 0,
	Option.SENSI: 50,
	Option.FULLSCREEN: 1,
	Option.QUALITY: 4,
	Option.SOUND: 75,
}

func LoadSettings():
	if not FileAccess.file_exists("user://.file"):
		# Handle missing file
		return

	var file = FileAccess.open("user://.file", FileAccess.READ)
	if not file:
		# Handle open error
		return

	var variant: Variant = file.get_var()
	if not variant is Dictionary:
		# Handle unexpected file content
		return

	if not option_value.is_same_typed(variant):
		# Handle unexpected Dictionary
		return
	
	for key: Option in option_value:
		if variant.has(key):
			option_value[key] = variant.get(key)

Yes, there are a lot of checks to do when working with files. You don’t have to store everything inside the dictionary as int, although that makes it easier to check. If you were to store Variant values, you will need additional checks to make sure you don’t get a String for SOUND from the file somehow.

ok but sensi is a float 0.004 is the default value and fullscreen is a bool, how do i add floats and bools to the var option_value

var option_value: Dictionary[Option, Variant] = {
	Option.LANGUAGE: "es",
	Option.SENSI: 0.004,
	Option.FULLSCREEN: true,
	# ...
}
1 Like

get_var() and store_var() are meant for binary serialization. With binary serialization, the data format/order is always implied. You read exactly and only the things that you’ve stored. There can’t be “unexpected” values. If you don’t want null in there - never store it. Do all your checks when storing, not when reading.

If the file is expected to be modified “from the outside” then binary serialization is not the right tool for the problem. In that case, you should use config file, json or resources.

1 Like

ok… but now. How do i set these values. I used to work with plain variables. i could add new variables and i think the dictionary is not going to work

var languague: int
var sensi: float = 0.004
var fullscreen: bool = true
var quality: int = 1
var sound: float = 1.0


func Savefile():
	var file= FileAccess.open("user://.data", FileAccess.WRITE)
	file.store_var(idioma)
	file.store_var(languague)
	file.store_var(fullscreen)
	file.store_var(quality)
	file.store_var(sound)
	file= null
func Loadfile():
	var file = FileAccess.open("user://.data", FileAccess.READ)
	idioma = file.get_var()
	languague = file.get_var()
	fullscreen = file.get_var()
	quality = file.get_var()
	sound = file.get_var()

Btw file = null serves no purose. file is a local, reference counted variable. It will vanish as soon as it gets out of scope. You may do file.close() for clarity, but that too is not necessary, as it will be called automatically when file is freed.

1 Like

You can add new “variables” to the dictionary whenever you need to. To save it to file you would do something like this:

func Savefile():
    var file = FileAccess.open("user://.data", FileAccess.WRITE)
    if not file:
        # Handle open error
        return
    if not file.store_var(option_value):
        # Handle file IO error

The entire script would look something like this:

# You add a new option here
enum Option {
	LANGUAGE,
	SENSI,
	FULLSCREEN,
	QUALITY,
	SOUND,
	# NEW_OPTION,
}

# And then set the default value for that option here
var option_value: Dictionary[Option, Variant] = {
	Option.LANGUAGE: "es",
	Option.SENSI: 0.004,
	Option.FULLSCREEN: true,
	Option.QUALITY: 4, # Change this to your actual value
	Option.SOUND: 75, # Change this to your actual value
	# Options.NEW_OPTION: "default",
}

# Change this if you need another file
const OPTIONS_FILE_PATH: String = "user://options.dat"

func Savefile():
	var file = FileAccess.open(OPTIONS_FILE_PATH, FileAccess.WRITE)
	if not file:
		# Handle open error
		return
	if not file.store_var(option_value):
		# Handle file IO error
		pass

func LoadSettings():
	if not FileAccess.file_exists(OPTIONS_FILE_PATH):
		return
	
	var file = FileAccess.open(OPTIONS_FILE_PATH, FileAccess.READ)
	if not file:
		# Handle open error
		return
	
	var variant: Variant = file.get_var()
	if not variant is Dictionary:
		# Handle unexpected file content
		return
	
	if not option_value.is_same_typed(variant):
		# Handle unexpected Dictionary
		return
	
	for key: Option in option_value:
		if variant.has(key):
			option_value[key] = variant.get(key)
1 Like