Lua/Metatables
Metatables are an advanced feature of Lua that can be used to define custom operations for variables. Each variable has a set of metatable events that are called in certain situations, usually when a particular operator is used on the variable. For example, when the length operator is used on a variable var
(e.g., #var
), the metatable event __len
is called. A variable can have a metatable applied to it, which is a table where the keys are the metatable events and the values are metamethods that will be executed when the corresponding metatable event is called. Metamethods are usually functions, although in some cases tables or other types of values are used. Applying metamethods to metatable events is similar to how functions can be bound to hooks. For example, if you want to control what happens when #var
is called, you can write a metamethod, create a metatable for var
and assign your metamethod to the metatable's __len
key. Then, whenever #var
is called, your metamethod is executed. For more information on how metatables work, consult the Metamethods Tutorial on the lua-users wiki.
Usage
Metatables are created like normal tables, with the keys being the names of the metatable events that you want to use:
local metatable = {__event = value}
local metatable = {__event1 = value1, __event2 = value2}
Individual events can be modified afterwards:
metatable.__event = newvalue
You can also assign the metamethods to other variables outside of the metatable:
local var = metatable.__event
If the metamethod of an event is a function, it may be called as such:
metatable.__event(arguments)
setmetatable
is used to apply a metatable to a table:
local myTable = {}
local metatable = {__event = value}
setmetatable(myTable, metatable)
Alternatively the metatable can be applied implicitly:
local myTable = {}
setmetatable(myTable, {__event = value})
Because setmetatable
returns the table that was supplied to it (in this example, {}
is used as a dummy table), the metatable can also be created and applied in the same line:
local myTable = setmetatable({}, {__event = value})
getmetatable
can be used to retrieve the metatable for a table or other variable:
local metatable = getmetatable(myTable)
getmetatable
can retrieve the metatable for any type of variable, not just tables. It will return nil
if no metatable is set. Note that getmetatable
returns a direct reference to the metatable rather than just a copy – if this reference is modified, it will directly affect the metatable for the variable itself!
By contrast, setmetatable
can only be used on tables. This means that it is not possible to create new metatables for other types of variables, including userdata. However, strings and userdata already have metatables applied to them in SRB2, which you can retrieve and modify with getmetatable
.
If a variable's metatable has the metatable event __metatable
set, the metatable cannot be retrieved or modified – instead, getmetatable
will return the value of __metatable
, while setmetatable
will print an error in the console.
List of events
For any examples below, a
represents the variable the metatable events are applied to. Binary operations such as addition or subtraction use a second variable, which will be represented by b
. In the "Type of value" column, functions are written in the format returntype function(args)
, where returntype
of the type of value returned by the function and args
is the list of arguments taken by the function.
Name | Event | Type of value | Further details |
---|---|---|---|
Table indexes | |||
__index
|
Accessing a table at a key that does not exist (a.key or a["key"] , value is nil )
|
any function(a, key) or table |
|
__newindex
|
Assigning a value to a new key in a table ( a.key = value or a["key"] = value , old value is nil )
|
void function(a, key, value) or table |
|
__usedindex
|
Assigning a value to an existing key in a table ( a.key = value or a["key"] = value , old value is not nil )
|
void function(a, key, value) or table |
This metamethod is only usable for tables.
|
Arithmetic | |||
__add
|
Addition (a + b )
|
any function(a, b)
|
|
__sub
|
Subtraction (a - b )
|
any function(a, b)
|
|
__mul
|
Multiplication (a * b )
|
any function(a, b)
|
|
__div
|
Division (a / b )
|
any function(a, b)
|
|
__mod
|
Modulus (a % b )
|
any function(a, b)
|
|
__pow
|
Exponentation (a ^ b )
|
any function(a, b)
|
|
__unm
|
Unary negation ( -a )
|
any function(a)
|
|
__len
|
Length ( #a )
|
any function(a)
|
|
Comparison | |||
__eq
|
Equals check (a == b )
|
any function(a, b)
|
|
__lt
|
Less-than (a < b )or Greater-than ( b > a )
|
any function(a, b)
|
|
__le
|
Less-than-or-equals (a <= b )or Greater-than-or-equals ( b >= a )
|
any function(a, b)
|
|
Bitwise operations | |||
__and
|
Bitwise AND (a & b )
|
any function(a, b)
|
|
__or
|
Bitwise OR (a | b )
|
any function(a, b)
|
|
__xor
|
Bitwise XOR (a ^^ b )
|
any function(a, b)
|
|
__shl
|
Left shift (a << b )
|
any function(a, b)
|
|
__shr
|
Right shift (a >> b )
|
any function(a, b)
|
|
__not
|
Bitwise NOT ( ~a )
|
any function(a)
|
|
Miscellaneous | |||
__concat
|
Concatenation (a..b )
|
any function(a, b)
|
|
__tostring
|
Conversion to string using tostring
|
string function(a)
|
Lua will pass a itself to the function set for __tostring . The function should return a string as the result of the conversion.
|
__call
|
Calling as a function (a([args]) )
|
any function(a, [args])
|
This is used to treat a like a function, even if it is not one, when followed by parantheses as shown. a and any arguments passed to it will be passed along to the function set for __call , and the return value of this function will be the return value of the a([args]) call.
|
__gc
|
Garbage collection for userdata | void function(a)
|
This metamethod is only usable for userdata. This metamethod is called before the garbage collector collects the userdata, making it possible to define additional garbage collection behavior. |
__mode
|
Weak references handling | string | This metamethod is only usable for tables. This controls whether the keys or values of the table are weak references, depending on whether certain letters are present in the string:
|
__metatable
|
Metatable protection | any | This prevents the metatable for a from being accessed and modified:
|
List of existing metatables
SRB2's userdata types and global tables are in fact simply empty tables whose entire functionality comes through the implementation of their metatable events, mainly __index
for accessing elements and __newindex
for modifying them. The metatables and events applied to them are listed here.
Name/Type of variable | Metamethods set |
---|---|
Userdata | |
(mobj_t )
|
|
(player_t )
|
|
player.powers ( player is player_t )
|
|
(ticcmd_t )
|
|
(skin_t )
|
|
skin.soundsid ( skin is skin_t )
|
|
(mobjinfo_t )
|
|
(state_t )
|
|
(sfxinfo_t )
|
|
(hudinfo_t )
|
|
(mapheader_t )
|
|
(mapthing_t )
|
|
(sector_t )
|
|
sector.lines ( sector is sector_t )
|
|
(subsector_t )
|
|
(line_t )
|
|
line.sidenum ( line is line_t )
|
|
(side_t )
|
|
(vertex_t )
|
|
(ffloor_t )
|
|
(camera_t )
|
|
(consvar_t )
|
|
(patch_t )
|
|
(colormap )
|
|
Tables | |
mobjinfo
|
|
states
|
|
S_sfx or sfxinfo
|
|
sprnames
|
|
players
|
|
skins
|
|
mapthings
|
|
vertexes
|
|
lines
|
|
sides
|
|
subsectors
|
|
sectors
|
|
mapheaderinfo
|
|
hudinfo
|
|
Miscellaneous | |
(string) |
|
Lua | [view] | |
Language features | Syntax • Metatables | |
SRB2 data | Actions • Constants • Functions • Global variables • Hooks • Userdata structures | |
SRB2Kart data | Kart Userdata structures • Kart Functions • Kart Hooks • Kart Global Variables and Constants | |
Tutorials | Freeslots and Object resources • Custom player ability |