Callbacks in RSG Core provide a way to request data from the server and wait for a response, or vice versa. Theyโre essential for synchronous communication between client and server scripts.
Callbacks are perfect for situations where you need to wait for data before continuing execution, like checking if a player has an item, getting database information, or validating actions.
Use RSGCore.Functions.CreateCallback on the server to create a callback:
Copy
-- SERVER SIDE (server/main.lua or your resource's server file)RSGCore.Functions.CreateCallback('rsg-example:server:getData', function(source, cb, arg1, arg2) local src = source local Player = RSGCore.Functions.GetPlayer(src) if not Player then cb(nil) return end -- Do your server-side logic local data = { name = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname, job = Player.PlayerData.job.label, money = Player.PlayerData.money.cash, customArg1 = arg1, customArg2 = arg2 } -- Send data back to client cb(data)end)
Parameters:
source: The player who triggered the callback
cb: The callback function to send data back
...: Any additional arguments passed from the client
Use RSGCore.Functions.TriggerCallback on the client to call the callback:
Copy
-- CLIENT SIDE (client/main.lua or your resource's client file)RSGCore.Functions.TriggerCallback('rsg-example:server:getData', function(data) if data then print('Player Name: ' .. data.name) print('Job: ' .. data.job) print('Cash: $' .. data.money) else print('No data received') endend, 'argument1', 'argument2')
Use RSGCore.Functions.CreateClientCallback on the client to create a callback:
Copy
-- CLIENT SIDERSGCore.Functions.CreateClientCallback('rsg-example:client:getVehicleData', function(cb) local ped = PlayerPedId() local vehicle = GetVehiclePedIsIn(ped, false) if vehicle ~= 0 then local data = { model = GetEntityModel(vehicle), plate = GetVehicleNumberPlateText(vehicle), health = GetVehicleEngineHealth(vehicle), coords = GetEntityCoords(vehicle) } cb(data) else cb(nil) -- Player not in a vehicle endend)
Use RSGCore.Functions.TriggerClientCallback on the server to call the callback:
Copy
-- SERVER SIDERSGCore.Functions.TriggerClientCallback('rsg-example:client:getVehicleData', source, function(data) if data then print('Vehicle Model: ' .. data.model) print('Plate: ' .. data.plate) print('Engine Health: ' .. data.health) else print('Player is not in a vehicle') endend)
-- SERVERRSGCore.Functions.CreateCallback('rsg-shop:server:hasEnoughMoney', function(source, cb, amount) local src = source local Player = RSGCore.Functions.GetPlayer(src) if not Player then cb(false) return end if Player.Functions.GetMoney('cash') >= amount then cb(true) else cb(false) endend)
Client Usage:
Copy
-- CLIENTlocal price = 50RSGCore.Functions.TriggerCallback('rsg-shop:server:hasEnoughMoney', function(hasEnough) if hasEnough then print('You have enough money!') -- Proceed with purchase else print('You don\'t have enough money!') endend, price)
-- SERVERRSGCore.Functions.CreateCallback('rsg-horses:server:getOwnedHorses', function(source, cb) local src = source local Player = RSGCore.Functions.GetPlayer(src) if not Player then cb({}) return end local result = MySQL.query.await('SELECT * FROM player_horses WHERE citizenid = ?', { Player.PlayerData.citizenid }) if result and result[1] then cb(result) else cb({}) endend)
Client Usage:
Copy
-- CLIENTRSGCore.Functions.TriggerCallback('rsg-horses:server:getOwnedHorses', function(horses) if #horses > 0 then print('You own ' .. #horses .. ' horses:') for i = 1, #horses do print(' - ' .. horses[i].name) end else print('You don\'t own any horses') endend)
-- SERVERRSGCore.Functions.CreateCallback('rsg-shop:server:purchaseItem', function(source, cb, itemName, amount, price) local src = source local Player = RSGCore.Functions.GetPlayer(src) if not Player then cb({ success = false, message = 'Player not found' }) return end local totalPrice = price * amount -- Check if player has enough money if Player.Functions.GetMoney('cash') < totalPrice then cb({ success = false, message = 'Not enough money' }) return end -- Check if player has inventory space if not exports['rsg-inventory']:CanAddItem(src, itemName, amount) then cb({ success = false, message = 'Not enough inventory space' }) return end -- Remove money if not Player.Functions.RemoveMoney('cash', totalPrice, 'shop-purchase') then cb({ success = false, message = 'Failed to remove money' }) return end -- Add item if exports['rsg-inventory']:AddItem(src, itemName, amount, nil, nil, 'shop-purchase') then cb({ success = true, message = 'Purchase successful' }) else -- Refund money if item add fails Player.Functions.AddMoney('cash', totalPrice, 'shop-refund') cb({ success = false, message = 'Failed to add item' }) endend)
Client Usage:
Copy
-- CLIENTlocal itemName = 'bread'local amount = 3local pricePerUnit = 5RSGCore.Functions.TriggerCallback('rsg-shop:server:purchaseItem', function(result) if result.success then lib.notify({ title = 'Shop', description = result.message, type = 'success' }) else lib.notify({ title = 'Shop', description = result.message, type = 'error' }) endend, itemName, amount, pricePerUnit)
-- CLIENTRSGCore.Functions.CreateClientCallback('rsg-admin:client:getPlayerCoords', function(cb) local ped = PlayerPedId() local coords = GetEntityCoords(ped) local heading = GetEntityHeading(ped) cb({ x = coords.x, y = coords.y, z = coords.z, heading = heading })end)
Server Usage:
Copy
-- SERVERRegisterCommand('getcoords', function(source, args) local src = source if not RSGCore.Functions.HasPermission(src, 'admin') then return end RSGCore.Functions.TriggerClientCallback('rsg-admin:client:getPlayerCoords', src, function(coords) if coords then print(('Player coords: x=%.2f, y=%.2f, z=%.2f, heading=%.2f'):format( coords.x, coords.y, coords.z, coords.heading )) end end)end)
Donโt spam callbacks: Avoid calling callbacks in loops or every frame
Cache results: If data doesnโt change often, cache it instead of calling repeatedly
Use events for one-way communication: If you donโt need a response, use events instead
Copy
-- BAD: Callback every secondCreateThread(function() while true do Wait(1000) RSGCore.Functions.TriggerCallback('getData', function(data) -- Do something end) endend)-- GOOD: Event for one-way or cache for repeated uselocal cachedData = nilRegisterNetEvent('updateData', function(data) cachedData = dataend)-- Request once and cacheRSGCore.Functions.TriggerCallback('getData', function(data) cachedData = dataend)
-- SERVERRSGCore.Functions.CreateCallback('checkPermission', function(source, cb, permission) cb(RSGCore.Functions.HasPermission(source, permission))end)-- CLIENTRSGCore.Functions.TriggerCallback('checkPermission', function(hasPermission) if hasPermission then -- Show admin menu endend, 'admin')
-- SERVERRSGCore.Functions.CreateCallback('getPlayerStats', function(source, cb) local Player = RSGCore.Functions.GetPlayer(source) if not Player then cb(nil) return end local stats = MySQL.query.await('SELECT * FROM player_stats WHERE citizenid = ?', { Player.PlayerData.citizenid }) cb(stats[1] or {})end)
-- SERVERRSGCore.Functions.CreateCallback('canCraftItem', function(source, cb, itemName, amount) local Player = RSGCore.Functions.GetPlayer(source) if not Player then cb({ canCraft = false, reason = 'Invalid player' }) return end -- Check recipe exists local recipe = Config.Recipes[itemName] if not recipe then cb({ canCraft = false, reason = 'Unknown recipe' }) return end -- Check player has required items for _, ingredient in pairs(recipe.required) do if not Player.Functions.HasItem(ingredient.item, ingredient.amount * amount) then cb({ canCraft = false, reason = 'Missing ' .. ingredient.item }) return end end -- Check inventory space if not exports['rsg-inventory']:CanAddItem(source, itemName, amount) then cb({ canCraft = false, reason = 'Inventory full' }) return end cb({ canCraft = true })end)
For cleaner code, you can wrap callbacks in promises:
Copy
-- CLIENTfunction GetServerDataAsync(arg1, arg2) local p = promise.new() RSGCore.Functions.TriggerCallback('example:getData', function(data) p:resolve(data) end, arg1, arg2) return Citizen.Await(p)end-- UsageCreateThread(function() local data = GetServerDataAsync('test', 123) if data then print('Got data: ' .. json.encode(data)) endend)