Improve wording and grammar [3/3]
This commit is contained in:
parent
5cad89cacc
commit
9e7657621a
@ -24,7 +24,7 @@ creating mods.
|
||||
## What are Games and Mods?
|
||||
|
||||
The power of Minetest is the ability to easily develop games without the need
|
||||
to create your own voxel graphics, voxel algorithms or fancy networking code.
|
||||
to create your own voxel graphics, voxel algorithms, or fancy networking code.
|
||||
|
||||
In Minetest, a game is a collection of modules which work together to provide the
|
||||
content and behaviour of a game.
|
||||
|
@ -7,8 +7,8 @@ idx: 6.1
|
||||
|
||||
## Introduction
|
||||
|
||||
The power of Minetest is the ability to easily create games without the need
|
||||
to write your own voxel graphics and algorithms or fancy networking.
|
||||
The power of Minetest is the ability to easily develop games without the need
|
||||
to create your own voxel graphics, voxel algorithms, or fancy networking code.
|
||||
|
||||
* [What is a Game?](#what-is-a-game)
|
||||
* [Game Directory](#game-directory)
|
||||
@ -21,8 +21,8 @@ to write your own voxel graphics and algorithms or fancy networking.
|
||||
|
||||
Games are a collection of mods which work together to make a cohesive game.
|
||||
A good game has a consistent underlying theme and a direction, for example
|
||||
maybe it's a classic crafter miner with hard survival elements, or maybe
|
||||
it's a space simulation game with a steam punk automation ascetic.
|
||||
it could be a classic crafter miner with hard survival elements, or
|
||||
it could be a space simulation game with a steam punk automation aesthetic.
|
||||
|
||||
Game design is a complex topic, and is actually a whole field of expertise.
|
||||
It's beyond the scope of the book to more than briefly touch on it.
|
||||
@ -31,7 +31,7 @@ It's beyond the scope of the book to more than briefly touch on it.
|
||||
|
||||
The structure and location of a game will seem rather familiar after working
|
||||
with mods.
|
||||
Games are found in a game location such as `minetest/games/<foo_game>`.
|
||||
Games are found in a game location, such as `minetest/games/foo_game`.
|
||||
|
||||
foo_game
|
||||
├── game.conf
|
||||
@ -57,9 +57,9 @@ convenient, as it'll make porting mods to another game much easier.
|
||||
|
||||
The best way to keep compatibility with another game is to keep API compatibility
|
||||
with any mods which have the same name.
|
||||
That is, if a mod uses the same name as another mod even if third party,
|
||||
then it should have a compatible API.
|
||||
For example, if a game includes a mod called `doors` then it should have the
|
||||
That is, if a mod uses the same name as another mod, even if third party,
|
||||
it should have a compatible API.
|
||||
For example, if a game includes a mod called `doors`, then it should have the
|
||||
same API as `doors` in Minetest Game.
|
||||
|
||||
API compatibility for a mod is the sum of the following things:
|
||||
@ -68,11 +68,11 @@ API compatibility for a mod is the sum of the following things:
|
||||
For example, `mobs.register_mob`.
|
||||
* Registered Nodes/Items - The presence of items.
|
||||
|
||||
It's probably fine to have partial breakages as long as 90% of dependency
|
||||
usecases still works. For example, not having a random utility function that was
|
||||
only actually used internally is ok, but not having `mobs.register_mobs` is bad.
|
||||
Small breakages aren't that bad, such as not having a random utility
|
||||
function that was only actually used internally, but bigger breakages
|
||||
related to core features are very bad.
|
||||
|
||||
It's difficult to maintain API compatibility with a disgusting God mega-mod like
|
||||
It's difficult to maintain API compatibility with a disgusting mega God-mod like
|
||||
*default* in Minetest Game, in which case the game shouldn't include a mod named
|
||||
default.
|
||||
|
||||
@ -83,7 +83,7 @@ To check whether a mod name has been taken, search for it on
|
||||
|
||||
### Groups and Aliases
|
||||
|
||||
Groups and Aliases are both massive tools in keeping compatibility between games,
|
||||
Groups and Aliases are both useful tools in keeping compatibility between games,
|
||||
as it allows item names to be different between different games. Common nodes
|
||||
like stone and wood should have groups to indicate the material. It's also a
|
||||
good idea to provide aliases from default nodes to any direct replacements.
|
||||
|
@ -42,8 +42,8 @@ local vm = minetest.get_voxel_manip()
|
||||
local emin, emax = vm:read_from_map(pos1, pos2)
|
||||
```
|
||||
|
||||
For performance reasons, an LVM may not read the exact area you tell it to.
|
||||
Instead, it may read a larger area. The larger area is given by `emin` and `emax`,
|
||||
For performance reasons, an LVM will almost never read the exact area you tell it to.
|
||||
Instead, it will likely read a larger area. The larger area is given by `emin` and `emax`,
|
||||
which stand for *emerged min pos* and *emerged max pos*. An LVM will load the area
|
||||
it contains for you - whether that involves loading from memory, from disk, or
|
||||
calling the map generator.
|
||||
@ -79,8 +79,9 @@ print(data[idx])
|
||||
|
||||
When you run this, you'll notice that `data[vi]` is an integer. This is because
|
||||
the engine doesn't store nodes using their name string, as string comparison
|
||||
is slow. Instead, the engine uses a content ID. You can find out the content
|
||||
ID for a particular type of node with `get_content_id()`. For example:
|
||||
is slow. Instead, the engine uses an integer called a content ID.
|
||||
You can find out the content ID for a particular type of node with
|
||||
`get_content_id()`. For example:
|
||||
|
||||
```lua
|
||||
local c_stone = minetest.get_content_id("default:stone")
|
||||
@ -117,13 +118,13 @@ end
|
||||
```
|
||||
|
||||
The reason for this touches on the topic of computer architecture. Reading from RAM is rather
|
||||
costly, so CPUs have multiple levels of caching. If the data a process requests
|
||||
costly, so CPUs have multiple levels of caching. If the data that a process requests
|
||||
is in the cache, it can very quickly retrieve it. If the data is not in the cache,
|
||||
then a cache miss occurs and it will fetch the data it needs from RAM. Any data
|
||||
surrounding the requested data is also fetched and then replaces the data in the cache. This is
|
||||
because it's quite likely that the process will ask for data near that location again. This means
|
||||
a good rule of optimisation is to iterate in a way that you read data one after
|
||||
another, and avoid memory thrashing.
|
||||
another, and avoid *cache thrashing*.
|
||||
|
||||
## Writing Nodes
|
||||
|
||||
@ -194,10 +195,10 @@ end
|
||||
|
||||
## Your Turn
|
||||
|
||||
* Create `replace_in_area(from, to, pos1, pos2)` which replaces all instances of
|
||||
* Create `replace_in_area(from, to, pos1, pos2)`, which replaces all instances of
|
||||
`from` with `to` in the area given, where `from` and `to` are node names.
|
||||
* Make a function which rotates all chest nodes by 90°.
|
||||
* Make a function which uses an LVM to cause mossy cobble to spread to nearby
|
||||
stone and cobble nodes.
|
||||
Does your implementation cause mossy cobble to spread more than a distance of one each
|
||||
Does your implementation cause mossy cobble to spread more than a distance of one node each
|
||||
time? If so, how could you stop this?
|
||||
|
@ -95,7 +95,7 @@ print(meta:get_string("count")) --> "3"
|
||||
### Special Keys
|
||||
|
||||
`infotext` is used in Node Metadata to show a tooltip when hovering the crosshair over a node.
|
||||
This is useful in order to show the owner of the node or the status.
|
||||
This is useful when showing the ownership or status of a node.
|
||||
|
||||
`description` is used in ItemStack Metadata to override the description when
|
||||
hovering over the stack in an inventory.
|
||||
@ -109,10 +109,10 @@ item or node.
|
||||
Tables must be converted to strings before they can be stored.
|
||||
Minetest offers two formats for doing this: Lua and JSON.
|
||||
|
||||
The Lua method tends to be a lot faster and exactly matches the format Lua
|
||||
uses for tables.
|
||||
JSON is a more standard format and is better structured, and so is well suited
|
||||
when you need to exchange information with another program.
|
||||
The Lua method tends to be a lot faster and matches the format Lua
|
||||
uses for tables, while JSON is a more standard format, is better
|
||||
structured, and is well suited when you need to exchange information
|
||||
with another program.
|
||||
|
||||
```lua
|
||||
local data = { username = "player1", score = 1234 }
|
||||
@ -164,8 +164,6 @@ If the mod is likely to be used on a server and will store lots of data,
|
||||
it's a good idea to offer a database storage method.
|
||||
You should make this optional by separating how the data is stored and where
|
||||
it is used.
|
||||
Using a database such as sqlite requires using the insecure environment, and
|
||||
can be painful for the user to set up.
|
||||
|
||||
```lua
|
||||
local backend
|
||||
@ -200,11 +198,17 @@ return backend
|
||||
|
||||
The backend_sqlite would do a similar thing, but use the Lua *lsqlite3* library
|
||||
instead of mod storage.
|
||||
You'll need to request an insecure environment and require the library:
|
||||
|
||||
Using a database such as SQLite requires using an insecure environment.
|
||||
An insecure environment is a table that is only available to mods
|
||||
explicitly whitelisted by the user, and it contains a less restricted
|
||||
copy of the Lua API which could be abused if available to malicious mods.
|
||||
Insecure environments will be covered in more detail in the
|
||||
[Security](../quality/security.html) chapter.
|
||||
|
||||
```lua
|
||||
local ie = minetest.request_insecure_environment()
|
||||
assert(ie, "Please add mymod to secure.trusted_mods")
|
||||
assert(ie, "Please add mymod to secure.trusted_mods in the settings")
|
||||
|
||||
local _sql = ie.require("lsqlite3")
|
||||
-- Prevent other mods from using the global sqlite3 library
|
||||
@ -214,8 +218,6 @@ end
|
||||
```
|
||||
|
||||
Teaching about SQL or how to use the lsqlite3 library is out of scope for this book.
|
||||
Insecure environments will be covered in more detail in the
|
||||
[security](../quality/security.html) chapter.
|
||||
|
||||
## Deciding Which to Use
|
||||
|
||||
@ -224,17 +226,21 @@ how it is formatted, and how large it is.
|
||||
As a guideline, small data is up to 10K, medium data is up to 10MB, and large
|
||||
data is any size above that.
|
||||
|
||||
Node metadata is a good choice when the data is related to the node.
|
||||
Node metadata is a good choice when you need to store node-related data.
|
||||
Storing medium data is fairly efficient if you make it private.
|
||||
|
||||
Item metadata should not be used to store anything but small amounts of data as it is not
|
||||
possible to avoid sending it to the client.
|
||||
The data will also be copied every time the stack is moved, or is accessed from Lua.
|
||||
The data will also be copied every time the stack is moved, or accessed from Lua.
|
||||
|
||||
Mod storage is good for medium data but writing large data may be inefficient.
|
||||
It's better to use a database for large data, to avoid having to write all the
|
||||
It's better to use a database for large data to avoid having to write all the
|
||||
data out on every save.
|
||||
|
||||
Databases are only viable for servers due to the
|
||||
need to whitelist the mod to access an insecure environment.
|
||||
They're well suited for large data sets.
|
||||
|
||||
## Your Turn
|
||||
|
||||
* Make a node which disappears after it has been punched five times.
|
||||
|
@ -8,12 +8,12 @@ redirect_from: /en/chapters/chat.html
|
||||
cmd_online:
|
||||
level: warning
|
||||
title: Offline players can run commands
|
||||
message: <p>A player name is passed instead of a player object, because mods
|
||||
message: <p>A player name is passed instead of a player object because mods
|
||||
can run commands on behalf of offline players. For example, the IRC
|
||||
bridge allows players to run commands without joining the game.</p>
|
||||
|
||||
<p>So make sure that you don't assume that the player is online.
|
||||
You can check by seeing if minetest.get_player_by_name returns a player.</p>
|
||||
You can check by seeing if <pre>minetest.get_player_by_name</pre> returns a player.</p>
|
||||
|
||||
cb_cmdsprivs:
|
||||
level: warning
|
||||
@ -27,7 +27,7 @@ cb_cmdsprivs:
|
||||
## Introduction
|
||||
|
||||
Mods can interact with player chat, including
|
||||
sending messages, intercepting messages and registering chat commands.
|
||||
sending messages, intercepting messages, and registering chat commands.
|
||||
|
||||
* [Sending Messages to All Players](#sending-messages-to-all-players)
|
||||
* [Sending Messages to Specific Players](#sending-messages-to-specific-players)
|
||||
@ -64,40 +64,25 @@ only visible to the named player, in this case player1.
|
||||
|
||||
## Chat Commands
|
||||
|
||||
To register a chat command, for example /foo, use register_chatcommand:
|
||||
To register a chat command, for example `/foo`, use `register_chatcommand`:
|
||||
|
||||
```lua
|
||||
minetest.register_chatcommand("foo", {
|
||||
privs = {
|
||||
interact = true
|
||||
interact = true,
|
||||
},
|
||||
func = function(name, param)
|
||||
return true, "You said " .. param .. "!"
|
||||
end
|
||||
end,
|
||||
})
|
||||
```
|
||||
|
||||
Calling /foo bar will display `You said bar!` in the chat console.
|
||||
In the above snippet, `interact` is listed as a required
|
||||
[privilege](privileges.html) meaning that only players with the `interact` privilege can run the command.
|
||||
|
||||
You can restrict which players are able to run commands:
|
||||
|
||||
```lua
|
||||
privs = {
|
||||
interact = true
|
||||
},
|
||||
```
|
||||
|
||||
This means only players with the `interact` [privilege](privileges.html) can run the
|
||||
command. Other players will see an error message informing them of which
|
||||
privilege they're missing. If the player has the necessary privileges, the command
|
||||
will run and the message will be sent:
|
||||
|
||||
```lua
|
||||
return true, "You said " .. param .. "!"
|
||||
```
|
||||
|
||||
This returns two values, a Boolean which shows the command succeeded
|
||||
and the chat message to send to the player.
|
||||
Chat commands can return up to two values,
|
||||
the first being a Boolean indicating success, and the second being a
|
||||
message to send to the user.
|
||||
|
||||
{% include notice.html notice=page.cmd_online %}
|
||||
|
||||
@ -117,30 +102,32 @@ Patterns are a way of extracting stuff from text using rules.
|
||||
local to, msg = string.match(param, "^([%a%d_-]+) (*+)$")
|
||||
```
|
||||
|
||||
The above implements `/msg <to> <message>`. Let's go through left to right:
|
||||
The above code implements `/msg <to> <message>`. Let's go through left to right:
|
||||
|
||||
* `^` means match the start of the string.
|
||||
* `()` is a matching group - anything that matches stuff in here will be
|
||||
returned from string.match.
|
||||
* `[]` means accept characters in this list.
|
||||
* `%a` means accept any letter and `%d` means any digit.
|
||||
* `%a` means accept any letter and `%d` means accept any digit.
|
||||
* `[%d%a_-]` means accept any letter or digit or `_` or `-`.
|
||||
* `+` means match the last thing one or more times.
|
||||
* `+` means match the thing before one or more times.
|
||||
* `*` means match any character in this context.
|
||||
* `$` means match the end of the string.
|
||||
|
||||
Put simply, this matches the name (a word with only letters/numbers/-/_),
|
||||
then a space, then the message (one of more of any character). The name and
|
||||
message are returned, as they're surrounded in parentheses.
|
||||
Put simply, the pattern matches the name (a word with only letters/numbers/-/_),
|
||||
then a space, then the message (one or more of any character). The name and
|
||||
message are returned, because they're surrounded by parentheses.
|
||||
|
||||
That's how most mods implement complex chat commands. A better guide to Lua
|
||||
Patterns would probably be the
|
||||
[lua-users.org tutorial](http://lua-users.org/wiki/PatternsTutorial)
|
||||
or the [PIL documentation](https://www.lua.org/pil/20.2.html).
|
||||
|
||||
<p class="book_hide">
|
||||
There is also a library written by the author of this book which can be used
|
||||
to make complex chat commands without Patterns called
|
||||
[ChatCmdBuilder](chat_complex.html).
|
||||
to make complex chat commands without patterns called
|
||||
<a href="chat_complex.html">Chat Command Builder</a>.
|
||||
</p>
|
||||
|
||||
|
||||
## Intercepting Messages
|
||||
@ -155,8 +142,8 @@ end)
|
||||
```
|
||||
|
||||
By returning false, you allow the chat message to be sent by the default
|
||||
handler. You can actually remove the line `return false`, and it would still
|
||||
work the same.
|
||||
handler. You can actually remove the line `return false` and it would still
|
||||
work the same, because `nil` is returned implicitly and is treated like false.
|
||||
|
||||
{% include notice.html notice=page.cb_cmdsprivs %}
|
||||
|
||||
|
@ -10,7 +10,7 @@ redirect_from: /en/chapters/hud.html
|
||||
|
||||
Heads Up Display (HUD) elements allow you to show text, images, and other graphical elements.
|
||||
|
||||
The HUD doesn't accept user input. For that, you should use a [Formspec](formspecs.html).
|
||||
The HUD doesn't accept user input; for that, you should use a [formspec](formspecs.html).
|
||||
|
||||
* [Positioning](#positioning)
|
||||
* [Position and Offset](#position-and-offset)
|
||||
@ -43,8 +43,8 @@ the HUD needs to work well on all screen types.
|
||||
Minetest's solution to this is to specify the location of an element using both
|
||||
a percentage position and an offset.
|
||||
The percentage position is relative to the screen size, so to place an element
|
||||
in the center of the screen you would need to provide a percentage position of half
|
||||
the screen, eg (50%, 50%), and an offset of (0, 0).
|
||||
in the centre of the screen, you would need to provide a percentage position of half
|
||||
the screen, e.g. (50%, 50%), and an offset of (0, 0).
|
||||
|
||||
The offset is then used to move an element relative to the percentage position.
|
||||
|
||||
@ -55,7 +55,7 @@ The offset is then used to move an element relative to the percentage position.
|
||||
Alignment is where the result of position and offset is on the element -
|
||||
for example, `{x = -1.0, y = 0.0}` will make the result of position and offset point
|
||||
to the left of the element's bounds. This is particularly useful when you want to
|
||||
make a text element left, center, or right justified.
|
||||
make a text element aligned to the left, centre, or right.
|
||||
|
||||
<figure>
|
||||
<img
|
||||
@ -65,7 +65,7 @@ make a text element left, center, or right justified.
|
||||
</figure>
|
||||
|
||||
The above diagram shows 3 windows (blue), each with a single HUD element (yellow)
|
||||
with a different alignment each time. The arrow is the result of the position
|
||||
and a different alignment each time. The arrow is the result of the position
|
||||
and offset calculation.
|
||||
|
||||
### Scoreboard
|
||||
@ -79,7 +79,7 @@ score panel like so:
|
||||
alt="screenshot of the HUD we're aiming for">
|
||||
</figure>
|
||||
|
||||
In the above screenshot all the elements have the same percentage position -
|
||||
In the above screenshot, all the elements have the same percentage position
|
||||
(100%, 50%) - but different offsets. This allows the whole thing to be anchored
|
||||
to the right of the window, but to resize without breaking.
|
||||
|
||||
@ -107,16 +107,21 @@ or remove a HUD element.
|
||||
The element's type is given using the `hud_elem_type` property in the definition
|
||||
table. The meaning of other properties varies based on this type.
|
||||
|
||||
`scale` is the maximum bounds of text, text outside these bounds is cropped, eg: `{x=100, y=100}`.
|
||||
`scale` is the maximum bounds of text; text outside these bounds is cropped, e.g.: `{x=100, y=100}`.
|
||||
|
||||
`number` is the text's colour, and is in [Hexadecimal form](http://www.colorpicker.com/), eg: `0xFF0000`.
|
||||
`number` is the text's colour, and is in [hexadecimal form](http://www.colorpicker.com/), e.g.: `0xFF0000`.
|
||||
|
||||
|
||||
### Our Example
|
||||
|
||||
Let's go ahead, and place all the text in our score panel:
|
||||
Let's go ahead and place all the text in our score panel:
|
||||
|
||||
```lua
|
||||
-- Get the dig and place count from storage, or default to 0
|
||||
local meta = player:get_meta()
|
||||
local digs_text = "Digs: " .. meta:get_int("score:digs")
|
||||
local places_text = "Places: " .. meta:get_int("score:places")
|
||||
|
||||
player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
position = {x = 1, y = 0.5},
|
||||
@ -127,12 +132,6 @@ player:hud_add({
|
||||
number = 0xFFFFFF,
|
||||
})
|
||||
|
||||
-- Get the dig and place count from storage, or default to 0
|
||||
local digs = tonumber(player:get_attribute("score:digs") or 0)
|
||||
local digs_text = "Digs: " .. digs
|
||||
local places = tonumber(player:get_attribute("score:digs") or 0)
|
||||
local places_text = "Places: " .. places
|
||||
|
||||
player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
position = {x = 1, y = 0.5},
|
||||
@ -191,8 +190,8 @@ You will now have this:
|
||||
The `text` field is used to provide the image name.
|
||||
|
||||
If a co-ordinate is positive, then it is a scale factor with 1 being the
|
||||
original image size, and 2 being double the size, and so on.
|
||||
However, if a co-ordinate is negative it is a percentage of the screensize.
|
||||
original image size, 2 being double the size, and so on.
|
||||
However, if a co-ordinate is negative, it is a percentage of the screensize.
|
||||
For example, `x=-100` is 100% of the width.
|
||||
|
||||
### Scale
|
||||
@ -200,7 +199,7 @@ For example, `x=-100` is 100% of the width.
|
||||
Let's make the progress bar for our score panel as an example of scale:
|
||||
|
||||
```lua
|
||||
local percent = tonumber(player:get_attribute("score:score") or 0.2)
|
||||
local percent = tonumber(meta:get("score:score") or 0.2)
|
||||
|
||||
player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
@ -226,7 +225,7 @@ There is one problem however, it won't update when the stats change.
|
||||
|
||||
## Changing an Element
|
||||
|
||||
You can use the ID returned by the hud_add method to update or remove it later.
|
||||
You can use the ID returned by the hud_add method to update it or remove it later.
|
||||
|
||||
```lua
|
||||
local idx = player:hud_add({
|
||||
@ -261,12 +260,11 @@ local saved_huds = {}
|
||||
function score.update_hud(player)
|
||||
local player_name = player:get_player_name()
|
||||
|
||||
local digs = tonumber(player:get_attribute("score:digs") or 0)
|
||||
local digs_text = "Digs: " .. digs
|
||||
local places = tonumber(player:get_attribute("score:digs") or 0)
|
||||
local places_text = "Places: " .. places
|
||||
|
||||
local percent = tonumber(player:get_attribute("score:score") or 0.2)
|
||||
-- Get the dig and place count from storage, or default to 0
|
||||
local meta = player:get_meta()
|
||||
local digs_text = "Digs: " .. meta:get_int("score:digs")
|
||||
local places_text = "Places: " .. meta:get_int("score:places")
|
||||
local percent = tonumber(meta:get("score:score") or 0.2)
|
||||
|
||||
local ids = saved_huds[player_name]
|
||||
if ids then
|
||||
|
@ -8,14 +8,16 @@ redirect_from: /en/chapters/player_physics.html
|
||||
|
||||
## Introduction
|
||||
|
||||
Player physics can be modified using physics overrides. Physics overrides can set the
|
||||
walking speed, jump speed and gravity constants. Physics overrides are set on a player
|
||||
by player basis, and are multipliers. For example, a value of 2 for gravity would make
|
||||
gravity twice as strong.
|
||||
Player physics can be modified using physics overrides.
|
||||
Physics overrides can set the walking speed, jump speed,
|
||||
and gravity constants.
|
||||
Physics overrides are set on a player-by-player basis,
|
||||
and are multipliers.
|
||||
For example, a value of 2 for gravity would make gravity twice as strong.
|
||||
|
||||
* [Basic Example](#basic_example)
|
||||
* [Available Overrides](#available_overrides)
|
||||
* [Mod Incompatibility ](#mod_incompatibility)
|
||||
* [Mod Incompatibility](#mod_incompatibility)
|
||||
* [Your Turn](#your_turn)
|
||||
|
||||
## Basic Example
|
||||
@ -28,16 +30,16 @@ minetest.register_chatcommand("antigravity", {
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
player:set_physics_override({
|
||||
gravity = 0.1 -- set gravity to 10% of its original value
|
||||
gravity = 0.1, -- set gravity to 10% of its original value
|
||||
-- (0.1 * 9.81)
|
||||
})
|
||||
end
|
||||
end,
|
||||
})
|
||||
```
|
||||
|
||||
## Available Overrides
|
||||
|
||||
player:set_physics_override() is given a table of overrides.\\
|
||||
`player:set_physics_override()` is given a table of overrides.\\
|
||||
According to [lua_api.txt]({{ page.root }}/lua_api.html#player-only-no-op-for-other-objects),
|
||||
these can be:
|
||||
|
||||
@ -57,7 +59,7 @@ unintended, it has been preserved in overrides due to its use on many servers.
|
||||
Two overrides are needed to fully restore old movement behaviour:
|
||||
|
||||
* new_move: whether the player uses new movement (default: true)
|
||||
* sneak_glitch: whether the player can use "sneak elevators" (default: false)
|
||||
* sneak_glitch: whether the player can use 'sneak elevators' (default: false)
|
||||
|
||||
## Mod Incompatibility
|
||||
|
||||
@ -69,5 +71,5 @@ player's speed, only the last one to run will be in effect.
|
||||
## Your Turn
|
||||
|
||||
* **Sonic**: Set the speed multiplier to a high value (at least 6) when a player joins the game.
|
||||
* **Super bounce**: Increase the jump value so that the player can jump 20 meters (1 meter is 1 node).
|
||||
* **Super bounce**: Increase the jump value so that the player can jump 20 metres (1 metre is 1 node).
|
||||
* **Space**: Make gravity decrease as the player gets higher.
|
||||
|
@ -12,10 +12,10 @@ Simple Fast Inventory (SFINV) is a mod found in Minetest Game that is used to
|
||||
create the player's inventory [formspec](formspecs.html). SFINV comes with
|
||||
an API that allows you to add and otherwise manage the pages shown.
|
||||
|
||||
Whilst SFINV by default shows pages as tabs, pages are called "pages" as
|
||||
it's entirely possible that a mod or game decides to show them in
|
||||
Whilst SFINV by default shows pages as tabs, pages are called pages
|
||||
because it is entirely possible that a mod or game decides to show them in
|
||||
some other format instead.
|
||||
For example, multiple pages could be shown on one view.
|
||||
For example, multiple pages could be shown in one formspec.
|
||||
|
||||
* [Registering a Page](#registering-a-page)
|
||||
* [Receiving events](#receiving-events)
|
||||
@ -25,7 +25,7 @@ For example, multiple pages could be shown on one view.
|
||||
## Registering a Page
|
||||
|
||||
SFINV provides the aptly named `sfinv.register_page` function to create pages.
|
||||
Simply call the function with the page's name, and its definition:
|
||||
Simply call the function with the page's name and its definition:
|
||||
|
||||
```lua
|
||||
sfinv.register_page("mymod:hello", {
|
||||
@ -41,7 +41,7 @@ The `make_formspec` function surrounds your formspec with SFINV's formspec code.
|
||||
The fourth parameter, currently set as `true`, determines whether the
|
||||
player's inventory is shown.
|
||||
|
||||
Let's make things more exciting. Here is the code for the formspec generation
|
||||
Let's make things more exciting; here is the code for the formspec generation
|
||||
part of a player admin tab. This tab will allow admins to kick or ban players by
|
||||
selecting them in a list and clicking a button.
|
||||
|
||||
@ -83,14 +83,11 @@ sfinv.register_page("myadmin:myadmin", {
|
||||
})
|
||||
```
|
||||
|
||||
There's nothing new about the above code, all the concepts are covered above and
|
||||
in previous chapters.
|
||||
There's nothing new about the above code; all the concepts are
|
||||
covered above and in previous chapters.
|
||||
|
||||
<figure>
|
||||
<img src="{{ page.root }}//static/sfinv_admin_fs.png" alt="Player Admin Page">
|
||||
<figcaption>
|
||||
The player admin page created above.
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
## Receiving events
|
||||
@ -104,10 +101,10 @@ on_player_receive_fields = function(self, player, context, fields)
|
||||
end,
|
||||
```
|
||||
|
||||
Fields is the exact same as the fields given to the subscribers of
|
||||
`minetest.register_on_player_receive_fields`. The return value of
|
||||
`on_player_receive_fields` is the same as a normal player receive fields.
|
||||
Please note that sfinv will consume events relevant to itself, such as
|
||||
`on_player_receive_fields` works the same as
|
||||
`minetest.register_on_player_receive_fields`, except that `context` is
|
||||
given instead of `formname`.
|
||||
Please note that SFINV will consume events relevant to itself, such as
|
||||
navigation tab events, so you won't receive them in this callback.
|
||||
|
||||
Now let's implement the `on_player_receive_fields` for our admin mod:
|
||||
@ -166,7 +163,7 @@ If you only need to check one priv or want to perform an 'and', you should use
|
||||
|
||||
Note that the `is_in_nav` is only called when the player's inventory formspec is
|
||||
generated. This happens when a player joins the game, switches tabs, or a mod
|
||||
requests it using SFINV's API.
|
||||
requests for SFINV to regenerate.
|
||||
|
||||
This means that you need to manually request that SFINV regenerates the inventory
|
||||
formspec on any events that may change `is_in_nav`'s result. In our case,
|
||||
|
@ -33,7 +33,7 @@ code is thrown in together with no clear boundaries. This ultimately makes a
|
||||
project completely unmaintainable, ending in its abandonment.
|
||||
|
||||
The opposite of this is to design your project as a collection of interacting
|
||||
smaller programs or areas of code.
|
||||
smaller programs or areas of code. <!-- Weird wording? -->
|
||||
|
||||
> Inside every large program, there is a small program trying to get out.
|
||||
>
|
||||
@ -51,14 +51,13 @@ a low amount of coupling, as this means that changing the APIs of certain areas
|
||||
will be more feasible.
|
||||
|
||||
Note that these apply both when thinking about the relationship between mods,
|
||||
and the relationship between areas inside a mod. In both cases you should try
|
||||
to get high cohesion and low coupling.
|
||||
and the relationship between areas inside a mod.
|
||||
|
||||
|
||||
## Model-View-Controller
|
||||
|
||||
In the next chapter we will discuss how to automatically test your code, and one
|
||||
of the problems we will have is how to separate your logic
|
||||
In the next chapter, we will discuss how to automatically test your
|
||||
code and one of the problems we will have is how to separate your logic
|
||||
(calculations, what should be done) from API calls (`minetest.*`, other mods)
|
||||
as much as possible.
|
||||
|
||||
@ -75,7 +74,7 @@ and any associated metadata. Actions you can take are `create`, `edit`, or
|
||||
receive fields. These are 3 areas that can usually be separated pretty well.
|
||||
|
||||
In your tests, you will be able to make sure that an action when triggered does
|
||||
the right thing to the data, but you won't need to test that an event calls an
|
||||
the right thing to the data. You won't need to test that an event calls an
|
||||
action (as this would require using the Minetest API, and this area of code
|
||||
should be made as small as possible anyway.)
|
||||
|
||||
@ -98,8 +97,8 @@ function land.get_by_name(area_name)
|
||||
end
|
||||
```
|
||||
|
||||
Your actions should also be pure, however calling other functions is more
|
||||
acceptable.
|
||||
Your actions should also be pure, but calling other functions is more
|
||||
acceptable than in the above.
|
||||
|
||||
```lua
|
||||
-- Controller
|
||||
@ -155,7 +154,7 @@ most of the calculations are made.
|
||||
The controller should have no knowledge about the Minetest API - notice how
|
||||
there are no Minetest calls or any view functions that resemble them.
|
||||
You should *NOT* have a function like `view.hud_add(player, def)`.
|
||||
Instead, the view defines some actions the controller can tell the view to do,
|
||||
Instead, the view defines some actions that the controller can tell the view to do,
|
||||
like `view.add_hud(info)` where info is a value or table which doesn't relate
|
||||
to the Minetest API at all.
|
||||
|
||||
@ -167,27 +166,27 @@ to the Minetest API at all.
|
||||
</figure>
|
||||
|
||||
It is important that each area only communicates with its direct neighbours,
|
||||
as shown above, in order to reduce how much you needs to change if you modify
|
||||
as shown above, in order to reduce how much you need to change if you modify
|
||||
an area's internals or externals. For example, to change the formspec you
|
||||
would only need to edit the view. To change the view API, you would only need to
|
||||
change the view and the controller, but not the model at all.
|
||||
|
||||
In practice, this design is rarely used because of the increased complexity
|
||||
and because it doesn't give many benefits for most types of mods. Instead,
|
||||
you tend to see a lot more of a less formal and strict kind of design -
|
||||
varients of the API-View.
|
||||
you will commonly see a less formal and strict kind of design -
|
||||
variants of the API-View.
|
||||
|
||||
|
||||
### API-View
|
||||
|
||||
In an ideal world, you'd have the above 3 areas perfectly separated with all
|
||||
events going into the controller before going back to the normal view. But
|
||||
this isn't the real world. A good half-way house is to reduce the mod into 2
|
||||
this isn't the real world. A good compromise is to reduce the mod into two
|
||||
parts:
|
||||
|
||||
* **API** - what was the model and controller. There should be no uses of
|
||||
* **API** - This was the model and controller above. There should be no uses of
|
||||
`minetest.` here.
|
||||
* **View** - the view as before. It's a good idea to structure this into separate
|
||||
* **View** - This was also the view above. It's a good idea to structure this into separate
|
||||
files for each type of event.
|
||||
|
||||
rubenwardy's [crafting mod](https://github.com/rubenwardy/crafting) roughly
|
||||
@ -204,40 +203,38 @@ as it doesn't use any Minetest APIs - as shown in the
|
||||
## Observer
|
||||
|
||||
Reducing coupling may seem hard to do to begin with, but you'll make a lot of
|
||||
progress by splitting your code up well using a design like the one given above.
|
||||
progress by splitting your code up using a design like the one given above.
|
||||
It's not always possible to remove the need for one area to communicate with
|
||||
another, but there are ways to decouple anyway - one such way being the Observer
|
||||
another, but there are ways to decouple anyway - one example being the Observer
|
||||
pattern.
|
||||
|
||||
Let's take the example of unlocking an achievement when a player first kills a
|
||||
rare animal. The naive approach would be to have achievement code in the mob
|
||||
rare animal. The naïve approach would be to have achievement code in the mob
|
||||
kill function, checking the mob name and unlocking the award if it matches.
|
||||
This is a bad idea however, as it makes the mobs mod coupled to the achievements
|
||||
code. If you kept on doing this - for example, adding XP to the mob death code -
|
||||
you could end up with a lot of messy dependencies.
|
||||
|
||||
Enter the Observer pattern. Instead of the mobs mod caring about awards, mobs
|
||||
exposes a way for other areas of code to register their interest in an event
|
||||
and receive data about the event.
|
||||
Enter the Observer pattern. Instead of the mymobs mod caring about awards,
|
||||
the mymobs mod exposes a way for other areas of code to register their
|
||||
interest in an event and receive data about the event.
|
||||
|
||||
```lua
|
||||
mobs.registered_on_death = {}
|
||||
function mobs.register_on_death(func)
|
||||
table.insert(mobs.registered_on_death, func)
|
||||
mymobs.registered_on_death = {}
|
||||
function mymobs.register_on_death(func)
|
||||
table.insert(mymobs.registered_on_death, func)
|
||||
end
|
||||
|
||||
-- mob death code
|
||||
for i=1, #mobs.registered_on_death do
|
||||
mobs.registered_on_death[i](entity, reason)
|
||||
for i=1, #mymobs.registered_on_death do
|
||||
mymobs.registered_on_death[i](entity, reason)
|
||||
end
|
||||
```
|
||||
|
||||
Then the other code registers its interest:
|
||||
|
||||
```lua
|
||||
|
||||
-- awards
|
||||
mobs.register_on_death(function(mob, reason)
|
||||
mymobs.register_on_death(function(mob, reason)
|
||||
if reason.type == "punch" and reason.object and
|
||||
reason.object:is_player() then
|
||||
awards.notify_mob_kill(reason.object, mob.name)
|
||||
@ -246,12 +243,12 @@ end)
|
||||
```
|
||||
|
||||
You may be thinking - wait a second, this looks awfully familiar. And you're right!
|
||||
The Minetest API is heavily Observer based to stop the engine having to care about
|
||||
The Minetest API is heavily Observer-based to stop the engine having to care about
|
||||
what is listening to something.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Good code design is subjective, and depends on the project you're making. As a
|
||||
Good code design is subjective, and highly depends on the project you're making. As a
|
||||
general rule, try to keep cohesion high and coupling low. Phrased differently,
|
||||
keep related code together and unrelated code apart, and keep dependencies simple.
|
||||
|
||||
|
@ -40,7 +40,7 @@ minetest.register_on_joinplayer(function(player)
|
||||
end)
|
||||
```
|
||||
|
||||
Do this:
|
||||
Do this instead:
|
||||
|
||||
```lua
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
@ -62,10 +62,10 @@ end)
|
||||
|
||||
## Don't Trust Formspec Submissions
|
||||
|
||||
Malicious clients can submit formspecs whenever they like with whatever content
|
||||
they like.
|
||||
Malicious clients can submit formspecs whenever they like, with
|
||||
whatever content they like.
|
||||
|
||||
For example, the following code has a vulnerability which will allow players to
|
||||
For example, the following code has a vulnerability which allows players to
|
||||
give themselves moderator privileges:
|
||||
|
||||
```lua
|
||||
@ -123,7 +123,7 @@ stack:get_meta():set_string("description", "Partially eaten")
|
||||
-- BAD! Modification will be lost
|
||||
```
|
||||
|
||||
Do this:
|
||||
Do this instead:
|
||||
|
||||
```lua
|
||||
local inv = player:get_inventory()
|
||||
@ -137,8 +137,6 @@ The behaviour of callbacks is slightly more complicated. Modifying an `ItemStack
|
||||
are given will change it for the caller too, and any subsequent callbacks. However,
|
||||
it will only be saved in the engine if the callback caller sets it.
|
||||
|
||||
Avoid this:
|
||||
|
||||
```lua
|
||||
minetest.register_on_item_eat(function(hp_change, replace_with_item,
|
||||
itemstack, user, pointed_thing)
|
||||
@ -149,7 +147,7 @@ end)
|
||||
```
|
||||
|
||||
If no callbacks cancel this, the stack will be set and the description will be updated,
|
||||
but if a callback cancels this, then the update may be lost.
|
||||
but if a callback does cancel this, then the update may be lost.
|
||||
|
||||
It's better to do this instead:
|
||||
|
||||
|
@ -88,10 +88,10 @@ a look at the list below.
|
||||
### Troubleshooting
|
||||
|
||||
* **accessing undefined variable foobar** - If `foobar` is meant to be a global,
|
||||
then add it to `read_globals`. Otherwise, add any missing `local`s to the mod.
|
||||
add it to `read_globals`. Otherwise, add any missing `local`s to the mod.
|
||||
* **setting non-standard global variable foobar** - If `foobar` is meant to be a global,
|
||||
then add it to `globals`. Remove from `read_globals` if present there.
|
||||
Otherwise add any missing `local`s to the mod.
|
||||
add it to `globals`. Remove from `read_globals` if present.
|
||||
Otherwise, add any missing `local`s to the mod.
|
||||
* **mutating read-only global variable 'foobar'** - Move `foobar` from `read_globals` to
|
||||
`globals`.
|
||||
|
||||
@ -101,16 +101,16 @@ It is highly recommended that you find and install a plugin for your editor of c
|
||||
to show you errors without running a command. Most editors will likely have a plugin
|
||||
available.
|
||||
|
||||
* **Atom** - `linter-luacheck`
|
||||
* **Atom** - `linter-luacheck`.
|
||||
* **Sublime** - Install using package-control:
|
||||
[SublimeLinter](https://github.com/SublimeLinter/SublimeLinter),
|
||||
[SublimeLinter-luacheck](https://github.com/SublimeLinter/SublimeLinter-luacheck)
|
||||
[SublimeLinter-luacheck](https://github.com/SublimeLinter/SublimeLinter-luacheck).
|
||||
|
||||
## Checking Commits with Travis
|
||||
|
||||
If your project is public and is on Github, you can use TravisCI - a free service
|
||||
to run jobs on commits to check them. This means that every commit you push will
|
||||
be checked against LuaCheck, and a green tick or red cross displayed next to them
|
||||
be checked against LuaCheck, and a green tick or red cross will be displayed next to them
|
||||
depending on whether LuaCheck finds any mistakes. This is especially helpful for
|
||||
when your project receives a pull request - you'll be able to see the LuaCheck output
|
||||
without downloading the code.
|
||||
@ -144,7 +144,7 @@ change the line after `script:` to:
|
||||
```
|
||||
|
||||
Now commit and push to Github. Go to your project's page on Github, and click
|
||||
commits. You should see an orange disc next to the commit you just made.
|
||||
'commits'. You should see an orange disc next to the commit you just made.
|
||||
After a while it should change either into a green tick or a red cross depending on the
|
||||
outcome of LuaCheck. In either case, you can click the icon to see the build logs
|
||||
and the output of LuaCheck.
|
||||
|
@ -29,7 +29,7 @@ but can be suitable choices for artistic works such as images, text and meshes.
|
||||
|
||||
You are allowed any license; however, mods which disallow derivatives are banned from the
|
||||
official Minetest forum. (For a mod to be allowed on the forum, other developers must be
|
||||
able modify it and release the modified version.)
|
||||
able to modify it and release the modified version.)
|
||||
|
||||
Please note that **public domain is not a valid licence**, because the definition varies
|
||||
in different countries.
|
||||
@ -37,17 +37,18 @@ in different countries.
|
||||
### LGPL and CC-BY-SA
|
||||
|
||||
This is a common license combination in the Minetest community, and is what
|
||||
Minetest and minetest_game use.
|
||||
You license your code under LGPL 2.1 and your art under CC-BY-SA. This means:
|
||||
Minetest and Minetest Game use.
|
||||
You license your code under LGPL 2.1 and your art under CC-BY-SA.
|
||||
This means that:
|
||||
|
||||
* Anyone can modify, redistribute and sell modified or unmodified versions.
|
||||
* If someone modifies your mod, they must give their version the same license.
|
||||
* Your copyright notice must be kept.
|
||||
|
||||
### WTFPL and CC0
|
||||
### CC0
|
||||
|
||||
These licenses allows anyone to do what they want with your mod.
|
||||
This means they can modify, redistribute, sell, or leave out attribution.
|
||||
These licenses allow anyone to do what they want with your mod,
|
||||
which means they can modify, redistribute, sell, or leave-out attribution.
|
||||
These licenses can be used for both code and art.
|
||||
|
||||
It is important to note that WTFPL is strongly discouraged and people may
|
||||
@ -61,19 +62,19 @@ in any copies of the mod or of substantial parts of the mod.
|
||||
|
||||
## Packaging
|
||||
|
||||
There are some files it is recommended to include in your mod
|
||||
when you release it.
|
||||
There are some files that are recommended to include in your mod
|
||||
before you release it.
|
||||
|
||||
### README.txt
|
||||
|
||||
The readme file should state:
|
||||
The README file should state:
|
||||
|
||||
* What the mod does.
|
||||
* What the license is.
|
||||
* Current version of mod.
|
||||
* What dependencies there are.
|
||||
* How to install the mod.
|
||||
* What dependencies there are / what the user needs to install.
|
||||
* Where to report problems/bugs or get help.
|
||||
* Current version of the mod.
|
||||
* Optionally, the where to report problems or get help.
|
||||
|
||||
### description.txt
|
||||
|
||||
@ -85,7 +86,7 @@ Good example:
|
||||
|
||||
Adds soup, cakes, bakes and juices.
|
||||
|
||||
Don't do this:
|
||||
Avoid this:
|
||||
|
||||
(BAD) The food mod for Minetest.
|
||||
|
||||
@ -133,10 +134,7 @@ your mods. This can be done when creating a mod's forum topic (covered below).
|
||||
|
||||
You need to zip the files for the mod into a single file. How to do this varies from
|
||||
operating system to operating system.
|
||||
|
||||
If you use Windows, go to the mod's folder and select all the files.
|
||||
Right click, Send To > Compressed (zipped) folder.
|
||||
Rename the resulting zip file to the name of your mod.
|
||||
This is nearly always done using the right click menu after selecting all files.
|
||||
|
||||
When making a forum topic, on the "Create a Topic" page (see below), go to the
|
||||
"Upload Attachment" tab at the bottom.
|
||||
@ -155,24 +153,13 @@ enter the version of your mod in the comment field.
|
||||
You can now create a forum topic. You should create it in
|
||||
the ["WIP Mods"](https://forum.minetest.net/viewforum.php?f=9) (Work In Progress)
|
||||
forum.\\
|
||||
When you consider your mod no longer a work in progress, you can
|
||||
When you no longer consider your mod a work in progress, you can
|
||||
[request that it be moved](https://forum.minetest.net/viewtopic.php?f=11&t=10418)
|
||||
to "Mod Releases."
|
||||
|
||||
### Content
|
||||
|
||||
The requirements of a forum topic are mostly the same as the recommendations for
|
||||
a readme file. The topic should include:
|
||||
|
||||
* What the mod does.
|
||||
* What the license is.
|
||||
* Current version of mod.
|
||||
* How to install the mod.
|
||||
* What dependencies there are.
|
||||
* Where to report problems/bugs or get help.
|
||||
* Link to download, or an attachment.
|
||||
|
||||
You should also include screenshots of your mod in action, if relevant.
|
||||
The forum topic should contain similar content to the README, but should
|
||||
be more promotional and also include a link to download the mod.
|
||||
It's a good idea to include screenshots of your mod in action, if possible.
|
||||
|
||||
The Minetest forum uses bbcode for formatting. Here is an example for a
|
||||
mod named superspecial:
|
||||
|
@ -20,12 +20,13 @@ owner to lose data or control.
|
||||
|
||||
The most important concept in security is to **never trust the user**.
|
||||
Anything the user submits should be treated as malicious.
|
||||
This means that you should always check that the user has the correct permissions,
|
||||
that the give valid information, and they are otherwise allowed to do that action
|
||||
(ie: in range or an owner)
|
||||
This means that you should always check that the information they
|
||||
enter is valid, that the user has the correct permissions,
|
||||
and that they are otherwise allowed to do that action
|
||||
(ie: in range or an owner).
|
||||
|
||||
A malicious action isn't necessarily the modification or destruction of data,
|
||||
but can be accessing data they're not supposed to such as password hashes or
|
||||
but can be accessing sensitive data, such as password hashes or
|
||||
private messages.
|
||||
This is especially bad if the server stores information such as emails or ages,
|
||||
which some may do for verification purposes.
|
||||
@ -59,8 +60,8 @@ This could even be automated using client modifications to essentially replicate
|
||||
the `/teleport` command with no need for a privilege.
|
||||
|
||||
The solution for this kind of issue is to use a
|
||||
[Context](../players/formspecs.html#contexts), as shown in
|
||||
the formspecs chapter.
|
||||
[Context](../players/formspecs.html#contexts), as shown previously in
|
||||
the Formspecs chapter.
|
||||
|
||||
### Time of Check isn't Time of Use
|
||||
|
||||
@ -71,8 +72,8 @@ engine forbids it:
|
||||
* From 5.0 onward, named formspecs will be blocked if they haven't been shown yet.
|
||||
|
||||
This means that you should check in the handler that the user meets the
|
||||
conditions for showing the formspec in the first place, and any corresponding
|
||||
actions.
|
||||
conditions for showing the formspec in the first place, as well as any
|
||||
corresponding actions.
|
||||
|
||||
The vulnerability caused by checking for permissions in the show formspec but not
|
||||
in the handle formspec is called Time Of Check is not Time Of Use (TOCTOU).
|
||||
@ -99,7 +100,7 @@ String.format = function()
|
||||
end
|
||||
```
|
||||
|
||||
The mod could pass something a lot more malicious than opening a website, such
|
||||
The mod could pass something much more malicious than opening a website, such
|
||||
as giving a remote user control over the machine.
|
||||
|
||||
Some rules for using an insecure environment:
|
||||
|
@ -9,7 +9,7 @@ idx: 7.5
|
||||
|
||||
Unit tests are an essential tool in proving and reassuring yourself that your code
|
||||
is correct. This chapter will show you how to write tests for Minetest mods and
|
||||
games using busted. Writing unit tests for functions where you call Minetest
|
||||
games using Busted. Writing unit tests for functions where you call Minetest
|
||||
functions is quite difficult, but luckily [in the previous chapter](clean_arch.html)
|
||||
we discussed how to make your code avoid this.
|
||||
|
||||
@ -26,7 +26,7 @@ First you'll need to install LuaRocks.
|
||||
* Windows: Follow the [installation instructions on LuaRock's wiki](https://github.com/luarocks/luarocks/wiki/Installation-instructions-for-Windows).
|
||||
* Debian/Ubuntu Linux: `sudo apt install luarocks`
|
||||
|
||||
Next you should then install Busted globally:
|
||||
Next you should install Busted globally:
|
||||
|
||||
sudo luarocks install busted
|
||||
|
||||
@ -104,8 +104,8 @@ functions not inside of it. You tend to only write tests for a single file at on
|
||||
## Mocking: Using External Functions
|
||||
|
||||
Mocking is the practice of replacing functions that the thing you're testing depends
|
||||
on. This can have two purposes - firstly, the function may not be available in the
|
||||
test environment. Secondly, you may want to capture calls to the function and any
|
||||
on. This can have two purposes; one, the function may not be available in the
|
||||
test environment, and two, you may want to capture calls to the function and any
|
||||
passed arguments.
|
||||
|
||||
If you follow the advice in the [Clean Architectures](clean_arch.html) chapter,
|
||||
@ -163,7 +163,7 @@ end)
|
||||
|
||||
## Checking Commits with Travis
|
||||
|
||||
The Travis script from the [Error Checking](luacheck.html)
|
||||
The Travis script from the [Automatic Error Checking](luacheck.html)
|
||||
chapter can be modified to also run Busted.
|
||||
|
||||
```yml
|
||||
|
Loading…
Reference in New Issue
Block a user