Cara Membuat Sistem Job Custom di QBCore (dengan Contoh Kode)
Tutorial membuat job custom di QBCore dari nol: mendaftarkan job dan grade, toggle duty, lokasi kerja, pembayaran yang divalidasi server, sampai cara mencegah exploit uang.
Job adalah tulang punggung server roleplay. Dari tukang ojek sampai barista, setiap pekerjaan memberi pemain alasan untuk berinteraksi, menghasilkan uang, dan membangun cerita karakternya. QBCore sudah membawa beberapa job bawaan, tapi cepat atau lambat kamu ingin membuat job khas kotamu sendiri.
Di tutorial ini kita membuat job barista sederhana di QBCore: mendaftarkan job dan grade, toggle duty, lokasi kerja, dan pembayaran yang aman dari exploit. Polanya bisa kamu pakai untuk job apa pun.
Prasyarat: server QBCore yang sudah berjalan (lihat cara install QBCore dengan txAdmin) dan pemahaman dasar Lua (lihat belajar Lua FiveM dari nol). Referensi API QBCore ada di docs.qbcore.org.
Langkah 1: Daftarkan job di qb-core
Definisi job ada di qb-core/shared/jobs.lua. Tambahkan entri baru di dalam tabel QBShared.Jobs:
['barista'] = {
label = 'Barista',
defaultDuty = false,
offDutyPay = false,
grades = {
['0'] = { name = 'Trainee', payment = 50 },
['1'] = { name = 'Barista', payment = 75 },
['2'] = { name = 'Head Barista', payment = 100 },
['3'] = { name = 'Manager', isboss = true, payment = 125 },
},
},Penjelasan singkat:
- Key job (
'barista') adalah nama internal. Gunakan huruf kecil tanpa spasi, karena nama ini dipakai di kode dan database. defaultDutymenentukan apakah pemain langsung on duty saat login.offDutyPaymenentukan apakah gaji tetap dibayar saat off duty. Untuk job sipil, biasanyafalse.gradesberisi tingkatan jabatan. Key grade ditulis sebagai string angka sesuai format bawaan QBCore.isboss = truemenandai grade yang bisa mengelola karyawan di resource manajemen bisnis.paymentadalah gaji per siklus paycheck. Interval dan sumber dananya diatur di config qb-core (cek bagian money diqb-core/config.luaversi kamu).
Format jobs.lua dapat sedikit berbeda antar versi QBCore. Selalu salin struktur dari job bawaan yang ada di file kamu sendiri, misalnya job mechanic, lalu sesuaikan.
Setelah menyimpan, restart server penuh. Kemudian tes dengan perintah admin:
/setjob [id pemain] barista 0Langkah 2: Buat resource job
resources/[jobs]/noctis-barista/
├── fxmanifest.lua
├── config.lua
├── client.lua
└── server.lua-- fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
lua54 'yes'
shared_script 'config.lua'
client_script 'client.lua'
server_script 'server.lua'
dependency 'qb-core'-- config.lua
Config = {}
Config.Job = 'barista'
Config.DutyPoint = vector3(-634.5, 227.4, 81.9)
Config.BrewPoint = vector3(-635.6, 233.1, 81.9)
Config.BrewTime = 5000 -- ms
Config.Cooldown = 15 -- detik antar seduhan per pemain
Config.PayPerCup = 35
Config.MaxDistance = 3.0Koordinat di atas hanya contoh. Ambil koordinat lokasi kafe di map kamu sendiri, misalnya dengan menu admin atau print GetEntityCoords(PlayerPedId()).
Langkah 3: Client, toggle duty dan aksi kerja
-- client.lua
local QBCore = exports['qb-core']:GetCoreObject()
local PlayerData = {}
AddEventHandler('QBCore:Client:OnPlayerLoaded', function()
PlayerData = QBCore.Functions.GetPlayerData()
end)
RegisterNetEvent('QBCore:Client:OnJobUpdate', function(job)
PlayerData.job = job
end)
RegisterNetEvent('QBCore:Client:SetDuty', function(duty)
if PlayerData.job then PlayerData.job.onduty = duty end
end)
-- Saat resource di-restart ketika pemain sudah login
AddEventHandler('onResourceStart', function(name)
if name == GetCurrentResourceName() then
PlayerData = QBCore.Functions.GetPlayerData()
end
end)
local function isBarista()
return PlayerData.job and PlayerData.job.name == Config.Job
end
CreateThread(function()
while true do
local sleep = 1000
if isBarista() then
local pos = GetEntityCoords(PlayerPedId())
if #(pos - Config.DutyPoint) < 1.5 then
sleep = 0
QBCore.Functions.DrawText3D(Config.DutyPoint.x, Config.DutyPoint.y, Config.DutyPoint.z,
'[E] Toggle duty')
if IsControlJustReleased(0, 38) then
TriggerServerEvent('QBCore:ToggleDuty')
end
elseif PlayerData.job.onduty and #(pos - Config.BrewPoint) < 1.5 then
sleep = 0
QBCore.Functions.DrawText3D(Config.BrewPoint.x, Config.BrewPoint.y, Config.BrewPoint.z,
'[E] Seduh kopi')
if IsControlJustReleased(0, 38) then
QBCore.Functions.Progressbar('brew_coffee', 'Menyeduh kopi...', Config.BrewTime,
false, true, { disableMovement = true }, {}, {}, {}, function()
TriggerServerEvent('noctis-barista:server:brew')
end)
end
end
end
Wait(sleep)
end
end)Perhatikan pola sleep: loop hanya berjalan setiap frame (Wait(0)) ketika pemain dekat titik kerja. Di luar itu loop tidur 1 detik. Ini perbedaan besar untuk performa client. Loop Wait(0) tanpa syarat adalah salah satu penyebab paling umum resource terlihat berat di resmon.
Fungsi DrawText3D dan Progressbar adalah helper QBCore. Ketersediaan dan signature-nya bisa berbeda antar versi. Banyak server kini memakai qb-target atau ox_target untuk interaksi dan ox_lib untuk progress bar, lihat cara install ox_lib di QBCore.
Langkah 4: Server, validasi lalu bayar
Ini bagian terpenting. Event noctis-barista:server:brew bisa dipicu siapa saja yang memakai executor. Server harus memeriksa semuanya sendiri:
-- server.lua
local QBCore = exports['qb-core']:GetCoreObject()
local lastBrew = {}
RegisterNetEvent('noctis-barista:server:brew', function()
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
local job = Player.PlayerData.job
-- 1. Job dan status duty
if job.name ~= Config.Job or not job.onduty then return end
-- 2. Jarak dari titik kerja, dihitung dari posisi server
local pos = GetEntityCoords(GetPlayerPed(src))
if #(pos - Config.BrewPoint) > Config.MaxDistance then
print(('[barista] %s memicu brew dari jarak jauh'):format(GetPlayerName(src)))
return
end
-- 3. Cooldown per pemain
local now = os.time()
if lastBrew[src] and now - lastBrew[src] < Config.Cooldown then return end
lastBrew[src] = now
-- 4. Bayar dengan nilai dari config server, bukan dari client
Player.Functions.AddMoney('cash', Config.PayPerCup, 'barista-brew')
TriggerClientEvent('QBCore:Notify', src, ('Kamu mendapat $%d'):format(Config.PayPerCup), 'success')
end)
AddEventHandler('playerDropped', function()
lastBrew[source] = nil
end)Empat pemeriksaan di atas (job, duty, jarak, cooldown) adalah standar minimum. Prinsip dasarnya: client hanya meminta, server yang memutuskan. Jangan pernah menerima jumlah uang, nama item, atau harga sebagai argumen event dari client.
Cooldown 15 detik dan bayaran $35 di contoh ini bukan angka rekomendasi. Sesuaikan dengan ekonomi servermu, karena payout job harus seimbang dengan harga barang, kendaraan, dan properti.
Langkah 5: Tambahkan bahan baku (opsional)
Job terasa lebih hidup kalau ada rantai produksi, misalnya biji kopi dibeli di toko lalu diseduh menjadi minuman yang bisa dijual. Dengan qb-inventory bawaan:
local item = Player.Functions.GetItemByName('biji_kopi')
if not item or item.amount < 1 then
return TriggerClientEvent('QBCore:Notify', src, 'Butuh biji kopi', 'error')
end
Player.Functions.RemoveItem('biji_kopi', 1)
Player.Functions.AddItem('kopi_susu', 1)Item biji_kopi dan kopi_susu harus didefinisikan dulu di qb-core/shared/items.lua. Kalau server kamu memakai ox_inventory, pakai export-nya seperti di artikel install ox_inventory di QBCore.
Langkah 6: Daftarkan dan uji
ensure qb-core
# ...
ensure noctis-baristaChecklist uji:
- Pemain tanpa job barista tidak melihat prompt sama sekali.
- Toggle duty berfungsi dan status tetap benar setelah
restart noctis-barista. - Off duty tidak bisa menyeduh.
- Memicu event dari jarak jauh atau berulang cepat ditolak server (uji dengan console client
TriggerServerEventdi server development). - Uang bertambah sesuai config dan tercatat di log.
Kesalahan umum
- Job tidak dikenali saat
/setjob: salah ketik key job, atau server belum di-restart penuh setelah mengubah shared/jobs.lua. PlayerData.jobnil: kamu membaca data sebelum pemain login, atau lupa menanganionResourceStart.- Uang tidak masuk: tipe akun salah. QBCore memakai
'cash'dan'bank'. - Resmon tinggi: loop
Wait(0)tanpa jeda. Ukur dengan resmon dan profiler.
Mengembangkan lebih jauh
- Simpan status job seperti "kafe buka" di GlobalState agar semua pemain tahu kafe sedang beroperasi.
- Buat menu manajemen karyawan dengan NUI React.
- Tambahkan whitelist dan pelatihan untuk job seperti polisi dan EMS. Dari sisi pemain, gambaran perannya ada di cara jadi polisi, EMS, atau mechanic.
Butuh tempat menjalankan kota kamu?
Noctis Roleplay sendiri dibangun di atas QBCore yang kami kembangkan in-house, dan berjalan di infrastruktur yang sama dengan yang kami sewakan. Kalau kamu sedang membangun server sendiri, lihat hosting FiveM Noctis: vDedicated AMD Ryzen, DDR5, NVMe, root penuh, dan anti-DDoS di Singapore. Daftar paket lengkap ada di Noctis Cloud.