Updating player data

So I am making a game with some data that has to be saved for when ever the player plays my experience again. This goes pretty well, since I have a table for each player containing all the data that is needed for the next play session, But when ever I want to add a new key in my constructor that makes all the data, and sets all the keys to a default value. It won’t actually add the key for players that already have played the game in the past, since their table doesn’t contain that new key. So I tried to do some research on how to fix it, but it was hard to find a solution, so that’s why I am making this post in hope to find an answer.

Here is the script where I determine if the player that joined already played my game, and if we have to give him a new table containing all the keys with default values, or just give him back his data from his last play session.

And here is the script that contains the constructor
rb2
I hope everything in my explanation was clear, and that somebody can help me to find a solution :melting_face:

1 Like

I was actually stuck at this problem for a few days and I found a very hacky solution.
If you haven’t published your game yet, I recommend switching to ProfileService or DataStore2 as they support adding new keys automatically.

If the game is published then you can use this code:

-- template is your data constructor
-- data is player's data in table form

function AddNewKeys(player, Data)
	for i1,v1 in pairs(template) do
		local available1 = false
		for i2, v2 in pairs(Data) do
			if i2 == i1 then
				available1 = true
			end
		end
		if available1 == false then
			Data[i1] = template[i1]
		end
		for i3, v3 in pairs(v1) do
			local available2 = false
			for i4, v4 in pairs(Data[i1]) do
				if i4 == i3 then
					available2 = true
				end
			end
			if available2 == false then
				Data[i1][i3] = template[i1][i3]
			end
			for i5, v5 in pairs(v3) do
				local available3 = false
				for i6, v6 in pairs(Data[i1][i3]) do
					if i6 == i5 then
						available3 = true
					end
					if available3 == false then
						Data[i1][i3][i5] = template[i1][i3][i5]
					end
				end
			end
		end
	end
end

The function basically checks if the stat in player’s Data exists, if not then it will duplicate from template to the player’s Data. The function supports up to 3 layers of data, if you want more then you can add next layers in a similar way.

1 Like

So it can’t add more then 4 pieces of Data at the same time?

You have to manually update it yourself, DataStoreService does not automatically do this for you. It actually is quite simple! Since ProfileService was mentioned in this thread, I’ll show you the method loleris implemented for this issue.

There are 2 main functions used:

local function DeepCopyTable(t)
	--<< Creates a deep copy of the given table and returns >>--
	local copy = {}
	for key, value in pairs(t) do
		if type(value) == "table" then
			copy[key] = DeepCopyTable(value)
		else
			copy[key] = value
		end
	end
	return copy
end

local function Reconcile(Data, Temp)
	for k, v in pairs(Temp) do
		if type(k) == "string" then -- Only string keys will be reconciled
			if Data[k] == nil then
				if type(v) == "table" then
					Data[k] = DeepCopyTable(v)
				else
					Data[k] = v
				end
			elseif type(Data[k]) == "table" and type(v) == "table" then
				Reconcile(Data[k], v)
			end
		end
	end

	return Data
end

DeepCopyTable returns a copy of the given table. Reconcile assigns the new key(s) and value(s) into the existing data.

Implementing this into your own system is relatively easy. You first retrieve the data and then use the data that you received and run the Reconcile function using the existing data and the data template for the 2 arguments. From the looks of it, however, you don’t seem to keep a data template anywhere other than a function that runs other stuff that you probably don’t want in the event that the player already has existing data. You don’t have to do much to change this, most of the key values do not need to be updated when the player joins for the first time - only the UserId does.

local DataTemplate = {
     ["PlayerUserId"] = 0, -- Temporary
     ["Cash"] = 0,
     ["CashText"] = 0,
     ["Multiplier"] = 1,
     ["MultiplierText"] = 1,
     ["Skill"] = 0
}

-- Or if you just want a function to return this template you can do that too
function CreateStats.ReturnTemplate()
     return {
          ["PlayerUserId"] = 0, -- Temporary
          ["Cash"] = 0,
          ["CashText"] = 0,
          ["Multiplier"] = 1,
          ["MultiplierText"] = 1,
          ["Skill"]  0
     }
end

To add this to the CreateStats.New() function, you can just reference the template and update the needed values (like the UserId) manually. The DeepCopyTable function we created earlier will also help with this:

local DataTemplate = {
     ["PlayerUserId"] = 0 -- Temporary
     ["Cash"] = 0,
     ["CashText"] = 0,
     ["Multiplier"] = 1,
     ["MultiplierText"] = 1,
     ["Skill"] = 0
}

function CreateStats.New(player)
     local Data = require(script.Parent.PlayerDataStorage)
     local GuiTemplate = game.ServerStorage.NewPlayerStats:Clone()
     local PlayerData = DeepCopyTable(DataTemplate)
     -- local PlayerData = DeepCopyTable(CreateStats.ReturnTemplate()) if you created a function to return the data
     PlayerData["PlayerUserId"] = player.UserId

     return PlayerData
end

Now, we just run the Reconcile function on the existing data when you first retrieve it.

function PlayerHasJoiend(player)
     local Data
     local success, err = pcall(function()
          Data = experienceStore:GetAsync(player.UserId)
     end)
     if success then
          Data = Data and Reconcile(Data, DataTemplate) or Constructor.New(player) -- Reconciles existing data or creates new data if existing data does not exist
          PlayerData.AddPlayerData(player, Data)
     end
     -- Rest of your code here
end

And there you have it! Existing data gets updated with any new keys added to your data template! I didn’t have much time to test or thoroughly examine what I was writing so if you have any issues let me know! :slight_smile:

1 Like

It works, but now I have run into a problem that I had earlier where I also received help for yesterday, basically my data will not safe when ever I test in roblox studio, but when I play the game from the roblox page it works perfectly fine.

And I just realized that when I add keys in this template, and later remove them that those keys will still be in the data of the player as well :frowning: