> ## Content Index
> Fetch the complete content index at: https://noctis.id/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Cara Membuat Sistem Job Custom di QBCore (dengan Contoh Kode)
- URL: https://noctis.id/blog/qbcore-custom-job/
- Published: 2026-10-18T21:36:24.000Z
- Updated: 2026-10-18T21:36:24.000Z
- Description: 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.
- Author: Noctis
- Tags: FiveM, FiveM Development

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](https://noctis.id/blog/install-qbcore-txadmin/)) dan pemahaman dasar Lua (lihat [belajar Lua FiveM dari nol](https://noctis.id/blog/tutorial-lua-fivem/)). Referensi API QBCore ada di [docs.qbcore.org](https://docs.qbcore.org/?ref=noctis.id).

## Langkah 1: Daftarkan job di qb-core

Definisi job ada di `qb-core/shared/jobs.lua`. Tambahkan entri baru di dalam tabel `QBShared.Jobs`:

```lua
['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.
- **`defaultDuty`** menentukan apakah pemain langsung on duty saat login.
- `**offDutyPay**` menentukan apakah gaji tetap dibayar saat off duty. Untuk job sipil, biasanya `false`.
- `**grades**` berisi tingkatan jabatan. Key grade ditulis sebagai string angka sesuai format bawaan QBCore. `isboss = true` menandai grade yang bisa mengelola karyawan di resource manajemen bisnis.
- `**payment**` adalah gaji per siklus paycheck. Interval dan sumber dananya diatur di config qb-core (cek bagian money di `qb-core/config.lua` versi 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:

```text
/setjob [id pemain] barista 0
```

## Langkah 2: Buat resource job

```text
resources/[jobs]/noctis-barista/
├── fxmanifest.lua
├── config.lua
├── client.lua
└── server.lua
```

```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'
```

```lua
-- 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.0
```

Koordinat 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

```lua
-- 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](https://noctis.id/blog/install-ox-inventory-ox-lib-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:

```lua
-- 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:

```lua
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](https://noctis.id/blog/install-ox-inventory-ox-lib-qbcore/).

## Langkah 6: Daftarkan dan uji

```text
ensure qb-core
# ...
ensure noctis-barista
```

Checklist uji:

1. Pemain tanpa job barista tidak melihat prompt sama sekali.
2. Toggle duty berfungsi dan status tetap benar setelah `restart noctis-barista`.
3. Off duty tidak bisa menyeduh.
4. Memicu event dari jarak jauh atau berulang cepat ditolak server (uji dengan console client `TriggerServerEvent` di server development).
5. 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.job` nil**: kamu membaca data sebelum pemain login, atau lupa menangani `onResourceStart`.
- **Uang tidak masuk**: tipe akun salah. QBCore memakai `'cash'` dan `'bank'`.
- **Resmon tinggi**: loop `Wait(0)` tanpa jeda. Ukur dengan [resmon dan profiler](https://noctis.id/blog/fivem-resmon-profiler/).

## Mengembangkan lebih jauh

- Simpan status job seperti "kafe buka" di [GlobalState](https://noctis.id/blog/fivem-state-bags/) agar semua pemain tahu kafe sedang beroperasi.
- Buat menu manajemen karyawan dengan [NUI React](https://noctis.id/blog/fivem-nui-react-typescript/).
- Tambahkan whitelist dan pelatihan untuk job seperti polisi dan EMS. Dari sisi pemain, gambaran perannya ada di [cara jadi polisi, EMS, atau mechanic](https://noctis.id/blog/cara-jadi-polisi-ems-mechanic-gta-rp/).

## 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](https://noctis.id/id/fivem-hosting/?ref=noctis.id): vDedicated AMD Ryzen, DDR5, NVMe, root penuh, dan anti-DDoS di Singapore. Daftar paket lengkap ada di [Noctis Cloud](https://noctis.id/id/cloud/?ref=noctis.id).