Skip to content

Commit

Permalink
feat: ✨ added refresh_config_data() and refresh_current_configs()
Browse files Browse the repository at this point in the history
Allows refreshing of config data at any time during runtime.
  • Loading branch information
KANAjetzt committed Mar 21, 2024
1 parent f9239a6 commit a6b83fe
Showing 1 changed file with 97 additions and 26 deletions.
123 changes: 97 additions & 26 deletions addons/mod_loader/api/config.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
class_name ModLoaderConfig
extends Object


const LOG_NAME := "ModLoader:Config"
const DEFAULT_CONFIG_NAME := "default"
const DEFAULT_CONFIG_NAME := "default"


# Creates a new configuration for a mod.
Expand All @@ -21,36 +20,39 @@ static func create_config(mod_id: String, config_name: String, config_data: Dict
var default_config: ModConfig = get_default_config(mod_id)
if not default_config:
ModLoaderLog.error(
"Failed to create config \"%s\". No config schema found for \"%s\"."
% [config_name, mod_id], LOG_NAME
(
'Failed to create config "%s". No config schema found for "%s".'
% [config_name, mod_id]
),
LOG_NAME
)
return null

# Make sure the config name is not empty
if config_name == "":
ModLoaderLog.error(
"Failed to create config \"%s\". The config name cannot be empty."
% config_name, LOG_NAME
'Failed to create config "%s". The config name cannot be empty.' % config_name, LOG_NAME
)
return null

# Make sure the config name is unique
if ModLoaderStore.mod_data[mod_id].configs.has(config_name):
ModLoaderLog.error(
"Failed to create config \"%s\". A config with the name \"%s\" already exists."
% [config_name, config_name], LOG_NAME
(
'Failed to create config "%s". A config with the name "%s" already exists.'
% [config_name, config_name]
),
LOG_NAME
)
return null

# Create the config save path based on the config_name
var config_file_path := _ModLoaderPath.get_path_to_mod_configs_dir(mod_id).path_join("%s.json" % config_name)
var config_file_path := _ModLoaderPath.get_path_to_mod_configs_dir(mod_id).path_join(
"%s.json" % config_name
)

# Initialize a new ModConfig object with the provided parameters
var mod_config := ModConfig.new(
mod_id,
config_data,
config_file_path
)
var mod_config := ModConfig.new(mod_id, config_data, config_file_path)

# Check if the mod_config is valid
if not mod_config.is_valid:
Expand All @@ -64,7 +66,7 @@ static func create_config(mod_id: String, config_name: String, config_data: Dict
if not is_save_success:
return null

ModLoaderLog.debug("Created new config \"%s\" for mod \"%s\"" % [config_name, mod_id], LOG_NAME)
ModLoaderLog.debug('Created new config "%s" for mod "%s"' % [config_name, mod_id], LOG_NAME)

return mod_config

Expand All @@ -82,19 +84,29 @@ static func update_config(config: ModConfig) -> ModConfig:

# Check if the config is the "default" config, which cannot be modified
if config.name == DEFAULT_CONFIG_NAME:
ModLoaderLog.error("The \"default\" config cannot be modified. Please create a new config instead.", LOG_NAME)
ModLoaderLog.error(
'The "default" config cannot be modified. Please create a new config instead.', LOG_NAME
)
return null

# Check if the config passed validation
if not config.is_valid:
ModLoaderLog.error("Update for config \"%s\" failed validation with error message \"%s\"" % [config.name, error_message], LOG_NAME)
ModLoaderLog.error(
(
'Update for config "%s" failed validation with error message "%s"'
% [config.name, error_message]
),
LOG_NAME
)
return null

# Save the updated config to the config file
var is_save_success := config.save_to_file()

if not is_save_success:
ModLoaderLog.error("Failed to save config \"%s\" to \"%s\"." % [config.name, config.save_path], LOG_NAME)
ModLoaderLog.error(
'Failed to save config "%s" to "%s".' % [config.name, config.save_path], LOG_NAME
)
return null

# Return the updated config
Expand Down Expand Up @@ -179,7 +191,13 @@ static func get_schema_for_prop(config: ModConfig, prop: String) -> Dictionary:

# If the schema for the property is empty, log an error and return an empty dictionary
if schema_for_prop.is_empty():
ModLoaderLog.error("No Schema found for property \"%s\" in config \"%s\" for mod \"%s\"" % [prop, config.name, config.mod_id], LOG_NAME)
ModLoaderLog.error(
(
'No Schema found for property "%s" in config "%s" for mod "%s"'
% [prop, config.name, config.mod_id]
),
LOG_NAME
)
return {}

return schema_for_prop
Expand Down Expand Up @@ -252,14 +270,14 @@ static func get_mods_with_config() -> Array:
static func get_configs(mod_id: String) -> Dictionary:
# Check if the mod ID is invalid
if not ModLoaderStore.mod_data.has(mod_id):
ModLoaderLog.fatal("Mod ID \"%s\" not found" % [mod_id], LOG_NAME)
ModLoaderLog.fatal('Mod ID "%s" not found' % [mod_id], LOG_NAME)
return {}

var config_dictionary: Dictionary = ModLoaderStore.mod_data[mod_id].configs

# Check if there is no config file for the mod
if config_dictionary.is_empty():
ModLoaderLog.debug("No config for mod id \"%s\"" % mod_id, LOG_NAME, true)
ModLoaderLog.debug('No config for mod id "%s"' % mod_id, LOG_NAME, true)
return {}

return config_dictionary
Expand All @@ -278,7 +296,9 @@ static func get_config(mod_id: String, config_name: String) -> ModConfig:
var configs := get_configs(mod_id)

if not configs.has(config_name):
ModLoaderLog.error("No config with name \"%s\" found for mod_id \"%s\" " % [config_name, mod_id], LOG_NAME)
ModLoaderLog.error(
'No config with name "%s" found for mod_id "%s" ' % [config_name, mod_id], LOG_NAME
)
return null

return configs[config_name]
Expand Down Expand Up @@ -328,20 +348,71 @@ static func get_current_config(mod_id: String) -> ModConfig:
# The currently active configuration name for the given mod id or an empty string if not found.
static func get_current_config_name(mod_id: String) -> String:
# Check if user profile has been loaded
if not ModLoaderStore.current_user_profile or not ModLoaderStore.user_profiles.has(ModLoaderStore.current_user_profile.name):
if (
not ModLoaderStore.current_user_profile
or not ModLoaderStore.user_profiles.has(ModLoaderStore.current_user_profile.name)
):
# Warn and return an empty string if the user profile has not been loaded
ModLoaderLog.warning("Can't get current mod config for \"%s\", because no current user profile is present." % mod_id, LOG_NAME)
ModLoaderLog.warning(
(
'Can\'t get current mod config for "%s", because no current user profile is present.'
% mod_id
),
LOG_NAME
)
return ""

# Retrieve the current user profile from ModLoaderStore
# *Can't use ModLoaderUserProfile because it causes a cyclic dependency*
var current_user_profile = ModLoaderStore.current_user_profile

# Check if the mod exists in the user profile's mod list and if it has a current config
if not current_user_profile.mod_list.has(mod_id) or not current_user_profile.mod_list[mod_id].has("current_config"):
if (
not current_user_profile.mod_list.has(mod_id)
or not current_user_profile.mod_list[mod_id].has("current_config")
):
# Log an error and return an empty string if the mod has no config file
ModLoaderLog.error("Mod \"%s\" has no config file." % mod_id, LOG_NAME)
ModLoaderLog.error('Mod "%s" has no config file.' % mod_id, LOG_NAME)
return ""

# Return the name of the current configuration for the mod
return current_user_profile.mod_list[mod_id].current_config


# Refreshes the data of the provided configuration by reloading it from the config file.
#
# Parameters:
# - config (ModConfig): The ModConfig object whose data needs to be refreshed.
#
# Returns:
# - ModConfig: The ModConfig object with refreshed data if successful, or the original object otherwise.
func refresh_config_data(config: ModConfig) -> ModConfig:
# Retrieve updated configuration data from the config file
var new_config_data := _ModLoaderFile.get_json_as_dict(config.save_path)
# Update the data property of the ModConfig object with the refreshed data
config.data = new_config_data

return config


# Iterates over all mods to refresh the data of their current configurations, if available.
# Compares the previous configuration data with the refreshed data and emits the `current_config_changed` signal if changes are detected.
#
# This function ensures that any changes made to the configuration files outside the application
# are reflected within the application's runtime, allowing for dynamic updates without the need for a restart.
func refresh_current_configs() -> void:
for mod_id in ModLoaderMod.get_mod_data_all().keys():
# Retrieve the current configuration for the mod
var config := ModLoaderConfig.get_current_config(mod_id)
# Skip if the mod has no config
if not config:
continue
# Create a deep copy of the current configuration data for comparison
var config_data_previous := config.data.duplicate(true)
# Refresh the configuration data
var config_new := refresh_config_data(config)

# Compare previous data with refreshed data
if not config_data_previous == config_new.data:
# Emit signal indicating that the current configuration has changed
ModLoader.current_config_changed.emit(config)

0 comments on commit a6b83fe

Please sign in to comment.