One framework layer for FiveM and RedM scripts. Free, open source, zero dependencies.
Write your script once. It runs on ESX, QBCore, Qbox, VORP, RSG, or no framework at
all, and you never write another framework.lua.
-- Same five lines on every server, every framework, both games.
RX.OnReady(function()
local player = RX.Player.get(source)
print(player.charId, player.job.name)
if RX.Money.remove(source, 'cash', 250, 'bought a horse') then
RX.Notify(source, 'Enjoy the horse.', 'success')
end
end)- Download the latest release and drop
rx_corein yourresourcesfolder. - Add
ensure rx_coretoserver.cfgbefore any script that uses it. - Done. No configuration required.
Two lines in fxmanifest.lua:
dependency 'rx_core'
shared_script '@rx_core/init.lua'That gives you a global RX table in both client and server files.
| Framework | Game | Detected by | Cash | Bank | Other currencies |
|---|---|---|---|---|---|
| ESX Legacy | FiveM | es_extended |
✅ | ✅ | black |
| QBCore | FiveM | qb-core |
✅ | ✅ | crypto |
| Qbox | FiveM | qbx_core |
✅ | ✅ | crypto |
| VORP | RedM | vorp_core |
✅ | ❌ | gold, rol |
| RSG | RedM | rsg-core |
✅ | ✅ | bloodmoney + regional banks |
| Standalone | both | (nothing found) | ❌ | ❌ | — |
Two details worth knowing before you write against them:
- VORP has no native bank. Banking lives in separate resources, so
RX.Framework.supports('bank')isfalserather than quietly aliased to cash. - Gold is VORP only. RSG has no gold; it has bloodmoney and per-region banks which each server configures itself, so rx_core reads them from the running RSG config instead of hardcoding a guess.
Never assume a currency exists. Ask:
if RX.Framework.supports('gold') then
RX.Money.add(source, 'gold', 5)
end
print(table.concat(RX.Money.types(), ', ')) -- what this server actually hasRX.OnReady(fn) -- run fn once the framework is resolved
RX.Require('>=1.1.0') -- assert an rx_core version, error loudly if unmetResources start in parallel. Always put startup logic inside RX.OnReady, or
you will ask which framework this is before anyone knows.
RX.Framework.name -- 'esx'|'qbcore'|'qbx'|'vorp'|'rsg'|'standalone_fivem'|'standalone_redm'
RX.Framework.platform -- 'fivem'|'redm'
RX.Framework.version -- rx_core version
RX.Framework.ready -- boolean
RX.Framework.supports(f) -- 'money'|'bank'|'gold'|'inventory'|'jobs'|any currencylocal player = RX.Player.get(source)
-- {
-- source, identifier, charId, name,
-- job = { name, label, grade, gradeLabel },
-- group
-- }identifier is the player. charId is the character — on RedM one player owns
several, so that is the key most things should hang off.
RX.Money.get(source, 'cash') --> number | false
RX.Money.add(source, 'bank', 500, 'reason') --> boolean
RX.Money.remove(source, 'cash', 250, 'reason') --> boolean, false if broke
RX.Money.types() --> { 'cash', 'bank', ... }Asking for a currency this framework does not have returns false and prints a
warning naming what is available. It never silently succeeds.
RX.Notify(source, 'Saved.', 'success', 4000) -- server
RX.Notify('Saved.', 'success', 4000) -- clientTypes: info, success, error, warning — translated to each framework's
native call. To route everything through your own notification resource, set
RXConfig.NotifyOverride in config.lua.
RX.Debug('anything', 'here')Silent unless RXConfig.Debug = true, and tagged with your resource name so a
server running ten RX scripts can tell which one is talking.
RX.Item.has / count / add / remove exist but are not implemented in 1.0.0.
Every framework fronts a different inventory resource and only some are
documented well enough to implement without guessing — and a guess that silently
loses a player's item is worse than no function at all. They return false and
say so. Landing in 1.1.
FiveM serialises everything that crosses an export boundary, and functions do
not survive that trip. If rx_core handed back a player object with methods on
it, every method would arrive as nil — which is exactly how VORP works
internally, since a VORP character is a table of methods.
So rx_core's exports only move plain data, and @rx_core/init.lua runs inside
your resource's Lua state to put a normal-looking API back on top. Same
approach ox_lib uses.
rx_core is open source and not escrowed. Your scripts can be. An escrowed
resource calling exports on a non-escrowed one is the same mechanism as any
escrowed script calling oxmysql.
Two things to remember when you escrow a resource that uses rx_core:
escrow_ignore { 'config.lua', 'locales/*.lua' } -- or buyers cannot edit themand note that NUI files (html/css/js) are not covered by escrow — only Lua, YFT, YDD and YDR are. Keep business logic out of your UI.
config.lua, all optional:
| Key | Default | Purpose |
|---|---|---|
Debug |
false |
Verbose output from rx_core and RX.Debug |
Banner |
true |
Print the RX banner on server start |
VersionCheck |
true |
Check for newer releases |
ForceFramework |
false |
Skip auto-detection, e.g. 'vorp' |
NotifyOverride |
false |
Function routing all notifications to your own resource |
rx # framework, platform, money types, registered RX scripts
lua tests/run.luaRuns the real source against a stubbed FiveM runtime. See tests/README.md.
Everything above, in one file:
-- fxmanifest.lua
dependency 'rx_core'
shared_script '@rx_core/init.lua'
server_script 'server.lua'-- server.lua
RX.OnReady(function()
print(('running on %s (%s)'):format(RX.Framework.name, RX.Framework.platform))
end)
RegisterCommand('buyhorse', function(source)
local player = RX.Player.get(source)
if not player then return end
if RX.Money.remove(source, 'cash', 250, 'bought a horse') then
RX.Notify(source, ('Sold to %s.'):format(player.name), 'success')
else
RX.Notify(source, 'Not enough cash.', 'error')
end
end, false)That runs unchanged on every framework in the table above.
MIT licensed · built by Ripexv · github.com/ripexv/rx_core