Checking for additions/removals in a data store table

The code I posted above is mostly just updates, so keep everything except changed code. I’ll post before and afters in case you’re unsure what goes where.

-- B:
for i, v in pairs(data) do
	playersData[player.UserId][i] = v
end

-- A:
local function addressTable(receivedValue)
	local tableToReturn = {}
	
	for key, value in pairs(receivedValue) do
		tableToReturn[key] = (type(receivedValue) == "table" and addressTable(value) or value)
	end
	
	return tableToReturn
end

for k, v in pairs(data) do -- assuming "data" is your prefab, changed that part for you
	playersData[player.UserId][k] = (type(v) == "table" and addressTable(v)) or v
end
-- B:
	if setData then
		for i, v in pairs(setData) do
			if playersData[player.UserId][i] then
				playersData[player.UserId][i] = v
			end
		end
	end

-- A (you'll have to tab this one):
if setData then
	for key, value in pairs(setData) do
		if playersData[player.UserId][key] then
			playersData[player.UserId][key] = (type(value) == "table" and addressTable(value)) or value
		end
	end
end
	playersData[player.UserId] = {}
	
	local function addressTable(receivedValue)
		local tableToReturn = {}
		
		for key, value in pairs(receivedValue) do -- ERROR ON THIS LINE!!
			tableToReturn[key] = (type(receivedValue) == "table" and addressTable(value) or value)
		end
	
		return tableToReturn
	end
	
	for k, v in pairs(data) do -- assuming "data" is your prefab, changed that part for you
		playersData[player.UserId][k] = (type(v) == "table" and addressTable(v)) or v
	end
		
	local loadJSON = playerDataStore:GetAsync(player.UserId)
	local setData = (loadJSON and httpService:JSONDecode(loadJSON)) or nil
	
	-- Data part
	if setData then
		for key, value in pairs(setData) do
			if playersData[player.UserId][key] then
				playersData[player.UserId][key] = (type(value) == "table" and addressTable(value)) or value
			end
		end
	end

Error:

[bad argument #1 to ‘pairs’ (table expected, got string)]

I am resetting the DataStore everytime I make a change in case it loads up a previous one as well

Are you able to provide the whole stack trace in your console? I find it odd that a string was passed to addressTable even though as far as I can see, it only passes values that are tables. Does it specify where else the issue occurs (either by the prefab location or by the review of fetched player data)?

13:31:59.071 - Stack Begin
13:31:59.072 - Script 'ServerScriptService.Core.GameManager.PlayerManager.DataManager', Line 125 - upvalue addressTable
13:31:59.075 - Script 'ServerScriptService.Core.GameManager.PlayerManager.DataManager', Line 126 - upvalue addressTable (x2)
13:31:59.076 - Script 'ServerScriptService.Core.GameManager.PlayerManager.DataManager', Line 126 - local addressTable
13:31:59.077 - Script 'ServerScriptService.Core.GameManager.PlayerManager.DataManager', Line 133 - method GetData
13:31:59.078 - Script 'ServerScriptService.Core.GameManager.PlayerManager', Line 13
13:31:59.080 - Stack End

For reference, Line 125 is the line where I have written --ERROR ON THIS LINE!! in the code

Lines 125 and 126 are what I assume are the table failing to run since non-table values are being passed to addressTable. What is Line 133, the function “GetData” and what do you currently use that for?

for key, value in pairs(receivedValue) do -- LINE 125
	tableToReturn[key] = (type(receivedValue) == 'table' and addressTable(value) or value) -- Line 126
end
	
for k, v in pairs(data) do -- assuming "data" is your prefab, changed that part for you
    playersData[player.UserId][k] = (type(v) == "table" and addressTable(v)) or v -- LINE 133
end

I’m under massive confusion. I’ll try and make a repro if the solution doesn’t somehow magically float its way to you, if that doesn’t trouble you too much.

Yea I got no clue what is going :sweat_smile:

Whatevers easiest :+1:

1 Like

scripting is so weird. i made the wonkiest repro and it worked.

Basically, I’ve given up all hope on trying to be graceful about it and I’m just letting the function handle all the work. It worked, but it may not be to your liking. I don’t even know what I did, it’s like black magic. I can’t fully comprehend what I did, but I know it works.

-- addressTable should now look like this. Replace the entire thing with this. Put it outside PlayerAdded if you can.
local function addressTable(receivedValue)
	local function produceTable(fromValue)
		local tableToReturn = {}
	
		for key, value in pairs(receivedValue) do -- yes
			tableToReturn[key] = addressTable(value)
		end
	
		return tableToReturn
	end
	
	return type(receivedValue) == "table" and produceTable(receivedValue) or receivedValue
end

-- Change anything that looks like below
(type(receivedValue) == 'table' and addressTable(value) or value)

-- to this:
addressTable(value)

Repro (my whole script except on an empty file):
weird data thing repro.rbxl (12.1 KB)

Trials below (using Crazyman32’s Data Store Editor plugin).

Trial result 1 (using test variable “timeOfUpdateTest”):

Trial result 2 (wiped everything except the array):

Mission success? I HOPE. PLEASE. THERE WERE NO ERRORS AND I USED ACCURATE PLAY SOLO.

Well it’s fixed those problems, and no errors but still doing the same thing as before :weary:

If I reset the data store, comment out any mention of the Scout, and run, yada yada works all good. Then leave, Uncomment the scout so it’s now part of the prefab, put a print(user.Classes.Scout) it prints nil.

Printing (user.Classes.Knight) and (user.Classes.Archer) prints a table, but no print for Scout. If I reset data store again, and join WITH scout in the table, then it prints Scout. But yea, not adding it to the player if they already have a data store :confused:

This is from your test place as well, just incase I put them in the wrong spot in my scripts

time for emergency sos request.

I’m sure that the strategy I had would’ve worked. The main thing I want to tackle is using your save data as configuration as opposed to using raw data so it’s easier to add things as you need, but apparently this has made the process a whole lot more complicated.

I think a new approach may have to be taken. I have any number of ways to go about it, such as changing class data to be saved as scopes to data stores (thus also decreasing the size of your primary data store).

Rip :confused: :confused: I got no clue what else to do :confused:

Tried messing around with the place more to see if I could magically come up with a fix but yeah nothing :confused: :confused:

1 Like

Sorry we couldn’t work out a solution with what I provided. Hope someone comes along with a better fix than mine and good luck with your game.

1 Like

Thanks for the time and effort though :+1:

Just been messing around with it, and ye, it still works with variables being added/removed whatever, but ye, not arrays :confused:

2 Likes

I made this little script for you (I didn’t test it).
This should update all the variables on the old object including tables.

function updateObject(old, new)
    local update = {}
    for name, variable in next, new do
        update[name] = (type(variable) == 'table' and old[name] and updateObject(old[name], variable)) or old[name] or variable
    end
    return update
end

Would I need to delete anything I already have?? And when would I call (updateObject)??

Guessing old would be the players data store, and new would be the preset one?

I’m not sure what you already have. I didn’t read the comment above. But you are correct. old is the old table that could need updating and new is the present one.
So in your case old is the decoded data off the datastore.

Ok, well Ill chuck it in and mess with it :+1:

Btw, current code I have is pasted in this thread

1 Like

I just plonked it in, not really knowing what needs to be changed, removed, but heres what I got

local data = {
	Classes = {
		['Knight'] = {
			['ID'] = 1,
			['Owned'] = true,
			['Weapons'] = {
				'Classic Sword',
			},
			['Armours'] = {
				'Knight Armour'
			},
			['Trails'] = {
				'None'
			},
		},
		['Archer'] = {
			['ID'] = 2,
			['Owned'] = true,
			['Weapons'] = {
				'Bow and Arrow'
			},
			['Armours'] = {
				'Archer Armour'
			},
			['Trails'] = {
				'None'
			},
		},
		['Scout'] = {
			['ID'] = 3,
			['Owned'] = false,
			['Weapons'] = {
				'Dagger'
			},
			['Armours'] = {
				'Scout Armour'
			},
			['Trails'] = {
				'None'
			},
		},
	},
	
	ClassEquips = {
		EquippedKnight = {
			Weapon = 'Classic Sword', 
			Armour = 'Knight Armour', 
			Trail = 'None'
		},
		EquippedArcher = {
			Weapon = 'Bow and Arrow', 
			Armour = 'Archer Armour', 
			Trail = 'None'
		},
		EquippedScout = {
			Weapon = 'Dagger', 
			Armour = 'Scout Armour', 
			Trail = 'None'
		},
	},
	
	EquippedClass = 'Knight',
	
	Level = 1,
	Exp = 0,
	Gold = 100,
	Gems = 0,
}

local httpService = game:GetService("HttpService")
local playerDataStore = game:GetService("DataStoreService"):GetDataStore("test005")
local playersData = {}

function updateObject(old, new)
local update = {}
for name, variable in next, new do
    update[name] = (type(variable) == 'table' and old[name] and updateObject(old[name], variable)) or old[name] or variable
end
return update
end

game.Players.PlayerAdded:Connect(function(player)
	playersData[player.UserId] = {}

	local function addressTable(receivedValue)
		local function produceTable(fromValue)
			local tableToReturn = {}
		
			for key, value in pairs(receivedValue) do -- yes
				tableToReturn[key] = addressTable(value)
			end
		
			return tableToReturn
		end
		
		return type(receivedValue) == "table" and produceTable(receivedValue) or receivedValue
	end
	
	for k, v in pairs(data) do -- assuming "data" is your prefab, changed that part for you
		playersData[player.UserId][k] = addressTable(v) -- yes
	end

	local loadJSON = playerDataStore:GetAsync(player.UserId)
	local setData = (loadJSON and httpService:JSONDecode(loadJSON)) or nil
	
	-- Data part
	if setData then
		for key, value in pairs(setData) do
			if playersData[player.UserId][key] then
				playersData[player.UserId][key] = addressTable(value)
			end
		end
	end
	
	updateObject(playersData[player.UserId], data)
	
	local user = playersData[player.UserId]
	
	print(user.Classes.Scout)
end)

game.Players.PlayerRemoving:Connect(function (player)
	local saveJSON = httpService:JSONEncode(playersData[player.UserId])
	playerDataStore:SetAsync(player.UserId, saveJSON)
end)

Didnt actually seem to change anything, probs cause I did it completely wrong

This is how it should work.

game.Players.PlayerAdded:Connect(function(player)
	local loadJSON = playerDataStore:GetAsync(player.UserId)
	local setData = (loadJSON and httpService:JSONDecode(loadJSON)) or {}}
	
	playersData[player.UserId] = updateObject(setData, data)
    
    local user = playersData[player.UserId]
	
	print(user.Classes.Scout)
end)

Ill also test it in a minute to check that it’s working.