I made a new DataStore module for my game, and I’d like to get your thoughts on it!
Thanks in advance.
local module = {}
local PlayerData = game:GetService("DataStoreService"):GetDataStore("PlayerData", 2)
module.LoadData = function(player, PlayerFolder)
local Data
local success, err = pcall(function()
Data = PlayerData:GetAsync(player.UserId)
end)
if not success then
warn(err)
player:Kick("Error loading data: Please rejoin the game")
end
if Data then
PlayerFolder.Credits.Value = Data.Credits
PlayerFolder.Wins.Value = Data.Wins
PlayerFolder.DataId.Value = Data.DataId
for i,v in pairs(Data.Codes) do
local t = Instance.new("BoolValue")
t.Name = v
t.Parent = PlayerFolder.Codes
end
for i,v in pairs(Data.Backpack) do
local t = Instance.new("BoolValue")
t.Name = v
t.Parent = PlayerFolder.Backpack
end
else
PlayerFolder.Credits.Value = 0
PlayerFolder.Wins.Value = 0
PlayerFolder.DataId.Value = 0
end
end
module.SaveData = function(player, PlayerFolder)
local ValuesToSave = {
Credits = PlayerFolder.Credits.Value;
Wins = PlayerFolder.Wins.Value;
DataId = PlayerFolder.DataId.Value;
Codes = {};
Backpack = {};
}
for i,v in pairs(PlayerFolder.Codes:GetChildren()) do
table.insert(ValuesToSave.Codes, v.Name)
end
for i,v in pairs(PlayerFolder.Backpack:GetChildren()) do
table.insert(ValuesToSave.Backpack, v.Name)
end
local success, err = pcall(function()
PlayerData:UpdateAsync(player.UserId, function(oldValue)
local prevData = oldValue or {DataId = 0}
if ValuesToSave.DataId == prevData.DataId then
ValuesToSave.DataId = ValuesToSave.DataId + 1
return ValuesToSave
else
return nil
end
end)
end)
if not success then
warn(err)
end
end
return module
You will want to be very careful about this. In the case of a DataStore failure, your game will become unplayable since most players will be unable to load their data and thus be kicked. What is normally recommended is to store the failed loads, notify the player that their stats won’t be saved due to the failed load, and not save any data. Example:
local FailedLoads = {}
...
--If the load failed, alert the player and make the stats unable to be saved.
if not success then
warn(err)
--TODO: Alert player stats won't save.
FailedLoads[player] = true
end
...
...
module.SaveData = function(player, PlayerFolder)
--Don't save if the player failed to load.
if FailedLoads[player] then
return
end
...
It might look bad to the player if they get kicked from the game very soon after they join - they might think your game is broken or bad, even if it’s just this 1 datastore call that didn’t work. I recommend retrying the datastore call a few times before kicking the player - a simple for loop would work, with maybe waiting a few seconds between each iteration. Of course, it’s not good to loop many times when your datastore budget is low. To check how many datastore calls you still have left, you can use DataStoreService | Documentation - Roblox Creator Hub. Depending on how many datastore calls you have left, you can decide how many times you want to loop (for example, if you normally loop 3 times, but you only have 2 GetAsync datastore calls left, you could choose NOT to do any extra loops in order to save datastore calls). Hope this helps!
Consider making full use of pcalls by cutting down your variable amount and boosting your readability. Instead of assigning an upvalue’s value, you can actually return the results in a pcall function or wrap a method call directly (the latter is more preferred).
local success, result = pcall(PlayerData.GetAsync, PlayerData, player.UserId)