2 --Copyright (C) 2013 sapier
4 --This program is free software; you can redistribute it and/or modify
5 --it under the terms of the GNU Lesser General Public License as published by
6 --the Free Software Foundation; either version 2.1 of the License, or
7 --(at your option) any later version.
9 --This program is distributed in the hope that it will be useful,
10 --but WITHOUT ANY WARRANTY; without even the implied warranty of
11 --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 --GNU Lesser General Public License for more details.
14 --You should have received a copy of the GNU Lesser General Public License along
15 --with this program; if not, write to the Free Software Foundation, Inc.,
16 --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 --------------------------------------------------------------------------------
19 function get_mods(path,retval,modpack)
20 local mods = core.get_dir_list(path, true)
22 for _, name in ipairs(mods) do
23 if name:sub(1, 1) ~= "." then
24 local prefix = path .. DIR_DELIM .. name .. DIR_DELIM
26 retval[#retval + 1] = toadd
28 local mod_conf = Settings(prefix .. "mod.conf"):to_table()
36 if modpack ~= nil and modpack ~= "" then
37 toadd.modpack = modpack
39 local modpackfile = io.open(prefix .. "modpack.txt")
42 toadd.is_modpack = true
43 get_mods(prefix, retval, name)
50 --modmanager implementation
53 --------------------------------------------------------------------------------
54 function modmgr.extract(modfile)
55 if modfile.type == "zip" then
56 local tempfolder = os.tempfolder()
58 if tempfolder ~= nil and
60 core.create_dir(tempfolder)
61 if core.extract_zip(modfile.name,tempfolder) then
69 -------------------------------------------------------------------------------
70 function modmgr.getbasefolder(temppath)
72 if temppath == nil then
79 local testfile = io.open(temppath .. DIR_DELIM .. "init.lua","r")
80 if testfile ~= nil then
88 testfile = io.open(temppath .. DIR_DELIM .. "modpack.txt","r")
89 if testfile ~= nil then
97 local subdirs = core.get_dir_list(temppath, true)
99 --only single mod or modpack allowed
100 if #subdirs ~= 1 then
108 io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."init.lua","r")
109 if testfile ~= nil then
113 path= temppath .. DIR_DELIM .. subdirs[1]
118 io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."modpack.txt","r")
119 if testfile ~= nil then
123 path=temppath .. DIR_DELIM .. subdirs[1]
133 --------------------------------------------------------------------------------
134 function modmgr.isValidModname(modpath)
135 if modpath:find("-") ~= nil then
142 --------------------------------------------------------------------------------
143 function modmgr.parse_register_line(line)
144 local pos1 = line:find("\"")
147 pos2 = line:find("\"",pos1+1)
150 if pos1 ~= nil and pos2 ~= nil then
151 local item = line:sub(pos1+1,pos2-1)
155 local pos3 = item:find(":")
158 local retval = item:sub(1,pos3-1)
169 --------------------------------------------------------------------------------
170 function modmgr.parse_dofile_line(modpath,line)
171 local pos1 = line:find("\"")
174 pos2 = line:find("\"",pos1+1)
177 if pos1 ~= nil and pos2 ~= nil then
178 local filename = line:sub(pos1+1,pos2-1)
180 if filename ~= nil and
182 filename:find(".lua") then
183 return modmgr.identify_modname(modpath,filename)
189 --------------------------------------------------------------------------------
190 function modmgr.identify_modname(modpath,filename)
191 local testfile = io.open(modpath .. DIR_DELIM .. filename,"r")
192 if testfile ~= nil then
193 local line = testfile:read()
198 if line:find("minetest.register_tool") then
199 modname = modmgr.parse_register_line(line)
202 if line:find("minetest.register_craftitem") then
203 modname = modmgr.parse_register_line(line)
207 if line:find("minetest.register_node") then
208 modname = modmgr.parse_register_line(line)
211 if line:find("dofile") then
212 modname = modmgr.parse_dofile_line(modpath,line)
215 if modname ~= nil then
220 line = testfile:read()
227 --------------------------------------------------------------------------------
228 function modmgr.render_modlist(render_list)
231 if render_list == nil then
232 if modmgr.global_mods == nil then
233 modmgr.refresh_globals()
235 render_list = modmgr.global_mods
238 local list = render_list:get_list()
239 local last_modpack = nil
241 for i, v in ipairs(list) do
244 local rawlist = render_list:get_raw_list()
245 color = mt_color_dark_green
247 for j = 1, #rawlist, 1 do
248 if rawlist[j].modpack == list[i].name and
249 rawlist[j].enabled ~= true then
250 -- Modpack not entirely enabled so showing as grey
251 color = mt_color_grey
255 elseif v.is_game_content then
256 color = mt_color_blue
257 elseif v.enabled then
258 color = mt_color_green
261 retval[#retval + 1] = color
262 if v.modpack ~= nil or v.typ == "game_mod" then
263 retval[#retval + 1] = "1"
265 retval[#retval + 1] = "0"
267 retval[#retval + 1] = core.formspec_escape(v.name)
270 return table.concat(retval, ",")
273 --------------------------------------------------------------------------------
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"
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")
287 dependency = dependency:gsub("\r", "")
288 if string.sub(dependency, -1, -1) == "?" then
289 table.insert(soft_dependencies, string.sub(dependency, 1, -2))
291 table.insert(hard_dependencies, dependency)
293 dependency = dependencyfile:read()
295 dependencyfile:close()
297 toadd_hard = table.concat(hard_dependencies, ",")
298 toadd_soft = table.concat(soft_dependencies, ",")
301 return toadd_hard, toadd_soft
304 --------------------------------------------------------------------------------
305 function modmgr.get_worldconfig(worldpath)
306 local filename = worldpath ..
307 DIR_DELIM .. "world.mt"
309 local worldfile = Settings(filename)
311 local worldconfig = {}
312 worldconfig.global_mods = {}
313 worldconfig.game_mods = {}
315 for key,value in pairs(worldfile:to_table()) do
316 if key == "gameid" then
317 worldconfig.id = value
318 elseif key:sub(0, 9) == "load_mod_" then
319 worldconfig.global_mods[key] = core.is_yes(value)
321 worldconfig[key] = value
326 local gamespec = gamemgr.find_by_gameid(worldconfig.id)
327 gamemgr.get_game_mods(gamespec, worldconfig.game_mods)
332 --------------------------------------------------------------------------------
333 function modmgr.installmod(modfilename,basename)
334 local modfile = modmgr.identify_filetype(modfilename)
335 local modpath = modmgr.extract(modfile)
337 if modpath == nil then
338 gamedata.errormessage = fgettext("Install Mod: file: \"$1\"", modfile.name) ..
339 fgettext("\nInstall Mod: unsupported filetype \"$1\" or broken archive", modfile.type)
343 local basefolder = modmgr.getbasefolder(modpath)
345 if basefolder.type == "modpack" then
346 local clean_path = nil
348 if basename ~= nil then
349 clean_path = "mp_" .. basename
352 if clean_path == nil then
353 clean_path = get_last_folder(cleanup_path(basefolder.path))
356 if clean_path ~= nil then
357 local targetpath = core.get_modpath() .. DIR_DELIM .. clean_path
358 if not core.copy_dir(basefolder.path,targetpath) then
359 gamedata.errormessage = fgettext("Failed to install $1 to $2", basename, targetpath)
362 gamedata.errormessage = fgettext("Install Mod: unable to find suitable foldername for modpack $1", modfilename)
366 if basefolder.type == "mod" then
367 local targetfolder = basename
369 if targetfolder == nil then
370 targetfolder = modmgr.identify_modname(basefolder.path,"init.lua")
373 --if heuristic failed try to use current foldername
374 if targetfolder == nil then
375 targetfolder = get_last_folder(basefolder.path)
378 if targetfolder ~= nil and modmgr.isValidModname(targetfolder) then
379 local targetpath = core.get_modpath() .. DIR_DELIM .. targetfolder
380 core.copy_dir(basefolder.path,targetpath)
382 gamedata.errormessage = fgettext("Install Mod: unable to find real modname for: $1", modfilename)
386 core.delete_dir(modpath)
388 modmgr.refresh_globals()
392 --------------------------------------------------------------------------------
393 function modmgr.preparemodlist(data)
396 local global_mods = {}
400 local modpath = core.get_modpath()
402 if modpath ~= nil and
404 get_mods(modpath,global_mods)
407 for i=1,#global_mods,1 do
408 global_mods[i].typ = "global_mod"
409 retval[#retval + 1] = global_mods[i]
413 local gamespec = gamemgr.find_by_gameid(data.gameid)
414 gamemgr.get_game_mods(gamespec, game_mods)
416 if #game_mods > 0 then
418 retval[#retval + 1] = {
420 is_game_content = true,
421 name = fgettext("Subgame Mods")
425 for i=1,#game_mods,1 do
426 game_mods[i].typ = "game_mod"
427 game_mods[i].is_game_content = true
428 retval[#retval + 1] = game_mods[i]
431 if data.worldpath == nil then
435 --read world mod configuration
436 local filename = data.worldpath ..
437 DIR_DELIM .. "world.mt"
439 local worldfile = Settings(filename)
441 for key,value in pairs(worldfile:to_table()) do
442 if key:sub(1, 9) == "load_mod_" then
446 if retval[i].name == key and
447 not retval[i].is_modpack then
452 if element ~= nil then
453 element.enabled = core.is_yes(value)
455 core.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found")
463 --------------------------------------------------------------------------------
464 function modmgr.comparemod(elem1,elem2)
465 if elem1 == nil or elem2 == nil then
468 if elem1.name ~= elem2.name then
471 if elem1.is_modpack ~= elem2.is_modpack then
474 if elem1.typ ~= elem2.typ then
477 if elem1.modpack ~= elem2.modpack then
481 if elem1.path ~= elem2.path then
488 --------------------------------------------------------------------------------
489 function modmgr.mod_exists(basename)
491 if modmgr.global_mods == nil then
492 modmgr.refresh_globals()
495 if modmgr.global_mods:raw_index_by_uid(basename) > 0 then
502 --------------------------------------------------------------------------------
503 function modmgr.get_global_mod(idx)
505 if modmgr.global_mods == nil then
509 if idx == nil or idx < 1 or
510 idx > modmgr.global_mods:size() then
514 return modmgr.global_mods:get_list()[idx]
517 --------------------------------------------------------------------------------
518 function modmgr.refresh_globals()
519 modmgr.global_mods = filterlist.create(
520 modmgr.preparemodlist, --refresh
521 modmgr.comparemod, --compare
522 function(element,uid) --uid match
523 if element.name == uid then
530 modmgr.global_mods:add_sort_mechanism("alphabetic", sort_mod_list)
531 modmgr.global_mods:set_sortmode("alphabetic")
534 --------------------------------------------------------------------------------
535 function modmgr.identify_filetype(name)
537 if name:sub(-3):lower() == "zip" then
544 if name:sub(-6):lower() == "tar.gz" or
545 name:sub(-3):lower() == "tgz"then
552 if name:sub(-6):lower() == "tar.bz2" then
559 if name:sub(-2):lower() == "7z" then