Help fixing Roblox Leaderboard Template

  1. What do I want to achieve?
    I want to patch and fix a really cool Roblox leaderboard script from their old Capture Points starter place

  2. What is the issue?
    The issue is I am getting this error in the image below:


    (I know what is causing the error, but I don’t know how to fix the script without breaking it’s functionality)

  3. What solutions have I tried so far?
    I’ve read up on the forum that the problem is related to destroying() a parent then calling for it again,
    I’ve tried commenting out the ClearAllChildren() which semi-fixes the leaderboard GUI and lets me open and close it, but no player names or stats display so I think the ClearLeaderboardFrame() function might be very important to the functionality of the leaderboard script, I’m not sure what to do exactly to get this old roblox leaderboard gui to display names and stats and to work properly. Any help would be greatly appreciated and it would be cool to bring back what was once a free and simple script back to the public.

This is a picture of me getting the GUI to display but as you can see no players or stats are displayed even testing with 2+ clients

local function WaitForChild(parent, instance)
	while parent:FindFirstChild(instance) == nil do 
		wait()
	end 
	return parent[instance]
end 

-- Global vars -----
--- CONSTANTS ----
local FRAME_X_MARGIN_SIZE = 20
local FRAME_Y_OFFSET = 160
local PLAYER_LIST_START_Y_OFFSET = 43
local PIXELS_Y_BETWEEN_PLAYER_ROWS = 20
local PIXELS_X_BETWEEN_STAT_COLUMNS = 65
local PLAYERS_X_PADDING_PIXELS = 10


local DARK_FONT_COLOR = Color3.new(0, 0, 0)
local LIGHT_FONT_COLOR = Color3.new(1, 1, 1)
local TOGGLES = false

local PlayersService = game.Players
local MainGui  = script.Parent
local LeaderboardFrame = WaitForChild(MainGui, "LeaderboardFrame")
local LeaderboardButton = WaitForChild(MainGui, 'LeaderboardButton')
local TeamsService = game:GetService("Teams") 
local DebrisService = game:GetService("Debris")
local MyMouse = PlayersService.localPlayer:GetMouse()
local PlayerFieldsList = {} -- This one stores all the fields to help in removal 
local Gui = {} -- Class, that contains functions to create various gui objects

-- This variable tells us if atleast one team exists
local TeamsExist = false 
-- In here we store a parallel representation of the leaderboard gui heirarchy in the data model
local TeamFramesList = {}
-- This table lets us index a TeamColor to the GUI for that team
local TeamColorToTeamFrame = {}

local StatTextLabel = Instance.new("TextLabel")
StatTextLabel.Font = Enum.Font.Legacy
StatTextLabel.FontSize = Enum.FontSize.Size10
StatTextLabel.BackgroundTransparency = 1.0
StatTextLabel.Size = UDim2.new(0, 0, 0, 0)
StatTextLabel.TextXAlignment = Enum.TextXAlignment.Right
StatTextLabel.ZIndex = 10
StatTextLabel.TextColor3 = LIGHT_FONT_COLOR										


local PlayerTextLabel = Instance.new("TextLabel")
PlayerTextLabel.BackgroundTransparency = 1.0
PlayerTextLabel.Font = Enum.Font.Arial
PlayerTextLabel.FontSize = Enum.FontSize.Size18
PlayerTextLabel.Size = UDim2.new(0, 0, 0, 0)
PlayerTextLabel.TextXAlignment = Enum.TextXAlignment.Left
PlayerTextLabel.ZIndex = 10
PlayerTextLabel.TextColor3 = LIGHT_FONT_COLOR	


-- For luminance calculation formula look at: http://en.wikipedia.org/wiki/Grayscale
local function IsDark(color)
	local cieLuminance = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b
	return cieLuminance < 0.5
end

