Help with DataStore Variables leaderboard

  1. What do you want to achieve? Keep it simple and clear!

I am trying to create a global leaderboard across all servers using highscores (that i’ve already got setup) saved via DataStore. Basically when a player joins the game if they don’t have the necessary variables they are given them (a folder called highscores in player). These highscores update whenever the player hits a new personal best. Then when a player leaves these variables are saved (I guess I should probably update them whenever they hit a new personal best as well).

  1. What is the issue? Include screenshots / videos if possible!
    I’m not sure how to use these variables to make a global leaderboard. I know what needs to be done: take all the player.Highscore.gamemode1 (which are saved on a datastore) variables (excluding the ones who’s variable is still equal to 0 because that would mean they haven’t played that gamemode yet) and make a list in order of players who’s got the biggest score.

  2. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I’ve looked around a bit and couldn’t find anything specifically with what I was looking for. I even followed a youtube tutorial and made a leaderboard, but ended up with more questions than answers. So here’s what I’ve made so far:

for i = 1,10 do
	local sample = game.ServerStorage.SampleFrame:Clone()
	sample.Parent = script.Parent
	sample.Name = i
	sample.place.Text = tostring(i..".")
	sample.LayoutOrder = i
end

_G.updateLeaderboard = function()
	for i, v in pairs(game.Players:GetChildren()) do
		local data = 1
		local datastore = game:GetService("DataStoreService"):GetOrderedDataStore("Highscores")
		datastore:SetAsync(v.UserId,data)
	end
	local datastore = game:GetService("DataStoreService"):GetOrderedDataStore("Highscores")
	local pages = datastore:GetSortedAsync(false,10)
	local data = pages:GetCurrentPage()
	for k, v in pairs(data) do
		if tonumber(v.key) >= 0 then
			local frame = script.Parent:FindFirstChild(tostring(k))
			if frame then
				frame.score.plrname.Text = game.Players:GetNameFromUserIdAsync(v.key)
				frame.score.Text = tostring(v.Value)
				
				local thumbType = Enum.ThumbnailType.AvatarThumbnail
				local content, isReady = game.Players:GetUserThumbnailAsync(v.key, thumbType, Enum.ThumbnailSize.Size150x150)
				frame.charpic.Image = content
			end
		end
	end
end

wait(1)
_G.updateLeaderboard()

That’s more of a test than anything else, and it has some issues of it’s own. Like the order goes 1, 10, 2, 3, 4, etc. BUT it does make a neat list of of the frames how I want them in a scrollingframe. I just want help on how to implement my datastore variables into that, get the players name that owns the value, as well as their id, and order the list off of that. (and also how to fix the 1, 10, 2 issue). Thanks for any help in advance I know this post got kind of long.

3 Likes

I would take a look at this post for more information on global leaderboards.

2 Likes

Alright, let me pick this apart. Let me know if I miss something or you have questions.

1, 10, 2, 3 Order

This is likely because you have a UIListLayout or something similar, but it’s SortOrder is set to Name instead of LayoutOrder. Just switching that should fix the issue.

Getting username from user ID

There’s actually a function just for this
Beware, it is a web call so you should use a pcall, like this example from its API page:

local name
pcall(function ()
	name = Players:GetNameFromUserIdAsync(userId)
end)

Storing actual value

You’re actually on track for this. Where you do

local data = 1

you simply have to change that to fetch the data instead. If you already load the data into a leader stat, you could do:

local data = v.leaderstats.NameOfStat.Value
--Or, if you have it somewhere else
local data = path.To.Stat.Value

I wouldn’t recommend doing a additional datastore fetch for each player in this loop, as that would easily exceed the limit. Instead, get it when the player joins, and store it in a ValueObject somewhere.

2 Likes

Thanks for the reply. So you were 100% right about the order. Only thing I’m still confused on really is how to use my saved variables. I already have a variable in each player that represents their highscores. Are you saying I should make a table that contains all of them, and if so how? I already save the players highscores so it would be convenient if I could just use those, but I don’t know how I could make a table all servers use that would be added to when a player hits a highscore (and only one variable per player which would need to get replaced when the player hits a new highscore).

1 Like

I’m glad the sort order thing worked, I’ve done that multiple times and been confused.

I’d you already have the data saved, you have to save it to the OrderedDataStore. Only worry about each player individually.

--When player gets new score
local datastore = game:GetService("DataStoreService"):GetOrderedDataStore("Highscores")
datastore:SetAsync(player.UserId, newScore)

Then, every server can use

datastore:GetSortedAsync(false,10)

If that doesn’t work:

You cant store tables, as only numerical variables can be sorted. I’m not sure what you mean by a variable in a player, do you mean a Value Object? Where exactly do you store the players high score? I need a bit more info to understand what you’re trying to do.

1 Like

yes they’re numbers values. I create a folder in the player called Highscores and inside that there’s a number value for every different gamemode. So game.Players.Highscores.gamemode1.Value would be how I normally would access an individual players highscore for gamemode1.

Alright, so you have 2 options. You can either update the stored ordered datastore value every time you update the leaderboard (which may cause issues depending on how often you do), or update the stored ordered datastore value every time its improved (which will cause issues if they improve really quickly). The code to do that would look like:

local gamemode1 = game:GetService("DataStoreService"):GetOrderedDataStore("HighscoresForGamemode1") --This line should be at the very top of the script, as it should be a global variable


--Where player is defined, and score is to be updated
gamemode1:SetAsync(player.UserId, player.highscores.gamemode1.Value)

Keep in mind, if you were to have a gamemode2, then you would need to make a separate ordered data store to use, as only value can be sorted.