How to make a simple global leaderboard

Do you want it to be monthly or to not be monthly? I’m confused as to what you want here. The leaderboard, by default, is not monthly. For it to be monthly, see the FAQ.

To make it work as a monthly leaderboard u will have to reset the datastore every month, not manually though. u can add a script in it to make it work.
you can maintain multiple datastore to do so, so that you also have a global leaderboard in case u need both.

I did a all time leaderboard on my game

I tried to make copies of a main leaderboard and then copy it to the secondary ones, but when the place is loaded, the secondary boards seem to receive the content very slowly.

This is the single part where I changed the script:

for number,d in pairs(data) do--Loop through our new data
		local name = d[1]
		local val = d[2]
		local image = d[3]
		local color = Color3.new(1, 1, 1)--Default color
		if number == 1 then
			color = Color3.new(1, 0, 0)--1st place color
		elseif number == 2 then
			color = Color3.new(1, 0.854902, 0.109804)--2nd place color
		elseif number == 3 then
			color = Color3.fromRGB(255, 158, 21)--3rd place color
		end
		local new = sample:Clone()--Make a clone of the sample frame
		new.Name = name--Set name for better recognition and debugging
		new.LayoutOrder = number--UIListLayout uses this to sort in the correct order
		new.Image.Image = image--Set the image
		new.Image.Place.Text = number--Set the place
		new.Image.Place.TextColor3 = color--Set the place color (Gold = 1st)
		new.PName.Text = name--Set the username
		val = MoneyLib.HandleMoney(val)
		new.Value.Text = val--Set the amount of points
		new.Value.TextColor3 = color--Set the place color (Gold = 1st)
		new.PName.TextColor3 = color--Set the place color (Gold = 1st)
		new.Parent = sf--Parent to scrolling frame

		--Trying to copy the main leaderboard to the secondary ones
		for i, list in pairs(workspace.LeaderBoards.SecondaryLeaderboards:GetChildren()) do
			list.CashBoard.Leader.SurfaceGui.ScrollingFrame:Destroy()
			local clone = sf:Clone()
			clone.Parent = list.CashBoard.Leader.SurfaceGui
		end
	end

Any ideas on how to make this work? I feel like the lines where I implemented the copying is not the right place, but I couldn’t find where else to make it.

It’s hard to tell when you’ve removed a chunk of the original code and I don’t know how your object hierarchy looks. My best bet would be testing and seeing if it gets created and where.

I didn’t include the rest of the code because it is exactly the same as the original one with the single change made being the addition of the four lines that are copying it. I thought it was reductant to include it.

However, this is the full code of the cash board:

local sg = script.Parent
local sample = script:WaitForChild("Sample") --Our Sample frame
local sf = sg:WaitForChild("ScrollingFrame") --The scrolling frame
local ui = sf:WaitForChild("UI") --The UI list layout

local MoneyLib = require(game:GetService("ReplicatedStorage").MoneyLib)
local dataStoreService = game:GetService("DataStoreService")
--The data store service
local dataStore = dataStoreService:GetOrderedDataStore("CashBoard")
--Get the data store with key "Leaderboard"

wait(10)
while true do
	for i,plr in pairs(game.Players:GetChildren()) do--Loop through players
		if plr.UserId>0 then--Prevent errors
			local w = math.floor(--[[Value that I count for the cash]])
			if w then
				pcall(function()
					--Wrap in a pcall so if Roblox is down, it won't error and break.
					dataStore:UpdateAsync(plr.UserId,function(oldVal)
						--Set new value
						return tonumber(w)
					end)
				end)
			end
		end
	end    
	local smallestFirst = false--false = 2 before 1, true = 1 before 2
	local numberToShow = 100--Any number between 1-100, how many will be shown
	local minValue = 1--Any numbers lower than this will be excluded
	local maxValue = 10e30--(10^30), any numbers higher than this will be excluded
	local pages = dataStore:GetSortedAsync(smallestFirst, numberToShow, minValue, maxValue)
	--Get data
	local top = pages:GetCurrentPage()--Get the first page
	local data = {}--Store new data
	for a,b in ipairs(top) do--Loop through data
		local userid = b.key--User id
		local points = b.value--Points
		local username = "[Failed To Load]"--If it fails, we let them know
		local s,e = pcall(function()
			username = game.Players:GetNameFromUserIdAsync(userid)--Get username
		end)
		if not s then--Something went wrong
			warn("Error getting name for "..userid..". Error: "..e)
		end
		local image = game.Players:GetUserThumbnailAsync(userid, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size150x150)
		--Make a image of them
		table.insert(data,{username,points,image})--Put new data in new table
	end
	ui.Parent = script
	sf:ClearAllChildren()--Remove old frames
	ui.Parent = sf
	for number,d in pairs(data) do--Loop through our new data
		local name = d[1]
		local val = d[2]
		local image = d[3]
		local color = Color3.new(1, 1, 1)--Default color
		if number == 1 then
			color = Color3.new(1, 0, 0)--1st place color
		elseif number == 2 then
			color = Color3.new(1, 0.854902, 0.109804)--2nd place color
		elseif number == 3 then
			color = Color3.fromRGB(255, 158, 21)--3rd place color
		end
		local new = sample:Clone()--Make a clone of the sample frame
		new.Name = name--Set name for better recognition and debugging
		new.LayoutOrder = number--UIListLayout uses this to sort in the correct order
		new.Image.Image = image--Set the image
		new.Image.Place.Text = number--Set the place
		new.Image.Place.TextColor3 = color--Set the place color (Gold = 1st)
		new.PName.Text = name--Set the username
		val = MoneyLib.HandleMoney(val)
		new.Value.Text = val--Set the amount of points
		new.Value.TextColor3 = color--Set the place color (Gold = 1st)
		new.PName.TextColor3 = color--Set the place color (Gold = 1st)
		new.Parent = sf--Parent to scrolling frame
		
		for i, list in pairs(workspace.LeaderBoards.SecondaryLeaderboards:GetChildren()) do
			list.CashBoard.Leader.SurfaceGui.ScrollingFrame:Destroy()
			local clone = sf:Clone()
			clone.Parent = list.CashBoard.Leader.SurfaceGui
		end
		
	end
	wait()
	sf.CanvasSize = UDim2.new(0,0,0,ui.AbsoluteContentSize.Y)
	--Give enough room for the frames to sit in
	wait(120)
