games / fivem
Client vs serveur : le modèle réseau FiveM
Explication
Ce que vous allez apprendre
- Comprendre pourquoi FiveM repose sur une architecture client/serveur stricte
- Identifier ce que le serveur ne peut jamais savoir sans qu'un client le lui envoie
- Comprendre pourquoi un client ne doit jamais être considéré comme fiable
- Distinguer ce qu'un script peut faire côté client de ce qu'il peut faire côté serveur
- Anticiper pourquoi les events réseau sont l'unique pont entre les deux mondes
Dans quel contexte ?
Un développeur débutant écrit une commande /heal censée soigner le joueur qui l'exécute, et place tout le code côté client pour aller plus vite. Le résultat semble fonctionner en test solo, mais dès qu'un deuxième joueur tente d'exploiter cette commande pour se rendre invincible en modifiant son client, rien côté serveur ne peut l'en empêcher : la logique n'a jamais été validée par l'autorité centrale. Cette leçon explique précisément pourquoi ce genre d'erreur est si fréquente chez les débutants, et comment l'éviter dès la conception.
| Question | Réponse |
|---|---|
| Le serveur connaît-il la position d'un joueur sans qu'un client la lui envoie ? | Non |
| Un joueur peut-il modifier son client localement ? | Oui, toujours |
| Qui doit valider une action qui affecte le jeu pour tous ? | Le serveur, exclusivement |
Piège fréquent
Croire qu'une vérification faite côté client (par exemple "le joueur a-t-il assez d'argent ?") protège réellement le serveur est une erreur classique et dangereuse : un client modifié peut simplement ignorer cette vérification et envoyer directement l'event qui déclenche l'action.
Le concept le plus important de tout le cours
Si une seule leçon de ce cours devait être parfaitement comprise avant toutes les autres, ce serait celle-ci. FiveM fonctionne sur un modèle client/serveur strict : chaque joueur fait tourner une copie du jeu sur sa propre machine (le "client"), pendant qu'un unique programme centralisé (le "serveur") coordonne tout le monde. Ces deux mondes ne voient pas du tout la même chose, et confondre leurs rôles est la source de la quasi-totalité des bugs — et des failles de sécurité — chez les débutants.
Ce que le serveur ne voit jamais
Un point contre-intuitif pour un débutant : le serveur n'a AUCUNE notion du monde 3D du jeu. Il ne sait pas où se trouve un joueur, à quoi ressemble le décor, ni ce qu'un joueur regarde à l'écran. Il ne connaît que ce que les clients choisissent explicitement de lui envoyer. Cela signifie concrètement qu'on ne peut jamais, côté serveur, appeler une fonction comme "récupérer la position du joueur" directement : il faut qu'un client la lui transmette lui-même.
Pourquoi le client n'est jamais fiable
À l'inverse, chaque client tourne sur une machine que le joueur contrôle totalement — y compris avec des outils de modification qui peuvent altérer le comportement du jeu localement. Un serveur qui ferait aveuglément confiance à ce qu'un client lui envoie ("j'ai payé", "j'ai 50 points de vie restants") s'expose à de la triche pure et simple. Le principe à retenir : le serveur doit être autoritaire, c'est-à-dire que toute décision qui affecte réellement le jeu de façon durable doit être décidée et validée par lui, jamais simplement acceptée telle quelle depuis un client.
Le seul pont entre les deux mondes
Puisque client et serveur sont deux programmes complètement séparés, ils ne peuvent communiquer que par un mécanisme dédié : les events réseau, présentés en détail dans la prochaine leçon. Retenir cette architecture avant d'y entrer évite de nombreuses confusions sur "pourquoi telle fonction ne marche pas ici".
Commandes & code
Client vs serveur : le modèle réseau FiveM
FiveM suit une architecture client/serveur stricte : le serveur ne "voit" pas le monde 3D, le client ne doit jamais être la source de vérité.
-- CLIENT (client/main.lua) : tourne sur la machine de CHAQUE joueur individuellement
local playerPed = PlayerPedId() -- entité locale, propre à ce client
local coords = GetEntityCoords(playerPed) -- position réelle dans le monde 3D
print(("Position: %.2f, %.2f, %.2f"):format(coords.x, coords.y, coords.z))
-- Le client peut lire/afficher tout ce qui est visuel : peds, véhicules, caméra, UI...-- SERVEUR (server/main.lua) : un seul processus, aucune notion de "monde 3D"
local players = GetPlayers() -- liste des ID serveur (source) connectés
for _, playerId in ipairs(players) do
print("Joueur connecté, ID serveur: " .. playerId)
end
-- Le serveur n'a PAS accès aux natives de rendu (pas de PlayerPedId(), pas de coords directement)
-- Il ne connaît que ce que les clients lui envoient via les events-- Le seul lien entre les deux mondes : les events réseau (voir leçon suivante)
RegisterNetEvent('technologik:reportPosition', function(x, y, z)
local src = source -- ID serveur du joueur qui a déclenché l'event
print(('Joueur %d se trouve en %.1f, %.1f, %.1f'):format(src, x, y, z))
end)| Côté | Sait faire | Ne sait PAS faire |
|---|---|---|
| Client | Lire coords, dessiner UI, jouer animations | Requêtes SQL, faire confiance sans risque |
| Serveur | Requêtes SQL, arbitrer, valider, persister | Lire une position sans qu'un client l'envoie |
Résumé
- Le serveur est autoritaire : toute décision qui affecte le jeu pour tous doit être validée côté serveur.
- Le client est untrusted par nature : un joueur peut modifier son client (voir leçon Sécurité).
- La communication entre les deux passe exclusivement par le système d'events.
Exercices pratiques
Mission : la commande /heal qui triche
Objectif : Identifier pourquoi une commande de soin entièrement côté client est exploitable, et concevoir une version où le serveur reste seul décisionnaire.
Contexte
Un développeur a écrit une commande /heal qui tourne entièrement côté client : elle lit PlayerPedId(), calcule le nouveau niveau de vie, et appelle directement SetEntityHealth(ped, 200). En solo, ça fonctionne parfaitement. Une fois le serveur ouvert à d'autres joueurs, certains utilisent un client modifié pour se soigner en boucle et devenir quasiment invincibles, sans que le serveur ne s'en aperçoive.