Los juegos de Roblox son multijugador por defecto, por lo que todos los juegos se comunican inherentemente entre el servidor y los clientes conectados de los jugadores. En el caso más simple, a medida que los jugadores mueven sus personajes, ciertas propiedades de Humanoid, como los estados, se comunican al servidor, que pasa esta información a otros clientes conectados.
Los eventos remotos y los callbacks te permiten comunicarte a través de la frontera cliente-servidor:
- RemoteEvents habilitan la comunicación unidireccional (enviando una solicitud y no esperando una respuesta).
- UnreliableRemoteEvents habilitan la comunicación unidireccional para datos que cambian continuamente o que no son críticos para el estado del juego. Estos eventos intercambian orden y fiabilidad por un mejor rendimiento de red.
- RemoteFunctions habilitan la comunicación bidireccional (enviando una solicitud y esperando hasta que se reciba una respuesta del destinatario).
A diferencia de los eventos vinculables, que tienen una utilidad más limitada, los casos de uso para eventos y funciones remotas son demasiado numerosos para enumerar:
- Jugabilidad - La jugabilidad básica, como un jugador alcanzando el final de un nivel, puede requerir un evento remoto. Un script del cliente notifica al servidor, y los scripts del servidor restablecen la posición del jugador.
- Verificación del servidor - Si un jugador intenta beber una poción, ¿realmente tiene esa poción? Para garantizar la equidad, el servidor debe ser la fuente de verdad para un juego. Un script del cliente puede usar un evento remoto para notificar al servidor que el jugador está bebiendo una poción, y luego los scripts del servidor pueden decidir si el jugador realmente tiene esa poción y si se le otorgan beneficios.
- Actualizaciones de la interfaz de usuario - A medida que cambia el estado del juego, los scripts del servidor pueden usar eventos remotos para notificar a los clientes sobre cambios en las puntuaciones, objetivos, etc.
- Compras en el mercado dentro del juego - Para una implementación de ejemplo que utiliza funciones remotas, consulta Compras de suscripción de aviso.
Referencia rápida
Las siguientes tablas sirven como una referencia rápida sobre cómo usar RemoteEvents y RemoteFunctions para comunicarse entre el cliente y el servidor.
| Cliente → Servidor | |
|---|---|
| Cliente | RemoteEvent:FireServer(args) |
| Servidor | RemoteEvent.OnServerEvent:Connect(function(player, args)) |
| Servidor → Cliente | |
| Servidor | RemoteEvent:FireClient(player, args) |
| Cliente | RemoteEvent.OnClientEvent:Connect(function(args)) |
| Servidor → Todos los Clientes | |
| Servidor | RemoteEvent:FireAllClients(args) |
| Cliente | RemoteEvent.OnClientEvent:Connect(function(args)) |
Eventos remotos
Un objeto RemoteEvent facilita la comunicación asíncrona y unidireccional a través de la frontera cliente-servidor sin esperar una respuesta.
Para crear un nuevo RemoteEvent a través de la ventana del Explorador en Studio:
- Pasa el cursor sobre el contenedor en el que deseas insertar el RemoteEvent. Para garantizar el acceso tanto del servidor como del cliente, debe estar en un lugar donde ambos lados puedan verlo, como ReplicatedStorage, aunque en algunos casos es apropiado almacenarlo en Workspace o dentro de un Tool.
- Haz clic en el botón ⊕ que aparece a la derecha del nombre del contenedor e inserta una instancia de RemoteEvent.
- Cambia el nombre de la instancia para describir su propósito.
Una vez que hayas creado un RemoteEvent, puede facilitar la comunicación unidireccional desde cliente a servidor, desde servidor a cliente, o desde el servidor a todos los clientes.



