Metatables are basically tables that describes how an object will act under certain conditions. The setmetatable function is usually invoked once during the initialization phase of a script and can contain the following methods/members:
Metatable Methods
__call(self, ...)
- The function referenced by this key will be invoked when you enter object(), for example:
object = {}
setmetatable(object, {__call = function(self, name)
print("Hello "..name)
end})
object("World") -- Prints out "Hello World"
__index(self, key)
- The function referenced by this key will be invoked when you attempt to pass in an undefined index, for example:
object = {}
setmetatable(object, {__index = function(self, name) -- Here name is the same as key
print("Hello "..name)
end})
object.World -- Prints out "Hello World"
__add(self, key)
- The function referenced by this key will be invoked when you use the binary operator '+' in conjunction with another object, for example:
vector = {0, 0}
setmetatable(object, {__add = function(self, other)
return {self[1]+other[1], self[2]+other[2]}
end})
vector + {10, 10} -- {10, 10}
and by extension, the following as well:
__sub(self, other) - Invoked on subtraction
__mul(self, other) - Invoked on multiplication
__div(self, other) - Invoked on division
__mod(self, other) - Invoked on modulus (multiplicative residual classes)
__pow(self, other) - Invoked on power (^)
__unm(self) - Invoked when a unary minus operation is done on the object (-object)
__concat(self, other) - Invoked on concatenate (..)
__len(self) - Invoked when a unary len operation is done on the object (#object)
__eq(self, other) - Invoked on equality test (object == object2)
__lt(self, other) - Invoked on less than and on greater than (object < object2), the latter will return the inverse of the former (object > object2 is equivalent to not (object < object2)
__le - Invoked on less than or equal to and on greater than or equal to (object <= object2), the latter will return the inverse of the former (object >= object2 is equivalent to not (object <= object2)
_newindex(self, key, val) - Invoked when you attempt to assign a new value to a key (will still be invoked if the key already exists in the _index.
There is also a hidden, but more arcane metamethod, as it cannot be invoked on native Lua types (IE: assigning it to numbers, tables, strings, and booleans will have no effect).
__gc(self) - Garbage collection method called when userdata are destroyed.
It's actually quite useful. One of the major problems with Lua in CS2D is that there is no way to tell when the server changes map or when the server shuts down through Lua (basically, CS2D doesn't have a hook for "exit"), however we can simulate this through the __gc metamethod. Note that it can only function on userdata so we need to construct a proxy first (proxies are empty C structs/embedded userdata that are basically prototypes for all Lua object):
(Note that these are undocumented features of Lua, aka not found in the standard documentation)
exit_registrar = {}
udata = newproxy(true) -- Constructs a new userdata 'proxy'
-- type(udata) == "userdata"
udata_mt = getmetatable(udata)
udata_mt.__gc = function(self)
-- Will be invoked when udata is destroyed, in other words, when the server dies.
-- Placeholder, for some reason, if this is not here, the __gc function becomes nondeterministic.
print("Server dying, calling exit hooks.")
-- Iterate through all of our exit hooks.
for _,k in ipairs(exit_registrar) do
local get = loadstring("return "..k)
if pcall(get) then -- If the variable exists
local fn = get()
if type(fn) == "function" then
fn()
end
end
end
end
local _addhook = addhook
function addhook(hook, fn)
if hook == "exit" then
-- Add fn to the exit registrar
if type(fn) ~= "string" then return end
table.insert(exit_registrar, fn)
else
-- Propagate the call back to _addhook
_addhook(hook, fn)
end
end
--Example:
addhook("exit", "extfunction")
function extfunction()
print "OH NOEZ"
end
On server exit, the following is printed:
Server dying, calling exit hooks. OH
NOEZ