How to make a basic player leaderboard

I seen SOME people struggle to make a player leaderboard, so I decided to make this tutorial. Hope this is helpful for you :wink:

SPOILER :

Saving data is the most hardest, breathtaking, insanely hard task to do (no it’s not).

Three things before you give me feedback

  • I am not some expert scripter, probably not me suddenly lost interest in making game
  • This is my first tutorial so be easy on me.
  • The knowledge that I had gathered from my coding experience finally being used to make this. Actually, if you want the full code you just need to scroll down and BOOM, there is it

Ahem, back to the topic. As the title suggest, this tutorial will show you on how to make a player leaderboard or in the other word “In-Experience Leaderboard” .

Don’t ask why I said “In-Experience Leaderboard”

The FIIIIIRRRRRRRSSSTTTTT thing is YOU need to do is to make a script, parent it to ServerScriptService, and finally name it “PlayerLeaderboardManager” (or anything you want it to be named) as the example shown below :

Tutorial_1_DevForum

Open the “script” and write the code shown below :

-- \\ I'm pretty sure you know the basic already. \\

-- Get all the nessecary Services that will be needed in this script
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")

-- Get a DataStore named "PlayerData" or whatever the DataStore name is
local PlayerData = DataStoreService:GetDataStore("PlayerData")

-- Quite obvious isn't it?
local IntValuesNames = {
	"Wins",
	"Points"
}

Players.PlayerAdded:Connect(function(Player)
	-- Make a folder named "leaderstats" and parent it inside of the Player
	local Leaderstats = Instance.new("Folder", Player)
	Leaderstats.Name = "leaderstats" -- MAKE SURE THE "L" IS LOWERCASE OR ELSE THE LEADERSTATS WON'T SHOW
end)

Explanation : When the player joined the game it will make a folder that will be named “leaderstats” and will be parented to the player.

The next step is to make the IntValues and naming them, which is simple ( Put this after the leaderstats one ).

-- We will do make the IntValues have some "value" later since we don't show the "Save Data Management"
for Index, Name in pairs(IntValuesNames) do
	-- Create a IntValue and parent it to leaderstats. And this time finally naming it.
	local IntValue = Instance.new("IntValue", Leaderstats)
	IntValue.Name = Name
end

Explanation : So for every stuffs that stored inside of “IntValuesName” will make a IntValue and will be named after the stuffs’ name

Now lets make the “Saving System” and “Load Data System”

First we are going to make the saving system since you probably wouldn’t understand why I do this instead of making the data and setting the IntValue “value” to that data

for Index, Name in pairs(IntValuesNames) do
	-- Create a IntValue and parent it to leaderstats. And this time finally naming it.
	local IntValue = Instance.new("IntValue", Leaderstats)
	IntValue.Name = Name
end

Okay, now let’s get on the code

-- On the player removal, it will save the IntValues "value"
Players.PlayerRemoving:Connect(function(Player)
	-- Get the player's leaderstats
	local Leaderstats = Player:FindFirstChild("leaderstats")
	
	-- Table because table gud
	local SavedStuffs = {}
	
	-- For every IntValues in the leaderstats will be inserted to the table, "SavedStuffs"
	for _, IntValue in pairs(Leaderstats:GetChildren()) do
		-- Insert the value to the "SavedStuffs"
		table.insert(SavedStuffs, IntValue.Value)
	end
	
	-- Make a pcall function about setting the "PlayerData" async
	local success, errorMessage = pcall(function()
		PlayerData:SetAsync("PlayerData_"..Player.UserId, SavedStuffs)
	end)
	
	-- Check if the success isn't true. If then, print the "errorMessage"
	if success ~= true then
		print(errorMessage)
	else
		print("Successfully saved data!")
	end
end)

Explanation : When the player left the game, it will get the player’s “leaderstats” children value and store them on a table, “SavedStuffs”. After that it will make a pcall function about setting the PlayerData async and check if the “success” is really a success. If not then, it will print the “errorMessage”

After doing that, let’s go to the “Load Data System” one

Players.PlayerAdded:Connect(function(Player)
	-- First of all, make a variable that will be called "data"
	local data
	
	-- Make a pcall function about getting the "PlayerData" async
	local success, errorMessage = pcall(function()
		data = PlayerData:GetAsync("PlayerData_"..Player.UserId)
	end)
	
	-- Check if the success isn't true. If then, it will print the "errorMessage"
	if success ~= true then
		warn(errorMessage)
	else
		print("Successfully loaded data!")
	end
	
	-- Make a folder named "leaderstats" and parent it inside of the Player
	local Leaderstats = Instance.new("Folder", Player)
	Leaderstats.Name = "leaderstats" -- MAKE SURE THE "L" IS LOWERCASE OR ELSE THE LEADERSTATS WON'T SHOW
	
	for Index, Name in pairs(IntValuesNames) do
		-- Create a IntValue and parent it to leaderstats. And this time finally naming it.
		local IntValue = Instance.new("IntValue", Leaderstats)
		IntValue.Name = Name
		
		-- Check the data isn't nil
		if data ~= nil then
			-- Set the IntValue "value" to the set data
			IntValue.Value = data[Index]
		else
			IntValue.Value = 0
		end
	end
end)

There will be no explanation for this one.

Please do note that I am not some expert scripter, and also this is my first tutorial so it may not be great.

Summary

