Subtracting an Int value with a number that is the same value of the int is always equals to a negative number

I made 2 currencies in my game. One is called “Coins” and the other is called “Gems”. I’ve also made a system where you can convert your Coins to Gems. When the player finishes converting their Coins to Gems it would subtract the value of their Coins to how much they’ve converted. Here’s when the problem started. If you convert all your Coins to Gems then your Coins will most likely be a negative number instead of 0. Here’s a footage of me testing the system where you can convert your Coins to Gems.


I converted all my Coins which is 10,000,000,000 and after I converted all my Coins it turned into -9,999,997,614 which doesn’t make any sense at all because it’s supposed to be 0 because the value of my Coins is 10,000,000,000 and I converted all. The value of My Gems did get increased but still my Coins is a negative number after I converted all my Coins even though its supposed to be 0. Here’s all the scripts that are related to the system where you convert your Coins to Gems

Script that opens the Gui so the player can use the system:

local UIS = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")

UIS.InputEnded:Connect(function(Input)
	if Input.UserInputType == Enum.UserInputType.Keyboard or Input.UserInputType == Enum.UserInputType.Gamepad1 then
		if Input.KeyCode == Enum.KeyCode.E or Input.KeyCode == Enum.KeyCode.ButtonY then
			if UIS:GetFocusedTextBox() == nil then
				local Distance = (workspace.Lands.Forest.GemMachine.PrimaryPart.Position-game.Players.LocalPlayer.Character:WaitForChild("HumanoidRootPart").Position).Magnitude
				if Distance <= 10 then
					if script.Parent.Parent.Parent.Parent.GemConverter.Open.Value == false then
						local Module = require(script.Parent.Parent.Parent.Parent.MainModule)
						Module:RequestGemConvert()
					end
				end
			end
		end
	end
end)

script.Parent.Display.MouseButton1Click:Connect(function()
	if script.Parent.Parent.Parent.Parent.GemConverter.Open.Value == false then
		local Module = require(script.Parent.Parent.Parent.Parent.MainModule)
		Module:RequestGemConvert()
	end
end)

while wait() do
	if script.Parent.Size == UDim2.new(2,0,2,0) then TweenService:Create(script.Parent,TweenInfo.new(0.3,Enum.EasingStyle.Back,Enum.EasingDirection.In),{Size = UDim2.new(0,0,0,0)}):Play() end
	local Distance = (workspace.Lands.Forest.GemMachine.PrimaryPart.Position-game.Players.LocalPlayer.Character:WaitForChild("HumanoidRootPart").Position).Magnitude
	if Distance <= 10 then
		TweenService:Create(script.Parent,TweenInfo.new(0.3,Enum.EasingStyle.Back,Enum.EasingDirection.Out),{Size = UDim2.new(2,0,2,0)}):Play()
	end
end

The ModuleScript that makes the system function or the main script of the system

function module:RequestGemConvert()
	module:CloseAllGui("GemConverter")
	local Coins = game.ReplicatedStorage.ConvertGems:InvokeServer(0,false)
	script.Parent.GemConverter.Open.Value = true
	script.Parent.GemConverter.Background.Dragger.MaximumNumber.Value = Coins
	
	script.Parent.GemConverter.Background:TweenPosition(UDim2.new(0.5,0,0.5,0),"Out","Quint",0.5,true)
	
	script.Parent.GemConverter.Background.Convert.MouseButton1Click:Connect(function()
		if script.Parent.GemConverter.Background.Dragger.CurrentNumber.Value < 5 then
			module:SendNotification("Error!","You only gave the machine a little amount of Coins! You need to spend 5 Coins or more in this machine to get any amount of Gems!")
		else
			game.ReplicatedStorage.ConvertGems:InvokeServer(script.Parent.GemConverter.Background.Dragger.CurrentNumber.Value,true)
			script.Parent.GemConverter.Background:TweenPosition(UDim2.new(0.5,0,1.5,0),"In","Quint",0.5,true)
			script.Parent.GemConverter.Open.Value = false
			script.Parent.GemConverter.Background.Dragger.MaximumNumber.Value = 0
			script.Parent.GemConverter.Background.Dragger.CurrentNumber.Value = 0
			script.Parent.GemConverter.Background.Dragger.Bar.SliderObject.Position = UDim2.new(0,0,0.5,-10)
		end
	end)
	
	script.Parent.GemConverter.Background.Exit.MouseButton1Click:Connect(function()
		script.Parent.GemConverter.Open.Value = false
		script.Parent.GemConverter.Background:TweenPosition(UDim2.new(0.5,0,1.5,0),"In","Quint",0.5,true)
		script.Parent.GemConverter.Background.Dragger.MaximumNumber.Value = 0
		script.Parent.GemConverter.Background.Dragger.CurrentNumber.Value = 0
		script.Parent.GemConverter.Background.Dragger.Bar.SliderObject.Position = UDim2.new(0,0,0.5,-10)
	end)
	
	script.Parent.GemConverter.Background.Coins.TextLabel.Text = module:RequestPlaceValue(script.Parent.GemConverter.Background.Dragger.CurrentNumber.Value)
	script.Parent.GemConverter.Background.Gems.TextLabel.Text = module:RequestPlaceValue(math.floor(script.Parent.GemConverter.Background.Dragger.CurrentNumber.Value / 5))
	script.Parent.GemConverter.Background.Dragger.MaximumNumber.Value = Coins
	
	
	script.Parent.GemConverter.Background.Dragger.CurrentNumber.Changed:Connect(function()
		script.Parent.GemConverter.Background.Coins.TextLabel.Text = module:RequestPlaceValue(script.Parent.GemConverter.Background.Dragger.CurrentNumber.Value)
		script.Parent.GemConverter.Background.Gems.TextLabel.Text = module:RequestPlaceValue(math.floor(script.Parent.GemConverter.Background.Dragger.CurrentNumber.Value / 5))
	end)	