end

The frames of every player get created, and they parrent correctly to the scrolling frames of the secondary boards. The problem occurs when the server/map is loading. The main 4 boards receive their content instantly, but the secondary ones receive every frame (of every player on the board) very slowly (I think it is one frame for every 10 seconds).

This is the hierarchy:
image

You’re getting the children of a non-existent (or not shown) TycoonLeaderboards. Assuming it does exist, and follow the same pattern as the rest of them, you seem to index each child directly with CashBoard when you’d need to index with Set1 first.

What exactly is your use case? Is there really no errors? If possible, a place file would help.

Also, it is possible that you might be cloning large copies of instances unintentionally, which would explain the loading issues.

Sorry for the confusion, I corrected the name of the model that their children are indexed.
Set1, Set2, Set3 etc are all identical (they contain the same children). So in

for i, list in pairs(workspace.LeaderBoards.SecondaryLeaderboards:GetChildren()) do
	list.CashBoard.Leader.SurfaceGui.ScrollingFrame:Destroy()
	local clone = sf:Clone()
	clone.Parent = list.CashBoard.Leader.SurfaceGui
end

list is every Set and list.CashBoard refers to every CashBoard in the set.

My use is that I want to have the main 4 leaderboards copied around the map. So every “Set” is a set of copies of the main leaderboards.

I don’t think I can provide a place file atm, but I can edit a new place to resemble my implementation if it helps.

If you’re cloning it after the layout is finished, you should be fine. If not (like you are now), then you will have issues. Check what is actually cloned to see.

1 Like

Oops, I should’ve put it after the for. Now it works, thanks!

Does the leaderboard display on the server only or it only displays cross server?

I haven’t tried but i need to make sure

As the name of this tutorial implies, this leaderboard is global.

Does daily/monthly/yearly leaderboard the points value to 0 every time it reset or it will stays on the same value

Ok for some example, a guy has 3 points for this month. Is it going to reset the points to 0 points for the next month? Or it will stay the value that the guy had last month?

Please read the FAQ or search this topic before asking a question.

This answer requires the context of the FAQ question about daily, monthly, and yearly leaderboards.

This depends how you set the data. Say you want to have a daily wins leaderboard. If after each win, you increment their wins by 1, it will reset, as it will look in a new datastore and not the previous one.

If, however, you just set their data to their total wins, it will not reset. This is because the value never gets reset to zero when the next day starts, so it will just be the same constantly.

1 Like

is it possible for me to get the minutes in os.time so that i could test this a lot quicker?

local dateTable = os.date("*t", os.time())
local key = "Y:"..dateTable["year"].." M:"..dateTable["month"].." D:"..dateTable["day"]..dateTable["hour"]..dateTable["min"]
print(key)
print(dateTable["min"])
game.Players.PlayerAdded:Connect(function(player)
	local value = Instance.new("IntValue")
	value.Name = 'Value'
	value.Parent = player
	local stats = game.DataStoreService:GetDataStore(key)
	local data = stats:GetAsync(player.UserId)
	if data then
		value.Value = data.Tests
	end
	value.Value = value.Value + 1
	while true do
		if os.date("*t", os.time())["min"] ~= key then
			key = os.date("*t", os.time())
			player.Value.Value = 0
			print(dateTable["min"])
		end
		wait(1)
	end
end)

game.Players.PlayerRemoving:Connect(function(player)
	local stats = game.DataStoreService:GetDataStore(key)
	local data = stats:SetAsync(player.UserId, {
		["Tests"] = player.Value.Value
	})
end)

when i print the key it would only show the year, month and days, and the days were weird because it appeared in the hundred thousands

Because you’re adding day + hour + minute, so day 30 at 5:30 would be D: 30530.

Additionally, you don’t need to run a while true loop to reset the key every second, and you should use task.wait instead.

