Kartik Scripts

17mov Phone Setup

kartik-properties includes built-in bridge support for 17mov_Phone's Housing App. This allows players to manage properties, view owned/leased homes, grant and revoke keys, and remotely toggle door locks directly from their smartphone.

Official Integration Notice: Official out-of-the-box support for kartik-properties is currently in progress and coming soon directly in an upcoming update of 17mov_Phone. In the meantime, follow the manual setup guide below.


Features Supported

  • 🏠 Property Inventory: Displays all citizen-owned properties, rented units, and properties where the player holds keys.
  • 🔑 Key Management: Grant keys to other players or contacts directly through the phone.
  • 🚫 Revoke Keys: Remove keyholders in real-time with instant synchronization.
  • 🔒 Remote Door Lock: Lock and unlock property doors remotely from the phone app.
  • 📍 GPS Navigation: Set a waypoint directly to any owned or keyed property entrance.

⚙️ Installation & Setup

Step 1: Configure 17mov_Phone

Open 17mov_Phone/configs/Bridge.lua and set Config.Housing to "kartik-properties" (or leave as "auto"):

Config.Housing = "kartik-properties"

Also ensure 'kartik-properties' is included in the auto-detection list in 17mov_Phone/configs/Bridge.lua:

local resources = { 'kartik-properties', 'ps-housing', 'qs-housing', 'loaf_housing', 'vms_housing', 'nolag_properties', 'RxHousing', 'esx_property', 'qb-houses', 'bcs_housing', 'rtx_housing' }

Step 2: Create Client Bridge File

Create the file: 17mov_Phone/bridge/housing/kartik-properties/client.lua and paste the following code:

while Config.Housing == "auto" do
    Wait(100)
end

if Config.Housing ~= "kartik-properties" then
    return
end

Houses = {}

Houses.GetPlayerOwnedHouses = function()
    return Core.TriggerServerCallback.await('17mov_Phone:Houses:Server:GetPlayerOwnedHouses') or {}
end

Houses.GiveKey = function(houseId, id)
    local result = Core.TriggerServerCallback.await('17mov_Phone:Houses:Server:GiveKey', houseId, id)
    return result
end

Houses.RemoveKey = function(houseId, keyholder)
    TriggerServerEvent('17mov_Phone:Houses:Server:RemoveKey', houseId, keyholder)
end

Houses.LockDoors = function(houseId, bool)
    TriggerServerEvent('17mov_Phone:Houses:Server:LockDoors', houseId, bool)
end

Houses.AddingRemovingHidden = function()
    return false
end

RegisterNetEvent('17mov_Phone:Houses:Removed', function(houseId, targetIdentifier)
end)

Step 3: Create Server Bridge File

Create the file: 17mov_Phone/bridge/housing/kartik-properties/server.lua and paste the following code:

while Config.Housing == "auto" do
    Wait(100)
end

if Config.Housing ~= "kartik-properties" then
    return
end

Houses = {}

Houses.GetPlayerOwnedHouses = function(src)
    local cid = Bridge.GetPlayerIdentifier(src)
    if not cid then return {} end
    return exports['kartik-properties']:GetPlayerHousingData(cid) or {}
end

Houses.GetHouseInfo = function(houseId)
    return exports['kartik-properties']:GetPropertyHousingData(houseId)
end

Houses.GiveKey = function(src, houseId, targetId)
    local result = { success = false, msg = '', data = {} }

    local plyIdentifier = Bridge.GetPlayerIdentifier(src)
    if not plyIdentifier then
        result.msg = 'Houses:Error.Player.Notfound'
        return result
    end

    local targetPlayerIdentifier = Bridge.GetPlayerIdentifier(targetId)
    if not targetPlayerIdentifier then
        result.msg = 'Houses:Error.Player.Notfound'
        return result
    end

    local propertyId = tonumber(houseId) or houseId
    local houseInfo = Houses.GetHouseInfo(houseId)

    if not houseInfo then
        result.msg = 'Houses:Error.Property.Notfound'
        return result
    end

    if tostring(houseInfo.owner) ~= tostring(plyIdentifier) then
        result.msg = 'Houses:Error.NotOwner'
        return result
    end

    if houseInfo.keyholders then
        for _, kh in ipairs(houseInfo.keyholders) do
            if tostring(kh.identifier) == tostring(targetPlayerIdentifier) then
                result.msg = 'Houses:Error.Haskey'
                return result
            end
        end
    end

    local grantRes = exports['kartik-properties']:GrantHousingKey(propertyId, targetPlayerIdentifier, plyIdentifier)
    if not grantRes or not grantRes.success then
        result.msg = grantRes and grantRes.msg or 'Houses:Error.Property.Notfound'
        return result
    end

    result.success = true
    result.data = grantRes.data or {
        name = Bridge.GetPlayerFullName(targetPlayerIdentifier),
        identifier = targetPlayerIdentifier,
    }

    local updatedHouse = Houses.GetHouseInfo(houseId)
    if updatedHouse then
        TriggerClientEvent('17mov_Phone:Houses:Client:AddHouse', targetId, updatedHouse)

        for _, kh in ipairs(updatedHouse.keyholders) do
            local permPlayer = Bridge.GetPlayerByIdentifier(kh.identifier)
            if permPlayer then
                TriggerClientEvent('17mov_Phone:Houses:UpdateKeyholders', permPlayer, houseId, result.data)
            end
        end
    end

    TriggerClientEvent('17mov_Phone:Houses:UpdateKeyholders', src, houseId, result.data)

    if Logger and Logger.Houses and Logger.Houses.GiveKey then
        Logger.Houses.GiveKey(src, houseId, targetId)
    end
    return result
