FiveM & QBCore · Practical guide

FiveM Drug Dealer Script — Design, Safety, and Implementation Guide

A practical, server-authoritative guide to building a secure, modular FiveM drug dealer script with QBCore/ESX integration, qb-target/ox_lib support, anti-cheat validation, and.

Stellar AI · Updated 8 September 2026 · 5 min read

This guide walks through designing and shipping a robust FiveM drug dealer script. Focus is on server authority, safe item/cash handling, target integration, and anti-exploit validation. Use the patterns below to build a maintainable, audit-ready resource.

Overview

Drug dealer mechanics are common in RP servers: players procure or produce illicit items and sell them to NPC dealers for cash. Because in-game illicit economies affect balance and invite exploits, the canonical rule is: trust nothing from the client. All checks that affect player inventory, cash, or police notifications must be authoritative on the server.

Design principles

  • Server authority: validate items, quantities, and payments server-side.
  • Modular integration: keep qb-target / ox_lib / radial menus as optional adapters.
  • Config-driven behavior: store prices, locations, cooldowns, and police odds in a config file.
  • Anti-exploit: rate-limits, ownership checks, and persistent logs for suspicious activity.

Assumptions and framework choices

This guide uses QBCore as the primary example because it's popular in FiveM RP communities. Where appropriate I'll note ESX alternatives. If your server uses ox_lib utilities or qb-target, the code shows how to add optional exports without hard dependencies.

High-level implementation plan

  1. Create a small resource with fxmanifest.lua, config.lua, client.lua and server.lua.
  2. Define dealer locations and a server-side sell event that validates items and pays the player.
  3. Support target interaction (qb-target) and a fallback keypress interaction.
  4. Add anti-exploit controls: cooldowns, item ownership checks, and server logging.
  5. Test with an admin account and run simulated abuse cases (rapid sell, forged item reports).

Key server-side rules

  • Never accept client-provided item amounts or prices without server-side verification.
  • Use framework inventory functions (QBCore.Functions.GetPlayer, QBCore.Functions.RemoveItem, AddMoney) rather than direct DB writes.
  • Keep payment and police notifications atomic: only notify police after successful item removal and payment.

Example files (complete minimal resource)

Below are destination-labelled files for a minimal QBCore resource. Adjust names, folder location, and exports to match your server's conventions.

-- fxmanifest.lua
fx_version 'cerulean'
game 'gta5'

author 'YourName'
description 'Simple drug dealer script (QBCore)'
version '1.0.0'

server_script 'server.lua'
client_script 'client.lua'
shared_script 'config.lua'
-- config.lua
Config = {}
Config.Dealers = {
  {name = 'Downtown Dealer', coords = vector3( -1486.1, -378.2, 40.16 ), sell_item = 'weed', price = 200, policeChance = 0.05},
}
Config.SellCooldown = 5 -- seconds per player per dealer
-- server.lua (QBCore example)
local QBCore = exports['qb-core']:GetCoreObject()
local lastSell = {}

RegisterNetEvent('drugdealer:server:sell', function(dealerIndex)
  local src = source
  local xPlayer = QBCore.Functions.GetPlayer(src)
  if not xPlayer then return end

  local dealer = Config.Dealers[dealerIndex]
  if not dealer then return end

  -- rate limit
  lastSell[src] = lastSell[src] or {}
  if lastSell[src][dealerIndex] and (os.time() - lastSell[src][dealerIndex]) < Config.SellCooldown then
    TriggerClientEvent('QBCore:Notify', src, 'You must wait before selling again', 'error')
    return
  end

  -- check inventory server-side
  local item = xPlayer.Functions.GetItemByName(dealer.sell_item)
  if not item or item.amount < 1 then
    TriggerClientEvent('QBCore:Notify', src, 'You have nothing to sell', 'error')
    return
  end

  -- remove item and give money atomically
  local removed = xPlayer.Functions.RemoveItem(dealer.sell_item, 1)
  if not removed then
    TriggerClientEvent('QBCore:Notify', src, 'Failed to remove item', 'error')
    return
  end

  xPlayer.Functions.AddMoney('cash', dealer.price, 'sold-drugs')
  lastSell[src][dealerIndex] = os.time()
  TriggerClientEvent('QBCore:Notify', src, 'Sold 1 '..dealer.sell_item..' for $'..dealer.price, 'success')

  -- optional police alert with validated chance
  if math.random() < dealer.policeChance then
    TriggerEvent('police:server:sendAlert', {x = dealer.coords.x, y = dealer.coords.y, z = dealer.coords.z, message = 'Suspicious deal'})
  end
end)
-- client.lua (target integration example)
local QBCore = exports['qb-core']:GetCoreObject()

