Snippets

Home

Programming, scripts and other digital minutiae. /// Probably gamedev, probably GDScript, but possibly other things.

Godot SaveLoader

A utility script for handling settings files, using ConfigFiles as an intermediary.
Includes documentation comments and (some) error checking.

Copy
##A helper class for saving data, and interfacing between ConfigFiles and Scripts.
##
##SaveLoader is a helper class designed for easily storing settings,
##save files and other data using a [ConfigFile] as an intermediary.
##[br]
##Any script intended to be used with the SaveLoader must include a "Template"
##dictionary, either as an internal property named [code]defaults[/code] or separately.
##[br][br]
##The template is formatted as a [Dictionary][[String], [Dictionary]]
##containing one dictionary per section of the output file.
##[br]
##Each section dictionary contains [String] keys named the same as variables
##in the source object, with default values given for each.
class_name SaveLoader

##Saves properties within [param source] to a config file with the given path,
##using [param template] as a reference for which properties to save.
##[br][br]
##By default, [param source] is searched for a member named
##[code]defaults[/code] to use as the template.
##[br][br]
##Returns various errors if the template dictionary is not correctly formatted.
##[br]
##Returns the error code given by ConfigFile.save() if an error occurs during saving.
static func save_to_file(
	path:String,
	source:Object,
	template:Dictionary = source.get("defaults")
) -> Error:
	#file setup
	var output = ConfigFile.new()
	
	#verify template
	var verify = _verify(template)
	if verify != OK: return verify
	
	#collect data
	for section in template.keys():
		for value in template[section].keys():
			output.set_value(section,value,source.get(value))
	
	#save and return
	return output.save(path)

##Packages properties within [param source] to a [ConfigFile],
##using [param template] as a reference for which properties to collect.
##[br]
##Returns [code]null[/code] in the event of an error during saving or if the template is incorrectly formatted.
static func save_to_new_config(
	source:Object,
	template:Dictionary = source.get("defaults")
)->ConfigFile:
	#file setup
	var output = ConfigFile.new()
	
	#verify template
	var verify = _verify(template)
	if verify != OK: return null
	
	#collect data
	for section in template.keys():
		for value in template[section].keys():
			output.set_value(section,value,source.get(value))
	
	#return ConfigFile
	return output

##Saves the properties within [param source] to [param destination],
##using [param template] as a reference for which properties to collect.
static func save_to_config(
	source:Object,destination:ConfigFile,
	template:Dictionary = source.get("defaults")
)->Error:
	#verify template
	var verify = _verify(template)
	if verify != OK: return verify
	
	#collect data
	for section in template.keys():
		for value in template[section].keys():
			destination.set_value(section,value,source.get(value))
	
	return OK

##Loads properties from [param config] into [param destination],
##using [param template] as a reference for which properties to load
##and as defaults for any missing values.
static func load_from_config(
	config:ConfigFile,destination:Object,
	template:Dictionary[String,Dictionary] = destination.get("defaults")
)->Error:
	#verify template
	var verify = _verify(template)
	if verify != OK: return verify
	
	#load data into destination object
	for section in template.keys():
		for key in template[section].keys():
			destination.set(key,config.get_value(section,key,template[section][key]))
	return OK

##Loads properties from the config file located at [param path] into [param destination],
##using [param template] as a reference for which properties to load
##and as defaults for any missing values.
##[br][br]
##By default, [param destination] is searched for a member named
##[code]defaults[/code] to use as the template.
##[br][br]
##Returns various errors if the template is not correctly formatted.
##[br]
##Returns the error code given by ConfigFile.load() if an error occurs during loading.
static func load_from_file(
	path:String,destination:Object,
	template:Dictionary[String,Dictionary] = destination.get("defaults")
)->Error:
	var input = ConfigFile.new()
	var file_load = input.load(path)
	if file_load != OK: return file_load
	return load_from_config(input,destination,template)

##Loads the default values from [param template] into [param destination].
static func load_defaults(
	destination:Object,
	template:Dictionary[String,Dictionary] = destination.get("defaults")
)->Error:
	#verify template
	var verify = _verify(template)
	if verify != OK: return verify
	
	#load data into destination object
	for section in template.keys():
		for key in template[section].keys():
			destination.set(key,template[section][key])
	
	return OK

static func _verify(template) -> Error:
	#TODO: ADD MORE TESTS IF NEEDED
	if template == null:
		#missing template
		push_error("Template is missing.")
		return ERR_DOES_NOT_EXIST
	if template is not Dictionary:
		#invalid template
		push_error("Template is not a Dictionary.")
		return ERR_INVALID_PARAMETER
	if template.is_typed() and template is not Dictionary[String,Dictionary]:
		#invalid template
		push_error("Template has the wrong types, should be Dictionary[String,Dictionary].")
		return ERR_INVALID_DATA
	for section in template.keys():
		if section is not String:
			push_error("Template has one or more non-String section keys.")
			return ERR_INVALID_DATA
		if template[section] is not Dictionary:
			push_error("Template has one or more non-Dictionary sections.")
			return ERR_INVALID_DATA
		for key in template[section].keys():
			if key is not String:
				push_error("Template has one or more sections containing non-String keys.")
				return ERR_INVALID_DATA
	return OK