2014-12-14 22:15:36 +03:00
|
|
|
---
|
|
|
|
title: Formspecs
|
|
|
|
layout: default
|
2018-07-15 21:36:35 +03:00
|
|
|
root: ../..
|
2018-07-15 17:28:10 +03:00
|
|
|
idx: 4.5
|
2018-07-15 21:13:16 +03:00
|
|
|
redirect_from: /en/chapters/formspecs.html
|
2014-12-14 22:15:36 +03:00
|
|
|
---
|
|
|
|
|
2015-02-22 13:28:37 +03:00
|
|
|
## Introduction
|
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.
|
2017-10-26 01:28:23 +03:00
|
|
|
In Minetest, forms are windows such as the player inventory, which can contain labels,
|
|
|
|
buttons and fields to allow you to enter information.
|
|
|
|
|
|
|
|
* [Formspec Syntax](#formspec-syntax)
|
|
|
|
* [Displaying Forms](#displaying-forms)
|
|
|
|
* [Callbacks](#callbacks)
|
|
|
|
* [Contexts](#contexts)
|
|
|
|
* [Node Meta Formspecs](#node-meta-formspecs)
|
|
|
|
|
|
|
|
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) elements instead of forms, because unexpected windows tend to disrupt gameplay.
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2015-02-22 13:28:37 +03:00
|
|
|
## Formspec Syntax
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2017-10-26 01:28:23 +03:00
|
|
|
Formspecs have an unusual syntax.
|
2014-12-14 22:15:36 +03:00
|
|
|
They consist of a series of tags which are in the following form:
|
|
|
|
|
2017-08-26 21:01:51 +03:00
|
|
|
element_type[param1;param2;...]
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2015-09-25 02:23:03 +03:00
|
|
|
Firstly the element type is declared, and then the attributes are given
|
|
|
|
in square brackets.
|
|
|
|
|
2017-10-26 01:28:23 +03:00
|
|
|
Elements are items such as text boxes or buttons, or can be metadata such
|
|
|
|
as size or background.
|
2015-09-25 02:23:03 +03:00
|
|
|
|
|
|
|
Here are two elements, of types foo and bar.
|
|
|
|
|
2017-08-26 21:01:51 +03:00
|
|
|
foo[param1]bar[param1]
|
2014-12-14 22:15:36 +03:00
|
|
|
|
|
|
|
### Size[w, h]
|
|
|
|
|
2017-10-26 01:28:23 +03:00
|
|
|
Nearly all forms have a size tag. This declares the size of the form window. Note that
|
|
|
|
**forms don't use pixels as co-ordinates; they use a grid based on inventories**.
|
2014-12-14 22:15:36 +03:00
|
|
|
A size of (1, 1) means the form is big enough to host a 1x1 inventory.
|
2017-10-26 01:28:23 +03:00
|
|
|
This means the size of the form is independent of screen resolution and it should work
|
|
|
|
just as well on large screens as small screens.
|
2014-12-14 22:15:36 +03:00
|
|
|
You can use decimals in sizes and co-ordinates.
|
|
|
|
|
2017-08-26 21:01:51 +03:00
|
|
|
size[5,2]
|
2014-12-14 22:15:36 +03:00
|
|
|
|
|
|
|
Co-ordinates and sizes only use one attribute.
|
|
|
|
The x and y values are separated by a comma, as you can see above.
|
|
|
|
|
|
|
|
### Field[x, y; w, h; name; label; default]
|
|
|
|
|
2014-12-17 22:45:32 +03:00
|
|
|
This is a textbox element. Most other elements have a similar style of attributes.
|
2017-10-26 01:28:23 +03:00
|
|
|
The name attribute is used in callbacks to get the submitted information.
|
|
|
|
The x and y attributes determine the position of the element, and
|
|
|
|
the w and h attributes provide the size.
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2017-08-26 21:01:51 +03:00
|
|
|
field[1,1;3,1;firstname;Firstname;]
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2017-10-26 01:28:23 +03:00
|
|
|
It is perfectly valid to not define an attribute.
|
2014-12-14 22:15:36 +03:00
|
|
|
|
|
|
|
### Other Elements
|
|
|
|
|
2017-10-26 01:28:23 +03:00
|
|
|
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.
|
|
|
|
At the time of writing, formspec information begins on line 1765.
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2015-02-22 13:28:37 +03:00
|
|
|
## Displaying Formspecs
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2017-10-26 01:28:23 +03:00
|
|
|
Here is a generalized way to show a formspec:
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2017-08-26 21:01:51 +03:00
|
|
|
minetest.show_formspec(playername, formname, formspec)
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2017-10-26 01:28:23 +03:00
|
|
|
Formnames should be itemnames; however, this is not enforced.
|
|
|
|
There is no need to override a formspec, because formspecs are not registered like
|
|
|
|
nodes and items are. The formspec code is sent to the player's client for them
|
2014-12-14 22:15:36 +03:00
|
|
|
to see, along with the formname.
|
|
|
|
Formnames are used in callbacks to identify which form has been submitted,
|
2017-10-26 01:28:23 +03:00
|
|
|
and to see if the callback is relevant.
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2014-12-17 22:45:32 +03:00
|
|
|
### Example
|
|
|
|
|
2017-10-26 01:28:23 +03:00
|
|
|
This example shows a formspec to a player when they use the /formspec command.
|
|
|
|
|
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_name.png" alt="Name Formspec">
|
2017-08-26 21:01:51 +03:00
|
|
|
<figcaption>
|
|
|
|
The formspec generated by<br />
|
|
|
|
the example's code
|
|
|
|
</figcaption>
|
2014-12-17 22:45:32 +03:00
|
|
|
</figure>
|
|
|
|
|
|
|
|
{% highlight lua %}
|
|
|
|
-- Show form when the /formspec command is used.
|
|
|
|
minetest.register_chatcommand("formspec", {
|
2017-08-26 21:01:51 +03:00
|
|
|
func = function(name, param)
|
|
|
|
minetest.show_formspec(name, "mymod:form",
|
|
|
|
"size[4,3]" ..
|
|
|
|
"label[0,0;Hello, " .. name .. "]" ..
|
|
|
|
"field[1,1.5;3,1;name;Name;]" ..
|
|
|
|
"button_exit[1,2;2,1;exit;Save]")
|
|
|
|
end
|
2014-12-17 22:45:32 +03:00
|
|
|
})
|
|
|
|
{% endhighlight %}
|
|
|
|
|
|
|
|
Note: the .. is used to join two strings together. The following two lines are equivalent:
|
|
|
|
|
|
|
|
{% highlight lua %}
|
|
|
|
"foobar"
|
|
|
|
"foo" .. "bar"
|
|
|
|
{% endhighlight %}
|
|
|
|
|
2015-02-22 13:28:37 +03:00
|
|
|
## Callbacks
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2017-10-26 01:28:23 +03:00
|
|
|
It's possible to expand the previous example with a callback:
|
2014-12-17 22:45:32 +03:00
|
|
|
|
2014-12-14 22:15:36 +03:00
|
|
|
{% highlight lua %}
|
|
|
|
-- Show form when the /formspec command is used.
|
|
|
|
minetest.register_chatcommand("formspec", {
|
2017-08-26 21:01:51 +03:00
|
|
|
func = function(name, param)
|
|
|
|
minetest.show_formspec(name, "mymod:form",
|
|
|
|
"size[4,3]" ..
|
|
|
|
"label[0,0;Hello, " .. name .. "]" ..
|
|
|
|
"field[1,1.5;3,1;name;Name;]" ..
|
|
|
|
"button_exit[1,2;2,1;exit;Save]")
|
|
|
|
end
|
2014-12-14 22:15:36 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
-- Register callback
|
|
|
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
2017-08-26 21:01:51 +03:00
|
|
|
if formname ~= "mymod:form" then
|
|
|
|
-- Formname is not mymod:form,
|
|
|
|
-- exit callback.
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Send message to player.
|
|
|
|
minetest.chat_send_player(player:get_player_name(), "You said: " .. fields.name .. "!")
|
|
|
|
|
|
|
|
-- Return true to stop other minetest.register_on_player_receive_fields
|
|
|
|
-- from receiving this submission.
|
|
|
|
return true
|
2014-12-14 22:15:36 +03:00
|
|
|
end)
|
|
|
|
{% endhighlight %}
|
|
|
|
|
|
|
|
The function given in minetest.register_on_player_receive_fields is called
|
2017-10-26 01:28:23 +03:00
|
|
|
every time a user submits a form. Most callbacks will check the formname given
|
|
|
|
to the function, and exit if it is not the right form; however, some callbacks
|
2014-12-14 22:15:36 +03:00
|
|
|
may need to work on multiple forms, or all forms - it depends on what you
|
|
|
|
want to do.
|
|
|
|
|
|
|
|
### Fields
|
|
|
|
|
2018-07-16 01:04:55 +03:00
|
|
|
The `fields` parameter to the function is a table, index by string, of the values
|
2017-10-26 01:28:23 +03:00
|
|
|
submitted by the user. You can access values in the table via fields.name,
|
2014-12-14 22:15:36 +03:00
|
|
|
where 'name' is the name of the element.
|
|
|
|
|
2017-10-26 01:28:23 +03:00
|
|
|
As well as retrieving the values of each element, you can also get which button
|
2014-12-14 22:15:36 +03:00
|
|
|
was clicked. In this case, the button called 'exit' was clicked, so fields.exit
|
|
|
|
will be true.
|
|
|
|
|
2017-10-26 01:28:23 +03:00
|
|
|
Some elements can submit the form without the user clicking a button,
|
|
|
|
such as a check box. You can detect these cases by looking
|
2014-12-14 22:15:36 +03:00
|
|
|
for a clicked button.
|
|
|
|
|
2014-12-17 22:45:32 +03:00
|
|
|
{% highlight lua %}
|
|
|
|
-- An example of what fields could contain,
|
|
|
|
-- using the above code
|
|
|
|
{
|
2017-08-26 21:01:51 +03:00
|
|
|
name = "Foo Bar",
|
|
|
|
exit = true
|
2014-12-17 22:45:32 +03:00
|
|
|
}
|
|
|
|
{% endhighlight %}
|
|
|
|
|
2015-02-22 13:28:37 +03:00
|
|
|
## Contexts
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2017-10-26 01:28:23 +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.
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2017-10-26 01:28:23 +03:00
|
|
|
For example, you might make a form to handle land protection information:
|
2014-12-14 22:15:36 +03:00
|
|
|
|
|
|
|
{% highlight lua %}
|
2014-12-17 22:45:32 +03:00
|
|
|
--
|
|
|
|
-- Step 1) set context when player requests the formspec
|
|
|
|
--
|
|
|
|
|
|
|
|
-- land_formspec_context[playername] gives the player's context.
|
2014-12-14 22:15:36 +03:00
|
|
|
local land_formspec_context = {}
|
|
|
|
|
|
|
|
minetest.register_chatcommand("land", {
|
2017-08-26 21:01:51 +03:00
|
|
|
func = function(name, param)
|
|
|
|
if param == "" then
|
|
|
|
minetest.chat_send_player(name, "Incorrect parameters - supply a land ID")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Save information
|
|
|
|
land_formspec_context[name] = {id = param}
|
|
|
|
|
|
|
|
minetest.show_formspec(name, "mylandowner:edit",
|
|
|
|
"size[4,4]" ..
|
|
|
|
"field[1,1;3,1;plot;Plot Name;]" ..
|
|
|
|
"field[1,2;3,1;owner;Owner;]" ..
|
|
|
|
"button_exit[1,3;2,1;exit;Save]")
|
|
|
|
end
|
2014-12-14 22:15:36 +03:00
|
|
|
})
|
|
|
|
|
2014-12-17 22:45:32 +03:00
|
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
-- Step 2) retrieve context when player submits the form
|
|
|
|
--
|
2014-12-14 22:15:36 +03:00
|
|
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
2017-08-26 21:01:51 +03:00
|
|
|
if formname ~= "mylandowner:edit" then
|
|
|
|
return false
|
|
|
|
end
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2017-08-26 21:01:51 +03:00
|
|
|
-- Load information
|
|
|
|
local context = land_formspec_context[player:get_player_name()]
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2017-08-26 21:01:51 +03:00
|
|
|
if context then
|
2017-08-27 21:13:56 +03:00
|
|
|
minetest.chat_send_player(player:get_player_name(), "Id " ..
|
|
|
|
context.id .. " is now called " .. fields.plot ..
|
|
|
|
" and owned by " .. fields.owner)
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2017-08-26 21:01:51 +03:00
|
|
|
-- Delete context if it is no longer going to be used
|
|
|
|
land_formspec_context[player:get_player_name()] = nil
|
2014-12-14 22:15:36 +03:00
|
|
|
|
2017-08-26 21:01:51 +03:00
|
|
|
return true
|
|
|
|
else
|
|
|
|
-- Fail gracefully if the context does not exist.
|
2017-08-27 21:13:56 +03:00
|
|
|
minetest.chat_send_player(player:get_player_name(),
|
|
|
|
"Something went wrong, try again.")
|
2017-08-26 21:01:51 +03:00
|
|
|
end
|
2014-12-14 22:15:36 +03:00
|
|
|
end)
|
|
|
|
{% endhighlight %}
|
2014-12-14 23:04:14 +03:00
|
|
|
|
2015-02-22 13:28:37 +03:00
|
|
|
## Node Meta Formspecs
|
2014-12-14 23:04:14 +03:00
|
|
|
|
2017-10-26 01:28:23 +03:00
|
|
|
minetest.show_formspec is not the only way to show a formspec; you can also
|
|
|
|
add formspecs to a [node's meta data](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.
|
2014-12-14 23:04:14 +03:00
|
|
|
|
|
|
|
{% highlight lua %}
|
|
|
|
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)
|
|
|
|
-- 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]"..
|
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)
|
|
|
|
if(fields.quit) then return end
|
|
|
|
print(fields.x)
|
|
|
|
end
|
2014-12-14 23:04:14 +03:00
|
|
|
})
|
|
|
|
{% endhighlight %}
|
|
|
|
|
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.
|