Implemented per-game mod contexts

This commit is contained in:
Mr-Wiseguy 2024-07-10 17:35:14 -04:00
parent 621ee53bd3
commit f34909ef42
3 changed files with 70 additions and 39 deletions

View file

@ -13,8 +13,10 @@ namespace recomp {
uint64_t rom_hash; uint64_t rom_hash;
std::string internal_name; std::string internal_name;
std::u8string game_id; std::u8string game_id;
std::u8string mod_subdirectory;
std::span<const char> cache_data; std::span<const char> cache_data;
bool is_enabled; bool is_enabled;
bool mods;
gpr entrypoint_address; gpr entrypoint_address;
void (*entrypoint)(uint8_t* rdram, recomp_context* context); void (*entrypoint)(uint8_t* rdram, recomp_context* context);

View file

@ -99,10 +99,10 @@ namespace recomp {
std::string error_param; std::string error_param;
}; };
std::vector<ModOpenErrorDetails> scan_mod_folder(const std::filesystem::path& mod_folder); std::vector<ModOpenErrorDetails> scan_mod_folder(const std::u8string& game_id, const std::filesystem::path& mod_folder);
void enable_mod(const std::string& mod_id, bool enabled); void enable_mod(const std::u8string& game_id, const std::string& mod_id, bool enabled);
bool is_mod_enabled(const std::string& mod_id); bool is_mod_enabled(const std::u8string& game_id, const std::string& mod_id);
size_t num_opened_mods(); size_t num_opened_mods(const std::u8string& game_id);
// Internal functions, TODO move to an internal header. // Internal functions, TODO move to an internal header.
struct ModHandle; struct ModHandle;

View file

