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