What is Lua?
Lua is a lightweight, high-level scripting language designed for embedded use in applications. RedM (and FiveM) use Lua 5.4 as their primary scripting language for creating game modifications and server resources.
Lua is easy to learn, fast to execute, and powerful enough to create complex game systems. It’s the perfect language for RedM development!
Client vs Server Scripts
RedM resources can have client scripts, server scripts, or both. Understanding the difference is crucial:
Client Scripts
- Run on each player’s machine independently
- Handle visual effects, UI, local player actions
- Cannot directly access server data or other players
- File location:
client/ folder or defined as client_script in fxmanifest.lua
Server Scripts
- Run once on the server
- Handle game logic, database operations, player data
- Manage communication between players
- File location:
server/ folder or defined as server_script in fxmanifest.lua
Shared Scripts
- Run on both client and server
- Used for shared configurations, item definitions, etc.
- File location:
shared/ folder or defined as shared_script in fxmanifest.lua
Clients cannot communicate directly with each other. All communication must go through the server using events.
Example Flow:
Client A → Server Event → Server Processing → Client Event → Client B
Basic Lua Syntax
Variables
-- Local variables (recommended - faster and scoped)
local playerName = 'John Doe'
local playerAge = 25
local isAlive = true
-- Global variables (avoid unless necessary)
globalVariable = 'I am global'
Always use local variables unless you specifically need a global variable. Global variables can cause conflicts and performance issues.
Data Types
-- Strings
local message = 'Hello, RedM!'
local message2 = "Double quotes work too"
local longText = [[
Multi-line strings
use double brackets
]]
-- Numbers
local health = 100
local armor = 50.5
local money = 1000
-- Booleans
local isDead = false
local isAdmin = true
-- Tables (arrays and dictionaries)
local weapons = {'revolver', 'rifle', 'shotgun'}
local player = {
name = 'John',
age = 25,
job = 'sheriff'
}
-- Nil (represents absence of value)
local nothing = nil
-- This is a single-line comment
--[[
This is a
multi-line comment
]]
--- This is a documentation comment (used for annotations)
---@param source number - Player server ID
---@return string - Player name
Functions
Defining Functions
-- Basic function
local function greet(name)
print('Hello, ' .. name .. '!')
end
greet('Arthur') -- Output: Hello, Arthur!
-- Function with return value
local function add(a, b)
return a + b
end
local sum = add(5, 3) -- sum = 8
-- Function with multiple return values
local function getCoords()
return 100.0, 200.0, 30.0
end
local x, y, z = getCoords()
Anonymous Functions
-- Function assigned to variable
local multiply = function(a, b)
return a * b
end
-- Callback function
TriggerEvent('someEvent', function(data)
print('Received:', data)
end)
Control Structures
If Statements
local health = 50
if health <= 0 then
print('Player is dead')
elseif health < 30 then
print('Player is critically wounded')
elseif health < 70 then
print('Player is injured')
else
print('Player is healthy')
end
-- Logical operators
if health > 0 and armor > 0 then
print('Player has health and armor')
end
if isDead or isUnconscious then
print('Player cannot move')
end
if not isAdmin then
print('Player is not an admin')
end
Loops
-- For loop (numeric)
for i = 1, 10 do
print('Iteration:', i)
end
-- For loop with step
for i = 10, 1, -1 do
print('Countdown:', i)
end
-- For loop (iterate over table array)
local weapons = {'revolver', 'rifle', 'shotgun'}
for index, weapon in ipairs(weapons) do
print(index, weapon)
end
-- For loop (iterate over table dictionary)
local player = {
name = 'John',
age = 25,
job = 'sheriff'
}
for key, value in pairs(player) do
print(key, '=', value)
end
-- While loop
local count = 0
while count < 5 do
print('Count:', count)
count = count + 1
end
-- Repeat-until loop
local health = 100
repeat
health = health - 10
print('Health:', health)
until health <= 0
Tables (Arrays & Dictionaries)
Tables are Lua’s primary data structure - they can act as arrays, dictionaries, or both!
Arrays (Numeric Indices)
-- Creating an array
local weapons = {'revolver', 'rifle', 'shotgun'}
-- Accessing elements (Lua arrays start at 1, not 0!)
print(weapons[1]) -- Output: revolver
print(weapons[2]) -- Output: rifle
-- Adding elements
weapons[4] = 'bow'
table.insert(weapons, 'lasso') -- Adds to end
-- Removing elements
table.remove(weapons, 2) -- Removes 'rifle'
-- Get array length
local count = #weapons
print('Weapon count:', count)
Dictionaries (Key-Value Pairs)
-- Creating a dictionary
local player = {
name = 'John Doe',
age = 25,
job = 'sheriff',
money = 1000
}
-- Accessing values
print(player.name) -- Output: John Doe
print(player['age']) -- Output: 25
-- Adding/modifying values
player.job = 'deputy'
player['money'] = 1500
-- Nested tables
local character = {
info = {
firstname = 'John',
lastname = 'Doe'
},
money = {
cash = 500,
bank = 2000
}
}
print(character.info.firstname) -- Output: John
print(character.money.bank) -- Output: 2000
String Manipulation
-- Concatenation
local firstName = 'John'
local lastName = 'Doe'
local fullName = firstName .. ' ' .. lastName
-- String formatting (modern way)
local message = string.format('Hello %s, you have $%d', playerName, money)
-- String methods
local text = 'Hello World'
print(string.upper(text)) -- HELLO WORLD
print(string.lower(text)) -- hello world
print(string.len(text)) -- 11
print(string.sub(text, 1, 5)) -- Hello
print(string.find(text, 'World')) -- 7 11
-- String splitting (common pattern)
local function split(str, delimiter)
local result = {}
for match in (str .. delimiter):gmatch("(.-)" .. delimiter) do
table.insert(result, match)
end
return result
end
local parts = split('apple,banana,orange', ',')
-- parts = {'apple', 'banana', 'orange'}
Math Operations
-- Basic arithmetic
local a = 10
local b = 3
print(a + b) -- 13 (addition)
print(a - b) -- 7 (subtraction)
print(a * b) -- 30 (multiplication)
print(a / b) -- 3.333... (division)
print(a % b) -- 1 (modulus - remainder)
print(a ^ b) -- 1000 (exponentiation)
-- Math library
print(math.abs(-10)) -- 10
print(math.ceil(3.2)) -- 4
print(math.floor(3.8)) -- 3
print(math.max(5, 10)) -- 10
print(math.min(5, 10)) -- 5
print(math.random(1, 10)) -- Random number between 1-10
print(math.sqrt(16)) -- 4
-- Rounding (common pattern)
local function round(num, decimals)
local mult = 10 ^ (decimals or 0)
return math.floor(num * mult + 0.5) / mult
end
print(round(3.14159, 2)) -- 3.14
Creating Your First Resource
Step 1: Create Resource Folder
Create a folder in your resources directory:
resources/
[myresources]/
my-first-script/
Step 2: Create fxmanifest.lua
Every resource needs a manifest file:
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.'
description 'My First Script'
version '1.0.0'
client_scripts {
'client/client.lua'
}
server_scripts {
'server/server.lua'
}
shared_scripts {
'shared/config.lua'
}
Step 3: Create Your Scripts
shared/config.lua:
Config = {}
Config.WelcomeMessage = 'Welcome to my server!'
Config.MaxPlayers = 32
client/client.lua:
local RSGCore = exports['rsg-core']:GetCoreObject()
-- Code runs when resource starts
CreateThread(function()
print('Client script loaded!')
end)
-- Listen for player spawn
AddEventHandler('RSGCore:Client:OnPlayerLoaded', function()
print('Player loaded!')
lib.notify({
description = Config.WelcomeMessage,
type = 'info'
})
end)
server/server.lua:
local RSGCore = exports['rsg-core']:GetCoreObject()
-- Code runs when resource starts
print('Server script loaded!')
-- Register a command
RSGCore.Commands.Add('hello', 'Say hello', {}, false, function(source, args)
local Player = RSGCore.Functions.GetPlayer(source)
if not Player then return end
TriggerClientEvent('chat:addMessage', source, {
args = {'System', 'Hello ' .. Player.PlayerData.charinfo.firstname .. '!'}
})
end)
-- Register an event
RegisterNetEvent('my-first-script:server:greet', function(message)
local src = source
print('Player ' .. src .. ' said: ' .. message)
end)
Step 4: Start Your Resource
Add to server.cfg:
Or start it in-game:
Common Mistakes to Avoid
1. Forgetting local keyword-- Bad
playerName = 'John' -- Global variable!
-- Good
local playerName = 'John'
2. Array indexing starts at 1, not 0local items = {'apple', 'banana', 'orange'}
print(items[0]) -- nil (wrong!)
print(items[1]) -- 'apple' (correct!)
3. Using == for string/table comparison-- Numbers and booleans
if health == 100 then -- Correct
-- Strings are fine too
if name == 'John' then -- Correct
-- Tables need special handling
local t1 = {a = 1}
local t2 = {a = 1}
if t1 == t2 then -- Always false! Different references
4. Not checking for nil-- Bad
local Player = RSGCore.Functions.GetPlayer(source)
print(Player.PlayerData.name) -- Error if Player is nil!
-- Good
local Player = RSGCore.Functions.GetPlayer(source)
if not Player then return end
print(Player.PlayerData.name) -- Safe
Next Steps
Now that you understand Lua basics, explore these advanced topics:
Practice makes perfect! Start with simple scripts and gradually add complexity as you learn.