My Inventory DataStore script doesnt load values

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,

  1. Convert your throwaway function into a named function
  2. Iterate through all in-game players and invoke your function on them with task.spawn
  3. 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:

  1. 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.
  2. 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

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.