Zum Hauptinhalt springen

Lua-Skripting: Fehlerbehebung

Diese Seite behandelt häufige Probleme, die beim Schreiben oder Debuggen von FreeMinecraftModels-Prop-Skripten auftreten können, sowie Tipps für die Arbeit mit dem System der verzögerten Konfigurationserzeugung. Wenn du nach funktionierenden Beispielen suchst, siehe Beispiele & Muster. Wenn du gerade erst anfängst, siehe Erste Schritte.

MagmaCore-Lua-Laufzeit

FMM verwendet die MagmaCore-Laufzeit ScriptInstance. Die Dokumentation zu FMM/MagmaCore-Sandbox, Scheduler, Zonen, Welt-API, Entity-Tabellen und Spieler-UI-Methoden findest du unter MagmaCore Lua-Skript-Engine. EliteMobs-Boss-Powers und NPC-Skripte verwenden dieselbe Lua-Sandbox, stellen jedoch eigene Context-Tabellen bereit.


Häufige Probleme​

1. Konfigurationsdatei wird nicht geladen / Skript nicht angehängt​

Symptom: Der Prop spawnt, reagiert aber nicht auf Klicks oder andere Hooks.

Ursachen und Lösungen:

  • Es existiert noch keine .yml-Konfigurationsdatei. FMM versucht beim ersten Prop-Spawn, sie asynchron mit Standardwerten zu erzeugen (aktiviert, keine Skripte), ohne bestehende Dateien zu überschreiben. Ergänze die Skriptnamen, führe /fmm reload erfolgreich aus und platziere einen neuen Prop. YAML- und Lua-Änderungen erfordern beide einen Reload.

  • Die Konfiguration hat isEnabled: false. Öffne die .yml-Datei neben der Modelldatei und setze isEnabled: true.

  • Die scripts:-Liste ist leer. Füge deine Skriptdateinamen hinzu:

    isEnabled: true
    scripts:
    - my_script.lua
  • Der .yml-Dateiname stimmt nicht mit dem Modelldateinamen überein. Die Konfiguration muss denselben Basisnamen wie die Modelldatei haben. Zum Beispiel braucht torch_01.fmmodel eine torch_01.yml im selben Verzeichnis.


2. Skriptdatei nicht gefunden​

Symptom: Konsole zeigt: [FMM Scripts] Script 'my_script.lua' not found in scripts/ folder

Ursachen und Lösungen:

  • Falsches Verzeichnis. Skriptdateien müssen in plugins/FreeMinecraftModels/scripts/ sein, nicht neben der Modelldatei.

  • Abweichender Dateiname. Der Name in der .yml-Konfiguration muss einschließlich Groß-/Kleinschreibung dem Dateinamen im scripts/-Ordner entsprechen. Die Endung .lua wird in der Konfiguration empfohlen, ist aber optional; FMM ergänzt sie intern, wenn du sie weglässt.

  • Datei nicht vorhanden. Überprüfe nochmals, ob die .lua-Datei tatsächlich im scripts/-Ordner existiert.

  • Zwischengespeicherte Definition oder doppelte Dateinamen. FMM durchsucht Unterordner bei der Initialisierung des Providers, bindet Skripte aber anhand des Dateinamens, nicht des relativen Pfads. Verwende ordnerübergreifend eindeutige Namen. Neue oder geänderte Lua-Dateien erfordern einen erfolgreichen /fmm reload; ein erneuter Prop-Spawn liest den zwischengespeicherten Lua-Code nicht neu ein.


3. Skript wird überhaupt nicht geladen​

Symptom: Kein Konsolenfehler, aber keine Hooks feuern.

Überprüfe die Serverkonsole auf Lua-Syntaxfehler beim Start. Die häufigsten Ursachen sind:

  • Fehlendes end zum Schließen einer Funktion oder eines if-Blocks
  • Nicht übereinstimmende Klammern
  • Fehlendes Komma zwischen Hook-Definitionen in der zurückgegebenen Tabelle
  • Datei endet nicht mit .lua

Fehler beim Einlesen erscheinen als Warnungen über ungültige oder eingestellte Skripte oder als Ladefehler. Hook- und Callback-Fehler verwenden einen [Lua]-Block mit Dateiname und Ausführungskontext; eine Zeilennummer erscheint nur, wenn sie aus der Ausnahme ermittelt werden kann.


4. Hook wird nie ausgelöst​

Symptom: Deine eigene Start-Protokollmeldung erscheint, aber ein bestimmter Hook wird nie ausgelöst.

Überprüfe, ob der Hook-Name exakt so geschrieben ist wie in der Hook-Referenz aufgeführt. Häufige Fehler:

Falscher NameKorrekter Name
on_clickon_right_click oder on_left_click
on_interacton_right_click
on_hiton_left_click
on_tickon_game_tick
on_removeon_destroy (läuft nur, wenn der Prop endgültig entfernt wird, nicht beim Entladen eines Chunks oder beim Neuladen)
on_arrow_hiton_projectile_hit

Prüfe, ob das Modell geladen ist, eine nutzbare Hitbox besitzt und kein Schutz-Listener die Interaktion vor dem Prop-Callback abgebrochen hat. Ein gespawnter Prop verwendet einen Rüstungsständer; eine Konfigurationsoption macht ein ungültiges oder ungeladenes Modell nicht anklickbar.


5. Timeout / Ausführungsbudget überschritten​

Symptom: Die Konsole meldet einen Lua-Fehler aus der VM. Ein Aufruf, der das Budget überschreitet, wird während der Ausführung mit einer dieser Meldungen abgebrochen:

Lua instruction budget exceeded (250000 instruction limit)
Lua CPU-time budget exceeded (50ms current-thread CPU limit)
Lua elapsed-time fallback budget exceeded (250ms fallback; current-thread CPU time unavailable)

Jeder Hook, Callback und jede Dateiauswertung darf höchstens 250.000 Lua-Instruktionen ausführen oder 50 ms CPU-Zeit des aktuellen Threads verbrauchen, je nachdem, welche Grenze zuerst erreicht wird. Verschachtelte Aufrufe teilen dasselbe Budget. Falls die JVM keine CPU-Zeit pro Thread unterstützt, gilt stattdessen eine Grenze von 250 ms verstrichener Zeit; nur dann erscheint die dritte Meldung. Das Skript erledigt zu viel Arbeit in einem einzelnen Aufruf. Häufige Ursachen:

  • Zu viele Entitäten in on_game_tick durchlaufen
  • Zu viele Partikel pro Tick erzeugen
  • Aufwendige Schleifen ausführen, ohne die Arbeit über mehrere Ticks zu verteilen

Lösung: Begrenze aufwendige Arbeit mit context.cooldowns oder verwende scheduler:run_repeating() mit einem angemessenen Intervall anstelle von on_game_tick.

Das Instruktionslimit begrenzt Lua-Schleifen. Zeitprüfungen erfolgen alle 1.024 Lua-Instruktionen und bei der Rückkehr; sie unterbrechen keine blockierende Java/Bukkit-Methode während des Aufrufs. Begrenze auch die Arbeit innerhalb jedes Callbacks.


6. context.event ist nil in einem Klick-Hook​

Das sollte normalerweise nicht für on_left_click und on_right_click passieren, aber schütze immer mit:

if context.event then
context.event.cancel()
end

Innerhalb von Scheduler-Callbacks ist context.event immer nil -- das ist erwartetes Verhalten. Event-Modifikation kann nur innerhalb des Event-Hooks selbst erfolgen.


7. Animation wird nicht abgespielt​

Symptom: play_animation() gibt false zurück, oder es passiert nichts Sichtbares.

