Need Help: Global Leaderboard Shows Player From The Bottom To Be The Top Of List & Game Is Laggy For Some Reason

Hello everyone! This is going to be my first ever post so apologies for any mistakes I’ll be making here :pray:

I have 2 problems to fix a leaderboard that would somehow make players with the worst time of speedrun(the highest in time) to be placed onto the top of the list(still shows the same speedrun time tho).
So for context me and my friend @hahaharee0814 have already finished making a tower obby game and we want to show the leaderboard of best speedruns(top 50) inside each respective towers. After publishing it the leaderboard seems to work fine(when there were fewer entries inside the leaderboard), but after around 20 entries from 20 different players or so the leaderboard started to error similar to the screenshots(but these screenshots I’ll be sending were taken more recent and after a few medium updates)

Here are the relevant scripts

Server

local function fetchGlobalLeaderboardData()
	local leaderboardData = {}

	local function getDataStorePage()
		return orderedDataStore:GetSortedAsync(true, 50):GetCurrentPage()  -- Sorting in ascending order (smallest time on top)
	end

	local success, data = retryWithExponentialBackoff(getDataStorePage, 5, false)

	if success then
		for _, entry in ipairs(data) do
			local userId = tonumber(entry.key)
			local username = "Unknown"
			if userId then
				local success, result = pcall(Players.GetNameFromUserIdAsync, Players, userId)
				if success then
					username = result
				else
					warn("Failed to get username for UserId:", userId, ":", result)
				end
			end

			local timeInSeconds = entry.value / 1000
			if timeInSeconds > MINIMUM_TIME_THRESHOLD then
				table.insert(leaderboardData, {
					userId = userId,
					username = username,
					time = timeInSeconds
				})
			end
		end
	else
		warn("Failed to fetch global leaderboard data for Yangnyeom:", data)
	end

	-- Sort data by their time (ascending)
	table.sort(leaderboardData, function(a, b)
		return a.time < b.time
	end)

	return leaderboardData
end

-- skip the other unrelevant parts(the grant speedrun badge function)

local function updateLeaderboard()
	local success, leaderboardData = pcall(fetchGlobalLeaderboardData)
	print("Fetched leaderboard data:", leaderboardData)

	if not success then
		warn("Failed to update leaderboard:", leaderboardData)
		return
	end

	UpdateLeaderboardEventYangnyeom:FireAllClients(leaderboardData)

	for i, data in ipairs(leaderboardData) do
		if data.time > MINIMUM_TIME_THRESHOLD then
			local userId = data.userId
			local username = data.username
			local stand

			if i == 1 then
				stand = FirstPlaceStand
			elseif i == 2 then
				stand = SecondPlaceStand
			elseif i == 3 then
				stand = ThirdPlaceStand
			end

			if stand then
				applyAppearanceToDummy(stand, userId, username, i)

				local animationId = LeaderboardYangnyeom:WaitForChild("AnimationToPlay").AnimationId
				AnimationPlayer.playAnimation(stand, animationId)

				-- Check and grant badge if applicable
				if userId then
					local player = Players:GetPlayerByUserId(userId)
					if player then
						grantSpeedrunBadge(player, data.time)
					end
				else
					warn("Invalid UserId:", userId)
				end
			end
		end
	end
end


while true do
	updateLeaderboard()
	wait(LEADERBOARD_UPDATE_INTERVAL)
end

Client

local function updateLeaderboardGui(leaderboardData)
	print("Received leaderboard data:", leaderboardData)

	-- Sort leaderboard data by time (ascending)
	table.sort(leaderboardData, function(a, b)
		return a.time < b.time
	end)

	-- Delete the  existing entries and then create the new entries afterwards, the problem could be lying here somewhere I believe
	for _, data in ipairs(leaderboardData) do
		if data.time > MINIMUM_TIME_THRESHOLD then
			-- Delete existing entry if it exists
			local existingEntry = ListContent.Items:FindFirstChild(data.username)
			if existingEntry then
				existingEntry:Destroy()
			end

			-- Create a new entry 
			local entry = Sample:Clone()
			entry.Name = data.username
			entry.Values.Username.Text = data.username
			entry.Values.Yangnyeom.Text = string.format("%.2f", data.time) -- Format time to 2 decimal
			entry.Parent = ListContent.Items
			entry.Visible = true
		end
	end

	-- Adjust the position of each entry
	local entries = ListContent.Items:GetChildren()
	local entryCount = 0  -- This is to track the valid entries to skip the original Sample

	for i, entry in ipairs(entries) do
		if entry ~= Sample then  -- We skip the original Sample entry
			if entry:IsA("GuiObject") then
				entryCount = entryCount + 1
				entry.LayoutOrder = entryCount

				if entry:FindFirstChild("Values") and entry.Values:FindFirstChild("Index") then
					entry.Values.Index.Text = tostring(entryCount) -- Set the index number

					-- Apply special colors based on index basically
					if entryCount == 1 then
						entry.Values.Index.TextColor3 = Color3.fromRGB(255, 215, 0) -- Gold color
					elseif entryCount == 2 then
						entry.Values.Index.TextColor3 = Color3.fromRGB(192, 192, 192) -- Silver color
					elseif entryCount == 3 then
						entry.Values.Index.TextColor3 = Color3.fromRGB(205, 127, 50) -- Bronze color
					else
						entry.Values.Index.TextColor3 = Color3.fromRGB(0, 0, 0) -- Default color
					end
				end
			end
		end
	end