Citizen.CreateThread(function()
  for i, d in ipairs(Config.Dealers) do
    if exports['qb-target'] then
      exports['qb-target']:AddBoxZone('dealer_'..i, d.coords, 1.2, 1.2, {name = 'dealer_'..i, heading = 0, debugPoly = false}, {
        options = {{type = 'client', event = 'drugdealer:client:openMenu', icon = 'fas fa-user-secret', label = 'Talk to dealer', dealerIndex = i}},
        distance = 2.5
      })
    end
  end
end)

RegisterNetEvent('drugdealer:client:openMenu', function(data)
  -- fallback: directly call server event
  TriggerServerEvent('drugdealer:server:sell', data.dealerIndex)
end)

Security and anti-exploit measures

  • Rate-limit sells per-player per-dealer to prevent rapid-grinding.
  • Perform all inventory checks and removes on the server using framework APIs.
  • Log unusual patterns to a secure server log: high-frequency sells, negative balances, or failed item removals.
  • Don’t trust client-sent dealerIndex blindly — validate index exists in Config.Dealers.

ESX differences

For ESX, replace QBCore.Functions.GetPlayer and inventory calls with ESX.GetPlayerFromId and xPlayer.getInventoryItem / xPlayer.removeInventoryItem. Also use ESX.ShowNotification on the client where appropriate. Keep the server authority rule unchanged.

Testing and validation

Local testing should include:

  1. Sell flow with correct item and without item.
  2. Rapid sell attempts to validate cooldown behavior.
  3. Simulated packet tampering: call the server event with invalid dealerIndex or spoofed player IDs (as server owner) to ensure checks block it.

Deployment checklist

TaskWhyDone?
Use framework APIs for inventory/cashServer authority
Add cooldowns & rate-limitsPrevent rapid abuse
Validate dealerIndex & item existencePrevent null deref / exploits
Log suspicious eventsAudit & ban support
Test with ESX/QBCore and qb-target/ox_libCompatibility

Next steps and resources

To iterate quickly, consider using a planning workspace that produces destination-labelled files and installs quickly. If you want a guided generator to produce full resource files, try the Stellar AI app to prototype variations and generate complete file sets: https://trystellarai.com/app. When you're iterating on police notification logic or balancing payout rates, keep the configuration external so you can tweak values without changing logic.

For community guides and deeper patterns (hooking into evidence systems, laundering mechanics, or vendor cooldown persistence), read articles and collections from the Stellar blog for recommended patterns: https://trystellarai.com/blog. You can also open the Stellar AI app to prototype alternate dealer behaviors and test multiple configurations quickly: https://trystellarai.com/app.

Closing notes

Keep social features (informing players, leaving hints) client-side only as UI, and keep all economic changes server-side. If you later add production chains (grow -> process -> sell), maintain the same atomic, validated operations at each step. That contract—server trust, client UI—keeps your server balanced and more resistant to exploits.

Build your next system with Stellar AI

Describe one feature, get organized project files, then bring back your errors to keep improving. Start free with no card required. Test generated code in a private development environment before release.

Create your first script free →