本ページの内容は、Bukkit WikiのPlugin Tutorialを和訳した物となります。(一部は省略しています)
最新ではない可能性があるため、より新しい情報を確認する場合は、本家を参照するようにして下さい。
本項目は、和訳の最中です。最後まで読むことは出来ません。
目次
Plugin用プロジェクトを始めるために
プロジェクトの作成
始める前にEclipseのワークスペースとファイルの設定を行う必要があります。
Eclipseを起動し、ファイル>新規>Java プロジェクトと選択して下さい。
プロジェクト名を入力し、新規プロジェクトウィザード画面の指示に従って、プロジェクトを作成してください。
Bukkit APIの参照
開発を始める前にbukkit APIライブラリをプロジェクトに追加する必要があります。 使用したい他のAPIも同じように追加することが可能です。
最新のBukkit APIはここからダウンロードが可能です。 Bukkit API - Development Snapshot
画面の左手にあるパッケージエクスプローラの(先ほど名前を付けた)プロジェクトを右クリックし、プロパティーを選択します。
開いた画面のJavaビルド・パスを選択し、ライブラリータブの中から、外部 Jar 追加ボタンを押して、ダウンロードしたBukkit APIを指定します。
BukkitのJavadocの利用方法
Eclipseを用いたJavaプログラミングの経験がある方なら、
Eclipseのエディタ上でクラスやメソッドにマウスカーソルを重ねた時に、
クラスやメソッドに関するドキュメントがポップアップで表示される事をご存知でしょう。
これは、Oracleのウェブサイトから得る事ができるJavadocの内容が表示されています。
Bukkitもまた、BukkitのAPIが提供するクラスやメソッドの各々のコードに同種のドキュメントを含んでおり、ポップアップで表示させる事ができます。
Eclipse上で、Bukkitのクラスやメソッドにマウスを重ねたタイミングでポップアップを表示できるようにするためには、下記の手順を行います。
- プロジェクトエクスプローラ上で、Bukkitのjarファイルを右クリックして、メニューを開く。
- "プロパティ"を選択し、表示されるポップアップ左側の"Javadoc ロケーション"項目を選択する。
- "Javadoc URL"の下部にあるテキストボックスに、"http://jd.bukkit.org/apidocs/" (ダブルクォートは除く)を貼り付ける。
- "検証"ボタンを押下し、URLがJavadocとして正しく識別される事をチェックしてから、OKボタンを押す。
Plugin開発の開始
onEnable() and onDisable()
このファンクションは、プラグインが有効/無効になったときに呼ばれます。
デフォルトでは、プラグインは自動的に読み込まれたときに、イベントを登録やデバッグ出力を行うことが出来ます。
onEnable()は、プラグインがBukkitから読み込まれるときに最初に呼ばれ、プラグインを実行するために必須です。
onEnable()とonDisable()の基本
前のセクションで作成したメインクラスに、onEnable()とonDisable()のメソッドを作成します。
public void onEnable(){ } public void onDisable(){ }
現時点では、このメソッドは何も行いません。また、エラーが発生する事にも気付くでしょう。このエラーは、メインクラスが、プラグインの機能を継承(extends)する必要がある事を示しています。当クラスの定義文を、下記のように変更しましょう。
これを・・・
Class <classname> {}
このように変更する。
Class <classname> extends JavaPlugin {}
すると、前述の追加コードが赤の下線でハイライト表示され、何かが間違っていることを通知してくるはずです。このハイライト部にマウスを重ねると、下記のようなポップアップが表示されるので、「'JavaPlugin'をインポートします(org.bukkit.plugin.java)」を選択します。
もしくは、
import org.bukkit.plugin.java.JavaPlugin;
をコード上部の定義部に記述する事でも同様の事が行えます。
Loggerを利用したメッセージ出力
始めに、プラグインが実際に動作するかどうかを確認するため、シンプルなメッセージをサーバコンソールに表示させてみましょう。この処理として、ログ出力機能(Logger)をメインクラスに定義して初期化します。
Logger log = this.getLogger();
その後、onEnable()メソッドに、プラグインが動作している事を通知するメッセージを出力する処理を記述します。
log.info("Your plugin has been enabled.");
onDisable()メソッドについても同等の記述を行います。ここまでのコーディングによって、メインクラスは下記の様な内容になっているはずです。
package me.<yourname>.<pluginname>; import java.util.logging.Logger; import org.bukkit.plugin.java.JavaPlugin; public class <classname> extends JavaPlugin { Logger log; public void onEnable(){ log = this.getLogger(); log.info("Your plugin has been enabled!"); } public void onDisable(){ log.info("Your plugin has been disabled."); } }
イベントとリスナ
コマンド
onCommand()メソッド
さて、どのようにイベントを登録しいつ発生するかについて理解しました。 しかし、コマンドを利用して何かを起こしたい場合はどのようなデータ型を利用すればよいのでしょうか?それには、onCommand()メソッドを利用します。 このメソッドは、プレイヤーが文字"/"を入力する度に実行されます。 具体的には、"/do something"とコマンドを実行した場合にonCommand()が呼び出されます。 今のところ、onCommand()はまだプログラムしていないので何も起こらないでしょう。
コマンド名は、Bukkitや他のプラグインが提供しているものや、 提供するであろうものとの重複を避け、 ユニークなものとなるように考慮して下さい。 具体的には、"give"コマンドは既にいくつかのプラグインで利用されています。 もし独自に"give"コマンドを実装した場合は、 "give"コマンドを実装している他のプラグインとの互換性は無くなります。
onCommand()は、常にboolean型の値としてtrue,falseのどちらかを戻り値として返さねばなりません。 trueを返した場合は、情報表示のためのイベントは発生しません。 falseを返した場合は、プラグインファイルを"usage: property"に戻し、コマンドを実行したプレイヤーに、コマンドの利用方法を通知するメッセージを表示します。
onCommand()を利用する際は、4つのパラメータを登録しなければなりません。
- CommandSender sender - コマンドの発行元
- Command cmd - 実行されたコマンドの内容
- String commandLabel - 利用されたコマンドエイリアス
- String[] args - コマンドの引数を格納した配列(例:/hello abc defコマンドが入力された場合の内容は、args[0]がabc、args[1]がdefとなる)
コマンドの設定
public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){ // プレイヤーが「/basic」コマンドを投入した際の処理... if(cmd.getName().equalsIgnoreCase("basic")){ // 何かの処理 return true; // コマンドが実行された場合は、trueを返して当メソッドを抜ける。 } return false; // コマンドが実行されなかった場合は、falseを返して当メソッドを抜ける。 }
onCommand()をコーディングする際は、上記の例のように、メソッドの最終ステップにfalseをreturnする行を記述するのが良い方法です。
falseが返る事で、plugin.yml(下記参照)内に記述されたメッセージが表示され、
onCommand()処理が正しく動作しなかった事をメッセージから検知できるため、動作確認の助けになるからです。
逆に、メソッドの最後でtrueを返す処理構造にしてしまった場合は、onCommand()内の各処理に対して、
処理結果チェック処理と結果が不正である場合にfalseを返すような、同じ処理を何度も記述する必要が出てきますので、大変な無駄となります。
なお、コード「.equalsIgnoreCase("basic")」のパラメタ「basic」は、大文字小文字の区別はありません。
plugin.ymlへのコマンドの追加
コマンドを用意する際は、plugin.ymlファイルにも定義を追加する必要があります。plugin.ymlの最後に次の行を追加します。
commands: basic: description: This is a demo command. usage: /<command> [player] permission: <plugin name>.basic permission-message: You don't have <permission>
- basic - コマンド名
- description - コマンドの説明文
- usage - onCommand()がfalseを返した際に、コマンド実行ユーザに向けて表示されるメッセージの内容。コマンドの使い方を明解に書いてください。
- permission - 当コマンドの動作に必要なプラグインの設定を、コマンド実行ユーザに知らせるメッセージ。(主に、コマンド実行に必要なpermissionを書きます)
- permission-message - コマンド実行権限を持たないユーザがコマンドを実行した場合に、その旨を同ユーザに知らせるメッセージ。
- Note: ymlファイルには、1個タブを2個のスペースで記述する必要があります。タブ文字は構文エラーとなるため利用できません。
コンソールコマンドとプレイヤーコマンド
察しの良い人は、CommandSender sender
パラメタに着目しているかもしれません。 CommandSender
クラスは、プラグイン開発者に2つの点で有用な、Bukkitが提供するインタフェースです。
CommandSenderインタフェースの実装: Player
と ConsoleCommandSender
(正確には Player
もインタフェース)です。
プラグインを作る際は、次の2点に注意しながら作る事が非常に良い方法です。
- プラグインが動作している事をサーバコンソールで確認しながら作る事
- ログインプレイヤー向けのコマンドが、実際にログインしているプレイヤーのみ実行できる事
プラグインは、senderがプレイヤーではない場合(例えばコンソールからプラグインのコマンドが投入された場合)であっても、サーバコンソールには明解でシンプルな動作結果を返します。(例えば天気を変えるコマンドの実行結果は、ゲーム内では無くサーバコンソール上のメッセージから確認すれば間違いありません)
記述例:
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { Player player = null; if (sender instanceof Player) { player = (Player) sender; } if (cmd.getName().equalsIgnoreCase("basic")){ // If the player typed /basic then do the following... // do something... return true; } else if (cmd.getName().equalsIgnoreCase("basic2")) { if (player == null) { sender.sendMessage("this command can only be run by a player"); } else { // do something else... } return true; } return false; }
この例では、basic コマンドはログインプレイヤーとサーバコンソールのどちらからでも実行できます。しかし、basic2 コマンドは、ログインプレイヤーしか実行できません。 一般的に、コマンドはプレイヤーとサーバコンソールの両者に実行を許可しますが、投入されたコマンドの実行可否をチェックする必要があるコマンドについては、ログインプレイヤー向けのコマンドとして実装されるべきでしょう。
つまり、プレイヤーに依存する処理(テレポートコマンドやアイテムを与えるコマンド等は、対象となるプレイヤーが必要です)を行うコマンドが、プレイヤーコマンドとして実装されるべきものと言えます。
凝ったコマンドを作りたい場合、コマンドのパラメタ(上記例では args
パラメタ)を利用して独自の処理実装を行うことができます。例えば、プレイヤー名を指定したテレポートコマンドのみが、サーバコンソールから実行できる実装にする等が考えられます。
- 訳者補記: コマンド実装方法には、プレイヤー向け・サーバコンソール向けの2種類があり、それぞれの区別はどのような考え方で行うべきかについて説明しています。
CommandExecutorのクラス分割
上記の例では、onCommand()メソッドをプラグインのメインクラスに記述していました。小さなプラグインでは良い方法ですが、大きなプラグインに拡張していくのであれば、適切なクラスを作成してそちらに配置するべきです。幸い、難しい事ではありません:
- プラグインのpackage内に、MyPluginCommandExecutor のような名前で新規のコマンド実行クラスを作成する(当然、MyPluginはあなたのプラグイン名に合わせて変えましょう)。
- MyPluginCommandExecutorに、BukkitのCommandExecutorインタフェースを実装(implements)させる。
- プラグインのonEnable()メソッドの処理で、MyPluginCommandExecutorクラスのインスタンスを生成する。
- MyPluginCommandExecutorインスタンスをパラメタとして、処理
getCommand("basic").setExecutor(myExecutor);
を実行させる。
- Note:
"basic"
は実行されたコマンドであり、myExecutor
はMyPluginCommandExecutorインスタンスです。
とっても良い具体例:
MyPlugin.java (the main plugin class): private MyPluginCommandExecutor myExecutor; @Override public void onEnable() { // .... myExecutor = new MyPluginCommandExecutor(this); getCommand("basic").setExecutor(myExecutor); // ... }
MyPluginCommandExecutor.java: public class MyPluginCommandExecutor implements CommandExecutor { private MyPlugin plugin; public MyPluginCommandExecutor(MyPlugin plugin) { this.plugin = plugin; } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { // ... implementation exactly as before ... } }
どのように、メインプラグインのインスタンスがMyPluginCommandExecutorのコンストラクタを実行するかに注目しましょう。
この節で紹介した方法により、メインのonCommand()メソッドが巨大で複雑になったとしても簡単に整理する事ができ、結果として、プラグインのメインクラスを複雑化させずに、処理分割する事ができます。
- Note: プラグインが複数のコマンドを持つ場合、個々のコマンドに対応するCommandExecutorをコーディングする必要があります。
- 訳者補記: 積極的に、大きな処理は小さく分割していきましょう。そのための仕組みをBukkitが用意しています。という事を教示しています。
堅牢なonCommandの記述
onCommand()メソッドを記述する際、パラメタの利用に関して、
決め付けて掛かってはいけない事がありますので念頭において下さい。
これらに留意した処理を記述する事で、堅牢なonCommand()をコーディングする事ができます。
senderがPlayer型であるかどうかをチェックする
senderがPlayer型であるとは限らない事を念頭において、チェックを行って下さい。
処理例:
public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){ if ((sender instanceof Player)) { // doSomething } else { sender.sendMessage(ChatColor.RED + "You must be a player!"); return false; } Player player = (Player) sender; return false; }
コマンドのパラメタ長さをチェックする
senderインスタンスは、妥当なパラメタを持っているとは限りません。パラメタ長をチェックして下さい。
処理例:
public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){ if (args.length > 4) { sender.sendMessage(ChatColor.RED + "Too many arguments!"); return false; } if (args.length < 2) { sender.sendMessage(ChatColor.RED + "Not enough arguments!"); return false; } }
プレイヤーがオンラインである事を確認する
特定のプレイヤーのPlayerインスタンスを利用したい場合、
必ずそのプレイヤーがオンラインである必要があります。
オンラインであるかどうかをチェックして下さい。
処理例:
public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){ Player other = (Bukkit.getServer().getPlayer(args[0])); if (other == null) { sender.sendMessage(ChatColor.RED + args[0] + " is not online!"); return false; } return false; }
オンラインではないプレイヤーのインスタンスに操作を加えたい場合は、一般的にはOfflinePlayer
クラスを利用して行う事ができます。