From b1fca34eccb2436ef72ad6d9abcbbd2e097a99d0 Mon Sep 17 00:00:00 2001 From: citRa Date: Fri, 31 Jan 2025 14:03:29 -0400 Subject: [PATCH 1/2] feat(groups): Add group management functionality --- client/groups.lua | 17 ++----- server/groups.lua | 68 ++++++++++++++++++++++++++++ server/storage/groups.lua | 93 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 13 deletions(-) create mode 100755 server/storage/groups.lua diff --git a/client/groups.lua b/client/groups.lua index 47f83630f..e8185bb37 100644 --- a/client/groups.lua +++ b/client/groups.lua @@ -1,16 +1,13 @@ -local jobs = require 'shared.jobs' -local gangs = require 'shared.gangs' - ---@return table function GetJobs() - return jobs + return lib.callback.await('qbx_core:server:getJobs') end exports('GetJobs', GetJobs) ---@return table function GetGangs() - return gangs + return lib.callback.await('qbx_core:server:getGangs') end exports('GetGangs', GetGangs) @@ -18,6 +15,7 @@ exports('GetGangs', GetGangs) ---@param name string ---@return Job? function GetJob(name) + local jobs = lib.callback.await('qbx_core:server:getJobs') return jobs[name] end @@ -26,15 +24,8 @@ exports('GetJob', GetJob) ---@param name string ---@return Gang? function GetGang(name) + local gangs = lib.callback.await('qbx_core:server:getGangs') return gangs[name] end exports('GetGang', GetGang) - -RegisterNetEvent('qbx_core:client:onJobUpdate', function(jobName, job) - jobs[jobName] = job -end) - -RegisterNetEvent('qbx_core:client:onGangUpdate', function(gangName, gang) - gangs[gangName] = gang -end) \ No newline at end of file diff --git a/server/groups.lua b/server/groups.lua index 8ca4dced5..8c8a7aacf 100644 --- a/server/groups.lua +++ b/server/groups.lua @@ -4,12 +4,28 @@ GroupType = { GANG = 'gang' } +---@type boolean +local managementEnabled = GetConvar('qbx:enableGroupManagement', 'false') == 'true' + ---@type table local jobs = require 'shared.jobs' ---@type table local gangs = require 'shared.gangs' +local storage = require 'server.storage.groups' + +if managementEnabled then + CreateThread(function() + storage.createGroupsTable() + + local jobData = storage.fetchJobs() + local gangData = storage.fetchGangs() + CreateJobs(jobData) + CreateGangs(gangData) + end) +end + for name in pairs(jobs) do if name ~= name:lower() then lib.print.error(('jobs.lua contains a job name with capital letters: %s'):format(name)) @@ -216,3 +232,55 @@ local function removeGangGrade(name, grade) end exports('RemoveGangGrade', removeGangGrade) + +---Update job data with persistence +---@param name string +---@param data Job +local function updateJob(name, data) + if not managementEnabled then + lib.print.error('Job management not enabled') + return + end + if not jobs[name] then + lib.print.error('Job must exist to update. Not found:', name) + return + end + if data.defaultDuty == nil then data.defaultDuty = jobs[name].defaultDuty end + if data.offDutyPay == nil then data.offDutyPay = jobs[name].offDutyPay end + if data.label == nil then data.label = jobs[name].label end + if data.type == nil then data.type = jobs[name].type end + storage.updateGroup(name, GroupType.JOB, data) + jobs[name] = data + TriggerEvent('qbx_core:server:onJobUpdate', name, jobs[name]) + TriggerClientEvent('qbx_core:client:onJobUpdate', -1, name, jobs[name]) +end + +exports('UpdateJob', updateJob) + +---Update gang data with persistence +---@param name string +---@param data Gang +local function updateGang(name, data) + if not managementEnabled then + lib.print.error('Gang management not enabled') + return + end + if not gangs[name] then + lib.print.error('Gang must exist to update. Not found:', name) + return + end + storage.updateGroup(name, GroupType.GANG, data) + gangs[name] = data + TriggerEvent('qbx_core:server:onGangUpdate', name, gangs[name]) + TriggerClientEvent('qbx_core:client:onGangUpdate', -1, name, gangs[name]) +end + +exports('UpdateGang', updateGang) + +lib.callback.register('qbx_core:server:getJobs', function() + return jobs +end) + +lib.callback.register('qbx_core:server:getGangs', function() + return gangs +end) diff --git a/server/storage/groups.lua b/server/storage/groups.lua new file mode 100755 index 000000000..7794da2c0 --- /dev/null +++ b/server/storage/groups.lua @@ -0,0 +1,93 @@ +local function createGroupsTable() + MySQL.query.await([[ + CREATE TABLE IF NOT EXISTS `groups` ( + `name` varchar(255) NOT NULL UNIQUE, + `type` varchar(10) NOT NULL, + `label` varchar(255) NOT NULL, + `defaultDuty` tinyint(1) DEFAULT 1, + `offDutyPay` tinyint(1) DEFAULT 0, + `grades` LONGTEXT DEFAULT NULL, + PRIMARY KEY (`name`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + ]]) +end + +---Update / insert group data +---@param name string +---@param groupType GroupType +---@param data Job|Gang +local function updateGroup(name, groupType, data) + if data.grades and table.type(data.grades) == 'hash' then + local _grades = {} + for k, v in pairs(data.grades) do + if not tonumber(k) then goto skip end + _grades[tonumber(k)] = v + ::skip:: + end + data.grades = _grades + end + MySQL.query([[ + INSERT INTO `groups` (name, type, label, defaultDuty, offDutyPay, grades) + VALUES (@name, @type, @label, @defaultDuty, @offDutyPay, @grades) + ON DUPLICATE KEY UPDATE + `type` = @type, `label` = @label, `defaultDuty` = @defaultDuty, `offDutyPay` = @offDutyPay, `grades` = @grades + ]], { + name = name, + type = groupType, + label = data.label, + defaultDuty = data.defaultDuty, + offDutyPay = data.offDutyPay, + grades = data.grades and json.encode(data.grades), + }) +end + +local function convertGrades(gangJson) + local _grades = json.decode(gangJson) + local grades = {} + for k, v in pairs(_grades) do + grades[tonumber(("%d"):format(k))] = v + end + return grades +end + +---Fetch job data +---@return table +local function fetchJobs() + local results = MySQL.query.await("SELECT * FROM `groups` WHERE `type` = 'job'") + local jobData = {} + + for i = 1, #results do + jobData[results[i].name] = { + label = results[i].label, + type = results[i].type, + defaultDuty = results[i].defaultDuty == 1, + offDutyPay = results[i].offDutyPay == 1, + grades = results[i].grades and convertGrades(results[i].grades) or {}, + } + end + + return jobData +end + +---Fetch gang data +---@return table +local function fetchGangs() + local results = MySQL.query.await("SELECT * FROM `groups` WHERE `type` = 'gang'") + local gangData = {} + + for i = 1, #results do + gangData[results[i].name] = { + label = results[i].label, + grades = results[i].grades and convertGrades(results[i].grades) or {}, + } + end + + return gangData +end + +return { + createGroupsTable = createGroupsTable, + updateGroup = updateGroup, + fetchJobs = fetchJobs, + fetchGangs = fetchGangs, +} From 51ed7375e0fdeb4d6724503c80a81de6044de0b5 Mon Sep 17 00:00:00 2001 From: citRa Date: Fri, 31 Jan 2025 14:16:14 -0400 Subject: [PATCH 2/2] fix(syntax): Trailing whitespace --- server/storage/groups.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/storage/groups.lua b/server/storage/groups.lua index 7794da2c0..747288a9c 100755 --- a/server/storage/groups.lua +++ b/server/storage/groups.lua @@ -27,7 +27,7 @@ local function updateGroup(name, groupType, data) data.grades = _grades end MySQL.query([[ - INSERT INTO `groups` (name, type, label, defaultDuty, offDutyPay, grades) + INSERT INTO `groups` (name, type, label, defaultDuty, offDutyPay, grades) VALUES (@name, @type, @label, @defaultDuty, @offDutyPay, @grades) ON DUPLICATE KEY UPDATE `type` = @type, `label` = @label, `defaultDuty` = @defaultDuty, `offDutyPay` = @offDutyPay, `grades` = @grades