---
title: Lua Voxel Manipulator
layout: default
root: ../..
idx: 6.2
description: Erfahren Sie, wie Sie LVMs nutzen können, um Map-Operationen zu beschleunigen.
redirect_from:
  - /de/chapters/lvm.html
  - /de/map/lvm.html
mapgen_object:
    level: warning
    title: LVMs und Mapgen
    message: Verwenden Sie nicht `minetest.get_voxel_manip()` mit mapgen, da dies zu Störungen führen kann.
            Verwenden Sie stattdessen `minetest.get_mapgen_object("voxelmanip")`.
---

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

Die im Kapitel [Grundlegende Kartenoperationen](environment.html) beschriebenen Funktionen sind bequem und einfach zu benutzen, aber für große Gebiete sind sie ineffizient. Jedes Mal, wenn Sie `set_node` oder `get_node` aufrufen, muss Ihr Mod mit der Engine kommunizieren. Dies führt zu ständigen einzelnen Kopiervorgängen zwischen der
Engine und Ihrem Mod, was langsam ist und die Leistung des Spiels schnell verringert. Die Verwendung eines Lua Voxel Manipulators (LVM) kann eine gute Alternative sein.

- [Konzepte](#konzepte)
- [Einlesen in die LVM](#einlesen-in-den-lvm)
- [Nodes lesen](#nodes-lesen)
- [Nodes schreiben](#nodes-schreiben)
- [Beispiel](#beispiel)
- [Sie sind dran](#sie-sind-dran)

## Konzepte

Ein LVM ermöglicht es Ihnen, große Bereiche der Karte in den Speicher Ihres Mods zu laden. Sie können diese Daten dann ohne weitere Interaktion mit der Engine und ohne die Ausführung von Callbacks lesen und schreiben, was bedeutet, dass diese Operationen sehr schnell sind. Anschließend können Sie den Bereich wieder in die Engine zurückschreiben und Beleuchtungsberechnungen durchführen.

## Einlesen in den LVM

Sie können nur einen kubischen Bereich in einen LVM laden, also müssen Sie die minimalen und maximalen Positionen ausarbeiten, die Sie ändern möchten. Dann können Sie einen LVM erstellen und einlesen LVM einlesen. Zum Beispiel:

```lua
local vm         = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(pos1, pos2)
```

Aus Leistungsgründen wird ein LVM fast nie genau den Bereich lesen, den Sie ihm vorgeben. Stattdessen wird er wahrscheinlich einen größeren Bereich lesen. Der größere Bereich wird durch `emin` und `emax` angegeben, die für *emerged min pos* und *emerged max pos* stehen, auf Deutch: *Ermittelte Minimum-Position* bzw. *Ermittelte Maximum-Position*. Ein LVM lädt den Bereich, den er enthält, für Sie - sei es durch Laden aus dem Speicher, von der Festplatte oder Aufruf des Map-Generators.

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

## Nodes lesen

Um die Typen von Knoten an bestimmten Positionen zu lesen, müssen Sie `get_data()` verwenden. Dies gibt ein flaches Array zurück, in dem jeder Eintrag den Typ eines bestimmten Knotens darstellt.

```lua
local data = vm:get_data()
```

Sie können param2 und Beleuchtungsdaten mit den Methoden `get_param2_data()` und `get_light_data()` erhalten.

Sie müssen `emin` und `emax` verwenden, um herauszufinden, wo ein Knoten in den flachen Arrays ist, die durch die oben genannten Methoden gegeben sind. Es gibt eine Hilfsklasse namens `VoxelArea`, die die die die Berechnung für Sie übernimmt.

```lua
local a = VoxelArea:new{
    MinEdge = emin,
    MaxEdge = emax
}

-- Index des Nodees abrufen
local idx = a:index(x, y, z)

-- Node lesen
print(data[idx])
```

Wenn Sie den obigen Code ausführen, werden Sie feststellen, dass `data[vi]` eine Ganzzahl ist. Das ist so, weil die Engine aus  Leistungsgründen keine Nodes in Form von Zeichenketten speichert. Stattdessen verwendet die Engine eine ganze Zahl, die sogenannte Inhalts-ID. Sie können die Inhalts-ID für einen bestimmten Nodetyp herausfinden mit `get_content_id()` herausfinden. Zum Beispiel:

```lua
local c_stone = minetest.get_content_id("default:stone")
```

Sie können dann überprüfen, ob der Node zum Beispiel Stein ist:

```lua
local idx = a:index(x, y, z)
if data[idx] == c_stone then
    print("ist Stein!")
end
```

Die Inhalts-IDs eines Knotentyps können sich während der Ladezeit ändern, daher wird es nicht empfohlen, diese während dieser Zeit abzurufen.

Knoten in einem LVM-Datenarray werden in umgekehrter Koordinatenreihenfolge gespeichert. Deshalb sollten Sie immer in der Reihenfolge `z, y, x` *iterieren* (umdrehen). Zum Beispiel:

```lua
for z = min.z, max.z do
    for y = min.y, max.y do
        for x = min.x, max.x do
            -- vi, Voxel-Index, ist hier ein gängiger Variablenname
            local vi = a:index(x, y, z)
            if data[vi] == c_stone then
                print("ist Stein!")
            end
        end
    end
end
```

Der Grund dafür liegt im Bereich der Computerarchitektur. Das Lesen aus dem RAM ist ziemlich kostspielig, daher verfügen CPUs über mehrere Caching-Ebenen. Wenn die Daten, die ein Prozess anfordert,
im Cache sind, kann er sie sehr schnell abrufen. Wenn sich die Daten nicht im Cache befinden, kommt es zu einem Cache-Miss und der Prozess holt sich die benötigten Daten aus dem RAM. Alles,
was die angeforderten Daten umgibt, wird ebenfalls geholt und ersetzt dann die Daten im Cache. Dies geschieht, weil es sehr wahrscheinlich ist, dass der Prozess erneut Daten in der Nähe dieser Stelle anfordert. Das bedeutet, dass es eine gute Optimierungsregel ist, so zu iterieren, dass die Daten nacheinander gelesen werden und *Chache Trashing* zu vermeiden.

## Nodes schreiben

Zunächst müssen Sie eine neue Inhalts-ID im Datenfeld festlegen:

```lua
for z = min.z, max.z do
    for y = min.y, max.y do
        for x = min.x, max.x do
            local vi = a:index(x, y, z)
            if data[vi] == c_stone then
                data[vi] = c_air
            end
        end
    end
end
```

Wenn Sie die Einstellung der Knoten im LVM abgeschlossen haben, müssen Sie das Daten Array in die Engine hochladen:

```lua
vm:set_data(data)
vm:write_to_map(true)
```

Zum Setzen von Beleuchtungs- und Param2-Daten verwenden Sie, wie schon erwähnt, die entsprechend benannten Methoden `set_light_data()` und `set_param2_data()`.

Die Methode `write_to_map()` nimmt einen booleschen Wert an, der `true` ist, wenn die Beleuchtung berechnet werden soll. Wenn Sie `false` übergeben, müssen Sie die Beleuchtung zu einem späteren Zeitpunkt mit `minetest.fix_light` neu berechnen lassen.

## Beispiel

```lua
local function grass_to_dirt(pos1, pos2)
    local c_dirt  = minetest.get_content_id("default:dirt")
    local c_grass = minetest.get_content_id("default:dirt_with_grass")

    -- Daten in LVM einlesen
    local vm = minetest.get_voxel_manip()
    local emin, emax = vm:read_from_map(pos1, pos2)
    local a = VoxelArea:new{
        MinEdge = emin,
        MaxEdge = emax
    }
    local data = vm:get_data()

    -- Daten ändern
    for z = pos1.z, pos2.z do
        for y = pos1.y, pos2.y do
            for x = pos1.x, pos2.x do
                local vi = a:index(x, y, z)
                if data[vi] == c_grass then
                    data[vi] = c_dirt
                end
            end
        end
    end

    -- Daten schreiben
    vm:set_data(data)
    vm:write_to_map(true)
end
```

## Sie sind dran

* Erstellen Sie `replace_in_area(from, to, pos1, pos2)`, das alle Instanzen von `von` durch `bis` in dem angegebenen Bereich ersetzt, wobei `von` und `bis` Knotennamen sind.
* Programmieren Sie eine Funktion, die alle Brustknoten um 90&deg; dreht.
* Erstellen Sie eine Funktion, die einen LVM benutzt, um zu bewirken, dass sich moosiges Kopfsteinpflaster auf nahegelegene Stein- und Pflastersteinknoten ausbreitet. Verursacht Ihre Implementierung, dass sich das Moospflaster auf mehr als einen Node ausbreitet? Wenn ja, wie können Sie dies verhindern?
