Skip to content

Commit 71b2570

Browse files
authoredMar 28, 2018
Load dependencies and description from mod.conf
1 parent dfc8198 commit 71b2570

File tree

20 files changed

+237
-140
lines changed

20 files changed

+237
-140
lines changed
 

Diff for: ‎builtin/mainmenu/modmgr.lua

+5-26
Original file line numberDiff line numberDiff line change
@@ -271,34 +271,13 @@ function modmgr.render_modlist(render_list)
271271
end
272272

273273
--------------------------------------------------------------------------------
274-
function modmgr.get_dependencies(modfolder)
275-
local toadd_hard = ""
276-
local toadd_soft = ""
277-
if modfolder ~= nil then
278-
local filename = modfolder ..
279-
DIR_DELIM .. "depends.txt"
280-
281-
local hard_dependencies = {}
282-
local soft_dependencies = {}
283-
local dependencyfile = io.open(filename,"r")
284-
if dependencyfile then
285-
local dependency = dependencyfile:read("*l")
286-
while dependency do
287-
dependency = dependency:gsub("\r", "")
288-
if string.sub(dependency, -1, -1) == "?" then
289-
table.insert(soft_dependencies, string.sub(dependency, 1, -2))
290-
else
291-
table.insert(hard_dependencies, dependency)
292-
end
293-
dependency = dependencyfile:read()
294-
end
295-
dependencyfile:close()
296-
end
297-
toadd_hard = table.concat(hard_dependencies, ",")
298-
toadd_soft = table.concat(soft_dependencies, ",")
274+
function modmgr.get_dependencies(path)
275+
if path == nil then
276+
return "", ""
299277
end
300278

301-
return toadd_hard, toadd_soft
279+
local info = core.get_mod_info(path)
280+
return table.concat(info.depends, ","), table.concat(info.optional_depends, ",")
302281
end
303282

304283
--------------------------------------------------------------------------------

Diff for: ‎builtin/mainmenu/tab_mods.lua

+14-27
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,11 @@ local function get_formspec(tabview, name, tabdata)
4040
end
4141

4242
if selected_mod ~= nil then
43-
local modscreenshot = nil
44-
4543
--check for screenshot beeing available
4644
local screenshotfilename = selected_mod.path .. DIR_DELIM .. "screenshot.png"
47-
local error = nil
48-
local screenshotfile,error = io.open(screenshotfilename,"r")
45+
local screenshotfile, error = io.open(screenshotfilename,"r")
46+
47+
local modscreenshot
4948
if error == nil then
5049
screenshotfile:close()
5150
modscreenshot = screenshotfilename
@@ -55,33 +54,20 @@ local function get_formspec(tabview, name, tabdata)
5554
modscreenshot = defaulttexturedir .. "no_screenshot.png"
5655
end
5756

