Hi there, I was trying to make a coin collection system on the client, so that each player has his own coin to collect without having other players stealing… My question is, how can you prevent exploiters from just collecting all the coins via script?
My initial idea was: Spawning the coin in a LocalScript, detecting the .Touched event in a LocalScript, and from there sending an event to the server for it to change money values and stuff like that, but using this method, can’t exploiters just open their booboo program and execute “collectEvent:FireServer(coin)”?
you’re right, exploiters can and will do that, a solution that some games use and probably the best solution is to fire a event from the server to the client to spawn in a coin and pass a UUID with said coin, if a player touches that coin then the client will fire a even saying they touched the UUID linked with the coin, and the server will check if the UUID sent by the player is valid.
I explained it kinda poorly but hopefully you get the idea
game.Players.PlayerAdded:Connect(function(plr)
while task.wait(.1) do
spawnEvent:FireClient(plr, UUID) --UUID will be made out of random letters or numbers?
end
end)
Client:
spawnEvent.OnClientEvent:Connect(function(UUID)
--Spawn the part
part.Touched:Connect(function(hit)
if hit.Parent:FindFirstChild("Humanoid") then
collectEvent:FireServer(UUID)
end
end)
end)
But then how would you check the UUID on the server?
Thanks for your solution though!
Create the event for the coin collection and send an argument with some data, for example the player’s name, UserId, Nickname or any other type of data, for example true or "Auth"
This way, randomly firing the event by an exploit won’t work.
Client side:
local Player = game:GetService("Players").LocalPlayer
local Coin = game:GetService("Workspace").Coin
Coin.Touched:Connect(function()
game:GetService("ReplicatedStorage").CoinCollected:FireServer(Player.UserId)
end)
Server side:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
ReplicatedStorage.CoinCollected.OnServerEvent:Connect(function(Player, Auth)
if Auth == Player.UserId then
-- Code that gives you the coins
else
Player:Kick("Exploit detected") -- Player called the event without auth
end
end)
might be kinda advanced for you idk how experienced you are though but here’s a relatively good solution
-- server
local function GenerateUUID()
return game:GetService("HttpService"):GenerateGUID(false)..game:GetService("HttpService"):GenerateGUID(false)
end
game.Players.PlayerAdded:Connect(function(plr)
local conns = {}
local ValidUUIDS = {}
local spawnEvent = nil -- spawn event
table.insert(conns,spawnEvent.OnServerEvent:Connect(function(plrr,uid) -- we can also use the spawn event for getting the response
if plrr == plr then
if table.find(ValidUUIDS,uid) then
table.remove(ValidUUIDS,table.find(ValidUUIDS,uid))
-- give them their reward
plrr.Data.Coins.Value += 1
end
end
end))
while true do
task.wait(1)
if not (plr and plr.Parent) then -- plr left
for i, v in pairs(conns) do
if v then v:Disconnect() end
end
break
end
local uuid = GenerateUUID()
table.insert(ValidUUIDS,uuid)
spawnEvent:FireClient(plr, uuid)
end
end)
-- local script
spawnEvent.OnClientEvent:Connect(function(UUID)
--Spawn the part
part.Touched:Connect(function(hit)
if hit.Parent:FindFirstChild("Humanoid") and hit.Parent == game.Players.LocalPlayer.Character then
spawnEvent:FireServer(UUID)
end
end)
end)
The reason why this won’t avoid exploits is that the argument passed in the event is a static value that can be grabbed and put into the event’s arguments and fire the event, instead using a random value would be the case since the user doesn’t know the value
@i_stabman’s solution sucks. It’s literally just a remotevent obfuscator which can be bypassed by just looking at the server return and using that to spam spawnevent
the problem is about the client firing the spawn event inf times whenever they want, my solution is to fix that problem, not make a perfect system that nobody can exploit, or prevent exploiters from instantly claiming any coin that spawns, like you said, maybe read the original post first?