What do you want to achieve? Keep it simple and clear!
I want to make a leaderboard that gets past the 9.223 quintillion limit since they are a lot of players that have passed it in my game
What is the issue? Include screenshots / videos if possible!
I can’t get it to work.
What solutions have you tried so far? Did you look for solutions on the Developer Hub?
I tried a solution from dev forum but i didn’t understand it and i tried getting them using the :GetAsync() function
Here is the script:
local DTS = game:GetService("DataStoreService")
local Lb = DTS:GetOrderedDataStore("Lb1")
local DataStore = DataStoreService:GetDataStore("DataStore3")
local function updateLeaderboard()
local success, errorMessage = pcall(function()
local Data = Lb:GetSortedAsync(false, 50,0,math.huge)
local ClicksPage = Data:GetCurrentPage()
for rank, data in ipairs(ClicksPage) do
local user = game.Players:GetNameFromUserIdAsync(tonumber(data.key))
local Name = user
local PlayerUserId = "Player_"..data.key
local LeaderboardData
local success, errormessage = pcall(function()
LeaderboardData = DataStore:GetAsync(PlayerUserId, data)
end)
local TimeWasted = math.floor(LeaderboardData.Currency)
local isLeaderboard = false
for i,v in pairs(game.Workspace.Leaderboard.SurfaceGui.ScrollingFrame:GetChildren()) do
if v.Player.Text == Name then
isLeaderboard = true
break
end
end
if TimeWasted and isLeaderboard == false then
local newLeaderboard = game.ReplicatedStorage:WaitForChild("ScrollingFrame"):Clone()
newLeaderboard.Player.Text = Name
newLeaderboard.Points.Text = TimeWasted
newLeaderboard.Ranks.Text = "#"..rank
newLeaderboard.Position = UDim2.new(0,0, newLeaderboard.Position.Y.Scale + (.02 * #game.Workspace.Leaderboard.SurfaceGui.ScrollingFrame:GetChildren()), 0)
newLeaderboard.Parent = game.Workspace.Leaderboard.SurfaceGui.ScrollingFrame
if rank == 1 then
local Description = game.Players:GetHumanoidDescriptionFromUserId(tonumber(data.key))
local Dummy = workspace:WaitForChild("Dummy")
Dummy.Humanoid:ApplyDescription(Description)
newLeaderboard.Player.TextColor3 = Color3.fromRGB(239, 184, 56)
newLeaderboard.Points.TextColor3 = Color3.fromRGB(239, 184, 56)
newLeaderboard.Ranks.TextColor3 = Color3.fromRGB(239, 184, 56)
end
end
end
end)
if not success then
warn(errorMessage)
end
end
while true do
for _, player in pairs(game.Players:GetPlayers()) do
Lb:SetAsync(player.UserId, player.leaderstats.Currency.Value)
end
for _, frame in pairs(game.Workspace.Leaderboard.SurfaceGui.ScrollingFrame:GetChildren()) do
frame:Destroy()
end
updateLeaderboard()
wait(20)
end```
You can’t get past it using numbers, you need to store the value as a string or an array. Once you’ve done this you will need to find your own way to allow players to spend / gain cash.
OrderedDataStores are limited to 9.223 QN as a max value.
What you could do (might be inaccurate, but its a solution) is save an exclusive value that is equal to their money, but divide it by a large amount (1,000,000 for example). Then, save that in the OrderedDataStore. Once you load the leaderboard, simply multiply what the text displays by 1,000,000, and it would be over that limit.
Since you use such large numbers, 1 Million might be a bit low.
Is there any way to do that without having to reset the leaderboard?
Edit: It gives me an error “103: double is not allowed in data stores.” What does it mean?
You can save 2 values, their Money, and their Leaderboard Money. Everytime Money changes, you can set Leaderboard Money equal to Money divided by 1 Million (or whatever works best). Then use that Leaderboard Money for the OrderedDataStore.
Dividing by a value will increase your range but it will decrease the accuracy. If you need to retain accuracy you’re going to have to use multiple keys. In the ODS you can store the divided result but after you get the list of, say, the top 10 players, you will have to get their actual value from the DS and the actual value will need to be stored across multiple values so it can reach a value bigger than 9.223 quintillion.
In other words, what you want to do is not entirely trivial. If you can, a better solution might be to make sure reaching such a large number isn’t possible in the first place .
Save the Leaderboard Money divided by an amount like previously, but instead of putting the Leaderboard Money multiplied on the leaderboard, use their actual Money. The Leaderboard Money can be used to sort, and once everyone is sorted, we don’t need that Leaderboard Money anymore.
Also, I disagree with this. It will fix your issue of Leaderboards, but part of the fun of a game can be how high in numbers you can go. Take Miner’s Haven, where its possible to hit the 1.7e+308 limit. Such a game wouldn’t be possible if you only could get to billions.
The “9.223 quintillion limit” is inaccurate, if you’re storing integers you can go up to 2^53, which is 9.007 quadrillion, which is 9 thousand thousand billion, so you can get well beyond billions safely. But I get your point. Anyways I don’t see how your solution is any different than mine. To hold integers greater than 2^53 you’re going to need to split them up into multiple integers (or use a string) though so on the leaderboard you will have to use a StringValue to display the actual amount (or you can display an approximate amount ig).
There is a simple way to do it
Save your value like that:
math.log10(example.Value+1)*(2^63)/308.254
Then when u displaying stats do it like that:
(10^(example.Value/(2^63)*308.254))-1
This should help you.
So it would be like that:
local DTS = game:GetService("DataStoreService")
local Lb = DTS:GetOrderedDataStore("Lb1")
local DataStore = DataStoreService:GetDataStore("DataStore3")
local function updateLeaderboard()
local success, errorMessage = pcall(function()
local Data = Lb:GetSortedAsync(false, 50,0,math.huge)
local ClicksPage = Data:GetCurrentPage()
for rank, data in ipairs(ClicksPage) do
local user = game.Players:GetNameFromUserIdAsync(tonumber(data.key))
local Name = user
local PlayerUserId = "Player_"..data.key
local LeaderboardData
local success, errormessage = pcall(function()
LeaderboardData = DataStore:GetAsync(PlayerUserId, data)
end)
local TimeWasted = (10^(LeaderboardData.Currency/(2^63)*308.254))-1
local isLeaderboard = false
for i,v in pairs(game.Workspace.Leaderboard.SurfaceGui.ScrollingFrame:GetChildren()) do
if v.Player.Text == Name then
isLeaderboard = true
break
end
end
if TimeWasted and isLeaderboard == false then
local newLeaderboard = game.ReplicatedStorage:WaitForChild("ScrollingFrame"):Clone()
newLeaderboard.Player.Text = Name
newLeaderboard.Points.Text = TimeWasted
newLeaderboard.Ranks.Text = "#"..rank
newLeaderboard.Position = UDim2.new(0,0, newLeaderboard.Position.Y.Scale + (.02 * #game.Workspace.Leaderboard.SurfaceGui.ScrollingFrame:GetChildren()), 0)
newLeaderboard.Parent = game.Workspace.Leaderboard.SurfaceGui.ScrollingFrame
if rank == 1 then
local Description = game.Players:GetHumanoidDescriptionFromUserId(tonumber(data.key))
local Dummy = workspace:WaitForChild("Dummy")
Dummy.Humanoid:ApplyDescription(Description)
newLeaderboard.Player.TextColor3 = Color3.fromRGB(239, 184, 56)
newLeaderboard.Points.TextColor3 = Color3.fromRGB(239, 184, 56)
newLeaderboard.Ranks.TextColor3 = Color3.fromRGB(239, 184, 56)
end
end
end
end)
if not success then
warn(errorMessage)
end
end
while true do
for _, player in pairs(game.Players:GetPlayers()) do
Lb:SetAsync(player.UserId, math.log10(player.leaderstats.Currency.Value+1)*(2^63)/308.254
)
end
for _, frame in pairs(game.Workspace.Leaderboard.SurfaceGui.ScrollingFrame:GetChildren()) do
frame:Destroy()
end
updateLeaderboard()
wait(20)
end
You lose accuracy if you divide the value just to multiply it back later. If you want to store integers larger than 2^53 safely, you have to use multiple values, period. You can’t fit more than 53 bits worth of data in 53 bits worth of data, it’s the classic pigeonhole principle. The ODS itself can hold these approximated scaled down values but when you read them from the ODS people who have very similar large and close values will be equal when you scale them down (for example, math.log10(2^52) == math.log10(2^52 + 1)) so you need to fetch their actual values from the original DS storing the value as a string or as multiple integers.