Datastores retrieving nil from a table?

Hi all, I was attempting to make StringValues be saved within a folder inside the player named “BombInventory.” To elaborate, if a player has a StringValue named “Value” inside of the folder and leaves the server, I want the StringValue named “Value” be saved of its name once the player rejoins.

The problem is, after inserting a default StringValue named “Value” it seems that the name turns to “nil” upon reconnect.

I have done some “Print” tests to try and figure out where in the code would be setting the name to this, but I could not find it. In studio, I noticed that the last print statement printed out everything that was in the folder including “Value”.

--// Variables
local DataStoreService = game:GetService("DataStoreService")
local bombData = DataStoreService:GetDataStore("bombData")
local StoredBombs = {}

game.Players.PlayerAdded:Connect(function(player)
	
	local PlayerBombInventory = Instance.new("Folder")
		PlayerBombInventory.Name = "BombInventory"
		PlayerBombInventory.Parent = player

	local data = bombData:GetAsync(tostring(player.userId))
 
		if data then
			for i,v in pairs(data) do
				print(tostring(v.Name))
				local BombValue = Instance.new("StringValue")
					BombValue.Name = tostring(v.Name)
					BombValue.Value = tostring(v.Value)
					BombValue.Parent = PlayerBombInventory
			end
		else
			print("Data not found")
		end
		
end)


game.Players.PlayerRemoving:Connect(function(player)
	
	for i, v in next, player.BombInventory:GetChildren() do
		table.insert(StoredBombs,tostring(v.Name))
		print(tostring(v.Name)) 
        end
	
	local success, err = pcall(function()
		return bombData:SetAsync(tostring(player.UserId),StoredBombs)
	end)
end)

Help would be much appreciated, thanks!

You might want to check if your pcall ever errors, perhaps for example you’re testing in studio with api services disabled?

Also you should define the StoredBombs table inside the PlayerRemoving handler, otherwise every next person will receive the previous person’s bombs automatically.

Aaaaalso the way you load the saved data is incorrect, you never save the values, only the names. Try doing something like this:

--// Variables
local DataStoreService = game:GetService("DataStoreService")
local bombData = DataStoreService:GetDataStore("bombData")

game.Players.PlayerAdded:Connect(function(player)
	
	local PlayerBombInventory = Instance.new("Folder")
		PlayerBombInventory.Name = "BombInventory"
		PlayerBombInventory.Parent = player

	local data = bombData:GetAsync(tostring(player.userId))
 
	if data then
		for i,v in pairs(data) do
			print(tostring(v.Name))
			local BombValue = Instance.new("StringValue")
			BombValue.Name = tostring(v.Name)
			BombValue.Value = tostring(v.Value)
			BombValue.Parent = PlayerBombInventory
		end
	else
		print("Data not found")
	end
		
end)


game.Players.PlayerRemoving:Connect(function(player)
	local StoredBombs = {}
	for i, v in next, player.BombInventory:GetChildren() do
		table.insert(StoredBombs, {Name = v.Name, Value = v.Value})
		print(tostring(v.Name)) 
    end
	
	local success, err = pcall(function()
		return bombData:SetAsync(tostring(player.UserId),StoredBombs)
	end)
	if not success then
		warn("Save error:",err)
	end
end)
2 Likes

have you already saved the values? it will be nil if there was no data initially saved into the datastore.

if you HAVE saved already then you could have corrupted your data by accidentally sending a malformed value.

but here is a module that handles this for you and it comes with a :ParseString() function to parse keys from your inventory data.
https://www.roblox.com/library/4805575741/DataManager

Throw it into ServerScriptService
(all you do is add String/Number/Bool values into the InitialData folder… saving/loading/autosaving is automatic)

to update a value with it, you’ll do something like.

local dm  = require(script.Parent.DataManager)

function addCash(player, amount)
    local cash = dm:GetData(player).Cash
    cash.Value = cash.Value + amount
end

Here is the issue - I think the above poster is confused:

Say you have the following Bomb Values in their inventory:

StandardBomb (With a Value of 1)
SpecialBomb (With a Value of 2)
ChristmasBomb (With a Value of 3)

What you’re trying to save has the following structure:

{
 "StandardBomb",
 "SpecialBomb",
 "ChristmasBomb",
}

However what you want to access when you create BombValue is this:

