I’m having an issue with tables syncing values even though they are not supposed to sync.
I have a module that stores a shallow copy of a table. The module has a function that will return said table. But when a script changes the value of the copied table, the original table will also change. I am unable to figure out why.
Here is a small snippet of my code:
function ClientSaveData:GetData()
return ClientSaveData._Data
--[[
For testing, it should return:
{
SaveFiles = {
[1] = {- -}
}
}
]]--
end
function ClientSaveData:GetSaveFileFromId(SaveFileId: number): (any?, number?)
if not ClientSaveData._Data then return end
for i, saveFile in ipairs(ClientSaveData._Data.SaveFiles) do
if saveFile.SaveFileId ~= SaveFileId then continue end
return saveFile, i
end
end
function ClientSaveData:LoadSaveFile(SaveFileId: number, SaveFile: any?)
if ClientSaveData._CurrentSaveFile then
error("A save file is already loaded.")
end
local SaveFile = SaveFile or ClientSaveData:GetSaveFileFromId(SaveFileId)
if not SaveFile then return end
local SaveFileCopy = table.clone(SaveFile)
ClientSaveData._CurrentSaveFile = SaveFileCopy
return ClientSaveData._CurrentSaveFile
end
function ClientSaveData:GetCurrentSaveFile()
return ClientSaveData._CurrentSaveFile
end
A separate script which loads the save file:
ClientSaveData:LoadSaveFile(1)
Another script, separate from the previous one:
local OriginalSaveFile = ClientSaveData:GetData().SaveFiles[1]
local SaveFile = ClientSaveData:GetCurrentSaveFile()
SaveFile.Test = true -- Will also change OriginalSaveFile.Test to true
print(SaveFile == OriginalSaveFile) -- Will print false
Does anyone know the reason why the two different tables sync? This issue breaks my game so I really need a fix for it.
Can you do the clone in the calling file? That will leave it as a reference for other scripts but allow you to modify the local copy here.
local OriginalSaveFile = ClientSaveData:GetData().SaveFiles[1]
local SaveFile = table.clone(ClientSaveData:GetCurrentSaveFile())
SaveFile.Test = true -- Will also change OriginalSaveFile.Test to true
print(SaveFile == OriginalSaveFile) -- Will print false
I found a cheeky solution. Instead of making a copy of the table in the same run context. I used a remote function that will clone the table on a different run context, then return it.
function ClientSaveData:LoadSaveFile(SaveFileId: number, SaveFile: any?)
if ClientSaveData._CurrentSaveFile then
error("A save file is already loaded.")
end
local SaveFile = SaveFile or ClientSaveData:GetSaveFileFromId(SaveFileId)
if not SaveFile then return end
local SaveFileCopy = CreateServerCopyOfTable:InvokeServer(SaveFile)
ClientSaveData._CurrentSaveFile = SaveFileCopy
return ClientSaveData._CurrentSaveFile
end
Server:
CreateServerCopyOfTable.OnServerInvoke = function(_, t)
return table.clone(t)
end
I’ve had a similar problem where one script was modifying the values passed from a module and it can be hard to track down. I ended up using deep clones whenever returning table values from the module and then adding a set function which made the modification of the internal data more explicit. It results in more data copying but makes debugging a lot easier.