Cliente → servidor
Puedes usar un LocalScript para activar un evento en el servidor llamando al método FireServer() en un RemoteEvent. Si pasas argumentos a FireServer(), se pasan al manejador de eventos en el servidor con ciertas limitaciones. Ten en cuenta que el primer parámetro del manejador de eventos en el servidor es siempre el objeto Player del cliente que lo llama, y los parámetros adicionales siguen.
| Cliente | RemoteEvent:FireServer(args) |
| Servidor | RemoteEvent.OnServerEvent:Connect(function(player, args)) |
El siguiente Script conecta un manejador de eventos a OnServerEvent que crea una nueva Part en el servidor. El LocalScript correspondiente luego llama a FireServer() en la instancia de RemoteEvent con el Color y Position deseados para la parte.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
-- Obtener referencia a la instancia de evento remoto
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onCreatePart(player, partColor, partPosition)
print(player.Name .. " activó el RemoteEvent")
local newPart = Instance.new("Part")
newPart.Color = partColor
newPart.Position = partPosition
newPart.Parent = Workspace
end
-- Conectar función al evento
remoteEvent.OnServerEvent:Connect(onCreatePart)local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Obtener referencia a la instancia de evento remoto
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- Activar el evento remoto y pasar argumentos adicionales
remoteEvent:FireServer(Color3.fromRGB(255, 0, 0), Vector3.new(0, 25, -20))Servidor → cliente
Puedes usar un Script para activar un evento en un cliente llamando al método FireClient() en un RemoteEvent. El primer argumento para FireClient() es el objeto Player del cliente al que deseas responder al evento, y los argumentos adicionales se pasan al cliente con ciertas limitaciones. Ten en cuenta que el manejador de eventos no necesita incluir el objeto Player como su primer argumento porque puedes determinar el jugador en el cliente con Players.LocalPlayer.
| Servidor | RemoteEvent:FireClient(player, args) |
| Cliente | RemoteEvent.OnClientEvent:Connect(function(args)) |
El siguiente LocalScript conecta un manejador de eventos al evento OnClientEvent. El Script correspondiente luego escucha a los jugadores entrantes al servidor y llama a FireClient() para cada uno con datos arbitrarios.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
-- Obtener referencia a la instancia de evento remoto
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local player = Players.LocalPlayer
local function onNotifyPlayer(maxPlayers, respawnTime)
print("[Cliente] Evento recibido por el jugador", player.Name)
print(maxPlayers, respawnTime)
end
-- Conectar función al evento
remoteEvent.OnClientEvent:Connect(onNotifyPlayer)local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
-- Obtener referencia a la instancia de evento remoto
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- Escuchar a los jugadores entrantes y despachar evento remoto a cada uno
local function onPlayerAdded(player)
print("[Servidor] Activando evento para el jugador", player.Name)
remoteEvent:FireClient(player, Players.MaxPlayers, Players.RespawnTime)
end
Players.PlayerAdded:Connect(onPlayerAdded)Servidor → todos los clientes
Puedes usar un Script para activar un evento en todos los clientes llamando al método FireAllClients() en un RemoteEvent. A diferencia de FireClient(), el método FireAllClients() no requiere un objeto Player porque activa el RemoteEvent para todos los clientes.
| Servidor | RemoteEvent:FireAllClients(args) |
| Cliente | RemoteEvent.OnClientEvent:Connect(function(args)) |
El siguiente LocalScript conecta un manejador de eventos al evento OnClientEvent que muestra un tiempo de cuenta regresiva restante. El Script correspondiente luego llama a FireAllClients() en un bucle cada segundo para activar el RemoteEvent para todos los clientes.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Obtener referencia a la instancia de evento remoto
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onTimerUpdate(seconds)
print(seconds)
end
-- Conectar función al evento
remoteEvent.OnClientEvent:Connect(onTimerUpdate)local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Obtener referencia a la instancia de evento remoto
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local countdown = 5
-- Activar el RemoteEvent cada segundo hasta que se agote el tiempo
for timeRemaining = -1, countdown do
remoteEvent:FireAllClients(countdown - timeRemaining)
task.wait(1)
endCallbacks remotos
Un objeto RemoteFunction facilita la comunicación sincrónica y bidireccional a través de la frontera cliente-servidor. El remitente de una función remota esperará hasta que reciba una respuesta del destinatario.
Para crear un nuevo RemoteFunction a través de la ventana del Explorador en Studio:
- Pasa el cursor sobre el contenedor en el que deseas insertar el RemoteFunction. Para garantizar el acceso tanto del servidor como del cliente, debe estar en un lugar donde ambos lados puedan verlo, como ReplicatedStorage, aunque en algunos casos es apropiado almacenarlo en Workspace o dentro de un Tool.
- Haz clic en el botón ⊕ que aparece a la derecha del nombre del contenedor e inserta una instancia de RemoteFunction.
- Cambia el nombre de la instancia para describir su propósito.
Una vez que hayas creado un RemoteFunction, puede facilitar la comunicación bidireccional entre cliente y servidor o entre servidor y cliente.


