Job management component API for employment, duty status, and permissions
The Jobs component manages character employment, on-duty status, and job-based permissions. Characters can have multiple jobs simultaneously, stored as an array on the character.
Characters can hold multiple jobs at once (Jobs array)
Grade System
Each job has workplaces and grades with levels
On-Duty Status
Clock in/out system per job
Permissions
Job-based access control via Permissions sub-component
Server-Side Only: All job operations must be performed on the server. Never attempt to modify job data from the client.
Important: Characters have a Jobs array (plural), NOT a single Job field. Each entry in the array is a job object with Id, WorkplaceId, GradeId, and GradeLevel.
-- Give a character the police joblocal char = Fetch:Source(source):GetData('Character')local stateId = char:GetData('SID')Jobs:GiveJob(stateId, 'police', 'lspd', 'officer')-- Give a second job (characters can have multiple)Jobs:GiveJob(stateId, 'mechanic', 'bennys', 'employee')-- Hire player callbackCallbacks:RegisterServerCallback('jobs:hire', function(source, data, cb) local player = Fetch:Source(source) if not player then return cb(false) end local char = player:GetData('Character') if not char then return cb(false) end local stateId = char:GetData('SID') Jobs:GiveJob(stateId, data.jobId, data.workplaceId, data.gradeId) Notification:Success(source, 'You have been hired!') cb(true)end)
-- Fire a character from their police joblocal char = Fetch:Source(source):GetData('Character')local stateId = char:GetData('SID')Jobs:RemoveJob(stateId, 'police')-- Fire player callbackCallbacks:RegisterServerCallback('jobs:fire', function(source, data, cb) Jobs:RemoveJob(data.targetSID, data.jobId) -- Notify target if online local targetPlayer = Fetch:SID(data.targetSID) if targetPlayer then Notification:Error(targetPlayer:GetData('Source'), 'You have been fired!') end cb(true)end)
-- Check if player is policeif Jobs.Permissions:HasJob(source, 'police') then print('Player is police')end-- Check if player has any emergency jobif Jobs.Permissions:HasJob(source, 'police', 'ems') then print('Player is emergency services')end-- Gate access by jobCallbacks:RegisterServerCallback('police:openDoor', function(source, data, cb) if not Jobs.Permissions:HasJob(source, 'police') then return cb(false, 'Not authorized') end -- Open the door Doors:SetLock(data.doorId, false) cb(true)end)
-- Get all of a player's jobslocal playerJobs = Jobs.Permissions:GetJobs(source)for _, job in ipairs(playerJobs) do print('Job:', job.Id, 'Grade:', job.GradeId)end
-- Clock in for police dutyJobs.Duty:On(source, 'police')-- Clock in with validationCallbacks:RegisterServerCallback('jobs:clockIn', function(source, data, cb) if not Jobs.Permissions:HasJob(source, data.jobId) then return cb(false, 'You do not have this job') end Jobs.Duty:On(source, data.jobId) Notification:Success(source, 'You are now on duty') cb(true)end)
-- Clock out of police dutyJobs.Duty:Off(source, 'police')-- Clock out with cleanupCallbacks:RegisterServerCallback('jobs:clockOut', function(source, data, cb) Jobs.Duty:Off(source, data.jobId) -- Remove job equipment RemoveJobEquipment(source, data.jobId) Notification:Info(source, 'You are now off duty') cb(true)end)
-- Get on-duty police officerslocal dutyData = Jobs.Duty:GetDutyData('police')local onDutyPolice = dutyData.DutyPlayersprint('On-duty police:', #onDutyPolice)-- Notify all on-duty policefunction NotifyPolice(message) local dutyData = Jobs.Duty:GetDutyData('police') for _, src in ipairs(dutyData.DutyPlayers) do Notification:Info(src, message) endend-- Check if any police are on dutyfunction AnyPoliceOnDuty() local dutyData = Jobs.Duty:GetDutyData('police') return #dutyData.DutyPlayers > 0end-- Dispatch systemfunction DispatchCall(callType, location) local dutyData = Jobs.Duty:GetDutyData('police') for _, src in ipairs(dutyData.DutyPlayers) do TriggerClientEvent('mythic-dispatch:client:NewCall', src, { type = callType, location = location, timestamp = os.time() }) endend
You can also access a character’s jobs through the DataStore:
-- Get character's jobs arraylocal player = Fetch:Source(source)local char = player:GetData('Character')local jobs = char:GetData('Jobs') -- Returns array of job objects-- Check if character has a specific jobfor _, job in ipairs(jobs) do if job.Id == 'police' then print('Character is police') print('Workplace:', job.WorkplaceId) print('Grade:', job.GradeId, 'Level:', job.GradeLevel) endend
While you can read char:GetData('Jobs') directly, always use Jobs:GiveJob() and Jobs:RemoveJob() to modify jobs, and Jobs.Permissions:HasJob() for checking job access. Do NOT use char:SetData('Jobs', ...) directly.
Always use Jobs.Permissions:HasJob() for access checks:
-- ✅ Good: Use permissions componentif Jobs.Permissions:HasJob(source, 'police') then -- Allow accessend-- ❌ Bad: Manually checking Jobs arraylocal char = Fetch:Source(source):GetData('Character')local jobs = char:GetData('Jobs')for _, j in ipairs(jobs) do if j.Id == 'police' then ... endend
Clean Up on Clock Out
function CleanClockOut(source, jobId) -- Clock out Jobs.Duty:Off(source, jobId) -- Remove job items RemoveJobItems(source) -- End active tasks EndActiveTasks(source) -- Remove job vehicles RemoveJobVehicles(source)end
Check Multiple Jobs
-- Check if player has any emergency service jobif Jobs.Permissions:HasJob(source, 'police', 'ems', 'fire') then -- Player is emergency servicesend
Job Integration: Use Jobs.Permissions:HasJob(source, jobId) for access checks and Jobs.Duty:GetDutyData(jobId).DutyPlayers to find on-duty players. These are the two most commonly used patterns.