Issue with Money Value Resetting Unexpectedly in Server Script

Problem Description

I’m working on a server script that deducts 5 Money from a player’s leaderstats when they interact with a button. The script works, but I’m running into a strange issue:

  1. Players start with 10,000 Money.
  2. If I manually change their Money (e.g., to 10,001) and then trigger my script, their Money doesn’t decrease correctly. player.leaderstats.Money.Value -= 5 - line 17
  3. Instead of subtracting 5 from the current value, the script seems to set it to 9,995 (starting value 10,000 - 5).

This suggests that Money.Value is being reset to the original starting amount somewhere before the subtraction happens, but I can’t figure out where.


Here is a video of the problem

Pcloud link ~ 30s


Script I’m Using (Server Script)

factory = script.Parent.Parent.Parent
local ting = 0

function onTouched(hit)
	if ting == 0 then
	ting = 1

	local owner = factory.OwnerName.Value
	local check = hit.Parent:FindFirstChild("Humanoid")
	local player = game.Players:FindFirstChild(owner)
	if check ~= nil then
		local user = game.Players:GetPlayerFromCharacter(hit.Parent) 
		if owner == user.Name then
			local stats = user:findFirstChild("leaderstats")
			if stats ~= nil then
					if player.leaderstats.Money.Value >= 5 then
					player.leaderstats.Money.Value  -= 5
					local machine = factory.Stuff.Friend1.Value:Clone()
					machine.Parent = factory
					machine:MakeJoints()
					local button = script.Parent.Parent
					button.Head.Transparency = 1
					button.Head.Glow:Destroy()
					button.Head.Union:Destroy()
					button.Head.BillboardGui:Destroy()
					button.Head.BillboardGui2:Destroy()
					while true do
						local player = game.Players:FindFirstChild(owner)
						local loadingBar = button.Parent.Friend1.Rig.PartUI.BillboardGui.CanvasGroup.Progress
						local Timer = button.Parent.Friend1.Rig.PartUI.BillboardGui2
						local timersize = 10
						local barsize = loadingBar.Size.X.Scale
						barsize = 0.001
						loadingBar.Size = UDim2.new(barsize, loadingBar.Size.X.Offset, loadingBar.Size.Y.Scale, loadingBar.Size.Y.Offset)
						Timer.Text.Text = 10 .."s"
							for i = 1, 100, 1 do
								Timer.Text.Text = math.round(timersize * 10)/10 .. "s"
								barsize += 0.01
								loadingBar.Size = UDim2.new(barsize, loadingBar.Size.X.Offset, loadingBar.Size.Y.Scale, loadingBar.Size.Y.Offset)
								timersize -= 0.1
								wait(0.1)
							end
						Timer.Text.Text = "0s"
						player.leaderstats.Money.Value += 1
						wait(0.3)
					end
				end
			end
		end

	end
	ting = 0
	end
end

script.Parent.Touched:connect(onTouched)

If you know what is happening let me know because I’m trying to fix this from a long time. Thanks a lot!

1 Like

You are manually substracting the value from the client since you are on the client testing mode, but the script does the calculations from the server so it’s like you didn’t change it in the first place.

If you want to manually substract the values from the server, please look into this guide on testing modes. Studio testing modes | Documentation - Roblox Creator Hub

1 Like

You have to change the value while testing in the server, here’s what that would look like:
image

1 Like

But when I’m changing the value from a local script game.Players.LocalPlayer:WaitForChild('leaderstats'):WaitForChild('Money').Value += 1 it’s happening the same issue. So where is the problem, in the local script or in the server script and how to change the one that is not right?

1 Like

The client is not able to interfere with the server. If you change a value for the client, it will ONLY work on that client, meaning that any server code will not see the changes you made and it will act like nothing changed (as you pointed out, " 1. Instead of subtracting 5 from the current value, the script seems to set it to 9,995 (starting value 10,000 - 5).").

2 Likes

Oh so I could fix that with a remote event right?

1 Like

That is incredibly insecure, just substract the value directly with a server script

1 Like

I have 2 questions: First why it’s insecure to use a remote event and second how else I could do it since the other script needs to be a local?

1 Like
  1. The client shouldn’t directly interfere with the server in the way you are proposing because it opens the door for exploiters to abuse the remotes you set up. Let’s say, for example, you make a remote called AddMoney that adds money to the leaderboard on the server. A malicious client (exploiter) could abuse that remote by firing it themselves and give themselves infinite money.
  2. Why does it need to be a localscript? What are you trying to do? For testing if you can subscract money, doing it from a server script will do the trick.
2 Likes

It needs to be a local because it’s an gui script and It will be harder to change this script instead of using remote events. So is there any good solutions of this?

1 Like

Why are you changing the Player’s money from the GUI? You should be doing that from the server in another place like ServerScriptService.

If you want to then update the new money value on a GUI, you can do something like this:

