Skip to content

Commit

Permalink
Merge branch 'main' into fix/mad-cleanup-handles
Browse files Browse the repository at this point in the history
  • Loading branch information
Alystrasz authored Dec 1, 2024
2 parents 43ecab8 + 5e07df8 commit 98f5979
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 103 deletions.
213 changes: 110 additions & 103 deletions primedev/mods/modmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(GetModFolderPath());
Expand All @@ -137,107 +135,8 @@ void ModManager::LoadMods()
m_bHasEnabledModsCfg = m_EnabledModsCfg.IsObject();
}

// get mod directories
std::filesystem::directory_iterator classicModsDir = fs::directory_iterator(GetModFolderPath());
std::filesystem::directory_iterator remoteModsDir = fs::directory_iterator(GetRemoteModFolderPath());
std::filesystem::directory_iterator thunderstoreModsDir = fs::directory_iterator(GetThunderstoreModFolderPath());

for (fs::directory_entry dir : classicModsDir)
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;
Expand Down Expand Up @@ -621,6 +520,114 @@ void ModManager::UnloadMods()
m_LoadedMods.clear();
}

void ModManager::SearchFilesystemForMods()
{
std::vector<fs::path> modDirs;
m_LoadedMods.clear();

// get mod directories
std::filesystem::directory_iterator classicModsDir = fs::directory_iterator(GetModFolderPath());
std::filesystem::directory_iterator remoteModsDir = fs::directory_iterator(GetRemoteModFolderPath());
std::filesystem::directory_iterator thunderstoreModsDir = fs::directory_iterator(GetThunderstoreModFolderPath());

for (fs::directory_entry dir : classicModsDir)
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();
Expand Down
12 changes: 12 additions & 0 deletions primedev/mods/modmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ class ModManager
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();
Expand Down

0 comments on commit 98f5979

Please sign in to comment.