Minecraft1.1向けのBukkit API以降、新しいイベントの仕組みが提供されています。
この新しい仕組みは、シンプルでかつ、汎用性・高速性・可読性等に優れています。
目次
解説動画
当項目の、翻訳元記事で紹介されている動画を参照して下さい。
http://wiki.bukkit.org/Introduction_to_the_New_Event_System#Video_Tutorial
基礎
PlayerLoginEventについて説明します。
このイベントは最低限動作するための処理のみを記述するよう心がけ、
極力シンプルに保ちつつ編集していきましょう。
イベントリスナ
まず、イベントを待ち受ける処理(Event Listenerと呼ばれます)が必要です:
public void onPlayerLogin(PlayerLoginEvent event) { // Your code here... }
次に、発生するイベント(Event Handlerと呼ばれます)が必要になります。
イベントハンドラ
イベントハンドラはイベントリスナに対する注釈であり、次のような書き方をします:
@EventHandler // EventPriority.NORMAL がデフォルト値
この記述は、メソッドがイベント優先度がNORMALのイベントハンドラである事を指定します。
イベントハンドラには優先度を指定でき、次のような書き方をします:
@EventHandler(priority = EventPriority.HIGHEST) // 高優先度のイベントとする @EventHandler(priority = EventPriority.LOW) // 低優先度のイベントとする
以上をまとめると、次のようになります:
@EventHandler public void onPlayerLogin(PlayerLoginEvent event) { // Your code here... }
リスナの追加
"PlayerListener"として拡張する場合は、"Listener"インタフェースを実装する必要があります。
この時点では、次のような形のメソッドになりそうです:
public class myPlayerListener implements Listener { @EventHandler public void onPlayerLogin(PlayerLoginEvent event) { // Your code here... } }
リスナ内部のメソッドは任意の名前を付けて呼び出する事が可能であり、
"onPlayerLogin()"のような、メソッドの名称が重要ではない事に言及しておきましょう。
「Bukkitは、どのEventを識別可能でどうやってListenするのか?」と不思議に思うかもしれませんが・・・
Bukkitは、あなたが指定したイベントからそれら(識別可能なEventと、そのListenの方法)を読み込みます。
上記の例で言えば、PlayerLoginEventから読み込みます。
- Note: 特定のイベントまたはBukkitが登録しないイベントに関しては、手動で指定しなければなりません。(訳が不適切か)
イベントハンドラの設定
イベントハンドラには、様々な内容を指定可能です。
現時点では次のような指定が行えます:
型 項目名 デフォルト値 概要 指定できる値 EventPriority priority EventPriority.NORMAL リスナに指定する優先度。
- EventPriority.MONITOR
- EventPriority.HIGHEST
- EventPriority.HIGH
- EventPriority.NORMAL
- EventPriority.LOW
- EventPriority.LOWEST
boolean ignoreCancelled false この値にTrueを指定したメソッド場合は、イベントがキャンセルされた際にイベントを受け付けない。
- true
- false
イベントの優先度
上記の表にあるように、イベントの優先度は6種類ありますが、イベントが呼び出される順序は下記のようになっています。
- EventPriority.LOWEST
- EventPriority.LOW
- EventPriority.NORMAL
- EventPriority.HIGH
- EventPriority.HIGHEST
- EventPriority.MONITOR
すべてのプラグインでイベント発生内容を取得できるようにする必要があります。
だから、キャンセルされてした後であっても、プラグインにイベントを渡します。
実際に別のプラグインが先にイベントをキャンセルしたとしても、イベントを uncancel することができます。
これを理解するには、優先順位の理解が本当に重要になります。
あるイベントが発生すると、まずLOWESTのリスナーが呼び出され、リスナーから設定内容の変更や、isCancelledの変更を行います。
その後、LOWからHIGHESTまで、順にリスナーが行われ、設定内容が書き換わります。
最終的に、MONITORのリスナーが呼び出されます。この時点で、全てのリスナーが設定した内容が最終結果として参照できるようになります。
MONITORは、イベントの内容を変更すべきではなく、イベントの結果を見るために使用されるべきです。
例として、BLOCK_PLACE イベントが処理されるとしましょう。
3つのプラグインが有効になっており、1つ目は基本的な領域保護プラグイン、2つ目はカンバンを使った付加価値的プラグイン、3つ目はログ記録プラグインです。
領域保護プラグインはPriority.LOWESTで待機します。
それは、「この領域内にブロックを配置することはできません」という理由で、イベントをキャンセルします。
カンバンを使った付加価値的プラグインはPriority.NORMALで待機します。
それは、「この領域内にカンバンを置くことができます」という理由で、イベントをuncancelします。
ログ記録プラグインはPriority.MONITORで待機します。
これは、イベントが実際に許されたという最終結果を確認し、イベント内容をログに記録します。
あなたはイベントの結果を変更する場合、LOWESTからHIGHESTの間で、慎重に選択してください。
あなたが、イベントを受け取りたいが、結果を変更しないときは、MONITORを使用してください。
ただし、MONITORのリスナーがイベントをキャンセルしたりしないように、イベントの内容を変更したりしないように、十分注意してください。最悪、他のプラグインの正常動作を阻害してしまうことになってしまいます。
イベントの登録
イベントを記述するクラスは、
Listenerインタフェースを実装(implements)し、かつイベントハンドラを含んでいなければなりません。
import org.bukkit.event.Listener; public class LoginListener implements Listener { }
イベントの登録処理は、PluginManagerインスタンスのregisterEventsメソッドとして、プラグインとリスナに記述します。
getServer().getPluginManager().registerEvents(Listener, Plugin);
リスナの例
このリスナは、2つのイベントハンドラを含んでいます。
1つは高優先度のもの、もうひとつは通常の優先度のものです。
import org.bukkit.event.Listener; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.player.PlayerLoginEvent; public class LoginListener implements Listener { @EventHandler public void normalLogin(PlayerLoginEvent event) { // 何かの処理 } @EventHandler(priority = EventPriority.HIGH) public void highLogin(PlayerLoginEvent event) { // 何かの処理 } }
プラグインへのイベントの登録
イベントの登録処理は、リスナとプラグインが必要です。
丁度よい事に、既にLoginListenerを作成していますので、これを利用してLoginPluginを作りましょう!
import org.bukkit.plugin.java.JavaPlugin; public class LoginPlugin extends JavaPlugin { public void onEnable() { getServer().getPluginManager().registerEvents(new LoginListener(), this); } }
プラグインへのイベントのリスナとしての登録
メインとなるクラスもイベントを持つ事ができます。
例:
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.event.Listener; import org.bukkit.event.EventHandler; import org.bukkit.event.player.PlayerLoginEvent; public class LoginPlugin extends JavaPlugin implements Listener { public void onEnable() { getServer().getPluginManager().registerEvents(this, this); } @EventHandler public void normalLogin(PlayerLoginEvent event) { // 何かの処理 } }
リスナへのイベントの登録
イベントの登録には複数の方法があります。リスナクラスでイベントを登録する例を記述します。
import org.bukkit.event.Listener; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.player.PlayerLoginEvent; public class LoginListener implements Listener { public LoginListener(LoginPlugin plugin) { plugin.getServer().getPluginManager().registerEvents(this, plugin); } @EventHandler public void normalLogin(PlayerLoginEvent event) { // Some code here } @EventHandler(priority = EventPriority.HIGH) public void highLogin(PlayerLoginEvent event) { // Some code here } }
LoginPluginは次のようになります:
import org.bukkit.plugin.java.JavaPlugin; public class LoginPlugin extends JavaPlugin { public void onEnable() { new LoginListener(this); } }
イベントリスナの登録解除
自分のプラグインから登録したイベントリスナだけでなく、他のプラグインから登録されたイベントリスナであっても、自由に登録を解除することが可能です。
特定のイベントからリスナを登録解除する
全てのイベントは、staticメソッドの getHandlerList() を持っています。
HandlerList を取得して unregister() メソッドを実行すれば、イベントに関連するリスナの登録を解除することが可能です。
例:
PlayerInteractEvent.getHandlerList().unregister(plugin); // 指定したプラグインから PlayerInteractEvent に登録された、 // 全てのイベントリスナを登録解除します。
getHandlerList() メソッドの詳細については、後述の自作イベントのところで説明します。
全てのイベントリスナの登録解除
HandlerList クラスの staticメソッド unregisterAll() を使用すると、指定したプラグイン、または、指定したイベントリスナークラスから登録された全てのイベントリスナを登録解除することができます。
例:
HandlerList.unregisterAll(plugin); // 指定したプラグインから登録された // 全てのイベントリスナを登録解除します。
イベントの自作
Bukkit自体が利用しているイベント記述の仕組みと全く同一の仕組みを利用して、 イベントを自作する事ができます。 この方法は、従来のような、 自作イベントである事に起因する固有のチェックが不要な点、 Bukkitの処理方法をそのまま利用するためにパフォーマンスを犠牲にしないという点で、 従来のイベント自作の方法よりも優れています。
イベントを自作する際は、次の2点に留意して下さい。
- Eventクラスを継承する必要がある
- static(静的)なハンドラとして作成する必要がある
staticハンドラは、自作イベント中に次のように記述して下さい。
private static final HandlerList handlers = new HandlerList(); public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; }
上記のコードを実際に自作のイベント処理の中に記述する事で、 処理は疎結合となり、実行速度が改善します。
自作イベントの例
import org.bukkit.event.Event; import org.bukkit.event.HandlerList; public class CustomEvent extends Event { private static final HandlerList handlers = new HandlerList(); private String message; public CustomEvent(String example) { message = example; } public String getMessage() { return message; } public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } }
自作イベントの呼び出し
イベントの呼び出し方法は、従来の方法と同様です:
// イベントのインスタンス化 CustomEvent event = new CustomEvent("Sample Message"); // イベントの実行 Bukkit.getServer().getPluginManager().callEvent(event); // イベントから得たメッセージの出力処理 Bukkit.getServer().broadcastMessage(event.getMessage());
自作イベントの待ち受け
イベントはどのようにListen(待受,受付などとも表記)すれば良いでしょう? 簡単です。通常のイベントと同様に待ち受けして下さい。
import org.bukkit.event.Listener; import org.bukkit.event.EventHandler; public class CustomListener implements Listener { @EventHandler public void normalLogin(CustomEvent event) { // 何かの処理 } }