player.leaderstats.Money.Changed:Connect(function()
      -- Update gui with the money value
end)

Please look into doing it from the server.

3 Likes

alright but here is my function: (these are the local scripts in gui)

Button.MouseButton1Click:Connect(function()
	if CorrectCheck.Value == "The Answer" then
		game.Players.LocalPlayer:WaitForChild('leaderstats'):WaitForChild('Money').Value += 1
		Button.Interactable = false
		Button2.Interactable = false
		Button3.Interactable = false
		Button4.Interactable = false
		task.wait(1)
		RemoteEvent:FireServer()
		Button.Interactable = true
		Button2.Interactable = true
		Button3.Interactable = true
		Button4.Interactable = true
		
		
		
	elseif CorrectCheck.Value == "Not The Answer" then
		print("WOMP WOMP")
		Button.Interactable = false
		Button2.Interactable = false
		Button3.Interactable = false
		Button4.Interactable = false
		task.wait(1)
		RemoteEvent:FireServer()
		Button.Interactable = true
		Button2.Interactable = true
		Button3.Interactable = true
		Button4.Interactable = true
	else 
		print("Something went wrong")
	end
	
end)

And it’s on 4 buttons:
image

And one main controls everything: (I know it’s not written in the best way but I’m still learning now so it’s easier to understand the script that way)

image

local CalculateButton = script.Parent
local ExitButton = CalculateButton.Parent.Exit
local UpgradeButton = CalculateButton.Parent.Upgrade
local BackButton = UpgradeButton.Parent.Back
local folder = CalculateButton.Parent.Folder
local RemoteEvent = game:GetService('ReplicatedStorage'):WaitForChild('ResetTheProblem')
local n = 15

folder.Answer1.Visible = false
folder.Answer2.Visible = false
folder.Answer3.Visible = false
folder.Answer4.Visible = false
folder.ModeEasy.Visible = false
folder.Question.Visible = false

game.ReplicatedStorage.CameraEvent.OnClientEvent:Connect(function()
	CalculateButton.Visible = true
	UpgradeButton.Visible = true
	ExitButton.Visible = true
end)

local function generateQuestion()
	CalculateButton.Visible = false
	UpgradeButton.Visible = false
	ExitButton.Visible = false
	BackButton.Visible = true

	-- setting vis on:
	folder.Answer1.Visible = true
	folder.Answer2.Visible = true
	folder.Answer3.Visible = true
	folder.Answer4.Visible = true
	folder.ModeEasy.Visible = true
	folder.Question.Visible = true
	---

	--IF MODE EASY IS ON:
	n = math.random(15)
	if n == 1 then
		folder.Question.Text = "8 + 5 ="
		folder.Answer1.Text = "13" ----
		folder.Answer2.Text = "11"
		folder.Answer3.Text = "14"
		folder.Answer4.Text = "12"
		folder.Answer1.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"

	elseif n == 2 then
		folder.Question.Text = "6 + 7 ="
		folder.Answer1.Text = "12"
		folder.Answer2.Text = "13" ----
		folder.Answer3.Text = "14"
		folder.Answer4.Text = "15"
		folder.Answer1.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"

	elseif n == 3 then
		folder.Question.Text = "9 - 4 ="
		folder.Answer1.Text = "6"
		folder.Answer2.Text = "5" ----
		folder.Answer3.Text = "7"
		folder.Answer4.Text = "4"
		folder.Answer1.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"

	elseif n == 4 then
		folder.Question.Text = "12 - 8 ="
		folder.Answer1.Text = "4" ----
		folder.Answer2.Text = "5"
		folder.Answer3.Text = "6"
		folder.Answer4.Text = "3"
		folder.Answer1.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"

	elseif n == 5 then
		folder.Question.Text = "7 + 8 ="
		folder.Answer1.Text = "15" ----
		folder.Answer2.Text = "13"
		folder.Answer3.Text = "11"
		folder.Answer4.Text = "12"
		folder.Answer1.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"

	elseif n == 6 then
		folder.Question.Text = "10 - 6 ="
		folder.Answer1.Text = "5"
		folder.Answer2.Text = "4" ----
		folder.Answer3.Text = "3"
		folder.Answer4.Text = "6"
		folder.Answer1.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"

	elseif n == 7 then
		folder.Question.Text = "4 + 9 ="
		folder.Answer1.Text = "13" ----
		folder.Answer2.Text = "12"
		folder.Answer3.Text = "11"
		folder.Answer4.Text = "14"
		folder.Answer1.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"

	elseif n == 8 then
		folder.Question.Text = "16 - 7 ="
		folder.Answer1.Text = "9" ----
		folder.Answer2.Text = "8"
		folder.Answer3.Text = "7"
		folder.Answer4.Text = "10"
		folder.Answer1.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"

	elseif n == 9 then
		folder.Question.Text = "3 + 16 ="
		folder.Answer1.Text = "20"
		folder.Answer2.Text = "18"
		folder.Answer3.Text = "19" ----
		folder.Answer4.Text = "17"
		folder.Answer1.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"

	elseif n == 10 then
		folder.Question.Text = "14 - 9 ="
		folder.Answer1.Text = "6"
		folder.Answer2.Text = "4"
		folder.Answer3.Text = "5" ----
		folder.Answer4.Text = "7"
		folder.Answer1.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"

	elseif n == 11 then
		folder.Question.Text = "11 - 8 ="
		folder.Answer1.Text = "2"
		folder.Answer2.Text = "4"
		folder.Answer3.Text = "3" ----
		folder.Answer4.Text = "5"
		folder.Answer1.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"

	elseif n == 12 then
		folder.Question.Text = "5 + 13 ="
		folder.Answer1.Text = "20"
		folder.Answer2.Text = "18" ----
		folder.Answer3.Text = "19"
		folder.Answer4.Text = "17"
		folder.Answer1.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"

	elseif n == 13 then
		folder.Question.Text = "14 - 6 ="
		folder.Answer1.Text = "8" ----
		folder.Answer2.Text = "6"
		folder.Answer3.Text = "7"
		folder.Answer4.Text = "5"
		folder.Answer1.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"

	elseif n == 14 then
		folder.Question.Text = "2 + 9 ="
		folder.Answer1.Text = "11" ----
		folder.Answer2.Text = "12"
		folder.Answer3.Text = "10"
		folder.Answer4.Text = "9"
		folder.Answer1.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"

	elseif n == 15 then
		folder.Question.Text = "19 - 13 ="
		folder.Answer1.Text = "7"
		folder.Answer2.Text = "8"
		folder.Answer3.Text = "6" ----
		folder.Answer4.Text = "5"
		folder.Answer1.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer2.WhatIsTheAnswer.Value = "Not The Answer"
		folder.Answer3.WhatIsTheAnswer.Value = "The Answer"
		folder.Answer4.WhatIsTheAnswer.Value = "Not The Answer"
	end
	