Some random guy decided to make a leaderstats tutorial because he seen some people struggle to make a simple leaderstats.

  • This is a VERY GOOD tutorial, it helped me out on making a leaderstats
  • Good tutorial bro :+1:
  • Nice tutorial, even though it has some flaws
  • Bad, because it’s bad
  • It’s the worst tutorial I ever seen in my entire lifetime.

0 voters

Those who are lazy, here is the full code :

-- \\ I'm pretty sure you know the basic already. \\

-- Get all the nessecary Services that will be needed in this script
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")

-- Get a DataStore named "PlayerData" or whatever the DataStore name is
local PlayerData = DataStoreService:GetDataStore("PlayerData")

-- Quite obvious isn't it?
local IntValuesNames = {
	"Wins",
	"Points"
}

Players.PlayerAdded:Connect(function(Player)
	-- First of all, make a variable that will be called "data"
	local data
	
	-- Make a pcall function about getting the "PlayerData" async
	local success, errorMessage = pcall(function()
		data = PlayerData:GetAsync("PlayerData_"..Player.UserId)
	end)
	
	-- Check if the success isn't true. If then, it will print the "errorMessage"
	if success ~= true then
		warn(errorMessage)
	else
		print("Successfully loaded data!")
	end
	
	-- Make a folder named "leaderstats" and parent it inside of the Player
	local Leaderstats = Instance.new("Folder", Player)
	Leaderstats.Name = "leaderstats" -- MAKE SURE THE "L" IS LOWERCASE OR ELSE THE LEADERSTATS WON'T SHOW
	
	for Index, Name in pairs(IntValuesNames) do
		-- Create a IntValue and parent it to leaderstats. And this time finally naming it.
		local IntValue = Instance.new("IntValue", Leaderstats)
		IntValue.Name = Name
		
		-- Check if the data isn't nil
		if data ~= nil then
			-- Set the IntValue "value" to the set data
			IntValue.Value = data[Index]
		else
			IntValue.Value = 0
		end
	end
end)

-- On the player removal, it will save the IntValues "value"
Players.PlayerRemoving:Connect(function(Player)
	-- Get the player's leaderstats
	local Leaderstats = Player:FindFirstChild("leaderstats")
	
	-- Table because table gud
	local SavedStuffs = {}
	
	-- For every IntValues in there will be inserted to the table, "SavedStuffs"
	for _, IntValue in pairs(Leaderstats:GetChildren()) do
		-- Insert the value to the "SavedStuffs"
		table.insert(SavedStuffs, IntValue.Value)
	end
	
	-- Make a pcall function about setting the "PlayerData" async
	local success, errorMessage = pcall(function()
		PlayerData:SetAsync("PlayerData_"..Player.UserId, SavedStuffs)
	end)
	
	-- Check if the success isn't true. If then, print the "errorMessage"
	if success ~= true then
		print(errorMessage)
	else
		print("Successfully saved data!")
	end
end)

Ah yes, spoiler. Who spread this spoiler shall be uh, I don’t know

Edit 1 : English and code mistakes.
Edit 2 : @DasKairo point out that player can change their username and will loss their data. My brain suck

3 Likes

Although I know what this Tutorial is intended for, this topic has been covered so much, that it doesnt really make sense to be creating another one, as you can easily find it on YouTube, and many similar ones on the DevForum.

You should not be using the Name of the Player when using GetAsync, aswell as SetAsync as that is not a permanent piece of Data the Player has, a change to the username can result in their data being wiped.

You can also Simplify the code to be shorter, which instead of having a data variable, you can instead add it into the pcall, like so:

success, result = pcall(function()
   return -- code
end)

-- if successful, it will return either Data or nil
-- if Failed, it will return an error.

But instead of adding return and a function, we can just shorten the code to fire only one function instead of two by changing the Colon : to a Period, if used like this it would be the exact same:

PlayerData:GetAsync("PlayerData_"..Player.UserId)
PlayerData.GetAsync(PlayerData, "PlayerData_"..Player.UserId)

The only difference is that you have to manually specify itself, but using this, we can now just format it like this:

pcall(
    PlayerData.GetAsync, -- this is the function using Period
    PlayerData, -- manually inserting itself
    "PlayerData_"..Player.UserId -- Variables
)

Whats cool about this is that Luau will know what you are trying to refer to, and will setup the function Accordingly if you type in everything correctly.

So the final result would look like this:

--Before:
local data
local success, errorMessage = pcall(function()
	data = PlayerData:GetAsync("PlayerData_"..Player.UserId)
end)

-- After:
local success, result = pcall(PlayerData.GetAsync, PlayerData, "PlayerData_"..Player.UserId)
5 Likes

You forgot game:BindToClose().

This function binds a function to be called prior to the game shutting down.

When using the DataStoreService, best practice is to bind a function saving all unsaved data to DataStores using BindToClose. Otherwise, data may be lost if the game shuts down unexpectedly.

3 Likes

@DasKairo I forgot that player can change their username name, I might as well delete that part.

One other thing you forgot is to make sure the data was loaded successfully before saving it.

Good work, look forward to more tutorials from you. Some mistakes people pointed out you can easily fix and this is a solid work. I think new tutorials should always come out over the same topic, many people code different ways and roblox changes things a lot. I know people aren’t going to be looking for a leaderboard guide from 2010 lol at least they shouldn’t.

1 Like