Skip to content

Commit

Permalink
Load dependencies and description from mod.conf
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenwardy committed Mar 28, 2018
1 parent dfc8198 commit 71b2570
Show file tree
Hide file tree
Showing 20 changed files with 237 additions and 140 deletions.
31 changes: 5 additions & 26 deletions builtin/mainmenu/modmgr.lua
Expand Up @@ -271,34 +271,13 @@ function modmgr.render_modlist(render_list)
end

--------------------------------------------------------------------------------
function modmgr.get_dependencies(modfolder)
local toadd_hard = ""
local toadd_soft = ""
if modfolder ~= nil then
local filename = modfolder ..
DIR_DELIM .. "depends.txt"

local hard_dependencies = {}
local soft_dependencies = {}
local dependencyfile = io.open(filename,"r")
if dependencyfile then
local dependency = dependencyfile:read("*l")
while dependency do
dependency = dependency:gsub("\r", "")
if string.sub(dependency, -1, -1) == "?" then
table.insert(soft_dependencies, string.sub(dependency, 1, -2))
else
table.insert(hard_dependencies, dependency)
end
dependency = dependencyfile:read()
end
dependencyfile:close()
end
toadd_hard = table.concat(hard_dependencies, ",")
toadd_soft = table.concat(soft_dependencies, ",")
function modmgr.get_dependencies(path)
if path == nil then
return "", ""
end

return toadd_hard, toadd_soft
local info = core.get_mod_info(path)
return table.concat(info.depends, ","), table.concat(info.optional_depends, ",")
end

--------------------------------------------------------------------------------
Expand Down
41 changes: 14 additions & 27 deletions builtin/mainmenu/tab_mods.lua
Expand Up @@ -40,12 +40,11 @@ local function get_formspec(tabview, name, tabdata)
end

if selected_mod ~= nil then
local modscreenshot = nil

--check for screenshot beeing available
local screenshotfilename = selected_mod.path .. DIR_DELIM .. "screenshot.png"
local error = nil
local screenshotfile,error = io.open(screenshotfilename,"r")
local screenshotfile, error = io.open(screenshotfilename,"r")

local modscreenshot
if error == nil then
screenshotfile:close()
modscreenshot = screenshotfilename
Expand All @@ -55,33 +54,20 @@ local function get_formspec(tabview, name, tabdata)
modscreenshot = defaulttexturedir .. "no_screenshot.png"
end

retval = retval
.. "image[5.5,0;3,2;" .. core.formspec_escape(modscreenshot) .. "]"
.. "label[8.25,0.6;" .. selected_mod.name .. "]"

local descriptionlines = nil
error = nil
local descriptionfilename = selected_mod.path .. "description.txt"
local descriptionfile,error = io.open(descriptionfilename,"r")
if error == nil then
local descriptiontext = descriptionfile:read("*all")

