From 5db1c639bca4a42de191336ad4f36565da269539 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Mon, 12 Jan 2026 00:58:04 -0500 Subject: [PATCH] Add custom gamemode option to mod manifest and implement gamemode mechanism --- N64Recomp | 2 +- librecomp/include/librecomp/game.hpp | 2 +- librecomp/include/librecomp/mods.hpp | 13 +++++--- librecomp/src/mod_manifest.cpp | 7 +++++ librecomp/src/mods.cpp | 46 ++++++++++++++++++++++------ librecomp/src/recomp.cpp | 11 +++++-- 6 files changed, 63 insertions(+), 18 deletions(-) diff --git a/N64Recomp b/N64Recomp index 98bf104..2b6f056 160000 --- a/N64Recomp +++ b/N64Recomp @@ -1 +1 @@ -Subproject commit 98bf104b1b5ed83126af8bcab0cc964782617dbf +Subproject commit 2b6f05688de2abc7d86da5b4a89b84c2c6acbabe diff --git a/librecomp/include/librecomp/game.hpp b/librecomp/include/librecomp/game.hpp index 16f731e..69e501e 100644 --- a/librecomp/include/librecomp/game.hpp +++ b/librecomp/include/librecomp/game.hpp @@ -108,7 +108,7 @@ namespace recomp { bool sram_allowed(); bool flashram_allowed(); - void start_game(const std::u8string& game_id); + void start_game(const std::u8string& game_id, const std::string& game_mode_id); std::u8string current_game_id(); std::string current_mod_game_id(); } diff --git a/librecomp/include/librecomp/mods.hpp b/librecomp/include/librecomp/mods.hpp index eb36d19..c404109 100644 --- a/librecomp/include/librecomp/mods.hpp +++ b/librecomp/include/librecomp/mods.hpp @@ -204,6 +204,7 @@ namespace recomp { std::vector dependencies; bool runtime_toggleable; bool enabled_by_default; + bool custom_gamemode; }; struct ModManifest { @@ -221,6 +222,7 @@ namespace recomp { Version version; bool runtime_toggleable; bool enabled_by_default; + bool custom_gamemode; std::vector native_libraries; std::unique_ptr file_handle; @@ -319,10 +321,10 @@ namespace recomp { void close_mods(); 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); + bool is_mod_enabled(const std::string& mod_id) const; + bool is_mod_auto_enabled(const std::string& mod_id) const; size_t num_opened_mods(); - std::vector load_mods(const GameEntry& game_entry, uint8_t* rdram, int32_t load_address, uint32_t& ram_used); + std::vector load_mods(const GameEntry& game_entry, const std::string& game_mode_id, uint8_t* rdram, int32_t load_address, uint32_t& ram_used); void unload_mods(); std::string get_mod_id_from_filename(const std::filesystem::path& mod_filename) const; std::filesystem::path get_mod_filename(const std::string& mod_id) const; @@ -330,6 +332,7 @@ namespace recomp { size_t get_mod_order_index(size_t mod_index) const; std::optional get_details_for_mod(const std::string& mod_id) const; std::vector get_all_mod_details(const std::string& mod_game_id); + size_t game_mode_count(const std::string& mod_game_id, bool include_disabled) const; recomp::Version get_mod_version(size_t mod_index); std::string get_mod_id(size_t mod_index); void set_mod_index(const std::string &mod_game_id, const std::string &mod_id, size_t index); @@ -475,7 +478,8 @@ namespace recomp { .authors = manifest.authors, .dependencies = manifest.dependencies, .runtime_toggleable = is_runtime_toggleable(), - .enabled_by_default = manifest.enabled_by_default + .enabled_by_default = manifest.enabled_by_default, + .custom_gamemode = manifest.custom_gamemode }; } private: @@ -592,6 +596,7 @@ namespace recomp { std::filesystem::path get_mods_directory(); std::optional get_details_for_mod(const std::string& mod_id); std::vector get_all_mod_details(const std::string& mod_game_id); + size_t game_mode_count(const std::string& mod_game_id, bool include_disabled); recomp::Version get_mod_version(size_t mod_index); std::string get_mod_id(size_t mod_index); void enable_mod(const std::string& mod_id, bool enabled); diff --git a/librecomp/src/mod_manifest.cpp b/librecomp/src/mod_manifest.cpp index c5058d1..71560eb 100644 --- a/librecomp/src/mod_manifest.cpp +++ b/librecomp/src/mod_manifest.cpp @@ -151,6 +151,7 @@ const std::string version_key = "version"; const std::string authors_key = "authors"; const std::string minimum_recomp_version_key = "minimum_recomp_version"; const std::string enabled_by_default_key = "enabled_by_default"; +const std::string custom_gamemode_key = "custom_gamemode"; const std::string dependencies_key = "dependencies"; const std::string optional_dependencies_key = "optional_dependencies"; const std::string native_libraries_key = "native_libraries"; @@ -706,6 +707,12 @@ recomp::mods::ModOpenError recomp::mods::parse_manifest(ModManifest& ret, const return current_error; } + // Custom gamemode (optional, false if not present) + current_error = try_get(ret.custom_gamemode, manifest_json, custom_gamemode_key, false, error_param, false); + if (current_error != ModOpenError::Good) { + return current_error; + } + // Dependencies (optional) std::vector dep_strings{}; current_error = try_get_vec(dep_strings, manifest_json, dependencies_key, false, error_param); diff --git a/librecomp/src/mods.cpp b/librecomp/src/mods.cpp index b1ead55..c26da02 100644 --- a/librecomp/src/mods.cpp +++ b/librecomp/src/mods.cpp @@ -1127,11 +1127,11 @@ void recomp::mods::ModContext::enable_mod(const std::string& mod_id, bool enable } } -bool recomp::mods::ModContext::is_mod_enabled(const std::string& mod_id) { +bool recomp::mods::ModContext::is_mod_enabled(const std::string& mod_id) const { return enabled_mods.contains(mod_id); } -bool recomp::mods::ModContext::is_mod_auto_enabled(const std::string& mod_id) { +bool recomp::mods::ModContext::is_mod_auto_enabled(const std::string& mod_id) const { return auto_enabled_mods.contains(mod_id); } @@ -1210,6 +1210,29 @@ std::vector recomp::mods::ModContext::get_all_mod_deta return ret; } +size_t recomp::mods::ModContext::game_mode_count(const std::string& mod_game_id, bool include_disabled) const { + size_t ret = 0; + bool all_games = mod_game_id.empty(); + size_t game_index = (size_t)-1; + + auto find_game_it = mod_game_ids.find(mod_game_id); + if (find_game_it != mod_game_ids.end()) { + game_index = find_game_it->second; + } + + for (const ModHandle &mod : opened_mods) { + if (all_games || mod.is_for_game(game_index)) { + if (include_disabled || is_mod_enabled(mod.manifest.mod_id) || is_mod_auto_enabled(mod.manifest.mod_id)) { + if (mod.manifest.custom_gamemode) { + ret++; + } + } + } + } + + return ret; +} + recomp::Version recomp::mods::ModContext::get_mod_version(size_t mod_index) { return opened_mods[mod_index].manifest.version; } @@ -1484,7 +1507,7 @@ void recomp::mods::ModContext::set_mod_config_directory(const std::filesystem::p mod_config_directory = path; } -std::vector recomp::mods::ModContext::load_mods(const GameEntry& game_entry, uint8_t* rdram, int32_t load_address, uint32_t& ram_used) { +std::vector recomp::mods::ModContext::load_mods(const GameEntry& game_entry, const std::string& game_mode_id, uint8_t* rdram, int32_t load_address, uint32_t& ram_used) { std::vector ret{}; ram_used = 0; num_events = recomp::overlays::num_base_events(); @@ -1529,15 +1552,18 @@ std::vector recomp::mods::ModContext::load_mo 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) || 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); + // Only load gamemode mods if the current gamemode matches the mod id. + if (!mod.manifest.custom_gamemode || game_mode_id == mod.manifest.mod_id) { + active_mods.push_back(mod_index); + loaded_mods_by_id.emplace(mod.manifest.mod_id, mod_index); - printf("Loading mod %s\n", mod.manifest.mod_id.c_str()); - std::string load_error_param; - ModLoadError load_error = load_mod(mod, load_error_param); + printf("Loading mod %s\n", mod.manifest.mod_id.c_str()); + std::string load_error_param; + ModLoadError load_error = load_mod(mod, load_error_param); - if (load_error != ModLoadError::Good) { - ret.emplace_back(mod.manifest.mod_id, load_error, load_error_param); + if (load_error != ModLoadError::Good) { + ret.emplace_back(mod.manifest.mod_id, load_error, load_error_param); + } } } } diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index 9c873a8..985751f 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -461,6 +461,7 @@ extern "C" void do_break(uint32_t vram) { exit(EXIT_FAILURE); } +std::string current_game_mode_id; std::optional current_game = std::nullopt; std::atomic game_status = GameStatus::None; @@ -526,8 +527,9 @@ std::string recomp::current_mod_game_id() { return game_entry.mod_game_id; } -void recomp::start_game(const std::u8string& game_id) { +void recomp::start_game(const std::u8string& game_id, const std::string& game_mode_id) { std::lock_guard lock(current_game_mutex); + current_game_mode_id = game_mode_id; current_game = game_id; game_status.store(GameStatus::Running); game_status.notify_all(); @@ -629,6 +631,11 @@ std::vector recomp::mods::get_all_mod_details(const st return mod_context->get_all_mod_details(mod_game_id); } +size_t recomp::mods::game_mode_count(const std::string& mod_game_id, bool include_disabled) { + std::lock_guard lock { mod_context_mutex }; + return mod_context->game_mode_count(mod_game_id, include_disabled); +} + recomp::Version recomp::mods::get_mod_version(size_t mod_index) { std::lock_guard lock { mod_context_mutex }; return mod_context->get_mod_version(mod_index); @@ -668,7 +675,7 @@ bool wait_for_game_started(uint8_t* rdram, recomp_context* context) { std::vector mod_load_errors; { std::lock_guard lock { mod_context_mutex }; - mod_load_errors = mod_context->load_mods(game_entry, rdram, recomp::mod_rdram_start, mod_ram_used); + mod_load_errors = mod_context->load_mods(game_entry, current_game_mode_id, rdram, recomp::mod_rdram_start, mod_ram_used); } if (!mod_load_errors.empty()) {