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 local in_game_mods = false
242 for i, v in ipairs(list) do
243 if v.typ == "game_mod" and not in_game_mods then
245 retval[#retval + 1] = mt_color_blue
246 retval[#retval + 1] = "0"
247 retval[#retval + 1] = fgettext("Subgame Mods")
252 local rawlist = render_list:get_raw_list()
253 color = mt_color_dark_green
255 for j = 1, #rawlist, 1 do
256 if rawlist[j].modpack == list[i].name and
257 rawlist[j].enabled ~= true then
258 -- Modpack not entirely enabled so showing as grey
259 color = mt_color_grey
263 elseif v.typ == "game_mod" then
264 color = mt_color_blue
265 elseif v.enabled then
266 color = mt_color_green
269 retval[#retval + 1] = color
270 if v.modpack ~= nil or v.typ == "game_mod" then
271 retval[#retval + 1] = "1"
273 retval[#retval + 1] = "0"
275 retval[#retval + 1] = core.formspec_escape(v.name)
278 return table.concat(retval, ",")
281 --------------------------------------------------------------------------------
282 function modmgr.get_dependencies(modfolder)
283 local toadd_hard = ""
284 local toadd_soft = ""
285 if modfolder ~= nil then
286 local filename = modfolder ..
287 DIR_DELIM .. "depends.txt"
289 local hard_dependencies = {}
290 local soft_dependencies = {}
291 local dependencyfile = io.open(filename,"r")
292 if dependencyfile then
293 local dependency = dependencyfile:read("*l")
295 dependency = dependency:gsub("\r", "")
296 if string.sub(dependency, -1, -1) == "?" then
297 table.insert(soft_dependencies, string.sub(dependency, 1, -2))
299 table.insert(hard_dependencies, dependency)
301 dependency = dependencyfile:read()
303 dependencyfile:close()
305 toadd_hard = table.concat(hard_dependencies, ",")
306 toadd_soft = table.concat(soft_dependencies, ",")
309 return toadd_hard, toadd_soft
312 --------------------------------------------------------------------------------
313 function modmgr.get_worldconfig(worldpath)
314 local filename = worldpath ..
315 DIR_DELIM .. "world.mt"
317 local worldfile = Settings(filename)
319 local worldconfig = {}
320 worldconfig.global_mods = {}
321 worldconfig.game_mods = {}
323 for key,value in pairs(worldfile:to_table()) do
324 if key == "gameid" then
325 worldconfig.id = value
326 elseif key:sub(0, 9) == "load_mod_" then
327 worldconfig.global_mods[key] = core.is_yes(value)
329 worldconfig[key] = value
334 local gamespec = gamemgr.find_by_gameid(worldconfig.id)
335 gamemgr.get_game_mods(gamespec, worldconfig.game_mods)
340 --------------------------------------------------------------------------------
341 function modmgr.installmod(modfilename,basename)
342 local modfile = modmgr.identify_filetype(modfilename)
343 local modpath = modmgr.extract(modfile)
345 if modpath == nil then
346 gamedata.errormessage = fgettext("Install Mod: file: \"$1\"", modfile.name) ..
347 fgettext("\nInstall Mod: unsupported filetype \"$1\" or broken archive", modfile.type)
351 local basefolder = modmgr.getbasefolder(modpath)
353 if basefolder.type == "modpack" then
354 local clean_path = nil
356 if basename ~= nil then
357 clean_path = "mp_" .. basename
360 if clean_path == nil then
361 clean_path = get_last_folder(cleanup_path(basefolder.path))
364 if clean_path ~= nil then
365 local targetpath = core.get_modpath() .. DIR_DELIM .. clean_path
366 if not core.copy_dir(basefolder.path,targetpath) then
367 gamedata.errormessage = fgettext("Failed to install $1 to $2", basename, targetpath)
370 gamedata.errormessage = fgettext("Install Mod: unable to find suitable foldername for modpack $1", modfilename)
374 if basefolder.type == "mod" then
375 local targetfolder = basename
377 if targetfolder == nil then
378 targetfolder = modmgr.identify_modname(basefolder.path,"init.lua")
381 --if heuristic failed try to use current foldername
382 if targetfolder == nil then
383 targetfolder = get_last_folder(basefolder.path)
386 if targetfolder ~= nil and modmgr.isValidModname(targetfolder) then
387 local targetpath = core.get_modpath() .. DIR_DELIM .. targetfolder
388 core.copy_dir(basefolder.path,targetpath)
390 gamedata.errormessage = fgettext("Install Mod: unable to find real modname for: $1", modfilename)
394 core.delete_dir(modpath)
396 modmgr.refresh_globals()
400 --------------------------------------------------------------------------------
401 function modmgr.preparemodlist(data)
404 local global_mods = {}
408 local modpath = core.get_modpath()
410 if modpath ~= nil and
412 get_mods(modpath,global_mods)
415 for i=1,#global_mods,1 do
416 global_mods[i].typ = "global_mod"
417 retval[#retval + 1] = global_mods[i]
421 local gamespec = gamemgr.find_by_gameid(data.gameid)
422 gamemgr.get_game_mods(gamespec, game_mods)
424 for i=1,#game_mods,1 do
425 game_mods[i].typ = "game_mod"
426 retval[#retval + 1] = game_mods[i]
429 if data.worldpath == nil then
433 --read world mod configuration
434 local filename = data.worldpath ..
435 DIR_DELIM .. "world.mt"
437 local worldfile = Settings(filename)
439 for key,value in pairs(worldfile:to_table()) do
440 if key:sub(1, 9) == "load_mod_" then
444 if retval[i].name == key and
445 not retval[i].is_modpack then
450 if element ~= nil then
451 element.enabled = core.is_yes(value)
453 core.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found")
461 --------------------------------------------------------------------------------
462 function modmgr.comparemod(elem1,elem2)
463 if elem1 == nil or elem2 == nil then
466 if elem1.name ~= elem2.name then
469 if elem1.is_modpack ~= elem2.is_modpack then
472 if elem1.typ ~= elem2.typ then
475 if elem1.modpack ~= elem2.modpack then
479 if elem1.path ~= elem2.path then
486 --------------------------------------------------------------------------------
487 function modmgr.mod_exists(basename)
489 if modmgr.global_mods == nil then
490 modmgr.refresh_globals()
493 if modmgr.global_mods:raw_index_by_uid(basename) > 0 then
500 --------------------------------------------------------------------------------
501 function modmgr.get_global_mod(idx)
503 if modmgr.global_mods == nil then
507 if idx == nil or idx < 1 or
508 idx > modmgr.global_mods:size() then
512 return modmgr.global_mods:get_list()[idx]
515 --------------------------------------------------------------------------------
516 function modmgr.refresh_globals()
517 modmgr.global_mods = filterlist.create(
518 modmgr.preparemodlist, --refresh
519 modmgr.comparemod, --compare
520 function(element,uid) --uid match
521 if element.name == uid then
528 modmgr.global_mods:add_sort_mechanism("alphabetic", sort_mod_list)
529 modmgr.global_mods:set_sortmode("alphabetic")
532 --------------------------------------------------------------------------------
533 function modmgr.identify_filetype(name)
535 if name:sub(-3):lower() == "zip" then
542 if name:sub(-6):lower() == "tar.gz" or
543 name:sub(-3):lower() == "tgz"then
550 if name:sub(-6):lower() == "tar.bz2" then
557 if name:sub(-2):lower() == "7z" then