Luaスクリプティング: プロップ&アイテムAPI
このページでは、FreeMinecraftModelsのプロップおよびアイテムスクリプトで利用可能なすべてのAPIについて説明します:context.prop、context.item、context.event、context.player、context.world、context.zones、context.scheduler、context.state、context.cooldowns、context.log。スクリプティングが初めての場合は、まずはじめにを参照してください。
context.prop
propテーブルは、プロップエンティティの情報とそのアニメーションを制御するメソッドを提供します。FMMはパフォーマンスのためにこのテーブルをプロップごとにキャッシュします。current_locationなどのフィールドは遅延評価のライブ値なので、読み取れば常に現在のプロップの状態が反映されます。
フィールド
| フィールド | 型 | 備考 |
|---|---|---|
prop.model_id | string | 設計図モデル名(例:"torch_01") |
prop.current_location | location テーブル | プロップの現在の位置。これは一度きりのスナップショットではなく、遅延評価のライブフィールドです。 |
locationテーブルには標準フィールドがあります:x、y、z、world、yaw、pitch。
例:propの情報を読み取る
return {
api_version = 1,
on_spawn = function(context)
context.log:info("Prop spawned: " .. (context.prop.model_id or "unknown"))
local loc = context.prop.current_location
if loc then
context.log:info("Location: " .. loc.x .. ", " .. loc.y .. ", " .. loc.z)
end
end
}
prop:play_animation(name, blend, loop)
プロップモデルで名前付きアニメーションを再生します。
| パラメータ | 型 | デフォルト | 備考 |
|---|---|---|---|
name | string | 必須 | モデルファイルで定義されたアニメーション名 |
blend | boolean | true | 現在のアニメーションとブレンドするかどうか |
loop | boolean | true | アニメーションがループするかどうか |
アニメーションが見つかって開始された場合はtrue、それ以外はfalseを返します。
例
return {
api_version = 1,
on_right_click = function(context)
local success = context.prop:play_animation("open", true, false)
if not success then
context.log:warn("Animation 'open' not found on this model!")
end
end
}
prop:stop_animation()
プロップで現在再生中のすべてのアニメーションを停止します。
パラメータなし。
例
return {
api_version = 1,
on_right_click = function(context)
context.prop:stop_animation()
end
}
prop:hurt_visual()
プロップに実際のダメージを与えずに、視覚的なダメージアニメーション(赤いティントフラッシュ)を再生します。
パラメータなし。
例
return {
api_version = 1,
on_left_click = function(context)
-- 殴られた時に赤くフラッシュするが、実際にはダメージを受けない
if context.event then
context.event.cancel()
end
context.prop:hurt_visual()
end
}
prop:pickup()
プロップをワールドから削除し、その場所に配置用の紙アイテムをドロップします。ドロップされたアイテムはブロックを右クリックすることでプロップを再配置できます。
パラメータはなく、戻り値もありません。削除とドロップの処理はBukkitのメインスレッドにキューイングされます。
例
return {
api_version = 1,
on_right_click = function(context)
-- プレイヤーが右クリックでプロップを拾えるようにする
context.prop:pickup()
end
}
prop:mount(player)
プロップ上の最初の利用可能なマウントポイントシートにプレイヤーを乗せます。モデルにはマウントポイントボーンが定義されている必要があります。
| パラメータ | 型 | 備考 |
|---|---|---|
player | エンティティテーブル | プレイヤーのエンティティテーブル(例:context.playerやcontext.event.playerから) |
プレイヤーが見つかり、プロップにマウントポイントがあり、マウント処理がキューイングされた場合はtrueを返します。これらの基本的な参照が有効でない場合はfalseを返します。trueが返っても、キューイングされた処理が実行された時点で最終的にシートが空いていたことまでは保証されません。
例
return {
api_version = 1,
on_right_click = function(context)
local player = context.event and context.event.player
if player then
context.prop:mount(player)
end
end
}
prop:dismount(player)
プロップ上のマウントポイントシートからプレイヤーを降ろします。
| パラメータ | 型 | 備考 |
|---|---|---|
player | エンティティテーブル | プレイヤーのエンティティテーブル |
プレイヤーが見つかり、プロップにマウントマネージャーがあり、降車チェックがキューイングされた場合はtrueを返します。これらの基本的な参照が有効でない場合はfalseを返します。
例
return {
api_version = 1,
on_right_click = function(context)
local player = context.event and context.event.player
if player then
-- マウント/ディスマウントをトグル
local passengers = context.prop:get_passengers()
for i = 1, #passengers do
if passengers[i].uuid == player.uuid then
context.prop:dismount(player)
return
end
end
context.prop:mount(player)
end
end
}
prop:get_passengers()
プロップ上の現在の乗員すべてに対するエンティティテーブルのLua配列を返します。
パラメータなし。
例
return {
api_version = 1,
on_game_tick = function(context)
local passengers = context.prop:get_passengers()
if #passengers > 0 then
context.log:info("Prop has " .. #passengers .. " passenger(s)")
end
end
}
prop:has_mount_points()
このプロップがモデルにマウントポイントボーンを定義しているかどうかを返します。
パラメータなし。trueまたはfalseを返します。
例
return {
api_version = 1,
on_right_click = function(context)
local player = context.event and context.event.player
if player and context.prop:has_mount_points() then
context.prop:mount(player)
end
end
}
prop:spawn_elitemobs_boss(filename, x, y, z)
指定された位置にEliteMobsカスタムボスをスポーンします。サーバーにEliteMobsがインストールされている必要があります。
| パラメータ | 型 | 備考 |
|---|---|---|
filename | string | カスタムボスのファイル名(例:"my_boss.yml") |
x | number | X座標 |
y | number | Y座標 |
z | number | Z座標 |
スポーンされたボスのリビングエンティティテーブルを返します。EliteMobsがインストールされていないか、ボスファイルが存在しない場合はnilを返します。
例
return {
api_version = 1,
on_right_click = function(context)
local loc = context.prop.current_location
if loc then
local boss = context.prop:spawn_elitemobs_boss("dungeon_guardian.yml", loc.x, loc.y + 1, loc.z)
if boss then
context.log:info("Spawned boss: " .. (boss.name or "unknown"))
else
context.log:warn("Could not spawn boss -- is EliteMobs installed?")
end
end
end
}
prop:open_inventory(player, title, rows)
プレイヤーに対して永続化されるチェストインベントリGUIを開きます。コンテンツはインベントリが閉じられた際にプロップのPersistentDataContainerに保存され、再度開かれたときに復元されます。
| パラメータ | 型 | デフォルト | 備考 |
|---|---|---|---|
player | エンティティテーブル | 必須 | インベントリを表示するプレイヤー |
title | string | 必須 | インベントリのタイトル(&カラーコードに対応) |
rows | int | 3 | 行数(1-6、6は54スロット=ダブルチェスト) |
プロップとプレイヤーの参照が有効で、オープン処理がキューイングされた場合はtrue、それ以外はfalseを返します。行数はインベントリが作成される前に1〜6の範囲へクランプされます。
prop:is_viewing_inventory(player)
指定されたプレイヤーが現在このプロップのインベントリを開いているかどうかを返します。
| パラメータ | 型 | 備考 |
|---|---|---|
player | エンティティテーブル | チェックするプレイヤー |
trueまたはfalseを返します。
例:インベントリが閉じられた時のクローズアニメーション
context.state["task_" .. player.uuid] = context.scheduler:run_repeating(5, 5, function(tick_context)
if not tick_context.prop:is_viewing_inventory(player) then
tick_context.prop:play_animation("close", true, false)
tick_context.scheduler:cancel(tick_context.state["task_" .. player.uuid])
end
end)
prop:place_book(player)
プレイヤーのメインハンドから記入済みまたは書き込み可能な本を取り出して、プロップに保管します。
| パラメータ | 型 | 備考 |
|---|---|---|
player | エンティティテーブル | 本を持っているプレイヤー |
プロップとプレイヤーの参照が有効で、処理がキューイングされた場合はtrueを返します。後続のメインスレッド処理が実際に本を置くのは、プレイヤーが書き込み可能な本または記入済みの本を持っており、かつプロップがまだ本を持っていない場合だけです。
prop:read_book(player)
プレイヤーが読むために保管された本を開きます。
| パラメータ | 型 | 備考 |
|---|---|---|
player | エンティティテーブル | 本を表示するプレイヤー |
プロップとプレイヤーの参照が有効で、読み取り処理がキューイングされた場合はtrueを返します。保管された本が実際に存在したことは保証されません。
prop:take_book(player)
保管された本をプレイヤーのインベントリに返し、プロップから削除します。
| パラメータ | 型 | 備考 |
|---|---|---|
player | エンティティテーブル | 本を渡すプレイヤー |
プロップとプレイヤーの参照が有効で、取り出し処理がキューイングされた場合はtrueを返します。保管された本が実際に存在したことは保証されません。
prop:has_book()
このプロップに本が保管されているかどうかを返します。パラメータなし。
prop:drop_inventory()
保管されたインベントリの内容をすべてプロップの位置にアイテムエンティティとしてドロップし、保管されたデータをクリアします。現在表示中のプレイヤーに対しては自動的にインベントリを閉じます。
パラメータなし。プロップに有効な裏付けのアーマースタンドがあり、ドロップ処理がキューイングされた場合はtrueを返します。保管されたアイテムが実際に存在したことは保証されません。
prop:drop_book()
保管された本をプロップの位置にアイテムエンティティとしてドロップし、保管された本のデータをクリアします。
パラメータなし。プロップに有効な裏付けのアーマースタンドがあり、ドロップ処理がキューイングされた場合はtrueを返します。保管された本が実際に存在したことは保証されません。
prop:set_persistent_data(key, value)
プロップのアーマースタンドのPersistentDataContainerに文字列値を保存します。このデータはサーバー再起動とチャンクアンロードを通じて存続します。
| パラメータ | 型 | 備考 |
|---|---|---|
key | string | 一意のキー名(内部的にfmm_lua_<key>の下に保存されます) |
value | string | 保存する値。数値とブール値にはtostring()を使用してください。 |
成功した場合はtrue、プロップに裏付けのアーマースタンドがない場合はfalseを返します。
prop:get_persistent_data(key)
set_persistent_dataで以前に保存された文字列値を取得します。キーが設定されていない場合はnilを返します。
| パラメータ | 型 | 備考 |
|---|---|---|
key | string | set_persistent_dataで使用されたキー名 |
例:永続的なトグル状態
return {
api_version = 1,
on_spawn = function(context)
local saved = context.prop:get_persistent_data("active")
context.state.active = saved == "true"
end,
on_right_click = function(context)
context.state.active = not context.state.active
context.prop:set_persistent_data("active", tostring(context.state.active))
end
}
context.item
itemテーブルはアイテムスクリプトのみで利用可能です(プロップスクリプトでは利用できません)。カスタムアイテムの情報とそれを操作するメソッドを提供します。このテーブルはフック呼び出しごとに新しく再構築されます。
set_amount、consume、set_uses、set_name、set_lore、耐久度消費系ヘルパーといったアイテムの書き込みメソッドは、変更をBukkitのメインスレッドにキューイングし、nilを返します。読み取りメソッドは、実行された時点で装備されている該当アイテムの状態を返します。
フィールド
| フィールド | 型 | 備考 |
|---|---|---|
item.id | string | アイテムタイプID(YML設定からのfmm_item_id) |
item:material()
アイテムのマテリアル名を文字列として返します(例:"DIAMOND_SWORD"、"STICK")。
item:get_amount() / item:set_amount(n)
アイテムのスタックサイズを取得または設定します。set_amount(n)は変更をキューイングし、nilを返します。
| パラメータ | 型 | 備考 |
|---|---|---|
n | int | 新しいスタック量 |
item:consume(n)
アイテムのスタック量をn(デフォルト1)だけ減らす処理をキューイングします。結果の量が0以下になった場合、アイテムはプレイヤーのインベントリから削除されます。nilを返します。
| パラメータ | 型 | デフォルト | 備考 |
|---|---|---|---|
n | int | 1 | 消費する量 |
item:get_uses() / item:set_uses(n)
アイテムのPersistentDataContainerに保存されたカスタム使用カウンターを取得または設定します。これはバニラ耐久度から独立しており、カスタムの耐久度やチャージシステムを実装するために使用できます。set_uses(n)は変更をキューイングし、nilを返します。
| パラメータ | 型 | 備考 |
|---|---|---|
n | int | 新しい使用回数 |
item:get_name() / item:set_name(s)
アイテムの表示名を取得または設定します。&によるカラーコードに対応しています。set_name(s)は変更をキューイングし、nilを返します。
| パラメータ | 型 | 備考 |
|---|---|---|
s | string | 新しい表示名(例:"&b&lFrost Sword") |
item:get_lore() / item:set_lore(table)
アイテムのロアを取得または設定します。get_lore()は文字列のテーブル(1行につき1つ)を返します。set_lore()は文字列のテーブルを受け取り、変更をキューイングしてnilを返します。
| パラメータ | 型 | 備考 |
|---|---|---|
table | table | 文字列の配列、1ロア行につき1つ |
例:使用回数を追跡するアイテムスクリプト
return {
api_version = 1,
on_right_click = function(context)
local uses = context.item:get_uses()
if uses <= 0 then
context.player:send_message("&cThis item is out of charges!")
return
end
context.item:set_uses(uses - 1)
context.player:send_message("&aUsed! Charges remaining: " .. (uses - 1))
end
}
item:get_durability()
アイテムのバニラ耐久度を表すcurrentとmaxフィールドを持つテーブルを返します。アイテムに耐久度バーがない場合はnilを返します。
例
local dur = context.item:get_durability()
if dur then
context.player:send_message("Durability: " .. dur.current .. "/" .. dur.max)
end
item:get_durability_percentage()
残りの耐久度を0.0から1.0の比率として返します。アイテムに耐久度バーがない場合はnilを返します。
item:use_durability(amount, can_break)
バニラ耐久度を一定量だけ減らす処理をキューイングし、nilを返します。
| パラメータ | 型 | デフォルト | 備考 |
|---|---|---|---|
amount | int | 必須 | 消費する耐久度ポイント数 |
can_break | boolean | false | trueの場合、耐久度が尽きるとアイテムは破壊されます。falseの場合、耐久度は1で止まります。 |
item:use_durability_percentage(fraction, can_break)
バニラ耐久度を最大値に対する割合で減らす処理をキューイングし、nilを返します。
| パラメータ | 型 | デフォルト | 備考 |
|---|---|---|---|
fraction | number | 必須 | 消費する最大耐久度の割合(例:0.1は10%) |
can_break | boolean | false | trueの場合、耐久度が尽きるとアイテムは破壊されます。falseの場合、耐久度は1で止まります。 |
context.event
現在のフックのイベントデータ。プロップとアイテムスクリプトのクリック、戦闘、インタラクション、および汎用ゾーンフックで利用可能です。関連するイベントもプレイヤーのアクターも存在しないフック(on_spawn、on_game_tick、on_destroy、on_equip)ではnilを返します。
以下のフックリファレンス表では、参考として基底のBukkitイベント型を示しています。ただしLuaのラッパーが公開するのは、ここに挙げたフィールドとメソッドだけです。
フィールドとメソッド
| フィールドまたはメソッド | 型 | 備考 |
|---|---|---|
event.player | プレイヤーエンティティテーブル | イベントをトリガーした、または監視中の汎用ゾーン境界を越えたプレイヤー。プロップのon_left_click、on_right_click、on_zone_enter、on_zone_leave、およびプレイヤーが原因となるアイテムフックで利用可能です。すべてのフィールドとメソッドについてはプレイヤーエンティティメソッドを参照してください。 |
event.is_cancelled | boolean | コンテキストが構築された時点でのキャンセル状態。このフィールドはcancel()やuncancel()を呼び出した後に更新されません。 |
event.cancel() | function | イベントをキャンセルします(例:ダメージやインタラクションを防ぐ) |
event.uncancel() | function | 以前にキャンセルされたイベントをアンキャンセルします |
すべてのイベントがキャンセル可能ではありません。基底のBukkitイベントがCancellableを実装していない場合、またはフックが背後にBukkitイベントを持たない汎用ゾーンフックである場合、event.cancel()とevent.uncancel()は存在せず、event.is_cancelledは常にfalseになります。
event.is_cancelledは初期状態のスナップショットとして扱ってください。自分のスクリプトがevent.cancel()やevent.uncancel()を呼び出し、その変更を同じフック内で後から参照する必要がある場合は、自前のローカルフラグを保持してください。
現在のFMMのeventテーブルは、target、block、projectile、itemといったBukkit固有のフィールドを公開していません。追加のコンテキストが必要な場合は、context.player、context.event.player、player:get_target_entity(range)、context.world:raycast(...)、または近傍エンティティのクエリを使用してください。
例:プロップを無敵にする
例
return {
api_version = 1,
on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}
例:キャンセル状態のチェック
例
return {
api_version = 1,
on_left_click = function(context)
if context.event and not context.event.is_cancelled then
context.event.cancel()
context.log:info("Damage cancelled!")
end
end
}
スケジュール済みコールバック(scheduler:run_later、scheduler:run_repeating)内では、context.eventは常にnilです。イベントの変更はイベントフック自体の間にしか行えません。
context.world
これはFreeMinecraftModels/MagmaCoreのworld APIです。完全なリファレンスについてはcontext.worldを参照してください。
グローバルページに記載されているすべてのメソッド(get_block_at、set_block_at、spawn_particle、play_sound、strike_lightning、get_time、set_time、get_nearby_entities、get_nearby_players、spawn_entity、get_highest_block_y、raycast、place_temporary_block、drop_item、spawn_firework)がFMMで利用できます。world:raycast()(光線を投射してヒットしたエンティティ/ブロックを検出)、world:place_temporary_block()(一時的なブロック置換)、world:spawn_firework()(カスタム色と形状で花火ロケットをスポーン)の完全な詳細については、MagmaCore world APIを参照してください。EliteMobsのボスパワーは同じworldの基盤から出発し、ボス、増援、落下ブロック、一時ブロックなどをスポーンするためのボス固有のlocationテーブルメソッドを追加します。EliteMobs ワールドと環境を参照してください。
FMM固有のワールド追加機能
FreeMinecraftModelsは、context.worldの上に3つのオプションのEliteMobsルートヘルパーを重ねています。これらは常に存在しますが、EliteMobsがインストールされていない場合はいずれもfalseを返して何もしません:
| メソッド | 備考 |
|---|---|
world:drop_elitemobs_procedural_loot(player, level, location?) | プレイヤー向けに手続き生成されたEliteMobsアイテムを1つドロップします。手続き生成アイテムのドロップが無効の場合はfalseを返します |
world:drop_elitemobs_random_loot(player, level, location?) | 指定レベルでプレイヤー向けにEliteMobsのルートテーブルを回します |
world:drop_elitemobs_custom_loot(player, file, level, location?) | プレイヤー向けに特定のEliteMobsカスタムアイテムファイルをドロップします。ファイルが解決できない場合はfalseを返します |
完全なシグネチャはLua APIリファレンスにあります。ボスに関するプロップ側の対応物は、上記で説明したprop:spawn_elitemobs_boss(...)です。
プレイヤーエンティティメソッド
プレイヤーエンティティテーブルは、context.player、context.event.player、context.world:get_nearby_players()から返されます。MagmaCoreの汎用on_zone_enter / on_zone_leaveフックでは、context.playerとcontext.event.playerに入場または退出したプレイヤーが設定されます。
グローバルページに記載されているエンティティテーブル、リビングエンティティメソッド、プレイヤー固有メソッド、プレイヤーUIメソッドが、FMMで使われているMagmaCoreのテーブルです。EliteMobsのボスパワーは似ていますがボス固有のエンティティテーブルを公開しており、ボスとエンティティに記載されています。エンティティ基本フィールド、リビングエンティティのフィールドとメソッド、プレイヤー固有のフィールドとメソッド、プレイヤーUIメソッドを網羅したFMMの完全リファレンスは、MagmaCore Luaスクリプティングエンジンを参照してください。新しいプレイヤーメソッドにはplayer:get_target_entity()(レイキャストターゲティング)、player:get_eye_location()、player:get_look_direction()、player:send_block_change()(プレイヤーごとのフェイクブロック)、player:reset_block()が含まれます -- 詳細はプレイヤー固有メソッドを参照してください。
FMM固有のエンティティフィールド
FMMスクリプト内で構築されたすべてのエンティティテーブルは、FMMのLuaEntityEnricherを介してこれらの追加フィールドが自動的に付与されます:
| フィールド | 型 | 備考 |
|---|---|---|
entity.is_modeled | boolean | このBukkitエンティティがModeledEntityの基底のエンティティである場合はtrue |
entity.is_prop | boolean | このエンティティがPropEntityを支えるアーマースタンドである場合はtrue |
entity.model | テーブルまたはnil | is_modeled = trueの場合のみ設定される(下記参照) |
entity.modelが存在する場合、以下が公開されます:
| フィールド / メソッド | 備考 |
|---|---|
model.model_id | 設計図モデル名(例:"dragon") |
model.is_dynamic | DynamicEntityの場合はtrue(リビングエンティティに付属) |
model:play_animation(name, blend, loop) | 名前付きアニメーションを再生します。エンティティモデルブリッジではblendとloopのデフォルトはfalseです。成功時にtrueを返します |
model:stop_animations() | すべての現在のアニメーションを停止します |
model:remove() | モデル化されたエンティティとそのすべてのボーンを即座に削除します |
on_right_click = function(context)
local player = context.event and context.event.player
if not player then return end
local target = player:get_target_entity(8)
if target and target.is_modeled then
target.model:play_animation("hurt", true, false)
end
end
EliteMobsエンティティフィールド
EliteMobsがインストールされている場合、FMMはEliteMobsのエンリッチャーに転送するため、同じエンティティテーブルが以下も公開します:
| フィールド | 型 | 備考 |
|---|---|---|
entity.is_elite | boolean | エンティティがEliteMobsによって追跡されている場合はtrue |
entity.is_custom_boss | boolean | カスタムボス設定の場合はtrue |
entity.is_significant_boss | boolean | healthMultiplier > 1を持つカスタムボスの場合はtrue(雑魚の名前付きMobをフィルタアウト) |
entity.elite | テーブルまたはnil | is_elite = trueの場合のみ設定される。level、name、health、max_health、health_multiplier、damage_multiplier、is_custom_boss、およびelite:remove()を含む |
context.zones
これはFreeMinecraftModels/MagmaCoreのzones APIです。完全なリファレンスについてはcontext.zonesを参照してください。EliteMobsのボスパワーは、EliteMobsネイティブのゾーン定義を持つ別のcontext.zonesテーブルを使用します。EliteMobs ゾーンとターゲティングを参照してください。
context.scheduler
ここに記載しているscheduler APIは、FreeMinecraftModelsのスクリプトとEliteMobsのNPCスクリプトで使われるMagmaCoreのスケジューラーです。EliteMobsスタイルの名前(run_after、run_every、cancel_task)は共有スケジューラー上のエイリアスなので、どちらの命名スタイルでも動作します。ボスパワーもボス固有のコンテキストを通じて同じエイリアスを公開しています。FMMの完全なリファレンスについてはcontext.schedulerを参照してください。
context.state
state APIは、FreeMinecraftModelsのスクリプト、EliteMobsのボスパワー、EliteMobsのNPCスクリプトで共有されています。完全なリファレンスについてはcontext.stateを参照してください。
context.log
ここに記載しているログAPIは、FreeMinecraftModels/MagmaCoreのロガー(info、warn、error)です。EliteMobsのNPCスクリプトも同じロガーを使用します。EliteMobsのボスパワーはinfo、warn、debugを公開しています。完全なリファレンスについてはcontext.logを参照してください。
context.cooldowns
ここに記載しているクールダウンAPIは、FreeMinecraftModelsのスクリプトとEliteMobsのNPCスクリプトで使われるMagmaCore/FMM共通の引数順です:check_local(key?, duration)。EliteMobsのボスパワーも、ボス固有のバッキングストアを用いて同じ引数順を使用します。完全なリファレンスについてはcontext.cooldownsを参照してください。
| メソッド | 備考 |
|---|---|
local_ready(key?) | ローカルクールダウンが準備完了かどうかをチェックします。 |
local_remaining(key?) | ローカルクールダウンの残りティック数、または0を返します。 |
check_local(key?, duration) | ローカルクールダウンをアトミックにチェックして開始します。 |
set_local(duration, key?) | チェックせずにローカルクールダウンを設定します。 |
global_ready() | スクリプト所有者の共有グローバルクールダウンをチェックします。 |
set_global(duration) | スクリプト所有者の共有グローバルクールダウンを設定します。 |
通常のプロップやアイテムのアクションのクールダウンにはcontext.cooldowns:check_local("my_key", 40)を使用してください。
ランタイムモデル
スクリプトインスタンスごとに1つのランタイム
スクリプトがアタッチされているすべてのプロップエンティティは、独自の独立したLuaランタイムインスタンスを取得します。プロップがスポーンすると、FMMはLuaソースをロードし、新鮮なサンドボックス化された環境で評価し、返されたテーブルを保存します。プロップが削除されると、ランタイムがシャットダウンされます。
アイテムスクリプトの場合、(プレイヤー、アイテムID)ペアごとに1つのランタイムが作成されます。プレイヤーがカスタムアイテムを装備すると、FMMはそのプレイヤーとアイテムタイプ用のスクリプトインスタンスを作成します。アイテムが装備解除されると、ランタイムはシャットダウンされます。
これは以下を意味します:
- ファイルスコープで宣言されたローカル変数は、そのスクリプトインスタンスにプライベートです。
context.stateは、同じスクリプトファイルを共有していても、インスタンス間で完全に分離されています。
スケジュールされたタスクの所有権
context.schedulerを介して作成されたすべてのタスクは、それを作成したランタイムによって所有されます。プロップが削除されると:
- ランタイムがシャットダウンします。
- 所有されているすべてのタスク(ワンショットと繰り返しの両方)が自動的にキャンセルされます。
- すべてのゾーンウォッチがクリアされます。
クールダウンのスコーピング
共有スクリプティングエンジンは、ローカルクールダウンのヘルパー(local_ready、local_remaining、check_local、set_local)とグローバルクールダウンのヘルパー(global_ready、set_global)を公開しています。FMMはこれらのストアを以下のようにスコープします:
| スクリプト型 | ローカルストアスコープ | グローバルストアスコープ |
|---|---|---|
| プロップスクリプト | ScriptInstanceごと(プロップ + スクリプトファイル) | PropEntityごと(そのプロップにバインドされたすべてのスクリプト間で共有) |
| アイテムスクリプト | (player, itemId, scriptFile)トリプルごと — アイテムがアクティブスロットから外れるたびにスクリプトインスタンスが破棄されても、再装備を通じて永続化される | プレイヤーごと(そのプレイヤーが実行するすべてのFMMアイテムスクリプト間で共有) |
アイテムスクリプトは装備/装備解除サイクルごとに破棄され再構築されるため、アイテムクールダウンはScriptInstanceに存続するのではなく、プレイヤーUUIDをキーとする静的マップに保持されます。これが、アイテムをホットバーから出し入れしてもアイテムクールダウンが引き続き適用される理由です。
この永続性は、現在のFMMランタイム内に限られます。/fmm reload、プラグインの無効化、サーバー再起動では、スクリプトマネージャのシャットダウンに伴い、アイテムクールダウンストアとプロップのグローバルクールダウンストアの両方がクリアされます。
実行予算
すべてのフック呼び出し、すべてのスケジュール済みコールバック、そしてスクリプトファイル自体の初回評価は、厳格な実行バジェットの下で実行されます。このバジェットはLua VMの内部で強制されるため、実行後に時計を確認するのではなく、コードの実行中に適用されます。
| 上限 | 値 |
|---|---|
| 現在のスレッドのCPU時間 | 50 ミリ秒 |
| 実行された Lua 命令数 | 250,000 |
先に到達した方の上限で呼び出しがLuaエラーとともに中断され、そのスクリプトインスタンスが無効化されます。メッセージは次のとおりです:
Lua instruction budget exceeded (250000 instruction limit)
Lua CPU-time budget exceeded (50ms current-thread CPU limit)
チェックは命令単位で行われるため、while true do end がサーバーをフリーズさせることはありません。
バジェットの時間側は、ウォールクロックではなく現在のスレッドのCPU時間として計測されます。そのため、サーバースレッドがスケジュールから外れていた時間はスクリプトに課金されません。現在のスレッドのCPU時間計測が利用できないJVMでは、MagmaCoreは意図的により寛容な250ミリ秒の経過時間の上限(Lua elapsed-time fallback budget exceeded (250ms fallback; current-thread CPU time unavailable))にフォールバックしつつ、同じ250,000命令の上限を維持します。これにより、いずれの場合でも終了しないスクリプトは制限された状態に保たれます。
ネストした呼び出しは1つのバジェットを共有します。フックがコールバックを呼び、そのコールバックがさらに別のものを呼ぶ場合、チェーン全体がCPU時間50ms/250,000命令の1つの割り当てとして計測され、それぞれに割り当てが与えられるわけではありません。
スクリプトファイルの初回評価時にはまだインスタンスが存在しないため、その定義は無効化されるのではなく拒否され、登録されません。
予算内に収めるには:
- フック内の無限ループを避けます。
on_game_tickハンドラを軽量に保ちます -- それらは毎ティックごとに実行されます。- ティックをまたいで作業を分散するには
context.scheduler:run_repeating(...)を使用します。
完全なフックリファレンス
このテーブルは、プロップとアイテムスクリプト両方で利用可能なすべてのフックをリストしています。
context.event列は、基底となるBukkitイベントのファミリーを示しています。FMMのLuaイベントラッパーが公開するのは、該当する場合のevent.player、event.is_cancelled、event.cancel()、event.uncancel()だけです。
有効なプロップフック(7個)
| フック | 発火するとき | context.event |
|---|---|---|
on_spawn | プロップがワールドにスポーンする | nil |
on_game_tick | 毎サーバーティック(50ms) | nil |
on_destroy | プロップが削除される | nil |
on_left_click | プレイヤーがプロップを左クリック | damageイベント |
on_right_click | プレイヤーがプロップを右クリック | interactionイベント |
on_zone_enter | プレイヤーが監視中のゾーンに入る | ゾーンのプレイヤーアクター(context.player / context.event.player。キャンセル不可) |
on_zone_leave | プレイヤーが監視中のゾーンから出る | ゾーンのプレイヤーアクター(context.player / context.event.player。キャンセル不可) |
現在のスクリプトバリデータはプロップスクリプトでon_projectile_hitを受け付けますが、現在のランタイムはまだ飛び道具の命中をプロップスクリプトへディスパッチしません。スクリプト付きアイテムに紐づく飛び道具の挙動にはアイテムのon_projectile_hitを使用し、プラグイン側でモデル化エンティティの飛び道具処理を行う場合はBukkitのModeledEntityHitByProjectileEvent APIを使用してください。
アイテムフック(22個)
| フック | カテゴリ | 発火するとき | context.event |
|---|---|---|---|
on_attack_entity | 戦闘 | プレイヤーがエンティティを攻撃 | damageイベント |
on_kill_entity | 戦闘 | プレイヤーがエンティティを倒す | deathイベント |
on_take_damage | 戦闘 | プレイヤーがダメージを受ける | damageイベント |
on_shield_block | 戦闘 | プレイヤーが盾でブロック | damageイベント |
on_shoot_bow | 戦闘 | プレイヤーが弓を射る | bow shootイベント |
on_projectile_hit | 戦闘 | プレイヤーの飛び道具が命中 | projectile hitイベント |
on_projectile_launch | 戦闘 | プレイヤーが飛び道具を発射 | launchイベント |
on_right_click | インタラクション | プレイヤーが右クリック | interactイベント |
on_left_click | インタラクション | プレイヤーが左クリック | interactイベント |
on_shift_right_click | インタラクション | プレイヤーがshift+右クリック | interactイベント |
on_shift_left_click | インタラクション | プレイヤーがshift+左クリック | interactイベント |
on_interact_entity | インタラクション | プレイヤーがエンティティを右クリック | entity interactイベント |
on_equip | 装備 | アイテムがアクティブスロットに入る | nil |
on_unequip | 装備 | アイテムがアクティブスロットから外れる | nil |
on_swap_hands | 装備 | メイン/オフハンドをスワップ | swapイベント |
on_drop | 装備 | プレイヤーがアイテムを落とす | dropイベント |
on_break_block | ユーティリティ | プレイヤーがブロックを壊す | block breakイベント |
on_consume | ユーティリティ | プレイヤーがアイテムを消費 | consumeイベント |
on_item_damage | ユーティリティ | アイテムが耐久度ダメージを受ける | item damageイベント |
on_fish | ユーティリティ | プレイヤーが釣り竿を使用 | fishイベント |
on_death | ユーティリティ | 装備中にプレイヤーが死亡 | deathイベント |
on_game_tick | ライフサイクル | 装備中の毎ティック | nil |
次のステップ
- サンプル&パターン -- ウォークスルー付きのプロップとアイテム向けの完全動作スクリプト
- トラブルシューティング -- 一般的な問題、デバッグのヒント、QCチェックリスト
- はじめに -- ファイル構造、フック、最初のスクリプトのウォークスルー
/fmm reload は再構築前にスクリプト所有のタスクとクールダウンを解除します。共有タスクには run_after、run_every、cancel_task、ワールドには raycast、place_temporary_block、drop_item があります。