If the player’s data fails to load, don’t save the data, and also notify the user that their data isn’t lost and that it just wasn’t loaded (better UX).
I would recommend storing the data in a server script and firing the client whenever the data updates, as you’re avoiding storing a potentially large amount of instances (large arrays could mean a hundred or more). (don’t send all the data everytime, just what is updating, ex: server tells client to update the cash to 50)
Due to roblox’s limitations, you are unable to have leaderboards if you’re storing all of the user’s data in a dictionary. Leaderboards must be done using OrderedDataStores as they have the GetSortedAsync method, and normal datastores don’t.
To make a global leaderboard, you will need to save the data of what the leaderboard should be about in its own OrderedDataStore (unique for each stat). To get the top x players, you will need to call GetSortedAsync on the OrderedDataStore which you want to retrieve the top stat players from. GetSortedAsync will return a DataStorePages, and to get the data from the DataStorePages you will need to call GetCurrentPage which will return an array of dictionaries. Each dictionary contains they key and the value, in a format like this.
local form = {
key = userId, -- assuming you're using userids for keys
value = 100 -- the amount
}
The array which contain all the dictionaries will look like this
local data = {
{
key = 1,
value = 100
},
{
key = 2,
value = 80
},
...
}
The key of the player’s data should be the UserId, so to get the username simply call GetNameFromUserIdAsync to get the name (and probably cache it too).
local Players = game:GetService"Players"
for i,v in pairs(data)
local key,value = v.key,v.value
local success,name = pcall(Players.GetNameFromUserIdAsync,Players,key)
name = (success and name) or "Unknown"
--You now have the name of the player, and their data
end
How you want to design the hierarchy of the leaderboard is up to you, but I would recommend having a TextLabel for each spot on the leaderboard, and naming it for the spot (ex: 15th spot would be named 15) so that you can simply get the spot by indexing the parent of the TextLabels.
local frameChanging = workspace.Leaderboard1.Frame[i]
--i would be the spot in the leaderboard, as the keys in the data correspond to the leaderboard position