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()
34 toadd.author = mod_conf.author
35 toadd.release = tonumber(mod_conf.release or "0")
39 if modpack ~= nil and modpack ~= "" then
40 toadd.modpack = modpack
42 local modpackfile = io.open(prefix .. "modpack.txt")
45 toadd.type = "modpack"
46 toadd.is_modpack = true
47 get_mods(prefix, retval, name)
54 --modmanager implementation
57 function pkgmgr.get_texture_packs()
58 local txtpath = core.get_texturepath()
59 local list = core.get_dir_list(txtpath, true)
62 local current_texture_path = core.settings:get("texture_path")
64 for _, item in ipairs(list) do
65 if item ~= "base" then
68 local path = txtpath .. DIR_DELIM .. item .. DIR_DELIM
69 if path == current_texture_path then
70 name = fgettext("$1 (Enabled)", name)
73 local conf = Settings(path .. "texture_pack.conf")
75 retval[#retval + 1] = {
77 author = conf:get("author"),
78 release = tonumber(conf:get("release") or "0"),
82 enabled = path == current_texture_path,
87 table.sort(retval, function(a, b)
88 return a.name > b.name
94 --------------------------------------------------------------------------------
95 function pkgmgr.extract(modfile)
96 if modfile.type == "zip" then
97 local tempfolder = os.tempfolder()
99 if tempfolder ~= nil and
100 tempfolder ~= "" then
101 core.create_dir(tempfolder)
102 if core.extract_zip(modfile.name,tempfolder) then
110 function pkgmgr.get_folder_type(path)
111 local testfile = io.open(path .. DIR_DELIM .. "init.lua","r")
112 if testfile ~= nil then
114 return { type = "mod", path = path }
117 testfile = io.open(path .. DIR_DELIM .. "modpack.txt","r")
118 if testfile ~= nil then
120 return { type = "modpack", path = path }
123 testfile = io.open(path .. DIR_DELIM .. "game.conf","r")
124 if testfile ~= nil then
126 return { type = "game", path = path }
129 testfile = io.open(path .. DIR_DELIM .. "texture_pack.conf","r")
130 if testfile ~= nil then
132 return { type = "txp", path = path }
138 -------------------------------------------------------------------------------
139 function pkgmgr.get_base_folder(temppath)
140 if temppath == nil then
141 return { type = "invalid", path = "" }
144 local ret = pkgmgr.get_folder_type(temppath)
149 local subdirs = core.get_dir_list(temppath, true)
150 if #subdirs == 1 then
151 ret = pkgmgr.get_folder_type(temppath .. DIR_DELIM .. subdirs[1])
155 return { type = "invalid", path = temppath .. DIR_DELIM .. subdirs[1] }
162 --------------------------------------------------------------------------------
163 function pkgmgr.isValidModname(modpath)
164 if modpath:find("-") ~= nil then
171 --------------------------------------------------------------------------------
172 function pkgmgr.parse_register_line(line)
173 local pos1 = line:find("\"")
176 pos2 = line:find("\"",pos1+1)
179 if pos1 ~= nil and pos2 ~= nil then
180 local item = line:sub(pos1+1,pos2-1)
184 local pos3 = item:find(":")
187 local retval = item:sub(1,pos3-1)
198 --------------------------------------------------------------------------------
199 function pkgmgr.parse_dofile_line(modpath,line)
200 local pos1 = line:find("\"")
203 pos2 = line:find("\"",pos1+1)
206 if pos1 ~= nil and pos2 ~= nil then
207 local filename = line:sub(pos1+1,pos2-1)
209 if filename ~= nil and
211 filename:find(".lua") then
212 return pkgmgr.identify_modname(modpath,filename)
218 --------------------------------------------------------------------------------
219 function pkgmgr.identify_modname(modpath,filename)
220 local testfile = io.open(modpath .. DIR_DELIM .. filename,"r")
221 if testfile ~= nil then
222 local line = testfile:read()
227 if line:find("minetest.register_tool") then
228 modname = pkgmgr.parse_register_line(line)
231 if line:find("minetest.register_craftitem") then
232 modname = pkgmgr.parse_register_line(line)
236 if line:find("minetest.register_node") then
237 modname = pkgmgr.parse_register_line(line)
240 if line:find("dofile") then
241 modname = pkgmgr.parse_dofile_line(modpath,line)
244 if modname ~= nil then
249 line = testfile:read()
256 --------------------------------------------------------------------------------
257 function pkgmgr.render_packagelist(render_list)
260 if render_list == nil then
261 if pkgmgr.global_mods == nil then
262 pkgmgr.refresh_globals()
264 render_list = pkgmgr.global_mods
267 local list = render_list:get_list()
268 local last_modpack = nil
270 for i, v in ipairs(list) do
273 local rawlist = render_list:get_raw_list()
274 color = mt_color_dark_green
276 for j = 1, #rawlist, 1 do
277 if rawlist[j].modpack == list[i].name and
278 rawlist[j].enabled ~= true then
279 -- Modpack not entirely enabled so showing as grey
280 color = mt_color_grey
284 elseif v.is_game_content or v.type == "game" then
285 color = mt_color_blue
286 elseif v.enabled or v.type == "txp" then
287 color = mt_color_green
290 retval[#retval + 1] = color
291 if v.modpack ~= nil or v.loc == "game" then
292 retval[#retval + 1] = "1"
294 retval[#retval + 1] = "0"
296 retval[#retval + 1] = core.formspec_escape(v.list_name or v.name)
299 return table.concat(retval, ",")
302 --------------------------------------------------------------------------------
303 function pkgmgr.get_dependencies(path)
308 local info = core.get_content_info(path)
309 return table.concat(info.depends or {}, ","), table.concat(info.optional_depends or {}, ",")
312 --------------------------------------------------------------------------------
313 function pkgmgr.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 = pkgmgr.find_by_gameid(worldconfig.id)
335 pkgmgr.get_game_mods(gamespec, worldconfig.game_mods)
340 --------------------------------------------------------------------------------
341 function pkgmgr.install_dir(type, path, basename, targetpath)
342 local basefolder = pkgmgr.get_base_folder(path)
344 -- There's no good way to detect a texture pack, so let's just assume
345 -- it's correct for now.
346 if type == "txp" then
347 if basefolder and basefolder.type ~= "invalid" and basefolder.type ~= "txp" then
348 return nil, fgettext("Unable to install a $1 as a texture pack", basefolder.type)
351 local from = basefolder and basefolder.path or path
353 core.delete_dir(targetpath)
354 core.create_dir(targetpath)
356 targetpath = core.get_texturepath() .. DIR_DELIM .. basename
358 if not core.copy_dir(from, targetpath) then
360 fgettext("Failed to install $1 to $2", basename, targetpath)
362 return targetpath, nil
364 elseif not basefolder then
365 return nil, fgettext("Unable to find a valid mod or modpack")
371 if basefolder.type == "modpack" then
372 if type ~= "mod" then
373 return nil, fgettext("Unable to install a modpack as a $1", type)
376 -- Get destination name for modpack
378 core.delete_dir(targetpath)
379 core.create_dir(targetpath)
381 local clean_path = nil
382 if basename ~= nil then
383 clean_path = "mp_" .. basename
385 if not clean_path then
386 clean_path = get_last_folder(cleanup_path(basefolder.path))
389 targetpath = core.get_modpath() .. DIR_DELIM .. clean_path
392 fgettext("Install Mod: unable to find suitable foldername for modpack $1",
396 elseif basefolder.type == "mod" then
397 if type ~= "mod" then
398 return nil, fgettext("Unable to install a mod as a $1", type)
402 core.delete_dir(targetpath)
403 core.create_dir(targetpath)
405 local targetfolder = basename
406 if targetfolder == nil then
407 targetfolder = pkgmgr.identify_modname(basefolder.path, "init.lua")
410 -- If heuristic failed try to use current foldername
411 if targetfolder == nil then
412 targetfolder = get_last_folder(basefolder.path)
415 if targetfolder ~= nil and pkgmgr.isValidModname(targetfolder) then
416 targetpath = core.get_modpath() .. DIR_DELIM .. targetfolder
418 return nil, fgettext("Install Mod: unable to find real modname for: $1", modfilename)
422 elseif basefolder.type == "game" then
423 if type ~= "game" then
424 return nil, fgettext("Unable to install a game as a $1", type)
428 core.delete_dir(targetpath)
429 core.create_dir(targetpath)
431 targetpath = core.get_gamepath() .. DIR_DELIM .. basename
436 if not core.copy_dir(basefolder.path, targetpath) then
438 fgettext("Failed to install $1 to $2", basename, targetpath)
441 pkgmgr.refresh_globals()
443 return targetpath, nil
446 --------------------------------------------------------------------------------
447 function pkgmgr.install(type, modfilename, basename, dest)
448 local archive_info = pkgmgr.identify_filetype(modfilename)
449 local path = pkgmgr.extract(archive_info)
453 fgettext("Install: file: \"$1\"", archive_info.name) .. "\n" ..
454 fgettext("Install: unsupported filetype \"$1\" or broken archive",
458 local targetpath, msg = pkgmgr.install_dir(type, path, basename, dest)
459 core.delete_dir(path)
460 return targetpath, msg
463 --------------------------------------------------------------------------------
464 function pkgmgr.preparemodlist(data)
467 local global_mods = {}
471 local modpath = core.get_modpath()
473 if modpath ~= nil and
475 get_mods(modpath,global_mods)
478 for i=1,#global_mods,1 do
479 global_mods[i].type = "mod"
480 global_mods[i].loc = "global"
481 retval[#retval + 1] = global_mods[i]
485 local gamespec = pkgmgr.find_by_gameid(data.gameid)
486 pkgmgr.get_game_mods(gamespec, game_mods)
488 if #game_mods > 0 then
490 retval[#retval + 1] = {
492 is_game_content = true,
493 name = fgettext("Subgame Mods")
497 for i=1,#game_mods,1 do
498 game_mods[i].type = "mod"
499 game_mods[i].loc = "game"
500 game_mods[i].is_game_content = true
501 retval[#retval + 1] = game_mods[i]
504 if data.worldpath == nil then
508 --read world mod configuration
509 local filename = data.worldpath ..
510 DIR_DELIM .. "world.mt"
512 local worldfile = Settings(filename)
514 for key,value in pairs(worldfile:to_table()) do
515 if key:sub(1, 9) == "load_mod_" then
519 if retval[i].name == key and
520 not retval[i].is_modpack then
525 if element ~= nil then
526 element.enabled = core.is_yes(value)
528 core.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found")
536 function pkgmgr.compare_package(a, b)
537 return a and b and a.name == b.name and a.path == b.path
540 --------------------------------------------------------------------------------
541 function pkgmgr.comparemod(elem1,elem2)
542 if elem1 == nil or elem2 == nil then
545 if elem1.name ~= elem2.name then
548 if elem1.is_modpack ~= elem2.is_modpack then
551 if elem1.type ~= elem2.type then
554 if elem1.modpack ~= elem2.modpack then
558 if elem1.path ~= elem2.path then
565 --------------------------------------------------------------------------------
566 function pkgmgr.mod_exists(basename)
568 if pkgmgr.global_mods == nil then
569 pkgmgr.refresh_globals()
572 if pkgmgr.global_mods:raw_index_by_uid(basename) > 0 then
579 --------------------------------------------------------------------------------
580 function pkgmgr.get_global_mod(idx)
582 if pkgmgr.global_mods == nil then
586 if idx == nil or idx < 1 or
587 idx > pkgmgr.global_mods:size() then
591 return pkgmgr.global_mods:get_list()[idx]
594 --------------------------------------------------------------------------------
595 function pkgmgr.refresh_globals()
596 local function is_equal(element,uid) --uid match
597 if element.name == uid then
601 pkgmgr.global_mods = filterlist.create(pkgmgr.preparemodlist,
602 pkgmgr.comparemod, is_equal, nil, {})
603 pkgmgr.global_mods:add_sort_mechanism("alphabetic", sort_mod_list)
604 pkgmgr.global_mods:set_sortmode("alphabetic")
607 --------------------------------------------------------------------------------
608 function pkgmgr.identify_filetype(name)
610 if name:sub(-3):lower() == "zip" then
617 if name:sub(-6):lower() == "tar.gz" or
618 name:sub(-3):lower() == "tgz"then
625 if name:sub(-6):lower() == "tar.bz2" then
632 if name:sub(-2):lower() == "7z" then
646 --------------------------------------------------------------------------------
647 function pkgmgr.find_by_gameid(gameid)
648 for i=1,#pkgmgr.games,1 do
649 if pkgmgr.games[i].id == gameid then
650 return pkgmgr.games[i], i
656 --------------------------------------------------------------------------------
657 function pkgmgr.get_game_mods(gamespec, retval)
658 if gamespec ~= nil and
659 gamespec.gamemods_path ~= nil and
660 gamespec.gamemods_path ~= "" then
661 get_mods(gamespec.gamemods_path, retval)
665 --------------------------------------------------------------------------------
666 function pkgmgr.get_game_modlist(gamespec)
669 pkgmgr.get_game_mods(gamespec, game_mods)
670 for i=1,#game_mods,1 do
674 retval = retval .. game_mods[i].name
679 --------------------------------------------------------------------------------
680 function pkgmgr.get_game(index)
681 if index > 0 and index <= #pkgmgr.games then
682 return pkgmgr.games[index]
688 --------------------------------------------------------------------------------
689 function pkgmgr.update_gamelist()
690 pkgmgr.games = core.get_games()
693 --------------------------------------------------------------------------------
694 function pkgmgr.gamelist()
696 if #pkgmgr.games > 0 then
697 retval = retval .. core.formspec_escape(pkgmgr.games[1].name)
699 for i=2,#pkgmgr.games,1 do
700 retval = retval .. "," .. core.formspec_escape(pkgmgr.games[i].name)
706 --------------------------------------------------------------------------------
708 --------------------------------------------------------------------------------
709 pkgmgr.update_gamelist()