]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - builtin/mainmenu/pkgmgr.lua
Move instead of copy during content install if possible
[dragonfireclient.git] / builtin / mainmenu / pkgmgr.lua
index dee4dabbbb74e38d08300a022c2b374c06dd8d40..e83a93c91ca72bf53b33670ac21d8a4c7a19d3d1 100644 (file)
 --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 --------------------------------------------------------------------------------
+local function get_last_folder(text,count)
+       local parts = text:split(DIR_DELIM)
+
+       if count == nil then
+               return parts[#parts]
+       end
+
+       local retval = ""
+       for i=1,count,1 do
+               retval = retval .. parts[#parts - (count-i)] .. DIR_DELIM
+       end
+
+       return retval
+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
+
+       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
+
+       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
+               if item ~= "base" then
+                       local name = item
+
+                       local path = txtpath .. DIR_DELIM .. item .. DIR_DELIM
+                       if path == current_texture_path then
+                               name = fgettext("$1 (Enabled)", name)
+                       end
+
+                       local conf = Settings(path .. "texture_pack.conf")
+
+                       retval[#retval + 1] = {
+                               name = item,
+                               author = conf:get("author"),
+                               release = tonumber(conf:get("release")) or 0,
+                               list_name = name,
+                               type = "txp",
+                               path = path,
+                               enabled = path == current_texture_path,
+                       }
+               end
+       end
+end
+
 function get_mods(path,retval,modpack)
        local mods = core.get_dir_list(path, true)
 
@@ -51,12 +135,12 @@ function get_mods(path,retval,modpack)
                        -- Read from config
                        toadd.name = name
                        toadd.author = mod_conf.author
-                       toadd.release = tonumber(mod_conf.release or "0")
+                       toadd.release = tonumber(mod_conf.release) or 0
                        toadd.path = prefix
                        toadd.type = "mod"
 
                        -- Check modpack.txt
-                       --  Note: modpack.conf is already checked above
+                       -- Note: modpack.conf is already checked above
                        local modpackfile = io.open(prefix .. DIR_DELIM .. "modpack.txt")
                        if modpackfile then
                                modpackfile:close()
@@ -80,32 +164,13 @@ pkgmgr = {}
 
 function pkgmgr.get_texture_packs()
        local txtpath = core.get_texturepath()
-       local list = core.get_dir_list(txtpath, true)
+       local txtpath_system = core.get_texturepath_share()
        local retval = {}
 
-       local current_texture_path = core.settings:get("texture_path")
-
-       for _, item in ipairs(list) do
-               if item ~= "base" then
-                       local name = item
-
-                       local path = txtpath .. DIR_DELIM .. item .. DIR_DELIM
-                       if path == current_texture_path then
-                               name = fgettext("$1 (Enabled)", name)
-                       end
-
-                       local conf = Settings(path .. "texture_pack.conf")
-
-                       retval[#retval + 1] = {
-                               name = item,
-                               author = conf:get("author"),
-                               release = tonumber(conf:get("release") or "0"),
-                               list_name = name,
-                               type = "txp",
-                               path = path,
-                               enabled = path == current_texture_path,
-                       }
-               end
+       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)
@@ -116,21 +181,6 @@ function pkgmgr.get_texture_packs()
 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
@@ -285,17 +335,14 @@ function pkgmgr.identify_modname(modpath,filename)
 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 = ""
@@ -350,35 +397,108 @@ function pkgmgr.is_modpack_entirely_enabled(data, name)
        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)
+
+       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
-                       list[i].enabled = toset
                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
+               end
+       end
+
+       -- Log the list of enabled mods
+       table.sort(toggled_mods)
+       core.log("info", "Following mods were enabled: " ..
+               table.concat(toggled_mods, ", "))
 end
 
 --------------------------------------------------------------------------------
@@ -426,11 +546,10 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
                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
@@ -451,7 +570,6 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
                -- 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
@@ -465,7 +583,7 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
                        else
                                return nil,
                                        fgettext("Install Mod: Unable to find suitable folder name for modpack $1",
-                                       modfilename)
+                                       path)
                        end
                end
        elseif basefolder.type == "mod" then
@@ -475,7 +593,6 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
 
                if targetpath then
                        core.delete_dir(targetpath)
-                       core.create_dir(targetpath)
                else
                        local targetfolder = basename
                        if targetfolder == nil then
@@ -490,7 +607,7 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
                        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 mod name for: $1", modfilename)
+                               return nil, fgettext("Install Mod: Unable to find real mod name for: $1", path)
                        end
                end
 
@@ -501,14 +618,13 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
 
                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
@@ -522,23 +638,6 @@ function pkgmgr.install_dir(type, path, basename, targetpath)
        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 file type \"$1\" or broken archive",
-                               archive_info.type)
-       end
-
-       local targetpath, msg = pkgmgr.install_dir(type, path, basename, dest)
-       core.delete_dir(path)
-       return targetpath, msg
-end
-
 --------------------------------------------------------------------------------
 function pkgmgr.preparemodlist(data)
        local retval = {}
@@ -547,11 +646,9 @@ function pkgmgr.preparemodlist(data)
        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
@@ -684,45 +781,6 @@ function pkgmgr.refresh_globals()
        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