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
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 .. DIR_DELIM .. "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 ----------- tests whether all of the mods in the modpack are enabled -----------
313 function pkgmgr.is_modpack_entirely_enabled(data, name)
314 local rawlist = data.list:get_raw_list()
315 for j = 1, #rawlist do
316 if rawlist[j].modpack == name and not rawlist[j].enabled then
323 ---------- toggles or en/disables a mod or modpack -----------------------------
324 function pkgmgr.enable_mod(this, toset)
325 local mod = this.data.list:get_list()[this.data.selected_mod]
327 -- game mods can't be enabled or disabled
328 if mod.is_game_content then
332 -- toggle or en/disable the mod
333 if not mod.is_modpack then
335 mod.enabled = not mod.enabled
342 -- toggle or en/disable every mod in the modpack, interleaved unsupported
343 local list = this.data.list:get_raw_list()
345 if list[i].modpack == mod.name then
347 toset = not list[i].enabled
349 list[i].enabled = toset
354 --------------------------------------------------------------------------------
355 function pkgmgr.get_worldconfig(worldpath)
356 local filename = worldpath ..
357 DIR_DELIM .. "world.mt"
359 local worldfile = Settings(filename)
361 local worldconfig = {}
362 worldconfig.global_mods = {}
363 worldconfig.game_mods = {}
365 for key,value in pairs(worldfile:to_table()) do
366 if key == "gameid" then
367 worldconfig.id = value
368 elseif key:sub(0, 9) == "load_mod_" then
369 worldconfig.global_mods[key] = core.is_yes(value)
371 worldconfig[key] = value
376 local gamespec = pkgmgr.find_by_gameid(worldconfig.id)
377 pkgmgr.get_game_mods(gamespec, worldconfig.game_mods)
382 --------------------------------------------------------------------------------
383 function pkgmgr.install_dir(type, path, basename, targetpath)
384 local basefolder = pkgmgr.get_base_folder(path)
386 -- There's no good way to detect a texture pack, so let's just assume
387 -- it's correct for now.
388 if type == "txp" then
389 if basefolder and basefolder.type ~= "invalid" and basefolder.type ~= "txp" then
390 return nil, fgettext("Unable to install a $1 as a texture pack", basefolder.type)
393 local from = basefolder and basefolder.path or path
395 core.delete_dir(targetpath)
396 core.create_dir(targetpath)
398 targetpath = core.get_texturepath() .. DIR_DELIM .. basename
400 if not core.copy_dir(from, targetpath) then
402 fgettext("Failed to install $1 to $2", basename, targetpath)
404 return targetpath, nil
406 elseif not basefolder then
407 return nil, fgettext("Unable to find a valid mod or modpack")
413 if basefolder.type == "modpack" then
414 if type ~= "mod" then
415 return nil, fgettext("Unable to install a modpack as a $1", type)
418 -- Get destination name for modpack
420 core.delete_dir(targetpath)
421 core.create_dir(targetpath)
423 local clean_path = nil
424 if basename ~= nil then
425 clean_path = "mp_" .. basename
427 if not clean_path then
428 clean_path = get_last_folder(cleanup_path(basefolder.path))
431 targetpath = core.get_modpath() .. DIR_DELIM .. clean_path
434 fgettext("Install Mod: Unable to find suitable folder name for modpack $1",
438 elseif basefolder.type == "mod" then
439 if type ~= "mod" then
440 return nil, fgettext("Unable to install a mod as a $1", type)
444 core.delete_dir(targetpath)
445 core.create_dir(targetpath)
447 local targetfolder = basename
448 if targetfolder == nil then
449 targetfolder = pkgmgr.identify_modname(basefolder.path, "init.lua")
452 -- If heuristic failed try to use current foldername
453 if targetfolder == nil then
454 targetfolder = get_last_folder(basefolder.path)
457 if targetfolder ~= nil and pkgmgr.isValidModname(targetfolder) then
458 targetpath = core.get_modpath() .. DIR_DELIM .. targetfolder
460 return nil, fgettext("Install Mod: Unable to find real mod name for: $1", modfilename)
464 elseif basefolder.type == "game" then
465 if type ~= "game" then
466 return nil, fgettext("Unable to install a game as a $1", type)
470 core.delete_dir(targetpath)
471 core.create_dir(targetpath)
473 targetpath = core.get_gamepath() .. DIR_DELIM .. basename
478 if not core.copy_dir(basefolder.path, targetpath) then
480 fgettext("Failed to install $1 to $2", basename, targetpath)
483 pkgmgr.refresh_globals()
485 return targetpath, nil
488 --------------------------------------------------------------------------------
489 function pkgmgr.install(type, modfilename, basename, dest)
490 local archive_info = pkgmgr.identify_filetype(modfilename)
491 local path = pkgmgr.extract(archive_info)
495 fgettext("Install: file: \"$1\"", archive_info.name) .. "\n" ..
496 fgettext("Install: Unsupported file type \"$1\" or broken archive",
500 local targetpath, msg = pkgmgr.install_dir(type, path, basename, dest)
501 core.delete_dir(path)
502 return targetpath, msg
505 --------------------------------------------------------------------------------
506 function pkgmgr.preparemodlist(data)
509 local global_mods = {}
513 local modpath = core.get_modpath()
515 if modpath ~= nil and
517 get_mods(modpath,global_mods)
520 for i=1,#global_mods,1 do
521 global_mods[i].type = "mod"
522 global_mods[i].loc = "global"
523 retval[#retval + 1] = global_mods[i]
527 local gamespec = pkgmgr.find_by_gameid(data.gameid)
528 pkgmgr.get_game_mods(gamespec, game_mods)
530 if #game_mods > 0 then
532 retval[#retval + 1] = {
534 is_game_content = true,
535 name = fgettext(gamespec.name .. " mods"),
540 for i=1,#game_mods,1 do
541 game_mods[i].type = "mod"
542 game_mods[i].loc = "game"
543 game_mods[i].is_game_content = true
544 retval[#retval + 1] = game_mods[i]
547 if data.worldpath == nil then
551 --read world mod configuration
552 local filename = data.worldpath ..
553 DIR_DELIM .. "world.mt"
555 local worldfile = Settings(filename)
557 for key,value in pairs(worldfile:to_table()) do
558 if key:sub(1, 9) == "load_mod_" then
562 if retval[i].name == key and
563 not retval[i].is_modpack then
568 if element ~= nil then
569 element.enabled = core.is_yes(value)
571 core.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found")
579 function pkgmgr.compare_package(a, b)
580 return a and b and a.name == b.name and a.path == b.path
583 --------------------------------------------------------------------------------
584 function pkgmgr.comparemod(elem1,elem2)
585 if elem1 == nil or elem2 == nil then
588 if elem1.name ~= elem2.name then
591 if elem1.is_modpack ~= elem2.is_modpack then
594 if elem1.type ~= elem2.type then
597 if elem1.modpack ~= elem2.modpack then
601 if elem1.path ~= elem2.path then
608 --------------------------------------------------------------------------------
609 function pkgmgr.mod_exists(basename)
611 if pkgmgr.global_mods == nil then
612 pkgmgr.refresh_globals()
615 if pkgmgr.global_mods:raw_index_by_uid(basename) > 0 then
622 --------------------------------------------------------------------------------
623 function pkgmgr.get_global_mod(idx)
625 if pkgmgr.global_mods == nil then
629 if idx == nil or idx < 1 or
630 idx > pkgmgr.global_mods:size() then
634 return pkgmgr.global_mods:get_list()[idx]
637 --------------------------------------------------------------------------------
638 function pkgmgr.refresh_globals()
639 local function is_equal(element,uid) --uid match
640 if element.name == uid then
644 pkgmgr.global_mods = filterlist.create(pkgmgr.preparemodlist,
645 pkgmgr.comparemod, is_equal, nil, {})
646 pkgmgr.global_mods:add_sort_mechanism("alphabetic", sort_mod_list)
647 pkgmgr.global_mods:set_sortmode("alphabetic")
650 --------------------------------------------------------------------------------
651 function pkgmgr.identify_filetype(name)
653 if name:sub(-3):lower() == "zip" then
660 if name:sub(-6):lower() == "tar.gz" or
661 name:sub(-3):lower() == "tgz"then
668 if name:sub(-6):lower() == "tar.bz2" then
675 if name:sub(-2):lower() == "7z" then
689 --------------------------------------------------------------------------------
690 function pkgmgr.find_by_gameid(gameid)
691 for i=1,#pkgmgr.games,1 do
692 if pkgmgr.games[i].id == gameid then
693 return pkgmgr.games[i], i
699 --------------------------------------------------------------------------------
700 function pkgmgr.get_game_mods(gamespec, retval)
701 if gamespec ~= nil and
702 gamespec.gamemods_path ~= nil and
703 gamespec.gamemods_path ~= "" then
704 get_mods(gamespec.gamemods_path, retval)
708 --------------------------------------------------------------------------------
709 function pkgmgr.get_game_modlist(gamespec)
712 pkgmgr.get_game_mods(gamespec, game_mods)
713 for i=1,#game_mods,1 do
717 retval = retval .. game_mods[i].name
722 --------------------------------------------------------------------------------
723 function pkgmgr.get_game(index)
724 if index > 0 and index <= #pkgmgr.games then
725 return pkgmgr.games[index]
731 --------------------------------------------------------------------------------
732 function pkgmgr.update_gamelist()
733 pkgmgr.games = core.get_games()
736 --------------------------------------------------------------------------------
737 function pkgmgr.gamelist()
739 if #pkgmgr.games > 0 then
740 retval = retval .. core.formspec_escape(pkgmgr.games[1].name)
742 for i=2,#pkgmgr.games,1 do
743 retval = retval .. "," .. core.formspec_escape(pkgmgr.games[i].name)
749 --------------------------------------------------------------------------------
751 --------------------------------------------------------------------------------
752 pkgmgr.update_gamelist()