descriptionlines = core.wrap_text(descriptiontext, 42, true)
descriptionfile:close()
else
descriptionlines = {}
descriptionlines[#descriptionlines + 1] = fgettext("No mod description available")
end

retval = retval ..
"label[5.5,1.7;".. fgettext("Mod Information:") .. "]" ..
"textlist[5.5,2.2;6.2,2.4;description;"
"image[5.5,0;3,2;" .. core.formspec_escape(modscreenshot) .. "]" ..
"label[8.25,0.6;" .. selected_mod.name .. "]" ..
"label[5.5,1.7;".. fgettext("Mod Information:") .. "]" ..
"textlist[5.5,2.2;6.2,2.4;description;"

for i=1,#descriptionlines,1 do

local info = core.get_mod_info(selected_mod.path)
local desc = info.description or fgettext("No mod description available")
local descriptionlines = core.wrap_text(desc, 42, true)
for i = 1, #descriptionlines do
retval = retval .. core.formspec_escape(descriptionlines[i]) .. ","
end


if selected_mod.is_modpack then
retval = retval .. ";0]" ..
"button[9.9,4.65;2,1;btn_mod_mgr_rename_modpack;" ..
Expand All @@ -90,7 +76,8 @@ local function get_formspec(tabview, name, tabdata)
.. fgettext("Uninstall Selected Modpack") .. "]"
else
--show dependencies
local toadd_hard, toadd_soft = modmgr.get_dependencies(selected_mod.path)
local toadd_hard = table.concat(info.depends, ",")
local toadd_soft = table.concat(info.optional_depends, ",")
if toadd_hard == "" and toadd_soft == "" then
retval = retval .. "," .. fgettext("No dependencies.")
else
Expand Down
33 changes: 26 additions & 7 deletions doc/lua_api.txt
Expand Up @@ -130,9 +130,8 @@ Mod directory structure

mods
|-- modname
| |-- depends.txt
| |-- mod.conf
| |-- screenshot.png
| |-- description.txt
| |-- settingtypes.txt
| |-- init.lua
| |-- models
Expand All @@ -145,12 +144,32 @@ Mod directory structure
| `-- <custom data>
`-- another


### modname
The location of this directory can be fetched by using
`minetest.get_modpath(modname)`.

### mod.conf
A key-value store of mod details.

* `name` - the mod name. Allows Minetest to determine the mod name even if the
folder is wrongly named.
* `description` - Description of mod to be shown in the Mods tab of the mainmenu.
* `depends` - A comma separated list of dependencies. These are mods that must
be loaded before this mod.
* `optional_depends` - A comma separated list of optional dependencies.
Like a dependency, but no error if the mod doesn't exist.

Note: to support 0.4.x, please also provide depends.txt.

### `screenshot.png`
A screenshot shown in the mod manager within the main menu. It should
have an aspect ratio of 3:2 and a minimum size of 300×200 pixels.

### `depends.txt`
**Deprecated:** you should use mod.conf instead.

This file is used if there are no dependencies in mod.conf.

List of mods that have to be loaded before loading this mod.

A single line contains a single modname.
Expand All @@ -159,11 +178,11 @@ Optional dependencies can be defined by appending a question mark
to a single modname. This means that if the specified mod
is missing, it does not prevent this mod from being loaded.

### `screenshot.png`
A screenshot shown in the mod manager within the main menu. It should
have an aspect ratio of 3:2 and a minimum size of 300×200 pixels.

### `description.txt`
**Deprecated:** you should use mod.conf instead.

This file is used if there is no description in mod.conf.

A file containing a description to be shown in the Mods tab of the mainmenu.

### `settingtypes.txt`
Expand Down
11 changes: 10 additions & 1 deletion doc/menu_lua_api.txt
Expand Up @@ -111,7 +111,7 @@ core.get_screen_info()
window_height = <current window height>
}

