Luaスクリプティング:トラブルシューティング
このページでは、Luaパワーの作成やデバッグ時に遭遇する一般的な問題と、EliteScriptから移行する作者向けのアドバイスを取り上げます。NPCスクリプトをデバッグしている場合はNPCスクリプトを参照してください。動作する例を探している場合は例とパターンを参照してください。はじめての方ははじめにを参照してください。
EliteMobsは、Nightbreakプラグイン全体で共有されるMagmaCore Luaスクリプティングエンジンを使用しています。サンドボックス、スケジューラ、ゾーン、ワールドAPI、エンティティテーブル、プレイヤーUIメソッドなどの共有概念のドキュメントについては、MagmaCore Luaスクリプティングエンジンページを参照してください。
一般的な問題
1. パワーがまったく読み込まれない
サーバー起動時のコンソールのエラーを確認してください。最も一般的な原因はLua構文エラー(endの欠落、括弧の不一致など)です。ファイルが.luaで終わり、正しいpowersディレクトリに配置されていることも確認してください。
2. フックが発火しない
フック名がフックリストに記載されている通りに正確に綴られていることを確認してください。よくある間違い:on_boss_hit(誤り)vs. on_boss_damaged_by_player(正しい)、またはon_tick(誤り)vs. on_game_tick(正しい)。
3. context.playerがnil
context.player が設定されるのは、背後のイベントがプレイヤーを伴うフックだけです。on_spawn、on_game_tick、on_boss_damaged、on_boss_damaged_by_elite、on_exit_combat、on_heal、on_death、on_phase_switch では常に nil です。
設定されるのは on_boss_damaged_by_player、on_player_damaged_by_boss、on_enter_combat、on_boss_target_changed、および on_zone_enter / on_zone_leave で関与するエンティティがたまたまプレイヤーだった場合です。使用する前には必ずnilガードを追加してください。完全な表はフックとライフサイクルを参照してください。
4. タイムアウト / 実行予算超過
各フック、予約されたコールバック、ファイル評価には、ネストした呼び出しと共有する上限として Lua 命令250,000回とスレッド CPU 時間50 msが適用されます。JVM が CPU 時間を測定できない場合は、代わりに経過時間250 msを使用します。エラーには超過した上限が表示されます。
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)
VM は暴走した Lua ループを実行途中で中断します。ただし、実行中の Java API 呼び出しは中断できず、呼び出し終了後に別の50 ms制限が適用されることもありません。クールダウン、サンプリング数の削減、context.scheduler:run_every(...) を使って処理を複数のティックに分散してください。
5. スケジューラコールバックが古いデータを使用している
おそらくコールバックパラメータではなく外側のcontextを使用しています。function() ... context.boss ... endをfunction(tick_context) ... tick_context.boss ... endに変更してください。
6. ゾーンクエリがエンティティを返さない
ゾーン定義を再確認してください。ネイティブゾーンの場合、kindが小文字であることを確認してください("sphere"であって"SPHERE"ではない)。スクリプトユーティリティの場合、shapeが大文字であることを確認してください("CONE"であって"cone"ではない)。また、originまたはTargetが実際に有効な位置に解決されることも確認してください。
7. パーティクルが表示されない
有効な Bukkit Particle 名を使ってください。"FLAME" と "flame" はどちらも大文字に正規化されます。位置、数、必要な追加データも確認してください。BLOCK にはこのワールドヘルパーが提供しないブロックデータが必要です。対応する別の粒子か、素材オプションを持つスクリプト用パーティクル API を使ってください。
8. クールダウンが機能していないように見える
check_local(key, duration)(1回の呼び出しでチェックと設定の両方を行う)を使用していることを確認してください。local_ready(key)の後に別途set_local(duration, key)を呼ぶ形ではありません。local_readyだけを使うと、チェックするだけでクールダウンを設定しません。
9. ボスが死後もパワーを実行し続ける
on_exit_combatおよび/またはon_deathにクリーンアップロジックを追加して、スケジューラタスクをキャンセルしてください。ボスが死ねばon_exit_combatは発火するはずですが、両方のフックに明示的なクリーンアップを追加しておくほうが安全です。
エラーメッセージの読み方
Luaパワーで何か問題が発生すると、コンソールは[Lua]プレフィックス付きの分かりやすいエラーブロックを出力します。これらのメッセージは、どのファイル、どの行、どのフックで、何が問題だったかを平易な言葉で正確に教えてくれます。デバッグの前に、必ずメッセージ全体を読んでください。
典型的なエラーは次のようになります。
[Lua] Error in 'push_zone.lua' at line 35 during 'on_boss_damaged_by_player':
[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.
このシステムは、よくあるLuaエラーを平易な言葉に翻訳します。最も一般的なものは次のとおりです。
| 生のLuaエラー | コンソールが教えてくれること |
|---|---|
attempt to call nil | 存在しないメソッドまたは関数を呼び出そうとしました。メソッド名のタイプミスを確認するか、メソッド呼び出しに.(ドット)ではなく:(コロン)を使用していることを確認してください。 |
index expected, got nil | nilであるものに対してフィールドにアクセスしようとしました。それより前のコードで初期化されていることを確認してください。 |
attempt to index | nilまたは無効な値のプロパティにアクセスしようとしました。 |
bad argument | 具体的な引数の不一致の詳細を表示します(期待される型 vs. 実際の型)。 |
| バジェット超過 | 命令数、CPU 時間、または代替の経過時間のどの上限を超えたかが表示されます。上記の説明を参照してください。 |
コンソールに[Lua]エラーが表示されたら、エラーメッセージが、どのファイル、どの行、どのフックで、何が問題だったかを平易な言葉で正確に教えてくれます。コードに飛び込む前にメッセージ全体を読んでください -- 通常、修正方法を直接示してくれます。
文書化されていないエイリアスが存在すると仮定しないでください
Lua APIは特定のメソッド名のセットを公開しています。手動またはAIの支援でパワーを書く場合、短縮名や代替名が存在すると仮定しないでください。以下は存在せず、エラーの原因となる名前の例です。
show_temporary_boss_bar()-- 代わりにplayer:show_boss_bar(title, color, style, duration)を使用してください。run_command_as_player()-- 代わりにplayer:run_command(command)を使用してください。em.location(...)-- メソッド名が誤りです。代わりにem.create_location(x, y, z)、またはcontext.boss:get_location()/context.player.current_locationを使用してください。em.vector(...)-- メソッド名が誤りです。代わりにem.create_vector(x, y, z)、またはプレーンな{x=0, y=1, z=0}テーブルを使用してください。em.zone.sphere(...)-- メソッド名が誤りです。代わりにem.zone.create_sphere_zone(radius)、または{kind = "sphere", radius = 5, origin = location}のようなゾーン定義テーブルを使用してください。entity:teleport_to(...)--entity:teleport_to_location(location)を使用してください。entity:set_velocity(...)--entity:set_velocity_vector(vector)を使用してください。entity:set_facing(...)--entity:face_direction_or_location(direction_or_location)を使用してください。
疑問がある場合は、APIリファレンスページを確認してください(ボスとエンティティ、ワールドと環境、ゾーンとターゲティング)。そこに文書化されていなければ、存在しません。
EliteScript作者向けの移行アドバイス
すでに良いEliteScriptを書けるなら、Luaパワーを学ぶ最も簡単な方法は次のとおりです。
-
イベント、ターゲット、ゾーン、相対ベクトル、パーティクルの観点で考え続けてください。 概念は同じです -- 構文だけが変わります。EliteScriptのイベントは
on_spawnやon_boss_damaged_by_playerのようなフック名になります。ターゲットとゾーンは、EliteScriptゾーンとEliteScriptターゲットのページに文書化されている同じフィールド名を使い、テーブルとしてcontext.scriptに渡されます。 -
制御フローをLuaに移行してください。 ランダムロール、共有ヘルパー関数、ループ、永続ステート(
context.state)、タスクスケジューリング(context.scheduler)は、純粋なEliteScriptでは簡単にできない、Luaが追加するものです。まずは1つの分岐や条件付きパワーをLuaに変換し、他はすべて同じに保つことから始めてください。 -
ターゲティングとゾーンジオメトリには
context.scriptを使用してください。 これらは単なる見た目の類似ではありません -- 渡した spec テーブルは実際のEliteScriptエンジンへ引き渡されるため、EliteScriptページで文書化されているすべてのフィールド(targetType、shape、Target、Target2、FinalTarget、range、offset、relativeOffset、coverage、filterなど)が、同じデフォルト値と同じ大文字小文字でそのまま機能します。EliteScriptのドキュメントを仕様リファレンスとして開いたまま、Luaは純粋にロジック層のために使ってください。 -
2つのゾーンシステムに注意してください。
context.script:zone({shape = "SPHERE", ...})はEliteScriptエンジンです(大文字のenum、Targetの spec テーブル)。context.zonesは別の軽量な実装です(小文字のkind、素のorigin/destinationの location)。キーの書き方を混在させると、警告なく空のゾーンが生成されます。
初心者の学習パス
このシステムをゼロから学びたい場合、次の段階を踏むとうまくいきます。
api_version = 1とon_spawnだけのファイルを書く。- ボスにメッセージを送信させるか、サウンドを再生させる。
context.cooldownsでクールダウンを追加する。on_boss_damaged_by_playerのような、プレイヤーがトリガーするフックを1つ追加する。context.scheduler:run_after(...)で遅延アクションを1つ追加する。- 単純なネイティブLuaゾーンクエリ、または単純な
context.script:target(...)を1つ追加する。 - それからやっと、回転攻撃、ステートマシン、マルチステップのメカニクスに進む。
各ステップは前のステップの上に積み上がり、どの段階でもテストできます。最初のLuaパワーとしてマルチフェーズのボスを書こうとしないでください。
