Global Leaderstats Help

Hey,
Right now I have one data store that holds all the player’s stats in a single key. Since all the stats are saved into one table, I cannot use ordered data stores because the saved value needs to be a positive integer. Would I need to make multiple datastores and save the player’s high score in each one for this to work? I’m not too experienced with data stores so any help would be great.

1 Like

Honestly, what I’d do (and this is what I’m doing for an upcoming game) is keep your current system where it saves all your player’s stats in a single table under their key. Making a separate datastore for each of these is generally a bad idea and limits could easily be breached.

I would then create a separate ordered data store. Each player is given a unique key, and their high score is stored as an integer under this. I’d then update the player’s key every time their high score is raised. This way, you’re only using 2 datastores total per player.

I really hope I understood your situation properly and my answer is clear enough. It’s 10pm here so I’m feeling pretty tired – hopefully it didn’t affect my answer too much! If you have any follow-up questions, please let me know :slight_smile:

PS: nice videos!

I have 6 maps and I want each map to have their own leaderboard. Would 6 ordered datastores on top of the regular data store have the potential to exceed the limits?

You could save your players data in a sub table.

The wiki has a nice example of sub tables:

local playerData = {}
playerData["Inventory"] = {}
playerData["Inventory"][1] = "Sword"
playerData["Inventory"][2] = "Bow"
playerData["Inventory"][3] = "Rope"
playerData["Gold"] = 20
playerData["Experience"] = 500

Simplified is right. Just use sub tables.

1 Like

That should be fine. You can set data (60 + numPlayers × 10) times a minute; you can read normal datastores (60 + numPlayers × 10) times a minute and you can get sorted results from an ordered datastore (5 + numPlayers × 2) times a minute.

Basically, if you:

  • Set the ordered data stores every time a user beats their high score
  • Update the normal data store every time they leave
  • Update the normal datastores every 5 minutes or so as a precaution
  • Update your leaderboard(s) at the start of every server and then I guess every 10 minutes or so (I can’t see a global leaderboard needing particularly fast refreshes)

… then you should be more than fine

Getting information from ordered data stores falls under a separate limit to reading from normal datastores so they wouldn’t affect one another


