Skip to Content
QBCore docs – powered by Nextra 4
GuidesQBCore Scripting Guide

QBCore Scripting Guide

Learn how to create custom scripts and resources for the QBCore framework. This comprehensive guide covers everything from basic script structure to advanced development techniques.

Table of Contents

Getting Started

Prerequisites

Before you start scripting for QBCore, ensure you have:

  • QBCore Framework: Latest version installed
  • Development Environment: VS Code or similar editor
  • Basic Lua Knowledge: Understanding of Lua programming language
  • FiveM Server: Development server for testing
  • Database Access: For data persistence

Development Environment Setup

  1. Install VS Code Extensions:

    • Lua Language Server
    • FiveM Development Tools
    • GitLens (for version control)
  2. Create Development Workspace:

    mkdir qbcore-scripts cd qbcore-scripts
  3. Set up Version Control:

    git init git add .gitignore

Basic Script Structure

Standard QBCore Resource Structure

resource-name/ ├── fxmanifest.lua # Resource manifest ├── config.lua # Configuration file ├── server/ │ ├── main.lua # Server-side logic │ └── callbacks.lua # Server callbacks ├── client/ │ ├── main.lua # Client-side logic │ └── events.lua # Client events ├── shared/ │ └── config.lua # Shared configuration └── html/ # NUI files (if needed) ├── index.html ├── style.css └── script.js

Essential Files

fxmanifest.lua

fx_version 'cerulean' game 'gta5' description 'QBCore Custom Script' version '1.0.0' author 'YourName' shared_scripts { 'shared/config.lua', 'config.lua' } client_scripts { 'client/main.lua', 'client/events.lua' } server_scripts { '@oxmysql/lib/MySQL.lua', -- If using database 'server/main.lua', 'server/callbacks.lua' } -- NUI (if using HTML interface) ui_page 'html/index.html' files { 'html/index.html', 'html/style.css', 'html/script.js' } dependencies { 'qb-core' } lua54 'yes'

QBCore API Integration

Getting QBCore Object

Always get the QBCore object at the beginning of your scripts:

local QBCore = exports['qb-core']:GetCoreObject()

Player Data Access

Client-Side Player Data

-- Get local player data local PlayerData = QBCore.Functions.GetPlayerData() -- Listen for player data updates RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() PlayerData = QBCore.Functions.GetPlayerData() -- Initialize your script here end) RegisterNetEvent('QBCore:Client:OnPlayerUnload', function() PlayerData = {} -- Clean up when player logs out end) RegisterNetEvent('QBCore:Player:SetPlayerData', function(val) PlayerData = val -- Handle player data updates end)

Server-Side Player Data

-- Get player by source local Player = QBCore.Functions.GetPlayer(source) if not Player then return end -- Get player by citizen ID local Player = QBCore.Functions.GetPlayerByCitizenId(citizenId) -- Get player by phone number local Player = QBCore.Functions.GetPlayerByPhone(phone)

Creating Your First Script

Let’s create a simple “Daily Bonus” script:

Step 1: Create the Resource Structure

mkdir qb-dailybonus cd qb-dailybonus

Step 2: Create fxmanifest.lua

fx_version 'cerulean' game 'gta5' description 'QBCore Daily Bonus System' version '1.0.0' author 'YourName' shared_script 'config.lua' client_script 'client/main.lua' server_script 'server/main.lua' dependencies { 'qb-core', 'oxmysql' } lua54 'yes'

Step 3: Create Configuration

-- config.lua Config = {} Config.DailyBonus = { money = 1000, -- Cash reward bank = 500, -- Bank reward items = { -- Item rewards {name = 'bread', amount = 5}, {name = 'water', amount = 3} } } Config.ResetTime = 24 * 60 * 60 * 1000 -- 24 hours in milliseconds

Step 4: Server-Side Logic