{
 {Name = "StandardBomb", Value = 1},
 {Name = "SpecialBomb", Value = 2},
 {Name = "ChristmasBomb", Value = 3},
}

So, to fix this your PlayerRemoving should look like this:

game.Players.PlayerRemoving:Connect(function(player)
	
	for i, v in next, player.BombInventory:GetChildren() do
		table.insert(StoredBombs,{Name = tostring(v.Name), Value = v.Value}) -- Changed line to rectify
		print(tostring(v.Name)) 
        end
	
	local success, err = pcall(function()
		return bombData:SetAsync(tostring(player.UserId),StoredBombs)
	end)
end)
1 Like

its inefficient to use PlayerRemoving alone, it wont always save.

he’ll need to use this in combination with a pcall inside of BindToClose, along with a loop that retries X amount of times, not to mention an autosave feature.

if he has none of these then there is no garuntee it’ll save and his players will lose data.
and then he’ll be back to having the same issue, based on his data changes.

This is not the problem the user cares about.

1 Like

im sorry, but its the proper way to do it or else he is going to be back at square one, so i’m sure he would definitely care about this, even if its not right now.


game.Players.PlayerAdded:Connect(function(player)
    --load player
end


game.Players.PlayerRemoving:Connect(function(player)
    --save player
    --dispose of values
end


game.BindToClose:Connect(function()
   --save all players with a loop and a pcall 
end)

It is not important for most cases - in fact, it’s only really important if he is going to frequently shut down the game. You can direct him to Best Practices, but again, the user is asking for a specific answer for their specific question.

actually if he doesn’t use BindToClose the very last player will rarely save and he wont be able to test his saving functions in studio evertime, which could be the whole source of his current issue and corrupt his data, based on his design.

there is loads of people reporting issues about it, you can search the forum yourself to see.

Thanks for that. I had no idea what the correct way to set up a table insert was. I am also fairly certain there were other mistakes than just that…

This is incorrect. Whilst it is best practice as it may lead to data loss, the user isn’t asking about that.

And this is definitely not the cause of their issues. Please read the marked solution for the issue, or my post. Please do not confuse this user by misleading them about the issue here.

its not misleading, I’ve literally had the same issue with not being able to save in studio and last player not saving, if you all aren’t going to teach him the proper way to save data, then its pointless, but okay im glad you found your problem @Auxicon

I understand that I should have a BindToClose, however I hadn’t focused on that until now.

As you know, a table is like a row of values. What table.insert does is it inserts a value at the end of the table.

local tbl = {1,2}
table.insert(tbl, 3) --the table now looks like this: {1, 2, 3}

but since tables can hold any kind of value, they can hold other tables too! Which is exactly what I did in my code, basically making it look like { {Name = "something", Value = "something else"}, {Name = "something", Value = "something else"} }, or to format it better

{
    {Name = "something", Value = "something else"}, 
    {Name = "something", Value = "something else"}
}

Also, if you’re unfamiliar with dictionary tables you might be confused by the look of that table.
You know how regular tables hold values under numerical keys:

local tbl = {"hello", "world"}
print(tbl[1]) --> hello
print(tbl[2]) --> world

Dictionary tables basically let you use strings (or any other object but let’s focus on strings) as the key, for example:

local tbl = {
    hello = "world"
    ["another way of doing this"] = "hi there!"
}
print(tbl.hello) --> world

The downside of them is they don’t have a specific “order” since the values aren’t identified by numbers anymore, but you can still loop through them with pairs() normally!

1 Like

It’s really not pointless, because that’s not what the post is asking, lol. This is not a question on best practices or resolving data saving, it’s that a DataStore is fetching nil and there can be any number of causes for this to happen beyond a malformed save request.

It’s misleading in the sense that you were needlessly arguing best practices on saving and straying away from answering the actual question instead of answering the question which is that certain fields in the DataStore are returning nil. Clearly their current code works, it’s not that their data doesn’t save. So it’s best to address the actual problem.

In the future, please actually address the question in the OP rather than leading them on a wild goose chase. Best practices on data saving is irrelevant to the topic, it’s about fields returning nil.

he has already found the solution, there really wasn’t a need to ping me in order to keep this ordeal going on and if you notice I was asking him questions before unix interrupted by becoming passive aggressive. (while also “misleading” the original poster too and his post wasn’t the chosen solution), lets be fair and respectful here, no more ‘positive’ flaming please?

thanks though, i’ll take this into consideration next time I post.