Leaderboard comes up fine (that’s the most basic of things), however, I keep getting an error around line 25 telling me:
ServerScriptService.DataStore:25 attempt to index nil with ‘Wins’
I’m still fairly new at programming so bear with me.
-- Datastore Service (for player statistics)
local dataStoreService = game:GetService("DataStoreService");
local dataStore = dataStoreService:GetDataStore("GeneralSaveData", "Players");
function generateDataKey(player)
local ret = "_uid" .. player.userId;
return ret;
end
function generateDataTable(player)
local dataTable = {
Wins = player.leaderstats.Wins.Value;
Points = player.leaderstats.Points.Value;
}
return dataTable;
end
function saveDataForPlayer(player)
local key = generateDataKey(player);
local data = generateDataTable(player);
dataStore:SetAsync(key, data);
end
function inputDataToPlayer(player, data)
player.leaderstats.Wins.Value = data.Wins;
player.leaderstats.Points.Value = data.Points;
end
function loadDataForPlayer(player)
local key = generateDataKey(player);
local data = dataStore:GetAsync(key);
inputDataToPlayer(player, data);
end
game.Players.PlayerAdded:connect(function(player)
-- Opens up the leaderboard --
local leaderstats = Instance.new("Folder");
leaderstats.Name = "leaderstats";
leaderstats.Parent = player;
local wins = Instance.new("IntValue");
wins.Name = "Wins";
--wins.Value = 1;
wins.Parent = leaderstats;
local points = Instance.new("IntValue");
points.Name = "Points";
points.Parent = leaderstats;
loadDataForPlayer(player); -- Load data the first thing when the player joins
player.leaderstats.Wins.Changed:connect(function()
saveDataForPlayer(player); -- Save the data when Wins change value
end)
end)
game.Players.PlayerRemoving:connect(saveDataForPlayer) -- Saves the data when the player leaves the game
Problem here is that data is nil thus “attempting to index nil with ‘Wins’”. If a player doesn’t have existing data then GetAsync will return nil. You will need to account for this by setting up some default data.
function inputDataToPlayer(player, data)
player.leaderstats.Wins.Value = data and data.Wins or 0;
player.leaderstats.Points.Value = data and data.Points or 0;
end
Your solution could be as simple as setting the values to 0 if no data was returned.
On a side note, you should be using pcall on datastore:GetAsync() because it may error.
In loading data for players, you must also expect that the player is new; hence, the data retrieved from loadDataForPlayer function will be nil.
Try adding a conditional statement in your inputDataToPlayer function:
function inputDataToPlayer(player, data)
if data ~= nil then -- If data is not equal to nil
player.leaderstats.Wins.Value = data.Wins;
player.leaderstats.Points.Value = data.Points;
else
player.leaderstats.Wins.Value = 0
player.leaderstats.Points.Value = 0
end
You basically ‘wrap’ your code within a pcall function.
function saveDataForPlayer(player)
local key = generateDataKey(player);
local data = generateDataTable(player);
local success, errormessage = pcall(function()
dataStore:SetAsync(key, data)
end)
end
function loadDataForPlayer(player)
local key = generateDataKey(player);
local data
local success, errormessage = pcall(function()
data = dataStore:GetAsync(key)
end)
inputDataToPlayer(player, data);
end
I was confused too when I was a beginner, but to make it simple, the pcall is usually used whenever there is a possibility for a certain function to cause an error. When a pcall is used and an error occurs, the thread will still continue; however, if the pcall function is not used, the thread will stop at that error, causing the whole script to stop.
Like Dundale said, anything which may or may not error should be wrapped up in a pcall. I think that anything marked Async on roblox may error (there are many other functions which could error too of course).
What’s important to know is that the first return value is a boolean which represents whether an error occured or not. The second return value represents an error message if an error occurred or otherwise the actual return value of the function you called.
local success, data = pcall(dataStore.GetAsync, dataStore, key)
if not success then
-- GetAsync threw some kind of error, data represents this error and not your actual player data
else
if not data then
-- The user had no actual data saved, GetAsync did not error but returned nil
else
-- User had data saved and it was returned
With that being said, you may want to have a while loop retrying after a short interval if the datastore fetch fails. Although you may want different behavior depending on what kind of error the datastore throws. See the Data Store Errors and Limits article on what you could expect.