minetest_modding_book/_de/quality/common_mistakes.md
2022-11-18 14:56:47 +01:00

4.8 KiB

title layout root idx redirect_from
Häufige Fehler default ../.. 8.1 /de/chapters/common_mistakes.html

Einleitung

In diesem Kapitel werden häufige Fehler beschrieben und es wird erklärt, wie man diese vermeiden kann.

Vorsicht beim Speichern von ObjectRefs (z.B.: Spieler oder Entitäten)

Eine ObjectRef wird ungültig, wenn der Spieler oder die Entität, die sie repräsentiert, das das Spiel verlässt. Dies kann passieren, wenn der Spieler offline geht oder die Entität nicht mehr geladen ist oder entfernt wird.

Die Methoden von ObjectRefs geben seit Minetest 5.2 immer nil zurück, wenn sie ungültig sind. Jeder Aufruf wird grundsätzlich ignoriert.

Sie sollten das Speichern von ObjectRefs nach Möglichkeit vermeiden. Wenn Sie dennoch eine ObjectRef speichern, sollten Sie dies vor der Verwendung überprüfen, etwa so:

-- Funktioniert nur in Minetest 5.2+
if obj:get_pos() then
	-- ist gültig!
end

Vertrauen Sie keinen Formspec-Einsendungen

Hacker können Formulare übermitteln, wann immer sie wollen und mit welchem Inhalt auch immer sie wollen.

Der folgende Code weist zum Beispiel eine Schwachstelle auf, die es Spielern ermöglicht sich selbst Moderatoren-Rechte zu geben:

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)
    -- SCHLECHT! Fehlender Privilegiencheck!

    local privs = minetest.get_player_privs(fields.target)
    privs.kick  = true
    privs.ban   = true
    minetest.set_player_privs(fields.target, privs)
    return true
end)

Fügen Sie eine Berechtigungsprüfung hinzu, um dieses Problem zu lösen:

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)

ItemStacks nach dem Ändern einstellen

Haben Sie bemerkt, dass es in der API einfach ItemStack genannt wird, nicht ItemStackRef, ähnlich wie InvRef? Das liegt daran, dass ein ItemStack keine Referenz ist - es ist eine Kopie. Stacks arbeiten mit einer Kopie der Daten und nicht mit dem Stapel im Inventar. Das bedeutet, dass das Ändern eines Stacks nicht wirklich den Stack im Inventar verändert.

Tun Sie zum Beispiel nicht dies:

local inv = player:get_inventory()
local stack = inv:get_stack("main", 1)
stack:get_meta():set_string("description", "Teilweise gegessen")
-- SCHLECHT! Änderung wird verloren gehen

Machen Sie stattdessen Folgendes:

local inv = player:get_inventory()
local stack = inv:get_stack("main", 1)
stack:get_meta():set_string("description", "Teilweise gegessen")
inv:set_stack("main", 1, stack)
-- Richtig! ItemStack ist eingestellt!

Das Verhalten von Callbacks ist etwas komplizierter. Das Ändern eines ItemStack, der zurückgegeben wird, ändert diesen auch für den Aufrufer und alle nachfolgenden Aufrufe. In jedem Fall wird der ItemStack jedoch nur dann in der Engine gespeichert, wenn der Callback-Aufrufer ihn setzt.

minetest.register_on_item_eat(function(hp_change, replace_with_item,
        itemstack, user, pointed_thing)
    itemstack:get_meta():set_string("description", "Teilweise gegessen")
    -- Fast richtig! Die Daten gehen verloren, wenn ein anderer
    -- Callback das Verhalten abbricht
end)

Wenn keine Rückrufe diesen Vorgang abbrechen, wird der Stack gesetzt und die Beschreibung aktualisiert. Wenn jedoch ein Rückruf dies abbricht, kann die Aktualisierung verloren gehen.

Es ist besser, dies stattdessen zu tun:

minetest.register_on_item_eat(function(hp_change, replace_with_item,
        itemstack, user, pointed_thing)
    itemstack:get_meta():set_string("description", "Teilweise gegessen")
    user:get_inventory():set_stack("main", user:get_wield_index(),
            itemstack)
    -- Richtig, die Beschreibung wird immer eingestellt!
end)

Wenn die Callbacks abbrechen oder der Callback-Runner den Stack nicht setzt, dann wird die Aktualisierung trotzdem durchgeführt. Wenn die Rückrufe oder der Rückruf-Läufer den Stack setzen, spielt die Verwendung von set_stack keine Rolle.