Help needed with exploitable script!

So i made these scripts for a wheel that you can spin for rewards but i just realized this is very exploitable! Didn’t really think about that when scripting, however now I don’t really know how i can make it non-exploitable. if you need more info feel free to ask.

This is a part of my client script, here i handle how many spins you have and the free spin button [in startergui]:

local Spins = 0
--Rest of the variables


local HandleSpins = coroutine.wrap(function()
	while task.wait() do
		if Spins >= 1 then
			HasSpinsFrame_Button.Visible = true
			HasSpinsLabel_Button.Text = tostring(Spins)
			HasSpinsFrame_Main.Visible = true
			HasSpinsLabel_Main.Text = tostring(Spins)
			FreeSpinButtonTextLabel.Text = "Spin!"
			task.wait(1)
		else
			HasSpinsFrame_Button.Visible = false
			HasSpinsFrame_Main.Visible = false
			for i = AddSpinDuration, 0, -1 do
				if Spins >= 1 then 
					break
				end
				local minutes = math.floor(i / 60)
				local seconds = i % 60
				FreeSpinButtonTextLabel.Text = string.format("Free spin in: %0d:%02d", minutes, seconds)
				task.wait(1)
				if i == 0 then
					Spins = Spins + 1
				end
			end
			
		end
	end
end)

local AddSpins = coroutine.wrap(function()
	while task.wait() do
		if Spins >= 1 then
			for i = AddSpinDuration, 0, -1 do
				wait(1)

				if Spins == 0 then
					break
				end

				if i == 0 then
					Spins = Spins + 1
				end
			end

		end
	end
end)

HandleSpins()
AddSpins()

FreeSpinButton.MouseButton1Click:Connect(function()
	if Spins > 0 and not IsTweening then
		IsTweening = true
		Spins = Spins - 1

		local Reward = SpinWheelModule.CalculateChance()

		local SpinWheelTween = TweenService:Create(FortuneWheel, SpinTweenInfo, {Rotation = FakeRotation + Reward.Degrees})
		SpinWheelTween:Play()
		FortuneWheel:WaitForChild("WheelSpin"):Play()
		SpinWheelTween.Completed:Wait()
		
		local ReturnedType, Val = RemoteFunction:InvokeServer(Reward, "FreeSpin")
local RevertSpinWheelTween = TweenService:Create(FortuneWheel, SpinBackTweenInfo, {Rotation = 0})
		RevertSpinWheelTween:Play()
		RevertSpinWheelTween.Completed:Wait()
		IsTweening = false

This is the serverscript [in serverscriptservice]:

game.ReplicatedStorage.SpinWheel.SW_RemoteFunction.OnServerInvoke = function(plr, reward, Button)
	if Button == "FreeSpin" then
		local leaderstats = plr:WaitForChild("leaderstats")
		local Size = leaderstats:WaitForChild("Size")
		local Skulls = plr:WaitForChild("Skulls")

		if reward.Type == "+3K Size" then
			Size.Value = Size.Value + 3000
			return reward.Type
		elseif reward.Type == "+11 Skulls" then
			Skulls.Value = Skulls.Value + 11
			return reward.Type
		elseif reward.Type == "+1 Spin" then
			return reward.Type, reward.Value
		elseif reward.Type == "+2X Size" then
			Size.Value = Size.Value * 2
			return reward.Type
		elseif reward.Type == "+24 Skulls" then
			Skulls.Value = Skulls.Value + 24
			return reward.Type
		elseif reward.Type == "+7K Size" then
			Size.Value = Size.Value + 7000
			return reward.Type
		elseif reward.Type == "Nothing" then
			return "Nothing"
		elseif reward.Type == "+2 Spin" then
			return reward.Type, reward.Value
		elseif reward.Type == "2X Size" then
			Size.Value = Size.Value * 2
			return reward.Type
		else
			warn("Unknown reward type:", reward.Type)
		end
    end
end

and this is the modulescript [in replicatedstorage]:

local SW_MODULE = {
	["+3K Size"] = {Type = "+3K Size", Value = 3000, Degrees = 0},
	["+11 Skulls"] = {Type = "+11 Skulls", Value = 11, Degrees = 30}, 
	["+1 Spin"] = {Type = "+1 Spin", Value = 1, Degrees = 75}, 
	["+2X Size"] = {Type = "2X Size", Value = 2, Degrees = 120}, 
	["+24 Skulls"] = {Type = "+24 Skulls", Value = 24, Degrees = 165},
	["+7K Size"] = {Type = "+7K Size", Value = 7000, Degrees = 210}, 
	["Nothing"] = {Type = "Nothing", Value = 0, Degrees = 255},
	["+2 Spin"] = {Type = "+2 Spin", Value = 2, Degrees = 300}, 
}	

function SW_MODULE.CalculateChance()
	local RandomNumber = math.random(1, 100)
	
	if RandomNumber > 0 and RandomNumber <= 20 then
		return SW_MODULE["+3K Size"]
	elseif RandomNumber > 20 and RandomNumber <= 35 then
		return SW_MODULE["+11 Skulls"]
	elseif RandomNumber > 35 and RandomNumber <= 46 then
		return SW_MODULE["+1 Spin"]
	elseif RandomNumber > 46 and RandomNumber <= 50 then
		return SW_MODULE["+2X Size"]
	elseif RandomNumber > 50 and RandomNumber <= 66 then
		return SW_MODULE["+24 Skulls"]
	elseif RandomNumber > 66 and RandomNumber <= 77 then
		return SW_MODULE["+7K Size"]
	elseif RandomNumber > 77 and RandomNumber <= 90 then
		return SW_MODULE.Nothing
	elseif RandomNumber > 90 and RandomNumber <= 100 then
		return SW_MODULE["+2 Spin"]
	end
end

return SW_MODULE

From what I can see there are at least two big issues:

  1. The client can simply fire your remote with whatever information they want to pass in (so they could choose the reward).
  2. You’re checking for the number of spins the player has on the client, which could be messed with.

A solution I can think of is whenever the player tries to spins, it invokes a remote function (no parameters) and the server does the following:

  1. Check if the player has enough spins. If they don’t, return “nil” or something.
  2. Spin (random number), updates leaderstats depending on the result from the spin, and returns the value so that the client can see what the result was.

By doing this, even if an exploiter tried to spam your remote, they would not be able to spin past whatever amount they are allowed to spin. Also, they wouldn’t be able to choose what rewards they want.

2 Likes

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