New: Nitro Boost our Discord server and receive full donation perks here on the website! Join the Scripting Helpers Discord Server to learn more! You can also Support on Patreon as always.
Ad
Still have questions? Join our Discord server and get real time help.
Log in to vote
1

Request was throttled output message?

Asked by 1 year ago

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)
0
You can only send so many requests in DataStoreService and you must wait in between. Here are the limits. https://gyazo.com/cbbe05a6b679dc39a75b4563973a4054 popeeyy 488 — 1y
0
yeah but it outputs it even when i click the button once? radusavin366 617 — 1y
0
Are other scripts using it? popeeyy 488 — 1y
0
Dont send how much cash to add :/ kingdom5 5449 — 1y
0
You should delete the .Changed event for saving, it already saves when they leave. popeeyy 488 — 1y

2 answers

Log in to vote
1
Answered by
Avigant 2364 Moderation Voter Community Moderator
1 year ago
Edited 1 year ago

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:

  1. When the player leaves the game. This should work most of the time.

  2. When the server is about to shut down. We can't guarantee this will work, but we can try.

  3. 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.

  4. 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.

0
Also of note, I didn't add auto-saving to my code, but that's definitely something you should do. Avigant 2364 — 1y
0
Thank you for the detailed answer! I'll try to load the userdata in a dictionary but it'll take me a while since I'm a noob at dictionaries :P And can you give me an idea of what can I replace the AddCash event with? radusavin366 617 — 1y
0
I didn't see that you were making a button to add currency, but you should at least make sure the client doesn't pass in how much currency to add, and you should probably add a delay of how often they can receive the cash Avigant 2364 — 1y
0
Nah, the button is just to test adding money to the player, it won't be part of the gameplay. Thank you, accepting your answer! radusavin366 617 — 1y
Ad
Log in to vote
1
Answered by
popeeyy 488 Moderation Voter
1 year ago
Edited 1 year ago

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!

0
BindableEvents are for server -> server and client -> same client, RemoteEvents are for server -> client(s) and client -> server. Avigant 2364 — 1y
0
Additionally, there are some unaddressed flaws in the player data management I addressed in my answer that may be worth checking out. Avigant 2364 — 1y
0
Thank you for the answer! This seems to do it, but I have another problem now. When the player joins the game, the currency shows 0 until the money changes. (game: https://www.roblox.com/games/1911479049/last-try). That's why the serverscriptservice script fired that event when a player joins, is there a way to fix this? radusavin366 617 — 1y
0
At the beginning of the script, you can do what the changed event does. popeeyy 488 — 1y

Answer this question