This guide will take you from a fresh Qbox Core installation to creating your first character and building a basic resource. You’ll learn the fundamentals of working with the framework.
This guide assumes you’ve already completed the installation steps. If not, start there first.
characters = { -- Give specific licenses more character slots playersNumberOfCharacters = { ['license2:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'] = 5, }, -- Default for all other players defaultNumberOfCharacters = 3,}
If you have a character appearance resource installed (like qbx_appearance), you’ll be able to customize your character’s look.
4
Spawn into the world
Your character will spawn at the default spawn location or an apartment selection screen (if startingApartment = true and you have qbx_spawn installed).Default spawn location:
-- Add money to player (source = player ID)exports.qbx_core:AddMoney(1, 'cash', 500)-- Remove moneyexports.qbx_core:RemoveMoney(1, 'bank', 100)-- Set money to specific amountexports.qbx_core:SetMoney(1, 'cash', 1000)-- Get money amountlocal cash = exports.qbx_core:GetMoney(1, 'cash')print('Player has $' .. cash)
-- Give player police job at grade 0exports.qbx_core:SetJob(1, 'police', 0)-- Set job duty statusexports.qbx_core:SetJobDuty(1, true)-- Add player to additional jobexports.qbx_core:AddPlayerToJob('A1234567', 'mechanic', 2)
Config = {}Config.JobName = 'garbage' -- Must match a job in shared/jobs.luaConfig.PayPerTask = 150Config.TaskLocations = { vector3(287.84, -874.93, 29.36), vector3(213.82, -862.37, 30.49), vector3(127.52, -1030.54, 29.36),}
4
Create the client script
my_job/client/main.lua
local currentTask = 0-- Check if player has the required joblocal function hasRequiredJob() return exports.qbx_core:HasPrimaryGroup(Config.JobName)end-- Create task markersCreateThread(function() for i, coords in ipairs(Config.TaskLocations) do local blip = AddBlipForCoord(coords.x, coords.y, coords.z) SetBlipSprite(blip, 318) SetBlipScale(blip, 0.8) SetBlipColour(blip, 2) SetBlipAsShortRange(blip, true) BeginTextCommandSetBlipName('STRING') AddTextComponentString('Task Location') EndTextCommandSetBlipName(blip) endend)-- Task interaction pointsCreateThread(function() local ox_target = GetResourceState('ox_target') == 'started' for i, coords in ipairs(Config.TaskLocations) do if ox_target then exports.ox_target:addSphereZone({ coords = coords, radius = 2.0, options = { { name = 'complete_task', icon = 'fas fa-hand', label = 'Complete Task', canInteract = function() return hasRequiredJob() end, onSelect = function() TriggerServerEvent('my_job:completeTask', i) end } } }) else -- Fallback to text UI lib.zones.sphere({ coords = coords, radius = 2.0, onEnter = function() if hasRequiredJob() then lib.showTextUI('[E] Complete Task') end end, onExit = function() lib.hideTextUI() end, inside = function() if hasRequiredJob() and IsControlJustPressed(0, 38) then TriggerServerEvent('my_job:completeTask', i) end end }) end endend)
5
Create the server script
my_job/server/main.lua
-- Track completed tasks per playerlocal completedTasks = {}RegisterNetEvent('my_job:completeTask', function(taskId) local src = source local player = exports.qbx_core:GetPlayer(src) if not player then return end -- Check if player has the required job if player.PlayerData.job.name ~= Config.JobName then exports.qbx_core:Notify(src, 'You need to be a ' .. Config.JobName .. ' to do this!', 'error') return end -- Check if player is on duty if not player.PlayerData.job.onduty then exports.qbx_core:Notify(src, 'You must be on duty!', 'error') return end -- Initialize player's task tracker if not completedTasks[src] then completedTasks[src] = {} end -- Check if task was already completed if completedTasks[src][taskId] then exports.qbx_core:Notify(src, 'You already completed this task!', 'error') return end -- Mark task as completed completedTasks[src][taskId] = true -- Give reward local success = exports.qbx_core:AddMoney(src, 'cash', Config.PayPerTask, 'job-task-completed') if success then exports.qbx_core:Notify(src, 'Task completed! You earned $' .. Config.PayPerTask, 'success') endend)-- Clear completed tasks when player logs outAddEventHandler('qbx_core:server:playerLoggedOut', function(source) completedTasks[source] = nilend)
-- Client-side: Get your own datalocal playerData = exports.qbx_core:GetPlayerData()local job = playerData.joblocal money = playerData.money-- Server-side: Get player objectlocal player = exports.qbx_core:GetPlayer(source)local citizenid = player.PlayerData.citizenid-- Get offline player by citizenidlocal player = exports.qbx_core:GetOfflinePlayer('A1234567')
-- Check if player has a specific job (primary only)if exports.qbx_core:HasPrimaryGroup('police') then -- Player is policeend-- Check if player has any of multiple jobsif exports.qbx_core:HasGroup({'police', 'ambulance'}) then -- Player is police OR ambulanceend-- Check if player has job at specific gradeif exports.qbx_core:HasGroup({police = 3}) then -- Player is police at grade 3 or higherend-- Get all player's jobslocal groups = exports.qbx_core:GetGroups()-- Returns: { police = 2, mechanic = 1 }
-- Client to server callbacklocal result = lib.callback.await('my_resource:serverCallback', false, arg1, arg2)-- Register server callbacklib.callback.register('my_resource:serverCallback', function(source, arg1, arg2) local player = exports.qbx_core:GetPlayer(source) if not player then return false end -- Do something return trueend)
local player = exports.qbx_core:GetPlayer(source)if not player then return end-- Safe to use player here
Use citizenid for offline operations
-- This works even if player is offlinelocal player = exports.qbx_core:GetOfflinePlayer('A1234567')if player then exports.qbx_core:AddMoney(player.PlayerData.source or player.PlayerData.citizenid, 'bank', 1000)end
Handle money operations properly
-- AddMoney returns boolean for successlocal success = exports.qbx_core:AddMoney(source, 'cash', 500)if success then -- Money was addedelse -- Failed to add moneyend
Use job types for categorization
-- Jobs have types for easy filtering['police'] = { label = 'LSPD', type = 'leo', -- Law enforcement -- ...}-- Check player's job typeif player.PlayerData.job.type == 'leo' then -- Player is law enforcementend