純規の暇人趣味ブログ

手を突っ込んで足を洗う

[Bukkitプラグイン制作講座-其之六]特定の出来事(イベント)に反応する

      2016/12/13    HimaJyun

このシリーズ、久しぶりですね。

飽きた訳ではありませんよ、他のネタが沢山あったから書けなかっただけです。

すなわち、今はネタがないと言う意味ですね。

と言う訳で、今回は、サーバ内で発生した特定の出来事(イベント:ブロックが置かれた、等)に反応して処理を行う方法です。

前回の記事は「[Bukkitプラグイン制作講座-其之五]コマンド、時々クラス分割」です。

あと、今回からは実際のコードをGitHubに載せていこうと思います。(そっちの方が分かりやすいと思うので)

イベント駆動

先程までは感覚が掴みやすいように「出来事」と書き記しましたが、正しくはイベントです。

Bukkitはイベント駆動型プログラミング(イベントドリブン)です。

プラグインがロードされればonEnableメソッドが、ユーザがコマンドを実行すればonCommandメソッドが呼び出されます。

普通のプログラムがmain関数などで始まり、上から順に処理されていくのに対して、イベント駆動は特定の条件を満たした時に特定の処理が呼び出される様なプログラムの事を言います。

今まではonCommandなどしか使いませんでしたが、今回紹介する方法を身に付けると「ブロックを置いた時」や「敵を倒した時」に処理を行う事も可能ですし、場合によってはそれをキャンセル(ブロックを置けない様にする)事も可能です。

今回は例示として、「ブロックを置いたら座標と種類を表示する」プラグインを作成してみようと思います。

イベントの種類

Bukkitには「ブロックを置かれた」などの様々な種類のイベントがあります。

イベントの種類などは「Spigot V1.8.3のeventの一覧作ってみた(石橋を叩いて壊すブログ)」のサイトでまとめられているので、これを参考にしましょう。

いざ、イベント!!

それでは早速やっていきましょう。

イベントリスナを追加する

まずはイベントを待ち受けるクラス「イベントリスナ」を追加しましょう。

とりあえずクラス名は「BlockPlace」としましょう(別に何でも構いませんが
bukkit-plugin-development-6-001

イベントリスナには「Listener」インターフェースを実装する必要があります。

public class BlockPlace implements Listener {}

この様に、クラス名の後に「implements Listener」を追加します。

次に、反応したいイベントの種類に応じて関数を追加します。

@EventHandler
public void 関数名は何でも良い(BlockPlaceEvent e) {}

上記の様に、「@EventHandler」の下の行に追加したい関数を書きます。

関数名は何でも構いません、引数のBlockPlaceEventの所ですが、これは利用したいイベントに合わせて記載します。

先程紹介した「イベントリスト」から、それっぽい奴を探しましょう。

今回の目標では、「ブロックを置いた時にメッセージを表示する」ので「ブロックを置いた時」に反応する必要があります、これが可能なのは「BlockPlaceEvent」です。

イベントの優先順位

先程の「@EventHandler」ですが、以下の様にする事で優先順位が設定出来ます。

@EventHandler(priority = EventPriority.HIGHEST)

設定出来るのは以下の6つで、番号が小さい順から先に呼び出されます。

  1. EventPriority.LOWEST
  2. EventPriority.LOW
  3. EventPriority.NORMAL
  4. EventPriority.HIGH
  5. EventPriority.HIGHEST
  6. EventPriority.MONITOR

設定しなかった場合は「EventPriority.NORMAL」と同じ扱いになります。

「HIGHEST」や「MONITOR」は最後の方に呼び出されるため、イベントの内容(例えば手に入れた物など)を変更してしまうと他のプラグインの動作を阻害してしまうかも知れません。

良く分からないうちは設定しなくてもよいかと思われます。

処理を書く

今回は「イベントの使い方」がメインなので処理の詳しい説明は省きますが、以下の様に好きな処理を記載しましょう。

@EventHandler
public void 関数名は何でも良い(BlockPlaceEvent e) {
	Player player = e.getPlayer(); // ブロックを置いたプレイヤーを取得する
	Block block = e.getBlock(); // 設置したブロックを取得

	// メッセージを組み立て
	StringBuilder builder = new StringBuilder("BlockPlaceEventが発生:");
	builder.append("設置した物=").append(block.toString()).append(" ");

	// プレイヤーにメッセージを送信
	player.sendMessage(builder.toString());
}

この時の注意ですが、イベントの種類によっては非常に高頻度で呼び出されるため、あまり重たい処理は入れないように心がけましょう。

例えば、PlayerMoveEventなんかは歩くたびに凄まじい回数で呼び出されるので、ここに重い処理を入れるのは以ての外です。

イベントの登録(他クラス編)

さて、用意したイベントはBukkit側に「これを呼び出してね」とお願いする必要があります。

主にコンストラクタでやる方法と、他のクラス側でやる方法があるのですが、まずは他クラスから登録する方法を紹介しましょう。

と言っても簡単で、以下の様にregisterEventsの第一引数にnewしたリスナークラスを渡すだけです。

@Override
public void onEnable() {
	getServer().getPluginManager().registerEvents(new BlockPlace(), this);
}

規模が小さいイベントの場合に有効な手法です。

イベントの登録(コンストラクタ編)

個人的にはこちらをよく使っています。(メインクラスのインスタンスを渡すことが多いので)

この場合、まずはリスナー側に以下の様なコンストラクタを用意します。

public class BlockPlace implements Listener {
	public BlockPlace(Plugin plugin) {
		plugin.getServer().getPluginManager().registerEvents(this, plugin);
	}
}

次に、メインクラスのonEnableなどで以下の様にします。

@Override
public void onEnable() {
	new BlockPlace(this);
}

複雑なプラグインになって来た際に有効な手法です。

イベントの登録解除(特定のイベント)

登録されたイベントを登録解除したい時があるとしましょう。

例えば、onDisableでイベントの登録解除を行うようにすると仮定しましょう。

まず、特定のイベントだけを登録解除する場合です。

@Override
public void onDisable() {
	BlockPlaceEvent.getHandlerList().unregister(this);
}

この様に、「イベント名.getHandlerList().unregister」で登録を解除することが出来ます。

特定のイベントだけを解除したい場合はこちらの手法です。

イベントの登録解除(全て)

いくつかのイベントを登録しており、それらをまとめて全て解除したいとしましょう。

その場合には、以下の様にします。

@Override
public void onDisable() {
	HandlerList.unregisterAll(this);
}

簡単ですね。

動作テスト

制作中のプラグインは出来るだけこまめにテストするようにしましょう。(可能であればJUnitなどの単体テストを身に付けるべきですが)

巨大な変更を加えてからテストして不具合があると原因探しに苦労しますよ。

この様に、ブロックを設置するたびにメッセージが送信されてきます。
bukkit-plugin-development-6-002

最後に

この様にして、サーバ内で特定のイベントが発生(専門用語?では発火)すると特定の処理を行う事が出来ます。

使いこなせるとMinecraft離れした気色悪い動きすら可能になるので、ぜひ色々試してみて下さい。

今回のコードのサンプルはGitHubにあります、実際に動作するコードを参考にしてみて下さい。

 - プログラミング , ,