local function CreateTeamFrames()
	local numberOfTeams = #TeamsService:GetTeams()
	local sizeOfTeamFrame = math.min(1 / 3, 1 / numberOfTeams)
	local templateFrame = LeaderboardFrame:FindFirstChild('TemplateFrame')
	for teamNumber, team in pairs(TeamsService:GetTeams()) do
		if templateFrame then
			local newTeamFrame = templateFrame:Clone()
			newTeamFrame.Name = team.Name
			newTeamFrame.Visible = true
			newTeamFrame.BackgroundColor3 = team.TeamColor.Color
			newTeamFrame.Parent = LeaderboardFrame
			newTeamFrame.Position = UDim2.new(sizeOfTeamFrame * (teamNumber - 1), FRAME_X_MARGIN_SIZE, 0, FRAME_Y_OFFSET)
			-- This special case is needed because we math.min the size of the frame
			if numberOfTeams == 1 then
				newTeamFrame.Position = UDim2.new(0.25, 20, 0, 160)
				sizeOfTeamFrame = 0.5
			elseif numberOfTeams == 2 then
				newTeamFrame.Position = UDim2.new((1 - (2 / 3)) / 2 + 1 / 3 * (teamNumber - 1), FRAME_X_MARGIN_SIZE, 0, FRAME_Y_OFFSET)
			end
			newTeamFrame.Size = UDim2.new(sizeOfTeamFrame, -2 * FRAME_X_MARGIN_SIZE, 0.5, 0)
			if newTeamFrame:FindFirstChild('Team') then
				newTeamFrame:FindFirstChild('Team').Text = team.Name
			end
			TeamColorToTeamFrame[team.TeamColor.Name] = newTeamFrame
		end
	end
end

local function CreateNeutralTeamFrame()
	local templateFrame = LeaderboardFrame:FindFirstChild('TemplateFrame')
	if templateFrame then
		local newTeamFrame = templateFrame:Clone()
		newTeamFrame.Name = "TeamlessFrame"
		newTeamFrame.Visible = true
		newTeamFrame.BackgroundColor3 = BrickColor.new("Black").Color
		newTeamFrame.Parent = LeaderboardFrame
		newTeamFrame.Position = UDim2.new(0.25, 0, 0, FRAME_Y_OFFSET)
		newTeamFrame.Size = UDim2.new(1 / 2, 0, 0.7, 0)
		if newTeamFrame:FindFirstChild('Team') then
			newTeamFrame:FindFirstChild('Team').Text = "Scores"
		end
	end	
end

--[[

	// Here is the thing - if the Leaderboard frame is split into multiple frames 
	// we assume that each of those frames represent different teams and 
	// that they are positioned correctly 
]]

local function MirrorTemplateFrame()
	-- The team color thing is annoying, ideally I want the frames 
	-- to be named by the team names instead of ridiculous team color things 
	-- But in this case, the frames are named after the brick color string
	local TeamFrames = LeaderboardFrame:GetChildren() 
	for i = 1, #TeamFrames do 
		if TeamFrames[i] then 
			TeamsExist = true 
			local t = {} 	
			local CurrentTeamFrame = TeamFrames[i]	
			t[CurrentTeamFrame] = {} 
			local CurrentTeamElements = TeamFrames[i]:GetChildren() 
			for j = 1, #CurrentTeamElements do 
				table.insert(t[CurrentTeamFrame], CurrentTeamElements[j]) 
			end 
			table.insert(TeamFramesList, t)		
		end 
	end 
end

local function ClearLeaderboardFrame()
	-- Clear the entire leaderboard so that we can repopulate it with new info
	LeaderboardFrame:ClearAllChildren()
	for _, t in pairs(TeamFramesList) do
		for frame, children in pairs(t) do
			frame.Parent = LeaderboardFrame
			for i = 1, #children do
				children[i].Parent = frame
			end
			frame.Parent = LeaderboardFrame
		end
	end
end

