---
title: GUIs (Formspecs)
layout: default
root: ../..
idx: 4.5
description: Lerne, wie man GUIs mit formspecs anzeigt
redirect_from: /de/chapters/formspecs.html
submit_vuln:
    level: warning
    title: Malicious clients can submit anything at anytime
    message: Sie sollten niemals einer formspec-Übermittlung vertrauen. Ein böswilliger Client
             kann jederzeit alles übermitteln, was er will - auch wenn Sie ihm nie die
             den formspec gezeigt haben. Das bedeutet, dass Sie die Berechtigungen prüfen sollten
             und sicherstellen, dass sie die Aktion durchführen dürfen.
---

## Einleitung <!-- omit in toc -->

<figure class="right_image">
    <img src="{{ page.root }}//static/formspec_example.png" alt="Ofen-Inventar">
    <figcaption>
        Screenshot eines formspec für Öfen, beschriftet.
    </figcaption>
</figure>

In diesem Kapitel werden wir lernen, wie man einen Formspec erstellt und ihn dem Benutzer anzeigt.
Ein Formspec ist der Spezifikationscode für ein Form.
In Minetest sind Forms Fenster wie das Spielerinventar und können eine
eine Vielzahl von Elementen wie Beschriftungen, Schaltflächen und Felder enthalten.

Beachten Sie, dass Sie, wenn Sie keine Benutzereingaben benötigen, zum Beispiel wenn Sie nur
Informationen für den Spieler bereitstellen möchten, Sie die Verwendung von
[Heads Up Display (HUD)](hud.html)-Elementen anstelle von Formspecs verwenden sollten, da
unerwartete Fenster das Spielgeschehen stören können.

