Some background information:
I am creating a system that manages player info in the AeroGameFramework. This info includes information on how their game session is progressing [Basically their money, the number of resources they have collected, etc].
Typically I would had used AeroGameFramework’s boiler plate .new function which looks like this:
local Class = {}
Class.__index = Clas
function Class.new()
local self = setmetatable({
["Key"] = "Number", --Example
}, PlayerClass)
return self
end
However, I want to be able to send this information to the client which I can’t because you can not send metatables through RemoteEvents. So I’d create a deepcopy of what I want to send every time to want to send it to the client!
So I want to avoid that by replacing my old .new functions with a new function that uses deep copying rather than metamethods.
The problem is I am unsure if replacing the old system would cause any problems. A bit of feedback would be appreciated.
Here is the copy function:
local function CopyTable(t)
assert(type(t) == "table", "First argument must be a table")
local tCopy = table.create(#t)
for k,v in pairs(t) do
if (type(v) == "table") then
tCopy[k] = CopyTable(v)
else
tCopy[k] = v
end
end
return tCopy
end
TL:DR Should I worry about replacing __index with copying?
local MyClass = {}
MyClass.__index = MyClass
function MyClass.new()
return setmetatable({
["Key"] = "Number"
}, MyClass)
end
function MyClass:DoSomething()
print("Something")
end
Are you asking if you can/should do this?:
local MyClass = {}
local function CopyTable(t) --[[ etc... ]] end
function MyClass.new()
local self = CopyTable(MyClass)
self["Key"] = "Number"
return self
end
function MyClass:DoSomething()
print("Something")
end
Well, no one’s gonna break down your door telling you it’s the wrong way to code
It would work as expected.
That being said, I don’t recommend it.
You’re allocating extra memory you don’t need – every instance now also keeps copies (at least of the keys) of all the members of its class.
If the class changes (some “static” member for example) changes at runtime, the instances will not pick up on that change, since they have their own copies.
I’m not sure what the issue is in the first place – you shouldn’t be sending metatables to the client, because you don’t (or at least shouldn’t) send classes over the wire, you send objects. Objects have their own state anyways (the Key = "Number" part), so why is the metatables not being copied a problem?
When I said
you send objects
That was sort of a lie. ROBLOX pretends like you send objects. Internally, they serialize that object (i.e. turn the table into a string) and send the string. On the other side, they parse the string back into a table.
That comes with its own limitations, and also the serialization includes things like private member variables that you might not necessarily want or care to send over the network.
… Instead, I would define some MyClass:Serialize() method on any classes that you want to be able to send over the network that turns the class into a compact string. Then, when you want to send the data, there’s no need for a deep copy. You just do remoteEvent:InvokeServer(object:Serialize()) and ROBLOX sends the string directly.
Add a Deserialize method as well that the client can call which takes in a string in the same format and spits out an instance of MyClass, and you’re good to go.
I wrote you a toy example. It's not tested, but demonstrates the concept.
local MyClass = {}
MyClass.__index = {}
-- some variable that only exists in the class, but not objects
MyClass._SomeStaticVariable = 10
function MyClass.new(a, b)
local self = {
-- public variables that exists in the object
Param1 = a,
Param2 = b,
-- private variable that exists in the object but not the class
_PrivateVariable = (a + b) % 10,
}
return setmetatable(self, MyClass)
end
-- convert to a string for network sending
function MyClass:Serialize()
-- you choose what to serialize, and no deep copying!
return self.Param1 .. ";" .. self.Param2
end
function MyClass:Deserialize(str)
-- convert back
local aStr, bStr = str:match("(%d+);(%d+)")
return MyClass.new(tonumber(aStr), tonumber(bStr))
end
function MyClass:DoSomething()
print("Something")
end
return MyClass