Development Guides
Write optimized, performant code for RSG Framework.
Optimized code keeps your server smooth at high player counts. These are the practices the RSG community recommends when writing resources.
General practices
Localize as many functions and variables as possible — Lua reads them faster.
myVariable = false -- Don't use this
local myVariable = false -- Use this
function someFunction() -- Don't use this
print('Im a global function!')
end
local function someFunction() -- Use this
print('Im a local function!')
endInstead of table.insert, use tableName[#tableName+1] = data.
local function someFunction()
local t = {}
table.insert(t, {}) -- Don't use this
t[#t+1] = {} -- Use this
endInstead of if something ~= nil, use if something then. This checks for nil and false at the same time.
local bool = nil
if bool ~= nil then -- Don't use this
print('bool was not nil but could still be false!')
end
if bool then -- Use this
print('bool was neither nil nor false!')
endKeep functions and events universal — accept parameters so they can be reused in multiple scenarios:
local function someFunction(param1, param2, param3)
if param1 == 'something' then
print('I met condition number one!')
elseif param2 == 'somethingelse' then
print('I met condition number two!')
elseif param3 == 'somethingelsemore' then
print('I met condition number three!')
end
endUse "short returns" for failed conditions:
local function someFunction(param1, param2, param3)
if not param1 then return end
print('I met condition number one!')
if not param2 then return end
print('I met condition number two!')
if not param3 then return end
print('I met condition number three!')
endNative usage
Always replace GetPlayerPed(-1) with PlayerPedId().
local ped = GetPlayerPed(-1) -- Don't use this
local ped = PlayerPedId() -- Use thisAlways replace GetDistanceBetweenCoords with Lua math — #(vector3 - vector3).
local ped = PlayerPedId()
local pCoords = GetEntityCoords(ped)
local coords = vector3(-29.53, -1103.67, 26.42)
local dist = GetDistanceBetweenCoords(pCoords, coords, true) -- Don't use this
local dist = #(pCoords - coords) -- Use this
if dist < 5 then
print('Im within 5 units of the coords!')
endNative research sites & tools
- RedM Native Reference
- Natives | RedLookup
- femga/rdr3_discoveries
- Halen84/RDR3-Native-Flags-And-Enums
Loops
Control your while loops and when they run. Only run a loop while it is actually needed:
local listen = false
local function exampleLoop()
CreateThread(function()
while listen do
print('running while loop only when needed')
Wait(1)
end
end)
end
CreateThread(function()
LoopZone = CircleZone:Create(vector3(-851.63, 74.36, 51.86), 5.0, {
name = "ExampleLoop",
debugPoly = true,
})
LoopZone:onPlayerInOut(function(isPointInside)
if isPointInside then
listen = true
exampleLoop() -- Initiate loop
else
listen = false -- turns off when you're outside the zone
end
end)
end)Avoid while true do where possible. If you must use it, control the thread's wait
time dynamically — run slowly until you're in range, then speed up:
CreateThread(function()
while true do
Wait(1)
inRange = false
local pos = GetEntityCoords(PlayerPedId())
if #(pos - vector3(-829.11, 75.03, 52.73)) < 10.0 then
inRange = true
print('Im in range and the loop runs faster')
else
print('Im not in range so I run once every 2.5 seconds')
end
if not inRange then
Wait(2500)
end
end
end)Keep job-specific loops on the right players only — there's no reason for a non-cop to run a police loop on their machine:
local function exampleJobLoop()
local job = RSGCore.Functions.GetPlayerData().job.name
CreateThread(function()
while job == 'law' do
print('im a lawman!')
Wait(1)
end
end)
endSecurity
- A surplus of security is not a bad thing — don't be afraid to add multiple
ifchecks or pass random variables through your events. - Never perform money or item transactions on the client side of a resource. Always do it on the server.
Event handlers
When setting variables inside your resource, event handlers save you from constantly running checks:
-- These are client-side examples
local isLoggedIn = false
local PlayerData = {}
AddStateBagChangeHandler('isLoggedIn', nil, function(_, _, value) -- FiveM native method
if value then
isLoggedIn = true
PlayerData = RSGCore.Functions.GetPlayerData()
else
isLoggedIn = false
PlayerData = {}
end
end)
AddEventHandler('RSGCore:Client:OnPlayerLoaded', function()
isLoggedIn = true
PlayerData = RSGCore.Functions.GetPlayerData()
end)
RegisterNetEvent('RSGCore:Client:OnPlayerUnload', function()
isLoggedIn = false
PlayerData = {}
end)
RegisterNetEvent('RSGCore:Player:SetPlayerData', function(val)
PlayerData = val
end)
