Skip to content

Instantly share code, notes, and snippets.

@MrChickenRocket
Created August 25, 2024 05:35
Show Gist options
  • Save MrChickenRocket/4162e68f5230fd6267c3bece413fbab1 to your computer and use it in GitHub Desktop.
Save MrChickenRocket/4162e68f5230fd6267c3bece413fbab1 to your computer and use it in GitHub Desktop.
local module = {}
local deltaTable = require(game.ReplicatedFirst.Modules.DeltaTable)
module.tables = {}
module.callbacks = {}
module.remoteEvent = nil
--Connect for a callback
-- function(currentTable, delta, oldTable)
function module:Connect(name, callback)
if (module.tables[name] == nil) then
warn("Table " .. name .. " not found")
return
end
local callbackList = module.callbacks[name]
if (callbackList == nil) then
callbackList = {}
module.callbacks[name]= callbackList
end
--Add it
callbackList[callback] = callback
end
function module:Setup()
if (module.remoteEvent == nil) then
module.remoteEvent = game.ReplicatedStorage:WaitForChild("ReplicatedTableRemoteEvent")
module.remoteEvent.OnClientEvent:Connect(function(event)
local oldTable = module.tables[event.name]
if (module.tables[event.name] == nil) then
module.tables[event.name] = deltaTable:DeepCopy(event.data)
else
module.tables[event.name] = deltaTable:MergeTable(module.tables[event.name], event.data)
end
--see if theres any callbacks
local callbackList = module.callbacks[event.name]
if (callbackList ~= nil) then
for funct,value in callbackList do
funct(module.tables[event.name], event.data, oldTable)
end
end
end)
end
end
module:Setup()
return module
local module = {}
------------------------------------------------------------------------------------------------
-- Helpers
local function RecursiveCall(old, new, key, count, res)
--its a table, recurse
-- predicate
if type(old[key]) ~= "table" then
count = count + 1
res[key] = new[key]
return res, count
end
local newtable, num = module:DiffTable(old[key],new[key])
if (num > 0) then
count = count + 1
res[key] = newtable
end
return res, count
end
local function IfChangedSetCall(old, new, key, count, res)
local a = new[key]
local b = old[key]
--deletion
if (b ~= nil and a == nil) then
count = count + 1
res[key] = "_"
return res, count
end
if (a ~= b) then
count = count + 1
res[key] = a
end
return res, count
end
function module:DeepCopy(original)
local copy = {}
for k, v in original do
if type(v) == "table" then
v = module:DeepCopy(v)
end
copy[k] = v
end
return copy
end
function module:DiffTable(old, new)
local res = {}
local count = 0
for var,data in new do
if (type(new[var]) == "table") then
res, count = RecursiveCall( old, new, var, count,res)
else
res, count = IfChangedSetCall(old, new, var, count, res)
end
end
return res,count
end
function module:MergeTable(old, diffTable)
local newTable = module:DeepCopy(old)
for key,value in diffTable do
-- If we signal a deletion then here we go
if diffTable[key] == "_" then
newTable[key] = nil
continue
end
if (type(diffTable[key])=="table") then
if type(old[key]) ~= "table" then
newTable[key] = diffTable[key]
else
newTable[key] = module:MergeTable(old[key], diffTable[key])
end
else
--see if its a numerical key
local numberKey = tonumber(key)
if (numberKey == nil) then
numberKey = key
end
newTable[numberKey] = diffTable[key]
end
end
return newTable
end
return module
--On the server
playerRecord.player = {
coins = 10,
} --Some random ass table
--Create the table, forget about it
--Remember to keep the handle around! Once tableHandle gets GC'd this cleans itself up
playerRecord.tableHandle = serverReplicatedTable:CreateReplicatedTable("runTimeData", playerRecord.runTimeData, playerRecord.player)
--On the client
--Event based access
clientReplicatedTableModule:Connect("runTimeData", function(current, delta, old)
print("runTimeData changed" ,delta)
end)
--or
--direct access
local runTimeData = clientReplicatedTableModule.tables["runTimeData"]
local module = {}
local deltaTable = require(game.ReplicatedFirst.Modules.DeltaTable)
module.remote = nil
module.tables = {}
setmetatable(module.tables,{__mode = "v" })
local function Map(sourceTable, fields)
local results = {}
results.fields = {}
results.numFields = 0
results.tables = {}
results.numTables = 0
for key,value in fields do
--see if it exists in the sourceTable
if (sourceTable[key] == nil) then
warn("Asked for a view of a field that doesnt exist:" .. key)
continue
end
if (typeof(value) == "table") then
--Recurse
local res = Map(sourceTable[key], value)
results.tables[key] = res
results.numTables += 1
else
results.fields[key] = sourceTable
results.numFields += 1
end
end
return results
end
local function Copy(data)
local result = {}
for key,value in data.fields do
local srcTable = value
local srcKey = key
local srcValue = srcTable[srcKey]
result[srcKey] = srcValue
end
--Do the tables now
for key,value in data.tables do
result[key] = Copy(value)
end
return result
end
function module:CreateReplicatedTable(tableName : string, sourceTable : any, player : Player)
return self:CreateReplicatedTableFiltered(tableName, sourceTable, player, nil)
end
function module:CreateReplicatedTableFiltered(tableName : string, sourceTable : any, player : Player, filter : any)
local record = {}
record.sourceTable = sourceTable
record.tableName = tableName
record.player = player
if (filter ~= nil) then
record.dataMapping = Map(sourceTable, filter)
else
record.dataMapping = nil
end
function record:Update()
local diff = nil
if (self.previous == nil) then
self.previous = {}
end
if (self.dataMapping) then
local result = Copy(record.dataMapping)
diff = deltaTable:DiffTable(self.previous, result)
self.previous = deltaTable:DeepCopy(result)
else
diff = deltaTable:DiffTable(self.previous, self.sourceTable)
self.previous = deltaTable:DeepCopy(self.sourceTable)
end
--has data?
if (next(diff) ~= nil) then
--send the diff
module.remote:FireClient(player, { id = "tableDelta", name = record.tableName, data = diff })
end
end
table.insert(self.tables, record)
--Replicate it right away
record:Update()
return record
end
function module:Setup()
if (game.ReplicatedStorage:FindFirstChild("ReplicatedTableRemoteEvent") == nil) then
module.remote = Instance.new("RemoteEvent")
module.remote.Name = "ReplicatedTableRemoteEvent"
module.remote.Parent = game.ReplicatedStorage
game["Run Service"].Heartbeat:Connect(function(dt)
for _,record in module.tables do
record:Update()
end
end)
end
end
module:Setup()
return module
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment