2025-07-17 02:24:59 +02:00
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 " )
2025-08-11 23:31:05 +02:00
-- Notify online players that jobs have been updated
2025-07-17 02:24:59 +02:00
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 " )
2025-08-11 23:31:05 +02:00
TriggerClientEvent ( ' ps-multijob:refreshJobs ' , Players [ i ] ) -- Trigger refresh event
2025-07-17 02:24:59 +02:00
end
end
end )