Really weird scoreboard bug

Hi guys!

I own and code for a small TTT server, and I’ve recently come across a bug I’ve never encountered before.

I have my scoreboard set out with the ranks and rank colors attributed to each player, but recently I added 2 new ranks (Senior Admin and VIP), and they bug out the scoreboard.



[ERROR] lua/vgui/dlabel.lua:68: attempt to index local 'col' (a number value)
  1. ApplySchemeSettings - lua/vgui/dlabel.lua:68
   2. unknown - lua/vgui/dlabel.lua:74


For the same code that works fine in giving mods and admins ranks.

If you want to point out why I shouldn’t code at 1:53 am go ahead:

Here’s sb_row:
[lua]---- Scoreboard player score row, based on sandbox version

include(“sb_info.lua”)

local GetTranslation = LANG.GetTranslation
local GetPTranslation = LANG.GetParamTranslation

SB_ROW_HEIGHT = 24 --16

local PANEL = {}

function PANEL:Init()
– cannot create info card until player state is known
self.info = nil

self.open = false

self.cols = {}
self.cols[1] = vgui.Create(“DLabel”, self)
self.cols[1]:SetText(GetTranslation(“sb_ping”))

self.cols[2] = vgui.Create(“DLabel”, self)
self.cols[2]:SetText(GetTranslation(“sb_deaths”))

self.cols[3] = vgui.Create(“DLabel”, self)
self.cols[3]:SetText(GetTranslation(“sb_score”))

self.cols[5] = vgui.Create(“DLabel”, self)
self.cols[5]:SetText(“User”)

if KARMA.IsEnabled() then
self.cols[4] = vgui.Create(“DLabel”, self)
self.cols[4]:SetText(GetTranslation(“sb_karma”))
end

for _, c in ipairs(self.cols) do
c:SetMouseInputEnabled(false)
end

self.tag = vgui.Create(“DLabel”, self)
self.tag:SetText("")
self.tag:SetMouseInputEnabled(false)

self.sresult = vgui.Create(“DImage”, self)
self.sresult:SetSize(16,16)
self.sresult:SetMouseInputEnabled(false)

self.avatar = vgui.Create( “AvatarImage”, self )
self.avatar:SetSize(SB_ROW_HEIGHT, SB_ROW_HEIGHT)
self.avatar:SetMouseInputEnabled(false)

self.nick = vgui.Create(“DLabel”, self)
self.nick:SetMouseInputEnabled(false)

self.voice = vgui.Create(“DImageButton”, self)
self.voice:SetSize(16,16)

self:SetCursor( “hand” )
end

local namecolor = {
default = COLOR_WHITE,
admin = Color(55,134,253, 255),
dev = Color(100, 240, 105, 255),
srvowner = Color(205,39,39, 255),
mod = Color(55, 194, 253, 255),
tempmod = Color(61, 235, 223, 255),
regular = Color(127, 0, 255, 255),
donator = Color(0, 255, 0, 255),
cown = Color(225, 25, 25, 255),
vip = Color(237, 128, 37, 255),
supporter = Color(125, 215, 100, 255),
dplus = Color(0, 255, 0, 255)
};

function GM:TTTScoreboardColorForPlayer(ply)
if not IsValid(ply) then return namecolor.default end

if ply:SteamID() == “STEAM_0:0:1963640” then
return namecolor.dev
elseif ply:IsUserGroup(“admin”) and GetGlobalBool(“ttt_highlight_admins”, true) then
return namecolor.admin
elseif ply:SteamID() == “STEAM_0:1:45852799” then
return namecolor.srvowner
elseif ply:IsUserGroup(“mod”) and GetGlobalBool(“ttt_highlight_admins”, true) then
return namecolor.mod
elseif ply:IsUserGroup(“donator”) then
return namecolor.donator
elseif ply:IsUserGroup(“trial mod”) then
return namecolor.tempmod
elseif ply:IsUserGroup(“senior admin”) then
return namecolor.cown
elseif ply:IsUserGroup(“VIP”) then
return namecolor.vip
elseif ply:IsUserGroup(“donator+”) then
return namecolor.dplus
elseif ply:IsUserGroup(“supporter”) then
return namecolor.supporter
elseif ply:IsUserGroup(“regular”) then
return namecolor.regular
end
return namecolor.default
end