As far as the other comments go, I really can’t see this working in the form of a global leaderboard (I’m assuming this is what you’re going for?). OrderedDataStores are by far the most efficient way of doing a global leaderboard and I don’t think a value assigned to a key can be anything other than an integer, so no tables (source https://developer.roblox.com/en-us/api-reference/class/OrderedDataStore – " with the exception that stored values must be positive integers.")

I am a little late to the party, but I had to go do something in the middle of typing this and I don’t want to totally scrap this. Although I don’t disagree with @PolyCrunch’s answer, and clearly you thought it was a solution, I want to show you another way to approach this.

As far as I’m understanding what you are asking, I think you want to save a player’s high score from 6 different maps separately and put them on a leaderboard. If I am correct, this is how you would go about doing so.

First off, you want to use a minimum amount of DataStores for your game to keep things simpler. In my example, I will be using one DataStore and save all data from 6 maps into a table. From there, on a separate script, I will retrieve each player’s data on each map, use OrderedDataStore to organize it, and then implement it on a leaderboard. (There will 6 leaderboards)

  • Creating the DataStore
local HttpService = game:GetService("HttpService") --We will use this to save table

local DataStoreService = game:GetService("DataStoreService")
local PlayersData = DataStoreService:GetDataStore("DATA_STORE") --Name of your DataStore

game.Players.PlayerAdded:Connect(function(player)
    local playerData = PlayersData:GetAsync("key_"..player.UserId) --The key of your DataStore
    if playerData == nil then --Create a new DataStore
        local newTableData = {
                                                ["Map 1 Highscore"] = 0;
                                                ["Map 2 Highscore"] = 0;
                                                ["Map 3 Highscore"] = 0;
                                                ["Map 4 Highscore"] = 0;
                                                ["Map 5 Highscore"] = 0;
                                                ["Map 6 Highscore"] = 0;
                                            }
        local newData = HttpService:JSONEncode(newTableData)
        PlayersData:SetAsync("key_"..player.UserId, newData) --Setting players data
    end
end)

Next we will update the player’s high score. @PolyCrunch has already listed how to do this, so I will be using one example.

  • Saving the DataStore
local HttpService = game:GetService("HttpService") --We will use this to save table

local DataStoreService = game:GetService("DataStoreService")
local PlayersData = DataStoreService:GetDataStore("DATA_STORE") --Name of your DataStore

game.Players.PlayerRemoving:Connect(function(player)
   local playerData = PlayersData:GetAsync("key_"..player.UserId) --The key of your DataStore
   if playerData ~= nil then
       local data_table = HttpService:JSONDecode(playerData)
       data_table["Map 1 Highscore"] = --New high score
       data_table["Map 2 Highscore"] = --New high score
       data_table["Map 3 Highscore"] = --New high score
       data_table["Map 4 Highscore"] = --New high score
       data_table["Map 5 Highscore"] = --New high score
       data_table["Map 6 Highscore"] = --New high score
       local newData = HttpService:JSONEncode(data_table)
      PlayersData:SetAsync("key_"..player.UserId, newData)
   end
end)

Lastly, we will create a leaderboard with OrderedDatastore. Note that this will only be one script to one leaderboard. You can just duplicate the script to the other leaderboards and edit the name of the what data is being retrieved. I am also taking huge inspiration from DutchDeveloper in his video.

  • Creating a leaderboard
local HttpService = game:GetService("HttpService") --We will use this to save table

local DataStoreService = game:GetService("DataStoreService")
local PlayersData = DataStoreService:GetDataStore("DATA_STORE") --Name of your DataStore

for i = 1, 10 do --Number of players in a leaderboard
	local newScore = script.Sample:Clone() --I'm using the UI from his video
	newScore.Name = i
	newScore.Parent = script.Parent.SurfaceGui.Frame.ScrollingFrame
	newScore.Score.Text = "0"
	newScore.UserName.Text = "ONLYTWENTYCHARACTERS"
	newScore.LayoutOrder = i
	script.Parent.SurfaceGui.Frame.ScrollingFrame.CanvasSize = UDim2.new(0, 0, 0, 50*i)
end

function updateLeaderboard()
	for i,v in pairs(game.Players:GetChildren()) do
		local playerData = PlayersData:GetAsync("key_"..v.UserId)
                local table_data = HttpService:JSONDecode(playerData)
                local Data = table_data["Map 1 Highscore"] --Change per leaderboardwa
		if Data ~= nil then
			local GlobalDataStore = DataStoreService:GetOrderedDataStore("Map_1_Highscores") --Change per leaderboard
			GlobalDataStore:SetAsync(v.UserId, Data)
		end
	end
	local GlobalDataStore = DataStoreService:GetOrderedDataStore("Map_1_Highscores") --Change per leaderboard
	local Pages = GlobalDataStore:GetSortedAsync(false, 20)
	local Data = Pages:GetCurrentPage()
	for i,v in pairs(Data) do
		if tonumber(v.key) >= 1 then
			local Frame = script.Parent.SurfaceGui.Frame.ScrollingFrame:FindFirstChild(tostring(i))
			if Frame then
				Frame.UserName.Text = game.Players:GetNameFromUserIdAsync(v.key)
				Frame.Score.Text = tostring(v.value)
				Frame.Visible = true
				Frame.UserPos.Text = i
			end
		end
	end
end


while true do
	updateLeaderboard()
	wait(10)
end

That should work as far as I am concerned.You can see an example here. It changes your highscore on each map when you rejoin. It’s a random number each time.

1 Like

I think what you showed is pretty similar to what Poly said. Based on your code it looks like you have one data store that holds all the high scores and then you create an ordered data store for each high score list. I think that’s what Poly said too.

1 Like

Aha, you’re right. I clearly didn’t read nor understand @PolyCrunch’s answer right. My bad, at least you have a visual example of what he meant.

1 Like