DataStore Guide


  • Lately I've seen people having problems using DataStores. They're a farily simple, yet powerful tool to use for your games. There are many uses for them including, but not limited to;

    • Saving player stats
    • Admin/Ban system
    • Game stats (amount of players played in a day, average play time, etc.)
    • Cross-server chat

    For this guide, I'll do game stats since they're easy to record.


    Setting Up the DataStore


    To get the DataStore, you use DataStoreService and DataStoreService:GetDataSore(string name, string scope = "global").

    local DataStoreService = game:GetService("DataStoreService")
    local GameStats = DataStoreService:GetDataStore("GameStats")
    

    GetDataStore will return a DataStore (obv.) with the given name and scope containing all of the data saved to it.


    Saving and Loading


    DataStore has a per place limit seen here; DataStore limitations

    Being mindful of the amount of requests you make is important to keeping data safe. With this in mind, saving data every time something changes probably isn't a good idea if it changes a lot and can be stored elsewhere, then saved later (all in one go).

    Loading

    To get the data from the DataStore, you use DataStore:GetAsync(string key).

    The key is important and should be easy to distinguish between each player -- if what you're saving is for each player. For example, if you're saving the player's leaderstats, the key should be something like user_(UserId). It shouldn't be a player's username since they can change it and their data would be lost if you used it as a key.

    Now for getting the scripting part;
    When you use GetAsync, you may also want to make sure there's something being returned, otherwise you may be setting nil values.

    local DataStoreService = game:GetService("DataStoreService")
    local GameStats = DataStoreService:GetDataStore("GameStats")
    local Data = GameStats:GetAsync(game.PlaceId) --// This key is the PlaceId (it will never change).
    
    if Data then
        print(game.PlaceId.." has data.")
    else
        warn("No data found in "..game.PlaceId.." or it is the first time being played.")
    end
    

    If the game is being played for the first time, there won't be anything in the DataStore, so Data will not return anything, which is what we checked for. (That's a good stat to add :stuck_out_tongue:). Let's create a table with the stuff we'd like to save in the DataStore. We should also put our if statement inside a PlayerAdded function with GetAsync inside so it returns new data.

    local DataStoreService = game:GetService("DataStoreService")
    local GameStats = DataStoreService:GetDataStore("GameStats")
    
    local StatsToSave = {["PlayingForTheFirstTime"] = true, ["TimesPlayed"] = 0, ["TotalTimePlayed"] = 0} --// These are all the stats we will save
    
    game.Players.PlayerAdded:Connect(function()
        local Data = GameStats:GetAsync(game.PlaceId) --// This key is the PlaceId (it will never change). This will return the table we save to the DataStore.
        if Data then
            print(game.PlaceId.." has data.")
            StatsToSave.PlayingForTheFirstTime = Data.PlayingForTheFirstTime
            StatsToSave.TimesPlayed = Data.TimesPlayed + StatsToSave + 1 --// Total amount of times played + the new player
    
        spawn(function() --// We don't want the loop to stop the rest of the script from running
            while true do
                StatsToSave.TotalTimePlayed = (Data ~= nil and Data.TotalTimePlayed + 
        StatsToSave.TotalTimePlayed + 1) or StatsToSave.TotalTimePlayed + 1 --// We have to make sure we aren't overwriting the old stats, and we have to make sure we're adding the new time to the old time.
                wait(1)
            end
        end)
    
        else
            warn("No data found in "..game.PlaceId.." or it is the first time being played.")
            StatsToSave.PlayingForTheFirstTime = false
            StatsToSave.TimesPlayed = StatsToSave.TimesPlayed + 1 --// Add 1 to the total amount of times played
            spawn(function() --// We don't want the loop to stop the rest of the script from running
                while true do
                        StatsToSave.TotalTimePlayed = (Data ~= nil and Data.TotalTimePlayed + 
            StatsToSave.TotalTimePlayed + 1) or StatsToSave.TotalTimePlayed + 1 --// We have to make sure we aren't overwriting the old stats, and we have to make sure we're adding the new time to the old time.
                    wait(1)
                end
            end)
    
        end
    end)
    

    Now we have the loading part done.


    Once a player leaves, we are done gathering information from that player. We can now save everything we've gathered to the DataStore.


    Saving

    To save the data to the DataStore, there are two methods; DataStore:SetAsync(string key, Variant value) and DataStore:UpdateAsync(string key, function<Variant>(Variant) transformFunction).

    The one you want to use depends on what's being saved, how often it's being saved, and if what's being saved can be overwritten. GetAsync sometimes returns cached data, and other servers may have modified the values. In my case, I would recommend using UpdateAsync since the values are being constantly changed when players enter and leave. This time we can put UpdateAsync inside a PlayerRemoving function.

    local DataStoreService = game:GetService("DataStoreService")
    local GameStats = DataStoreService:GetDataStore("GameStats")
    
    local StatsToSave = {["PlayingForTheFirstTime"] = true, ["TimesPlayed"] = 0, ["TotalTimePlayed"] = 0} --// These are all the stats we will save
    
    game.Players.PlayerAdded:Connect(function()
        local Data = GameStats:GetAsync(game.PlaceId) --// This key is the PlaceId (it will never change). This will return the table we save to the DataStore.
        if Data then
            print(game.PlaceId.." has data.")
            StatsToSave.PlayingForTheFirstTime = Data.PlayingForTheFirstTime
            StatsToSave.TimesPlayed = Data.TimesPlayed + StatsToSave + 1 --// Total amount of times played + the new player
    
        spawn(function() --// We don't want the loop to stop the rest of the script from running
            while true do
                StatsToSave.TotalTimePlayed = (Data ~= nil and Data.TotalTimePlayed + 
        StatsToSave.TotalTimePlayed + 1) or StatsToSave.TotalTimePlayed + 1 --// We have to make sure we aren't overwriting the old stats, and we have to make sure we're adding the new time to the old time.
                wait(1)
            end
        end)
    
        else
            warn("No data found in "..game.PlaceId.." or it is the first time being played.")
            StatsToSave.PlayingForTheFirstTime = false
            StatsToSave.TimesPlayed = StatsToSave.TimesPlayed + 1 --// Add 1 to the total amount of times played
            spawn(function() --// We don't want the loop to stop the rest of the script from running
                while true do
                        StatsToSave.TotalTimePlayed = (Data ~= nil and Data.TotalTimePlayed + 
            StatsToSave.TotalTimePlayed + 1) or StatsToSave.TotalTimePlayed + 1 --// We have to make sure we aren't overwriting the old stats, and we have to make sure we're adding the new time to the old time.
                    wait(1)
                end
            end)
    
        end
    end)
    
    game.Players.PlayerRemoving:Connect(function()
        GameStats:UpdateAsync(game.PlaceId, function()
            return StatsToSave --// This is what we're saving to the DataStore
        end)
    end)
    

    And we are done. This will load the game's data when a player joins, and save when a player leaves. :smile:

    Please note there may be errors that I missed to due the lack of a computer to test this on. Sorry if there is.


  • The data limits section says that you can only update the value of the same key 10 times a minute, if your game receives high amounts of traffic, the gamestats datastore wouldn't be able to keep up?

    A way to combat this is to save the data on server close instead of when each player leaves?


  • You could set datastores every time a change occurs, as if 6 players is the max, you could set it 660 times a minute, 11 times a second. Still not the best idea, but you can do that.


  • @hiimgoodpack Thats not how it works, you can only set the same key 10 times per min. This includes the full path ie [scope][ds name][key].

Log in to reply
 

Looks like your connection to Scripting Helpers was lost, please wait while we try to reconnect.