How do I make a datastore that saves a leaderstat?

I have a leaderstat called “Wins” and I want it to be saved to datastore for all players. I have a working code for it already, but for some reason it fails sometimes without printing any errors or warnings. I want to see a working code with the best method, then I will look over it and remember it for future coding!

1 Like

Would using modules assist you in any way? You can use them without the need to maintain your own code, as long as you don’t need ordered datastores.

Well thing is, I have no idea how modules work yet. Is there 1 script I can put inside of ServerScriptService that will work for the datastore?

Yes, there are multiple to choose from, 2 examples are ProfileService and DataStore2.

I’ll chose DataStore2, because I want to know how that works and it seems more useful than ProfileService. Also is there a DataStore1?

No, only DataStore2 from what I am aware of. Why, may I ask?

Because I was wondering if there was a DataStore1 will it will be a completely different method and I wanted to know the difference between them.

I don’t know if you want this but here is a script from my game it saves them money or whatever you want you can change a little bit of things about it.

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 Cash = Instance.new(“IntValue”,leader)
Cash.Name = “Cash”
Cash.Value = ds:GetAsync(player.UserId) or 0
ds:SetAsync(player.UserId, Cash.Value)
Cash.Changed:connect(function()
ds:SetAsync(player.UserId, Cash.Value)
end)
end)

Wouldn’t you need to use pcall functions and save it when they leave?

I don’t know if I understand what you said, but it automatically will save your progress when you leave the game.

Well that code you sent is hard for me to read, and I don’t trust it because it looks like it’s missing some things

There’s no DataStore1, there is the simple datastore, here’s an example:

local dss = game:GetService("DataStoreService")
local saveData = dss:GetDataStore("putanythinghere")


game.Players.PlayerAdded:Connect(function(player)
      local leaderstats = Instance.new(“Folder”,player)
      leaderstats.Name = “leaderstats”

      local Cash = Instance.new(“IntValue”,leaderstats)
      Cash.Name = “Cash”
      Cash.Value = 0
      
      local data = saveData:GetAsync(player.UserId)
      if data ~= nil then
          Cash.Value = data[1]
          else
          Cash.Value = 0
    end
end)

game.Players.PlayerRemoving(player)
      local dataToSave = {
          player.leaderstats.Cash.Value
      }

      saveData:SetAsync(player.UserId, dataToSave)
end)

I don’t need to create a leaderstat because I already did that in another script. Does it affect anything in this script? Datastore is new to me and I’m not good at reading it either.

1 Like

You need to do the first part of the datastore inside of the leaderstats, format it how I did.

And also put the whole datastore in the same script as the leaderstats.

I don’t know what that means, sorry.

Another thing to add, wouldn’t you need to put pcall functions around the datastore stuff? I saw on devhub that you always need to put pcalls around datastore.

Also here is the code that I currently have in the game, maybe we can improve the already existing one?

local DS = game:GetService("DataStoreService"):GetDataStore("Wins")

game:GetService("Players").PlayerAdded:Connect(function(plr)
	wait()
	local plrkey = "id_"..plr.userId
	local save1 = plr.leaderstats.Wins
	
	local GetSaved = nil
	local success, errorMessage = pcall(function()
		GetSaved = DS:GetAsync(plrkey)
	end)
	if not success then
		warn(errorMessage)
	end
	if GetSaved ~= nil then
		save1.Value = GetSaved[1]
	else
		local NumberForSaving = {save1.Value}
		local success, errorMessage = pcall(function()
			DS:GetAsync{plrkey, NumberForSaving}
		end)
		if not success then
			warn(errorMessage)
		end
	end
end)

game:GetService("Players").PlayerRemoving:Connect(function(plr)
	local success, errorMessage = pcall(function()
		DS:SetAsync("id_"..plr.userId, {plr.leaderstats.Wins.Value})
	end)
	if not success then
		warn(errorMessage)
	end
end)

