Skip to main content
TRACK B·FIVEM FUNDAMENTALS·Verified June 2026 · Lua 5.4 · ox_lib 3.x
Learning with an AI assistant?
Copies this lesson plus 2026 ground rules (no lua54 'yes', Cfx.re Portal, correct callback signatures) as a ready-to-paste mentor prompt.

Players, peds, and ids

This is the bug behind almost every "works for me, not for others" report. A script grabs a number on the client, sends it to the server, and the server uses it to mean the wrong player. The fix is knowing which id is local, which is stable across the wall, and which one the server already has for free. Build qu_whoami once and the whole family of ids stops blurring together.

You'll build
qu_whoami, a /whoami command that prints your client ped handle and server id, plus a server event that reports who sent it.
Time
~26 minutes
You need
A running FXServer you can join, plus TriggerServerEvent and TriggerClientEvent.
You'll learn
PlayerId vs PlayerPedId vs GetPlayerServerId, the server-side source, GetPlayers, and entity handle vs network id
BEFORE YOU START

Build it

Make the resource folder

The server has one folder for this lesson.

Inside your server's resources folder, create this folder:

code
resources/qu_whoami

Create the files

Every file named in the manifest exists.

Create this exact file layout:

code
resources/qu_whoami/
fxmanifest.lua
client.lua
server.lua

Write fxmanifest.lua

FiveM knows which files to load.

Open fxmanifest.lua and paste this:

code
fx_version 'cerulean'
game 'gta5'

client_script 'client.lua'
server_script 'server.lua'

Older tutorials add a lua54 'yes' line here. As of June 2025 that setting is deprecated and ignored: Lua 5.4 is now the only Lua runtime, so you leave it out.

Write the client

The command gathers every id and shows the local vs stable split.

Open client.lua and paste this:

code
RegisterCommand('whoami', function()
local playerId = PlayerId()                 -- local table index, only meaningful on this client
local ped = PlayerPedId()                    -- this client's current ped entity handle
local serverId = GetPlayerServerId(playerId) -- the stable id the server knows me by
local netId = NetworkGetNetworkIdFromEntity(ped) -- the ped's id everyone shares

print('[qu_whoami] PlayerId (local): ' .. playerId)
print('[qu_whoami] PlayerPedId (entity handle): ' .. ped)
print('[qu_whoami] GetPlayerServerId (stable): ' .. serverId)
print('[qu_whoami] ped network id (shared): ' .. netId)

-- Send NOTHING but the event. The server already knows who I am.
TriggerServerEvent('qu:whoami')
end, false)

RegisterNetEvent('qu:whoami:reply', function(serverSawSource, serverSawName)
print('[qu_whoami] server says my source is ' .. serverSawSource .. ' (' .. serverSawName .. ')')
end)

Write the server

The server reports who it thinks sent the event, using source, not a number from the client.

Open server.lua and paste this:

code
RegisterNetEvent('qu:whoami', function()
local src = source -- capture on line one, before anything yields

print('[qu_whoami] event received. source = ' .. src .. ' name = ' .. GetPlayerName(src))
print('[qu_whoami] players currently online: ' .. #GetPlayers())

-- Reply only to the player who asked, addressed by their stable server id.
TriggerClientEvent('qu:whoami:reply', src, src, GetPlayerName(src))
end)

Start and test it

The expected proof appears across both consoles.

Open server.cfg and add this line:

code
ensure qu_whoami

Save, then run in the server console:

code
restart qu_whoami

Join the server, open F8, and run:

code
/whoami

Your exact ped handle and network id numbers will differ from the sample, that is expected, they are runtime handles. Notice the split: your PlayerId is 0 and your GetPlayerServerId is 1, and the server independently agrees source = 1 without you sending it a single number. The next section explains why sending that 0 would have been the bug.

CLIENT
the player's game
network
SERVER
validate here
✓ trust boundary
The client triggers the event with no payload. The server reads source on its own side. The id that matters crosses the wall as identity, not as data the client could fake.

Keep reading the full lesson

Sign in to start, then unlock every step of this lesson and the full FiveM School with a membership.

Still ahead in this lesson
  • How it works
  • If something went wrong
  • What you can do now
  • Try it yourself

The remainder of Players, peds, and ids is available to FiveM School members.