Storing Settings in an .ini vs a Binary File

Godot Version

4.3

Question

I am currently storing game settings in a binary file, using FileAccess. I just store everything in a Dictionary and save and load it. I recently came across someone talking about how settings should be stored in an .ini or .cfg file so you can edit the file easily and see all the settings. My thought is that I’d rather no have to parse things like InputEvents into text just for the convenience of a .ini file that likely no one will ever look at, but I wonder if I’m missing any good arguments.

Any thoughts?

ini gives to user an additional way to edit settings without running an app.
There also exist native way to save config - just into ProjectsSettings.set_setting()
But if your way works for you, why do you have to listen to someone?

After years of experimenting with various formats, I’ve come to the conclusion that using existing, general-purpose file formats is always better than rolling up your own parser.

My recommendation is:

  1. Use Protobuf or a similar format (e.g. flatbuffers) for non-human-readable settings. There’s even a GDScript implementation if you’re not using C#
  2. Use JSON5 or XML (I usually prefer JSON5) for human-readable settings.

My thought is that if I’m doing a casual game, or a GameJam game, people probably aren’t going to spend the time looking for a text file to change settings. I mainly see it useful for debugging purposes for me if I need someone to send me their settings.

I looked this up in the docs and found out that ProjectSettings.save_custom() is the way to do this for games. Which makes sense, as you don’t want to override the default settings while you’re editing your game. That is useful though for things like fullscreen, so that the game just starts that way once the player has ticked that box.

I don’t, but I have found that when you think you know everything about a subject, you stop learning. This is an area where I felt like I had something to learn. In programming there is always more than one way to do things. Sometimes the better way, like you said, is the one that works for you.

My way is the easiest way so far that I’ve found. Godot serializes the objects, and if I change something, it’s in a dictionary, so the save file doesn’t get corrupted. I also can use the same code for saving the game as I do for saving the settings, so less code to maintain.

Using ConfigFile.set_value(), ConfigFile.save() and ConfigFile.get_section_keys() is pretty straightforward though.

1 Like

Agreed. However I’m looking at two built-in serialization methods in GDScript. One stores the file as a binary file, the other as a text config file.

This is my current code, based on Saving games — Godot Engine (stable) documentation in English but using the binary serialization instead of JSON.

## To add a setting to be saved, add a node to the "Settings" Global Group on
## the Nodes tab. Then implement save_setting() and load_setting() functions.
## The first function should return a value to store, and the second should
## use that value to load the information. Make sure the load function has a
## await Signal(self, "ready") line at the top so it doesn't try to load values
## before the node exists. If you need to store multiple values, use a
## dictionary or changes later will result in save/load errors.
func save_settings() -> void:
	var save_information: Dictionary
	var settings_nodes = get_tree().get_nodes_in_group("Settings")
	for node in settings_nodes:
		# Check the node has a save function.
		if not node.has_method("save_setting"):
			print("Setting node '%s' is missing a save_setting() function, skipped" % node.name)
			continue
		
		save_information[node.name] = node.save_setting()
	_save_file(save_information, settings_path)


func load_settings() -> void:
	var save_information: Dictionary = _load_file(settings_path)
	if save_information == null:
		return
	var settings_nodes = get_tree().get_nodes_in_group("Settings")
	for node in settings_nodes:
		# Check the node has a load function.
		if not node.has_method("load_setting"):
			print("Setting node '%s' is missing a load_setting() function, skipped" % node.name)
			continue
		# Check if we have information to load for the value
		if save_information.has(node.name):
			node.load_setting(save_information[node.name])


func _save_file(save_information: Dictionary, path: String) -> bool:
	var file = FileAccess.open(path, FileAccess.WRITE)
	if file == null:
		print("File '%s' could not be opened. File not saved." % path)
		return false
	file.store_var(save_information)
	return true


func _load_file(path: String) -> Variant:
	if not FileAccess.file_exists(path):
		print("File '%s' does not exist. File not loaded." % path)
		return
	var file = FileAccess.open(path, FileAccess.READ)
	return file.get_var()

This is the code on my volume sliders:

func save_setting():
	return value


func load_setting(_value):
	await Signal(self, "ready")
	value = _value

The alternative I’m looking at is using ConfigFile — Godot Engine (stable) documentation in English which works for most settings without any additional code.

This is very cool, and one of the reasons I made this post - I was hoping to learn new things. Having seen HUGE JSON files in the past, I like the idea of a protocol that takes up less time for talking over the internet.

I’ve used both extensively over the years myself. Both are quite useful. I read something somewhere in the Godot documentation that they recommended not using JSON, but I cannot remember where I saw it or what the reasoning was.

My question really goes back to: Do configuration settings need to be human readable these days?

Not sure if you also looked at using Resource saving. There are numerous advantages that it will allow flexible comments, better organization, better documented and you can have code directly tied to the resource.

Of course check ResourceLoader for loading.

Thanks for the idea. I’m using resources for storing song information so I can display it to the user. I have read that documentation before. I’m wondering if this is overkill however. Right now I’m storing the following information:

  • Fullscreen - bool
  • Resolution - Vector2i (when not in fullscreen)
  • Monitor Number - int (I have 4 monitors and I hate it when I can’t play a game on the one I want)
  • Zoom/Scale - float (when in fullscreen)
  • Volume Controls - float

I could make a resource for the monitor, volume controls, etc, but it seems like a lot of work for values that I don’t even need to store if they don’t change. This all came up because I’ve added key bindings for keyboard/mouse/controller in game, and I wanted to store them.

Right now, I’m looking at storing the following types of events: InputEventKey, InputEventMouseButton, InputEventJoypadButton, InputEventJoypadMotion

I’m not sure they store as easily as an .ini file. But normal file saving works fine for serialization. My goal is to make it easily extensible, and while your documentation argument is intriguing, I’m not sure how that would be different from me just adding comments as I’ve already done (see my earlier reply.)

This from the docs.

Note that comments will be lost when saving the ConfigFile.

Unless you are just using text editor.

Well, yes, but I didn’t mean comments in the config file, I meant in the code. But I see what you’re getting at. I could store comments in the config file iteself?