mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-28 21:01:30 +00:00
ADD - integrate TGIANN House
Authorize owner properties from the documented TGIANN schema and enrich entrance waypoints through the published client export. Keep unsupported key, lock, CCTV, and garage capabilities disabled.
This commit is contained in:
@@ -89,7 +89,7 @@ Sky Phone is built to be the **free FiveM phone you can choose without accepting
|
||||
| **Inventories** | ak47_inventory, codem-inventory, core_inventory, jaksam_inventory, jpr-inventory, lj-inventory, mf-inventory, one_inventory, origen_inventory, ox_inventory, ps-inventory, qb-inventory, qs-inventory, smx-inventory, tgiann-inventory, hex_4_inventory, and native ESX inventory |
|
||||
| **Calls** | YACA, PMA Voice, SaltyChat |
|
||||
| **Radio** | YACA, PMA Voice, SaltyChat |
|
||||
| **Housing** | RTX Housing, Quasar Housing, VMS Housing, RX Housing, NoLag Properties, SN Properties, ESX Property, qbx_properties |
|
||||
| **Housing** | RTX Housing, Quasar Housing, TGIANN House, VMS Housing, RX Housing, NoLag Properties, SN Properties, ESX Property, qbx_properties |
|
||||
| **Garages** | Built-in/custom data and a broad set of popular garage providers configured through the bridge |
|
||||
| **Custom app contracts** | Sky Phone, LB Phone, 17Movement, High Phone, Quasar Smartphone, YSeries |
|
||||
| **Languages** | English, German |
|
||||
@@ -650,7 +650,7 @@ Select the provider under `Config.Garage.System`. Vehicle images use the configu
|
||||
|
||||
### Housing
|
||||
|
||||
Select `rtx`, `quasar`, `vms`, `rx`, `nolag`, `sn`, `esx_property`, or `qbx_properties` under `Config.Housing.System`. Automatic mode uses `Config.Housing.AutoPriority` and keeps the existing `esx_property` and `qbx_properties` defaults ahead of newly supported providers. Select a provider explicitly when multiple housing resources are running. Each bridge exposes only the capabilities supported by the documented provider API.
|
||||
Select `rtx`, `quasar`, `tgiann`, `vms`, `rx`, `nolag`, `sn`, `esx_property`, or `qbx_properties` under `Config.Housing.System`. Automatic mode uses `Config.Housing.AutoPriority` and keeps the existing `esx_property` and `qbx_properties` defaults ahead of newly supported providers. Select a provider explicitly when multiple housing resources are running. Each bridge exposes only the capabilities supported by the documented provider API. The TGIANN bridge expects `tgiann-core` to start before `tgiann-house`, lists server-authorized owner properties, and supports entrance waypoints; TGIANN does not publish stable external contracts for keyholders, lock controls, CCTV, or live garage status.
|
||||
|
||||
### Companies
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const readResourceFile = (path: string) =>
|
||||
readFileSync(
|
||||
new URL(`../../sky_phone/${path}`, import.meta.url),
|
||||
'utf8',
|
||||
).replace(/\r\n/g, '\n')
|
||||
|
||||
describe('housing provider contracts', () => {
|
||||
it('registers TGIANN House with server-authorized owner properties', () => {
|
||||
const adapter = readResourceFile('source/bridge/server/housing/tgiann.lua')
|
||||
|
||||
expect(adapter).toContain('local resource_name = "tgiann-house"')
|
||||
expect(adapter).toContain('FROM `tgiann_house`')
|
||||
expect(adapter).toContain('WHERE `owner` = ?')
|
||||
expect(adapter).toContain('Bridge.Framework.GetIdentifier(source)')
|
||||
expect(adapter).toContain(
|
||||
'Bridge.Housing.RegisterProvider(provider_name, {',
|
||||
)
|
||||
expect(adapter).toContain('return nil, "property_access_denied"')
|
||||
expect(adapter).not.toContain('`houseKeys`')
|
||||
expect(adapter).not.toContain('tgiann-house:getPlayerHouses')
|
||||
})
|
||||
|
||||
it('uses the published client export for entrance waypoints only', () => {
|
||||
const adapter = readResourceFile('source/bridge/client/housing/tgiann.lua')
|
||||
|
||||
expect(adapter).toContain(
|
||||
'return exports[resource_name]:getHouseData(house)',
|
||||
)
|
||||
expect(adapter).toContain(
|
||||
'Bridge.Normalize.Coordinates(house_data.doorCoord)',
|
||||
)
|
||||
expect(adapter).toContain(
|
||||
'Bridge.Housing.RegisterClientProvider(provider_name, {',
|
||||
)
|
||||
expect(adapter).toContain('SetNewWaypoint(')
|
||||
expect(adapter).not.toContain('enterHouse(')
|
||||
expect(adapter).not.toContain('forceOpenDoorHouse')
|
||||
})
|
||||
|
||||
it('advertises TGIANN in housing configuration and documentation', () => {
|
||||
const config = readResourceFile('config/config.lua')
|
||||
const readme = readFileSync(
|
||||
new URL('../../README.md', import.meta.url),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
expect(config).toContain(
|
||||
'"rtx", "quasar", "tgiann", "vms", "rx", "nolag", "sn"',
|
||||
)
|
||||
expect(readme).toContain('Quasar Housing, TGIANN House, VMS Housing')
|
||||
expect(readme).toContain('`quasar`, `tgiann`, `vms`')
|
||||
expect(readme).toContain(
|
||||
'expects `tgiann-core` to start before `tgiann-house`',
|
||||
)
|
||||
expect(readme).toContain(
|
||||
'lists server-authorized owner properties, and supports entrance waypoints',
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -450,8 +450,8 @@ Config.Garage = {
|
||||
}
|
||||
|
||||
Config.Housing = {
|
||||
System = "auto", -- auto, rtx, quasar, vms, rx, nolag, sn, esx_property, qbx_properties
|
||||
AutoPriority = { "esx_property", "qbx_properties", "rtx", "quasar", "vms", "rx", "nolag", "sn" },
|
||||
System = "auto", -- auto, rtx, quasar, tgiann, vms, rx, nolag, sn, esx_property, qbx_properties
|
||||
AutoPriority = { "esx_property", "qbx_properties", "rtx", "quasar", "tgiann", "vms", "rx", "nolag", "sn" },
|
||||
MaximumProperties = 50,
|
||||
OverviewRequestsPerMinute = 30,
|
||||
ActionsPerMinute = 12,
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
local provider_name = "tgiann"
|
||||
local resource_name = "tgiann-house"
|
||||
|
||||
local function house_reference(value)
|
||||
if type(value) ~= "string" then
|
||||
return nil
|
||||
end
|
||||
return value ~= "" and value or nil
|
||||
end
|
||||
|
||||
local function house_entrance(house)
|
||||
local success, house_data = pcall(function()
|
||||
return exports[resource_name]:getHouseData(house)
|
||||
end)
|
||||
if not success then
|
||||
Bridge.Debug(
|
||||
"error",
|
||||
"[sky_phone] tgiann-house:getHouseData failed for '%s': %s",
|
||||
house,
|
||||
tostring(house_data)
|
||||
)
|
||||
return nil, "provider_error"
|
||||
end
|
||||
local entrance = type(house_data) == "table"
|
||||
and Bridge.Normalize.Coordinates(house_data.doorCoord) or nil
|
||||
if not entrance then
|
||||
return nil, "invalid_coordinates"
|
||||
end
|
||||
return entrance
|
||||
end
|
||||
|
||||
local function enrich_overview(properties)
|
||||
local result = {}
|
||||
if GetResourceState(resource_name) ~= "started" or type(properties) ~= "table" then
|
||||
return result
|
||||
end
|
||||
|
||||
for _, property in ipairs(properties) do
|
||||
local house = type(property) == "table" and house_reference(property.providerId) or nil
|
||||
if house and property.id == provider_name .. ":" .. house then
|
||||
local entrance = house_entrance(house)
|
||||
if entrance then
|
||||
result[#result + 1] = {
|
||||
id = property.id,
|
||||
entrance = entrance,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
Bridge.Housing.RegisterClientProvider(provider_name, {
|
||||
enrich_overview = enrich_overview,
|
||||
execute = function(action, data)
|
||||
if GetResourceState(resource_name) ~= "started" then
|
||||
return false, "provider_unavailable"
|
||||
end
|
||||
if action ~= "set_waypoint" then
|
||||
return false, "capability_unavailable"
|
||||
end
|
||||
local house = type(data) == "table" and house_reference(data.providerId) or nil
|
||||
if not house then
|
||||
return false, "invalid_property"
|
||||
end
|
||||
local entrance, error_code = house_entrance(house)
|
||||
if not entrance then
|
||||
return false, error_code
|
||||
end
|
||||
SetNewWaypoint(entrance.x + 0.0, entrance.y + 0.0)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
@@ -0,0 +1,124 @@
|
||||
local provider_name = "tgiann"
|
||||
local resource_name = "tgiann-house"
|
||||
|
||||
local function query_owned_houses(identifier)
|
||||
local success, properties = pcall(function()
|
||||
return MySQL.query.await([[
|
||||
SELECT `name`
|
||||
FROM `tgiann_house`
|
||||
WHERE `owner` = ?
|
||||
ORDER BY `name` ASC
|
||||
]], { identifier })
|
||||
end)
|
||||
if not success or type(properties) ~= "table" then
|
||||
Bridge.Debug("error", "[sky_phone] tgiann-house overview query failed: %s", tostring(properties))
|
||||
return nil
|
||||
end
|
||||
return properties
|
||||
end
|
||||
|
||||
local function owns_house(identifier, house)
|
||||
local success, property = pcall(function()
|
||||
return MySQL.single.await([[
|
||||
SELECT `name`
|
||||
FROM `tgiann_house`
|
||||
WHERE `name` = ? AND `owner` = ?
|
||||
LIMIT 1
|
||||
]], { house, identifier })
|
||||
end)
|
||||
if not success then
|
||||
Bridge.Debug("error", "[sky_phone] tgiann-house ownership query failed: %s", tostring(property))
|
||||
return nil, "provider_error"
|
||||
end
|
||||
return type(property) == "table"
|
||||
end
|
||||
|
||||
local function house_from_property_id(value)
|
||||
if type(value) ~= "string" then
|
||||
return nil
|
||||
end
|
||||
local house = value:match("^tgiann:(.+)$")
|
||||
return house and house ~= "" and house or nil
|
||||
end
|
||||
|
||||
local function normalized_property(house)
|
||||
return {
|
||||
id = provider_name .. ":" .. house,
|
||||
providerId = house,
|
||||
name = house,
|
||||
access = "owner",
|
||||
locked = false,
|
||||
capabilities = {
|
||||
lock = false,
|
||||
keys = false,
|
||||
waypoint = true,
|
||||
cctv = false,
|
||||
garageStatus = false,
|
||||
},
|
||||
cctv = { enabled = false },
|
||||
garage = nil,
|
||||
keys = nil,
|
||||
}
|
||||
end
|
||||
|
||||
Bridge.Housing.RegisterProvider(provider_name, {
|
||||
resource_name = resource_name,
|
||||
is_available = function()
|
||||
return GetResourceState(resource_name) == "started"
|
||||
end,
|
||||
get_overview = function(source)
|
||||
local identifier = Bridge.Framework.GetIdentifier(source)
|
||||
if not identifier then
|
||||
return nil, "housing_unavailable"
|
||||
end
|
||||
local properties = query_owned_houses(identifier)
|
||||
if not properties then
|
||||
return nil, "provider_error"
|
||||
end
|
||||
|
||||
local result = {}
|
||||
local seen = {}
|
||||
local maximum = math.max(0, math.floor(tonumber(Config.Housing.MaximumProperties) or 0))
|
||||
for _, property in ipairs(properties) do
|
||||
if #result >= maximum then
|
||||
break
|
||||
end
|
||||
local house = type(property.name) == "string" and property.name or nil
|
||||
if house and house ~= "" and not seen[house] then
|
||||
seen[house] = true
|
||||
result[#result + 1] = normalized_property(house)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end,
|
||||
prepare = function(source, action, data)
|
||||
if action == "toggle_lock" or action == "grant_key" or action == "revoke_key"
|
||||
or action == "key_candidates"
|
||||
then
|
||||
return nil, "capability_unavailable"
|
||||
end
|
||||
if action == "open_cctv" then
|
||||
return nil, "cctv_unavailable"
|
||||
end
|
||||
if action ~= "set_waypoint" then
|
||||
return nil, "invalid_action"
|
||||
end
|
||||
|
||||
local house = house_from_property_id(data and data.propertyId)
|
||||
if not house then
|
||||
return nil, "invalid_property"
|
||||
end
|
||||
local identifier = Bridge.Framework.GetIdentifier(source)
|
||||
if not identifier then
|
||||
return nil, "housing_unavailable"
|
||||
end
|
||||
local owned, error_code = owns_house(identifier, house)
|
||||
if owned == nil then
|
||||
return nil, error_code
|
||||
end
|
||||
if not owned then
|
||||
return nil, "property_access_denied"
|
||||
end
|
||||
return { providerId = house }
|
||||
end,
|
||||
})
|
||||
@@ -435,8 +435,8 @@ Config.Garage = {
|
||||
}
|
||||
|
||||
Config.Housing = {
|
||||
System = "auto", -- auto, rtx, quasar, vms, rx, nolag, sn, esx_property, qbx_properties
|
||||
AutoPriority = { "esx_property", "qbx_properties", "rtx", "quasar", "vms", "rx", "nolag", "sn" },
|
||||
System = "auto", -- auto, rtx, quasar, tgiann, vms, rx, nolag, sn, esx_property, qbx_properties
|
||||
AutoPriority = { "esx_property", "qbx_properties", "rtx", "quasar", "tgiann", "vms", "rx", "nolag", "sn" },
|
||||
MaximumProperties = 50,
|
||||
OverviewRequestsPerMinute = 30,
|
||||
ActionsPerMinute = 12,
|
||||
|
||||
Reference in New Issue
Block a user