-- server/main.lua local QBCore = exports['qb-core']:GetCoreObject() -- Database table creation MySQL.ready(function() MySQL.query([[ CREATE TABLE IF NOT EXISTS player_dailybonus ( citizenid VARCHAR(50) PRIMARY KEY, last_claim TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ]]) end) -- Server callback for checking eligibility QBCore.Functions.CreateCallback('qb-dailybonus:server:canClaim', function(source, cb) local Player = QBCore.Functions.GetPlayer(source) if not Player then return cb(false) end local citizenId = Player.PlayerData.citizenid MySQL.single('SELECT last_claim FROM player_dailybonus WHERE citizenid = ?', { citizenId }, function(result) if not result then -- First time claim cb(true) else local lastClaim = result.last_claim local currentTime = os.time() * 1000 local lastClaimTime = tonumber(lastClaim) if (currentTime - lastClaimTime) >= Config.ResetTime then cb(true) else cb(false) end end end) end) -- Claim daily bonus RegisterNetEvent('qb-dailybonus:server:claimBonus', function() local src = source local Player = QBCore.Functions.GetPlayer(src) if not Player then return end local citizenId = Player.PlayerData.citizenid -- Give rewards Player.Functions.AddMoney('cash', Config.DailyBonus.money) Player.Functions.AddMoney('bank', Config.DailyBonus.bank) -- Give items for _, item in pairs(Config.DailyBonus.items) do Player.Functions.AddItem(item.name, item.amount) end -- Update database MySQL.insert('INSERT INTO player_dailybonus (citizenid, last_claim) VALUES (?, ?) ON DUPLICATE KEY UPDATE last_claim = ?', { citizenId, os.time() * 1000, os.time() * 1000 }) TriggerClientEvent('QBCore:Notify', src, 'Daily bonus claimed successfully!', 'success') end)

Step 5: Client-Side Logic

-- client/main.lua local QBCore = exports['qb-core']:GetCoreObject() -- Command to claim daily bonus RegisterCommand('dailybonus', function() QBCore.Functions.TriggerCallback('qb-dailybonus:server:canClaim', function(canClaim) if canClaim then TriggerServerEvent('qb-dailybonus:server:claimBonus') else QBCore.Functions.Notify('You already claimed your daily bonus!', 'error') end end) end) -- Add command suggestion TriggerEvent('chat:addSuggestion', '/dailybonus', 'Claim your daily bonus')

Advanced Concepts

Database Integration

Using oxmysql

-- Insert data MySQL.insert('INSERT INTO table_name (column1, column2) VALUES (?, ?)', { value1, value2 }, function(insertId) print('Inserted with ID: ' .. insertId) end) -- Select single row MySQL.single('SELECT * FROM table_name WHERE id = ?', {id}, function(result) if result then print('Found: ' .. result.name) end end) -- Select multiple rows MySQL.query('SELECT * FROM table_name WHERE active = ?', {true}, function(results) for i = 1, #results do print('Row: ' .. results[i].name) end end) -- Update data MySQL.update('UPDATE table_name SET column1 = ? WHERE id = ?', { newValue, id }, function(affectedRows) print('Updated ' .. affectedRows .. ' rows') end)

Event Handling

Custom Events

-- Server to Client TriggerClientEvent('custom:event', source, data) -- Client to Server TriggerServerEvent('custom:event', data) -- Register event handler RegisterNetEvent('custom:event', function(data) -- Handle the event print('Received data: ' .. json.encode(data)) end)

QBCore Specific Events

-- Player loaded RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() -- Player finished loading end) -- Job change RegisterNetEvent('QBCore:Client:OnJobUpdate', function(JobInfo) -- Player job changed end) -- Gang change RegisterNetEvent('QBCore:Client:OnGangUpdate', function(GangInfo) -- Player gang changed end)

Creating Callbacks

-- Server callback QBCore.Functions.CreateCallback('script:getData', function(source, cb, ...) local args = {...} local Player = QBCore.Functions.GetPlayer(source) -- Process data cb(responseData) end) -- Client trigger callback QBCore.Functions.TriggerCallback('script:getData', function(data) -- Handle response print(json.encode(data)) end, arg1, arg2)

NUI Integration

HTML Interface

<!-- html/index.html --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Custom Interface</title> <link rel="stylesheet" href="style.css"> </head> <body> <div id="container"> <h1>Custom Script Interface</h1> <button id="closeBtn">Close</button> </div> <script src="script.js"></script> </body> </html>

JavaScript Communication

// html/script.js window.addEventListener('message', function(event) { const data = event.data; switch(data.action) { case 'open': $('#container').show(); break; case 'close': $('#container').hide(); break; } }); $('#closeBtn').click(function() { $.post('https://resource-name/close', {}); });

Lua NUI Integration

-- Client-side NUI control RegisterCommand('openui', function() SetNuiFocus(true, true) SendNUIMessage({ action = 'open' }) end) RegisterNUICallback('close', function(data, cb) SetNuiFocus(false, false) SendNUIMessage({ action = 'close' }) cb('ok') end)

Best Practices

Code Organization

  1. Separate Concerns: Keep client and server logic separate
  2. Use Config Files: Make your scripts configurable
  3. Error Handling: Always check for nil values and edge cases
  4. Consistent Naming: Use clear, descriptive variable names

Performance Optimization

-- Cache frequently used values local PlayerData = QBCore.Functions.GetPlayerData() -- Use proper thread management CreateThread(function() while true do if PlayerData then -- Do work end Wait(1000) -- Don't run every frame end end) -- Clean up resources AddEventHandler('onResourceStop', function(resource) if resource == GetCurrentResourceName() then -- Cleanup code here end end)

Security Considerations

-- Server-side validation RegisterNetEvent('script:doSomething', function(data) local src = source local Player = QBCore.Functions.GetPlayer(src) -- Always validate player exists if not Player then return end -- Validate input data if not data or type(data) ~= 'table' then return end -- Additional security checks if not Player.Functions.GetItemByName('required_item') then TriggerClientEvent('QBCore:Notify', src, 'You need the required item', 'error') return end -- Process request end)

Deployment and Testing

Local Testing

  1. Add to server.cfg:

    start qb-dailybonus
  2. Monitor for errors:

    tail -f server_log.txt
  3. Test all functions:

    • Join server as different players
    • Test edge cases
    • Verify database operations

Production Deployment

  1. Code Review: Review all code for security issues
  2. Database Backup: Backup database before deployment
  3. Staged Rollout: Test on staging environment first
  4. Monitoring: Monitor server performance after deployment

Congratulations! You now have the foundation to create powerful QBCore scripts. Remember to always test thoroughly and follow best practices for security and performance.

For more advanced topics, check out our API documentation or join our community discussions.

Last updated on