-- Currently we are clearing the leaderboard and recreating it so
-- that it will stay up to date no matter what kinds of stat changes
-- player joins/leaves occur
local function PopulateLeaderboard()
	-- First clear the leaderboard 	
	if TeamsExist then
		ClearLeaderboardFrame()
	end

	-- Add the labels for the stat columns
	if PlayersService.LocalPlayer ~= nil then
		for _, t in pairs(TeamFramesList) do
			for frame, children in pairs(t) do
				-- Update each teams scores by finding the team that corresponds to this frame
				local frameTeamColor
				for _, team in pairs(TeamsService:GetTeams()) do
					if team.Name == frame.Name then
						frameTeamColor = team.TeamColor
						if frame:FindFirstChild('TeamScore') ~= nil then
							frame:FindFirstChild('TeamScore').Text = team.Score
						end
					end
				end
				-- Get the leaderstats object
				local leaderstats = WaitForChild(PlayersService.LocalPlayer, "leaderstats")
				local leaderstatsList = leaderstats:GetChildren()
				local counter = 0 
				for _, stat in pairs(leaderstatsList) do
					-- For each stat we create a label for the column
					if stat:IsA("IntValue") or stat:IsA("NumberValue") or stat:IsA("StringValue") then 
						local stat_TextLabel = StatTextLabel:Clone()
						stat_TextLabel.Name = stat.Name									
						stat_TextLabel.Text = stat.Name
						stat_TextLabel.Position = UDim2.new(1, (-PIXELS_X_BETWEEN_STAT_COLUMNS * counter) - PLAYERS_X_PADDING_PIXELS, 0, 15)
						stat_TextLabel.Parent = frame

						counter = counter + 1
						-- For each stat create a sum label at the bottom
						local sum = 0
						for _, player in pairs(PlayersService:GetPlayers()) do
							if frameTeamColor and player.TeamColor == frameTeamColor and player:FindFirstChild('leaderstats') then
								local currStat = player:FindFirstChild('leaderstats'):FindFirstChild(stat.Name)
								if currStat:IsA('IntValue') or currStat:IsA('NumberValue') or currStat:IsA('IntConstrainedValue') then
									sum = sum + currStat.Value
								end
							end
						end
						local stat_SumLabel = stat_TextLabel:Clone()
						stat_SumLabel.Name = "Team" .. stat_TextLabel.Name
						stat_SumLabel.Text = sum
						stat_SumLabel.Position = UDim2.new(stat_TextLabel.Position.X.Scale, stat_TextLabel.Position.X.Offset, 1, -20)
						stat_SumLabel.Parent = frame
					end
				end
			end
		end
	end	

	-- This variable will keep track of how many players are in each team's frame
	-- so we now how far down to put the next player in the score list
	local numberOfPlayersInTeamSoFar = {}

	local players = PlayersService:GetPlayers()
	for i = 1, #players do 
		if players[i] then
			local xOffset = 0 
			local yOffset = 0
			-- Get the leaderstats object
			local leaderstats = WaitForChild(players[i], "leaderstats")
			local leaderstatsList = leaderstats:GetChildren()
			-- Create the player name
			local playerName_TextLabel = PlayerTextLabel:Clone()
			playerName_TextLabel.Name = players[i].Name
			playerName_TextLabel.Text = players[i].Name

			-- If we have frames for each team, then encapsulate the score inside their team's frame
			if TeamsExist and TeamColorToTeamFrame[players[i].TeamColor.Name] ~= nil then
				local teamFrame = TeamColorToTeamFrame[players[i].TeamColor.Name]
				-- Create or increment the player count
				if not numberOfPlayersInTeamSoFar[players[i].TeamColor.Name] then
					numberOfPlayersInTeamSoFar[players[i].TeamColor.Name] = 1
				else
					numberOfPlayersInTeamSoFar[players[i].TeamColor.Name] = numberOfPlayersInTeamSoFar[players[i].TeamColor.Name] + 1
				end
				playerName_TextLabel.Parent = teamFrame
				yOffset = PLAYER_LIST_START_Y_OFFSET +
							  (numberOfPlayersInTeamSoFar[players[i].TeamColor.Name] - 1) * PIXELS_Y_BETWEEN_PLAYER_ROWS
				playerName_TextLabel.Position = UDim2.new(0, PLAYERS_X_PADDING_PIXELS, 0, yOffset)
			elseif #TeamsService:GetTeams() == 0 then -- Otherwise we will fallback to putting the score inside the root of leaderboard
				if not LeaderboardFrame:FindFirstChild('TeamlessFrame') then CreateNeutralTeamFrame() end
				local teamlessFrame = LeaderboardFrame:FindFirstChild('TeamlessFrame')
				if not numberOfPlayersInTeamSoFar['Neutral'] then
					numberOfPlayersInTeamSoFar['Neutral'] = 1
				else
					numberOfPlayersInTeamSoFar['Neutral'] = numberOfPlayersInTeamSoFar['Neutral'] + 1
				end
				playerName_TextLabel.Parent = teamlessFrame
				yOffset = PLAYER_LIST_START_Y_OFFSET +
							  (numberOfPlayersInTeamSoFar['Neutral'] - 1) * PIXELS_Y_BETWEEN_PLAYER_ROWS
				playerName_TextLabel.Position = UDim2.new(0, PLAYERS_X_PADDING_PIXELS, 0, yOffset)
			end 

			-- For this player we will incrementally place their leaderstats to the right of their name
			local counter = 0 
			for _, stat in pairs(leaderstatsList) do 
				if stat:IsA("IntValue") or stat:IsA("NumberValue") or stat:IsA("StringValue") then
					local stat_TextLabel = playerName_TextLabel:Clone()
					stat_TextLabel.Text = stat.Value
					stat_TextLabel.Position = UDim2.new(1, -PIXELS_X_BETWEEN_STAT_COLUMNS * counter - PLAYERS_X_PADDING_PIXELS, 0, yOffset)
					stat_TextLabel.TextXAlignment = Enum.TextXAlignment.Right
					stat_TextLabel.Parent = playerName_TextLabel.Parent

					counter = counter + 1
					-- If this player isn't on a team then we can add up their points in the neutral team window
					if not TeamsExist or TeamColorToTeamFrame[players[i].TeamColor.Name] == nil then
						local teamlessFrame = LeaderboardFrame:FindFirstChild('TeamlessFrame')
						if teamlessFrame and teamlessFrame:FindFirstChild("Team" .. stat.Name) then
							local statLabel = teamlessFrame:FindFirstChild("Team" .. stat.Name)
							-- Make sure that statlabel's value can be turned into a number
							if tonumber(statLabel.Text) then statLabel.Text = tonumber(statLabel.Text) + stat.Value end
						else
							print("No teamlessframe")
						end
					end

				end 
			end 
		end 
	end
	-- Change all the text and color within the frame to contrast with the background
	for _, frame in pairs(TeamColorToTeamFrame) do
		local textColor = DARK_FONT_COLOR
		if IsDark(frame.BackgroundColor3) then
			textColor = LIGHT_FONT_COLOR
		end
		for _, item in pairs(frame:GetChildren()) do
			if item.Name == 'Team' or item.Name == 'TeamScore' then
				--skip
			elseif item:IsA('TextLabel') then
				item.TextColor3 = textColor
			elseif item:IsA('Frame') then
				item.BorderColor3 = textColor
			end
		end
	end
