Lua/Metatables

From SRB2 Wiki
< Lua
Jump to: navigation, search

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
  • __index is highly useful for allowing a table to fall back on another table if a key doesn't exist in the first. This can be use to simulate object inheritance and object-oriented programming in Lua (see this page for more info): If a table a falls back on a table b for keys that don't exist in a, it will appear as if a inherits those keys from b. The value for a key key that is inherited from b can be overridden in a by assigning a new value to a.key, which will prevent the fallback from occurring.
  • As a reminder, a.key is the same as a["key"], i.e., key from a.key is a variable of type string in this situation.
  • If a function is used as the value for __index, that function will be executed and its return value will be returned as the value of the a.key call.
  • If a table table is used as the value for __index, it will be used as the fallback table. If a.key does not exist, table.key will be used instead. If table.key does not exist either and table has its own __index metamethod, this metamethod will be called. This can be used to create a chain of tables falling back on each other.
__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
  • When simulating inheritance, as described for __index above, __newindex can be used to ensure that if the key whose value is changed is inherited from a fallback table, the value is changed in that fallback table.
  • If a function is used as the value for __newindex, that function will be executed with value as a parameter instead of assigning value to a.key.
  • If a table table is used as the value for __newindex, it will be used as the fallback table. If a.key does not exist, the value will be assigned to table.key instead. However, if table.key already exists and table has its own __usedindex metamethod, that metamethod will be called instead. If table.key does not exist and table has its own __newindex metamethod, this metamethod will be called.
__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.
  • If a function is used as the value for __usedindex, that function will be executed with value as a parameter instead of assigning value to a.key.
  • If a table table is used as the value for __usedindex, it will be used as the fallback table. Instead of assigning the value to a.key, it will be assigned to table.key instead. However, if table.key already exists and table has its own __usedindex metamethod, that metamethod will be called instead. If table.key does not exist and table has its own __newindex metamethod, this metamethod will be called.
Arithmetic
__add Addition (a + b) any function(a, b)
  • This operation can be performed even when the other value does not share the same metamethod or does not have one.
  • If a does not have a metamethod, b's metamethod is used instead.
  • a is always the first argument in the function, and b the second, even if b's metamethod is the one called.
__sub Subtraction (a - b) any function(a, b)
  • This operation can be performed even when the other value does not share the same metamethod or does not have one.
  • If a does not have a metamethod, b's metamethod is used instead.
  • a is always the first argument in the function, and b the second, even if b's metamethod is the one called.
__mul Multiplication (a * b) any function(a, b)
  • This operation can be performed even when the other value does not share the same metamethod or does not have one.
  • If a does not have a metamethod, b's metamethod is used instead.
  • a is always the first argument in the function, and b the second, even if b's metamethod is the one called.
__div Division (a / b) any function(a, b)
  • This operation can be performed even when the other value does not share the same metamethod or does not have one.
  • If a does not have a metamethod, b's metamethod is used instead.
  • a is always the first argument in the function, and b the second, even if b's metamethod is the one called.
__mod Modulus (a % b) any function(a, b)
  • This operation can be performed even when the other value does not share the same metamethod or does not have one.
  • If a does not have a metamethod, b's metamethod is used instead.
  • a is always the first argument in the function, and b the second, even if b's metamethod is the one called.
__pow Exponentation (a ^ b) any function(a, b)
  • This operation can be performed even when the other value does not share the same metamethod or does not have one.
  • If a does not have a metamethod, b's metamethod is used instead.
  • a is always the first argument in the function, and b the second, even if b's metamethod is the one called.
__unm Unary negation ( -a ) any function(a)
__len Length ( #a ) any function(a)
Comparison
__eq Equals check (a == b) any function(a, b)
  • a and b must share the same metamethod for __eq for the function to be run.
__lt Less-than (a < b)
or Greater-than (b > a)
any function(a, b)
  • Greater-than comparisons are done by flipping the order of a and b so it is treated as a less-than operation.
  • a and b must share the same metamethod for __lt for the function to be run.
  • __lt may also be run for less-than operations if a __le metamethod does not exist for both a and b (or they do not share the same __le metamethod). In this case, a <= b is checked as b > a, and b >= a as a < b.
__le Less-than-or-equals (a <= b)
or Greater-than-or-equals (b >= a)
any function(a, b)
  • Greater-than-or-equals comparisons are done by flipping the order of a and b so it is treated as a less-than-or-equals operation.
  • a and b must share the same metamethod for __le for the function to be run.
Bitwise operations
__and Bitwise AND (a & b) any function(a, b)
  • This operation can be performed even when the other value does not share the same metamethod or does not have one.
  • If a does not have a metamethod, b's metamethod is used instead.
  • a is always the first argument in the function, and b the second, even if b's metamethod is the one called.
__or Bitwise OR (a | b) any function(a, b)
  • This operation can be performed even when the other value does not share the same metamethod or does not have one.
  • If a does not have a metamethod, b's metamethod is used instead.
  • a is always the first argument in the function, and b the second, even if b's metamethod is the one called.
__xor Bitwise XOR (a ^^ b) any function(a, b)
  • This operation can be performed even when the other value does not share the same metamethod or does not have one.
  • If a does not have a metamethod, b's metamethod is used instead.
  • a is always the first argument in the function, and b the second, even if b's metamethod is the one called.
__shl Left shift (a << b) any function(a, b)
  • This operation can be performed even when the other value does not share the same metamethod or does not have one.
  • If a does not have a metamethod, b's metamethod is used instead.
  • a is always the first argument in the function, and b the second, even if b's metamethod is the one called.
__shr Right shift (a >> b) any function(a, b)
  • This operation can be performed even when the other value does not share the same metamethod or does not have one.
  • If a does not have a metamethod, b's metamethod is used instead.
  • a is always the first argument in the function, and b the second, even if b's metamethod is the one called.
__not Bitwise NOT ( ~a ) any function(a)
Miscellaneous
__concat Concatenation (a..b) any function(a, b)
  • This operation can be performed even when the other value does not share the same metamethod or does not have one.
  • If a does not have a metamethod, b's metamethod is used instead.
  • a is always the first argument in the function, and b the second, even if b's metamethod is the one called.
__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:
  • "k" – Keys are marked as weak
  • "v" – Values are marked as weak.
__metatable Metatable protection any This prevents the metatable for a from being accessed and modified:
  • getmetatable will return the value set for __metatable instead of the metatable itself.
  • setmetatable will print an error in the console instead of setting a new metatable.

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)
  • __indexmobj.var
  • __newindexmobj.var = value
(player_t)
  • __indexplayer.var
  • __newindexplayer.var = value
  • __len#player
player.powers
(player is player_t)
  • __indexplayer.powers[pw_powername]
  • __newindexplayer.powers[pw_powername] = value
  • __len#player.powers
(ticcmd_t)
  • __indexticcmd.var
  • __newindexticcmd.var = value
(skin_t)
  • __indexskin.var
  • __newindex → warning message to state skin_t cannot be modified
  • __len#skin
skin.soundsid
(skin is skin_t)
  • __indexskin.soundsid[SKSNAME]
  • __len#skin.soundsid
(mobjinfo_t)
  • __indexinfo.var
  • __newindexinfo.var = value
  • __len#info
(state_t)
  • __indexinfo.var
  • __newindexinfo.var = value
  • __len#info
(sfxinfo_t)
  • __indexinfo.var
  • __newindexinfo.var = value
  • __len#info
(hudinfo_t)
  • __indexinfo.var
  • __newindexinfo.var = value
  • __len#info
(mapheader_t)
  • __indexmapheader.var
  • __len#mapheader
(mapthing_t)
  • __indexmthing.var
  • __newindexmthing.var = value
(sector_t)
  • __indexsector.var
  • __newindexsector.var = value
  • __len#sector
sector.lines
(sector is sector_t)
  • __indexsector.lines[i]
  • __len#sector.lines
(subsector_t)
  • __indexsubsector.var
  • __len#subsector
(line_t)
  • __indexline.var
  • __len#line
line.sidenum
(line is line_t)
  • __indexline.sidenum[i]
(side_t)
  • __indexside.var
  • __newindexside.var = value
  • __len#side
(vertex_t)
  • __indexvertex.var
  • __len#vertex
(ffloor_t)
  • __indexffloor.var
  • __newindexffloor.var = value
(camera_t)
  • __indexcamera.var
(consvar_t)
  • __indexconsvar.var
(patch_t)
  • __indexpatch.var
  • __newindex → warning message to state patch_t cannot be modified
(colormap)
  • __index → warning message to state colormap is not a struct
Tables
mobjinfo
  • __indexmobjinfo[MT_NAME]
  • __newindexmobjinfo[MT_NAME] = { }
  • __len#mobjinfo
states
  • __indexstates[S_NAME]
  • __newindexstates[S_NAME] = { }
  • __len#states
S_sfx
or sfxinfo
  • __indexsfxinfo[sfx_name]
  • __newindexsfxinfo[sfx_name] = { }
  • __len#sfxinfo
sprnames
  • __indexsprnames[SPR_NAME] or sprnames["NAME"]
  • __len#sprnames
players
  • __indexplayers[i]
  • __len#players
skins
  • __indexskins[i] or skins["name"]
  • __len#skins
mapthings
  • __indexmapthings[i]
  • __len#mapthings
vertexes
  • __indexvertexes[i]
  • __len#vertexes
lines
  • __indexlines[i]
  • __len#lines
sides
  • __indexsides[i]
  • __len#sides
subsectors
  • __indexsubsectors[i]
  • __len#subsectors
sectors
  • __indexsectors[i]
  • __len#sectors
mapheaderinfo
  • __indexmapheaderinfo[i]
  • __len#mapheaderinfo
hudinfo
  • __indexhudinfo[HUD_NAME]
  • __len#hudinfo
Miscellaneous
(string)
  • __add → Concatenation using "+" (string1"+variable+"string2)
  • __indexString library functions, called as stringvar.function([args])
  Lua [view]
Language features SyntaxMetatables
SRB2 data ActionsConstantsFunctionsGlobal variablesHooksUserdata structures
Tutorials Freeslots and Object resourcesCustom player ability