Ursachen und Lösungen:

  • Falscher Animationsname. Der Name muss exakt mit dem übereinstimmen, was in der Modelldatei definiert ist. Überprüfe deine .bbmodel- oder .fmmodel-Datei auf den korrekten Animationsnamen.

  • Modell hat keine Animationen. Nicht alle Modelle haben Animationen. Überprüfe, ob die Modelldatei tatsächlich Animationsdaten enthält.

  • Dem Betrachter fehlt das Paket oder er befindet sich außerhalb der Modellsichtweite. Spieler benötigen sowohl das aktuelle Ressourcenpaket als auch ein sichtbares Modell.

  • Die Anfrage wartet oder wird unterdrückt. true für blend nutzt einen einzigen Warteschlangenplatz, keine Überblendung. Eine spätere Anfrage kann ihn ersetzen. Ein Angriff während einer benutzerdefinierten Animation kann true zurückgeben, ohne diese zu unterbrechen. Verwende die Positionsargumente unter Animationen und unterscheide eine angenommene Anfrage von einem angezeigten Bild.


8. Partikel erscheinen nicht​

  • Partikelnamen akzeptieren FLAME und flame; die API vereinheitlicht die Großschreibung vor der Bukkit-Suche.
  • Überprüfe, ob die Position in einem geladenen Chunk ist. Wenn keine Spieler in der Nähe sind, kann der Chunk entladen sein.
  • Überprüfe, ob count mindestens 1 ist.
  • Einige Partikel (wie DUST) benötigen spezifische Zusatzdaten, die das Basis-spawn_particle() möglicherweise nicht unterstützt. Verwende Standard-Partikel wie FLAME, HEART, HAPPY_VILLAGER, NOTE, ENCHANT, etc.

9. Sound wird nicht abgespielt​

  • Sounds akzeptieren Bukkit-Enum-Namen unabhängig von der Großschreibung oder gültige Schlüssel wie minecraft:block.note_block.harp und eigene Ressourcenpaket-Schlüssel. Der Sound muss im Ressourcenpaket des Clients vorhanden sein.
  • Überprüfe, ob die Positionskoordinaten korrekt sind (nicht alles Nullen oder NaN).
  • Überprüfe, ob die Lautstärke größer als 0 ist.

10. State setzt sich unerwartet zurück​

context.state gehört zu einer Skriptinstanz. Getrennte Einträge in der scripts:-Liste desselben Props teilen ihn nicht. Er bleibt während der aktiven Instanz erhalten. Falls er sich scheinbar zurücksetzt:

  • Der Prop könnte entfernt und erneut gespawnt worden sein, oder sein Chunk wurde entladen und wieder geladen. Jeder Spawn und jedes Laden des Chunks erstellt eine neue Instanz. Speichere Werte, die das überstehen müssen, mit prop:set_persistent_data.
  • Ein erfolgreicher Reload oder ein Skriptfehler kann die alte Instanz beendet haben. Prüfe die Konsole; eine Dateikorrektur reaktiviert keine beendete Instanz ohne erneutes Laden und Binden.
  • Du liest möglicherweise State in einem Scheduler-Callback mit der falschen Context-Variable. Verwende immer den eigenen Context-Parameter des Callbacks.

11. Die os-Bibliothek ist nicht verfügbar​

Symptom: Das Skript stürzt mit einem Fehler wie attempt to index nil (os) ab.

Die os-Bibliothek ist vollständig aus der Lua-Sandbox entfernt. Du kannst weder os.time(), os.clock() noch irgendeine andere os-Funktion verwenden.

Lösung: Verwende context.world:get_time() für die Weltzeit oder verfolge die verstrichene Zeit manuell über State-Flags und Tick-Zähler in on_game_tick. Für Abklingzeiten ist context.cooldowns:check_local(key, ticks) vorzuziehen.


12. Item-Skript wird nicht aktiviert​

