FiveM & QBCore · Practical guide

How to Add a Casino to Your QBCore FiveM Server

A casino script adds a fun money sink to your FiveM economy. This guide covers setting up slots, blackjack, coinflip and a chip system on QBCore.

Stellar AI · Updated 8 September 2026 · 7 min read

A practical guide to how to add a casino to your qbcore fivem server, with implementation decisions, validation steps, and security considerations for a production-minded project.

Overview

Adding a casino to a QBCore-based FiveM server involves more than creating UI and placing slot machines. The feature touches player economics, persistence, concurrency, and anti-cheat strategies. This guide focuses on architecture, server-side validation, implementation choices (including ox_lib / ESX considerations), testing, and deployment. If you want to pair resource telemetry or automated checks with a hosted app, consider using the Stellar tools available at https://trystellarai.com/app for operational dashboards and notifications.

Architectural design

Architect the casino as a set of cooperating resources: a server-side game engine that validates bets and handles money, a persistence layer (MySQL, PostgreSQL, or another supported database), and a client NUI for visuals and input. Keep game logic authoritative on the server. Common dependencies in the QBCore ecosystem include qb-core, ox_lib, qb-target, qb-input, and an inventory or money system (qb-banking or qb-boss for house accounts). ESX servers follow the same pattern but substitute ESX server functions where QBCore ones are used.

Minimal components:

  • Server game controller: validates bets, resolves outcomes, updates accounts, writes transaction logs.
  • Database: stores house bank, player history, weekly/monthly stats, and blacklists.
  • Client NUI: presentation layer only; all sensitive actions must be confirmed server-side.
  • Admin interfaces: control house payouts, emergency freeze, and audit tools.

Integration choices and trade-offs

Decide if the casino is a standalone resource or deeply integrated into your economy. Standalone development lowers blast radius and simplifies deployment, but tighter integration (e.g., into qb-boss) makes boss controls and accounting more seamless.

UI options:

  • NUI (HTML/CSS/JS): full control and easy to iterate — must never replace server checks.
  • Native GTA UI: lighter and more resilient, but limited for complex games.

RNG placement:

  • Server-authoritative RNG (recommended): resolve outcomes on the server and send results to clients.
  • Client-only RNG (not recommended): vulnerable to manipulation and should be considered only for purely cosmetic visuals, after server confirmation.

Data model and persistence

Persist the minimum required state to ensure recoverability and auditability. Store each bet, resulting payout, player identifier, resource name, and timestamp. Maintain a house ledger for transfers to the boss account.

Table Key columns Purpose
casino_bets id (pk), player_id, amount, game_type, result, payout, timestamp Immutable audit trail of every bet
casino_house id, balance, last_adjusted House bank for payouts and audits
casino_sessions id, player_id, start_time, end_time, net_win_loss Session summaries for anti-fraud and limits
casino_blacklist id, player_id, reason, expires_at Block known abusers

Persistence tips

  • Write every financial change to a database using transactions or atomic updates.
  • Use MySQL transactions (e.g., START TRANSACTION / COMMIT) or equivalent to avoid double-spend.
  • Log raw server events to a file or logging system for post-incident analysis.

Security and server validation

Never trust client input. For FiveM (QBCore/ESX) treat any message from the client as untrusted until validated server-side. For Roblox, always enforce server authority with RemoteEvents and DataStore checks (see later section). Key validation points:

  1. Validate player balance on the server before accepting a bet.
  2. Validate bet amounts against configured min/max and per-session limits.
  3. Apply throttling and rate limits to prevent automated high-frequency betting exploits.
  4. Use server RNG for outcome generation and optionally seed it with a rotating server secret.
  5. Verify that payouts do not exceed available house/liquidity where applicable; if using separate house accounts, lock rows or use atomic DB updates.

Anti-exploit measures:

  • Maintain temporary in-memory locks per player to avoid concurrent bet races.
  • Detect improbable win patterns and pause accounts for review.
  • Implement admin freeze and emergency rollback procedures.

Implementation patterns (QBCore example)

Keep server logic in a server.lua file and expose minimal client events. Example server-side pattern for handling a bet using QBCore functions and server-authoritative RNG:

-- server.lua
RegisterNetEvent('casino:server:PlaceBet', function(gameType, betAmount)
  local src = source
  local player = QBCore.Functions.GetPlayer(src)
  if not player then return end

  -- Validate bet
  if type(betAmount) ~= 'number' or betAmount <= 0 then
    TriggerClientEvent('casino:client:BetRejected', src, 'invalid_amount')
    return
  end

  local balance = player.Functions.GetMoney('bank') -- or 'cash' per your config
  if balance < betAmount then
    TriggerClientEvent('casino:client:BetRejected', src, 'insufficient_funds')
    return
  end

  -- Atomic withdrawal + resolve
  player.Functions.RemoveMoney('bank', betAmount, 'casino-bet')
  local outcome = math.random() -- server RNG
  local payout = CalculatePayout(gameType, betAmount, outcome)

  if payout > 0 then
    player.Functions.AddMoney('bank', payout, 'casino-win')
  end

  -- Persist bet and result
  exports.ghmattimysql:execute('INSERT INTO casino_bets (player_id, amount, game_type, result, payout, timestamp) VALUES (?, ?, ?, ?, ?, ?)',
    {player.PlayerData.citizenid, betAmount, gameType, outcome, payout, os.time()})

  TriggerClientEvent('casino:client:BetResult', src, { outcome = outcome, payout = payout })
end)

