minetest_modding_book/_en/players/formspecs.md

289 lines
9.2 KiB
Markdown
Raw Normal View History

2014-12-14 22:15:36 +03:00
---
title: Formspecs
layout: default
2017-08-26 18:40:30 +03:00
root: ../../
2018-07-15 17:28:10 +03:00
idx: 4.5
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
<figure class="right_image">
<img src="{{ page.root }}/static/formspec_example.png" alt="Furnace Inventory">
<figcaption>
Screenshot of furnace formspec, labelled.
</figcaption>
</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:
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.
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.
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]
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
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
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
### Example
2017-10-26 01:28:23 +03:00
This example shows a formspec to a player when they use the /formspec command.
<figure class="right_image">
<img src="{{ page.root }}/static/formspec_name.png" alt="Name Formspec">
<figcaption>
The formspec generated by<br />
the example's code
</figcaption>
</figure>
{% highlight lua %}
-- Show form when the /formspec command is used.
minetest.register_chatcommand("formspec", {
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
})
{% 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-14 22:15:36 +03:00
{% highlight lua %}
-- Show form when the /formspec command is used.
minetest.register_chatcommand("formspec", {
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)
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
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.
{% highlight lua %}
-- An example of what fields could contain,
-- using the above code
{
name = "Foo Bar",
exit = true
}
{% 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 %}
--
-- 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", {
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
})
--
-- 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)
if formname ~= "mylandowner:edit" then
return false
end
2014-12-14 22:15:36 +03:00
-- Load information
local context = land_formspec_context[player:get_player_name()]
2014-12-14 22:15:36 +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
-- 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
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.")
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", {
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
2014-12-14 23:04:14 +03:00
})
{% endhighlight %}
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`;
however, this kind of form can only be shown by right-clicking on a
node. It cannot be triggered programmatically.