DE/Lua Tutorial
Vorwort
Da es bis auf einige knappe und teils unprofessionelle Tutorials kein deutsches Lua Tutorial gibt, habe ich beschlossen, nun ein eigenes zu schreiben, welches alle für MTA notwendigen Grundlagen einfach und übersichtlich erklärt. Weiterführende Möglichkeiten und Funktionen können der Offiziellen Lua Dokumentation entnommen werden, die es jedoch nicht auf Deutsch gibt.
Ein weiterer Grund ist, dass ich schon von einigen gehört habe und auch so schon den Verdacht hatte, dass MTA einfach so wenige deutsche Spieler und vor allem Server hat (gibt es überhaupt einen deutschen?), weil die meisten Probleme mit den englischen Dokumentationen haben oder der Meinung sind, dass für MTA ja sowieso kein deutscher Support vorhanden ist.
Soviel nun dazu, wie es zu der Idee des deutschen Lua Tutorials kam. Fangen wir an mit dem Erlernen einer neuen Sprache!
Grundlagen
Lua, eine Skriptsprache
Lua (portugiesisch für Mond) ist eine Skriptsprache zum Einbinden in Programme, um diese leichter weiterentwickeln und warten zu können. Insbesondere die geringe Größe des Interpreters von 120 KB und die hohe Geschwindigkeit verglichen mit anderen Skriptsprachen überzeugen viele Entwickler davon, Lua einzusetzen.
Die Syntax lehnt sich an die von Pascal an, was besonders Anfängern den Einstieg in Lua erleichtert. Im Gegensatz zu von Pascal abgeleiteten Sprachen nutzt Lua jedoch „==“ und nicht „=“ als Vergleichsoperator.
(Quelle: Wikipedia)
Einige werden sich bei dieser Einführung vielleicht gefragt haben, was der Unterschied zwischen Skript- und Programmiersprache ist und was ein Interpreter sein soll. Im folgenden will ich diese beiden Fragen so kurz und einfach wie möglich klären.
Eine Programmiersprache (z.B. C, C++, Pascal, Delphi) wird - wie der Name schon sagt - verwendet, um Programme zu schreiben. Ein Programm liegt bekannterweise in Form einer Executable (*.exe) vor. Diese kann weitgehend unabhängig von irgendwelchen Betriebssystemen oder der Umgebung selbstständig ausgeführt werden (z.B. durch Doppelklick). Weitgehend deswegen, da einige Programme aufgrund von Speicherersparnissen sogenannte Programmbibliotheken (original Dynamic Link Library, kurz dll) benötigen, um zu funktionieren. Erklärungen dazu findet ihr hier.
Um einen Programmquellcode, d.h. einen Text, der in einer Programmiersprache geschrieben wurde, aus dem Textdokumentformat in eine Anwendung (original Executable, kurz exe) umzuwandeln, benötigt man nun einen Compiler (zu Deutsch Kompilierer oder Übersetzer), der die Programmiersprache aus der menschenlesbaren Form in die maschinenlesbare Form umwandelt. Neben dieser Hauptaufgabe übernimmt der Compiler natürlich noch weitere Aufgaben, auf die ich jetzt aber nicht genauer eingehen werde. Infos dazu findet ihr hier.
Die maschinenlesbare Form ist für Menschen unlesbar.
Eine Skriptsprache (z.B. JavaScript, DOS-Batch(Win) bzw. Shell-Scripts(UNIX)) hingegen liegt standardmäßig in der menschenlesbaren Form vor. Skriptsprachen werden - wie im ersten Satz bereits erwähnt - vor allem von Programmen benutzt, um benutzerdefinierte Abläufe verwenden zu können. Zur Ausführung von Scripts wird ein sogenannter Interpreter benötigt, der das Skript liest und es Schritt für Schritt in die Maschinensprache umwandelt und ausführt. Manche Skriptsprachen kann man mit einem Precompiler (zu Deutsch Vorkompilierer oder Vorübersetzer) vorkompilieren. Diesen könnte man auch den Vorkauer vom Interpreter nennen, da er das Script schon zum Teil in die Maschinensprache umwandelt, jedoch nicht so extrem wie der Compiler der Programmiersprachen. Das Ergebnis davon ist logischerweise die schnellere Ausführung des Skripts.
Genug der langweiligen Definitionen. Klären wir lieber, was man denn alles braucht, um ein Lua Script zu schreiben.
Materialien
Lua Skripte werden üblicherweise als Textdateien mit der Endung .lua gespeichert. Die Endung ist jedoch nicht von belang. Es kann jede beliebige Endung gewählt werden.
Wie jede normale Textdatei kann man also auch Lua Skripte mit dem standard Texteditor des vorhandenen Betriebssystems schreiben, editieren und lesen. Ich persönlich empfehle jedoch das Programm Notepad++, welches hier heruntergeladen werden kann. Das Programm ist Freeware und wird regelmäßig aktualisiert und erweitert. Der Vorteil daran ist, dass es ein professionelles Syntax-Highlighting eingebaut hat, welches Schlagwörter und Standardfunktionen farblich oder anderweitig hervorhebt und somit die Übersichtlichkeit erheblich verbessert.
Standardmäßig wird die Sprache Lua automatisch an der Dateiendung .lua erkannt, solltest Du eine andere Endung verwenden und trotzdem das Lua Syntax-Highlighting verwenden wollen, kannst Du es oben im Menü „Sprachen“ einstellen.
Wer ohne MTA seine kleinen Lua Skripte testen oder ausführen möchte, kann sich hier einen kostenlosen standalone Interpreter herunterladen. Wenn Du Dir nicht sicher bist, welche Windows Edition Du hast, dann hast Du wahrscheinlich eine 32 Bit Version, welche auf dieser Seite der „Windows x86 Executables“ entspricht. Die aktuelle Version (Win 32 Bit) dafür gibt es bei einem Klick auf lua5_1_3_Win32_bin.zip.
Nach dem Download kann der Standalone Interpreter mit einem Doppelklick auf die „lua5.1.exe“ gestartet werden. Dort kann man dann direkt Lua Befehle eingeben oder ein Script laden. Dies geht wie folgt:
dofile("dateiname.lua")
Kommentare
Um sein Skript übersichtlicher zu gestalten, kann man mit Kommentaren bestimmte Zeilen eräutern, Bereiche abgrenzen, alten Code deaktivieren, ohne ihn zu entfernen und generell das Skript übersichtlicher gestalten.
Kommentare werden vom Interpreter vollkommen ignoriert und sind nur für den Menschen lesbar und von Belang.
Einzeilig
-- Dies ist ein einzeiliger Kommentar. Er gilt von -- bis zum Ende dieser Zeile. Automatische Zeilenumbrüche, die eingefügt werden, wenn die Zeilenlänge überschritten wurde, zählen nicht.
Mehrzeilig
--[[ Dieser Kommentar geht über beliebig viele Zeilen und endet mit ]]
Variablen
Einführung
Eine Variable kann man sich wie eine kleine Kiste vorstellen, auf der außen ein Bezeichner (oder Name) steht und die einen Wert enthält. Während der Bezeichner immer gleich ist, kann der Inhalt variieren. Jede Variable hat einen Variablentypen, der bei Programmiersprachen normalerweise zu Beginn des Programmes fest definiert wird und sich nicht ändern kann. Da Lua aber ja keine Programmiersprache ist, muss der Variablentyp zu Beginn und auch sonst nie festgelegt werden und kann sich beliebig oft verändern. Der Interpreter erkennt Variablentypen automatisch und nimmt dem Skripter somit eine Menge Arbeit (vor allem bzgl. der Planung) ab.
Typen
Im Folgenden will ich die wichtigsten Variablentypen auflisten und kurz beschreiben.
- Integer (ganze Zahlen)
- Umfasst alle Ganzzahlen, sowohl negative als auch positive.
- Beispiele: -8, 0, 12, 2
- Float (Komma- bzw. gebrochene Zahlen)
- Umfasst alle Fließkommazahlen, sowohl negative als auch positive. Wichtig dabei ist, dass anstelle des Kommas (,) ein Dezimalpunkt (.) zum Trennen des ganzen vom gebrochenen Teil verwendet werden muss.
- Beispiele: -77.2, 0.0, 3.14159, 9.81
- String (Zeichenkette)
- Kann jedes beliebige Zeichen enthalten. Eine maximale Länge ist mir nicht bekannt. Strings müssen immer in Anführungszeichen(" oder ') eingschlossen werden.
- Beispiele: "hallo", 'Lua ist toll!', "", " :D "
- Boolean (Schalter)
- Kann zwei verschiedene Werte enthalten: true (dt. wahr bzw. an) oder false (dt. falsch bzw. aus). true entspricht dabei nicht dem Wert 1 und false nicht dem Wert 0.
- Beispiele: true, false
- Nil (Leer oder Nichts)
- nil ist sowohl ein Wert als auch ein Typ. Eine Variable vom Typ nil enthält automatisch den Wert nil. nil bedeutet ganz einfach nichts. Wenn eine Variable nil ist, existiert sie nicht. nil entspricht weder dem Wert 0 noch dem Wert false noch dem Wert ""!
- Beispiele: nil
Ich werde auf jeden Variablentypen noch detaillierter eingehen. Außerdem wurden hier die Variablentypen function, table und userdata nicht aufgeführt, da diese jeweils ein eigenes Kapitel bekommen.
Deklaration
Eine Variable deklarieren bedeutet im Grunde genommen eine Variable anlegen. In Lua muss einer Variable bei der Deklaration ein Wert zugewiesen werden. nil ist dabei als Wert auch möglich, macht jedoch wenig Sinn, da jede beliebige Variablenbezeichnung automatisch den Wert nil enthält.
Man unterscheidet zwischen lokalen und globalen Variablen. Um eine Variable als lokal zu definieren muss das Schlüsselwort local davor geschrieben werden. Lokal bedeutet für uns im Moment noch ganz einfach, dass die Variable nur im aktuellen Script verfügbar ist. Sollten also z.B. von einem Programm zwei verschiedene Lua Skripte geladen sein und benutzt werden, können diese untereinander auf die globalen Variablen des jeweils anderen Skripts zugreifen, auf die lokalen jedoch nicht. Generell empfiehlt es sich, immer lokale Variablen zu nehmen, falls man nicht explizit eine globale benötig, da so vermieden wird, dass mehrere parallel laufende Skripte aufgrund von gleichen Variablenbezeichnern durcheinander kommen.
Einzeln
Deklarieren wir uns also nun unsere erste lokale Variable und geben ihr einen Ganzzahligen Inhalt:
local zahl = 7 -- weist der lokalen Variable zahl den Wert 7 zu
Eine globale Variable erhält man, indem man einfach das local weglässt:
gZahl = 13 -- weist der globalen Variable gZahl den Wert 13 zu
Das selbe geht natürlich nun auch mit allen anderen Variablentypen:
local ganzzahl = -5 local kommazahl = 0.008 local zeichenkette = "Hello World" local schalter = true local nichts = nil -- diese Zeile kann man sich im Grunde genommen sparen, da jede nicht deklarierte Variable automatisch den Wert nil hat
Längere Strings mit mehreren Zeilenumbrüchen können in einer vereinfachten Weise zugewiesen werden. Dies funktioniert so ähnlich wie bei Kommentaren. Anstelle von Anführungszeichen verwendet man als Anfangs- und als Endmarkierung des Strings.
local text = [[Willkommen auf unserem Server. Bitte halte Dich an die Regeln. MfG die Administration]]
Alternativ kann in einem String, der in doppelten Anführungszeichen (") eingeschlossen ist, folgende Steuerzeichen enthalten:
- \n
- Entspricht einem Zeilenumbruch.
- \t
- Entspricht einem Tabulator.
- \"
- Wird verwendet, um doppelte Anführungszeichen in einem String, der von doppelten Anführungszeichen eingeschlossen ist, darzustellen, ohne dass der Interpreter diese fälschlicherweise als Stringende auffasst.
- \'
- Wie \", nur gilt dies in Strings, die von einfachen Anführungszeichen eingeschlossen sind.
- \\
- Entspricht einem Backslash (\)
Mehrere
Man kann in einer Zeile mehrere Variablen deklarieren, indem man sie mit Kommas voneinander trennt. Die Variablentypen können dabei alle unterschiedlich sein. Hier mal ein Beispiel:
local frucht, anzahl, reif = "apfel", 4, true
local darf dabei nur einmal am Zeilenanfang stehen und bewirkt, dass alle dahinter aufgelisteten Variablen lokal werden.
Sollten links neben dem Gleichheitszeichen mehr Variablenbezeichner stehen als rechts Werte, so bleiben die überschüssigen Variablen nil. Im umgekehrten Fall werden überschüssige Werte rechts einfach ignoriert.
Der Unterstrich (_) kann verwendet werden, wenn ein Wert auf der rechten Seite ignoriert werden soll.
local _, farbe = 7, "blau"
Dabei wird der Wert 7 komplett ignoriert und der Variable farbe der Wert "blau" zugewiesen. Diese Methode wird erst Sinn ergeben, wenn wir uns mit Funktionen und deren Rückgabewerte beschäftigen. Ich wollte sie jedoch der Vollständigkeit halber hier schon einmal erwähnen.
Operatoren
Arithmetische
Arithmetische Operatoren werden verwendet, um Berechnungen durchzuführen. Somit kann man entweder mit Variablen, festen Zahlen oder einer Mischung neue Werte berechnen oder verändern.
Bei Berechnungen gilt stets: Punkt- vor Strichrechnung.
- Plus (+)
- Addiert zwei Werte miteinander.
local a, b = 3, 8 local c = a + b -- a + b entspricht hier der Rechnung 3 + 8, c erhält nun also den Wert 11
- Minus (-)
- Subtrahiert den zweiten vom ersten Wert.
local a, b = 3, 8 local c = a - b -- a - b entspricht hier der Rechnung 3 - 8, c erhält nun also den Wert -5
- Mal (*)
- Multipliziert zwei Werte miteinander.
local a, b = 3, 8 local c = a * b -- a * b entspricht hier der Rechnung 3 * 8, c erhält nun also den Wert 24
- Geteilt (/)
- Dividiert den ersten durch den zweiten Wert. Hierbei entsteht in den meisten Fällen eine Kommazahl!
local a, b = 3, 8 local c = a / b -- a / b entspricht hier der Rechnung 3 / 8, c erhält nun also den Wert 0.375
- Modulo (%)
- Ermittelt den Rest, der übrig bleibt, wenn man die erste Zahl durch die zweite teilt.
- Der Modulo-Operator gehört zur Punktrechnung.
local a, b = 14, 3 local c = a % b -- a % b entspricht hier der Rechnung 14 % 3, c erhält nun also den Wert 2, da 14 / 3 = 4 REST 2
- Minus (-) (als Vorzeichen)
- Dient zur Darstellung einer negativen Zahl, also einer Zahl unter 0.
local a = -3 -- entspricht der Rechnung a = 0 - 3
- Hoch (^)
- Potenziert die erste Zahl mit der zweiten.
- Potenzen werden noch vor der Punktrechnung ausgerechnet.
local a, b = 3, 8 local c = a ^ b -- a ^ b entspricht hier der Rechnung 3 ^ 8 (3<sup>8</sup>), c erhält nun also den Wert 6561
Logische
Logische Operatoren werden Benutzt, um Werte miteinander zu vergleichen und mehrere Bedingungen zu verknüpfen. Wir werden genauer auf sie zurückkommen, wenn es um Bedingungen geht. Bis dahin reicht es, wenn man sich merkt, dass ein logischer Vergleich entweder true (wenn die Aussage wahr ist) oder false (wenn die Aussage falsch ist) ergibt.
- Gleich (==)
- Prüft, ob zwei Werte gleich sind.
local a = (5 == 5) -- a ist true, da die Aussage wahr ist local b = (5 == 8) -- b ist false, da die Aussage falsch ist
- Ungleich (~=)
- Prüft, ob zwei Werte ungleich sind.
local a = (5 ~= 5) -- a ist false, da die Aussage falsch ist local b = (5 ~= 8) -- b ist true, da die Aussage wahr ist
- Größer als (>)
- Prüft, ob der erste Wert größer als der zweite ist.
local a = (5 > 5) -- a ist false, da die Aussage falsch ist local b = (5 > 8) -- b ist false, da die Aussage falsch ist local c = (8 > 5) -- c ist true, da die Aussage wahr ist
- Größer als oder gleich (>=)
- Prüft, ob der erste Wert größer als der zweite ist oder dem zweiten gleicht.
local a = (5 >= 5) -- a ist true, da die Aussage wahr ist local b = (5 >= 8) -- b ist false, da die Aussage falsch ist local c = (8 >= 5) -- c ist true, da die Aussage wahr ist
- Kleiner als (<)
- Prüft, ob der erste Wert kleiner als der zweite ist.
local a = (5 < 5) -- a ist false, da die Aussage falsch ist local b = (5 < 8) -- b ist true, da die Aussage wahr ist local c = (8 < 5) -- c ist false, da die Aussage falsch ist
- Kleiner als (<=)
- Prüft, ob der erste Wert kleiner als der zweite ist oder dem zweiten gleicht.
local a = (5 <= 5) -- a ist true, da die Aussage wahr ist local b = (5 <= 8) -- b ist true, da die Aussage wahr ist local c = (8 <= 5) -- c ist false, da die Aussage falsch ist
- Und (and)
- Verknüpft zwei Bedingungen miteinander.
local a = (true and false) -- a ist false, da nicht beide Aussagen wahr sind local b = (5 < 8) and (7 == 7) -- b ist true, da beide Aussagen wahr sind local c = (8 > 5) and (1 ~= 2) and (0 <= -7) -- c ist false, da die dritte Aussage falsch ist
- Oder (or)
- Verknüpft zwei Bedingungen miteinander.
local a = (true or false) -- a ist true, da die erste Aussage wahr ist local b = (5 < 8) or (7 == 7) -- b ist true, da mindestens eine der Aussagen wahr ist local c = (8 > 5) or (1 ~= 2) or (0 <= -7) -- c ist true, da mindestens eine der Aussagen wahr ist
- Nicht (not)
- Kehrt einen Booleanwert um.
local a = not true -- a ist false local b = not false -- b ist true local c = not (0 == 8) -- c ist true, da die Aussage falsch ist
Sonstige
Lua hat noch zwei weitere Operatoren, die sich jedoch nicht in die oberen Gruppen eingliedern lassen.
- Länge (#)
- Steht für die Länge eines Strings oder einer Table.
local sprache = "deutsch" local laenge = #sprache -- laenge wird hier auf 7 gesetzt, da das Wort "deutsch" 7 Buchstaben hat
- Verknüpfung (..)
- Verknüpft zwei Strings miteinander.
local geschlecht, alter = "männlich", "18" local satz = "Ich bin "..geschlecht.." und "..alter.." Jahre alt." -- weist satz den Wert "Ich bin männlich und 18 Jahre alt." zu
Natürlich können auch alle anderen Variablentypen mit- und untereinander verglichen werden.
Der Längenoperator (#) sowie der Verknüpfungsoperator (..) sind nur für Strings gedacht, wobei der erste später noch bei Tables eine Rolle spielen wird und der zweite es nicht so genau nimmt mit den Variablentypen. Bei Booleans und nil jedoch führt er in jedem Fall zu einem Fehler. Wir kommen später noch darauf zu sprechen, wie man soetwas umgeht.
Für Integer und Floats sind alle Vergleichsoperatoren und die arithmetischen verfügbar. Strings unterstützen nur die Vergleichsoperatoren, wobei man mit den größer/kleiner Operatoren zwei Strings nach dem Alphabet ordnen kann.
"a" < "b" -- ergibt true, da a vor b im Alphabet kommt
Strings, Booleans und nil kann man nur untereinander vergleichen, nicht miteinander. Miteinander führt in jedem Fall zu false.
Funktionen
Einführung
Eine Funktion ist mehr oder weniger ein Variablentyp.