diff --git a/addons/mod_loader/_export_plugin/export_plugin.gd b/addons/mod_loader/_export_plugin/export_plugin.gd index 51fa328b..f44c1ab0 100644 --- a/addons/mod_loader/_export_plugin/export_plugin.gd +++ b/addons/mod_loader/_export_plugin/export_plugin.gd @@ -1,7 +1,7 @@ extends EditorExportPlugin -static var hook_pre_processor: _ModLoaderModHookPreProcessor +var hook_pre_processor: _ModLoaderModHookPreProcessor func _get_name() -> String: return "Godot Mod Loader Export Plugin" diff --git a/addons/mod_loader/api/config.gd b/addons/mod_loader/api/config.gd index bca8a03c..d1a96d66 100644 --- a/addons/mod_loader/api/config.gd +++ b/addons/mod_loader/api/config.gd @@ -53,7 +53,8 @@ static func create_config(mod_id: String, config_name: String, config_data: Dict ) # Check if the mod_config is valid - if not mod_config.is_valid: + #if not mod_config.is_valid: + if not mod_config.is_valid(): return null # Store the mod_config in the mod's ModData @@ -86,7 +87,8 @@ static func update_config(config: ModConfig) -> ModConfig: return null # Check if the config passed validation - if not config.is_valid: + #if not config.is_valid: + if not config.is_valid(): ModLoaderLog.error("Update for config \"%s\" failed validation with error message \"%s\"" % [config.name, error_message], LOG_NAME) return null diff --git a/addons/mod_loader/api/log.gd b/addons/mod_loader/api/log.gd index f9fedc8f..e6e15d88 100644 --- a/addons/mod_loader/api/log.gd +++ b/addons/mod_loader/api/log.gd @@ -18,34 +18,15 @@ enum VERBOSITY_LEVEL { DEBUG, ## For debugging, can get quite verbose } -## Keeps track of logged messages, to avoid flooding the log with duplicate notices -## Can also be used by mods, eg. to create an in-game developer console that -## shows messages -static var logged_messages := { - "all": {}, - "by_mod": {}, - "by_type": { - "fatal-error": {}, - "error": {}, - "warning": {}, - "info": {}, - "success": {}, - "debug": {}, - "hint": {}, - } +enum VERBOSITY_COLOR { + ERROR, ## For errors and fatal errors + WARNING, ## For warnings + INFO, ## For everything informational + SUCCESS, + DEBUG, ## For debugging, can get quite verbose + hint, } -## Verbosity/Logging level. -## Used to filter out messages below the set level -## (if the [enum VERBOSITY_LEVEL] int of a new entry is larger than the [member verbosity] it is ignored) -static var verbosity: VERBOSITY_LEVEL = VERBOSITY_LEVEL.DEBUG - -## Array of mods that should be ignored when logging messages (contains mod IDs as strings) -static var ignored_mods: Array[String] = [] - -## Highlighting color for hint type log messages -static var hint_color := Color("#70bafa") - ## This Sub-Class represents a log entry in ModLoader. class ModLoaderLogEntry: extends Resource @@ -99,9 +80,15 @@ class ModLoaderLogEntry: ## Get the prefix string for the log entry, including the log type and mod name.[br] ## [br] + ## [b]Parameters:[/b][br] + ## [param exclude_type] ([bool]): (Optional) If true, the log type (e.g., DEBUG, WARN) will be excluded from the prefix. Default is false.[br] + ## [br] ## [b]Returns:[/b] [String] - func get_prefix() -> String: - return "%s %s: " % [type.to_upper(), mod_name] + func get_prefix(exclude_type := false) -> String: + return "%s%s: " % [ + "" if exclude_type else "%s " % type.to_upper(), + mod_name + ] ## Generate an MD5 hash of the log entry (prefix + message).[br] @@ -323,8 +310,8 @@ static func get_all() -> Array: var log_entries := [] # Get all log entries - for entry_key in logged_messages.all.keys(): - var entry: ModLoaderLogEntry = logged_messages.all[entry_key] + for entry_key in ModLoaderStore.logged_messages.all.keys(): + var entry: ModLoaderLogEntry = ModLoaderStore.logged_messages.all[entry_key] log_entries.append_array(entry.get_all_entries()) # Sort them by time @@ -343,12 +330,12 @@ static func get_all() -> Array: static func get_by_mod(mod_name: String) -> Array: var log_entries := [] - if not logged_messages.by_mod.has(mod_name): + if not ModLoaderStore.logged_messages.by_mod.has(mod_name): error("\"%s\" not found in logged messages." % mod_name, _LOG_NAME) return [] - for entry_key in logged_messages.by_mod[mod_name].keys(): - var entry: ModLoaderLogEntry = logged_messages.by_mod[mod_name][entry_key] + for entry_key in ModLoaderStore.logged_messages.by_mod[mod_name].keys(): + var entry: ModLoaderLogEntry = ModLoaderStore.logged_messages.by_mod[mod_name][entry_key] log_entries.append_array(entry.get_all_entries()) return log_entries @@ -364,8 +351,8 @@ static func get_by_mod(mod_name: String) -> Array: static func get_by_type(type: String) -> Array: var log_entries := [] - for entry_key in logged_messages.by_type[type].keys(): - var entry: ModLoaderLogEntry = logged_messages.by_type[type][entry_key] + for entry_key in ModLoaderStore.logged_messages.by_type[type].keys(): + var entry: ModLoaderLogEntry = ModLoaderStore.logged_messages.by_type[type][entry_key] log_entries.append_array(entry.get_all_entries()) return log_entries @@ -391,6 +378,17 @@ static func get_all_entries_as_string(log_entries: Array) -> Array: # Internal log functions # ============================================================================= +static func _print_rich(prefix: String, message: String, color: Color, bold := true) -> void: + if OS.has_feature("editor"): + var prefix_text := "[b]%s[/b]" % prefix if bold else prefix + print_rich("[color=%s]%s[/color]%s" % [ + color.to_html(false), + prefix_text, + message + ]) + else: + print(prefix + message) + static func _log(message: String, mod_name: String, log_type: String = "info", only_once := false) -> void: if _is_mod_name_ignored(mod_name): return @@ -422,63 +420,125 @@ static func _log(message: String, mod_name: String, log_type: String = "info", o _write_to_log_file(JSON.stringify(get_stack(), " ")) assert(false, message) "error": - printerr(log_entry.get_prefix() + message) + if ModLoaderStore.has_feature.editor: + printerr(log_entry.get_prefix(true) + message) + else: + printerr(log_entry.get_prefix() + message) push_error(message) _write_to_log_file(log_entry.get_entry()) "warning": - if verbosity >= VERBOSITY_LEVEL.WARNING: - print(log_entry.get_prefix() + message) + if _get_verbosity() >= VERBOSITY_LEVEL.WARNING: + _print_rich( + log_entry.get_prefix(), + message, + _get_color(VERBOSITY_COLOR.WARNING) + ) push_warning(message) _write_to_log_file(log_entry.get_entry()) - "info", "success": - if verbosity >= VERBOSITY_LEVEL.INFO: - print(log_entry.get_prefix() + message) + "success": + if _get_verbosity() >= VERBOSITY_LEVEL.INFO: + _print_rich( + log_entry.get_prefix(), + message, + _get_color(VERBOSITY_COLOR.SUCCESS) + ) + _write_to_log_file(log_entry.get_entry()) + "info": + if _get_verbosity() >= VERBOSITY_LEVEL.INFO: + _print_rich( + log_entry.get_prefix(), + message, + _get_color(VERBOSITY_COLOR.INFO) + ) _write_to_log_file(log_entry.get_entry()) "debug": - if verbosity >= VERBOSITY_LEVEL.DEBUG: - print(log_entry.get_prefix() + message) + if _get_verbosity() >= VERBOSITY_LEVEL.DEBUG: + _print_rich( + log_entry.get_prefix(), + message, + _get_color(VERBOSITY_COLOR.DEBUG), + true if not ModLoaderStore else ModLoaderStore.ml_options.debug_bold + ) _write_to_log_file(log_entry.get_entry()) "hint": - if OS.has_feature("editor") and verbosity >= VERBOSITY_LEVEL.DEBUG: - print_rich("[color=%s]%s[/color]" % [hint_color.to_html(false), log_entry.get_prefix() + message]) + if ModLoaderStore.has_feature.editor: + if _get_verbosity() >= VERBOSITY_LEVEL.DEBUG: + _print_rich( + log_entry.get_prefix(), + message, + _get_color(VERBOSITY_COLOR.hint) + ) + + +static func _is_mod_name_ignored(mod_log_name: String) -> bool: + if not ModLoaderStore: + return false + var ignored_mod_log_names := ModLoaderStore.ml_options.ignored_mod_names_in_log as Array -static func _is_mod_name_ignored(mod_name: String) -> bool: - if ignored_mods.is_empty(): + # No ignored mod names + if ignored_mod_log_names.size() == 0: return false - if mod_name in ignored_mods: + # Directly match a full mod log name. ex: "ModLoader:Deprecated" + if mod_log_name in ignored_mod_log_names: return true + # Match a mod log name with a wildcard. ex: "ModLoader:*" + for ignored_mod_name in ignored_mod_log_names: + if ignored_mod_name.ends_with("*"): + if mod_log_name.begins_with(ignored_mod_name.trim_suffix("*")): + return true + + # No match return false +static func _get_color(verbosity: VERBOSITY_COLOR) -> Color: + if not ModLoaderStore: + return Color("#d4d4d4") + + var color = ModLoaderStore.ml_options.get( + "%s_color" % VERBOSITY_COLOR.keys()[verbosity].to_lower() + ) + if color == null: + return Color("#d4d4d4") + + return color + +static func _get_verbosity() -> int: + if not ModLoaderStore: + return VERBOSITY_LEVEL.DEBUG + return ModLoaderStore.ml_options.log_level static func _store_log(log_entry: ModLoaderLogEntry) -> void: + # HACK: this makes logs from ModLoaderStore unable to be stored + if not ModLoaderStore: + return var existing_entry: ModLoaderLogEntry # Store in all # If it's a new entry - if not logged_messages.all.has(log_entry.get_md5()): - logged_messages.all[log_entry.get_md5()] = log_entry + if not ModLoaderStore.logged_messages.all.has(log_entry.get_md5()): + ModLoaderStore.logged_messages.all[log_entry.get_md5()] = log_entry # If it's a existing entry else: - existing_entry = logged_messages.all[log_entry.get_md5()] + existing_entry = ModLoaderStore.logged_messages.all[log_entry.get_md5()] existing_entry.time = log_entry.time existing_entry.stack.push_back(log_entry) # Store in by_mod # If the mod is not yet in "by_mod" init the entry - if not logged_messages.by_mod.has(log_entry.mod_name): - logged_messages.by_mod[log_entry.mod_name] = {} + if not ModLoaderStore.logged_messages.by_mod.has(log_entry.mod_name): + ModLoaderStore.logged_messages.by_mod[log_entry.mod_name] = {} - logged_messages.by_mod[log_entry.mod_name][log_entry.get_md5()] = log_entry if not existing_entry else existing_entry + ModLoaderStore.logged_messages.by_mod[log_entry.mod_name][log_entry.get_md5()] = log_entry if not existing_entry else existing_entry # Store in by_type - logged_messages.by_type[log_entry.type.to_lower()][log_entry.get_md5()] = log_entry if not existing_entry else existing_entry + ModLoaderStore.logged_messages.by_type[log_entry.type.to_lower()][log_entry.get_md5()] = log_entry if not existing_entry else existing_entry static func _is_logged_before(entry: ModLoaderLogEntry) -> bool: - if not logged_messages.all.has(entry.get_md5()): + if not ModLoaderStore.logged_messages.all.has(entry.get_md5()): return false return true diff --git a/addons/mod_loader/api/mod.gd b/addons/mod_loader/api/mod.gd index 069e5240..fb8fa8d9 100644 --- a/addons/mod_loader/api/mod.gd +++ b/addons/mod_loader/api/mod.gd @@ -38,6 +38,9 @@ const LOG_NAME := "ModLoader:Mod" static func install_script_extension(child_script_path: String) -> void: var mod_id: String = _ModLoaderPath.get_mod_dir(child_script_path) var mod_data: ModData = get_mod_data(mod_id) + if mod_data == null: + ModLoaderLog.warning('"%s" is not a valid mod id! Please ensure the supplied path is valid!' % mod_id, LOG_NAME) + if not ModLoaderStore.saved_extension_paths.has(mod_data.manifest.get_mod_id()): ModLoaderStore.saved_extension_paths[mod_data.manifest.get_mod_id()] = [] ModLoaderStore.saved_extension_paths[mod_data.manifest.get_mod_id()].append(child_script_path) diff --git a/addons/mod_loader/api/profile.gd b/addons/mod_loader/api/profile.gd index e8313544..94bb76c9 100644 --- a/addons/mod_loader/api/profile.gd +++ b/addons/mod_loader/api/profile.gd @@ -350,7 +350,7 @@ static func _generate_mod_list_entry(mod_id: String, is_active: bool) -> Diction # Set the current_config if the mod has a config schema and is active if is_active and not ModLoaderConfig.get_config_schema(mod_id).is_empty(): var current_config: ModConfig = ModLoaderStore.mod_data[mod_id].current_config - if current_config and current_config.is_valid: + if current_config and current_config.is_valid(): # Set to the current_config name if valid mod_list_entry.current_config = current_config.name else: diff --git a/addons/mod_loader/internal/dependency.gd b/addons/mod_loader/internal/dependency.gd index 472697b1..f6559339 100644 --- a/addons/mod_loader/internal/dependency.gd +++ b/addons/mod_loader/internal/dependency.gd @@ -49,6 +49,7 @@ static func check_dependencies(mod: ModData, is_required := true, dependency_cha _handle_missing_dependency(mod_id, dependency_id) # Flag the mod so it's not loaded later mod.is_loadable = false + mod.is_active = false else: var dependency: ModData = ModLoaderStore.mod_data[dependency_id] diff --git a/addons/mod_loader/internal/file.gd b/addons/mod_loader/internal/file.gd index 5377bad2..d697a6c7 100644 --- a/addons/mod_loader/internal/file.gd +++ b/addons/mod_loader/internal/file.gd @@ -99,7 +99,8 @@ static func get_json_as_dict_from_zip(zip_path: String, file_path: String, is_fu for path in reader.get_files(): if Array(path.rsplit("/", false, 1)).back() == file_path: full_path = path - if not full_path: + #if not full_path: + if full_path.is_empty(): ModLoaderLog.error("File was not found in zip at path %s" % [file_path], LOG_NAME) return {} @@ -194,11 +195,8 @@ static func file_exists_in_zip(zip_path: String, path: String) -> bool: var reader := zip_reader_open(zip_path) if not reader: return false - - if _ModLoaderGodot.is_version_below(_ModLoaderGodot.ENGINE_VERSION_HEX_4_2_0): - return reader.get_files().has(path.trim_prefix("res://")) - else: - return reader.file_exists(path.trim_prefix("res://")) + + return reader.get_files().has(path.trim_prefix("res://")) static func get_mod_dir_name_in_zip(zip_path: String) -> String: diff --git a/addons/mod_loader/internal/godot.gd b/addons/mod_loader/internal/godot.gd index 9cf2b4cf..3bdb5d78 100644 --- a/addons/mod_loader/internal/godot.gd +++ b/addons/mod_loader/internal/godot.gd @@ -12,9 +12,6 @@ const AUTOLOAD_CONFIG_HELP_MSG := "To configure your autoloads, go to Project > const ENGINE_VERSION_HEX_4_2_2 := 0x040202 const ENGINE_VERSION_HEX_4_2_0 := 0x040200 -static var engine_version_hex: int = Engine.get_version_info().hex - - # Check autoload positions: # Ensure 1st autoload is `ModLoaderStore`, and 2nd is `ModLoader`. static func check_autoload_positions() -> void: @@ -106,11 +103,3 @@ static func get_autoload_index(autoload_name: String) -> int: var autoload_index := autoloads.find(autoload_name) return autoload_index - - -static func is_version_below(version_hex: int) -> bool: - return engine_version_hex < version_hex - - -static func is_version_above(version_hex: int) -> bool: - return engine_version_hex > version_hex diff --git a/addons/mod_loader/internal/hooks.gd b/addons/mod_loader/internal/hooks.gd index 4e7b44d6..68398810 100644 --- a/addons/mod_loader/internal/hooks.gd +++ b/addons/mod_loader/internal/hooks.gd @@ -8,13 +8,10 @@ extends Object const LOG_NAME := "ModLoader:Hooks" -static var any_mod_hooked := false - - ## Internal ModLoader method. [br] ## To add hooks from a mod use [method ModLoaderMod.add_hook]. static func add_hook(mod_callable: Callable, script_path: String, method_name: String) -> void: - any_mod_hooked = true + ModLoaderStore.any_mod_hooked = true var hash := get_hook_hash(script_path, method_name) if not ModLoaderStore.modding_hooks.has(hash): ModLoaderStore.modding_hooks[hash] = [] diff --git a/addons/mod_loader/internal/mod_hook_preprocessor.gd b/addons/mod_loader/internal/mod_hook_preprocessor.gd index 039ce6e3..e562ef0f 100644 --- a/addons/mod_loader/internal/mod_hook_preprocessor.gd +++ b/addons/mod_loader/internal/mod_hook_preprocessor.gd @@ -99,7 +99,7 @@ func process_script(path: String, enable_hook_check := false, method_mask: Array if not method.name in method_mask: continue - var type_string := get_return_type_string(method.return) + var type_string := get_return_type_string(method.get("return")) var is_static := true if method.flags == METHOD_FLAG_STATIC + METHOD_FLAG_NORMAL else false var func_def: RegExMatch = match_func_with_whitespace(method.name, source_code) @@ -318,7 +318,6 @@ static func get_closing_paren_index(opening_paren_index: int, text: String) -> i return closing_paren_index - func edit_vanilla_method( method_name: String, text: String, @@ -327,27 +326,12 @@ func edit_vanilla_method( prefix := METHOD_PREFIX, ) -> String: text = fix_method_super(method_name, func_body, text) - text = text.erase(func_def.get_start(), func_def.get_end() - func_def.get_start()) + text = text.substr(func_def.get_start(), func_def.get_end() - func_def.get_start()) text = text.insert(func_def.get_start(), "func %s%s(" % [prefix, method_name]) return text - func fix_method_super(method_name: String, func_body: RegExMatch, text: String) -> String: - if _ModLoaderGodot.is_version_below(_ModLoaderGodot.ENGINE_VERSION_HEX_4_2_2): - return fix_method_super_before_4_2_2(method_name, func_body, text) - - return regex_super_call.sub( - text, "super.%s" % method_name, - true, func_body.get_start(), func_body.get_end() - ) - - -# https://github.com/godotengine/godot/pull/86052 -# Quote: -# When the end argument of RegEx.sub was used, -# it would truncate the Subject String before even doing the substitution. -func fix_method_super_before_4_2_2(method_name: String, func_body: RegExMatch, text: String) -> String: var text_after_func_body_end := text.substr(func_body.get_end()) text = regex_super_call.sub( @@ -359,7 +343,6 @@ func fix_method_super_before_4_2_2(method_name: String, func_body: RegExMatch, t return text - static func get_func_body_start_index(closing_paren_index: int, source_code: String) -> int: if closing_paren_index == -1: return -1 @@ -517,12 +500,14 @@ static func get_return_type_string(return_data: Dictionary) -> String: if return_data.type == 0: return "" var type_base: String - if return_data.has("class_name") and not str(return_data.class_name).is_empty(): - type_base = str(return_data.class_name) + if return_data.has("class_name") and not str(return_data.get("class_name")).is_empty(): + type_base = str(return_data.get("class_name")) else: - type_base = get_type_name(return_data.type) + type_base = get_type_name(return_data.get("type")) - var type_hint: String = "" if return_data.hint_string.is_empty() else ("[%s]" % return_data.hint_string) + var type_hint: String = "" + if not return_data.get("hint_string").is_empty(): + type_hint = "[%s]" % return_data.get("hint_string") return "%s%s" % [type_base, type_hint] diff --git a/addons/mod_loader/internal/path.gd b/addons/mod_loader/internal/path.gd index 9c67928d..8808be89 100644 --- a/addons/mod_loader/internal/path.gd +++ b/addons/mod_loader/internal/path.gd @@ -102,7 +102,8 @@ static func get_flat_view_dict(p_dir := "res://", p_match := "", p_match_is_rege else: var path := dir.get_current_dir() + ("/" if not first else "") + file_name # grab all - if not p_match: + #if not p_match: + if p_match.is_empty(): data.append(path) # grab matching strings elif not p_match_is_regex and file_name.find(p_match, 0) != -1: diff --git a/addons/mod_loader/internal/script_extension.gd b/addons/mod_loader/internal/script_extension.gd index 009a2460..22f437f8 100644 --- a/addons/mod_loader/internal/script_extension.gd +++ b/addons/mod_loader/internal/script_extension.gd @@ -115,6 +115,9 @@ static func apply_extension(extension_path: String) -> Script: child_script.reload() var parent_script: Script = child_script.get_base_script() + if not parent_script: + ModLoaderLog.warning('"%s" does not extend or inheret from a script! Please ensure you\'re using `extend "res://path/to/script"` not `extend Node`' % extension_path, LOG_NAME) + var parent_script_path: String = parent_script.resource_path # We want to save scripts for resetting later diff --git a/addons/mod_loader/mod_loader.gd b/addons/mod_loader/mod_loader.gd index 5ce020f4..66fdd304 100644 --- a/addons/mod_loader/mod_loader.gd +++ b/addons/mod_loader/mod_loader.gd @@ -206,13 +206,13 @@ func _init() -> void: ModLoaderStore.is_initializing = false - new_hooks_created.connect(_ModLoaderHooks.on_new_hooks_created) + #new_hooks_created.connect(_ModLoaderHooks.on_new_hooks_created) func _ready(): # Hooks must be generated after all autoloads are available. # Variables initialized with an autoload property cause errors otherwise. - if _ModLoaderHooks.any_mod_hooked: + if ModLoaderStore.any_mod_hooked: if OS.has_feature("editor"): ModLoaderLog.hint("No mod hooks .zip will be created when running from the editor.", LOG_NAME) ModLoaderLog.hint("You can test mod hooks by running the preprocessor on the vanilla scripts once.", LOG_NAME) diff --git a/addons/mod_loader/mod_loader_store.gd b/addons/mod_loader/mod_loader_store.gd index 5bc7bebd..bf3a84af 100644 --- a/addons/mod_loader/mod_loader_store.gd +++ b/addons/mod_loader/mod_loader_store.gd @@ -48,6 +48,8 @@ var modding_hooks := {} # } var hooked_script_paths := {} +var any_mod_hooked := false + # Order for mods to be loaded in, set by `get_load_order` var mod_load_order := [] @@ -85,13 +87,24 @@ var saved_mod_mains := {} # Stores script extension paths with the key being the namespace of a mod var saved_extension_paths := {} -var logged_messages: Dictionary: - set(val): - ModLoaderDeprecated.deprecated_changed("ModLoaderStore.logged_messages", "ModLoaderLog.logged_messages", "7.0.1") - ModLoaderLog.logged_messages = val - get: - ModLoaderDeprecated.deprecated_changed("ModLoaderStore.logged_messages", "ModLoaderLog.logged_messages", "7.0.1") - return ModLoaderLog.logged_messages +# Keeps track of logged messages, to avoid flooding the log with duplicate notices +# Can also be used by mods, eg. to create an in-game developer console that +# shows messages +var logged_messages := { + "all": {}, + "by_mod": {}, + "by_type": { + "fatal-error": {}, + "error": {}, + "warning": {}, + "info": {}, + "success": {}, + "debug": {}, + } +} + +## Array of mods that should be ignored when logging messages (contains mod IDs as strings) +var ignored_mods: Array[String] = [] # Active user profile var current_user_profile: ModUserProfile @@ -119,7 +132,6 @@ var has_feature := { func _init(): _update_ml_options_from_options_resource() _update_ml_options_from_cli_args() - _configure_logger() # ModLoaderStore is passed as argument so the cache data can be loaded on _init() _ModLoaderCache.init_cache(self) @@ -221,10 +233,3 @@ func _update_ml_options_from_cli_args() -> void: var ignore_mod_names := _ModLoaderCLI.get_cmd_line_arg_value("--log-ignore") if not ignore_mod_names == "": ml_options.ignored_mod_names_in_log = ignore_mod_names.split(",") - - -# Update static variables from the options -func _configure_logger() -> void: - ModLoaderLog.verbosity = ml_options.log_level - ModLoaderLog.ignored_mods = ml_options.ignored_mod_names_in_log - ModLoaderLog.hint_color = ml_options.hint_color diff --git a/addons/mod_loader/resources/mod_manifest.gd b/addons/mod_loader/resources/mod_manifest.gd index 7a55e0a5..1b80250b 100644 --- a/addons/mod_loader/resources/mod_manifest.gd +++ b/addons/mod_loader/resources/mod_manifest.gd @@ -80,15 +80,15 @@ func validate(manifest: Dictionary, path: String) -> bool: var missing_fields: Array[String] = [] missing_fields.append_array(ModLoaderUtils.get_missing_dict_fields(manifest, REQUIRED_MANIFEST_KEYS_ROOT)) - missing_fields.append_array(ModLoaderUtils.get_missing_dict_fields(manifest.extra, ["godot"])) - missing_fields.append_array(ModLoaderUtils.get_missing_dict_fields(manifest.extra.godot, REQUIRED_MANIFEST_KEYS_EXTRA)) + missing_fields.append_array(ModLoaderUtils.get_missing_dict_fields(manifest.get("extra"), ["godot"])) + missing_fields.append_array(ModLoaderUtils.get_missing_dict_fields(manifest.get("extra").get("godot"), REQUIRED_MANIFEST_KEYS_EXTRA)) if not missing_fields.is_empty(): validation_messages_error.push_back("Manifest is missing required fields: %s" % str(missing_fields)) - name = manifest.name - mod_namespace = manifest.namespace - version_number = manifest.version_number + name = manifest.get("name") + mod_namespace = manifest.get("namespace") + version_number = manifest.get("version_number") is_name_or_namespace_valid(name) is_name_or_namespace_valid(mod_namespace) @@ -97,11 +97,11 @@ func validate(manifest: Dictionary, path: String) -> bool: is_semver_valid(mod_id, version_number, "version_number") - description = manifest.description - website_url = manifest.website_url - dependencies = manifest.dependencies + description = manifest.get("description") + website_url = manifest.get("website_url") + dependencies = manifest.get("dependencies") - var godot_details: Dictionary = manifest.extra.godot + var godot_details: Dictionary = manifest.get("extra").get("godot") authors = ModLoaderUtils.get_array_from_dict(godot_details, "authors") optional_dependencies = ModLoaderUtils.get_array_from_dict(godot_details, "optional_dependencies") incompatibilities = ModLoaderUtils.get_array_from_dict(godot_details, "incompatibilities") @@ -113,14 +113,15 @@ func validate(manifest: Dictionary, path: String) -> bool: config_schema = ModLoaderUtils.get_dict_from_dict(godot_details, "config_schema") steam_workshop_id = ModLoaderUtils.get_string_from_dict(godot_details, "steam_workshop_id") - if ModLoaderStore.ml_options.game_version_validation == ModLoaderOptionsProfile.VERSION_VALIDATION.DEFAULT: - _is_game_version_compatible(mod_id) + if ModLoaderStore.get("ml_options"): + if ModLoaderStore.ml_options.game_version_validation == ModLoaderOptionsProfile.VERSION_VALIDATION.DEFAULT: + _is_game_version_compatible(mod_id) - if ModLoaderStore.ml_options.game_version_validation == ModLoaderOptionsProfile.VERSION_VALIDATION.CUSTOM: - if ModLoaderStore.ml_options.custom_game_version_validation_callable: - ModLoaderStore.ml_options.custom_game_version_validation_callable.call(self) - else: - ModLoaderLog.error("No custom game version validation callable detected. Please provide a valid validation callable.", LOG_NAME) + if ModLoaderStore.ml_options.game_version_validation == ModLoaderOptionsProfile.VERSION_VALIDATION.CUSTOM: + if ModLoaderStore.ml_options.custom_game_version_validation_callable: + ModLoaderStore.ml_options.custom_game_version_validation_callable.call(self) + else: + ModLoaderLog.error("No custom game version validation callable detected. Please provide a valid validation callable.", LOG_NAME) is_mod_id_array_valid(mod_id, dependencies, "dependency") is_mod_id_array_valid(mod_id, incompatibilities, "incompatibility") diff --git a/addons/mod_loader/resources/options_profile.gd b/addons/mod_loader/resources/options_profile.gd index f1982f48..2ae870c7 100644 --- a/addons/mod_loader/resources/options_profile.gd +++ b/addons/mod_loader/resources/options_profile.gd @@ -55,7 +55,18 @@ enum VERSION_VALIDATION { ## [code]ModLoader:Dependency[/code] - ignore the exact name [br] ## [code]ModLoader:*[/code] - ignore all beginning with this name [br] @export var ignored_mod_names_in_log: Array[String] = [] -@export var hint_color := Color("#70bafa") +## Highlighting color for warning type log messages +@export var warning_color := Color("#ffde66") +## Highlighting color for success type log messages +@export var success_color := Color("#5d8c3f") +## Highlighting color for info type log messages +@export var info_color := Color("#70bafa") +## Highlighting color for hint type log messages +@export var hint_color := Color("#b293fa") +## Highlighting color for debug type log messages +@export var debug_color := Color("#d4d4d4") +## Highlight debug log prefixes with bold formatting +@export var debug_bold := true @export_group("Game Data") ## Steam app id, can be found in the steam page url