Das Skriptformat für ausgerüstete Items wurde eingestellt. Ein benutzerdefiniertes Item mit einer nicht leeren scripts:-Liste oder alten Verzauberungen ohne Namespace wird übersprungen und protokolliert. Ersetze es durch eine aktuelle benutzerdefinierte Item-Definition und Verzauberungen mit Namespace. Prop-Skripte verwenden weiterhin scripts:.

13. Item-Definition nicht verfügbar​

Prüfe die Warnung beim Start oder Reload auf den genauen Dateinamen. Auch ein nicht verfügbares Material, etwa ein neuerer Minecraft-Item-Typ auf einem älteren Server, führt dazu, dass das Item übersprungen wird. Korrigiere oder ersetze die Definition passend zu deiner Serverversion; FMM schreibt sie nicht um und erfindet kein Ersatzmaterial.

14. Hook wird ausgelöst, aber das Skript stürzt bei Entity-Methoden ab​

Symptom: Ein Hook wie on_right_click funktioniert, aber das Skript stürzt innerhalb eines geplanten Callbacks mit Fehlern wie attempt to call nil ab, wenn entity:damage() oder entity:push() aufgerufen wird.

Ursache: context.world:get_nearby_entities() gibt ALLE Entities in Reichweite zurück, einschließlich nicht lebender Entities wie fallengelassener Items, Erfahrungskugeln und Area-Effect-Clouds. Diese besitzen keine Living-Entity-Methoden wie damage(), push() oder add_potion_effect().

Lösung: Sichere Aufrufe immer mit if entity.damage then ab, bevor du Living-Entity-Methoden aufrufst:

local entities = context.world:get_nearby_entities(x, y, z, radius)
for _, entity in ipairs(entities) do
if entity.damage then
-- Safe to call living entity methods
entity:damage(5.0)
entity:push(0, 0.5, 0)
end
end

15. Nur im Event vorhandene Daten fehlen in geplanten Callbacks​

Symptom: Daten aus dem Klick-/Kampf-Event fehlen innerhalb eines scheduler:run_later()- oder scheduler:run_repeating()-Callbacks.

Ursache: Geplante Callbacks erhalten einen frischen Context und behalten weder das ursprüngliche Bukkit-Event noch den generischen Zonen-Akteur. context.event ist in Callbacks immer nil. Prop-Skripte erhalten context.player auf oberster Ebene nur während spielergetriebener Hooks wie Klicks und Zonenein- oder -austritten. Wenn sie den Spieler später benötigen, sollten sie ihn im ursprünglichen Hook aus context.player oder context.event.player festhalten.

Lösung: Verwende den frischen Context des Callbacks für den normalen Welt- und State-Zugriff. Benötigst du genau den Spieler oder die Entity des ursprünglichen Events, halte sie vor dem Planen fest und prüfe später, ob sie noch gültig sind.

on_right_click = function(context)
local clicked_player = context.player or (context.event and context.event.player)

context.scheduler:run_later(20, function(later_context)
-- Prop scripts should use a captured player when they need the clicker.
local player = later_context.player or clicked_player
if player and player.is_valid then
player:send_message("&aDelayed message!")
end
end)
end

Wie die verzögerte Konfigurationserzeugung funktioniert​

