So I’ve got this datastore that saves every stringvalue on the player’s inventory folder, when i leave the game it says Saved but when I join the game it just doesnt load.
local DSService = game:GetService("DataStoreService")
local DS = DSService:GetDataStore("InventoryData")
local players = game:GetService("Players")
players.PlayerAdded:Connect(function(player)
local storage = player:WaitForChild("PokemonData")
local load = DS:GetAsync("User_"..player.UserId) or {} --load data from datastore
if type(load) == "table" then --expected type of value
for i, v in pairs(load) do --cycle through dictionary of tables
for name, value in pairs(v) do --cycle through those tables
local intVal = Instance.new("StringValue")
intVal.Name = name
intVal.Value = value
intVal.Parent = storage --load all the stringvalue instances back into storage
print("Loaded " .. intVal.Name)
--perform other code
end
end
else
load = {} --empty table indicates no intvalues
--perform other code
end
end)
players.PlayerRemoving:Connect(function(player)
local data = {}
local storage = player:WaitForChild("PokemonData")
for i, v in pairs(storage:GetChildren()) do --cycle through all of the intvalue instances
if v:IsA("IntValue") then
local nameAndVal = {} --make new table to store name and value
nameAndVal[v.Name] = v.Value --insert name and val into table as single item
table.insert(data, nameAndVal) --insert the value of the intvalue instance into the table
end
end
local res = DS:SetAsync("User_"..player.UserId, data) --save data to datastore
print("Saved!")
--perform other code
end)
game:BindToClose(function() --bindtoclose executes whenever the server shuts down
for i, player in pairs(players:GetPlayers()) do
local data = {}
local storage = player:WaitForChild("PlayerData"):WaitForChild("Inventory")
for i, v in pairs(storage:GetChildren()) do --cycle through all of the intvalue instances
if v:IsA("IntValue") then
local nameAndVal = {} --make new table to store name and value
nameAndVal[v.Name] = v.Value --insert name and val into table as single item
table.insert(data, nameAndVal) --insert the value of the intvalue instance into the table
end
end
local res = DS:SetAsync("User_"..player.UserId, data) --save data to datastore
--perform other code
end
end)
Just to confirm - is the player added event running correctly? Sometimes the player joins before the connection is set when testing in Studio. (just add a print statement).
If they are joining correctly, can you please print out the value of load?
You do load the values into storage as Strings though, and check that they are Integers when building the savedata table here:
“Saved” will print regardless of whether any code before it executes successfully.
And you should wrap your datastore calls in a pcall and use the returns from the call to see whether the data was set successfully.
local success, errorMessage = pcall(function()
DS:SetAsync("User_"..player.UserId, data)
end)
if not success then
print(errorMessage)
end
This could be due to a race condition with locally hosted servers. When you run a solo test session in Roblox Studio, it is possible for the scripts in your game to begin executing after your client finalizes its connection to said server. This would lead to a missed Players.PlayerAdded event. The solution to this race condition is to,
Convert your throwaway function into a named function
Iterate through all in-game players and invoke your function on them with task.spawn
Connect the named function to the Players.PlayerAdded event to ready the handling of future players.
local Players = game:GetService("Players")
local function onPlayerAdded(player: Player)
-- ...
end
for _, player in Players:GetPlayers() do
task.spawn(onPlayerAdded, player)
end
Players.PlayerAdded:Connect(onPlayerAdded)
On a side-note, your code contradicts the function you stated it to perform; your save logic searches for IntValues, not StringValues. This is falsely remedied in your loading logic by creating a StringValue to represent the saved results, but you still name the variable holding this instance “intVal”.
Finally, you should consider the following optimizations:
Your Players.PlayerRemoving event listener yields the same code as the throwaway callback you pass to game:BindToClose. Consider converting each function into a single named function that can be shared between both the event and the game:BindToClose function.
You do not need a nested loop to load the saved data. You can instead index each field of the dictionaries you stored in the array:
for _, datum in data do
local stringValue = Instance.new("StringValue")
stringValue.Name = datum.Name
stringValue.Value = datum.Value
stringValue.Parent = -- ...
end
Why are you expecting a value type of IntValue but creating a value of type StringValue?
I also recommend using the following construction in case the folder is not found:
local storage = player:FindFirstChild("PokemonData") or Instance.new("Folder", player)
storage.Name = "PokemonData"
Finally, when working with DataStoreService, you should use the pcall() or xpcall() method, as calling DataStoreService can often fail. An example for your code is as follows:
local success, result = pcall(function()
return DS:GetAsync("User_"..player.UserId)
end)
if success and result then
load = result
else
warn("Failed to load data: ", result)
load = {}
end