Hi there, I currently have a stats replicator that passes new values to the client every time that value on the server gets altered. Here’s the code for it:
local plrConst = {}
local activeTbl = {}
local RunService = game:GetService("RunService")
local transferEvent = script.transfer
local setUpModule = require(script.setUpData)
local plrStatsTbl = {
}
local function printTable(tab, string)
for i,v in pairs(tab) do
if type(v) == "table" then
print(string,i," : ",v,":::")
printTable(v, string.." ")
else
print(string,i," : ",v)
end
end
end
local function addChangedEvent(tbl)
local events = {}
local changedEvent = Instance.new("BindableEvent")
rawset(tbl, "Changed", changedEvent.Event )
local function getPropertyChangedSignal(key)
local properyChangedEvent = Instance.new("BindableEvent")
events[key] = events[key] or {}
table.insert(events[key], properyChangedEvent)
return properyChangedEvent.Event
end
rawset(tbl, "getPropertyChangedSignal", getPropertyChangedSignal)
return changedEvent, events
end
function constructMetatbl(originalTbl, callback)
local changedEvent, events
local proxy = setmetatable({}, {
__index = function(self, key)
return rawget(originalTbl, key)
end,
__newindex = function(self, key, v)
if callback then
callback(key, v)
end
if changedEvent and events then
changedEvent:Fire(key, v)
if events[key] then
for i, bindable in pairs(events[key]) do
bindable:Fire(v)
end
end
end
return rawset(originalTbl, key, v)
end,
})
changedEvent, events = addChangedEvent(proxy)
return proxy
end
function plrConst:addNewPlr(plr, dataTbl)
if not activeTbl[plr] then
local plrStatsProxy = constructMetatbl(plrStatsTbl, function(key, v)
transferEvent:FireClient(plr, {tab = "plrStats", Key = key, Val = v})
end)
local dataProxy = constructMetatbl(dataTbl, function(key, v)
transferEvent:FireClient(plr, {tab = "data", Key = key, Val = v})
end)
local newTbl = {
plrStats = plrStatsProxy,
data = dataProxy,
}
activeTbl[plr] = newTbl
transferEvent:FireClient(plr, {newTbl = { -- send non-metatables cuz client can't recieve metatables
plrStats = plrStatsTbl,
data = dataTbl,
}})
setUpModule:setUp(plr, activeTbl[plr])
return newTbl
end
end
function constructReadOnly(originalTbl)
local changedEvent, events
local proxy = setmetatable({}, {
__index = function(self, key)
return rawget(originalTbl, key)
end,
__newindex = function(self, key, v)
if changedEvent and events then
changedEvent:Fire(key, v)
if events[key] then
for i, bindable in pairs(events[key]) do
bindable:Fire(v)
end
end
end
print(key, v)
return nil
end,
})
changedEvent, events = addChangedEvent(proxy)
return proxy
end
function plrConst:returnTbl(plr, key)
if activeTbl[plr] then
if RunService:IsServer() then
return activeTbl[plr][key] or activeTbl[plr]
else
return constructReadOnly(activeTbl[plr][key] or activeTbl[plr]) --constructReadOnly(activeTbl[plr][key] or activeTbl[plr]) --activeTbl[plr][key] or activeTbl[plr]
end
end
end
if RunService:IsClient() then
local plr = game.Players.LocalPlayer
transferEvent.OnClientEvent:Connect(function(args)
if args.newTbl then -- construct
args.newTbl = {
plrStats = constructMetatbl(args.newTbl.plrStats, function(key, v)
--if serverTbl[plr] and serverTbl[plr][key] ~= v then
--return error("Cannot set values on the client")
--end
end),
data = constructMetatbl(args.newTbl.data, function(key, v)
--if serverTbl[plr] and serverTbl[plr][key] ~= v then
--return error("Cannot set values on the client")
--end
end),
}
--addChangedEvent(args.newTbl)
activeTbl[plr] = args.newTbl or activeTbl[plr]
end
--printTable(args, "")
if args.Key and args.tab then
--print(args.Key, args.Val)
activeTbl[plr][args.tab][args.Key] = args.Val
end
end)
end
return plrConst
As you can see I hooked up a remote event in the metatable on the server, update the value to the client. However, doing so allow the client to change the values without restriction (The values of the client’s table of course), although this doesn’t replicate to the server, all of my stats checking on the client are based off of the client’s table, so this will bypass that validation.
I have tried returning a read only table in the returnTbl
method as this is what the client uses to get their tables, however it doesn’t seem to work properly.
Basically I need to make the returnTbl
return a read-only table while referencing to the original client’s table to get updated values from it. (Making the original client table read-only does work but this will completely malfunction the remote the server sends as it can’t update any values unless I pass the whole table to it, which is very excessive)
Any help is appreciated