i have a another question, in the script im trying to make daily, what im doing is getting the minute every second then if the script realises the minute has changed, it changes the key to the new date, iis this efficient?

local sg = script.Parent --Surface GUI
local sample = script:WaitForChild("Sample") --Our Sample frame
local sf = sg:WaitForChild("ScrollingFrame") --The scrolling frame
local ui = sf:WaitForChild("UI") --The UI list layout
local dateTable = os.date("*t", os.time())
local key = "Y:"..dateTable["year"].." M:"..dateTable["month"].." D:"..dateTable["day"].." H:"..dateTable["hour"].." I:"..dateTable["min"]


local dataStoreService = game:GetService("DataStoreService")
--The data store service
local dataStore = dataStoreService:GetOrderedDataStore(key)
--Get the data store with key "Leaderboard"
wait(10)
while true do
	for i,plr in pairs(game.Players:GetChildren()) do--Loop through players
		if plr.UserId>0 then--Prevent errors --PointsService
			local w = plr.Value.Value --Get point balance
			if w then
				pcall(function()
				--Wrap in a pcall so if Roblox is down, it won't error and break.
					dataStore:UpdateAsync(plr.UserId,function(oldVal)
				        --Set new value
						return tonumber(w)
					end)
				end)
			end
		end
	end    
	local smallestFirst = false--false = 2 before 1, true = 1 before 2
    local numberToShow = 100--Any number between 1-100, how many will be shown
    local minValue = 1--Any numbers lower than this will be excluded
    local maxValue = 10e30--(10^30), any numbers higher than this will be excluded
    local pages = dataStore:GetSortedAsync(smallestFirst, numberToShow, minValue, maxValue)
    --Get data
    local top = pages:GetCurrentPage()--Get the first page
	local data = {}--Store new data
	for a,b in ipairs(top) do--Loop through data
		local userid = b.key--User id
		local points = b.value--Points
		local username = "[Failed To Load]"--If it fails, we let them know
		local s,e = pcall(function()
		 username = game.Players:GetNameFromUserIdAsync(userid)--Get username
		end)
		if not s then--Something went wrong
		   warn("Error getting name for "..userid..". Error: "..e)
		end
		local image = game.Players:GetUserThumbnailAsync(userid, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size150x150)
		--Make a image of them
		table.insert(data,{username,points,image})--Put new data in new table
	end
	ui.Parent = script
	sf:ClearAllChildren()--Remove old frames
	ui.Parent = sf
	for number,d in pairs(data) do--Loop through our new data
		local name = d[1]
		local val = d[2]
		local image = d[3]
		local color = Color3.new(1,1,1)--Default color
		if number == 1 then
			color = Color3.new(1,1,0)--1st place color
		elseif number == 2 then
			color = Color3.new(0.9,0.9,0.9)--2nd place color
		elseif number == 3 then
			color = Color3.fromRGB(166, 112, 0)--3rd place color
		end
		local new = sample:Clone()--Make a clone of the sample frame
		new.Name = name--Set name for better recognition and debugging
        new.LayoutOrder = number--UIListLayout uses this to sort in the correct order
		new.Image.Image = image--Set the image
		new.Image.Place.Text = number--Set the place
		new.Image.Place.TextColor3 = color--Set the place color (Gold = 1st)
		new.PName.Text = name--Set the username
		new.Value.Text = val--Set the amount of points
		new.Value.TextColor3 = color--Set the place color (Gold = 1st)
		new.PName.TextColor3 = color--Set the place color (Gold = 1st)
		new.Parent = sf--Parent to scrolling frame
	end
	wait()
	sf.CanvasSize = UDim2.new(0,0,0,ui.AbsoluteContentSize.Y)
	--Give enough room for the frames to sit in
	wait(120)
end

while true do
	dateTable = os.date("*t", os.time())
	print(dateTable["min"])
	if dateTable["min"] ~= key[10] then
		print(key[10])
		key = os.date("*t", os.time())
		for i,v in game.Players:GetChildren() do
			v.Value.Value = 0
		end
		print("lower than value")
		print(dateTable["min"])
	else
	end
	wait(1)
end

Additionally i would like to know how to get the value of dataTable[“min”] from the variable key because i do not know how

and if your wondering why its minutes instead of days, its becausing im testing it, if it wasnt obvious

one question, why won’t this work? I don’t see anything wrong with it and there are no errors

while true do
	local newdateTable = os.date("*t", os.time())
	print(newdateTable["min"])
	if newdateTable["min"] ~= dateTable["min"] then
		key = "Y:"..newdateTable["year"].." M:"..newdateTable["month"].." D:"..newdateTable["day"].." H:"..newdateTable["hour"].." I:"..newdateTable["min"]
		min = newdateTable["min"]
		for i,v in game.Players:GetChildren() do
			v.Value.Value = 0
		end
		print("lower than value")
		print(dateTable["min"])
	else
	end
	wait(1)
end

Where do you set datetable? That will work as long as you update datetable.

the while true updates the dateTable that was defined in the very beginning, my question is why is it not sensing when the newdateTable has a different minute to the original one