Files
community-plugins/prismlauncher-instances/prismlauncher-instances.luau
T

185 lines
5.3 KiB
Luau

--!nonstrict
local function getPrismPath()
local prismPath = noctalia.getConfig("prism_path")
return noctalia.expandPath(prismPath)
end
local function getLauncherCommand()
local launcherCommand = noctalia.getConfig("launcher_exec_command")
return noctalia.expandPath(launcherCommand)
end
local function getInstancesDir()
local prismPath = getPrismPath()
local configuredInstanceDir = getCfgValue(prismPath .. "/*.cfg", "InstanceDir")
local instances
if configuredInstanceDir == nil then -- default path
instances = prismPath .. "/instances"
elseif configuredInstanceDir:sub(1, 1) == "/" then -- absolute path
instances = configuredInstanceDir
else -- relative path
instances = prismPath .. "/" .. configuredInstanceDir
end
return instances
end
local function loadInstances()
local instances = {}
local list = noctalia.listDir(getInstancesDir())
if list ~= nil then
for _, name in ipairs(list) do
local instanceDir = getInstancesDir() .. "/" .. name
if name ~= "" and noctalia.fileExists(instanceDir .. "/instance.cfg") then -- same check as prism to only show instance dirs
table.insert(instances, name)
end
end
end
return instances
end
local function instanceName(path)
local instanceCfg = getInstancesDir() .. "/" .. path .. "/instance.cfg"
local configuredName = getCfgValue(instanceCfg, "name")
if configuredName ~= nil then
return configuredName
end
return path:match("([^/]+)$") or path
end
local function filterInstances(instances, query)
if query == "" then return instances end
local results = {}
for _, path in instances do
local insName = instanceName(path)
if insName:lower():find(query:lower(), 1, true) then
table.insert(results, path)
end
end
return results
end
local function makeRows(instances)
local rows = {}
for _, path in instances do
table.insert(rows, {
id = path,
title = instanceName(path),
subtitle = path,
icon = getIcon(path),
glyph = "nut",
})
end
return rows
end
local function showResults(query, instances, filter)
local filtered = filterInstances(instances, filter)
if #filtered == 0 then
launcher.setResults(query, {
{ id = "", title = noctalia.tr("no_instances.title"), subtitle = filter ~= "" and noctalia.tr("no_instances.subtitle") .. filter, glyph = "folder-x" }
})
else
launcher.setResults(query, makeRows(filtered))
end
end
function onQuery(query)
local filter = noctalia.string.trim(query)
showResults(query, loadInstances(), filter)
end
local function shellQuote(s)
return "'" .. s:gsub("'", "'\"'\"'") .. "'"
end
function onActivate(id)
local launcherCommand = getLauncherCommand()
if id == "" then return end
noctalia.runAsync(string.format(launcherCommand .. " --launch %s &>/dev/null", shellQuote(id)))
end
-- doesn't handle icons that were changed to different default icon, rest is fine
-- never change icons since creating instance => OK
-- change icon to manually provided icon or to icon from mod provider => OK
-- change icon to different default icon baked into prism binary => NOK, old icon will be shown because <instance>/minecraft/icon.png isn't updated
function getIcon(instancePath)
local instanceFullPath = getInstancesDir() .. "/" .. instancePath
local defaultIcon = instanceFullPath .. "/minecraft/icon.png"
local cfgPath = instanceFullPath .. "/instance.cfg"
local iconVal = getCfgValue(cfgPath, "iconKey")
if (iconVal == nil) then
return defaultIcon
end
local iconDir = getPrismPath() .. "/icons/"
local bestIcon = findBestIconIn(iconDir, iconVal)
if (bestIcon == nil) then
return defaultIcon
end
return bestIcon
end
-- https://github.com/PrismLauncher/PrismLauncher/blob/d2fa7cf7f7aa8fa5e3d4f5f7b474621c732cd525/launcher/icons/IconUtils.cpp
local validIconExtensions = { "svg", "png", "ico", "gif", "jpg", "jpeg", "webp" }
local function isIconSuffix(suffix)
for _, ext in ipairs(validIconExtensions) do
if ext == suffix then
return true
end
end
return false
end
local function splitNameAndSuffix(filename)
local dotIndex = filename:find("%.[^.]*$")
if not dotIndex then
return filename, ""
end
local base = filename:sub(1, dotIndex - 1)
local suffix = filename:sub(dotIndex + 1)
return base, suffix
end
function findBestIconIn(folder, iconKey)
local entries = noctalia.listDir(folder)
if not entries then
return nil
end
for _, filename in ipairs(entries) do
local baseName, suffix = splitNameAndSuffix(filename)
if (baseName == iconKey or filename == iconKey) and isIconSuffix(suffix) then
local sep = folder:sub(-1) == "/" and "" or "/"
return folder .. sep .. filename
end
end
return nil
end
function getCfgValue(cfgPath, key) -- naive INI value fetcher, doesn't handle sections, it's enough for this usecase
local content, err = noctalia.readFile(cfgPath)
if not content then
noctalia.log("Failed to read file ".. cfgPath .. " : " .. tostring(err))
return nil
end
for line in content:gmatch("[^\r\n]+") do
if line:sub(1, #key + 1) == key .. "=" then
return line:sub(#key + 2)
end
end
return nil
end