end

Houses.RemoveKey = function(src, houseId, targetIdentifier)
    local playerIdentifier = Bridge.GetPlayerIdentifier(src)
    if not playerIdentifier then return end

    local propertyId = tonumber(houseId) or houseId
    local house = Houses.GetHouseInfo(houseId)
    if not house then return end

    if tostring(house.owner) ~= tostring(playerIdentifier) then return end

    exports['kartik-properties']:RevokeHousingKey(propertyId, targetIdentifier, playerIdentifier)

    local targetPlayer = Bridge.GetPlayerByIdentifier(targetIdentifier)
    if targetPlayer then
        TriggerClientEvent('17mov_Phone:Houses:Client:KeyRevoked', targetPlayer, houseId)
    end

    local removalname = Bridge.GetPlayerFullName(targetIdentifier)

    TriggerClientEvent('17mov_Phone:Houses:UpdateKeyholders', src, houseId, {
        identifier = targetIdentifier,
        name = "Removed"
    })

    for _, kh in ipairs(house.keyholders) do
        if tostring(kh.identifier) ~= tostring(targetIdentifier) then
            local permPlayer = Bridge.GetPlayerByIdentifier(kh.identifier)
            if permPlayer then
                TriggerClientEvent('17mov_Phone:Houses:UpdateKeyholders', permPlayer, houseId, {
                    identifier = targetIdentifier,
                    name = "Removed"
                })
            end
        end
    end

    TriggerClientEvent('17mov_Phone:Houses:Removed', src, houseId, targetIdentifier)

    if Logger and Logger.Houses and Logger.Houses.RemoveKey then
        Logger.Houses.RemoveKey(src, houseId, targetIdentifier)
    end
end

Houses.LockDoors = function(src, houseId, locked)
    local plyIdentifier = Bridge.GetPlayerIdentifier(src)
    if not plyIdentifier then return end

    local propertyId = tonumber(houseId) or houseId
    local houseInfo = Houses.GetHouseInfo(houseId)
    if not houseInfo then return end

    local hasAccess = (tostring(houseInfo.owner) == tostring(plyIdentifier))
    if not hasAccess then
        if houseInfo.keyholders then
            for _, kh in ipairs(houseInfo.keyholders) do
                if tostring(kh.identifier) == tostring(plyIdentifier) then
                    hasAccess = true
                    break
                end
            end
        end
    end

    if not hasAccess then return end

    exports['kartik-properties']:SetPropertyLocked(propertyId, locked)
    TriggerClientEvent('17mov_Phone:Houses:Client:LockDoors', -1, houseId, locked)
end

CreateThread(function()
    Core.RegisterServerCallback('17mov_Phone:Houses:Server:GetPlayerOwnedHouses', function(src)
        return Houses.GetPlayerOwnedHouses(src)
    end)

    Core.RegisterServerCallback('17mov_Phone:Houses:Server:GiveKey', function(src, houseId, targetId)
        return Houses.GiveKey(src, houseId, targetId)
    end)
end)

Core.RegisterNetEvent('17mov_Phone:Houses:Server:RemoveKey', function(src, houseId, keyholder)
    local targetIdentifier = type(keyholder) == 'table' and keyholder.identifier or keyholder
    Houses.RemoveKey(src, houseId, targetIdentifier)
end)

Core.RegisterNetEvent('17mov_Phone:Houses:Server:LockDoors', function(src, houseId, locked)
    Houses.LockDoors(src, houseId, locked)
end)

Step 4: Ensure Startup Order in server.cfg

Make sure kartik-properties starts before 17mov_Phone:

ensure kartik-properties
ensure 17mov_Phone

On this page