1
0
mirror of https://github.com/luanti-org/luanti.git synced 2025-10-20 19:45:22 +02:00

Allow game to specify first and last mod in mod loading order (#14177)

Co-authored-by: Lars Mueller <appgurulars@gmx.de>
Co-authored-by: sfan5 <sfan5@live.de>
This commit is contained in:
sfence
2024-06-01 16:36:20 +02:00
committed by GitHub
parent a4768d1638
commit 140b9e5a5a
12 changed files with 141 additions and 64 deletions

View File

@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gettext.h"
#include "exceptions.h"
#include "util/numeric.h"
#include <optional>
std::string ModConfiguration::getUnsatisfiedModsError() const
{
@@ -116,6 +116,9 @@ void ModConfiguration::addGameMods(const SubgameSpec &gamespec)
std::string game_virtual_path;
game_virtual_path.append("games/").append(gamespec.id).append("/mods");
addModsInPath(gamespec.gamemods_path, game_virtual_path);
m_first_mod = gamespec.first_mod;
m_last_mod = gamespec.last_mod;
}
void ModConfiguration::addModsFromConfig(
@@ -221,13 +224,20 @@ void ModConfiguration::checkConflictsAndDeps()
void ModConfiguration::resolveDependencies()
{
// Step 1: Compile a list of the mod names we're working with
// Compile a list of the mod names we're working with
std::set<std::string> modnames;
for (const ModSpec &mod : m_unsatisfied_mods) {
modnames.insert(mod.name);
std::optional<ModSpec> first_mod_spec, last_mod_spec;
for (ModSpec &mod : m_unsatisfied_mods) {
if (mod.name == m_first_mod) {
first_mod_spec = mod;
} else if (mod.name == m_last_mod) {
last_mod_spec = mod;
} else {
modnames.insert(mod.name);
}
}
// Step 1.5 (optional): shuffle unsatisfied mods so non declared depends get found by their devs
// Optionally shuffle unsatisfied mods so non declared depends get found by their devs
if (g_settings->getBool("random_mod_load_order")) {
MyRandGenerator rg;
std::shuffle(m_unsatisfied_mods.begin(),
@@ -236,25 +246,55 @@ void ModConfiguration::resolveDependencies()
);
}
// Step 2: get dependencies (including optional dependencies)
// Check for presence of first and last mod
if (!m_first_mod.empty() && !first_mod_spec.has_value())
throw ModError("The mod specified as first by the game was not found.");
if (!m_last_mod.empty() && !last_mod_spec.has_value())
throw ModError("The mod specified as last by the game was not found.");
// Check and add first mod
if (first_mod_spec.has_value()) {
// Dependencies are not allowed for first mod
if (!first_mod_spec->depends.empty() || !first_mod_spec->optdepends.empty())
throw ModError("Mod specified by first_mod cannot have dependencies");
m_sorted_mods.push_back(*first_mod_spec);
}
// Get dependencies (including optional dependencies)
// of each mod, split mods into satisfied and unsatisfied
std::vector<ModSpec> satisfied;
std::list<ModSpec> unsatisfied;
for (ModSpec mod : m_unsatisfied_mods) {
if (mod.name == m_first_mod || mod.name == m_last_mod)
continue; // skip, handled separately
mod.unsatisfied_depends = mod.depends;
// check which optional dependencies actually exist
for (const std::string &optdep : mod.optdepends) {
if (modnames.count(optdep) != 0)
mod.unsatisfied_depends.insert(optdep);
}
mod.unsatisfied_depends.erase(m_first_mod); // first is already satisfied
if (last_mod_spec.has_value() && mod.unsatisfied_depends.count(last_mod_spec->name) != 0) {
throw ModError("Impossible to depend on the mod specified by last_mod");
}
// if a mod has no depends it is initially satisfied
if (mod.unsatisfied_depends.empty())
if (mod.unsatisfied_depends.empty()) {
satisfied.push_back(mod);
else
} else {
unsatisfied.push_back(mod);
}
}
// Step 3: mods without unmet dependencies can be appended to
// All dependencies of the last mod are initially unsatisfied
if (last_mod_spec.has_value()) {
last_mod_spec->unsatisfied_depends = last_mod_spec->depends;
last_mod_spec->unsatisfied_depends.erase(m_first_mod);
}
// Mods without unmet dependencies can be appended to
// the sorted list.
while (!satisfied.empty()) {
ModSpec mod = satisfied.back();
@@ -270,8 +310,18 @@ void ModConfiguration::resolveDependencies()
++it;
}
}
if (last_mod_spec.has_value())
last_mod_spec->unsatisfied_depends.erase(mod.name);
}
// Step 4: write back list of unsatisfied mods
// Write back list of unsatisfied mods
m_unsatisfied_mods.assign(unsatisfied.begin(), unsatisfied.end());
// Check and add last mod
if (last_mod_spec.has_value()) {
if (last_mod_spec->unsatisfied_depends.empty())
m_sorted_mods.push_back(*last_mod_spec);
else
m_unsatisfied_mods.push_back(*last_mod_spec);
}
}