58-
retval = retval
59-
.. "image[5.5,0;3,2;" .. core.formspec_escape(modscreenshot) .. "]"
60-
.. "label[8.25,0.6;" .. selected_mod.name .. "]"
61-
62-
local descriptionlines = nil
63-
error = nil
64-
local descriptionfilename = selected_mod.path .. "description.txt"
65-
local descriptionfile,error = io.open(descriptionfilename,"r")
66-
if error == nil then
67-
local descriptiontext = descriptionfile:read("*all")
68-
69-
descriptionlines = core.wrap_text(descriptiontext, 42, true)
70-
descriptionfile:close()
71-
else
72-
descriptionlines = {}
73-
descriptionlines[#descriptionlines + 1] = fgettext("No mod description available")
74-
end
75-
7657
retval = retval ..
77-
"label[5.5,1.7;".. fgettext("Mod Information:") .. "]" ..
78-
"textlist[5.5,2.2;6.2,2.4;description;"
58+
"image[5.5,0;3,2;" .. core.formspec_escape(modscreenshot) .. "]" ..
59+
"label[8.25,0.6;" .. selected_mod.name .. "]" ..
60+
"label[5.5,1.7;".. fgettext("Mod Information:") .. "]" ..
61+
"textlist[5.5,2.2;6.2,2.4;description;"
7962

80-
for i=1,#descriptionlines,1 do
63+
64+
local info = core.get_mod_info(selected_mod.path)
65+
local desc = info.description or fgettext("No mod description available")
66+
local descriptionlines = core.wrap_text(desc, 42, true)
67+
for i = 1, #descriptionlines do
8168
retval = retval .. core.formspec_escape(descriptionlines[i]) .. ","
8269
end
8370

84-
8571
if selected_mod.is_modpack then
8672
retval = retval .. ";0]" ..
8773
"button[9.9,4.65;2,1;btn_mod_mgr_rename_modpack;" ..
@@ -90,7 +76,8 @@ local function get_formspec(tabview, name, tabdata)
9076
.. fgettext("Uninstall Selected Modpack") .. "]"
9177
else
9278
--show dependencies
93-
local toadd_hard, toadd_soft = modmgr.get_dependencies(selected_mod.path)
79+
local toadd_hard = table.concat(info.depends, ",")
80+
local toadd_soft = table.concat(info.optional_depends, ",")
9481
if toadd_hard == "" and toadd_soft == "" then
9582
retval = retval .. "," .. fgettext("No dependencies.")
9683
else

Diff for: ‎doc/lua_api.txt

+26-7
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,8 @@ Mod directory structure
130130

131131
mods
132132
|-- modname
133-
| |-- depends.txt
133+
| |-- mod.conf
134134
| |-- screenshot.png
135-
| |-- description.txt
136135
| |-- settingtypes.txt
137136
| |-- init.lua
138137
| |-- models
@@ -145,12 +144,32 @@ Mod directory structure
145144
| `-- <custom data>
146145
`-- another
147146

148-
149147
### modname
150148
The location of this directory can be fetched by using
151149
`minetest.get_modpath(modname)`.
152150

151+
### mod.conf
152+
A key-value store of mod details.
153+
154+
* `name` - the mod name. Allows Minetest to determine the mod name even if the
155+
folder is wrongly named.
156+
* `description` - Description of mod to be shown in the Mods tab of the mainmenu.
157+
* `depends` - A comma separated list of dependencies. These are mods that must
158+
be loaded before this mod.
159+
* `optional_depends` - A comma separated list of optional dependencies.
160+
Like a dependency, but no error if the mod doesn't exist.
161+
162+
Note: to support 0.4.x, please also provide depends.txt.
163+
164+
### `screenshot.png`
165+
A screenshot shown in the mod manager within the main menu. It should
166+
have an aspect ratio of 3:2 and a minimum size of 300×200 pixels.
167+
153168
### `depends.txt`
169+
**Deprecated:** you should use mod.conf instead.
170+
171+
This file is used if there are no dependencies in mod.conf.
172+
154173
List of mods that have to be loaded before loading this mod.
155174

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

162-
### `screenshot.png`
163-
A screenshot shown in the mod manager within the main menu. It should
164-
have an aspect ratio of 3:2 and a minimum size of 300×200 pixels.
165-
166181
### `description.txt`
182+
**Deprecated:** you should use mod.conf instead.
183+
184+
This file is used if there is no description in mod.conf.
185+
167186
A file containing a description to be shown in the Mods tab of the mainmenu.
168187

169188
### `settingtypes.txt`

Diff for: ‎doc/menu_lua_api.txt

+10-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ core.get_screen_info()
111111
window_height = <current window height>
112112
}
113113

