I have my own currency in my game which allows you to rent items such as a tool, hat, or horse. When people purchase an item, it removes the amount you spent, for example if the horse cost 250 gold, it will remove 250 gold once the horse is bought, however when you the currency adds a bit after every minute or so it goes back to the exact amount you had before you bought the item. Which means people can get items in my game for free. Does anyone know how to fix this? Any help is greatly appreciated.
Shop Script
local receiveTool = game.ReplicatedStorage:WaitForChild("ReceiveTool")
local Player = game.Players.LocalPlayer.Backpack
repeat wait() until game.Players.LocalPlayer
game.Players.LocalPlayer:WaitForChild("Silver")
function onClicked()
if game.Players.LocalPlayer.Silver.Value >= 250 then
game.Players.LocalPlayer.Silver.Value = game.Players.LocalPlayer.Silver.Value-250
receiveTool:InvokeServer("Horses", "BrownHorse")
end
end
script.Parent.MouseButton1Down:connect(onClicked)
game.Players.LocalPlayer.Silver.Changed:connect(function()
script.Parent.Text=(game.Players.LocalPlayer.Silver.Value)
end)
Server Script
local DataStore = game:GetService("DataStoreService"):GetGlobalDataStore("SILVER")
game.Players.PlayerAdded:connect(function(player)
local silver = "Silver"..player.UserId
local Silver = Instance.new("IntValue", player)
Silver.Name = "Silver"
Silver.Value = DataStore:GetAsync(silver)
Silver.Changed:connect(function(Val)
DataStore:SetAsync(silver, Val)
end)
end)
game.Players.PlayerRemoving:connect(function(player)
local silver = "Silver"..player.UserId
DataStore:SetAsync(silver, (player.Currency.Silver.Value))
end)
Your problem here is that you’re trying to change server data from the client. You’ll need to edit the server side of the ReceiveTool remote to perform the following:
Confirm that the user can afford the item
Subtract the appropriate cost
This will fix your problem and increase security (an exploiter could easily call the ReceiveTool remote and get items for free).
The server code will look something like this:
local receiveToolRemote=game.ReplicatedStorage.ReceiveTool --remote function
function invoked(player,category,name) --function to be invoked
if player.Silver.Value>=250 then --check for adequate money
player.Silver.Value=player.Silver.Value-250 --subtract the money
--TODO: give horse or thing or whatever
end
end
receiveToolRemote.OnServerInvoke=invoked --connect the function to the remote
And the client code will look something like this:
function onClicked() --function to be called on click
if game.Players.LocalPlayer.Silver.Value>=250 then --it doesn't hurt to check on the client anyway
receiveTool:InvokeServer("Horses","BrownHorse") --invoke the server to do all of the work
end
end
Don’t update the player’s data everytime the value changes (they purchase something), this can throttle the request and return an error. There’s a 60-second cooldown for using SetAsync, so use it wisely. I’d rather add a loop that automatically saves the player’s data every 60 seconds or more to not throttle the request.
It’s reccomended to save when a change is made to a player’s stats that’s noticeable enough to save, I. E. Buying an item, buying currency, earning a lot of currency at once, etc.
That’s not what I meant. I meant to have a while loop running on the server saving the player’s data every 60 seconds. If you saved the data below the required time, it will just return an error and won’t save at all.
What would throttle the request and make it not save, and throw an error instead, is having the data getting saved every time the values changes, which someone could abuse by purchasing a lot of items within a minute.