local function ColorForPlayer(ply)
if IsValid(ply) then
local c = hook.Call(“TTTScoreboardColorForPlayer”, GAMEMODE, ply)

  -- verify that we got a proper color
  if c and type(c) == "table" and c.r and c.b and c.g and c.a then
     return c
  else
     ErrorNoHalt("TTTScoreboardColorForPlayer hook returned something that isn't a color!

")
end
end
return namecolor.default
end

function PANEL:Paint()
if not IsValid(self.Player) then return end

– if ( self.Player:GetFriendStatus() == “friend” ) then
– color = Color( 236, 181, 113, 255 )
– end

local ply = self.Player

if ply:IsTraitor() then
surface.SetDrawColor(255, 0, 0, 30)
surface.DrawRect(0, 0, self:GetWide(), SB_ROW_HEIGHT)
elseif ply:IsDetective() then
surface.SetDrawColor(0, 0, 255, 30)
surface.DrawRect(0, 0, self:GetWide(), SB_ROW_HEIGHT)
end

if ply == LocalPlayer() then
surface.SetDrawColor( 200, 200, 200, math.Clamp(math.sin(RealTime() * 2) * 50, 0, 100))
surface.DrawRect(0, 0, self:GetWide(), SB_ROW_HEIGHT )
end

return true
end

function PANEL:SetPlayer(ply)
self.Player = ply
self.avatar:SetPlayer(ply)

if not self.info then
local g = ScoreGroup(ply)
if g == GROUP_TERROR and ply != LocalPlayer() then
self.info = vgui.Create(“TTTScorePlayerInfoTags”, self)
self.info:SetPlayer(ply)

     self:InvalidateLayout()
  elseif g == GROUP_FOUND or g == GROUP_NOTFOUND then
     self.info = vgui.Create("TTTScorePlayerInfoSearch", self)
     self.info:SetPlayer(ply)
     self:InvalidateLayout()
  end

else
self.info:SetPlayer(ply)

  self:InvalidateLayout()

end

self.voice.DoClick = function()
if IsValid(ply) and ply != LocalPlayer() then
ply:SetMuted(not ply:IsMuted())
end
end

self:UpdatePlayerData()
end

function PANEL:GetPlayer() return self.Player end

function PANEL:UpdatePlayerData()
if not IsValid(self.Player) then return end

local ply = self.Player
self.cols[1]:SetText(ply:Ping())
self.cols[2]:SetText(ply:Deaths())
self.cols[3]:SetText(ply:Frags())

if self.cols[4] then
self.cols[4]:SetText(math.Round(ply:GetBaseKarma()))
end

self.nick:SetText(ply:Nick())
self.nick:SizeToContents()
self.nick:SetTextColor(ColorForPlayer(ply))

local ptag = ply.sb_tag
if ScoreGroup(ply) != GROUP_TERROR then
ptag = nil
end

self.tag:SetText(ptag and GetTranslation(ptag.txt) or “”)
self.tag:SetTextColor(ptag and ptag.color or COLOR_WHITE)

self.sresult:SetVisible(ply.search_result != nil)

– more blue if a detective searched them
if ply.search_result and (LocalPlayer():IsDetective() or (not ply.search_result.show)) then
self.sresult:SetImageColor(Color(200, 200, 255))
end

– cols are likely to need re-centering
self:LayoutColumns()

if self.info then
self.info:UpdatePlayerData()
end

if self.Player != LocalPlayer() then
local muted = self.Player:IsMuted()
self.voice:SetImage(muted and “icon16/sound_mute.png” or “icon16/sound.png”)
else
self.voice:Hide()
end

if ply:IsUserGroup(“superadmin”) then
self.cols[5]:SetText(“Owner”)
self.cols[5]:SetTextColor(Color(205,39,39))
end

if ply:IsUserGroup(“admin”) then
self.cols[5]:SetText(“Admin”)
self.cols[5]:SetTextColor(Color(55,134,253))
end

if ply:IsUserGroup(“mod”) then
self.cols[5]:SetText(“Mod”)
self.cols[5]:SetTextColor(Color(55,194,253))
end

if ply:IsUserGroup(“trial mod”) then
self.cols[5]:SetText(“Trial Mod”)
self.cols[5]:SetTextColor(Color(61, 235, 223))
end

if ply:IsUserGroup(“regular”) then
self.cols[5]:SetText(“Regular”)
self.cols[5]:SetTextColor(Color(127,0,255))
end

if ply:IsUserGroup(“donator”) then
self.cols[5]:SetText(“Donator”)
self.cols[5]:SetTextColor(Color(38,153,38))
end

if ply:IsUserGroup(“senior admin”) then
self.cols[5]:SetText(“Snr. Admin”)
self.cols[5]:SetTextColor(240,35,35)
end

if ply:IsUserGroup(“donator+”) then
self.cols[5]:SetText(“Donator+”)
self.cols[5]:SetTextColor(0,255,0)
end

if ply:IsUserGroup(“vip”) then
self.cols[5]:SetText(“VIP”)
self.cols[5]:SetTextColor(237,128,37)
end

if ply:IsUserGroup(“supporter”) then
self.cols[5]:SetText(“Supporter”)
self.cols[5]:SetTextColor(125,215,100)
end

end

function PANEL:ApplySchemeSettings()
for k,v in pairs(self.cols) do
v:SetFont(“treb_small”)
v:SetTextColor(COLOR_WHITE)
end

self.nick:SetFont(“treb_small”)
self.nick:SetTextColor(ColorForPlayer(self.Player))

local ptag = self.Player and self.Player.sb_tag
self.tag:SetTextColor(ptag and ptag.color or COLOR_WHITE)
self.tag:SetFont(“treb_small”)

self.sresult:SetImage(“icon16/magnifier.png”)
self.sresult:SetImageColor(Color(170, 170, 170, 150))

end

function PANEL:LayoutColumns()
for k,v in ipairs(self.cols) do
v:SizeToContents()
v:SetPos(self:GetWide() - (50k) - v:GetWide()/2, (SB_ROW_HEIGHT - v:GetTall()) / 2)
if v == self.cols[5] then
v:SetPos(self:GetWide() - (50
k) - v:GetWide()/2, (SB_ROW_HEIGHT - v:GetTall()) / 2)
end
end

self.tag:SizeToContents()
self.tag:SetPos(self:GetWide() - (50 * 6) - self.tag:GetWide()/2, (SB_ROW_HEIGHT - self.tag:GetTall()) / 2)

self.sresult:SetPos(self:GetWide() - (50*6) - 8, (SB_ROW_HEIGHT - 16) / 2)
end

function PANEL:PerformLayout()
self.avatar:SetPos(0,0)
self.avatar:SetSize(SB_ROW_HEIGHT,SB_ROW_HEIGHT)

if not self.open then
self:SetSize(self:GetWide(), SB_ROW_HEIGHT)

  if self.info then self.info:SetVisible(false) end

elseif self.info then
self:SetSize(self:GetWide(), 100 + SB_ROW_HEIGHT)

  self.info:SetVisible(true)
  self.info:SetPos(5, SB_ROW_HEIGHT + 5)
  self.info:SetSize(self:GetWide(), 100)
  self.info:PerformLayout()

  self:SetSize(self:GetWide(), SB_ROW_HEIGHT + self.info:GetTall())

end

self.nick:SizeToContents()

self.nick:SetPos(SB_ROW_HEIGHT + 10, (SB_ROW_HEIGHT - self.nick:GetTall()) / 2)

self:LayoutColumns()

self.voice:SetVisible(not self.open)
self.voice:SetSize(16, 16)
self.voice:DockMargin(4, 4, 4, 4)
self.voice:Dock(RIGHT)
end

function PANEL:DoClick(x, y)
self:SetOpen(not self.open)
end

function PANEL:SetOpen(o)
if self.open then
surface.PlaySound(“ui/buttonclickrelease.wav”)
else
surface.PlaySound(“ui/buttonclick.wav”)
end

self.open = o

self:PerformLayout()
self:GetParent():PerformLayout()
sboard_panel:PerformLayout()
end

function PANEL:DoRightClick()
SetClipboardText(self.Player:SteamID());
chat.AddText(Color(255,255,255), “Copied " … self.Player:Nick() …”'s SteamID (", Color(139, 35, 35), self.Player:SteamID(), Color(255,255,255), “)”)
end

vgui.Register( “TTTScorePlayerRow”, PANEL, “Button” )[/lua]

And sb_main:

[lua]
---- VGUI panel version of the scoreboard, based on TEAM GARRY’s sandbox mode
---- scoreboard.

local surface = surface
local draw = draw
local math = math
local string = string
local vgui = vgui

local GetTranslation = LANG.GetTranslation
local GetPTranslation = LANG.GetParamTranslation

include(“sb_team.lua”)

surface.CreateFont(“cool_small”, {font = “coolvetica”,
size = 20,
weight = 400})
surface.CreateFont(“cool_large”, {font = “coolvetica”,
size = 24,
weight = 400})
surface.CreateFont(“treb_small”, {font = “Trebuchet18”,
size = 14,
weight = 700})

local logo = surface.GetTextureID(“VGUI/ttt/score_logo”)

local PANEL = {}

local max = math.max
local floor = math.floor
local function UntilMapChange()
local rounds_left = max(0, GetGlobalInt(“ttt_rounds_left”, 6))
local time_left = floor(max(0, ((GetGlobalInt(“ttt_time_limit_minutes”) or 60) * 60) - CurTime()))

local h = floor(time_left / 3600)
time_left = time_left - floor(h * 3600)
local m = floor(time_left / 60)
time_left = time_left - floor(m * 60)
local s = floor(time_left)

return rounds_left, string.format("%02i:%02i:%02i", h, m, s)
end

GROUP_TERROR = 1
GROUP_NOTFOUND = 2
GROUP_FOUND = 3
GROUP_SPEC = 4

GROUP_COUNT = 4

function ScoreGroup§
if not IsValid§ then return -1 end – will not match any group panel

if DetectiveMode() then
if p:IsSpec() and (not p:Alive()) then
if p:GetNWBool(“body_found”, false) then
return GROUP_FOUND
else
local client = LocalPlayer()
– To terrorists, missing players show as alive
if client:IsSpec() or
client:IsActiveTraitor() or
((GAMEMODE.round_state != ROUND_ACTIVE) and client:IsTerror()) then
return GROUP_NOTFOUND
else
return GROUP_TERROR
end
end
end
end

return p:IsTerror() and GROUP_TERROR or GROUP_SPEC
end

function PANEL:Init()

self.hostdesc = vgui.Create(“DLabel”, self)
self.hostdesc:SetText(GetTranslation(“sb_playing”))
self.hostdesc:SetContentAlignment(9)

self.hostname = vgui.Create( “DLabel”, self )
self.hostname:SetText( GetHostName() )
self.hostname:SetContentAlignment(6)

self.mapchange = vgui.Create(“DLabel”, self)
self.mapchange:SetText(“Map changes in 00 rounds or in 00:00:00”)
self.mapchange:SetContentAlignment(9)

self.mapchange.Think = function (sf)
local r, t = UntilMapChange()

                         sf:SetText(GetPTranslation("sb_mapchange",
                                                    {num = r, time = t}))
                         sf:SizeToContents()
                      end

self.ply_frame = vgui.Create( “TTTPlayerFrame”, self )

self.ply_groups = {}

local t = vgui.Create(“TTTScoreGroup”, self.ply_frame:GetCanvas())
t:SetGroupInfo(GetTranslation(“terrorists”), Color(0,200,0,100), GROUP_TERROR)
self.ply_groups[GROUP_TERROR] = t

t = vgui.Create(“TTTScoreGroup”, self.ply_frame:GetCanvas())
t:SetGroupInfo(GetTranslation(“spectators”), Color(200, 200, 0, 100), GROUP_SPEC)
self.ply_groups[GROUP_SPEC] = t

if DetectiveMode() then
t = vgui.Create(“TTTScoreGroup”, self.ply_frame:GetCanvas())
t:SetGroupInfo(GetTranslation(“sb_mia”), Color(130, 190, 130, 100), GROUP_NOTFOUND)
self.ply_groups[GROUP_NOTFOUND] = t

  t = vgui.Create("TTTScoreGroup", self.ply_frame:GetCanvas())
  t:SetGroupInfo(GetTranslation("sb_confirmed"), Color(130, 170, 10, 100), GROUP_FOUND)
  self.ply_groups[GROUP_FOUND] = t

end

– the various score column headers
self.cols = {}
self.cols[1] = vgui.Create( “DLabel”, self )
self.cols[1]:SetText( GetTranslation(“sb_ping”) )

self.cols[2] = vgui.Create( “DLabel”, self )
self.cols[2]:SetText( GetTranslation(“sb_deaths”) )

self.cols[3] = vgui.Create( “DLabel”, self )
self.cols[3]:SetText( GetTranslation(“sb_score”) )

if KARMA.IsEnabled() then
self.cols[4] = vgui.Create(“DLabel”, self)
self.cols[4]:SetText(GetTranslation(“sb_karma”))
end

self.cols[5] = vgui.Create( “DLabel”, self )
self.cols[5]:SetText(“Rank”)

self:UpdateScoreboard()
self:StartUpdateTimer()
end

function PANEL:StartUpdateTimer()
if not timer.Exists(“TTTScoreboardUpdater”) then
timer.Create( “TTTScoreboardUpdater”, 0.3, 0,
function()
local pnl = GAMEMODE:GetScoreboardPanel()
if IsValid(pnl) then
pnl:UpdateScoreboard()
end
end)
end
end

local colors = {
bg = Color(30,30,30, 235),
bar = Color(199,209,6,255)
};

local y_logo_off = 72

function PANEL:Paint()
– Logo sticks out, so always offset bg
draw.RoundedBox( 8, 0, y_logo_off, self:GetWide(), self:GetTall() - y_logo_off, colors.bg)

– Server name is outlined by orange/gold area
draw.RoundedBox( 8, 0, y_logo_off + 25, self:GetWide(), 32, colors.bar)

– TTT Logo
surface.SetTexture( logo )
surface.SetDrawColor( 255, 255, 255, 255 )
surface.DrawTexturedRect( 5, 0, 256, 256 )

end

function PANEL:PerformLayout()
– position groups and find their total size
local gy = 0
– can’t just use pairs (undefined ordering) or ipairs (group 2 and 3 might not exist)
for i=1, GROUP_COUNT do
local group = self.ply_groups*
if ValidPanel(group) then
if group:HasRows() then
group:SetVisible(true)
group:SetPos(0, gy)
group:SetSize(self.ply_frame:GetWide(), group:GetTall())
group:InvalidateLayout()
gy = gy + group:GetTall() + 5
else
group:SetVisible(false)
end
end
end

self.ply_frame:GetCanvas():SetSize(self.ply_frame:GetCanvas():GetWide(), gy)

local h = y_logo_off + 110 + self.ply_frame:GetCanvas():GetTall()

– if we will have to clamp our height, enable the mouse so player can scroll
local scrolling = h > ScrH() * 0.95
– gui.EnableScreenClicker(scrolling)
self.ply_frame:SetScroll(scrolling)

h = math.Clamp(h, 110 + y_logo_off, ScrH() * 0.95)

local w = math.max(ScrW() * 0.6, 640)

self:SetSize(w, h)
self:SetPos( (ScrW() - w) / 2, math.min(72, (ScrH() - h) / 4))

self.ply_frame:SetPos(8, y_logo_off + 109)
self.ply_frame:SetSize(self:GetWide() - 16, self:GetTall() - 109 - y_logo_off - 5)

– server stuff
self.hostdesc:SizeToContents()
self.hostdesc:SetPos(w - self.hostdesc:GetWide() - 8, y_logo_off + 5)

local hw = w - 180 - 8
self.hostname:SetSize(hw, 32)
self.hostname:SetPos(w - self.hostname:GetWide() - 8, y_logo_off + 27)

surface.SetFont(“cool_large”)
local hname = self.hostname:GetValue()
local tw, _ = surface.GetTextSize(hname)
while tw > hw do
hname = string.sub(hname, 1, -6) … “…”
tw, th = surface.GetTextSize(hname)
end

self.hostname:SetText(hname)

self.mapchange:SizeToContents()
self.mapchange:SetPos(w - self.mapchange:GetWide() - 8, y_logo_off + 60)

– score columns
local cy = y_logo_off + 90
for k,v in ipairs(self.cols) do
v:SizeToContents()
v:SetPos( w - (50*k) - v:GetWide()/2 - 8, cy)
end
end

function PANEL:ApplySchemeSettings()
self.hostdesc:SetFont(“cool_small”)
self.hostname:SetFont(“cool_large”)
self.mapchange:SetFont(“treb_small”)

self.hostdesc:SetTextColor(COLOR_WHITE)
self.hostname:SetTextColor(COLOR_BLACK)
self.mapchange:SetTextColor(COLOR_WHITE)

for k,v in pairs(self.cols) do
v:SetFont(“treb_small”)
v:SetTextColor(COLOR_WHITE)
end
end

function PANEL:UpdateScoreboard( force )
if not force and not self:IsVisible() then return end

local layout = false

– Put players where they belong. Groups will dump them as soon as they don’t
– anymore.
for k, p in pairs(player.GetAll()) do
if IsValid§ then
local group = ScoreGroup§
if self.ply_groups[group] and not self.ply_groups[group]:HasPlayerRow§ then
self.ply_groups[group]:AddPlayerRow§
layout = true
end
end
end

for k, group in pairs(self.ply_groups) do
if ValidPanel(group) then
group:SetVisible( group:HasRows() )
group:UpdatePlayerData()
end
end

if layout then
self:PerformLayout()
else
self:InvalidateLayout()
end
end

vgui.Register( “TTTScoreboard”, PANEL, “Panel” )

---- PlayerFrame is defined in sandbox and is basically a little scrolling
---- hack. Just putting it here (slightly modified) because it’s tiny.

local PANEL = {}
function PANEL:Init()
self.pnlCanvas = vgui.Create( “Panel”, self )
self.YOffset = 0

self.scroll = vgui.Create(“DVScrollBar”, self)
end

function PANEL:GetCanvas() return self.pnlCanvas end

function PANEL:OnMouseWheeled( dlta )
self.scroll:AddScroll(dlta * -2)

self:InvalidateLayout()
end

function PANEL:SetScroll(st)
self.scroll:SetEnabled(st)
end

function PANEL:PerformLayout()
self.pnlCanvas:SetVisible(self:IsVisible())

– scrollbar
self.scroll:SetPos(self:GetWide() - 16, 0)
self.scroll:SetSize(16, self:GetTall())

local was_on = self.scroll.Enabled
self.scroll:SetUp(self:GetTall(), self.pnlCanvas:GetTall())
self.scroll:SetEnabled(was_on) – setup mangles enabled state

self.YOffset = self.scroll:GetOffset()

self.pnlCanvas:SetPos( 0, self.YOffset )
self.pnlCanvas:SetSize( self:GetWide() - (self.scroll.Enabled and 16 or 0), self.pnlCanvas:GetTall() )
end
vgui.Register( “TTTPlayerFrame”, PANEL, “Panel” )[/lua]

Seriously confusing me right now,

I’ve been testing on my dedicated server for maybe an hour. No success.

It could be due to the fact you are trying to run Server Side scripts on the client, check your code.

I’m sorry, I’m new to lua.

But I dont understand why that would be a factor when I only added copy paste code in the same positions? Have I forgotten something, or…?

Should I just redownload it and start from scratch?