Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
1

Why is DataStoreService not working when saving leaderboard stats?

Asked by 6 years ago
local DataStore = game:GetService("DataStoreService")
local ds = DataStore:GetDataStore("CashSaveSystem")

game.Players.PlayerAdded:Connect(function(player)
    local leader = Instance.new("Folder", player)
    leader.Name = "leaderstats"
    local Stage = Instance.new("IntValue", leader)
    Stage.Name = "Stage"
    Stage.Value = ds:GetAsync(player.UserId) or 1
    ds:SetAsync(player.UserId, Stage.Value)
    Stage.Changed:Connect(function()
        ds:SetAsync(player.UserId, Stage.Value)
        print("Saved")
    end)
end)

game.Players.PlayerRemoving:Connect(function(player)
    ds:SetAsync(player.UserId, player.leaderstats:FindFirstChild("Stage").Value)
    print("Saved")
end)

For some reason this code doesn't save the stage value of the player when they join, when the value is updated/changed, or when they leave the game. Been stuck for quite awhile and I haven't seemed to figure it out.

This has also worked on a different game, and still does, so I have no clue why it doesn't work on this game. (And yes, studio access to API services is on)

1
Why would you need to save the stage value of the player when they join? You'd only need to do it when they leave, or if you autosave. Fifkee 2017 — 6y
1
Additionally, you shouldn't use SetAsync, use UpdateAsync. Also, don't save on each change, because you might throttle the request limit. User#19524 175 — 6y

1 answer

Log in to vote
2
Answered by
Link150 1355 Badge of Merit Moderation Voter
6 years ago
Edited 6 years ago

A Possible Reason For the Problem

First of all, if neither request work then I'd recommend making sure your script can execute from its current location. Scripts will not execute in certain locations. Make sure this is not one of them.

Other issues

As pointed out by Fifkee and incapaz in the comments, you shouldn't use DataStore:SetAsync() if the data store key already exists and the new value to be saved depends on the old. Also, there is no reason to save immediately after loading. Only save when a player leaves the game or during an autosave.

Secondly, the PlayerRemoving event is never fired when the last player in a game server leaves, because the server stops anything it's doing and focuses solely on shutting down. Which means your players' data will not be saved before leaving, and so their data may not be up to date when they rejoin.

For this reason you will have to save the player's data from a game:BindToClose() callback function.

About BindToClose

A function passed to BindToClose will be invoked right before Roblox shuts down. Multiple functions can be bound with BindToClose, in which case they will execute in parallel.

Also, from the Roblox Wiki's documentation:

"The game will wait a maximum of 30 seconds for all bound functions to complete running before shutting down. After 30 seconds, the game will shut down regardless if all bound functions have completed or not."

The Solution

But wait, that's not all! We shouldn't only save the last connected player's data from a BindToClose() callback function, as it's entirely possible for the server to close even if there are more players in-game; in case of a network issue, or if you manually shutdown all servers from your game's page, for example.

Additionally, since data store requests need to make web calls, and therefore take time to complete, if we saved every player's data one after the other we could run out of time, as each data store request would have to wait for the previous one to finish. Remember, Roblox will only allow you 30 seconds before shutting down.


So what we're going to do is save every connected player's data in parallel using spawn():

local Players = game:GetService("Players")


local function savePlayerData(player)
    -- logic to save a player's data
end


game:BindToClose(function()
    for _, player in pairs(Players:GetPlayers()) do
        spawn(function()
            savePlayerData(player)
        end)
    end
end)

This way our code will save every connected player's data when the server closes -- regardless of why it closes -- and start the next data store request when one yields (that is, when it pauses the current thread until it's finished).

Ad

Answer this question