mirror of
https://github.com/R2Northstar/NorthstarLauncher
synced 2026-08-23 20:26:04 -04:00
Merge branch 'main' into feat/overhaul-mod-loading-locations
This commit is contained in:
commit
c277e3caf6
10 changed files with 212 additions and 168 deletions
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
|
|
@ -32,7 +32,7 @@ jobs:
|
|||
- name: Build
|
||||
run: cmake --build build/
|
||||
- name: Upload launcher build as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: northstar-launcher
|
||||
path: |
|
||||
|
|
@ -40,7 +40,7 @@ jobs:
|
|||
build/game/*.dll
|
||||
build/game/bin/x64_retail/*.dll
|
||||
- name: Upload debug build artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: launcher-debug-files
|
||||
path: |
|
||||
|
|
@ -53,12 +53,12 @@ jobs:
|
|||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Download compiled launcher
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: northstar-launcher
|
||||
path: northstar-launcher
|
||||
- name: Download compiled launcher
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: launcher-debug-files
|
||||
path: launcher-debug-files
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ int ModDownloader::ModFetchingProgressCallback(
|
|||
return 0;
|
||||
}
|
||||
|
||||
std::optional<fs::path> ModDownloader::FetchModFromDistantStore(std::string_view modName, VerifiedModVersion version)
|
||||
std::tuple<fs::path, bool> ModDownloader::FetchModFromDistantStore(std::string_view modName, VerifiedModVersion version)
|
||||
{
|
||||
std::string url = version.downloadLink;
|
||||
std::string archiveName = fs::path(url).filename().generic_string();
|
||||
|
|
@ -190,7 +190,7 @@ std::optional<fs::path> ModDownloader::FetchModFromDistantStore(std::string_view
|
|||
modState.state = DOWNLOADING;
|
||||
|
||||
// Download the actual archive
|
||||
bool failed = false;
|
||||
bool success = false;
|
||||
FILE* fp = fopen(downloadPath.generic_string().c_str(), "wb");
|
||||
CURLcode result;
|
||||
CURL* easyhandle;
|
||||
|
|
@ -219,13 +219,14 @@ std::optional<fs::path> ModDownloader::FetchModFromDistantStore(std::string_view
|
|||
if (result == CURLcode::CURLE_OK)
|
||||
{
|
||||
spdlog::info("Mod archive successfully fetched.");
|
||||
return std::optional<fs::path>(downloadPath);
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::error("Fetching mod archive failed: {}", curl_easy_strerror(result));
|
||||
return std::optional<fs::path>();
|
||||
}
|
||||
|
||||
return {downloadPath, success};
|
||||
}
|
||||
|
||||
bool ModDownloader::IsModLegit(fs::path modPath, std::string_view expectedChecksum)
|
||||
|
|
@ -399,11 +400,9 @@ int GetModArchiveSize(unzFile file, unz_global_info64 info)
|
|||
return totalSize;
|
||||
}
|
||||
|
||||
void ModDownloader::ExtractMod(fs::path modPath, VerifiedModPlatform platform)
|
||||
void ModDownloader::ExtractMod(fs::path modPath, fs::path destinationPath, VerifiedModPlatform platform)
|
||||
{
|
||||
unzFile file;
|
||||
std::string name;
|
||||
fs::path modDirectory;
|
||||
|
||||
file = unzOpen(modPath.generic_string().c_str());
|
||||
ScopeGuard cleanup(
|
||||
|
|
@ -445,11 +444,6 @@ void ModDownloader::ExtractMod(fs::path modPath, VerifiedModPlatform platform)
|
|||
return;
|
||||
}
|
||||
|
||||
// Mod directory name (removing the ".zip" fom the archive name)
|
||||
name = modPath.filename().string();
|
||||
name = name.substr(0, name.length() - 4);
|
||||
modDirectory = GetRemoteModFolderPath() / name;
|
||||
|
||||
for (int i = 0; i < gi.number_entry; i++)
|
||||
{
|
||||
char zipFilename[256];
|
||||
|
|
@ -459,7 +453,7 @@ void ModDownloader::ExtractMod(fs::path modPath, VerifiedModPlatform platform)
|
|||
// Extract file
|
||||
{
|
||||
std::error_code ec;
|
||||
fs::path fileDestination = modDirectory / zipFilename;
|
||||
fs::path fileDestination = destinationPath / zipFilename;
|
||||
spdlog::info("=> {}", fileDestination.generic_string());
|
||||
|
||||
// Create parent directory if needed
|
||||
|
|
@ -589,6 +583,9 @@ void ModDownloader::ExtractMod(fs::path modPath, VerifiedModPlatform platform)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mod extraction went fine
|
||||
modState.state = DONE;
|
||||
}
|
||||
|
||||
void ModDownloader::DownloadMod(std::string modName, std::string modVersion)
|
||||
|
|
@ -603,11 +600,14 @@ void ModDownloader::DownloadMod(std::string modName, std::string modVersion)
|
|||
std::thread requestThread(
|
||||
[this, modName, modVersion]()
|
||||
{
|
||||
std::string name;
|
||||
fs::path archiveLocation;
|
||||
fs::path modDirectory;
|
||||
|
||||
ScopeGuard cleanup(
|
||||
[&]
|
||||
{
|
||||
// Remove downloaded archive
|
||||
try
|
||||
{
|
||||
remove(archiveLocation);
|
||||
|
|
@ -617,14 +617,31 @@ void ModDownloader::DownloadMod(std::string modName, std::string modVersion)
|
|||
spdlog::error("Error while removing downloaded archive: {}", a.what());
|
||||
}
|
||||
|
||||
// Remove mod if auto-download process failed
|
||||
if (modState.state != DONE)
|
||||
{
|
||||
try
|
||||
{
|
||||
remove_all(modDirectory);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
spdlog::error("Error while removing downloaded mod: {}", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
spdlog::info("Done cleaning after downloading {}.", modName);
|
||||
});
|
||||
|
||||
// Download mod archive
|
||||
VerifiedModVersion fullVersion = verifiedMods[modName].versions[modVersion];
|
||||
std::string expectedHash = fullVersion.checksum;
|
||||
std::optional<fs::path> fetchingResult = FetchModFromDistantStore(std::string_view(modName), fullVersion);
|
||||
if (!fetchingResult.has_value())
|
||||
|
||||
const std::tuple<fs::path, bool> downloadResult = FetchModFromDistantStore(std::string_view(modName), fullVersion);
|
||||
archiveLocation = get<0>(downloadResult);
|
||||
bool downloadSuccessful = get<1>(downloadResult);
|
||||
|
||||
if (!downloadSuccessful)
|
||||
{
|
||||
spdlog::error("Something went wrong while fetching archive, aborting.");
|
||||
if (modState.state != ABORTED)
|
||||
|
|
@ -633,7 +650,7 @@ void ModDownloader::DownloadMod(std::string modName, std::string modVersion)
|
|||
}
|
||||
return;
|
||||
}
|
||||
archiveLocation = fetchingResult.value();
|
||||
|
||||
if (!IsModLegit(archiveLocation, std::string_view(expectedHash)))
|
||||
{
|
||||
spdlog::warn("Archive hash does not match expected checksum, aborting.");
|
||||
|
|
@ -641,9 +658,13 @@ void ModDownloader::DownloadMod(std::string modName, std::string modVersion)
|
|||
return;
|
||||
}
|
||||
|
||||
// Mod directory name (removing the ".zip" fom the archive name)
|
||||
name = archiveLocation.filename().string();
|
||||
name = name.substr(0, name.length() - 4);
|
||||
modDirectory = GetRemoteModFolderPath() / name;
|
||||
|
||||
// Extract downloaded mod archive
|
||||
ExtractMod(archiveLocation, fullVersion.platform);
|
||||
modState.state = DONE;
|
||||
ExtractMod(archiveLocation, modDirectory, fullVersion.platform);
|
||||
});
|
||||
|
||||
requestThread.detach();
|
||||
|
|
|
|||
|
|
@ -43,14 +43,12 @@ private:
|
|||
* input mod name as mod dependency string if bypass flag is set up; fetched
|
||||
* archive is then stored in a temporary location.
|
||||
*
|
||||
* If something went wrong during archive download, this will return an empty
|
||||
* optional object.
|
||||
*
|
||||
* @param modName name of the mod to be downloaded
|
||||
* @param modVersion version of the mod to be downloaded
|
||||
* @returns location of the downloaded archive
|
||||
* @returns tuple containing location of the downloaded archive and whether download completed successfully
|
||||
*/
|
||||
std::optional<fs::path> FetchModFromDistantStore(std::string_view modName, VerifiedModVersion modVersion);
|
||||
std::tuple<fs::path, bool> FetchModFromDistantStore(std::string_view modName, VerifiedModVersion modVersion);
|
||||
|
||||
/**
|
||||
* Tells if a mod archive has not been corrupted.
|
||||
|
|
@ -69,14 +67,16 @@ private:
|
|||
/**
|
||||
* Extracts a mod archive to the game folder.
|
||||
*
|
||||
* This extracts a downloaded mod archive from its original location to the
|
||||
* current game profile; the install folder is defined by the platform parameter.
|
||||
* This extracts a downloaded mod archive from its original location, `modPath`,
|
||||
* to the specified `destinationPath`; the way to decompress the archive is
|
||||
* defined by the `platform` parameter.
|
||||
*
|
||||
* @param modPath location of the downloaded archive
|
||||
* @param destinationPath destination of the extraction
|
||||
* @param platform origin of the downloaded archive
|
||||
* @returns nothing
|
||||
*/
|
||||
void ExtractMod(fs::path modPath, VerifiedModPlatform platform);
|
||||
void ExtractMod(fs::path modPath, fs::path destinationPath, VerifiedModPlatform platform);
|
||||
|
||||
public:
|
||||
ModDownloader();
|
||||
|
|
|
|||
|
|
@ -111,8 +111,6 @@ void ModManager::LoadMods()
|
|||
if (m_bHasLoadedMods)
|
||||
UnloadMods();
|
||||
|
||||
std::vector<fs::path> modDirs;
|
||||
|
||||
// ensure dirs exist
|
||||
fs::remove_all(GetCompiledAssetsPath());
|
||||
fs::create_directories(GetCoreModFolderPath());
|
||||
|
|
@ -138,123 +136,8 @@ void ModManager::LoadMods()
|
|||
m_bHasEnabledModsCfg = m_EnabledModsCfg.IsObject();
|
||||
}
|
||||
|
||||
// get mod directories
|
||||
std::filesystem::directory_iterator coreModsDir = fs::directory_iterator(GetCoreModFolderPath());
|
||||
std::filesystem::directory_iterator manualModsDir = fs::directory_iterator(GetManualModFolderPath());
|
||||
std::filesystem::directory_iterator remoteModsDir = fs::directory_iterator(GetRemoteModFolderPath());
|
||||
std::filesystem::directory_iterator thunderstoreModsDir = fs::directory_iterator(GetThunderstoreLegacyModFolderPath());
|
||||
|
||||
// Set up regex for `Northstar.*` pattern
|
||||
std::regex northstar_pattern(R"(.*\\Northstar\..+)");
|
||||
for (fs::directory_entry dir : coreModsDir)
|
||||
{
|
||||
if (!std::regex_match(dir.path().string(), northstar_pattern))
|
||||
{
|
||||
spdlog::warn(
|
||||
"The following directory did not match 'Northstar.*' and is most likely an incorrectly manually installed mod: {}",
|
||||
dir.path().string());
|
||||
continue; // skip loading mod that doesn't match
|
||||
}
|
||||
if (fs::exists(dir.path() / "mod.json"))
|
||||
modDirs.push_back(dir.path());
|
||||
}
|
||||
|
||||
for (fs::directory_entry dir : manualModsDir)
|
||||
if (fs::exists(dir.path() / "mod.json"))
|
||||
modDirs.push_back(dir.path());
|
||||
|
||||
// Special case for Thunderstore and remote mods directories
|
||||
// Set up regex for `AUTHOR-MOD-VERSION` pattern
|
||||
std::regex pattern(R"(.*\\([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)-(\d+\.\d+\.\d+))");
|
||||
|
||||
for (fs::directory_iterator dirIterator : {thunderstoreModsDir, remoteModsDir})
|
||||
{
|
||||
for (fs::directory_entry dir : dirIterator)
|
||||
{
|
||||
fs::path modsDir = dir.path() / "mods"; // Check for mods folder in the Thunderstore mod
|
||||
// Use regex to match `AUTHOR-MOD-VERSION` pattern
|
||||
if (!std::regex_match(dir.path().string(), pattern))
|
||||
{
|
||||
spdlog::warn("The following directory did not match 'AUTHOR-MOD-VERSION': {}", dir.path().string());
|
||||
continue; // skip loading mod that doesn't match
|
||||
}
|
||||
if (fs::exists(modsDir) && fs::is_directory(modsDir))
|
||||
{
|
||||
for (fs::directory_entry subDir : fs::directory_iterator(modsDir))
|
||||
{
|
||||
if (fs::exists(subDir.path() / "mod.json"))
|
||||
{
|
||||
modDirs.push_back(subDir.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (fs::path modDir : modDirs)
|
||||
{
|
||||
// read mod json file
|
||||
std::ifstream jsonStream(modDir / "mod.json");
|
||||
std::stringstream jsonStringStream;
|
||||
|
||||
// fail if no mod json
|
||||
if (jsonStream.fail())
|
||||
{
|
||||
spdlog::warn(
|
||||
"Mod file at '{}' does not exist or could not be read, is it installed correctly?", (modDir / "mod.json").string());
|
||||
continue;
|
||||
}
|
||||
|
||||
while (jsonStream.peek() != EOF)
|
||||
jsonStringStream << (char)jsonStream.get();
|
||||
|
||||
jsonStream.close();
|
||||
|
||||
Mod mod(modDir, (char*)jsonStringStream.str().c_str());
|
||||
|
||||
for (auto& pair : mod.DependencyConstants)
|
||||
{
|
||||
if (m_DependencyConstants.find(pair.first) != m_DependencyConstants.end() && m_DependencyConstants[pair.first] != pair.second)
|
||||
{
|
||||
spdlog::error(
|
||||
"'{}' attempted to register a dependency constant '{}' for '{}' that already exists for '{}'. "
|
||||
"Change the constant name.",
|
||||
mod.Name,
|
||||
pair.first,
|
||||
pair.second,
|
||||
m_DependencyConstants[pair.first]);
|
||||
mod.m_bWasReadSuccessfully = false;
|
||||
break;
|
||||
}
|
||||
if (m_DependencyConstants.find(pair.first) == m_DependencyConstants.end())
|
||||
m_DependencyConstants.emplace(pair);
|
||||
}
|
||||
|
||||
for (std::string& dependency : mod.PluginDependencyConstants)
|
||||
{
|
||||
m_PluginDependencyConstants.insert(dependency);
|
||||
}
|
||||
|
||||
if (m_bHasEnabledModsCfg && m_EnabledModsCfg.HasMember(mod.Name.c_str()))
|
||||
mod.m_bEnabled = m_EnabledModsCfg[mod.Name.c_str()].IsTrue();
|
||||
else
|
||||
mod.m_bEnabled = true;
|
||||
|
||||
if (mod.m_bWasReadSuccessfully)
|
||||
{
|
||||
if (mod.m_bEnabled)
|
||||
spdlog::info("'{}' loaded successfully, version {}", mod.Name, mod.Version);
|
||||
else
|
||||
spdlog::info("'{}' loaded successfully, version {} (DISABLED)", mod.Name, mod.Version);
|
||||
|
||||
m_LoadedMods.push_back(mod);
|
||||
}
|
||||
else
|
||||
spdlog::warn("Mod file at '{}' failed to load", (modDir / "mod.json").string());
|
||||
}
|
||||
|
||||
// sort by load prio, lowest-highest
|
||||
std::sort(m_LoadedMods.begin(), m_LoadedMods.end(), [](Mod& a, Mod& b) { return a.LoadPriority < b.LoadPriority; });
|
||||
// Load mod info from filesystem into `m_LoadedMods`
|
||||
SearchFilesystemForMods();
|
||||
|
||||
// This is used to check if some mods have a folder but no entry in enabledmods.json
|
||||
bool newModsDetected = false;
|
||||
|
|
@ -638,6 +521,130 @@ void ModManager::UnloadMods()
|
|||
m_LoadedMods.clear();
|
||||
}
|
||||
|
||||
void ModManager::SearchFilesystemForMods()
|
||||
{
|
||||
std::vector<fs::path> modDirs;
|
||||
m_LoadedMods.clear();
|
||||
|
||||
// get mod directories
|
||||
std::filesystem::directory_iterator coreModsDir = fs::directory_iterator(GetCoreModFolderPath());
|
||||
std::filesystem::directory_iterator manualModsDir = fs::directory_iterator(GetManualModFolderPath());
|
||||
std::filesystem::directory_iterator remoteModsDir = fs::directory_iterator(GetRemoteModFolderPath());
|
||||
std::filesystem::directory_iterator thunderstoreModsDir = fs::directory_iterator(GetThunderstoreLegacyModFolderPath());
|
||||
|
||||
// Set up regex for `Northstar.*` pattern
|
||||
std::regex northstar_pattern(R"(.*\\Northstar\..+)");
|
||||
for (fs::directory_entry dir : coreModsDir)
|
||||
{
|
||||
if (!std::regex_match(dir.path().string(), northstar_pattern))
|
||||
{
|
||||
spdlog::warn(
|
||||
"The following directory did not match 'Northstar.*' and is most likely an incorrectly manually installed mod: {}",
|
||||
dir.path().string());
|
||||
continue; // skip loading mod that doesn't match
|
||||
}
|
||||
if (fs::exists(dir.path() / "mod.json"))
|
||||
modDirs.push_back(dir.path());
|
||||
}
|
||||
|
||||
for (fs::directory_entry dir : manualModsDir)
|
||||
if (fs::exists(dir.path() / "mod.json"))
|
||||
modDirs.push_back(dir.path());
|
||||
|
||||
// Special case for Thunderstore and remote mods directories
|
||||
// Set up regex for `AUTHOR-MOD-VERSION` pattern
|
||||
std::regex pattern(R"(.*\\([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)-(\d+\.\d+\.\d+))");
|
||||
|
||||
for (fs::directory_iterator dirIterator : {thunderstoreModsDir, remoteModsDir})
|
||||
{
|
||||
for (fs::directory_entry dir : dirIterator)
|
||||
{
|
||||
fs::path modsDir = dir.path() / "mods"; // Check for mods folder in the Thunderstore mod
|
||||
// Use regex to match `AUTHOR-MOD-VERSION` pattern
|
||||
if (!std::regex_match(dir.path().string(), pattern))
|
||||
{
|
||||
spdlog::warn("The following directory did not match 'AUTHOR-MOD-VERSION': {}", dir.path().string());
|
||||
continue; // skip loading mod that doesn't match
|
||||
}
|
||||
if (fs::exists(modsDir) && fs::is_directory(modsDir))
|
||||
{
|
||||
for (fs::directory_entry subDir : fs::directory_iterator(modsDir))
|
||||
{
|
||||
if (fs::exists(subDir.path() / "mod.json"))
|
||||
{
|
||||
modDirs.push_back(subDir.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (fs::path modDir : modDirs)
|
||||
{
|
||||
// read mod json file
|
||||
std::ifstream jsonStream(modDir / "mod.json");
|
||||
std::stringstream jsonStringStream;
|
||||
|
||||
// fail if no mod json
|
||||
if (jsonStream.fail())
|
||||
{
|
||||
spdlog::warn(
|
||||
"Mod file at '{}' does not exist or could not be read, is it installed correctly?", (modDir / "mod.json").string());
|
||||
continue;
|
||||
}
|
||||
|
||||
while (jsonStream.peek() != EOF)
|
||||
jsonStringStream << (char)jsonStream.get();
|
||||
|
||||
jsonStream.close();
|
||||
|
||||
Mod mod(modDir, (char*)jsonStringStream.str().c_str());
|
||||
|
||||
for (auto& pair : mod.DependencyConstants)
|
||||
{
|
||||
if (m_DependencyConstants.find(pair.first) != m_DependencyConstants.end() && m_DependencyConstants[pair.first] != pair.second)
|
||||
{
|
||||
spdlog::error(
|
||||
"'{}' attempted to register a dependency constant '{}' for '{}' that already exists for '{}'. "
|
||||
"Change the constant name.",
|
||||
mod.Name,
|
||||
pair.first,
|
||||
pair.second,
|
||||
m_DependencyConstants[pair.first]);
|
||||
mod.m_bWasReadSuccessfully = false;
|
||||
break;
|
||||
}
|
||||
if (m_DependencyConstants.find(pair.first) == m_DependencyConstants.end())
|
||||
m_DependencyConstants.emplace(pair);
|
||||
}
|
||||
|
||||
for (std::string& dependency : mod.PluginDependencyConstants)
|
||||
{
|
||||
m_PluginDependencyConstants.insert(dependency);
|
||||
}
|
||||
|
||||
if (m_bHasEnabledModsCfg && m_EnabledModsCfg.HasMember(mod.Name.c_str()))
|
||||
mod.m_bEnabled = m_EnabledModsCfg[mod.Name.c_str()].IsTrue();
|
||||
else
|
||||
mod.m_bEnabled = true;
|
||||
|
||||
if (mod.m_bWasReadSuccessfully)
|
||||
{
|
||||
if (mod.m_bEnabled)
|
||||
spdlog::info("'{}' loaded successfully, version {}", mod.Name, mod.Version);
|
||||
else
|
||||
spdlog::info("'{}' loaded successfully, version {} (DISABLED)", mod.Name, mod.Version);
|
||||
|
||||
m_LoadedMods.push_back(mod);
|
||||
}
|
||||
else
|
||||
spdlog::warn("Mod file at '{}' failed to load", (modDir / "mod.json").string());
|
||||
}
|
||||
|
||||
// sort by load prio, lowest-highest
|
||||
std::sort(m_LoadedMods.begin(), m_LoadedMods.end(), [](Mod& a, Mod& b) { return a.LoadPriority < b.LoadPriority; });
|
||||
}
|
||||
|
||||
std::string ModManager::NormaliseModFilePath(const fs::path path)
|
||||
{
|
||||
std::string str = path.lexically_normal().string();
|
||||
|
|
|
|||
|
|
@ -47,6 +47,18 @@ public:
|
|||
std::unordered_map<std::string, std::string> m_DependencyConstants;
|
||||
std::unordered_set<std::string> m_PluginDependencyConstants;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Load information for all mods from filesystem.
|
||||
*
|
||||
* This looks for mods in several directories (expecting them to be formatted in
|
||||
* some way); it then uses respective `mod.json` manifest files to create `Mod`
|
||||
* instances, which are then stored in the `m_LoadedMods` variable.
|
||||
*
|
||||
* @returns nothing
|
||||
**/
|
||||
void SearchFilesystemForMods();
|
||||
|
||||
public:
|
||||
ModManager();
|
||||
void LoadMods();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "pluginmanager.h"
|
||||
|
||||
#include <regex>
|
||||
#include <ranges>
|
||||
#include "plugins.h"
|
||||
#include "config/profile.h"
|
||||
#include "core/convar/concommand.h"
|
||||
|
|
@ -111,13 +112,14 @@ bool PluginManager::LoadPlugins(bool reloaded)
|
|||
|
||||
void PluginManager::ReloadPlugins()
|
||||
{
|
||||
for (const Plugin& plugin : this->GetLoadedPlugins())
|
||||
{
|
||||
plugin.Unload();
|
||||
}
|
||||
NS::log::PLUGINSYS->info("Reloading plugins");
|
||||
|
||||
this->plugins.clear();
|
||||
this->LoadPlugins(true);
|
||||
for (const Plugin& plugin : this->plugins | std::views::reverse)
|
||||
{
|
||||
std::string name = plugin.GetName();
|
||||
if (plugin.Reload())
|
||||
NS::log::PLUGINSYS->info("Reloaded {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::RemovePlugin(HMODULE handle)
|
||||
|
|
|
|||
|
|
@ -138,9 +138,9 @@ bool Plugin::Unload() const
|
|||
|
||||
if (IsValid())
|
||||
{
|
||||
bool unloaded = m_callbacks->Unload();
|
||||
bool shouldUnload = m_callbacks->Unload();
|
||||
|
||||
if (!unloaded)
|
||||
if (!shouldUnload)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -154,14 +154,18 @@ bool Plugin::Unload() const
|
|||
return true;
|
||||
}
|
||||
|
||||
void Plugin::Reload() const
|
||||
bool Plugin::Reload() const
|
||||
{
|
||||
std::string location = m_location;
|
||||
|
||||
bool unloaded = Unload();
|
||||
|
||||
if (!unloaded)
|
||||
return;
|
||||
return false;
|
||||
|
||||
g_pPluginManager->LoadPlugin(fs::path(m_location), true);
|
||||
g_pPluginManager->LoadPlugin(fs::path(location), true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Plugin::Log(spdlog::level::level_enum level, char* msg) const
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public:
|
|||
|
||||
Plugin(std::string path);
|
||||
bool Unload() const;
|
||||
void Reload() const;
|
||||
bool Reload() const;
|
||||
|
||||
// sys
|
||||
void Log(spdlog::level::level_enum level, char* msg) const;
|
||||
|
|
|
|||
|
|
@ -1,25 +1,22 @@
|
|||
|
||||
AUTOHOOK_INIT()
|
||||
|
||||
bool* bIsOriginOverlayEnabled;
|
||||
|
||||
// clang-format off
|
||||
AUTOHOOK(OpenExternalWebBrowser, engine.dll + 0x184E40,
|
||||
void, __fastcall, (char* pUrl, char flags))
|
||||
// clang-format on
|
||||
static void(__fastcall* o_pOpenExternalWebBrowser)(char* pUrl, char flags) = nullptr;
|
||||
static void __fastcall h_OpenExternalWebBrowser(char* pUrl, char flags)
|
||||
{
|
||||
bool bIsOriginOverlayEnabledOriginal = *bIsOriginOverlayEnabled;
|
||||
bool isHttp = !strncmp(pUrl, "http://", 7) || !strncmp(pUrl, "https://", 8);
|
||||
if (flags & 2 && isHttp) // custom force external browser flag
|
||||
*bIsOriginOverlayEnabled = false; // if this bool is false, game will use an external browser rather than the origin overlay one
|
||||
|
||||
OpenExternalWebBrowser(pUrl, flags);
|
||||
o_pOpenExternalWebBrowser(pUrl, flags);
|
||||
*bIsOriginOverlayEnabled = bIsOriginOverlayEnabledOriginal;
|
||||
}
|
||||
|
||||
ON_DLL_LOAD_CLIENT("engine.dll", ScriptExternalBrowserHooks, (CModule module))
|
||||
{
|
||||
AUTOHOOK_DISPATCH()
|
||||
o_pOpenExternalWebBrowser = module.Offset(0x184E40).RCast<decltype(o_pOpenExternalWebBrowser)>();
|
||||
HookAttach(&(PVOID&)o_pOpenExternalWebBrowser, (PVOID)h_OpenExternalWebBrowser);
|
||||
|
||||
bIsOriginOverlayEnabled = module.Offset(0x13978255).RCast<bool*>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,6 +301,7 @@ void FixupCvarFlags()
|
|||
// these 2 could be FCVAR_CHEAT, i guess?
|
||||
{"cl_draw_player_model", FCVAR_DEVELOPMENTONLY},
|
||||
{"cl_always_draw_3p_player", FCVAR_DEVELOPMENTONLY},
|
||||
{"cl_gib_lifetime", FCVAR_CHEAT},
|
||||
{"idcolor_neutral", FCVAR_DEVELOPMENTONLY},
|
||||
{"idcolor_ally", FCVAR_DEVELOPMENTONLY},
|
||||
{"idcolor_ally_cb1", FCVAR_DEVELOPMENTONLY},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue