Settings for Italian translation ready, _it folder created

This commit is contained in:
Marco 2020-04-27 15:11:54 +02:00 committed by rubenwardy
parent bffaa74d37
commit f802a7fae1
30 changed files with 5853 additions and 0 deletions

View File

@ -1,3 +1,6 @@
- code: en
name: English (UK)
cta: This book is available in English
- code: it
name: Italiano
cta: Questo libro è disponibile in italiano

243
_it/advmap/biomesdeco.md Normal file
View File

@ -0,0 +1,243 @@
---
title: Biomes and Decorations
author: Shara
layout: default
root: ../..
idx: 6.1
description: Create biomes and decorations to customise the map
---
## Introduction <!-- omit in toc -->
The ability to register biomes and decorations is vital when aiming to create an
interesting and varied in-game environment. This chapter teaches you how to
register biomes, how to control biome distribution, and how to place decorations in biomes.
- [What are Biomes?](#what-are-biomes)
- [Biome Placement](#biome-placement)
- [Heat and Humidity](#heat-and-humidity)
- [Visualising Boundaries using Voronoi Diagrams](#visualising-boundaries-using-voronoi-diagrams)
- [Creating a Voronoi Diagram using Geogebra](#creating-a-voronoi-diagram-using-geogebra)
- [Registering a Biome](#registering-a-biome)
- [What are Decorations?](#what-are-decorations)
- [Registering a Simple Decoration](#registering-a-simple-decoration)
- [Registering a Schematic Decoration](#registering-a-schematic-decoration)
- [Mapgen Aliases](#mapgen-aliases)
## What are Biomes?
A Minetest biome is a specific in-game environment. When registering biomes, you
can determine the types of nodes that appear in them during map generation.
Some of the most common types of node that may vary between biomes include:
* Top node: This is the node most commonly found on the surface. A well-known
example would be "Dirt with Grass" from Minetest Game.
* Filler node: This is the layer immediately beneath the top node.
In biomes with grass, it will often be dirt.
* Stone node: This is the node you most commonly see underground.
* Water node: This is usually a liquid and will be the node that appears
where you would expect bodies of water.
Other types of node can also vary between biomes, providing an opportunity
to create vastly different environments within the same game.
## Biome Placement
### Heat and Humidity
It is not enough to simply register a biome; you must also decide where it can
occur in game. This is done by assigning a heat and a humidity value to each biome.
You should think carefully about these values; they determine which biomes can
be neighbours to each other. Poor decisions could result in what is meant to
be a hot desert sharing a border with a glacier, and other improbable
combinations which you may prefer to avoid.
In game, heat and humidity values at any point of the map will usually be between
0 and 100. The values gradually change, increasing or decreasing as you move
around the map. The biome at any given point will be determined by which of the
registered biomes has heat and humidity values closest to those at that position on the map.
Because the changes in heat and humidity are gradual, it is good practice to assign
heat and humidity values to biomes based on reasonable expectations about that
biomes environment. For example:
* A desert might have high heat and low humidity.
* A snowy forest might have low heat and a medium humidity value.
* A swamp biome would generally have high humidity.
*
In practice, this means that, as long as you have a diverse range of biomes, you
are likely to find that the biomes which border each other form a logical progression.
### Visualising Boundaries using Voronoi Diagrams
<figure class="right_image">
<img src="{{ page.root }}/static/biomes_voronoi.png" alt="Vernoi">
<figcaption>
Voronoi diagram, showing the closest point.
<span class="credit">By <a href="https://en.wikipedia.org/wiki/Voronoi_diagram#/media/File:Euclidean_Voronoi_diagram.svg">Balu Ertl</a>, CC BY-SA 4.0.</span>
</figcaption>
</figure>
Fine-tuning heat and humidity values for biomes is
easier if you can visualise the relationship between the biomes you are using.
This is most important if you are creating a full set of your own biomes, but
can also be helpful if you are adding a biome to an existing set.
The simplest way to visualise which biomes may share borders is to create a
Voronoi diagram, which can be used to show which point on a 2-dimensional
diagram any given position is closest to.
A Voronoi diagram can reveal where biomes that should border each other do not,
and where biomes that should not border each other do. It can also give a
general insight into how common biomes will be in-game, with larger and more
central biomes being more common than smaller biomes or biomes that are located
on the outer edge of the diagram.
This is done by marking a point for each biome based on heat and humidity values,
where the x-axis is heat and the y-axis is humidity. The diagram is then
divided into areas, such that every position in a given area is closer to the
point inside that area than it is to any other point on the diagram.
Each area represents a biome. If two areas share a border, the biomes they
represent in-game can be located next to each other. The length of the border
shared between two areas, compared to the length shared with other areas, will
tell you how frequently two biomes are likely to be found next to each other.
### Creating a Voronoi Diagram using Geogebra
As well as drawing them by hand, you can also create Voronoi diagrams using
programs such as [Geogebra](https://www.geogebra.org).
1. Create points by selecting the point tool in the toolbar (icon is a point with 'A'),
and then clicking the chart. You can drag points around or explicitly set their
position in the left sidebar. You should also give each point a label, to make things clearer.
1. Next, create the voronoi by entering the following function into the
input box in the left sidebar:
```cpp
Voronoi({ A, B, C, D, E })
```
Where the each point is inside the curly brackets, separated by commas. You should now
3. Profit! You should now have a voronoi diagram with all draggable points.
## Registering a Biome
The following code registers a simple biome named grasslands biome:
```lua
minetest.register_biome({
name = "grasslands",
node_top = "default:dirt_with_grass",
depth_top = 1,
node_filler = "default:dirt",
depth_filler = 3,
y_max = 1000,
y_min = -3,
heat_point = 50,
humidity_point = 50,
})
```
This biome has one layer of Dirt with Grass nodes on the surface, and three layers
of Dirt nodes beneath this. It does not specify a stone node, so the node defined
in the mapgen alias registration for `mapgen_stone` will be present underneath the dirt.
There are many options when registering a biome, and these are documented
in the [Minetest Lua API Reference](https://minetest.gitlab.io/minetest/definition-tables/#biome-definition),
as always.
You dont need to define every option for every biome you create, but in some cases failure
to define either a specific option, or a suitable mapgen alias, can result in map generation errors.
## What are Decorations?
Decorations are either nodes or schematics that can be placed on the map at mapgen.
Some common examples include flowers, bushes, and trees. Other more creative uses
may include hanging icicles or stalagmites in caves, underground crystal formations,
or even the placement of small buildings.
Decorations can be restricted to specific biomes, by height, or by which nodes
they can be placed on. They are often used to develop the environment of a biome
by ensuring it has specific plants, trees or other features.
## Registering a Simple Decoration
Simple decorations are used to place single node decorations on the map during
map generation. You must specify the node that is to be placed as a decoration,
details for where it can be placed, and how frequently it occurs.
For example:
```lua
minetest.register_decoration({
deco_type = "simple",
place_on = {"base:dirt_with_grass"},
sidelen = 16,
fill_ratio = 0.1,
biomes = {"grassy_plains"},
y_max = 200,
y_min = 1,
decoration = "plants:grass",
})
```
In this example, the node named `plants:grass` will be placed in the biome named
grassy_plains on top of `base:dirt_with_grass` nodes, between the heights of `y = 1` and `y = 200`.
The fill_ratio value determines how frequently the decoration appears, with higher
values up to 1 resulting in a great number of decorations being placed. It is possible
to instead use noise parameters to determine placement.
## Registering a Schematic Decoration
Schematic decorations are very similar to simple decoration, but involve the placement
of a schematic instead of the placement of a single node. For example:
```lua
minetest.register_decoration({
deco_type = "schematic",
place_on = {"base:desert_sand"},
sidelen = 16,
fill_ratio = 0.0001,
biomes = {"desert"},
y_max = 200,
y_min = 1,
schematic = minetest.get_modpath("plants") .. "/schematics/cactus.mts",
flags = "place_center_x, place_center_z",
rotation = "random",
})
```
In this example the cactus.mts schematic is placed in desert biomes. You need to provide
a path to a schematic, which in this case is stored in a dedicated schematic directory within the mod.
This example also sets flags to center the placement of the schematic, and the rotation
is set to random. The random rotation of schematics when they are placed as decorations
helps introduce more variation when asymmetrical schematics are used.
## Mapgen Aliases
Existing games should already include suitable mapgen aliases, so you only need
to consider registering mapgen aliases of your own if you are making your own game.
Mapgen aliases provide information to the core mapgen, and can be registered in the form:
```lua
minetest.register_alias("mapgen_stone", "base:smoke_stone")
```
At a minimum you should register:
* mapgen_stone
* mapgen_water_source
* mapgen_river_water_source
If you are not defining cave liquid nodes for all biomes, you should also register:
* mapgen_lava_source

213
_it/advmap/lvm.md Normal file
View File

@ -0,0 +1,213 @@
---
title: Lua Voxel Manipulators
layout: default
root: ../..
idx: 6.2
description: Learn how to use LVMs to speed up map operations.
redirect_from:
- /en/chapters/lvm.html
- /en/map/lvm.html
mapgen_object:
level: warning
title: LVMs and Mapgen
message: Don't use `minetest.get_voxel_manip()` with mapgen, as it can cause glitches.
Use `minetest.get_mapgen_object("voxelmanip")` instead.
---
## Introduction <!-- omit in toc -->
The functions outlined in the [Basic Map Operations](environment.html) chapter
are convenient and easy to use, but for large areas they are inefficient.
Every time you call `set_node` or `get_node`, your mod needs to communicate with
the engine. This results in constant individual copying operations between the
engine and your mod, which is slow and will quickly decrease the performance of
your game. Using a Lua Voxel Manipulator (LVM) can be a better alternative.
- [Concepts](#concepts)
- [Reading into the LVM](#reading-into-the-lvm)
- [Reading Nodes](#reading-nodes)
- [Writing Nodes](#writing-nodes)
- [Example](#example)
- [Your Turn](#your-turn)
## Concepts
An LVM allows you to load large areas of the map into your mod's memory.
You can then read and write this data without further interaction with the
engine and without running any callbacks, which means that these
operations are very fast. Once done, you can then write the area back into
the engine and run any lighting calculations.
## Reading into the LVM
You can only load a cubic area into an LVM, so you need to work out the minimum
and maximum positions that you need to modify. Then you can create and read into
an LVM. For example:
```lua
local vm = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(pos1, pos2)
```
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.
{% include notice.html notice=page.mapgen_object %}
## Reading Nodes
To read the types of nodes at particular positions, you'll need to use `get_data()`.
This returns a flat array where each entry represents the type of a
particular node.
```lua
local data = vm:get_data()
```
You can get param2 and lighting data using the methods `get_light_data()` and `get_param2_data()`.
You'll need to use `emin` and `emax` to work out where a node is in the flat arrays
given by the above methods. There's a helper class called `VoxelArea` which handles
the calculation for you.
```lua
local a = VoxelArea:new{
MinEdge = emin,
MaxEdge = emax
}
-- Get node's index
local idx = a:index(x, y, z)
-- Read node
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 strings, for performance reasons.
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")
```
You can then check whether the node is stone:
```lua
local idx = a:index(x, y, z)
if data[idx] == c_stone then
print("is stone!")
end
```
It is recommended that you find and store the content IDs of nodes types
at load time because the IDs of a node type will never change. Make sure to store
the IDs in a local variable for performance reasons.
Nodes in an LVM data array are stored in reverse co-ordinate order, so you should
always iterate in the order `z, y, x`. For example:
```lua
for z = min.z, max.z do
for y = min.y, max.y do
for x = min.x, max.x do
-- vi, voxel index, is a common variable name here
local vi = a:index(x, y, z)
if data[vi] == c_stone then
print("is stone!")
end
end
end
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 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 *cache thrashing*.
## Writing Nodes
First, you need to set the new content ID in the data array:
```lua
for z = min.z, max.z do
for y = min.y, max.y do
for x = min.x, max.x do
local vi = a:index(x, y, z)
if data[vi] == c_stone then
data[vi] = c_air
end
end
end
end
```
When you finish setting nodes in the LVM, you then need to upload the data
array to the engine:
```lua
vm:set_data(data)
vm:write_to_map(true)
```
For setting lighting and param2 data, use the appropriately named
`set_light_data()` and `set_param2_data()` methods.
`write_to_map()` takes a Boolean which is true if you want lighting to be
calculated. If you pass false, you need to recalculate lighting at a future
time using `minetest.fix_light`.
## Example
```lua
-- Get content IDs during load time, and store into a local
local c_dirt = minetest.get_content_id("default:dirt")
local c_grass = minetest.get_content_id("default:dirt_with_grass")
local function grass_to_dirt(pos1, pos2)
-- Read data into LVM
local vm = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(pos1, pos2)
local a = VoxelArea:new{
MinEdge = emin,
MaxEdge = emax
}
local data = vm:get_data()
-- Modify data
for z = pos1.z, pos2.z do
for y = pos1.y, pos2.y do
for x = pos1.x, pos2.x do
local vi = a:index(x, y, z)
if data[vi] == c_grass then
data[vi] = c_dirt
end
end
end
end
-- Write data
vm:set_data(data)
vm:write_to_map(true)
end
```
## Your Turn
* 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&deg;.
* 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 node each
time? If so, how could you stop this?

View File

@ -0,0 +1,221 @@
---
title: Getting Started
layout: default
root: ../..
idx: 1.1
description: Learn how to make a mod folder, including init.lua, mod.conf and more.
redirect_from:
- /en/chapters/folders.html
- /en/basics/folders.html
---
## Introduction <!-- omit in toc -->
Understanding the basic structure of a mod's folder is an essential skill when
creating mods.
- [What are Games and Mods?](#what-are-games-and-mods)
- [Where are mods stored?](#where-are-mods-stored)
- [Mod Directory](#mod-directory)
- [Dependencies](#dependencies)
- [mod.conf](#modconf)
- [depends.txt](#dependstxt)
- [Mod Packs](#mod-packs)
- [Example](#example)
- [Mod Folder](#mod-folder)
- [depends.txt](#dependstxt-1)
- [init.lua](#initlua)
- [mod.conf](#modconf-1)
## 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.
In Minetest, a game is a collection of modules which work together to provide the
content and behaviour of a game.
A module, commonly known as a mod, is a collection of scripts and resources.
It's possible to make a game using only one mod, but this is rarely done because it
reduces the ease by which parts of the game can be adjusted and replaced
independently of others.
It's also possible to distribute mods outside of a game, in which case they
are also *mods* in the more traditional sense - modifications. These mods adjust
or extend the features of a game.
Both the mods contained in a game and third-party mods use the same API.
This book will cover the main parts of the Minetest API,
and is applicable for both game developers and modders.
## Where are mods stored?
<a name="mod-locations"></a>
Each mod has its own directory where its Lua code, textures, models, and
sounds are placed. Minetest checks in a number of different locations for
mods. These locations are commonly called *mod load paths*.
For a given world/save game, three mod locations are checked.
They are, in order:
1. Game mods. These are the mods that form the game that the world is running.
Eg: `minetest/games/minetest_game/mods/`, `/usr/share/minetest/games/minetest/`
2. Global mods, the location to which mods are nearly always installed to.
If in doubt, place them here.
Eg: `minetest/mods/`
3. World mods, the location to store mods which are specific to a
particular world.
Eg: `minetest/worlds/world/worldmods/`
Minetest will check the locations in the order given above. If it encounters a mod
with a name the same as one found previously, the later mod will be loaded in place
of the earlier mod.
This means that you can override game mods by placing a mod with the same name
in the global mod location.
The actual location of each mod load path depends on what operating system you're
using, and how you installed Minetest.
* **Windows:**
* For portable builds, ie: from a .zip file, just go to the directory where
you extracted the zip and look for the `games`, `mods`, and `worlds`
directories.
* For installed builds, ie: from a setup.exe,
look in C:\\\\Minetest or C:\\\\Games\\Minetest.
* **GNU/Linux:**
* For system-wide installs, look in `~/.minetest`.
Note that `~` means the user home directory, and that files and directories
starting with a dot (`.`) are hidden.
* For portable installs, look in the build directory.
* For Flatpak installs, look in `~/.var/app/net.minetest.Minetest/.minetest/mods/`.
* **MacOS**
* Look in `~/Library/Application Support/minetest/`.
Note that `~` means the user home, ie: `/Users/USERNAME/`.
## Mod Directory
![Find the mod's directory]({{ page.root }}/static/folder_modfolder.jpg)
A *mod name* is used to refer to a mod. Each mod should have a unique name.
Mod names can include letters, numbers, and underscores. A good name should
describe what the mod does, and the directory which contains the components of a mod
must have the same name as the mod name.
To find out if a mod name is available, try searching for it on
[content.minetest.net](https://content.minetest.net).
mymod
├── init.lua (required) - Runs when the game loads.
├── mod.conf (recommended) - Contains description and dependencies.
├── textures (optional)
│   └── ... any textures or images
├── sounds (optional)
│   └── ... any sounds
└── ... any other files or directories
Only the init.lua file is required in a mod for it to run on game load;
however, mod.conf is recommended and other components may be needed
depending on the mod's functionality.
## Dependencies
A dependency occurs when a mod requires another mod to be loaded before itself.
One mod may require another mod's code, items, or other resources to be available
for it to use.
There are two types of dependencies: hard and optional dependencies.
Both require the mod to be loaded first. If the mod being depended on isn't
available, a hard dependency will cause the mod to fail to load, while an optional
dependency might lead to fewer features being enabled.
An optional dependency is useful if you want to optionally support another mod; it can
enable extra content if the user wishes to use both the mods at the same time.
Dependencies should be listed in mod.conf.
### mod.conf
This file is used for mod metadata including the mod's name, description, and other
information. For example:
name = mymod
description = Adds foo, bar, and bo.
depends = modone, modtwo
optional_depends = modthree
### depends.txt
For compatibility with 0.4.x versions of Minetest, instead of only specifying
dependencies in mod.conf, you need to provide a depends.txt file in which
you list all dependencies:
modone
modtwo
modthree?
Each mod name is on its own line, and mod names with a question mark
following them are optional dependencies.
If an optional dependency is installed, it is loaded before the mod;
however, if the dependency is not installed, the mod still loads.
This is in contrast to normal dependencies which will cause the current
mod not to work if the dependency is not installed.
## Mod Packs
Mods can be grouped into mod packs which allow multiple mods to be packaged
and moved together. They are useful if you want to supply multiple mods to
a player, but don't want to make them download each one individually.
modpack1
├── modpack.lua (required) - signals that this is a mod pack
├── mod1
│   └── ... mod files
└── mymod (optional)
   └── ... mod files
Please note that a modpack is not a *game*.
Games have their own organisational structure which will be explained in the
Games chapter.
## Example
Here is an example which puts all of this together:
### Mod Folder
mymod
├── textures
│   └── mymod_node.png files
├── depends.txt
├── init.lua
└── mod.conf
### depends.txt
default
### init.lua
```lua
print("This file will be run at load time!")
minetest.register_node("mymod:node", {
description = "This is a node",
tiles = {"mymod_node.png"},
groups = {cracky = 1}
})
```
### mod.conf
name = mymod
descriptions = Adds a node
depends = default
This mod has the name "mymod". It has three text files: init.lua, mod.conf,
and depends.txt.\\
The script prints a message and then registers a node
which will be explained in the next chapter.\\
There's a single dependency, the
[default mod](https://content.minetest.net/metapackages/default/), which is
usually found in Minetest Game.\\
There is also a texture in textures/ for the node.

314
_it/basics/lua.md Normal file
View File

@ -0,0 +1,314 @@
---
title: Lua Scripting
layout: default
root: ../..
idx: 1.2
description: A basic introduction to Lua, including a guide on global/local scope.
redirect_from: /en/chapters/lua.html
---
## Introduction <!-- omit in toc -->
In this chapter we will talk about scripting in Lua, the tools required,
and go over some techniques which you will probably find useful.
- [Code Editors](#code-editors)
- [Coding in Lua](#coding-in-lua)
- [Program Flow](#program-flow)
- [Variable Types](#variable-types)
- [Arithmetic Operators](#arithmetic-operators)
- [Selection](#selection)
- [Logical Operators](#logical-operators)
- [Programming](#programming)
- [Local and Global Scope](#local-and-global-scope)
- [Locals should be used as much as possible](#locals-should-be-used-as-much-as-possible)
- [Including other Lua Scripts](#including-other-lua-scripts)
## Code Editors
A code editor with code highlighting is sufficient for writing scripts in Lua.
Code highlighting gives different colours to different words and characters
depending on what they mean. This allows you to spot mistakes.
```lua
function ctf.post(team,msg)
if not ctf.team(team) then
return false
end
if not ctf.team(team).log then
ctf.team(team).log = {}
end
table.insert(ctf.team(team).log,1,msg)
ctf.save()
return true
end
```
For example, keywords in the above snippet are highlighted such as if, then, end, and return.
table.insert is a function which comes with Lua by default.
Here is a list of common editors well suited for Lua.
Other editors are available, of course.
* Windows: [Notepad++](http://notepad-plus-plus.org/), [Atom](http://atom.io/), [VS Code](https://code.visualstudio.com/)
* Linux: Kate, Gedit, [Atom](http://atom.io/), [VS Code](https://code.visualstudio.com/)
* OSX: [Atom](http://atom.io/), [VS Code](https://code.visualstudio.com/)
## Coding in Lua
### Program Flow
Programs are a series of commands that run one after another.
We call these commands "statements."
Program flow is how these statements are executed.
Different types of flow allow you to skip or jump over sets of commands.
There are three main types of flow:
* Sequence: Just run one statement after another, no skipping.
* Selection: Skip over sequences depending on conditions.
* Iteration: Repeating, looping. Keep running the same
statements until a condition is met.
So, what do statements in Lua look like?
```lua
local a = 2 -- Set 'a' to 2
local b = 2 -- Set 'b' to 2
local result = a + b -- Set 'result' to a + b, which is 4
a = a + 10
print("Sum is "..result)
```
Whoa, what happened there?
a, b, and result are *variables*. Local variables are declared
by using the local keyword, and then given an initial value.
Local will be discussed in a bit, as it's part of a very important concept called
*scope*.
The `=` means *assignment*, so `result = a + b` means set "result" to a + b.
Variable names can be longer than one character unlike in mathematics, as seen with the "result" variable.
It's also worth noting that Lua is *case-sensitive*; A is a different variable to a.
### Variable Types
A variable will be only one of the following types and can change type after an
assignment.
It's good practice to make sure a variable is only ever nil or a single non-nil type.
| Type | Description | Example |
|----------|---------------------------------|----------------|
| Nil | Not initialised. The variable is empty, it has no value | `local A`, `D = nil` |
| Number | A whole or decimal number. | `local A = 4` |
| String | A piece of text | `local D = "one two three"` |
| Boolean | True or False | `local is_true = false`, `local E = (1 == 1)` |
| Table | Lists | Explained below |
| Function | Can run. May require inputs and may return a value | `local result = func(1, 2, 3)` |
### Arithmetic Operators
Not an exhaustive list. Doesn't contain every possible operator.
| Symbol | Purpose | Example |
|--------|----------------|---------------------------|
| A + B | Addition | 2 + 2 = 4 |
| A - B | Subtraction | 2 - 10 = -8 |
| A * B | Multiplication | 2 * 2 = 4 |
| A / B | Division | 100 / 50 = 2 |
| A ^ B | Powers | 2 ^ 2 = 2<sup>2</sup> = 4 |
| A .. B | Join strings | "foo" .. "bar" = "foobar" |
### Selection
The most basic selection is the if statement. It looks like this:
```lua
local random_number = math.random(1, 100) -- Between 1 and 100.
if random_number > 50 then
print("Woohoo!")
else
print("No!")
end
```
That example generates a random number between 1 and 100. It then prints
"Woohoo!" if that number is bigger than 50, otherwise it prints "No!".
What else can you get apart from '>'?
### Logical Operators
| Symbol | Purpose | Example |
|---------|--------------------------------------|-------------------------------------------------------------|
| A == B | Equals | 1 == 1 (true), 1 == 2 (false) |
| A ~= B | Doesn't equal | 1 ~= 1 (false), 1 ~= 2 (true) |
| A > B | Greater than | 5 > 2 (true), 1 > 2 (false), 1 > 1 (false) |
| A < B | Less than | 1 < 3 (true), 3 < 1 (false), 1 < 1 (false) |
| A >= B | Greater than or equals | 5 >= 5 (true), 5 >= 3 (true), 5 >= 6 (false) |
| A <= B | Less than or equals | 3 <= 6 (true), 3 <= 3 (true) |
| A and B | And (both must be correct) | (2 > 1) and (1 == 1) (true), (2 > 3) and (1 == 1) (false) |
| A or B | either or. One or both must be true. | (2 > 1) or (1 == 2) (true), (2 > 4) or (1 == 3) (false) |
| not A | not true | not (1 == 2) (true), not (1 == 1) (false) |
That doesn't contain every possible operator, and you can combine operators like this:
```lua
if not A and B then
print("Yay!")
end
```
Which prints "Yay!" if A is false and B is true.
Logical and arithmetic operators work exactly the same;
they both accept inputs and return a value which can be stored.
```lua
local A = 5
local is_equal = (A == 5)
if is_equal then
print("Is equal!")
end
```
## Programming
Programming is the action of taking a problem, such as sorting a list
of items, and then turning it into steps that a computer can understand.
Teaching you the logical process of programming is beyond the scope of this book;
however, the following websites are quite useful in developing this:
* [Codecademy](http://www.codecademy.com/) is one of the best resources for
learning to 'code', it provides an interactive tutorial experience.
* [Scratch](https://scratch.mit.edu) is a good resource when starting from
absolute basics, learning the problem-solving techniques required to program.\\
Scratch is **designed to teach children** how to program and isn't a serious
programming language.
## Local and Global Scope
Whether a variable is local or global determines where it can be written to or read to.
A local variable is only accessible from where it is defined. Here are some examples:
```lua
-- Accessible from within this script file
local one = 1
function myfunc()
-- Accessible from within this function
local two = one + one
if two == one then
-- Accessible from within this if statement
local three = one + two
end
end
```
Whereas global variables can be accessed from anywhere in the script file, and from any other mod.
```lua
my_global_variable = "blah"
function one()
my_global_variable = "three"
end
print(my_global_variable) -- Output: "blah"
one()
print(my_global_variable) -- Output: "three"
```
### Locals should be used as much as possible
Lua is global by default (unlike most other programming languages).
Local variables must be identified as such.
```lua
function one()
foo = "bar"
end
function two()
print(dump(foo)) -- Output: "bar"
end
one()
two()
```
dump() is a function that can turn any variable into a string so the programmer can
see what it is. The foo variable will be printed as "bar", including the quotes
which show it is a string.
This is sloppy coding and Minetest will, in fact, warn about this:
Assignment to undeclared global 'foo' inside function at init.lua:2
To correct this, use "local":
```lua
function one()
local foo = "bar"
end
function two()
print(dump(foo)) -- Output: nil
end
one()
two()
```
Remember that nil means **not initialised**.
The variable hasn't been assigned a value yet,
doesn't exist, or has been uninitialised (ie: set to nil).
The same goes for functions. Functions are variables of a special type, and
should be made local, as other mods could have functions of the same name.
```lua
local function foo(bar)
return bar * 2
end
```
API tables should be used to allow other mods to call the functions, like so:
```lua
mymod = {}
function mymod.foo(bar)
return "foo" .. bar
end
-- In another mod, or script:
mymod.foo("foobar")
```
## Including other Lua Scripts
The recommended way to include other Lua scripts in a mod is to use *dofile*.
```lua
dofile(minetest.get_modpath("modname") .. "/script.lua")
```
"local" variables declared outside of any functions in a script file will be local to that script.
A script can return a value, which is useful for sharing private locals:
```lua
-- script.lua
return "Hello world!"
-- init.lua
local ret = dofile(minetest.get_modpath("modname") .. "/script.lua")
print(ret) -- Hello world!
```
Later chapters will discuss how to split up the code of a mod in a lot of detail.
However, the simplistic approach for now is to have different files for different
types of things - nodes.lua, crafts.lua, craftitems.lua, etc.

93
_it/games/games.md Normal file
View File

@ -0,0 +1,93 @@
---
title: Creating Games
layout: default
root: ../..
idx: 7.1
---
## Introduction <!-- omit in toc -->
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)
- [Inter-game Compatibility](#inter-game-compatibility)
- [API Compatibility](#api-compatibility)
- [Groups and Aliases](#groups-and-aliases)
- [Your Turn](#your-turn)
## What is a Game?
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,
it could be a classic crafter miner with hard survival elements, or
it could be a space simulation game with a steampunk 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.
## Game Directory
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`.
foo_game
├── game.conf
├── menu
│   ├── header.png
│   ├── background.png
│   └── icon.png
├── minetest.conf
├── mods
│   └── ... mods
├── README.txt
└── settingtypes.txt
The only thing that is required is a mods folder, but `game.conf` and `menu/icon.png`
are recommended.
## Inter-game Compatibility
### API Compatibility
It's a good idea to try to keep as much API compatibility with Minetest Game as
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,
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:
* Lua API table - All documented/advertised functions in the global table which shares the same name.
For example, `mobs.register_mob`.
* Registered Nodes/Items - The presence of items.
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 mega God-mod like
*default* in Minetest Game, in which case the game shouldn't include a mod named
default.
API compatibility also applies to other third-party mods and games,
so try to make sure that any new mods have a unique mod name.
To check whether a mod name has been taken, search for it on
[content.minetest.net](https://content.minetest.net/).
### Groups and Aliases
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.
## Your Turn
* Create a simple game where the player gains points from digging special blocks.

35
_it/index.md Normal file
View File

@ -0,0 +1,35 @@
---
title: Front Cover
layout: default
homepage: true
no_header: true
root: ..
idx: 0.1
---
<header>
<h1>Minetest Modding Book</h1>
<span>by <a href="https://rubenwardy.com" rel="author">rubenwardy</a></span>
<span>with editing by <a href="http://rc.minetest.tv/">Shara</a></span>
</header>
## Introduction
Minetest uses Lua scripts to provide modding support.
This book aims to teach you how to create your own mods, starting from the basics.
Each chapter focuses on a particular part of the API, and will soon get you making
your own mods.
As well as [reading this book online](https://rubenwardy.com/minetest_modding_book),
you can also [download it in HTML form](https://github.com/rubenwardy/minetest_modding_book/releases).
### Feedback and Contributions
Noticed a mistake, or want to give feedback? Make sure to tell me about it.
* Create a [GitHub Issue](https://github.com/rubenwardy/minetest_modding_book/issues).
* Post in the [Forum Topic](https://forum.minetest.net/viewtopic.php?f=14&t=10729).
* [Contact me](https://rubenwardy.com/contact/).
* Fancy contributing?
[Read the README](https://github.com/rubenwardy/minetest_modding_book/blob/master/README.md).

View File

@ -0,0 +1,84 @@
---
title: Creating Textures
layout: default
root: ../..
idx: 2.2
description: An introduction to making textures in your editor of choice, an a guide on GIMP.
redirect_from: /en/chapters/creating_textures.html
---
## Introduction <!-- omit in toc -->
Being able to create and optimise textures is a very useful skill when
developing for Minetest.
There are many techniques relevant to working on pixel art textures,
and understanding these techniques will greatly improve
the quality of the textures you create.
Detailed approaches to creating good pixel art are outside the scope
of this book, and instead only the most relevant basic techniques
will be covered.
There are many [good online tutorials](http://www.photonstorm.com/art/tutorials-art/16x16-pixel-art-tutorial)
available, which cover pixel art in much more detail.
- [Techniques](#techniques)
- [Using the Pencil](#using-the-pencil)
- [Tiling](#tiling)
- [Transparency](#transparency)
- [Editors](#editors)
- [MS Paint](#ms-paint)
- [GIMP](#gimp)
## Techniques
### Using the Pencil
The pencil tool is available in most editors. When set to its lowest size,
it allows you to edit one pixel at a time without changing any other parts
of the image. By manipulating the pixels one at a time, you create clear
and sharp textures without unintended blurring. It also gives you a high
level of precision and control.
### Tiling
Textures used for nodes should generally be designed to tile. This means
when you place multiple nodes with the same texture together, the edges line
up correctly.
<!-- IMAGE NEEDED - cobblestone that tiles correctly -->
If you fail to match the edges correctly, the result is far less pleasing
to look at.
<!-- IMAGE NEEDED - node that doesn't tile correctly -->
### Transparency
Transparency is important when creating textures for nearly all craftitems
and some nodes, such as glass.
Not all editors support transparency, so make sure you choose an
editor which is suitable for the textures you wish to create.
## Editors
### MS Paint
MS Paint is a simple editor which can be useful for basic texture
design; however, it does not support transparency.
This usually won't matter when making textures for the sides of nodes,
but if you need transparency in your textures you should choose a
different editor.
### GIMP
GIMP is commonly used in the Minetest community. It has quite a high
learning curve because many of its features are not immediately
obvious.
When using GIMP, the pencil tool can be selected from the Toolbox:
<figure>
<img src="{{ page.root }}//static/pixel_art_gimp_pencil.png" alt="Pencil in GIMP">
</figure>
It's also advisable to select the Hard edge checkbox for the eraser tool.

342
_it/items/inventories.md Normal file
View File

@ -0,0 +1,342 @@
---
title: ItemStacks and Inventories
layout: default
root: ../..
idx: 2.4
description: Manipulate InvRefs and ItemStacks
redirect_from:
- /en/chapters/inventories.html
- /en/chapters/itemstacks.html
- /en/inventories/inventories.html
- /en/inventories/itemstacks.html
---
## Introduction <!-- omit in toc -->
In this chapter, you will learn how to use and manipulate inventories, whether
that be a player inventory, a node inventory, or a detached inventory.
- [What are ItemStacks and Inventories?](#what-are-itemstacks-and-inventories)
- [ItemStacks](#itemstacks)
- [Inventory Locations](#inventory-locations)
- [Lists](#lists)
- [Size and Width](#size-and-width)
- [Checking Contents](#checking-contents)
- [Modifying Inventories and ItemStacks](#modifying-inventories-and-itemstacks)
- [Adding to a List](#adding-to-a-list)
- [Taking Items](#taking-items)
- [Manipulating Stacks](#manipulating-stacks)
- [Wear](#wear)
- [Lua Tables](#lua-tables)
## What are ItemStacks and Inventories?
An ItemStack is the data behind a single cell in an inventory.
An *inventory* is a collection of *inventory lists*, each of which
is a 2D grid of ItemStacks.
Inventory lists are simply called *lists* in the context
of inventories.
The point of an inventory is to allow multiple grids when Players
and Nodes only have at most one inventory in them.
## ItemStacks
ItemStacks have three components to them.
The item name may be the item name of a registered item, an alias, or an unknown
item name.
Unknown items are common when users uninstall mods, or when mods remove items without
precautions, such as registering aliases.
```lua
print(stack:get_name())
stack:set_name("default:dirt")
if not stack:is_known() then
print("Is an unknown item!")
end
```
The count will always be 0 or greater.
Through normal gameplay, the count should be no more than the maximum stack size
of the item - `stack_max`.
However, admin commands and buggy mods may result in stacks exceeding the maximum
size.
```lua
print(stack:get_stack_max())
```
An ItemStack can be empty, in which case the count will be 0.
```lua
print(stack:get_count())
stack:set_count(10)
```
ItemStacks can be constructed in multiple ways using the ItemStack function.
```lua
ItemStack() -- name="", count=0
ItemStack("default:pick_stone") -- count=1
ItemStack("default:stone 30")
ItemStack({ name = "default:wood", count = 10 })
```
Item metadata is an unlimited key-value store for data about the item.
Key-value means that you use a name (called the key) to access the data (called the value).
Some keys have special meaning, such as `description` which is used to have a per-stack
item description.
This will be covered in more detail in the Metadata and Storage chapter.
## Inventory Locations
An Inventory Location is where and how the inventory is stored.
There are three types of inventory location: player, node, and detached.
An inventory is directly tied to one and only one location - updating the inventory
will cause it to update immediately.
Node inventories are related to the position of a specific node, such as a chest.
The node must be loaded because it is stored in [node metadata](../map/storage.html#metadata).
```lua
local inv = minetest.get_inventory({ type="node", pos={x=1, y=2, z=3} })
```
The above obtains an *inventory reference*, commonly referred to as *InvRef*.
Inventory references are used to manipulate an inventory.
*Reference* means that the data isn't actually stored inside that object,
but the object instead directly updates the data in-place.
The location of an inventory reference can be found like so:
```lua
local location = inv:get_location()
```
Player inventories can be obtained similarly or using a player reference.
The player must be online to access their inventory.
```lua
local inv = minetest.get_inventory({ type="player", name="player1" })
-- or
local inv = player:get_inventory()
```
A detached inventory is one which is independent of players or nodes.
Detached inventories also don't save over a restart.
Detached inventories need to be created before they can be used -
this will be covered later.
```lua
local inv = minetest.get_inventory({
type="detached", name="inventory_name" })
```
Unlike the other types of inventory, you must first create a detached inventory:
```lua
minetest.create_detached_inventory("inventory_name")
```
The create_detached_inventory function accepts 3 arguments, only first is required.
The second argument takes a table of callbacks, which can be used to control how
players interact with the inventory:
```lua
-- Input only detached inventory
minetest.create_detached_inventory("inventory_name", {
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
return count -- allow moving
end,
allow_put = function(inv, listname, index, stack, player)
return stack:get_count() -- allow putting
end,
allow_take = function(inv, listname, index, stack, player)
return -1 -- don't allow taking
end,
on_put = function(inv, listname, index, stack, player)
minetest.chat_send_all(player:get_player_name() ..
" gave " .. stack:to_string() ..
" to the donation chest at " .. minetest.pos_to_str(pos))
end,
})
```
Permission callbacks - ie: those starting with `allow_`- return the number
of items to transfer, with -1 being used to prevent transfer completely.
Action callbacks - starting with `on_` - don't have a return value and
can't prevent transfers.
## Lists
Inventory Lists are a concept used to allow multiple grids to be stored inside a single location.
This is especially useful for the player as there are a number of common lists
which all games have, such as the *main* inventory and *craft* slots.
### Size and Width
Lists have a size, which is the total number of cells in the grid, and a width,
which is only used within the engine.
The width of the list is not used when drawing the inventory in a window,
because the code behind the window determines the width to use.
```lua
if inv:set_size("main", 32) then
inv:set_width("main", 8)
print("size: " .. inv.get_size("main"))
print("width: " .. inv:get_width("main"))
else
print("Error! Invalid itemname or size to set_size()")
end
```
`set_size` will fail and return false if the listname or size is invalid.
For example, the new size may be too small to fit all the current items
in the inventory.
### Checking Contents
`is_empty` can be used to see if a list contains any items:
```lua
if inv:is_empty("main") then
print("The list is empty!")
end
```
`contains_item` can be used to see if a list contains a specific item.
## Modifying Inventories and ItemStacks
### Adding to a List
To add items to a list named `"main"` while respecting maximum stack sizes:
```lua
local stack = ItemStack("default:stone 99")
local leftover = inv:add_item("main", stack)
if leftover:get_count() > 0 then
print("Inventory is full! " ..
leftover:get_count() .. " items weren't added")
end
```
### Taking Items
To remove items from a list:
```lua
local taken = inv:remove_item("main", stack)
print("Took " .. taken:get_count())
```
### Manipulating Stacks
You can modify individual stacks by first getting them:
```lua
local stack = inv:get_stack(listname, 0)
```
Then modifying them by setting properties or by using the methods which
respect `stack_size`:
```lua
local stack = ItemStack("default:stone 50")
local to_add = ItemStack("default:stone 100")
local leftover = stack:add_item(to_add)
local taken = stack:take_item(19)
print("Could not add" .. leftover:get_count() .. " of the items.")
-- ^ will be 51
print("Have " .. stack:get_count() .. " items")
-- ^ will be 80
-- min(50+100, stack_max) - 19 = 80
-- where stack_max = 99
```
`add_item` will add items to an ItemStack and return any that could not be added.
`take_item` will take up to the number of items but may take less, and returns the stack taken.
Finally, set the item stack:
```lua
inv:set_stack(listname, 0, stack)
```
## Wear
Tools can have wear; wear shows a progress bar and makes the tool break when completely worn.
Wear is a number out of 65535; the higher it is, the more worn the tool is.
Wear can be manipulated using `add_wear()`, `get_wear()`, and `set_wear(wear)`.
```lua
local stack = ItemStack("default:pick_mese")
local max_uses = 10
-- This is done automatically when you use a tool that digs things
-- It increases the wear of an item by one use.
stack:add_wear(65535 / (max_uses - 1))
```
When digging a node, the amount of wear a tool gets may depend on the node
being dug. So max_uses varies depending on what is being dug.
## Lua Tables
ItemStacks and Inventories can be converted to and from tables.
This is useful for copying and bulk operations.
```lua
-- Entire inventory
local data = inv1:get_lists()
inv2:set_lists(data)
-- One list
local listdata = inv1:get_list("main")
inv2:set_list("main", listdata)
```
The table of lists returned by `get_lists()` will be in this form:
```lua
{
list_one = {
ItemStack,
ItemStack,
ItemStack,
ItemStack,
-- inv:get_size("list_one") elements
},
list_two = {
ItemStack,
ItemStack,
ItemStack,
ItemStack,
-- inv:get_size("list_two") elements
}
}
```
`get_list()` will return a single list as just a list of ItemStacks.
One important thing to note is that the set methods above don't change the size
of the lists.
This means that you can clear a list by setting it to an empty table and it won't
decrease in size:
```lua
inv:set_list("main", {})
```

446
_it/items/node_drawtypes.md Normal file
View File

@ -0,0 +1,446 @@
---
title: Node Drawtypes
layout: default
root: ../..
idx: 2.3
description: Guide to all drawtypes, including node boxes/nodeboxes and mesh nodes.
redirect_from: /en/chapters/node_drawtypes.html
---
## Introduction <!-- omit in toc -->
The method by which a node is drawn is called a *drawtype*. There are many
available drawtypes. The behaviour of a drawtype can be controlled
by providing properties in the node type definition. These properties
are fixed for all instances of this node. It is possible to control some properties
per-node using something called `param2`.
In the previous chapter, the concept of nodes and items was introduced, but a
full definition of a node wasn't given. The Minetest world is a 3D grid of
positions. Each position is called a node, and consists of the node type
(name) and two parameters (param1 and param2). The function
`minetest.register_node` is a bit misleading in that it doesn't actually
register a node - it registers a new *type* of node.
The node params are used to control how a node is individually rendered.
`param1` is used to store the lighting of a node, and the meaning of
`param2` depends on the `paramtype2` property of the node type definition.
- [Cubic Nodes: Normal and Allfaces](#cubic-nodes-normal-and-allfaces)
- [Glasslike Nodes](#glasslike-nodes)
- [Glasslike_Framed](#glasslikeframed)
- [Airlike Nodes](#airlike-nodes)
- [Lighting and Sunlight Propagation](#lighting-and-sunlight-propagation)
- [Liquid Nodes](#liquid-nodes)
- [Node Boxes](#node-boxes)
- [Wallmounted Node Boxes](#wallmounted-node-boxes)
- [Mesh Nodes](#mesh-nodes)
- [Signlike Nodes](#signlike-nodes)
- [Plantlike Nodes](#plantlike-nodes)
- [Firelike Nodes](#firelike-nodes)
- [More Drawtypes](#more-drawtypes)
## Cubic Nodes: Normal and Allfaces
<figure class="right_image">
<img src="{{ page.root }}//static/drawtype_normal.png" alt="Normal Drawtype">
<figcaption>
Normal Drawtype
</figcaption>
</figure>
The normal drawtype is typically used to render a cubic node.
If the side of a normal node is against a solid side, then that side won't be rendered,
resulting in a large performance gain.
In contrast, the allfaces drawtype will still render the inner side when up against
a solid node. This is good for nodes with partially transparent sides, such as
leaf nodes. You can use the allfaces_optional drawtype to allow users to opt-out
of the slower drawing, in which case it'll act like a normal node.
```lua
minetest.register_node("mymod:diamond", {
description = "Alien Diamond",
tiles = {"mymod_diamond.png"},
groups = {cracky = 3},
})
minetest.register_node("default:leaves", {
description = "Leaves",
drawtype = "allfaces_optional",
tiles = {"default_leaves.png"}
})
```
Note: the normal drawtype is the default drawtype, so you don't need to explicitly
specify it.
## Glasslike Nodes
The difference between glasslike and normal nodes is that placing a glasslike node
next to a normal node won't cause the side of the normal node to be hidden.
This is useful because glasslike nodes tend to be transparent, and so using a normal
drawtype would result in the ability to see through the world.
<figure>
<img src="{{ page.root }}//static/drawtype_glasslike_edges.png" alt="Glasslike's Edges">
<figcaption>
Glasslike's Edges
</figcaption>
</figure>
```lua
minetest.register_node("default:obsidian_glass", {
description = "Obsidian Glass",
drawtype = "glasslike",
tiles = {"default_obsidian_glass.png"},
paramtype = "light",
is_ground_content = false,
sunlight_propagates = true,
sounds = default.node_sound_glass_defaults(),
groups = {cracky=3,oddly_breakable_by_hand=3},
})
```
### Glasslike_Framed
This makes the node's edge go around the whole thing with a 3D effect, rather
than individual nodes, like the following:
<figure>
<img src="{{ page.root }}//static/drawtype_glasslike_framed.png" alt="Glasslike_framed's Edges">
<figcaption>
Glasslike_Framed's Edges
</figcaption>
</figure>
You can use the glasslike_framed_optional drawtype to allow the user to *opt-in*
to the framed appearance.
```lua
minetest.register_node("default:glass", {
description = "Glass",
drawtype = "glasslike_framed",
tiles = {"default_glass.png", "default_glass_detail.png"},
inventory_image = minetest.inventorycube("default_glass.png"),
paramtype = "light",
sunlight_propagates = true, -- Sunlight can shine through block
groups = {cracky = 3, oddly_breakable_by_hand = 3},
sounds = default.node_sound_glass_defaults()
})
```
## Airlike Nodes
These nodes are not rendered and thus have no textures.
```lua
minetest.register_node("myair:air", {
description = "MyAir (you hacker you!)",
drawtype = "airlike",
paramtype = "light",
sunlight_propagates = true,
walkable = false, -- Would make the player collide with the air node
pointable = false, -- You can't select the node
diggable = false, -- You can't dig the node
buildable_to = true, -- Nodes can be replace this node.
-- (you can place a node and remove the air node
-- that used to be there)
air_equivalent = true,
drop = "",
groups = {not_in_creative_inventory=1}
})
```
## Lighting and Sunlight Propagation
The lighting of a node is stored in param1. In order to work out how to shade
a node's side, the light value of the neighbouring node is used.
Because of this, solid nodes don't have light values because they block light.
By default, a node type won't allow light to be stored in any node instances.
It's usually desirable for some nodes such as glass and air to be able to
let light through. To do this, there are two properties which need to be defined:
```lua
paramtype = "light",
sunlight_propagates = true,
```
The first line means that param1 does, in fact, store the light level.
The second line means that sunlight should go through this node without decreasing in value.
## Liquid Nodes
<figure class="right_image">
<img src="{{ page.root }}//static/drawtype_liquid.png" alt="Liquid Drawtype">
<figcaption>
Liquid Drawtype
</figcaption>
</figure>
Each type of liquid requires two node definitions - one for the liquid source, and
another for flowing liquid.
```lua
-- Some properties have been removed as they are beyond
-- the scope of this chapter.
minetest.register_node("default:water_source", {
drawtype = "liquid",
paramtype = "light",
inventory_image = minetest.inventorycube("default_water.png"),
-- ^ this is required to stop the inventory image from being animated
tiles = {
{
name = "default_water_source_animated.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 2.0
}
}
},
special_tiles = {
-- New-style water source material (mostly unused)
{
name = "default_water_source_animated.png",
animation = {type = "vertical_frames", aspect_w = 16,
aspect_h = 16, length = 2.0},
backface_culling = false,
}
},
--
-- Behavior
--
walkable = false, -- The player falls through
pointable = false, -- The player can't highlight it
diggable = false, -- The player can't dig it
buildable_to = true, -- Nodes can be replace this node
alpha = 160,
--
-- Liquid Properties
--
drowning = 1,
liquidtype = "source",
liquid_alternative_flowing = "default:water_flowing",
-- ^ when the liquid is flowing
liquid_alternative_source = "default:water_source",
-- ^ when the liquid is a source
liquid_viscosity = WATER_VISC,
-- ^ how fast
liquid_range = 8,
-- ^ how far
post_effect_color = {a=64, r=100, g=100, b=200},
-- ^ colour of screen when the player is submerged
})
```
Flowing nodes have a similar definition, but with a different name and animation.
See default:water_flowing in the default mod in minetest_game for a full example.
## Node Boxes
<figure class="right_image">
<img src="{{ page.root }}//static/drawtype_nodebox.gif" alt="Nodebox drawtype">
<figcaption>
Nodebox drawtype
</figcaption>
</figure>
Node boxes allow you to create a node which is not cubic, but is instead made out
of as many cuboids as you like.
```lua
minetest.register_node("stairs:stair_stone", {
drawtype = "nodebox",
paramtype = "light",
node_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
{-0.5, 0, 0, 0.5, 0.5, 0.5},
},
}
})
```
The most important part is the node box table:
```lua
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
{-0.5, 0, 0, 0.5, 0.5, 0.5}
```
Each row is a cuboid which are joined to make a single node.
The first three numbers are the co-ordinates, from -0.5 to 0.5 inclusive, of
the bottom front left most corner, the last three numbers are the opposite corner.
They are in the form X, Y, Z, where Y is up.
You can use the [NodeBoxEditor](https://forum.minetest.net/viewtopic.php?f=14&t=2840) to
create node boxes by dragging the edges, it is more visual than doing it by hand.
### Wallmounted Node Boxes
Sometimes you want different nodeboxes for when it is placed on the floor, wall, or ceiling like with torches.
```lua
minetest.register_node("default:sign_wall", {
drawtype = "nodebox",
node_box = {
type = "wallmounted",
-- Ceiling
wall_top = {
{-0.4375, 0.4375, -0.3125, 0.4375, 0.5, 0.3125}
},
-- Floor
wall_bottom = {
{-0.4375, -0.5, -0.3125, 0.4375, -0.4375, 0.3125}
},
-- Wall
wall_side = {
{-0.5, -0.3125, -0.4375, -0.4375, 0.3125, 0.4375}
}
},
})
```
## Mesh Nodes
Whilst node boxes are generally easier to make, they are limited in that
they can only consist of cuboids. Node boxes are also unoptimised;
Inner faces will still be rendered even when they're completely hidden.
A face is a flat surface on a mesh. An inner face occurs when the faces of two
different node boxes overlap, causing parts of the node box model to be
invisible but still rendered.
You can register a mesh node as so:
```lua
minetest.register_node("mymod:meshy", {
drawtype = "mesh",
-- Holds the texture for each "material"
tiles = {
"mymod_meshy.png"
},
-- Path to the mesh
mesh = "mymod_meshy.b3d",
})
```
Make sure that the mesh is available in a `models` directory.
Most of the time the mesh should be in your mod's folder, however, it's okay to
share a mesh provided by another mod you depend on. For example, a mod that
adds more types of furniture may want to share the model provided by a basic
furniture mod.
## Signlike Nodes
Signlike nodes are flat nodes with can be mounted on the sides of other nodes.
Despite the name of this drawtype, signs don't actually tend to use signlike but
instead use the `nodebox` drawtype to provide a 3D effect. The `signlike` drawtype
is, however, commonly used by ladders.
```lua
minetest.register_node("default:ladder_wood", {
drawtype = "signlike",
tiles = {"default_ladder_wood.png"},
-- Required: store the rotation in param2
paramtype2 = "wallmounted",
selection_box = {
type = "wallmounted",
},
})
```
## Plantlike Nodes
<figure class="right_image">
<img src="{{ page.root }}//static/drawtype_plantlike.png" alt="Plantlike Drawtype">
<figcaption>
Plantlike Drawtype
</figcaption>
</figure>
Plantlike nodes draw their tiles in an X like pattern.
```lua
minetest.register_node("default:papyrus", {
drawtype = "plantlike",
-- Only one texture used
tiles = {"default_papyrus.png"},
selection_box = {
type = "fixed",
fixed = {-6 / 16, -0.5, -6 / 16, 6 / 16, 0.5, 6 / 16},
},
})
```
## Firelike Nodes
Firelike is similar to plantlike, except that it is designed to "cling" to walls
and ceilings.
<figure>
<img src="{{ page.root }}//static/drawtype_firelike.png" alt="Firelike nodes">
<figcaption>
Firelike nodes
</figcaption>
</figure>
```lua
minetest.register_node("mymod:clingere", {
drawtype = "firelike",
-- Only one texture used
tiles = { "mymod:clinger" },
})
```
## More Drawtypes
This is not a comprehensive list, there are more types including:
* Fencelike
* Plantlike rooted - for underwater plants
* Raillike - for cart tracks
* Torchlike - for 2D wall/floor/ceiling nodes.
The torches in Minetest Game actually use two different node definitions of
mesh nodes (default:torch and default:torch_wall).
As always, read the [Lua API documentation](../../lua_api.html#node-drawtypes)
for the complete list.

View File

@ -0,0 +1,382 @@
---
title: Nodes, Items, and Crafting
layout: default
root: ../..
idx: 2.1
description: Learn how to register node, items, and craft recipes using register_node, register_item, and register_craft.
redirect_from: /en/chapters/nodes_items_crafting.html
---
## Introduction <!-- omit in toc -->
Registering new nodes and craftitems, and creating craft recipes, are
basic requirements for many mods.
- [What are Nodes and Items?](#what-are-nodes-and-items)
- [Registering Items](#registering-items)
- [Item Names and Aliases](#item-names-and-aliases)
- [Textures](#textures)
- [Registering a basic node](#registering-a-basic-node)
- [Actions and Callbacks](#actions-and-callbacks)
- [on_use](#onuse)
- [Crafting](#crafting)
- [Shaped](#shaped)
- [Shapeless](#shapeless)
- [Cooking and Fuel](#cooking-and-fuel)
- [Groups](#groups)
- [Tools, Capabilities, and Dig Types](#tools-capabilities-and-dig-types)
## What are Nodes and Items?
Nodes, craftitems, and tools are all Items.
An item is something that could be found in an inventory -
even though it may not be possible through normal gameplay.
A node is an item which can be placed or be found in the world.
Every position in the world must be occupied with one and only one node -
seemingly blank positions are usually air nodes.
A craftitem can't be placed and is only found in inventories or as a dropped item
in the world.
A tool has the ability to wear and typically has non-default digging capabilities.
In the future, it's likely that craftitems and tools will merge into one type of
item, as the distinction between them is rather artificial.
## Registering Items
Item definitions consist of an *item name* and a *definition table*.
The definition table contains attributes which affect the behaviour of the item.
```lua
minetest.register_craftitem("modname:itemname", {
description = "My Special Item",
inventory_image = "modname_itemname.png"
})
```
### Item Names and Aliases
Every item has an item name used to refer to it, which should be in the
following format:
modname:itemname
The modname is the name of the mod in which the item is registered, and the
item name is the name of the item itself.
The item name should be relevant to what the item is and can't already be registered.
Items can also have *aliases* pointing to their name.
An *alias* is a pseudo-item name which results in the engine treating any
occurrences of the alias as if it were the item name.
There are two main common uses of this:
* Renaming removed items to something else.
There may be unknown nodes in the world and in inventories if an item is
removed from a mod without any corrective code.
It's important to avoid aliasing to an unobtainable node if the remove node
could be obtained.
* Adding a shortcut. `/giveme dirt` is easier than `/giveme default:dirt`.
Registering an alias is pretty simple.
A good way to remember the order of the arguments is `from → to` where
*from* is the alias and *to* is the target.
```lua
minetest.register_alias("dirt", "default:dirt")
```
Mods need to make sure to resolve aliases before dealing directly with item names,
as the engine won't do this.
This is pretty simple though:
```lua
itemname = minetest.registered_aliases[itemname] or itemname
```
### Textures
Textures should be placed in the textures/ folder with names in the format
`modname_itemname.png`.\\
JPEG textures are supported, but they do not support transparency and are generally
bad quality at low resolutions.
It is often better to use the PNG format.
Textures in Minetest are usually 16 by 16 pixels.
They can be any resolution, but it is recommended that they are in the order of 2,
for example, 16, 32, 64, or 128.
This is because other resolutions may not be supported correctly on older devices,
resulting in decreased performance.
## Registering a basic node
```lua
minetest.register_node("mymod:diamond", {
description = "Alien Diamond",
tiles = {"mymod_diamond.png"},
is_ground_content = true,
groups = {cracky=3, stone=1}
})
```
The `tiles` property is a table of texture names the node will use.
When there is only one texture, this texture is used on every side.
To give a different texture per-side, supply the names of 6 textures in this order:
up (+Y), down (-Y), right (+X), left (-X), back (+Z), front (-Z).
(+Y, -Y, +X, -X, +Z, -Z)
Remember that +Y is upwards in Minetest, as is the convention with
3D computer graphics.
```lua
minetest.register_node("mymod:diamond", {
description = "Alien Diamond",
tiles = {
"mymod_diamond_up.png", -- y+
"mymod_diamond_down.png", -- y-
"mymod_diamond_right.png", -- x+
"mymod_diamond_left.png", -- x-
"mymod_diamond_back.png", -- z+
"mymod_diamond_front.png", -- z-
},
is_ground_content = true,
groups = {cracky = 3},
drop = "mymod:diamond_fragments"
-- ^ Rather than dropping diamond, drop mymod:diamond_fragments
})
```
The is_ground_content attribute allows caves to be generated over the stone.
This is essential for any node which may be placed during map generation underground.
Caves are cut out of the world after all the other nodes in an area have generated.
## Actions and Callbacks
Minetest heavily uses a callback-based modding design.
Callbacks can be placed in the item definition table to allow response to various
different user events.
### on_use
By default, the use callback is triggered when a player left-clicks with an item.
Having a use callback prevents the item being used to dig nodes.
One common use of the use callback is for food:
```lua
minetest.register_craftitem("mymod:mudpie", {
description = "Alien Mud Pie",
inventory_image = "myfood_mudpie.png",
on_use = minetest.item_eat(20),
})
```
The number supplied to the minetest.item_eat function is the number of hit points
healed when this food is consumed.
Each heart icon the player has is worth two hitpoints.
A player can usually have up to 10 hearts, which is equal to 20 hitpoints.
Hitpoints don't have to be integers (whole numbers); they can be decimals.
minetest.item_eat() is a function which returns a function, setting it
as the on_use callback.
This means the code above is roughly similar to this:
```lua
minetest.register_craftitem("mymod:mudpie", {
description = "Alien Mud Pie",
inventory_image = "myfood_mudpie.png",
on_use = function(...)
return minetest.do_item_eat(20, nil, ...)
end,
})
```
By understanding how item_eat works by simply returning a function, it's
possible to modify it to do more complex behaviour such as play a custom sound.
## Crafting
There are several types of crafting recipe available, indicated by the `type`
property.
* shaped - Ingredients must be in the correct position.
* shapeless - It doesn't matter where the ingredients are,
just that there is the right amount.
* cooking - Recipes for the furnace to use.
* fuel - Defines items which can be burned in furnaces.
* tool_repair - Defines items which can be tool repaired.
Craft recipes are not items, so they do not use Item Names to uniquely
identify themselves.
### Shaped
Shaped recipes are when the ingredients need to be in the right shape or
pattern to work. In the example below, the fragments need to be in a
chair-like pattern for the craft to work.
```lua
minetest.register_craft({
type = "shaped",
output = "mymod:diamond_chair 99",
recipe = {
{"mymod:diamond_fragments", "", ""},
{"mymod:diamond_fragments", "mymod:diamond_fragments", ""},
{"mymod:diamond_fragments", "mymod:diamond_fragments", ""}
}
})
```
One thing to note is the blank column on the right-hand side.
This means that there *must* be an empty column to the right of the shape, otherwise
this won't work.
If this empty column shouldn't be required, then the empty strings can be left
out like so:
```lua
minetest.register_craft({
output = "mymod:diamond_chair 99",
recipe = {
{"mymod:diamond_fragments", "" },
{"mymod:diamond_fragments", "mymod:diamond_fragments"},
{"mymod:diamond_fragments", "mymod:diamond_fragments"}
}
})
```
The type field isn't actually needed for shaped crafts, as shaped is the
default craft type.
### Shapeless
Shapeless recipes are a type of recipe which is used when it doesn't matter
where the ingredients are placed, just that they're there.
```lua
minetest.register_craft({
type = "shapeless",
output = "mymod:diamond 3",
recipe = {
"mymod:diamond_fragments",
"mymod:diamond_fragments",
"mymod:diamond_fragments",
},
})
```
### Cooking and Fuel
Recipes with the type "cooking" are not made in the crafting grid,
but are cooked in furnaces, or other cooking tools that might be found in mods.
```lua
minetest.register_craft({
type = "cooking",
output = "mymod:diamond_fragments",
recipe = "default:coalblock",
cooktime = 10,
})
```
The only real difference in the code is that the recipe is just a single item,
compared to being in a table (between braces).
They also have an optional "cooktime" parameter which
defines how long the item takes to cook.
If this is not set, it defaults to 3.
The recipe above works when the coal block is in the input slot,
with some form of fuel below it.
It creates diamond fragments after 10 seconds!
This type is an accompaniment to the cooking type, as it defines
what can be burned in furnaces and other cooking tools from mods.
```lua
minetest.register_craft({
type = "fuel",
recipe = "mymod:diamond",
burntime = 300,
})
```
They don't have an output like other recipes, but they have a burn time
which defines how long they will last as fuel in seconds.
So, the diamond is good as fuel for 300 seconds!
## Groups
Items can be members of many groups and groups can have many members.
Groups are defined using the `groups` property in the definition table
and have an associated value.
```lua
groups = {cracky = 3, wood = 1}
```
There are several reasons you use groups.
Firstly, groups are used to describe properties such as dig types and flammability.
Secondly, groups can be used in a craft recipe instead of an item name to allow
any item in the group to be used.
```lua
minetest.register_craft({
type = "shapeless",
output = "mymod:diamond_thing 3",
recipe = {"group:wood", "mymod:diamond"}
})
```
## Tools, Capabilities, and Dig Types
Dig types are groups which are used to define how strong a node is when dug
with different tools.
A dig type group with a higher associated value means the node is easier
and quicker to cut.
It's possible to combine multiple dig types to allow the more efficient use
of multiple types of tools.
A node with no dig types cannot be dug by any tools.
| Group | Best Tool | Description |
|--------|-----------|-------------|
| crumbly | spade | Dirt, sand |
| cracky | pickaxe | Tough (but brittle) stuff like stone |
| snappy | *any* | Can be cut using fine tools;<br>e.g. leaves, smallplants, wire, sheets of metal |
| choppy | axe | Can be cut using a sharp force; e.g. trees, wooden planks |
| fleshy | sword | Living things like animals and the player.<br>This could imply some blood effects when hitting. |
| explody | ? | Especially prone to explosions |
| oddly_breakable_by_hand | *any* | Torches and such - very quick to dig |
Every tool has a tool capability.
A capability includes a list of supported dig types, and associated properties
for each type such as dig times and the amount of wear.
Tools can also have a maximum supported hardness for each type, which makes
it possible to prevent weaker tools from digging harder nodes.
It's very common for tools to include all dig types in their capabilities,
with the less suitable ones having very inefficient properties.
If the item a player is currently wielding doesn't have an explicit tool
capability, then the capability of the current hand is used instead.
```lua
minetest.register_tool("mymod:tool", {
description = "My Tool",
inventory_image = "mymod_tool.png",
tool_capabilities = {
full_punch_interval = 1.5,
max_drop_level = 1,
groupcaps = {
crumbly = {
maxlevel = 2,
uses = 20,
times = { [1]=1.60, [2]=1.20, [3]=0.80 }
},
},
damage_groups = {fleshy=2},
},
})
```
Groupcaps is the list of supported dig types for digging nodes.
Damage groups are for controlling how tools damage objects, which will be
discussed later in the Objects, Players, and Entities chapter.

225
_it/map/environment.md Normal file
View File

@ -0,0 +1,225 @@
---
title: Basic Map Operations
layout: default
root: ../..
idx: 3.1
description: Basic operations like set_node and get_node
redirect_from: /en/chapters/environment.html
---
## Introduction <!-- omit in toc -->
In this chapter, you will learn how to perform basic actions on the map.
- [Map Structure](#map-structure)
- [Reading](#reading)
- [Reading Nodes](#reading-nodes)
- [Finding Nodes](#finding-nodes)
- [Writing](#writing)
- [Writing Nodes](#writing-nodes)
- [Removing Nodes](#removing-nodes)
- [Loading Blocks](#loading-blocks)
- [Deleting Blocks](#deleting-blocks)
## Map Structure
The Minetest map is split into MapBlocks, each MapBlocks being a cube of size 16.
As players travel around the map, MapBlocks are created, loaded, and unloaded.
Areas of the map which are not yet loaded are full of *ignore* nodes, an impassable
unselectable placeholder node. Empty space is full of *air* nodes, an invisible node
you can walk through.
Loaded map blocks are often referred to as *active blocks*. Active Blocks can be
read from or written to by mods or players, and have active entities. The Engine also
performs operations on the map, such as performing liquid physics.
MapBlocks can either be loaded from the world database or generated. MapBlocks
will be generated up to the map generation limit (`mapgen_limit`) which is set
to its maximum value, 31000, by default. Existing MapBlocks can, however, be
loaded from the world database outside of the generation limit.
## Reading
### Reading Nodes
You can read from the map once you have a position:
```lua
local node = minetest.get_node({ x = 1, y = 3, z = 4 })
print(dump(node)) --> { name=.., param1=.., param2=.. }
```
If the position is a decimal, it will be rounded to the containing node.
The function will always return a table containing the node information:
* `name` - The node name, which will be *ignore* when the area is unloaded.
* `param1` - See the node definition. This will commonly be light.
* `param2` - See the node definition.
It's worth noting that the function won't load the containing block if the block
is inactive, but will instead return a table with `name` being `ignore`.
You can use `minetest.get_node_or_nil` instead, which will return `nil` rather
than a table with a name of `ignore`. It still won't load the block, however.
This may still return `ignore` if a block actually contains ignore.
This will happen near the edge of the map as defined by the map generation
limit (`mapgen_limit`).
### Finding Nodes
Minetest offers a number of helper functions to speed up common map actions.
The most commonly used of these are for finding nodes.
For example, say we wanted to make a certain type of plant that grows
better near mese; you would need to search for any nearby mese nodes,
and adapt the growth rate accordingly.
```lua
local grow_speed = 1
local node_pos = minetest.find_node_near(pos, 5, { "default:mese" })
if node_pos then
minetest.chat_send_all("Node found at: " .. dump(node_pos))
grow_speed = 2
end
```
Let's say, for example, that the growth rate increases the more mese there is
nearby. You should then use a function which can find multiple nodes in area:
```lua
local pos1 = vector.subtract(pos, { x = 5, y = 5, z = 5 })
local pos2 = vector.add(pos, { x = 5, y = 5, z = 5 })
local pos_list =
minetest.find_nodes_in_area(pos1, pos2, { "default:mese" })
local grow_speed = 1 + #pos_list
```
The above code doesn't quite do what we want, as it checks based on area, whereas
`find_node_near` checks based on range. In order to fix this, we will,
unfortunately, need to manually check the range ourselves.
```lua
local pos1 = vector.subtract(pos, { x = 5, y = 5, z = 5 })
local pos2 = vector.add(pos, { x = 5, y = 5, z = 5 })
local pos_list =
minetest.find_nodes_in_area(pos1, pos2, { "default:mese" })
local grow_speed = 1
for i=1, #pos_list do
local delta = vector.subtract(pos_list[i], pos)
if delta.x*delta.x + delta.y*delta.y <= 5*5 then
grow_speed = grow_speed + 1
end
end
```
Now your code will correctly increase `grow_speed` based on mese nodes in range.
Note how we compared the squared distance from the position, rather than square
rooting it to obtain the actual distance. This is because computers find square
roots computationally expensive, so you should avoid them as much as possible.
There are more variations of the above two functions, such as
`find_nodes_with_meta` and `find_nodes_in_area_under_air`, which work similarly
and are useful in other circumstances.
## Writing
### Writing Nodes
You can use `set_node` to write to the map. Each call to set_node will cause
lighting to be recalculated, which means that set_node is fairly slow for large
numbers of nodes.
```lua
minetest.set_node({ x = 1, y = 3, z = 4 }, { name = "default:mese" })
local node = minetest.get_node({ x = 1, y = 3, z = 4 })
print(node.name) --> default:mese
```
set_node will remove any associated metadata or inventory from that position.
This isn't desirable in all circumstances, especially if you're using multiple
node definitions to represent one conceptual node. An example of this is the
furnace node - whilst you think conceptually of it as one node, it's actually
two.
You can set a node without deleting metadata or the inventory like so:
```lua
minetest.swap_node({ x = 1, y = 3, z = 4 }, { name = "default:mese" })
```
### Removing Nodes
A node must always be present. To remove a node, you set the position to `air`.
The following two lines will both remove a node, and are both identical:
```lua
minetest.remove_node(pos)
minetest.set_node(pos, { name = "air" })
```
In fact, remove_node will call set_node with the name being air.
## Loading Blocks
You can use `minetest.emerge_area` to load map blocks. Emerge area is asynchronous,
meaning the blocks won't be loaded instantly. Instead, they will be loaded
soon in the future, and the callback will be called each time.
```lua
-- Load a 20x20x20 area
local halfsize = { x = 10, y = 10, z = 10 }
local pos1 = vector.subtract(pos, halfsize)
local pos2 = vector.add (pos, halfsize)
local context = {} -- persist data between callback calls
minetest.emerge_area(pos1, pos2, emerge_callback, context)
```
Minetest will call `emerge_callback` whenever it loads a block, with some
progress information.
```lua
local function emerge_callback(pos, action,
num_calls_remaining, context)
-- On first call, record number of blocks
if not context.total_blocks then
context.total_blocks = num_calls_remaining + 1
context.loaded_blocks = 0
end
-- Increment number of blocks loaded
context.loaded_blocks = context.loaded_blocks + 1
-- Send progress message
if context.total_blocks == context.loaded_blocks then
minetest.chat_send_all("Finished loading blocks!")
end
local perc = 100 * context.loaded_blocks / context.total_blocks
local msg = string.format("Loading blocks %d/%d (%.2f%%)",
context.loaded_blocks, context.total_blocks, perc)
minetest.chat_send_all(msg)
end
end
```
This is not the only way of loading blocks; using an LVM will also cause the
encompassed blocks to be loaded synchronously.
## Deleting Blocks
You can use delete_blocks to delete a range of map blocks:
```lua
-- Delete a 20x20x20 area
local halfsize = { x = 10, y = 10, z = 10 }
local pos1 = vector.subtract(pos, halfsize)
local pos2 = vector.add (pos, halfsize)
minetest.delete_area(pos1, pos2)
```
This will delete all map blocks in that area, *inclusive*. This means that some
nodes will be deleted outside the area as they will be on a mapblock which overlaps
the area bounds.

281
_it/map/objects.md Normal file
View File

@ -0,0 +1,281 @@
---
title: Objects, Players, and Entities
layout: default
root: ../..
idx: 3.4
description: Using an ObjectRef
degrad:
level: warning
title: Degrees and Radians
message: Attachment rotation is set in degrees, whereas object rotation is in radians.
Make sure to convert to the correct angle measurement.
---
## Introduction <!-- omit in toc -->
In this chapter, you will learn how to manipulate objects and how to define your
own.
- [What are Objects, Players, and Entities?](#what-are-objects-players-and-entities)
- [Position and Velocity](#position-and-velocity)
- [Object Properties](#object-properties)
- [Entities](#entities)
- [Attachments](#attachments)
- [Your Turn](#your-turn)
## What are Objects, Players, and Entities?
Players and Entities are both types of Objects. An Object is something that can move
independently of the node grid and has properties such as velocity and scale.
Objects aren't items, and they have their own separate registration system.
There are a few differences between Players and Entities.
The biggest one is that Players are player-controlled, whereas Entities are mod-controlled.
This means that the velocity of a player cannot be set by mods - players are client-side,
and entities are server-side.
Another difference is that Players will cause map blocks to be loaded, whereas Entities
will just be saved and become inactive.
This distinction is muddied by the fact that Entities are controlled using a table
which is referred to as a Lua entity, as discussed later.
## Position and Velocity
`get_pos` and `set_pos` exist to allow you to get and set an entity's position.
```lua
local object = minetest.get_player_by_name("bob")
local pos = object:get_pos()
object:set_pos({ x = pos.x, y = pos.y + 1, z = pos.z })
```
`set_pos` immediately sets the position, with no animation. If you'd like to
smoothly animate an object to the new position, you should use `move_to`.
This, unfortunately, only works for entities.
```lua
object:move_to({ x = pos.x, y = pos.y + 1, z = pos.z })
```
An important thing to think about when dealing with entities is network latency.
In an ideal world, messages about entity movements would arrive immediately,
in the correct order, and with a similar interval as to how you sent them.
However, unless you're in singleplayer, this isn't an ideal world.
Messages will take a while to arrive. Position messages may arrive out of order,
resulting in some `set_pos` calls being skipped as there's no point going to
a position older than the current known position.
Moves may not be similarly spaced, which makes it difficult to use them for animation.
All this results in the client seeing different things to the server, which is something
you need to be aware of.
## Object Properties
Object properties are used to tell the client how to render and deal with an
object. It's not possible to define custom properties, because the properties are
for the engine to use, by definition.
Unlike nodes, objects have a dynamic rather than set appearance.
You can change how an object looks, among other things, at any time by updating
its properties.
```lua
object:set_properties({
visual = "mesh",
mesh = "character.b3d",
textures = {"character_texture.png"},
visual_size = {x=1, y=1},
})
```
The updated properties will be sent to all players in range.
This is very useful to get a large amount of variety very cheaply, such as having
different skins per-player.
As shown in the next section, entities can have initial properties
provided in their definition.
The default Player properties are defined in the engine, however, so you'll
need to use `set_properties()` in `on_joinplayer` to set the properties for newly
joined players.
## Entities
An Entity has a definition table that resembles an item definition table.
This table can contain callback methods, initial object properties, and custom
members.
However, entities differ in one very important way from items. When an entity is
emerged (ie: loaded or created), a new table is created for that entity that
*inherits* from the definition table using metatables.
This new table is commonly referred to as a Lua Entity table.
Metatables are an important Lua feature that you will need
to be aware of, as it is an essential part of the Lua language.
In layman's terms, a metatable allows you to control how the table behaves when
using certain Lua syntax. The most common use of metatables is the ability to use
another table as a prototype, defaulting to the other table's properties and methods when
they do not exist in the current table.
Say you want to access member X on table A. If table A has that member, then
it will be returned as normal. However, if the table doesn't have that member but
it does have a metatable could table B, then table B will be checked to see if it
has that member.
<!--table A is a metatable of table B, then table
B will have all the properties and methods of table A if the derived table doesn't
have any itself.-->
```lua
local MyEntity = {
initial_properties = {
hp_max = 1,
physical = true,
collide_with_objects = false,
collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3},
visual = "wielditem",
visual_size = {x = 0.4, y = 0.4},
textures = {""},
spritediv = {x = 1, y = 1},
initial_sprite_basepos = {x = 0, y = 0},
},
message = "Default message",
}
function MyEntity:set_message(msg)
self.message = msg
end
```
When an entity has emerged, a table is created for it by copying everything from
its type table.
This table can be used to store variables for that particular entity.
Both an ObjectRef and an entity table provide ways to get the counterpart:
```lua
local entity = object:get_luaentity()
local object = entity.object
print("entity is at " .. minetest.pos_to_string(object:get_pos()))
```
There are a number of available callbacks for use with entities.
A complete list can be found in [lua_api.txt]({{ page.root }}/lua_api.html#registered-entities).
```lua
function MyEntity:on_step(dtime)
local pos = self.object:get_pos()
local pos_down = vector.subtract(pos, vector.new(0, 1, 0))
local delta
if minetest.get_node(pos_down).name == "air" then
delta = vector.new(0, -1, 0)
elseif minetest.get_node(pos).name == "air" then
delta = vector.new(0, 0, 1)
else
delta = vector.new(0, 1, 0)
end
delta = vector.multiply(delta, dtime)
self.object:move_to(vector.add(pos, delta))
end
function MyEntity:on_punch(hitter)
minetest.chat_send_player(hitter:get_player_name(), self.message)
end
```
Now, if you were to spawn and use this entity, you'd notice that the message
would be forgotten when the entity becomes inactive then active again.
This is because the message isn't saved.
Rather than saving everything in the entity table, Minetest gives you control over
how to save things.
Staticdata is a string which contains all the custom information that
needs to stored.
```lua
function MyEntity:get_staticdata()
return minetest.write_json({
message = self.message,
})
end
function MyEntity:on_activate(staticdata, dtime_s)
if staticdata ~= "" and staticdata ~= nil then
local data = minetest.parse_json(staticdata) or {}
self:set_message(data.message)
end
end
```
Minetest may call `get_staticdata()` as many times as it wants and at any time.
This is because Minetest doesn't wait for a MapBlock to become inactive to save
it, as this would result in data loss. MapBlocks are saved roughly every 18
seconds, so you should notice a similar interval for `get_staticdata()` being called.
`on_activate()`, on the other hand, will only be called when an entity becomes
active either from the MapBlock becoming active or from the entity spawning.
This means that staticdata could be empty.
Finally, you need to register the type table using the aptly named `register_entity`.
```lua
minetest.register_entity("mymod:entity", MyEntity)
```
The entity can be spawned by a mod like so:
```lua
local pos = { x = 1, y = 2, z = 3 }
local obj = minetest.add_entity(pos, "mymod:entity", nil)
```
The third parameter is the initial staticdata.
To set the message, you can use the entity table method:
```lua
obj:get_luaentity():set_message("hello!")
```
Players with the *give* [privilege](../players/privileges.html) can
use a [chat command](../players/chat.html) to spawn entities:
/spawnentity mymod:entity
## Attachments
Attached objects will move when the parent - the object they are attached to -
is moved. An attached object is said to be a child of the parent.
An object can have an unlimited number of children, but at most one parent.
```lua
child:set_attach(parent, bone, position, rotation)
```
An Object's `get_pos()` will always return the global position of the object, no
matter whether it is attached or not.
`set_attach` takes a relative position, but not as you'd expect.
The attachment position is relative to the parent's origin as scaled up by 10 times.
So, `0,5,0` would be half a node above the parent's origin.
{% include notice.html notice=page.degrad %}
For 3D models with animations, the bone argument is used to attach the entity
to a bone.
3D animations are based on skeletons - a network of bones in the model where
each bone can be given a position and rotation to change the model, for example,
to move the arm.
Attaching to a bone is useful if you want to make a character hold something:
```lua
obj:set_attach(player,
"Arm_Right", -- default bone
{x=0.2, y=6.5, z=3}, -- default position
{x=-100, y=225, z=90}) -- default rotation
```
## Your Turn
* Make a windmill by combining nodes and an entity.
* Make a mob of your choice (using just the entity API, and without using any other mods).

247
_it/map/storage.md Normal file
View File

@ -0,0 +1,247 @@
---
title: Storage and Metadata
layout: default
root: ../..
idx: 3.3
description: Mod Storage, NodeMetaRef (get_meta).
redirect_from:
- /en/chapters/node_metadata.html
- /en/map/node_metadata.html
---
## Introduction <!-- omit in toc -->
In this chapter, you will learn how you can store data.
- [Metadata](#metadata)
- [What is Metadata?](#what-is-metadata)
- [Obtaining a Metadata Object](#obtaining-a-metadata-object)
- [Reading and Writing](#reading-and-writing)
- [Special Keys](#special-keys)
- [Storing Tables](#storing-tables)
- [Private Metadata](#private-metadata)
- [Lua Tables](#lua-tables)
- [Mod Storage](#mod-storage)
- [Databases](#databases)
- [Deciding Which to Use](#deciding-which-to-use)
- [Your Turn](#your-turn)
## Metadata
### What is Metadata?
In Minetest, Metadata is a key-value store used to attach custom data to something.
You can use metadata to store information against a Node, Player, or ItemStack.
Each type of metadata uses the exact same API.
Metadata stores values as strings, but there are a number of methods to
convert and store other primitive types.
Some keys in metadata may have special meaning.
For example, `infotext` in node metadata is used to store the tooltip which shows
when hovering over the node using the crosshair.
To avoid conflicts with other mods, you should use the standard namespace
convention for keys: `modname:keyname`.
The exception is for conventional data such as the owner name which is stored as
`owner`.
Metadata is data about data.
The data itself, such as a node's type or an stack's count, is not metadata.
### Obtaining a Metadata Object
If you know the position of a node, you can retrieve its metadata:
```lua
local meta = minetest.get_meta({ x = 1, y = 2, z = 3 })
```
Player and ItemStack metadata are obtained using `get_meta()`:
```lua
local pmeta = player:get_meta()
local imeta = stack:get_meta()
```
### Reading and Writing
In most cases, `get_<type>()` and `set_<type>()` methods will be used to read
and write to meta.
Metadata stores strings, so the string methods will directly set and get the value.
```lua
print(meta:get_string("foo")) --> ""
meta:set_string("foo", "bar")
print(meta:get_string("foo")) --> "bar"
```
All of the typed getters will return a neutral default value if the key doesn't
exist, such as `""` or `0`.
You can use `get()` to return a string or nil.
As Metadata is a reference, any changes will be updated to the source automatically.
ItemStacks aren't references however, so you'll need to update the itemstack in the
inventory.
The non-typed getters and setters will convert to and from strings:
```lua
print(meta:get_int("count")) --> 0
meta:set_int("count", 3)
print(meta:get_int("count")) --> 3
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 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.
You can use colours by encoding them with `minetest.colorize()`.
`owner` is a common key used to store the username of the player that owns the
item or node.
### Storing Tables
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 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 }
meta:set_string("foo", minetest.serialize(data))
data = minetest.deserialize(minetest:get_string("foo"))
```
### Private Metadata
Entries in Node Metadata can be marked as private, and not sent to the client.
Entries not marked as private will be sent to the client.
```lua
meta:set_string("secret", "asd34dn")
meta:mark_as_private("secret")
```
### Lua Tables
You can convert to and from Lua tables using `to_table` and `from_table`:
```lua
local tmp = meta:to_table()
tmp.foo = "bar"
meta:from_table(tmp)
```
## Mod Storage
Mod storage uses the exact same API as Metadata, although it's not technically
Metadata.
Mod storage is per-mod, and can only be obtained during load time in order to
know which mod is requesting it.
```lua
local storage = minetest.get_mod_storage()
```
You can now manipulate the storage just like metadata:
```lua
storage:set_string("foo", "bar")
```
## Databases
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.
```lua
local backend
if use_database then
backend =
dofile(minetest.get_modpath("mymod") .. "/backend_sqlite.lua")
else
backend =
dofile(minetest.get_modpath("mymod") .. "/backend_storage.lua")
end
backend.get_foo("a")
backend.set_foo("a", { score = 3 })
```
The backend_storage.lua file should include a mod storage implementation:
```lua
local storage = minetest.get_mod_storage()
local backend = {}
function backend.set_foo(key, value)
storage:set_string(key, minetest.serialize(value))
end
function backend.get_foo(key, value)
return minetest.deserialize(storage:get_string(key))
end
return backend
```
The backend_sqlite would do a similar thing, but use the Lua *lsqlite3* library
instead of mod storage.
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 in the settings")
local _sql = ie.require("lsqlite3")
-- Prevent other mods from using the global sqlite3 library
if sqlite3 then
sqlite3 = nil
end
```
Teaching about SQL or how to use the lsqlite3 library is out of scope for this book.
## Deciding Which to Use
The type of method you use depends on what the data is about,
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 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 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
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.
(Use `on_punch` in the node definition and `minetest.set_node`.)

120
_it/map/timers.md Normal file
View File

@ -0,0 +1,120 @@
---
title: Node Timers and ABMs
layout: default
root: ../..
idx: 3.2
description: Learn how to make ABMs to change blocks.
redirect_from:
- /en/chapters/abms.html
- /en/map/abms.html
---
## Introduction <!-- omit in toc -->
Periodically running a function on certain nodes is a common task.
Minetest provides two methods of doing this: Active Block Modifiers (ABMs) and node timers.
ABMs scan all loaded MapBlocks looking for nodes that match a criteria.
They are best suited for nodes which are frequently found in the world,
such as grass.
They have a high CPU overhead, but a low memory and storage overhead.
For nodes that are uncommon or already use metadata, such as furnaces
and machines, node timers should be used instead.
Node timers work by keeping track of pending timers in each MapBlock, and then
running them when they expire.
This means that timers don't need to search all loaded nodes to find matches,
but instead require slightly more memory and storage for the tracking
of pending timers.
- [Node Timers](#node-timers)
- [Active Block Modifiers](#active-block-modifiers)
- [Your Turn](#your-turn)
## Node Timers
Node timers are directly tied to a single node.
You can manage node timers by obtaining a NodeTimerRef object.
```lua
local timer = minetest.get_node_timer(pos)
timer:start(10.5) -- in seconds
```
You can also check the status or stop the timer:
```lua
if timer:is_started() then
print("The timer is running, and has " .. timer:get_timeout() .. "s remaining!")
print(timer:get_elapsed() .. "s has elapsed.")
end
timer:stop()
```
When a node timer is up, the `on_timer` method in the node's definition table will
be called.
The method only takes a single parameter, the position of the node.
```lua
minetest.register_node("autodoors:door_open", {
on_timer = function(pos)
minetest.set_node(pos, { name = "autodoors:door" })
return false
end
})
```
Returning true in `on_timer` will cause the timer to run again for the same interval.
You may have noticed a limitation with timers: for optimisation reasons, it's
only possible to have one type of timer per node type, and only one timer running per node.
## Active Block Modifiers
Alien grass, for the purposes of this chapter, is a type of grass which
has a chance to appear near water.
```lua
minetest.register_node("aliens:grass", {
description = "Alien Grass",
light_source = 3, -- The node radiates light. Min 0, max 14
tiles = {"aliens_grass.png"},
groups = {choppy=1},
on_use = minetest.item_eat(20)
})
minetest.register_abm({
nodenames = {"default:dirt_with_grass"},
neighbors = {"default:water_source", "default:water_flowing"},
interval = 10.0, -- Run every 10 seconds
chance = 50, -- Select every 1 in 50 nodes
action = function(pos, node, active_object_count,
active_object_count_wider)
local pos = {x = pos.x, y = pos.y + 1, z = pos.z}
minetest.set_node(pos, {name = "aliens:grass"})
end
})
```
This ABM runs every ten seconds, and for each matching node, there is
a 1 in 50 chance of it running.
If the ABM runs on a node, an alien grass node is placed above it.
Please be warned, this will delete any node previously located in that position.
To prevent this you should include a check using minetest.get_node to make sure there is space for the grass.
Specifying a neighbour is optional.
If you specify multiple neighbours, only one of them needs to be
present to meet the requirements.
Specifying chance is also optional.
If you don't specify the chance, the ABM will always run when the other conditions are met.
## Your Turn
* Midas touch: Make water turn to gold blocks with a 1 in 100 chance, every 5 seconds.
* Decay: Make wood turn into dirt when water is a neighbour.
* Burnin': Make every air node catch on fire. (Tip: "air" and "fire:basic_flame").
Warning: expect the game to crash.

166
_it/players/chat.md Normal file
View File

@ -0,0 +1,166 @@
---
title: Chat and Commands
layout: default
root: ../..
idx: 4.2
description: Registering a chatcommand and handling chat messages with register_on_chat_message
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
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 <pre>minetest.get_player_by_name</pre> returns a player.</p>
cb_cmdsprivs:
level: warning
title: Privileges and Chat Commands
message: The shout privilege isn't needed for a player to trigger this callback.
This is because chat commands are implemented in Lua, and are just
chat messages that begin with a /.
---
## Introduction <!-- omit in toc -->
Mods can interact with player chat, including
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)
- [Chat Commands](#chat-commands)
- [Complex Subcommands](#complex-subcommands)
- [Intercepting Messages](#intercepting-messages)
## Sending Messages to All Players
To send a message to every player in the game, call the chat_send_all function.
```lua
minetest.chat_send_all("This is a chat message to all players")
```
Here is an example of how this appears in-game:
<player1> Look at this entrance
This is a chat message to all players
<player2> What about it?
The message appears on a separate line to distinguish it from in-game player chat.
## Sending Messages to Specific Players
To send a message to a specific player, call the chat_send_player function:
```lua
minetest.chat_send_player("player1", "This is a chat message for player1")
```
This message displays in the same manner as messages to all players, but is
only visible to the named player, in this case, player1.
## Chat Commands
To register a chat command, for example `/foo`, use `register_chatcommand`:
```lua
minetest.register_chatcommand("foo", {
privs = {
interact = true,
},
func = function(name, param)
return true, "You said " .. param .. "!"
end,
})
```
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.
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 %}
## Complex Subcommands
It is often required to make complex chat commands, such as:
* `/msg <to> <message>`
* `/team join <teamname>`
* `/team leave <teamname>`
* `/team list`
This is usually done using [Lua patterns](https://www.lua.org/pil/20.2.html).
Patterns are a way of extracting stuff from text using rules.
```lua
local to, msg = string.match(param, "^([%a%d_-]+) (*+)$")
```
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 accept any digit.
* `[%d%a_-]` means accept any letter or digit or `_` or `-`.
* `+` 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, 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
<a href="chat_complex.html">Chat Command Builder</a>.
</p>
## Intercepting Messages
To intercept a message, use register_on_chat_message:
```lua
minetest.register_on_chat_message(function(name, message)
print(name .. " said " .. message)
return false
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, because `nil` is returned implicitly and is treated like false.
{% include notice.html notice=page.cb_cmdsprivs %}
You should make sure you take into account that it may be a chat command,
or the user may not have `shout`.
```lua
minetest.register_on_chat_message(function(name, message)
if message:sub(1, 1) == "/" then
print(name .. " ran chat command")
elseif minetest.check_player_privs(name, { shout = true }) then
print(name .. " said " .. message)
else
print(name .. " tried to say " .. message ..
" but doesn't have shout")
end
return false
end)
```

183
_it/players/chat_complex.md Normal file
View File

@ -0,0 +1,183 @@
---
title: Chat Command Builder
layout: default
root: ../..
idx: 4.3
description: Use ChatCmdBuilder to make a complex chat command
redirect_from: /en/chapters/chat_complex.html
---
## Introduction <!-- omit in toc -->
This chapter will show you how to make complex chat commands with ChatCmdBuilder,
such as `/msg <name> <message>`, `/team join <teamname>` or `/team leave <teamname>`.
Note that ChatCmdBuilder is a library created by the author of this book, and most
modders tend to use the method outlined in the
[Chat and Commands](chat.html#complex-subcommands) chapter.
- [Why ChatCmdBuilder?](#why-chatcmdbuilder)
- [Routes](#routes)
- [Subcommand functions](#subcommand-functions)
- [Installing ChatCmdBuilder](#installing-chatcmdbuilder)
- [Admin complex command](#admin-complex-command)
## Why ChatCmdBuilder?
Traditionally mods implemented these complex commands using Lua patterns.
```lua
local name = string.match(param, "^join ([%a%d_-]+)")
```
I, however, find Lua patterns annoying to write and unreadable.
Because of this, I created a library to do this for you.
```lua
ChatCmdBuilder.new("sethp", function(cmd)
cmd:sub(":target :hp:int", function(name, target, hp)
local player = minetest.get_player_by_name(target)
if player then
player:set_hp(hp)
return true, "Killed " .. target
else
return false, "Unable to find " .. target
end
end)
end, {
description = "Set hp of player",
privs = {
kick = true
-- ^ probably better to register a custom priv
}
})
```
`ChatCmdBuilder.new(name, setup_func, def)` creates a new chat command called
`name`. It then calls the function passed to it (`setup_func`), which then creates
subcommands. Each `cmd:sub(route, func)` is a subcommand.
A subcommand is a particular response to an input param. When a player runs
the chat command, the first subcommand that matches their input will be run,
and no others. If no subcommands match, then the user will be told of the invalid
syntax. For example, in the above code snippet if a player
types something of the form `/sethp username 12` then the function passed
to cmd:sub will be called. If they type `/sethp 12 bleh`, then a wrong
input message will appear.
`:name :hp:int` is a route. It describes the format of the param passed to /teleport.
## Routes
A route is made up of terminals and variables. Terminals must always be there.
For example, `join` in `/team join :username :teamname`. The spaces also count
as terminals.
Variables can change value depending on what the user types. For example, `:username`
and `:teamname`.
Variables are defined as `:name:type`. The `name` is used in the help documentation.
The `type` is used to match the input. If the type is not given, then the type is
`word`.
Valid types are:
* `word` - default. Any string without spaces.
* `int` - Any integer/whole number, no decimals.
* `number` - Any number, including ints and decimals.
* `pos` - 1,2,3 or 1.1,2,3.4567 or (1,2,3) or 1.2, 2 ,3.2
* `text` - Any string. There can only ever be one text variable,
no variables or terminals can come afterwards.
In `:name :hp:int`, there are two variables:
* `name` - type of `word` as no type is specified. Accepts any string without spaces.
* `hp` - type of `int`
## Subcommand functions
The first argument is the caller's name. The variables are then passed to the
function in order.
```lua
cmd:sub(":target :hp:int", function(name, target, hp)
-- subcommand function
end)
```
## Installing ChatCmdBuilder
The source code can be found and downloaded on
[Github](https://github.com/rubenwardy/ChatCmdBuilder/).
There are two ways to install:
1. Install ChatCmdBuilder as a mod and depend on it.
2. Include the init.lua file in ChatCmdBuilder as chatcmdbuilder.lua in your mod,
and dofile it.
## Admin complex command
Here is an example that creates a chat command that allows us to do this:
* `/admin kill <username>` - kill user
* `/admin move <username> to <pos>` - teleport user
* `/admin log <username>` - show report log
* `/admin log <username> <message>` - log to report log
```lua
local admin_log
local function load()
admin_log = {}
end
local function save()
-- todo
end
load()
ChatCmdBuilder.new("admin", function(cmd)
cmd:sub("kill :name", function(name, target)
local player = minetest.get_player_by_name(target)
if player then
player:set_hp(0)
return true, "Killed " .. target
else
return false, "Unable to find " .. target
end
end)
cmd:sub("move :name to :pos:pos", function(name, target, pos)
local player = minetest.get_player_by_name(target)
if player then
player:setpos(pos)
return true, "Moved " .. target .. " to " ..
minetest.pos_to_string(pos)
else
return false, "Unable to find " .. target
end
end)
cmd:sub("log :username", function(name, target)
local log = admin_log[target]
if log then
return true, table.concat(log, "\n")
else
return false, "No entries for " .. target
end
end)
cmd:sub("log :username :message", function(name, target, message)
local log = admin_log[target] or {}
table.insert(log, message)
admin_log[target] = log
save()
return true, "Logged"
end)
end, {
description = "Admin tools",
privs = {
kick = true,
ban = true
}
})
```

385
_it/players/formspecs.md Normal file
View File

@ -0,0 +1,385 @@
---
title: Formspecs
layout: default
root: ../..
idx: 4.5
redirect_from: /en/chapters/formspecs.html
minetest510:
level: warning
title: Real coordinates will be in 5.1.0
classes: web-only
message: This chapter describes the use of a feature that hasn't been released yet.
You can still use this chapter and the code in Minetest 5.0, but elements will
be positioned differently to what is shown.
submit_vuln:
level: warning
title: Malicious clients can submit anything at anytime
message: You should never trust a formspec submission. A malicious client
can submit anything they like at any time - even if you never showed
them the formspec. This means that you should check privileges
and make sure that they should be allowed to perform the action.
---
## Introduction <!-- omit in toc -->
<figure class="right_image">
<img src="{{ page.root }}//static/formspec_example.png" alt="Furnace Inventory">
<figcaption>
Screenshot of furnace formspec, labelled.
</figcaption>
</figure>
In this chapter we will learn how to create a formspec and display it to the user.
A formspec is the specification code for a form.
In Minetest, forms are windows such as the player inventory and can contain a
variety of elements, such as labels, buttons and fields.
Note that if you do not need to get user input, for example when you only need
to provide information to the player, you should consider using
[Heads Up Display (HUD)](hud.html) elements instead of forms, because
unexpected windows tend to disrupt gameplay.
- [Real or Legacy Coordinates](#real-or-legacy-coordinates)
- [Anatomy of a Formspec](#anatomy-of-a-formspec)
- [Elements](#elements)
- [Header](#header)
- [Guessing Game](#guessing-game)
- [Padding and Spacing](#padding-and-spacing)
- [Receiving Formspec Submissions](#receiving-formspec-submissions)
- [Contexts](#contexts)
- [Formspec Sources](#formspec-sources)
- [Node Meta Formspecs](#node-meta-formspecs)
- [Player Inventory Formspecs](#player-inventory-formspecs)
- [Your Turn](#your-turn)
## Real or Legacy Coordinates
In older versions of Minetest, formspecs were inconsistent. The way that different
elements were positioned varied in unexpected ways; it was hard to predict the
placement of elements and align them. Minetest 5.1.0 contains a feature
called real coordinates which aims to rectify this by introducing a consistent
coordinate system. The use of real coordinates is highly recommended, and so
this chapter will use them exclusively.
{% include notice.html notice=page.minetest510 %}
## Anatomy of a Formspec
### Elements
Formspec is a domain-specific language with an unusual format.
It consists of a number of elements with the following form:
type[param1;param2]
The element type is declared and then any parameters are given
in square brackets. Multiple elements can be joined together, or placed
on multiple lines, like so:
foo[param1]bar[param1]
bo[param1]
Elements are items such as text boxes or buttons, or can be metadata such
as size or background. You should refer to
[lua_api.txt](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt#L1019)
for a list of all possible elements. Search for "Formspec" to locate the correct
part of the document.
### Header
The header of a formspec contains information which must appear first. This
includes the size of the formspec, the position, the anchor, and whether the
game-wide theme should be applied.
The elements in the header must be defined in a specific order, otherwise you
will see an error. This order is given in the above paragraph, and, as always,
documented in [lua_api.txt](../../lua_api.html#sizewhfixed_size)
The size is in formspec slots - a unit of measurement which is roughly
around 64 pixels, but varies based on the screen density and scaling
settings of the client. Here's a formspec which is `2,2` in size:
size[2,2]
real_coordinates[true]
Notice how we explicitly need to enable the use of the real coordinate system.
Without this, the legacy system will instead be used to size the formspec, which will
result in a larger size. This element is a special case, as it is the only element
which may appear both in the header and the body of a formspec. When in the header,
it must appear immediately after the size.
The position and anchor elements are used to place the formspec on the screen.
The position sets where on the screen the formspec will be, and defaults to
the center (`0.5,0.5`). The anchor sets where on the formspec the position is,
allowing you to line the formspec up with the edge of the screen. The formspec
can be placed to the left of the screen like so:
size[2,2]
real_coordinates[true]
position[0,0.5]
anchor[0,0.5]
This sets the anchor to the left middle edge of the formspec box, and then the
position of that anchor to the left of the screen.
## Guessing Game
<figure class="right_image">
<img src="{{ page.root }}/static/formspec_guessing.png" alt="Guessing Formspec">
<figcaption>
The guessing game formspec.
</figcaption>
</figure>
The best way to learn is to make something, so let's make a guessing game.
The principle is simple: the mod decides on a number, then the player makes
guesses on the number. The mod then says if the guess is higher or lower then
the actual number.
First, let's make a function to create the formspec code. It's good practice to
do this, as it makes it easier to reuse elsewhere.
<div style="clear: both;"></div>
```lua
guessing = {}
function guessing.get_formspec(name)
-- TODO: display whether the last guess was higher or lower
local text = "I'm thinking of a number... Make a guess!"
local formspec = {
"size[6,3.476]",
"real_coordinates[true]",
"label[0.375,0.5;", minetest.formspec_escape(text), "]",
"field[0.375,1.25;5.25,0.8;number;Number;]",
"button[1.5,2.3;3,0.8;guess;Guess]"
}
-- table.concat is faster than string concatenation - `..`
return table.concat(formspec, "")
end
```
In the above code, we place a field, a label, and a button. A field allows text
entry, and a button is used to submit the form. You'll notice that the elements
are positioned carefully in order to add padding and spacing, this will be explained
later.
Next, we want to allow the player to show the formspec. The main way to do this
is using `show_formspec`:
```lua
function guessing.show_to(name)
minetest.show_formspec(name, "guessing:game", guessing.get_formspec(name))
end
minetest.register_chatcommand("game", {
func = function(name)
guessing.show_to(name)
end,
})
```
The show_formspec function accepts a player name, the formspec name, and the
formspec itself. The formspec name should be a valid itemname, ie: in the format
`modname:itemname`.
### Padding and Spacing
<figure class="right_image">
<img src="{{ page.root }}/static/formspec_padding_spacing.png" alt="Padding and spacing">
<figcaption>
The guessing game formspec.
</figcaption>
</figure>
Padding is the gap between the edge of the formspec and its contents, or between unrelated
elements, shown in red. Spacing is the gap between related elements, shown in blue.
It is fairly standard to have a padding of `0.375` and a spacing of `0.25`.
<div style="clear: both;"></div>
### Receiving Formspec Submissions
When `show_formspec` is called, the formspec is sent to the client to be displayed.
For formspecs to be useful, information needs to be returned from the client to server.
The method for this is called formspec field submission, and for `show_formspec`, that
submission is received using a global callback:
```lua
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "guessing:game" then
return
end
if fields.guess then
local pname = player:get_player_name()
minetest.chat_send_all(pname .. " guessed " .. fields.number)
end
end)
```
The function given in minetest.register_on_player_receive_fields is called
every time a user submits a form. Most callbacks will need to check the formname given
to the function, and exit if it is not the right form; however, some callbacks
may need to work on multiple forms, or on all forms.
The `fields` parameter to the function is a table of the values submitted by the
user, indexed by strings. Named elements will appear in the field under their own
name, but only if they are relevent for the event that caused the submission.
For example, a button element will only appear in fields if that particular button
was pressed.
{% include notice.html notice=page.submit_vuln %}
So, now the formspec is sent to the client and the client sends information back.
The next step is to somehow generate and remember the target value, and to update
the formspec based on guesses. The way to do this is using a concept called
"contexts".
### Contexts
In many cases you want minetest.show_formspec to give information
to the callback which you don't want to send to the client. This might include
what a chat command was called with, or what the dialog is about. In this case,
the target value that needs to be remembered.
A context is a per-player table to store information, and the contexts for all
online players are stored in a file-local variable:
```lua
local _contexts = {}
local function get_context(name)
local context = _contexts[name] or {}
_contexts[name] = context
return context
end
minetest.register_on_leaveplayer(function(player)
_contexts[player:get_player_name()] = nil
end)
```
Next, we need to modify the show code to update the context
before showing the formspec:
```lua
function guessing.show_to(name)
local context = get_context(name)
context.target = context.target or math.random(1, 10)
local fs = guessing.get_formspec(name, context)
minetest.show_formspec(name, "guessing:game", fs)
end
```
We also need to modify the formspec generation code to use the context:
```lua
function guessing.get_formspec(name, context)
local text
if not context.guess then
text = "I'm thinking of a number... Make a guess!"
elseif context.guess == context.target then
text = "Hurray, you got it!"
elseif context.guess > context.target then
text = "To high!"
else
text = "To low!"
end
```
Note that it's good practice for get_formspec to only read the context, and not
update it at all. This can make the function simpler, and also easier to test.
And finally, we need to update the handler to update the context with the guess:
```lua
if fields.guess then
local name = player:get_player_name()
local context = get_context(name)
context.guess = tonumber(fields.number)
guessing.show_to(name)
end
```
## Formspec Sources
There are three different ways that a formspec can be delivered to the client:
* [show_formspec](#guessing-game): the method used above, fields are received by register_on_player_receive_fields.
* [Node Meta Formspecs](#node-meta-formspecs): the node contains a formspec in its meta data, and the client
shows it *immediately* when the player rightclicks. Fields are received by a
method in the node definition called `on_receive_fields`.
* [Player Inventory Formspecs](#player-inventory-formspecs): the formspec is sent to the client at some point, and then
shown immediately when the player presses `i`. Fields are received by
register_on_player_receive_fields.
### Node Meta Formspecs
minetest.show_formspec is not the only way to show a formspec; you can also
add formspecs to a [node's metadata](node_metadata.html). For example,
this is used with chests to allow for faster opening times -
you don't need to wait for the server to send the player the chest formspec.
```lua
minetest.register_node("mymod:rightclick", {
description = "Rightclick me!",
tiles = {"mymod_rightclick.png"},
groups = {cracky = 1},
after_place_node = function(pos, placer)
-- This function is run when the chest node is placed.
-- The following code sets the formspec for chest.
-- Meta is a way of storing data onto a node.
local meta = minetest.get_meta(pos)
meta:set_string("formspec",
"size[5,5]"..
"label[1,1;This is shown on right click]"..
"field[1,2;2,1;x;x;]")
end,
on_receive_fields = function(pos, formname, fields, player)
if(fields.quit) then return end
print(fields.x)
end
})
```
Formspecs set this way do not trigger the same callback. In order to
receive form input for meta formspecs, you must include an
`on_receive_fields` entry when registering the node.
This style of callback triggers when you press enter
in a field, which is impossible with `minetest.show_formspec`;
however, this kind of form can only be shown by right-clicking on a
node. It cannot be triggered programmatically.
### Player Inventory Formspecs
The player inventory formspec is the one shown when the player presses i.
The global callback is used to receive events from this formspec, and the
formname is `""`.
There are a number of different mods which allow multiple mods to customise
the player inventory. The officially recommended mod is
[Simple Fast Inventory (sfinv)](sfinv.html), and is included in Minetest Game.
### Your Turn
* Extend the Guessing Game to keep track of each player's top score, where the
top score is how many guesses it took.
* Make a node called "Inbox" where users can open up a formspec and leave messages.
This node should store the placers' name as `owner` in the meta, and should use
`show_formspec` to show different formspecs to different players.

293
_it/players/hud.md Normal file
View File

@ -0,0 +1,293 @@
---
title: HUD
layout: default
root: ../..
idx: 4.6
redirect_from: /en/chapters/hud.html
---
## Introduction <!-- omit in toc -->
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).
- [Positioning](#positioning)
- [Position and Offset](#position-and-offset)
- [Alignment](#alignment)
- [Scoreboard](#scoreboard)
- [Text Elements](#text-elements)
- [Parameters](#parameters)
- [Our Example](#our-example)
- [Image Elements](#image-elements)
- [Parameters](#parameters-1)
- [Scale](#scale)
- [Changing an Element](#changing-an-element)
- [Storing IDs](#storing-ids)
- [Other Elements](#other-elements)
## Positioning
### Position and Offset
<figure class="right_image">
<img
width="300"
src="{{ page.root }}//static/hud_diagram_center.svg"
alt="Diagram showing a centered text element">
</figure>
Screens come in a variety of different physical sizes and resolutions, and
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 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.
<div style="clear:both;"></div>
### Alignment
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 aligned to the left, centre, or right.
<figure>
<img
width="500"
src="{{ page.root }}//static/hud_diagram_alignment.svg"
alt="Diagram showing alignment">
</figure>
The above diagram shows 3 windows (blue), each with a single HUD element (yellow)
and a different alignment each time. The arrow is the result of the position
and offset calculation.
### Scoreboard
For the purposes of this chapter, you will learn how to position and update a
score panel like so:
<figure>
<img
src="{{ page.root }}//static/hud_final.png"
alt="screenshot of the HUD we're aiming for">
</figure>
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.
## Text Elements
You can create a HUD element once you have a copy of the player object:
```lua
local player = minetest.get_player_by_name("username")
local idx = player:hud_add({
hud_elem_type = "text",
position = {x = 0.5, y = 0.5},
offset = {x = 0, y = 0},
text = "Hello world!",
alignment = {x = 0, y = 0}, -- center aligned
scale = {x = 100, y = 100}, -- covered later
})
```
The `hud_add` function returns an element ID - this can be used later to modify
or remove a HUD element.
### Parameters
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, e.g.: `{x=100, y=100}`.
`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:
```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},
offset = {x = -120, y = -25},
text = "Stats",
alignment = 0,
scale = { x = 100, y = 30},
number = 0xFFFFFF,
})
player:hud_add({
hud_elem_type = "text",
position = {x = 1, y = 0.5},
offset = {x = -180, y = 0},
text = digs_text,
alignment = -1,
scale = { x = 50, y = 10},
number = 0xFFFFFF,
})
player:hud_add({
hud_elem_type = "text",
position = {x = 1, y = 0.5},
offset = {x = -70, y = 0},
text = places_text,
alignment = -1,
scale = { x = 50, y = 10},
number = 0xFFFFFF,
})
```
This results in the following:
<figure>
<img
src="{{ page.root }}//static/hud_text.png"
alt="screenshot of the HUD we're aiming for">
</figure>
## Image Elements
Image elements are created in a very similar way to text elements:
```lua
player:hud_add({
hud_elem_type = "image",
position = {x = 1, y = 0.5},
offset = {x = -220, y = 0},
text = "score_background.png",
scale = { x = 1, y = 1},
alignment = { x = 1, y = 0 },
})
```
You will now have this:
<figure>
<img
src="{{ page.root }}//static/hud_background_img.png"
alt="screenshot of the HUD so far">
</figure>
### Parameters
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, 2 being double the size, and so on.
However, if a co-ordinate is negative, it is a percentage of the screen size.
For example, `x=-100` is 100% of the width.
### Scale
Let's make the progress bar for our score panel as an example of scale:
```lua
local percent = tonumber(meta:get("score:score") or 0.2)
player:hud_add({
hud_elem_type = "image",
position = {x = 1, y = 0.5},
offset = {x = -215, y = 23},
text = "score_bar_empty.png",
scale = { x = 1, y = 1},
alignment = { x = 1, y = 0 },
})
player:hud_add({
hud_elem_type = "image",
position = {x = 1, y = 0.5},
offset = {x = -215, y = 23},
text = "score_bar_full.png",
scale = { x = percent, y = 1},
alignment = { x = 1, y = 0 },
})
```
We now have a HUD that looks like the one in the first post!
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 it or remove it later.
```lua
local idx = player:hud_add({
hud_elem_type = "text",
text = "Hello world!",
-- parameters removed for brevity
})
player:hud_change(idx, "text", "New Text")
player:hud_remove(idx)
```
The `hud_change` method takes the element ID, the property to change, and the new
value. The above call changes the `text` property from "Hello World" to "Test".
This means that doing the `hud_change` immediately after the `hud_add` is
functionally equivalent to the following, in a rather inefficient way:
```lua
local idx = player:hud_add({
hud_elem_type = "text",
text = "New Text",
})
```
## Storing IDs
```lua
score = {}
local saved_huds = {}
function score.update_hud(player)
local player_name = player:get_player_name()
-- 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
player:hud_change(ids["places"], "text", places_text)
player:hud_change(ids["digs"], "text", digs_text)
player:hud_change(ids["bar_foreground"],
"scale", { x = percent, y = 1 })
else
ids = {}
saved_huds[player_name] = ids
-- create HUD elements and set ids into `ids`
end
end
minetest.register_on_joinplayer(score.update_hud)
minetest.register_on_leaveplayer(function(player)
saved_huds[player:get_player_name()] = nil
end)
```
## Other Elements
Read [lua_api.txt]({{ page.root }}/lua_api.html#hud-element-types) for a complete list of HUD elements.

View File

@ -0,0 +1,76 @@
---
title: Player Physics
layout: default
root: ../..
idx: 4.4
redirect_from: /en/chapters/player_physics.html
---
## Introduction <!-- omit in toc -->
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)
- [Old Movement Behaviour](#old-movement-behaviour)
- [Mod Incompatibility](#mod-incompatibility)
- [Your Turn](#your-turn)
## Basic Example
Here is an example of how to add an antigravity command, which
puts the caller in low G:
```lua
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
-- (0.1 * 9.81)
})
end,
})
```
## Available 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:
* speed: multiplier to default walking speed value (default: 1)
* jump: multiplier to default jump value (default: 1)
* gravity: multiplier to default gravity value (default: 1)
* sneak: whether the player can sneak (default: true)
### Old Movement Behaviour
Player movement prior to the 0.4.16 release included the sneak glitch, which
allows various movement glitches, including the ability
to climb an 'elevator' made from a certain placement of nodes by sneaking
(pressing shift) and pressing space to ascend. Though the behaviour was
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)
## Mod Incompatibility
Please be warned that mods which override the same physics value of a player tend
to be incompatible with each other. When setting an override, it overwrites
any overrides that have been set before. This means that if multiple overrides set a
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 metres (1 metre is 1 node).
* **Space**: Make gravity decrease as the player gets higher.

138
_it/players/privileges.md Normal file
View File

@ -0,0 +1,138 @@
---
title: Privileges
layout: default
root: ../..
idx: 4.1
description: Registering privs.
redirect_from: /en/chapters/privileges.html
---
## Introduction <!-- omit in toc -->
Privileges, often called privs for short, give players the ability to perform
certain actions. Server owners can grant and revoke privileges to control
which abilities each player has.
- [When to use Privileges](#when-to-use-privileges)
- [Declaring Privileges](#declaring-privileges)
- [Checking for Privileges](#checking-for-privileges)
- [Getting and Setting Privileges](#getting-and-setting-privileges)
- [Adding Privileges to basic_privs](#adding-privileges-to-basicprivs)
## When to use Privileges
A privilege should give a player the ability to do something.
Privileges are **not** for indicating class or status.
**Good Privileges:**
* interact
* shout
* noclip
* fly
* kick
* ban
* vote
* worldedit
* area_admin - admin functions of one mod is ok
**Bad Privileges:**
* moderator
* admin
* elf
* dwarf
## Declaring Privileges
Use `register_privilege` to declare a new privilege:
```lua
minetest.register_privilege("vote", {
description = "Can vote on issues",
give_to_singleplayer = true
})
```
`give_to_singleplayer` defaults to true when not specified, so it isn't
actually needed in the above definition.
## Checking for Privileges
To quickly check whether a player has all the required privileges:
```lua
local has, missing = minetest.check_player_privs(player_or_name, {
interact = true,
vote = true })
```
In this example, `has` is true if the player has all the privileges needed.
If `has` is false, then `missing` will contain a key-value table
of the missing privileges.
```lua
local has, missing = minetest.check_player_privs(name, {
interact = true,
vote = true })
if has then
print("Player has all privs!")
else
print("Player is missing privs: " .. dump(missing))
end
```
If you don't need to check the missing privileges, you can put
`check_player_privs` directly into the if statement.
```lua
if not minetest.check_player_privs(name, { interact=true }) then
return false, "You need interact for this!"
end
```
## Getting and Setting Privileges
Player privileges can be accessed or modified regardless of the player
being online.
```lua
local privs = minetest.get_player_privs(name)
print(dump(privs))
privs.vote = true
minetest.set_player_privs(name, privs)
```
Privileges are always specified as a key-value table with the key being
the privilege name and the value being a boolean.
```lua
{
fly = true,
interact = true,
shout = true
}
```
## Adding Privileges to basic_privs
Players with the `basic_privs` privilege are able to grant and revoke a limited
set of privileges. It's common to give this privilege to moderators so that
they can grant and revoke `interact` and `shout`, but can't grant themselves or other
players privileges with greater potential for abuse such as `give` and `server`.
To add a privilege to `basic_privs`, and adjust which privileges your moderators can
grant and revoke from other players, you must change the `basic_privs` setting.
By default, `basic_privs` has the following value:
basic_privs = interact, shout
To add `vote`, update this to:
basic_privs = interact, shout, vote
This will allow players with `basic_privs` to grant and revoke the `vote` privilege.

242
_it/players/sfinv.md Normal file
View File

@ -0,0 +1,242 @@
---
title: "SFINV: Inventory Formspec"
layout: default
root: ../..
idx: 4.7
redirect_from: /en/chapters/sfinv.html
---
## Introduction <!-- omit in toc -->
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
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 in one formspec.
- [Registering a Page](#registering-a-page)
- [Receiving events](#receiving-events)
- [Conditionally showing to players](#conditionally-showing-to-players)
- [on_enter and on_leave callbacks](#onenter-and-onleave-callbacks)
- [Adding to an existing page](#adding-to-an-existing-page)
## 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:
```lua
sfinv.register_page("mymod:hello", {
title = "Hello!",
get = function(self, player, context)
return sfinv.make_formspec(player, context,
"label[0.1,0.1;Hello world!]", true)
end
})
```
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
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.
```lua
sfinv.register_page("myadmin:myadmin", {
title = "Tab",
get = function(self, player, context)
local players = {}
context.myadmin_players = players
-- Using an array to build a formspec is considerably faster
local formspec = {
"textlist[0.1,0.1;7.8,3;playerlist;"
}
-- Add all players to the text list, and to the players list
local is_first = true
for _ , player in pairs(minetest.get_connected_players()) do
local player_name = player:get_player_name()
players[#players + 1] = player_name
if not is_first then
formspec[#formspec + 1] = ","
end
formspec[#formspec + 1] =
minetest.formspec_escape(player_name)
is_first = false
end
formspec[#formspec + 1] = "]"
-- Add buttons
formspec[#formspec + 1] = "button[0.1,3.3;2,1;kick;Kick]"
formspec[#formspec + 1] = "button[2.1,3.3;2,1;ban;Kick + Ban]"
-- Wrap the formspec in sfinv's layout
-- (ie: adds the tabs and background)
return sfinv.make_formspec(player, context,
table.concat(formspec, ""), false)
end,
})
```
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">
</figure>
## Receiving events
You can receive formspec events by adding a `on_player_receive_fields` function
to a sfinv definition.
```lua
on_player_receive_fields = function(self, player, context, fields)
-- TODO: implement this
end,
```
`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:
```lua
on_player_receive_fields = function(self, player, context, fields)
-- text list event, check event type and set index if selection changed
if fields.playerlist then
local event = minetest.explode_textlist_event(fields.playerlist)
if event.type == "CHG" then
context.myadmin_selected_idx = event.index
end
-- Kick button was pressed
elseif fields.kick then
local player_name =
context.myadmin_players[context.myadmin_selected_idx]
if player_name then
minetest.chat_send_player(player:get_player_name(),
"Kicked " .. player_name)
minetest.kick_player(player_name)
end
-- Ban button was pressed
elseif fields.ban then
local player_name =
context.myadmin_players[context.myadmin_selected_idx]
if player_name then
minetest.chat_send_player(player:get_player_name(),
"Banned " .. player_name)
minetest.ban_player(player_name)
minetest.kick_player(player_name, "Banned")
end
end
end,
```
There's a rather large problem with this, however. Anyone can kick or ban players! You
need a way to only show this to players with the kick or ban privileges.
Luckily SFINV allows you to do this!
## Conditionally showing to players
You can add an `is_in_nav` function to your page's definition if you'd like to
control when the page is shown:
```lua
is_in_nav = function(self, player, context)
local privs = minetest.get_player_privs(player:get_player_name())
return privs.kick or privs.ban
end,
```
If you only need to check one priv or want to perform an 'and', you should use
`minetest.check_player_privs()` instead of `get_player_privs`.
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 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,
we need to do that whenever kick or ban is granted or revoked to a player:
```lua
local function on_grant_revoke(grantee, granter, priv)
if priv ~= "kick" and priv ~= "ban" then
return
end
local player = minetest.get_player_by_name(grantee)
if not player then
return
end
local context = sfinv.get_or_create_context(player)
if context.page ~= "myadmin:myadmin" then
return
end
sfinv.set_player_inventory_formspec(player, context)
end
minetest.register_on_priv_grant(on_grant_revoke)
minetest.register_on_priv_revoke(on_grant_revoke)
```
## on_enter and on_leave callbacks
A player *enters* a tab when the tab is selected and *leaves* a
tab when another tab is about to be selected.
It's possible for multiple pages to be selected if a custom theme is
used.
Note that these events may not be triggered by the player.
The player may not even have the formspec open at that time.
For example, on_enter is called for the home page when a player
joins the game even before they open their inventory.
It's not possible to cancel a page change, as that would potentially
confuse the player.
```lua
on_enter = function(self, player, context)
end,
on_leave = function(self, player, context)
end,
```
## Adding to an existing page
To add content to an existing page, you will need to override the page
and modify the returned formspec.
```lua
local old_func = sfinv.registered_pages["sfinv:crafting"].get
sfinv.override_page("sfinv:crafting", {
get = function(self, player, context, ...)
local ret = old_func(self, player, context, ...)
if type(ret) == "table" then
ret.formspec = ret.formspec .. "label[0,0;Hello]"
else
-- Backwards compatibility
ret = ret .. "label[0,0;Hello]"
end
return ret
end
})
```

253
_it/quality/clean_arch.md Normal file
View File

@ -0,0 +1,253 @@
---
title: Intro to Clean Architectures
layout: default
root: ../..
idx: 8.4
---
## Introduction <!-- omit in toc -->
Once your mod reaches a respectable size, you'll find it harder and harder to
keep the code clean and free of bugs. This is an especially big problem when using
a dynamically typed language like Lua, given that the compiler gives you very little
compiler-time help when it comes to things like making sure that types are used correctly.
This chapter covers important concepts needed to keep your code clean,
and common design patterns to achieve that. Please note that this chapter isn't
meant to be prescriptive, but to instead give you an idea of the possibilities.
There is no one good way of designing a mod, and good mod design is very subjective.
- [Cohesion, Coupling, and Separation of Concerns](#cohesion-coupling-and-separation-of-concerns)
- [Observer](#observer)
- [Model-View-Controller](#model-view-controller)
- [API-View](#api-view)
- [Conclusion](#conclusion)
## Cohesion, Coupling, and Separation of Concerns
Without any planning, a programming project will tend to gradually descend into
spaghetti code. Spaghetti code is characterised by a lack of structure - all the
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. <!-- Weird wording? -->
> Inside every large program, there is a small program trying to get out.
>
> --C.A.R. Hoare
This should be done in such a way that you achieve Separation of Concerns -
each area should be distinct and address a separate need or concern.
These programs/areas should have the following two properties:
* **High Cohesion** - the area should be closely/tightly related.
* **Low Coupling** - keep dependencies between areas as low as possible, and avoid
relying on internal implementations. It's a very good idea to make sure you have
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.
## Observer
A simple way to separate different areas of code is to use the Observer pattern.
Let's take the example of unlocking an achievement when a player first kills a
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 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
mymobs.registered_on_death = {}
function mymobs.register_on_death(func)
table.insert(mymobs.registered_on_death, func)
end
-- in mob death code
for i=1, #mymobs.registered_on_death do
mymobs.registered_on_death[i](entity, reason)
end
```
Then the other code registers its interest:
```lua
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)
end
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
what is listening to something.
## 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
(calculations, what should be done) from API calls (`minetest.*`, other mods)
as much as possible.
One way to do this is to think about:
* What **data** you have.
* What **actions** you can take with this data.
* How **events** (ie: formspec, punches, etc) trigger these actions, and how
these actions cause things to happen in the engine.
Let's take an example of a land protection mod. The data you have is the areas
and any associated metadata. Actions you can take are `create`, `edit`, or
`delete`. The events that trigger these actions are chat commands and formspec
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. 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.)
You should write your data representation using Pure Lua. "Pure" in this context
means that the functions could run outside of Minetest - none of the engine's
functions are called.
```lua
-- Data
function land.create(name, area_name)
land.lands[area_name] = {
name = area_name,
owner = name,
-- more stuff
}
end
function land.get_by_name(area_name)
return land.lands[area_name]
end
```
Your actions should also be pure, but calling other functions is more
acceptable than in the above.
```lua
-- Controller
function land.handle_create_submit(name, area_name)
-- process stuff
-- (ie: check for overlaps, check quotas, check permissions)
land.create(name, area_name)
end
function land.handle_creation_request(name)
-- This is a bad example, as explained later
land.show_create_formspec(name)
end
```
Your event handlers will have to interact with the Minetest API. You should keep
the number of calculations to a minimum, as you won't be able to test this area
very easily.
```lua
-- View
function land.show_create_formspec(name)
-- Note how there's no complex calculations here!
return [[
size[4,3]
label[1,0;This is an example]
field[0,1;3,1;area_name;]
button_exit[0,2;1,1;exit;Exit]
]]
end
minetest.register_chatcommand("/land", {
privs = { land = true },
func = function(name)
land.handle_creation_request(name)
end,
})
minetest.register_on_player_receive_fields(function(player,
formname, fields)
land.handle_create_submit(player:get_player_name(),
fields.area_name)
end)
```
The above is the Model-View-Controller pattern. The model is a collection of data
with minimal functions. The view is a collection of functions which listen to
events and pass it to the controller, and also receives calls from the controller to
do something with the Minetest API. The controller is where the decisions and
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 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.
<figure class="right_image">
<img
width="100%"
src="{{ page.root }}/static/mvc_diagram.svg"
alt="Diagram showing a centered text element">
</figure>
It is important that each area only communicates with its direct neighbours,
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 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 compromise is to reduce the mod into two
parts:
* **API** - This was the model and controller above. There should be no uses of
`minetest.` here.
* **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
follows this design. `api.lua` is almost all pure Lua functions handling the data
storage and controller-style calculations. `gui.lua` is the view for formspecs
and formspec submission, and `async_crafter.lua` is the view and controller for
a node formspec and node timers.
Separating the mod like this means that you can very easily test the API part,
as it doesn't use any Minetest APIs - as shown in the
[next chapter](unit_testing.html) and seen in the crafting mod.
## Conclusion
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.
I highly recommend reading the [Game Programming Patterns](http://gameprogrammingpatterns.com/)
book. It's freely available to [read online](http://gameprogrammingpatterns.com/contents.html)
and goes into much more detail on common programming patterns relevant to games.

View File

@ -0,0 +1,167 @@
---
title: Common Mistakes
layout: default
root: ../..
idx: 8.1
redirect_from: /en/chapters/common_mistakes.html
---
## Introduction <!-- omit in toc -->
This chapter details common mistakes, and how to avoid them.
- [Never Store ObjectRefs (ie: players or entities)](#never-store-objectrefs-ie-players-or-entities)
- [Don't Trust Formspec Submissions](#dont-trust-formspec-submissions)
- [Set ItemStacks After Changing Them](#set-itemstacks-after-changing-them)
## Never Store ObjectRefs (ie: players or entities)
If the object an ObjectRef represents is deleted - for example, if the player goes
offline or the entity is unloaded - then calling methods on that object
will result in a crash.
For example, don't do this:
```lua
minetest.register_on_joinplayer(function(player)
local function func()
local pos = player:get_pos() -- BAD!
-- `player` is stored then accessed later.
-- If the player leaves in that second, the server *will* crash.
end
minetest.after(1, func)
foobar[player:get_player_name()] = player
-- RISKY
-- It's not recommended to do this.
-- Use minetest.get_connected_players() and
-- minetest.get_player_by_name() instead.
end)
```
Do this instead:
```lua
minetest.register_on_joinplayer(function(player)
local function func(name)
-- Attempt to get the ref again
local player = minetest.get_player_by_name(name)
-- Check that the player is still online
if player then
-- Yay! This is fine
local pos = player:get_pos()
end
end
-- Pass the name into the function
minetest.after(1, func, player:get_player_name())
end)
```
## Don't Trust Formspec Submissions
Malicious clients can submit formspecs whenever they like, with
whatever content they like.
For example, the following code has a vulnerability which allows players to
give themselves moderator privileges:
```lua
local function show_formspec(name)
if not minetest.check_player_privs(name, { privs = true }) then
return false
end
minetest.show_formspec(name, "modman:modman", [[
size[3,2]
field[0,0;3,1;target;Name;]
button_exit[0,1;3,1;sub;Promote]
]])
return true
})
minetest.register_on_player_receive_fields(function(player,
formname, fields)
-- BAD! Missing privilege check here!
local privs = minetest.get_player_privs(fields.target)
privs.kick = true
privs.ban = true
minetest.set_player_privs(fields.target, privs)
return true
end)
```
Add a privilege check to solve this:
```lua
minetest.register_on_player_receive_fields(function(player,
formname, fields)
if not minetest.check_player_privs(name, { privs = true }) then
return false
end
-- code
end)
```
## Set ItemStacks After Changing Them
Have you noticed that it's simply called an `ItemStack` in the API, not an `ItemStackRef`,
similar to `InvRef`? This is because an `ItemStack` isn't a reference - it's a
copy. Stacks work on a copy of the data rather than the stack in the inventory.
This means that modifying a stack won't actually modify that stack in the inventory.
For example, don't do this:
```lua
local inv = player:get_inventory()
local stack = inv:get_stack("main", 1)
stack:get_meta():set_string("description", "Partially eaten")
-- BAD! Modification will be lost
```
Do this instead:
```lua
local inv = player:get_inventory()
local stack = inv:get_stack("main", 1)
stack:get_meta():set_string("description", "Partially eaten")
inv:set_stack("main", 1, stack)
-- Correct! Item stack is set
```
The behaviour of callbacks is slightly more complicated. Modifying an `ItemStack` you
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.
```lua
minetest.register_on_item_eat(function(hp_change, replace_with_item,
itemstack, user, pointed_thing)
itemstack:get_meta():set_string("description", "Partially eaten")
-- Almost correct! Data will be lost if another
-- callback cancels the behaviour
end)
```
If no callbacks cancel this, the stack will be set and the description will be updated,
but if a callback does cancel this, then the update may be lost.
It's better to do this instead:
```lua
minetest.register_on_item_eat(function(hp_change, replace_with_item,
itemstack, user, pointed_thing)
itemstack:get_meta():set_string("description", "Partially eaten")
user:get_inventory():set_stack("main", user:get_wield_index(),
itemstack)
-- Correct, description will always be set!
end)
```
If the callbacks cancel or the callback runner doesn't set the stack,
then the update will still be set.
If the callbacks or the callback runner set the stack, then the use of
set_stack doesn't matter.

152
_it/quality/luacheck.md Normal file
View File

@ -0,0 +1,152 @@
---
title: Automatic Error Checking
layout: default
root: ../..
idx: 8.2
description: Use LuaCheck to find errors
redirect_from: /en/chapters/luacheck.html
---
## Introduction <!-- omit in toc -->
In this chapter, you will learn how to use a tool called LuaCheck to automatically
scan your mod for any mistakes. This tool can be used in combination with your
editor to provide alerts to any mistakes.
- [Installing LuaCheck](#installing-luacheck)
- [Windows](#windows)
- [Linux](#linux)
- [Running LuaCheck](#running-luacheck)
- [Configuring LuaCheck](#configuring-luacheck)
- [Troubleshooting](#troubleshooting)
- [Using with editor](#using-with-editor)
- [Checking Commits with Travis](#checking-commits-with-travis)
## Installing LuaCheck
### Windows
Simply download luacheck.exe from
[the Github Releases page](https://github.com/mpeterv/luacheck/releases).
### Linux
First, you'll need to install LuaRocks:
sudo apt install luarocks
You can then install LuaCheck globally:
sudo luarocks install luacheck
Check that it's installed with the following command:
luacheck -v
## Running LuaCheck
The first time you run LuaCheck, it will probably pick up a lot of false
errors. This is because it still needs to be configured.
On Windows, open powershell or bash in the root folder of your project
and run `path\to\luacheck.exe .`
On Linux, run `luacheck .` whilst in the root folder of your project.
## Configuring LuaCheck
Create a file called .luacheckrc in the root of your project. This could be the
root of your game, modpack, or mod.
Put the following contents in it:
```lua
unused_args = false
allow_defined_top = true
globals = {
"minetest",
}
read_globals = {
string = {fields = {"split"}},
table = {fields = {"copy", "getn"}},
-- Builtin
"vector", "ItemStack",
"dump", "DIR_DELIM", "VoxelArea", "Settings",
-- MTG
"default", "sfinv", "creative",
}
```
Next, you'll need to test that it works by running LuaCheck. You should get a lot
fewer errors this time. Starting at the first error you get, modify the code to
remove the issue, or modify the configuration if the code is correct. See the list
below.
### Troubleshooting
* **accessing undefined variable foobar** - If `foobar` is meant to be a global,
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,
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`, or stop writing to foobar.
## Using with editor
It is highly recommended that you find and install a plugin for your editor of choice
to show you errors without running a command. Most editors will likely have a plugin
available.
* **Atom** - `linter-luacheck`.
* **VSCode** - Ctrl+P, then paste: `ext install dwenegar.vscode-luacheck`
* **Sublime** - Install using package-control:
[SublimeLinter](https://github.com/SublimeLinter/SublimeLinter),
[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 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.
First, you should visit [travis-ci.org](https://travis-ci.org/) and sign in with
your Github account. Then find your project's repo in your Travis profile,
and enable Travis by flipping the switch.
Next, create a file called .travis.yml with the following content:
```yml
language: generic
sudo: false
addons:
apt:
packages:
- luarocks
before_install:
- luarocks install --local luacheck
script:
- $HOME/.luarocks/bin/luacheck .
notifications:
email: false
```
If your project is a game rather than a mod or mod pack,
change the line after `script:` to:
```yml
- $HOME/.luarocks/bin/luacheck mods/
```
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.
After awhile 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.

28
_it/quality/readmore.md Normal file
View File

@ -0,0 +1,28 @@
---
title: Read More
layout: default
root: ../..
idx: 8.7
redirect_from: /en/chapters/readmore.html
---
## List of Resources
After you've read this book, take a look at the following.
### Minetest Modding
* Minetest's Lua API Reference - [HTML version]({{ page.root }}/lua_api.html) |
[Text version](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt).
* Explore the [Developer Wiki](http://dev.minetest.net/Main_Page).
* Look at [existing mods](https://forum.minetest.net/viewforum.php?f=11).
### Lua Programming
* [Programming in Lua (PIL)](http://www.lua.org/pil/).
* [Lua Crash Course](http://luatut.com/crash_course.html).
### 3D Modelling
* [Blender 3D: Noob to pro](https://en.wikibooks.org/wiki/Blender_3D:_Noob_to_Pro).
* [Using Blender with Minetest](http://wiki.minetest.net/Using_Blender).

214
_it/quality/releasing.md Normal file
View File

@ -0,0 +1,214 @@
---
title: Releasing a Mod
layout: default
root: ../..
idx: 8.6
redirect_from: /en/chapters/releasing.html
---
## Introduction <!-- omit in toc -->
Releasing, or publishing, a mod allows other people to make use of it. Once a mod has been
released it might be used in singleplayer games or on servers, including public servers.
- [License Choices](#license-choices)
- [LGPL and CC-BY-SA](#lgpl-and-cc-by-sa)
- [CC0](#cc0)
- [MIT](#mit)
- [Packaging](#packaging)
- [README.txt](#readmetxt)
- [description.txt](#descriptiontxt)
- [screenshot.png](#screenshotpng)
- [Uploading](#uploading)
- [Version Control Systems](#version-control-systems)
- [Forum Attachments](#forum-attachments)
- [Forum Topic](#forum-topic)
- [Subject](#subject)
## License Choices
You need to specify a license for your mod. This is important because it tells other
people the ways in which they are allowed to use your work. If your mod doesn't have
a license, people won't know whether they are allowed to modify, distribute or use your
mod on a public server.
Your code and your art need different things from the licenses they use. For example,
Creative Commons licenses shouldn't be used with source code,
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 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.
### 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 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.
### CC0
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
choose not to use your mod if it has this license.
### MIT
This is a common license for mod code. The only restriction it places on users
of your mod is that they must include the same copyright notice and license
in any copies of the mod or of substantial parts of the mod.
## Packaging
There are some files that are recommended to include in your mod
before you release it.
### README.txt
The README file should state:
* What the mod does.
* What the license is.
* What dependencies there are.
* How to install the mod.
* Current version of the mod.
* Optionally, the where to report problems or get help.
### description.txt
This should explain what your mod does. Be concise without being vague.
It should be short because it will be displayed in the content installer which has
limited space.
Good example:
Adds soup, cakes, bakes and juices.
Avoid this:
(BAD) The food mod for Minetest.
### screenshot.png
Screenshots should be 3:2 (3 pixels of width for every 2 pixels of height)
and have a minimum size of 300 x 200px.
The screenshot is displayed in the mod store.
## Uploading
So that a potential user can download your mod, you need to upload it somewhere
publicly accessible. There are several ways to do this, but you should use the
approach that works best for you, as long as it meets these requirements, and any
others which may be added by forum moderators:
* **Stable** - The hosting website should be unlikely to shut down without warning.
* **Direct link** - You should be able to click a link on the forum and download the file
without having to view another page.
* **Virus Free** - Mods with malicious content will be removed from the forum.
### Version Control Systems
It is recommended that you use a version control system which:
* Allows other developers to easily submit changes.
* Allows the code to be previewed before downloading.
* Allows users to submit bug reports.
The majority of Minetest modders use GitHub as a website to host their code,
but alternatives are possible.
Using a GitHub can be difficult at first. If you need help with this, for
information on using GitHub, please see:
* [Pro Git book](http://git-scm.com/book/en/v1/Getting-Started) - Free to read online.
* [GitHub for Windows app](https://help.github.com/articles/getting-started-with-github-for-windows/) -
Using a graphical interface on Windows to upload your code.
### Forum Attachments
As an alternative to using a version management system, you can use forum attachments to share
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.
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.
Click "Browse" and select the zipped file. It is recommended that you
enter the version of your mod in the comment field.
<figure>
<img src="{{ page.root }}/static/releasing_attachments.png" alt="Upload Attachment">
<figcaption>
Upload Attachment tab.
</figcaption>
</figure>
## Forum Topic
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 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."
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:
Adds magic, rainbows and other special things.
See download attached.
[b]Version:[/b] 1.1
[b]License:[/b] LGPL 2.1 or later
Dependencies: default mod (found in minetest_game)
Report bugs or request help on the forum topic.
[h]Installation[/h]
Unzip the archive, rename the folder to superspecial and
place it in minetest/mods/
( GNU/Linux: If you use a system-wide installation place
it in ~/.minetest/mods/. )
( If you only want this to be used in a single world, place
the folder in worldmods/ in your world directory. )
For further information or help see:
[url]https://wiki.minetest.net/Installing_Mods[/url]
If you modify the above example for your mod topic, remember to
change "superspecial" to the name of your mod.
### Subject
The subject of topic must be in one of these formats:
* [Mod] Mod Title [modname]
* [Mod] Mod Title [version number] [modname]
For example:
* [Mod] More Blox [0.1] [moreblox]

110
_it/quality/security.md Normal file
View File

@ -0,0 +1,110 @@
---
title: Security
layout: default
root: ../..
idx: 8.3
---
## Introduction <!-- omit in toc -->
Security is very important in making sure that your mod doesn't cause the server
owner to lose data or control.
- [Core Concepts](#core-concepts)
- [Formspecs](#formspecs)
- [Never Trust Submissions](#never-trust-submissions)
- [Time of Check isn't Time of Use](#time-of-check-isnt-time-of-use)
- [(Insecure) Environments](#insecure-environments)
## Core Concepts
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 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 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.
## Formspecs
### Never Trust Submissions
Any users can submit almost any formspec with any values at any time.
Here's some real code found in a mod:
```lua
minetest.register_on_player_receive_fields(function(player,
formname, fields)
for key, field in pairs(fields) do
local x,y,z = string.match(key,
"goto_([%d-]+)_([%d-]+)_([%d-]+)")
if x and y and z then
player:set_pos({ x=tonumber(x), y=tonumber(y),
z=tonumber(z) })
return true
end
end
end
```
Can you spot the problem? A malicious user could submit a formspec containing
their own position values, allowing them to teleport to anywhere they wish to.
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 previously in
the Formspecs chapter.
### Time of Check isn't Time of Use
Any users can submit any formspec with any values at any time, except where the
engine forbids it:
* A node formspec submission will be blocked if the user is too far away.
* 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, 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).
## (Insecure) Environments
Minetest allows mods to request an unsandboxed environment, giving them access
to the full Lua API.
Can you spot the vulnerability in the following?
```lua
local ie = minetest.request_insecure_environment()
ie.os.execute(("path/to/prog %d"):format(3))
```
`string.format` is a function in the global shared table `string`.
A malicious mod could override this function and pass stuff to os.execute:
```lua
string.format = function()
return "xdg-open 'http://example.com'"
end
```
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:
* Always store it in a local and never pass it into a function.
* Make sure you can trust any input given to an insecure function, to avoid the
issue above. This means avoiding globally redefinable functions.

195
_it/quality/unit_testing.md Normal file
View File

@ -0,0 +1,195 @@
---
title: Automatic Unit Testing
layout: default
root: ../..
idx: 8.5
---
## Introduction <!-- omit in toc -->
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
functions is quite difficult, but luckily [in the previous chapter](clean_arch.html),
we discussed how to structure your code avoid this.
- [Installing Busted](#installing-busted)
- [Your First Test](#your-first-test)
- [init.lua](#initlua)
- [api.lua](#apilua)
- [tests/api_spec.lua](#testsapispeclua)
- [Mocking: Using External Functions](#mocking-using-external-functions)
- [Checking Commits with Travis](#checking-commits-with-travis)
- [Conclusion](#conclusion)
## Installing Busted
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 install Busted globally:
sudo luarocks install busted
Finally, check that it is installed:
busted --version
## Your First Test
Busted is Lua's leading unit test framework. Busted looks for Lua files with
names ending in `_spec`, and then executes them in a standalone Lua environment.
mymod/
├── init.lua
├── api.lua
└── tests
└── api_spec.lua
### init.lua
```lua
mymod = {}
dofile(minetest.get_modpath("mymod") .. "/api.lua")
```
### api.lua
```lua
function mymod.add(x, y)
return x + y
end
```
### tests/api_spec.lua
```lua
-- Look for required things in
package.path = "../?.lua;" .. package.path
-- Set mymod global for API to write into
_G.mymod = {} --_
-- Run api.lua file
require("api")
-- Tests
describe("add", function()
it("adds", function()
assert.equals(2, mymod.add(1, 1))
end)
it("supports negatives", function()
assert.equals(0, mymod.add(-1, 1))
assert.equals(-2, mymod.add(-1, -1))
end)
end)
```
You can now run the tests by opening a terminal in the mod's directory and
running `busted .`
It's important that the API file doesn't create the table itself, as globals in
Busted work differently. Any variable which would be global in Minetest is instead
a file local in busted. This would have been a better way for Minetest to do things,
but it's too late for that now.
Another thing to note is that any files you're testing should avoid calls to any
functions not inside of it. You tend to only write tests for a single file at once.
## Mocking: Using External Functions
Mocking is the practice of replacing functions that the thing you're testing depends
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,
you'll already have a pretty clean file to test. You will still have to mock
things not in your area, however - for example, you'll have to mock the view when
testing the controller/API. If you didn't follow the advice, then things are a
little harder as you may have to mock the Minetest API.
```lua
-- As above, make a table
_G.minetest = {}
-- Define the mock function
local chat_send_all_calls = {}
function minetest.chat_send_all(name, message)
table.insert(chat_send_all_calls, { name = name, message = message })
end
-- Tests
describe("list_areas", function()
it("returns a line for each area", function()
chat_send_all_calls = {} -- reset table
mymod.list_areas_to_chat("singleplayer", "singleplayer")
assert.equals(2, #chat_send_all_calls)
end)
it("sends to right player", function()
chat_send_all_calls = {} -- reset table
mymod.list_areas_to_chat("singleplayer", "singleplayer")
for _, call in pairs(chat_send_all_calls) do --_
assert.equals("singleplayer", call.name)
end
end)
-- The above two tests are actually pointless,
-- as this one tests both things
it("returns correct thing", function()
chat_send_all_calls = {} -- reset table
mymod.list_areas_to_chat("singleplayer", "singleplayer")
local expected = {
{ name = "singleplayer", message = "Town Hall (2,43,63)" },
{ name = "singleplayer", message = "Airport (43,45,63)" },
}
assert.same(expected, chat_send_all_calls)
end)
end)
```
## Checking Commits with Travis
The Travis script from the [Automatic Error Checking](luacheck.html)
chapter can be modified to also run Busted.
```yml
language: generic
sudo: false
addons:
apt:
packages:
- luarocks
before_install:
- luarocks install --local luacheck && luarocks install --local busted
script:
- $HOME/.luarocks/bin/luacheck .
- $HOME/.luarocks/bin/busted .
notifications:
email: false
```
## Conclusion
Unit tests will greatly increase the quality and reliability of your project if used
well, but they require you to structure your code in a different way than usual.
For an example of a mod with lots of unit tests, see
[crafting by rubenwardy](https://github.com/rubenwardy/crafting).

View File

@ -22,6 +22,8 @@ layout: compress
{% if language == "en" %}
{% assign links = site.en | sort: "idx" %}
{% else if language == "it" %}
{% assign links = site.it | sort: "idx" %}
{% else %}
{% assign language = "en" %}
{% assign links = site.en | sort: "idx" %}