-
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 -
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) -
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)