From e814ad6f833e1e73269625b8beef67f50dd54682 Mon Sep 17 00:00:00 2001 From: Dario Date: Wed, 29 Jan 2025 23:53:22 -0300 Subject: [PATCH] Auto-enabled mods. --- librecomp/include/librecomp/mods.hpp | 2 + librecomp/src/mods.cpp | 80 ++++++++++++++++++++++++++-- librecomp/src/recomp.cpp | 2 +- 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/librecomp/include/librecomp/mods.hpp b/librecomp/include/librecomp/mods.hpp index aad1385..ee983cc 100644 --- a/librecomp/include/librecomp/mods.hpp +++ b/librecomp/include/librecomp/mods.hpp @@ -326,6 +326,7 @@ namespace recomp { void load_mods_config(); void enable_mod(const std::string& mod_id, bool enabled, bool trigger_save); bool is_mod_enabled(const std::string& mod_id); + bool is_mod_auto_enabled(const std::string& mod_id); size_t num_opened_mods(); std::vector load_mods(const GameEntry& game_entry, uint8_t* rdram, int32_t load_address, uint32_t& ram_used); void unload_mods(); @@ -369,6 +370,7 @@ namespace recomp { std::mutex opened_mods_mutex; std::unordered_set mod_ids; std::unordered_set enabled_mods; + std::unordered_set auto_enabled_mods; std::unordered_map patched_funcs; std::unordered_map loaded_mods_by_id; std::unique_ptr mod_configuration_thread; diff --git a/librecomp/src/mods.cpp b/librecomp/src/mods.cpp index 0728241..9b1f80a 100644 --- a/librecomp/src/mods.cpp +++ b/librecomp/src/mods.cpp @@ -272,8 +272,8 @@ recomp::mods::ModHandle::ModHandle(const ModContext& context, ModManifest&& mani code_handle(), recompiler_context{std::make_unique()}, content_types{std::move(content_types)}, - game_indices{std::move(game_indices)}, - thumbnail{std::move(thumbnail)} + thumbnail{ std::move(thumbnail) }, + game_indices{std::move(game_indices)} { runtime_toggleable = true; for (ModContentTypeId type : this->content_types) { @@ -630,6 +630,7 @@ void recomp::mods::ModContext::close_mods() { opened_mods_order.clear(); mod_ids.clear(); enabled_mods.clear(); + auto_enabled_mods.clear(); } bool save_mod_config_storage(const std::filesystem::path &path, const std::string &mod_id, const recomp::Version &mod_version, const recomp::mods::ConfigStorage &config_storage, const recomp::mods::ConfigSchema &config_schema) { @@ -959,21 +960,90 @@ void recomp::mods::ModContext::enable_mod(const std::string& mod_id, bool enable if (enabled) { bool was_enabled = enabled_mods.emplace(mod_id).second; + // If mods have been loaded and a mod was successfully enabled by this call, call the on_enabled handlers for its content types. if (was_enabled && mods_loaded) { for (ModContentTypeId type_id : mod.content_types) { content_types[type_id.value].on_enabled(*this, mod); } } + + if (was_enabled) { + std::vector mod_stack; + mod_stack.emplace_back(mod_id); + while (!mod_stack.empty()) { + std::string mod_from_stack = std::move(mod_stack.back()); + mod_stack.pop_back(); + + auto mod_from_stack_it = opened_mods_by_id.find(mod_from_stack); + if (mod_from_stack_it != opened_mods_by_id.end()) { + const ModHandle &mod_from_stack_handle = opened_mods[mod_from_stack_it->second]; + for (const Dependency &dependency : mod_from_stack_handle.manifest.dependencies) { + if (!auto_enabled_mods.contains(dependency.mod_id)) { + auto_enabled_mods.emplace(dependency.mod_id); + mod_stack.emplace_back(dependency.mod_id); + + if (mods_loaded) { + for (ModContentTypeId type_id : mod_from_stack_handle.content_types) { + content_types[type_id.value].on_enabled(*this, mod_from_stack_handle); + } + } + } + } + } + } + } } else { bool was_disabled = enabled_mods.erase(mod_id) != 0; + // If mods have been loaded and a mod was successfully disabled by this call, call the on_disabled handlers for its content types. if (was_disabled && mods_loaded) { for (ModContentTypeId type_id : mod.content_types) { content_types[type_id.value].on_disabled(*this, mod); } } + + if (was_disabled) { + // The algorithm needs to be run again with a new set of auto-enabled mods from scratch for all enabled mods. + std::unordered_set new_auto_enabled_mods; + for (const std::string &enabled_mod_id : enabled_mods) { + std::vector mod_stack; + mod_stack.emplace_back(enabled_mod_id); + while (!mod_stack.empty()) { + std::string mod_from_stack = std::move(mod_stack.back()); + mod_stack.pop_back(); + + auto mod_from_stack_it = opened_mods_by_id.find(mod_from_stack); + if (mod_from_stack_it != opened_mods_by_id.end()) { + const ModHandle &mod_from_stack_handle = opened_mods[mod_from_stack_it->second]; + for (const Dependency &dependency : mod_from_stack_handle.manifest.dependencies) { + if (!new_auto_enabled_mods.contains(dependency.mod_id)) { + new_auto_enabled_mods.emplace(dependency.mod_id); + mod_stack.emplace_back(dependency.mod_id); + } + } + } + } + } + + if (mods_loaded) { + // Before replacing the old set with the new one, whatever does not exist in the new set anymore should trigger it's on_disabled callback. + for (const std::string &enabled_mod_id : auto_enabled_mods) { + if (!new_auto_enabled_mods.contains(enabled_mod_id)) { + auto enabled_mod_it = opened_mods_by_id.find(enabled_mod_id); + if (enabled_mod_it != opened_mods_by_id.end()) { + const ModHandle &enabled_mod_handle = opened_mods[enabled_mod_it->second]; + for (ModContentTypeId type_id : enabled_mod_handle.content_types) { + content_types[type_id.value].on_disabled(*this, enabled_mod_handle); + } + } + } + } + } + + auto_enabled_mods = new_auto_enabled_mods; + } } if (trigger_save) { @@ -985,6 +1055,10 @@ bool recomp::mods::ModContext::is_mod_enabled(const std::string& mod_id) { return enabled_mods.contains(mod_id); } +bool recomp::mods::ModContext::is_mod_auto_enabled(const std::string& mod_id) { + return auto_enabled_mods.contains(mod_id); +} + size_t recomp::mods::ModContext::num_opened_mods() { return opened_mods.size(); } @@ -1338,7 +1412,7 @@ std::vector recomp::mods::ModContext::load_mo // Find and load active mods. for (size_t mod_index = 0; mod_index < opened_mods.size(); mod_index++) { auto& mod = opened_mods[mod_index]; - if (mod.is_for_game(mod_game_index) && enabled_mods.contains(mod.manifest.mod_id)) { + if (mod.is_for_game(mod_game_index) && (enabled_mods.contains(mod.manifest.mod_id) || auto_enabled_mods.contains(mod.manifest.mod_id))) { active_mods.push_back(mod_index); loaded_mods_by_id.emplace(mod.manifest.mod_id, mod_index); diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index e53ac1e..9316c6b 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -517,7 +517,7 @@ bool recomp::mods::is_mod_enabled(const std::string& mod_id) { bool recomp::mods::is_mod_auto_enabled(const std::string& mod_id) { std::lock_guard lock{ mod_context_mutex }; - return false; // TODO + return mod_context->is_mod_auto_enabled(mod_id); } const recomp::mods::ConfigSchema &recomp::mods::get_mod_config_schema(const std::string &mod_id) {