Notes: Replace exports.ghmattimysql with your configured DB integration (MySQL-async, oxmysql). Use QBCore event names and money functions appropriate to your version. The above pattern demonstrates key checks: get player, check balance server-side, remove funds server-side, compute payout server-side, persist operation, then notify client.

Roblox considerations (Luau)

Roblox servers must be authoritative for game state and persistence. Use RemoteEvents or RemoteFunctions sparingly — always validate parameters in the server callback. DataStore calls can fail; handle failures gracefully and implement retry/backoff logic.

-- Server Script (Roblox)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Remote = ReplicatedStorage:WaitForChild("PlaceBet")
local DataStoreService = game:GetService("DataStoreService")
local playerStore = DataStoreService:GetDataStore("PlayerCasino")

Remote.OnServerEvent:Connect(function(player, gameType, betAmount)
  if type(betAmount) ~= "number" or betAmount <= 0 then return end

  -- Validate server-side economy
  local success, balance = pcall(function()
    return playerStore:GetAsync(player.UserId .. "_balance")
  end)
  if not success or (balance or 0) < betAmount then
    Remote:FireClient(player, "Rejected", "insufficient_funds")
    return
  end

  -- Deduct, resolve, and persist with retry
  local newBalance = balance - betAmount
  local outcome = math.random()
  local payout = calculatePayout(gameType, betAmount, outcome)
  newBalance = newBalance + payout

  local attempts = 0
  local ok
  repeat
    ok = pcall(function()
      playerStore:SetAsync(player.UserId .. "_balance", newBalance)
    end)
    attempts = attempts + 1
    if not ok then wait(2 ^ attempts) end
  until ok or attempts >= 5

  if not ok then
    -- rollback server state in-memory or flag issue; notify player
    Remote:FireClient(player, "Error", "save_failed")
    return
  end

  Remote:FireClient(player, "Result", { outcome = outcome, payout = payout })
end)

Important: Roblox DataStore calls should always be wrapped in pcall and have a recovery or queueing strategy. Never rely on a client to report wins.

Testing and QA

Testing should cover functional correctness, concurrency, and resilience:

  • Unit tests for payout calculations and edge cases (min/max bets, rounding).
  • Integration tests that simulate DB failures and check rollback behavior.
  • Load tests to simulate many simultaneous bets and ensure atomicity and acceptable latency.
  • Exploit tests: simulate a modified client sending repeated events, malformed payloads, large bet amounts, and concurrent place-bet races.

Include logging hooks and a debug mode that emits structured logs for each transactional step, making it straightforward to replay or audit events after an issue.

Deployment and maintenance

Deploy changes behind feature flags or to a staging server first. Maintain database migration scripts to change schema safely. Plan backups and a rollback strategy for both code and data.

For operations, consider centralized monitoring and alerting. If you use a managed app for alerts or runbook integrations, you can link runtime events to your tools at https://trystellarai.com/app. Keep an admin-only UI to pause gameplay, adjust house limits, and review the audit trail.

Performance and scaling

Optimize for low-latency responses:

  • Cache non-critical read-heavy data in memory (e.g., game configurations) and invalidate on change.
  • Keep transactional DB writes minimal and batched where possible (e.g., asynchronously archive low-value logs to reduce primary DB load).
  • Consider sharding or separating analytics/metrics into a different datastore to avoid affecting transactional performance.

Operational considerations

  • Rotate any server-side secrets used in RNG seeding at regular intervals.
  • Limit the number of concurrent bets per player with a server-side session object.
  • Use connection pools for DB access to prevent overload under bursts.

Practical checklist before going live

Task Why Done
Server-side validation of all bet events Prevents client-side exploits and cheating [ ]
DB transaction logic for withdrawals and payouts Ensures atomic finance operations [ ]
Persistent audit log (casino_bets) For investigations and rollback [ ]
Admin controls (freeze, audit, refund) Operational safety [ ]
Load and exploit testing Ensures reliability under stress [ ]
Backup and migration plan Safe deployment and recovery [ ]

Maintenance and monitoring

Regular tasks:

  • Periodic reconciliation between house ledger and DB to detect drift;
  • Review audit logs for suspicious patterns;
  • Monitor latencies for DB writes and key RPCs;
  • Rotate admin credentials and keep role-based access control tight.

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 →