Das Verständnis des verzögerten Konfigurationssystems hilft, Verwirrung bei der Einrichtung neuer Props zu vermeiden:

  1. Erster Spawn: Wenn ein Prop spawnt und keine begleitende .yml-Datei existiert, erstellt FMM die Konfiguration asynchron. Das bedeutet, die Datei wird in einem Hintergrund-Thread geschrieben und ist nicht sofort verfügbar.

  2. Standardwerte: Die generierte Konfiguration hat isEnabled: true und eine leere scripts: []-Liste.

  3. Keine Skripte beim ersten Spawn: Da die Konfiguration erstellt wird, nachdem der Prop bereits gespawnt hat (und keine Skripte auflistet), wird der Prop bei seinem ersten Spawn keine Skripte angehängt haben.

  4. Bearbeiten, neu laden und erneut spawnen: Ergänze die Skriptnamen und führe /fmm reload erfolgreich aus. Prop-Einstellungen, Platzierungsprüfungen und Skriptlisten im Menü nutzen denselben vorbereiteten Katalog. Ein neuer Prop verwendet diesen Katalog und liest geändertes YAML nicht neu ein. Auch Lua-Änderungen erfordern einen Reload.

  5. Konfigurationsort: Die .yml-Datei wird im selben Verzeichnis wie die Modelldatei erstellt, mit demselben Basisnamen. Zum Beispiel:

    • Modell: plugins/FreeMinecraftModels/models/fountain.fmmodel
    • Konfiguration: plugins/FreeMinecraftModels/models/fountain.yml
Schneller Einrichtungs-Workflow
  1. Modell in models/ platzieren
  2. Skript in scripts/ platzieren
  3. Den Prop einmal spawnen (generiert die .yml)
  4. Die .yml bearbeiten und scripts: [my_script.lua] hinzufügen
  5. Mit einem Reload die Lua-Datei einlesen, einen neuen Prop platzieren und seine eigene Startmeldung prüfen

Fehlermeldungen lesen​

Hook- und Callback-Fehler erzeugen einen [Lua]-Block mit Dateiname und Ausführungskontext. Eine Zeilennummer wird angegeben, wenn die Ausnahme eine erkennbare Quelltextposition enthält. Fehler beim Einlesen oder in der Konfiguration können andere Warnungen verwenden.

Ein typischer Fehler sieht so aus:

[Lua] Error in 'my_door.lua' at line 12 during 'on_right_click':
[Lua] -> You tried to call a method or function that doesn't exist.
[Lua] -> Check the method name for typos, or make sure you're using ':' (colon) for method calls, not '.' (dot).
[Lua] -> Script has been disabled for this entity to prevent further errors.

Das System übersetzt häufige Lua-Fehler in verständliche Sprache:

Roher Lua-FehlerWas die Konsole dir sagt
attempt to call nilDu hast versucht, eine Methode oder Funktion aufzurufen, die nicht existiert. Prüfe auf Tippfehler und : vs . Verwendung.
index expected, got nilDu hast versucht, auf ein Feld von etwas zuzugreifen, das nil ist. Prüfe, ob vorheriger Code es initialisiert hat.
attempt to indexDu hast versucht, auf eine Eigenschaft eines nil- oder ungültigen Werts zuzugreifen.
bad argumentEine Funktion hat den falschen Argumenttyp erhalten. Die Nachricht zeigt den erwarteten vs. tatsächlichen Typ.
Budget überschrittenLua instruction budget exceeded (250000 instruction limit) oder Lua CPU-time budget exceeded (50ms current-thread CPU limit) — der Aufruf wurde während der Ausführung abgebrochen und die Skriptinstanz deaktiviert.
Tipp

Lies immer die vollständige [Lua]-Fehlermeldung, bevor du in den Code eintauchst. Sie zeigt dir normalerweise direkt die Lösung.


Gehe nicht davon aus, dass undokumentierte Methoden existieren​

Die FMM-Lua-API stellt einen bestimmten Satz von Methoden bereit. Gehe nicht davon aus, dass Kurzformen oder alternative Namen existieren. Häufige Fehler:

  • context.prop:get_location() -- existiert nicht. Verwende context.prop.current_location (ein Feld, keine Methode).
  • context.prop:set_animation("open") -- existiert nicht. Verwende context.prop:play_animation("open", true, true).
  • context.event:setCancelled(true) -- existiert nicht. Verwende context.event.cancel().
  • context.cooldowns:check_global(...) -- existiert nicht. Verwende context.cooldowns:global_ready() und context.cooldowns:set_global(ticks) für globale Cooldowns.
  • context.player -- wird nur für FMM-Prop-Hooks mit Spielerbezug gesetzt, etwa Klicks, Ein-/Austritte bei überwachten Zonen und on_projectile_hit, wenn ein Spieler das Projektil abgeschossen hat. Es ist kein allgemeines Feld für den Prop-Besitzer und bleibt in Prop-Lebenszyklus-Hooks und Scheduler-Callbacks nil.
  • context.boss -- existiert nicht. Das ist EliteMobs. Verwende context.prop in FMM.

