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

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