- [Reale oder Legacy-Koordinaten](#reale-oder-legacy-koordinaten)
- [Anatomie eines formspecs](#anatomie-eines-a-formspecs)
	- [Elemente](#elemente)
	- [Header](#header)
- [Ratespiel](#ratespiel)
	- [Padding und Abstände](#padding-und-abstände)
	- [Empfang von Formspec-Übermittlungen](#empfang-von-formspec-übermittlungen)
	- [Contexts](#contexts)
- [Formspec-Quellen](#formspec-quellen)
	- [Node Meta Formspecs](#node-meta-formspecs)
	- [Spieler Inventar Formspecs](#spieler-inventar-formspecs)
	- [Sie sind dran](#sie-sind-dran)


## Reale oder Legacy-Koordinaten

In älteren Versionen von Minetest waren die Formspecs inkonsistent. Wegen der Art und Weise, wie verschiedene
Elemente auf unerwartete Art und Weise positioniert wurden war es schwierig, die
Platzierung der Elemente vorherzusagen und auszurichten. Minetest 5.1.0 enthält eine Funktion namens
Koordinaten, die dieses Problem durch die Einführung eines konsistenten
Koordinatensystem beheben. Die Verwendung von realen Koordinaten wird dringend empfohlen, und deshalb
dieses Kapitel ausschließlich diese verwenden.

Die Verwendung einer formspec_version von 2 oder höher aktiviert reale Koordinaten.

## Anatomie eines formspecs

### Elemente

Formspec ist eine domänenspezifische Sprache mit einem ungewöhnlichen Format.
Sie besteht aus einer Reihe von Elementen mit der folgenden Form:

    type[param1;param2]

    Der Elementtyp wird deklariert und dann werden alle Parameter
    in eckigen Klammern angegeben. Mehrere Elemente können miteinander verbunden werden, oder
    auf mehrere Zeilen verteilt werden, etwa so:

    foo[param1]bar[param1]
    bo[param1]


Elemente sind Elemente wie Textfelder oder Schaltflächen oder können Metadaten wie Größe
oder Hintergrund sein. Sie sollten nachschlagen in der
[lua_api.txt 🇬🇧](https://minetest.gitlab.io/minetest/formspec/)
für eine Liste aller möglichen Elemente.


### Header

Der Header eines Formspec enthält Informationen, die zuerst erscheinen müssen. Diese
umfasst die Größe des Formspec, die Position, den Anker und ob das
spielweite Thema angewendet werden soll.

Die Elemente im Header müssen in einer bestimmten Reihenfolge definiert werden, sonst
wird ein Fehler angezeigt. Diese Reihenfolge ist im obigen Absatz angegeben und - wie immer -
in der Lua-API-Referenz dokumentiert.

Die Größe wird in Formspec-Slots angegeben - eine Maßeinheit, die etwa
64 Pixeln entspricht, jedoch abhängig von der Bildschirmdichte und den Skalierungs
Einstellungen des Clients ist. Hier ist ein formspec mit der Größe "2,2":

    formspec_version[4]
    size[2,2]

Beachten Sie, dass wir die Formspec-Sprachversion ausdrücklich definiert haben müssen.
Ohne dies wird stattdessen das Altsystem verwendet - was
die Verwendung der konsistenten Elementpositionierung und anderer neuer Funktionen verhindert.

Die Elemente position und anchor werden verwendet, um das Formspec auf dem Bildschirm zu platzieren.
Die Position legt fest, wo auf dem Bildschirm das formspec sein wird, und ist standardmäßig auf
die Mitte (`0,5,0,5`). Der Anker legt fest, wo auf dem formspec die Position ist,
so dass Sie das Formspec mit dem Rand des Bildschirms ausrichten können. Das Formspec
kann auf diese Weise links vom Bildschirm platziert werden:

    formspec_version[4]
    size[2,2]
    position[0,0.5]
    anchor[0,0.5]

Dadurch wird der Anker an den linken mittleren Rand des Formspec-Feldes gesetzt, und die
Position dieses Ankers auf der linken Seite des Bildschirms.


## Ratespiel

<figure class="right_image">
    <img src="{{ page.root }}/static/formspec_guessing.png" alt="Rate-Formspec">
    <figcaption>
        Das Ratespiel formspec.
    </figcaption>
</figure>

Der beste Weg, etwas zu lernen, ist, etwas zu machen, also lasst uns ein Ratespiel machen.
Das Prinzip ist einfach: Die Mod entscheidet sich für eine Zahl, und der Spieler
errät die Zahl. Die Mod sagt dann, ob die erratene Zahl höher oder niedriger ist als
die tatsächliche Zahl.

Zunächst erstellen wir eine Funktion, die den Formspec-Code erzeugt. Es ist gute Praxis, dies
zu tun, da es die Wiederverwendung an anderer Stelle erleichtert.

<div style="clear: both;"></div>

```lua
guessing = {}

function guessing.get_formspec(name)
    -- TODO: Anzeige, ob die letzte Schätzung höher oder niedriger war
    local text = "Ich denke an eine Zahl... Raten Sie mal!"

    local formspec = {
        "formspec_version[4]",
        "size[6,3.476]",
        "label[0.375,0.5;", minetest.formspec_escape(text), "]",
        "field[0.375,1.25;5.25,0.8;nummer;Nummer;]",
        "button[1.5,2.3;3,0.8;raten;Raten]"
    }

    -- table.concat ist schneller als String-Verkettung - `..`
    return table.concat(formspec, "")
end
```

Im obigen Code platzieren wir ein Feld, eine Beschriftung und eine Schaltfläche.
Ein Feld erlaubt die Eingabe von Text und eine Schaltfläche dient zum
Absenden des Forms. Sie werden feststellen, dass die Elemente
sorgfältig positioniert sind, um Padding und Abstände hinzuzufügen, was später erklärt wird.

Als Nächstes wollen wir dem Spieler erlauben, den Formspec anzuzeigen. Der beste Weg, dies zu tun
ist die Verwendung von `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,
})
```

Die Funktion `show_formspec` akzeptiert einen Spielernamen, den Namen der Formspec und den
Formspec selbst. Der Formspec-Name sollte ein gültiger Itemname sein, d.h. im Format
`Modname:Gegenstandsname`.


### Padding und Abstände

<figure class="right_image">
    <img src="{{ page.root }}/static/formspec_padding_spacing.png" alt="Padding und Abstände">
    <figcaption>
        The guessing game formspec.
    </figcaption>
</figure>

Padding ist der Abstand zwischen dem Rand des Formspec und seinem Inhalt oder zwischen nicht verwandten Elementen,
dargestellt in Rot. Abstand ist der Abstand zwischen zusammenhängenden Elementen, der blau dargestellt wird.


Ein Padding von `0,375` und ein Abstand von `0,25` sind üblich.

<div style="clear: both;"></div>


### Empfang von Formspec-Übermittlungen

Wenn `show_formspec` aufgerufen wird, wird der formspec an den Client gesendet, um angezeigt zu werden.
Damit Formspecs nützlich sind, müssen Informationen vom Client zum Server zurückgeschickt werden.
Die Methode dafür heißt formspec field submission, und für `show_formspec` wird diese
Übermittlung über einen globalen Callback empfangen:

```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 .. " riet " .. fields.number)
    end
end)
```

Die in `minetest.register_on_player_receive_fields` angegebene Funktion wird
jedes Mal aufgerufen, wenn ein Benutzer ein Formular absendet. Die meisten Callbacks müssen den
an die Funktion übergebenen Formularnamen überprüfen und beenden, wenn es sich nicht um das richtige Form handelt; einige
müssen jedoch möglicherweise für mehrere Formulare oder für alle Formulare funktionieren.

Der Parameter `fields` der Funktion ist eine Tabelle mit den vom Benutzer übermittelten Werten
Benutzer übermittelten Werte, die durch Zeichenketten indiziert sind. Benannte Elemente erscheinen in dem Feld unter ihrem eigenen
Namen, aber nur, wenn sie für das Ereignis, das die Übermittlung verursacht hat, relevant sind.
Ein Schaltflächenelement erscheint beispielsweise nur dann in Feldern, wenn die betreffende Schaltfläche
gedrückt wurde.

{% include notice.html notice=page.submit_vuln %}

Der formspec wird also an den Client gesendet, und der Client sendet Informationen zurück.
Der nächste Schritt besteht darin, den Zielwert irgendwie zu generieren und zu speichern, und die
die formspec auf der Grundlage von Schätzungen zu aktualisieren. Dies geschieht mit Hilfe eines Konzepts namens
"contexts".


### Contexts

In vielen Fällen möchten Sie, dass minetest.show_formspec Informationen
an den Callback weitergeben, die nicht an den Client gesendet werden sollen.
Dies könnte beinhalten dass ein Chat-Befehl aufgerufen wurde, oder worum es
in dem Dialog geht. In diesem Fall, der Zielwert, der gespeichert werden muss.

Ein Context ist eine pro-Spieler-Tabelle zum Speichern von Informationen, und die Contexts für alle
Online-Spieler werden in einer dateilokalen Variablen gespeichert:

```lua
local _contexts = {}
local function get_context(name)
    local context = _contexts[name] or {}
    _contexts[name] = context
    return context
end

minetest.register_on_leaveplayer(function(spieler)
    _contexts[spieler:get_player_name()] = nil
end)
```

Als nächstes müssen wir den Show-Code ändern, um den Context zu aktualisieren
zu aktualisieren, bevor der Formspec angezeigt wird:

```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
```

Wir müssen auch den Code für die Generierung von Formularen ändern, um den Context zu verwenden:

```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 = "Too high!"
    else
        text = "Too low!"
    end
```

Beachten Sie, dass es gute Praxis ist, wenn `get_formspec` den Context nur liest und
überhaupt nicht zu aktualisiert. Dies kann die Funktion einfacher machen, und auch leichter zu testen.

Und schließlich müssen wir den Handler aktualisieren, um den Context mit der Vermutung zu aktualisieren:

```lua
if fields.guess then
    local name = spieler:get_player_name()
    local context = get_context(name)
    context.guess = tonumber(fields.number)
    guessing.show_to(name)
end
```


## Formspec-Quellen

Es gibt drei verschiedene Möglichkeiten, wie ein Formspec an den Client übermittelt werden kann:

* [show_formspec](#ratespiel): Bei der oben beschriebenen Methode werden die Felder durch
     `register_on_player_receive_fields` empfangen.
* [Node Meta Formspecs](#node-meta-formspecs): der Node enthält in seinen Metadaten eine Formularvorgabe,
     und der Client zeigt es *sofort* an, wenn der Spieler mit der rechten Maustaste klickt. Felder werden
     durch eine Methode in der Node-Definition namens `on_receive_fields` empfangen.
* [Player Inventory Formspecs](#spieler-inventar-formspecs): der Formspec wird irgendwann an den Client gesendet und dann
     sofort angezeigt, wenn der Spieler auf "i" drückt. Felder werden durch
     `register_on_player_receive_fields` empfangen.

### Node Meta Formspecs

`minetest.show_formspec` ist nicht die einzige Möglichkeit, einen Formspec anzuzeigen; Sie können auch
Formspecs zu den Metadaten eines [Nodes](node_metadata.html) hinzufügen. Zum Beispiel,
wird dieses bei Truhen verwendet, um ein schnelleres Öffnen zu ermöglichen -
man muss nicht darauf warten, dass der Server dem Spieler den Formspec für die Truhe schickt.

```lua
minetest.register_node("meinemod:rechtsclick", {
    description = "Rechtsclicke me!",
    tiles = {"mymod_rechtsclick.png"},
    groups = {cracky = 1},
    after_place_node = function(pos, placer)
        -- Diese Funktion wird ausgeführt, wenn das Kisten-Node platziert wird.
        -- Der folgende Code setzt den formspec für Kiste.
        -- Meta ist eine Möglichkeit, Daten in einem Node zu speichern.

        local meta = minetest.get_meta(pos)
        meta:set_string("formspec",
                "formspec_version[4]" ..
                "size[5,5]" ..
                "label[1,1;Dies wird beim Rechtsklick angezeigt]" ..
                "field[1,2;2,1;x;x;]")
    end,
    on_receive_fields = function(pos, formname, fields, spieler)
        if fields.quit then
            return
        end

        print(fields.x)
    end
})
```

Auf diese Weise eingestellte Formspecs lösen nicht denselben Callback aus. Um
Formulareingaben für meta formspecs zu erhalten, müssen Sie einen
`on_receive_fields`-Eintrag bei der Registrierung des Nodes enthalten.

Diese Art von Callback wird ausgelöst, wenn Sie die Eingabetaste
in einem Feld drücken, was mit `minetest.show_formspec` unmöglich ist;
diese Art von Formular kann jedoch nur durch Rechtsklick auf einen Knoten angezeigt werden. Sie kann nicht programmatisch ausgelöst werden.

### Spieler Inventar Formspecs

Der Formspec für das Spielerinventar wird angezeigt, wenn der Spieler auf i drückt.
Der globale Callback wird verwendet, um Ereignisse von diesem Formspec zu empfangen, und der
formname ist `""`.

Es gibt eine Reihe von verschiedenen Mods, die es ermöglichen, das
das Spielerinventar anzupassen. Die offiziell empfohlene Mod ist
[Simple Fast Inventory (sfinv)](https://github.com/rubenwardy/sfinv/blob/master/Tutorial.md),
und ist in Minetest Game enthalten. Ich als Übersetzer empfehle jedoch
eher [i3](https://github.com/minetest-mods/i3) oder [unified inventory](https://github.com/minetest-mods/unified_inventory)


### Sie sind dran

* Erweitern Sie das Ratespiel, um die höchste Punktzahl jedes Spielers zu ermitteln, wobei
  die höchste Punktzahl angibt, wie viele Ratschläge nötig waren.
* Erstellen Sie einen Node namens "Inbox", in dem Benutzer ein Formspec öffnen und Nachrichten hinterlassen können.
  Dieser Node sollte den Namen des Placers als `owner` in der Meta speichern, und sollte
  `show_formspec` verwenden, um verschiedene Formspecs für verschiedene Spieler anzuzeigen.
