RedM runs on the Cfx.re platform (the same FXServer program as FiveM). This page is a compact guide to the pieces you touch every day: the resource manifest, server.cfg, events, exports and state bags. The full official manual is on docs.fivem.net.
Resource layout#
resources/
[jrs]/
my_resource/
fxmanifest.lua
config.lua
client.lua
server.lua
html/ (optional NUI)A folder name in square brackets is only a category: it is ignored by the server, so ensure my_resource works wherever the folder sits.
fxmanifest.lua#
fx_version 'cerulean'
game 'rdr3'
rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.'
lua54 'yes'
author 'JRS TEAM'
description 'What this resource does'
version '1.0.0'
shared_scripts { 'config.lua' }
client_scripts { 'client.lua' }
server_scripts { '@oxmysql/lib/MySQL.lua', 'server.lua' }
ui_page 'html/index.html'
files { 'html/index.html', 'html/app.js', 'html/style.css' }
dependencies { 'jrs_core', 'oxmysql' }| Field | Purpose |
|---|---|
fx_version |
Manifest version. cerulean is the current one. |
game |
rdr3 for RedM. Required. |
rdr3_warning |
The acknowledgement line RedM asks for. Keep it exactly. |
lua54 'yes' |
Use the Lua 5.4 runtime (needed for <const>, integers, bit operators). |
client_scripts / server_scripts / shared_scripts |
Files loaded on each side. A leading @resource/path loads a file from another resource. |
files |
Files the client may download (UI, data). |
ui_page |
Entry page of the NUI. |
dependencies |
Resources that must be running first. |
provide |
Lets your resource stand in for another resource name. |
exports / server_exports |
Names of functions you export (see below). |
escrow_ignore |
Files that stay readable in an escrowed resource. |
Save the manifest as UTF-8 without BOM. A BOM makes the resource fail to parse.
server.cfg#
endpoint_add_tcp "0.0.0.0:30120"
endpoint_add_udp "0.0.0.0:30120"
sv_hostname "My RedM server"
sv_maxclients 48
set onesync on
sv_licenseKey "xxxxxxxx" # your key from portal.cfx.re (keep it secret)
set mysql_connection_string "mysql://user:password@127.0.0.1/database?charset=utf8mb4"
ensure oxmysql
ensure jrs_core
ensure jrs_inventory
add_ace group.admin command allow
add_principal identifier.steam:110000xxxxxxxx group.admin| Directive | What it does |
|---|---|
ensure name |
Start the resource, or restart it if it is running. Use it in the config. |
start / stop / restart name |
Control a single resource from the console. |
refresh |
Rescan the resources folder after you add or rename one. |
set name value |
Server only convar. setr also sends it to clients. sets shows it in the server list. |
exec file.cfg |
Run another config file. |
add_ace / add_principal |
Permissions for console commands. |
Load order matters: a resource that depends on another must come after it. Do not put comments on the same line as ensure.
Threads, events and callbacks#
-- loop that sleeps when nothing happens
CreateThread(function()
while true do
local sleep = 1000
if nearSomething then sleep = 0 end
Wait(sleep)
end
end)
-- server -> client
RegisterNetEvent('myres:notify', function(text) print(text) end)
TriggerClientEvent('myres:notify', source, 'hello')
-- client -> server (source is set by the server automatically)
RegisterNetEvent('myres:buy', function(item)
local src = source
-- never trust arguments: validate item, distance and price on the server
end)
TriggerServerEvent('myres:buy', 'bread')Useful built in events: onResourceStart, onResourceStop, onServerResourceStart, playerConnecting, playerJoining, playerDropped, onClientResourceStart.
Everything a client sends can be forged. Check the player, the distance, the cooldown and the price on the server and ignore client supplied amounts.
Exports#
-- resource A (fxmanifest: nothing special is needed on the server side)
exports('GetPrice', function(item) return 5 end)
-- resource B
local price = exports.resource_a:GetPrice('bread')State bags#
State bags are small key/value stores that sync between server and clients.
-- server
Player(source).state:set('isCuffed', true, true) -- third argument: replicate to clients
GlobalState.serverMode = 'day'
-- client
local cuffed = LocalPlayer.state.isCuffedConvars and storage#
local name = GetConvar('sv_projectName', 'my server')
local max = GetConvarInt('sv_maxclients', 32)
SetResourceKvp('last_seen', tostring(GetGameTimer()))
local v = GetResourceKvpString('last_seen')Promises and async#
local p = promise.new()
SetTimeout(1000, function() p:resolve('done') end)
local result = Citizen.Await(p)txAdmin#
txAdmin is the web panel bundled with the server: start/stop, console, player list, bans, schedules and the recipe deployer that installs JRS Core. Open it at http://localhost:40120 the first time, then keep it behind a password and your own firewall rules. The official setup guide: Setting up a server with txAdmin.
Credits#
This page is an original summary. For the complete, always current manual see docs.fivem.net.