Server-Side vs Client-Side Scripting in FiveM
The single most important concept in FiveM scripting is the split between server-side and client-side code. Every FiveM resource can have both, and getting confused about which is which causes the majority of beginner bugs and nearly every security hole. The short version: client-side code runs on each player's own computer and handles what they see and feel blips, menus, animations, NUI. Server-side code runs once on your server, is authoritative, and handles anything that must be trusted money, inventory, jobs, and saving data.
This guide explains what belongs where, how the two sides talk through events, and the security rule that underpins all roleplay scripting: never trust the client. Understanding this is what separates someone who copies scripts from someone who can build and secure their own. PlayDeck teaches this with AI by having you build features that span both sides correctly the AI writes the client UI and the server validation, and you learn to spot when a script wrongly trusts the client.
What runs on the client
Client-side scripts execute on each connected player's machine. They have direct access to the local game world: the player's ped, camera, controls, and screen. Anything visual or input-related is client-side drawing markers and text, spawning particle effects, playing animations, showing an NUI menu, detecting which key a player pressed, and reading the player's coordinates.
The catch is that the client is fully under the player's control. A cheater can modify client memory, fake events, and lie about anything their client reports. So while the client is where the experience lives, it is also untrusted: you can use it to request actions and render results, but you can never let it be the final authority on anything that matters.
- Drawing blips, markers, text, and 3D UI
- Playing animations and particle effects
- Showing NUI menus and detecting keypresses
- Reading local player position and game state
What runs on the server
Server-side scripts run once, on the machine hosting your server, and they are the source of truth. Anything that must be consistent, persistent, or cheat-proof belongs here: adding and removing money, granting items, setting jobs, writing to and reading from the database, and validating that a player is actually allowed and able to do what they requested.
The server identifies each player by a numeric source (their server ID for the session). Framework functions like ESX.GetPlayerFromId(source), QBCore.Functions.GetPlayer(source), and exports.qbx_core:GetPlayer(source) all run server-side and hand you the authoritative player object. If a piece of data needs to survive a reconnect or be safe from cheaters, it must be created and stored on the server.
How the two sides communicate: events
Client and server cannot call each other's functions directly they pass messages through events. From the client, TriggerServerEvent('myResource:buyItem', itemName) sends an event up to the server. From the server, TriggerClientEvent('myResource:updateHud', source, newBalance) sends an event down to a specific client. On the receiving side, you register a handler with RegisterNetEvent and AddEventHandler (or the combined RegisterNetEvent form) listening for that event name.
There is a strict directionality: server events can only be listened for on the server (but triggered from either side), and client events can only be listened for on a client (but triggered from either side). For large payloads many kilobytes of data prefer TriggerLatentServerEvent, which streams the data without blocking the network channel the way a normal TriggerServerEvent would. For the small, frequent events most scripts send, the regular trigger functions are correct.
- TriggerServerEvent: client asks the server to do something
- TriggerClientEvent: server tells a specific client to do something
- RegisterNetEvent + handler: receive a networked event
- TriggerLatentServerEvent: for large data transfers only
The golden rule: never trust the client
Because the client is under the player's control, every server net event handler must assume the incoming data is hostile. A naive shop might have the client tell the server 'add me 5000 dollars' after a purchase but a cheater can simply trigger that event directly and print money. The correct design sends only the intent ('I want to buy item X') and lets the server look up the price, verify the player can afford it, deduct the money, and grant the item, all server-side.
Concretely: inside every server event, fetch the player with the framework's GetPlayer(source), confirm they exist, confirm they meet the requirements (enough money, correct job, in range), and only then mutate state. Do the money math on the server using server-known prices, never amounts the client sent. This one discipline prevents the vast majority of exploits in roleplay servers, and it's the first thing to check when reviewing any script your own or one you bought.
Frequently asked questions
Can client-side code change a player's money safely?
No. Money must be changed server-side. Client code can request a transaction, but the server has to verify and perform it. Any script that adds money directly from the client is exploitable and should be fixed.
What is the source in a server event?
source is the server ID of the player who triggered the event. You use it with your framework's GetPlayer function to fetch the authoritative player object on the server. It is reliable because the server assigns it, unlike data the client sends in the event body.
When should I use TriggerLatentServerEvent instead of TriggerServerEvent?
Use the latent version only for large transfers (multiple kilobytes and up), because it streams data without clogging the network channel. For the small, frequent events most scripts send, regular TriggerServerEvent is the right choice.
How do I know if a script I bought is secure?
Check whether the server validates everything in its net events or whether it trusts client-sent values like prices and amounts. If the client tells the server how much money to add, it's insecure. We teach this review habit so you can audit scripts before deploying them.