2014-12-14 22:15:36 +03:00
|
|
|
---
|
2020-05-05 00:10:23 +03:00
|
|
|
title: GUIs (Formspecs)
|
2014-12-14 22:15:36 +03:00
|
|
|
layout: default
|
2018-07-15 21:36:35 +03:00
|
|
|
root: ../..
|
2018-07-15 17:28:10 +03:00
|
|
|
idx: 4.5
|
2020-07-14 06:04:38 +03:00
|
|
|
description: Learn how to display GUIs using formspecs
|
2018-07-15 21:13:16 +03:00
|
|
|
redirect_from: /en/chapters/formspecs.html
|
2018-07-20 22:51:31 +03:00
|
|
|
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.
|
2014-12-14 22:15:36 +03:00
|
|
|
---
|
|
|
|
|
2019-05-31 20:32:40 +03:00
|
|
|
## Introduction <!-- omit in toc -->
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2014-12-17 22:45:32 +03:00
|
|
|
<figure class="right_image">
|
2018-07-15 21:36:35 +03:00
|
|
|
<img src="{{ page.root }}//static/formspec_example.png" alt="Furnace Inventory">
|
2017-08-26 21:01:51 +03:00
|
|
|
<figcaption>
|
|
|
|
Screenshot of furnace formspec, labelled.
|
|
|
|
</figcaption>
|
2014-12-17 22:45:32 +03:00
|
|
|
</figure>
|
|
|
|
|
2014-12-14 22:15:36 +03:00
|
|
|
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.
|
2019-08-14 02:50:30 +03:00
|
|
|
In Minetest, forms are windows such as the player inventory and can contain a
|
|
|
|
variety of elements, such as labels, buttons and fields.
|
2017-10-26 01:28:23 +03:00
|
|
|
|
|
|
|
Note that if you do not need to get user input, for example when you only need
|
2019-08-14 02:50:30 +03:00
|
|
|
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.
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
- [Real or Legacy Coordinates](#real-or-legacy-coordinates)
|
|
|
|
- [Anatomy of a Formspec](#anatomy-of-a-formspec)
|
2022-07-31 22:33:16 +03:00
|
|
|
- [Elements](#elements)
|
|
|
|
- [Header](#header)
|
2019-08-14 02:50:30 +03:00
|
|
|
- [Guessing Game](#guessing-game)
|
2022-07-31 22:33:16 +03:00
|
|
|
- [Padding and Spacing](#padding-and-spacing)
|
|
|
|
- [Receiving Formspec Submissions](#receiving-formspec-submissions)
|
|
|
|
- [Contexts](#contexts)
|
2019-08-14 02:50:30 +03:00
|
|
|
- [Formspec Sources](#formspec-sources)
|
2022-07-31 22:33:16 +03:00
|
|
|
- [Node Meta Formspecs](#node-meta-formspecs)
|
|
|
|
- [Player Inventory Formspecs](#player-inventory-formspecs)
|
|
|
|
- [Your Turn](#your-turn)
|
2014-12-14 22:15:36 +03:00
|
|
|
|
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
## Real or Legacy Coordinates
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
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.
|
2015-09-25 02:23:03 +03:00
|
|
|
|
2020-12-24 16:25:31 +03:00
|
|
|
Using a formspec_version of 2 or above will enable real coordinates.
|
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
## Anatomy of a Formspec
|
|
|
|
|
|
|
|
### Elements
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
Formspec is a domain-specific language with an unusual format.
|
|
|
|
It consists of a number of elements with the following form:
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
type[param1;param2]
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
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:
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
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
|
2023-05-01 14:22:02 +03:00
|
|
|
[lua_api.md](https://minetest.gitlab.io/minetest/formspec/)
|
2020-07-14 06:04:38 +03:00
|
|
|
for a list of all possible elements.
|
2014-12-14 22:15:36 +03:00
|
|
|
|
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
### Header
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
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.
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
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,
|
2020-12-24 16:38:26 +03:00
|
|
|
documented in the Lua API reference.
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
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:
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2020-12-24 16:25:31 +03:00
|
|
|
formspec_version[4]
|
2019-08-14 02:50:30 +03:00
|
|
|
size[2,2]
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2020-05-05 00:10:23 +03:00
|
|
|
Notice how we explicitly defined the formspec language version.
|
|
|
|
Without this, the legacy system will instead be used instead - which will
|
2023-01-27 16:54:49 +03:00
|
|
|
prevent the use of consistent element positioning and other new features.
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
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:
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2020-12-24 16:25:31 +03:00
|
|
|
formspec_version[4]
|
2019-08-14 02:50:30 +03:00
|
|
|
size[2,2]
|
|
|
|
position[0,0.5]
|
|
|
|
anchor[0,0.5]
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
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.
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2014-12-17 22:45:32 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
## Guessing Game
|
2017-10-26 01:28:23 +03:00
|
|
|
|
2014-12-17 22:45:32 +03:00
|
|
|
<figure class="right_image">
|
2019-08-14 02:50:30 +03:00
|
|
|
<img src="{{ page.root }}/static/formspec_guessing.png" alt="Guessing Formspec">
|
2017-08-26 21:01:51 +03:00
|
|
|
<figcaption>
|
2019-08-14 02:50:30 +03:00
|
|
|
The guessing game formspec.
|
2017-08-26 21:01:51 +03:00
|
|
|
</figcaption>
|
2014-12-17 22:45:32 +03:00
|
|
|
</figure>
|
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
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>
|
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2019-08-14 02:50:30 +03:00
|
|
|
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 = {
|
2020-12-24 16:25:31 +03:00
|
|
|
"formspec_version[4]",
|
2019-08-14 02:50:30 +03:00
|
|
|
"size[6,3.476]",
|
|
|
|
"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
|
2018-09-19 14:04:51 +03:00
|
|
|
```
|
2014-12-17 22:45:32 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
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`:
|
2014-12-17 22:45:32 +03:00
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2019-08-14 02:50:30 +03:00
|
|
|
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,
|
|
|
|
})
|
2018-09-19 14:04:51 +03:00
|
|
|
```
|
2014-12-17 22:45:32 +03:00
|
|
|
|
2020-07-14 06:04:38 +03:00
|
|
|
The `show_formspec` function accepts a player name, the formspec name, and the
|
2019-08-14 02:50:30 +03:00
|
|
|
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
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
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:
|
2014-12-17 22:45:32 +03:00
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2019-08-14 02:50:30 +03:00
|
|
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|
|
|
if formname ~= "guessing:game" then
|
|
|
|
return
|
2017-08-26 21:01:51 +03:00
|
|
|
end
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
if fields.guess then
|
|
|
|
local pname = player:get_player_name()
|
|
|
|
minetest.chat_send_all(pname .. " guessed " .. fields.number)
|
2017-08-26 21:01:51 +03:00
|
|
|
end
|
2014-12-14 22:15:36 +03:00
|
|
|
end)
|
2018-09-19 14:04:51 +03:00
|
|
|
```
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2020-07-14 06:04:38 +03:00
|
|
|
The function given in `minetest.register_on_player_receive_fields` is called
|
2019-08-14 02:50:30 +03:00
|
|
|
every time a user submits a form. Most callbacks will need to check the formname given
|
2017-10-26 01:28:23 +03:00
|
|
|
to the function, and exit if it is not the right form; however, some callbacks
|
2019-08-14 02:50:30 +03:00
|
|
|
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
|
2023-01-27 16:54:49 +03:00
|
|
|
name, depending on the event. Some elements will only be submitted if they caused
|
|
|
|
the event, such as buttons, and some elements will always appear in submissions,
|
|
|
|
such as fields.
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2018-07-20 22:51:31 +03:00
|
|
|
{% include notice.html notice=page.submit_vuln %}
|
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
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".
|
2014-12-14 22:15:36 +03:00
|
|
|
|
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
### Contexts
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
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:
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2019-08-14 02:50:30 +03:00
|
|
|
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)
|
2018-09-19 14:04:51 +03:00
|
|
|
```
|
2014-12-17 22:45:32 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
Next, we need to modify the show code to update the context
|
|
|
|
before showing the formspec:
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
```lua
|
|
|
|
function guessing.show_to(name)
|
|
|
|
local context = get_context(name)
|
|
|
|
context.target = context.target or math.random(1, 10)
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
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:
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2019-08-14 02:50:30 +03:00
|
|
|
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
|
2020-07-14 06:04:38 +03:00
|
|
|
text = "Too high!"
|
2019-08-14 02:50:30 +03:00
|
|
|
else
|
2020-07-14 06:04:38 +03:00
|
|
|
text = "Too low!"
|
2017-08-26 21:01:51 +03:00
|
|
|
end
|
2019-08-14 02:50:30 +03:00
|
|
|
```
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2020-07-14 06:04:38 +03:00
|
|
|
Note that it's good practice for `get_formspec` to only read the context, and not
|
2019-08-14 02:50:30 +03:00
|
|
|
update it at all. This can make the function simpler, and also easier to test.
|
2014-12-17 22:45:32 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
And finally, we need to update the handler to update the context with the guess:
|
2014-12-17 22:45:32 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
```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
|
|
|
|
```
|
2014-12-14 22:15:36 +03:00
|
|
|
|
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
## Formspec Sources
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
There are three different ways that a formspec can be delivered to the client:
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2020-07-14 06:04:38 +03:00
|
|
|
* [show_formspec](#guessing-game): the method used above, fields are received by `register_on_player_receive_fields`.
|
2019-08-14 02:50:30 +03:00
|
|
|
* [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
|
2020-07-14 06:04:38 +03:00
|
|
|
`register_on_player_receive_fields`.
|
2014-12-14 23:04:14 +03:00
|
|
|
|
2019-08-14 02:50:30 +03:00
|
|
|
### Node Meta Formspecs
|
2014-12-14 23:04:14 +03:00
|
|
|
|
2020-07-14 06:04:38 +03:00
|
|
|
`minetest.show_formspec` is not the only way to show a formspec; you can also
|
2023-04-30 03:06:04 +03:00
|
|
|
add formspecs to a [node's metadata](../map/storage.html). For example,
|
2017-10-26 01:28:23 +03:00
|
|
|
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.
|
2014-12-14 23:04:14 +03:00
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2014-12-14 23:04:14 +03:00
|
|
|
minetest.register_node("mymod:rightclick", {
|
2017-08-26 21:01:51 +03:00
|
|
|
description = "Rightclick me!",
|
|
|
|
tiles = {"mymod_rightclick.png"},
|
|
|
|
groups = {cracky = 1},
|
|
|
|
after_place_node = function(pos, placer)
|
2020-05-05 00:10:23 +03:00
|
|
|
-- This function is run when the chest node is placed.
|
2017-08-26 21:01:51 +03:00
|
|
|
-- 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",
|
2020-12-24 16:25:31 +03:00
|
|
|
"formspec_version[4]" ..
|
2020-05-05 00:10:23 +03:00
|
|
|
"size[5,5]" ..
|
|
|
|
"label[1,1;This is shown on right click]" ..
|
2015-06-28 06:39:02 +03:00
|
|
|
"field[1,2;2,1;x;x;]")
|
2017-08-26 21:01:51 +03:00
|
|
|
end,
|
2015-06-28 06:39:02 +03:00
|
|
|
on_receive_fields = function(pos, formname, fields, player)
|
2020-05-05 00:10:23 +03:00
|
|
|
if fields.quit then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2015-06-28 06:39:02 +03:00
|
|
|
print(fields.x)
|
|
|
|
end
|
2014-12-14 23:04:14 +03:00
|
|
|
})
|
2018-09-19 14:04:51 +03:00
|
|
|
```
|
2014-12-14 23:04:14 +03:00
|
|
|
|
2015-06-28 06:39:02 +03:00
|
|
|
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.
|
|
|
|
|
2017-10-26 01:28:23 +03:00
|
|
|
This style of callback triggers when you press enter
|
|
|
|
in a field, which is impossible with `minetest.show_formspec`;
|
2015-06-28 06:39:02 +03:00
|
|
|
however, this kind of form can only be shown by right-clicking on a
|
|
|
|
node. It cannot be triggered programmatically.
|
2019-08-14 02:50:30 +03:00
|
|
|
|
|
|
|
### 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 `""`.
|
|
|
|
|
2022-07-31 22:33:16 +03:00
|
|
|
There are a number of different mods which allow multiple mods to customise the
|
|
|
|
player inventory. Minetest Game uses
|
|
|
|
[SFINV](https://github.com/rubenwardy/sfinv/blob/master/Tutorial.md).
|
2019-08-14 02:50:30 +03:00
|
|
|
|
|
|
|
|
|
|
|
### 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.
|