A server loop that pays every player every second — your first passive mechanic and a classic idle-game heart.
Idle games — where coins pile up even when you do nothing — run on one simple idea: a server loop that pays everyone on a timer. It's the same clicker loop, minus the click.
local Players = game:GetService("Players")
task.spawn(function()
while true do
task.wait(1)
for _, player in ipairs(Players:GetPlayers()) do
local coins = player.leaderstats.Coins
if coins then
coins.Value = coins.Value + 1
clickEvent:FireClient(player, coins.Value)
end
end
end
end)
Players:GetPlayers() — a table of everyone in the server.task.spawn keeps the loop running alongside the rest of the script.if coins then guard skips players whose stats aren't set up yet.FireClient the clicks use — the label updates however coins arrive.What does Players:GetPlayers() return?
A player joins and PlayerAdded runs — but until their leaderstats folder is created, they have no Coins. The loop races that setup:
if coins then
coins.Value = coins.Value + 1
end
Without the guard, nil.Value errors and the whole loop dies on the first player without stats. The guard is what makes the loop safe to run from the moment the game starts.
Before adding idle coins to a player, the loop checks the player has a Coins value with: if then
Clicks, idle ticks, purchases — they all change coins, and they all push the update the same way:
clickEvent:FireClient(player, coins.Value)
The client's OnClientEvent doesn't care why the total changed. It just reflects the new number. That's the design payoff: one receive handler, many sources of income.
Inside the idle loop, after adding 1 coin, push the new total to the player's screen. Write the fire line.
-- inside the for loop, after coins.Value + 1
if coins then prevents the crash.while true with no task.wait is a freeze and a coins fountain at once.Next: spending those coins — the upgrades shop.