end


UpdateLeaderboardEvent.OnClientEvent:Connect(function(leaderboardData)
	updateLeaderboardGui(leaderboardData)
end)


1st Update

2nd Update(notice how there is no data with the time 1503? It’s the worst one but hmmm doesn’t update it to the top like the rest)

A few updates later(still no 1503 I see)

I can’t seem to wrap my head around this lol, tried changing the logic a bit on the entry but still wouldn’t work. Maybe I need to put more conditional statements haha. The fetching leaderboard data print actually shows the correct order even after the updated leaderboard(server) and so is the receiving leaderboard data(client) prints. But the weird part is that it doesn’t actually update the worst time(like the most bottom one) to the top, only the 2nd worst and then so on. What a weird error huh?

Also for anyone wondering what datastore I’m using, it’s Suphi’s Datastore thanks @5uphi !!

And the 2nd problem is the lag, now I don’t know what might be causing already reduced lag from many scripts but still the same. It could be because of too many RunService connections that never gets stopped(I use RS on many scripts so could be that but I made sure to try and disconnect those when not needed, but sometimes it would just break the script).

But here’s the odd thing, me and my friend(the other dev) doesn’t actually lag while playing the game, even on different devices yet other people says the game is sooo laggy that even one of my other friend said that it’s “laggier than playing on other bigger games”. Here’s a screenshot taken from his PC. Oh yeah his CPU on average is at 78 in this game for some reasonsss

And other players that we invited& some random people also said the same things



A screenshot from the other dev's screen 1

(pretty sure they said lag)

Now here’s screenshots of current scripts from me and the other dev @hahaharee0814


And here’s the script profiler from me

(Sorry for posting too many images, I’m kind of baffled at this point and don’t know what might be the root cause. Could it be from all the RS connections perhaps? And what do you think is more likely to lag the game more? CPU or Ping?)

And for anyone wondering why it might be you could try the game for yourself, it’s called Korean Chicken Tower and make sure to give some suggestions aswell for the game lol(if you want)

TLDR; 1st problem: The leaderboard in my game somehow would show players with the worst ranking to be at the top of the ranking for some reason yet still stores the same data for each username. And it keeps putting the player from the worst rankings to the top(as if it’s trying to recreate the data over again but one by one.). Weird thing is that the worst of the worst(sorry hehe) data doesn’t get updated to the top though. 2nd problem: My game is laggy and I don’t know why, already made fixes for the lag but still ultimately the same.

Anyways thanks for reading till the end lol

4 Likes
local function getDataStorePage()
	return orderedDataStore:GetSortedAsync(false, 50):GetCurrentPage()
end
2 Likes

So sorry, I had actually found out the solution for the leaderboard myself(and forgot to mark a solution so here it is). Turns out I just need to change the way I write the code for cloning and deleting the entries. Here are the changes I made and a comparison of the logic before and after:

Previous logic

for _, data in ipairs(leaderboardData) do
		if data.time > MINIMUM_TIME_THRESHOLD then
			-- Delete existing entry if it exists
			local existingEntry = ListContent.Items:FindFirstChild(data.username)
			if existingEntry then
				existingEntry:Destroy()
			end

			-- Create a new entry 
			local entry = Sample:Clone()
			entry.Name = data.username
			entry.Values.Username.Text = data.username
			entry.Values.Yangnyeom.Text = string.format("%.2f", data.time) -- Format time to 2 decimal
			entry.Parent = ListContent.Items
			entry.Visible = true
		end
	end

Updated logic

for _, child in ipairs(ListContent.Items:GetChildren()) do
	if child ~= Sample and child.Name ~= Sample and child:IsA("GuiObject") then

		child:Destroy()
	end
end

-- Create new entries
for _, data in ipairs(leaderboardData) do
	if data.time > MINIMUM_TIME_THRESHOLD then


		-- Create a new entry
		local entry = Sample:Clone()
		entry.Name = data.username
		entry.Values.Username.Text = data.username
		entry.Values.Yangnyeom.Text = string.format("%.2f", data.time) -- Format time to 2 decimal places
		entry.Parent = ListContent.Items
		entry.Visible = true

	end
end

I basically added more checks for the original Sample to make sure that the original Sample doesn’t get counted as an entry by adding a check of child.name like this(yes this is pretty unnecessary I knoww but kind of necessarry for my case)

if child ~= Sample and child.Name ~= Sample and child:IsA("GuiObject") then

And another changes made is that I separated the logic of clear all existing entries and cloning all entries(in the orderedDataStore) into 2 different ipairs loops. Because the previous method I checked the same entry twice which is not the brightest idea to say the least.

Also for the lag, I think I also figured out why. It’s probably because of StreamingEnabled and that most of the run service connections weren’t able to be disconnected when player teleports soo far( I have many parts that teleports player to really far places) And when teleporting happens too much without properly connecting and disconnecting RunService connections it would break many scripts. Especially ones using both CollectionService & TweenService inside of it for some reason. Or I might be wrong idk

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.