@ -11,6 +11,7 @@
#include <mutex> #include <mutex>
#include <array> #include <array>
#include <cinttypes> #include <cinttypes>
#include <cuchar>
#include "librecomp/recomp.h" #include "librecomp/recomp.h"
#include "librecomp/overlays.hpp" #include "librecomp/overlays.hpp"
@ -46,10 +47,12 @@ enum GameStatus {
// Mutexes // Mutexes
std::mutex game_roms_mutex; std::mutex game_roms_mutex;
std::mutex current_game_mutex; std::mutex current_game_mutex;
std::mutex mod_context_mutex{};
// Global variables // Global variables
std::filesystem::path config_path; std::filesystem::path config_path;
std::unordered_map<std::u8string, recomp::GameEntry> game_roms {}; std::unordered_map<std::u8string, recomp::GameEntry> game_roms {};
std::unordered_map<std::u8string, recomp::mods::ModContext> mod_contexts {};
std::u8string recomp::GameEntry::stored_filename() const { std::u8string recomp::GameEntry::stored_filename() const {
return game_id + u8".z64"; return game_id + u8".z64";
@ -60,8 +63,25 @@ void recomp::register_config_path(std::filesystem::path path) {
} }
bool recomp::register_game(const recomp::GameEntry& entry) { bool recomp::register_game(const recomp::GameEntry& entry) {
std::lock_guard<std::mutex> lock(game_roms_mutex); // TODO verify that there's no game with this ID already.
game_roms.insert({ entry.game_id, entry }); {
std::lock_guard<std::mutex> lock(game_roms_mutex);
game_roms.insert({ entry.game_id, entry });
}
// Scan for mods in the main mod folder if enabled.
if (entry.mods) {
std::vector<recomp::mods::ModOpenErrorDetails> mod_open_errors;
{
std::lock_guard mod_lock{ mod_context_mutex };
recomp::mods::ModContext& mod_context = mod_contexts[entry.game_id];
mod_open_errors = mod_context.scan_mod_folder(config_path / "mods" / entry.mod_subdirectory);
}
for (const auto& cur_error : mod_open_errors) {
printf("Error loading mod " PATHFMT ": %s (%s)\n", cur_error.mod_path.c_str(), recomp::mods::error_to_string(cur_error.error).c_str(), cur_error.error_param.c_str());
}
}
return true; return true;
} }
@ -382,27 +402,40 @@ void ultramodern::quit() {
current_game.reset(); current_game.reset();
} }
recomp::mods::ModContext mod_context{}; std::vector<recomp::mods::ModOpenErrorDetails> recomp::mods::scan_mod_folder(const std::u8string& game_id, const std::filesystem::path& mod_folder) {
std::mutex mod_context_mutex{};
std::vector<recomp::mods::ModOpenErrorDetails> recomp::mods::scan_mod_folder(const std::filesystem::path& mod_folder) {
std::lock_guard lock { mod_context_mutex }; std::lock_guard lock { mod_context_mutex };
return mod_context.scan_mod_folder(mod_folder); auto find_it = mod_contexts.find(game_id);
if (find_it == mod_contexts.end()) {
return {};
}
return find_it->second.scan_mod_folder(mod_folder);
} }
void recomp::mods::enable_mod(const std::string& mod_id, bool enabled) { void recomp::mods::enable_mod(const std::u8string& game_id, const std::string& mod_id, bool enabled) {
std::lock_guard lock { mod_context_mutex }; std::lock_guard lock { mod_context_mutex };
return mod_context.enable_mod(mod_id, enabled); auto find_it = mod_contexts.find(game_id);
if (find_it == mod_contexts.end()) {
return;
}
return find_it->second.enable_mod(mod_id, enabled);
} }
bool recomp::mods::is_mod_enabled(const std::string& mod_id) { bool recomp::mods::is_mod_enabled(const std::u8string& game_id, const std::string& mod_id) {
std::lock_guard lock { mod_context_mutex }; std::lock_guard lock { mod_context_mutex };
return mod_context.is_mod_enabled(mod_id); auto find_it = mod_contexts.find(game_id);
if (find_it == mod_contexts.end()) {
return false;
}
return find_it->second.is_mod_enabled(mod_id);
} }
size_t recomp::mods::num_opened_mods() { size_t recomp::mods::num_opened_mods(const std::u8string& game_id) {
std::lock_guard lock { mod_context_mutex }; std::lock_guard lock { mod_context_mutex };
return mod_context.num_opened_mods(); auto find_it = mod_contexts.find(game_id);
if (find_it == mod_contexts.end()) {
return 0;
}
return find_it->second.num_opened_mods();
} }
bool wait_for_game_started(uint8_t* rdram, recomp_context* context) { bool wait_for_game_started(uint8_t* rdram, recomp_context* context) {
@ -423,19 +456,25 @@ bool wait_for_game_started(uint8_t* rdram, recomp_context* context) {
init(rdram, context, game_entry.entrypoint_address); init(rdram, context, game_entry.entrypoint_address);
uint32_t mod_ram_used = 0; if (game_entry.mods) {
std::vector<recomp::mods::ModLoadErrorDetails> mod_load_errors; uint32_t mod_ram_used = 0;
{ std::vector<recomp::mods::ModLoadErrorDetails> mod_load_errors;
std::lock_guard lock { mod_context_mutex }; {
mod_load_errors = mod_context.load_mods(rdram, recomp::mod_rdram_start, mod_ram_used); std::lock_guard lock { mod_context_mutex };
} auto find_it = mod_contexts.find(current_game.value());
if (find_it == mod_contexts.end()) {
if (!mod_load_errors.empty()) { return false;
for (const auto& cur_error : mod_load_errors) { }
printf("Mod %s failed to load with error %d (%s)\n", cur_error.mod_id.c_str(), (int)cur_error.error, cur_error.error_param.c_str()); mod_load_errors = find_it->second.load_mods(rdram, recomp::mod_rdram_start, mod_ram_used);
}
if (!mod_load_errors.empty()) {
for (const auto& cur_error : mod_load_errors) {
printf("Mod %s failed to load with error %d (%s)\n", cur_error.mod_id.c_str(), (int)cur_error.error, cur_error.error_param.c_str());
}
game_status.store(GameStatus::None);
return false;
} }
game_status.store(GameStatus::None);
return false;
} }
ultramodern::load_shader_cache(game_entry.cache_data); ultramodern::load_shader_cache(game_entry.cache_data);
@ -496,16 +535,6 @@ void recomp::start(
} }
} }
// Scan for mods in the main mod folder.
std::vector<recomp::mods::ModOpenErrorDetails> mod_open_errors;
{
std::lock_guard mod_lock{ mod_context_mutex };
mod_open_errors = mod_context.scan_mod_folder(config_path / "mods");
}
for (const auto& cur_error : mod_open_errors) {
printf("Error loading mod " PATHFMT ": %s (%s)\n", cur_error.mod_path.c_str(), recomp::mods::error_to_string(cur_error.error).c_str(), cur_error.error_param.c_str());
}
// Allocate rdram_buffer // Allocate rdram_buffer
std::unique_ptr<uint8_t[]> rdram_buffer = std::make_unique<uint8_t[]>(rdram_size); std::unique_ptr<uint8_t[]> rdram_buffer = std::make_unique<uint8_t[]>(rdram_size);
std::memset(rdram_buffer.get(), 0, rdram_size); std::memset(rdram_buffer.get(), 0, rdram_size);