end 

MyMouse.KeyDown:connect(function(key) 
	if string.lower(key) == 'q' then
		if LeaderboardFrame then
			if TOGGLES then
				LeaderboardFrame.Visible = not LeaderboardFrame.Visible
			else
				LeaderboardFrame.Visible = true
			end
		end
	end
end)

MyMouse.KeyUp:connect(function(key)
	if string.lower(key) == 'q' then
		if LeaderboardFrame then
			if not TOGGLES then
				LeaderboardFrame.Visible = false
			end
		end
	end
end)

LeaderboardButton.MouseButton1Click:connect(function()
	LeaderboardFrame.Visible = not LeaderboardFrame.Visible
end)


CreateTeamFrames()
MirrorTemplateFrame()
PopulateLeaderboard()
Spawn(function() while true do
	PopulateLeaderboard() 
	wait(3)
	end
end)

original roblox game & isolated roblox leaderboard testing zone attached below

Roblox Original Control Points Game Template.rbxl (111.9 KB) (THIS IS THE ORIGINAL GAME)

BrokenRobloxLeaderboardGUI.rbxl (48.6 KB)
(THIS IS THE LEADERBOARDS GUI ISOLATED AND ON ITS OWN FOR TESTING PURPOSES RELATED TO THE POST)

I’m not very experienced but could you try setting the parent of TemplateFrame to LeaderboardFrame before the line that causes this error?

