I'm making a button that increases a player's currency and everything's working alright except I get a message in the output every time I press the button:
10:24:33.663 - Request was throttled. Try sending fewer requests. Key = 26139261 (x11)
Should I be worried or even mind this warning?
Scripts:
Inside ServerScriptService:
local DataStore = game:GetService("DataStoreService") local ds = DataStore:GetDataStore("CashSave") local UpEv = game.ReplicatedStorage.UpdateCash game.Players.PlayerAdded:connect(function(player) local data = Instance.new("Folder", player) data.Name = "PlayerData" local Cash = Instance.new("IntValue", data) Cash.Name = "Cash" Cash.Value = ds:GetAsync(player.UserId) or 100 ds:SetAsync(player.UserId, Cash.Value) Cash.Changed:connect(function() ds:SetAsync(player.UserId, Cash.Value) UpEv:FireClient(player) end) local AddCashEve = game.ReplicatedStorage.AddCash AddCashEve.Event:Connect(function(amount) Cash.Value = Cash.Value + amount end) end) game.Players.PlayerRemoving:connect(function(player) ds:SetAsync(player.UserId, player.PlayerData.Cash.Value) end)
Inside StarterGui:
local cash = script.Parent.Parent.PlayerData.Cash local display = script.Parent.MainGui.CashBox.Cash local iniEve = game.ReplicatedStorage.UpdateCash function DisplayCash() display.Text = cash.Value end cash.Changed:Connect(function() display.Text = cash.Value end) iniEve.OnClientEvent:Connect(DisplayCash())
Inside the button:
local click = script.Parent.ClickDetector local AddEve = game.ReplicatedStorage.AddCash click.MouseClick:Connect(function() AddEve:Fire(100) end)
You are going over the DataStore
request limit by attempting to save whenever a value changes.
There are four times that we should be saving to a DataStore
:
When the player leaves the game. This should work most of the time.
When the server is about to shut down. We can't guarantee this will work, but we can try.
Auto-saving every few minutes. Attempting to save when the server shuts down or player leaves might fail for some reason, so we want to make sure the player only loses a few minutes of data at most.
After a player makes a R$ purchase, we should save immediately before accepting the purchase. This is very important. Players should never lose data after spending money.
You should wrap your DataStore
requests in a pcall
, since they can error.
Additionally, we should be saving and loading a dictionary as our player data. This is because it allows us to easily add and remove what we want to save at any time. Do this always, even if you only have one value you want to save at the time. It'll make your life easier, trust me!
Something I see a lot is that people forget to check if the attempt to load a player's data failed or not before saving. This is a must, because you can overwrite a player's data if you don't do this.
We can also avoid wasting DataStore
requests on guests by checking if we're not in Studio's Test Server mode and checking their Player.UserId
. Though guests have been officially removed from the platform, they're still around, and players can still join as them. The reason to check if the game is running in Test Server mode is that players all get negative user IDs in it.
Let's try something like this:
local Services = { DataStore = game:GetService("DataStoreService"), Players = game:GetService("Players"), Run = game:GetService("RunService") } local PlayerDataStore = Services.DataStore:GetDataStore("PlayerDataStore") local LoadedDataPlayers = {} local function IsPlayerGuest(Player) return Services.Run:IsStudio() and Player.UserId < 1 end local function GetPlayerDataToSave() -- Return the data here. This is just an example! return { Cash = 0, FavoriteColor = "Red", ToolsOwned = { NormalSword = true, GreatSword = true, AnotherSword = true } } end local function OnPlayerAdded(Player) if LoadedDataPlayers[Player.UserId] then -- This should be a rare occurence, but it's possible the player rejoins the same server while our game.Players.PlayerRemoving event listener is yielding. This could cause problems with loading outdated data. Player:Kick("Data loading error: Please rejoin the game") return end if not IsPlayerGuest(Player) then local IsLoadSuccess, LoadedData = pcall(function() return PlayerDataStore:GetAsync(Player.UserId) end) if IsLoadSuccess then -- It doesn't matter whether or not there was loaded data for this part, we just care about whether or not the request errored. LoadedDataPlayers[Player.UserId] = true end if IsLoadSuccess and LoadedData then -- Set the player data to our LoadedData. Optionally, you can choose to merge the data in, so you won't have to shut down your game every time you change what data you need to store. end end -- Other code here. end local function OnPlayerRemoving(Player) if not LoadedDataPlayers[Player.UserId] then return end local PlayerDataToSave = GetPlayerDataToSave() pcall(function() PlayerDataStore:SetAsync(Player.UserId, PlayerDataToSave) end) LoadedDataPlayers[Player.UserId] = nil end local function OnGameShutdown() -- game:BindToClose() gives us 30 seconds maximum to work with. This means we can try to save all player data, but it isn't guaranteed to work, particularly in larger servers. This is one of the reasons to auto-save. local Players = Services.Players:GetPlayers() for Index = 1, #Players do local Player = Players[Index] OnPlayerRemoving(Player) end end Services.Players.PlayerAdded:Connect(OnPlayerAdded) Services.Players.PlayerRemoving:Connect(OnPlayerRemoving) game:BindToClose(OnGameShutdown)
You may see a warning message about your data request being throttled when testing the game in Studio, because the game will both attempt to save the data when the player leaves, and right after when the game shuts down. It's fine to ignore this.
Additionally, your AddCash
RemoteEvent
is very insecure. Exploiters can fire it and get unlimited amounts of cash. The client should never be in the business of ordering the server to perform actions. They should never have access to a RemoteEvent
that adds cash like that.
Your requests are throttled because you're sending too many requests. Here are the request limits. https://gyazo.com/cbbe05a6b679dc39a75b4563973a4054 To conserve requests, I've fixed some code below.
Inside ServerScriptService:
local DataStore = game:GetService("DataStoreService") local ds = DataStore:GetDataStore("CashSave") local UpEv = game.ReplicatedStorage.UpdateCash local AddCashEve = game.ReplicatedStorage.AddCash game.Players.PlayerAdded:connect(function(player) local data = Instance.new("Folder", player) data.Name = "PlayerData" local Cash = Instance.new("IntValue", data) Cash.Name = "Cash" Cash.Value = ds:GetAsync(player.UserId) or 100 end) game.Players.PlayerRemoving:connect(function(player) ds:SetAsync(player.UserId, player.PlayerData.Cash.Value) end) AddCashEve.Event:Connect(function(player, amount) player.PlayerData.Cash.Value = Cash.Value + amount end)
First of all, you should specify what player for your AddCash event in ReplicatedStorage. As of right now, it will change it for all players and use another request up. You can also take the saving for .Changed away due to it saving when they leave.You should change the script that sets off the BindableEvent by adding the player as the first argument. Also, there's no need to save data when they join, just load it. Also, the LocalScript already checks for .Changed, so no need to tell it to change the value if it already does.
Inside StarterGui:
local cash = script.Parent.Parent.PlayerData.Cash local display = script.Parent.MainGui.CashBox.Cash display.Text = cash.Value cash.Changed:Connect(function() display.Text = cash.Value end)
I removed the event due to the reason explained above.
Inside the button:
local click = script.Parent.ClickDetector local AddEve = game.ReplicatedStorage.AddCash click.MouseClick:Connect(function(plr) AddEve:Fire(plr, 100) end)
You should add the player due to the extra argument I added in the BindableEvent.
I hope this helped, if you continue to have this issue, please comment below so I can edit and try to help you out!