ProjectSettings.save_custom() doesn't seem to work as intended

Godot Version

4.3beta1

Question

EDIT: It seems like ProjectSettings.set_setting() isn’t doing anything and ProjectSettings.save_custom() is saving the changes I made using the editor, not at runtime with code.

Hello there! I need some help with creating a settings menu using ProjectSettings and DisplayServer to update the change at runtime. I use ProjectSettings.save() and ProjectSettings.save_custom(“user://settings.binary”) to save the settings and I have updated the path for the override using the editor in Project Settings.

The issue is that it doesn’t seem to be working as intended. The file gets created, but when I restart the project after saving the display mode to be windowed, it starts in exclusive fullscreen as if no changes were saved!

func update_display_mode(index: int) -> void:
  match index:
    0:
      ProjectSettings.set_setting("display/window/size/mode", 0)
      ProjectSettings.set_initial_value("display/window/size/mode", 0)
      DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
    1:
      ProjectSettings.set_setting("display/window/size/mode", 3)
      ProjectSettings.set_initial_value("display/window/size/mode", 3)
      DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
    2:
      ProjectSettings.set_setting("display/window/size/mode", 4)
      ProjectSettings.set_initial_value("display/window/size/mode", 4)
      DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN)

func save_settings() -> void:
  ProjectSettings.save()
  ProjectSettings.save_custom("user://settings.binary")

I’ve got a workaround at the moment where I just use a custom resource to save settings and then update them at runtime, but this doesn’t work as intended. It’s supposed to START in windowed mode if it was set that way, not update to windowed after the game has been started in exclusive fullscreen.

Please could someone explain how I am messing this up?

Are you loading the settings? If so please show the code where you do, if not then this is the reason for this, the engine won’t find them automatically

Thanks for the response! Oh really? Well then that’s exactly what the issue is! I was under the impression ProjectSettings was loaded when I started the game, and I had to use DisplayServer if I wanted to make the changes update as they were made at runtime without needing to restart.

How do I load ProjectSettings? Would it be ProjectSettings.get_setting(“display/window/size/mode”) if so, where would I have to call this function so that the saved settings are loaded when the game starts?

They are loaded but it won’t load some custom options without you specifying them, you can make it load it automatically by having override.cfg I think it is, but I think it might have to be in the path of the executable, but could work under user:// as well

1 Like

I don’t have any custom options. Currently the only thing I am changing is the display mode. I am using “user://settings.binary” because as far as I know “res://” is only writeable while I am developing the game with the editor. Once I export it, the root directory is read only.

Top of ProjectSettings documentation: ProjectSettings — Godot Engine (stable) documentation in English

Overriding: Any project setting can be overridden by creating a file named override.cfg in the project’s root directory. This can also be used in exported projects by placing this file in the same directory as the project binary.

Then further down in methods:

Error save ( )

Saves the configuration to the project.godot file.

Note: This method is intended to be used by editor plugins, as modified ProjectSettings can’t be loaded back in the running app. If you want to change project settings in exported projects, use save_custom to save override.cfg file.

Error save_custom ( String file )

Saves the configuration to a custom file. The file extension must be .godot (to save in text-based ConfigFile format) or .binary (to save in binary format). You can also save override.cfg file, which is also text, but can be used in exported projects unlike other formats.

Note that I get an error when I try and use “override.cfg” telling me it is an unrecognised format. So I used “settings.binary” to prevent users editing the file as there are some things in ProjectSettings that I don’t want them fiddling with.

Things to note:

  1. The binary file is being correctly created in the user:// directory, but doesn’t seem to be loaded on startup.
  2. I am using ProjectSettings.set_setting() to change settings that are built-in, no custom settings.
  3. I can’t rely on just res://override.cfg as the user will be stuck with default values and be unable to save their own settings.
  4. I am also using ProjectSettings.set_initial_value() is this redundant?

I am beginning to suspect that the Project Settings I have set in the editor are being used instead of the override files, as those settings match what the game is doing when launched - not the settings saved using the in-game settings menu.

1 Like

As you can see from the documentation the file has to be in the same location as the executable, the engine won’t load settings from the user path normally, you can use application/config/project_settings_override to add it though, try it out

2 Likes

Yes, you are correct. I’ve done that though, mentioned it in my original post. Though admittedly it was probably poorly worded.

I’ve done everything according to the documentation, but it doesn’t seem to load the file. It instead loads the settings that have been made using the editor itself, not the settings that I have changed in the code using ProjectSettings.set_setting("value") and then saving it with ProjectSettings.save_custom("path").

I found this, but I don’t know if it’s relevant. ProjectSettings.SaveCustom doesn’t write default values #83494

1 Like

Do you mean ProjectSettings.save_custom(path)?

Yes. Specifically it is set to ProjectSettings.save_custom("user://settings.binary)"

1 Like

Try saving it as cfg instead, as in use save_custom("user://settings.cfg")

1 Like

Throws an error:

E 0:00:14:0333   game.res::GDScript_2b1jy:110 @ _on_save_pressed(): Unknown config file format: user://settings.cfg
  <C++ Error>    Method/function failed. Returning: ERR_FILE_UNRECOGNIZED
  <C++ Source>   core/config/project_settings.cpp:1106 @ save_custom()
  <Stack Trace>  game.res::GDScript_2b1jy:110 @ _on_save_pressed()

saving it as .binary or .godot works

Then try naming it override.cfg, it only allows *.godot or *.binary, and the custom one declared with the property requires it to be a text based one

1 Like

This is kinda clarified at the top of the settings, but should be more explicit

1 Like

Okay, so it no longer throws that error at me if I use override.cfg but it still doesn’t actually load the settings on startup /o\

i think you just need to save the override.cfg on where the executable is. try copy and paste the project.godot and rename it override.cfg, put it inside the same directory of your built executable. try change the viewport width to something like 960, when you open the game it should use that override.cfg settings instead

I’d suggest using your own settings system for your game, relying on the built in settings system loading and writing depends on various things, storing it yourself makes it easier to integrate and handle different settings, especially when you’re making custom settings for graphics etc.

2 Likes

There’s already an override.cfg in res:// as well as in user:// and neither of them seem to be working correctly. I think it’s a bug with my version as I found a report on github.

As a note, using the override.cfg in res:// is not a good idea, as those are just for defaults and not user settings like I am intending.

this one is on appdata, not where the executable path is. hence it doesnt work

It seems like I am going to have to. Thanks for your help, I noticed that you actually posted a response on the github bug report on the same thing from 2023.

I really don’t like it, but I guess that’s the way the cookie crumbles.

he doesnt want it to load it quickly like normal save and load is. because of that we are here discussing to use this pretty built-in method