2026-04-14 17:41:39 +02:00

1208 lines
56 KiB
Lua

--[[
ESX Property - Properties Made Right!
Copyright (C) 2025 ESX-Framework
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
]] local function Log(title, color, fields, Level)
if Level <= Config.Logs.LogLevel then
local webHook = Config.Logs.Webhook
local embedData = {{['title'] = title, ['color'] = color, ['footer'] = {['text'] = "ESX-Property | " .. os.date(),
['icon_url'] = 'https://cdn.discordapp.com/attachments/944789399852417096/1004915039414788116/imageedit_1_2564956129.png'},
['fields'] = fields, ['description'] = "", ['author'] = {['name'] = "ESX-Framework | Log Level " .. Level,
['icon_url'] = 'https://cdn.discordapp.com/emojis/939245183621558362.webp?size=128&quality=lossless'}}}
PerformHttpRequest(webHook, function(err, text, headers)
end, 'POST', json.encode({username = 'ESX-Framework', embeds = embedData}), {['Content-Type'] = 'application/json'})
end
end
local PM = Config.PlayerManagement
local Properties = {}
function PropertiesRefresh()
Properties = {}
local PropertiesList = LoadResourceFile(GetCurrentResourceName(), 'properties.json')
if PropertiesList then
Properties = json.decode(PropertiesList)
Wait(10)
for i = 1, #Properties do
if not Properties[i].furniture then
Properties[i].furniture = {}
end
if not Properties[i].cctv then
Properties[i].cctv = {enabled = false}
end
if not Properties[i].garage then
Properties[i].garage = {enabled = false}
end
if not Properties[i].garage.StoredVehicles then
Properties[i].garage.StoredVehicles = {}
end
if not Properties[i].setName then
Properties[i].setName = ""
end
if not Properties[i].positions then
local Interior = GetInteriorValues(Properties[i].Interior)
Properties[i].positions = Interior.positions
end
Properties[i].plysinside = {}
if Config.OxInventory then
if Properties[i].Owned then
exports.ox_inventory:RegisterStash("property-" .. i, Properties[i].Name, 15, 100000, Properties[i].Owner)
end
else
Properties[i].positions.Storage = nil
end
end
local Players = ESX.GetExtendedPlayers()
Log("ESX-Property Loaded", 11141375, {{name = "Property Count", value = #Properties, inline = true},
{name = "OX Inventory", value = Config.OxInventory and "Enabled" or "Disabled", inline = true}}, 1)
for _, xPlayer in pairs(Players) do
TriggerClientEvent("esx_property:syncProperties", xPlayer.source, Properties, xPlayer.get("lastProperty"))
end
else
Properties = {}
print("[^1ERROR^7]: ^5Properties.json^7 Not Found!")
end
end
function IsPlayerAdmin(player, action)
local xPlayer = ESX.GetPlayerFromId(player)
for i = 1, #Config.AllowedGroups do
if xPlayer.group == Config.AllowedGroups[i] then
return true
end
end
if Config.PlayerManagement.Enabled and action then
if xPlayer.job.name == Config.PlayerManagement.job and xPlayer.job.grade >= Config.PlayerManagement.Permissions[action] then
return true
end
end
return false
end
CreateThread(function()
Wait(3000)
PropertiesRefresh()
MySQL.query("ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `last_property` LONGTEXT NULL", function(result)
if result?.affectedRows > 0 then
print("[^2INFO^7] Added ^5last_property^7 column to users table")
end
end)
-- Check if datastore table exists before to insert values.
if MySQL.scalar.await("SELECT EXISTS (SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_TYPE LIKE 'BASE TABLE' AND TABLE_NAME = 'datastore')") > 0 then
MySQL.insert("INSERT IGNORE INTO `datastore` (name, label, shared) VALUES ('property', 'Property' , 1)", function(affectedRows)
if affectedRows > 0 then
print("[^2INFO^7] Added ^5Property^7 into ^5datastore^7 table")
end
end)
MySQL.insert("INSERT IGNORE INTO `datastore_data` (name, owner, data) VALUES ('property', NULL, '{}')", function(affectedRows)
if affectedRows > 0 then
print("[^2INFO^7] Added ^5Property^7 into ^5datastore_data^7 table")
end
end)
end
if PM.Enabled then
if MySQL.scalar.await("SELECT EXISTS (SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_TYPE LIKE 'BASE TABLE' AND TABLE_NAME = 'datastore')") > 0 then
MySQL.insert("INSERT IGNORE INTO `addon_account` (name, label, shared) VALUES (?, ? , 1)", {PM.society, PM.joblabel}, function(affectedRows)
if affectedRows > 0 then
print("[^2INFO^7] Added ^5" .. PM.society .. " - " .. PM.joblabel .. "^7 into ^5addon_account^7 table")
end
end)
end
if not ESX.DoesJobExist(PM.job, 0) then
MySQL.insert("INSERT INTO `jobs` SET name = ?, label = ?, whitelisted = 1", {PM.job, PM.joblabel}, function(affectedRows)
if affectedRows > 0 then
print("[^2INFO^7] Inserted ^5" .. PM.job .. " - " .. PM.joblabel .. "^7 into ^5jobs^7 table")
end
end)
end
local QUERIES = {}
for i = 1, #PM.jobRanks do
if not ESX.DoesJobExist(PM.job, PM.jobRanks[i].grade) then
QUERIES[i] = {
query = "INSERT INTO `job_grades` SET job_name = ?, grade = ?, name = ?, label = ?, salary = ?, skin_male = '{}', skin_female = '{}'",
parameters = { PM.job, PM.jobRanks[i].grade, PM.jobRanks[i].name, PM.jobRanks[i].label, PM.jobRanks[i].salary }
}
end
end
MySQL.transaction(QUERIES)
Wait(10)
ESX.RefreshJobs()
exports["esx_society"]:registerSociety('realestateagent', 'realestateagent', 'society_realestateagent', 'society_realestateagent', 'society_realestateagent', {type = 'private'})
end
end)
AddEventHandler("esx:playerLoaded", function(playerId, xPlayer)
Wait(1000)
local lastProperty = nil
MySQL.query("SELECT last_property FROM users WHERE identifier = ?", {xPlayer.identifier}, function(result)
if result then
if result[1].last_property then
local Data = json.decode(result[1].last_property)
xPlayer.set("lastProperty", Data)
lastProperty = Data
end
end
TriggerClientEvent("esx_property:syncProperties", playerId, Properties, lastProperty)
end)
end)
--- Commands
ESX.RegisterCommand(_("refresh_name"), Config.AllowedGroups, function(xPlayer)
PropertiesRefresh()
end, false, {help = TranslateCap("refresh_desc")})
ESX.RegisterCommand(_("create_name"), "user", function(xPlayer)
if IsPlayerAdmin(xPlayer.source) or (PM.Enabled and xPlayer.job.name == PM.job) then
xPlayer.triggerEvent("esx_property:CreateProperty")
end
end, false,{help = TranslateCap("create_desc")})
ESX.RegisterCommand(_("admin_name"), Config.AllowedGroups, function(xPlayer)
xPlayer.triggerEvent("esx_property:AdminMenu")
end, false,{help = TranslateCap("admin_desc")})
-- Buy Property
ESX.RegisterServerCallback("esx_property:buyProperty", function(source, cb, PropertyId)
local xPlayer = ESX.GetPlayerFromId(source)
local Price = Properties[PropertyId].Price
local canAfford = xPlayer.getAccount("bank").money >= Price
if canAfford then
xPlayer.removeAccountMoney("bank", Price, "Bought Property")
Properties[PropertyId].Owner = xPlayer.identifier
Properties[PropertyId].OwnerName = xPlayer.getName()
Properties[PropertyId].Owned = true
Log("Property Bought", 65280, {{
name = "**Property Name**",
value = Properties[PropertyId].Name,
inline = true
}, {
name = "**Price**",
value = ESX.Math.GroupDigits(Price),
inline = true
}, {
name = "**Player**",
value = xPlayer.getName(),
inline = true
}}, 1)
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
if Config.OxInventory then
exports.ox_inventory:RegisterStash("property-" .. PropertyId, Properties[PropertyId].Name, 15, 100000, xPlayer.identifier)
end
end
cb(canAfford)
end)
ESX.RegisterServerCallback("esx_property:attemptSellToPlayer", function(source, cb, PropertyId, PlayerId)
local xPlayer = ESX.GetPlayerFromId(source)
local xTarget = ESX.GetPlayerFromId(PlayerId)
local Price = Properties[PropertyId].Price
if xTarget and (xTarget.getAccount("bank").money >= Price) and (xPlayer.job.name == PM.job) then
xTarget.removeAccountMoney("bank", Price, "Sold Property")
Properties[PropertyId].Owner = xTarget.identifier
Properties[PropertyId].OwnerName = xTarget.getName()
Properties[PropertyId].Owned = true
Log("Property Sold To Player", 65280, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Price**", value = ESX.Math.GroupDigits(Price), inline = true},
{name = "**Player**", value = xTarget.getName(), inline = true},
{name = "**Agent**", value = xPlayer.getName(), inline = true}}, 1)
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
if Config.OxInventory then
exports.ox_inventory:RegisterStash("property-" .. PropertyId, Properties[PropertyId].Name, 15, 100000, xTarget.identifier)
end
if PM.Enabled then
local PlayerPrice = Price * PM.SalePercentage
local SocietyPrice = Price - PlayerPrice
TriggerEvent('esx_addonaccount:getSharedAccount', PM.society, function(account)
account.addMoney(SocietyPrice)
end)
xPlayer.addAccountMoney("bank", PlayerPrice, "Sold Property")
end
end
cb(xPlayer.getAccount("bank").money >= Price)
end)
-- Buy Property
ESX.RegisterServerCallback("esx_property:buyFurniture", function(source, cb, PropertyId, PropName, PropIndex, PropCatagory, pos, heading)
local xPlayer = ESX.GetPlayerFromId(source)
local Owner = Properties[PropertyId].Owner
if xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]) then
local Price = Config.FurnitureCatagories[PropCatagory][PropIndex].price
if xPlayer.getAccount("bank").money >= Price then
xPlayer.removeAccountMoney("bank", Price, "Furniture")
cb(true)
local furniture = {Name = PropName, Index = PropIndex, Catagory = PropCatagory, Pos = pos, Heading = heading, Price = Price}
table.insert(Properties[PropertyId].furniture, furniture)
for i = 1, #Properties[PropertyId].plysinside do
TriggerClientEvent("esx_property:placeFurniture", Properties[PropertyId].plysinside[i], PropertyId, furniture,
#Properties[PropertyId].furniture)
end
TriggerClientEvent("esx_property:syncFurniture", -1, PropertyId, Properties[PropertyId].furniture)
else
cb(false)
xPlayer.showNotification(TranslateCap("furni_cannot_afford"))
end
else
cb(false)
end
Log("Furniture Bought", 3640511,
{{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Player**", value = xPlayer.getName(), inline = true}, {name = "**Has Access**",
value = (xPlayer.identifier == Owner or IsPlayerAdmin(source) or
(Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier])) and "Yes" or "No", inline = true},
{name = "**Prop Name**", value = Config.FurnitureCatagories[PropCatagory][PropIndex].title, inline = true},
{name = "**Price**", value = tostring(Config.FurnitureCatagories[PropCatagory][PropIndex].price), inline = true},
{name = "**Can Afford**",
value = xPlayer.getAccount("bank").money >= Config.FurnitureCatagories[PropCatagory][PropIndex].price and "Yes" or "No", inline = true}}, 1)
end)
-- Selling Property
ESX.RegisterServerCallback("esx_property:sellProperty", function(source, cb, PropertyId)
local xPlayer = ESX.GetPlayerFromId(source)
local Owner = Properties[PropertyId].Owner
if xPlayer.identifier == Owner then
local Price = ESX.Math.Round(Properties[PropertyId].Price * 0.6)
xPlayer.addAccountMoney("bank", Price, "Sold Property")
Properties[PropertyId].Owner = ""
Properties[PropertyId].OwnerName = ""
Properties[PropertyId].Owned = false
Properties[PropertyId].Locked = false
Properties[PropertyId].Keys = {}
local furn = #(Properties[PropertyId].furniture)
if Config.WipeFurnitureOnSell then
Properties[PropertyId].furniture = {}
end
if Config.WipeCustomNameOnSell then
Properties[PropertyId].setName = ""
end
Properties[PropertyId].plysinside = {}
Log("Property Sold", 16711680, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Price**", value = ESX.Math.GroupDigits(Price), inline = true},
{name = "**Owner**", value = xPlayer.getName(), inline = true},
{name = "**Furniture Count**", value = tostring(furn), inline = true},
{name = "**Vehicle Count**", value = tostring(Properties[PropertyId].garage.StoredVehicles and #Properties[PropertyId].garage.StoredVehicles or "N/A"), inline = true}}, 1)
if Properties[PropertyId].garage.StoredVehicles then
Properties[PropertyId].garage.StoredVehicles = {}
end
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
if Config.OxInventory then
exports.ox_inventory:ClearInventory("property-" .. PropertyId)
end
end
cb(xPlayer.identifier == Owner)
end)
-- Admin Menu Options
ESX.RegisterServerCallback("esx_property:toggleLock", function(source, cb, PropertyId)
local xPlayer = ESX.GetPlayerFromId(source)
local Owner = Properties[PropertyId].Owner
if xPlayer.identifier == Owner or IsPlayerAdmin(source, "ToggleLock") or
(Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]) then
Properties[PropertyId].Locked = not Properties[PropertyId].Locked
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
end
Log("Lock Toggled", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
{name = "**Executing User**", value = xPlayer.getName(), inline = true},
{name = "**Status**", value = Properties[PropertyId].Locked and "Locked" or "Unlocked", inline = true}}, 3)
cb(xPlayer.identifier == Owner or IsPlayerAdmin(source, "ToggleLock") or
(Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]))
end)
ESX.RegisterServerCallback("esx_property:toggleGarage", function(source, cb, PropertyId)
local xPlayer = ESX.GetPlayerFromId(source)
if IsPlayerAdmin(source, "ToggleGarage") then
Properties[PropertyId].garage.enabled = not Properties[PropertyId].garage.enabled
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
Log("Property Garage Toggled", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
{name = "**Admin**", value = xPlayer.getName(), inline = true},
{name = "**Status**", value = Properties[PropertyId].garage.enabled and "Enabled" or "Disabled",
inline = true}}, 2)
cb(true, Properties[PropertyId].garage.enabled)
else
cb(false)
end
end)
ESX.RegisterServerCallback("esx_property:toggleCCTV", function(source, cb, PropertyId)
local xPlayer = ESX.GetPlayerFromId(source)
if IsPlayerAdmin(source, "ToggleCCTV") then
Properties[PropertyId].cctv.enabled = not Properties[PropertyId].cctv.enabled
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
cb(true, Properties[PropertyId].cctv.enabled)
Log("Property CCTV Toggled", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
{name = "**Admin**", value = xPlayer.getName(), inline = true},
{name = "**Status**", value = Properties[PropertyId].cctv.enabled and "Enabled" or "Disabled",
inline = true}}, 2)
else
cb(false)
end
end)
ESX.RegisterServerCallback("esx_property:SetGaragePos", function(source, cb, PropertyId, heading)
local xPlayer = ESX.GetPlayerFromId(source)
if IsPlayerAdmin(source, "ToggleGarage") then
local PlayerPed = GetPlayerPed(source)
local PlayerPos = GetEntityCoords(PlayerPed)
local Property = Properties[PropertyId]
local Original = Properties[PropertyId].garage.pos and Properties[PropertyId].garage.pos.x .. ", " .. Properties[PropertyId].garage.pos.y .. ", " .. Properties[PropertyId].garage.pos.z or "N/A"
Properties[PropertyId].garage.pos = PlayerPos
Properties[PropertyId].garage.Heading = heading
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
Log("Property Garage Location Changed", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
{name = "**Admin**", value = xPlayer.getName(), inline = true},
{name = "**Original Position**", value = tostring(Original), inline = true},
{name = "**New Position**", value = tostring(PlayerPos), inline = true}}, 2)
cb(true)
else
cb(false)
end
end)
ESX.RegisterServerCallback("esx_property:SetCCTVangle", function(source, cb, PropertyId, angles)
local xPlayer = ESX.GetPlayerFromId(source)
if IsPlayerAdmin(source, "ToggleCCTV") then
local Property = Properties[PropertyId]
Properties[PropertyId].cctv.rot = angles.rot
Properties[PropertyId].cctv.maxleft = angles.maxleft
Properties[PropertyId].cctv.maxright = angles.maxright
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
cb(true)
Log("Property CCTV Angle Changed", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
{name = "**Admin**", value = xPlayer.getName(), inline = true}}, 2)
else
cb(false)
end
end)
ESX.RegisterServerCallback("esx_property:CCTV", function(source, cb, PropertyId)
local xPlayer = ESX.GetPlayerFromId(source)
local Owner = Properties[PropertyId].Owner
local Property = Properties[PropertyId]
if xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]) then
SetEntityCoords(GetPlayerPed(source), vector3(Property.Entrance.x, Property.Entrance.y, Property.Entrance.z + 5.0))
SetPlayerRoutingBucket(source, 0)
end
Log("Player Entered CCTV", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Player**", value = xPlayer.getName(), inline = true}}, 3)
cb(xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]))
end)
ESX.RegisterServerCallback("esx_property:ExitCCTV", function(source, cb, PropertyId)
local xPlayer = ESX.GetPlayerFromId(source)
local Owner = Properties[PropertyId].Owner
local Property = Properties[PropertyId]
if xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]) then
local Interior = GetInteriorValues(Property.Interior)
if Interior.type == "shell" then
SetEntityCoords(GetPlayerPed(source), vector3(Property.Entrance.x, Property.Entrance.y, 2001))
else
SetEntityCoords(GetPlayerPed(source), Interior.pos)
end
SetPlayerRoutingBucket(source, PropertyId + 1)
end
Log("Player Exited CCTV", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Player**", value = xPlayer.getName(), inline = true}}, 3)
cb(xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]))
end)
ESX.RegisterServerCallback("esx_property:SetPropertyName", function(source, cb, PropertyId, name)
local xPlayer = ESX.GetPlayerFromId(source)
local Owner = Properties[PropertyId].Owner
local Property = Properties[PropertyId]
if xPlayer.identifier == Owner or IsPlayerAdmin(source) then
if name and #name <= Config.MaxNameLength then
Properties[PropertyId].setName = name
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
Log("Property Name Changed", 3640511,
{{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Player**", value = xPlayer.getName(), inline = true}, {name = "**New Name**", value = name, inline = true}}, 2)
end
end
cb((xPlayer.identifier == Owner or IsPlayerAdmin(source, "SetPropertyName")) and name and #name <= Config.MaxNameLength)
end)
ESX.RegisterServerCallback("esx_property:KnockOnDoor", function(source, cb, PropertyId, name)
local xPlayer = ESX.GetPlayerFromId(source)
local Property = Properties[PropertyId]
local Owner = ESX.GetPlayerFromIdentifier(Property.Owner)
if Owner then
for i = 1, #(Property.plysinside) do
if Property.plysinside[i] == Owner.source then
Owner.showNotification(TranslateCap("knocking"), "info")
cb(true)
Log("Player Knocked On Door", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
{name = "**Player**", value = xPlayer.getName(), inline = true}}, 3)
end
end
cb(false)
else
cb(false)
end
end)
ESX.RegisterServerCallback("esx_property:RemoveCustomName", function(source, cb, PropertyId, name)
local Property = Properties[PropertyId]
local xPlayer = ESX.GetPlayerFromId(source)
if IsPlayerAdmin(source, "RemovePropertyName") then
local n = Properties[PropertyId].setName
Properties[PropertyId].setName = ""
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
Log("Property Name Reset", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
{name = "**Admin**", value = xPlayer.getName(), inline = true},
{name = "**Removed Name**", value = n, inline = true}}, 3)
cb(true)
else
cb(false)
end
end)
ESX.RegisterServerCallback("esx_property:deleteProperty", function(source, cb, PropertyId)
local xPlayer = ESX.GetPlayerFromId(source)
if IsPlayerAdmin(source, "DeleteProperty") then
Log("Property Deleted", 16711680,
{{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Admin**", value = xPlayer.getName(), inline = true},
{name = "**Owner**", value = Properties[PropertyId].OwnerName ~= "" and Properties[PropertyId].OwnerName or "N/A", inline = true},
{name = "**Furniture Count**", value = #(Properties[PropertyId].furniture), inline = true},
{name = "**Vehicle Count**", value = Properties[PropertyId].garage.StoredVehicles and #(Properties[PropertyId].garage.StoredVehicles) or "N/A", inline = true}}, 1)
table.remove(Properties, PropertyId)
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
if Config.OxInventory then
exports.ox_inventory:ClearInventory("property-" .. PropertyId)
end
end
cb(IsPlayerAdmin(source, "DeleteProperty"))
end)
ESX.RegisterServerCallback("esx_property:ChangePrice", function(source, cb, PropertyId, NewPrice)
local xPlayer = ESX.GetPlayerFromId(source)
if IsPlayerAdmin(source, "SetPropertyPrice") then
local Original = Properties[PropertyId].Price
Properties[PropertyId].Price = NewPrice
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
Log("Property Price Changed", 3640511,
{{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Admin**", value = xPlayer.getName(), inline = true}, {name = "**Original Price**", value = tostring(Original), inline = true},
{name = "**New Price**", value = tostring(NewPrice), inline = true}}, 2)
end
cb(IsPlayerAdmin(source, "SetPropertyPrice"))
end)
ESX.RegisterServerCallback("esx_property:ChangeInterior", function(source, cb, PropertyId, Interior)
local xPlayer = ESX.GetPlayerFromId(source)
if IsPlayerAdmin(source, "ChangeInterior") then
local Original = GetInteriorValues(Properties[PropertyId].Interior).label
Properties[PropertyId].Interior = Interior
Properties[PropertyId].furniture = {}
Properties[PropertyId].positions = GetInteriorValues(Interior).positions
if not Config.OxInventory then
Properties[PropertyId].positions.Storage = nil
end
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
Log("Property Interior Changed", 3640511,
{{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Admin**", value = xPlayer.getName(), inline = true}, {name = "**Original**", value = tostring(Original), inline = true},
{name = "**New Interior**", value = GetInteriorValues(Properties[PropertyId].Interior).label, inline = true}}, 2)
end
cb(IsPlayerAdmin(source, "ChangeInterior"))
end)
ESX.RegisterServerCallback("esx_property:RemoveAllfurniture", function(source, cb, PropertyId)
local xPlayer = ESX.GetPlayerFromId(source)
local Owner = Properties[PropertyId].Owner
if xPlayer.identifier == Owner or IsPlayerAdmin(source, "ResetFurniture") or
(Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]) then
for i = 1, #Properties[PropertyId].plysinside do
for furniture = 1, #Properties[PropertyId].furniture do
TriggerClientEvent("esx_property:removeFurniture", Properties[PropertyId].plysinside[i], PropertyId, furniture)
end
end
Properties[PropertyId].furniture = {}
TriggerClientEvent("esx_property:syncFurniture", -1, PropertyId, Properties[PropertyId].furniture)
end
Log("Property Furniture Reset", 16711680, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Admin**", value = xPlayer.getName(), inline = true}}, 1)
cb(xPlayer.identifier == Owner or IsPlayerAdmin(source, "ResetFurniture") or
(Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]))
end)
ESX.RegisterServerCallback("esx_property:deleteFurniture", function(source, cb, PropertyId, furnitureIndex)
local xPlayer = ESX.GetPlayerFromId(source)
local Owner = Properties[PropertyId].Owner
if xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]) then
if Properties[PropertyId].furniture[furnitureIndex] then
Properties[PropertyId].furniture[furnitureIndex] = nil
TriggerClientEvent("esx_property:syncFurniture", -1, PropertyId, Properties[PropertyId].furniture)
end
for i = 1, #Properties[PropertyId].plysinside do
TriggerClientEvent("esx_property:removeFurniture", Properties[PropertyId].plysinside[i], PropertyId, furnitureIndex)
end
end
Log("Property Furniture Deleted", 16711680,
{{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true}, {name = "**Admin**", value = xPlayer.getName(), inline = true},
{name = "**Furniture Name**", value = xPlayer.getName(), inline = true}}, 3)
cb(xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]))
end)
ESX.RegisterServerCallback("esx_property:editFurniture", function(source, cb, PropertyId, furnitureIndex, Pos, Heading)
local xPlayer = ESX.GetPlayerFromId(source)
local Owner = Properties[PropertyId].Owner
if xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]) then
if Properties[PropertyId].furniture[furnitureIndex] then
Properties[PropertyId].furniture[furnitureIndex].Pos = Pos
Properties[PropertyId].furniture[furnitureIndex].Heading = Heading
TriggerClientEvent("esx_property:syncFurniture", -1, PropertyId, Properties[PropertyId].furniture)
for i = 1, #Properties[PropertyId].plysinside do
TriggerClientEvent("esx_property:editFurniture", Properties[PropertyId].plysinside[i], PropertyId, furnitureIndex, Pos, Heading)
end
end
end
cb(xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]))
end)
ESX.RegisterServerCallback("esx_property:evictOwner", function(source, cb, PropertyId, Interior)
local xPlayer = ESX.GetPlayerFromId(source)
if IsPlayerAdmin(source, "EvictOwner") then
local xOwner = ESX.GetPlayerFromIdentifier(Properties[PropertyId].Owner)
if xOwner then
xOwner.showNotification("Your Have Been ~r~Evicted~s~.", "error")
end
local pName = Properties[PropertyId].OwnerName
Properties[PropertyId].Owner = ""
Properties[PropertyId].Owned = false
Properties[PropertyId].OwnerName = ""
Properties[PropertyId].Locked = false
Properties[PropertyId].Keys = {}
if Config.WipeFurnitureOnSell then
Properties[PropertyId].furniture = {}
end
Properties[PropertyId].setName = ""
Properties[PropertyId].plysinside = {}
if Properties[PropertyId].garage.StoredVehicles then
Properties[PropertyId].garage.StoredVehicles = {}
end
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
if Config.OxInventory then
exports.ox_inventory:ClearInventory("property-" .. PropertyId)
end
Log("Property Owner Evicted", 3640511,
{{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true}, {name = "**Owner**", value = pName, inline = true},
{name = "**Admin**", value = xPlayer.getName(), inline = true},
{name = "**Has Access**", value = IsPlayerAdmin(source, "EvictOwner") and "Yes" or "No", inline = true}}, 1)
end
cb(IsPlayerAdmin(source, "EvictOwner"))
end)
ESX.RegisterServerCallback("esx_property:CanRaid", function(source, cb, PropertyId, Interior)
local xPlayer = ESX.GetPlayerFromId(source)
local Can = false
if Config.Raiding.Enabled then
if (Config.CanAdminsRaid and IsPlayerAdmin(source)) or xPlayer.job.name == "police" then
if Config.Raiding.ItemRequired then
local itemCount = xPlayer.getInventoryItem(Config.Raiding.ItemRequired.name).count
if itemCount >= Config.Raiding.ItemRequired.ItemCount then
if Config.Raiding.ItemRequired.RemoveItem then
xPlayer.removeInventoryItem(Config.Raiding.ItemRequired.name, Config.Raiding.ItemRequired.ItemCount)
end
Can = true
else
xPlayer.showNotification(TranslateCap("raid_notify_error", Config.Raiding.ItemRequired.ItemCount, Config.Raiding.ItemRequired.name), "error")
end
else
Can = true
end
end
end
cb(Can)
if Can then
local xOwner = ESX.GetPlayerFromIdentifier(Properties[PropertyId].Owner)
if xOwner then
xOwner.showNotification(TranslateCap("raid_notify_success"), "error")
end
Wait(15000)
Properties[PropertyId].Locked = false
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
end
end)
ESX.RegisterServerCallback("esx_property:ChangeEntrance", function(source, cb, PropertyId, Coords)
local xPlayer = ESX.GetPlayerFromId(source)
if IsPlayerAdmin(source, "ChangeEntrance") then
local Origonal = Properties[PropertyId].Entrance.x .. "," .. Properties[PropertyId].Entrance.y .. "," .. Properties[PropertyId].Entrance.z
Properties[PropertyId].Entrance = {x = ESX.Math.Round(Coords.x, 2), y = ESX.Math.Round(Coords.y, 2), z = ESX.Math.Round(Coords.z, 2) - 0.8}
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
Log("Property Entrance Changed", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
{name = "**Admin**", value = xPlayer.getName(), inline = true},
{name = "**Has Access**", value = IsPlayerAdmin(source, "ChangeEntrance") and "Yes" or "No",
inline = true}, {name = "**Original**", value = Origonal, inline = true},
{name = "**New**",
value = Properties[PropertyId].Entrance.x .. "," .. Properties[PropertyId].Entrance.y .. "," ..
Properties[PropertyId].Entrance.z, inline = true}}, 1)
end
cb(IsPlayerAdmin(source, "ChangeEntrance"))
end)
ESX.RegisterServerCallback("esx_property:SetInventoryPosition", function(source, cb, PropertyId, Coords, Reset)
if Config.OxInventory then
local Property = Properties[PropertyId]
local xPlayer = ESX.GetPlayerFromId(source)
if IsPlayerAdmin(source, "EditInteriorPositions") or (Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier]) then
local Interior = GetInteriorValues(Property.Interior)
if Reset then
Properties[PropertyId].positions.Storage = {x = ESX.Math.Round(Coords.x, 2), y = ESX.Math.Round(Coords.y, 2), z = ESX.Math.Round(Coords.z, 2)}
else
if Interior.type == "ipl" then
Properties[PropertyId].positions.Storage = {x = ESX.Math.Round(Coords.x, 2), y = ESX.Math.Round(Coords.y, 2),
z = ESX.Math.Round(Coords.z, 2)}
else
Properties[PropertyId].positions.Storage = vector3(Property.Entrance.x, Property.Entrance.y, 2000) - Coords
end
end
Log("Property Storage Location Set", 3640511,
{{name = "**Property Name**", value = Property.Name, inline = true}, {name = "**Owner**", value = Property.OwnerName, inline = true},
{name = "**Player**", value = xPlayer.getName(), inline = true}, {name = "**Has Access**",
value = (IsPlayerAdmin(source, "EditInteriorPositions") or
(Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier])) and "Yes" or "No", inline = true},
{name = "**Reset?**", value = Reset and "Yes" or "No", inline = true}}, 1)
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
end
cb(IsPlayerAdmin(source, "EditInteriorPositions") or (Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier]))
else
cb(false)
end
end)
-- Wardrobe
ESX.RegisterServerCallback("esx_property:SetWardrobePosition", function(source, cb, PropertyId, Coords, Reset)
local Property = Properties[PropertyId]
local xPlayer = ESX.GetPlayerFromId(source)
if IsPlayerAdmin(source, "EditInteriorPositions") or (Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier]) then
local Interior = GetInteriorValues(Property.Interior)
if Reset then
Properties[PropertyId].positions.Wardrobe = Interior.positions.Wardrobe
else
if Interior.type == "ipl" then
Properties[PropertyId].positions.Wardrobe =
{x = ESX.Math.Round(Coords.x, 2), y = ESX.Math.Round(Coords.y, 2), z = ESX.Math.Round(Coords.z, 2)}
else
Properties[PropertyId].positions.Wardrobe = vector3(Property.Entrance.x, Property.Entrance.y, 1999.8) - Coords
end
Log("Property Wardrobe Location Set", 3640511,
{{name = "**Property Name**", value = Property.Name, inline = true}, {name = "**Owner**", value = Property.OwnerName, inline = true},
{name = "**Player**", value = xPlayer.getName(), inline = true}, {name = "**Has Access**",
value = (IsPlayerAdmin(source, "EditInteriorPositions") or
(Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier])) and "Yes" or "No", inline = true},
{name = "**Reset?**", value = Reset and "Yes" or "No", inline = true}}, 1)
end
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
end
cb(IsPlayerAdmin(source, "EditInteriorPositions") or (Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier]))
end)
ESX.RegisterServerCallback('esx_property:getPlayerDressing', function(source, cb)
local xPlayer = ESX.GetPlayerFromId(source)
TriggerEvent('esx_datastore:getDataStore', 'property', xPlayer.identifier, function(store)
local count = store.count('dressing')
local labels = {}
for i = 1, count, 1 do
local entry = store.get('dressing', i)
table.insert(labels, entry.label)
end
cb(labels)
end)
end)
ESX.RegisterServerCallback('esx_property:GetInsidePlayers', function(source, cb, property)
local Property = Properties[property]
local Players = {}
local xPlayer = ESX.GetPlayerFromId(source)
local NearbyPlayers = Property.plysinside
for k, v in pairs(NearbyPlayers) do
local xPlayer = ESX.GetPlayerFromId(v)
if not Properties[property].Keys then
Properties[property].Keys = {}
end
if xPlayer.identifier ~= Property.Owner and not Properties[property].Keys[xPlayer.identifier] then
Players[#Players + 1] = {Name = xPlayer.getName(), Id = xPlayer.source}
end
end
cb(Players)
end)
ESX.RegisterServerCallback('esx_property:GetNearbyPlayers', function(source, cb, property)
local Property = Properties[property]
local Players = {}
local xPlayer = ESX.GetPlayerFromId(source)
local NearbyPlayers = ESX.OneSync.GetPlayersInArea(vector3(Property.Entrance.x, Property.Entrance.y, Property.Entrance.z), 5.0)
Wait(100)
for k, v in pairs(NearbyPlayers) do
local xTarget = ESX.GetPlayerFromId(v.id)
if xPlayer.identifier ~= xTarget.identifier then
Players[#Players + 1] = {name = xTarget.getName(), source = xTarget.source}
end
end
cb(Players)
end)
ESX.RegisterServerCallback('esx_property:GetPlayersWithKeys', function(source, cb, property)
local Property = Properties[property]
local Players = {}
local xPlayer = ESX.GetPlayerFromId(source)
if xPlayer.identifier == Property.Owner then
cb(Property.Keys or {})
end
end)
ESX.RegisterServerCallback('esx_property:ShouldHaveKey', function(source, cb, property)
local xPlayer = ESX.GetPlayerFromId(source)
cb(Properties[property].Keys[xPlayer.identifier])
end)
ESX.RegisterServerCallback('esx_property:GetWebhook', function(source, cb, property)
cb(Config.CCTV.PictureWebook)
end)
ESX.RegisterServerCallback('esx_property:RemoveLastProperty', function(source, cb, property)
local xPlayer = ESX.GetPlayerFromId(source)
MySQL.query("UPDATE `users` SET `last_property` = NULL WHERE `identifier` = ?", {xPlayer.identifier}) -- Remove Saved Data
SetPlayerRoutingBucket(source, 0) -- Reset Routing Bucket
xPlayer.set("lastProperty", nil)
cb()
end)
ESX.RegisterServerCallback('esx_property:GiveKey', function(source, cb, property, player)
local xPlayer = ESX.GetPlayerFromId(source)
local xTarget = ESX.GetPlayerFromId(player)
local Property = Properties[property]
if Property.Owner == xPlayer.identifier then
if not Property.Keys then
Properties[property].Keys = {}
end
local id = xTarget.identifier
if not Properties[property].Keys[id] then
Property.Keys[id] = {name = xTarget.getName(), identifier = id}
xTarget.showNotification(TranslateCap("you_granted", Property.Name), 'success')
xTarget.triggerEvent("esx_property:giveKeyAccess")
cb(true)
else
xPlayer.showNotification(TranslateCap("already_has"), 'error')
cb(false)
end
else
xPlayer.showNotification(TranslateCap("do_not_own"), 'error')
cb(false)
end
Log("Property Key Given", 3640511,
{{name = "**Property Name**", value = Property.Name, inline = true}, {name = "**Owner**", value = Property.OwnerName, inline = true},
{name = "**Player**", value = xPlayer.getName(), inline = true},
{name = "**Has Access**", value = Property.Owner == xPlayer.identifier and "Yes" or "No", inline = true}}, 1)
end)
ESX.RegisterServerCallback('esx_property:StoreVehicle', function(source, cb, PropertyId, VehicleProperties)
local xPlayer = ESX.GetPlayerFromId(source)
local Property = Properties[PropertyId]
if Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier] then
if Property.garage.enabled then
if Config.Garage.OwnedVehiclesOnly then
MySQL.scalar("SELECT `owner` FROM `owned_vehicles` WHERE `plate` = ?", {VehicleProperties.plate}, function(result)
if result then
if result == xPlayer.identifier then
Properties[PropertyId].garage.StoredVehicles[#Properties[PropertyId].garage.StoredVehicles + 1] = {owner = xPlayer.identifier,
vehicle = VehicleProperties}
cb(true)
elseif (Properties[PropertyId].Keys[result] or Property.Owner == result) then
Properties[PropertyId].garage.StoredVehicles[#Properties[PropertyId].garage.StoredVehicles + 1] = {owner = xPlayer.identifier,
vehicle = VehicleProperties}
cb(true)
else
cb(false)
end
else
cb(false)
end
end)
else
Properties[PropertyId].garage.StoredVehicles[#Properties[PropertyId].garage.StoredVehicles + 1] = {owner = xPlayer.identifier,
vehicle = VehicleProperties}
cb(true)
end
MySQL.query(Config.Garage.MySQLquery, {1, VehicleProperties.plate}) -- Set vehicle as stored in MySQL
else
xPlayer.showNotification(TranslateCap("garage_not_enabled"), 'error')
cb(false)
end
else
xPlayer.showNotification(TranslateCap("cannot_access_property"), 'error')
cb(false)
end
Log("User Attempted To Store Vehicle", 3640511,
{{name = "**Property Name**", value = Property.Name, inline = true}, {name = "**Owner**", value = Property.OwnerName, inline = true},
{name = "**Player**", value = xPlayer.getName(), inline = true},
{name = "**Has Access**", value = (Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier]) and "Yes" or "No",
inline = true}, {name = "**Garage Status**", value = Property.garage.enabled and "Enabled" or "Disabled", inline = true},
{name = "**Vehicle Name**", value = VehicleProperties.DisplayName, inline = true}}, 2)
end)
ESX.RegisterServerCallback('esx_property:AccessGarage', function(source, cb, PropertyId, VehicleProperties)
local xPlayer = ESX.GetPlayerFromId(source)
local Property = Properties[PropertyId]
if Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier] then
if Property.garage.enabled then
cb(Property.garage.StoredVehicles)
else
xPlayer.showNotification(TranslateCap("garage_not_enabled"), 'error')
cb(false)
end
else
xPlayer.showNotification(TranslateCap("cannot_access_property"), 'error')
cb(false)
end
Log("User Opened Garage Menu", 3640511,
{{name = "**Property Name**", value = Property.Name, inline = true}, {name = "**Owner**", value = Property.OwnerName, inline = true},
{name = "**Player**", value = xPlayer.getName(), inline = true},
{name = "**Garage Status**", value = Property.garage.enabled and "Enabled" or "Disabled", inline = true}}, 2)
end)
ESX.RegisterServerCallback('esx_property:RemoveKey', function(source, cb, property, player)
local xPlayer = ESX.GetPlayerFromId(source)
local xTarget = ESX.GetPlayerFromIdentifier(player)
local Property = Properties[property]
if Property.Owner == xPlayer.identifier then
if Property.Keys then
if Properties[property].Keys[player] then
Log("Property Key Revoked", 3640511,
{{name = "**Property Name**", value = Property.Name, inline = true}, {name = "**Owner**", value = xPlayer.getName(), inline = true},
{name = "**Removed From**", value = tostring(Properties[property].Keys[player].name), inline = true}}, 3)
Properties[property].Keys[player] = nil
xTarget.showNotification(TranslateCap("key_revoked", Property.Name), 'error')
xTarget.triggerEvent("esx_property:RemoveKeyAccess", property)
cb(true)
else
xPlayer.showNotification(TranslateCap("no_keys"), 'error')
cb(false)
end
else
cb(false)
end
else
xPlayer.showNotification(TranslateCap("do_not_own"), 'error')
cb(false)
end
end)
ESX.RegisterServerCallback('esx_property:CanOpenFurniture', function(source, cb, property)
local xPlayer = ESX.GetPlayerFromId(source)
local Property = Properties[property]
cb(Property.Owner == xPlayer.identifier or (Property.Keys and Properties[property].Keys[xPlayer.identifier]))
end)
ESX.RegisterServerCallback('esx_property:getPlayerOutfit', function(source, cb, num)
local xPlayer = ESX.GetPlayerFromId(source)
TriggerEvent('esx_datastore:getDataStore', 'property', xPlayer.identifier, function(store)
local outfit = store.get('dressing', num)
cb(outfit.skin)
end)
end)
-- Player Management
if PM.Enabled then
ESX.RegisterServerCallback('esx_property:PMenterOffice', function(source, cb)
local xPlayer = ESX.GetPlayerFromId(source)
local PlayerPed = GetPlayerPed(source)
if xPlayer.job.name == PM.job then
SetEntityCoords(PlayerPed, PM.Locations.Exit)
SetPlayerRoutingBucket(source, 1)
cb(true)
else
cb(false)
end
end)
ESX.RegisterServerCallback('esx_property:PMexitOffice', function(source, cb)
local xPlayer = ESX.GetPlayerFromId(source)
local PlayerPed = GetPlayerPed(source)
if xPlayer.job.name == PM.job then
SetEntityCoords(PlayerPed, PM.Locations.Entrance)
SetPlayerRoutingBucket(source, 0)
cb(true)
else
cb(false)
end
end)
end
-- Enter/leave Events
RegisterNetEvent('esx_property:enter', function(PropertyId)
local player = source
local PlayerPed = GetPlayerPed(player)
local xPlayer = ESX.GetPlayerFromId(player)
local Property = Properties[PropertyId]
local Interior = GetInteriorValues(Property.Interior)
if not Properties[PropertyId].plysinside then
Properties[PropertyId].plysinside = {}
end
table.insert(Properties[PropertyId].plysinside, player)
local PropertyData = {id = PropertyId, coords = Property.Entrance} -- Save the property data to the table
MySQL.query("UPDATE `users` SET `last_property` = ? WHERE `identifier` = ?", {json.encode(PropertyData), xPlayer.identifier}) -- Save the property data to the database
xPlayer.set("lastProperty", PropertyData)
if Interior.type == "shell" then
SetEntityCoords(PlayerPed, vector3(Property.Entrance.x, Property.Entrance.y, 2001))
else
SetEntityCoords(PlayerPed, Interior.pos)
SetEntityHeading(PlayerPed, 0.0)
end
SetPlayerRoutingBucket(player, PropertyId + 1)
Log("Player Entered Property", 3640511,
{{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Player**", value = xPlayer.getName(), inline = true},
{name = "**Property Player Count**", value = tostring(#(Properties[PropertyId].plysinside)), inline = true}}, 3)
end)
RegisterNetEvent('esx_property:leave', function(PropertyId)
local player = source
local Property = Properties[PropertyId]
local xPlayer = ESX.GetPlayerFromId(player)
MySQL.query("UPDATE `users` SET `last_property` = NULL WHERE `identifier` = ?", {xPlayer.identifier}) -- Remove Saved Data
xPlayer.set("lastProperty", nil)
SetEntityCoords(player, vector3(Property.Entrance.x, Property.Entrance.y, Property.Entrance.z))
SetEntityHeading(player, 0.0)
SetPlayerRoutingBucket(player, 0)
for i = 1, #(Properties[PropertyId].plysinside) do
if Properties[PropertyId].plysinside[i] == player then
table.remove(Properties[PropertyId].plysinside, i)
break
end
end
Log("Player Left Property", 3640511,
{{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
{name = "**Player**", value = xPlayer.getName(), inline = true},
{name = "**Property Player Count**", value = tostring(#(Properties[PropertyId].plysinside)), inline = true}}, 3)
end)
RegisterNetEvent('esx_property:SetVehicleOut', function(PropertyId, VehIndex)
local VehicleData = Properties[PropertyId].garage.StoredVehicles[VehIndex]
local plate = VehicleData.vehicle.plate
table.remove(Properties[PropertyId].garage.StoredVehicles, VehIndex)
MySQL.query(Config.Garage.MySQLquery, {0, plate}) -- Set vehicle as no longer stored
end)
AddEventHandler('playerDropped', function()
local source = source
for PropertyId = 1, #Properties do
for i = 1, #(Properties[PropertyId].plysinside) do
if Properties[PropertyId].plysinside[i] == source then
table.remove(Properties[PropertyId].plysinside, i)
break
end
end
end
end)
ESX.RegisterServerCallback('esx_property:CanCreateProperty', function(source, cb)
local Re = false
local xPlayer = ESX.GetPlayerFromId(source)
if xPlayer then
if IsPlayerAdmin(source, "CreateProperty") then
Re = true
end
end
cb(Re)
end)
ESX.RegisterServerCallback('esx_property:IsAdmin', function(source, cb)
cb(IsPlayerAdmin(source, "ViewProperties"))
end)
ESX.RegisterServerCallback('esx_property:CanAccessRealEstateMenu', function(source, cb)
local xPlayer = ESX.GetPlayerFromId(source)
local Re = (Config.PlayerManagement.Enabled and xPlayer.job.name == Config.PlayerManagement.job and xPlayer.job.grade >= Config.PlayerManagement.Permissions.ManagePropertiesFromQuickActions) and true or false
cb(Re)
end)
RegisterNetEvent('esx_property:server:createProperty', function(Property)
local source = source
local xPlayer = ESX.GetPlayerFromId(source)
local Interior = GetInteriorValues(Property.interior)
local garageData =
Property.garage.enabled and {enabled = true, pos = Property.garage.pos, Heading = Property.garage.heading, StoredVehicles = {}} or
{enabled = false}
if IsPlayerAdmin(source, "CreateProperty") then
local ActualProperty = {Name = Property.name, setName = "", Price = Property.price, furniture = {}, plysinside = {}, Interior = Property.interior,
Entrance = Property.entrance, Owner = "", Keys = {}, positions = Interior.positions, cctv = Property.cctv,
garage = garageData, Owned = false, Locked = false}
Properties[#Properties + 1] = ActualProperty
end
Log("Property Created", 65280,
{{name = "**Admin**", value = xPlayer.getName(), inline = true}, {name = "**Name**", value = Property.name, inline = true},
{name = "**Price**", value = ESX.Math.GroupDigits(Property.price), inline = true},
{name = "**Interior**", value = Interior.label, inline = true},
{name = "**Garage Status**", value = Property.garage.enabled and "Enabled" or "Disabled", inline = true},
{name = "**CCTV Status**", value = Property.cctv.enabled and "Enabled" or "Disabled", inline = true},
{name = "**Entrance**", value = tostring(Property.entrance.x .. ", " .. Property.entrance.y .. ", " .. Property.entrance.z), inline = true}}, 1)
TriggerClientEvent("esx_property:syncProperties", -1, Properties)
end)
-- Json File Saving
--- Save Properties On Server Scheduled Restart
AddEventHandler('txAdmin:events:scheduledRestart', function(eventData)
if eventData.secondsRemaining == 60 then
CreateThread(function()
Wait(50000)
if Properties and #Properties > 0 then
SaveResourceFile(GetCurrentResourceName(), 'properties.json', json.encode(Properties))
Log("Properties Saving", 11141375, {{name = "**Reason**", value = "Scheduled Server Restart", inline = true},
{name = "**Property Count**", value = tostring(#Properties), inline = true}}, 1)
end
end)
end
end)
function PropertySave(Reason)
if Properties and #Properties > 0 then
SaveResourceFile(GetCurrentResourceName(), 'properties.json', json.encode(Properties))
Log("Properties Saving", 11141375,
{{name = "**Reason**", value = Reason, inline = true}, {name = "**Property Count**", value = tostring(#Properties), inline = true}}, 1)
end
end
--- Save Properties On Server Stop/Restart
AddEventHandler('txAdmin:events:serverShuttingDown', function()
PropertySave(TranslateCap("server_shutdown"))
end)
--- Save Properties On Resource Stop/Restart
AddEventHandler('onResourceStop', function(ResourceName)
if ResourceName == GetCurrentResourceName() then
PropertySave(TranslateCap("resource_stop"))
end
end)
AddEventHandler('onServerResourceStop', function(ResourceName)
if ResourceName == GetCurrentResourceName() then
PropertySave(TranslateCap("resource_stop"))
end
end)
-- Save Properties every x Minutes
CreateThread(function()
while true do
Wait(60000 * Config.SaveInterval)
PropertySave(TranslateCap("interval_saving"))
end
end)
ESX.RegisterCommand(_("save_name"), Config.AllowedGroups, function(xPlayer)
PropertySave(TranslateCap("manual_save", GetPlayerName(xPlayer.source)))
end, false,{help = TranslateCap("save_desc")})
----- Exports -----
exports("GetProperties", function()
return Properties
end)
exports("GetOwnedProperties", function()
local OwnedProperties = {}
for i=1, #Properties do
if Properties[i].Owned then
OwnedProperties[#OwnedProperties + 1] = Properties[i]
end
end
return OwnedProperties
end)
exports("GetNonOwnedProperties", function()
local NonOwnedProperties = {}
for i=1, #Properties do
if not Properties[i].Owned then
NonOwnedProperties[#NonOwnedProperties + 1] = Properties[i]
end
end
return NonOwnedProperties
end)
exports("GetPlayerProperties", function(identifier)
local PlayerProperties = {}
for i=1, #Properties do
if Properties[i].Owned and Properties[i].Owner == identifier then
PlayerProperties[#PlayerProperties + 1] = Properties[i]
end
end
return PlayerProperties
end)
exports("GetPropertyKeys", function(PropertyId)
local Property = Properties[PropertyId]
if Property.Keys then
return Property.Keys
end
return {}
end)
exports("DoesPlayerHaveKeys", function(PropertyId, Identifier)
local Property = Properties[PropertyId]
if Property.Keys then
return Property.Keys[Identifier] and true or false
end
return {}
end)
exports("ForceSaveProperties", function()
local ExecutingResource = GetInvokingResource()
PropertySave(TranslateCap("forced_save", ExecutingResource))
end)