end

Server Script that increases the Gems of the player and subtracts the Coins of the player when the player finishes converting their Coins to Gems

game.ReplicatedStorage.ConvertGems.OnServerInvoke = function(Player,Coins,Convert)
	if Convert then
		local Module = require(Player:WaitForChild("PlayerGui").MainModule)
		Module:SendNotification("Ahoy!","Subtracting your Coins by the same value ("..Player.Leaderstats.Coins.Value.." - "..Player.Leaderstats.Coins.Value..")")
		wait(3)
		Player:WaitForChild("Leaderstats").Gems.Value = Player:WaitForChild("Leaderstats").Gems.Value + math.floor(Coins / 5)
		Player:WaitForChild("Leaderstats").Coins.Value = Player:WaitForChild("Leaderstats").Coins.Value - Coins
		Module:SendNotification("Awesome!","Your Coins have been subtracted by the same value!")
	else
		return Player:WaitForChild("Leaderstats").Coins.Value
	end
end

The Module script and the script that opens the gui so the player can use the system are both descendants of PlayerGui while the server Script that increases the Gems of the player and subtracts the Coins of the player when the player finishes converting their Coins to Gems is parented to ServerScriptService

Hello. Your server script is somehow doing Coins.Value - Coins - Coins instead of Coins.Value - Coins. Do the math and you’ll see that it happens with the gems too (it can’t be a floating point error anyway). I don’t know what the error could be, but I’d try adding a debounce in this function of the module script:

script.Parent.GemConverter.Background.Convert.MouseButton1Click:Connect(function()

Maybe it is fired twice?

It still turns negative even with debounce

are you requiring the module through a local script?

Why not just do math.abs(the num here) or whatever returns the number.

Yeah. And the module script will invoke the RemoteFunction

1.
Your server-side code does not verify the argument Coins against how many actual coins the player has. - So it is very open to exploit.

If an exploiter uses this, he will get more coins (and loose some Gems):

game.ReplicatedStorage.ConvertGems:InvokeServer(-9999999, true) -- Negative amount of 'coins'

So you should at least verify, server-side, that the values coming from the client, are “correct”.

2.
Another issue is your excessive usage of :WaitForChild.

You should use local variables, to store the return value from :WaitForChild, when needing to use it multiple times.

Something like (this code also contains a sanity check of the argument Coins’s value):

game.ReplicatedStorage.ConvertGems.OnServerInvoke = function(Player,Coins,Convert)
	local coinsObj = Player:WaitForChild("Leaderstats").Coins
	if Convert then
		-- Ensure that the Coins value from client, is positive and not greater than the player's actual amount of coins
		if Coins > 0 and Coins <= coinsObj.Value then
			local Module = require(Player:WaitForChild("PlayerGui").MainModule)
			Module:SendNotification("Ahoy!","Subtracting your Coins by the same value ("..coinsObj.Value.." - "..Coins..")")

			wait(3) -- Why the wait? Why do you need to wait 3 seconds here?
			        -- During these 3 seconds, the coinsObj.Value could be changed (reduced even)
			        -- which will make the sanity check be futile.

			local gemsObj = Player:WaitForChild("Leaderstats").Gems -- Again, only one call to :WaitForChild()
			gemsObj.Value = gemsObj.Value + math.floor(Coins / 5)
			coinsObj.Value = coinsObj.Value - Coins
			Module:SendNotification("Awesome!","Your Coins have been subtracted by the same value!")
		else
			-- Someone is not playing by the rules...
		end
	else
		return coinsObj.Value
	end
end
1 Like

Thank you so much! Not only that the bug is gone but it also can’t be exploited! (bc its the only feature in my game that can be easily exploited)

1 Like