FreeMinecraftModels APIと開発者ガイド
生成されたJavaリファレンスでは、クラス、メソッド、シグネチャを検索できます。各モジュールのソースバージョンとコミットも記録されています。
FreeMinecraftModelsは独立したプラグインとして動作し、他のプラグイン向けのAPIも提供します。
共通の依存関係と実行環境の設定はJava API索引を参照してください。以下の依存関係は公開版2.12.9を使用しています。現在のソースは2.12.9です。サーバーにインストールしたプラグインと同じバージョンを指定してください。
Mavenリポジトリ
<repository>
<id>magmaguy-repo-releases</id>
<name>MagmaGuy's Repository</name>
<url>https://repo.magmaguy.com/releases</url>
</repository>
<repository>
<id>magmaguy-repo-snapshots</id>
<name>MagmaGuy's Snapshot Repository</name>
<url>https://repo.magmaguy.com/snapshots</url>
</repository>
依存関係
<dependency>
<groupId>com.magmaguy</groupId>
<artifactId>FreeMinecraftModels</artifactId>
<version>2.12.9</version>
<scope>provided</scope>
</dependency>
compileOnly/providedとして使用してください。プラグインを自分のjarにシェードしないでください。
コアエントリポイント
ModeledEntityManager.modelExists(String)ModeledEntityManager.reload()ModeledEntityManager.getAllEntities()ModeledEntityManager.getDynamicEntities()ModeledEntityManager.propEntities()DisguiseAPI— プレイヤーをロード済みモデルに変装させる/解除するLocationAPI— ダンジョン検出器と保護プロバイダを登録する(Luaem.location.*述語に供給)ScriptedItemAPI— 他のプラグインのItemStackにFMMアイテムの識別情報と表示データを設定するItemConstructionSnapshot— 別スレッドでアイテムを構築するために、アイテムと表示モデルのデータをサーバースレッド上で取得する
ModeledEntityManager.getAllEntities()、getDynamicEntities()、propEntities()は、その時点のコピーを返します。返されたセットやマップを変更してもFMMのライブレジストリは変更されません。存在確認には、レジストリ全体を保持して繰り返しコピーするのではなくmodelExists(String)を使用してください。
コアランタイム型
ModeledEntityStaticEntityDynamicEntityPropEntity
エンティティの作成
StaticEntity preview = StaticEntity.create("example_model", location);
DynamicEntity mobModel = DynamicEntity.create("example_model", livingEntity);
DynamicEntity mount = DynamicEntity.createWithInvisibility("example_model", livingEntity);
PropEntity prop = PropEntity.spawnPropEntity("example_model", location);
すべての作成経路は、要求されたモデルIDがロードされていない場合はnullを返します。
PropEntity.spawnPropEntityはさらに、同じモデルのプロップが対象ブロック上に既にロードされている場合にもnullを返します — 重複は積み重ねられるのではなく拒否され、[FMM Props] Prevented duplicate prop spawn ...の警告がログに出力されます。戻り値は必ずnullチェックしてください。プロップが実際に作成されたことを確認できるのは、null以外が返ってきたときだけです。
createWithInvisibilityは、クライアントからエンティティを隠す代わりに不可視化のポーション効果を適用するバリアントです。これによりエンティティがクライアント側で追跡され続け、乗り物の操舵に必要です(/fmm mountの内部で使用)。また、Java クライアントでモデルのディスプレイボーンがエンティティに乗れるようになります。displayBonesRideTheirEntityを参照してください。
有用なランタイムメソッド
ModeledEntity#setDisplayName(String)-- モデルのネームプレートを設定しますModeledEntity#setDisplayNameVisible(boolean)-- ネームプレートの表示を切り替えますModeledEntity#setLeftClickCallback(...)ModeledEntity#setRightClickCallback(...)ModeledEntity#setHitboxContactCallback(...)ModeledEntity#setModeledEntityHitByProjectileCallback(...)ModeledEntity#playAnimation(String, boolean blend, boolean loop)-- 名前が組み込みステートにもモデル内のアニメーションにも一致しない場合と、deathが要求された後はfalseを返します。カスタムアニメーションでは、blend = trueなら idle や walk の上のアクションレイヤーで再生され、falseならポーズ全体を置き換えます。loopはカスタムアニメーションにのみ適用されます。アニメーションのブレンドを参照してくださいModeledEntity#stopCurrentAnimations()-- カスタムアニメーションと攻撃を終了し、モデルを idle に切り替えます。死亡が始まった後は何もしませんModeledEntity#hasAnimation(String)ModeledEntity#damage(double)/damage(Entity damager, double)/damage(Entity damager)/damage(Projectile)-- エンティティのDamageableComponentを経由しますModeledEntity#attack(LivingEntity)/attack(LivingEntity, double damage)ModeledEntity#teleport(Location, boolean teleportUnderlyingEntity)ModeledEntity#setUnderlyingEntity(Entity)/ModeledEntity.getModeledEntity(Entity)-- Bukkitエンティティからそれに紐づくモデルへの静的な逆引き。存在しない場合はnullModeledEntity#showUnderlyingEntity(Player)/hideUnderlyingEntity(Player)-- 背後のバニラエンティティのプレイヤーごとの可視性ModeledEntity#getEntityID()-- モデルID文字列を返しますModeledEntity#getModelInstanceId()-- インスタンスごとのUUID。モデルの生存期間中は安定していますModeledEntity#isRemoved()/isDying()-- ライフサイクルフラグModeledEntity#getLocation()-- 現在のLocationを返しますModeledEntity#getSpawnLocation()-- モデルが作成されたLocationを返しますModeledEntity#getWorld()--Worldを返しますModeledEntity#getSkeleton()/getSkeletonBlueprint()/getMountPointManager()-- ランタイム構造へのアクセスModeledEntity#getInteractionComponent()/getHitboxComponent()/getDamageableComponent()/getAnimationComponent()-- 上記の便利メソッドの背後にあるコンポーネントオブジェクトModeledEntity.getLoadedModeledEntities()-- ロード済みのすべてのモデル化エンティティの静的なライブセットModeledEntity#getViewers()-- エンティティが見えるプレイヤーのHashSet<UUID>を返しますModeledEntity#getNametagBones()-- ネームタグボーンのList<Bone>を返します(追加テキストの配置に便利)ModeledEntity#getScaleModifier()/setScaleModifier(double)ModeledEntity#removeWithDeathAnimation()-- 死亡アニメーション付きで削除します(存在する場合)ModeledEntity#removeWithMinimizedAnimation()-- 縮小アニメーション付きで削除しますModeledEntity#remove()-- エンティティとすべてのボーンを即座に削除しますModeledEntity#setTintColor(Color)/getTintColor()-- 革鎧の染色チャネルを介して永続的なティントを適用します。ダメージフラッシュは一時的にティントをオーバーライドし、その後ティントへ戻ります。クリアするにはnullを渡してください。ModeledEntity#setViewDistanceOverride(int)/getEffectiveViewDistance()-- 単一のエンティティに対してDefaultConfig.maxModelViewDistanceをオーバーライドします。プラグイン全体のデフォルトに戻すには-1を渡してください。DynamicEntity#setSyncMovement(boolean)DynamicEntity#isDamagesOnContact()/setDamagesOnContact(boolean)-- エンティティがヒットボックス接触でプレイヤーにダメージを与えるかどうかを制御しますDynamicEntity.isDynamicEntity(Entity)/DynamicEntity.getDynamicEntity(Entity)-- Bukkitエンティティからの静的な検索DynamicEntity#getBodyLocation()-- モデルのヨーの向きを向いた、エンティティの位置。weightedTurningがオンの場合、このヨーは旋回の慣性を伴ってモブの体のヨーに追従するため、モデルが旋回している間はモブより遅れることがあります。移動の同期がオンの間、getLocation()は同じ位置を返しますBone#getBoneLocation()
明るさ、傾き、中心合わせ、クリック
ModeledEntity#setBrightnessOverride(int blockLight, int skyLight)-- ボーンがある場所の光ではなく、すべての表示ボーンを固定のレベル(それぞれ0から15)で照らします。15, 15は完全な明るさです。閲覧者には次のボーン更新で反映されます。Javaの表示ボーンのみが対象で、Bedrockと防具立てのボーンはワールドの光のままですModeledEntity#clearBrightnessOverride()/hasBrightnessOverride()/getBrightnessOverrideBlockLight()/getBrightnessOverrideSkyLight()-- 固定の光を解除、または読み取ります。ゲッターは、上書きがない場合は-1を返しますDynamicEntity#setTravelOrientation(TravelOrientation)/getTravelOrientation()-- モデルを移動に合わせて傾けます。TravelOrientationはStyle(UPRIGHT、TOP_FIRST、FRONT_FIRST)、startSpeed、fullSpeed、maxTilt、turnSpeedからなるレコードです。デフォルトはTravelOrientation.UPRIGHTで、nullを渡すとデフォルトに戻ります。移動方向への傾きを参照してくださいModeledEntity#setBodyTilt(Quaterniondc)/getBodyTilt()-- 向きとアニメーションの適用後に、キューブの中央を軸としたワールド空間の回転でモデル全体を傾けます。nullを渡すと直立します。UPRIGHT以外の移動方向への傾きは毎ティックこれを設定します。直立のモデルでは、設定した傾きはそのまま残りますModeledEntity#setCenteredOnBody(boolean)/isCenteredOnBody()/getBodyOffset()-- キューブの中央がエンティティの中央に来るようにモデルを描画します。ボディへの中心合わせを参照してくださいDynamicEntity#setClickSurface(boolean)/isClickSurface()--trueの場合、クライアントにはモデルを覆う不可視のInteractionエンティティが送られ、プレイヤーはボディ自身のボックスではなく、見えているモデルをクリックします。サイズは、モデルのhitboxボーンのサイズ、ない場合はモデル自身の境界のサイズになり、2ティック以内に適用されます。細長いモデルや回転したモデルを覆うボックスは何もない隅へのクリックも拾ってしまうため、デフォルトではオフですDynamicEntity#isSteerableMount()/hasSteeringRider()/seatSteeringRider(Player)-- 鞍付きの馬がまとったモデルは、操縦可能な乗り物です。seatSteeringRiderは、誰も乗っていない場合にプレイヤーを馬自体に操縦する乗り手として座らせ、乗れたかどうかを返します。そのようなモデルへの右クリックは、デフォルトでこれを行いますDynamicEntity#liftSaddleTo(Bone)-- モデルのサイズを保ったまま、モデルの静止ポーズで鞍が指定のシートボーンに来るように、モブをスケールします。/fmm mountはこれをモデルの最初のm_ボーンで使います。サーバーが鞍の高さを取得できない場合は何もしません
ネームプレートと視線の対象
ModeledEntityにはsetDisplayNameLines(List<String>)、setDisplayNameScale(float)、setDisplayNameLineGap(double)、setDisplayNameVisible(boolean)、getNameplateLocation()があります。タグボーンは任意です。ない場合、ネームプレートは、静止時のモデルの最も高いキューブの4分の1ブロック(4ピクセル)上に、エンティティに合わせて拡大縮小して配置されます。
複数行の名前:
setDisplayName("Top line\nBottom line")とsetDisplayNameLines(List<String>)は、読む順(上から下)に行を受け取ります。一番下の行はアンカーに残り、追加の行は上方向に伸びます。行は明示的な改行で定義され、テキストは折り返されません。- 空の名前はネームプレートを非表示にします。
setDisplayNameVisible(false)はすべての行を非表示にし、テキストが変わってもその状態が続きます。 setDisplayNameScale(float)は、モデルのサイズとは独立してテキストを拡大縮小します。setDisplayNameLineGap(double)は、行間の空きをスケール1でのブロック数で設定し、デフォルトは0.1です。1行の高さはスケール1で0.225ブロックなので、連続する行のデフォルトの間隔は0.325ブロックになります。- モデルに複数の
tag_ボーンがある場合、アルファベット順で最初のものが、その動きとアニメーションを含めてネームプレートのアンカーになります。 - Javaプレイヤーには、グリフと間隔の両方が拡大縮小するテキストディスプレイが表示されます。Bedrockプレイヤーには、個別のマーカー用防具立てのラベルが表示され、これは拡大縮小できないため、間隔はスケール1のままです。Bedrockのラベルを選ぶには、Bedrockプレイヤーを識別するためにバックエンドサーバーにFloodgateまたはGeyserが必要です。ID転送のないプロキシのみの構成では、確実に選べません。
- 表示範囲、不可視、削除、リロードはモデルに従います。テキストや位置が変わらなければ、パケットは送られません。
FreeMinecraftModelsのモデルを使うNPCの名前と役割を、EliteMobsがどのように配置するかについては、NPCの作成を参照してください。
DynamicEntity.setLookTarget(Location)は、元のエンティティを回転・移動させずに、スケルトンの見た目の視線を指定します。渡した位置はコピーされます。解除するにはnullを渡してください。
PropEntity固有のメソッド
PropEntity.isPropEntity(ArmorStand)/PropEntity.getPropEntityID(ArmorStand)-- 背後の防具立てからプロップを識別しますPropEntity.hasLoadedPropOnSameBlock(String entityID, Location)--spawnPropEntityが内部で実行する重複チェック。nullチェックではなく事前に分岐したい場合は、先にこれを呼び出してくださいPropEntity.respawnPropEntityFromArmorStand(String entityID, ArmorStand)-- チャンクの再読み込みを生き延びた防具立ての周囲にプロップを再構築しますPropEntity.getPropEntities()-- アーマースタンドの UUID をキーにした読み込み済みプロップのスナップショット。変更しても稼働中のレジストリは変わりませんPropEntity#setPersistent(boolean)-- 背後の防具立ての永続性を切り替えますPropEntity#setCustomDataString(NamespacedKey, String)/getCustomDataString(NamespacedKey)-- プロップ上の独自のPDC値を読み書きします。これはLuaのset_persistent_data/get_persistent_dataヘルパーがfmm_lua_<key>名前空間で使用しているのと同じストアですPropEntity#remove()/remove(boolean showRealBlocks)-- モデルを削除しますが、永続エントリは残しますPropEntity#permanentlyRemove()-- モデルおよびその永続エントリを削除しますPropEntity#setVoxelizeConfig(boolean voxelize, boolean solidify)/applySolidify()-- YMLのvoxelize:/solidify:フィールドに相当するランタイム版PropEntity#showFakePropBlocksToPlayer(Player)/showRealBlocksToPlayer(Player)および...ToAllPlayers()バリアント -- solidify済みプロップにクライアント側の当たり判定を与える、パケットのみのバリアブロックを制御します
魔法武器との連携
魔法サービスを利用する前にMagicWeaponAPI.isOperational()を確認してください。isWeapon(String)は定義済みIDを確認し、applyWeaponData(ItemStack, String)は元の名前、ロア、レベル、エンチャントを置き換えずに武器の識別情報と表示を適用します。サービスが登録されていても、動作可能とは限りません。
任意のMagicAttackResolverで発動の拒否、対象の選別と優先順位付け、発射時の戦闘情報の保存、ダメージの決定ができます。対象へ直接ダメージを与えず、渡された一度限りのMagicDamageApplicationを使用してください。入力、飛翔体の移動、衝突、ダメージの適用はFMMが担当します。
MagicProjectileTravelEventは同期的でキャンセル可能な衝突照会で、移動区間全体と命中時の確認を含みます。キャンセルすると、ダメージも爆発も発生せず飛翔体が消えます。同じ区間が複数回確認される場合があるため、リスナーはこれをダメージ通知として扱わないでください。
イベント
右クリック入力では、まず実際の基底エンティティに対するModeledEntityInteractEventが発火します。PlayerInteractEntityEventのハンドラーを継承するため、通常のエンティティ保護リスナーでキャンセルできます。キャンセルするとモデルの右クリックイベントとコールバックを抑止します。この確認はブロックの設置・破壊権限とは別です。
右クリックの確認がどのように組み合わさるか:
- ブロックとモデルのインタラクションは別々に判定されます。
PlayerInteractEventをキャンセルすると、ブロックやアイテムの動作はキャンセルされたままになりますが、それでも右クリックは、FMMのリーチとブロックによる遮蔽の確認を通じてモデルを選択できます。左クリックのキャンセルはこれまでどおり機能します。 ModeledEntityInteractEventはメインハンドを使います。FMMはそのディスパッチが終わった後でのみModeledEntityRightClickEventを発火し、ModeledEntityRightClickEventをキャンセルすると、モデルのコールバック、プロップの動作、デフォルトの騎乗が止まります。- ブリッジイベントは追加のクリックではなく、権限の確認です。FMM自身の入力、魔法武器、プロップのルーターはこれを無視します。ネイティブのエンティティインタラクションイベントで処理を行うプラグインは、二重に動作しないよう
ModeledEntityInteractEventをスキップできます。保護リスナーはこれを引き続き処理してください。 - 背後にBukkitエンティティを持たないモデルは、Bukkitのエンティティイベントを発火できません。キャンセル可能な
ModeledEntityRightClickEventは引き続き発火します。永続的なプロップとEliteMobsのNPCは背後にエンティティを持つため、両方の確認が行われます。
汎用インタラクションイベント:
ModeledEntityLeftClickEventModeledEntityRightClickEventModeledEntityHitboxContactEventModeledEntityHitByProjectileEvent
4つすべてがキャンセル可能なBukkitイベントであり、FMMの組み込み検出パスはこれらをメインサーバースレッド上でディスパッチします。イベントをキャンセルすると、対応するFMMのコールバック/デフォルト動作が抑止されます。ヒットボックス接触のスキャンは2サーバーティックごとに実行されます。クリックイベントには依然としてプレイヤーごとの短い重複排除ウィンドウがあり、パケットインタラクション経路とOBBレイトレース経路が同じアクションを2回発火させることはありません。
ライフサイクルイベント:
FmmReloadedEvent-- 起動時や完全な再読み込みを含む、完全初期化の成功から 40 ティック後にメインスレッドで予定されます。インポートしたコンテンツだけの再読み込みも成功時に通知します。準備段階で拒否された要求は稼働中の状態を解体せず、再読み込み完了イベントも通知しません
公開されているインタラクションのインターフェースは、意図的にモデル型に依存しない形になっています。古いStaticEntity*Event、DynamicEntity*Event、PropEntity*Eventのバリアントは、ResourcePackGenerationEventとともに、現在のAPIには含まれていません。代わりに上記4つの汎用モデル化エンティティイベントとFmmReloadedEventを使用してください。
長寿命のDynamicEntityまたはPropEntity参照を保持するコンシューマプラグイン(EliteMobs、BetterStructuresなど)は、生き残っている基底のエンティティ上でモデルアタッチメントを再作成することによって、このイベントを処理する必要があります。これがないと、コンシューマの参照が現在は古くなっている間にFMMがonDisable中にディスプレイエンティティを破棄したため、それらのエンティティはリロード後に不可視になります。
@EventHandler
public void onFmmReloaded(FmmReloadedEvent event) {
for (MyTrackedEntity tracked : myEntities) {
if (tracked.bukkitEntity != null && tracked.bukkitEntity.isValid()) {
tracked.fmmModel = DynamicEntity.create("my_model", tracked.bukkitEntity);
}
}
}
ModeledEntityHitByProjectileEventとOBB飛び道具検出
FMMは、モデル化されたエンティティに対する飛び道具にOBB(指向性バウンディングボックス)ヒット検出を使用します。飛び道具がモデル化されたエンティティのOBBヒットボックスと交差すると、FMMはModeledEntityHitByProjectileEventを発火します。これは標準的なキャンセル可能なBukkitイベントです。
重要な詳細:
- 検出は記録した位置の間の区間を調べ、薄いモデルの通過を検出します。前の位置がなければ重なり判定を使います
- その区間上の候補は、モデルレジストリの反復順序とは無関係に、近い順に解決されます
- 貫通しない飛び道具は1つのモデル化ターゲットにのみ振り分けられます。Piercing(貫通)の矢は、飛行順に
貫通レベル + 1個の異なるモデル化ターゲットにヒットでき、OBBとバニラの背後のヒットボックスの両方が観測しても二重発火しません - 動的エンティティのデフォルトハンドラは、衝突時の飛び道具の速度を用いて、その衝撃を実際の飛び道具ダメージイベントとして基盤のリビングエンティティへ転送します。これにより、飛び道具という原因、戦闘プラグインの処理、射手の帰属が保持されます
- イベントをキャンセルするとFMMのコールバック/デフォルトのダメージ動作は抑止されますが、その衝突は依然としてその飛び道具のモデル化ターゲット枠を消費します。したがって、キャンセルされた非貫通のヒットでも飛び道具は消費されます
@EventHandler
public void onProjectileHitModel(ModeledEntityHitByProjectileEvent event) {
ModeledEntity target = event.getEntity();
Projectile projectile = event.getProjectile();
// Cancel to prevent damage
event.setCancelled(true);
}
アイテムとモデルのユーティリティ
ModelItemFactory
モデル関連のItemStackをプログラム的に作成するためのファクトリクラス。
// Create a prop placement item (uses "model_id" PDC key)
ItemStack placementItem = ModelItemFactory.createModelItem("lamp_post", Material.STICK);
// Create a custom item from config (uses "fmm_item_id" PDC key)
PropScriptConfigFields config = ItemScriptManager.getItemDefinitions().get("magic_sword");
ItemStack customItem = ModelItemFactory.createCustomItem("magic_sword", config);
createModelItem(String modelId, Material material)-- プロップ用の配置アイテムを作成します。1.21.4以降では、ディスプレイJSONが存在する場合にディスプレイモデルレンダリングが自動的に適用されます。createCustomItem(String itemId, PropScriptConfigFields config)-- 統合設定から名前、ロア、エンチャント、ディスプレイモデルを持つカスタムアイテムを作成します。formatModelName(String modelId)--01_em_flame_swordのようなモデルIDをFlame Swordに変換するユーティリティです。
DisplayModelRegistry
どのモデルがディスプレイJSONを利用可能かを追跡するシンプルなレジストリ。
// Check if a model has a display model JSON registered
boolean has3D = DisplayModelRegistry.hasDisplayModel("magic_sword");
register(String modelId)-- モデルIDを登録します(リロード中に内部的に呼び出されます)hasDisplayModel(String modelId)-- 表示モデルが登録されたかを確認します。不正な.jsonはファイルが存在するだけでは登録されませんgetRegisteredModels()-- ディスプレイモデルが登録されているすべてのモデルIDのイミュータブルSet<String>を返しますshutdown()-- すべての登録をクリアします
ItemScriptManager
クラス名は残っていますが、現在はアイテムと魔法武器の定義を管理するカタログです。装備アイテムのスクリプトは実行しません。getItemDefinitions()は登録済みアイテム定義、getWeaponCatalog()は検証済み武器定義を返します。以前の実行中スクリプト、装備更新、プレイヤースクリプト削除のAPIは廃止されました。
ScriptedItemAPI
isValidItemId(String):定義したアイテムID(設定ファイル名から.ymlを除いたもの)の完全一致を確認します。getItemConfig(String):定義を返します。なければnullを返します。applyScriptedItemData(ItemStack, String):fmm_item_idと、登録されていれば表示モデルを設定します。IDが無効、またはアイテムのメタデータがない場合はfalseを返します。
このメソッドは元の名前、ロア、エンチャントを変更せず、Luaの動作も追加しません。エンチャントはMagmaCoreで別途適用してください。弓とクロスボウを引く際の状態は基底の表示モデル定義に属します。アイテムIDに_idleを追加しないでください。
ModelItemAPI
FMMの表示モデルだけが必要な場合はModelItemAPI.applyDisplayModel(ItemStack, String)を使用してください。表示モデルのレジストリを確認しますが、アイテムの識別情報は設定しません。FMMの武器・アイテム定義の選択とは別の操作です。
ItemConstructionSnapshot
サーバースレッド以外でアイテムを構築するプラグイン向けです。サーバースレッド上でItemConstructionSnapshot.capture()を呼ぶと、現在のアイテム定義、表示モデル、魔法武器の種類がコピーされます。その後ワーカースレッドでitem(id)またはweapon(id)を呼び、返されたItemDataをapply(ItemStack)でItemStackに適用できます。これにより、定義済みアイテムにはfmm_item_idが、表示モデルが登録されていればその表示モデルが設定されます。weapon(id)は、IDが魔法武器であり、かつ取得時に魔法サービスが動作していた場合を除きnullを返します。構築したアイテムを公開する前に、サーバースレッド上でrequireCurrent()を呼んでください。リロード後など、取得以降にFMMの定義が変更されていた場合はConcurrentModificationExceptionをスローします。
DisguiseAPI
プレイヤーディスガイズ機能のパブリックエントリポイント。内部リファクタが安全に保たれるように、サードパーティプラグインは内部のDisguiseManagerではなく、このクラスを呼び出すべきです。
import com.magmaguy.freeminecraftmodels.api.DisguiseAPI;
// Disguise a player as a loaded model. Replaces any existing disguise cleanly.
boolean ok = DisguiseAPI.disguise(player, "dragon");
// Undisguise (returns true if a disguise was removed).
DisguiseAPI.undisguise(player);
// Query state.
boolean disguised = DisguiseAPI.isDisguised(player);
String modelID = DisguiseAPI.getDisguiseModelID(player); // null if not disguised
// Snapshot of all currently disguised players.
Collection<Player> all = DisguiseAPI.getDisguisedPlayers();
disguise(Player, String modelID)-- 置き換えを試す前に古い変装を削除します。未知の ID では削除後にfalseを返します。オンラインのプレイヤーと正確な読み込み済み ID を渡してください。API は ID の正規化やコマンド権限の確認を行いませんundisguise(Player)-- ディスガイズが削除された場合trueを返しますisDisguised(Player)-- クイックブーリアンチェックgetDisguiseModelID(Player)-- アクティブなモデルIDまたはnullを返しますgetDisguisedPlayers()-- ディスガイズされたプレイヤーの変更不可なスナップショット
FMM はプレイヤーの既定の可視性、透明化ポーション効果、装備表示の上書きを組み合わせます。その変装が最初に追加した効果だけを再付与し、変装前からあった効果は管理・再付与しません。API と異なり、コマンドは置き換え前にモデルを検証します。ライフサイクルとクライアント描画の制限はプレイヤーの変装を参照してください。
LocationAPI
プラグインがダンジョン検出とリージョン保護チェックに貢献するためのパブリックAPI。登録された述語はFMMのLua em.location.is_in_dungeonとem.location.is_protectedチェック(pickupable.luaやstorage_double.luaなどの既製スクリプトで使用)に供給されます。
プラグインは単純なPredicate<Location>を渡すため、シェード化されたFMM型がプラグインクラスローダーを跨ぐことはありません。
import com.magmaguy.freeminecraftmodels.api.LocationAPI;
// On your plugin's enable, after WorldGuard/EliteMobs/etc. are available.
LocationAPI.registerDungeonLocator("EliteMobs",
location -> EliteMobs.isInsideDungeon(location));
LocationAPI.registerProtectionProvider("WorldGuard",
location -> WorldGuardBridge.isProtected(location));
registerDungeonLocator(String providerName, Predicate<Location> predicate)— 登録された述語のいずれかがtrueを返すと、その場所は「ダンジョン内」とフラグされますregisterProtectionProvider(String providerName, Predicate<Location> predicate)— 登録された述語のいずれかがtrueを返すと、その場所は保護されているとフラグされますunregisterDungeonLocator(String providerName)とunregisterProtectionProvider(String providerName)は、その名前で登録された述語を削除します。無効化されたインスタンスをFMMが呼び出さないよう、プラグインのonDisableから呼んでください
同じプロバイダ名で再度登録すると以前の述語が置き換えられるため、有効化のたびに再登録するプラグインでもコピーが重複することはありません。プロバイダ名がnullの場合、置き換えも登録解除もできない述語が登録されます。
オペレーターは/fmm locationで登録を確認できます。これはライブのプロバイダ数を報告し、現在地で両方の述語をテストします。
保護プロバイダとプロップ設置
同じ保護プロバイダはpreventPropPlacementInProtectedRegionsのプロップ設置チェックも駆動しますが、そのチェックは位置のみを見るisProtected(location)ではなくcanBuild(player, location)を呼び出します。この違いは重要です:
| プロバイダ | canBuildの挙動 |
|---|---|
| 組み込みのWorldGuardアダプタ | WorldGuard自身のバイパスを尊重したうえで、WorldGuardのtestBuildに委譲します — そのためリージョンのメンバーやオーナーは通常どおり建築できます |
| 組み込みのGriefPreventionアダプタ | その位置にクレームがなければ許可、クレーム内であればそのプレイヤーに対するGriefPrevention自身の建築権限に委譲します |
LocationAPI.registerProtectionProviderで登録したプロバイダ | !isProtected(location)にフォールバックします — 位置のみの判定で、全員をブロックします(あなたの述語が保護されていると判断した位置において) |
このフォールバックは意図的かつ保守的なものです。Predicate<Location>はプレイヤーごとの権限を表現できないため、FMMがそれを勝手に作り出すことはありません。独自のリージョンシステムで本当にプレイヤーを考慮した挙動が欲しい場合は、MagmaCoreのRegionProtectionProviderを直接実装してcanBuildをオーバーライドし、述語向けの簡易ラッパーを経由せずにLocationQueryRegistry.registerProtectionProviderで登録してください。
さらに知っておくべき挙動が2つあります:
- アダプタの失敗はフェイルクローズします。 建築クエリの途中でプロバイダが例外をスローした場合、設置は拒否され、該当プロバイダ名を含む警告がログに出力されます。黙って「許可」に流れることはありません。
- 登録されたすべてのプロバイダが同意する必要があります。 最初に拒否したプロバイダが勝ちます。
LocationOwnershipを通じて登録された所有権プロバイダは依然として位置のみの判定であり、全員をブロックします。
アイテムによる設置は最初に隣接セルを確認します。ボクセル化したプロップは、丸めたヒットボックス寸法を最低 1 ブロックとして回転後の占有範囲も調べます。パケットによるバリアの範囲は別の計算です。PropEntity.spawnPropEntity の直接呼び出しは、プレイヤーの設置アイテムの検査を実行しません。別プラグインから設置を提供する前に設定を確認してください。
PropScriptConfigFields
モデルと同じ場所にあるYAMLを読み取り、プロップとアイテムの定義に使用します。プロップのscripts:リストは、独立したLuaスクリプトを関連付けます。一方、material:を指定すると手持ちアイテムを登録するため、空でないscripts:リストは使用できません。
それぞれの例はモデル設定、武器の設定形式は魔法武器を参照してください。
Luaスクリプト
FMMはMagmaCoreエンジンを通じてプロップのLuaスクリプトを実行します。ファイルをplugins/FreeMinecraftModels/scripts/に置き、モデルと同じ場所のYMLで関連付けます。ディスク上のファイル名は.luaで終わる必要があります。設定では拡張子を省略できます。
プロップはscripts:に列挙したすべてのスクリプトを独立したインスタンスとして実行します。手持ちアイテムの効果は別のエンチャントプロバイダーで実行します。アイテムに空でないscripts:リストは指定できません。
プロップスクリプトのフック
| フック | トリガー |
|---|---|
on_spawn | プロップがワールドにスポーンされたとき |
on_game_tick | プロップが生存している間、毎ティック |
on_zone_enter | プレイヤーがスクリプトで作成した監視ゾーンに入ったとき |
on_zone_leave | プレイヤーがスクリプトで作成した監視ゾーンから出たとき |
on_destroy | プロップが完全に削除されたとき(チャンクのアンロードやリロードでは呼ばれません) |
on_left_click | プレイヤーがプロップを左クリックしたとき |
on_right_click | プレイヤーがプロップを右クリックしたとき |
on_projectile_hit | 投射物がプロップに当たったとき。context.projectileは投射物、context.event.playerは発射者です |
アイテムスクリプトのフック
装備アイテムのLuaフックとcontext.itemは廃止されました。アイテムの効果にはエンチャント定義を使用してください。プロップのフックは引き続き利用できます。
プロップスクリプトのコンテキストテーブル
プロップスクリプトはcontextテーブルを受け取ります。以下は主要APIの概要です -- 完全な詳細についてはLua プロップAPIを参照してください。
context.prop:
model_id-- 設計図モデル名current_location-- プロップの現在地play_animation(name, blend, loop)-- 指定アニメーションを要求します(blend と loop は既定でtrue。これはループするアニメーションをプロップの idle の上に重ねます)。要求はモデルの次のティックに適用されますstop_animation()-- カスタムアニメーションを終了し、idleがあれば戻ります。終了状態である死亡アニメーションは中断できませんhurt_visual()-- プロップにダメージビジュアル(赤いフラッシュ)を再生しますpickup()-- プロップの削除と配置アイテムのドロップをキューに入れますmount(player)-- 搭乗の試行をキューに入れます。trueが返るのはプレイヤーとマウントマネージャが有効だったという意味であり、最終的に座席が割り当てられたことを意味するわけではありませんdismount(player)-- 降車チェックをキューに入れます。trueが返るのはプレイヤーとマウントマネージャが有効だったという意味ですget_passengers()-- 現在プロップに乗っているプレイヤーのリストを返しますspawn_elitemobs_boss(filename, x, y, z)-- プロップの現在のワールドの絶対座標にEliteMobsボスをスポーンします
context.event:
- プロップの
on_left_click、on_right_click、on_zone_enter、on_zone_leave、on_projectile_hitで利用できます - 基となるフックがキャンセル可能な場合は
cancel()、uncancel()、is_cancelledが使えます player-- イベントをトリガーしたプレイヤー。on_projectile_hitでは投射物を発射した生物エンティティです
context.world:
spawn_entity(entity_type, x, y, z)-- バニラエンティティをスポーンします。エンティティタイプが無効な場合はnilを返しますset_block_at(x, y, z, material)-- マテリアルが有効ならブロック変更をキューに入れます。ロードされていないチャンクはスキップされます- パーティクル、サウンド、ブロッククエリ、雷、近隣エンティティの検索も
context.cooldowns:
check_local(key, ticks)-- スクリプトごとのクールダウンをチェックして開始しますglobal_ready()/set_global(ticks)-- プロップで共有するクールダウン
プレイヤーオブジェクト(context.playerまたはcontext.event.playerから):
get_held_item()-- プレイヤーが持っているアイテムを返します。typeは大文字のBukkitマテリアル名ですis_holding_elitemobs_item(filename)-- メインハンドのアイテムにあるEliteMobsのファイル名タグを確認します。例はshroomwhispers_shears.ymlです。表示名や翻訳は判定に影響しません。手が空の場合、別のアイテムの場合、EliteMobs連携が利用できない場合はfalseを返します。consume_held_item()-- タスク実行時点のメインハンドから 1 個減らす処理を予定します。支払い結果は返しませんhas_item(material)-- プレイヤーがアイテムを持っているかチェックしますsend_message(text)-- プレイヤーにチャットメッセージを送信しますgame_mode-- プレイヤーの現在のゲームモード
EliteMobsのクエストアイテムはファイル名で識別してください。
if not player:is_holding_elitemobs_item("shroomwhispers_shears.yml") then
return
end
このメソッドはFMMのEliteMobs連携が提供します。通常のアイテムを改名しても、必要なファイル名タグは付きません。
プロップスクリプトの例
return {
api_version = 1,
on_spawn = function(context)
context.prop:play_animation("idle", true, true)
end,
on_right_click = function(context)
if context.cooldowns:check_local("activate", 40) then
context.prop:play_animation("activate", false, false)
end
end
}
アイテムスクリプトの例
以前の装備アイテムの例は使用できません。現在の仕組みでアイテムを作成するには魔法武器とエンチャント定義を参照してください。
ソースからのビルド
ソースはGitHubにあります。FreeMinecraftModelsはJava 21のMavenプロジェクトです。
mvn -DskipTests package
- プラグインのjarは
target/FreeMinecraftModels.jarに出力されます。 - MagmaCore、bStats、その他の同梱ライブラリは、
packageフェーズでシェードされます。MagmaCoreはcom.magmaguy.freeminecraftmodels.magmacoreにリロケートされるため、サーバーに別途MagmaCoreをインストールする必要はありません。 - MavenはMagmaCore
2.2.0-SNAPSHOTをMagmaGuyのリポジトリから解決します。ソースが、まだそこに公開されていないMagmaCoreの変更を必要とする場合は、先にMagmaCoreをビルドしてローカルのMavenリポジトリに公開してください。-Dmagmacore.version=<version>を指定すると、別のMagmaCoreバージョンに対してビルドします。 - 環境変数
MC_DIST_DIRが設定されている場合、ビルドはそのフォルダにもjarをコピーします。
インポートされたモデル、テクスチャ、フォント、コンテンツパックは、それぞれの作者のライセンスを保ちます。それらからリソースパックを生成しても、これらの権利は変わりません。
注意事項
- FreeMinecraftModelsはインストール済みプラグインの依存関係であり、組み込み可能なライブラリではありません。
- プラグインが新しくインポートされたモデルを必要とする場合は、FreeMinecraftModelsの状態を自分で再構築しようとせず、
ModeledEntityManager.reload()を呼び出してください。 - 現在の FMM ソースは MagmaCore
2.2.0-SNAPSHOTに依存します。共有 Lua エンジン、LocationQueryRegistry、WorldFolderResolverを提供します。 - FreeMinecraftModels は WorldGuard、WorldEdit、GriefPrevention、Vault、floodgate、Geyser-Spigot を
softdependとして宣言しています。WorldGuard と GriefPrevention が組み込みのプレイヤー別保護アダプターを提供します。WorldEdit は WorldGuard の依存先であり、独立した FMM 保護アダプターではありません。ショップには Vault、経済プロバイダー、有効なショップ設定が必要です。Bedrock 描画にも対応パックと連携経路が必要です。 ModeledEntityManager.reload()は/fmm reloadと同じ非同期準備と完全なライフサイクルを要求し、完了を待ちません。メインスレッドから呼び出し、voidの戻り値ではなく成功したライフサイクルの通知で完了を確認してください。候補コンテンツが不正な場合、稼働状態を維持したまま再読み込みを拒否できます。