142 lines
5.6 KiB
Lua
142 lines
5.6 KiB
Lua
-- ============================================================
|
||
-- db/main.lua – Database module (cache + core queries)
|
||
-- ============================================================
|
||
|
||
_G.db = { cache = {} }
|
||
|
||
-- ──────────────────────────────────────────────
|
||
-- saveCache
|
||
-- cacheName : string – cache key name
|
||
-- cacheId : any – secondary identifier
|
||
-- data : any – data to store
|
||
-- ttl : number? – time-to-live in seconds (default 3 600 000)
|
||
-- ──────────────────────────────────────────────
|
||
|
||
function db.saveCache(self, cacheName, data, cacheId, ttl)
|
||
if not data then
|
||
Debug("No data to save to cache", cacheName, cacheId)
|
||
return
|
||
end
|
||
|
||
local expireAt = os.time() + (ttl or 3600000)
|
||
|
||
self.cache[#self.cache + 1] = {
|
||
name = cacheName,
|
||
id = cacheId,
|
||
time = os.time(),
|
||
expire = expireAt,
|
||
data = data,
|
||
}
|
||
|
||
Debug("db:saveCache", cacheName, cacheId)
|
||
end
|
||
|
||
-- ──────────────────────────────────────────────
|
||
-- clearCache
|
||
-- cacheName : string – cache key to clear
|
||
-- cacheId : any? – optional secondary identifier
|
||
-- ──────────────────────────────────────────────
|
||
|
||
function db.clearCache(self, cacheName, cacheId)
|
||
self.cache = table.filter(self.cache, function(entry)
|
||
return entry.name ~= cacheName
|
||
end)
|
||
Debug("db:clearCache", cacheName, cacheId)
|
||
end
|
||
|
||
-- ──────────────────────────────────────────────
|
||
-- getCache
|
||
-- Returns the cached data, or nil if not found.
|
||
-- ──────────────────────────────────────────────
|
||
|
||
function db.getCache(self, cacheName, cacheId)
|
||
local found = table.find(self.cache, function(entry)
|
||
return entry.name == cacheName
|
||
end)
|
||
|
||
if found then
|
||
Debug("db:getCache", cacheName, cacheId)
|
||
return found.data
|
||
end
|
||
return nil
|
||
end
|
||
|
||
-- ──────────────────────────────────────────────
|
||
-- convertVectors
|
||
-- Recursively converts plain {x,y,z[,w]} tables
|
||
-- to FiveM vec3/vec4 native types.
|
||
-- ──────────────────────────────────────────────
|
||
|
||
function db.convertVectors(self, value)
|
||
if type(value) ~= "table" then return value end
|
||
|
||
-- vec4 detection
|
||
if value.w and value.x and value.y and value.z then
|
||
return vec4(value.x, value.y, value.z, value.w)
|
||
end
|
||
|
||
-- Recurse into nested tables
|
||
for key, nested in pairs(value) do
|
||
if type(nested) == "table" then
|
||
value[key] = self:convertVectors(nested)
|
||
end
|
||
end
|
||
return value
|
||
end
|
||
|
||
-- ──────────────────────────────────────────────
|
||
-- getGarages
|
||
-- Fetches all player_garages rows and decodes
|
||
-- their JSON columns into Lua tables.
|
||
-- ──────────────────────────────────────────────
|
||
|
||
function db.getGarages()
|
||
local rows = MySQL.query.await("SELECT * FROM player_garages")
|
||
|
||
for _, garage in pairs(rows) do
|
||
-- Normalize available flag
|
||
garage.available = (garage.available == true or garage.available == 1) or false
|
||
|
||
garage.zone = json.decode(garage.zone)
|
||
garage.coords = json.decode(garage.coords)
|
||
garage.shell = json.decode(garage.shell)
|
||
garage.holders = (garage.holders and json.decode(garage.holders)) or {}
|
||
garage.jobs = (garage.jobs and json.decode(garage.jobs)) or {}
|
||
garage.gangs = (garage.gangs and json.decode(garage.gangs)) or {}
|
||
garage.interior_type = garage.interior_type or "ipl"
|
||
end
|
||
|
||
return rows
|
||
end
|
||
|
||
-- ──────────────────────────────────────────────
|
||
-- checkVehicleGarages
|
||
-- For each vehicle whose garage no longer exists,
|
||
-- moves it to the nearest impound. Used by /migratev5.
|
||
-- ──────────────────────────────────────────────
|
||
|
||
function db.checkVehicleGarages()
|
||
local selectQuery = string.format("SELECT type, garage, plate FROM `%s`", garageTable)
|
||
local vehicles = MySQL.query.await(selectQuery)
|
||
|
||
local pendingUpdates = {}
|
||
for _, row in pairs(vehicles) do
|
||
if row.garage ~= "OUT" then
|
||
local garageExists = Config.Garages[row.garage]
|
||
if not garageExists then
|
||
local impound = GetImpoundOfType(row.type)
|
||
local updateQuery = string.format("UPDATE `%s` SET garage = ? WHERE plate = ?", garageTable)
|
||
pendingUpdates[#pendingUpdates + 1] = {
|
||
query = updateQuery,
|
||
values = { impound, row.plate },
|
||
}
|
||
end
|
||
end
|
||
end
|
||
|
||
if #pendingUpdates > 0 then
|
||
Debug("db.checkVehicleGarages", "We collected", #pendingUpdates, "queries to update vehicles because their garages were not found")
|
||
MySQL.transaction(pendingUpdates)
|
||
end
|
||
end
|