GKSHOP
GKSPHONE V2Configuration

Custom housing

Write an adapter so the House app works with an unsupported housing script.

Needs Lua experience. We can't write or debug the adapter for you.

Eight adapters ship built in — loaf_housing, bcs_housing, qs-housing, vms_housing, ps-housing, rtx_housing, qb-houses and esx_property. A ninth is one file: fetch the player's places, map each through HouseEntry, and hand the list back.

The phone never learns your script exists. It only ever sees the shape HouseEntry returns.

1. Set the config

gksphone/config/config.lua
Config.HouseScript = "my_housing"

Anything other than auto skips detection, so the name only has to match what you check for in step 2.

2. Create the file

gksphone/client/apps/house/my_housing.lua

No fxmanifest.lua change — client/**/* is already globbed, and the whole folder is outside escrow.

Start it with the guard every adapter uses, so only the selected one loads:

gksphone/client/apps/house/my_housing.lua
if Config.HouseScript ~= "my_housing" then return end

3. Return the houses

fetchHouseData is the only callback that has to do real work. Return one list containing both the places the player owns and the ones they only hold a key to — owned = false marks the difference.

gksphone/client/apps/house/my_housing.lua
RegisterNUICallback('gksphone:home:fetchHouseData', function(data, cb)
    local out = {}

    for _, house in pairs(exports['my_housing']:GetMyHouses() or {}) do
        out[#out + 1] = HouseEntry({
            id = house.id,                 -- required, unique, stable
            label = house.name,
            owned = true,
            coords = house.coords,
            garage = house.has_garage,
            price = house.price,
            keyholders = HouseKeys(house.keys, function(id, value)
                return HouseKey(id, value.name)
            end),
            -- Only claim what your script can actually do; see below.
            canLocate = true,
            canGiveKey = true,
            canRemoveKey = true,
            canTransfer = false,
            canLock = false
        })
    end

    Debugprint('my_housing houses', out)
    cb(out)
end)

HouseEntry fields

Everything is optional except id. What you leave out gets a safe default.

FieldTypeDefault
idstring | numberrequired, must be stable
labelstringthe script's name, else #id
tagstringsmall second line on the tile
ownedbooleantrue
keyholderslist{}
garagebooleanfalse
coordstable | vector3nil
tier · pricenumbernil
apartmentbooleanfalse
bills · services · upgradeslist{} — read-only, the phone never pays or buys
lockedbooleannil — "unlocked" and "unknown" are different

The can* flags decide the buttons

canTransfer, canLocate, canGiveKey, canRemoveKey and canLock all default to false, and a tile is only drawn for what is true. Claiming something your script cannot do and then failing is worse than not offering it.

Two helpers

HouseKeys(source, map) walks a keyholder table of any shape — array, map keyed by identifier, map keyed by something else — with pairs, and your mapper decides what key and value mean. Return nil from it to skip an entry.

HouseWaypoint(coords) reads x/y from a table or a vector3 and sets the waypoint. It returns false instead of dropping a marker in the sea when the coordinates are missing.

4. Register the other five

All six callbacks must exist even when the script cannot do the thing — an unregistered callback hangs the app instead of failing. Return false for the ones you skipped.

gksphone/client/apps/house/my_housing.lua
-- data.id, data.phoneNumber
RegisterNUICallback('gksphone:home:transferHouse', function(data, cb) cb(false) end)

-- data.id, data.coords
RegisterNUICallback('gksphone:home:houseLocation', function(data, cb)
    if not HouseWaypoint(data.coords) then
        cb(false)
        return
    end
    exports['gksphone']:ToastNotification(
        _T(LastItemData?.info?.phoneLang, 'HouseAPP.APP_HOUSE_SETGPS'))
    cb('ok')
end)

-- data.id, data.phoneNumber — the recipient is looked up by phone number
RegisterNUICallback('gksphone:home:giveKey', function(data, cb)
    CallBackServerTrigger('gksphone:server:getCizitinIDSource', function(_, target)
        if not target then return cb(false) end
        cb(exports['my_housing']:GiveKey(data.id, tonumber(target)) and 'ok' or false)
    end, data.phoneNumber)
end)

-- data.id, data.holderId
RegisterNUICallback('gksphone:home:removeKey', function(data, cb)
    if not data.holderId then return cb(false) end
    cb(exports['my_housing']:RemoveKey(data.id, data.holderId) and 'ok' or false)
end)

-- data.id, data.locked
RegisterNUICallback('gksphone:home:toggleLock', function(data, cb) cb(false) end)

Return 'ok' on success and false on failure. Anything else is treated as failure.

5. Test it

Restart gksphone — a reconnect is not enough — and open the House app. Set Config.Debug = true and Debugprint will show exactly what your adapter handed over.

Stuck on the shape? gksphone/config/house/sh_house.lua is the contract itself, and the eight adapters in client/apps/house/ are worked examples. esx_property.lua is the shortest; loaf_housing.lua shows keys and transfers.

On this page