end

CalculateButton.MouseButton1Click:Connect(function()
	n = math.random()
	generateQuestion()
	n = math.random()
end)

RemoteEvent.OnClientEvent:Connect(function()
	n = math.random()
	generateQuestion()
	n = math.random()

end)

btw I forgot to tell you that I’m using a server script for the communication between the 2 local script:
server script:

local remoteEvent = game.ReplicatedStorage:WaitForChild("ResetTheProblem")

remoteEvent.OnServerEvent:Connect(function(player)
	-- Relay the event back to the client
	remoteEvent:FireClient(player)
end)

When it comes to a client communicating with the server, the most common way of doing this is through remote events. However, exploiters are also able to fire remote events themselves, which can lead to some exploits vulnerabilities. Here’s an example:

Say you have a UI button, and each time you click on it you’ll get +1 cash multiplied by how long you’ve been playing the game in seconds, a developer would first implement this mechanic by using remote events this way:
Local script:

local seconds = 0
local button = script.Parent
local remote = game:GetService("ReplicatedStorage"):WaitForChild("Click")
button.MouseButton1Up:Connect(function()
         remote:FireServer(seconds)
end)
while task.wait(1) do
      seconds += 1
end

Script:

local remote = game:GetService("ReplicatedStorage"):FindFirstChild("Click")
remote.OnServerEvent:Connect(function(plr,amount)
       plr.leaderstats.Money.Value += amount
end)

This looks fine at first, however the developer would shortly realise that the exploiter is capable of firing any remote events they want with any parameter, this means they are able to give themselves infinite money by simply firing the remote event with infinity as the parameter, like this:
> game.ReplicatedStorage.Click:FireServer(math.huge)
So to prevent this, the server would have to stop trusting the client. This means that the developer will have to install server-side checks and log the player’s playtime through the server-side script. This means the server script would have to look like this:

local remote = game:GetService("ReplicatedStorage"):FindFirstChild("Click")
local playTimeforPlayers = {}
remote.OnServerEvent:Connect(function(plr)
       local playtime
       --lines of script that would retrieve the player's play time
       plr.leaderstats.Money.Value += playtime
end)

Meanwhile the local script would simply fire the remote event with no arguments.

button.MouseButton1Up:Connect(function()
         remote:FireServer()
end)

So in the end, you should never trust the client as a server when it comes to dealing with remote events

1 Like

Thanks, I didn’t know that, I really appreciate it. So I have a few questions. First can exploiters call any remote event no matter if it’s called from local of server script. Because if I can use any remote event that will be save I could tell the server script when the money value is updated. If that’s not possible how I could fix all of the local scripts.

With the help of @Ransomwavee and @zaydoudou I did ended up fixing this. Thank you a lot for the help!

2 Likes

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