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

Datastore not working and leaderboards not saving?

Asked by 5 years ago

My leaderstats aren't saving and i've tried everything so I need some help

local datastore = game:GetService("DataStoreService")
local ds1 = datastore:GetDataStore("SaveCashSystem")
local ds2 = datastore:GetDataStore("SaveCoffeeSystem")


game.Players.PlayerAdded:Connect(function(player)

    local leaderstats = Instance.new("Folder", player)
    leaderstats.Name = "leaderstats"

    local money = Instance.new("IntValue", leaderstats)
    money.Name = "Money"

    local totalcoffee = Instance.new("IntValue", leaderstats)
    totalcoffee.Name = "Total Coffee"

    money.Value = ds1:GetAsync(player.UserId) or 0
    ds1:SetAsync(player.UserId, money.Value)
    totalcoffee.Value = ds2:GetAsync(player.UserId) or 0
    ds2:SetAsync(player.UserId, totalcoffee.Value)

    totalcoffee.Changed:Connect(function(changed)
        ds2:SetAsync(player.UserId, totalcoffee.Value)
        print("Saved " .. totalcoffee.Value .. " coffees!")
    end)
    money.Changed:Connect(function(changed)
        ds1:SetAsync(player.UserId, money.Value)
    end)
end)


game.Players.PlayerRemoving:Connect(function(player)

end)
0
Is there even a need for lines 20 and 23? OptimisticSide 199 — 5y
0
Also, keep in mind that using .Changed might overload the system, and having a button to save is a MUCH better way. OptimisticSide 199 — 5y
0
Thanks! muffin1234 0 — 5y
0
If my answer had helped you then click on the 'Approve Answer' button below my answer to mark this question as solved. xPolarium 1388 — 5y

2 answers

Log in to vote
1
Answered by
xPolarium 1388 Moderation Voter
5 years ago
Edited 5 years ago

There's much to go through and explain in this DataStore script that needs to be said since I've seen many individuals have been loading data this way. Thankfully my explanation follows a lot of Roblox's own article on the RobloxDev site.


I see a problem when needing more than one DataStore. DataStores give you many options like saving to different scopes but your case could be done an easier way.

DataStores become even easier to use when you understand that they can save tables. Even subtables within the table for some data organization. This is useful to know because you use Folders and IntValues to show the player the data they have. All we need to do is convert them to a table.


We can start in the PlayerAdded event which is usually when you load player data:

--Defined services with reasonable names
local Players = game:GetService("Players")
local DSS = game:GetService("DataStoreService")

--Our DataStore we save everything to
local LeaderboardStore = DSS:GetDataStore("Leaderboards")

Players.PlayerAdded:Connect(function(player)
    local playerData --Will be set to any data player has as a table

    local leaderstats = Instance.new("Folder")
    leaderstats.Name = "leaderstats"
    leaderstats.Parent = player 

    local money = Instance.new("IntValue")
    money.Name = "Money"
    money.Parent = leaderstats

    local totalcoffee = Instance.new("IntValue")
    totalcoffee.Name = "Total Coffee"
    totalcoffee.Parent = leaderstats

    local key =  player.UserId.."_data"
    --We need a personal key for the joining player.
    --You should know what this is for already.

    --This part is explained some below
    local success, _error = pcall(function()
        LeaderboardStore:UpdateAsync(key, function(data)
            playerData = data 
        end)
    end)

    if success then
        if playerData then
            money.Value = --set to the values in 'playerData' table
            totalcoffee.Value = --set to the values in 'playerData' table
        else
            --If no data was found then we set default values.
            money.Value = 0
            totalcoffee.Value = 0
        end
    else
        --DataStores requests fail sometimes so we could warn the player here
        warn("Data for player could not be loaded.")
    end
end)

After creating the custom key for the player, UpdateAsync is used here to set our playerData variable to any previous data the player may have. This is also wrapped in a pcall() function to check if calling for the data fails for some reason.

Below that we use the first result of the pcall, success variable of type boolean, to check if the DataStore call had worked. If it didn't, then we could handle the player differently. If success was true then it means the DataStore did work but it doesn't really mean that the player had any data to start.


Next we need to actually save data... but how?

Your original code does this by saving (By using :SetAsync()) everytime the IntValues change through the .Changed() event. This is NEVER the way you want to go about saving because you will exceed data store limits. You could read on that here.

Instead you should only save at important moments or use some interval. A point you should always save that I will touch on is when the player leaves through PlayerRemoving

First we need to convert our 'leaderstats' folder to a table that will serve as the data to be saved. We can do this with a new function with a generic for-loop:

--This function will be passed the player as an argument
local function requestPlayerData(player)
    local data = {}
    local folder = player.leaderstats

    for _, object in pairs(folder:GetChildren()) do
        if object:IsA("IntValue") then
            data[object.Name] = object.Value
        end
    end

    return data
end

We define a table that we will write our IntValues to using the Name as the key and Value as the value. We then return that table once it's done.

We then can continue with the PlayerRemoving:

Players.PlayerRemoving:Connect(function(player)
    local key = player.UserId.."_data"

    --We call our function and pass the player to get player stats in table form
    local dataToSave = requestPlayerData(player)

    --pcall again to catch errors.
    local success, _error = pcall(function()
        LeaderboardStore:SetAsync(key, dataToSave)
    end)
end)

When we call the requestPlayerData() function, we are returned with a proper table that the DataStore can use to save. Almost that simple.

We then bring all the code together to get a more secure DataStore system:

local Players = game:GetService("Players")
local DSS = game:GetService("DataStoreService")

local LeaderboardStore = DSS:GetDataStore("Leaderboards")

local function requestPlayerData(player)
    local data = {}
    local folder = player.leaderstats

    for _, object in pairs(folder:GetChildren()) do
        if object:IsA("IntValue") then
            data[object.Name] = object.Value
        end
    end

    return data
end

Players.PlayerAdded:Connect(function(player)
    local playerData;

    local leaderstats = Instance.new("Folder")
    leaderstats.Name = "leaderstats"
    leaderstats.Parent = player 

    local money = Instance.new("IntValue")
    money.Name = "Money"
    money.Parent = leaderstats

    local totalcoffee = Instance.new("IntValue", leaderstats)
    totalcoffee.Name = "Total Coffee"
    totalcoffee.Parent = leaderstats

    local key = player.UserId.."_Data"

    local success, _error = pcall(function()
        LeaderboardStore:UpdateAsync(key, function(data)
            playerData = data
        end)
    end)

    if success then
        if playerData then
            money.Value = playerData[money.Name]
            totalcoffee.Value = playerData[totalcoffee.Name]
        else
            money.Value = 0
            totalcoffee.Value = 0
        end
    else
        warn("Data could not be loaded for "..player.Name)
    end
end)


Players.PlayerRemoving:Connect(function(player)
    local key = player.UserId.."_data"

    local dataToSave = requestPlayerData(player)

    local success, _error = pcall(function()
        LeaderboardStore:SetAsync(key, dataToSave)
    end)
end)

The last thing I had added to the above code are lines 44 and 45. If any data retrieved is found for the player then the playerData table can be indexed using the Names of our IntValues just like we had it set up in our requestPlayerData() function!

That wraps up my explanation in using tables to save and when not to save for DataStores. This might be pretty lengthy so do make sure to read the articles I had linked.

If I missed something or got something wrong please let me know.
Ad
Log in to vote
0
Answered by 5 years ago

Thank you!

0
If my answer had helped you then click on the "Accept Answer" button. Helps me and you. xPolarium 1388 — 5y

Answer this question