Cliente → servidor → cliente
Puedes usar un LocalScript para llamar a una función en el servidor llamando al método InvokeServer() en un RemoteFunction. A diferencia de un evento remoto, el LocalScript que invoca el RemoteFunction espera hasta que el callback regrese. Los argumentos que pasas a InvokeServer() se pasan al callback OnServerInvoke del RemoteFunction con ciertas limitaciones. Ten en cuenta que si defines múltiples callbacks para el mismo RemoteFunction, solo se ejecutará la última definición.
| Cliente | RemoteFunction:InvokeServer(args) |
| Servidor | RemoteFunction.OnServerInvoke = function(player, args) |
El siguiente Script define la función de callback a través de OnServerInvoke y devuelve la Part solicitada a través de su valor de return. El LocalScript correspondiente luego llama a InvokeServer() con argumentos adicionales que definen el color y la posición de la parte solicitada.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
-- Obtener referencia a la instancia de función remota
local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")
-- Función de callback
local function createPart(player, partColor, partPosition)
print(player.Name .. " solicitó una nueva parte")
local newPart = Instance.new("Part")
newPart.Color = partColor
newPart.Position = partPosition
newPart.Parent = Workspace
return newPart
end
-- Establecer la función como callback de la función remota
remoteFunction.OnServerInvoke = createPartlocal ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Obtener referencia a la instancia de función remota
local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")
-- Pasar un color y una posición al invocar el callback
local newPart = remoteFunction:InvokeServer(Color3.fromRGB(255, 0, 0), Vector3.new(0, 25, -20))
-- Mostrar la referencia de la parte devuelta
print("El servidor creó la parte solicitada:", newPart)Servidor → cliente → servidor
Puedes usar un Script para llamar a una función en el cliente llamando al método InvokeClient() en un RemoteFunction, pero tiene riesgos serios como los siguientes:
- Si el cliente lanza un error, el servidor también lanza el error.
- Si el cliente se desconecta mientras se está invocando, InvokeClient() lanza un error.
- Si el cliente no devuelve un valor, el servidor espera indefinidamente.
Para acciones que no requieren comunicaciones bidireccionales, como actualizar una GUI, utiliza un RemoteEvent y comunícate desde servidor a cliente.
Garantías de entrega
Cada tipo remoto hace diferentes promesas sobre si un mensaje llega y en qué orden. Estas promesas solo se aplican a una sola conexión en una sola dirección, es decir, desde el servidor a un cliente específico, o desde un cliente específico al servidor.
RemoteEvents
RemoteEvents son fiables y ordenados:
- Si un mensaje se pierde en tránsito, Roblox lo retransmite hasta que llegue, siempre que el destinatario permanezca conectado.
- Los mensajes llegan en el orden en que los envías, incluyendo a través de diferentes instancias de RemoteEvent. Por ejemplo, si el servidor activa el evento A, luego B, y luego A nuevamente para el mismo cliente, ese cliente recibe A, B, A. Esto se aplica ya sea que uses FireClient() o FireAllClients().
- Si un cliente activa eventos más rápido que el límite de limitación, el servidor procesa los eventos excedentes más tarde en lugar de descartarlos, y mantienen su orden.
Un mensaje fiable y ordenado aún puede fallar en llegar a tu código en los siguientes casos:
- No hay manejador conectado. Si nada está conectado a OnServerEvent o OnClientEvent cuando llega un mensaje, Roblox lo pone en cola y lo entrega, en orden, tan pronto como se conecta un manejador. La cola está limitada tanto en cantidad como en memoria. Una vez que está llena, Roblox descarta mensajes adicionales y registra un error de Invocación de evento remoto en la ventana de Salida.
- El destinatario se desconecta. Los mensajes que no se han entregado cuando un cliente se va se pierden.
El orden se aplica a cuando cada manejador comienza, no cuando termina. Si un manejador espera, como al llamar a task.wait() o WaitForChild(), Roblox puede comenzar a manejar el siguiente mensaje antes de que el manejador anterior reanude.
RemoteFunctions
RemoteFunctions envían invocaciones y respuestas de manera fiable, por lo que Roblox retransmite mensajes perdidos siempre que ambos lados permanezcan conectados. Debido a que InvokeServer() y InvokeClient() esperan hasta que llegue una respuesta, un solo hilo no puede tener más de una invocación en vuelo. Sin embargo, hilos separados pueden invocar al mismo tiempo, y si un callback espera, las respuestas pueden llegar en un orden diferente al que se enviaron las invocaciones.
Si no se asigna ningún callback a OnServerInvoke o OnClientInvoke, Roblox pone en cola las invocaciones hasta que uno esté. Esta cola tiene los mismos límites que la cola de RemoteEvent.
UnreliableRemoteEvents
UnreliableRemoteEvents no garantizan ni la entrega ni el orden. Tampoco tienen relación de orden con RemoteEvents o RemoteFunctions. Roblox puede descartar un mensaje en cualquiera de los siguientes casos, sin volver a enviarlo:
- El mensaje se pierde en tránsito.
- La red está congestionada y el mensaje espera demasiado tiempo para enviarse.
- La carga útil es mayor de 1,000 bytes.
- El cliente activa más rápido que el límite de limitación. A diferencia de un RemoteEvent, el servidor descarta los mensajes excedentes en lugar de retrasarlos.
- Nada está conectado a OnServerEvent o OnClientEvent cuando llega el mensaje. A diferencia de un RemoteEvent, Roblox descarta el mensaje de inmediato en lugar de ponerlo en cola.
Roblox tampoco espera a que llegue un mensaje anterior antes de procesar uno posterior, por lo que los mensajes pueden llegar fuera de orden. Si el orden importa, incluye una marca de tiempo o un número de secuencia en cada mensaje e ignora los mensajes más antiguos que el último que has manejado.
Limitaciones de limitación y tamaño
Los mensajes de cliente a servidor están sujetos a un límite de tasa que cada tipo remoto comparte entre todas sus instancias. Para los límites actuales, consulta la sección de Limitación de RemoteEvent y los límites de carga útil en UnreliableRemoteEvent.
Limitaciones de argumentos
Cuando activas un RemoteEvent o invocas un RemoteFunction, este reenvía cualquier argumento que pases con el evento o a la función de callback. Cualquier tipo de objeto de Roblox, como un Enum, Instance, u otros, puede ser pasado, así como tipos de Luau como números, cadenas y booleanos, aunque debes explorar cuidadosamente las siguientes limitaciones.
Índices no string
Si algún índice de una tabla pasada son tipos no string, como un Instance, userdata, o función, Roblox convierte automáticamente esos índices a strings.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onEventFire(passedTable)
for k, v in passedTable do
print(typeof(k)) --> string
end
end
-- Conectar función al evento
remoteEvent.OnClientEvent:Connect(onEventFire)local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- Escuchar a los jugadores entrantes y despachar evento remoto a cada uno
local function onPlayerAdded(player)
remoteEvent:FireClient(player,
{
[Workspace.Baseplate] = true
}
)
end
Players.PlayerAdded:Connect(onPlayerAdded)Funciones pasadas
Las funciones incluidas como argumentos para un RemoteEvent o RemoteFunction no se replican a través de la frontera cliente-servidor, lo que hace imposible pasar funciones de forma remota. En su lugar, el argumento resultante en el lado receptor será nil.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onClientEvent(func)
print(func) --> nil
end
remoteEvent.OnClientEvent:Connect(onClientEvent)local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function testFunction()
print("¡Hola mundo!")
end
-- Activar evento remoto con función como argumento
remoteEvent:FireAllClients(testFunction)Indexación de tablas
Si pasas una tabla de datos, no pases una tabla mixta de claves numéricas y string. En su lugar, pasa una tabla que consista entera de pares clave-valor (diccionario) o entera de índices numéricos.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onEventFire(player, passedTable)
for k, v in passedTable do
print(k .. " = " .. v)
--> 1 = Espada
--> 2 = Arco
--> CharName = Diva Dragonslayer
--> CharClass = Rogue
end
end
-- Conectar función al evento
remoteEvent.OnServerEvent:Connect(onEventFire)local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- Tabla indexada numéricamente
local inventoryData = {
"Espada", "Arco"
}
-- Tabla de diccionario
local characterData = {
CharName = "Diva Dragonslayer",
CharClass = "Rogue"
}
remoteEvent:FireServer(inventoryData)
remoteEvent:FireServer(characterData)Identidades de tablas
Las tablas pasadas como argumentos a eventos/callbacks remotos se copian, lo que significa que no serán exactamente equivalentes a las proporcionadas al activar el evento o invocar el callback. Tampoco las tablas devueltas al invocador serán exactamente equivalentes a las proporcionadas. Puedes demostrar esto ejecutando el siguiente script en un RemoteFunction y observando cómo las identidades de las tablas difieren.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")
-- Función de callback
local function returnTable(player, passedTable)
-- Mostrar identidad de la tabla en la invocación
print(tostring(passedTable)) --> table: 0x48eb7aead27563d9
return passedTable
end
-- Establecer la función como callback de la función remota
remoteFunction.OnServerInvoke = returnTablelocal ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")
local inventoryData = {
"Espada", "Arco"
}
-- Mostrar identidad de la tabla original
print(tostring(inventoryData)) --> table: 0x059bcdbb2b576549
local invokeReturn = remoteFunction:InvokeServer(inventoryData)
-- Mostrar identidad de la tabla al regresar
print(tostring(invokeReturn)) --> table: 0x9fcae7919563a0e9Metatables
Si una tabla tiene una metatable, toda la información de la metatable se pierde en la transferencia. En el siguiente ejemplo de código, la propiedad NumWheels es parte de la metatable de Car. Cuando el servidor recibe la siguiente tabla, la tabla truck tiene la propiedad Name pero no la propiedad NumWheels.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onEvent(player, param)
print(param) --> {["Name"] = "MyTruck"}
end
-- Conectar función al evento
remoteEvent.OnServerEvent:Connect(onEvent)local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local Car = {}
Car.NumWheels = 4
Car.__index = Car
local truck = {}
truck.Name = "MyTruck"
setmetatable(truck, Car)
-- Activar evento con tabla que incluye una metatable
remoteEvent:FireServer(truck)Instancias no replicadas
Si un RemoteEvent o RemoteFunction pasa un valor que solo es visible para el remitente, Roblox no lo replica a través de la frontera cliente-servidor y pasa nil en lugar del valor. Por ejemplo, si un Script pasa un descendiente de ServerStorage, el cliente que escucha el evento recibirá un valor nil porque ese objeto no es replicable para el cliente.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- Se recibirá como "nil" porque el cliente no puede acceder a ServerStorage
local storedPart = Instance.new("Part")
storedPart.Parent = ServerStorage
local function onPlayerAdded(player)
remoteEvent:FireClient(player, storedPart)
end
Players.PlayerAdded:Connect(onPlayerAdded)De manera similar, si creas una parte en un LocalScript y tratas de pasarla a un Script, el servidor verá nil porque la parte no es replicable para el servidor.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- Se recibirá como "nil" porque el servidor no conoce esta parte
local clientPart = Instance.new("Part")
clientPart.Parent = Workspace
remoteEvent:FireServer(clientPart)