mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-06-07 11:27:26 -04:00
Improve native mod load failure diagnostics
This commit is contained in:
@@ -45,6 +45,41 @@ struct NativeMod {
|
||||
FnCleanup fn_cleanup = nullptr;
|
||||
};
|
||||
|
||||
enum class NativeModStatus : u8 {
|
||||
/**
|
||||
* Mod does not have native code included.
|
||||
*/
|
||||
None,
|
||||
|
||||
/**
|
||||
* Native code mod loaded successfully.
|
||||
*
|
||||
* Note that this only indicates load status of the native library. If the native lib throws in
|
||||
* its init function, it will still be disabled!
|
||||
*/
|
||||
Loaded,
|
||||
|
||||
/**
|
||||
* This build was compiled without native mod support!
|
||||
*/
|
||||
BuildDisabled,
|
||||
|
||||
/**
|
||||
* Mod does not have a native library suitable for this build's platform.
|
||||
*/
|
||||
ModMissingPlatform,
|
||||
|
||||
/**
|
||||
* Mod is built for a different API version than this build of the game.
|
||||
*/
|
||||
ApiVersionMismatch,
|
||||
|
||||
/**
|
||||
* Unknown error loading the native mod.
|
||||
*/
|
||||
Unknown,
|
||||
};
|
||||
|
||||
struct LoadedMod {
|
||||
ModMetadata metadata;
|
||||
std::string mod_path;
|
||||
@@ -53,7 +88,9 @@ struct LoadedMod {
|
||||
bool active = false;
|
||||
bool load_failed = false;
|
||||
|
||||
NativeModStatus native_status = NativeModStatus::None;
|
||||
std::unique_ptr<NativeMod> native;
|
||||
|
||||
std::unique_ptr<modding::ModBundle> bundle;
|
||||
|
||||
std::vector<RmlTabContentCallback> tab_content;
|
||||
@@ -77,7 +114,7 @@ private:
|
||||
bool m_initialized = false;
|
||||
|
||||
void tryLoadDusk(const std::filesystem::path& modPath, bool fromDir);
|
||||
bool tryLoadNativeMod(LoadedMod& mod);
|
||||
void tryLoadNativeMod(LoadedMod& mod);
|
||||
void buildAPI(LoadedMod& mod);
|
||||
void initOverlayFiles();
|
||||
};
|
||||
|
||||
@@ -119,10 +119,11 @@ static bool checkDuplicateMod(const ModMetadata& metadata, const std::vector<Loa
|
||||
});
|
||||
}
|
||||
|
||||
bool ModLoader::tryLoadNativeMod(LoadedMod& mod) {
|
||||
void ModLoader::tryLoadNativeMod(LoadedMod& mod) {
|
||||
if (!EnableCodeMods) {
|
||||
Log.error("Code mods are not available in this build");
|
||||
return false;
|
||||
mod.native_status = NativeModStatus::BuildDisabled;
|
||||
return;
|
||||
}
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
@@ -135,7 +136,8 @@ bool ModLoader::tryLoadNativeMod(LoadedMod& mod) {
|
||||
if (dllEntry.empty()) {
|
||||
Log.error(
|
||||
"no *{} found in {} — skipping", NativeModule::LibraryExtension, mod.metadata.id);
|
||||
return false;
|
||||
mod.native_status = NativeModStatus::ModMissingPlatform;
|
||||
return;
|
||||
}
|
||||
|
||||
const fs::path cacheDir = m_modsDir / ".cache" / mod.metadata.id;
|
||||
@@ -150,14 +152,14 @@ bool ModLoader::tryLoadNativeMod(LoadedMod& mod) {
|
||||
} catch (const std::runtime_error& e) {
|
||||
Log.error(
|
||||
"failed to extract {} from {}", dllEntry, mod.metadata.id);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::ofstream out(dllCachePath, std::ios::binary | std::ios::out);
|
||||
if (!out) {
|
||||
Log.error("failed to write {}", io::fs_path_to_string(dllCachePath));
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
out.write(
|
||||
@@ -170,14 +172,15 @@ bool ModLoader::tryLoadNativeMod(LoadedMod& mod) {
|
||||
nativeMod->handle = std::make_unique<NativeModule>(dllCachePath);
|
||||
} catch (const std::runtime_error& e) {
|
||||
Log.error("failed to open {}: {}", io::fs_path_to_string(dllCachePath), e.what());
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto mod_api_ver = nativeMod->handle->LookupSymbol<uint32_t*>("mod_api_version");
|
||||
if (mod_api_ver && *mod_api_ver != DUSK_MOD_API_VERSION) {
|
||||
Log.error("{} expects API v{} but engine is v{}, skipping",
|
||||
io::fs_path_to_string(fs::path(dllEntry).filename()), *mod_api_ver, DUSK_MOD_API_VERSION);
|
||||
return false;
|
||||
mod.native_status = NativeModStatus::ApiVersionMismatch;
|
||||
return;
|
||||
}
|
||||
|
||||
nativeMod->fn_init = nativeMod->handle->LookupSymbol<NativeMod::FnInit>("mod_init");
|
||||
@@ -187,12 +190,12 @@ bool ModLoader::tryLoadNativeMod(LoadedMod& mod) {
|
||||
if (!nativeMod->fn_init || !nativeMod->fn_tick) {
|
||||
Log.error("{} missing mod_init or mod_tick — skipping",
|
||||
io::fs_path_to_string(fs::path(dllEntry).filename()));
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
mod.dir = io::fs_path_to_string(fs::absolute(cacheDir));
|
||||
mod.native = std::move(nativeMod);
|
||||
return true;
|
||||
mod.native_status = NativeModStatus::Loaded;
|
||||
}
|
||||
|
||||
void ModLoader::tryLoadDusk(const std::filesystem::path& modPath, bool fromDir) {
|
||||
@@ -226,12 +229,21 @@ void ModLoader::tryLoadDusk(const std::filesystem::path& modPath, bool fromDir)
|
||||
}
|
||||
|
||||
LoadedMod mod;
|
||||
mod.active = true;
|
||||
mod.mod_path = io::fs_path_to_string(fs::absolute(modPath));
|
||||
mod.metadata = std::move(metadata);
|
||||
mod.bundle = std::move(bundle);
|
||||
|
||||
if (mod.metadata.hasCode && !tryLoadNativeMod(mod)) {
|
||||
return;
|
||||
if (mod.metadata.hasCode) {
|
||||
mod.native_status = NativeModStatus::Unknown;
|
||||
tryLoadNativeMod(mod);
|
||||
// Native mod lod failure DOES NOT block insertion into m_mods.
|
||||
// We still want to be able to present the failed load in the UI!
|
||||
|
||||
if (mod.native_status != NativeModStatus::Loaded) {
|
||||
Log.error("Native mod '{}' failed to load, disabling", metadata.id);
|
||||
mod.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& inserted = m_mods.emplace_back(std::move(mod));
|
||||
@@ -282,36 +294,41 @@ void ModLoader::init() {
|
||||
return;
|
||||
}
|
||||
|
||||
initOverlayFiles();
|
||||
|
||||
Log.info("initializing {} mod(s)...", m_mods.size());
|
||||
for (auto& mod : m_mods) {
|
||||
if (mod.native) {
|
||||
if (mod.native && mod.active) {
|
||||
buildAPI(mod);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& mod : m_mods) {
|
||||
if (!mod.native) {
|
||||
if (!mod.native || !mod.active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Log.debug("Initializing '{}'", mod.metadata.id);
|
||||
|
||||
ModGuard guard(&mod);
|
||||
try {
|
||||
mod.native->fn_init(&mod.native->api);
|
||||
if (!mod.load_failed) {
|
||||
mod.active = true;
|
||||
Log.info("'{}' initialized", mod.metadata.id);
|
||||
} else {
|
||||
mod.active = false;
|
||||
Log.error("'{}' failed to load due to hook conflicts", mod.metadata.id);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
mod.active = false;
|
||||
Log.error("exception in {}.mod_init(): {}", mod.metadata.id, e.what());
|
||||
} catch (...) {
|
||||
mod.active = false;
|
||||
Log.error("unknown exception in {}.mod_init()", mod.metadata.id);
|
||||
}
|
||||
}
|
||||
|
||||
initOverlayFiles();
|
||||
|
||||
auto active =
|
||||
std::count_if(m_mods.begin(), m_mods.end(), [](const LoadedMod& m) { return m.active; });
|
||||
Log.info("{}/{} mod(s) active", active, m_mods.size());
|
||||
|
||||
@@ -115,7 +115,9 @@ void ModLoader::initOverlayFiles() {
|
||||
std::vector<AuroraOverlayFile> files;
|
||||
|
||||
for (auto& mod : m_mods) {
|
||||
findOverlayFiles(files, mod);
|
||||
if (mod.active) {
|
||||
findOverlayFiles(files, mod);
|
||||
}
|
||||
}
|
||||
|
||||
Log.debug("Found {} overlay files.", files.size());
|
||||
|
||||
Reference in New Issue
Block a user