ADD - expand phone application platform

Add the Companies app, resilient call handling, custom app registry, vendor compatibility, storage policies, frontend bridge, tests, documentation, config, locale, and SQL migrations.
This commit is contained in:
Leon.Schmidt
2026-08-11 00:22:39 +02:00
parent 72e5f91e02
commit 42ac955052
77 changed files with 17630 additions and 333 deletions
@@ -0,0 +1,138 @@
local RESOURCE_NAME = GetCurrentResourceName()
local SNAPSHOT_COOLDOWN_MS = 2000
local SNAPSHOT_REJECTION_LOG_COOLDOWN_MS = 10000
local registered_apps = {}
local snapshot_requests = {}
local snapshot_rejection_logs = {}
local function get_calling_resource(export_name)
local owner_resource = GetInvokingResource()
if owner_resource then
return owner_resource
end
print(("[%s] %s rejected: the export must be called by another resource."):format(
RESOURCE_NAME,
export_name
))
return nil, "invalid_owner"
end
local function add_high_application(app_name, data, locales)
local owner_resource, owner_error = get_calling_resource("addApplication")
if not owner_resource then
return false, owner_error
end
local definition, definition_error = SkyPhoneCompatibility.BuildHighDefinition(
owner_resource,
app_name,
data,
locales
)
if not definition then
print(("[%s] High Phone registration rejected for %s: %s."):format(
RESOURCE_NAME,
owner_resource,
definition_error
))
return false, definition_error
end
local existing = registered_apps[app_name]
if existing and existing.owner_resource ~= owner_resource then
return false, "duplicate_app_id"
end
local revision = existing and existing.revision + 1 or 1
registered_apps[app_name] = {
definition = definition,
owner_resource = owner_resource,
revision = revision,
}
TriggerClientEvent(
"sky_phone:compat:high:client:syncApplication",
-1,
owner_resource,
definition,
revision
)
return true
end
local function build_snapshot()
local ids = {}
for app_id in pairs(registered_apps) do
ids[#ids + 1] = app_id
end
table.sort(ids)
local snapshot = {}
for index = 1, #ids do
snapshot[index] = registered_apps[ids[index]]
end
return snapshot
end
RegisterNetEvent("sky_phone:compat:high:server:requestSnapshot", function()
local player_source = source
if player_source <= 0 then
print(("[%s] Rejected High Phone snapshot request without a player source."):format(
RESOURCE_NAME
))
return
end
local now = GetGameTimer()
local last_request = snapshot_requests[player_source]
local elapsed = last_request and now - last_request or SNAPSHOT_COOLDOWN_MS
if elapsed >= 0 and elapsed < SNAPSHOT_COOLDOWN_MS then
local last_log = snapshot_rejection_logs[player_source]
local log_elapsed = last_log and now - last_log or SNAPSHOT_REJECTION_LOG_COOLDOWN_MS
if log_elapsed < 0 or log_elapsed >= SNAPSHOT_REJECTION_LOG_COOLDOWN_MS then
snapshot_rejection_logs[player_source] = now
print(("[%s] Rate-limited High Phone snapshot request from player %s."):format(
RESOURCE_NAME,
player_source
))
end
return
end
snapshot_requests[player_source] = now
TriggerClientEvent(
"sky_phone:compat:high:client:replaceSnapshot",
player_source,
build_snapshot()
)
end)
AddEventHandler("playerDropped", function()
snapshot_requests[source] = nil
snapshot_rejection_logs[source] = nil
end)
AddEventHandler("onResourceStop", function(resource_name)
if resource_name == RESOURCE_NAME then
return
end
local removed_ids = {}
for app_id, record in pairs(registered_apps) do
if record.owner_resource == resource_name then
removed_ids[#removed_ids + 1] = app_id
end
end
for index = 1, #removed_ids do
local app_id = removed_ids[index]
registered_apps[app_id] = nil
TriggerClientEvent(
"sky_phone:compat:high:client:removeApplication",
-1,
resource_name,
app_id
)
end
end)
exports("addApplication", add_high_application)