114-
Games:
114+
Packages:
115115
core.get_game(index)
116116
^ returns {
117117
id = <id>,
@@ -125,6 +125,15 @@ core.get_game(index)
125125
core.get_games() -> table of all games in upper format (possible in async calls)
126126
core.get_mapgen_names([include_hidden=false]) -> table of map generator algorithms
127127
registered in the core (possible in async calls)
128+
core.get_mod_info(path)
129+
^ returns {
130+
name = "name of mod",
131+
type = "mod" or "modpack",
132+
description = "description",
133+
path = "path/to/mod",
134+
depends = {"mod", "names"},
135+
optional_depends = {"mod", "names"},
136+
}
128137

129138
Favorites:
130139
core.get_favorites(location) -> list of favorites (possible in async calls)

Diff for: ‎games/minimal/mods/bucket/depends.txt

-2
This file was deleted.

Diff for: ‎games/minimal/mods/bucket/mod.conf

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name = bucket
2+
description = Minimal bucket to place and pick up liquids
3+
depends = default

Diff for: ‎games/minimal/mods/default/mod.conf

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
name = default
2+
description = Minimal default, adds basic nodes

Diff for: ‎games/minimal/mods/experimental/depends.txt

-2
This file was deleted.

Diff for: ‎games/minimal/mods/experimental/mod.conf

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name = experimental
2+
description = Minimal mod to test features
3+
depends = default, stairs

Diff for: ‎games/minimal/mods/give_initial_stuff/depends.txt

-2
This file was deleted.

Diff for: ‎games/minimal/mods/give_initial_stuff/mod.conf

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name = give_initial_stuff
2+
description = Gives items to players on join
3+
depends = default

Diff for: ‎games/minimal/mods/legacy/depends.txt

-2
This file was deleted.

Diff for: ‎games/minimal/mods/legacy/mod.conf

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name = legacy
2+
description = Aliases allowing support for 0.3.x worlds
3+
depends = default

Diff for: ‎games/minimal/mods/stairs/depends.txt

-1
This file was deleted.

Diff for: ‎games/minimal/mods/stairs/mod.conf

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name = stairs
2+
description = Adds stairs and slabs
3+
depends = default

Diff for: ‎games/minimal/mods/test/mod.conf

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
name = test
2+
description = Adds unit tests for the engine

Diff for: ‎src/mods.cpp

+56-18
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2020
#include <cctype>
2121
#include <fstream>
2222
#include <json/json.h>
23+
#include <algorithm>
2324
#include "mods.h"
2425
#include "filesys.h"
2526
#include "log.h"
@@ -28,14 +29,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2829
#include "porting.h"
2930
#include "convert_json.h"
3031

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

6161
// Handle modpacks (defined by containing modpack.txt)
6262
std::ifstream modpack_is((spec.path+DIR_DELIM+"modpack.txt").c_str());
63-
if(modpack_is.good()){ //a modpack, recursively get the mods in it
63+
if (modpack_is.good()) { // a modpack, recursively get the mods in it
6464
modpack_is.close(); // We don't actually need the file
6565
spec.is_modpack = true;
6666
spec.modpack_content = getModsInPath(spec.path, true);
67-
6867
// modpacks have no dependencies; they are defined and
6968
// tracked separately for each mod in the modpack
70-
}
71-
else{ // not a modpack, parse the dependencies
72-
std::ifstream is((spec.path+DIR_DELIM+"depends.txt").c_str());
73-
while(is.good()){
74-
std::string dep;
75-
std::set<char> symbols;
76-
if(parseDependsLine(is, dep, symbols)){
77-
if(symbols.count('?') != 0){
78-
spec.optdepends.insert(dep);
79-
}
80-
else{
81-
spec.depends.insert(dep);
69+
70+
} else {
71+
// Attempt to load dependencies from mod.conf
72+
bool mod_conf_has_depends = false;
73+
if (info.exists("depends")) {
74+
mod_conf_has_depends = true;
75+
std::string dep = info.get("depends");
76+
dep.erase(std::remove_if(dep.begin(), dep.end(),
77+
static_cast<int(*)(int)>(&std::isspace)), dep.end());
78+
for (const auto &dependency : str_split(dep, ',')) {
79+
spec.depends.insert(dependency);
80+
}
81+
}
82+
83+
if (info.exists("optional_depends")) {
84+
mod_conf_has_depends = true;
85+
std::string dep = info.get("optional_depends");
86+
dep.erase(std::remove_if(dep.begin(), dep.end(),
87+
static_cast<int(*)(int)>(&std::isspace)), dep.end());
88+
for (const auto &dependency : str_split(dep, ',')) {
89+
spec.optdepends.insert(dependency);
90+
}
91+
}
92+
93+
// Fallback to depends.txt
94+
if (!mod_conf_has_depends) {
95+
std::vector<std::string> dependencies;
96+
97+
std::ifstream is((spec.path + DIR_DELIM + "depends.txt").c_str());
98+
while (is.good()) {
99+
std::string dep;
100+
std::getline(is, dep);
101+
dependencies.push_back(dep);
102+
}
103+
104+
for (auto &dependency : dependencies) {
105+
std::unordered_set<char> symbols;
106+
if (parseDependsString(dependency, symbols)) {
107+
if (symbols.count('?') != 0) {
108+
spec.optdepends.insert(dependency);
109+
} else {
110+
spec.depends.insert(dependency);
111+
}
82112
}
83113
}
84114
}
115+
116+
if (info.exists("description")) {
117+
spec.desc = info.get("description");
118+
} else {
119+
std::ifstream is((spec.path + DIR_DELIM + "description.txt").c_str());
120+
spec.desc = std::string((std::istreambuf_iterator<char>(is)),
121+
std::istreambuf_iterator<char>());
122+
}
85123
}
86124
}
87125

Diff for: ‎src/mods.h

+3
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,16 @@ struct ModSpec
3737
{
3838
std::string name;
3939
std::string path;
40+
std::string desc;
41+
4042
//if normal mod:
4143
std::unordered_set<std::string> depends;
4244
std::unordered_set<std::string> optdepends;
4345
std::unordered_set<std::string> unsatisfied_depends;
4446

4547
bool part_of_modpack = false;
4648
bool is_modpack = false;
49+
4750
// if modpack:
4851
std::map<std::string,ModSpec> modpack_content;
4952
ModSpec(const std::string &name_ = "", const std::string &path_ = ""):

Diff for: ‎src/script/lua_api/l_mainmenu.cpp

+98-50
Original file line numberDiff line numberDiff line change
@@ -257,56 +257,6 @@ int ModApiMainMenu::l_get_worlds(lua_State *L)
257257
return 1;
258258
}
259259

260-
/******************************************************************************/
261-
int ModApiMainMenu::l_get_games(lua_State *L)
262-
{
263-
std::vector<SubgameSpec> games = getAvailableGames();
264-
265-
lua_newtable(L);
266-
int top = lua_gettop(L);
267-
unsigned int index = 1;
268-
269-
for (const SubgameSpec &game : games) {
270-
lua_pushnumber(L,index);
271-
lua_newtable(L);
272-
int top_lvl2 = lua_gettop(L);
273-
274-
lua_pushstring(L,"id");
275-
lua_pushstring(L, game.id.c_str());
276-
lua_settable(L, top_lvl2);
277-
278-
lua_pushstring(L,"path");
279-
lua_pushstring(L, game.path.c_str());
280-
lua_settable(L, top_lvl2);
281-
282-
lua_pushstring(L,"gamemods_path");
283-
lua_pushstring(L, game.gamemods_path.c_str());
284-
lua_settable(L, top_lvl2);
285-
286-
lua_pushstring(L,"name");
287-
lua_pushstring(L, game.name.c_str());
288-
lua_settable(L, top_lvl2);
289-
290-
lua_pushstring(L,"menuicon_path");
291-
lua_pushstring(L, game.menuicon_path.c_str());
292-
lua_settable(L, top_lvl2);
293-
294-
lua_pushstring(L,"addon_mods_paths");
295-
lua_newtable(L);
296-
int table2 = lua_gettop(L);
297-
int internal_index=1;
298-
for (const std::string &addon_mods_path : game.addon_mods_paths) {
299-
lua_pushnumber(L,internal_index);
300-
lua_pushstring(L, addon_mods_path.c_str());
301-
lua_settable(L, table2);
302-
internal_index++;
303-
}
304-
lua_settable(L, top_lvl2);
305-
lua_settable(L, top);
306-
index++;
307-
}
308-
return 1;
309-
}
310260
/******************************************************************************/
311261
int ModApiMainMenu::l_get_favorites(lua_State *L)
312262
{
@@ -477,6 +427,103 @@ int ModApiMainMenu::l_delete_favorite(lua_State *L)
477427
return 0;
478428
}
479429

430+
/******************************************************************************/
431+
int ModApiMainMenu::l_get_games(lua_State *L)
432+
{
433+
std::vector<SubgameSpec> games = getAvailableGames();
434+
435+
lua_newtable(L);
436+
int top = lua_gettop(L);
437+
unsigned int index = 1;
438+
439+
for (const SubgameSpec &game : games) {
440+
lua_pushnumber(L, index);
441+
lua_newtable(L);
442+
int top_lvl2 = lua_gettop(L);
443+
444+
lua_pushstring(L, "id");
445+
lua_pushstring(L, game.id.c_str());
446+
lua_settable(L, top_lvl2);
447+
448+
lua_pushstring(L, "path");
449+
lua_pushstring(L, game.path.c_str());
450+
lua_settable(L, top_lvl2);
451+
452+
lua_pushstring(L, "gamemods_path");
453+
lua_pushstring(L, game.gamemods_path.c_str());
454+
lua_settable(L, top_lvl2);
455+
456+
lua_pushstring(L, "name");
457+
lua_pushstring(L, game.name.c_str());
458+
lua_settable(L, top_lvl2);
459+
460+
lua_pushstring(L, "menuicon_path");
461+
lua_pushstring(L, game.menuicon_path.c_str());
462+
lua_settable(L, top_lvl2);
463+
464+
lua_pushstring(L, "addon_mods_paths");
465+
lua_newtable(L);
466+
int table2 = lua_gettop(L);
467+
int internal_index = 1;
468+
for (const std::string &addon_mods_path : game.addon_mods_paths) {
469+
lua_pushnumber(L, internal_index);
470+
lua_pushstring(L, addon_mods_path.c_str());
471+
lua_settable(L, table2);
472+
internal_index++;
473+
}
474+
lua_settable(L, top_lvl2);
475+
lua_settable(L, top);
476+
index++;
477+
}
478+
return 1;
479+
}
480+
481+
/******************************************************************************/
482+
int ModApiMainMenu::l_get_mod_info(lua_State *L)
483+
{
484+
std::string path = luaL_checkstring(L, 1);
485+
486+
ModSpec spec;
487+
spec.path = path;
488+
parseModContents(spec);
489+
490+
lua_newtable(L);
491+
492+
lua_pushstring(L, spec.name.c_str());
493+
lua_setfield(L, -2, "name");
494+
495+
lua_pushstring(L, spec.is_modpack ? "modpack" : "mod");
496+
lua_setfield(L, -2, "type");
497+
498+
lua_pushstring(L, spec.desc.c_str());
499+
lua_setfield(L, -2, "description");
500+
501+
lua_pushstring(L, spec.path.c_str());
502+
lua_setfield(L, -2, "path");
503+
504+
// Dependencies
505+
lua_newtable(L);
506+
int i = 1;
507+
for (const auto &dep : spec.depends) {
508+
lua_pushstring(L, dep.c_str());
509+
lua_rawseti(L, -2, i);
510+
i++;
511+
}
512+
lua_setfield(L, -2, "depends");
513+
514+
// Optional Dependencies
515+
lua_newtable(L);
516+
i = 1;
517+
for (const auto &dep : spec.optdepends) {
518+
lua_pushstring(L, dep.c_str());
519+
lua_rawseti(L, -2, i);
520+
i++;
521+
}
522+
lua_setfield(L, -2, "optional_depends");
523+
524+
return 1;
525+
}
526+
480527
/******************************************************************************/
481528
int ModApiMainMenu::l_show_keys_menu(lua_State *L)
482529
{
@@ -968,6 +1015,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
9681015
API_FCT(get_table_index);
9691016
API_FCT(get_worlds);
9701017
API_FCT(get_games);
1018+
API_FCT(get_mod_info);
9711019
API_FCT(start);
9721020
API_FCT(close);
9731021
API_FCT(get_favorites);

Diff for: ‎src/script/lua_api/l_mainmenu.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@ class ModApiMainMenu: public ModApiBase
7171

7272
static int l_get_worlds(lua_State *L);
7373

74-
static int l_get_games(lua_State *L);
75-
7674
static int l_get_mapgen_names(lua_State *L);
7775

7876
static int l_get_favorites(lua_State *L);
@@ -81,6 +79,12 @@ class ModApiMainMenu: public ModApiBase
8179

8280
static int l_gettext(lua_State *L);
8381

82+
//packages
83+
84+
static int l_get_games(lua_State *L);
85+
86+
static int l_get_mod_info(lua_State *L);
87+
8488
//gui
8589

8690
static int l_show_keys_menu(lua_State *L);

0 commit comments

Comments
 (0)
Please sign in to comment.