Prüfe unterstützte Methoden in der Prop-API und den dort verlinkten gemeinsamen Referenzen. Ein plausibler Name beweist nicht, dass die Methode existiert.


Debug-Tipps​

1. Verwende context.log:info() großzügig​

Füge Log-Nachrichten bei jedem Schritt beim Debuggen hinzu:

on_right_click = function(context)
context.log:info("Right click received!")
context.log:info("Event present: " .. tostring(context.event ~= nil))
context.log:info("State is_open: " .. tostring(context.state.is_open))
end

2. Überprüfe die Konsole beim Start​

Füge context.log:info("Prop script started") zu on_spawn hinzu und platziere nach dem Reload einen neuen Prop. Prüfe die Konsole auf deine Meldung sowie auf Skript- und Konfigurationsfehler. FMM gibt nicht für jede Bindung eine Erfolgsmeldung aus.

3. Überprüfe den Modelldateipfad​

Die .yml-Konfiguration muss ein Geschwister der Modelldatei sein (gleiches Verzeichnis, gleicher Basisname). Überprüfe dies durch:

  • Modelldateipfad: plugins/FreeMinecraftModels/models/my_model.fmmodel
  • Konfigurationsdateipfad: plugins/FreeMinecraftModels/models/my_model.yml

4. Teste Hooks einzeln​

Wenn du ein komplexes Skript erstellst, beginne damit, jeden Hook isoliert zu testen. Füge jeweils einen Hook hinzu und überprüfe, ob er feuert, bevor du zum nächsten übergehst.

5. Prüfe auf Tippfehler in Hook-Namen​

Der häufigste Grund für einen nicht ausgelösten Hook ist ein falsch geschriebener Hook-Name. Ein unbekannter Hook führt bei der Validierung zur Ablehnung des Skripts. Prüfe den Konsolenfehler, statt davon auszugehen, dass die übrigen Hooks dieser Datei ausgeführt werden.


Lernpfad für Einsteiger​

Wenn du dieses System von Grund auf lernen möchtest, funktioniert dieser Lernpfad gut:

  1. Schreibe eine Datei mit nur api_version = 1 und on_spawn, das eine Nachricht protokolliert.
  2. Füge das Skript zu einer Prop-Konfiguration hinzu und überprüfe, ob die Log-Nachricht erscheint.
  3. Füge on_right_click hinzu und protokolliere, wenn der Prop angeklickt wird.
  4. Füge on_left_click mit context.event.cancel() hinzu, um den normalen Klickschaden zu unterdrücken.
  5. Spiele eine Animation bei Rechtsklick ab.
  6. Füge einen Sound bei Rechtsklick hinzu.
  7. Füge State und Umschaltverhalten hinzu.
  8. Füge eine Zonenüberwachung für Näherungserkennung hinzu.
  9. Gehe erst dann zu komplexen Multi-Hook-Skripten mit Schedulern über.

Jeder Schritt baut auf dem vorherigen auf, und du kannst in jeder Phase testen.


Nächste Schritte​

  • Erste Schritte -- Dateistruktur, Hooks, erste Skript-Erklärung, Kopier-Vorlagen
  • Prop-API -- vollständige API-Referenz
  • Beispiele & Muster -- vollständige funktionierende Skripte, die du studieren und anpassen kannst