You aren’t using a pcall, so the print statement is only an indication that your code ran without issues, not that your DataStore calls were successfully performed. You should be sure to use pcalls when working with DataStore calls at nearly every opportunity.
The function connected to PlayerAdded may not run due to GetDataStore being a yielding function. The best case scenario for this is to assign the function to a variable, hook it to PlayerAdded and then call it for all existing players of the game. This will ensure that everything in terms of initialisation occurs.
Next huge problem is in relation to the way you’re creating values. Avoid using the parent argument if you’re setting properties immediately after creating an instance and try to consolidate where possible to lessen how much code you actually have to write out. Consider scalability and being able to handle arbitrary amounts of information.
And the cherry on top, my favourite nitpick against developers: use GetService to fetch services as opposed to using dot syntax. It is canonically correct and holds up consistency when working with services as you need to use GetService for a lot of them anyway.
Here is some code that incorporates my feedback accordingly. I have changed a few things that may interrupt how your DataStore workflow originally went, in regards to:
- Spelling and naming conventions on code
- Shortened a lot of bloat text for the DataStore
- Generally focused on readability and scalability
(Code sample below, hidden for thread viewing convenience. It has been changed since initial posting to fix bugs and such.)
View code sample
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local PlayerDataStore = DataStoreService:GetDataStore("PlayerData")
local DATA_VALUES = {
"Rank", "Weapon", "Accessory1", "Accessory2",
"Armor1", "Armor2", "Ability1", "Ability2",
"Ability3", "Ability4", "Ability5", "Ability6"
}
local function playerAdded(player)
-- Set parent of this after all child values are created
local dataFolder = Instance.new("Folder")
dataFolder.Name = "Data"
-- Ditto
local mainFolder = Instance.new("Folder")
mainFolder.Name = "Main"
for _, datumName in ipairs(DATA_VALUES) do
local datum = Instance.new("StringValue")
datum.Name = datumName
datum.Parent = mainFolder
end
local success, playerData = pcall(function ()
return PlayerDataStore:GetAsync(player.UserId)
end)
-- Came up a bit lazy on this part
if success then
if playerData then
for key, value in pairs(playerData) do
mainFolder[key].Value = value
end
else
mainFolder["Rank"].Value = "F-"
end
else
warn(("%s (%d)'s data could not be loaded!\n\t%s"):format(player.Name, player.UserId, playerData))
end
mainFolder.Parent = dataFolder
dataFolder.Parent = player
end
local function playerRemoving(player) -- Consistency and organisation's sake
-- No need to error handle: let this pass or fail organically
local mainFolder = player.Data.Main
local mainFolderContents = mainFolder:GetChildren()
local data = table.create(#mainFolderContents)
for _, datum in ipairs(mainFolderContents) do
data[datum.Name] = datum.Value
end
local success, exception = pcall(function ()
return PlayerDataStore:SetAsync(player.UserId, data)
end)
if not success then
warn(("%s (%d)'s data was not saved!\n\t%s"):format(player.Name, player.UserId, exception))
end
end
Players.PlayerAdded:Connect(playerAdded)
Players.PlayerRemoving:Connect(playerRemoving)
for _, player in ipairs(Players:GetPlayers()) do
playerAdded(player)
end
When testing that your DataStore works by setting values directly from the Studio window, ensure that you are using the Server view and not the Client view. Changes from the client view will be simulated as the client making changes to the DataModel and will follow standard replication patterns (the change will not be received by the server). If you are testing it with game systems, then just play normally enough to receive data and you’re good to go.