[How-to] Send Lua Metatables over MessagePack

I ran into an issue when trying to send metatables through TriggerEvent and finally thought of a simple solution to it.

Class FIle

---@class Something
Something = setmetatable({}, Something)

Something.__call = function()
    return "Something"
end

Something.__index = Something

function Something.new(name)
    local _Something = {
        Name = name
    }

    return setmetatable(_Something, Something)
end

function Something:Hello()
    print("My name is " .. self.Name)
end

function Something:SetName(name)
    print("Setting name to ", name)
    self.Name = name
end

Another file which instantiates the Something class and adds it to an array for indexing later. i.e. An array of currently logged in users.This also contains the “magic” function

local something = Something.new("Bob")

---@param tb table
function toPack(tb)
    local tmpObj = {}
    local tmpMt = getmetatable(tb) or {}

    for k,v in pairs(tb) do
        tmpObj[k] = v
    end

    for k,v in pairs(tmpMt) do
        if k ~= "__index" and k ~= "__call" and k ~= "new" then
            tmpObj[k] = v
        end
    end

    for k,v in pairs(tmpObj) do
        if type(v) == "function" then
            tmpObj[k] = function(...)
                v(tmpObj, ...)
            end
        end
    end

    return tmpObj
end

local mySomething = toPack(something)

local Cache = {}

Cache[1] = mySomething

RegisterCommand('doit', function()
    TriggerEvent('server:do', Cache[1])
end)

RegisterCommand('first', function()
    Cache[1].Hello()
end)

Another file that receives the table and wishes to manipulate/run functions on it

RegisterServerEvent('server:do')
AddEventHandler('server:do', function(something)
    something.Hello()
    something.SetName("Something")
end)

The idea of the “magic” function is to reassign all of the functions that exist on the metatable to the table itself along with a wrapped function which passes itself as the self parameter for ease of use.
This also allows for mutability of the orignal object through MessagePack, which means the call to SetName in the 3rd file is able to mutate the Name property of Cache[1]

Could probably design the class in a different way to not require this (such as a function that simply returns a table), but this was my solution if it was desired to use classes in this fashion

3 Likes