Table items not being detected

So basically I’m trying to store points and weapons. For some reason the script isn’t able to detect anything within the table provided for the async, after the UserId is provided. As evident, I am using print statements to see if the points and “child” table are there.

game.Players.PlayerAdded:Connect(function(plr)
	local leaderstats = script.leaderstats:Clone()
	leaderstats.Parent = plr
	
	local data
	local success, error = pcall(function()
		--data = PointsDS:GetAsync(tostring(plr.UserId))
		--if data == nil then
			data = PointsDS:SetAsync(tostring(plr.UserId),{
				Points = 0,
				Weapons = {"S_Classic", "L_Classic", "T_Classic"}
			})
		--end
	end)
	if success and data then
		print(data) -- line 21
		print(data.Points) -- line 22
		print(data.Weapons) -- line 23
		leaderstats.Points.Value = data.Points -- line 24
		for i, weapon in pairs(data.Weapons) do -- line 25
			ShopEvent:FireClient(plr, "Update", data.Weapons)
		end
	else
		task.wait(1)
		plr:Kick("Data failed to load.")
	end
	
	PlrWeapons.S_Classic:Clone().Parent = plr.Backpack
	PlrWeapons.L_Classic:Clone().Parent = plr.Backpack
	PlrWeapons.T_Classic:Clone().Parent = plr.Backpack

	while true do
		leaderstats.Points.Value += 1
		task.wait(10)
	end
end)

Output

I’m assuming you were meaning to get the data, not to set the data?
If so, you should change

data = PointsDS:SetAsync(tostring(plr.UserId),{
				Points = 0,
				Weapons = {"S_Classic", "L_Classic", "T_Classic"}
			})

to

data = PointsDS:GetAsync(tostring(plr.UserId)) or {
				Points = 0,
				Weapons = {"S_Classic", "L_Classic", "T_Classic"}
			} -- GetAsync doesn't let you put the default values in - you have to add it after the fact

I purposefully set the data in order to make sure that whenever I change stuff within the table with data, it will always update. I’ve done it this way only for testing.

In that case, you’ll need to set the value of data differently - that’s where the issue is occuring.
(Because it’s just for testing, you can get away with using GetAsync directly after the SetAsync)

Updated code

game.Players.PlayerAdded:Connect(function(plr)
	local leaderstats = script.leaderstats:Clone()
	leaderstats.Parent = plr
	
	local data
	local success, error = pcall(function()
		data = PointsDS:GetAsync(tostring(plr.UserId))
		if data == nil then
			data = PointsDS:SetAsync(tostring(plr.UserId),{
				Points = 0,
				Weapons = {"S_Classic", "L_Classic", "T_Classic"}
			})
		end
	end)
	if success and data then
		print(data)
		print(data.Points)
		print(data.Weapons)
		leaderstats.Points.Value = data.Points
		for i, weapon in pairs(data.Weapons) do
			ShopEvent:FireClient(plr, "Update", data.Weapons)
		end
	else
		task.wait(1)
		plr:Kick("Data failed to load.")
	end
	
	PlrWeapons.S_Classic:Clone().Parent = plr.Backpack
	PlrWeapons.L_Classic:Clone().Parent = plr.Backpack
	PlrWeapons.T_Classic:Clone().Parent = plr.Backpack

	while true do
		leaderstats.Points.Value += 1
		task.wait(10)
	end
end)

Haven’t used data stores yet (haven’t reached that stage of development), but I think I see why your code, old and new, still doesn’t update the data variable.

Even under this new version, the protected call is still not returning a table. SetAsync returns a version identifier of a GlobalDataStore, not whatever you have sent to it. Only if the data is valid prior will the data variable be correct thanks to GetAsync, but if the player has no save, data will have the wrong type assigned to it. Send the default data using a variable first, then return that variable.

data = PointsDS:GetAsync(tostring(plr.UserId))
if data == nil then
	-- Assign the default save data to a local variable first. That way, the data can both be
	-- saved and returned/assigned to "data".
	local defaultSaveData = {
		Points = 0,
		Weapons = {"S_Classic", "L_Classic", "T_Classic"}
	}
	
	PointsDS:SetAsync(tostring(plr.UserId), defaultSaveData)
	data = defaultSaveData