the example file is at the bottom of the post so you can try it for yourself too

1 Like

Is this what you meant?

I made this change and now the leaderboard won’t open at all, but if I comment out line 138 where it says ClearAllChildren, the GUI is toggable again however no stats are displayed.

its sort of what i meant, does this at least remove the error in output?

No, there is no error, and there is no longer a template frame. I do not think this is helping though.

1 Like

Oh then my bad, sorry

Summary

This text will be hidden

Roblox Original Control Points Game Template.rbxl (43.2 KB)

I just removed the ClearAllChildren call and created an empty ‘leaderstats’ folder for each player.

local leaderstats = WaitForChild(PlayersService.LocalPlayer, "leaderstats")

This part of the gui’s code was causing the script to yield. If you add stats to the ‘leaderstats’ folder they should be displayed on the custom leaderboard.

2 Likes

This is really quite amazing! A wonderful fundamental Roblox game script brought back to life.

Tried modifying the script a little and playerleaderstat GUI contents seem to keep duplicating??

More and more textlabels are being created over time for keeping the scoreboard open, the changes I made were opening the leaderboard with “TAB” using userinputservice and enum

I want to know is this dangerous for performance and is it running as intended or something very broken?

Nothing in output window

LeaderBoardTABTOOPEN.rbxl (44.7 KB)

changes I made was adding a KO/WO script to serverscriptservice & changing how keypress is handled in the localscript, text also seems to overlap when updating KO/WO with new data during gameplay

After further inspection and some refactoring I’ve managed to get it working.

local function ClearLeaderboardFrame()
	-- Clear the entire leaderboard so that we can repopulate it with new info
	for _, t in pairs(TeamFramesList) do
		for frame, children in pairs(t) do
			frame.Parent = LeaderboardFrame
			for i = 1, #children do
				if children[i].Parent then
					children[i].Parent = frame
				end
			end
			frame.Parent = LeaderboardFrame
		end
	end
	local TeamFrames = LeaderboardFrame:GetChildren() 
	for i = 1, #TeamFrames do 
		if TeamFrames[i] then 
			local CurrentTeamElements = TeamFrames[i]:GetChildren() 
			for j = 1, #CurrentTeamElements do 
				if CurrentTeamElements[j] then
					if CurrentTeamElements[j]:IsA("TextLabel") then
						if CurrentTeamElements[j].Name ~= "Name" and CurrentTeamElements[j].Name ~= "Team" and CurrentTeamElements[j].Name ~= "TeamScore" and CurrentTeamElements[j].Name ~= "Total" then
							CurrentTeamElements[j]:Destroy()
						end
					end
				end
			end 
		end
	end
end

Roblox Original Control Points Game Template.rbxl (43.1 KB)

1 Like

what was causing the text-stacking? my own thinking was that the leaderstats were not getting cleared therefore they would keep stacking but I don’t think you mentioned altering clearleaderstats() in your posts so i’m not too sure what was causing the stacking in the first place

The removal of ClearAllChildren caused the stacking, I modified the ‘ClearLeaderboardFrame’ function to resolve the issue.

1 Like

why does the kills and deaths text disappear after like a second

This file by @Forummer was the solution for the post and contains the best functioning script

I have not had any problems so far.