細 (→アイテムの操作) |
(<source lang="xml">...</source>の部分を<pre lang="xml">...</pre>に変更(xml内の<source>タグと競合していたため)) |
||
(11人の利用者による、間の131版が非表示) | |||
1行目: | 1行目: | ||
本ページの内容は、[http://wiki.bukkit.org/ Bukkit Wiki]の[http://wiki.bukkit.org/Plugin_Tutorial Plugin Tutorial]を和訳した物となります。(一部は省略しています)<br /> | 本ページの内容は、[http://wiki.bukkit.org/ Bukkit Wiki]の[http://wiki.bukkit.org/Plugin_Tutorial Plugin Tutorial]を和訳した物となります。(一部は省略しています)<br /> | ||
最新ではない可能性があるため、より新しい情報を確認する場合は、本家を参照するようにして下さい。<br /> | 最新ではない可能性があるため、より新しい情報を確認する場合は、本家を参照するようにして下さい。<br /> | ||
− | |||
== 始めに == | == 始めに == | ||
− | + | :'''重要''': 当ページには、原文の翻訳ではない、日本語読者向けの独自の記述を行っている箇所が少量あります。そのような内容を含む節ではNoteにて通知しています。 | |
+ | :'''重要''': 当ページでは、技術的な解説やたとえ話の原意を理解し易くするために、一部意訳を行っています。 | ||
+ | :'''重要''': 当ページは訳文であるため、訳文自体に対する文責は原著者にありません。当ページの内容を受けて原著者へ何らかのアクションを行う場合、その点を考慮して必ず原文にも目を通して下さい。 | ||
− | + | このチュートリアルは、Bukkitプラグインを開発するための基本的なノウハウを網羅しております。 | |
− | + | Javaを習得する、IDEで開発環境を構築する、などの非常に基本的な部分から説明が始まっていますが、既にご理解頂いている場合は読み飛ばしていただいて構いません。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
== Javaの習得 == | == Javaの習得 == | ||
20行目: | 16行目: | ||
このチュートリアルの読者には、Java言語プログラミングの基礎がわかる方を対象としています。<br/> | このチュートリアルの読者には、Java言語プログラミングの基礎がわかる方を対象としています。<br/> | ||
Javaの基礎知識が少ない方は、下記の情報に触れてください。 | Javaの基礎知識が少ない方は、下記の情報に触れてください。 | ||
− | |||
− | |||
*[http://docs.oracle.com/cd/E26537_01/tutorial/index.html Oracleの記事] (現在のJavaの公式サイト) | *[http://docs.oracle.com/cd/E26537_01/tutorial/index.html Oracleの記事] (現在のJavaの公式サイト) | ||
38行目: | 32行目: | ||
あなたがJavaの初心者なら、当チュートリアルが解説に利用しているEclipseを利用する事をお勧めします。<br/> | あなたがJavaの初心者なら、当チュートリアルが解説に利用しているEclipseを利用する事をお勧めします。<br/> | ||
− | Eclipseのお勧めのバージョンの配布元は、[http://mergedoc.sourceforge.jp/ | + | Eclipseのお勧めのバージョンの配布元は、[http://mergedoc.sourceforge.jp/ 日本語 Eclipse / Pleiades All in One 日本語ディストリビューション]です。<br/> |
これはMergedocProjectが配布する拡張されたパッケージであり、Eclipseの一次配布元が提供するパッケージではありません。 | これはMergedocProjectが配布する拡張されたパッケージであり、Eclipseの一次配布元が提供するパッケージではありません。 | ||
− | + | Pleiadesを利用する場合、「開発対象用 JDK」と、ビルドツールMavenが実行できるプラグイン「m2e」が同梱されているものを選択してください。<br/> | |
− | + | (Eclipseを既に利用している場合でも、m2eを後から追加インストールすることは可能です。) | |
− | |||
− | + | spigotなどのBukkitサーバーの実行環境を利用する場合、BuildTools.jar を利用してサーバー実行環境をビルドする必要がありますが、Mavenが利用できる場合は、Mavenが開発用のライブラリを自動でダウンロードするため、BuildTools.jar を使わなくても開発環境を構築することが可能です。<br/> | |
+ | ただし、当然ですが、デバッグ実行するための実行環境は必要になりますから、BuildTools.jar を利用して実行環境も準備しておいてください。このチュートリアルでは、BuildTools.jar を利用した実行環境の構築は割愛いたします。 | ||
+ | <!-- | ||
+ | もし、BuildTools.jar を利用した実行環境の構築について、きれいにまとめられている資料があれば、ここにリンクを置いてください。 | ||
+ | --> | ||
== Plugin用プロジェクトを始めるために == | == Plugin用プロジェクトを始めるために == | ||
=== プロジェクトの作成 === | === プロジェクトの作成 === | ||
− | + | 始めるために、新しいワークスペースを作成する必要があります。<br /> | |
− | + | Eclipseを起動し、ファイル>新規>プロジェクト... と選択して下さい。<br /> | |
+ | |||
+ | [[Image:create_project.png]] | ||
+ | |||
+ | 新規プロジェクトのダイアログが開きます。Mavenフォルダを開いて、Mavenプロジェクト を選択し、次へ を押してください。 | ||
+ | |||
+ | [[Image:create_project1.png]] | ||
+ | |||
+ | 次のダイアログで、「シンプルなプロジェクトの作成」にチェックを入れて、次へ を押してください。 | ||
+ | |||
+ | [[Image:create_project2.png]] | ||
+ | |||
+ | 次のダイアログで、作成するプラグインの「グループID」と「アーティファクトID」を入力します。<br/> | ||
+ | (アーティファクトIDとは、これから作ろうとするプラグインの名前と同義と思っていただいて構いません。) | ||
+ | |||
+ | グループIDは下記の方法で命名して下さい: | ||
+ | <blockquote> | ||
+ | * ドメインを持っているなら、そのドメインの逆引き名にしましょう。 | ||
+ | ** ドメイン名が'''i-am-a-bukkit-developer.com'''の場合、パッケージ名は'''com.i_am_a_bukkit_developer'''となります。 | ||
+ | * ドメイン名を持っていない場合、下記の選択肢があります。お勧め順です。 | ||
+ | ** '''Option 1''' githubやsourceforgeのようなソース管理サイトにアカウント登録します。 | ||
+ | *** githubの場合: [http://pages.github.com/ こちら]から作成したユーザページのURLを元に、'''com.github.<username>'''と命名します。 | ||
+ | ** '''Option 2''' emailアドレスを元に命名します。('''<username>@gmail.com'''であれば'''com.gmail.<username>'''とします) | ||
+ | ** '''Option 3''' 重複しなさそうなパッケージ名を指定します(他者の開発物と重複する可能性が高いので非推奨) | ||
+ | </blockquote> | ||
+ | |||
+ | |||
+ | 下記の名前で始まるような、他者の開発物と重複するパッケージで命名すべきでは'''ありません''': | ||
+ | <blockquote> | ||
+ | * org.bukkit | ||
+ | * net.bukkit | ||
+ | * com.bukkit | ||
+ | * net.minecraft | ||
+ | </blockquote> | ||
+ | |||
+ | ベースとなるグループIDを決めたら、次にプラグイン名を決めましょう。ここでは例として、TutorialPlugin と名付けます。 | ||
+ | |||
+ | ダイアログには次のように入力し、完了 を押してください。 | ||
+ | |||
+ | [[Image:create_project3.png]] | ||
+ | |||
+ | Eclipseの画面に戻ると、左側に TutorialPlugin プロジェクトが作成されているはずです。ここまで、うまく作成できましたか? | ||
− | + | 「ビルドプランを計算できませんでした。」と出た場合、作成したパッケージを右クリックし「実行」から「Maven install」をクリックすることにより、 | |
+ | Mavenのインストールが始まりエラーが出なくなります。 | ||
− | + | === Bukkit API の参照設定 === | |
− | + | 次に、左側に作成された TutorialPlugin プロジェクトの中にある、「pom.xml」というファイルをダブルクリックしてください。そして、画面右下のあたりにある「pom.xml」というタブをクリックしてください。 | |
− | |||
− | |||
− | + | [[Image:create_project4.png]] | |
− | + | まず、Java開発環境(JDK)の参照設定をします。<br/> | |
− | + | 一番最後の行に '''</project>''' というタグがありますが、その1行上に、次の内容を挿入してください。<br/> | |
+ | (なお、これは Java 7 を参照してビルドするための設定です。もし Java 6 を参照してビルドしたい場合は、sourceタグとtargetタグに書かれている 1.7 のところを 1.6 に変更してください。また、Java 8を参照したい場合は、1.7を1.8に変更してください。) | ||
− | + | <pre lang="xml"> | |
+ | <build> | ||
+ | <plugins> | ||
+ | <plugin> | ||
+ | <groupId>org.apache.maven.plugins</groupId> | ||
+ | <artifactId>maven-compiler-plugin</artifactId> | ||
+ | <configuration> | ||
+ | <source>1.7</source> | ||
+ | <target>1.7</target> | ||
+ | </configuration> | ||
+ | </plugin> | ||
+ | </plugins> | ||
+ | </build> | ||
+ | </pre> | ||
− | + | 次に、Bukkit APIを実装しているSpigotのリポジトリがどこにあるかURLで示します。<br/> | |
+ | 下記の内容を追記してください。 | ||
− | + | <pre lang="xml"> | |
− | + | <repositories> | |
− | + | <repository> | |
− | + | <id>spigot-repo</id> | |
+ | <url>https://hub.spigotmc.org/nexus/content/groups/public</url> | ||
+ | </repository> | ||
+ | </repositories> | ||
+ | </pre> | ||
− | + | 最後に、Bukkit API の参照設定を追加します。<br/> | |
− | + | 下記の内容を追記してください。 | |
− | + | <pre lang="xml"> | |
− | + | <dependencies> | |
− | + | <dependency> | |
− | + | <groupId>org.bukkit</groupId> | |
+ | <artifactId>bukkit</artifactId> | ||
+ | <version>1.10.2-R0.1-SNAPSHOT</version> | ||
+ | </dependency> | ||
+ | </dependencies> | ||
+ | </pre> | ||
− | + | この設定では、Bukkit API 1.10.2-R0.1-SNAPSHOT が参照されます。<br/> | |
− | + | 別のバージョンを参照したい場合は、versionタグの中を変更してください。<br/> | |
+ | 設定が可能なバージョン番号の一覧は、[https://hub.spigotmc.org/nexus/content/groups/public/org/bukkit/bukkit/ こちら] を参照してください。 | ||
− | + | ここまで編集をすると、pom.xmlは次のようになっているはずです。確認してみてください。 | |
− | + | <pre lang="xml"> | |
+ | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
+ | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
+ | <modelVersion>4.0.0</modelVersion> | ||
+ | <groupId>my.test.plugin</groupId> | ||
+ | <artifactId>TutorialPlugin</artifactId> | ||
+ | <version>0.0.1-SNAPSHOT</version> | ||
+ | <build> | ||
+ | <plugins> | ||
+ | <plugin> | ||
+ | <groupId>org.apache.maven.plugins</groupId> | ||
+ | <artifactId>maven-compiler-plugin</artifactId> | ||
+ | <configuration> | ||
+ | <source>1.7</source> | ||
+ | <target>1.7</target> | ||
+ | </configuration> | ||
+ | </plugin> | ||
+ | </plugins> | ||
+ | </build> | ||
+ | <repositories> | ||
+ | <repository> | ||
+ | <id>spigot-repo</id> | ||
+ | <url>https://hub.spigotmc.org/nexus/content/groups/public</url> | ||
+ | </repository> | ||
+ | </repositories> | ||
+ | <dependencies> | ||
+ | <dependency> | ||
+ | <groupId>org.bukkit</groupId> | ||
+ | <artifactId>bukkit</artifactId> | ||
+ | <version>1.10.2-R0.1-SNAPSHOT</version> | ||
+ | </dependency> | ||
+ | </dependencies> | ||
+ | </project> | ||
+ | </pre> | ||
− | == onEnable() | + | pom.xmlの編集が完了したら、Ctrl + S でファイルを保存してください。 |
− | + | ||
+ | 次に、プロジェクトを右クリックして、Maven>プロジェクトの更新... と選択してください。 | ||
+ | |||
+ | [[Image:Create_project7.png]] | ||
+ | |||
+ | Mavenプロジェクトの更新ダイアログが開きます。そのまま OK を押してください。 | ||
+ | |||
+ | [[Image:Create_project5.png]] | ||
+ | |||
+ | Eclipseが、Bukkit API のダウンロードを開始します。<br/> | ||
+ | しばらく待つと、プロジェクトが更新され、「Maven依存関係」というところにダウンロードされたBukkit APIが表示されます。<br/> | ||
+ | また、JREシステム・ライブラリ のところも、指定したJavaビルドバージョンが反映されていることを確認して下さい。 | ||
+ | |||
+ | [[Image:Create_project6.png]] | ||
+ | |||
+ | :'''訳者補記''': ダウンロードされた bukkit-x.x.x-Rxx.jar を右クリックし、Maven > Javadoc のダウンロード を実行しておくとよいでしょう。 | ||
+ | |||
+ | === パッケージの作成 === | ||
+ | |||
+ | 次に、作成したプラグインのプロジェクトに、パッケージを追加します。<br/> | ||
+ | 「src/main/java」のところを右クリックして、新規>パッケージ と選択してください。 | ||
+ | |||
+ | [[Image:Create_package1.png]] | ||
+ | |||
+ | パッケージ名は、先ほど設定したグループIDに、プラグイン名を小文字に変換した名前を後ろに付けたものが望ましいです。<br/> | ||
+ | ここでは例として、パッケージIDに '''my.test.plugin'''、プラグイン名に '''TutorialPlugin''' を使っているので、'''my.test.plugin.tutorialplugin''' と設定します。 | ||
+ | |||
+ | [[Image:Create_package2.png]] | ||
+ | |||
+ | === メインクラスの作成 === | ||
+ | |||
+ | 次に、プラグインのメインクラスを作成します。<br/> | ||
+ | メインクラスは、JavaPlugin を継承する必要があります。<br/> | ||
+ | (逆に、メインクラス以外のクラスは、直接的にも間接的にも、JavaPlugin を継承しないようにしてください。CraftBukkit 1.7.2-R0.3 以降では、プラグインが正しく動作しなくなります。)<br/> | ||
+ | メインクラスは、プラグイン名と同じ名前にすることが望ましいです。 | ||
+ | |||
+ | 先ほど作成したパッケージを右クリックして、新規>クラス と選択してください。 | ||
+ | |||
+ | [[Image:Create_package3.png]] | ||
+ | |||
+ | 名前の欄にクラス名を入力してください。<br/> | ||
+ | スーパークラスの欄に「org.bukkit.plugin.java.JavaPlugin」と入力してください。<br/> | ||
+ | (参照... ボタンを押して、開いたダイアログに「JavaPlugin」と入力して該当クラスを検索して選択しても構いません。) | ||
+ | |||
+ | 完了 を押して、新規クラスが作成されたことを確認して下さい。ソースコードは次のようになっているはずです。 | ||
+ | |||
+ | <pre lang="java"> | ||
+ | package my.test.plugin.tutorialplugin; | ||
+ | |||
+ | import org.bukkit.plugin.java.JavaPlugin; | ||
+ | |||
+ | public class TutorialPlugin extends JavaPlugin { | ||
+ | |||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | {{warning}} メインクラスは、コンストラクタを実行したり、新しいインスタンスを作成したりしないでください。 | ||
+ | |||
+ | === plugin.ymlの作成 === | ||
+ | |||
+ | メインクラスが作成できたら、次は plugin.yml ファイルを作成します。<br/> | ||
+ | plugin.yml ファイルは、プラグインとしてBukkitに読み込みされるときに、プラグインの設定を記述しておくファイルです。<br/> | ||
+ | 必須のファイルですから、必ず作成してください。 | ||
+ | |||
+ | プロジェクトの ''src/main/resources'' を右クリックして、新規>ファイル を選択してください。 | ||
+ | |||
+ | [[Image:Create_pluginyml1.png]] | ||
+ | |||
+ | 開いたダイアログの ファイル名 の欄で、「plugin.yml」と入力し、完了を押してください。 | ||
+ | |||
+ | [[Image:Create_pluginyml2.png]] | ||
+ | |||
+ | ''src/main/resources'' のところに、''plugin.yml'' が作成されたことを確認してください。<br/> | ||
+ | 作成された ''plugin.yml'' を、画面の右側へドラッグアンドドロップしてください。<br/> | ||
+ | 画面右側で ''plugin.yml'' の編集画面が開きます。 | ||
+ | |||
+ | ''plugin.yml'' に、下記の3行を書いてください。 | ||
+ | |||
+ | <syntaxhighlight lang="yaml"> | ||
+ | name: (あなたのプラグイン名) | ||
+ | main: (作成したパッケージ名).(作成したメインクラス) | ||
+ | version: (あなたのプラグインのバージョン) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | このチュートリアルでは、次のように作成します。 | ||
+ | |||
+ | <syntaxhighlight lang="yml"> | ||
+ | name: TutorialPlugin | ||
+ | main: my.test.plugin.tutorialplugin.TutorialPlugin | ||
+ | version: 0.0.1 | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | {{note}} メインクラスの設定は、大文字小文字が区別されるので、大文字小文字に注意して設定してください。 | ||
+ | {{note}} コロン(:)のあとにワンスペース( )あるのに注意してください。 | ||
+ | |||
+ | |||
+ | plugin.yml の詳細な内容一覧は、[[plugin.ymlの設定一覧]] をご参照ください。 | ||
+ | |||
+ | == onEnable()メソッドとonDisable()メソッド == | ||
+ | このメソッドは、プラグインが有効/無効になったときに、Bukkitから呼び出しされます。<br /> | ||
デフォルトでは、プラグインは自動的に読み込まれたときに、イベントを登録やデバッグ出力を行うことが出来ます。<br /> | デフォルトでは、プラグインは自動的に読み込まれたときに、イベントを登録やデバッグ出力を行うことが出来ます。<br /> | ||
− | onEnable() | + | onEnable()は、Bukkitが起動するときに、プラグインが有効化されたときに呼び出されます。onDisable()は、Bukkitが停止するときに、プラグインが無効化されたときに呼び出されます。<br /> |
=== onEnable()とonDisable()の基本 === | === onEnable()とonDisable()の基本 === | ||
前のセクションで作成したメインクラスに、onEnable()とonDisable()のメソッドを作成します。<br /> | 前のセクションで作成したメインクラスに、onEnable()とonDisable()のメソッドを作成します。<br /> | ||
<blockquote><source lang="java"> | <blockquote><source lang="java"> | ||
− | + | package my.test.plugin.tutorialplugin; | |
+ | |||
+ | import org.bukkit.plugin.java.JavaPlugin; | ||
− | + | public class TutorialPlugin extends JavaPlugin { | |
− | public void | + | @Override |
+ | public void onEnable() { | ||
+ | // TODO ここに、プラグインが有効化された時の処理を実装してください。 | ||
+ | } | ||
+ | @Override | ||
+ | public void onDisable() { | ||
+ | // TODO ここに、プラグインが無効化された時の処理を実装してください。 | ||
+ | } | ||
} | } | ||
</source></blockquote> | </source></blockquote> | ||
− | + | 現時点では、このメソッドは何も行いません。 | |
− | + | === Loggerを利用したメッセージ出力 === | |
− | |||
− | + | 始めに、プラグインが実際に動作するかどうかを確認するため、シンプルなメッセージをサーバコンソールに表示させてみましょう。<br/> | |
− | + | ログを出力するには、''getLogger()''メソッドを実行してLoggerを取得し、そのinfoメソッドを呼び出します。 | |
− | + | <blockquote><source lang="java"> | |
+ | getLogger().info("onEnableメソッドが呼び出されたよ!!"); | ||
+ | </source></blockquote> | ||
− | < | + | onDisable()メソッドについても同等の記述を行います。<br/> |
+ | メインクラスに、次のように実装してみてください。 | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
<blockquote><source lang="java"> | <blockquote><source lang="java"> | ||
− | package | + | package my.test.plugin.tutorialplugin; |
− | |||
import org.bukkit.plugin.java.JavaPlugin; | import org.bukkit.plugin.java.JavaPlugin; | ||
− | public class | + | public class TutorialPlugin extends JavaPlugin { |
− | + | @Override | |
+ | public void onEnable() { | ||
+ | getLogger().info("onEnableメソッドが呼び出されたよ!!"); | ||
+ | } | ||
− | + | @Override | |
− | + | public void onDisable() { | |
− | + | getLogger().info("onDisableメソッドが呼び出されたよ!!"); | |
− | + | } | |
− | |||
− | |||
− | |||
− | |||
} | } | ||
</source></blockquote> | </source></blockquote> | ||
+ | |||
+ | ここまで作ったところで、[[#プラグインの配布|プラグインのJarファイルの発行]] を行ってプラグインを作成し、実際にCraftBukkitのpluginsフォルダへ導入して、コンソールに設定したログメッセージが表示されることを確認してみてください。 | ||
+ | |||
+ | === Reloadを制御する === | ||
+ | |||
+ | サーバーが終了処理をするときや、開始処理をするときに限らず、 | ||
+ | サーバーが /reload コマンドにより、プラグインがdisableされenableされる動作について理解しておくことも重要です。<br/> | ||
+ | サーバーが開始したときについてだけ初期化処理を考慮することは危険です。<br/> | ||
+ | なぜなら、プレイヤーは既にオンライン状態で接続していますし、ワールドデータやチャンクは既にロードされていますし、他にもいろいろ想定していない違いがあります。 | ||
+ | |||
+ | * /reload が実行されると、プラグインとしては、onDisable() が実行されて、onEnable() が実行されます。 | ||
+ | * staticで保持していないデータは、全て失われます。 | ||
+ | * 上記に書いてあるように、プレイヤーやワールドやチャンクは既にロード済みの状態です。 | ||
== イベントとリスナ == | == イベントとリスナ == | ||
161行目: | 365行目: | ||
さて、どのようにイベントを登録しいつ発生するかについて理解しました。 | さて、どのようにイベントを登録しいつ発生するかについて理解しました。 | ||
しかし、コマンドを利用して何かを起こしたい場合はどのようなデータ型を利用すればよいのでしょうか?それには、onCommand()メソッドを利用します。 | しかし、コマンドを利用して何かを起こしたい場合はどのようなデータ型を利用すればよいのでしょうか?それには、onCommand()メソッドを利用します。 | ||
− | + | このメソッドは、plugin.ymlに設定したプラグインのコマンドが実行されたときに呼び出されます。 | |
− | |||
今のところ、onCommand()はまだプログラムしていないので何も起こらないでしょう。 | 今のところ、onCommand()はまだプログラムしていないので何も起こらないでしょう。 | ||
170行目: | 373行目: | ||
具体的には、"give"コマンドは既にいくつかのプラグインで利用されています。 | 具体的には、"give"コマンドは既にいくつかのプラグインで利用されています。 | ||
もし独自に"give"コマンドを実装した場合は、 | もし独自に"give"コマンドを実装した場合は、 | ||
− | "give"コマンドを実装している他のプラグインとの互換性は無くなります。 | + | "give"コマンドを実装している他のプラグインとの互換性は無くなります。<ref group="注">実際には、相互のコマンドを呼び分けるために特定のプレフィクスがコマンドの前に付与されます。しかしそれでも、単にgiveコマンドを呼んだだけではどのコマンドが呼ばれるかわからないことには変わりありません。</ref> |
− | onCommand() | + | onCommand()は、常にboolean型の値<ref group="注">trueかfalseのどちらか</ref>を戻り値として返さねばなりません。 |
trueを返した場合は、情報表示のためのイベントは発生しません。 | trueを返した場合は、情報表示のためのイベントは発生しません。 | ||
falseを返した場合は、プラグインファイルを"usage: property"に戻し、コマンドを実行したプレイヤーに、コマンドの利用方法を通知するメッセージを表示します。 | falseを返した場合は、プラグインファイルを"usage: property"に戻し、コマンドを実行したプレイヤーに、コマンドの利用方法を通知するメッセージを表示します。 | ||
181行目: | 384行目: | ||
* Command cmd - 実行されたコマンドの内容 | * Command cmd - 実行されたコマンドの内容 | ||
* String commandLabel - 利用されたコマンドエイリアス | * String commandLabel - 利用されたコマンドエイリアス | ||
− | * String[] args - コマンドの引数を格納した配列(例:/hello abc defコマンドが入力された場合の内容は、args[0]がabc、args[1]がdefとなる) | + | * String[] args - コマンドの引数を格納した配列(例:/hello abc defコマンドが入力された場合の内容は、args[0]がabc、args[1]がdefとなる)<ref group="注">半角空白 (U+0020)で分割されて、配列に格納される</ref> |
=== コマンドの設定 === | === コマンドの設定 === | ||
210行目: | 413行目: | ||
description: This is a demo command. | description: This is a demo command. | ||
usage: /<command> [player] | usage: /<command> [player] | ||
− | permission: | + | permission: tutorialplugin.basic |
permission-message: You don't have <permission> | permission-message: You don't have <permission> | ||
</source> | </source> | ||
− | * basic - | + | * basic - コマンド名。 |
− | * description - | + | * description - コマンドの説明文。 |
− | * usage - | + | * usage - コマンドの使い方。onCommand() でfalseを返したときに、コマンド実行ユーザに向けて表示されるメッセージの内容。あなたの作ろうとしているコマンドの使い方を、わかりやすく説明してください。 |
− | * permission - | + | * permission - コマンドの実行権限。このコマンドの実行に必要なパーミッションノードを設定します。あなたのプラグイン名と、コマンド名を、ピリオド(.)でつなげたパーミッションノードを設定することを推奨します(例:myplugin.test)。 |
− | * permission-message - | + | * permission-message - 上で設定したコマンド実行権限を持たないユーザがコマンドを実行した場合に、実行権限が無いことを同ユーザに知らせるメッセージ。 |
</blockquote> | </blockquote> | ||
− | :'''Note''': | + | なお、usage の欄では <command>、permission-message の欄では <permission> というキーワードをそのまま指定できます。それぞれ、設定したコマンド名と、設定したパーミッションノードに置き換えられます。<br> |
+ | 詳しい書き方は、[[plugin.ymlの設定一覧#コマンドのオプション設定|plugin.ymlの書き方の、コマンドのオプション設定の節]] を参照してください。 | ||
+ | |||
+ | :'''Note''': ymlファイルには、インデントに2個以上の半角スペースを記述する必要があります。タブ文字は構文エラーとなるため利用できません。 | ||
=== コンソールコマンドとプレイヤーコマンド === | === コンソールコマンドとプレイヤーコマンド === | ||
234行目: | 440行目: | ||
記述例: | 記述例: | ||
<blockquote><source lang="java"> | <blockquote><source lang="java"> | ||
+ | @Override | ||
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { | ||
− | + | if (cmd.getName().equalsIgnoreCase("basic")) { | |
− | + | // プレイヤーが /basic を実行すると、この部分の処理が実行されます... | |
− | |||
− | |||
− | |||
− | if (cmd.getName().equalsIgnoreCase("basic")){ // | ||
− | |||
return true; | return true; | ||
} else if (cmd.getName().equalsIgnoreCase("basic2")) { | } else if (cmd.getName().equalsIgnoreCase("basic2")) { | ||
− | if ( | + | // プレイヤーが /basic2 を実行すると、この部分の処理が実行されます... |
− | sender.sendMessage(" | + | if (!(sender instanceof Player)) { |
+ | sender.sendMessage("このコマンドはゲーム内から実行してください。"); | ||
} else { | } else { | ||
− | // | + | Player player = (Player) sender; |
+ | // コマンドの実行処理... | ||
} | } | ||
return true; | return true; | ||
253行目: | 457行目: | ||
return false; | return false; | ||
} | } | ||
− | </source> </blockquote> | + | </source> </blockquote> |
この例では、'''basic''' コマンドはログインプレイヤーとサーバコンソールのどちらからでも実行できます。しかし、'''basic2''' コマンドは、ログインプレイヤーしか実行できません。 | この例では、'''basic''' コマンドはログインプレイヤーとサーバコンソールのどちらからでも実行できます。しかし、'''basic2''' コマンドは、ログインプレイヤーしか実行できません。 | ||
274行目: | 478行目: | ||
:'''Note''': <code>'''"basic"'''</code>は実行されたコマンドであり、<code>'''myExecutor'''</code>はMyPluginCommandExecutorインスタンスです。 | :'''Note''': <code>'''"basic"'''</code>は実行されたコマンドであり、<code>'''myExecutor'''</code>はMyPluginCommandExecutorインスタンスです。 | ||
− | |||
とっても良い具体例: | とっても良い具体例: | ||
− | |||
− | + | MyPlugin.java (プラグインのメインクラス): | |
+ | <blockquote><source lang="java"> | ||
@Override | @Override | ||
public void onEnable() { | public void onEnable() { | ||
− | // . | + | // plugin.yml に basic というコマンドを定義していないと、 |
− | + | //実行した時にNullPointerExceptionが発生します。注意してください。 | |
− | + | getCommand("basic").setExecutor(new MyPluginCommandExecutor(this)); | |
− | getCommand("basic").setExecutor( | + | } |
− | + | </source></blockquote> | |
− | |||
− | |||
+ | MyPluginCommandExecutor.java: | ||
<blockquote><source lang="java"> | <blockquote><source lang="java"> | ||
− | MyPluginCommandExecutor | + | public class MyPluginCommandExecutor implements CommandExecutor { |
+ | |||
+ | // メインクラスの参照です。処理の中でメインクラスのメソッドを利用しない場合は、省略して構いません。 | ||
+ | private final MyPlugin instance; | ||
− | |||
− | |||
− | |||
− | |||
public MyPluginCommandExecutor(MyPlugin plugin) { | public MyPluginCommandExecutor(MyPlugin plugin) { | ||
this.plugin = plugin; | this.plugin = plugin; | ||
} | } | ||
− | + | ||
@Override | @Override | ||
− | public boolean onCommand(CommandSender sender, Command | + | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { |
− | // | + | // コマンドの実行内容... |
} | } | ||
} | } | ||
322行目: | 523行目: | ||
senderの内部データがPlayer型であるとは限らない事を念頭において、チェックを行って下さい。<br/> | senderの内部データがPlayer型であるとは限らない事を念頭において、チェックを行って下さい。<br/> | ||
処理例: | 処理例: | ||
− | <blockquote><source lang="java">public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){ | + | <blockquote><source lang="java"> |
− | + | @Override | |
− | + | public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){ | |
− | + | if (sender instanceof Player) { | |
− | |||
− | |||
− | |||
Player player = (Player) sender; | Player player = (Player) sender; | ||
+ | // ここに、処理を実装する。 | ||
+ | return true; | ||
+ | } else { | ||
+ | sender.sendMessage(ChatColor.RED + "ゲーム内から実行してください!"); | ||
return false; | return false; | ||
− | }</source> </blockquote> | + | } |
+ | return false; | ||
+ | } | ||
+ | </source> </blockquote> | ||
+ | |||
+ | このように、'''<code>if (sender instanceof Player)</code>''' で必ずPlayerであることをチェックしないと、プレイヤーでないコマンド実行者(例えばコンソール)がコマンドを実行したときに、'''<code>Player player = (Player) sender</code>''' のところで処理が失敗してしまいます。 | ||
=== コマンドのパラメタ長をチェックする === | === コマンドのパラメタ長をチェックする === | ||
338行目: | 545行目: | ||
<blockquote><source lang="java">public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){ | <blockquote><source lang="java">public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){ | ||
if (args.length > 4) { | if (args.length > 4) { | ||
− | sender.sendMessage(ChatColor.RED + " | + | sender.sendMessage(ChatColor.RED + "パラメタが多すぎます!"); |
return false; | return false; | ||
} | } | ||
if (args.length < 2) { | if (args.length < 2) { | ||
− | sender.sendMessage(ChatColor.RED + " | + | sender.sendMessage(ChatColor.RED + "パラメタが足りません!"); |
return false; | return false; | ||
} | } | ||
348行目: | 555行目: | ||
=== プレイヤーがオンラインである事を確認する === | === プレイヤーがオンラインである事を確認する === | ||
− | + | 特定のプレイヤーのPlayerインスタンスを利用したい場合、必ずそのプレイヤーがオンラインである必要があります。<br/> | |
− | |||
オンラインであるかどうかをチェックして下さい。<br/> | オンラインであるかどうかをチェックして下さい。<br/> | ||
+ | Bukkit.getPlayer(プレイヤー名) または、Bukkit.getPlayerExact(プレイヤー名) で、オンラインのプレイヤーを取得できます。<br/> | ||
+ | (Bukkit.getPlayer はプレイヤー名と前方一致で、Bukkit.getPlayerExact は完全一致で、プレイヤーを取得します。)<br/> | ||
+ | もし、指定したプレイヤーがオフラインだった場合は、nullが返されます。<br/> | ||
処理例: | 処理例: | ||
− | <blockquote><source lang="java">public boolean onCommand(CommandSender sender, Command | + | |
− | + | <blockquote><source lang="java"> | |
− | + | @Override | |
− | + | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { | |
− | + | if (args.length < 1) { | |
− | + | sender.sendMessage("コマンドの後にプレイヤー名を指定してください!"); | |
return false; | return false; | ||
− | }</source> </blockquote> | + | } |
+ | Player target = Bukkit.getPlayerExact(args[0]); | ||
+ | if ( target == null ) { | ||
+ | sender.sendMessage(ChatColor.RED + args[0] + "さんはオフラインです!"); | ||
+ | return true; | ||
+ | } else { | ||
+ | sender.sendMessage(ChatColor.AQUA + args[0] + "さんはオンラインです!"); | ||
+ | return true; | ||
+ | } | ||
+ | } | ||
+ | </source> </blockquote> | ||
オンラインではないプレイヤーのインスタンスに操作を加えたい場合は、一般的には<code>'''OfflinePlayer'''</code>クラスを利用して行う事ができます。 | オンラインではないプレイヤーのインスタンスに操作を加えたい場合は、一般的には<code>'''OfflinePlayer'''</code>クラスを利用して行う事ができます。 | ||
367行目: | 586行目: | ||
== 権限 == | == 権限 == | ||
− | + | :'''Note''': この節は、日本語読者向けに独自の内容を記述しています。 | |
− | + | Bukkitにおける権限の利用は、とっても簡単です! | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | 権限がセットされているか、いない(Javaの'''null'''と同等) | + | 権限がセットされているか、いない(Javaの'''null'''と同等)かを調べるには以下のメソッドを利用します: |
<blockquote><source lang="java">boolean isPermissionSet(String name)</source></blockquote> | <blockquote><source lang="java">boolean isPermissionSet(String name)</source></blockquote> | ||
− | + | また、権限を持っているかどうかを調べるには以下のメソッドを利用します: | |
− | + | <blockquote><source lang="java">boolean hasPermission(String name)</source></blockquote> | |
− | + | これを利用してプレイヤーが特定の権限を持っているかどうかを調べる処理は次のようになります: | |
− | + | <blockquote><source lang="java">if(player.hasPermission("some.pointless.permission")) { | |
− | + | player.sendMessage("あなたは権限を持っています。"); | |
− | + | }else{ | |
− | + | player.sendMessage("あなたは権限を持っていません。"); | |
− | + | }</source></blockquote> | |
− | |||
− | |||
− | |||
− | <blockquote><source lang="java"> | ||
− | |||
− | |||
− | |||
− | |||
− | + | 2メソッドの違いは、権限がセットされていなくてもデフォルトで与えられていれば権限を持っているという扱いになるかどうかです。<br> | |
+ | hasPermissionでは デフォルトで与えられていればtrueが返ります。 | ||
− | + | PermissionsExなどで 権限はグループとして扱われることが多いですが、Bukkitには権限をグループとして扱う機能はありません。<br/> | |
− | + | グループとして扱う場合は、別途 上記のようなプラグインを使うか、自分で作る必要があります。 | |
− | |||
− | |||
− | |||
− | |||
=== 権限の設定 === | === 権限の設定 === | ||
− | + | デフォルトで権限を付与させたり、OPにのみデフォルトで付与させたい場合は、<br> | |
− | + | ''plugin.yml''を使うと簡単にできます。 | |
− | |||
下記は、''plugin.yml''の最後に追加する形で設定する権限です: | 下記は、''plugin.yml''の最後に追加する形で設定する権限です: | ||
− | <blockquote>< | + | <blockquote> |
+ | <syntaxhighlight lang="yaml"> | ||
+ | permissions: | ||
doorman.*: | doorman.*: | ||
description: Gives access to all doorman commands | description: Gives access to all doorman commands | ||
431行目: | 635行目: | ||
default: true | default: true | ||
doorman.denied: | doorman.denied: | ||
− | description: Prevents this user from entering the door</ | + | description: Prevents this user from entering the door |
− | + | </syntaxhighlight> | |
+ | </blockquote> | ||
450行目: | 655行目: | ||
==== 子権限 ==== | ==== 子権限 ==== | ||
− | + | 権限を設定するときに''* 権限''を利用すると、その権限の子権限すべてを操作することができます。<br/> | |
これは、変更されたBukkit APIと、子権限の定義によって実現した機能であり、<br/> | これは、変更されたBukkit APIと、子権限の定義によって実現した機能であり、<br/> | ||
高い柔軟性を提供しています。<br/> | 高い柔軟性を提供しています。<br/> | ||
下記はその実装例です: | 下記はその実装例です: | ||
− | <blockquote>< | + | <blockquote> |
+ | <syntaxhighlight lang="yaml"> | ||
+ | permissions: | ||
doorman.*: | doorman.*: | ||
description: Gives access to all doorman commands | description: Gives access to all doorman commands | ||
462行目: | 669行目: | ||
doorman.knock: true | doorman.knock: true | ||
doorman.denied: false</source> | doorman.denied: false</source> | ||
− | + | </syntaxhighlight> | |
''doorman.*''権限は、いくつかの子権限を含んでいます。 | ''doorman.*''権限は、いくつかの子権限を含んでいます。 | ||
''doorman.*''権限がtrueである場合に、 | ''doorman.*''権限がtrueである場合に、 | ||
469行目: | 676行目: | ||
その子権限のデフォルト権限は、全て反転(trueが定義値ならfalseになる)された状態で機能します。 | その子権限のデフォルト権限は、全て反転(trueが定義値ならfalseになる)された状態で機能します。 | ||
</blockquote> | </blockquote> | ||
+ | |||
+ | ねっ、簡単でしょ? | ||
=== 独自の権限設定 === | === 独自の権限設定 === | ||
487行目: | 696行目: | ||
といった複数の方法で、長大なタスクをゲームロジックと並行で処理させる事ができます。 | といった複数の方法で、長大なタスクをゲームロジックと並行で処理させる事ができます。 | ||
− | 詳しくは、[[ | + | 詳しくは、[[スケジューラのプログラミング]]中の、同期タスクと非同期タスクのスケジューリング方法に関する解説を参考にして下さい。 |
== ブロックの操作 == | == ブロックの操作 == | ||
+ | |||
ブロックを生成する簡単な方法は、既存のブロックを取得して変更する事です。<br/> | ブロックを生成する簡単な方法は、既存のブロックを取得して変更する事です。<br/> | ||
例えば、あなたの5ブロック上方にあるブロックを変更したい場合、<br/> | 例えば、あなたの5ブロック上方にあるブロックを変更したい場合、<br/> | ||
まずはそのブロックを取得した上で、変更を加えることになります。<br/> | まずはそのブロックを取得した上で、変更を加えることになります。<br/> | ||
PlayerMoveイベントの処理中でこれを行う例を示します: | PlayerMoveイベントの処理中でこれを行う例を示します: | ||
− | <blockquote><source lang="java">public void onPlayerMove(PlayerMoveEvent evt) { | + | <blockquote><source lang="java"> |
− | + | @EventHandler | |
− | + | public void onPlayerMove(PlayerMoveEvent evt) { | |
− | + | // プレイヤーの位置を取得します。 | |
− | + | Location loc = event.getPlayer().getLocation(); | |
− | + | // 位置のY座標を+5します。位置情報を変更しているだけで、実際にプレイヤーの位置が移動するわけではないことに注意してください。 | |
+ | loc.setY(loc.getY() + 5); | ||
+ | // 指定位置のブロックを取得します。 | ||
+ | Block b = loc.getBlock(); | ||
+ | // ブロックの種類に石(STONE)を設定します。 | ||
+ | b.setType(Material.STONE); | ||
}</source></blockquote> | }</source></blockquote> | ||
このコードの処理は、プレイヤーが移動する(=PlayerMoveイベントが発生する)度に、<br/> | このコードの処理は、プレイヤーが移動する(=PlayerMoveイベントが発生する)度に、<br/> | ||
− | + | プレイヤーの5ブロック上方のブロックがStoneに変化する動作として見えるでしょう。<br/> | |
ブロック取得までの流れは・・・ | ブロック取得までの流れは・・・ | ||
# 取得したプレイヤーの位置から、ワールドを取得する | # 取得したプレイヤーの位置から、ワールドを取得する | ||
# 位置のY座標を+5する | # 位置のY座標を+5する | ||
− | # <code>''' | + | # <code>'''loc.getBlockAt(loc);'''</code>で、指定位置に存在するブロックを得る |
となります。<br/> | となります。<br/> | ||
位置(''loc'')中のブロックインスタンス(''b'')に対して、<br/> | 位置(''loc'')中のブロックインスタンス(''b'')に対して、<br/> | ||
− | + | Materialを変えたり、ブロックデータ自体を変更する事もできます。 | |
+ | 詳しくは、JavaDocを参照してください。<br/> | ||
− | + | 建物の生成や、一定のアルゴリズムに従ったブロック生成処理などを行う一例を示します。<br/> | |
− | |||
− | |||
− | |||
− | |||
− | |||
例えば、固体ブロックによる立方体を生成する処理は、<br/> | 例えば、固体ブロックによる立方体を生成する処理は、<br/> | ||
3階層にネストしたforループ処理によって記述できます。 | 3階層にネストしたforループ処理によって記述できます。 | ||
− | <blockquote><source lang="java">public void generateCube(Location | + | <blockquote><source lang="java"> |
− | + | public void generateCube(Location loc, int length){ | |
− | + | // 与えられたLocationから、立方体の端の座標を取得します。 | |
− | + | // getN()メソッドを使うと intへキャストする必要がありますが、 | |
− | + | // getBlockN()メソッドを使えばそのままintで座標を取得できます。 | |
− | + | int x1 = loc.getBlockX(); | |
− | + | int y1 = loc.getBlockY(); | |
− | + | int z1 = loc.getBlockZ(); | |
− | + | ||
− | + | // 一辺の長さを足すことで、立方体の反対側の座標を計算します。 | |
− | + | int x2 = x1 + length; | |
− | + | int y2 = y1 + length; | |
− | + | int z2 = z1 + length; | |
− | + | ||
− | + | World world = loc.getWorld(); | |
− | + | ||
− | + | // x座標方向のループ | |
− | + | for (int xPoint = x1; xPoint <= x2; xPoint++) { | |
− | + | // y座標方向のループ | |
− | + | for (int yPoint = y1; yPoint <= y2; yPoint++) { | |
− | + | // z座標方向のループ | |
− | + | for (int zPoint = z1; zPoint <= z2; zPoint++) { | |
− | + | // ループで処理する座標のブロックを取得します。 | |
− | + | Block currentBlock = world.getBlockAt(xPoint, yPoint, zPoint); | |
− | }</source></blockquote> | + | // ダイアモンドブロックに設定します! |
+ | currentBlock.setType(Material.DIAMOND_BLOCK); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </source></blockquote> | ||
このメソッドは、辺の長さと開始点の指定を受けて、任意のサイズと位置を持つ直方体を生成する処理です。<br/> | このメソッドは、辺の長さと開始点の指定を受けて、任意のサイズと位置を持つ直方体を生成する処理です。<br/> | ||
ブロックの削除処理の場合も、このロジックを真似て同様のアルゴリズムで実装する事ができます。<br/> | ブロックの削除処理の場合も、このロジックを真似て同様のアルゴリズムで実装する事ができます。<br/> | ||
− | + | ただし、セットするブロックの種類はMaterial.AIRになりますね。 | |
== プレイヤーインベントリの操作 == | == プレイヤーインベントリの操作 == | ||
582行目: | 799行目: | ||
== アイテムの操作 == | == アイテムの操作 == | ||
− | + | アイテムはスタックという単位で操作します。<br/> | |
− | + | スタックデータに対する全ての操作は、[http://jd.bukkit.org/apidocs/org/bukkit/inventory/ItemStack.html ItemStackクラスの仕様]を参照して下さい。 | |
+ | |||
+ | === エンチャント === | ||
+ | |||
+ | アイテムにエンチャントを付与するには、ItemStackクラスの '''addEnchantment(Enchantment enchant, int level)''' メソッドを使用します。 | ||
+ | |||
+ | addEnchantment()メソッドでは、元々エンチャントが出来ないアイテムに対して、独自にエンチャントを付与する処理は書けません。<br/> | ||
+ | もし、通常ありえないエンチャントを設定したい場合は、addEnchantment()メソッドの代わりにaddUnsafeEnchantment()メソッドを使ってください。 | ||
− | == | + | Sharpness 1 エンチャントを石の剣に付与する例を示します。 |
+ | <blockquote><source lang="java"> | ||
+ | // 新しい石の剣を生成します。 | ||
+ | ItemStack myItem = new ItemStack(Material.STONE_SWORD); | ||
− | == | + | // エンチャントを付与します。 |
+ | myItem.addEnchantment(Enchantment.DAMAGE_ALL, 1); | ||
+ | </source></blockquote> | ||
+ | |||
+ | 次に、火属性 100 を、木の棒に付与する例を示します。 | ||
+ | <blockquote><source lang="java"> | ||
+ | // 新しい木の棒を生成します。 | ||
+ | ItemStack myItem = new ItemStack(Material.STICK); | ||
+ | |||
+ | // 木の棒にFireAspectレベル100を付与します。 | ||
+ | // ただしFireAspectレベル100は通常存在しないので、addEnchantment を使うと失敗します。 | ||
+ | // ありえないエンチャントを設定したい場合は、addUnsafeEnchantment を使ってください。 | ||
+ | myItem.addUnsafeEnchantment(Enchantment.FIRE_ASPECT, 100); | ||
+ | </source></blockquote> | ||
+ | |||
+ | === ItemMeta === | ||
+ | |||
+ | アイテムの表示名を変更するには、次のようにします。 | ||
+ | <blockquote><source lang="java"> | ||
+ | String myDisplayName = "すごい剣"; | ||
+ | |||
+ | ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD); //アイテムを生成します。 | ||
+ | ItemMeta im = myItem.getItemMeta(); //ItemStackから、ItemMetaを取得します。 | ||
+ | im.setDisplayName(myDisplayName); //アイテム表示名を設定します。 | ||
+ | myItem.setItemMeta(im); //元のItemStackに、変更したItemMetaを設定します。 | ||
+ | </source></blockquote> | ||
+ | |||
+ | 次に、loreを設定してみましょう。loreは、ゲーム内でアイテムにカーソルを合わせたときに表示される説明文のことです。 | ||
+ | <blockquote><source lang="java"> | ||
+ | List<String> lores = new ArrayList<String>(); | ||
+ | lores.add("loreのテストです。"); | ||
+ | lores.add("これは2行目です。"); | ||
+ | |||
+ | ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD); //アイテムを生成します。 | ||
+ | ItemMeta im = myItem.getItemMeta(); //ItemStackから、ItemMetaを取得します。 | ||
+ | im.setLore(lores); //loreを設定します。 | ||
+ | myItem.setItemMeta(im); //元のItemStackに、変更したItemMetaを設定します。 | ||
+ | </source></blockquote> | ||
+ | |||
+ | == Metadata == | ||
+ | |||
+ | Bukkit では、プラグインの開発をより簡単にするため、Playerクラス、Entityクラス、Worldクラスに紐づく追加データをMetadataという形式で管理できるようになっています。 | ||
+ | 今までは、それぞれのプラグイン内で、Player、Entity、World などをキーとしたHashMap型の変数内で管理していたと思いますが、それをMetadataで置き換えすることができます。 | ||
+ | Metadataのデータは、全てMetadatableのメンバーで構成されます([http://jd.bukkit.org/doxygen/de/d59/interfaceorg_1_1bukkit_1_1metadata_1_1MetadataValue.html#ab49975fe013a0626dd29d3b85c63a82f javadoc]も参照してください)。 | ||
+ | 動作は非常に単純です。 | ||
+ | Metadatableクラスは、それぞれのインスタンスが自分のMetadataのHashMapを持っています。 | ||
+ | つまり例えば、経済プラグインを作る場合、HashMap<Player, Double> のようなデータをプラグイン内で持つ必要はありません。 | ||
+ | プレイヤーにMetadataを直接設定すればよいのです! | ||
+ | |||
+ | === Metadataを使うメリット === | ||
+ | |||
+ | * Metadataは全てBukkit側で管理されます。プラグイン側で管理する必要がありません。 | ||
+ | * プラグイン間で共通にアクセスできるので、データの共有に使用できます。 | ||
+ | |||
+ | === Metadataを使うデメリット === | ||
+ | |||
+ | * データの取得・設定を行うときに、ひと手間が必要になります。 | ||
+ | * Bukkitが停止すると、全てのMetadataが消えます。 | ||
+ | |||
+ | === Metadataの使い方 === | ||
+ | |||
+ | <source lang="java"> | ||
+ | |||
+ | /** | ||
+ | * PlayerにMetadataを設定するサンプルメソッドです。 | ||
+ | * @param player 対象プレイヤー | ||
+ | * @param key Metadataのキー名 | ||
+ | * @param value Metadataの値 | ||
+ | * @param plugin プラグインクラス | ||
+ | */ | ||
+ | public void setMetadata(Player player, String key, Object value, Plugin plugin) { | ||
+ | player.setMetadata(key, new FixedMetadataValue(plugin, value)); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * PlayerからMetadataを取得するサンプルメソッドです。 | ||
+ | * @param player 対象プレイヤー | ||
+ | * @param key Metadataのキー名 | ||
+ | * @param plugin プラグインクラス | ||
+ | * @return Metadataの値 | ||
+ | */ | ||
+ | public Object getMetadata(Player player, String key, Plugin plugin) { | ||
+ | List<MetadataValue> values = player.getMetadata(key); | ||
+ | for (MetadataValue value : values) { | ||
+ | if (value.getOwningPlugin().getDescription().getName() | ||
+ | .equals(plugin.getDescription().getName())) { | ||
+ | return value.value(); | ||
+ | } | ||
+ | } | ||
+ | return null; | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | :'''Note''': もしあなたが、必ず boolean、int、String としてMetadataの値を取得したいのであれば、asBoolean()、asInt()、asString() メソッドを使うことで、キャストせずに直接取得が可能です。 | ||
== データベース == | == データベース == | ||
+ | 時にフラットファイルは、データベースとして利用するには不足があるでしょう。 | ||
+ | Linux/Mac/Windowsのマシンにおいて、もっとも汎用的なデータベースはSQL(Structured Query Language)です。 | ||
+ | |||
+ | ソフトウェアとしてのSQLは、個々のデータを格納するセルと、それらを識別するための列およびヘッダから成るデータベースを生成します。 | ||
+ | 会計用ソフトウェア(たとえばエクセルのような)の表が、より便利になったモノを思い浮かべてください。 | ||
+ | SQLは、この表形態のデータに、データ同士の整合性の確保を目的とした独自のルール付けが行え、 | ||
+ | かつ、フラットなファイルよりも高速で高機能なデータ利用のための方法も提供します。 | ||
+ | |||
+ | Bukkitは、SQLの標準的な機能を利用するための仕組みを提供しています。 | ||
+ | 残念なことに、SQLには複数の種類が存在し、それぞれの設定方法と利用方法が少しずつ異なっています。 | ||
+ | 各自のニーズに応じて、好きな種類のSQLを選んで利用してください。(プラグインについても、利用するデータベースの種類に応じて複数の接続・利用方法が利用できます) | ||
+ | |||
+ | === SQLite === | ||
+ | |||
+ | Alta189は、ダウンロード・インポートによって導入や移動が容易に可能なSQLiteを、Bukkitのプラグインで利用するためのライブラリを使う方法のチュートリアル、[http://forums.bukkit.org/threads/lib-tut-mysql-sqlite-bukkit-drivers.33849/ fantastic SQLite tutorial]を書きました。 | ||
+ | |||
+ | SQLの構文については、次に紹介する動画を一度見てみる事をお勧めします。簡単な内容なので短時間で済みます。[http://www.w3schools.com/sql/default.asp SQL Tutorials @W3Schools] | ||
+ | |||
+ | SQLiteは、その稼動のためにサーバを構築する必要がないため、シンプルさにおいて非常に優れています。 | ||
+ | 新規にデータベースとその中のテーブルを作成する手順は少量です。また、データのバックアップも、1個のデータベースファイルをバックアップするだけです。ただし、データ間の整合性の確保や柔軟性、データ件数が100万を超えるような膨大なデータの扱いにおいては、多少弱い面もあります。 | ||
+ | |||
+ | とはいえSQLiteは、SQLデータベースを利用する新規プラグインの開発作業を、迅速で簡単にしてくれるメリットがあります。また、サーバ向けの大規模なSQLの予習のためにも有用です。 | ||
+ | |||
+ | === MySQL === | ||
+ | もうひとつ、人気があるSQLにMySQLというものがあります。 | ||
+ | SQLiteよりもサーバ寄りの用途をもった種類のSQLで、多くの有名な企業の業務システムや、一日に100万アクセスを捌くようなウェブサイトが、この機能に依存しています。ただし、チューニング可能な範囲や機能が多岐に渡るため、セキュリティ上のリスクが、データベース管理者のMySQLへの習熟度に大きく左右されます。 | ||
+ | |||
+ | プラグインからMySQLを利用する処理のコーディング自体は、小規模なSQLiteからMByte単位のOracleのデータベースに対するものと、大差ありません。しかし、サーバ用途のデータベースの管理作業は、どんどん増えていくものです。使い続ける内に、データベース利用者のアカウントや権限の設定作業を行う事になっていくでしょう。また、データのバックアップやバックアップからの復旧(Rollbackと呼ぶ)作業のために、SQL文のスクリプトを書く事にもなるはずです。 | ||
== プラグインの配布 == | == プラグインの配布 == | ||
+ | |||
+ | プラグインを実装し終わったら、Mavenを使ってビルドして、リリース用のJarファイルを作成してみましょう。 | ||
+ | |||
+ | プロジェクトを右クリックして、実行>Maven install と選択してください。 | ||
+ | |||
+ | [[Image:Plugin_deploy1.png]] | ||
+ | |||
+ | Eclipseのコンソールに、ビルド情報が流れます。<br/> | ||
+ | ビルドがうまくいけば、コンソールに「BUILD SUCCESS」と表示されてビルドが終了します。<br/> | ||
+ | もしビルドが失敗したなら、エラー情報を元に、エラーの解決を試みてください。 | ||
+ | |||
+ | このチュートリアルで紹介したJDKが同梱のPleiadesを利用していない場合、MavenがJDKを見つけられずにエラーになっていることが多いです。<br/> | ||
+ | その場合は、Eclipseの設定を開いて、正しいJDKを選択しなおしてください。<br/> | ||
+ | Eclipseの設定は、「ウィンドウ」メニュー>設定 を選択し、開いたダイアログで、Java>インストール済みのJRE を選択します。<br/> | ||
+ | ここで、正しいバージョンのJDKが選択されていることを確認してください。 | ||
+ | |||
+ | このチュートリアルで紹介したJDKが同梱のPleiadesを利用している場合で、 | ||
+ | <blockquote> | ||
+ | [ERROR] Unable to locate the Javac Compiler in:<br/> | ||
+ | [ERROR] C:\pleiades\java\7\..\lib\tools.jar | ||
+ | </blockquote> | ||
+ | のようなエラーが出ることがあります。これは上記のメッセージの通り、最初から同梱されているJDKにtools.jarが含まれているのに参照設定されていないからです。<br/> | ||
+ | これを解決するには、次のようにします。 | ||
+ | *Pleiadesのメニューから、ウィンドウ > 設定 と選び、設定ダイアログを表示します。 | ||
+ | *設定ダイアログの左側で、java > インストール済みのJRE と選びます。 | ||
+ | *使用しているJRE(先ほどのエラーメッセージに含まれるファイルパスと、ロケーションが、一致するもの)を選択し、「編集...」を押します。 | ||
+ | *JREの編集ダイアログで、「外部Jar追加...」を押し、tools.jarを探して選択します。例えばエラーメッセージが「C:\pleiades\java\7\..\lib\tools.jar」なら、「C:\pleiades\java\7\lib\tools.jar」に見つかるはずです。 | ||
+ | *「OK」を押して、設定ダイアログを閉じます。 | ||
+ | *再度、「mvn install」を実行して、今度は正常にビルドができることを確認してください。 | ||
+ | |||
+ | ビルドがうまくいった場合、プロジェクトのフォルダの中に target フォルダが作成されており、そのフォルダの中にビルドされたJarファイルがあります。 | ||
+ | |||
+ | [[Image:Plugin_deploy2.png]] | ||
+ | |||
+ | |||
+ | プラグインのコードとplugin.ymlに不備が無ければ、エクスポートしたJarファイルはすぐにBukkitプラグインとして動作します。Jarファイルを、Bukkitサーバの'''"plugins"'''フォルダの中に配置し、Bukkitサーバを起動し、プラグインの動作確認をしてみましょう。なお、ローカルマシン上で起動するBukkitサーバへは、Minecraftクライアントのマルチプレイヤーサーバの接続先IPアドレスに'''"localhost"'''を指定して接続する事でログインできます。 | ||
+ | |||
+ | もしプラグインが上手く動かず、それがどうしても自分で解決できない場合は、当Wikiやその原文をもう一度よく読み、それでも駄目なら[http://forums.bukkit.org/forums/plugin-development.5/ Bukkitプラグイン開発者フォーラム(英語圏)], [http://wiki.bukkit.org/IRC bukkitdev Bukkit公式サイトの開発者向けIRCチャンネル(英語圏)], [http://forum.minecraftuser.jp/viewforum.php?f=21 マインクラフト非公式日本ユーザフォーラムの関連トピック(日本語圏)], [http://minecraftjp.info/modding/index.php/Minecraft_Modding_Wiki#IRC 当WikiのMOD制作関連IRCチャンネル(日本語圏)]をたずねてみて下さい。有用なプラグインが作れたら、[http://dev.bukkit.org/ dev.bukkit]に登録し、プラグインを広く公開する事を検討してみて下さい。他のBukkitユーザ・開発者に貢献する事ができます。 | ||
== ヒントとノウハウ == | == ヒントとノウハウ == | ||
+ | CraftBukkit APIは、すばらしい機能をたくさん提供しています。 | ||
+ | 下記に、面白い効果を実現するコードを示します。 | ||
+ | |||
+ | === プレイヤーに着火する === | ||
+ | 下記は、指定されたプレイヤーに着火するサンプルです。例えば '''/ignite Notch''' と実行すると、Notchが燃えます! | ||
+ | <blockquote><source lang="java"> | ||
+ | @Override | ||
+ | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { | ||
+ | // equals() の代わりに equalsIgnoreCase() を使うと、大文字小文字に関係なく、 | ||
+ | // 文字列の一致を確認できます。"ignite"でも"IgNiTe"でも指定可能になります。 | ||
+ | if (cmd.getName().equalsIgnoreCase("ignite")) { | ||
+ | // コマンドのパラメータに、燃やすプレイヤーが指定されているかどうかを | ||
+ | // 確認します。 | ||
+ | if (args.length != 1) { | ||
+ | // onCommandでfalseを戻すと、plugin.ymlのusageに設定したメッセージを | ||
+ | // コマンド実行者の画面に表示します。 | ||
+ | return false; | ||
+ | } | ||
+ | |||
+ | // 燃やすプレイヤーを取得します。 | ||
+ | Player target = Bukkit.getPlayerExact(args[0]); | ||
+ | |||
+ | // 対象プレイヤーが、オンラインかどうかを確認します。 | ||
+ | if (target == null) { | ||
+ | sender.sendMessage(args[0] + " というプレイヤーが見つかりません!"); | ||
+ | return true; | ||
+ | } | ||
+ | |||
+ | // 対象プレイヤーを、1000tick(=50秒) の間、燃えるようにします。 | ||
+ | target.setFireTicks(1000); | ||
+ | return true; | ||
+ | } | ||
+ | return false; | ||
+ | } | ||
+ | </source> </blockquote> | ||
+ | |||
+ | === プレイヤーを殺す === | ||
+ | |||
+ | 同じ要領で、プレイヤーを殺害するコマンドの例を紹介します。 | ||
+ | onCommand()メソッドに記述します: | ||
+ | <blockquote><source lang="java"> | ||
+ | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args){ | ||
+ | if(cmd.getName().equalsIgnoreCase("KillPlayer")){ | ||
+ | // コマンドのパラメータに、殺害するプレイヤーが指定されているかどうかを | ||
+ | // 確認します。 | ||
+ | if (args.length != 1) { | ||
+ | // onCommandでfalseを戻すと、plugin.ymlのusageに設定したメッセージを | ||
+ | // コマンド実行者の画面に表示します。 | ||
+ | return false; | ||
+ | } | ||
+ | Player target = Bukkit.getPlayerExact(args[0]); | ||
+ | // 対象プレイヤーがオンラインかどうかを確認します。 | ||
+ | if (target == null) { | ||
+ | sender.sendMessage(args[0] + " というプレイヤーは見つかりません!"); | ||
+ | return true; | ||
+ | } | ||
+ | // 対象に1000ダメージを与えます。 | ||
+ | target.damage(1000); | ||
+ | return true; | ||
+ | } | ||
+ | return false; | ||
+ | } | ||
+ | </source></blockquote> | ||
+ | |||
+ | 上記の拡張版として、プレイヤーを爆死させる処理を下記に示します: | ||
+ | <blockquote><source lang="java"> | ||
+ | @Override | ||
+ | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { | ||
+ | if(cmd.getName().equalsIgnoreCase("KillPlayer")){ | ||
+ | Player target = Bukkit.getPlayerExact(args[0]); | ||
+ | // 対象プレイヤーがオンラインかどうかを確認します。 | ||
+ | if (target == null) { | ||
+ | sender.sendMessage(args[0] + " というプレイヤーは見つかりません!"); | ||
+ | return true; | ||
+ | } | ||
+ | float explosionPower = 4F; // 爆発の大きさです。1Fでガストの火球、3Fでクリーパーの爆発、4FでTNTの爆発 に相当します。 | ||
+ | |||
+ | // 爆発を起こしつつ、対象に1000ダメージを与えます。 | ||
+ | target.getWorld().createExplosion(target.getLocation(), explosionPower); | ||
+ | target.damage(1000); | ||
+ | return true; | ||
+ | } | ||
+ | return false; | ||
+ | } | ||
+ | </source> </blockquote> | ||
+ | |||
+ | === 爆発を起こす === | ||
+ | このコードは、TNTの爆発と同様の音とヴィジュアルを再現します。 | ||
+ | これは、TNTの爆発効果は無効化しつつ、音とヴィジュアル効果を発生させる処理に転用できます。 | ||
+ | <blockquote><source lang="java"> | ||
+ | @EventHandler | ||
+ | public void onExplosionPrime(ExplosionPrimeEvent event) { | ||
+ | Entity entity = event.getEntity(); | ||
+ | |||
+ | // このイベントは、点火されたTNTにより発生したのかどうかを確認します。 | ||
+ | // (つまり、TNTの爆発はこれで無効化されますが、クリーパーの爆発は無効化されません) | ||
+ | if (entity instanceof TNTPrimed) { | ||
+ | event.setCancelled(true); // イベントをキャンセルして、爆発を無かったことにする | ||
+ | entity.getWorld().createExplosion(entity.getLocation(), 0); // 偽物の爆発を発生させる | ||
+ | } | ||
+ | } | ||
+ | </source></blockquote> | ||
+ | |||
+ | === プレイヤーを非表示にする === | ||
+ | |||
+ | これは指定したプレイヤーから自分を非表示にするサンプルです。 | ||
+ | 指定したプレイヤー以外のプレイヤーからは、自分が見えたままになっています。 | ||
+ | |||
+ | <blockquote> | ||
+ | <source lang="java"> | ||
+ | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { | ||
+ | if(cmd.getName().equalsIgnoreCase("HideMe") && args.length == 1) { | ||
+ | |||
+ | // コマンド実行者がプレイヤーかどうかを確認します。 | ||
+ | if (!(sender instanceof Player)) { | ||
+ | sender.sendMessage("このコマンドはゲーム内から実行してください!"); | ||
+ | return true; | ||
+ | } | ||
+ | // sender instanceof Player の検査が終わっているので、Playerクラスへ安全にキャストできます。 | ||
+ | Player s = (Player) sender; | ||
+ | |||
+ | // コマンドのパラメータに、対象のプレイヤーが指定されているかどうかを確認します。 | ||
+ | if (args.length != 1) { | ||
+ | // onCommandでfalseを戻すと、plugin.ymlのusageに設定したメッセージを | ||
+ | // コマンド実行者の画面に表示します。 | ||
+ | return false; | ||
+ | } | ||
+ | |||
+ | // 指定されたプレイヤーを取得します。 | ||
+ | // 指定されたプレイヤーがサーバーに接続していない場合、targetはnullになります。 | ||
+ | Player target = Bukkit.getPlayerExact(args[0]); | ||
+ | if (target == null) { | ||
+ | sender.sendMessage("Player " + args[0] + " というプレイヤーは見つかりません!"); | ||
+ | return true; | ||
+ | } | ||
+ | // プレイヤー "s" を、指定したプレイヤー "target" から、非表示に設定します。 | ||
+ | target.hidePlayer(s); | ||
+ | return true; | ||
+ | } | ||
+ | return false; | ||
+ | } | ||
+ | </source> | ||
+ | </blockquote> | ||
+ | |||
+ | === クリックした場所に雷を落とす === | ||
+ | |||
+ | 下記のサンプルは、釣竿を持ってクリックしたときに、クリックした場所を取得して、その場所に雷を落とします。 | ||
+ | |||
+ | <blockquote> | ||
+ | <source lang="java"> | ||
+ | @EventHandler | ||
+ | public void onPlayerInteractBlock(PlayerInteractEvent event) { | ||
+ | |||
+ | Player player = event.getPlayer(); | ||
+ | |||
+ | if (player.getItemInHand().getType() == Material.FISHING_ROD) { | ||
+ | // プレイヤーが見ている場所に雷をおとします。 | ||
+ | |||
+ | Block target = getTargetBlock(player); | ||
+ | if (target != null) { | ||
+ | target.getWorld().strikeLightning(target.getLocation()); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | private Block getTargetBlock(Player player) { | ||
+ | |||
+ | // 視線上のブロックを100ブロック先まで取得 | ||
+ | BlockIterator it = new BlockIterator(player, 100); | ||
+ | |||
+ | // 手前側から検証を行う。 | ||
+ | // Blockが取得できた時点でreturnして終了する。 | ||
+ | while ( it.hasNext() ) { | ||
+ | |||
+ | Block block = it.next(); | ||
+ | |||
+ | if ( block.getType() != Material.AIR ) { | ||
+ | // ブロックが見つかった | ||
+ | return block; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // 最後までブロックがみつからなかった | ||
+ | return null; | ||
+ | } | ||
+ | </source> | ||
+ | </blockquote> | ||
+ | |||
+ | |||
+ | == プラグインのサンプル兼雛形 == | ||
+ | * [http://pastebin.com/fKjjReyv Example.Java] | ||
+ | * [http://pastebin.com/VhNBnLPU ExamplePlayerListener.Java] | ||
+ | * [http://pastebin.com/dRVeQKjw ExampleBlockListener.Java] | ||
+ | * [http://pastebin.com/GG5R4vAn ExampleEntityListener.Java] | ||
+ | |||
+ | この内容について質問がある場合、遠慮なく[http://forums.bukkit.org/members/adamki11s.42417/ Adamki11s]か[http://wiki.bukkit.org/IRC BukkitDevのIRCチャンネル](当Wikiの原文を掲載しているサイトのIRCチャンネルです)で聞いてください。 | ||
+ | :'''Note''': 当ページは訳文であるため、訳文自体に対する文責は原著者にありません。その点を考慮して必ず原文にも目を通してから質問して下さい。 | ||
+ | |||
+ | [[カテゴリ:Bukkitチュートリアル|*]] |
2020年1月20日 (月) 12:57時点における最新版
本ページの内容は、Bukkit WikiのPlugin Tutorialを和訳した物となります。(一部は省略しています)
最新ではない可能性があるため、より新しい情報を確認する場合は、本家を参照するようにして下さい。
目次
始めに[編集]
- 重要: 当ページには、原文の翻訳ではない、日本語読者向けの独自の記述を行っている箇所が少量あります。そのような内容を含む節ではNoteにて通知しています。
- 重要: 当ページでは、技術的な解説やたとえ話の原意を理解し易くするために、一部意訳を行っています。
- 重要: 当ページは訳文であるため、訳文自体に対する文責は原著者にありません。当ページの内容を受けて原著者へ何らかのアクションを行う場合、その点を考慮して必ず原文にも目を通して下さい。
このチュートリアルは、Bukkitプラグインを開発するための基本的なノウハウを網羅しております。
Javaを習得する、IDEで開発環境を構築する、などの非常に基本的な部分から説明が始まっていますが、既にご理解頂いている場合は読み飛ばしていただいて構いません。
Javaの習得[編集]
- Note: 当記事の一部は原文の訳ではありません、日本語読者向けに独自の内容を記述しています。
このチュートリアルの読者には、Java言語プログラミングの基礎がわかる方を対象としています。
Javaの基礎知識が少ない方は、下記の情報に触れてください。
- Oracleの記事 (現在のJavaの公式サイト)
- とほほのJava入門 - 基礎をみっちり解説している
- Javaのオブジェクト指向入門 - 継承や実装等のオブジェクト指向用語が分からない人向け
Javaがどうしても理解できない場合は、この他にも様々な資料があるので探して触れてください。
Javaの開発環境[編集]
- Note: 当記事の一部は原文の訳ではありません、日本語読者向けに独自の内容を記述しています。
プラグインを開発する(またはJavaを学ぶ)前に、開発環境(IDE (Integrated Development Environment))をインストールして下さい。
開発環境は、プラグインのコンパイルとデバッグを行うために使います。
Javaの主要なIDEには、Eclipse, Netbeans, IntelliJ IDEA の3つがあります。
EclipseはBukkit開発者の中では最も人気があり、IntelliJの方は業界内で広く利用されています。
あなたがJavaの初心者なら、当チュートリアルが解説に利用しているEclipseを利用する事をお勧めします。
Eclipseのお勧めのバージョンの配布元は、日本語 Eclipse / Pleiades All in One 日本語ディストリビューションです。
これはMergedocProjectが配布する拡張されたパッケージであり、Eclipseの一次配布元が提供するパッケージではありません。
Pleiadesを利用する場合、「開発対象用 JDK」と、ビルドツールMavenが実行できるプラグイン「m2e」が同梱されているものを選択してください。
(Eclipseを既に利用している場合でも、m2eを後から追加インストールすることは可能です。)
spigotなどのBukkitサーバーの実行環境を利用する場合、BuildTools.jar を利用してサーバー実行環境をビルドする必要がありますが、Mavenが利用できる場合は、Mavenが開発用のライブラリを自動でダウンロードするため、BuildTools.jar を使わなくても開発環境を構築することが可能です。
ただし、当然ですが、デバッグ実行するための実行環境は必要になりますから、BuildTools.jar を利用して実行環境も準備しておいてください。このチュートリアルでは、BuildTools.jar を利用した実行環境の構築は割愛いたします。
Plugin用プロジェクトを始めるために[編集]
プロジェクトの作成[編集]
始めるために、新しいワークスペースを作成する必要があります。
Eclipseを起動し、ファイル>新規>プロジェクト... と選択して下さい。
新規プロジェクトのダイアログが開きます。Mavenフォルダを開いて、Mavenプロジェクト を選択し、次へ を押してください。
次のダイアログで、「シンプルなプロジェクトの作成」にチェックを入れて、次へ を押してください。
次のダイアログで、作成するプラグインの「グループID」と「アーティファクトID」を入力します。
(アーティファクトIDとは、これから作ろうとするプラグインの名前と同義と思っていただいて構いません。)
グループIDは下記の方法で命名して下さい:
- ドメインを持っているなら、そのドメインの逆引き名にしましょう。
- ドメイン名がi-am-a-bukkit-developer.comの場合、パッケージ名はcom.i_am_a_bukkit_developerとなります。
- ドメイン名を持っていない場合、下記の選択肢があります。お勧め順です。
- Option 1 githubやsourceforgeのようなソース管理サイトにアカウント登録します。
- githubの場合: こちらから作成したユーザページのURLを元に、com.github.<username>と命名します。
- Option 2 emailアドレスを元に命名します。(<username>@gmail.comであればcom.gmail.<username>とします)
- Option 3 重複しなさそうなパッケージ名を指定します(他者の開発物と重複する可能性が高いので非推奨)
下記の名前で始まるような、他者の開発物と重複するパッケージで命名すべきではありません:
- org.bukkit
- net.bukkit
- com.bukkit
- net.minecraft
ベースとなるグループIDを決めたら、次にプラグイン名を決めましょう。ここでは例として、TutorialPlugin と名付けます。
ダイアログには次のように入力し、完了 を押してください。
Eclipseの画面に戻ると、左側に TutorialPlugin プロジェクトが作成されているはずです。ここまで、うまく作成できましたか?
「ビルドプランを計算できませんでした。」と出た場合、作成したパッケージを右クリックし「実行」から「Maven install」をクリックすることにより、 Mavenのインストールが始まりエラーが出なくなります。
Bukkit API の参照設定[編集]
次に、左側に作成された TutorialPlugin プロジェクトの中にある、「pom.xml」というファイルをダブルクリックしてください。そして、画面右下のあたりにある「pom.xml」というタブをクリックしてください。
まず、Java開発環境(JDK)の参照設定をします。
一番最後の行に </project> というタグがありますが、その1行上に、次の内容を挿入してください。
(なお、これは Java 7 を参照してビルドするための設定です。もし Java 6 を参照してビルドしたい場合は、sourceタグとtargetタグに書かれている 1.7 のところを 1.6 に変更してください。また、Java 8を参照したい場合は、1.7を1.8に変更してください。)
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
次に、Bukkit APIを実装しているSpigotのリポジトリがどこにあるかURLで示します。
下記の内容を追記してください。
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public</url>
</repository>
</repositories>
最後に、Bukkit API の参照設定を追加します。
下記の内容を追記してください。
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
<version>1.10.2-R0.1-SNAPSHOT</version>
</dependency>
</dependencies>
この設定では、Bukkit API 1.10.2-R0.1-SNAPSHOT が参照されます。
別のバージョンを参照したい場合は、versionタグの中を変更してください。
設定が可能なバージョン番号の一覧は、こちら を参照してください。
ここまで編集をすると、pom.xmlは次のようになっているはずです。確認してみてください。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>my.test.plugin</groupId>
<artifactId>TutorialPlugin</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
<version>1.10.2-R0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
pom.xmlの編集が完了したら、Ctrl + S でファイルを保存してください。
次に、プロジェクトを右クリックして、Maven>プロジェクトの更新... と選択してください。
Mavenプロジェクトの更新ダイアログが開きます。そのまま OK を押してください。
Eclipseが、Bukkit API のダウンロードを開始します。
しばらく待つと、プロジェクトが更新され、「Maven依存関係」というところにダウンロードされたBukkit APIが表示されます。
また、JREシステム・ライブラリ のところも、指定したJavaビルドバージョンが反映されていることを確認して下さい。
- 訳者補記: ダウンロードされた bukkit-x.x.x-Rxx.jar を右クリックし、Maven > Javadoc のダウンロード を実行しておくとよいでしょう。
パッケージの作成[編集]
次に、作成したプラグインのプロジェクトに、パッケージを追加します。
「src/main/java」のところを右クリックして、新規>パッケージ と選択してください。
パッケージ名は、先ほど設定したグループIDに、プラグイン名を小文字に変換した名前を後ろに付けたものが望ましいです。
ここでは例として、パッケージIDに my.test.plugin、プラグイン名に TutorialPlugin を使っているので、my.test.plugin.tutorialplugin と設定します。
メインクラスの作成[編集]
次に、プラグインのメインクラスを作成します。
メインクラスは、JavaPlugin を継承する必要があります。
(逆に、メインクラス以外のクラスは、直接的にも間接的にも、JavaPlugin を継承しないようにしてください。CraftBukkit 1.7.2-R0.3 以降では、プラグインが正しく動作しなくなります。)
メインクラスは、プラグイン名と同じ名前にすることが望ましいです。
先ほど作成したパッケージを右クリックして、新規>クラス と選択してください。
名前の欄にクラス名を入力してください。
スーパークラスの欄に「org.bukkit.plugin.java.JavaPlugin」と入力してください。
(参照... ボタンを押して、開いたダイアログに「JavaPlugin」と入力して該当クラスを検索して選択しても構いません。)
完了 を押して、新規クラスが作成されたことを確認して下さい。ソースコードは次のようになっているはずです。
package my.test.plugin.tutorialplugin;
import org.bukkit.plugin.java.JavaPlugin;
public class TutorialPlugin extends JavaPlugin {
}
plugin.ymlの作成[編集]
メインクラスが作成できたら、次は plugin.yml ファイルを作成します。
plugin.yml ファイルは、プラグインとしてBukkitに読み込みされるときに、プラグインの設定を記述しておくファイルです。
必須のファイルですから、必ず作成してください。
プロジェクトの src/main/resources を右クリックして、新規>ファイル を選択してください。
開いたダイアログの ファイル名 の欄で、「plugin.yml」と入力し、完了を押してください。
src/main/resources のところに、plugin.yml が作成されたことを確認してください。
作成された plugin.yml を、画面の右側へドラッグアンドドロップしてください。
画面右側で plugin.yml の編集画面が開きます。
plugin.yml に、下記の3行を書いてください。
name: (あなたのプラグイン名) main: (作成したパッケージ名).(作成したメインクラス) version: (あなたのプラグインのバージョン)
このチュートリアルでは、次のように作成します。
name: TutorialPlugin main: my.test.plugin.tutorialplugin.TutorialPlugin version: 0.0.1
plugin.yml の詳細な内容一覧は、plugin.ymlの設定一覧 をご参照ください。
onEnable()メソッドとonDisable()メソッド[編集]
このメソッドは、プラグインが有効/無効になったときに、Bukkitから呼び出しされます。
デフォルトでは、プラグインは自動的に読み込まれたときに、イベントを登録やデバッグ出力を行うことが出来ます。
onEnable()は、Bukkitが起動するときに、プラグインが有効化されたときに呼び出されます。onDisable()は、Bukkitが停止するときに、プラグインが無効化されたときに呼び出されます。
onEnable()とonDisable()の基本[編集]
前のセクションで作成したメインクラスに、onEnable()とonDisable()のメソッドを作成します。
package my.test.plugin.tutorialplugin; import org.bukkit.plugin.java.JavaPlugin; public class TutorialPlugin extends JavaPlugin { @Override public void onEnable() { // TODO ここに、プラグインが有効化された時の処理を実装してください。 } @Override public void onDisable() { // TODO ここに、プラグインが無効化された時の処理を実装してください。 } }
現時点では、このメソッドは何も行いません。
Loggerを利用したメッセージ出力[編集]
始めに、プラグインが実際に動作するかどうかを確認するため、シンプルなメッセージをサーバコンソールに表示させてみましょう。
ログを出力するには、getLogger()メソッドを実行してLoggerを取得し、そのinfoメソッドを呼び出します。
getLogger().info("onEnableメソッドが呼び出されたよ!!");
onDisable()メソッドについても同等の記述を行います。
メインクラスに、次のように実装してみてください。
package my.test.plugin.tutorialplugin; import org.bukkit.plugin.java.JavaPlugin; public class TutorialPlugin extends JavaPlugin { @Override public void onEnable() { getLogger().info("onEnableメソッドが呼び出されたよ!!"); } @Override public void onDisable() { getLogger().info("onDisableメソッドが呼び出されたよ!!"); } }
ここまで作ったところで、プラグインのJarファイルの発行 を行ってプラグインを作成し、実際にCraftBukkitのpluginsフォルダへ導入して、コンソールに設定したログメッセージが表示されることを確認してみてください。
Reloadを制御する[編集]
サーバーが終了処理をするときや、開始処理をするときに限らず、
サーバーが /reload コマンドにより、プラグインがdisableされenableされる動作について理解しておくことも重要です。
サーバーが開始したときについてだけ初期化処理を考慮することは危険です。
なぜなら、プレイヤーは既にオンライン状態で接続していますし、ワールドデータやチャンクは既にロードされていますし、他にもいろいろ想定していない違いがあります。
- /reload が実行されると、プラグインとしては、onDisable() が実行されて、onEnable() が実行されます。
- staticで保持していないデータは、全て失われます。
- 上記に書いてあるように、プレイヤーやワールドやチャンクは既にロード済みの状態です。
イベントとリスナ[編集]
- 新しいEventSystemの使い方を参照して下さい
コマンド[編集]
onCommand()メソッド[編集]
さて、どのようにイベントを登録しいつ発生するかについて理解しました。 しかし、コマンドを利用して何かを起こしたい場合はどのようなデータ型を利用すればよいのでしょうか?それには、onCommand()メソッドを利用します。 このメソッドは、plugin.ymlに設定したプラグインのコマンドが実行されたときに呼び出されます。 今のところ、onCommand()はまだプログラムしていないので何も起こらないでしょう。
コマンド名は、Bukkitや他のプラグインが提供しているものや、 提供するであろうものとの重複を避け、 ユニークなものとなるように考慮して下さい。 具体的には、"give"コマンドは既にいくつかのプラグインで利用されています。 もし独自に"give"コマンドを実装した場合は、 "give"コマンドを実装している他のプラグインとの互換性は無くなります。<ref group="注">実際には、相互のコマンドを呼び分けるために特定のプレフィクスがコマンドの前に付与されます。しかしそれでも、単にgiveコマンドを呼んだだけではどのコマンドが呼ばれるかわからないことには変わりありません。</ref>
onCommand()は、常にboolean型の値<ref group="注">trueかfalseのどちらか</ref>を戻り値として返さねばなりません。 trueを返した場合は、情報表示のためのイベントは発生しません。 falseを返した場合は、プラグインファイルを"usage: property"に戻し、コマンドを実行したプレイヤーに、コマンドの利用方法を通知するメッセージを表示します。
onCommand()を利用する際は、4つのパラメータを登録しなければなりません。
- CommandSender sender - コマンドの発行元
- Command cmd - 実行されたコマンドの内容
- String commandLabel - 利用されたコマンドエイリアス
- String[] args - コマンドの引数を格納した配列(例:/hello abc defコマンドが入力された場合の内容は、args[0]がabc、args[1]がdefとなる)<ref group="注">半角空白 (U+0020)で分割されて、配列に格納される</ref>
コマンドの設定[編集]
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: tutorialplugin.basic permission-message: You don't have <permission>
- basic - コマンド名。
- description - コマンドの説明文。
- usage - コマンドの使い方。onCommand() でfalseを返したときに、コマンド実行ユーザに向けて表示されるメッセージの内容。あなたの作ろうとしているコマンドの使い方を、わかりやすく説明してください。
- permission - コマンドの実行権限。このコマンドの実行に必要なパーミッションノードを設定します。あなたのプラグイン名と、コマンド名を、ピリオド(.)でつなげたパーミッションノードを設定することを推奨します(例:myplugin.test)。
- permission-message - 上で設定したコマンド実行権限を持たないユーザがコマンドを実行した場合に、実行権限が無いことを同ユーザに知らせるメッセージ。
なお、usage の欄では <command>、permission-message の欄では <permission> というキーワードをそのまま指定できます。それぞれ、設定したコマンド名と、設定したパーミッションノードに置き換えられます。
詳しい書き方は、plugin.ymlの書き方の、コマンドのオプション設定の節 を参照してください。
- Note: ymlファイルには、インデントに2個以上の半角スペースを記述する必要があります。タブ文字は構文エラーとなるため利用できません。
コンソールコマンドとプレイヤーコマンド[編集]
察しの良い人は、CommandSender sender
パラメタに着目しているかもしれません。 CommandSender
クラスは、プラグイン開発者に2つの点で有用な、Bukkitが提供するインタフェースです。
CommandSenderインタフェースの実装: Player
と ConsoleCommandSender
(正確には Player
もインタフェース)です。
プラグインを作る際は、次の2点に注意しながら作る事が非常に良い方法です。
- プラグインが動作している事をサーバコンソールで確認しながら作る事
- ログインプレイヤー向けのコマンドが、実際にログインしているプレイヤーのみ実行できる事
プラグインは、senderがプレイヤーではない場合(例えばコンソールからプラグインのコマンドが投入された場合)であっても、サーバコンソールには明解でシンプルな動作結果を返します。(例えば天気を変えるコマンドの実行結果は、ゲーム内では無くサーバコンソール上のメッセージから確認すれば間違いありません)
記述例:
@Override public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { if (cmd.getName().equalsIgnoreCase("basic")) { // プレイヤーが /basic を実行すると、この部分の処理が実行されます... return true; } else if (cmd.getName().equalsIgnoreCase("basic2")) { // プレイヤーが /basic2 を実行すると、この部分の処理が実行されます... if (!(sender instanceof Player)) { sender.sendMessage("このコマンドはゲーム内から実行してください。"); } else { Player player = (Player) sender; // コマンドの実行処理... } 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 (プラグインのメインクラス):
@Override public void onEnable() { // plugin.yml に basic というコマンドを定義していないと、 //実行した時にNullPointerExceptionが発生します。注意してください。 getCommand("basic").setExecutor(new MyPluginCommandExecutor(this)); }
MyPluginCommandExecutor.java:
public class MyPluginCommandExecutor implements CommandExecutor { // メインクラスの参照です。処理の中でメインクラスのメソッドを利用しない場合は、省略して構いません。 private final MyPlugin instance; public MyPluginCommandExecutor(MyPlugin plugin) { this.plugin = plugin; } @Override public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { // コマンドの実行内容... } }
どのように、メインプラグインのインスタンスがMyPluginCommandExecutorのコンストラクタを実行するかに注目しましょう。
この節で紹介した方法により、メインのonCommand()メソッドが巨大で複雑になったとしても簡単に整理する事ができ、結果として、プラグインのメインクラスを複雑化させずに、処理分割する事ができます。
- Note: プラグインが複数のコマンドを持つ場合、個々のコマンドに対応するCommandExecutorをコーディングする必要があります。
- 訳者補記: 積極的に、大きな処理は小さく分割していきましょう。そのための仕組みをBukkitが用意しています。という事を教示しています。
堅牢なonCommandの記述[編集]
onCommand()メソッドを記述する際、パラメタの利用に関して、
決め付けて掛かってはいけない事がありますので念頭において下さい。
これらに留意した処理を記述する事で、堅牢なonCommand()をコーディングする事ができます。
senderの内部データ型をチェックする[編集]
senderの内部データがPlayer型であるとは限らない事を念頭において、チェックを行って下さい。
処理例:
@Override public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){ if (sender instanceof Player) { Player player = (Player) sender; // ここに、処理を実装する。 return true; } else { sender.sendMessage(ChatColor.RED + "ゲーム内から実行してください!"); return false; } return false; }
このように、if (sender instanceof Player)
で必ずPlayerであることをチェックしないと、プレイヤーでないコマンド実行者(例えばコンソール)がコマンドを実行したときに、Player player = (Player) sender
のところで処理が失敗してしまいます。
コマンドのパラメタ長をチェックする[編集]
senderインスタンスは、妥当なパラメタを持っているとは限りません。パラメタ長をチェックして下さい。
処理例:
public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){ if (args.length > 4) { sender.sendMessage(ChatColor.RED + "パラメタが多すぎます!"); return false; } if (args.length < 2) { sender.sendMessage(ChatColor.RED + "パラメタが足りません!"); return false; } }
プレイヤーがオンラインである事を確認する[編集]
特定のプレイヤーのPlayerインスタンスを利用したい場合、必ずそのプレイヤーがオンラインである必要があります。
オンラインであるかどうかをチェックして下さい。
Bukkit.getPlayer(プレイヤー名) または、Bukkit.getPlayerExact(プレイヤー名) で、オンラインのプレイヤーを取得できます。
(Bukkit.getPlayer はプレイヤー名と前方一致で、Bukkit.getPlayerExact は完全一致で、プレイヤーを取得します。)
もし、指定したプレイヤーがオフラインだった場合は、nullが返されます。
処理例:
@Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (args.length < 1) { sender.sendMessage("コマンドの後にプレイヤー名を指定してください!"); return false; } Player target = Bukkit.getPlayerExact(args[0]); if ( target == null ) { sender.sendMessage(ChatColor.RED + args[0] + "さんはオフラインです!"); return true; } else { sender.sendMessage(ChatColor.AQUA + args[0] + "さんはオンラインです!"); return true; } }
オンラインではないプレイヤーのインスタンスに操作を加えたい場合は、一般的にはOfflinePlayer
クラスを利用して行う事ができます。
プラグインのConfiguration/Settings[編集]
- 新しいConfigurationの使い方を参照して下さい
権限[編集]
- Note: この節は、日本語読者向けに独自の内容を記述しています。
Bukkitにおける権限の利用は、とっても簡単です!
権限がセットされているか、いない(Javaのnullと同等)かを調べるには以下のメソッドを利用します:
boolean isPermissionSet(String name)
また、権限を持っているかどうかを調べるには以下のメソッドを利用します:
boolean hasPermission(String name)
これを利用してプレイヤーが特定の権限を持っているかどうかを調べる処理は次のようになります:
if(player.hasPermission("some.pointless.permission")) { player.sendMessage("あなたは権限を持っています。"); }else{ player.sendMessage("あなたは権限を持っていません。"); }
2メソッドの違いは、権限がセットされていなくてもデフォルトで与えられていれば権限を持っているという扱いになるかどうかです。
hasPermissionでは デフォルトで与えられていればtrueが返ります。
PermissionsExなどで 権限はグループとして扱われることが多いですが、Bukkitには権限をグループとして扱う機能はありません。
グループとして扱う場合は、別途 上記のようなプラグインを使うか、自分で作る必要があります。
権限の設定[編集]
デフォルトで権限を付与させたり、OPにのみデフォルトで付与させたい場合は、
plugin.ymlを使うと簡単にできます。
下記は、plugin.ymlの最後に追加する形で設定する権限です:
permissions: doorman.*: description: Gives access to all doorman commands children: doorman.kick: true doorman.ban: true doorman.knock: true doorman.denied: false doorman.kick: description: Allows you to kick a user default: op doorman.ban: description: Allows you to ban a user default: op doorman.knock: description: Knocks on the door! default: true doorman.denied: description: Prevents this user from entering the door
まず、プラグインが利用する各権限を、permissionsノードの子ノードとして定義します。
それぞれの権限はオプションとして、概要(description)、デフォルト値、子ノードを持ちます。
デフォルト権限[編集]
プレイヤーが権限を持たない場合、 hasPermission はデフォルトではfalseを返します。
plugin.yml中のデフォルトのノードに対して、下記の4つのどれか1つを設定する事で、
この挙動を変更する事ができます:
- true - デフォルトで、権限を持つ(=hasPermissionがtrueを返す)。
- false - デフォルトで、権限を持たない(=hasPermissionがfalseを返す)。
- op - プレイヤーがOPであれば、権限を持つ(=hasPermissionがtrueを返す)。
- not op - プレイヤーがOPでなければ、権限を持つ(=hasPermissionがtrueを返す)。
子権限[編集]
権限を設定するときに* 権限を利用すると、その権限の子権限すべてを操作することができます。
これは、変更されたBukkit APIと、子権限の定義によって実現した機能であり、
高い柔軟性を提供しています。
下記はその実装例です:
permissions: doorman.*: description: Gives access to all doorman commands children: doorman.kick: true doorman.ban: true doorman.knock: true doorman.denied: false</source>doorman.*権限は、いくつかの子権限を含んでいます。 doorman.*権限がtrueである場合に、 その子権限はplugin.ymlに定義された値(trueかfalse)をデフォルト権限として機能します。 doorman.*権限がfalseである場合は、 その子権限のデフォルト権限は、全て反転(trueが定義値ならfalseになる)された状態で機能します。
ねっ、簡単でしょ?
独自の権限設定[編集]
独自に権限の仕組みを提供するプラグインを開発したい場合、 権限プラグインの開発方法を参考にして下さい。
スケジューリングタスクとバックグラウンドタスク[編集]
現在、Minecraftサーバはゲームロジックのほとんどがシングルスレッドで稼動しています。
このため、発生する個々の処理はごく小さいものにする必要があります。
プラグイン中に複雑なコードが存在して、それが適切に処理されないような場合は、
ゲームロジックに多大なラグや処理遅延を発生させる原因となります。
幸運なことに、Bukkitはプラグインに対してスケジューリングのためのコーディング方法を提供しています。
どこかのタイミングで一度だけRunnableタスクを実行したり、
小さなタスクに分けた定期的な繰り返しであったり、
新規に独立したスレッドを派生させたり・・・
といった複数の方法で、長大なタスクをゲームロジックと並行で処理させる事ができます。
詳しくは、スケジューラのプログラミング中の、同期タスクと非同期タスクのスケジューリング方法に関する解説を参考にして下さい。
ブロックの操作[編集]
ブロックを生成する簡単な方法は、既存のブロックを取得して変更する事です。
例えば、あなたの5ブロック上方にあるブロックを変更したい場合、
まずはそのブロックを取得した上で、変更を加えることになります。
PlayerMoveイベントの処理中でこれを行う例を示します:
@EventHandler public void onPlayerMove(PlayerMoveEvent evt) { // プレイヤーの位置を取得します。 Location loc = event.getPlayer().getLocation(); // 位置のY座標を+5します。位置情報を変更しているだけで、実際にプレイヤーの位置が移動するわけではないことに注意してください。 loc.setY(loc.getY() + 5); // 指定位置のブロックを取得します。 Block b = loc.getBlock(); // ブロックの種類に石(STONE)を設定します。 b.setType(Material.STONE); }
このコードの処理は、プレイヤーが移動する(=PlayerMoveイベントが発生する)度に、
プレイヤーの5ブロック上方のブロックがStoneに変化する動作として見えるでしょう。
ブロック取得までの流れは・・・
- 取得したプレイヤーの位置から、ワールドを取得する
- 位置のY座標を+5する
loc.getBlockAt(loc);
で、指定位置に存在するブロックを得る
となります。
位置(loc)中のブロックインスタンス(b)に対して、
Materialを変えたり、ブロックデータ自体を変更する事もできます。
詳しくは、JavaDocを参照してください。
建物の生成や、一定のアルゴリズムに従ったブロック生成処理などを行う一例を示します。
例えば、固体ブロックによる立方体を生成する処理は、
3階層にネストしたforループ処理によって記述できます。
public void generateCube(Location loc, int length){ // 与えられたLocationから、立方体の端の座標を取得します。 // getN()メソッドを使うと intへキャストする必要がありますが、 // getBlockN()メソッドを使えばそのままintで座標を取得できます。 int x1 = loc.getBlockX(); int y1 = loc.getBlockY(); int z1 = loc.getBlockZ(); // 一辺の長さを足すことで、立方体の反対側の座標を計算します。 int x2 = x1 + length; int y2 = y1 + length; int z2 = z1 + length; World world = loc.getWorld(); // x座標方向のループ for (int xPoint = x1; xPoint <= x2; xPoint++) { // y座標方向のループ for (int yPoint = y1; yPoint <= y2; yPoint++) { // z座標方向のループ for (int zPoint = z1; zPoint <= z2; zPoint++) { // ループで処理する座標のブロックを取得します。 Block currentBlock = world.getBlockAt(xPoint, yPoint, zPoint); // ダイアモンドブロックに設定します! currentBlock.setType(Material.DIAMOND_BLOCK); } } } }
このメソッドは、辺の長さと開始点の指定を受けて、任意のサイズと位置を持つ直方体を生成する処理です。
ブロックの削除処理の場合も、このロジックを真似て同様のアルゴリズムで実装する事ができます。
ただし、セットするブロックの種類はMaterial.AIRになりますね。
プレイヤーインベントリの操作[編集]
この節では、プレイヤーのインベントリ操作を特に扱っていますが、
チェストのインベントリの操作にも適用できますので、必要なら応用して下さい(゚∀゚)
インベントリ操作のシンプルな例:
public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); // Joinしたプレイヤー PlayerInventory inventory = player.getInventory(); // プレイヤーのインベントリ ItemStack diamondstack = new ItemStack(Material.DIAMOND, 64); // 山積みのダイヤモンド! if (inventory.contains(diamondstack)) { inventory.addItem(diamondstack); // プレイヤーインベントリに山積みのダイヤモンドを加える player.sendMessage(ChatColor.GOLD + "よく来たな!もっとダイヤモンドをくれてやろう、このとんでもない成金め!!"); } }
さて、onPlayerJoin()メソッドの先頭で
player, inventory、diamondstack変数を用意して下さい。
inventoryはプレイヤーのインベントリで、diamondstackは(アイテムとしての)64個のダイヤモンドです。
次に、プレイヤーのインベントリがダイヤモンドを含んでいるかをチェックします。
プレイヤーがダイヤモンドをインベントリに所持している場合、
inventory.addItem(diamondstack)
処理にて
同プレイヤーのインベントリに別のスタックを与え、黄金色のメッセージを送信します。
インベントリ操作は全然難しくありません。
ダイヤモンドのスタックを削除したい場合でも単純に、
inventory.addItem(diamondstack)
をinventory.remove(diamondstack)
に置き換えるだけです。
(メッセージにも少しイタズラしておきましょう)
アイテムの操作[編集]
アイテムはスタックという単位で操作します。
スタックデータに対する全ての操作は、ItemStackクラスの仕様を参照して下さい。
エンチャント[編集]
アイテムにエンチャントを付与するには、ItemStackクラスの addEnchantment(Enchantment enchant, int level) メソッドを使用します。
addEnchantment()メソッドでは、元々エンチャントが出来ないアイテムに対して、独自にエンチャントを付与する処理は書けません。
もし、通常ありえないエンチャントを設定したい場合は、addEnchantment()メソッドの代わりにaddUnsafeEnchantment()メソッドを使ってください。
Sharpness 1 エンチャントを石の剣に付与する例を示します。
// 新しい石の剣を生成します。 ItemStack myItem = new ItemStack(Material.STONE_SWORD); // エンチャントを付与します。 myItem.addEnchantment(Enchantment.DAMAGE_ALL, 1);
次に、火属性 100 を、木の棒に付与する例を示します。
// 新しい木の棒を生成します。 ItemStack myItem = new ItemStack(Material.STICK); // 木の棒にFireAspectレベル100を付与します。 // ただしFireAspectレベル100は通常存在しないので、addEnchantment を使うと失敗します。 // ありえないエンチャントを設定したい場合は、addUnsafeEnchantment を使ってください。 myItem.addUnsafeEnchantment(Enchantment.FIRE_ASPECT, 100);
ItemMeta[編集]
アイテムの表示名を変更するには、次のようにします。
String myDisplayName = "すごい剣"; ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD); //アイテムを生成します。 ItemMeta im = myItem.getItemMeta(); //ItemStackから、ItemMetaを取得します。 im.setDisplayName(myDisplayName); //アイテム表示名を設定します。 myItem.setItemMeta(im); //元のItemStackに、変更したItemMetaを設定します。
次に、loreを設定してみましょう。loreは、ゲーム内でアイテムにカーソルを合わせたときに表示される説明文のことです。
List<String> lores = new ArrayList<String>(); lores.add("loreのテストです。"); lores.add("これは2行目です。"); ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD); //アイテムを生成します。 ItemMeta im = myItem.getItemMeta(); //ItemStackから、ItemMetaを取得します。 im.setLore(lores); //loreを設定します。 myItem.setItemMeta(im); //元のItemStackに、変更したItemMetaを設定します。
Metadata[編集]
Bukkit では、プラグインの開発をより簡単にするため、Playerクラス、Entityクラス、Worldクラスに紐づく追加データをMetadataという形式で管理できるようになっています。 今までは、それぞれのプラグイン内で、Player、Entity、World などをキーとしたHashMap型の変数内で管理していたと思いますが、それをMetadataで置き換えすることができます。 Metadataのデータは、全てMetadatableのメンバーで構成されます(javadocも参照してください)。 動作は非常に単純です。 Metadatableクラスは、それぞれのインスタンスが自分のMetadataのHashMapを持っています。 つまり例えば、経済プラグインを作る場合、HashMap<Player, Double> のようなデータをプラグイン内で持つ必要はありません。 プレイヤーにMetadataを直接設定すればよいのです!
Metadataを使うメリット[編集]
- Metadataは全てBukkit側で管理されます。プラグイン側で管理する必要がありません。
- プラグイン間で共通にアクセスできるので、データの共有に使用できます。
Metadataを使うデメリット[編集]
- データの取得・設定を行うときに、ひと手間が必要になります。
- Bukkitが停止すると、全てのMetadataが消えます。
Metadataの使い方[編集]
/** * PlayerにMetadataを設定するサンプルメソッドです。 * @param player 対象プレイヤー * @param key Metadataのキー名 * @param value Metadataの値 * @param plugin プラグインクラス */ public void setMetadata(Player player, String key, Object value, Plugin plugin) { player.setMetadata(key, new FixedMetadataValue(plugin, value)); } /** * PlayerからMetadataを取得するサンプルメソッドです。 * @param player 対象プレイヤー * @param key Metadataのキー名 * @param plugin プラグインクラス * @return Metadataの値 */ public Object getMetadata(Player player, String key, Plugin plugin) { List<MetadataValue> values = player.getMetadata(key); for (MetadataValue value : values) { if (value.getOwningPlugin().getDescription().getName() .equals(plugin.getDescription().getName())) { return value.value(); } } return null; }
- Note: もしあなたが、必ず boolean、int、String としてMetadataの値を取得したいのであれば、asBoolean()、asInt()、asString() メソッドを使うことで、キャストせずに直接取得が可能です。
データベース[編集]
時にフラットファイルは、データベースとして利用するには不足があるでしょう。 Linux/Mac/Windowsのマシンにおいて、もっとも汎用的なデータベースはSQL(Structured Query Language)です。
ソフトウェアとしてのSQLは、個々のデータを格納するセルと、それらを識別するための列およびヘッダから成るデータベースを生成します。 会計用ソフトウェア(たとえばエクセルのような)の表が、より便利になったモノを思い浮かべてください。 SQLは、この表形態のデータに、データ同士の整合性の確保を目的とした独自のルール付けが行え、 かつ、フラットなファイルよりも高速で高機能なデータ利用のための方法も提供します。
Bukkitは、SQLの標準的な機能を利用するための仕組みを提供しています。 残念なことに、SQLには複数の種類が存在し、それぞれの設定方法と利用方法が少しずつ異なっています。 各自のニーズに応じて、好きな種類のSQLを選んで利用してください。(プラグインについても、利用するデータベースの種類に応じて複数の接続・利用方法が利用できます)
SQLite[編集]
Alta189は、ダウンロード・インポートによって導入や移動が容易に可能なSQLiteを、Bukkitのプラグインで利用するためのライブラリを使う方法のチュートリアル、fantastic SQLite tutorialを書きました。
SQLの構文については、次に紹介する動画を一度見てみる事をお勧めします。簡単な内容なので短時間で済みます。SQL Tutorials @W3Schools
SQLiteは、その稼動のためにサーバを構築する必要がないため、シンプルさにおいて非常に優れています。 新規にデータベースとその中のテーブルを作成する手順は少量です。また、データのバックアップも、1個のデータベースファイルをバックアップするだけです。ただし、データ間の整合性の確保や柔軟性、データ件数が100万を超えるような膨大なデータの扱いにおいては、多少弱い面もあります。
とはいえSQLiteは、SQLデータベースを利用する新規プラグインの開発作業を、迅速で簡単にしてくれるメリットがあります。また、サーバ向けの大規模なSQLの予習のためにも有用です。
MySQL[編集]
もうひとつ、人気があるSQLにMySQLというものがあります。 SQLiteよりもサーバ寄りの用途をもった種類のSQLで、多くの有名な企業の業務システムや、一日に100万アクセスを捌くようなウェブサイトが、この機能に依存しています。ただし、チューニング可能な範囲や機能が多岐に渡るため、セキュリティ上のリスクが、データベース管理者のMySQLへの習熟度に大きく左右されます。
プラグインからMySQLを利用する処理のコーディング自体は、小規模なSQLiteからMByte単位のOracleのデータベースに対するものと、大差ありません。しかし、サーバ用途のデータベースの管理作業は、どんどん増えていくものです。使い続ける内に、データベース利用者のアカウントや権限の設定作業を行う事になっていくでしょう。また、データのバックアップやバックアップからの復旧(Rollbackと呼ぶ)作業のために、SQL文のスクリプトを書く事にもなるはずです。
プラグインの配布[編集]
プラグインを実装し終わったら、Mavenを使ってビルドして、リリース用のJarファイルを作成してみましょう。
プロジェクトを右クリックして、実行>Maven install と選択してください。
Eclipseのコンソールに、ビルド情報が流れます。
ビルドがうまくいけば、コンソールに「BUILD SUCCESS」と表示されてビルドが終了します。
もしビルドが失敗したなら、エラー情報を元に、エラーの解決を試みてください。
このチュートリアルで紹介したJDKが同梱のPleiadesを利用していない場合、MavenがJDKを見つけられずにエラーになっていることが多いです。
その場合は、Eclipseの設定を開いて、正しいJDKを選択しなおしてください。
Eclipseの設定は、「ウィンドウ」メニュー>設定 を選択し、開いたダイアログで、Java>インストール済みのJRE を選択します。
ここで、正しいバージョンのJDKが選択されていることを確認してください。
このチュートリアルで紹介したJDKが同梱のPleiadesを利用している場合で、
[ERROR] Unable to locate the Javac Compiler in:
[ERROR] C:\pleiades\java\7\..\lib\tools.jar
のようなエラーが出ることがあります。これは上記のメッセージの通り、最初から同梱されているJDKにtools.jarが含まれているのに参照設定されていないからです。
これを解決するには、次のようにします。
- Pleiadesのメニューから、ウィンドウ > 設定 と選び、設定ダイアログを表示します。
- 設定ダイアログの左側で、java > インストール済みのJRE と選びます。
- 使用しているJRE(先ほどのエラーメッセージに含まれるファイルパスと、ロケーションが、一致するもの)を選択し、「編集...」を押します。
- JREの編集ダイアログで、「外部Jar追加...」を押し、tools.jarを探して選択します。例えばエラーメッセージが「C:\pleiades\java\7\..\lib\tools.jar」なら、「C:\pleiades\java\7\lib\tools.jar」に見つかるはずです。
- 「OK」を押して、設定ダイアログを閉じます。
- 再度、「mvn install」を実行して、今度は正常にビルドができることを確認してください。
ビルドがうまくいった場合、プロジェクトのフォルダの中に target フォルダが作成されており、そのフォルダの中にビルドされたJarファイルがあります。
プラグインのコードとplugin.ymlに不備が無ければ、エクスポートしたJarファイルはすぐにBukkitプラグインとして動作します。Jarファイルを、Bukkitサーバの"plugins"フォルダの中に配置し、Bukkitサーバを起動し、プラグインの動作確認をしてみましょう。なお、ローカルマシン上で起動するBukkitサーバへは、Minecraftクライアントのマルチプレイヤーサーバの接続先IPアドレスに"localhost"を指定して接続する事でログインできます。
もしプラグインが上手く動かず、それがどうしても自分で解決できない場合は、当Wikiやその原文をもう一度よく読み、それでも駄目ならBukkitプラグイン開発者フォーラム(英語圏), bukkitdev Bukkit公式サイトの開発者向けIRCチャンネル(英語圏), マインクラフト非公式日本ユーザフォーラムの関連トピック(日本語圏), 当WikiのMOD制作関連IRCチャンネル(日本語圏)をたずねてみて下さい。有用なプラグインが作れたら、dev.bukkitに登録し、プラグインを広く公開する事を検討してみて下さい。他のBukkitユーザ・開発者に貢献する事ができます。
ヒントとノウハウ[編集]
CraftBukkit APIは、すばらしい機能をたくさん提供しています。 下記に、面白い効果を実現するコードを示します。
プレイヤーに着火する[編集]
下記は、指定されたプレイヤーに着火するサンプルです。例えば /ignite Notch と実行すると、Notchが燃えます!
@Override public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { // equals() の代わりに equalsIgnoreCase() を使うと、大文字小文字に関係なく、 // 文字列の一致を確認できます。"ignite"でも"IgNiTe"でも指定可能になります。 if (cmd.getName().equalsIgnoreCase("ignite")) { // コマンドのパラメータに、燃やすプレイヤーが指定されているかどうかを // 確認します。 if (args.length != 1) { // onCommandでfalseを戻すと、plugin.ymlのusageに設定したメッセージを // コマンド実行者の画面に表示します。 return false; } // 燃やすプレイヤーを取得します。 Player target = Bukkit.getPlayerExact(args[0]); // 対象プレイヤーが、オンラインかどうかを確認します。 if (target == null) { sender.sendMessage(args[0] + " というプレイヤーが見つかりません!"); return true; } // 対象プレイヤーを、1000tick(=50秒) の間、燃えるようにします。 target.setFireTicks(1000); return true; } return false; }
プレイヤーを殺す[編集]
同じ要領で、プレイヤーを殺害するコマンドの例を紹介します。 onCommand()メソッドに記述します:
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args){ if(cmd.getName().equalsIgnoreCase("KillPlayer")){ // コマンドのパラメータに、殺害するプレイヤーが指定されているかどうかを // 確認します。 if (args.length != 1) { // onCommandでfalseを戻すと、plugin.ymlのusageに設定したメッセージを // コマンド実行者の画面に表示します。 return false; } Player target = Bukkit.getPlayerExact(args[0]); // 対象プレイヤーがオンラインかどうかを確認します。 if (target == null) { sender.sendMessage(args[0] + " というプレイヤーは見つかりません!"); return true; } // 対象に1000ダメージを与えます。 target.damage(1000); return true; } return false; }
上記の拡張版として、プレイヤーを爆死させる処理を下記に示します:
@Override public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { if(cmd.getName().equalsIgnoreCase("KillPlayer")){ Player target = Bukkit.getPlayerExact(args[0]); // 対象プレイヤーがオンラインかどうかを確認します。 if (target == null) { sender.sendMessage(args[0] + " というプレイヤーは見つかりません!"); return true; } float explosionPower = 4F; // 爆発の大きさです。1Fでガストの火球、3Fでクリーパーの爆発、4FでTNTの爆発 に相当します。 // 爆発を起こしつつ、対象に1000ダメージを与えます。 target.getWorld().createExplosion(target.getLocation(), explosionPower); target.damage(1000); return true; } return false; }
爆発を起こす[編集]
このコードは、TNTの爆発と同様の音とヴィジュアルを再現します。 これは、TNTの爆発効果は無効化しつつ、音とヴィジュアル効果を発生させる処理に転用できます。
@EventHandler public void onExplosionPrime(ExplosionPrimeEvent event) { Entity entity = event.getEntity(); // このイベントは、点火されたTNTにより発生したのかどうかを確認します。 // (つまり、TNTの爆発はこれで無効化されますが、クリーパーの爆発は無効化されません) if (entity instanceof TNTPrimed) { event.setCancelled(true); // イベントをキャンセルして、爆発を無かったことにする entity.getWorld().createExplosion(entity.getLocation(), 0); // 偽物の爆発を発生させる } }
プレイヤーを非表示にする[編集]
これは指定したプレイヤーから自分を非表示にするサンプルです。 指定したプレイヤー以外のプレイヤーからは、自分が見えたままになっています。
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { if(cmd.getName().equalsIgnoreCase("HideMe") && args.length == 1) { // コマンド実行者がプレイヤーかどうかを確認します。 if (!(sender instanceof Player)) { sender.sendMessage("このコマンドはゲーム内から実行してください!"); return true; } // sender instanceof Player の検査が終わっているので、Playerクラスへ安全にキャストできます。 Player s = (Player) sender; // コマンドのパラメータに、対象のプレイヤーが指定されているかどうかを確認します。 if (args.length != 1) { // onCommandでfalseを戻すと、plugin.ymlのusageに設定したメッセージを // コマンド実行者の画面に表示します。 return false; } // 指定されたプレイヤーを取得します。 // 指定されたプレイヤーがサーバーに接続していない場合、targetはnullになります。 Player target = Bukkit.getPlayerExact(args[0]); if (target == null) { sender.sendMessage("Player " + args[0] + " というプレイヤーは見つかりません!"); return true; } // プレイヤー "s" を、指定したプレイヤー "target" から、非表示に設定します。 target.hidePlayer(s); return true; } return false; }
クリックした場所に雷を落とす[編集]
下記のサンプルは、釣竿を持ってクリックしたときに、クリックした場所を取得して、その場所に雷を落とします。
@EventHandler public void onPlayerInteractBlock(PlayerInteractEvent event) { Player player = event.getPlayer(); if (player.getItemInHand().getType() == Material.FISHING_ROD) { // プレイヤーが見ている場所に雷をおとします。 Block target = getTargetBlock(player); if (target != null) { target.getWorld().strikeLightning(target.getLocation()); } } } private Block getTargetBlock(Player player) { // 視線上のブロックを100ブロック先まで取得 BlockIterator it = new BlockIterator(player, 100); // 手前側から検証を行う。 // Blockが取得できた時点でreturnして終了する。 while ( it.hasNext() ) { Block block = it.next(); if ( block.getType() != Material.AIR ) { // ブロックが見つかった return block; } } // 最後までブロックがみつからなかった return null; }
プラグインのサンプル兼雛形[編集]
この内容について質問がある場合、遠慮なくAdamki11sかBukkitDevのIRCチャンネル(当Wikiの原文を掲載しているサイトのIRCチャンネルです)で聞いてください。
- Note: 当ページは訳文であるため、訳文自体に対する文責は原著者にありません。その点を考慮して必ず原文にも目を通してから質問して下さい。