Prevent exploitation of a timer

I went back to my older code that utilizes remote events and realized they are susceptible to exploitation so I fixed them up and they are fine now, except this script
This script is a timer, when the timer reaches 0 and the player presses a button they get some money, allowance if you will, but I have absolutely no idea how I could make the server check if the timer actually reached 0

Local timer script
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local getFunds = ReplicatedStorage:WaitForChild("Allowance")
local button = script.Parent:FindFirstChild("Claim")
local alert = script.Parent:WaitForChild("Alert")
local p = script.Parent:WaitForChild("purchase")
local canClaim = false

local timer = script.Parent.Timer
local minutes = 10
local seconds = 0

local function startimer()
	repeat
		if seconds <= 0 then
			minutes = minutes - 1
			seconds = 59
		else
			seconds = seconds - 1
		end
		if seconds <= 9  then
			timer.Text = tostring(minutes)..":0"..tostring(seconds)
		else
			timer.Text = tostring(minutes)..":"..tostring(seconds)
		end
	task.wait(1)
	until minutes <= 0 and seconds <= 0 do
		button.TextColor3 = Color3.fromRGB(195, 151, 46)
		alert:Play()
		game:GetService("StarterGui"):SetCore("SendNotification",{
			Title = "FUNDS ALERT",
			Text = "Your funds are ready to claim in the [EXCLUSIVE SHOP]",
			Duration = 15
		})
		canClaim = true
	end
end

startimer()

button.MouseButton1Click:Connect(function()
	if canClaim then
		getFunds:FireServer()
		button.TextColor3 = Color3.fromRGB(0, 0, 0)
		game:GetService("StarterGui"):SetCore("SendNotification",{
			Title = "FUNDS ALERT",
			Text = "Your funds have been successfully claimed at the [EXCLUSIVE SHOP], thank you for playing",
			Duration = 7
		})
		p:Play()
		canClaim = false
		minutes = 10
		seconds = 0
		startimer()
	end
end)

local player = game.Players.LocalPlayer

player.Character.Humanoid.Died:Connect(function()
	script.Parent.Parent.Visible = false
end)
Server script
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local getloan = ReplicatedStorage:WaitForChild("Allowance")

getloan.OnServerEvent:Connect(function(player)
	local leaderstats = player.leaderstats
	if leaderstats then
		if player.MembershipType == Enum.MembershipType.Premium then
			leaderstats.Money.Value = leaderstats.Money.Value + 3200
		--	print("Premium user")
		else 
			leaderstats.Money.Value = leaderstats.Money.Value + 2500
		--	print("Non Premium user")
		end
	end	
end)

If I am correct on how exploits work, this is (probably) the worst way I could have written this
Any suggestions/solutions on what I should change here?

Run the timer on the server and then signal the client they can use their button. When they do, confirm this was allowed on the server again and then give the reward.

2 Likes

thats a good idea but the client could just ignore that
I would:

  1. Send an event to the server that the button was clicked and the server stores the points in a variable and adds one

  2. the server now could recieve a new message from the client but
    now we outsmart the exploiter because the server also started an timer when the
    event reached the server

  3. the server would only give the players points when the time has passed

  4. when the signal now gone through and made an new point the server will start everything all over again

I’m not sure how to keep the local timer and the server timer in sync though

I’m not quite sure what you are saying here…

They don’t really need to be all that in sync. Any timer you may have on the client is just eye candy to make it look nice and give the player some idea (give or take a few short moments) as to when they can collect their allowance or w/e you giving them. The client timer doesn’t effect the game mechanic, its purely visual. The mechanic itself should be totally handled by the server, and the server simply signals the UI to tell the player the time interval is up. When the player clicks the button they are requesting the server allow them to collect. The server checks the interval and ensures they can and gives the reward. So you can start it off by having the server send the time remaining until next collection to the client and then run a timer on the client based on that time. Even if its off by a few milliseconds or seconds the player is virtually unaffected by this delay.

Here is a quick and dirty example

– Server

local collection_interval = 5 -- secs
local collection_timer = nil
local collectionEvent = game:GetService("ReplicatedStorage").CollectionEvent


collectionEvent.OnServerEvent:Connect(function(player, msg)
	if(msg == "StartTimerRequest")then
		if(not(collection_timer))then
			collection_timer = tick()+collection_interval
		end
	elseif(msg == "CollectionRequest")then
		if(not(collection_timer))then
			
			--Give them the money....
			
			collection_timer = tick()+collection_interval --Reset the timer
			collectionEvent:FireClient(<somePlayer>, "StartTimer", collection_interval)--Tell the client
		end
	end
end)

game:GetService("RunService").Heartbeat:Connect(function()
	if(collection_timer and collection_timer <= tick())then
		--TIMES UP!!
		collection_timer = nil --Clear the timer
		collectionEvent:FireClient(<somePlayer>, "CollectionReady")--Tell client its ready
	end
end)

–Client

collectionEvent.OnClientEvent:Connect(function(msg, interval)
	if(msg == "StartTimer")then
		--Start a timer using the interval given
		--or you could of passed the timeRemaining
	elseif(msg == "CollectionReady")then
		--Run some code to enable the UI button they can press to make collection request
	else
		--Unhandled msg
	end
end)

<someButton>.Activated:Connect(function()
	collectionEvent:FireServer("CollectionRequest")
end)

--Request the server start the timer
collectionEvent:FireServer("StartTimerRequest")
1 Like

I mean that the server is the one who has the timer so its not editable by the client
and the server doesnt let requests go through before the timer ends , so the client timer wouldt sync.
but if you want it sync you can have that the client asks the server if the time is already over and else it would wait one more sec.

It’s hard to understand i know but i think when you understand it it could make sence
but I could be wrong too so who knows

I guess you’re right, thanks for the assistance

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.