Just use DataStore2 like I said, you don’t have to worry about data not saving etc as it handles it all for you. As I said, as long as you aren’t using ordered datastores its perfect for your usecase.

Well I don’t know what DataStore2 is and idk how to use it.

Read the documentation like everyone else.

Hey there! So, in essence, modules are just tables, which are returned to you by “requiring” them. Luau, however, is unique in the fact that it can store functions in tables and dictionaries. Because of this, people commonly use modules to neatly store functions. For example:

local Module = {}

function Module.Example()
     print("Cool, it works!")
     return
end

return Module

This “module”, would return a dictionary (a table that stores values with names/strings and other values instead of numbers indexing from 1+), and you can execute the function in it with

local Module = require(Path.To.Module)

Module.Example() -- This would print out "Cool, it works!"
return

This special property is why a lot of very popular utilities and libraries for use in ROBLOX are made with modules, because any script can access them and they are neatly organized!

So back to the original topic, the values in leaderstats are essentially just instances that store values. For example, a “NumberValue” stores a number that you can access by doing

NumberValue.Value

Then, you can use this value to save a player’s data! Here’s a quick example of a datastore system using ROBLOX’s built in datastores (but feel free to use a module, they’re great!)

local Datastore = { 
    Cache = {}, -- The currently saved datastore data!
    Default = { -- The default data a player with no saved data gets!
        Wins = 0,
        Losses = 0,
        Statistics = {}
    }
}

local datastoreService = game:GetService("DatastoreService")

local datastore = datastoreService:GetDataStore("Game")

local function cloneTable(target)
	if typeof(target) ~= "table" then
		return target
	end
	
	local clone = {}
	
	for index, value in pairs(target) do
		clone[cloneTable(index)] = cloneTable(value)
	end
	
	return clone
end


function Datastore.Get(player)
    local userId = tostring(player.UserId)
    local cached = Datastore.Cache[userId]

    if (cached ~= nil) then
         return cached
    end

    local attempts = 0
    local success = false
    local data = nil

    while true do
        if (success ~= false or attempts >= 3) then
            break
        end

        data = datastore:GetAsync(userId)
        attempts += 1

        if (data ~= nil) then 
            success = true break
        end

        wait(0.125)
    end

    if (data ~= nil) then
        Datastore.Cache[userId] = data        
        return data
    end
    
    data = cloneTable(Datastore.Default)
    
    Datastore.Cache[userId] = data        
    return data
end

function Datastore.Save(player)
     local data = Datastore.Get(player)

     local attempts = 0
     local success = false
     local data = nil

     while true do
         if (success ~= false or attempts >= 3) then
             break
         end

         success = pcall(function() datastore:SetAsync(userId, data) end)
         attempts += 1

         wait(0.125)
     end
end

return Datastore

You can then use this “module”, require it , and load data in a process similar to this:

-- The datastore module to get and save player data!
local Datastore = require(Path.To.Module)

-- The players service for all the important events we need!
local players = game:GetService("Players")

-- The event for when a player joins, so we can print out their wins/losses!
players.PlayerAdded(function(player)
    local data = Datastore.Get(player)

    print("The player has " .. data.Wins .. " wins, and " .. data.Losses .. " losses!")
    return
end)

-- The event for when a player leaves, so we can save their data!
players.PlayerRemoving:Conncet(function(player)
    Datastore.Save(player)
    return
end)

But you may be wondering, where do leaderstats tie in with all this stuff? Well here’s the magic of modules, instead of rewriting the datastore saving code each time to set the player’s datastore values, you can use the following code to update the cached value, and then it’ll save automatically! (Assuming you are also using the previous script(s) I mentioned)

local Datastore = require(Path.To.Module)

Datastore.Get(player).Wins = leaderstats.Wins.Value
return

Please note, I wrote all of this in this text box, so I’m not quite sure if its 100% bug-free, but I’d be happy to help through any issues :slight_smile:

Hope this helps! Have a wonderful day! :wave:

1 Like