RSG Framework
Getting Started

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!')
end

Instead 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
end

Instead 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!')
end

Keep 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
end

Use "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!')
end

Native usage

Always replace GetPlayerPed(-1) with PlayerPedId().

local ped = GetPlayerPed(-1) -- Don't use this
local ped = PlayerPedId()    -- Use this

Always 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!')
end

Native research sites & tools

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)
end

Security

  • A surplus of security is not a bad thing — don't be afraid to add multiple if checks 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)

Next steps

On this page