function on_open(params) if params then mode = params.mode end refresh() end -- add - packs to be added to the world (after apply) -- rem - packs that should be removed from the world (after apply) add_packs = {} rem_packs = {} -- included - connected packs to the world -- excluded - packs that are not connected to the world packs_included = {} packs_excluded = {} packs_info = {} local function include(id, is_include) if is_include then table.insert(packs_included, id) table.remove_value(packs_excluded, id) else table.insert(packs_excluded, id) table.remove_value(packs_included, id) end end function apply() core.reconfig_packs(add_packs, rem_packs) if mode ~= "world" then menu:back() end end function reposition_func(_pack) local INTERVAL = 2 local STEP = 1 local SIZE = 80 local tbl = nil if table.has(packs_included, _pack) then tbl = packs_included elseif table.has(packs_excluded, _pack) then tbl = packs_excluded else tbl = packs_excluded local packinfo = pack.get_info(_pack) packinfo[packinfo.id] = {packinfo.id, packinfo.title} table.insert(packs_excluded, packinfo.id) end local indx = table.index(tbl, _pack) - 1 local pos = {0, (SIZE + INTERVAL) * indx + STEP} return pos[1], pos[2] end function refresh_search() local search_text = document.search_textbox.text:lower() local new_included = table.copy(packs_included) local new_excluded = table.copy(packs_excluded) local function score(pack_name) if pack_name:lower():find(search_text) then return 1 end return 0 end local function sorting(a, b) local score_a = score(packs_info[a][2]) local score_b = score(packs_info[b][2]) if score_a ~= score_b then return score_a > score_b else return packs_info[a][2] < packs_info[b][2] end end table.sort(new_included, sorting) table.sort(new_excluded, sorting) packs_included = new_included packs_excluded = new_excluded for _, id in ipairs(table.merge(table.copy(packs_included), packs_excluded)) do local content = document["pack_" .. id] content:reposition() end end function refresh_changes() document.apply_btn.enabled = (#add_packs>0) or (#rem_packs>0) refresh_search() end function move_pack(id) -- cancel pack addition if table.has(add_packs, id) then document["pack_"..id]:moveInto(document.packs_add) table.remove_value(add_packs, id) include(id, false) -- cancel pack removal elseif table.has(rem_packs, id) then document["pack_"..id]:moveInto(document.packs_cur) table.remove_value(rem_packs, id) include(id, true) -- add pack elseif table.has(packs_installed, id) then document["pack_"..id]:moveInto(document.packs_add) table.insert(rem_packs, id) include(id, false) -- remove pack else document["pack_"..id]:moveInto(document.packs_cur) table.insert(add_packs, id) include(id, true) end refresh_changes() end function move_left() for _, id in pairs(table.copy(packs_excluded)) do if not document["pack_"..id].enabled then goto continue end include(id, true) table.insert(add_packs, id) table.remove_value(rem_packs, id) document["pack_"..id]:moveInto(document.packs_cur) ::continue:: end refresh_changes() end function move_right() for _, id in pairs(table.copy(packs_included)) do if not document["pack_"..id].enabled then goto continue end include(id, false) if table.has(packs_installed, id) then table.insert(rem_packs, id) end table.remove_value(add_packs, id) document["pack_"..id]:moveInto(document.packs_add) ::continue:: end refresh_changes() end function place_pack(panel, packinfo, callback, position_func) if packinfo.error then callback = nil end if packinfo.has_indices then packinfo.id_verbose = packinfo.id.."*" else packinfo.id_verbose = packinfo.id end packinfo.callback = callback packinfo.position_func = position_func or function () end panel:add(gui.template("pack", packinfo)) if not callback then document["pack_"..packinfo.id].enabled = false end end local Version = {}; function Version.matches_pattern(version) for _, letter in string.gmatch(version, "%.+") do if type(letter) ~= "number" or letter ~= "." then return false; end local t = string.split(version, "."); return #t == 2 or #t == 3; end end function Version.__equal(ver1, ver2) return ver1[1] == ver2[1] and ver1[2] == ver2[2] and ver1[3] == ver2[3]; end function Version.__greater(ver1, ver2) if ver1[1] ~= ver2[1] then return ver1[1] > ver2[1] end; if ver1[2] ~= ver2[2] then return ver1[2] > ver2[2] end; return ver1[3] > ver2[3]; end function Version.__less(ver1, ver2) return Version.__greater(ver2, ver1); end function Version.__greater_or_equal(ver1, ver2) return not Version.__less(ver1, ver2); end function Version.__less_or_equal(ver1, ver2) return not Version.__greater(ver1, ver2); end Version.operators = { ["="] = Version.__equal, [">"] = Version.__greater, ["<"] = Version.__less, [">="] = Version.__greater_or_equal, ["<="] = Version.__less_or_equal } function Version.compare(op, ver1, ver2) ver1 = string.split(ver1, "."); ver2 = string.split(ver2, "."); local comparison_func = Version.operators[op]; if comparison_func then return comparison_func(ver1, ver2); else return false; end end function Version.parse(version) local op = string.sub(version, 1, 2); if op == ">=" or op == "<=" then return op, string.sub(version, #op + 1); end op = string.sub(version, 1, 1); if op == ">" or op == "<" then return op, string.sub(version, #op + 1); end return "=", version; end local function compare_version(dependent_version, actual_version) if Version.matches_pattern(dependent_version) and Version.matches_pattern(actual_version) then local op, dep_ver = Version.parse_version(dependent_version); Version.compare(op, dep_ver, actual_version); elseif dependent_version == "*" or dependent_version == actual_version then return true; else return false; end end function check_dependencies(packinfo) if packinfo.dependencies == nil then return end for i, dep in ipairs(packinfo.dependencies) do local depid, depver = unpack(string.split(dep:sub(2,-1), "@")) if dep:sub(1,1) ~= '!' then goto continue end if not table.has(packs_all, depid) then return string.format( "%s (%s)", gui.str("error.dependency-not-found"), depid ) end local dep_pack = pack.get_info(depid); if not compare_version(depver, dep_pack.version) then local op, ver = Version.parse(depver) return string.format( "%s: %s != %s (%s)", gui.str("error.dependency-version-not-met"), dep_pack.version, ver, depid ); end if table.has(packs_installed, packinfo.id) then table.insert(required, depid) end ::continue:: end return end function check_deleted() for i = 1, math.max(#packs_included, #packs_excluded) do local pack = packs_included[i] if pack and not table.has(packs_all, pack) then table.remove(packs_included, i) table.insert(rem_packs, pack) end pack = packs_excluded[i] if pack and not table.has(packs_all, pack) then table.remove(packs_excluded, i) table.insert(rem_packs, pack) end end end function refresh() packs_installed = pack.get_installed() packs_available = pack.get_available() base_packs = pack.get_base_packs() packs_all = {unpack(packs_installed)} required = {} table.merge(packs_all, packs_available) local packs_cur = document.packs_cur local packs_add = document.packs_add packs_cur:clear() packs_add:clear() -- lock pack that contains used generator if world.is_open() then local genpack, genname = parse_path(world.get_generator()) if genpack ~= "core" then table.insert(base_packs, genpack) end end local packids = {unpack(packs_installed)} for i,k in ipairs(packs_available) do table.insert(packids, k) end local packinfos = pack.get_info(packids) for _,id in ipairs(base_packs) do local packinfo = pack.get_info(id) packs_info[id] = {packinfo.id, packinfo.title} end for _,id in ipairs(packs_all) do local packinfo = pack.get_info(id) packs_info[id] = {packinfo.id, packinfo.title} end if #packs_excluded == 0 then packs_excluded = table.copy(packs_available) end if #packs_included == 0 then packs_included = table.copy(packs_installed) end for i,id in ipairs(packs_installed) do local packinfo = packinfos[id] packinfo.index = i callback = not table.has(base_packs, id) and string.format('move_pack("%s")', id) or nil packinfo.error = check_dependencies(packinfo) place_pack(packs_cur, packinfo, callback, string.format('reposition_func("%s")', packinfo.id)) end for i,id in ipairs(packs_available) do local packinfo = packinfos[id] packinfo.index = i callback = string.format('move_pack("%s")', id) packinfo.error = check_dependencies(packinfo) place_pack(packs_add, packinfo, callback, string.format('reposition_func("%s")', packinfo.id)) end for i,id in ipairs(packs_installed) do if table.has(required, id) then document["pack_"..id].enabled = false end end check_deleted() apply_movements(packs_cur, packs_add) refresh_changes() end function apply_movements(packs_cur, packs_add) for i,id in ipairs(packs_installed) do if table.has(rem_packs, id) then document["pack_"..id]:moveInto(packs_add) end end for i,id in ipairs(packs_available) do if table.has(add_packs, id) then document["pack_"..id]:moveInto(packs_cur) end end end