Games:
Packages:
core.get_game(index)
^ returns {
id = <id>,
Expand All @@ -125,6 +125,15 @@ core.get_game(index)
core.get_games() -> table of all games in upper format (possible in async calls)
core.get_mapgen_names([include_hidden=false]) -> table of map generator algorithms
registered in the core (possible in async calls)
core.get_mod_info(path)
^ returns {
name = "name of mod",
type = "mod" or "modpack",
description = "description",
path = "path/to/mod",
depends = {"mod", "names"},
optional_depends = {"mod", "names"},
}

Favorites:
core.get_favorites(location) -> list of favorites (possible in async calls)
Expand Down
2 changes: 0 additions & 2 deletions games/minimal/mods/bucket/depends.txt

This file was deleted.

3 changes: 3 additions & 0 deletions games/minimal/mods/bucket/mod.conf
@@ -0,0 +1,3 @@
name = bucket
description = Minimal bucket to place and pick up liquids
depends = default
2 changes: 2 additions & 0 deletions games/minimal/mods/default/mod.conf
@@ -0,0 +1,2 @@
name = default
description = Minimal default, adds basic nodes
2 changes: 0 additions & 2 deletions games/minimal/mods/experimental/depends.txt

This file was deleted.

3 changes: 3 additions & 0 deletions games/minimal/mods/experimental/mod.conf
@@ -0,0 +1,3 @@
name = experimental
description = Minimal mod to test features
depends = default, stairs
2 changes: 0 additions & 2 deletions games/minimal/mods/give_initial_stuff/depends.txt

This file was deleted.

3 changes: 3 additions & 0 deletions games/minimal/mods/give_initial_stuff/mod.conf
@@ -0,0 +1,3 @@
name = give_initial_stuff
description = Gives items to players on join
depends = default
2 changes: 0 additions & 2 deletions games/minimal/mods/legacy/depends.txt

This file was deleted.

3 changes: 3 additions & 0 deletions games/minimal/mods/legacy/mod.conf
@@ -0,0 +1,3 @@
name = legacy
description = Aliases allowing support for 0.3.x worlds
depends = default
1 change: 0 additions & 1 deletion games/minimal/mods/stairs/depends.txt

This file was deleted.

3 changes: 3 additions & 0 deletions games/minimal/mods/stairs/mod.conf
@@ -0,0 +1,3 @@
name = stairs
description = Adds stairs and slabs
depends = default
2 changes: 2 additions & 0 deletions games/minimal/mods/test/mod.conf
@@ -0,0 +1,2 @@
name = test
description = Adds unit tests for the engine
74 changes: 56 additions & 18 deletions src/mods.cpp
Expand Up @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cctype>
#include <fstream>
#include <json/json.h>
#include <algorithm>
#include "mods.h"
#include "filesys.h"
#include "log.h"
Expand All @@ -28,14 +29,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h"
#include "convert_json.h"

static bool parseDependsLine(std::istream &is,
std::string &dep, std::set<char> &symbols)
bool parseDependsString(std::string &dep,
std::unordered_set<char> &symbols)
{
std::getline(is, dep);
dep = trim(dep);
symbols.clear();
size_t pos = dep.size();
while(pos > 0 && !string_allowed(dep.substr(pos-1, 1), MODNAME_ALLOWED_CHARS)){
while (pos > 0 && !string_allowed(dep.substr(pos-1, 1), MODNAME_ALLOWED_CHARS)) {
// last character is a symbol, not part of the modname
symbols.insert(dep[pos-1]);
--pos;
Expand All @@ -60,28 +60,66 @@ void parseModContents(ModSpec &spec)

// Handle modpacks (defined by containing modpack.txt)
std::ifstream modpack_is((spec.path+DIR_DELIM+"modpack.txt").c_str());
if(modpack_is.good()){ //a modpack, recursively get the mods in it
if (modpack_is.good()) { // a modpack, recursively get the mods in it
modpack_is.close(); // We don't actually need the file
spec.is_modpack = true;
spec.modpack_content = getModsInPath(spec.path, true);

// modpacks have no dependencies; they are defined and
// tracked separately for each mod in the modpack
}
else{ // not a modpack, parse the dependencies
std::ifstream is((spec.path+DIR_DELIM+"depends.txt").c_str());
while(is.good()){
std::string dep;
std::set<char> symbols;
if(parseDependsLine(is, dep, symbols)){
if(symbols.count('?') != 0){
spec.optdepends.insert(dep);
}
else{
spec.depends.insert(dep);

} else {
// 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());
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);
}
}
}
}

if (info.exists("description")) {
spec.desc = info.get("description");
} else {
std::ifstream is((spec.path + DIR_DELIM + "description.txt").c_str());
spec.desc = std::string((std::istreambuf_iterator<char>(is)),
std::istreambuf_iterator<char>());
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/mods.h
Expand Up @@ -37,13 +37,16 @@ struct ModSpec
{
std::string name;
std::string path;
std::string desc;

//if normal mod:
std::unordered_set<std::string> depends;
std::unordered_set<std::string> optdepends;
std::unordered_set<std::string> unsatisfied_depends;

bool part_of_modpack = false;
bool is_modpack = false;

// if modpack:
std::map<std::string,ModSpec> modpack_content;
ModSpec(const std::string &name_ = "", const std::string &path_ = ""):
Expand Down

0 comments on commit 71b2570

Please sign in to comment.