The secure bridge between player and server, and the pattern that keeps your economy safe.
Two computers are running your game: the player's and the server's. They need to talk. The RemoteEvent is the phone line between them. You've learned why the client must ask the server — now you build the asking.
A RemoteEvent lives in a place both sides can see — ReplicatedStorage is the classic home (it exists on both computers, unlike ServerScriptService which the client never sees).
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local coinEvent = Instance.new("RemoteEvent")
coinEvent.Name = "CoinClicked"
coinEvent.Parent = ReplicatedStorage
One object, two directions of phone calls. The client calls the server with :FireServer. The server calls one client with :FireClient or every client with :FireAllClients.
Which method does the client use to send a request to the server?
-- LocalScript (on the player's computer)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local coinEvent = ReplicatedStorage.CoinClicked
coinEvent:FireServer()
That's the whole ask: "hey server, I clicked." No coins here. The client just reports the click. The power to give coins lives entirely on the server.
The method a LocalScript calls to send a message up to the server is :
-- Script (on the server)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local coinEvent = ReplicatedStorage.CoinClicked
coinEvent.OnServerEvent:Connect(function(player)
local coins = player.leaderstats.Coins
if coins then
coins.Value = coins.Value + 10
end
end)
OnServerEvent hands you the player who fired — the server never has to guess who's asking. The client reports, the server verifies (the if coins check) and decides.
The first argument to your callback is the player — and it can't be forged. Roblox derives identity from the connection itself, not from data the client sent. An exploiter can't pretend to be someone else.
What is the first argument your OnServerEvent callback receives?
The server needs to tell clients things too — "your UI should update", "the round ended".
-- to a specific player
coinEvent:FireClient(player, newCoins)
-- to everyone
coinEvent:FireAllClients("round over")
And on the client side, you receive:
-- LocalScript
coinEvent.OnClientEvent:Connect(function(message)
print("The server says: " .. message)
end)
Any values you pass after the target get delivered to the other side. This is the full bridge: client fires up, server fires down.
The server wants to tell ONE specific player their new coin total. Write the fire line to the player variable.
-- send newCoins to one player
FireServer().OnServerEvent fires → verifies → adds 10 coins.The client never once told the server "give me coins." It said "I clicked," and the server decided what that click was worth. That's the pattern to copy for every reward in your game.
Next section: putting all of this to work in a real game — Game Systems.