--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--------------------------------------------------------------------------------
-function get_mods(path,retval,modpack)
- local mods = core.get_dir_list(path, true)
+local function get_last_folder(text,count)
+ local parts = text:split(DIR_DELIM)
- for _, name in ipairs(mods) do
- if name:sub(1, 1) ~= "." then
- local prefix = path .. DIR_DELIM .. name .. DIR_DELIM
- local toadd = {}
- retval[#retval + 1] = toadd
+ if count == nil then
+ return parts[#parts]
+ end
- local mod_conf = Settings(prefix .. "mod.conf"):to_table()
- if mod_conf.name then
- name = mod_conf.name
- end
+ local retval = ""
+ for i=1,count,1 do
+ retval = retval .. parts[#parts - (count-i)] .. DIR_DELIM
+ end
- toadd.name = name
- toadd.author = mod_conf.author
- toadd.release = tonumber(mod_conf.release or "0")
- toadd.path = prefix
- toadd.type = "mod"
+ return retval
+end
- if modpack ~= nil and modpack ~= "" then
- toadd.modpack = modpack
- else
- local modpackfile = io.open(prefix .. "modpack.txt")
- if modpackfile then
- modpackfile:close()
- toadd.type = "modpack"
- toadd.is_modpack = true
- get_mods(prefix, retval, name)
- end
- end
+local function cleanup_path(temppath)
+
+ local parts = temppath:split("-")
+ temppath = ""
+ for i=1,#parts,1 do
+ if temppath ~= "" then
+ temppath = temppath .. "_"
end
+ temppath = temppath .. parts[i]
end
-end
---modmanager implementation
-pkgmgr = {}
+ parts = temppath:split(".")
+ temppath = ""
+ for i=1,#parts,1 do
+ if temppath ~= "" then
+ temppath = temppath .. "_"
+ end
+ temppath = temppath .. parts[i]
+ end
-function pkgmgr.get_texture_packs()
- local txtpath = core.get_texturepath()
- local list = core.get_dir_list(txtpath, true)
- local retval = {}
+ parts = temppath:split("'")
+ temppath = ""
+ for i=1,#parts,1 do
+ if temppath ~= "" then
+ temppath = temppath .. ""
+ end
+ temppath = temppath .. parts[i]
+ end
+ parts = temppath:split(" ")
+ temppath = ""
+ for i=1,#parts,1 do
+ if temppath ~= "" then
+ temppath = temppath
+ end
+ temppath = temppath .. parts[i]
+ end
+
+ return temppath
+end
+
+local function load_texture_packs(txtpath, retval)
+ local list = core.get_dir_list(txtpath, true)
local current_texture_path = core.settings:get("texture_path")
for _, item in ipairs(list) do
retval[#retval + 1] = {
name = item,
author = conf:get("author"),
- release = tonumber(conf:get("release") or "0"),
+ release = tonumber(conf:get("release")) or 0,
list_name = name,
type = "txp",
path = path,
}
end
end
+end
+
+function get_mods(path,retval,modpack)
+ local mods = core.get_dir_list(path, true)
+
+ for _, name in ipairs(mods) do
+ if name:sub(1, 1) ~= "." then
+ local prefix = path .. DIR_DELIM .. name
+ local toadd = {
+ dir_name = name,
+ parent_dir = path,
+ }
+ retval[#retval + 1] = toadd
+
+ -- Get config file
+ local mod_conf
+ local modpack_conf = io.open(prefix .. DIR_DELIM .. "modpack.conf")
+ if modpack_conf then
+ toadd.is_modpack = true
+ modpack_conf:close()
+
+ mod_conf = Settings(prefix .. DIR_DELIM .. "modpack.conf"):to_table()
+ if mod_conf.name then
+ name = mod_conf.name
+ toadd.is_name_explicit = true
+ end
+ else
+ mod_conf = Settings(prefix .. DIR_DELIM .. "mod.conf"):to_table()
+ if mod_conf.name then
+ name = mod_conf.name
+ toadd.is_name_explicit = true
+ end
+ end
+
+ -- Read from config
+ toadd.name = name
+ toadd.author = mod_conf.author
+ toadd.release = tonumber(mod_conf.release) or 0
+ toadd.path = prefix
+ toadd.type = "mod"
+
+ -- Check modpack.txt
+ -- Note: modpack.conf is already checked above
+ local modpackfile = io.open(prefix .. DIR_DELIM .. "modpack.txt")
+ if modpackfile then
+ modpackfile:close()
+ toadd.is_modpack = true
+ end
+
+ -- Deal with modpack contents
+ if modpack and modpack ~= "" then
+ toadd.modpack = modpack
+ elseif toadd.is_modpack then
+ toadd.type = "modpack"
+ toadd.is_modpack = true
+ get_mods(prefix, retval, name)
+ end
+ end
+ end
+end
+
+--modmanager implementation
+pkgmgr = {}
+
+function pkgmgr.get_texture_packs()
+ local txtpath = core.get_texturepath()
+ local txtpath_system = core.get_texturepath_share()
+ local retval = {}
+
+ load_texture_packs(txtpath, retval)
+ -- on portable versions these two paths coincide. It avoids loading the path twice
+ if txtpath ~= txtpath_system then
+ load_texture_packs(txtpath_system, retval)
+ end
table.sort(retval, function(a, b)
return a.name > b.name
end
--------------------------------------------------------------------------------
-function pkgmgr.extract(modfile)
- if modfile.type == "zip" then
- local tempfolder = os.tempfolder()
-
- if tempfolder ~= nil and
- tempfolder ~= "" then
- core.create_dir(tempfolder)
- if core.extract_zip(modfile.name,tempfolder) then
- return tempfolder
- end
- end
- end
- return nil
-end
-
function pkgmgr.get_folder_type(path)
local testfile = io.open(path .. DIR_DELIM .. "init.lua","r")
if testfile ~= nil then
return { type = "mod", path = path }
end
+ testfile = io.open(path .. DIR_DELIM .. "modpack.conf","r")
+ if testfile ~= nil then
+ testfile:close()
+ return { type = "modpack", path = path }
+ end
+
testfile = io.open(path .. DIR_DELIM .. "modpack.txt","r")
if testfile ~= nil then
testfile:close()
end
--------------------------------------------------------------------------------
function pkgmgr.render_packagelist(render_list)
- local retval = ""
-
- if render_list == nil then
- if pkgmgr.global_mods == nil then
+ if not render_list then
+ if not pkgmgr.global_mods then
pkgmgr.refresh_globals()
end
render_list = pkgmgr.global_mods
end
local list = render_list:get_list()
- local last_modpack = nil
local retval = {}
for i, v in ipairs(list) do
local color = ""
for j = 1, #rawlist, 1 do
if rawlist[j].modpack == list[i].name and
- rawlist[j].enabled ~= true then
+ not rawlist[j].enabled then
-- Modpack not entirely enabled so showing as grey
color = mt_color_grey
break
--------------------------------------------------------------------------------
function pkgmgr.get_dependencies(path)
if path == nil then
- return "", ""
+ return {}, {}
end
local info = core.get_content_info(path)
- return table.concat(info.depends or {}, ","), table.concat(info.optional_depends or {}, ",")
+ return info.depends or {}, info.optional_depends or {}
end
----------- tests whether all of the mods in the modpack are enabled -----------
return true
end
----------- toggles or en/disables a mod or modpack -----------------------------
+---------- toggles or en/disables a mod or modpack and its dependencies --------
+local function toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod)
+ if not mod.is_modpack then
+ -- Toggle or en/disable the mod
+ if toset == nil then
+ toset = not mod.enabled
+ end
+ if mod.enabled ~= toset then
+ mod.enabled = toset
+ toggled_mods[#toggled_mods+1] = mod.name
+ end
+ if toset then
+ -- Mark this mod for recursive dependency traversal
+ enabled_mods[mod.name] = true
+ end
+ else
+ -- Toggle or en/disable every mod in the modpack,
+ -- interleaved unsupported
+ for i = 1, #list do
+ if list[i].modpack == mod.name then
+ toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, list[i])
+ end
+ end
+ end
+end
+
function pkgmgr.enable_mod(this, toset)
- local mod = this.data.list:get_list()[this.data.selected_mod]
+ local list = this.data.list:get_list()
+ local mod = list[this.data.selected_mod]
- -- game mods can't be enabled or disabled
+ -- Game mods can't be enabled or disabled
if mod.is_game_content then
return
end
- -- toggle or en/disable the mod
- if not mod.is_modpack then
- if toset == nil then
- mod.enabled = not mod.enabled
- else
- mod.enabled = toset
- end
+ local toggled_mods = {}
+ local enabled_mods = {}
+ toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod)
+ toset = mod.enabled -- Update if toggled
+
+ if not toset then
+ -- Mod(s) were disabled, so no dependencies need to be enabled
+ table.sort(toggled_mods)
+ core.log("info", "Following mods were disabled: " ..
+ table.concat(toggled_mods, ", "))
return
end
- -- toggle or en/disable every mod in the modpack, interleaved unsupported
- local list = this.data.list:get_raw_list()
- for i = 1, #list do
- if list[i].modpack == mod.name then
- if toset == nil then
- toset = not list[i].enabled
+ -- Enable mods' depends after activation
+
+ -- Make a list of mod ids indexed by their names
+ local mod_ids = {}
+ for id, mod2 in pairs(list) do
+ if mod2.type == "mod" and not mod2.is_modpack then
+ mod_ids[mod2.name] = id
+ end
+ end
+
+ -- to_enable is used as a DFS stack with sp as stack pointer
+ local to_enable = {}
+ local sp = 0
+ for name in pairs(enabled_mods) do
+ local depends = pkgmgr.get_dependencies(list[mod_ids[name]].path)
+ for i = 1, #depends do
+ local dependency_name = depends[i]
+ if not enabled_mods[dependency_name] then
+ sp = sp+1
+ to_enable[sp] = dependency_name
+ end
+ end
+ end
+ -- If sp is 0, every dependency is already activated
+ while sp > 0 do
+ local name = to_enable[sp]
+ sp = sp-1
+
+ if not enabled_mods[name] then
+ enabled_mods[name] = true
+ local mod_to_enable = list[mod_ids[name]]
+ if not mod_to_enable then
+ core.log("warning", "Mod dependency \"" .. name ..
+ "\" not found!")
+ else
+ if mod_to_enable.enabled == false then
+ mod_to_enable.enabled = true
+ toggled_mods[#toggled_mods+1] = mod_to_enable.name
+ end
+ -- Push the dependencies of the dependency onto the stack
+ local depends = pkgmgr.get_dependencies(mod_to_enable.path)
+ for i = 1, #depends do
+ if not enabled_mods[name] then
+ sp = sp+1
+ to_enable[sp] = depends[i]
+ end
+ end
end
- list[i].enabled = toset
end
end
+
+ -- Log the list of enabled mods
+ table.sort(toggled_mods)
+ core.log("info", "Following mods were enabled: " ..
+ table.concat(toggled_mods, ", "))
end
--------------------------------------------------------------------------------
if key == "gameid" then
worldconfig.id = value
elseif key:sub(0, 9) == "load_mod_" then
- worldconfig.global_mods[key] = core.is_yes(value)
+ -- Compatibility: Check against "nil" which was erroneously used
+ -- as value for fresh configured worlds
+ worldconfig.global_mods[key] = value ~= "false" and value ~= "nil"
+ and value
else
worldconfig[key] = value
end
local from = basefolder and basefolder.path or path
if targetpath then
core.delete_dir(targetpath)
- core.create_dir(targetpath)
else
targetpath = core.get_texturepath() .. DIR_DELIM .. basename
end
- if not core.copy_dir(from, targetpath) then
+ if not core.copy_dir(from, targetpath, false) then
return nil,
fgettext("Failed to install $1 to $2", basename, targetpath)
end
-- Get destination name for modpack
if targetpath then
core.delete_dir(targetpath)
- core.create_dir(targetpath)
else
local clean_path = nil
if basename ~= nil then
- clean_path = "mp_" .. basename
+ clean_path = basename
end
if not clean_path then
clean_path = get_last_folder(cleanup_path(basefolder.path))
targetpath = core.get_modpath() .. DIR_DELIM .. clean_path
else
return nil,
- fgettext("Install Mod: unable to find suitable foldername for modpack $1",
- modfilename)
+ fgettext("Install Mod: Unable to find suitable folder name for modpack $1",
+ path)
end
end
elseif basefolder.type == "mod" then
if targetpath then
core.delete_dir(targetpath)
- core.create_dir(targetpath)
else
local targetfolder = basename
if targetfolder == nil then
if targetfolder ~= nil and pkgmgr.isValidModname(targetfolder) then
targetpath = core.get_modpath() .. DIR_DELIM .. targetfolder
else
- return nil, fgettext("Install Mod: unable to find real modname for: $1", modfilename)
+ return nil, fgettext("Install Mod: Unable to find real mod name for: $1", path)
end
end
if targetpath then
core.delete_dir(targetpath)
- core.create_dir(targetpath)
else
targetpath = core.get_gamepath() .. DIR_DELIM .. basename
end
end
-- Copy it
- if not core.copy_dir(basefolder.path, targetpath) then
+ if not core.copy_dir(basefolder.path, targetpath, false) then
return nil,
fgettext("Failed to install $1 to $2", basename, targetpath)
end
- pkgmgr.refresh_globals()
-
- return targetpath, nil
-end
-
---------------------------------------------------------------------------------
-function pkgmgr.install(type, modfilename, basename, dest)
- local archive_info = pkgmgr.identify_filetype(modfilename)
- local path = pkgmgr.extract(archive_info)
-
- if path == nil then
- return nil,
- fgettext("Install: file: \"$1\"", archive_info.name) .. "\n" ..
- fgettext("Install: unsupported filetype \"$1\" or broken archive",
- archive_info.type)
+ if basefolder.type == "game" then
+ pkgmgr.update_gamelist()
+ else
+ pkgmgr.refresh_globals()
end
- local targetpath, msg = pkgmgr.install_dir(type, path, basename, dest)
- core.delete_dir(path)
- return targetpath, msg
+ return targetpath, nil
end
--------------------------------------------------------------------------------
local game_mods = {}
--read global mods
- local modpath = core.get_modpath()
-
- if modpath ~= nil and
- modpath ~= "" then
- get_mods(modpath,global_mods)
+ local modpaths = core.get_modpaths()
+ for _, modpath in ipairs(modpaths) do
+ get_mods(modpath, global_mods)
end
for i=1,#global_mods,1 do
retval[#retval + 1] = {
type = "game",
is_game_content = true,
- name = fgettext("Subgame Mods")
+ name = fgettext("$1 mods", gamespec.name),
+ path = gamespec.path
}
end
end
end
if element ~= nil then
- element.enabled = core.is_yes(value)
+ element.enabled = value ~= "false" and value ~= "nil" and value
else
core.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found")
end
pkgmgr.global_mods:set_sortmode("alphabetic")
end
---------------------------------------------------------------------------------
-function pkgmgr.identify_filetype(name)
-
- if name:sub(-3):lower() == "zip" then
- return {
- name = name,
- type = "zip"
- }
- end
-
- if name:sub(-6):lower() == "tar.gz" or
- name:sub(-3):lower() == "tgz"then
- return {
- name = name,
- type = "tgz"
- }
- end
-
- if name:sub(-6):lower() == "tar.bz2" then
- return {
- name = name,
- type = "tbz"
- }
- end
-
- if name:sub(-2):lower() == "7z" then
- return {
- name = name,
- type = "7z"
- }
- end
-
- return {
- name = name,
- type = "ukn"
- }
-end
-
-
--------------------------------------------------------------------------------
function pkgmgr.find_by_gameid(gameid)
for i=1,#pkgmgr.games,1 do