Module 04 · Leçon 2 sur 5
Communiquer avec les RemoteEvents
Fais dialoguer tes LocalScripts et tes Scripts serveur avec les RemoteEvents, passe-leur des arguments et range-les proprement dans ReplicatedStorage.
- ⏱️ 30 min
- 📝 Quiz de 6 questions
- 🔄 Vérifié le 24 octobre 2026
Sur cette page
- Un RemoteEvent, c’est une boîte aux lettres
- Créer et ranger ses remotes
- Du client vers le serveur
- Du serveur vers les clients
- Vers un seul joueur : FireClient
- Vers tout le monde : FireAllClients
- Ce qu’on peut envoyer (et ce qui se perd en route)
- Fiabilité, ordre et limites de débit
- Aperçu : l’UnreliableRemoteEvent
- Quiz de fin de leçon
🎯 Dans cette leçon, tu vas apprendre à…
- Envoyer un message du client vers le serveur, et du serveur vers un ou tous les clients
- Passer des arguments en connaissant les types transmissibles et leurs limites
- Organiser ses remotes dans ReplicatedStorage
- Savoir quand utiliser un UnreliableRemoteEvent
Dans la leçon précédente, tu as vu que le client ne peut pas modifier l’état du jeu pour les autres. Mais il doit bien pouvoir demander des choses : « je veux réapparaître au checkpoint », « je veux acheter cette traînée ». Et le serveur doit pouvoir prévenir les joueurs : « étape 3 atteinte ! ». C’est le rôle des RemoteEvents, les messages qui traversent la frontière entre client et serveur.
Un RemoteEvent, c’est une boîte aux lettres
Un RemoteEvent est un objet que tu places dans l’Explorer. Les deux côtés s’en servent comme d’une boîte aux lettres :
- un côté envoie un message (avec
Fire…) ; - l’autre côté écoute et réagit (avec
On…Event).
C’est un message à sens unique : celui qui envoie n’attend pas de réponse et continue son code immédiatement.
Créer et ranger ses remotes
Les deux côtés doivent pouvoir voir le RemoteEvent. La documentation recommande ReplicatedStorage. Pour l’Obby des Nuages, on adopte une convention pour toute la formation :
- Dans l’Explorer, survole ReplicatedStorage, clique sur ⊕ et insère un Folder (dossier) nommé
Remotes. - Dans
Remotes, insère un RemoteEvent pour chaque action, avec un nom qui décrit l’action :DemanderReapparition,Notification,AnnonceVictoire,AcheterObjet…
ReplicatedStorage
└── Remotes (Folder)
├── AcheterObjet (RemoteEvent)
├── AnnonceVictoire (RemoteEvent)
├── DemanderReapparition (RemoteEvent)
└── Notification (RemoteEvent)
Du client vers le serveur
Côté client, on appelle FireServer(...). Côté serveur, on se connecte à OnServerEvent. Point capital : le serveur reçoit toujours le joueur expéditeur en premier paramètre, ajouté automatiquement par Roblox. Les arguments passés à FireServer arrivent ensuite.
Exemple du fil rouge : un bouton « Retour au checkpoint » pour un joueur coincé. Le client demande, le serveur fait réapparaître le personnage.
-- LocalScript placé dans StarterPlayer > StarterPlayerScripts (côté client)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
-- WaitForChild : côté client, les objets peuvent arriver un peu après le démarrage du script
local remotes = ReplicatedStorage:WaitForChild("Remotes")
local demanderReapparition = remotes:WaitForChild("DemanderReapparition") :: RemoteEvent
-- Touche R : on DEMANDE au serveur, on ne fait rien nous-mêmes
UserInputService.InputBegan:Connect(function(input: InputObject, dejaTraite: boolean)
if dejaTraite then
return -- le joueur tapait dans le chat, par exemple
end
if input.KeyCode == Enum.KeyCode.R then
demanderReapparition:FireServer()
end
end)
-- Script placé dans ServerScriptService (côté serveur)
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remotes = ReplicatedStorage:WaitForChild("Remotes")
local demanderReapparition = remotes:WaitForChild("DemanderReapparition") :: RemoteEvent
local DELAI_MINIMUM = 5 -- secondes entre deux demandes d'un même joueur
local derniereDemande: { [Player]: number } = {}
-- Le premier paramètre est TOUJOURS le joueur qui a envoyé le message
demanderReapparition.OnServerEvent:Connect(function(player: Player)
local maintenant = os.clock()
local precedente = derniereDemande[player]
if precedente and maintenant - precedente < DELAI_MINIMUM then
return -- trop rapproché : on ignore (on détaillera ces protections en leçon 4)
end
derniereDemande[player] = maintenant
player:LoadCharacterAsync() -- recrée le personnage du joueur (remplace l'ancienne méthode LoadCharacter, dépréciée)
end)
-- On oublie le joueur quand il part, pour ne pas garder de données inutiles
Players.PlayerRemoving:Connect(function(player: Player)
derniereDemande[player] = nil
end)
Le :: RemoteEvent en fin de ligne est une annotation de type, vue dans la leçon 6 du module 3 : elle aide Studio à proposer les bonnes méthodes, sans rien changer à l’exécution.
Tu remarques le petit délai minimum ? Sans lui, un joueur (ou un programme de triche) pourrait envoyer la demande en rafale, des centaines de fois par seconde. La référence de Player déconseille d’ailleurs de rappeler LoadCharacterAsync pour un même joueur avant que son apparence ait fini de se charger. On approfondira ces protections dans la leçon 4.
Du serveur vers les clients
Vers un seul joueur : FireClient
Le serveur appelle FireClient(player, ...) : le premier argument indique à qui envoyer. Côté client, on écoute OnClientEvent. Pas besoin de paramètre player ici : le client sait qui il est grâce à Players.LocalPlayer.
-- Script placé dans ServerScriptService (côté serveur)
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local notification = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("Notification") :: RemoteEvent
-- Prévient UN joueur précis
local function prevenirEtape(player: Player, numeroEtape: number)
notification:FireClient(player, "Étape " .. numeroEtape .. " atteinte !")
end
-- Démonstration : chaque joueur reçoit un message 5 secondes après son arrivée
Players.PlayerAdded:Connect(function(player: Player)
task.wait(5)
prevenirEtape(player, 1)
end)
-- LocalScript placé dans StarterPlayer > StarterPlayerScripts (côté client)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local notification = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("Notification") :: RemoteEvent
notification.OnClientEvent:Connect(function(message: string)
-- Pour l'instant on affiche dans l'Output (au module 6, un LocalScript l'affichera à l'écran)
print("Notification :", message)
end)
Vers tout le monde : FireAllClients
FireAllClients(...) envoie le message à tous les clients connectés. Exemple : annoncer qu’un joueur a terminé l’obby.
-- Script placé dans ServerScriptService (côté serveur)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local Players = game:GetService("Players")
local annonceVictoire = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("AnnonceVictoire") :: RemoteEvent
local ligneArrivee = Workspace:WaitForChild("LigneArrivee") :: BasePart
local dejaAnnonces: { [Player]: boolean } = {}
ligneArrivee.Touched:Connect(function(partieTouchee: BasePart)
local player = Players:GetPlayerFromCharacter(partieTouchee.Parent)
if player and not dejaAnnonces[player] then
dejaAnnonces[player] = true
-- Tous les clients reçoivent le nom du gagnant
annonceVictoire:FireAllClients(player.Name)
end
end)
Players.PlayerRemoving:Connect(function(player: Player)
dejaAnnonces[player] = nil
end)
Attention, ce script fait encore confiance au contact avec la ligne d’arrivée : la leçon 5 ajoutera la vérification « a-t-il vraiment passé toutes les étapes ? ».
Ce qu’on peut envoyer (et ce qui se perd en route)
Tu peux passer plusieurs arguments à FireServer, FireClient et FireAllClients. La plupart des types passent sans souci :
- nombres, textes (
string), booléens,nil; - types Roblox comme
Vector3,CFrame,Color3, lesEnum; - des instances (une Part, un joueur…), à condition que le destinataire puisse les voir ;
- des tables, avec des limites.
La documentation détaille ces limites, à connaître absolument :
| Situation | Ce qui arrive |
|---|---|
| Une fonction passée en argument | Elle n’est pas transmise : le destinataire reçoit nil. |
| Une table dont les clés ne sont pas des textes (une instance, par exemple) | Les clés sont converties en textes. |
| Une table mixte (des clés numériques ET des clés texte) | À éviter : envoie soit une liste, soit un dictionnaire. |
Une table avec des trous (nil au milieu) |
À éviter, pour les listes comme pour les dictionnaires. |
| Une table avec une métatable | La métatable est perdue : seuls les champs directs de la table arrivent. |
| N’importe quelle table | Elle est copiée : le destinataire reçoit une nouvelle table, pas la même. |
| Une instance que le destinataire ne voit pas (dans ServerStorage pour un client, ou créée par un LocalScript pour le serveur) | Elle arrive sous la forme nil. |
-- LocalScript (côté client) : exemples d'envois vers un RemoteEvent de test
-- (pour l'essayer, crée un RemoteEvent « RemoteTest » dans ReplicatedStorage.Remotes)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local remoteTest = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("RemoteTest") :: RemoteEvent
-- Bien : une liste pure
remoteTest:FireServer({ "TrailNuage", "TrailSoleil" })
-- Bien : un dictionnaire pur
remoteTest:FireServer({ nom = "Traînée Nuage", couleur = Color3.fromRGB(0, 170, 255) })
-- À éviter : table mixte (liste + dictionnaire)
remoteTest:FireServer({ "TrailNuage", equipee = true })
-- Le serveur recevra nil : cette Part n'existe que sur ce client
local partLocale = Instance.new("Part")
partLocale.Parent = Workspace
remoteTest:FireServer(partLocale)
Fiabilité, ordre et limites de débit
Selon la documentation, un RemoteEvent est fiable et ordonné : un message perdu est renvoyé tant que le destinataire reste connecté, et les messages arrivent dans l’ordre d’envoi. Deux cas où un message peut quand même se perdre :
- personne n’écoute : Roblox garde les messages en file d’attente jusqu’à ce qu’une fonction se connecte, mais cette file est limitée. Une fois pleine, les messages suivants sont jetés et une erreur apparaît dans l’Output ;
- le destinataire se déconnecte avant de recevoir le message.
Les messages envoyés par un client au serveur sont aussi limités en débit. Au moment de la rédaction (octobre 2026), la référence de RemoteEvent indique environ 500 requêtes par seconde et par client, une limite partagée entre tous les RemoteEvents du jeu (et, séparément, entre tous les UnreliableRemoteEvents). Pour un RemoteEvent classique, les messages en excès sont traités plus tard, pas jetés. Ce chiffre peut évoluer : vérifie la page officielle. Retiens surtout qu’il ne faut pas envoyer de messages en boucle sans raison.
Aperçu : l’UnreliableRemoteEvent
Il existe une variante : l’UnreliableRemoteEvent (« remote event non fiable »). Il s’utilise exactement comme un RemoteEvent (FireServer, FireClient, FireAllClients, OnServerEvent, OnClientEvent), mais il échange la fiabilité contre de meilleures performances réseau :
- un message perdu n’est pas renvoyé ;
- les messages peuvent arriver dans le désordre ;
- un message de plus de 1 000 octets est jeté ;
- s’il n’y a personne pour écouter, le message est jeté tout de suite (pas de file d’attente) ;
- si un client dépasse la limite de débit, les messages en trop sont jetés.
Il sert pour les effets éphémères et les données qui changent en continu, quand seule la dernière valeur compte. Exemple : un petit effet d’étincelles chez tous les joueurs quand une pièce est ramassée. Si un message se perd, personne ne s’en rendra compte.
-- Extrait d'un Script serveur : effet purement visuel, la perte d'un message est sans gravité
-- (crée d'abord un UnreliableRemoteEvent « EffetEtincelles » dans ReplicatedStorage.Remotes)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local effetEtincelles = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("EffetEtincelles") :: UnreliableRemoteEvent
local function montrerEtincelles(position: Vector3)
effetEtincelles:FireAllClients(position)
end
montrerEtincelles(Vector3.new(0, 50, 0))
Pour tout ce qui compte (achat, progression, annonce de victoire), garde un RemoteEvent classique.
Ce qu’il faut retenir
- Un RemoteEvent transporte un message à sens unique entre client et serveur ; on les range dans
ReplicatedStorage.Remotes. - Client → serveur :
FireServer(...)etOnServerEvent, dont le premier paramètre est toujours le joueur expéditeur. - Serveur → client :
FireClient(player, ...); serveur → tous :FireAllClients(...); côté client :OnClientEvent. - Limites des arguments : fonctions →
nil, tables mixtes et trous à éviter, métatables perdues, tables copiées, instances invisibles pour le destinataire →nil. - L’UnreliableRemoteEvent sacrifie fiabilité et ordre pour les données éphémères (1 000 octets maximum par message).
- Le client peut envoyer n’importe quoi : le serveur devra tout vérifier.
Sources et pour aller plus loin
- Remote events and callbacks — documentation Roblox
- RemoteEvent — référence de l’API (limite de débit)
- UnreliableRemoteEvent — référence de l’API
- ReplicatedStorage — référence de l’API
- Securing the client-server boundary — documentation Roblox
- Player — référence de l’API (LoadCharacterAsync, LoadCharacter dépréciée)
- Security and cheat mitigation tactics — documentation Roblox (le premier argument
Playerest le seul garanti)