local QBCore = exports['qb-core']:GetCoreObject() local function GetJobs(citizenid) local p = promise.new() MySQL.Async.fetchAll("SELECT jobdata FROM multijobs WHERE citizenid = @citizenid",{ ["@citizenid"] = citizenid }, function(jobs) if jobs[1] and jobs ~= "[]" then jobs = json.decode(jobs[1].jobdata) else local Player = QBCore.Functions.GetOfflinePlayerByCitizenId(citizenid) local temp = {} if not Config.IgnoredJobs[Player.PlayerData.job.name] then temp[Player.PlayerData.job.name] = Player.PlayerData.job.grade.level MySQL.insert('INSERT INTO multijobs (citizenid, jobdata) VALUES (:citizenid, :jobdata) ON DUPLICATE KEY UPDATE jobdata = :jobdata', { citizenid = citizenid, jobdata = json.encode(temp), }) end jobs = temp end p:resolve(jobs) end) return Citizen.Await(p) end exports("GetJobs", GetJobs) local function AddJob(citizenid, job, grade) local jobs = GetJobs(citizenid) for ignored in pairs(Config.IgnoredJobs) do if jobs[ignored] then jobs[ignored] = nil end end jobs[job] = grade MySQL.insert('INSERT INTO multijobs (citizenid, jobdata) VALUES (:citizenid, :jobdata) ON DUPLICATE KEY UPDATE jobdata = :jobdata', { citizenid = citizenid, jobdata = json.encode(jobs), }) end exports("AddJob", AddJob) local function UpdatePlayerJob(Player, job, grade) if Player.PlayerData.source ~= nil then Player.Functions.SetJob(job,grade) else -- player is offline local sharedJobData = QBCore.Shared.Jobs[job] if sharedJobData == nil then return end local sharedGradeData = sharedJobData.grades[grade] if sharedGradeData == nil then return end local isBoss = false if sharedGradeData.isboss then isBoss = true end MySQL.update.await("update players set job = @jobData where citizenid = @citizenid", { jobData = json.encode({ label = sharedJobData.label, name = job, isboss = isBoss, onduty = sharedJobData.defaultDuty, payment = sharedGradeData.payment, grade = { name = sharedGradeData.name, level = grade, }, }), citizenid = Player.PlayerData.citizenid }) end end local function UpdateJobRank(citizenid, job, grade) local Player = QBCore.Functions.GetOfflinePlayerByCitizenId(citizenid) if Player == nil then return end local jobs = GetJobs(citizenid) if jobs[job] == nil then return end jobs[job] = grade MySQL.update.await("update multijobs set jobdata = :jobdata where citizenid = :citizenid", { citizenid = citizenid, jobdata = json.encode(jobs), }) -- if the current job matches, then update if Player.PlayerData.job.name == job then UpdatePlayerJob(Player, job, grade) end end exports("UpdateJobRank", UpdateJobRank) local function RemoveJob(citizenid, job) local Player = QBCore.Functions.GetPlayerByCitizenId(citizenid) if Player == nil then Player = QBCore.Functions.GetOfflinePlayerByCitizenId(citizenid) end if Player == nil then return end local jobs = GetJobs(citizenid) jobs[job] = nil -- Since we removed a job, put player in a new job local foundNewJob = false if Player.PlayerData.job.name == job then for k,v in pairs(jobs) do UpdatePlayerJob(Player, k,v) foundNewJob = true break end end if not foundNewJob then UpdatePlayerJob(Player, "unemployed", 0) end MySQL.insert('INSERT INTO multijobs (citizenid, jobdata) VALUES (:citizenid, :jobdata) ON DUPLICATE KEY UPDATE jobdata = :jobdata', { citizenid = citizenid, jobdata = json.encode(jobs), }) end exports("RemoveJob", RemoveJob) QBCore.Commands.Add('removejob', 'Remove Multi Job (Admin Only)', { { name = 'id', help = 'ID of player' }, { name = 'job', help = 'Job Name' } }, false, function(source, args) local source = source if source ~= 0 then if args[1] then local Player = QBCore.Functions.GetPlayer(tonumber(args[1])) if Player then if args[2] then RemoveJob(Player.PlayerData.citizenid, args[2]) else TriggerClientEvent("QBCore:Notify", source, "Wrong usage!") end else TriggerClientEvent("QBCore:Notify", source, "Wrong usage!") end else TriggerClientEvent("QBCore:Notify", source, "Wrong usage!") end else TriggerClientEvent("QBCore:Notify", source, "Wrong usage!") end end, 'admin') QBCore.Commands.Add('addjob', 'Add Multi Job (Admin Only)', { { name = 'id', help = 'ID of player' }, { name = 'job', help = 'Job Name' }, { name = 'grade', help = 'Job Grade' } }, false, function(source, args) local source = source if source ~= 0 then if args[1] then local Player = QBCore.Functions.GetPlayer(tonumber(args[1])) if Player then if args[2]and args[3] then AddJob(Player.PlayerData.citizenid, args[2], args[3]) else TriggerClientEvent("QBCore:Notify", source, "Wrong usage!") end else TriggerClientEvent("QBCore:Notify", source, "Wrong usage!") end else TriggerClientEvent("QBCore:Notify", source, "Wrong usage!") end else TriggerClientEvent("QBCore:Notify", source, "Wrong usage!") end end, 'admin') QBCore.Functions.CreateCallback("ps-multijob:getJobs", function(source, cb) local Player = QBCore.Functions.GetPlayer(source) local jobs = GetJobs(Player.PlayerData.citizenid) local multijobs = {} local whitelistedjobs = {} local civjobs = {} local active = {} local pendingJobs = {} -- Track jobs that might be loaded later -- Initialize active jobs count local Players = QBCore.Functions.GetPlayers() for i = 1, #Players, 1 do local xPlayer = QBCore.Functions.GetPlayer(Players[i]) if xPlayer then active[xPlayer.PlayerData.job.name] = active[xPlayer.PlayerData.job.name] or 0 if xPlayer.PlayerData.job.onduty then active[xPlayer.PlayerData.job.name] = active[xPlayer.PlayerData.job.name] + 1 end end end for job, grade in pairs(jobs) do if QBCore.Shared.Jobs[job] == nil then -- Store this job for later processing pendingJobs[job] = grade else local online = active[job] or 0 local getjobs = { name = job, grade = grade, description = Config.Descriptions[job] or "No description available", icon = Config.FontAwesomeIcons[job] or "fa-solid fa-briefcase", label = QBCore.Shared.Jobs[job].label, active = online, } -- Safer access to grade properties if QBCore.Shared.Jobs[job].grades and QBCore.Shared.Jobs[job].grades[tostring(grade)] then getjobs.gradeLabel = QBCore.Shared.Jobs[job].grades[tostring(grade)].name or "Unknown" getjobs.salary = QBCore.Shared.Jobs[job].grades[tostring(grade)].payment or 0 else getjobs.gradeLabel = "Unknown" getjobs.salary = 0 end if Config.WhitelistJobs[job] then whitelistedjobs[#whitelistedjobs+1] = getjobs else civjobs[#civjobs+1] = getjobs end end end multijobs = { whitelist = whitelistedjobs, civilian = civjobs, } cb(multijobs) end) RegisterNetEvent("ps-multijob:changeJob",function(cjob, cgrade) local source = source local Player = QBCore.Functions.GetPlayer(source) if cjob == "unemployed" and cgrade == 0 then Player.Functions.SetJob(cjob, cgrade) return end local jobs = GetJobs(Player.PlayerData.citizenid) for job, grade in pairs(jobs) do if cjob == job and cgrade == grade then Player.Functions.SetJob(job, grade) end end end) RegisterNetEvent("ps-multijob:removeJob",function(job, grade) local source = source local Player = QBCore.Functions.GetPlayer(source) RemoveJob(Player.PlayerData.citizenid, job) end) -- QBCORE EVENTS RegisterNetEvent('ps-multijob:server:removeJob', function(targetCitizenId) MySQL.Async.execute('DELETE FROM multijobs WHERE citizenid = ?', { targetCitizenId }, function(affectedRows) if affectedRows > 0 then print('Removed job: ' .. targetCitizenId) else print('Cannot remove job: ' .. targetCitizenId) end end) end) RegisterNetEvent('QBCore:Server:OnJobUpdate', function(source, newJob) local source = source local Player = QBCore.Functions.GetPlayer(source) local jobs = GetJobs(Player.PlayerData.citizenid) local amount = 0 local setjob = newJob for k,v in pairs(jobs) do amount = amount + 1 end local maxJobs = Config.MaxJobs if QBCore.Functions.HasPermission(source, "admin") then maxJobs = math.huge end if amount < maxJobs and not Config.IgnoredJobs[setjob.name] then local foundOldJob = jobs[setjob.name] if not foundOldJob or foundOldJob ~= setjob.grade.level then AddJob(Player.PlayerData.citizenid, setjob.name, setjob.grade.level) end end end) -- Add the event listener for jobs_creator RegisterNetEvent("jobs_creator:injectJobs", function(jobs) -- This will run when jobs are injected by jobs_creator print("ps-multijob: Jobs have been updated by jobs_creator") -- Optional: You could notify online players that jobs have been updated -- This would allow them to refresh their job menu to see any new jobs local Players = QBCore.Functions.GetPlayers() for i = 1, #Players do local Player = QBCore.Functions.GetPlayer(Players[i]) if Player then TriggerClientEvent('QBCore:Notify', Players[i], "Job system has been updated", "success") end end end)