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:
Players start with 10,000 Money.
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
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.
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!
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.
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?
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).").
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.
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.
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?
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)
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.
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.