RDE_OX_STANDARDS DOCS
RED DRAGON ELITE // THE STANDARD

OX Only.
Next-Gen Only. Free Forever.

The internal development standard behind every RDE resource — battle-tested in production, zero compromises. Every pattern here is extracted from real bugs caught in real RDE code, not theoretical best practice.

01 / Start Here

Philosophy

This document is the internal standard behind every RDE resource. Every script Red Dragon Elite releases is built against these rules — no exceptions, no shortcuts. We don't gatekeep knowledge — read this, understand it, build better things.

Core Principle Statebags over polling. Caching over repeat queries. Event-driven over tick-based. Every thread has a Wait(N) — nothing runs at frame rate unless it absolutely has to.

Technology Stack

UseNever
ox_core, ox_lib, ox_inventory, ox_target, oxmysqlESX, QBCore, any legacy compatibility shim

Use the CommunityOx forks — ox_core was discontinued by the original team in 2025 and forked by the community. Overextended officially resumed development in 2026; official docs live at overextended.dev.

02 / Start Here

Core Principles

PrincipleIn practice
Free & Open SourceEvery resource is source-available and forkable — no subscriptions, no obfuscation
Next-Gen Stack OnlyBuilt exclusively on OX — no ESX shims, no QBCore layers
Multilanguage Built InEvery resource ships EN + DE, inside Config.Locales, not separate files
Bulletproof SecurityServer-side validation on every action, triple-layer admin verification
Zero Config to StartTables auto-create with the rde_ prefix on first boot
No Duplicate BroadcastsOne sync path per state change — UpdateStatebag() and nothing else
No Wait() in NetEventsBlocking a NetEvent stalls the server thread — wrap async work in CreateThread
03 / Reference

fxmanifest & Config Pattern

fxmanifest.lua
fx_version 'cerulean'
lua54 'yes'

dependencies {
    '/server:7290', 'oxmysql', 'ox_lib', 'ox_core',
}
shared_scripts { '@ox_lib/init.lua', '@ox_core/lib/init.lua', 'config.lua' }
server_scripts { '@oxmysql/lib/MySQL.lua', 'server/server.lua' }
client_scripts { 'client/client.lua' }

Locales live inside config.lua

config.lua
Config.Locales = {
    en = { success = 'Success', no_permission = 'No permission' },
    de = { success = 'Erfolg', no_permission = 'Keine Berechtigung' },
}
04 / Reference

StateBag Sync — The Golden Rule

One sync path per state change. UpdateStatebag() fires both the GlobalState write AND the client event — callers call nothing else.

✅ correct pattern — production, rde_props v1.0.1
local function UpdateStatebag(id, data)
    local key = Config.StatebagPrefix .. id
    if data then
        GlobalState[key] = data
        TriggerClientEvent('rde_ys:statebagUpdate', -1, id, data)
    else
        GlobalState[key] = { _deleted = true }
        SetTimeout(1000, function() GlobalState[key] = nil end)
    end
end
-- Caller: UpdateStatebag(itemId, prop) — NO second TriggerClientEvent. Ever.
05 / Reference

Security & ox_target

Triple-layer admin check: ACE → ox_core groups → fallback false. Server validates everything — client is never authoritative.

ox_target zone lifecycle
-- Always pcall. Always remove before re-adding. Unique zone names.
local function CreateZone(itemId, entity)
    if State.zones[itemId] then
        pcall(function() exports.ox_target:removeZone(State.zones[itemId]) end)
    end
    local ok = pcall(function()
        exports.ox_target:addSphereZone({ coords = GetEntityCoords(entity), radius = 2.0 })
    end)
end
06 / More

Anti-Patterns Hall of Shame

Real production bugs caught in real RDE code. Never repeat these.

Double Broadcast

UpdateStatebag() already fires TriggerClientEvent(-1,...).

Fix: a second manual TriggerClientEvent call for the same data is a duplicate — delete it.

Wait() Inside a NetEvent

Blocks the server Lua thread entirely for every player until it resolves.

Fix: wrap any delay/retry logic in CreateThread(function() Wait(500) ... end).

Tombstone Immediately Overwritten

GlobalState[key] = { _deleted = true } followed instantly by GlobalState[key] = nil kills the tombstone before clients ever see it.

Fix: delay the nil-out with SetTimeout(1000, ...).

String Concat in SQL

MySQL.query('SELECT * FROM t WHERE id = ' .. id) is an injection risk.

Fix: always use ? placeholders with parameter arrays.

07 / More

FAQ

Do I have to follow this to use RDE resources?

No — this is RDE's internal standard. You don't need to follow it to run our scripts, only if you want to build your own in the same style.

Why not ESX or QBCore compatibility?

Compatibility shims add overhead and legacy assumptions that fight against StateBag-driven, event-first architecture. RDE builds ox_core-native from the ground up.

08 / More

Version History

v2.0.0

Updated for Overextended's 2026 development resumption. All patterns re-verified against production-proven rde_props v1.0.1 source.