This release adds codem-garage compatibility, fixes garage vehicle tracking, improves phone repair NPC spawning with distance-based loading, and resolves CEX app event issues.
Full list of files changed
| Change | File |
|---|---|
| Added | bridge/server/garage/codem-garage.lua |
| Modified | bridge/server/garage/main.lua |
| Modified | client/apps/cex.lua |
| Modified | client/open/phone_repairs.lua |
| Modified | config/config.lua |
| Modified | fxmanifest.lua |
| Modified | server/apps/garage.lua |
| Modified | server/server.js |
| Modified | web/ |
These files are not encrypted. Review the changes below — you may need to manually apply updates to your server's config files.
bridge/server/garage/codem-garage.lua+local function LoadVendorConfig(path)
+ local env = {
+ Config = {}
+ }
+
+ setmetatable(env, {
+ __index = _G
+ })
+
+ local file = LoadResourceFile("codem-garage", path)
+ local chunk, err = load(file, "@@codem-garage", "t", env)
+ if not chunk then
+ error(("Failed to load vendor config %s: %s"):format(path, err))
+ end
+
+ local ok, result = pcall(chunk)
+ if not ok then
+ error(("Error running vendor config %s: %s"):format(path, result))
+ end
+
+ return env.Config
+end
+
+local codemConfig = LoadVendorConfig("config.lua")
+
+---@return vector3 | nil
+local function getClosestGarage(source)
+ local coords = GetEntityCoords(GetPlayerPed(source))
+ local closest = nil
+ local closestDistance = nil
+ for id, v in pairs(codemConfig.Garages) do
+ if v.garage == "normal" then
+ local garageCoords = vector3(v.car.garage.x, v.car.garage.y, v.car.garage.z)
+ local distance = #(coords - garageCoords)
+ if closest == nil or distance < closestDistance then
+ closest = garageCoords
+ closestDistance = distance
+ end
+ end
+ end
+
+ return closest
+end
+
+local function findGarageCoords(garageId)
+ for id, garage in ipairs(codemConfig.Garages) do
+ if id == garageId then
+ return garage.car.garage --[[ @as vector3 ]]
+ end
+ end
+end
+
+local function findImpoundCoords(impoundId)
+ for id, v in pairs(codemConfig.Garages) do
+ if id == impoundId then
+ return v.car.garage --[[ @as vector3 ]]
+ end
+ end
+end
+
+---@type GarageBridge
+local functions = {}
+
+local findQuery =
+ CurrentFramework == 'esx' and 'SELECT * FROM owned_vehicles WHERE plate = ? AND owner = ? LIMIT 1' or
+ 'SELECT * FROM player_vehicles WHERE plate = ? AND citizenid = ? LIMIT 1'
+
+---Finds the location of a vehicle
+---@param plate string
+---@param owner string
+---@return vector3 | nil
+---@diagnostic disable-next-line: duplicate-set-field
+function functions:getVehicleLocation(plate, owner, source)
+ local vehicle = MySQL.prepare.await(findQuery, { plate, owner })
+ if not vehicle then return end
+
+ local state = CurrentFramework == 'esx' and vehicle.stored or vehicle.state
+
+ --- If vehicle is outside
+ if state == 0 then
+ return self.findVehicleOutsideByPlate(plate)
+
+ --- If vehicle is in garage
+ elseif state == 1 then
+ return findGarageCoords(CurrentFramework == 'esx' and vehicle.parking or vehicle.garage) or getClosestGarage(source)
+ --- If vehicle is impounded
+ elseif state == 2 then
+ return findImpoundCoords(vehicle.location)
+ end
+end
+
+local removeQuery =
+ CurrentFramework == 'esx' and
+ 'UPDATE owned_vehicles SET stored = ?, parking = NULL WHERE plate = ? LIMIT 1' or
+ 'UPDATE player_vehicles SET state = ?, garage = NULL WHERE plate = ? LIMIT 1'
+function functions.removeVehicleFromGarage(plate)
+ MySQL.prepare.await(removeQuery, { 0, plate })
+end
+
+local storedQuery =
+ CurrentFramework == 'esx' and
+ 'SELECT stored FROM owned_vehicles WHERE plate = ?' or
+ 'SELECT state FROM player_vehicles WHERE plate = ?'
+---@return boolean
+function functions.isVehicleStored(plate)
+ return MySQL.prepare.await(storedQuery, { plate }) == 1
+end
+
+local getQuery = CurrentFramework == 'esx' and
+ 'SELECT * FROM owned_vehicles WHERE owner = ?' or
+ 'SELECT * FROM player_vehicles WHERE citizenid = ?'
+
+---@return {model:string, plate:string, mods:table, garage:string, state:number}[] | nil
+function functions:getPlayerVehicles(char_id)
+ local result = MySQL.rawExecute.await(getQuery, { char_id })
+ if not result then return end
+
+ local vehicles = {}
+
+ for _, vehicle in ipairs(result) do
+ local mods = CurrentFramework == "esx" and json.decode(vehicle.vehicle) or json.decode(vehicle.mods)
+ table.insert(vehicles, {
+ model = mods.model,
+ plate = vehicle.plate,
+ mods = mods,
+ garage = CurrentFramework == 'esx' and vehicle.parking or vehicle.garage,
+ state = CurrentFramework == 'esx' and vehicle.stored or vehicle.state
+ })
+ end
+
+ return vehicles
+end
+
+return functions
bridge/server/garage/main.lua---- Load garages
-
----@class GarageBridge
-local functions = {}
-
-
-local list = {
- "cd_garage",
- "jg-advancedgarages",
- "vrs_garage",
- "okokGarage",
- "loaf_garage",
- "lunar_garage",
- "ak47_qb_garage",
- "ak47_garage",
- "qs-advancedgarages",
- "m-Garages",
- "op-garages",
- "hex_2_Garage"
-}
-
-local foundGarageScript = false
-for _, r in ipairs(list) do
- if GetResourceState(r) == "started" then
- foundGarageScript = true
- functions = Ok.require(('bridge.server.garage.%s'):format(r))
- break
- end
-end
-
-if not foundGarageScript then
- local event
- event = AddEventHandler('onResourceStart', function(resource)
- for _, script in ipairs(list) do
- if resource == script then
- functions = Ok.require(('bridge.server.garage.%s'):format(script))
- RemoveEventHandler(event)
- foundGarageScript = true
- end
- end
- end)
-end
-
---- Attempts to find coordinates of a vehicle outside
----@param plate string
----@return vector3 | nil
-function functions.findVehicleOutsideByPlate(plate)
- local vehList = GetAllVehicles()
-
- --Remove white spaces from plate
- local trimmedPlate = plate:gsub('%s+', '')
-
- for _, veh in ipairs(vehList --[[ @as number[] ]]) do
- local p = GetVehicleNumberPlateText(veh)
- if trimmedPlate == p:gsub('%s+', '') then
- return GetEntityCoords(veh)
- end
- end
-end
-
-if foundGarageScript then return functions end
-
---- Fallback, didn't find a garage script
---- Framework specific functions
-if CurrentFramework == 'esx' then
- if not GetResourceState('esx_garage') == 'started' then
- error("No compatible garage script found, check bridge/server/garage, for more info")
- return
- end
-
- local garages = exports["esx_garage"]:getGarages()
- local impounds = exports["esx_garage"]:getImpounds()
-
- function functions.removeVehicleFromGarage(plate)
- MySQL.prepare.await("UPDATE owned_vehicles SET stored = ?, parking = NULL WHERE plate = ? LIMIT 1", { 0, plate })
- end
-
- function functions.isVehicleStored(plate)
- return MySQL.prepare.await("SELECT stored FROM owned_vehicles WHERE plate = ? LIMIT 1", { plate }) == 1
- end
-
- ---@return vector3 | nil
- function functions:getVehicleLocation(plate, owner)
- local vehicle = MySQL.prepare.await("SELECT * FROM owned_vehicles WHERE plate = ? AND owner = ? LIMIT 1",
- { plate, owner })
- if not vehicle then return end
-
- --- If vehicle is outside
- if vehicle.stored == 0 then
- return self.findVehicleOutsideByPlate(plate)
-
- --- If vehicle is in garage
- elseif vehicle.stored == 1 then
- local garageInfo = garages[vehicle.parking]
- if garageInfo then
- return vector3(garageInfo.EntryPoint.x, garageInfo.EntryPoint.y, garageInfo.EntryPoint.z)
- end
- elseif vehicle.stored == 2 then
- local impoundInfo = impounds[vehicle.pound]
- if impoundInfo then
- return vector3(impoundInfo.SpawnPoint.x, impoundInfo.SpawnPoint.y, impoundInfo.SpawnPoint.z)
- end
- end
- end
-
- ---@return {model:string, plate:string, mods:table, garage:string, state:number}[] | nil
- function functions:getPlayerVehicles(char_id, source)
- local result = MySQL.query.await('SELECT * FROM player_vehicles WHERE citizenid = ?', { char_id })
- if not result then return end
-
- local vehicles = {}
-
- for _, vehicle in ipairs(result) do
- local data = json.decode(vehicle.vehicle)
- table.insert(vehicles, {
- model = data.model,
- plate = vehicle.plate,
- mods = json.decode(vehicle.mods),
- garage = vehicle.parking,
- state = vehicle.stored
- })
- end
-
- return vehicles
- end
-elseif CurrentFramework == 'qb' then
- if not GetResourceState('qb-garages') == 'started' then
- error("No compatible garage script found, check bridge/server/garage, for more info")
- return
- end
-
- local env = { vector3 = vector3, vector4 = vector4, vec3 = vec3, vec4 = vec4, vector2 = vector2, vec2 = vec2 }
- Ok.require("@qb-garages.config", env)
- local garages = env.Config.Garages
-
- function functions.removeVehicleFromGarage(plate)
- MySQL.prepare.await("UPDATE player_vehicles SET state = ?, garage = NULL WHERE plate = ? LIMIT 1", { 0, plate })
- end
-
- function functions.isVehicleStored(plate)
- return MySQL.prepare.await("SELECT state FROM player_vehicles WHERE plate = ? LIMIT 1", { plate }) == 1
- end
-
- ---@return vector3 | nil
- function functions:getVehicleLocation(plate, owner)
- local vehicle = MySQL.prepare.await(
- "SELECT * FROM player_vehicles WHERE plate = ? AND citizenid = ? LIMIT 1",
- { plate, owner })
- if not vehicle then return end
-
- --- If vehicle is outside
- if vehicle.state == 0 then
- return self.findVehicleOutsideByPlate(plate)
-
- --- If vehicle is in garage
- elseif vehicle.state == 1 then
- return garages[vehicle.garage]?.takeVehicle --[[ @as vector3[]? ]]
-
- --- If vehicle is impounded
- elseif vehicle.state == 2 then
- return garages[vehicle.garage]?.takeVehicle --[[ @as vector3[]? ]]
- end
- end
-
- ---@return {model:string, plate:string, mods:table, garage:string, state:number}[] | nil
- function functions:getPlayerVehicles(char_id)
- local result = MySQL.rawExecute.await('SELECT * FROM player_vehicles WHERE citizenid = ?', { char_id })
- if not result then return end
-
- local vehicles = {}
-
- for _, vehicle in ipairs(result) do
- table.insert(vehicles, {
- model = vehicle.vehicle,
- plate = vehicle.plate,
- mods = json.decode(vehicle.mods),
- garage = vehicle.garage,
- state = vehicle.state
- })
- end
-
- return vehicles
- end
-elseif CurrentFramework == 'qbx' then
- if not GetResourceState('qbx_garages') == 'started' then
- error("No compatible garage script found, check bridge/server/garage, for more info")
- return
- end
-
- local garages = exports["qbx_garages"]:GetGarages()
-
- function functions.isVehicleStored(plate)
- return MySQL.prepare.await("SELECT state FROM player_vehicles WHERE plate = ? LIMIT 1", { plate }) == 1
- end
-
- function functions.removeVehicleFromGarage(plate)
- MySQL.prepare.await("UPDATE player_vehicles SET state = ?, garage = NULL WHERE plate = ? LIMIT 1", { 0, plate })
- end
-
- ---@return vector3 | nil
- function functions:getVehicleLocation(plate, owner)
- local vehicle = MySQL.prepare.await(
- "SELECT * FROM player_vehicles WHERE plate = ? AND citizenid = ? LIMIT 1",
- { plate, owner })
- if not vehicle then return end
-
- --- If vehicle is outside
- if vehicle.state == 0 then
- return self.findVehicleOutsideByPlate(plate)
-
- --- If vehicle is in garage
- elseif vehicle.state == 1 then
- local coords = garages[vehicle.garage]?.accessPoints?[1]?.coords --[[ @as vector4? ]]
- if coords then
- return vector3(coords.x, coords.y, coords.z)
- end
-
- --- If vehicle is impounded
- elseif vehicle.state == 2 then
- local coords = garages["impoundlot"]?.accessPoints?[1]?.coords --[[ @as vector4? ]]
- if coords then
- return vector3(coords.x, coords.y, coords.z)
- end
- end
- end
-
- ---@return {model:string, plate:string, mods:table, garage:string, state:number}[] | nil
- function functions:getPlayerVehicles(char_id)
- local result = MySQL.rawExecute.await('SELECT * FROM player_vehicles WHERE citizenid = ?', { char_id })
- if not result then return end
-
- local vehicles = {}
-
- for _, vehicle in ipairs(result) do
- table.insert(vehicles, {
- model = vehicle.vehicle,
- plate = vehicle.plate,
- mods = json.decode(vehicle.mods),
- garage = vehicle.garage,
- state = vehicle.state
- })
- end
-
- return vehicles
- end
-elseif CurrentFramework == 'ox' then
- local Ox = Framework --[[ @as OxServer ]]
-
- function functions.isVehicleStored(plate)
- local result = MySQL.prepare.await("SELECT stored FROM vehicles WHERE plate = ? LIMIT 1", { plate })
- return result ~= nil and result ~= "impound"
- end
-
- --- This part needs to be adapted to the proper garage script
- --- I have one here: <https://github.com/luxu-gg/luxu_garages_ox>
- --- You can find more here <https://github.com/overextended/awesome-ox?tab=readme-ov-file>
- ---@return vector3 | nil
- function functions:getVehicleLocation(plate, owner)
- local garage = MySQL.prepare.await(
- "SELECT stored FROM vehicles WHERE plate = ? AND owner = ? LIMIT 1",
- { plate, owner })
- if not garage then return end
-
- --- If vehicle is outside
- if garage == nil then
- return self.findVehicleOutsideByPlate(plate)
-
- --- If vehicle is in garage
- elseif garage ~= "impound" then
- return vector3(0, 0, 0)
-
- --- If vehicle is impounded
- else
- return vector3(0, 0, 0)
- end
- end
-
- ---@return {model:string, plate:string, mods:table, garage:string, state:number}[] | nil
- function functions:getPlayerVehicles(char_id)
- local result = MySQL.rawExecute.await('SELECT * FROM vehicles where owner = ?', { char_id })
- if not result then return end
-
- local vehicles = {}
-
- for _, vehicle in ipairs(result) do
- vehicles[#vehicles + 1] = {
- model = vehicle.model,
- plate = vehicle.plate,
- mods = json.decode(vehicle.data.properties),
- garage = vehicle.stored,
- state = vehicle.stored == 'impound' and 2 or (vehicle.stored and 1 or 0)
- }
- end
-
- return vehicles
- end
-end
-
-
-return functions
+--- Load garages
+
+---@class GarageBridge
+local functions = {}
+
+
+local list = {
+ "cd_garage",
+ "jg-advancedgarages",
+ "vrs_garage",
+ "okokGarage",
+ "loaf_garage",
+ "lunar_garage",
+ "ak47_qb_garage",
+ "ak47_garage",
+ "qs-advancedgarages",
+ "m-Garages",
+ "op-garages",
+ "hex_2_Garage",
+ "codem-garage"
+}
+
+local foundGarageScript = false
+for _, r in ipairs(list) do
+ if GetResourceState(r) == "started" then
+ foundGarageScript = true
+ functions = Ok.require(('bridge.server.garage.%s'):format(r))
+ break
+ end
+end
+
+if not foundGarageScript then
+ local event
+ event = AddEventHandler('onResourceStart', function(resource)
+ for _, script in ipairs(list) do
+ if resource == script then
+ functions = Ok.require(('bridge.server.garage.%s'):format(script))
+ RemoveEventHandler(event)
+ foundGarageScript = true
+ end
+ end
+ end)
+end
+
+--- Attempts to find coordinates of a vehicle outside
+---@param plate string
+---@return vector3 | nil
+function functions.findVehicleOutsideByPlate(plate)
+ local vehList = GetAllVehicles()
+
+ --Remove white spaces from plate
+ local trimmedPlate = plate:gsub('%s+', '')
+
+ for _, veh in ipairs(vehList --[[ @as number[] ]]) do
+ local p = GetVehicleNumberPlateText(veh)
+ if trimmedPlate == p:gsub('%s+', '') then
+ return GetEntityCoords(veh)
+ end
+ end
+end
+
+if foundGarageScript then return functions end
+
+--- Fallback, didn't find a garage script
+--- Framework specific functions
+if CurrentFramework == 'esx' then
+ if not GetResourceState('esx_garage') == 'started' then
+ error("No compatible garage script found, check bridge/server/garage, for more info")
+ return
+ end
+
+ local garages = exports["esx_garage"]:getGarages()
+ local impounds = exports["esx_garage"]:getImpounds()
+
+ function functions.removeVehicleFromGarage(plate)
+ MySQL.prepare.await("UPDATE owned_vehicles SET stored = ?, parking = NULL WHERE plate = ? LIMIT 1", { 0, plate })
+ end
+
+ function functions.isVehicleStored(plate)
+ return MySQL.prepare.await("SELECT stored FROM owned_vehicles WHERE plate = ? LIMIT 1", { plate }) == 1
+ end
+
+ ---@return vector3 | nil
+ function functions:getVehicleLocation(plate, owner)
+ local vehicle = MySQL.prepare.await("SELECT * FROM owned_vehicles WHERE plate = ? AND owner = ? LIMIT 1",
+ { plate, owner })
+ if not vehicle then return end
+
+ --- If vehicle is outside
+ if vehicle.stored == 0 then
+ return self.findVehicleOutsideByPlate(plate)
+
+ --- If vehicle is in garage
+ elseif vehicle.stored == 1 then
+ local garageInfo = garages[vehicle.parking]
+ if garageInfo then
+ return vector3(garageInfo.EntryPoint.x, garageInfo.EntryPoint.y, garageInfo.EntryPoint.z)
+ end
+ elseif vehicle.stored == 2 then
+ local impoundInfo = impounds[vehicle.pound]
+ if impoundInfo then
+ return vector3(impoundInfo.SpawnPoint.x, impoundInfo.SpawnPoint.y, impoundInfo.SpawnPoint.z)
+ end
+ end
+ end
+
+ ---@return {model:string, plate:string, mods:table, garage:string, state:number}[] | nil
+ function functions:getPlayerVehicles(char_id, source)
+ local result = MySQL.query.await('SELECT * FROM player_vehicles WHERE citizenid = ?', { char_id })
+ if not result then return end
+
+ local vehicles = {}
+
+ for _, vehicle in ipairs(result) do
+ local data = json.decode(vehicle.vehicle)
+ table.insert(vehicles, {
+ model = data.model,
+ plate = vehicle.plate,
+ mods = json.decode(vehicle.mods),
+ garage = vehicle.parking,
+ state = vehicle.stored
+ })
+ end
+
+ return vehicles
+ end
+elseif CurrentFramework == 'qb' then
+ if not GetResourceState('qb-garages') == 'started' then
+ error("No compatible garage script found, check bridge/server/garage, for more info")
+ return
+ end
+
+ local env = { vector3 = vector3, vector4 = vector4, vec3 = vec3, vec4 = vec4, vector2 = vector2, vec2 = vec2 }
+ Ok.require("@qb-garages.config", env)
+ local garages = env.Config.Garages
+
+ function functions.removeVehicleFromGarage(plate)
+ MySQL.prepare.await("UPDATE player_vehicles SET state = ?, garage = NULL WHERE plate = ? LIMIT 1", { 0, plate })
+ end
+
+ function functions.isVehicleStored(plate)
+ return MySQL.prepare.await("SELECT state FROM player_vehicles WHERE plate = ? LIMIT 1", { plate }) == 1
+ end
+
+ ---@return vector3 | nil
+ function functions:getVehicleLocation(plate, owner)
+ local vehicle = MySQL.prepare.await(
+ "SELECT * FROM player_vehicles WHERE plate = ? AND citizenid = ? LIMIT 1",
+ { plate, owner })
+ if not vehicle then return end
+
+ --- If vehicle is outside
+ if vehicle.state == 0 then
+ return self.findVehicleOutsideByPlate(plate)
+
+ --- If vehicle is in garage
+ elseif vehicle.state == 1 then
+ return garages[vehicle.garage]?.takeVehicle --[[ @as vector3[]? ]]
+
+ --- If vehicle is impounded
+ elseif vehicle.state == 2 then
+ return garages[vehicle.garage]?.takeVehicle --[[ @as vector3[]? ]]
+ end
+ end
+
+ ---@return {model:string, plate:string, mods:table, garage:string, state:number}[] | nil
+ function functions:getPlayerVehicles(char_id)
+ local result = MySQL.rawExecute.await('SELECT * FROM player_vehicles WHERE citizenid = ?', { char_id })
+ if not result then return end
+
+ local vehicles = {}
+
+ for _, vehicle in ipairs(result) do
+ table.insert(vehicles, {
+ model = vehicle.vehicle,
+ plate = vehicle.plate,
+ mods = json.decode(vehicle.mods),
+ garage = vehicle.garage,
+ state = vehicle.state
+ })
+ end
+
+ return vehicles
+ end
+elseif CurrentFramework == 'qbx' then
+ if not GetResourceState('qbx_garages') == 'started' then
+ error("No compatible garage script found, check bridge/server/garage, for more info")
+ return
+ end
+
+ local garages = exports["qbx_garages"]:GetGarages()
+
+ function functions.isVehicleStored(plate)
+ return MySQL.prepare.await("SELECT state FROM player_vehicles WHERE plate = ? LIMIT 1", { plate }) == 1
+ end
+
+ function functions.removeVehicleFromGarage(plate)
+ MySQL.prepare.await("UPDATE player_vehicles SET state = ?, garage = NULL WHERE plate = ? LIMIT 1", { 0, plate })
+ end
+
+ ---@return vector3 | nil
+ function functions:getVehicleLocation(plate, owner)
+ local vehicle = MySQL.prepare.await(
+ "SELECT * FROM player_vehicles WHERE plate = ? AND citizenid = ? LIMIT 1",
+ { plate, owner })
+ if not vehicle then return end
+
+ --- If vehicle is outside
+ if vehicle.state == 0 then
+ return self.findVehicleOutsideByPlate(plate)
+
+ --- If vehicle is in garage
+ elseif vehicle.state == 1 then
+ local coords = garages[vehicle.garage]?.accessPoints?[1]?.coords --[[ @as vector4? ]]
+ if coords then
+ return vector3(coords.x, coords.y, coords.z)
+ end
+
+ --- If vehicle is impounded
+ elseif vehicle.state == 2 then
+ local coords = garages["impoundlot"]?.accessPoints?[1]?.coords --[[ @as vector4? ]]
+ if coords then
+ return vector3(coords.x, coords.y, coords.z)
+ end
+ end
+ end
+
+ ---@return {model:string, plate:string, mods:table, garage:string, state:number}[] | nil
+ function functions:getPlayerVehicles(char_id)
+ local result = MySQL.rawExecute.await('SELECT * FROM player_vehicles WHERE citizenid = ?', { char_id })
+ if not result then return end
+
+ local vehicles = {}
+
+ for _, vehicle in ipairs(result) do
+ table.insert(vehicles, {
+ model = vehicle.vehicle,
+ plate = vehicle.plate,
+ mods = json.decode(vehicle.mods),
+ garage = vehicle.garage,
+ state = vehicle.state
+ })
+ end
+
+ return vehicles
+ end
+elseif CurrentFramework == 'ox' then
+ local Ox = Framework --[[ @as OxServer ]]
+
+ function functions.isVehicleStored(plate)
+ local result = MySQL.prepare.await("SELECT stored FROM vehicles WHERE plate = ? LIMIT 1", { plate })
+ return result ~= nil and result ~= "impound"
+ end
+
+ --- This part needs to be adapted to the proper garage script
+ --- I have one here: <https://github.com/luxu-gg/luxu_garages_ox>
+ --- You can find more here <https://github.com/overextended/awesome-ox?tab=readme-ov-file>
+ ---@return vector3 | nil
+ function functions:getVehicleLocation(plate, owner)
+ local garage = MySQL.prepare.await(
+ "SELECT stored FROM vehicles WHERE plate = ? AND owner = ? LIMIT 1",
+ { plate, owner })
+ if not garage then return end
+
+ --- If vehicle is outside
+ if garage == nil then
+ return self.findVehicleOutsideByPlate(plate)
+
+ --- If vehicle is in garage
+ elseif garage ~= "impound" then
+ return vector3(0, 0, 0)
+
+ --- If vehicle is impounded
+ else
+ return vector3(0, 0, 0)
+ end
+ end
+
+ ---@return {model:string, plate:string, mods:table, garage:string, state:number}[] | nil
+ function functions:getPlayerVehicles(char_id)
+ local result = MySQL.rawExecute.await('SELECT * FROM vehicles where owner = ?', { char_id })
+ if not result then return end
+
+ local vehicles = {}
+
+ for _, vehicle in ipairs(result) do
+ vehicles[#vehicles + 1] = {
+ model = vehicle.model,
+ plate = vehicle.plate,
+ mods = json.decode(vehicle.data.properties),
+ garage = vehicle.stored,
+ state = vehicle.stored == 'impound' and 2 or (vehicle.stored and 1 or 0)
+ }
+ end
+
+ return vehicles
+ end
+end
+
+
+return functions