This page contains ready-to-use Lua snippets for RedM.
All examples use oxmysql directly (e.g., MySQL.query.await
) and common framework patterns.
In all of our scripts we ship a frameworkBridge.lua.
Use it to access player/job/money data on VORP or RSG, and see how to plug in a custom framework.
Reference: Framework bridge & custom setup.
1) Database: oxmysql (await)
We recommend using the await versions for predictable, synchronous-like flow on the server.
SELECT
-- server.lua
local charId = GetPlayerIdentifier(source) -- or Bridge.getCharacterId(source) if you use the bridge
local rows = MySQL.query.await('SELECT balance FROM bank_accounts WHERE charid = ?', { charId })
if rows and rows[1] then
local balance = rows[1].balance or 0
print(('Balance for %s: %s'):format(charId, balance))
else
print(('No bank account found for %s'):format(charId))
end
UPDATE
-- server.lua
local affected = MySQL.update.await('UPDATE bank_accounts SET balance = balance + ? WHERE charid = ?', { 250, charId })
print(('Updated %d row(s)'):format(affected))
INSERT
-- server.lua
local insertedId = MySQL.insert.await('INSERT INTO items (name, owner) VALUES (?, ?)', { 'goldbar', charId })
print(('Inserted item ID: %s'):format(insertedId))
Always use ?
placeholders. Never build SQL via string concatenation.
2) Conditions & Guards
Require supported framework (when not using custom)
if not Bridge or (Bridge.framework ~= 'vorp' and Bridge.framework ~= 'rsg' and Bridge.framework ~= 'custom') then
print('Unsupported or missing frameworkBridge. Aborting.')
return
end
Ensure player has an item
-- Prefer a synchronous check on the server
local function hasItem(src, item, amount)
amount = amount or 1
if Bridge and Bridge.framework == 'vorp' then
local p = promise.new()
exports.vorp_inventory:getItemCount(src, function(count) p:resolve(count or 0) end, item)
local count = Citizen.Await(p)
return count >= amount
elseif Bridge and Bridge.framework == 'rsg' then
local player = Bridge.RSGCore.Functions.GetPlayer(src)
local data = player and player.Functions.GetItemByName(item)
local count = (data and (data.count or data.amount)) or 0
return count >= amount
end
return false
end
if not hasItem(source, 'lockpick', 1) then
-- your notify here
return
end
Check money
local function getCash(src)
if Bridge and Bridge.framework == 'vorp' then
return Bridge.getCharacterMoney(src) or 0
elseif Bridge and Bridge.framework == 'rsg' then
local ply = Bridge.RSGCore.Functions.GetPlayer(src)
return (ply and ply.PlayerData and ply.PlayerData.money and ply.PlayerData.money.cash) or 0
end
return 0
end
if getCash(source) < 100 then
-- your notify here
return
end
3) Purchase flow (DB + items + money)
Minimal example showing a safe buy action on the server:
-- server.lua
local PRICE = 50
RegisterCommand('buyapple', function(src)
-- 1) Check funds
if getCash(src) < PRICE then
TriggerClientEvent('ox_lib:notify', src, { description = 'Not enough cash', type = 'error', duration = 4000 })
return
end
-- 2) Charge player (framework-specific)
if Bridge and Bridge.framework == 'vorp' then
Bridge.removeMoney(src, PRICE)
elseif Bridge and Bridge.framework == 'rsg' then
local ply = Bridge.RSGCore.Functions.GetPlayer(src)
if ply then ply.Functions.RemoveMoney('cash', PRICE) end
end
-- 3) Give item (best-effort via framework)
if Bridge then
Bridge.giveItem(src, 'apple', 1)
end
-- 4) Persist in DB (optional audit)
local charId = (Bridge and Bridge.getCharacterId(src)) or GetPlayerIdentifier(src)
MySQL.insert.await('INSERT INTO purchases (charid, item, price) VALUES (?, ?, ?)', { charId, 'apple', PRICE })
-- 5) Notify
TriggerClientEvent('ox_lib:notify', src, { description = 'You bought an apple', type = 'success', duration = 4000 })
end)
Keep all money and inventory mutations on the server. Validate inputs; never trust the client.
4) Usable items (server-only registration)
-- server.lua
if Bridge and Bridge.registerUsableItem then
Bridge.registerUsableItem('bandage', function(src, itemName)
-- effect + feedback
TriggerClientEvent('ox_lib:notify', src, { description = 'You used a bandage', type = 'success', duration = 3000 })
-- add your heal logic here
end)
end
5) Notifications (server & client)
Use a single code path where possible. For RSG we fallback to ox_lib notifications; VORP uses core notify.
-- server.lua
local function notify(src, text, kind)
kind = kind or 'inform' -- 'success' | 'error' | 'inform'
TriggerClientEvent('ox_lib:notify', src, { description = text, type = kind, duration = 4000 })
end
Client-side usage example:
-- client.lua
RegisterCommand('hello', function()
lib.notify({ description = 'Hello from client', type = 'inform', duration = 3000 })
end)
6) Prompts (client)
-- client.lua
local GROUP = 'interaction_demo'
CreateThread(function()
CreatePromptButton(GROUP, 'Open Menu', 0x760A9C6F) -- INPUT_CONTEXT_Y
while true do
Wait(0)
DisplayPrompt(GROUP, 'Demo Actions')
if IsPromptCompleted(GROUP, 0x760A9C6F) then
print('[Prompt] Open Menu')
-- open your UI
end
end
end)
-- later, to remove:
-- DeletePromptButton(GROUP, 0x760A9C6F)
7) Custom framework hook
If you use neither VORP nor RSG, plug your functions into the bridge once and keep scripts portable:
-- frameworkBridge.lua (custom)
Bridge.framework = 'custom'
function Bridge.getPlayerData(src)
-- return a table matching { identifier, charid, name, money, job }
return {
identifier = 'steam:110000112345678',
charid = 'CHAR_123',
name = 'John Doe',
money = 500,
job = 'trader'
}
end
function Bridge.giveItem(src, item, amount)
-- call into your custom inventory here
end
function Bridge.removeMoney(src, amount)
-- call into your custom economy here
end
Keep return shapes stable across frameworks. Your game logic should not need to change when switching frameworks.