mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-28 17:01:18 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a13cd7cb9 |
@@ -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