Skip to content

Commit 47370db

Browse files
port over draft from personal fork
1 parent 0f2e534 commit 47370db

File tree

5 files changed

+132
-0
lines changed

5 files changed

+132
-0
lines changed

content/Guides/Lua Scripting/Common Pitfalls.md

Whitespace-only changes.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
Lua is a very small and feature-light programming language. Compared to several other programming languages, there are very few built-in functionalities and data structures. This makes it much easier to pick up the language, but it can also lead to repetitive code and result in rebuilding the wheel several times.
3+
4+
The most comprehensive (freely available) Lua guide is the first edition of the [Programming in Lua (PIL)](https://www.lua.org/pil/contents.html) book. Although, it is not without it's faults. Namely:
5+
- PIL is designed for Lua 5.0, while MWSE and openmw both use modified versions of Lua 5.1. This means some sections of PIL are outdated, and that PIL doesn't cover all of the features you'll have at your disposal when developing Lua mods.
6+
- Some parts of the book can be rather terse and/or highly abstract.
7+
- The book does not contain very many examples.
8+
9+
The goals of these pages will be to fill in some of the gaps in PIL book.
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
2+
## Overview
3+
4+
**Metatables** provide a way to extend the functionality of ordinary tables in Lua. They allow you give tables **metamethods**. The most commonly used metamethod is the `__index` metamethod, which controls what happens after a table tries to look up a missing key. Other commonly used metamethods include `__len`, which governs what happens when you use the `#` operator on a table, and `__call`, which lets you call a table as if it were a function. Typically, a metatable will be a table of the form `table<string, fun(...): ...>`. You can set and retrive metatables by using the `setmetatable` and `getmetatable` functions as follows:
5+
```lua
6+
local tbl = {}
7+
local meta = {}
8+
setmetatable(tbl, meta) -- Set the metatable of `tbl` to be `meta`
9+
10+
local m = getmetatable(tbl) -- retrieve the metatable of a table. Note that in this example, we have `m == meta`.
11+
```
12+
13+
14+
Strictly speaking, there is no difference between a "metatable" and an "ordinary" table: any `table` can act as a metatable to any other `table`.
15+
16+
> [!example]- Example: Any table can be a metatable.
17+
>
18+
> The following code does not produce any errors (although it doesn't do anything useful either):
19+
> ```lua
20+
> setmetatable({ a = 1, b = 2}, {c = 3, d = 4})
21+
> ```
22+
23+
You can think of a "metatable" as a way to provide a list of rules, that dictate what should happen whenever something tries to do certain things to a table. Let's continue with the above notation, where `tbl` is an "ordinary" `table` and `meta` is its metatable. Each `(key, value)` pair in `meta` takes the form `(name_of_action, response)`, where `name_of_action` is a string, and `response` is a function. Whenever your code tries to perform certain actions on `tbl`, it will check if `meta` has an entry that governs a `response` for that action.
24+
25+
26+
### In-depth Example: `__index` metamethod.
27+
28+
Let's work through an example to see how the various pieces fall into place. We will use the `__index` metamethod to control what a specific `table` does when we try to index a missing value from the table. We will use the following variables throughout this example.
29+
```lua
30+
local my_tbl = {a = 1, b = 2}
31+
local meta = {}
32+
setmetatable(my_tbl, meta)
33+
```
34+
(Note that you can update metamethods _after_ calling `setmetatable`.
35+
)
36+
The `__index` metamethod should be a `function` that takes two arguments:
37+
```lua
38+
---@param self table the table being accessed.
39+
---@param key: string|int|any the key that we tried to access
40+
---@return any value
41+
function meta.__index(self, key)
42+
end
43+
```
44+
(We haven't implemented any behavior yet, we'll do that later.)
45+
46+
Intuitively, the `__index` metamethod works as follows: whenever you try to retrive a value from `my_tbl`, by passing a specified `key`, (i.e. by writing `my_tbl[key]`), Lua will secretly be performing the following:
47+
```lua
48+
local value = my_tbl[key]
49+
-- Tried to access a missing key!
50+
if value == nil then
51+
-- Lets see if there's a metatable.
52+
local metatbl = getmetatable(my_tbl)
53+
if metatbl ~= nil and metatbl.__index then
54+
-- Metatable exists and it has an `__index` metamethod.
55+
-- Set `value` equal to whatever the metatable says it should be.
56+
value = metatbl.__index(my_tbl, key)
57+
end
58+
end
59+
```
60+
(The actual behavior is a bit more complicated than this, but this description is fine for the moment.)
61+
62+
> [!warning]- `__index` is only called when the relevant key is missing from the table.
63+
> As the above example makes clear, the `__index` metamethod **will not do anything** if you try to retrieve a `key` that actually exists in a table. Why is it like this? Who knows.
64+
65+
Let's say that for whatever reason we want to print a message whenever something tries to access a missing value from a table. We can do that as follows:
66+
```lua
67+
function meta.__index(self, key)
68+
print(string.format("tried to access missing key from a table: %q", key))
69+
end
70+
```
71+
This will result in the following:
72+
```lua
73+
-- add some vaues t
74+
my_tbl.a -- "a" is present in this table, so __index isn't called.
75+
my_tbl.c -- "c" is NOT present, so the following gets printed: tried to access missing key from a table: "c"
76+
```
77+
78+
What if we want missing values to be treated as if they were `0`? Then we could write
79+
```lua
80+
function meta.__index(self, key)
81+
-- Someone tried to access a value that didn't exist, let's just say it was 0.
82+
return 0
83+
end
84+
```
85+
We now get the following:
86+
```lua
87+
my_tbl.a + my_tbl.b == 3 -- Both keys are present in the table, __index isn't called.
88+
my_tbl.a + my_tbl[10] == 1 -- the key `10` is missing, so __index gets called and it returns 0
89+
```
90+
91+
What if we instead want `my_tbl` to look up missing values in some other table?
92+
Let's say we're writing some code that depends on config settings, and we have a table that stores default config settings, and another table that stores the users current config. To be precise:
93+
```lua
94+
local saved_config = {...} -- Stores user-specific settings.
95+
local default_config = {...} -- Stores default settings.
96+
```
97+
98+
If we added some new default settings since the user last used the mod, or if we provided a way to reset the saved config settings, then there could be keys in `default_config` that are not present in `saved_config`. It would be annoying to have check each time where a user-specific setting exists. Thankfully, we can use metatables to solve this problem:
99+
```lua
100+
local saved_config_meta = {}
101+
-- In our setting, the first parameter will always be equal to `saved_config`.
102+
-- We can ignore it because we're only interested in the contents of `default_config`
103+
function saved_config_meta.__index(_, key)
104+
-- Remember that this will only be called whenever `key` is NOT present in `saved_config`.
105+
-- So we don't need to perform any additional logic inside this function.
106+
return default_config[key]
107+
end
108+
setmetatable(saved_config, saved_config_meta)
109+
```
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
3+
Lua does not offer much support for object-oriented programming. It's recommended that you use a third party library to implement OOP features, unless you're only looking for the bare minimum amount of functionality. Trying to implement OOP by hand in Lua can lead to several bugs that are difficult to track down.
4+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
title: Lua Scripting
3+
aliases:
4+
- lua-scripting
5+
tags:
6+
- "#MWSE-Lua"
7+
- OpenMW-Lua
8+
description: Fundamentals of lua scripting
9+
---
10+
## Fundamentals of Lua Scripting

0 commit comments

Comments
 (0)