end

OR

data = PointsDS:GetAsync(tostring(plr.UserId))
if data == nil then
	-- Assign the default data directly to "data", then send that same variable to the data store.
	data = {
		Points = 0,
		Weapons = {"S_Classic", "L_Classic", "T_Classic"}
	}
	
	PointsDS:SetAsync(tostring(plr.UserId), data)
end

You are still trying to use the result of SetAsync as a means to access data.
I recommend that the code within the pcall to instead be something like this:

data = PointsDS:GetAsync(tostring(plr.UserId))
	if data == nil then
		data = { -- set the default values
				Points = 0,
				Weapons = {"S_Classic", "L_Classic", "T_Classic"}
			}	
		PointsDS:SetAsync(tostring(plr.UserId), data)
	end

If you already have some saving logic elsewhere in the script, you can probably just remove the SetAsync altogether from this portion of the code, and could instead replace the contents of the pcall with

data = PointsDS:GetAsync(tostring(plr.UserId)) or {
			Points = 0,
			Weapons = {"S_Classic", "L_Classic", "T_Classic"}
		}

Isn’t the data supposed to be the second parameter? Right after tostring(plr.UserId)

Yeah, apologies. That was a typo

Sorry for the delay in response. The points now print, however the weapons table is still not recognised by the script.

Can you please send your updated script, plus a screenshot of the output?

game.Players.PlayerAdded:Connect(function(plr)
	local leaderstats = script.leaderstats:Clone()
	leaderstats.Parent = plr
	
	local data
	local success, error = pcall(function()
		data = PointsDS:GetAsync(tostring(plr.UserId))
		if data == nil then
			data = {
				Points = 0,
				Weapons = {"S_Classic", "L_Classic", "T_Classic"}
			}	
			PointsDS:SetAsync(tostring(plr.UserId), data)
		end
	end)
	if success and data then
		print(data)
		print(data.Points)
		print(data.Weapons)
		leaderstats.Points.Value = data.Points
		for i, weapon in pairs(data.Weapons) do
			ShopEvent:FireClient(plr, "Update", data.Weapons)
		end
	else
		task.wait(1)
		plr:Kick("Data failed to load.")
	end
	
	PlrWeapons.S_Classic:Clone().Parent = plr.Backpack
	PlrWeapons.L_Classic:Clone().Parent = plr.Backpack
	PlrWeapons.T_Classic:Clone().Parent = plr.Backpack

	while true do
		leaderstats.Points.Value += 1
		task.wait(10)
	end
end)

I’ll take a quick guess. Have you saved the default data once without the Weapons table? Just to be sure, try overwriting the save data at least once with the nested Weapons table in it.

At the top right of the output window, can you please ensure that these 2 are turned off?

Then resend the output

1 Like

@Y_VRN I’ve tried what you asked, and have properly cleared the data and saved. Now the weapons table is working. Output is alright. Now it’s just a matter of saving data when changes occur to points or weapons.

1 Like

Since the main issue with the code seems to be resolved, I would like to suggest some tweaks for improvement.

Rather than having all that in the pcall, it could be placed outside of its scope.

local success, data = pcall(PointsDS.GetAsync, PointsDS, tostring(plr.UserId))
if success then
	if data == nil then
		data = {
			Points = 0,
			Weapons = {"S_Classic", "L_Classic", "T_Classic"}
		}
	elseif data ~= nil then
		-- if there is save data present
	end
elseif not success then
	 -- error handling if GetAsync failed
end

...

PointsDS:SetAsync(tostring(plr.UserId), data)

It makes it somewhat more readable, not to mention it’ll set up the default data if GetAsync threw an exception, which the original code didn’t account for.

I recommend making just a small change to this section of the code so that it doesn’t run until the server closes.

while plr:IsDescendantOf(game.Players) do -- while the player's in the game
	leaderstats.Points.Value += 1
	task.wait(10)
end
2 Likes