mirror of https://github.com/minetest/minetest.git
Merge 4d73a56377
into b23042839b
This commit is contained in:
commit
59ca4a839b
|
@ -63,6 +63,8 @@ The game directory can contain the following files:
|
|||
* `name`: (Deprecated) same as title.
|
||||
* `description`: Short description to be shown in the content tab.
|
||||
See [Translating content meta](#translating-content-meta).
|
||||
* `first_mod`: Use this to specify the mod that must be loaded before any other mod.
|
||||
* `last_mod`: Use this to specify the mod that must be loaded after all other mods
|
||||
* `allowed_mapgens = <comma-separated mapgens>`
|
||||
e.g. `allowed_mapgens = v5,v6,flat`
|
||||
Mapgens not in this list are removed from the list of mapgens for the
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
title = Development Test
|
||||
description = Testing environment to help with testing the engine features of Minetest. It can also be helpful in mod development.
|
||||
first_mod = first_mod
|
||||
last_mod = last_mod
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
-- Nothing to do here, loading order is tested in C++ unittests.
|
|
@ -0,0 +1,2 @@
|
|||
name = first_mod
|
||||
description = Mod which should be loaded before every other mod.
|
|
@ -0,0 +1 @@
|
|||
-- Nothing to do here, loading order is tested in C++ unittests.
|
|
@ -0,0 +1,2 @@
|
|||
name = last_mod
|
||||
description = Mod which should be loaded as last mod.
|
|
@ -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,10 +224,19 @@ 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 (m_first_mod.has_value() && mod.name == *m_first_mod) {
|
||||
first_mod_spec = mod;
|
||||
} else if (m_last_mod.has_value() && mod.name == *m_last_mod) {
|
||||
// only non optional depends have to be check for last mod
|
||||
mod.unsatisfied_depends = mod.depends;
|
||||
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
|
||||
|
@ -236,7 +248,13 @@ void ModConfiguration::resolveDependencies()
|
|||
);
|
||||
}
|
||||
|
||||
// Step 2: get dependencies (including optional dependencies)
|
||||
// Check for presence of first and last mod
|
||||
if (m_first_mod.has_value() && !first_mod_spec.has_value())
|
||||
throw ModError("The mod specified as first by the game was not found.");
|
||||
if (m_last_mod.has_value() && !last_mod_spec.has_value())
|
||||
throw ModError("The mod specified as last by the game was not found.");
|
||||
|
||||
// Get dependencies (including optional dependencies)
|
||||
// of each mod, split mods into satisfied and unsatisfied
|
||||
std::vector<ModSpec> satisfied;
|
||||
std::list<ModSpec> unsatisfied;
|
||||
|
@ -247,14 +265,27 @@ void ModConfiguration::resolveDependencies()
|
|||
if (modnames.count(optdep) != 0)
|
||||
mod.unsatisfied_depends.insert(optdep);
|
||||
}
|
||||
if (last_mod_spec.has_value() && mod.unsatisfied_depends.count(last_mod_spec->name) != 0) {
|
||||
throw ModError("It is not allowed to have mod selected as last by the game in dependencies.");
|
||||
}
|
||||
// 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
|
||||
// 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 selected as first by the game is not allowed to have dependencies.");
|
||||
|
||||
satisfied.push_back(*first_mod_spec);
|
||||
}
|
||||
|
||||
// Mods without unmet dependencies can be appended to
|
||||
// the sorted list.
|
||||
while (!satisfied.empty()) {
|
||||
ModSpec mod = satisfied.back();
|
||||
|
@ -270,8 +301,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())
|
||||
throw ModError("Mod selected as last by the game has an unsatisfied dependency.");
|
||||
|
||||
m_sorted_mods.push_back(*last_mod_spec);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#pragma once
|
||||
|
||||
#include "mods.h"
|
||||
#include <optional>
|
||||
|
||||
|
||||
/**
|
||||
|
@ -83,10 +84,16 @@ public:
|
|||
|
||||
/**
|
||||
* Call this function once all mods have been added
|
||||
*
|
||||
* @param first_mod First mod to be loaded
|
||||
* @param last_mod Last mod to be loaded
|
||||
*/
|
||||
void checkConflictsAndDeps();
|
||||
|
||||
private:
|
||||
std::optional<std::string> m_first_mod;
|
||||
std::optional<std::string> m_last_mod;
|
||||
|
||||
std::vector<ModSpec> m_sorted_mods;
|
||||
|
||||
/**
|
||||
|
|
|
@ -170,6 +170,20 @@ SubgameSpec findSubgame(const std::string &id)
|
|||
if (conf.exists("release"))
|
||||
game_release = conf.getS32("release");
|
||||
|
||||
std::optional<std::string> first_mod;
|
||||
if (conf.exists("first_mod")) {
|
||||
first_mod = conf.get("first_mod");
|
||||
if (first_mod->empty())
|
||||
first_mod = std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> last_mod;
|
||||
if (conf.exists("last_mod")) {
|
||||
last_mod = conf.get("last_mod");
|
||||
if (last_mod->empty())
|
||||
last_mod = std::nullopt;
|
||||
}
|
||||
|
||||
std::string menuicon_path;
|
||||
#ifndef SERVER
|
||||
menuicon_path = getImagePath(
|
||||
|
@ -177,7 +191,7 @@ SubgameSpec findSubgame(const std::string &id)
|
|||
#endif
|
||||
|
||||
SubgameSpec spec(id, game_path, gamemod_path, mods_paths, game_title,
|
||||
menuicon_path, game_author, game_release);
|
||||
menuicon_path, game_author, game_release, first_mod, last_mod);
|
||||
|
||||
if (conf.exists("name") && !conf.exists("title"))
|
||||
spec.deprecation_msgs.push_back("\"name\" setting in game.conf is deprecated, please use \"title\" instead");
|
||||
|
|
|
@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
class Settings;
|
||||
|
||||
|
@ -32,6 +33,8 @@ struct SubgameSpec
|
|||
std::string title;
|
||||
std::string author;
|
||||
int release;
|
||||
std::optional<std::string> first_mod;
|
||||
std::optional<std::string> last_mod;
|
||||
std::string path;
|
||||
std::string gamemods_path;
|
||||
|
||||
|
@ -49,10 +52,16 @@ struct SubgameSpec
|
|||
const std::unordered_map<std::string, std::string> &addon_mods_paths = {},
|
||||
const std::string &title = "",
|
||||
const std::string &menuicon_path = "",
|
||||
const std::string &author = "", int release = 0) :
|
||||
const std::string &author = "", int release = 0,
|
||||
const std::optional<std::string> &first_mod = std::nullopt,
|
||||
const std::optional<std::string> &last_mod = std::nullopt) :
|
||||
id(id),
|
||||
title(title), author(author), release(release), path(path),
|
||||
gamemods_path(gamemods_path), addon_mods_paths(addon_mods_paths),
|
||||
title(title), author(author), release(release),
|
||||
first_mod(first_mod),
|
||||
last_mod(last_mod),
|
||||
path(path),
|
||||
gamemods_path(gamemods_path),
|
||||
addon_mods_paths(addon_mods_paths),
|
||||
menuicon_path(menuicon_path)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -139,6 +139,9 @@ void TestServerModManager::testGetMods()
|
|||
|
||||
UASSERTEQ(bool, default_found, true);
|
||||
UASSERTEQ(bool, test_mod_found, true);
|
||||
|
||||
UASSERT(mods.front().name == "first_mod");
|
||||
UASSERT(mods.back().name == "last_mod");
|
||||
}
|
||||
|
||||
void TestServerModManager::testGetModspec()
|
||||
|
|
Loading…
Reference in New Issue