From 924f783e35421347a50db0c7fdbf4aaa7d223a86 Mon Sep 17 00:00:00 2001 From: Kai Roth Date: Fri, 23 Jun 2023 15:37:41 +0200 Subject: [PATCH] feat: :sparkles: added `zip_name` and `zip_path` to `ModData` --- addons/mod_loader/api/profile.gd | 3 +- addons/mod_loader/internal/file.gd | 37 ++++++++---- .../mod_loader/internal/third_party/steam.gd | 12 ++-- addons/mod_loader/mod_loader.gd | 60 ++++++++++++------- addons/mod_loader/mod_loader_store.gd | 3 + addons/mod_loader/resources/mod_data.gd | 8 +-- 6 files changed, 80 insertions(+), 43 deletions(-) diff --git a/addons/mod_loader/api/profile.gd b/addons/mod_loader/api/profile.gd index 96c114e1..657d7d6e 100644 --- a/addons/mod_loader/api/profile.gd +++ b/addons/mod_loader/api/profile.gd @@ -208,7 +208,8 @@ static func _update_disabled_mods() -> void: # Iterate through the mod list in the current user profile to find disabled mods for mod_id in current_user_profile.mod_list: var mod_list_entry: Dictionary = current_user_profile.mod_list[mod_id] - ModLoaderStore.mod_data[mod_id].is_active = mod_list_entry.is_active + if ModLoaderStore.mod_data.has(mod_id): + ModLoaderStore.mod_data[mod_id].is_active = mod_list_entry.is_active ModLoaderLog.debug( "Updated the active state of all mods, based on the current user profile \"%s\"" diff --git a/addons/mod_loader/internal/file.gd b/addons/mod_loader/internal/file.gd index 2e956f06..a7287392 100644 --- a/addons/mod_loader/internal/file.gd +++ b/addons/mod_loader/internal/file.gd @@ -44,18 +44,20 @@ static func _get_json_string_as_dict(string: String) -> Dictionary: # Load the mod ZIP from the provided directory -static func load_zips_in_folder(folder_path: String) -> int: - var temp_zipped_mods_count := 0 +static func load_zips_in_folder(folder_path: String) -> Dictionary: + var zip_data := {} var mod_dir := Directory.new() var mod_dir_open_error := mod_dir.open(folder_path) if not mod_dir_open_error == OK: ModLoaderLog.error("Can't open mod folder %s (Error: %s)" % [folder_path, mod_dir_open_error], LOG_NAME) - return -1 + return {} var mod_dir_listdir_error := mod_dir.list_dir_begin() if not mod_dir_listdir_error == OK: ModLoaderLog.error("Can't read mod folder %s (Error: %s)" % [folder_path, mod_dir_listdir_error], LOG_NAME) - return -1 + return {} + + # Get all zip folders inside the game mod folder while true: @@ -76,9 +78,25 @@ static func load_zips_in_folder(folder_path: String) -> int: # Go to the next file continue - var mod_folder_path := folder_path.plus_file(mod_zip_file_name) - var mod_folder_global_path := ProjectSettings.globalize_path(mod_folder_path) - var is_mod_loaded_successfully := ProjectSettings.load_resource_pack(mod_folder_global_path, false) + var mod_zip_path := folder_path.plus_file(mod_zip_file_name) + var mod_zip_global_path := ProjectSettings.globalize_path(mod_zip_path) + var is_mod_loaded_successfully := ProjectSettings.load_resource_pack(mod_zip_global_path, false) + + # Get the current directories inside UNPACKED_DIR + # This array is used to determine which directory is new + var current_mod_dirs := _ModLoaderPath.get_dir_paths_in_dir(_ModLoaderPath.get_unpacked_mods_dir_path()) + # Create a backup to reference when the next mod is loaded + var current_mod_dirs_backup := current_mod_dirs.duplicate() + + # Remove all directory paths that existed before, leaving only the one added last + for previous_mod_dir in ModLoaderStore.previous_mod_dirs: + current_mod_dirs.erase(previous_mod_dir) + + # The key is the mod_id of the latest loaded mod, and the value is the path to the zip file + zip_data[current_mod_dirs[0].get_slice("/", 3)] = mod_zip_global_path + + # Update previous_mod_dirs in ModLoaderStore to use for the next mod + ModLoaderStore.previous_mod_dirs = current_mod_dirs_backup # Notifies developer of an issue with Godot, where using `load_resource_pack` # in the editor WIPES the entire virtual res:// directory the first time you @@ -94,7 +112,7 @@ static func load_zips_in_folder(folder_path: String) -> int: "Please unpack your mod ZIPs instead, and add them to ", _ModLoaderPath.get_unpacked_mods_dir_path()), LOG_NAME) ModLoaderStore.has_shown_editor_zips_warning = true - ModLoaderLog.debug("Found mod ZIP: %s" % mod_folder_global_path, LOG_NAME) + ModLoaderLog.debug("Found mod ZIP: %s" % mod_zip_global_path, LOG_NAME) # If there was an error loading the mod zip file if not is_mod_loaded_successfully: @@ -104,11 +122,10 @@ static func load_zips_in_folder(folder_path: String) -> int: # Mod successfully loaded! ModLoaderLog.success("%s loaded." % mod_zip_file_name, LOG_NAME) - temp_zipped_mods_count += 1 mod_dir.list_dir_end() - return temp_zipped_mods_count + return zip_data # Save Data diff --git a/addons/mod_loader/internal/third_party/steam.gd b/addons/mod_loader/internal/third_party/steam.gd index b05fc8fd..e4371896 100644 --- a/addons/mod_loader/internal/third_party/steam.gd +++ b/addons/mod_loader/internal/third_party/steam.gd @@ -9,8 +9,8 @@ const LOG_NAME := "ModLoader:ThirdParty:Steam" # Load mod ZIPs from Steam workshop folders. Uses 2 loops: One for each # workshop item's folder, with another inside that which loops over the ZIPs # inside each workshop item's folder -static func load_steam_workshop_zips() -> int: - var temp_zipped_mods_count := 0 +static func load_steam_workshop_zips() -> Dictionary: + var zip_data := {} var workshop_folder_path := _get_path_to_workshop() ModLoaderLog.info("Checking workshop items, with path: \"%s\"" % workshop_folder_path, LOG_NAME) @@ -19,11 +19,11 @@ static func load_steam_workshop_zips() -> int: var workshop_dir_open_error := workshop_dir.open(workshop_folder_path) if not workshop_dir_open_error == OK: ModLoaderLog.error("Can't open workshop folder %s (Error: %s)" % [workshop_folder_path, workshop_dir_open_error], LOG_NAME) - return -1 + return {} var workshop_dir_listdir_error := workshop_dir.list_dir_begin() if not workshop_dir_listdir_error == OK: ModLoaderLog.error("Can't read workshop folder %s (Error: %s)" % [workshop_folder_path, workshop_dir_listdir_error], LOG_NAME) - return -1 + return {} # Loop 1: Workshop folders while true: @@ -42,11 +42,11 @@ static func load_steam_workshop_zips() -> int: continue # Loop 2: ZIPs inside the workshop folders - temp_zipped_mods_count += _ModLoaderFile.load_zips_in_folder(ProjectSettings.globalize_path(item_path)) + zip_data.merge(_ModLoaderFile.load_zips_in_folder(ProjectSettings.globalize_path(item_path))) workshop_dir.list_dir_end() - return temp_zipped_mods_count + return zip_data # Get the path to the Steam workshop folder. Only works for Steam games, as it diff --git a/addons/mod_loader/mod_loader.gd b/addons/mod_loader/mod_loader.gd index e2a3040b..3380c119 100644 --- a/addons/mod_loader/mod_loader.gd +++ b/addons/mod_loader/mod_loader.gd @@ -85,11 +85,20 @@ func _exit_tree() -> void: func _load_mods() -> void: # Loop over "res://mods" and add any mod zips to the unpacked virtual # directory (UNPACKED_DIR) - var unzipped_mods := _load_mod_zips() - if unzipped_mods > 0: - ModLoaderLog.success("DONE: Loaded %s mod files into the virtual filesystem" % unzipped_mods, LOG_NAME) - else: + var zip_data := _load_mod_zips() + + if zip_data.empty(): ModLoaderLog.info("No zipped mods found", LOG_NAME) + else: + ModLoaderLog.success("DONE: Loaded %s mod files into the virtual filesystem" % zip_data.size(), LOG_NAME) + + # Initializes the mod_data dictionary if zipped mods are loaded. + # If mods are unpacked in the "mods-unpacked" directory, + # mod_data is initialized in the _setup_mods() function. + for mod_id in zip_data.keys(): + var zip_path: String = zip_data[mod_id] + _init_mod_data(mod_id, zip_path) + # Loop over UNPACKED_DIR. This triggers _init_mod_data for each mod # directory, which adds their data to mod_data. @@ -219,19 +228,21 @@ func _check_autoload_positions() -> void: # Loop over "res://mods" and add any mod zips to the unpacked virtual directory # (UNPACKED_DIR) -func _load_mod_zips() -> int: - var zipped_mods_count := 0 +func _load_mod_zips() -> Dictionary: + var zip_data := {} if not ModLoaderStore.ml_options.steam_workshop_enabled: var mods_folder_path := _ModLoaderPath.get_path_to_mods() # If we're not using Steam workshop, just loop over the mod ZIPs. - zipped_mods_count += _ModLoaderFile.load_zips_in_folder(mods_folder_path) + var loaded_zip_data := _ModLoaderFile.load_zips_in_folder(mods_folder_path) + zip_data.merge(loaded_zip_data) else: # If we're using Steam workshop, loop over the workshop item directories - zipped_mods_count += _ModLoaderSteam.load_steam_workshop_zips() + var loaded_workshop_zip_data := _ModLoaderSteam.load_steam_workshop_zips() + zip_data.merge(loaded_workshop_zip_data) - return zipped_mods_count + return zip_data # Loop over UNPACKED_DIR and triggers `_init_mod_data` for each mod directory, @@ -271,8 +282,12 @@ func _setup_mods() -> int: ModLoaderLog.info("Skipped setting up mod: \"%s\"" % mod_dir_name, LOG_NAME) continue - # Init the mod data for each mod - _init_mod_data(mod_dir_name) + # Checks if there is no existing mod_data for this mod, + # indicating that the mod is loaded from UNPACKED_DIR + if not ModLoaderStore.mod_data.has(mod_dir_name): + # If there is no existing mod_data for this mod, initialize it now. + _init_mod_data(mod_dir_name) + unpacked_mods_count += 1 dir.list_dir_end() @@ -282,19 +297,20 @@ func _setup_mods() -> int: # Add a mod's data to mod_data. # The mod_folder_path is just the folder name that was added to UNPACKED_DIR, # which depends on the name used in a given mod ZIP (eg "mods-unpacked/Folder-Name") -func _init_mod_data(mod_folder_path: String) -> void: - # The file name should be a valid mod id - var dir_name := _ModLoaderPath.get_file_name_from_path(mod_folder_path, false, true) - - # Path to the mod in UNPACKED_DIR (eg "res://mods-unpacked/My-Mod") - var local_mod_path := _ModLoaderPath.get_unpacked_mods_dir_path().plus_file(dir_name) - - var mod := ModData.new(local_mod_path) - mod.dir_name = dir_name +func _init_mod_data(mod_id: String, zip_path := "") -> void: + # Path to the mod in UNPACKED_DIR (eg "res://mods-unpacked/My-Mod") + var local_mod_path := _ModLoaderPath.get_unpacked_mods_dir_path().plus_file(mod_id) + + var mod := ModData.new() + if not zip_path.empty(): + mod.zip_name = _ModLoaderPath.get_file_name_from_path(zip_path) + mod.zip_path = zip_path + mod.dir_path = local_mod_path + mod.dir_name = mod_id var mod_overwrites_path := mod.get_optional_mod_file_path(ModData.optional_mod_files.OVERWRITES) mod.is_overwrite = _ModLoaderFile.file_exists(mod_overwrites_path) - mod.is_locked = true if dir_name in ModLoaderStore.ml_options.locked_mods else false - ModLoaderStore.mod_data[dir_name] = mod + mod.is_locked = true if mod_id in ModLoaderStore.ml_options.locked_mods else false + ModLoaderStore.mod_data[mod_id] = mod # Get the mod file paths # Note: This was needed in the original version of this script, but it's diff --git a/addons/mod_loader/mod_loader_store.gd b/addons/mod_loader/mod_loader_store.gd index 4c9c24f6..4c89d790 100644 --- a/addons/mod_loader/mod_loader_store.gd +++ b/addons/mod_loader/mod_loader_store.gd @@ -44,6 +44,9 @@ var mod_missing_dependencies := {} # Helps to decide whether a script extension should go through the _ModLoaderScriptExtension.handle_script_extensions() process var is_initializing := true +# Used when loading mod zips to determine which mod zip corresponds to which mod directory in the UNPACKED_DIR. +var previous_mod_dirs := [] + # Store all extenders paths var script_extensions := [] diff --git a/addons/mod_loader/resources/mod_data.gd b/addons/mod_loader/resources/mod_data.gd index a2a2dbe9..c7640e09 100644 --- a/addons/mod_loader/resources/mod_data.gd +++ b/addons/mod_loader/resources/mod_data.gd @@ -23,6 +23,10 @@ enum optional_mod_files { OVERWRITES } +# Name of the Mod's zip file +var zip_name := "" +# Path to the Mod's zip file +var zip_path := "" # Directory of the mod. Has to be identical to [method ModManifest.get_mod_id] var dir_name := "" # Path to the Mod's Directory @@ -47,10 +51,6 @@ var current_config: ModConfig setget _set_current_config var file_paths: PoolStringArray = [] -func _init(_dir_path: String) -> void: - dir_path = _dir_path - - # Load meta data from a mod's manifest.json file func load_manifest() -> void: if not _has_required_files():