← Check previous PART 3 HERE
[Tutorial] Scripting tutorial for beginners - Part 4
In the previous parts, we learned that a client script runs on the player’s PC and a server script runs on the server. In this part, they will finally start talking to each other.
We won’t study theory first. Instead, we’ll build a real feature that almost every RP server has — the /me command. A player types:
me tips his hat
…and everyone nearby sees:
* John Marston tips his hat
Step 1. A new resource: rp-me
You already know how to create a resource from Part 2. Create a folder rp-me inside resources, with three files:
-- rp-me/fxmanifest.lua
fx_version 'cerulean'
game 'common'
lua54 'yes'
client_scripts {
'client.lua',
}
server_scripts {
'server.lua',
}
Leave client.lua and server.lua empty for now, and add ensure rp-me to your server.cfg.
Step 2. First attempt — client only
Let’s try the naive way first. Write this in client.lua:
RegisterCommand('me', function(_, args)
local text = table.concat(args, ' ')
print('* ' .. GetPlayerName(PlayerId()) .. ' ' .. text)
end)
Run refresh, ensure rp-me, then in the F8 console type:
me tips his hat
You’ll see * Player 1 tips his hat in your F8 console. Works!
Now connect your second client from Part 3 (-cl2) and type the command on the first one. Check the second player’s F8 console…
Nothing. The second player sees nothing at all.
And this is the core problem of this whole part. Your client script runs only on your PC. The second player’s game is a different program on (potentially) a different computer on the other side of the world. Your print — and any variable, any function call — simply does not exist there.
But there is one machine that every player is connected to: the server. If we can deliver our text to the server, the server can deliver it to everyone else. That is exactly what events do.
Step 3. Sending the action to the server
Change client.lua — instead of printing, we now send:
RegisterCommand('me', function(_, args)
local text = table.concat(args, ' ')
TriggerServerEvent('rp-me:share', text)
end)
And receive it in server.lua:
RegisterNetEvent('rp-me:share', function(text)
print('The server received: ' .. text)
end)
Restart the resource, type me tips his hat in F8, then check the server console (Part 3):
The server received: tips his hat
Your text just traveled from your game to the server.
Step 4. Delivering the action to everyone
The server has the text. Now it must build the line * Player 1 tips his hat and deliver it to the players.
In server.lua:
RegisterNetEvent('rp-me:share', function(text)
local src = source
TriggerClientEvent('rp-me:display', -1, GetPlayerName(src), text)
end)
And the receiving side in client.lua:
RegisterCommand('me', function(_, args)
local text = table.concat(args, ' ')
TriggerServerEvent('rp-me:share', text)
end)
RegisterNetEvent('rp-me:display', function(name, text)
print(('* %s %s'):format(name, text))
end)
Restart, connect with both clients, type me tips his hat on the first one. Check both F8 consoles:
* Player 1 tips his hat
Both players see it. The full journey of your text: your client → the server → every client.
Step 5. Only nearby players
A real /me is roleplay — a player across the map shouldn’t see you tipping your hat. So instead of broadcasting with -1, the server should pick who to send to. In server.lua:
local DISPLAY_RADIUS = 20.0
RegisterNetEvent('rp-me:share', function(text)
local src = source
local name = GetPlayerName(src)
local myCoords = GetEntityCoords(GetPlayerPed(src))
for _, playerId in ipairs(GetPlayers()) do
local coords = GetEntityCoords(GetPlayerPed(playerId))
if #(coords - myCoords) <= DISPLAY_RADIUS then
TriggerClientEvent('rp-me:display', playerId, name, text)
end
end
end)
Test it: stand next to each other — both see the action. Ride away and repeat — the far player sees nothing.
The command is done, and it’s genuinely server-ready.
Conclusion
Everything you used in this part, in one place:
| Function | Direction | Notes |
|---|---|---|
TriggerServerEvent('name', ...) |
client → server | sender is identified by source on arrival |
TriggerClientEvent('name', playerId, ...) |
server → one client | -1 instead of an playerId = all players |
RegisterNetEvent('name', handler) |
receiving side | required on both client and server |
Tutorial made with
at
