Skip to content

Instantly share code, notes, and snippets.

@topokel
Created October 21, 2024 22:37
Show Gist options
  • Save topokel/dbb35d65f5b7027ff63f95a16084dddd to your computer and use it in GitHub Desktop.
Save topokel/dbb35d65f5b7027ff63f95a16084dddd to your computer and use it in GitHub Desktop.

Revisions

  1. topokel created this gist Oct 21, 2024.
    125 changes: 125 additions & 0 deletions config_base.gd
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,125 @@
    class_name ConfigBase extends Node
    ## Base class for Configuration


    ## Types to filter on when reflecting on subclass properties.
    const VALID_PROP_TYPES : Array[int] = [
    Variant.Type.TYPE_BOOL,
    Variant.Type.TYPE_INT,
    Variant.Type.TYPE_FLOAT,
    Variant.Type.TYPE_STRING,
    ]


    ## Name of the file to load and save config on.
    var _file_name : String = ""


    ## Name of the current script, used for debugging.
    var _script_name : String = ""


    ## Call this in subclasses after setting [field self._file_name].
    func _init() -> void:
    self._script_name = self._get_script_name()
    if self._file_name.length() > 0:
    var config_err: Error = self.read()
    if config_err == Error.ERR_FILE_NOT_FOUND: self.write()


    ## Synchronously reads this config's file values into the singleton.
    func read() -> Error:
    assert(self._file_name.length() > 0, "Config _file_name must be set in subclass.")
    var config := ConfigFile.new()
    var config_err: Error = config.load(self._file_name)
    if config_err == Error.OK:
    print_debug("[", self._script_name, "]: Reading user cfg from " + self._file_name)
    self._read_config(config)
    elif config_err == Error.ERR_FILE_NOT_FOUND:
    print("[", self._script_name, "]: Config file not yet created: " + self._file_name)
    else:
    printerr("[", self._script_name,
    "]: Error when loading settings cfg: ",
    config_err)
    return config_err


    ## Synchronously writes this config's values into the destination file.
    func write() -> Error:
    assert(self._file_name.length() > 0, "Config _file_name must be set in subclass.")
    print_debug("Writing user cfg to " + self._file_name)
    var config := ConfigFile.new()
    self._write_config(config)
    var config_err: Error = config.save(self._file_name)
    if config_err != Error.OK:
    printerr("[", self._script_name,
    "]: Error when saving settings cfg:",
    config_err)
    else:
    print_debug("[", self._script_name,
    "]: Successfully wrote cfg to path: ",
    self._file_name)
    return config_err


    ## Loops over the values in a [ConfigFile], setting the fields on the class.
    func _read_config(config: ConfigFile) -> void:
    # Loop over all properties on self, getting the value in the ConfigFile.
    for prop in self._get_props():
    var prop_section : String = prop[0]
    var prop_key : String = prop[1]
    var prop_name : String = prop_section + "_" + prop_key
    var default_val : Variant = self[prop_name]
    self[prop_name] = config.get_value(prop_section, prop_key, default_val)
    print_debug("[", self._script_name, "]: Loaded ", prop_name,
    " (", prop_section,
    "[", prop_key,
    "]) from user cfg as ", self[prop_name],
    " with default ", default_val)


    ## Saves all valid setting names on current script to [ConfigFile].
    func _write_config(config: ConfigFile) -> void:
    # Loop over all properties on self, setting the value in the ConfigFile.
    for prop in self._get_props():
    var prop_section : String = prop[0]
    var prop_key : String = prop[1]
    var prop_name : String = prop[0] + "_" + prop[1]
    var prop_val : Variant = self[prop_name]
    config.set_value(prop_section, prop_key, prop_val)
    print_debug("[", self._script_name, "]: Wrote ", prop_name,
    " (", prop_section,
    "[", prop_key,
    "]) to user cfg with value: ", prop_val)


    ## Reflects on all properties of the current script, returning valid setting names.
    func _get_props() -> Array[PackedStringArray]:
    var result : Array[PackedStringArray] = []
    var script: Script = self.get_script()
    for prop in script.get_script_property_list():
    var prop_type : int = prop["type"]
    var prop_name : String = prop["name"]
    var is_internal_prop : bool = prop_name.begins_with("_")
    var is_valid_prop_type : bool = VALID_PROP_TYPES.has(prop_type)
    if !is_internal_prop and is_valid_prop_type:
    print_debug("[", self._script_name,
    "]: Reflecting on valid public property \"", prop_name,
    "\" with type: ", prop_type)
    result.push_back(prop_name.split("_", false, 1))
    else:
    print_debug("[", self._script_name, "]: Ignoring reflected ",
    "valid " if is_valid_prop_type else "invalid ",
    "private " if is_internal_prop else "public ",
    "property \"", prop_name, "\" with type: ", prop_type)
    return result

    ## Reflects on script property list, fetching just the script name.
    func _get_script_name() -> String:
    var script: Script = self.get_script()
    for prop in script.get_script_property_list():
    var prop_type : int = prop["type"]
    var prop_name : String = prop["name"]
    if prop_type == Variant.Type.TYPE_NIL: return prop_name
    return ""

    40 changes: 40 additions & 0 deletions config_settings.gd
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,40 @@
    extends ScConfigBase
    ## Configurable user settings file


    ## Destination file name to copy to the base class.
    const FILE_NAME : String = "user://settings.cfg"


    ## Multiplier for mouse look sensitivity. Not used for UI.
    var input_mouse_sens : float = 1.0


    ## Multiplier for joystick look sensitivity (lateral). Not used for UI.
    var input_joy_look_x_sens : float = 0.02


    ## Multiplier for joystick look sensitivity (vertical). Not used for UI.
    var input_joy_look_y_sens : float = 0.02


    ## Multiplier for joystick look deadzone (lateral). Not used for UI.
    var input_joy_look_x_deadzone : float = 0.08


    ## Multiplier for joystick look deadzone (vertical). Not used for UI.
    var input_joy_look_y_deadzone : float = 0.08


    ## Multiplier for joystick move deadzone (lateral). Used for movement and UI.
    var input_joy_move_x_deadzone : float = 0.08


    ## Multiplier for joystick move deadzone (vertical). Used for movement and UI.
    var input_joy_move_y_deadzone : float = 0.08


    ## Sets the destination file name and initializes the configuration.
    func _init() -> void:
    self._file_name = FILE_NAME
    super._init()