Files
sky_phone-sky-systems/sky_phone/source/shared/custom_app_compat.lua
T
2026-08-11 20:31:01 +02:00

270 lines
8.6 KiB
Lua

SkyPhoneCompatibility = {}
function SkyPhoneCompatibility.RegisterExportAlias(resource_name, export_name, handler)
assert(type(resource_name) == "string" and resource_name ~= "", "Export alias resource must be a non-empty string")
assert(type(export_name) == "string" and export_name ~= "", "Export alias name must be a non-empty string")
assert(type(handler) == "function", "Export alias handler must be a function")
AddEventHandler(("__cfx_export_%s_%s"):format(resource_name, export_name), function(set_callback)
set_callback(handler)
end)
end
SkyPhoneCompatibility.Providers = {
lb = "lb_phone",
seventeen = "17mov",
high = "high_phone",
quasar = "quasar_v3",
yseries = "yseries",
}
local function normalize_at_resource_url(owner_resource, url, error_prefix)
if type(url) ~= "string" or url == "" then
return nil, "invalid_" .. error_prefix
end
if url:sub(1, 7) == "http://" then
return nil, "insecure_" .. error_prefix
end
if url:sub(1, 1) ~= "@" then
return url
end
local url_owner, url_path = url:match("^@([^/]+)/(.+)$")
if url_owner ~= owner_resource or not url_path then
return nil, error_prefix .. "_owner_mismatch"
end
return owner_resource .. "/" .. url_path
end
local function resolve_lb_asset_resource(owner_resource, app_data)
local ui = app_data.ui
if type(ui) ~= "string" then
return nil
end
local explicit_ui_resource = ui:match("^https://cfx%-nui%-([^/]+)/")
or ui:match("^nui://([^/]+)/")
local asset_resource = explicit_ui_resource or ui:match("^([%w][%w._-]*)/")
if not asset_resource or asset_resource == owner_resource then
return nil
end
if explicit_ui_resource or app_data.resource == asset_resource then
return asset_resource
end
local icon = app_data.icon
local icon_resource = type(icon) == "string" and (
icon:match("^https://cfx%-nui%-([^/]+)/") or icon:match("^nui://([^/]+)/")
) or nil
if icon_resource == asset_resource then
return asset_resource
end
return nil
end
local function build_locale_maps(app_name, locales)
local names = {}
local descriptions = {}
if type(locales) == "table" then
for locale, locale_data in pairs(locales) do
if type(locale) == "string" and type(locale_data) == "table" then
if type(locale_data.label) == "string" and locale_data.label ~= "" then
names[locale] = locale_data.label
end
if type(locale_data.description) == "string" and locale_data.description ~= "" then
descriptions[locale] = locale_data.description
end
end
end
end
if not next(names) then
return app_name, app_name
end
for locale, label in pairs(names) do
if not descriptions[locale] then
descriptions[locale] = label
end
end
return names, descriptions
end
function SkyPhoneCompatibility.CopyQuasarData(app_data)
local copy = {}
for key, value in pairs(app_data) do
if key == "iframe" and type(value) == "table" then
copy.iframe = {}
for iframe_key, iframe_value in pairs(value) do
copy.iframe[iframe_key] = iframe_value
end
else
copy[key] = value
end
end
return copy
end
function SkyPhoneCompatibility.BuildLbDefinition(owner_resource, app_data)
if type(app_data) ~= "table" or type(app_data.identifier) ~= "string" then
return nil, "invalid_app_data"
end
local ui, ui_error = normalize_at_resource_url(owner_resource, app_data.ui, "ui")
if not ui then
return nil, ui_error
end
return {
schemaVersion = 1,
id = app_data.identifier,
name = app_data.name,
description = app_data.description,
developer = app_data.developer,
category = app_data.game and "games" or "utilities",
ui = ui,
assetResource = resolve_lb_asset_resource(owner_resource, app_data),
bridgeMode = "legacy",
icon = app_data.icon,
defaultInstalled = app_data.defaultApp or false,
removable = not app_data.defaultApp,
orientation = app_data.landscape and "landscape" or "portrait",
onInstall = app_data.onInstall,
onOpen = app_data.onOpen or app_data.onUse,
onClose = app_data.onClose,
compatibility = {
provider = SkyPhoneCompatibility.Providers.lb,
apiVersion = 1,
resourceName = type(app_data.resource) == "string" and app_data.resource or owner_resource,
},
}
end
function SkyPhoneCompatibility.Build17MovDefinition(app_data)
if type(app_data) ~= "table" or type(app_data.name) ~= "string" then
return nil, "invalid_app_data"
end
return {
schemaVersion = 1,
id = app_data.name,
name = app_data.label,
description = app_data.description or app_data.label,
category = "utilities",
ui = app_data.ui,
bridgeMode = "legacy",
icon = app_data.icon,
iconBackground = type(app_data.iconBackground) == "string" and app_data.iconBackground or nil,
defaultInstalled = app_data.default or app_data.preInstalled or false,
removable = not app_data.default,
orientation = "portrait",
compatibility = {
provider = SkyPhoneCompatibility.Providers.seventeen,
apiVersion = 1,
},
}
end
function SkyPhoneCompatibility.BuildHighDefinition(owner_resource, app_name, data, locales)
if type(owner_resource) ~= "string" or type(app_name) ~= "string" or type(data) ~= "table" then
return nil, "invalid_app_data"
end
if #app_name > 64 or not app_name:match("^[a-z0-9][a-z0-9._-]+$") then
return nil, "invalid_app_id"
end
local external_url, url_error = normalize_at_resource_url(
owner_resource,
data.externalUrl,
"external_url"
)
if not external_url then
return nil, url_error
end
local name, description = build_locale_maps(app_name, locales)
local icon = type(data.icon) == "table" and data.icon.imageUrl or data.icon
return {
schemaVersion = 1,
id = app_name,
name = name,
description = description,
developer = data.developer,
category = "utilities",
ui = external_url,
bridgeMode = "legacy",
icon = icon,
iconBackground = type(data.icon) == "table" and data.icon.background or nil,
defaultInstalled = data.preAdded or false,
removable = data.removable ~= false,
orientation = "portrait",
compatibility = {
provider = SkyPhoneCompatibility.Providers.high,
apiVersion = 1,
},
}
end
function SkyPhoneCompatibility.BuildQuasarDefinition(app_data)
local iframe_url = type(app_data) == "table"
and type(app_data.iframe) == "table"
and app_data.iframe.url
or nil
if type(app_data) ~= "table"
or type(app_data.id) ~= "string"
or type(app_data.label) ~= "string"
or type(iframe_url) ~= "string"
then
return nil, "invalid_app_data"
end
return {
schemaVersion = 1,
id = app_data.id,
name = app_data.label,
description = app_data.description or app_data.label,
category = "utilities",
ui = iframe_url,
bridgeMode = "legacy",
icon = app_data.icon,
defaultInstalled = app_data.defaultInstalled or false,
removable = true,
orientation = "portrait",
compatibility = {
provider = SkyPhoneCompatibility.Providers.quasar,
apiVersion = 1,
},
}
end
function SkyPhoneCompatibility.BuildYSeriesDefinition(app_data)
if type(app_data) ~= "table" or type(app_data.key) ~= "string" then
return nil, "invalid_app_data"
end
local icon = app_data.icon
if type(icon) == "table" then
icon = icon.yos or icon.humanoid
end
return {
schemaVersion = 1,
id = app_data.key,
name = app_data.name,
description = app_data.description or app_data.name,
category = app_data.game and "games" or "utilities",
ui = app_data.ui,
bridgeMode = "legacy",
icon = icon,
defaultInstalled = app_data.defaultApp or false,
removable = true,
orientation = "portrait",
compatibility = {
provider = SkyPhoneCompatibility.Providers.yseries,
apiVersion = 1,
},
}
end