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.
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 reloaderfolgreich 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 setzeisEnabled: 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 brauchttorch_01.fmmodeleinetorch_01.ymlim 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 imscripts/-Ordner entsprechen. Die Endung.luawird 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 imscripts/-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
endzum Schließen einer Funktion oder einesif-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 Name | Korrekter Name |
|---|---|
on_click | on_right_click oder on_left_click |
on_interact | on_right_click |
on_hit | on_left_click |
on_tick | on_game_tick |
on_remove | on_destroy (läuft nur, wenn der Prop endgültig entfernt wird, nicht beim Entladen eines Chunks oder beim Neuladen) |
on_arrow_hit | on_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_tickdurchlaufen - 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.
truefürblendnutzt einen einzigen Warteschlangenplatz, keine Überblendung. Eine spätere Anfrage kann ihn ersetzen. Ein Angriff während einer benutzerdefinierten Animation kanntruezurü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
FLAMEundflame; 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
countmindestens 1 ist. - Einige Partikel (wie
DUST) benötigen spezifische Zusatzdaten, die das Basis-spawn_particle()möglicherweise nicht unterstützt. Verwende Standard-Partikel wieFLAME,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.harpund 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:
-
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. -
Standardwerte: Die generierte Konfiguration hat
isEnabled: trueund eine leerescripts: []-Liste. -
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.
-
Bearbeiten, neu laden und erneut spawnen: Ergänze die Skriptnamen und führe
/fmm reloaderfolgreich 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. -
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
- Modell:
- Modell in
models/platzieren - Skript in
scripts/platzieren - Den Prop einmal spawnen (generiert die
.yml) - Die
.ymlbearbeiten undscripts: [my_script.lua]hinzufügen - 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-Fehler | Was die Konsole dir sagt |
|---|---|
attempt to call nil | Du hast versucht, eine Methode oder Funktion aufzurufen, die nicht existiert. Prüfe auf Tippfehler und : vs . Verwendung. |
index expected, got nil | Du hast versucht, auf ein Feld von etwas zuzugreifen, das nil ist. Prüfe, ob vorheriger Code es initialisiert hat. |
attempt to index | Du hast versucht, auf eine Eigenschaft eines nil- oder ungültigen Werts zuzugreifen. |
bad argument | Eine Funktion hat den falschen Argumenttyp erhalten. Die Nachricht zeigt den erwarteten vs. tatsächlichen Typ. |
| Budget überschritten | Lua 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. |
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. Verwendecontext.prop.current_location(ein Feld, keine Methode).context.prop:set_animation("open")-- existiert nicht. Verwendecontext.prop:play_animation("open", true, true).context.event:setCancelled(true)-- existiert nicht. Verwendecontext.event.cancel().context.cooldowns:check_global(...)-- existiert nicht. Verwendecontext.cooldowns:global_ready()undcontext.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 undon_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-Callbacksnil.context.boss-- existiert nicht. Das ist EliteMobs. Verwendecontext.propin 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:
- Schreibe eine Datei mit nur
api_version = 1undon_spawn, das eine Nachricht protokolliert. - Füge das Skript zu einer Prop-Konfiguration hinzu und überprüfe, ob die Log-Nachricht erscheint.
- Füge
on_right_clickhinzu und protokolliere, wenn der Prop angeklickt wird. - Füge
on_left_clickmitcontext.event.cancel()hinzu, um den normalen Klickschaden zu unterdrücken. - Spiele eine Animation bei Rechtsklick ab.
- Füge einen Sound bei Rechtsklick hinzu.
- Füge State und Umschaltverhalten hinzu.
- Füge eine Zonenüberwachung für Näherungserkennung hinzu.
- 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