I know this question has been asked before but the ones I found didn’t have an answer in them that I could use but basically well I think its better to put the code first:
local rs = game:GetService("ReplicatedStorage")
local st = rs:WaitForChild("SaveTime")
local dss = game:GetService("DataStoreService")
local leaderboardstore = dss:GetOrderedDataStore("leaderboardstore")
st.OnServerEvent:Connect(function(player, runtime)
local playertime = tonumber(runtime)
local function update()
local success, errormessage = pcall(function()
local Data = leaderboardstore:GetSortedAsync(false,10)
local timepage = Data:GetCurrentPage()
for rank, data in ipairs(timepage) do
print(""..tonumber(data.key))
local username = game.Players:GetNameFromUserIdAsync(tonumber(data.key))
local name = username
local timing = data.value
local onboard = false
for i, v in pairs(game.Workspace.Leaderboard.LeaderboardGui.LeaderboardHolder:GetChildren()) do
if v.Player.Text == name then
onboard = true
break
end
end
if timing and onboard == false then
local newframe = rs:WaitForChild("Template"):Clone()
newframe.Player.Text = name
newframe.Time.Text = timing
newframe.Rank.Text = "#"..rank
newframe.Position = UDim2.new(0,0,newframe.Position.Y.Scale + (.1 * #game.Workspace.Leaderboard.LeaderboardGui.LeaderboardHolder:GetChildren()), 0)
newframe.Parent = game.Workspace.Leaderboard.LeaderboardGui.LeaderboardHolder
end
end
end)
if not success then
print(errormessage)
warn(errormessage)
end
end
while true do
for _, player in pairs(game.Players:GetPlayers()) do
leaderboardstore:SetAsync(player.UserId,playertime*100)
end
for _, frame in pairs(game.Workspace.Leaderboard.LeaderboardGui.LeaderboardHolder:GetChildren()) do
frame:Destroy()
end
update()
print("update")
wait(10)
end
end)
while looping through the page, data.key returns a negative value and then getnamefromuseridasync says unknown user. I tried printing data.key and published it but even if i play the actual game and go into the server console it shows its a negative number and getnamefromuseridasync has the same error.
Is that negative value “-1” by any chance? If so then I’m assuming you opened the game in test server mode in studio. When you do this you’ll notice that for each player their “Name” property is set to “Player” followed by a number indicating their position in the test server, i.e; “Player1” would represent the first player that was loaded into the test server. Taking a further inspection of any test player’s “UserId” property you’ll notice the following.
The issue is definitely what @Forummer mentioned, most leaderboard scripts counter this issue by placing an if condition for the userIds:
if data.key < 1 then continue end --if a user has an id below 1, skip the current loop iteration
print(""..tonumber(data.key))
--etc.
and on your saving loop:
for _, player in pairs(game.Players:GetPlayers()) do
if player.UserId < 1 then continue end --same check
leaderboardstore:SetAsync(player.UserId,playertime*100)
end
This will restrict the dummy accounts from appearing on the leaderboards, which is what the majority of games does.
PS: You may also want to use pcalls to handle API request errors:
--example
local username
local Success, Error = pcall(function()
username = game.Players:GetNameFromUserIdAsync(tonumber(data.key))
end)
if Error then
warn(Error) --what's the error?
continue --ignore the current loop iteration, user doesn't exist or Roblox services are down
end
All the API requests that are able to error and can break the script should be wrapped inside a pcall( GetSortedAsync , GetNameFromUserIdAsync , SetAsync) so the script wont break if certain APIs are down.
The value appears negative even for alts that go inside the actual game not studio, also the whole thing is already in a pcall, do you mean make another one inside it?
The reason the values are negative is because the datastore data contains the dummy accounts userIds, you have to either erase their leaderboard data or add the if statements I mentioned.
I actually have that same issue, at least in studio… Haven’t tested in game.
for rank, data in ipairs(page) do
local playerName = game.Players:GetNameFromUserIdAsync(tonumber(data.key))
local value = data.value
local isOnLeaderboard = false
for i,v in pairs(parent:GetChildren()) do
if v:IsA("Frame") and v.PlayerName.Text == playerName then
isOnLeaderboard = true
break
end
end
if value and isOnLeaderboard == false then
local GLTemplate = game.ReplicatedStorage.ASSETS.GLTemplate:Clone()
GLTemplate.Rank.Text = "#"..rank
GLTemplate.Kills.Text = value
GLTemplate.PlrName.Text = playerName
GLTemplate.Parent = parent
end
end
Im not too advanced with datastores but I think Ive seen that modifying the name of the actual datastore name resets the data, by name I mean the name that you call it when getting the ordered data store.
You don’t need to erase the entire datastore data, you can just use a plugin such as Datastore Editor, search for all the keys with a number less than 1(assuming they represent userIds), and manually remove them one by one.