Has hecho un botón que da monedas. Funciona en tu pantalla, la cifra sube, todo perfecto. Sales, vuelves a entrar y las monedas no están. O peor: tu amigo entra y no ve nada de lo que tú ves.
No has roto nada. Lo que pasa es que lo hiciste en el cliente, y el cliente es una mentira bonita que solo existe en tu ordenador. Para que algo sea real tiene que pasar por el servidor, y para hablar con el servidor hacen falta los RemoteEvents.
Respuesta rápida
Un RemoteEvent es un mensajero entre el cliente y el servidor. Va en ReplicatedStorage porque es el único sitio que ven los dos. El cliente lanza el mensaje con :FireServer() y el servidor lo recoge con .OnServerEvent. Todo lo que importe (monedas, objetos, puntos) se decide en el servidor, nunca en el cliente.
Lo que necesitas antes de empezar
- Saber la diferencia entre un
Scripty unLocalScript. Si no la tienes clara, empieza por ServerScript, LocalScript y ModuleScript. - Roblox Studio abierto con un lugar cualquiera.
- Unos 25 minutos.
Por qué existen los RemoteEvents
Roblox ejecuta tu juego en dos sitios a la vez:
- El servidor: una máquina de Roblox donde está la partida de verdad. Es la única fuente de la verdad.
- El cliente: el Roblox de cada jugador. Dibuja lo que ve, recoge sus teclas, y no se fía nadie de él.
Y por diseño no pueden leerse las variables entre ellos. El servidor no ve lo que hay en la memoria de tu cliente y tú no puedes tocar la del servidor. Si pudieras, cualquiera se daría un millón de monedas.
El RemoteEvent es la puerta con portero: el cliente puede llamar, pero el que decide qué pasa es el servidor.
Antes: crea el leaderstats desde cero
El script del servidor da por hecho que el jugador tiene un contador de monedas. Si no lo tienes, no verás nada cambiar. Añade este Script en ServerScriptService y ya tendrás el marcador arriba a la derecha:
-- Script en ServerScriptService: crea el marcador de cada jugador
game.Players.PlayerAdded:Connect(function(jugador)
local stats = Instance.new("Folder")
stats.Name = "leaderstats" -- el nombre tiene que ser EXACTAMENTE este
stats.Parent = jugador
local monedas = Instance.new("IntValue")
monedas.Name = "Monedas"
monedas.Value = 0
monedas.Parent = stats
end)
El nombre leaderstats es obligatorio y va en minúsculas: es lo que hace que Roblox lo pinte en la tabla de jugadores. Cualquier otro nombre y no sale.
Paso 1: crea el RemoteEvent
En el Explorer, clic derecho sobre ReplicatedStorage → Insert Object → RemoteEvent.
Renómbralo DarMonedas. Los nombres genéricos tipo Remote1 son la receta para no entender tu propio juego dentro de dos semanas.
ReplicatedStorage es la carpeta que ven cliente y servidor. Por eso el RemoteEvent vive ahí y no en ServerStorage (que solo ve el servidor) ni en StarterGui (que es cosa del cliente).
Paso 2: el cliente pide
Crea un LocalScript dentro de StarterPlayer > StarterPlayerScripts y llámalo ClienteBoton:
-- LocalScript en StarterPlayerScripts
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
-- Buscamos el RemoteEvent que creamos antes
local darMonedas = ReplicatedStorage:WaitForChild("DarMonedas")
UserInputService.InputBegan:Connect(function(entrada, escribiendo)
-- Si el jugador está escribiendo en un chat, no hacemos nada
if escribiendo then return end
if entrada.KeyCode == Enum.KeyCode.E then
-- Avisamos al servidor. Nosotros NO damos las monedas.
darMonedas:FireServer()
end
end)
Fíjate en dos cosas:
WaitForChilden vez de un punto. El cliente puede cargar antes que el objeto, y con el punto te saldría unattempt to index nil.- El cliente no suma nada. Solo dice «he pulsado E». Quién decide cuántas monedas es el servidor.
Paso 3: el servidor decide
Crea un Script normal dentro de ServerScriptService y llámalo ServidorMonedas:
-- Script en ServerScriptService
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local darMonedas = ReplicatedStorage:WaitForChild("DarMonedas")
-- Tiempo de espera por jugador, para que no puedan pulsar en bucle
local ultimaVez = {}
local ESPERA = 1 -- segundos
darMonedas.OnServerEvent:Connect(function(jugador)
-- OJO: el primer parámetro SIEMPRE es el jugador, lo pone Roblox
local ahora = tick()
if ultimaVez[jugador] and ahora - ultimaVez[jugador] < ESPERA then
return -- llega demasiado pronto, lo ignoramos
end
ultimaVez[jugador] = ahora
local stats = jugador:FindFirstChild("leaderstats")
if not stats then return end
local monedas = stats:FindFirstChild("Monedas")
if not monedas then return end
monedas.Value = monedas.Value + 10
print(jugador.Name .. " ahora tiene " .. monedas.Value .. " monedas")
end)
-- Limpiamos cuando alguien se va, para no dejar basura en la tabla
game.Players.PlayerRemoving:Connect(function(jugador)
ultimaVez[jugador] = nil
end)
El primer parámetro de OnServerEvent es siempre el jugador que lo lanzó, y lo pone Roblox automáticamente. No hace falta que el cliente lo mande, y de hecho no debes fiarte si lo manda: podría mentir y poner el nombre de otro.
Paso 4: la parte que casi nadie hace
Todo lo de arriba funciona. Y aun así, un tramposo puede abrir un ejecutor y lanzar FireServer() mil veces por segundo.
Por eso el servidor tiene ese tiempo de espera. La regla es simple: el servidor no se fía nunca del cliente. Da igual lo que te mande, tú validas:
- ¿Puede este jugador hacer esto ahora mismo?
- ¿Está lo bastante cerca del objeto?
- ¿Han pasado los segundos que tenían que pasar?
- Si te manda un número, ¿está dentro del rango que tiene sentido?
Mandar datos con el evento
Puedes pasar información en la llamada:
-- Cliente
darMonedas:FireServer("cofre_del_bosque")
-- Servidor
darMonedas.OnServerEvent:Connect(function(jugador, idCofre)
-- idCofre viene del cliente: NO te fíes de él sin comprobarlo
if type(idCofre) ~= "string" then return end
if not COFRES_VALIDOS[idCofre] then return end
-- ahora sí
end)
Todo lo que venga del cliente hay que comprobarlo. El tipo, el rango, que exista. Un if type(x) ~= "number" then return end te ahorra la mitad de los problemas.
Del servidor al cliente
También funciona al revés, para avisos y efectos visuales:
-- Servidor: avisa a un jugador concreto
darMonedas:FireClient(jugador, "¡Has encontrado un cofre!")
-- Servidor: avisa a todos
darMonedas:FireAllClients("El jefe ha aparecido")
-- Cliente: escucha
darMonedas.OnClientEvent:Connect(function(mensaje)
print(mensaje)
end)
Úsalo para lo visual: mensajes, sonidos, animaciones. Nunca para decidir nada importante.
Ponte a prueba
¿Cliente o servidor?
Ocho acciones. Decide dónde tiene que ocurrir cada una para que nadie pueda hacer trampas.
Errores habituales
attempt to index nil with 'FireServer'
El RemoteEvent no existe todavía cuando el script lo busca. Usa WaitForChild("DarMonedas") en vez de ReplicatedStorage.DarMonedas.
FireServer can only be called from the client
Estás llamando a :FireServer() desde un Script normal. Ese método es solo para LocalScript. Si estás en el servidor, lo que quieres es :FireClient().
El evento se dispara dos veces
Tienes el LocalScript duplicado, o lo pusiste en un sitio que se replica a varios jugadores. Mira en el Explorer durante el Play si aparece más de una copia.
Funciona en Studio pero no en el juego publicado
En Studio, cliente y servidor van en la misma máquina y todo va instantáneo. Publicado hay latencia. Si tu código daba por hecho que el servidor responde al momento, ahí se rompe. Prueba siempre con Test → Local Server con 2 jugadores antes de publicar.
Preguntas frecuentes
¿Cuál es la diferencia con un RemoteFunction?
El RemoteEvent lanza y se olvida. La RemoteFunction espera respuesta y bloquea hasta que llega, así que si el otro lado tarda o falla, tu código se queda colgado. Usa RemoteEvent salvo que necesites de verdad la respuesta.
¿Cuántos RemoteEvents debo tener?
Uno por acción con sentido propio: abrir cofre, comprar en la tienda, usar habilidad. Ni uno para todo con un parámetro que dice qué hacer (imposible de validar), ni cuarenta para variantes de lo mismo.
¿Puede un jugador ver mis RemoteEvents?
Sí. Todo lo que hay en ReplicatedStorage es visible desde el cliente, nombres incluidos. Por eso la seguridad nunca puede estar en esconderlos: está en que el servidor valide.
¿Y si solo quiero un efecto visual, hace falta RemoteEvent?
Si es puramente visual y solo para ese jugador, no: hazlo en el LocalScript y ya. El RemoteEvent hace falta cuando algo tiene que ser real para todos.
Última revisión: 1 de septiembre de 2026




Deja una respuesta