274 lines
11 KiB
Lua
274 lines
11 KiB
Lua
if Config.Framework ~= "esx" then return end
|
|
|
|
local function DetectGarageSystem()
|
|
local garageSystems = {
|
|
{ name = "cd_garage", resource = "cd_garage" },
|
|
{ name = "loaf_garage", resource = "loaf_garage" },
|
|
{ name = "jg-advancedgarages", resource = "jg-advancedgarages" },
|
|
{ name = "qs-advancedgarages", resource = "qs-advancedgarages" },
|
|
{ name = "codem-garage", resource = "codem-garage" },
|
|
{ name = "qb-garages", resource = "qb-garages" }, -- left for cross-compat
|
|
{ name = "tgiann-real-parking", resource = "tgiann-real-parking" },
|
|
{ name = "okokGarage", resource = "okokGarage" },
|
|
{ name = "op-garages", resource = "op-garages" },
|
|
}
|
|
|
|
for _, system in ipairs(garageSystems) do
|
|
if GetResourceState(system.resource) == "started" then
|
|
DebugPrint("Garage system detected: " .. system.name)
|
|
return system.name
|
|
end
|
|
end
|
|
return "default"
|
|
end
|
|
|
|
local function ProcessVehicleData(row, garageSystem)
|
|
if not row then return nil end
|
|
|
|
local processed = {
|
|
plate = row.plate,
|
|
stored = false,
|
|
location= "out",
|
|
garage = nil,
|
|
type = row.type or row.category or nil, -- some schemas store type
|
|
model = nil,
|
|
statistics = {
|
|
engine = 100,
|
|
body = 100,
|
|
fuel = 100
|
|
}
|
|
}
|
|
|
|
if row.stored ~= nil then
|
|
if type(row.stored) == "boolean" then
|
|
processed.stored = row.stored
|
|
else
|
|
processed.stored = (tonumber(row.stored) or 0) == 1
|
|
end
|
|
elseif row.in_garage ~= nil then
|
|
processed.stored = (row.in_garage == 1) or (row.in_garage == true)
|
|
elseif row.state ~= nil then
|
|
processed.stored = (row.state == 1) or (row.state == true)
|
|
end
|
|
|
|
if garageSystem == "cd_garage" then
|
|
processed.stored = (row.in_garage == 1) or (row.in_garage == true) or processed.stored
|
|
processed.garage = row.garage_id or row.garage
|
|
processed.type = row.garage_type or processed.type
|
|
elseif garageSystem == "loaf_garage" then
|
|
processed.stored = true -- loaf marks everything as recallable
|
|
processed.garage = row.garage or processed.garage
|
|
elseif garageSystem == "jg-advancedgarages" then
|
|
processed.stored = (row.in_garage == 1) or (row.in_garage == true) or processed.stored
|
|
processed.garage = row.garage_id or row.garage
|
|
elseif garageSystem == "qs-advancedgarages" then
|
|
processed.stored = (row.garage and row.garage ~= "out") or processed.stored
|
|
processed.garage = row.garage or processed.garage
|
|
elseif garageSystem == "codem-garage" then
|
|
processed.stored = (row.stored == 1) or (row.stored == true) or processed.stored
|
|
processed.garage = row.parking or row.garage or processed.garage
|
|
elseif garageSystem == "tgiann-real-parking" then
|
|
processed.stored = exports["tgiann-realparking"] and exports["tgiann-realparking"]:IsVehicleParkedAnyGarage(row.plate) or false
|
|
processed.garage = row.garage or ''
|
|
elseif garageSystem == "qb-garages" then
|
|
processed.stored = (row.state == 1) or (row.state == true) or processed.stored
|
|
processed.garage = row.garage or processed.garage
|
|
elseif garageSystem == "okokGarage" then
|
|
processed.stored = (row.state == 1) or (row.state == true) or processed.stored
|
|
processed.garage = row.garage or processed.garage
|
|
elseif garageSystem == "op-garages" then
|
|
processed.stored = (row.state == 0 and row.isTowedOut == 0) or (row.state == false and row.isTowedOut == false) or processed.stored
|
|
processed.garage = row.garage or 'Garage'
|
|
else
|
|
processed.garage = row.garage or processed.garage
|
|
end
|
|
|
|
processed.location = processed.stored and (processed.garage or "garage") or "out"
|
|
|
|
local props
|
|
if row.vehicle then
|
|
local ok, decoded = pcall(json.decode, row.vehicle)
|
|
if ok and decoded then props = decoded end
|
|
elseif row.mods then
|
|
local ok, decoded = pcall(json.decode, row.mods)
|
|
if ok and decoded then props = decoded end
|
|
end
|
|
|
|
if props then
|
|
processed.model = props.model or processed.model
|
|
|
|
local eng = row.engine or props.engineHealth or props.healthEngine
|
|
local bod = row.body or props.bodyHealth or props.healthBody
|
|
local fuel = row.fuel or props.fuelLevel or props.fuel
|
|
|
|
processed.statistics.engine = eng and math.floor((tonumber(eng) or 1000) / 10 + 0.5) or processed.statistics.engine
|
|
processed.statistics.body = bod and math.floor((tonumber(bod) or 1000) / 10 + 0.5) or processed.statistics.body
|
|
processed.statistics.fuel = tonumber(fuel) or processed.statistics.fuel
|
|
else
|
|
DebugPrint(("Warning: Could not decode vehicle props for plate %s"):format(tostring(row.plate)))
|
|
processed.model = processed.model or row.model or row.hash and tonumber(row.hash) or nil
|
|
processed.statistics = {
|
|
engine = row.engine and math.floor(row.engine / 10 + 0.5) or 100,
|
|
body = row.body and math.floor(row.body / 10 + 0.5) or 100,
|
|
fuel = row.fuel or 100,
|
|
}
|
|
end
|
|
|
|
return processed
|
|
end
|
|
|
|
-- =============== LIST PLAYER VEHICLES (ESX) ===============
|
|
Core.Functions.GetPlayerVehicles = function(playerId)
|
|
if not playerId then
|
|
DebugPrint('Error: playerId is required')
|
|
return {}
|
|
end
|
|
|
|
local identifier = Core.Functions.GetIdentifier(playerId)
|
|
if not identifier then
|
|
DebugPrint('Error: Could not get player identifier for ID: ' .. tostring(playerId))
|
|
return {}
|
|
end
|
|
|
|
|
|
local result = MySQL.query.await("SELECT * FROM owned_vehicles WHERE owner = ?", { identifier })
|
|
if not result then
|
|
DebugPrint('Error: Database query failed for identifier: ' .. identifier)
|
|
return {}
|
|
end
|
|
|
|
local garageSystem = DetectGarageSystem()
|
|
local out = {}
|
|
for i = 1, #result do
|
|
local row = result[i]
|
|
local processed = ProcessVehicleData(row, garageSystem)
|
|
if processed then out[#out + 1] = processed end
|
|
end
|
|
|
|
DebugPrint(('Retrieved %d vehicles for player: %s'):format(#out, identifier))
|
|
return out
|
|
end
|
|
|
|
-- =============== TAKE A VEHICLE OUT (ESX) ===============
|
|
Core.Functions.GetVehicleData = function(playerId, plate)
|
|
if not playerId then
|
|
DebugPrint('Error: playerId is required')
|
|
return {}
|
|
end
|
|
|
|
local identifier = Core.Functions.GetIdentifier(playerId)
|
|
if not identifier then
|
|
DebugPrint('Error: Could not get player identifier for ID: ' .. tostring(playerId))
|
|
return {}
|
|
end
|
|
|
|
local garageSystem = DetectGarageSystem()
|
|
|
|
local selectSql, params = "", {}
|
|
if garageSystem == "cd_garage" or garageSystem == "jg-advancedgarages" then
|
|
selectSql = "SELECT plate, vehicle, in_garage, garage_id as garage, stored FROM owned_vehicles WHERE owner = ? AND plate = ? AND in_garage = 1"
|
|
params = { identifier, plate }
|
|
elseif garageSystem == "qs-advancedgarages" then
|
|
selectSql = "SELECT plate, vehicle, garage, stored FROM owned_vehicles WHERE owner = ? AND plate = ? AND garage <> 'out'"
|
|
params = { identifier, plate }
|
|
elseif garageSystem == "codem-garage" then
|
|
selectSql = "SELECT plate, vehicle, stored, parking FROM owned_vehicles WHERE owner = ? AND plate = ? AND stored = 1"
|
|
params = { identifier, plate }
|
|
elseif garageSystem == "qb-garages" or garageSystem == "okokGarage" then
|
|
selectSql = "SELECT plate, vehicle, stored, state FROM owned_vehicles WHERE owner = ? AND plate = ? AND stored = 1"
|
|
params = { identifier, plate }
|
|
elseif garageSystem == "op-garages" then
|
|
selectSql = "SELECT plate, vehicle, state, garage FROM owned_vehicles WHERE owner = ? AND plate = ? AND state = 0"
|
|
params = { identifier, plate }
|
|
else
|
|
selectSql = "SELECT plate, vehicle, stored FROM owned_vehicles WHERE owner = ? AND plate = ? AND stored = 1"
|
|
params = { identifier, plate }
|
|
end
|
|
|
|
local row = MySQL.single.await(selectSql, params)
|
|
if not row then
|
|
DebugPrint('Error: No vehicle found IN GARAGE for plate: ' .. plate)
|
|
return
|
|
end
|
|
|
|
|
|
local updateSql, updateParams = "", {}
|
|
if garageSystem == "cd_garage" or garageSystem == "jg-advancedgarages" then
|
|
updateSql = "UPDATE owned_vehicles SET in_garage = 0 WHERE plate = ?"
|
|
updateParams = { plate }
|
|
elseif garageSystem == "qs-advancedgarages" then
|
|
updateSql = "UPDATE owned_vehicles SET garage = 'out' WHERE plate = ?"
|
|
updateParams = { plate }
|
|
elseif garageSystem == "codem-garage" or garageSystem == "qb-garages" or garageSystem == "okokGarage" then
|
|
updateSql = "UPDATE owned_vehicles SET stored = 0 WHERE plate = ?"
|
|
updateParams = { plate }
|
|
elseif garageSystem == "op-garages" then
|
|
updateSql = "UPDATE owned_vehicles SET state = 1 WHERE plate = ?"
|
|
updateParams = { plate }
|
|
else
|
|
updateSql = "UPDATE owned_vehicles SET stored = 0 WHERE plate = ?"
|
|
updateParams = { plate }
|
|
end
|
|
|
|
local changed = MySQL.update.await(updateSql, updateParams)
|
|
if not changed or changed == 0 then
|
|
DebugPrint('Error: Database update failed for plate: ' .. plate)
|
|
return
|
|
end
|
|
|
|
|
|
local vehicleData = {
|
|
plate = row.plate,
|
|
mods = row.vehicle,
|
|
hash = nil,
|
|
fuel = 100,
|
|
model = nil
|
|
}
|
|
|
|
if row.vehicle then
|
|
local ok, props = pcall(json.decode, row.vehicle)
|
|
if ok and props then
|
|
vehicleData.model = props.model and tonumber(props.model) or nil
|
|
vehicleData.hash = vehicleData.model
|
|
vehicleData.fuel = props.fuelLevel or props.fuel or 100
|
|
vehicleData.mods = row.vehicle
|
|
end
|
|
end
|
|
|
|
-- Fallback values if not found in vehicle JSON
|
|
if not vehicleData.model and row.hash then
|
|
vehicleData.model = tonumber(row.hash)
|
|
vehicleData.hash = vehicleData.model
|
|
end
|
|
|
|
return vehicleData
|
|
end
|
|
|
|
local function WaitForEntity(entity)
|
|
local timer = GetGameTimer() + 5000
|
|
while not DoesEntityExist(entity) and timer > GetGameTimer() do
|
|
Wait(0)
|
|
end
|
|
return DoesEntityExist(entity)
|
|
end
|
|
|
|
function Core.Functions.CreateServerVehicle(model, coords, heading)
|
|
heading = heading or 0
|
|
local vehicle = CreateVehicle(model, coords.x, coords.y, coords.z, heading, true, true)
|
|
if not WaitForEntity(vehicle) then
|
|
return
|
|
end
|
|
SetEntityIgnoreRequestControlFilter(vehicle, true)
|
|
return vehicle
|
|
end
|
|
|
|
function Core.Functions.CreateServerPed(model, coords, heading)
|
|
heading = heading or 0
|
|
local ped = CreatePed(4, model, coords.x, coords.y, coords.z, heading, true, true)
|
|
if not WaitForEntity(ped) then
|
|
return
|
|
end
|
|
SetEntityIgnoreRequestControlFilter(ped, true)
|
|
return ped
|
|
end
|