はじめに
以下の内容は、Java初心者であるhevohevoが調べた内容なので、
間違いが含まれている可能性が高いです。
その場合は、優しくご指摘いただけると助かります。
また、以下はMinecraft1.7.10およびForge#1180をターゲットに調査した内容です。
これよりも古いバージョンでは異なる可能性があるのでご注意ください。
Forgeを用いたModdingの基礎知識
自作Modを作るには、まず自分のModで初期化処理を書かなくてはならない。
たとえば、設定ファイルを読み込んだり、ブロックを追加したり、独自レシピを追加したり、他のModとの連携処理を書いたりなどなど。
Forge(厳密に言うとForgeModLoader、以下FMLと呼称)は、初期化処理のフレームワークを以下のように定めている。
FMLにおけるMod初期化処理の書き方
基礎知識・・・FMLはイベント駆動
FMLは、○○○というイベントメッセージが来たらあらかじめ登録してある処理を実行する、というイベント駆動型で実装されている。
FMLにおけるMod初期化処理は、以下の3つのイベントにより3段階に分かれており、1から3まで順番に実行される。
- FMLPreInitializationEvent イベント
- FMLInitializationEvent イベント
- FMLPostInitializationEvent イベント
Modを作成するときには、適切なイベントに適切な初期化処理を登録しなくてはならない。
なお、10個のModがあったらFMLは次のように初期化処理を進める。
- "FMLPreInitializationEvent"イベントメッセージ来た
- 有効なModリストを作成。今回は10個。
- 10個のModそれぞれについて、このイベントに対応する初期化処理を実行する
- "FMLInitializationEvent"イベントメッセージ来た
- 10個のModそれぞれについて、このイベントに対応する初期化処理を実行する
- "FMLPostInitializationEvent"メッセージ来た
- 10個のModそれぞれについて、このイベントに対応する初期化処理を実行する
特に、他のModと連携するModを書くときには、初期化の順序を意識する必要がある。
初期化処理を登録する方法
ModのメインとなるClassに@EventHandlerアノテーションを宣言しつつ、初期化用メソッドを追加する。
このとき、適切な初期化イベント(クラス)を引数として選択すること。
@EventHandler アノテーションの使い方
書式は以下の通り。
@EventHandler public XXXX(YYYY event){ // 具体的な初期化処理 }
- XXXXは任意のメソッド名、YYYYは3種類ある初期化イベントクラスのどれか
- FMLPreInitializationEvent クラス
- FMLInitializationEvent クラス
- FMLPostInitializationEvent クラス
ここで注意事項
好きなメソッド名を登録できるが、可読性のために、以下のメソッド名にすることがModderの間では推奨されている。
以下、またそれぞれの初期化イベントでどのような初期化処理を書くべきかの目安も記述。
1. FMLPreInitializationEvent イベント
一般的には、設定ファイルの読み込みやブロックなどの登録。事前の初期化作業に使う。
@EventHandler public void preInit(FMLPreInitializationEvent event) { // 具体的な処理 }
2. FMLInitializationEvent イベント
レシピの追加やこのMod用の各種データ設定など。メソッド名は load()を使っている人も多い模様。
@EventHandler public void init(FMLInitializationEvent event) { // 具体的な処理 }
3. FMLPostInitializationEvent イベント
本当に後に回したい初期化処理など? 他のModとの連携はここで書くらしい。
@EventHandler public void postInit(FMLPostInitializationEvent event) { // 具体的な処理 }
(補足事項)やってしまいがちなミス。経験者は語る
任意の初期化メソッドを登録できるのだが、これには大きな罠がある。
たとえば、1つのクラスの中で、同じイベントに対して複数の初期化メソッドを登録することができる(できてしまう)。
具体的には以下のように、"FMLInitializationEvent"を指定した別の名前のメソッドを複数登録できてしまう。
// 名前の異なる3つの初期化メソッドを、同じFMLInitializationEventに登録 @EventHandler public hoge(FMLInitializationEvent event){処理の中身}; @EventHandler public init(FMLInitializationEvent event){処理の中身}; @EventHandler public load(FMLInitializationEvent event){処理の中身};
しかしこれは混乱の元なので避けたい。なぜならば、同じイベントに登録された複数のメソッドは実行順序が不定(環境依存)になってしまうためである*1。
混乱を避けるために、1つの初期化イベントに対して1つだけ初期化メソッドを登録するのが無難だろう。少なくとも初心者モダーにとっては・・・。*2
他のモダーのコードを眺めてみる
他のモダーが、初期化処理をどのイベント段階で記述しているか、調べてみました。
日本Modder界の重鎮、reginnさんのコード。マイクラ1.7系のチュートリアルを大量に公開されているので、ありがたい。- Mekanismはアイテム登録どころか設定ファイルの読み込みまで全部initでやってる ^q^ というかコードが長いよw
- Modder界のK&R、cpw氏のIronChest*3では、preInitでブロック登録まで、init(loadを使ってる)でレシピ追加など他全部、postInitは何もしない
FMLの @EventHandlerのJavaDoc
cpw.mods.fml.common.Mod より抜粋
/** * Marks the associated method as handling an FML lifecycle event. * The method must have a single parameter, one of the following types. This annotation * replaces the multiple different annotations that previously were used. * * Current event classes. This first section is standard lifecycle events. They are dispatched * at various phases as the game starts. Each event should have information useful to that * phase of the lifecycle. They are fired in this order. * * These suggestions are mostly just suggestions on what to do in each event. * <ul> * <li> {@link FMLPreInitializationEvent} : Run before anything else. Read your config, create blocks, * items, etc, and register them with the {@link GameRegistry}.</li> * <li> {@link FMLInitializationEvent} : Do your mod setup. Build whatever data structures you care about. Register recipes, * send {@link FMLInterModComms} messages to other mods.</li> * <li> {@link FMLPostInitializationEvent} : Handle interaction with other mods, complete your setup based on this.</li> * </ul> * <p>These are the server lifecycle events. They are fired whenever a server is running, or about to run. Each time a server * starts they will be fired in this sequence. * <ul> * <li> {@link FMLServerAboutToStartEvent} : Use if you need to handle something before the server has even been created.</li> * <li> {@link FMLServerStartingEvent} : Do stuff you need to do to set up the server. register commands, tweak the server.</li> * <li> {@link FMLServerStartedEvent} : Do what you need to with the running server.</li> * <li> {@link FMLServerStoppingEvent} : Do what you need to before the server has started it's shutdown sequence.</li> * <li> {@link FMLServerStoppedEvent} : Do whatever cleanup you need once the server has shutdown. Generally only useful * on the integrated server.</li> * </ul> * The second set of events are more specialized, for receiving notification of specific * information. * <ul> * <li> {@link FMLFingerprintViolationEvent} : Sent just before {@link FMLPreInitializationEvent} * if something is wrong with your mod signature</li> * <li> {@link IMCEvent} : Sent just after {@link FMLInitializationEvent} if you have IMC messages waiting * from other mods</li> * </ul> * * @author cpw * */
Google Guava EventBusについて参考にしたページ
- googleさんの提供するイベント駆動基盤
- 日本語で解説 http://cco.hatenablog.jp/entry/20111210/1323509513
@Subscribeアノテーションを使うことでevent handlerとなる公開メソッドを実装することができます。イベントである引数は必ず1つのみであることに注意が必要です。
- 日本語で解説 http://blog.kengo-toda.jp/entry/20111006/1317907239
リスナーはどのようなクラスでもよく、メソッドを@Subscribeアノテーションで修飾するだけで使えます。
リスナーが@Subscribeで修飾されたメソッドを複数個持っている場合、これらが実行される順番は環境依存となります。
調査内容を書きなぐったメモ書き(現在進行中)
http://codeshare.io/5Xx5E ・・・FMLとBlockとTileEntityとTileEntitySpecialRendererについて http://codeshare.io/4KSk6 ・・・上記が重くなってきたので増設。ItemとNBTについて調査
- Codeshare なので、どなたでも、誤り指摘や修正など直接やっていただいてかまいません。
- ただし、このメモ書きに書いた内容を定期的に記事にまとめてアップします。この点、ご了承ください。
- 定期的にバックアップはとっていますが、荒らしはご勘弁ください。