Here’s my first forum post. I’ll be sharing with you what I’ve called a “Proxy Listener”. Its core features are :
-
Event based updates to a table.
-
A key path for modified nested tables.
It’s come to my attention that this sounds like a “Shadow Table”, however the proxy listener permits a bit more usability and supports nested tables.
Here is the magic sauce :
local function getProxyListener(t, accessSignal : BindableEvent, keyNamePath : string?)
if not (accessSignal and accessSignal.Fire) then return error("Did not provide access signaler") end
keyNamePath = keyNamePath or "Base"
local prox = newproxy(true)
local proxMeta = getmetatable(prox)
proxMeta.__index = function(self, k)
local val = t[k]
if val == nil then
local interpretedKey = k:gsub("_", "")
if interpretedKey == "true" then
return t
else
return t[interpretedKey]
end
end
if type(val) == "table" then
return getProxyListener(val, accessSignal, keyNamePath .. "." .. k)
else
return val
end
end
proxMeta.__newindex = function(self, k, v)
if (type(v) == "table") or (type(v) == "function") then error("Cannot assign a table or function") end
t[k] = v
accessSignal:Fire(keyNamePath, k, v)
end
return prox
end
Example usage :
local _realTable = {Nest = { Nest = {Test = 0} } }
local tAccessSignal = Instance.new("BindableEvent")
local UsedTable = getProxyListener(_realTable, tAccessSignal)
-- Sometimes it is necessary to retrieve the "true" version of the table (for example, in loops):
for k, v in pairs(UsedTable._true) do
-- Code
end
tAccessSignal.Event:Connect(function(dataPath, key, value)
-- Fire event to the client (player, dataPath, key, value)
end)
I realize that creating a signal explicitly beforehand may be a bit uncomfortable. Please do share if you take the time to further condense this method of listening to table modifications.
The most useful thing in my opinion through my limited experience with using it is the ability to replicate changes from one machine to another without losing your references to an old table. You can use the parameters passed to easily and directly modify the specific nested key that was modified. This can be done with the following method :
local MyTable = {}
SomeService.TableUpdated:Connect(function(path : string, nodeKey, value : any)
local terminalNode = MyTable
for _, node in ipairs(path:split(".")) do
terminalNode = terminalNode[node]
end
terminalNode[nodeKey] = value
end)
Note: depending on your use, you may need to drop the “Base” key (can be modified on initialization) when parsing the path.
You’ll find that any reference to the table (and indeed, references to a nested table within MyTable) are perfectly usable and have no need to be updated with a :GetData() method - it’s all ready to go at any time !
I hope the readers may find this useful and please comment any concerns or suggestions !