提供: Minecraft Modding Wiki
移動先: 案内検索

警告: ログインしていません。編集を行うと、あなたの IP アドレスが公開されます。ログインまたはアカウントを作成すれば、あなたの編集はその利用者名とともに表示されるほか、その他の利点もあります。

この編集を取り消せます。 下記の差分を確認して、本当に取り消していいか検証してください。よろしければ変更を保存して取り消しを完了してください。
最新版 編集中の文章
3行目: 3行目:
  
 
== 始めに ==
 
== 始めに ==
 +
:'''Note''': 当ページの内容は、[[Bukkit用MODの作成方法]]の内容とは異なるアプローチによるBukkitプラグイン開発手法の解説です。
 
:'''重要''': 当ページには、原文の翻訳ではない、日本語読者向けの独自の記述を行っている箇所が少量あります。そのような内容を含む節ではNoteにて通知しています。
 
:'''重要''': 当ページには、原文の翻訳ではない、日本語読者向けの独自の記述を行っている箇所が少量あります。そのような内容を含む節ではNoteにて通知しています。
 
:'''重要''': 当ページでは、技術的な解説やたとえ話の原意を理解し易くするために、一部意訳を行っています。
 
:'''重要''': 当ページでは、技術的な解説やたとえ話の原意を理解し易くするために、一部意訳を行っています。
 
:'''重要''': 当ページは訳文であるため、訳文自体に対する文責は原著者にありません。当ページの内容を受けて原著者へ何らかのアクションを行う場合、その点を考慮して必ず原文にも目を通して下さい。
 
:'''重要''': 当ページは訳文であるため、訳文自体に対する文責は原著者にありません。当ページの内容を受けて原著者へ何らかのアクションを行う場合、その点を考慮して必ず原文にも目を通して下さい。
  
このチュートリアルは、Bukkitプラグインを開発するための基本的なノウハウを網羅しております。
+
このチュートリアルは、[http://forums.bukkit.org/threads/plugin-development-a-huge-tutorial-status-under-development.15167/ こちらのスレッド]からの転載です。当ページの原著者は、'''Adamki11s'''氏ですが、多くの編集を受けて今に至ります。
  
Javaを習得する、IDEで開発環境を構築する、などの非常に基本的な部分から説明が始まっていますが、既にご理解頂いている場合は読み飛ばしていただいて構いません。
+
[http://forums.bukkit.org/members/adamki11s.42417/ Adamki11s Bukkit Profile Page]
 +
 
 +
このチュートリアルを読み終えた後は、'''Adamki11s''''氏の"Extras" からより深い情報を得る事が出来ます。[http://forums.bukkit.org/threads/dev-extras-v1-1-additional-useful-and-advanced-methods-for-plugin-developers-953.26207/ Extras library forums thread.]
  
 
== Javaの習得 ==
 
== Javaの習得 ==
16行目: 19行目:
 
このチュートリアルの読者には、Java言語プログラミングの基礎がわかる方を対象としています。<br/>
 
このチュートリアルの読者には、Java言語プログラミングの基礎がわかる方を対象としています。<br/>
 
Javaの基礎知識が少ない方は、下記の情報に触れてください。
 
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の公式サイト)  
32行目: 37行目:
  
 
あなたがJavaの初心者なら、当チュートリアルが解説に利用しているEclipseを利用する事をお勧めします。<br/>
 
あなたがJavaの初心者なら、当チュートリアルが解説に利用しているEclipseを利用する事をお勧めします。<br/>
Eclipseのお勧めのバージョンの配布元は、[http://mergedoc.sourceforge.jp/ 日本語 Eclipse / Pleiades All in One 日本語ディストリビューション]です。<br/>
+
Eclipseのお勧めのバージョンの配布元は、[http://mergedoc.sourceforge.jp/pleiades_distros3.7.html 日本語 Eclipse / Pleiades All in One 日本語ディストリビューション]です。<br/>
 
これはMergedocProjectが配布する拡張されたパッケージであり、Eclipseの一次配布元が提供するパッケージではありません。
 
これはMergedocProjectが配布する拡張されたパッケージであり、Eclipseの一次配布元が提供するパッケージではありません。
  
Pleiadesを利用する場合、「開発対象用 JDK」と、ビルドツールMavenが実行できるプラグイン「m2e」が同梱されているものを選択してください。<br/>
+
どのIDEも気に入らなければ、BlueJも利用できます。<br/>
(Eclipseを既に利用している場合でも、m2eを後から追加インストールすることは可能です。)
+
プラグイン開発が初めてであるなら、とにかく一度Eclipseを使ってみて、<br/>
 +
良ければNetBeansを試してみると良いでしょう。
  
spigotなどのBukkitサーバーの実行環境を利用する場合、BuildTools.jar を利用してサーバー実行環境をビルドする必要がありますが、Mavenが利用できる場合は、Mavenが開発用のライブラリを自動でダウンロードするため、BuildTools.jar を使わなくても開発環境を構築することが可能です。<br/>
+
[http://www.okapiproject.com/java/java_tools/eclipse/index.html 優良なEclipseの解説]。お勧めのものより古いバージョンの解説ですが、ほぼ同様です。
ただし、当然ですが、デバッグ実行するための実行環境は必要になりますから、BuildTools.jar を利用して実行環境も準備しておいてください。このチュートリアルでは、BuildTools.jar を利用した実行環境の構築は割愛いたします。
 
<!--
 
もし、BuildTools.jar を利用した実行環境の構築について、きれいにまとめられている資料があれば、ここにリンクを置いてください。
 
-->
 
  
 
== Plugin用プロジェクトを始めるために ==
 
== Plugin用プロジェクトを始めるために ==
  
 
=== プロジェクトの作成 ===
 
=== プロジェクトの作成 ===
始めるために、新しいワークスペースを作成する必要があります。<br />
+
始める前にEclipseのワークスペースとファイルの設定を行う必要があります。<br />
Eclipseを起動し、ファイル>新規>プロジェクト... と選択して下さい。<br />
+
Eclipseを起動し、ファイル>新規>Java プロジェクトと選択して下さい。<br />
  
[[Image:create_project.png]]
+
[[Image:NewJavaProject.png]]
  
新規プロジェクトのダイアログが開きます。Mavenフォルダを開いて、Mavenプロジェクト を選択し、次へ を押してください。
+
プロジェクト名を入力し、新規プロジェクトウィザード画面の指示に従って、プロジェクトを作成してください。
  
[[Image:create_project1.png]]
+
=== Bukkit APIの参照 ===
 +
開発を始める前にbukkit APIライブラリをプロジェクトに追加する必要があります。
 +
使用したい他のAPIも同じように追加することが可能です。
  
次のダイアログで、「シンプルなプロジェクトの作成」にチェックを入れて、次へ を押してください。
+
最新のBukkit APIはここからダウンロードが可能です。 {{BDownload}}
  
[[Image:create_project2.png]]
+
画面の左手にあるパッケージエクスプローラの(先ほど名前を付けた)プロジェクトを右クリックし、プロパティーを選択します。<br />
 +
開いた画面のJavaビルド・パスを選択し、ライブラリータブの中から、外部 Jar 追加ボタンを押して、ダウンロードしたBukkit APIを指定します。<br />
  
次のダイアログで、作成するプラグインの「グループID」と「アーティファクトID」を入力します。<br/>
+
[[Image:BuildPathPic.png|800px]]
(アーティファクトIDとは、これから作ろうとするプラグインの名前と同義と思っていただいて構いません。)
 
  
グループIDは下記の方法で命名して下さい:
+
=== BukkitのJavadocの利用方法 ===
 +
 
 +
Eclipseを用いたJavaプログラミングの経験がある方なら、<br/>
 +
Eclipseのエディタ上でクラスやメソッドにマウスカーソルを重ねた時に、<br/>
 +
クラスやメソッドに関するドキュメントがポップアップで表示される事をご存知でしょう。<br/>
 +
これは、[http://download.oracle.com/javase/6/docs/api/ Oracleのウェブサイト]から得る事ができるJavadocの内容が表示されています。<br/>
 +
 
 +
Bukkitもまた、BukkitのAPIが提供するクラスやメソッドの各々のコードに[http://jd.bukkit.org/apidocs/ 同種のドキュメント]を含んでおり、ポップアップで表示させる事ができます。<br/>
 +
Eclipse上で、Bukkitのクラスやメソッドにマウスを重ねたタイミングでポップアップを表示できるようにするためには、下記の手順を行います。
 +
 
 +
# プロジェクトエクスプローラ上で、Bukkitのjarファイルを右クリックして、メニューを開く。
 +
# "プロパティ"を選択し、表示されるポップアップ左側の"Javadoc ロケーション"項目を選択する。
 +
# "Javadoc URL"の下部にあるテキストボックスに、"http://jd.bukkit.org/apidocs/" (ダブルクォートは除く)を貼り付ける。
 +
# "検証"ボタンを押下し、URLがJavadocとして正しく識別される事をチェックしてから、OKボタンを押す。
 +
 
 +
:次ような画面になります:<br/>
 +
:[[Image:Bukkitjavadocs.png]]
 +
 
 +
=== Plugin開発の開始 ===
 +
Javaのクラスを格納するための'パッケージ'を作成する必要があります。
 +
既存の"src"フォルダを右クリックし、''新規 &gt; パッケージ''を選択します:
 +
<blockquote>
 +
[[Image:MakePackage.png]]
 +
</blockquote>
 +
パッケージ名は下記の方法で命名して下さい:
 
<blockquote>
 
<blockquote>
 
* ドメインを持っているなら、そのドメインの逆引き名にしましょう。
 
* ドメインを持っているなら、そのドメインの逆引き名にしましょう。
** ドメイン名が'''i-am-a-bukkit-developer.com'''の場合、パッケージ名は'''com.i_am_a_bukkit_developer'''となります。
+
** ドメイン名が'''i-am-a-bukkit-developer.com'''の場合、パッケージ名は'''com.i_am_a_bukkit_developer'''となります。[http://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html source]
 
* ドメイン名を持っていない場合、下記の選択肢があります。お勧め順です。
 
* ドメイン名を持っていない場合、下記の選択肢があります。お勧め順です。
 
** '''Option 1''' githubやsourceforgeのようなソース管理サイトにアカウント登録します。
 
** '''Option 1''' githubやsourceforgeのようなソース管理サイトにアカウント登録します。
80行目: 108行目:
 
* net.bukkit
 
* net.bukkit
 
* com.bukkit
 
* com.bukkit
* net.minecraft
 
 
</blockquote>
 
</blockquote>
  
ベースとなるグループIDを決めたら、次にプラグイン名を決めましょう。ここでは例として、TutorialPlugin と名付けます。
+
ベースとなるパッケージ名(例: '''com.github.<username>''')を決めたら、末尾にプラグイン名称(例: '''TestPlugin''')を追加したものを、パッケージ名称としましょう。つまり、'''com.github.<username>.TestPlugin'''になります。
 
 
ダイアログには次のように入力し、完了 を押してください。
 
 
 
[[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>
 
 
 
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/>
+
プラグイン開発のためのプロジェクトに、クラスファイルを追加する準備ができました。メインのクラス名は、プラグインの名称と同一にすると良いでしょう(つまりこの例では'''TestPlugin.java'''が良いでしょう)。パッケージを右クリックし、&nbsp; ''新規 &gt; クラス''を選択して追加します。
plugin.yml ファイルは、プラグインとしてBukkitに読み込みされるときに、プラグインの設定を記述しておくファイルです。<br/>
 
必須のファイルですから、必ず作成してください。
 
  
プロジェクトの ''src/main/resources'' を右クリックして、新規>ファイル を選択してください。
+
以上で、プロジェクトとメインファイルの準備ができました。次に、bukkitが'''[[Plugin_YAML|plugin.yml]]'''ファイルを参照できるようにします。このファイルは、Javaプログラムがプラグインとして動作するための必須情報を記述するファイルであり、これ無しではプラグインは動作しません。'''ソースフォルダ以外'''(この例では"src"以外)のプロジェクトフォルダを右クリックし、''新規 &gt; ファイル''を選択し、ファイル名を"'''plugin.yml'''"としてOKを押下します。Eclipseは、作成した空の'''plugin.yml'''を、標準テキストエディタで開きます。
  
[[Image:Create_pluginyml1.png]]
+
''(Hint:&nbsp;ワークスペースのファイル構成を維持したければ、テキストエディタを閉じて'''plugin.yml'''ファイルをワークスペースへドラッグ・ドロップして移動させてください。当ファイルに必須の内容は、メインクラスへの参照と、プラグインのコマンドの情報です。最もシンプルな'''plugin.yml'''は、次のような内容になります&nbsp;:
 +
<blockquote><code><pre>name: &lt;PluginName&gt;
 +
main: &lt;packagename&gt;.&lt;MainClass&gt;
 +
version: &lt;Version Number&gt;</pre></code></blockquote>
  
開いたダイアログの ファイル名 の欄で、「plugin.yml」と入力し、完了を押してください。
+
:'''Note''': メインクラスへの参照として記入されたパッケージ名が、'''&lt;pluginname&gt;.&lt;pluginname&gt;'''のように、パッケージ名がプラグイン名を含んでいる事がありますが、間違いではありません。
 
+
:'''Note''': クラスの名称には、大文字小文字の区別があることに留意してください。
[[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()メソッド ==
 
== onEnable()メソッドとonDisable()メソッド ==
このメソッドは、プラグインが有効/無効になったときに、Bukkitから呼び出しされます。<br />
+
このファンクションは、プラグインが有効/無効になったときに呼ばれます。<br />
 
デフォルトでは、プラグインは自動的に読み込まれたときに、イベントを登録やデバッグ出力を行うことが出来ます。<br />
 
デフォルトでは、プラグインは自動的に読み込まれたときに、イベントを登録やデバッグ出力を行うことが出来ます。<br />
onEnable()は、Bukkitが起動するときに、プラグインが有効化されたときに呼び出されます。onDisable()は、Bukkitが停止するときに、プラグインが無効化されたときに呼び出されます。<br />
+
onEnable()は、プラグインが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;
+
public void onEnable(){
 
   
 
   
import org.bukkit.plugin.java.JavaPlugin;
+
}
 
public class TutorialPlugin extends JavaPlugin {
 
 
   
 
   
    @Override
+
public void onDisable(){  
    public void onEnable() {
 
        // TODO ここに、プラグインが有効化された時の処理を実装してください。
 
    }
 
 
   
 
   
    @Override
 
    public void onDisable() {
 
        // TODO ここに、プラグインが無効化された時の処理を実装してください。
 
    }
 
 
}
 
}
 
</source></blockquote>
 
</source></blockquote>
  
  
現時点では、このメソッドは何も行いません。
+
現時点では、このメソッドは何も行いません。また、エラーが発生する事にも気付くでしょう。このエラーは、メインクラスが、プラグインの機能を継承(extends)する必要がある事を示しています。当クラスの定義文を、下記のように変更しましょう。
 +
 
 +
これを・・・
 +
<blockquote><source lang="java">Class <classname> {}</source></blockquote>
  
=== Loggerを利用したメッセージ出力 ===
+
このように変更する。
 +
<blockquote><source lang="java">Class <classname> extends JavaPlugin {}</source></blockquote>
 +
 
 +
すると、前述の追加コードが赤の下線でハイライト表示され、何かが間違っていることを通知してくるはずです。このハイライト部にマウスを重ねると、下記のようなポップアップが表示されるので、「'JavaPlugin'をインポートします(org.bukkit.plugin.java)」を選択します。
 +
 
 +
<blockquote>[[ファイル:to_need_import_package.png]]</blockquote>
  
始めに、プラグインが実際に動作するかどうかを確認するため、シンプルなメッセージをサーバコンソールに表示させてみましょう。<br/>
+
もしくは、
ログを出力するには、''getLogger()''メソッドを実行してLoggerを取得し、そのinfoメソッドを呼び出します。
+
<blockquote><source lang="java">import org.bukkit.plugin.java.JavaPlugin;</source></blockquote>
 +
をコード上部の定義部に記述する事でも同様の事が行えます。
  
<blockquote><source lang="java">
+
=== Loggerを利用したメッセージ出力 ===
getLogger().info("onEnableメソッドが呼び出されたよ!!");
 
</source></blockquote>
 
  
onDisable()メソッドについても同等の記述を行います。<br/>
+
始めに、プラグインが実際に動作するかどうかを確認するため、シンプルなメッセージをサーバコンソールに表示させてみましょう。この処理として、ログ出力機能(Logger)をメインクラスに定義して初期化します。
メインクラスに、次のように実装してみてください。
+
<blockquote><source lang="java">Logger log = this.getLogger();</source></blockquote>
 +
その後、onEnable()メソッドに、プラグインが動作している事を通知するメッセージを出力する処理を記述します。
 +
<blockquote><source lang="java">log.info("Your plugin has been enabled.");</source></blockquote>
  
 +
onDisable()メソッドについても同等の記述を行います。ここまでのコーディングによって、メインクラスは下記の様な内容になっているはずです。
 
<blockquote><source lang="java">
 
<blockquote><source lang="java">
package my.test.plugin.tutorialplugin;
+
package me.<yourname>.<pluginname>;
 
   
 
   
 +
import java.util.logging.Logger;
 
import org.bukkit.plugin.java.JavaPlugin;
 
import org.bukkit.plugin.java.JavaPlugin;
 
   
 
   
public class TutorialPlugin extends JavaPlugin {
+
public class <classname> extends JavaPlugin {
 
   
 
   
    @Override
+
Logger log;
    public void onEnable() {
+
        getLogger().info("onEnableメソッドが呼び出されたよ!!");
+
public void onEnable(){
    }
+
log = this.getLogger();
 +
log.info("Your plugin has been enabled!");
 +
}
 
   
 
   
    @Override
+
public void onDisable(){
    public void onDisable() {
+
log.info("Your plugin has been disabled.");
        getLogger().info("onDisableメソッドが呼び出されたよ!!");
+
}
    }
 
 
}
 
}
 
</source></blockquote>
 
</source></blockquote>
 
ここまで作ったところで、[[#プラグインの配布|プラグインのJarファイルの発行]] を行ってプラグインを作成し、実際にCraftBukkitのpluginsフォルダへ導入して、コンソールに設定したログメッセージが表示されることを確認してみてください。
 
 
=== Reloadを制御する ===
 
 
サーバーが終了処理をするときや、開始処理をするときに限らず、
 
サーバーが /reload コマンドにより、プラグインがdisableされenableされる動作について理解しておくことも重要です。<br/>
 
サーバーが開始したときについてだけ初期化処理を考慮することは危険です。<br/>
 
なぜなら、プレイヤーは既にオンライン状態で接続していますし、ワールドデータやチャンクは既にロードされていますし、他にもいろいろ想定していない違いがあります。
 
 
* /reload が実行されると、プラグインとしては、onDisable() が実行されて、onEnable() が実行されます。
 
* staticで保持していないデータは、全て失われます。
 
* 上記に書いてあるように、プレイヤーやワールドやチャンクは既にロード済みの状態です。
 
  
 
== イベントとリスナ ==
 
== イベントとリスナ ==
365行目: 195行目:
 
さて、どのようにイベントを登録しいつ発生するかについて理解しました。
 
さて、どのようにイベントを登録しいつ発生するかについて理解しました。
 
しかし、コマンドを利用して何かを起こしたい場合はどのようなデータ型を利用すればよいのでしょうか?それには、onCommand()メソッドを利用します。
 
しかし、コマンドを利用して何かを起こしたい場合はどのようなデータ型を利用すればよいのでしょうか?それには、onCommand()メソッドを利用します。
このメソッドは、plugin.ymlに設定したプラグインのコマンドが実行されたときに呼び出されます。
+
このメソッドは、プレイヤーが文字"/"を入力する度に実行されます。
 +
具体的には、"/do something"とコマンドを実行した場合にonCommand()が呼び出されます。
 
今のところ、onCommand()はまだプログラムしていないので何も起こらないでしょう。
 
今のところ、onCommand()はまだプログラムしていないので何も起こらないでしょう。
  
373行目: 204行目:
 
具体的には、"give"コマンドは既にいくつかのプラグインで利用されています。
 
具体的には、"give"コマンドは既にいくつかのプラグインで利用されています。
 
もし独自に"give"コマンドを実装した場合は、
 
もし独自に"give"コマンドを実装した場合は、
"give"コマンドを実装している他のプラグインとの互換性は無くなります。<ref group="注">実際には、相互のコマンドを呼び分けるために特定のプレフィクスがコマンドの前に付与されます。しかしそれでも、単にgiveコマンドを呼んだだけではどのコマンドが呼ばれるかわからないことには変わりありません。</ref>
+
"give"コマンドを実装している他のプラグインとの互換性は無くなります。
  
onCommand()は、常にboolean型の値<ref group="注">trueかfalseのどちらか</ref>を戻り値として返さねばなりません。
+
onCommand()は、常にboolean型の値としてtrue,falseのどちらかを戻り値として返さねばなりません。
 
trueを返した場合は、情報表示のためのイベントは発生しません。
 
trueを返した場合は、情報表示のためのイベントは発生しません。
 
falseを返した場合は、プラグインファイルを"usage: property"に戻し、コマンドを実行したプレイヤーに、コマンドの利用方法を通知するメッセージを表示します。
 
falseを返した場合は、プラグインファイルを"usage: property"に戻し、コマンドを実行したプレイヤーに、コマンドの利用方法を通知するメッセージを表示します。
384行目: 215行目:
 
* Command cmd - 実行されたコマンドの内容
 
* Command cmd - 実行されたコマンドの内容
 
* String commandLabel - 利用されたコマンドエイリアス
 
* String commandLabel - 利用されたコマンドエイリアス
* String[] args - コマンドの引数を格納した配列(例:/hello abc defコマンドが入力された場合の内容は、args[0]がabc、args[1]がdefとなる)<ref group="注">半角空白 (U+0020)で分割されて、配列に格納される</ref>
+
* String[] args - コマンドの引数を格納した配列(例:/hello abc defコマンドが入力された場合の内容は、args[0]がabc、args[1]がdefとなる)
  
 
=== コマンドの設定 ===
 
=== コマンドの設定 ===
413行目: 244行目:
 
       description: This is a demo command.
 
       description: This is a demo command.
 
       usage: /<command> [player]
 
       usage: /<command> [player]
       permission: tutorialplugin.basic
+
       permission: <plugin name>.basic
 
       permission-message: You don't have <permission>
 
       permission-message: You don't have <permission>
 
</source>
 
</source>
* basic - コマンド名。
+
* basic - コマンド名
* description - コマンドの説明文。
+
* description - コマンドの説明文
* usage - コマンドの使い方。onCommand() でfalseを返したときに、コマンド実行ユーザに向けて表示されるメッセージの内容。あなたの作ろうとしているコマンドの使い方を、わかりやすく説明してください。
+
* usage - onCommand()がfalseを返した際に、コマンド実行ユーザに向けて表示されるメッセージの内容。コマンドの使い方を明解に書いてください。
* permission - コマンドの実行権限。このコマンドの実行に必要なパーミッションノードを設定します。あなたのプラグイン名と、コマンド名を、ピリオド(.)でつなげたパーミッションノードを設定することを推奨します(例:myplugin.test)。
+
* permission - 当コマンドの動作に必要なプラグインの設定を、コマンド実行ユーザに知らせるメッセージ。(主に、コマンド実行に必要なpermissionを書きます)
* permission-message - 上で設定したコマンド実行権限を持たないユーザがコマンドを実行した場合に、実行権限が無いことを同ユーザに知らせるメッセージ。
+
* permission-message - コマンド実行権限を持たないユーザがコマンドを実行した場合に、その旨を同ユーザに知らせるメッセージ。
 
</blockquote>
 
</blockquote>
  
なお、usage の欄では <command>、permission-message の欄では <permission> というキーワードをそのまま指定できます。それぞれ、設定したコマンド名と、設定したパーミッションノードに置き換えられます。<br>
+
:'''Note''': ymlファイルには、1個タブを2個のスペースで記述する必要があります。タブ文字は構文エラーとなるため利用できません。
詳しい書き方は、[[plugin.ymlの設定一覧#コマンドのオプション設定|plugin.ymlの書き方の、コマンドのオプション設定の節]] を参照してください。
 
 
 
:'''Note''': ymlファイルには、インデントに2個以上の半角スペースを記述する必要があります。タブ文字は構文エラーとなるため利用できません。
 
  
 
=== コンソールコマンドとプレイヤーコマンド ===
 
=== コンソールコマンドとプレイヤーコマンド ===
440行目: 268行目:
 
記述例:  
 
記述例:  
 
<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")) {  
+
Player player = null;
// プレイヤーが /basic を実行すると、この部分の処理が実行されます...
+
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;
 
return true;
 
} else if (cmd.getName().equalsIgnoreCase("basic2")) {
 
} else if (cmd.getName().equalsIgnoreCase("basic2")) {
                // プレイヤーが /basic2 を実行すると、この部分の処理が実行されます...
+
if (player == null) {
if (!(sender instanceof Player)) {
+
sender.sendMessage("this command can only be run by a player");
sender.sendMessage("このコマンドはゲーム内から実行してください。");
 
 
} else {
 
} else {
Player player = (Player) sender;
+
// do something else...
// コマンドの実行処理...
 
 
}
 
}
 
return true;
 
return true;
457行目: 287行目:
 
return false;
 
return false;
 
}
 
}
</source> </blockquote>
+
</source> </blockquote>  
  
 
この例では、'''basic''' コマンドはログインプレイヤーとサーバコンソールのどちらからでも実行できます。しかし、'''basic2''' コマンドは、ログインプレイヤーしか実行できません。
 
この例では、'''basic''' コマンドはログインプレイヤーとサーバコンソールのどちらからでも実行できます。しかし、'''basic2''' コマンドは、ログインプレイヤーしか実行できません。
480行目: 310行目:
 
とっても良い具体例:
 
とっても良い具体例:
  
MyPlugin.java (プラグインのメインクラス):
+
MyPlugin.java (the main plugin class):
 
<blockquote><source lang="java">
 
<blockquote><source lang="java">
 +
 +
private MyPluginCommandExecutor myExecutor;
 
@Override
 
@Override
 
public void onEnable() {
 
public void onEnable() {
// plugin.yml に basic というコマンドを定義していないと、
+
// ....
        //実行した時にNullPointerExceptionが発生します。注意してください。
+
getCommand("basic").setExecutor(new MyPluginCommandExecutor(this));
+
myExecutor = new MyPluginCommandExecutor(this);
}
+
getCommand("basic").setExecutor(myExecutor);
</source></blockquote>
+
 +
// ...
 +
}</source></blockquote>
  
 
MyPluginCommandExecutor.java:
 
MyPluginCommandExecutor.java:
 
<blockquote><source lang="java">
 
<blockquote><source lang="java">
 +
 
public class MyPluginCommandExecutor implements CommandExecutor {
 
public class MyPluginCommandExecutor implements CommandExecutor {
 
+
        // メインクラスの参照です。処理の中でメインクラスのメソッドを利用しない場合は、省略して構いません。
+
private MyPlugin plugin;
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 cmd, String label, String[] args) {
+
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
// コマンドの実行内容...
+
// ... implementation exactly as before ...
 
}
 
}
 
}
 
}
523行目: 357行目:
 
senderの内部データがPlayer型であるとは限らない事を念頭において、チェックを行って下さい。<br/>
 
senderの内部データがPlayer型であるとは限らない事を念頭において、チェックを行って下さい。<br/>
 
処理例:
 
処理例:
<blockquote><source lang="java">
+
<blockquote><source lang="java">public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){
@Override
+
if ((sender instanceof Player)) {
public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){
+
          // doSomething
    if (sender instanceof Player) {
+
        } else {
 +
          sender.sendMessage(ChatColor.RED + "You must be a player!");
 +
          return false;
 +
        }
 
         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>''' のところで処理が失敗してしまいます。
 
  
 
=== コマンドのパラメタ長をチェックする ===
 
=== コマンドのパラメタ長をチェックする ===
545行目: 373行目:
 
<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 + "Too many arguments!");
 
           return false;
 
           return false;
 
         }  
 
         }  
 
         if (args.length < 2) {
 
         if (args.length < 2) {
           sender.sendMessage(ChatColor.RED + "パラメタが足りません!");
+
           sender.sendMessage(ChatColor.RED + "Not enough arguments!");
 
           return false;
 
           return false;
 
         }
 
         }
555行目: 383行目:
  
 
=== プレイヤーがオンラインである事を確認する ===
 
=== プレイヤーがオンラインである事を確認する ===
特定のプレイヤーのPlayerインスタンスを利用したい場合、必ずそのプレイヤーがオンラインである必要があります。<br/>
+
特定のプレイヤーのPlayerインスタンスを利用したい場合、<br/>
 +
必ずそのプレイヤーがオンラインである必要があります。<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 cmd, String commandLabel, String[] args){
<blockquote><source lang="java">
+
Player other = (Bukkit.getServer().getPlayer(args[0]));
@Override
+
        if (other == null) {
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+
          sender.sendMessage(ChatColor.RED + args[0] + " is not online!");
    if (args.length < 1) {
+
          return false;
        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>クラスを利用して行う事ができます。
586行目: 402行目:
  
 
== 権限 ==
 
== 権限 ==
:'''Note''': この節は、日本語読者向けに独自の内容を記述しています。
+
Bukkitのパーミッション(権限)APIの利用は、簡単ではありません。
  
Bukkitにおける権限の利用は、とっても簡単です!
+
プレイヤーが特定の権限を持っているかどうかを調べる処理は次のようになります:
 +
<blockquote><source lang="java">if(player.hasPermission("some.pointless.permission")) {
 +
  //Do something
 +
}else{
 +
  //Do something else
 +
}</source></blockquote>
  
権限がセットされているか、いない(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>
+
そもそも、権限にはグルーピングの概念は不要なのです。
 +
 
 +
元々、グループの主な用途はチャットメッセージをフォーマッティングする事にありました。<br/>
 +
これは権限の機能を利用してもっと簡単に行えます。<br/>
 +
例えば、チャットプラグインの設定において、権限とプレフィクスの関連を定義する事が該当します。<br/>
 +
具体的には、権限'''"someChat.prefix.admin"'''をプレフィクス'''[Admin]'''に対応させる定義を行い、<br/>
 +
プレイヤーがチャットで発言する度に、プレイヤー名の先頭に'''[Admin]'''が付くようになる機能が挙げられます。
 +
 
 +
他にも、グループに所属する複数のユーザに、<br/>
 +
メッセージを送信するような機能を実現するために利用する事が考えられます。<br/>
 +
この例を、権限を利用した処理で記述すると以下のようになります:  
 +
<blockquote><source lang="java">for(Player player: getServer().getOnlinePlayers()) {
  
これを利用してプレイヤーが特定の権限を持っているかどうかを調べる処理は次のようになります:
+
    if(player.hasPermission("send.recieve.message")) {
<blockquote><source lang="java">if(player.hasPermission("some.pointless.permission")) {
+
        player.sendMessage("You were sent a message");
  player.sendMessage("あなたは権限を持っています。");
+
    }
}else{
 
  player.sendMessage("あなたは権限を持っていません。");
 
}</source></blockquote>
 
  
2メソッドの違いは、権限がセットされていなくてもデフォルトで与えられていれば権限を持っているという扱いになるかどうかです。<br>
+
}</source> </blockquote>  
hasPermissionでは デフォルトで与えられていればtrueが返ります。
 
  
PermissionsExなどで 権限はグループとして扱われることが多いですが、Bukkitには権限をグループとして扱う機能はありません。<br/>
+
さて、依然として<br/>
グループとして扱う場合は、別途 上記のようなプラグインを使うか、自分で作る必要があります。
+
「グループを利用せずに、複数のプレイヤーに権限をセットする良い方法は何なのか?」が疑問かと思いますが・・・<br/>
 +
BukkitのAPI自体は、グループの概念を提供していません。<br/>
 +
グループの概念を利用するためには、permissionsBukkitのような<br/>
 +
グループ権限の機能を提供するプラグインを利用する事になります。<br/>
 +
とどのつまり、'''このAPIはインタフェース(Interface)であり、実装(Implementation)ではない'''のです。
  
 
=== 権限の設定 ===
 
=== 権限の設定 ===
  
デフォルトで権限を付与させたり、OPにのみデフォルトで付与させたい場合は、<br>
+
権限を利用して細かな制御を行いたい場合は、<br/>
''plugin.yml''を使うと簡単にできます。
+
デフォルト権限と、子権限の設定を''plugin.yml''に追記する事を検討して下さい。<br/>
 +
この2種類の設定は、オプション(必須ではなく完全に任意で利用される)項目ではありますが、お勧めします。<br/>
  
 
下記は、''plugin.yml''の最後に追加する形で設定する権限です:
 
下記は、''plugin.yml''の最後に追加する形で設定する権限です:
<blockquote>
+
<blockquote><code><source lang="yaml">permissions:
<syntaxhighlight lang="yaml">
 
permissions:
 
 
     doorman.*:
 
     doorman.*:
 
         description: Gives access to all doorman commands
 
         description: Gives access to all doorman commands
635行目: 466行目:
 
         default: true
 
         default: true
 
     doorman.denied:
 
     doorman.denied:
         description: Prevents this user from entering the door
+
         description: Prevents this user from entering the door</source>
</syntaxhighlight>
+
</code></blockquote>
</blockquote>
 
  
  
655行目: 485行目:
 
==== 子権限 ====
 
==== 子権限 ====
  
権限を設定するときに''* 権限''を利用すると、その権限の子権限すべてを操作することができます。<br/>
+
恐らく今までに、''* 権限''を利用してサブ権限を割り当てた事があると思います。<br/>
 
これは、変更されたBukkit APIと、子権限の定義によって実現した機能であり、<br/>
 
これは、変更されたBukkit APIと、子権限の定義によって実現した機能であり、<br/>
 
高い柔軟性を提供しています。<br/>
 
高い柔軟性を提供しています。<br/>
 
下記はその実装例です:
 
下記はその実装例です:
<blockquote>
+
<blockquote><source lang="yaml">permissions:
<syntaxhighlight lang="yaml">
 
permissions:
 
 
     doorman.*:
 
     doorman.*:
 
         description: Gives access to all doorman commands
 
         description: Gives access to all doorman commands
669行目: 497行目:
 
             doorman.knock: true
 
             doorman.knock: true
 
             doorman.denied: false</source>
 
             doorman.denied: false</source>
</syntaxhighlight>
+
 
 
''doorman.*''権限は、いくつかの子権限を含んでいます。
 
''doorman.*''権限は、いくつかの子権限を含んでいます。
 
''doorman.*''権限がtrueである場合に、
 
''doorman.*''権限がtrueである場合に、
676行目: 504行目:
 
その子権限のデフォルト権限は、全て反転(trueが定義値ならfalseになる)された状態で機能します。
 
その子権限のデフォルト権限は、全て反転(trueが定義値ならfalseになる)された状態で機能します。
 
</blockquote>  
 
</blockquote>  
 
ねっ、簡単でしょ?
 
  
 
=== 独自の権限設定 ===
 
=== 独自の権限設定 ===
699行目: 525行目:
  
 
== ブロックの操作 ==
 
== ブロックの操作 ==
 
 
ブロックを生成する簡単な方法は、既存のブロックを取得して変更する事です。<br/>
 
ブロックを生成する簡単な方法は、既存のブロックを取得して変更する事です。<br/>
 
例えば、あなたの5ブロック上方にあるブロックを変更したい場合、<br/>
 
例えば、あなたの5ブロック上方にあるブロックを変更したい場合、<br/>
 
まずはそのブロックを取得した上で、変更を加えることになります。<br/>
 
まずはそのブロックを取得した上で、変更を加えることになります。<br/>
 
PlayerMoveイベントの処理中でこれを行う例を示します:
 
PlayerMoveイベントの処理中でこれを行う例を示します:
<blockquote><source lang="java">
+
<blockquote><source lang="java">public void onPlayerMove(PlayerMoveEvent evt) {
@EventHandler
+
Location loc = evt.getPlayer().getLocation(); // プレイヤーからその位置を得る
public void onPlayerMove(PlayerMoveEvent evt) {
+
World w = loc.getWorld(); // 位置からワールドを得る
    // プレイヤーの位置を取得します。
+
loc.setY(loc.getY() + 5); // 位置のY座標を+5する
    Location loc = event.getPlayer().getLocation();
+
Block b = w.getBlockAt(loc); // ワールド上の指定位置のブロックを得る
    // 位置のY座標を+5します。位置情報を変更しているだけで、実際にプレイヤーの位置が移動するわけではないことに注意してください。
+
b.setTypeId(1); // ブロックのタイプIDに1をセットする
    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/>
+
プレイヤーの5ブロック上方のブロックがStone(TypeIdが1のブロック)に変化する動作として見えるでしょう。<br/>
 
ブロック取得までの流れは・・・
 
ブロック取得までの流れは・・・
 
# 取得したプレイヤーの位置から、ワールドを取得する
 
# 取得したプレイヤーの位置から、ワールドを取得する
 
# 位置のY座標を+5する
 
# 位置のY座標を+5する
# <code>'''loc.getBlockAt(loc);'''</code>で、指定位置に存在するブロックを得る
+
# <code>'''w.getBlockAt(loc);'''</code>で、ワールドにおける位置に存在するブロックを得る
 
となります。<br/>
 
となります。<br/>
  
 
位置(''loc'')中のブロックインスタンス(''b'')に対して、<br/>
 
位置(''loc'')中のブロックインスタンス(''b'')に対して、<br/>
Materialを変えたり、ブロックデータ自体を変更する事もできます。
+
IDを変えたり、ブロックデータ自体を変更する事もできます。
詳しくは、JavaDocを参照してください。<br/>
 
  
建物の生成や、一定のアルゴリズムに従ったブロック生成処理などを行う一例を示します。<br/>
+
また、ブロックの種類を表すデータはbyte型であるため、<br/>
 +
byte型にキャストしたデータを与えた指定ができます。<br/>
 +
例えば、<code>'''b.setData((byte)3); '''</code>です。
 +
 
 +
 
 +
建物の生成や、一定のアルゴリズムに従ったブロック生成処理などを行う事ができます。<br/>
 
例えば、固体ブロックによる立方体を生成する処理は、<br/>
 
例えば、固体ブロックによる立方体を生成する処理は、<br/>
 
3階層にネストしたforループ処理によって記述できます。
 
3階層にネストしたforループ処理によって記述できます。
<blockquote><source lang="java">
+
<blockquote><source lang="java">public void generateCube(Location point, int length){ // public visible method generateCube() with 2 parameters point and location
    public void generateCube(Location loc, int length){
+
World world = point.getWorld();
        // 与えられたLocationから、立方体の端の座標を取得します。
+
 
        // getN()メソッドを使うと intへキャストする必要がありますが、
+
int x_start = point.getBlockX();     // Set the startpoints to the coordinates of the given location
        // getBlockN()メソッドを使えばそのままintで座標を取得できます。
+
int y_start = point.getBlockY();     // I use getBlockX() instead of getX() because it gives you a int value and so you dont have to cast it with (int)point.getX()
        int x1 = loc.getBlockX();  
+
int z_start = point.getBlockZ();
        int y1 = loc.getBlockY();
+
 
        int z1 = loc.getBlockZ();
+
int x_lenght = x_start + length;   // now i set the lenghts for each dimension... should be clear.
   
+
int y_lenght = y_start + length;
        // 一辺の長さを足すことで、立方体の反対側の座標を計算します。
+
int z_lenght = z_start + length;
        int x2 = x1 + length;
+
 
        int y2 = y1 + length;
+
for(int x_operate = x_start; x_operate <= x_length; x_operate++){  
        int z2 = z1 + length;
+
// Loop 1 for the X-Dimension "for x_operate (which is set to x_start)
   
+
//do whats inside the loop while x_operate is
        World world = loc.getWorld();
+
//<= x_length and after each loop increase
   
+
//x_operate by 1 (x_operate++ is the same as x_operate=x_operate+1;)
        // x座標方向のループ
+
for(int y_operate = y_start; y_operate <= y_length; y_operate++){// Loop 2 for the Y-Dimension
        for (int xPoint = x1; xPoint <= x2; xPoint++) {  
+
for(int z_operate = z_start; z_operate <= z_length; z_operate++){// Loop 3 for the Z-Dimension
            // y座標方向のループ
+
 
            for (int yPoint = y1; yPoint <= y2; yPoint++) {
+
Block blockToChange = world.getBlockAt(x_operate,y_operate,z_operate); // get the block with the current coordinates
                // z座標方向のループ
+
blockToChange.setTypeId(34);   // set the block to Type 34
                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になりますね。
+
ただし、セットするブロックのIDはゼロ(=air)になりますね。
  
 
== プレイヤーインベントリの操作 ==
 
== プレイヤーインベントリの操作 ==
804行目: 622行目:
 
=== エンチャント ===
 
=== エンチャント ===
  
アイテムにエンチャントを付与するには、ItemStackクラスの '''addEnchantment(Enchantment enchant, int level)''' メソッドを使用します。
+
アイテムに対するエンチャントに触れる前に、[http://ja.minecraftwiki.net/wiki/Data_values Item Code] と [http://ja.minecraftwiki.net/wiki/Enchanting EID]を見てから以下の解説を読んでください。
  
addEnchantment()メソッドでは、元々エンチャントが出来ないアイテムに対して、独自にエンチャントを付与する処理は書けません。<br/>
+
エンチャントは、Enchantmentクラスが受け持っている機能ですが、<br/>
もし、通常ありえないエンチャントを設定したい場合は、addEnchantment()メソッドの代わりにaddUnsafeEnchantment()メソッドを使ってください。
+
Enchantmentクラス自体は抽象クラスであるため、インスタンス化('''new Enchantment()''')出来ません。
 +
エンチャントはEnchantmentWrapperクラスから利用する必要があるからです。
  
Sharpness 1 エンチャントを石の剣に付与する例を示します。
+
元々エンチャントが出来ないアイテムに対して、独自にエンチャントを付与する処理は書けません。<br/>
 +
Bukkitサーバ自体が、エンチャント不可能なアイテムに対して、エンチャント情報を付与する仕組みを備えていないからです。<br/>
 +
つまり、Fire AspectedなStick(火のエンチャントが付いた木の棒)はあり得ません。
 
<blockquote><source lang="java">
 
<blockquote><source lang="java">
// 新しい石の剣を生成します。
+
int itemCode = 280;  // StickのItemID
ItemStack myItem = new ItemStack(Material.STONE_SWORD);
+
int effectId = 20; // 効果Fire AspectのEID
 +
int enchantmentLevel = 100; // エンチャントのレベル
  
// エンチャントを付与します。
+
ItemStack myItem = new ItemStack(itemCode);  // 木の棒のインスタンスを生成する
myItem.addEnchantment(Enchantment.DAMAGE_ALL, 1);   
+
Enchantment myEnchantment = new EnchantmentWrapper(effectId);  // エンチャント効果のインスタンスを生成する
 +
myItem.addEnchantment(myEnchantment, enchantmentLevel);  // 木の棒にFireAspectを付与する(ただし付与は成功しない)
 
</source></blockquote>
 
</source></blockquote>
  
次に、火属性 100 を、木の棒に付与する例を示します。
+
== HashMapの応用 ==
 +
 
 +
:'''Note''': このセクションは原文が独特な書き回しだったため意訳を行っています。
 +
 
 +
複数のプレイヤーが発するアクションやイベントを処理するためには、<br/>
 +
イベントの発生や状態を保持するための領域として単一の変数を利用していたのでは不十分です。
 +
 
 +
例を挙げると、<br/>
 +
私が作った古いプラグインのZones(今はRegionsに改名して処理も改善しています)では、<br/>
 +
プラグインがサーバ上で実際にどのように振舞うかに関して考慮し切れていなかったために、<br/>
 +
多くのエラーに直面しました。<br/>
 +
具体的には、プレイヤーが特定の領域内に居たかどうかをチェックする処理で<br/>
 +
チェック結果の格納先として単一のboolean値を利用したため、<br/>
 +
複数のプレイヤーそれぞれに対するチェックを個別に保持する事が出来ずに膨大なエラーを引き起こしたのです。
 +
 
 +
HashMapはこのような問題を解決する素晴らしい方法です。<br/>
 +
HashMapは、データを「'''キー'''と'''値'''のペア」の集まりとして保管できるデータ保持機能です。<br/>
 +
これを利用するメリットは、HashMapが下記の仕様を備えている事です。<br/>
 +
* 1つのキーに対して、必ず1つの値が対応する
 +
* 同じ内容のキーが、同一のインスタンス内に重複して存在できない
 +
つまり、キーがプレイヤー、値がプレイヤーに紐付く情報の保管先になるようにHashMapを利用する事で、<br/>
 +
プレイヤーとプレイヤーに紐付く値が強制的にペア(対)で管理できますし、<br/>
 +
プレイヤーがインスタンス内で重複し得ない(ゲーム内で同一のプレイヤーは重複し得ない)事を保証できるのです。
 +
 
 +
具体的には、"TaroYamada"がキーとなり、"False"が値となります。<br/>
 +
また、後からキー"TaroYamada"に対応する値を"True"に変更する事もできます:
 
<blockquote><source lang="java">
 
<blockquote><source lang="java">
// 新しい木の棒を生成します。
+
import java.util.HashMap;
ItemStack myItem = new ItemStack(Material.STICK);
+
public class Sample{
 +
  public Sample(){
 +
    HashMap<String, Boolean> playerFlags = new HashMap<String, Boolean>();
 +
    playerFlags.put("TaroYamada", false);
 +
    playerFlags.put("TaroYamada", true);
 +
    playerFlags.put("GorillaMatsui", true);
 +
  }
 +
}
 +
</source></blockquote>
 +
:'''Note''': 理解のために独自に追加したコードです。
 +
 
 +
=== HashMapの定義 ===
 +
<blockquote><source lang="java">public Map<Key, DataType> HashMapName = new HashMap<Key, Datatype>(); //Example syntax
 +
 
 +
//Example Declaration
 +
 
 +
public Map<Player, Boolean> pluginEnabled = new HashMap<Player, Boolean>();
 +
public Map<Player, Boolean> isGodMode = new HashMap<Player, Boolean>();
 +
</source> </blockquote>
 +
 
 +
このコードは、以降のHashMapsの説明で使うので覚えてください。
 +
さて、例としてプラグインが有効・無効化する時に、天候を切り替える簡単な処理を作りましょう。
 +
まずはonCommand()の中に、前述の説明と同様にプレイヤーの状態に応じて、プレイヤー名を送信する処理を記述します。
  
// 木の棒にFireAspectレベル100を付与します。
+
onCommand()には下記を記述します。
// ただしFireAspectレベル100は通常存在しないので、addEnchantment を使うと失敗します。
+
内部で呼んでいるメソッドの名前は変えても良いですが、意味が通じるシンプルなものにして下さい。
// ありえないエンチャントを設定したい場合は、addUnsafeEnchantment を使ってください。
+
<blockquote><source lang="java">Player player = (Player) sender;
myItem.addUnsafeEnchantment(Enchantment.FIRE_ASPECT, 100);
+
togglePluginState(player);</source> </blockquote>
</source></blockquote>
+
上記のコードは、senderをPlayer型にキャストしたものをパラメタとして'''togglePluginState()'''に与えています。
 +
では、'''togglePluginState()'''を実装しましょう。
 +
<blockquote><source lang="java">public void togglePluginState(Player player){
 +
   
 +
    if(pluginEnabled.containsKey(player)){
 +
        if(pluginEnabled.get(player)){
 +
            pluginEnabled.put(player, false);
 +
            player.sendMessage("Plugin disabled");
 +
        } else {
 +
            pluginEnabled.put(player, true);
 +
            player.sendMessage("Plugin enabled");
 +
        }
 +
    } else {
 +
        pluginEnabled.put(player, true); //If you want plugin enabled by default change this value to false.
 +
        player.sendMessage("Plugin enabled");
 +
    }
 +
 
 +
}</source> </blockquote>  
 +
 
 +
このコードが何をやっているかというと、
 +
まず、HashMapの'''pluginEnabled'''が'''player'''をキーとして持っているかと、
 +
次に、値が'''true'''と'''false'''のどちらなのかを調べています。
  
=== ItemMeta ===
+
playerがキーに含まれており、かつ
 +
値がtrueであれば、falseにリセットしてプレイヤーへメッセージを送信します。
 +
falseであれば、trueにリセットして同様にメッセージを送信します。
  
アイテムの表示名を変更するには、次のようにします。
+
playerがキーに含まれていない場合、
<blockquote><source lang="java">
+
今処理を'''player'''が初めて行った当処理の実行とみなして、
String myDisplayName = "すごい剣";
+
HashMapへプレイヤーと値のペアを追加し、メッセージを送信します。
 
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は、ゲーム内でアイテムにカーソルを合わせたときに表示される説明文のことです。
+
=== まだあるHashMapsの用途 ===
<blockquote><source lang="java">
 
List<String> lores = new ArrayList<String>();
 
lores.add("loreのテストです。");
 
lores.add("これは2行目です。");
 
  
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //アイテムを生成します。
+
HashMap(よく似たMapも)は、データの集合体です。
ItemMeta im = myItem.getItemMeta(); //ItemStackから、ItemMetaを取得します。
+
集合の中から、一意な'''キー'''を使って対応する'''値'''を参照する処理を、高速・効率的に行います。
im.setLore(lores); //loreを設定します。
 
myItem.setItemMeta(im); //元のItemStackに、変更したItemMetaを設定します。
 
</source></blockquote>
 
  
== Metadata ==
+
これに類する処理は付き物ですが、Mapはこれらを解決します。
 +
Mapの理想的な用途の例はいくつもあります。
 +
下記を見れば分かる通り、
 +
用途は個々のプレイヤーに対応したデータである事にこだわる必要もありません。
 +
必要に応じて、対象を置き換えて他の用途にも活用して下さい。
  
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を使うメリット ===
+
<blockquote><source lang="java">
 +
public Map<String, Integer> wool_colors = new HashMap<String, Integer>();
  
* Metadataは全てBukkit側で管理されます。プラグイン側で管理する必要がありません。
+
// Run this on plugin startup (ideally reading from a file instead of copied out row by row):
* プラグイン間で共通にアクセスできるので、データの共有に使用できます。
+
wool_colors("orange", 1);
 +
wool_colors("magenta", 2);
 +
wool_colors("light blue", 3);
 +
  ...
 +
wool_colors("black", 15);
  
=== Metadataを使うデメリット ===
+
// Run this in response to user commands - turn "green" into 13
 +
int datavalue = 0;
 +
if (wool_colors.containsKey(argument)
 +
    datavalue = wool_colors.get(argument);
 +
else {
 +
    try { datavalue = Integer.parseInt(argument); }
 +
    catch (Exception e) { ; }
 +
}
 +
</source> </blockquote>
  
* データの取得・設定を行うときに、ひと手間が必要になります。
+
=== HashMapデータのセーブ/ロード ===
* Bukkitが停止すると、全てのMetadataが消えます。
+
HashMapがどのように動作するかを学びましたが、
 +
次は恐らく、HashMapのデータを読み書きする方法を知りたくなるかと思います。
 +
もし次のような要求があれば、HashMapが適しています。
 +
* 管理者に手作業でデータ編集をさせたくない
 +
* バイナリ形式でデータを保管したい(YAMLで扱うには複雑すぎるデータ)
 +
* ブロック等のオブジェクトの引き当てに、任意の文字列を使いたい
  
=== Metadataの使い方 ===
+
これらは、HasMapの定義(HashMap&lt;Player,Boolean&gt;)を、任意のデータ型に変更することでシンプルに行えます。
 +
引き続き、前節で出てきた"pluginEnabled"インスタンスの例を使って説明をします。
 +
このコードは、与えられたパスに存在するファイルに対して、HashMapの値を保存します。
 +
<blockquote><source lang="java">public void save(HashMap<Player,Boolean> pluginEnabled, String path)
 +
{
 +
try{
 +
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
 +
oos.writeObject(pluginEnabled);
 +
oos.flush();
 +
oos.close();
 +
//Handle I/O exceptions
 +
}catch(Exception e){
 +
e.printStackTrace();
 +
}
 +
}</source> </blockquote>
  
<source lang="java">
+
これも非常に簡単で、ロード処理も他の例とそっくりな記述をします。
 +
ObjectOutputStreamの代わりにObjectInputStream、
 +
FileOutputStreamの代わりにFileInputStream、
 +
writeObject()の代わりにreadObject()を使っています。
 +
そして、最後にHashMapをreturnしています。
 +
<blockquote><source lang="java">public HashMap<Player,Boolean> load(String path) {
 +
try{
 +
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
 +
Object result = ois.readObject();
 +
//you can feel free to cast result to HashMap<Player,Boolean> if you know there's that HashMap in the file
 +
return (HashMap<Player,Boolean>)result;
 +
}catch(Exception e){
 +
e.printStackTrace();
 +
}
 +
}</source> </blockquote>  
  
    /**
+
このAPIを、HashMap, ArrayLists, Blocks, Players...その他全てのオブジェクトの、セーブ/ロードに利用できます。もし利用する場合は、私(Tomsik68)の著作である事を明記して下さい。
    * PlayerにMetadataを設定するサンプルメソッドです。
+
<blockquote><source lang="java">/** SLAPI = Saving/Loading API
    * @param player 対象プレイヤー
+
* API for Saving and Loading Objects.
    * @param key Metadataのキー名
+
* @author Tomsik68
    * @param value Metadataの値
+
*/
    * @param plugin プラグインクラス
+
public class SLAPI
    */
+
{
    public void setMetadata(Player player, String key, Object value, Plugin plugin) {
+
public static void save(Object obj,String path) throws Exception
        player.setMetadata(key, new FixedMetadataValue(plugin, value));
+
{
    }
+
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
 +
oos.writeObject(obj);
 +
oos.flush();
 +
oos.close();
 +
}
 +
public static Object load(String path) throws Exception
 +
{
 +
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
 +
Object result = ois.readObject();
 +
ois.close();
 +
return result;
 +
}
 +
}</source> </blockquote>
 +
APIを使った実装方法: '''一部だけ抜粋'''
 +
<blockquote><source lang="java">public class Example extends JavaPlugin {
 +
private ArrayList<Object> list = new ArrayList<Object>();
 +
public void onEnable()
 +
{
 +
list = (ArrayList<Object>)SLAPI.load("example.bin");
 +
}
 +
public void onDisable()
 +
{
 +
SLAPI.save(list,"example.bin");
 +
}
 +
}</source> </blockquote>
  
    /**
+
:'''SLAPIとJavaのObjectOutputStreamに関する補足''':Integer, String, HashMapといったJavaのデータ型について理解しているのであれば、この処理は未編集で動作します。Bukkitが提供するデータ型に対しても同様です。しかし、独自のデータクラスを自作してこのテクニックを適用したい場合は、JavaのSerializableインタフェースについて読んで下さい。それにより、簡単に少量の変更で動かす事ができます。コードを丁寧に解析する必要はありません。
    * 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() メソッドを使うことで、キャストせずに直接取得が可能です。
+
== Map・Set・Listの応用 ==
 +
HashMapクラスのもうひとつの機能は、Javaの持つ多くのデータ構造を提供する事にあります。<br/>
 +
しかし、クラスの種類によってMapは時に適切ではないケースも存在します。<br/>
 +
こちらに、そのようなケースについて詳しく記述するためのページとして、[[Javaのデータ構造クラス]]を用意しています。
  
 
== データベース ==
 
== データベース ==
940行目: 874行目:
  
 
== プラグインの配布 ==
 
== プラグインの配布 ==
 +
:'''Note''': 当セクションは、日本語圏の読者向けの資料として外部の紹介サイトを独自に追加しています。
  
プラグインを実装し終わったら、Mavenを使ってビルドして、リリース用のJarファイルを作成してみましょう。
+
プラグインのコーディング後に行うべき事は、サーバへインストールするための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]]
 
  
 +
# まずは、[http://ja.minecraftwiki.net/wiki/Server#.E3.82.B5.E3.83.BC.E3.83.90.E3.83.BC.E3.81.AE.E3.82.BB.E3.83.83.E3.83.88.E3.82.A2.E3.83.83.E3.83.97 サーバのセットアップ]を参考にしながらローカルマシン上にBukkitサーバを起動可能な状態で設置して下さい。(とりあえずは[[http://www.minecraft.net/ minecraft公式サーバ]]と[[http://dl.bukkit.org/ bukkitサーバ]]を取得して、bukkitサーバを起動コマンドで起動可能な状態にするだけで良い)
 +
# 次に、プラグインをJar形式でエクスポートするための画面を開きます。(Eclipseを例にした場合、プラグインのプロジェクトを選択した状態で、'''パッケージエクスプローラ上のプロジェクト'''の右クリックか、'''ファイル &gt;&nbsp;エクスポート'''の選択で表示される機能)
 +
# 開いたエクスポート画面上で'''"Java"''', '''"JARファイル"'''を選択し、'''次へ'''ボタンを押下し、下記のような画面に遷移します。[[Image:Exportwindow.png|Eclipse export]]
 +
# 画面左側のメニューで、プラグインのソースコードが存在するフォルダ(この例ではsrc)が選択されている事を確認します。
 +
# 画面右側のメニューで、ファイル名がドットで始まるファイルの選択を解除します(この例では''.classpath'', ''.gitgnore'', ''.project''の3ファイル)。これらはEclipseが利用するファイルでありプラグインには不要です。
 +
# プラグインを動作させるために重要な事ですが、''plugin.yml''が選択されている事も確認します。
 +
# 最後に、任意の場所にJARファイルをエクスポート(書き出し)します。
  
 
プラグインのコードとplugin.ymlに不備が無ければ、エクスポートしたJarファイルはすぐにBukkitプラグインとして動作します。Jarファイルを、Bukkitサーバの'''"plugins"'''フォルダの中に配置し、Bukkitサーバを起動し、プラグインの動作確認をしてみましょう。なお、ローカルマシン上で起動するBukkitサーバへは、Minecraftクライアントのマルチプレイヤーサーバの接続先IPアドレスに'''"localhost"'''を指定して接続する事でログインできます。
 
プラグインのコードとplugin.ymlに不備が無ければ、エクスポートしたJarファイルはすぐにBukkitプラグインとして動作します。Jarファイルを、Bukkitサーバの'''"plugins"'''フォルダの中に配置し、Bukkitサーバを起動し、プラグインの動作確認をしてみましょう。なお、ローカルマシン上で起動するBukkitサーバへは、Minecraftクライアントのマルチプレイヤーサーバの接続先IPアドレスに'''"localhost"'''を指定して接続する事でログインできます。
984行目: 895行目:
  
 
=== プレイヤーに着火する ===
 
=== プレイヤーに着火する ===
下記は、指定されたプレイヤーに着火するサンプルです。例えば '''/ignite Notch''' と実行すると、Notchが燃えます!
+
プレイヤーを燃やすコマンド:
 
<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("ignite")){
    // equals() の代わりに equalsIgnoreCase() を使うと、大文字小文字に関係なく、
+
         Player s = (Player)sender;
    // 文字列の一致を確認できます。"ignite"でも"IgNiTe"でも指定可能になります。
+
         Player target = s.getWorld().getPlayer(args[0]); // 誰がコマンドを実行したか
     if (cmd.getName().equalsIgnoreCase("ignite")) {
+
         // "/ignite notch"コマンドが打たれた場合、targetインスタンスは"notch"を対象とします。
         // コマンドのパラメータに、燃やすプレイヤーが指定されているかどうかを
+
         // Note: 最初の引数は[0]で、[1]ではないので、arg[0]がプレイヤー名を格納しています。
        // 確認します。
+
         target.setFireTicks(10000);
        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 true;
 
     }
 
     }
 
     return false;
 
     return false;
 
}
 
}
</source> </blockquote>
+
</source> </blockquote>  
 +
 
 +
プレイヤー"Notch"がオンラインである場合のみ'''/ignite Notch'''コマンドは動作し、Notchが燃やされます。
  
 
=== プレイヤーを殺す ===
 
=== プレイヤーを殺す ===
1,023行目: 919行目:
 
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("KillPlayer")){
 
     if(cmd.getName().equalsIgnoreCase("KillPlayer")){
         // コマンドのパラメータに、殺害するプレイヤーが指定されているかどうかを
+
         Player player = (Player)sender;
        // 確認します。
+
         Player target = player.getWorld().getPlayer(args[0]);
        if (args.length != 1) {
+
         target.setHealth(0);  
            // 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;
 
     return false;
}
+
}</source></blockquote>  
</source></blockquote>  
 
  
 
上記の拡張版として、プレイヤーを爆死させる処理を下記に示します:  
 
上記の拡張版として、プレイヤーを爆死させる処理を下記に示します:  
 
<blockquote><source lang="java">
 
<blockquote><source lang="java">
@Override
+
float explosionPower = 4F; //This is the explosion power - TNT explosions are 4F by default
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
+
Player target = sender.getWorld().getPlayer(args[0]);
    if(cmd.getName().equalsIgnoreCase("KillPlayer")){
+
target.getWorld().createExplosion(target.getLocation(), explosionPower);
        Player target = Bukkit.getPlayerExact(args[0]);
+
target.setHealth(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>
 
</source> </blockquote>
  
 
=== 爆発を起こす ===
 
=== 爆発を起こす ===
このコードは、TNTの爆発と同様の音とヴィジュアルを再現します。
+
このコードは、TNTやクリーパーの爆発と同様の音とヴィジュアルを再現します。
 
これは、TNTの爆発効果は無効化しつつ、音とヴィジュアル効果を発生させる処理に転用できます。
 
これは、TNTの爆発効果は無効化しつつ、音とヴィジュアル効果を発生させる処理に転用できます。
 
<blockquote><source lang="java">
 
<blockquote><source lang="java">
@EventHandler
+
public void onExplosionPrime(ExplosionPrimeEvent event){
public void onExplosionPrime(ExplosionPrimeEvent event) {
+
    Entity entity = event.getEntity();
+
Entity entity = event.getEntity();
 
+
    // このイベントは、点火されたTNTにより発生したのかどうかを確認します。
+
if (entity instanceof TNTPrimed){
    // (つまり、TNTの爆発はこれで無効化されますが、クリーパーの爆発は無効化されません)
+
TNTPrimed tnt = (TNTPrimed) entity;
    if (entity instanceof TNTPrimed) {
+
event.getEntity().getWorld().createExplosion(tnt.getLocation(), 0);
        event.setCancelled(true); // イベントをキャンセルして、爆発を無かったことにする
+
        entity.getWorld().createExplosion(entity.getLocation(), 0); // 偽物の爆発を発生させる
+
}
    }
 
 
}
 
}
 
</source></blockquote>
 
</source></blockquote>
  
=== プレイヤーを非表示にする ===
+
== リクエストに応じて書かれた記事 ==
  
これは指定したプレイヤーから自分を非表示にするサンプルです。
+
=== mavenを利用したプラグイン開発 ===
指定したプレイヤー以外のプレイヤーからは、自分が見えたままになっています。
+
gitのリポジトリ上にある'''BukkitPluginArchetype'''をcloneし、それをビルドする:
 +
<blockquote><source lang="bash">
 +
git clone git://github.com/keyz182/BukkitPluginArchetype.git
 +
cd BukkitPluginArchetype
 +
mvn clean install
 +
</source></blockquote>
  
<blockquote>
+
作成するプラグインのフォルダに移動して下記のコマンドを実行します:
<source lang="java">
+
<blockquote><source lang="bash">mvn archetype:generate -DarchetypeCatalog=local</source></blockquote>
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
 
    if(cmd.getName().equalsIgnoreCase("HideMe") && args.length == 1) {
 
  
        // コマンド実行者がプレイヤーかどうかを確認します。
+
プロンプトに表示されたリストから下記を選択します:
        if (!(sender instanceof Player)) {
+
<blockquote>uk.co.dbyz:bukkitplugin (bukkitplugin) </blockquote>
            sender.sendMessage("このコマンドはゲーム内から実行してください!");
 
            return true;
 
        }
 
        // sender instanceof Player の検査が終わっているので、Playerクラスへ安全にキャストできます。
 
        Player s = (Player) sender;
 
  
        // コマンドのパラメータに、対象のプレイヤーが指定されているかどうかを確認します。
 
        if (args.length != 1) {
 
            // onCommandでfalseを戻すと、plugin.ymlのusageに設定したメッセージを
 
            // コマンド実行者の画面に表示します。
 
            return false;
 
        }
 
  
        // 指定されたプレイヤーを取得します。
+
GroupIDには筆頭(プラグインのトップ階層)としたいJavaパッケージ名を指定し、ArtifactIDにはパッケージ名の末端の名称(Jarファイルのような成果物の名称として利用されます)を入力します。そして、確認メッセージに'''Y&lt;enter&gt;'''で応答します。
        // 指定されたプレイヤーがサーバーに接続していない場合、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="bash">
 +
Define value for property 'groupId': : uk.co.dbyz.mc
 +
Define value for property 'artifactId': : plugin
 +
Define value for property 'version': 1.0-SNAPSHOT:
 +
Define value for property 'package': uk.co.dbyz.mc:
 +
</source></blockquote>
  
下記のサンプルは、釣竿を持ってクリックしたときに、クリックした場所を取得して、その場所に雷を落とします。
+
ArchetypeIDに指定した文字列と同名のフォルダが生成され、配下に'''src'''フォルダと'''pom.xml'''ファイルが配置されます。
  
<blockquote>
+
'''src/main/java/&lt;package&gt;'''フォルダに存在する'''&lt;archetypeid&gt;CommandExecuter.java'''ファイルを開き、コード<source lang="java">//Do Something</source>の部分に書きのコードを記述します:
<source lang="java">
+
<blockquote><source lang="java">
@EventHandler
+
Player player = (Player) sender;
public void onPlayerInteractBlock(PlayerInteractEvent event) {
+
player.setHealth(1000);
 +
</source></blockquote>
  
    Player player = event.getPlayer();
+
ベースのフォルダに移動して下記を実行します:
 
+
<blockquote><source lang="bash">
    if (player.getItemInHand().getType() == Material.FISHING_ROD) {
+
mvn clean package
        // プレイヤーが見ている場所に雷をおとします。
+
</source></blockquote>  
 
+
ダウンロード処理が走りますが、他の作業を並行して行っても大丈夫です。
        Block target = getTargetBlock(player);
+
''clean package''処理が完了すると、targetフォルダの中に'''&lt;archetypeid&gt;-1.0-SNAPSHOT.jar'''ファイルが生成されます(これがビルドされたプラグインのJarファイルです)。このファイルをBukkitのpluginsフォルダへコピーして、Bukkitサーバを起動して下さい。
        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>
 
  
 +
ゲーム内で'''/&lt;archetypeid&gt;'''コマンドを実行すると、そのプレイヤーのHealthが全快します。
  
 
== プラグインのサンプル兼雛形 ==
 
== プラグインのサンプル兼雛形 ==
1,177行目: 1,002行目:
 
この内容について質問がある場合、遠慮なく[http://forums.bukkit.org/members/adamki11s.42417/ Adamki11s]か[http://wiki.bukkit.org/IRC BukkitDevのIRCチャンネル](当Wikiの原文を掲載しているサイトのIRCチャンネルです)で聞いてください。
 
この内容について質問がある場合、遠慮なく[http://forums.bukkit.org/members/adamki11s.42417/ Adamki11s]か[http://wiki.bukkit.org/IRC BukkitDevのIRCチャンネル](当Wikiの原文を掲載しているサイトのIRCチャンネルです)で聞いてください。
 
:'''Note''': 当ページは訳文であるため、訳文自体に対する文責は原著者にありません。その点を考慮して必ず原文にも目を通してから質問して下さい。
 
:'''Note''': 当ページは訳文であるため、訳文自体に対する文責は原著者にありません。その点を考慮して必ず原文にも目を通してから質問して下さい。
 
[[カテゴリ:Bukkitチュートリアル|*]]
 

Minecraft Modding Wikiへの投稿はすべて、他の投稿者によって編集、変更、除去される場合があります。 自分が書いたものが他の人に容赦なく編集されるのを望まない場合は、ここに投稿しないでください。
また、投稿するのは、自分で書いたものか、パブリック ドメインまたはそれに類するフリーな資料からの複製であることを約束してください(詳細はMinecraft Modding Wiki:著作権を参照)。 著作権保護されている作品は、許諾なしに投稿しないでください!

このページを編集するには、下記の確認用の質問に回答してください (詳細):

取り消し 編集の仕方 (新しいウィンドウで開きます)

このページで使用されているテンプレート: