What I want to achieve is basically making sure this is a secure way of handling a leaderboard. I want to know if multiple servers running this function will cause trouble!
This isn’t my first time making a global leaderboard but this time I’m doing it with one key on a normal datastore, the way it gets updated is by taking previous data and comparing it live every few minutes to see if any of the previous top ten players need to be replaced. It works well for me but in theory I can see issues happening if multiple servers call getasync or setasync for the key at the same time.
Here is the function, I gets called once when the first player gets added to a server then again every 200 seconds.
local function UpdateLeaderboard(Signal, key, tab)
local ColorByRank = Module.ColorByRank
local NewValues = {
["Player1"] = {
Name = "None",
Level = "None"
},
["Player2"] = {
Name = "None",
Level = "None"
},
["Player3"] = {
Name = "None",
Level = "None"
},
["Player4"] = {
Name = "None",
Level = "None"
},
["Player5"] = {
Name = "None",
Level = "None"
},
["Player6"] = {
Name = "None",
Level = "None"
},
["Player7"] = {
Name = "None",
Level = "None"
},
["Player8"] = {
Name = "None",
Level = "None"
},
["Player9"] = {
Name = "None",
Level = "None"
},
["Player10"] = {
Name = "None",
Level = "None"
}
}
if Signal == "New" then
local NewTopTen = {}
for i, v in pairs(game.Players:GetPlayers()) do
v:WaitForChild("leaderstats")
v.leaderstats:WaitForChild("Level")
if v.leaderstats.Level.Value < 1 then
v.leaderstats.Level.Changed:Wait()
else
-- player is ready to be inserted into table
end
table.insert(NewTopTen, v)
end
if #NewTopTen > 10 then
for i = 11, ((#NewTopTen - 10) + 10) do
table.remove(NewTopTen, i)
end
end
table.sort(NewTopTen, function(p, p2)
return p.leaderstats.Level.Value > p2.leadertats.Level.Value
end)
for x = 1, 10 do
if NewTopTen[x] ~= nil then
NewValues["Player"..x].Name = NewTopTen[x].UserId
NewValues["Player"..x].Level = NewTopTen[x].leaderstats.Level.Value
end
end
local Leaderboard = workspace.Lobby.Top10Players.LB.SurfaceGui
for z = 1, 10 do
if NewValues["Player"..z].Name == "None" and NewValues["Player"..z].Level == "None" then
Leaderboard[tostring(z)].Player.Text = "(To Be Decided)"
Leaderboard[tostring(z)].Level.Text = "Lvl - -"
Leaderboard[tostring(z)].Level.TextColor3 = ColorByRank.Bronze
Leaderboard[tostring(z)].Thumbnail.Image = ""
else
Leaderboard[tostring(z)].Player.Text = game.Players:GetNameFromUserIdAsync(NewValues["Player"..z].Name)
Leaderboard[tostring(z)].Level.Text = "Lvl "..NewValues["Player"..z].Level
Leaderboard[tostring(z)].Level.TextColor3 = Module.GetRankColor(NewValues["Player"..z].Level)
Leaderboard[tostring(z)].Thumbnail.Image = game.Players:GetUserThumbnailAsync(NewValues["Player"..z].Name, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size420x420)
-- Leaves info for applicable users
end
end
print(NewValues.Player1.Level)
LeaderBoardDS:SetAsync(key, NewValues)
elseif Signal == "Update" then
local NewTopTen = {}
local OldTab = {tab.Player1, tab.Player2, tab.Player3, tab.Player4, tab.Player5, tab.Player6, tab.Player7, tab.Player8, tab.Player9, tab.Player10}
for x, PreviousPlayer in pairs(OldTab) do
if PreviousPlayer.Level ~= "None" then
local PlayerModel = Instance.new("Model")
PlayerModel.Name = game.Players:GetNameFromUserIdAsync(PreviousPlayer.Name)
PlayerModel.Parent = game.ServerStorage.LeadeboardStation
local UserId = Instance.new("StringValue")
UserId.Name = "UserId"
UserId.Value = PreviousPlayer.Name
UserId.Parent = PlayerModel
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = PlayerModel
local Level = Instance.new("IntValue")
Level.Name = "Level"
Level.Value = PreviousPlayer.Level
Level.Parent = leaderstats
table.insert(NewTopTen, PlayerModel)
end
end
table.sort(NewTopTen, function(p, p1)
return p.leaderstats.Level.Value > p1.leaderstats.Level.Value
end)
for i, v in pairs(game.Players:GetPlayers()) do
local leaderstats = v:WaitForChild("leaderstats")
local Level = leaderstats:WaitForChild("Level")
if not game.ServerStorage.LeadeboardStation:FindFirstChild(v.Name) then
if Level.Value < 1 then
Level.Changed:Wait()
else
-- ready to be inseted to table
end
table.insert(NewTopTen, v)
end
end
table.sort(NewTopTen, function(p, p1)
return p.leaderstats.Level.Value > p1.leaderstats.Level.Value
end)
if #NewTopTen > 10 then
for i = 11, ((#NewTopTen - 10) + 10) do
table.remove(NewTopTen, i)
end
end
for x = 1, 10 do
if NewTopTen[x] ~= nil then
if NewTopTen[x]:IsA("Player") then
NewValues["Player"..x].Name = NewTopTen[x].UserId
elseif NewTopTen[x]:IsA("Model") then
NewValues["Player"..x].Name = NewTopTen[x].UserId.Value
end
NewValues["Player"..x].Level = NewTopTen[x].leaderstats.Level.Value
end
end
local Leaderboard = workspace.Lobby.Top10Players.LB.SurfaceGui
for z = 1, 10 do
if NewValues["Player"..z].Name == "None" and NewValues["Player"..z].Level == "None" then
Leaderboard[tostring(z)].Player.Text = "(To Be Decided)"
Leaderboard[tostring(z)].Level.Text = "Lvl - -"
Leaderboard[tostring(z)].Level.TextColor3 = ColorByRank.Bronze
Leaderboard[tostring(z)].Thumbnail.Image = ""
else
Leaderboard[tostring(z)].Player.Text = game.Players:GetNameFromUserIdAsync(NewValues["Player"..z].Name)
Leaderboard[tostring(z)].Level.Text = "Lvl "..NewValues["Player"..z].Level
Leaderboard[tostring(z)].Level.TextColor3 = Module.GetRankColor(NewValues["Player"..z].Level)
Leaderboard[tostring(z)].Thumbnail.Image = game.Players:GetUserThumbnailAsync(NewValues["Player"..z].Name, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size420x420)
-- Leaves info for applicable users
end
end
LeaderBoardDS:UpdateAsync(key, function()
return NewValues
end)
game.ServerStorage.LeadeboardStation:ClearAllChildren()
end
end
Then this is the loop after the first call, new is the signal that only gets called when there is no previous data on the key, then it’s update for every call after.
while wait(200) do
local LBData = nil
local Loaded, Fail = pcall(function()
LBData = LeaderBoardDS:GetAsync(LeaderboardKey)
end)
if Loaded then
if LBData ~= nil then
UpdateLeaderboard("Update", LeaderboardKey, LBData)
else
UpdateLeaderboard("New", LeaderboardKey, LBData)
end
else
warn("There was an issue trying to receive data for the global leaderboard, trying again next call")
end
end