How to run join code for players already in the server
A script that waits before connecting PlayerAdded misses players who join during the wait. How to connect first and handle existing players too.
What it looks like
A server script waits for something before it connects Players.PlayerAdded: a data store read, a WaitForChild, a task.wait, or a require of a module that does one of those while it loads. Players who join during that wait are already in the server when the handler connects, and the script never runs its join code for them because it doesn't go through Players:GetPlayers() as well.
local season = config:GetAsync("Season") Players.PlayerAdded:Connect(setUpSeason)
What an exploiter could do
Players who join while the script waits skip whatever the join code does. Depending on that code, they keep last season's progress and can claim this season's rewards with it, skip a ban check and play while banned, never get their data loaded so nothing they earn is saved, or never get the perk they paid for. A player who notices can try to repeat it by joining servers as they start.
How to fix it
- Connect
PlayerAddedbefore anything that yields, then run the same function for every player inPlayers:GetPlayers(). - If the join code needs what the script waits for, connect first and have the handler wait for it, or start the wait before connecting and run the join code for existing players once it's done.
- Make the join code safe to run twice for one player, since a player can be picked up both ways in rare cases.
Vulnerable example
local DataStoreService = game:GetService("DataStoreService") local Players = game:GetService("Players") local PlayerData = require(script.Parent.PlayerData) local events = DataStoreService:GetDataStore("Events") local ok, current = pcall(events.GetAsync, events, "Current") local eventId = if ok and typeof(current) == "number" then current else 0 Players.PlayerAdded:Connect(function(player) local profile = PlayerData.waitFor(player) if profile and profile.Progress.EventId ~= eventId then profile.Progress.EventId = eventId profile.Progress.EventPoints = 0 end end)
Fixed example
local DataStoreService = game:GetService("DataStoreService") local Players = game:GetService("Players") local PlayerData = require(script.Parent.PlayerData) local events = DataStoreService:GetDataStore("Events") local ok, current = pcall(events.GetAsync, events, "Current") local eventId = if ok and typeof(current) == "number" then current else 0 local function startEvent(player: Player) local profile = PlayerData.waitFor(player) if profile and profile.Progress.EventId ~= eventId then profile.Progress.EventId = eventId profile.Progress.EventPoints = 0 end end Players.PlayerAdded:Connect(startEvent) for _, player in Players:GetPlayers() do task.spawn(startEvent, player) end
How to spot this in your own scripts, and the cases that only look like it, are part of the Luaudit check your AI runs.