mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
Mapgen selection and init code
This commit is contained in:
parent
aba2b6638e
commit
f9bad58d77
14 changed files with 211 additions and 59 deletions
|
@ -80,8 +80,17 @@ local mgv6_biomes = {
|
|||
|
||||
local function create_world_formspec(dialogdata)
|
||||
|
||||
local current_mg = dialogdata.mg
|
||||
local current_mapgen = dialogdata.mg
|
||||
local mapgens = core.get_mapgen_names()
|
||||
local lua_mapgens = core.get_lua_mapgen_descriptions()
|
||||
for k, v in pairs(lua_mapgens) do
|
||||
mapgens[#mapgens+1] = k
|
||||
end
|
||||
|
||||
local current_mapgen_internal = dialogdata.mg
|
||||
if lua_mapgens[current_mapgen_internal] then
|
||||
current_mapgen_internal = "singlenode"
|
||||
end
|
||||
|
||||
local flags = dialogdata.flags
|
||||
|
||||
|
@ -137,7 +146,7 @@ local function create_world_formspec(dialogdata)
|
|||
if not first_mg then
|
||||
first_mg = v
|
||||
end
|
||||
if current_mg == v then
|
||||
if current_mapgen == v then
|
||||
selindex = i
|
||||
end
|
||||
i = i + 1
|
||||
|
@ -145,7 +154,7 @@ local function create_world_formspec(dialogdata)
|
|||
end
|
||||
if not selindex then
|
||||
selindex = 1
|
||||
current_mg = first_mg
|
||||
current_mapgen = first_mg
|
||||
end
|
||||
mglist = mglist:sub(1, -2)
|
||||
end
|
||||
|
@ -254,7 +263,7 @@ local function create_world_formspec(dialogdata)
|
|||
local str_flags, str_spflags
|
||||
local label_flags, label_spflags = "", ""
|
||||
y = y + 0.3
|
||||
str_flags, y = mg_main_flags(current_mg, y)
|
||||
str_flags, y = mg_main_flags(current_mapgen_internal, y)
|
||||
if str_flags ~= "" then
|
||||
label_flags = "label[0,"..y_start..";" .. fgettext("Mapgen flags") .. "]"
|
||||
y_start = y + 0.4
|
||||
|
@ -262,7 +271,7 @@ local function create_world_formspec(dialogdata)
|
|||
y_start = 0.0
|
||||
end
|
||||
y = y_start + 0.3
|
||||
str_spflags = mg_specific_flags(current_mg, y)
|
||||
str_spflags = mg_specific_flags(current_mapgen_internal, y)
|
||||
if str_spflags ~= "" then
|
||||
label_spflags = "label[0,"..y_start..";" .. fgettext("Mapgen-specific flags") .. "]"
|
||||
end
|
||||
|
@ -370,11 +379,20 @@ local function create_world_buttonhandler(this, fields)
|
|||
if message == nil then
|
||||
this.data.seed = fields["te_seed"] or ""
|
||||
this.data.mg = fields["dd_mapgen"]
|
||||
local mapgen_internal = this.data.mg
|
||||
local mapgen = nil
|
||||
|
||||
local lua_mapgens = core.get_lua_mapgen_descriptions()
|
||||
if lua_mapgens[this.data.mg] then
|
||||
mapgen_internal = "singlenode"
|
||||
mapgen = this.data.mg
|
||||
end
|
||||
|
||||
-- actual names as used by engine
|
||||
local settings = {
|
||||
fixed_map_seed = this.data.seed,
|
||||
mg_name = this.data.mg,
|
||||
mg_name = mapgen_internal,
|
||||
lua_mapgen = mapgen,
|
||||
mg_flags = table_to_flags(this.data.flags.main),
|
||||
mgv5_spflags = table_to_flags(this.data.flags.v5),
|
||||
mgv6_spflags = table_to_flags(this.data.flags.v6),
|
||||
|
|
|
@ -403,6 +403,40 @@ Any mod can redefine `experimental:tnt` by using the name
|
|||
when registering it. For this to work correctly, that mod must have
|
||||
`experimental` as a dependency.
|
||||
|
||||
Lua-defined Mapgens
|
||||
====
|
||||
|
||||
Except what is refrenced here, lua-defined mapgens are identical to mods.
|
||||
|
||||
mapgen load path
|
||||
-------------
|
||||
|
||||
Paths are relative to the directories listed in the [Paths] section above.
|
||||
* `mapgens/`
|
||||
|
||||
Mapgen directory structure
|
||||
-----------------------
|
||||
|
||||
mapgens
|
||||
├── mapgenname
|
||||
│ ├── mapgen.conf
|
||||
│ ├── init.lua
|
||||
│ └── <custom data and code>
|
||||
└── another
|
||||
|
||||
### mapgen.conf
|
||||
|
||||
A `Settings` file that provides meta information about the mapgen.
|
||||
|
||||
* `name`: The mapgen name. Allows Luanti to determine the mapgen name even if the
|
||||
folder is wrongly named.
|
||||
* `title`: A human-readable title to address the mapgen. See [Translating content meta](#translating-content-meta).
|
||||
* `description`: Description of mapgen. See [Translating content meta](#translating-content-meta).
|
||||
* `author`: The author's ContentDB username.
|
||||
* `release`: Ignore this: Should only ever be set by ContentDB, as it is an
|
||||
internal ID used to track versions.
|
||||
* `textdomain`: Textdomain used to translate title and description. Defaults to mapgen.
|
||||
See [Translating content meta](#translating-content-meta).
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -109,6 +109,8 @@ of manually putting one, as different OSs use different delimiters. E.g.
|
|||
* `handle:stop()` or `core.sound_stop(handle)`
|
||||
* `core.get_mapgen_names([include_hidden=false])` -> table of map generator algorithms
|
||||
registered in the core (possible in async calls)
|
||||
* `core.get_lua_mapgen_descriptions()` -> map of `[mapgen_name] = mapgen_description` as listed
|
||||
in `mapgen.conf`.
|
||||
* `core.get_cache_path()` -> path of cache
|
||||
* `core.get_temp_path([param])` (possible in async calls)
|
||||
* `param`=true: returns path to a newly created temporary file
|
||||
|
|
1
mapgens/mapgens_here.txt
Normal file
1
mapgens/mapgens_here.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Put lua mapgens in this directory.
|
|
@ -112,6 +112,39 @@ void ModConfiguration::addGameMods(const SubgameSpec &gamespec)
|
|||
m_last_mod = gamespec.last_mod;
|
||||
}
|
||||
|
||||
void ModConfiguration::addMapgenFromConfig(
|
||||
const std::string &settings_path,
|
||||
const std::unordered_map<std::string, std::string> &mapgenPaths)
|
||||
{
|
||||
Settings conf;
|
||||
conf.readConfigFile(settings_path.c_str());
|
||||
|
||||
if (!conf.exists("mapgen"))
|
||||
return;
|
||||
|
||||
const std::string mapgen = conf.get("mapgen");
|
||||
|
||||
// List of enabled mapgens
|
||||
std::vector<ModSpec> mapgen_mod;
|
||||
|
||||
/*
|
||||
* Iterate through all installed mapgens
|
||||
*
|
||||
* If the mod is enabled, add it to `mapgen_mod` and break
|
||||
*/
|
||||
for (const auto &mapgenPath : mapgenPaths) {
|
||||
std::vector<ModSpec> addon_mods_in_path = flattenMods(getModsInPath(mapgenPath.second, mapgenPath.first));
|
||||
for (const auto &mod : addon_mods_in_path) {
|
||||
if (mod.name == mapgen) {
|
||||
mapgen_mod.push_back(mod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addMods(mapgen_mod);
|
||||
}
|
||||
|
||||
void ModConfiguration::addModsFromConfig(
|
||||
const std::string &settings_path,
|
||||
const std::unordered_map<std::string, std::string> &modPaths)
|
||||
|
|
|
@ -66,6 +66,15 @@ public:
|
|||
void addModsFromConfig(const std::string &settings_path,
|
||||
const std::unordered_map<std::string, std::string> &modPaths);
|
||||
|
||||
/**
|
||||
* Adds mods specified by a world.mt config
|
||||
*
|
||||
* @param settings_path Path to world.mt
|
||||
* @param mapgenPaths Map from virtual name to mapgen path
|
||||
*/
|
||||
void addMapgenFromConfig(const std::string &settings_path,
|
||||
const std::unordered_map<std::string, std::string> &mapgenPaths);
|
||||
|
||||
/**
|
||||
* Call this function once all mods have been added
|
||||
*/
|
||||
|
|
|
@ -76,7 +76,12 @@ bool parseModContents(ModSpec &spec)
|
|||
|
||||
|
||||
Settings info;
|
||||
info.readConfigFile((spec.path + DIR_DELIM + "mod.conf").c_str());
|
||||
if (fs::IsFile(spec.path + DIR_DELIM + "mapgen.conf")) {
|
||||
info.readConfigFile((spec.path + DIR_DELIM + "mapgen.conf").c_str());
|
||||
spec.is_mapgen = true;
|
||||
}
|
||||
else
|
||||
info.readConfigFile((spec.path + DIR_DELIM + "mod.conf").c_str());
|
||||
|
||||
if (info.exists("name"))
|
||||
spec.name = info.get("name");
|
||||
|
@ -89,50 +94,52 @@ bool parseModContents(ModSpec &spec)
|
|||
if (info.exists("release"))
|
||||
spec.release = info.getS32("release");
|
||||
|
||||
// Attempt to load dependencies from mod.conf
|
||||
bool mod_conf_has_depends = false;
|
||||
if (info.exists("depends")) {
|
||||
mod_conf_has_depends = true;
|
||||
std::string dep = info.get("depends");
|
||||
dep.erase(std::remove_if(dep.begin(), dep.end(),
|
||||
static_cast<int (*)(int)>(&std::isspace)), dep.end());
|
||||
for (const auto &dependency : str_split(dep, ',')) {
|
||||
spec.depends.insert(dependency);
|
||||
}
|
||||
}
|
||||
|
||||
if (info.exists("optional_depends")) {
|
||||
mod_conf_has_depends = true;
|
||||
std::string dep = info.get("optional_depends");
|
||||
dep.erase(std::remove_if(dep.begin(), dep.end(),
|
||||
static_cast<int (*)(int)>(&std::isspace)), dep.end());
|
||||
for (const auto &dependency : str_split(dep, ',')) {
|
||||
spec.optdepends.insert(dependency);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to depends.txt
|
||||
if (!mod_conf_has_depends) {
|
||||
std::vector<std::string> dependencies;
|
||||
|
||||
std::ifstream is((spec.path + DIR_DELIM + "depends.txt").c_str());
|
||||
|
||||
if (is.good())
|
||||
spec.deprecation_msgs.push_back("depends.txt is deprecated, please use mod.conf instead.");
|
||||
|
||||
while (is.good()) {
|
||||
std::string dep;
|
||||
std::getline(is, dep);
|
||||
dependencies.push_back(dep);
|
||||
// Attempt to load dependencies from mod.conf if not mapgen
|
||||
if (!spec.is_mapgen) {
|
||||
bool mod_conf_has_depends = false;
|
||||
if (info.exists("depends")) {
|
||||
mod_conf_has_depends = true;
|
||||
std::string dep = info.get("depends");
|
||||
dep.erase(std::remove_if(dep.begin(), dep.end(),
|
||||
static_cast<int (*)(int)>(&std::isspace)), dep.end());
|
||||
for (const auto &dependency : str_split(dep, ',')) {
|
||||
spec.depends.insert(dependency);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &dependency : dependencies) {
|
||||
std::unordered_set<char> symbols;
|
||||
if (parseDependsString(dependency, symbols)) {
|
||||
if (symbols.count('?') != 0) {
|
||||
spec.optdepends.insert(dependency);
|
||||
} else {
|
||||
spec.depends.insert(dependency);
|
||||
if (info.exists("optional_depends")) {
|
||||
mod_conf_has_depends = true;
|
||||
std::string dep = info.get("optional_depends");
|
||||
dep.erase(std::remove_if(dep.begin(), dep.end(),
|
||||
static_cast<int (*)(int)>(&std::isspace)), dep.end());
|
||||
for (const auto &dependency : str_split(dep, ',')) {
|
||||
spec.optdepends.insert(dependency);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to depends.txt
|
||||
if (!mod_conf_has_depends) {
|
||||
std::vector<std::string> dependencies;
|
||||
|
||||
std::ifstream is((spec.path + DIR_DELIM + "depends.txt").c_str());
|
||||
|
||||
if (is.good())
|
||||
spec.deprecation_msgs.push_back("depends.txt is deprecated, please use mod.conf instead.");
|
||||
|
||||
while (is.good()) {
|
||||
std::string dep;
|
||||
std::getline(is, dep);
|
||||
dependencies.push_back(dep);
|
||||
}
|
||||
|
||||
for (auto &dependency : dependencies) {
|
||||
std::unordered_set<char> symbols;
|
||||
if (parseDependsString(dependency, symbols)) {
|
||||
if (symbols.count('?') != 0) {
|
||||
spec.optdepends.insert(dependency);
|
||||
} else {
|
||||
spec.depends.insert(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,9 @@ struct ModSpec
|
|||
bool part_of_modpack = false;
|
||||
bool is_modpack = false;
|
||||
|
||||
// lua-defined mapgen
|
||||
bool is_mapgen = false;
|
||||
|
||||
/**
|
||||
* A constructed canonical path to represent this mod's location.
|
||||
* This intended to be used as an identifier for a modpath that tolerates file movement,
|
||||
|
|
|
@ -81,7 +81,8 @@ std::string getSubgamePathEnv()
|
|||
|
||||
static SubgameSpec getSubgameSpec(const std::string &game_id,
|
||||
const std::string &game_path,
|
||||
const std::unordered_map<std::string, std::string> &mods_paths)
|
||||
const std::unordered_map<std::string, std::string> &mods_paths,
|
||||
const std::unordered_map<std::string, std::string> &mapgen_paths)
|
||||
{
|
||||
const auto gamemods_path = game_path + DIR_DELIM + "mods";
|
||||
// Get meta
|
||||
|
@ -113,8 +114,12 @@ static SubgameSpec getSubgameSpec(const std::string &game_id,
|
|||
if (conf.exists("last_mod"))
|
||||
last_mod = conf.get("last_mod");
|
||||
|
||||
std::string mapgen;
|
||||
if (conf.exists("mapgen"))
|
||||
mapgen = conf.get("mapgen");
|
||||
|
||||
SubgameSpec spec(game_id, game_path, gamemods_path, mods_paths, game_title,
|
||||
game_author, game_release, first_mod, last_mod);
|
||||
game_author, game_release, first_mod, last_mod, mapgen, mapgen_paths);
|
||||
|
||||
if (conf.exists("name") && !conf.exists("title"))
|
||||
spec.deprecation_msgs.push_back("\"name\" setting in game.conf is deprecated, please use \"title\" instead");
|
||||
|
@ -167,6 +172,14 @@ SubgameSpec findSubgame(const std::string &id)
|
|||
|
||||
// Find mod directories
|
||||
std::unordered_map<std::string, std::string> mods_paths;
|
||||
std::unordered_map<std::string, std::string> mapgen_paths;
|
||||
|
||||
// Find mapgen directories
|
||||
mapgen_paths["mapgens"] = user + DIR_DELIM + "mapgens";
|
||||
if (!user_game && user != share)
|
||||
mapgen_paths["mapgens_share"] = share + DIR_DELIM + "mapgens";
|
||||
|
||||
// Find mod directories
|
||||
mods_paths["mods"] = user + DIR_DELIM + "mods";
|
||||
if (!user_game && user != share)
|
||||
mods_paths["share"] = share + DIR_DELIM + "mods";
|
||||
|
@ -175,7 +188,7 @@ SubgameSpec findSubgame(const std::string &id)
|
|||
mods_paths[fs::AbsolutePath(mod_path)] = mod_path;
|
||||
}
|
||||
|
||||
return getSubgameSpec(id, game_path, mods_paths);
|
||||
return getSubgameSpec(id, game_path, mods_paths, mapgen_paths);
|
||||
}
|
||||
|
||||
SubgameSpec findWorldSubgame(const std::string &world_path)
|
||||
|
@ -184,7 +197,7 @@ SubgameSpec findWorldSubgame(const std::string &world_path)
|
|||
// See if world contains an embedded game; if so, use it.
|
||||
std::string world_gamepath = world_path + DIR_DELIM + "game";
|
||||
if (fs::PathExists(world_gamepath))
|
||||
return getSubgameSpec(world_gameid, world_gamepath, {});
|
||||
return getSubgameSpec(world_gameid, world_gamepath, {}, {});
|
||||
return findSubgame(world_gameid);
|
||||
}
|
||||
|
||||
|
@ -330,7 +343,7 @@ std::vector<WorldSpec> getAvailableWorlds()
|
|||
}
|
||||
|
||||
void loadGameConfAndInitWorld(const std::string &path, const std::string &name,
|
||||
const SubgameSpec &gamespec, bool create_world)
|
||||
const SubgameSpec &gamespec, bool create_world, const std::string &lua_mapgen)
|
||||
{
|
||||
std::string final_path = path;
|
||||
|
||||
|
@ -380,6 +393,9 @@ void loadGameConfAndInitWorld(const std::string &path, const std::string &name,
|
|||
}
|
||||
conf.set("backend", backend);
|
||||
|
||||
if (lua_mapgen != "")
|
||||
conf.set("mapgen", lua_mapgen);
|
||||
|
||||
conf.set("player_backend", "sqlite3");
|
||||
conf.set("auth_backend", "sqlite3");
|
||||
conf.set("mod_storage_backend", "sqlite3");
|
||||
|
|
|
@ -19,6 +19,7 @@ struct SubgameSpec
|
|||
int release;
|
||||
std::string first_mod; // "" <=> no mod
|
||||
std::string last_mod; // "" <=> no mod
|
||||
std::string lua_mapgen; // "" <=> use internal
|
||||
std::string path;
|
||||
std::string gamemods_path;
|
||||
|
||||
|
@ -26,6 +27,7 @@ struct SubgameSpec
|
|||
* Map from virtual path to mods path
|
||||
*/
|
||||
std::unordered_map<std::string, std::string> addon_mods_paths;
|
||||
std::unordered_map<std::string, std::string> mapgen_paths;
|
||||
|
||||
// For logging purposes
|
||||
std::vector<const char *> deprecation_msgs;
|
||||
|
@ -36,14 +38,18 @@ struct SubgameSpec
|
|||
const std::string &title = "",
|
||||
const std::string &author = "", int release = 0,
|
||||
const std::string &first_mod = "",
|
||||
const std::string &last_mod = "") :
|
||||
const std::string &last_mod = "",
|
||||
const std::string &lua_mapgen = "",
|
||||
const std::unordered_map<std::string, std::string> &mapgen_paths = {}) :
|
||||
id(id),
|
||||
title(title), author(author), release(release),
|
||||
first_mod(first_mod),
|
||||
last_mod(last_mod),
|
||||
lua_mapgen(lua_mapgen),
|
||||
path(path),
|
||||
gamemods_path(gamemods_path),
|
||||
addon_mods_paths(addon_mods_paths)
|
||||
addon_mods_paths(addon_mods_paths),
|
||||
mapgen_paths(mapgen_paths)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -88,4 +94,4 @@ std::vector<WorldSpec> getAvailableWorlds();
|
|||
// loads the subgame's config and creates world directory
|
||||
// and world.mt if they don't exist
|
||||
void loadGameConfAndInitWorld(const std::string &path, const std::string &name,
|
||||
const SubgameSpec &gamespec, bool create_world);
|
||||
const SubgameSpec &gamespec, bool create_world, const std::string &lua_mapgen);
|
||||
|
|
|
@ -593,7 +593,10 @@ int ModApiMainMenu::l_create_world(lua_State *L)
|
|||
|
||||
// Create world if it doesn't exist
|
||||
try {
|
||||
loadGameConfAndInitWorld(path, name, *game_it, true);
|
||||
std::string lua_mapgen = "";
|
||||
if (use_settings.find("lua_mapgen") != use_settings.end())
|
||||
lua_mapgen = use_settings["lua_mapgen"];
|
||||
loadGameConfAndInitWorld(path, name, *game_it, true, lua_mapgen);
|
||||
lua_pushnil(L);
|
||||
} catch (const BaseException &e) {
|
||||
auto err = std::string("Failed to initialize world: ") + e.what();
|
||||
|
@ -660,6 +663,21 @@ int ModApiMainMenu::l_get_mapgen_names(lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
int ModApiMainMenu::l_get_lua_mapgen_descriptions(lua_State *L)
|
||||
{
|
||||
std::vector<ModSpec> addon_mods_in_path = flattenMods(getModsInPath(porting::path_share + DIR_DELIM + "mapgens" + DIR_DELIM, "mapgen/"));
|
||||
|
||||
lua_newtable(L);
|
||||
for (const auto &mod : addon_mods_in_path) {
|
||||
if (mod.is_mapgen) {
|
||||
lua_pushstring(L, mod.desc.c_str());
|
||||
lua_setfield(L, -2, mod.name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
int ModApiMainMenu::l_get_user_path(lua_State *L)
|
||||
|
@ -1059,6 +1077,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
|
|||
API_FCT(set_background);
|
||||
API_FCT(set_topleft_text);
|
||||
API_FCT(get_mapgen_names);
|
||||
API_FCT(get_lua_mapgen_descriptions);
|
||||
API_FCT(get_user_path);
|
||||
API_FCT(get_modpath);
|
||||
API_FCT(get_modpaths);
|
||||
|
@ -1100,6 +1119,7 @@ void ModApiMainMenu::InitializeAsync(lua_State *L, int top)
|
|||
API_FCT(get_worlds);
|
||||
API_FCT(get_games);
|
||||
API_FCT(get_mapgen_names);
|
||||
API_FCT(get_lua_mapgen_descriptions);
|
||||
API_FCT(get_user_path);
|
||||
API_FCT(get_modpath);
|
||||
API_FCT(get_modpaths);
|
||||
|
|
|
@ -51,6 +51,8 @@ private:
|
|||
|
||||
static int l_get_mapgen_names(lua_State *L);
|
||||
|
||||
static int l_get_lua_mapgen_descriptions(lua_State *L);
|
||||
|
||||
static int l_get_language(lua_State *L);
|
||||
|
||||
//packages
|
||||
|
|
|
@ -447,7 +447,7 @@ void Server::init()
|
|||
try {
|
||||
loadGameConfAndInitWorld(m_path_world,
|
||||
fs::GetFilenameFromPath(m_path_world.c_str()),
|
||||
m_gamespec, false);
|
||||
m_gamespec, false, "");
|
||||
} catch (const BaseException &e) {
|
||||
throw ServerError(std::string("Failed to initialize world: ") + e.what());
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ ServerModManager::ServerModManager(const std::string &worldpath, SubgameSpec gam
|
|||
|
||||
// Load normal mods
|
||||
std::string worldmt = worldpath + DIR_DELIM + "world.mt";
|
||||
configuration.addMapgenFromConfig(worldmt, gamespec.mapgen_paths);
|
||||
configuration.addModsFromConfig(worldmt, gamespec.addon_mods_paths);
|
||||
configuration.checkConflictsAndDeps();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue