Administrer un serveur FiveM, c'est d'abord assembler des scripts existants : framework, garage, inventaire, métiers. Mais dès qu'une mécanique personnalisée manque, ou qu'un script tiers pose plus de problèmes qu'il n'en résout, savoir écrire son propre code Lua devient un avantage concret. Ce guide couvre la création d'un premier script fonctionnel, de la structure des fichiers au déploiement sur le serveur, en s'appuyant sur la documentation officielle Cfx.re.
Lua est le langage de script le plus utilisé dans l'écosystème FiveM. FiveM embarque CfxLua, une version modifiée de Lua 5.4, avec des ajouts spécifiques : vecteurs natifs (vector3, vector4), hachage de chaînes à la compilation, et une boucle d'exécution intégrée via Citizen.CreateThread. C'est le même langage que celui utilisé par ESX Legacy, QBCore et la grande majorité des ressources communautaires. FiveM supporte aussi JavaScript et C#, mais Lua reste le choix par défaut pour la plupart des développeurs de ressources.
Prérequis
- Un serveur FiveM fonctionnel avec un accès au système de fichiers (VPS avec accès root ou panel type txAdmin/Pterodactyl). Si votre serveur tourne chez un hébergeur FiveM, vérifiez que vous disposez d'un accès FTP ou d'un gestionnaire de fichiers dans le panel ;
- Un éditeur de code : Visual Studio Code avec l'extension cfxlua-vscode (IntelliSense pour les fonctions natives FiveM et les types Lua) ;
- Une connaissance de base de Lua (variables, fonctions, tables, boucles). La syntaxe Lua est plus légère que celle de Python ou de JavaScript : pas de point-virgule obligatoire, pas de typage explicite ;
- Le fichier de configuration
server.cfgdu serveur, accessible en écriture.
Si votre serveur n'est pas encore en place, notre guide de mise en place d'un serveur FiveM couvre l'installation complète avant de commencer à scripter.
Préparer l'environnement de développement
Avant d'écrire la première ligne de code, configurez votre poste de travail pour travailler efficacement. L'éditeur de code et les extensions adaptées évitent la majorité des erreurs de syntaxe et accélèrent l'écriture des scripts.
Visual Studio Code et extensions
Installez Visual Studio Code (disponible sous Windows, macOS et Linux), puis ajoutez ces extensions depuis le marketplace :
- cfxlua-vscode (ou FiveM Lua IntelliSense) : autocomplétion des fonctions natives FiveM, des événements et des types CfxLua. L'extension charge la liste complète des natives depuis la documentation Cfx.re et propose les paramètres attendus ;
- Lua (sumneko) : diagnostic de syntaxe, navigation dans le code, détection des variables inutilisées. Ce plugin analyse le code en temps réel et signale les erreurs avant l'exécution ;
- FiveM Natives (optionnel) : documentation des natives directement dans l'éditeur au survol de la fonction.
Connexion au serveur distant
Si votre serveur FiveM tourne sur un VPS distant, vous avez plusieurs méthodes pour transférer les fichiers de vos scripts :
- FTP/SFTP : connectez-vous avec un client comme FileZilla en utilisant l'adresse IP du serveur, le port et les identifiants d'authentification. SFTP chiffre la connexion via le protocole SSH ;
- Terminal SSH : en ligne de commande, connectez-vous au serveur avec
ssh utilisateur@adresse-ipet éditez les fichiers directement dans le shell avecnanoouvim; - Panel txAdmin ou Pterodactyl : le gestionnaire de fichiers intégré au panel permet de créer des dossiers, d'éditer des fichiers et de téléverser des scripts sans quitter le navigateur.
Pour un flux de travail fluide, développez en local dans Visual Studio Code, testez la syntaxe, puis transférez le dossier de la ressource sur le serveur. Sauvegardez vos scripts dans un dépôt Git pour garder un historique des modifications et pouvoir revenir en arrière après une mise à jour qui casse quelque chose.
Structure d'une ressource FiveM
Dans FiveM, chaque script est une ressource : un dossier dans le répertoire resources/ du serveur, contenant au minimum un fichier manifeste et un ou plusieurs fichiers Lua. La structure minimale d'une ressource :
resources/
[mon-script]/
fxmanifest.lua -- manifeste : declare les fichiers et les metadonnees
client.lua -- code execute cote client (chaque joueur)
server.lua -- code execute cote serveur (une seule instance)
Le fichier fxmanifest.lua est obligatoire. C'est lui que FiveM lit au démarrage pour savoir quels fichiers charger et dans quel contexte (client ou serveur). Sans ce fichier, le dossier est ignoré. Les ressources sont organisées en sous-dossiers dans resources/ : par convention, [local] pour vos scripts personnalisés, [esx] ou [qb] pour les scripts du framework.
Le fichier fxmanifest.lua
Le manifeste déclare la version de l'API, le jeu cible et les fichiers à charger. Voici un manifeste minimal pour une ressource avec un script côté client et un script côté serveur :
fx_version 'cerulean'
game 'gta5'
author 'VotreNom'
description 'Mon premier script FiveM'
version '1.0.0'
client_script 'client.lua'
server_script 'server.lua'
Les champs obligatoires sont fx_version et game. La valeur 'cerulean' est la version d'API la plus récente et donne accès à toutes les fonctionnalités actuelles. Pour un serveur RedM, remplacez 'gta5' par 'rdr3'. Les champs author, description et version sont des métadonnées utiles pour l'organisation, mais pas techniquement bloquants.
Pour les ressources plus complexes, le manifeste accepte des directives supplémentaires :
| Directive | Rôle | Exemple |
|---|---|---|
shared_script | Fichier Lua chargé côté client et côté serveur | shared_script 'config.lua' |
client_scripts | Liste de fichiers client (accolades pour plusieurs) | client_scripts { 'client/*.lua' } |
server_scripts | Liste de fichiers serveur | server_scripts { 'server/*.lua' } |
files | Fichiers embarqués accessibles par le client (JSON, HTML) | files { 'ui/index.html' } |
ui_page | Page NUI (interface HTML intégrée au jeu) | ui_page 'ui/index.html' |
dependency | Ressource requise, chargée avant celle-ci | dependency 'oxmysql' |
Écrire un script côté client
Le code côté client s'exécute sur la machine de chaque joueur connecté. C'est là qu'on interagit avec le monde du jeu : position du personnage, véhicules, interface, effets visuels. Chaque joueur a sa propre instance du script client, sans variables partagées entre les connexions.
Un premier exemple concret : une commande /position qui affiche les coordonnées du joueur dans le chat.
-- client.lua
RegisterCommand('position', function()
local ped = PlayerPedId()
local coords = GetEntityCoords(ped)
TriggerEvent('chat:addMessage', {
args = {
string.format('Position : %.2f, %.2f, %.2f', coords.x, coords.y, coords.z)
}
})
end, false)
Trois éléments clés dans cet exemple :
RegisterCommandenregistre une commande utilisable dans le chat avec le préfixe/. Le troisième argument (false) indique que la commande n'est pas restreinte aux administrateurs ;PlayerPedId()etGetEntityCoords()sont des fonctions natives de FiveM, c'est-à-dire des fonctions prédéfinies qui interagissent directement avec le moteur du jeu GTA V ;TriggerEventdéclenche un événement local (icichat:addMessagepour afficher un message dans le chat du joueur).
Écrire un script côté serveur
Le code côté serveur s'exécute sur le serveur FiveM, en une seule instance. C'est le bon endroit pour la logique partagée entre joueurs : vérifications de droits, requêtes vers la base de données, distribution de récompenses, logs d'administration. Le serveur centralise la logique métier pour garantir l'intégrité des données : un joueur ne peut pas contourner une vérification qui s'exécute côté serveur.
Voici un script serveur qui enregistre une commande /annonce réservée aux administrateurs, qui envoie un message à tous les joueurs connectés :
-- server.lua
RegisterCommand('annonce', function(source, args)
if not IsPlayerAceAllowed(source, 'command.annonce') then
return
end
local message = table.concat(args, ' ')
if message == '' then
return
end
TriggerClientEvent('chat:addMessage', -1, {
args = { '[Annonce] ' .. message }
})
print('[Annonce] ' .. GetPlayerName(source) .. ' : ' .. message)
end, true)
Les différences avec le script client :
- Le premier argument de la fonction (
source) est l'identifiant réseau du joueur qui a exécuté la commande ; IsPlayerAceAllowedvérifie que le joueur a la permissioncommand.annoncedans le système ACE (Access Control Entry) du serveur. Ce système d'authentification des permissions se configure dansserver.cfg;TriggerClientEventenvoie un événement à un ou plusieurs clients. La valeur-1cible toutes les connexions actives ;print()écrit un log dans la console serveur. Le message apparaît dans le terminal du serveur et dans la console txAdmin.
Communication client-serveur avec les événements
Dans FiveM, le client et le serveur sont séparés. Pour qu'ils communiquent, on utilise des événements réseau. Le principe :
- Le client déclenche un événement vers le serveur avec
TriggerServerEvent('nomEvenement', ...); - Le serveur écoute cet événement avec
RegisterNetEvent('nomEvenement')puisAddEventHandler; - Le serveur peut répondre au client avec
TriggerClientEvent('nomEvenement', source, ...).
Exemple complet : le joueur tape /solde, le client envoie une requête au serveur, le serveur renvoie un solde fictif.
-- client.lua
RegisterNetEvent('monscript:recevoirSolde')
AddEventHandler('monscript:recevoirSolde', function(montant)
TriggerEvent('chat:addMessage', {
args = { 'Votre solde : ' .. tostring(montant) .. ' $' }
})
end)
RegisterCommand('solde', function()
TriggerServerEvent('monscript:demanderSolde')
end, false)
-- server.lua
RegisterNetEvent('monscript:demanderSolde')
AddEventHandler('monscript:demanderSolde', function()
local source = source
-- En production, ce montant viendrait d'une requete vers la base de donnees
local solde = 15000
TriggerClientEvent('monscript:recevoirSolde', source, solde)
end)
Le préfixe monscript: dans le nom des événements est une convention qui évite les collisions avec d'autres ressources. RegisterNetEvent est obligatoire côté récepteur pour les événements réseau : sans lui, l'événement est ignoré silencieusement. Cette architecture client-serveur est fondamentale dans FiveM : le client ne doit jamais exécuter de logique critique (argent, inventaire, sanctions) directement.
Les fonctions natives
Les fonctions natives sont le point d'accès au moteur de jeu GTA V. FiveM en documente plusieurs milliers, organisées par catégorie (véhicules, peds, interface, météo, armes, etc.) dans la référence officielle sur docs.fivem.net/natives/. Quelques natives fréquemment utilisées côté client :
| Native | Rôle | Exemple |
|---|---|---|
PlayerPedId() | Récupère l'entité du personnage du joueur | local ped = PlayerPedId() |
GetEntityCoords(entity) | Coordonnées d'une entité (vector3) | local pos = GetEntityCoords(ped) |
RequestModel(hash) | Charge un modèle en mémoire (RAM) | RequestModel(`adder`) |
HasModelLoaded(hash) | Vérifie si le modèle est chargé | while not HasModelLoaded(`adder`) do Wait(100) end |
CreateVehicle(...) | Crée un véhicule dans le monde | CreateVehicle(hash, x, y, z, heading, true, false) |
SetEntityAsNoLongerNeeded(entity) | Libère l'entité de la mémoire du script | SetEntityAsNoLongerNeeded(vehicle) |
GetPlayerIdentifiers(source) | Identifiants du joueur (serveur) | GetPlayerIdentifiers(source) |
GetNumPlayerIndices() | Nombre de connexions actives (serveur) | local count = GetNumPlayerIndices() |
Pour trouver la bonne native, consultez la documentation officielle ou utilisez la barre de recherche sur docs.fivem.net. Chaque native indique si elle est client, serveur ou partagée, et documente les paramètres attendus. L'extension VS Code charge ces informations pour proposer l'autocomplétion directement dans l'éditeur.
Boucles et threads avec Citizen.CreateThread
Certaines logiques nécessitent une exécution continue : affichage d'un texte à l'écran, détection d'une zone, vérification d'une condition à chaque image (frame). FiveM fournit Citizen.CreateThread pour créer un thread coopératif, et Citizen.Wait (ou son alias Wait) pour contrôler la fréquence d'exécution.
-- client.lua : afficher un message a l'ecran en continu
Citizen.CreateThread(function()
while true do
Citizen.Wait(0) -- 0 = chaque frame (~16 ms a 60 FPS)
DrawTextOnScreen('Mon serveur FiveM', 0.5, 0.02)
end
end)
function DrawTextOnScreen(text, x, y)
SetTextFont(0)
SetTextScale(0.5, 0.5)
SetTextCentre(true)
BeginTextCommandDisplayText('STRING')
AddTextComponentSubstringPlayerName(text)
EndTextCommandDisplayText(x, y)
end
Citizen.Wait(0) cède le contrôle au moteur de jeu à chaque frame. Sans cet appel, la boucle bloque le thread principal et le jeu se fige. Citizen.Wait(1000) attend une seconde, ce qui convient pour des vérifications moins fréquentes (détection de zone, mise à jour d'un compteur). Adaptez la valeur à la fréquence réellement nécessaire pour limiter la charge sur le processeur du client et la RAM du serveur.
Les exports : partager des fonctions entre ressources
Quand votre serveur compte plusieurs dizaines de ressources, certaines fonctions doivent être appelées depuis d'autres scripts. Les exports permettent à une ressource d'exposer des fonctions que les autres ressources peuvent exécuter sans passer par un événement réseau.
Déclarez un export dans votre script Lua :
-- server.lua de la ressource "monscript"
exports('GetPlayerMoney', function(playerId)
-- En production, requete vers la base de donnees
return 15000
end)
Appelez cet export depuis une autre ressource :
-- server.lua d'une autre ressource
local solde = exports.monscript:GetPlayerMoney(source)
print('Solde du joueur : ' .. tostring(solde))
Les exports sont synchrones : l'appel bloque jusqu'au retour de la fonction. C'est la méthode recommandée pour les appels entre ressources sur le même côté (serveur-serveur ou client-client), plus simple et plus performante que les événements. Pour les exports côté serveur, ajoutez server_export 'GetPlayerMoney' dans le manifeste, ou utilisez la syntaxe exports() directement dans le script Lua (les deux fonctionnent).
Déployer la ressource sur le serveur
Une fois les fichiers créés, le déploiement se fait en deux étapes :
- Copiez le dossier de votre ressource dans
resources/[local]/(ou tout autre sous-dossier deresources/selon les configurations de votre serveur) ; - Ajoutez la ligne suivante dans votre fichier de configuration
server.cfgpour que la ressource se charge au démarrage :
ensure mon-script
Pour charger la ressource sans redémarrer le serveur, utilisez la console txAdmin ou le terminal de la console serveur :
refresh
start mon-script
refresh relit les manifestes de toutes les ressources. start charge et démarre la ressource ciblée. Pour exécuter un rechargement après modification du code :
restart mon-script
Surveillez les logs dans la console serveur après chaque start ou restart : les erreurs Lua s'affichent directement avec le nom du fichier et le numéro de ligne. Si votre serveur tourne sur un hébergement FiveM ElypseCloud avec txAdmin préinstallé, la console web affiche les mêmes logs en temps réel et permet d'exécuter les commandes serveur depuis le navigateur.
Déboguer un script Lua FiveM
Le débogage sur FiveM repose sur la lecture attentive des logs et quelques techniques simples :
print()côté serveur écrit un log dans la console du terminal serveur et dans txAdmin. C'est l'outil de débogage le plus direct pour vérifier qu'un événement est bien reçu ou qu'une variable contient la bonne valeur ;- La touche F8 ouvre la console client dans le jeu, qui affiche les erreurs et les
print()côté client ; - Les erreurs Lua indiquent le fichier, le numéro de ligne et le type d'erreur (
nil value,attempt to index,attempt to call, etc.). Lisez le message d'erreur avant de chercher ailleurs ; - Pour les problèmes d'événements réseau, ajoutez un
print()dans le handler côté récepteur : si rien ne s'affiche dans les logs, l'événement n'arrive pas (vérifiezRegisterNetEvent, le nom exact de l'événement, et que la ressource est bien démarrée des deux côtés).
Erreurs courantes et leur cause :
| Message d'erreur dans les logs | Cause probable | Solution |
|---|---|---|
attempt to call a nil value | Fonction native mal orthographiée ou appelée dans le mauvais contexte (client/serveur) | Vérifiez le nom exact dans la documentation des natives |
attempt to index a nil value | Variable non initialisée ou retour inattendu d'une fonction | Ajoutez un print() avant la ligne pour inspecter la valeur |
No such export | La ressource exportant la fonction n'est pas démarrée | Vérifiez l'ordre de chargement avec dependency dans le manifeste |
| Aucune erreur, mais rien ne se passe | Événement non enregistré, RegisterNetEvent manquant | Ajoutez un print() au début du handler pour confirmer la réception |
SCRIPT ERROR: @monscript/... | Erreur d'exécution dans le fichier indiqué | Le numéro de ligne dans le log pointe vers la source du problème |
Pour aller plus loin dans le diagnostic de problèmes serveur, notre guide sur l'optimisation des performances d'un serveur FiveM couvre les outils de monitoring intégrés à txAdmin (utilisation du processeur, RAM, temps d'exécution des ressources) et les indicateurs à surveiller. Si vos scripts exécutent des requêtes vers une base de données MySQL, notre guide de liaison MySQL sur serveur FiveM détaille la configuration et les bonnes pratiques pour les requêtes asynchrones.
Bonnes pratiques pour vos scripts
- Préfixer les événements réseau avec le nom de la ressource (
monscript:nomEvenement) pour éviter les collisions avec d'autres scripts ou frameworks ; - Ne jamais faire confiance au client : toute donnée envoyée par
TriggerServerEventpeut être falsifiée par un tricheur. Vérifiez côté serveur avant d'exécuter une action (argent, objets, téléportation). L'authentification des permissions passe par le système ACE, configuré dansserver.cfg; - Libérer les ressources mémoire : après avoir créé un véhicule ou chargé un modèle, appelez
SetEntityAsNoLongerNeededetSetModelAsNoLongerNeededpour éviter les fuites de RAM. Un serveur avec beaucoup de scripts qui ne libèrent pas leurs entités consomme de plus en plus de mémoire jusqu'au crash ; - Adapter la fréquence des boucles :
Wait(0)(chaque frame) uniquement pour l'affichage. Pour les vérifications logiques,Wait(500)ouWait(1000)suffit et réduit la charge processeur ; - Versionner et sauvegarder le code : utilisez Git pour suivre les modifications. Avant chaque mise à jour majeure, faites une sauvegarde complète du dossier
resources/et de la base de données. Notre guide sur les sauvegardes de serveur FiveM détaille les stratégies de backup automatisé ; - Séparer la configuration du code : placez les valeurs modifiables (messages, coordonnées, montants) dans un fichier
config.luachargé viashared_scriptdans le manifeste. Les administrateurs du serveur peuvent ainsi ajuster les configurations sans toucher au code principal ; - Structurer les logs : utilisez
print()avec des préfixes clairs ([monscript]) pour retrouver vos messages dans les logs du terminal. Sur un serveur avec des dizaines de ressources, des logs non identifiés sont inutiles.
Aller plus loin
Ce premier script couvre les bases : manifeste, commandes, natives, événements réseau, threads, exports. Pour approfondir :
- Intégrer votre script avec un framework (ESX Legacy ou QBCore) pour accéder aux systèmes d'argent, d'inventaire et de métiers existants ;
- Connecter votre script à une base de données MySQL pour persister les données entre les redémarrages du serveur. Les requêtes passent par une ressource dédiée comme oxmysql, qui gère le pool de connexions à la base de données ;
- Lire et installer des scripts communautaires en sachant évaluer leur fiabilité ;
- Configurer txAdmin pour gérer les ressources, les logs et les redémarrages depuis une interface web ;
- Sécuriser votre serveur contre les tricheurs avec un anti-cheat adapté ;
- Configurer un nom de domaine avec enregistrement SRV pour que les joueurs se connectent avec une adresse lisible plutôt qu'une adresse IP.
La documentation officielle Cfx.re (docs.fivem.net) reste la référence pour les natives, les événements et les fonctionnalités avancées comme les exports, le NUI (interface HTML/JavaScript intégrée au jeu) et le système de state bags pour synchroniser des données entre client et serveur.