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

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

この編集を取り消せます。 下記の差分を確認して、本当に取り消していいか検証してください。よろしければ変更を保存して取り消しを完了してください。
最新版 編集中の文章
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 />
 +
'''本項目は、和訳の最中です。最後まで読むことは出来ません。'''<br />
  
 
== 始めに ==
 
== 始めに ==
:'''重要''': 当ページには、原文の翻訳ではない、日本語読者向けの独自の記述を行っている箇所が少量あります。そのような内容を含む節ではNoteにて通知しています。
+
このチュートリアルは、[http://forums.bukkit.org/threads/plugin-development-a-huge-tutorial-status-under-development.15167/ こちらのスレッド]からの転載です。当ページの原著者は、'''Adamki11s'''氏ですが、多くの編集を受けて今に至ります。
:'''重要''': 当ページでは、技術的な解説やたとえ話の原意を理解し易くするために、一部意訳を行っています。
 
:'''重要''': 当ページは訳文であるため、訳文自体に対する文責は原著者にありません。当ページの内容を受けて原著者へ何らかのアクションを行う場合、その点を考慮して必ず原文にも目を通して下さい。
 
  
このチュートリアルは、Bukkitプラグインを開発するための基本的なノウハウを網羅しております。
+
[http://forums.bukkit.org/members/adamki11s.42417/ Adamki11s Bukkit Profile Page]
  
Javaを習得する、IDEで開発環境を構築する、などの非常に基本的な部分から説明が始まっていますが、既にご理解頂いている場合は読み飛ばしていただいて構いません。
+
このチュートリアルを読み終えた後は、'''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.]
 +
 
 +
 
 +
:'''Note''': 当ページには、原文の翻訳ではない、日本語読者向けの独自の記述を行っている箇所が少量あります。そのような内容を含む節ではNoteにて通知しています。
 +
:'''Note''': 当ページの内容は、[[Bukkit用MODの作成方法]]の内容とは異なるアプローチによるBukkitプラグイン開発手法の解説です。
 +
:'''Note''': 当ページでは、技術的な解説やたとえ話の原意を理解し易くするために、一部意訳を行っています。
  
 
== Javaの習得 ==
 
== Javaの習得 ==
16行目: 20行目:
 
このチュートリアルの読者には、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行目: 38行目:
  
 
あなたが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の利用方法 ===
<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>
 
  
 +
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/>
<blockquote>
+
Eclipse上で、Bukkitのクラスやメソッドにマウスを重ねたタイミングでポップアップを表示できるようにするためには、下記の手順を行います。
* org.bukkit
 
* net.bukkit
 
* com.bukkit
 
* net.minecraft
 
</blockquote>
 
  
ベースとなるグループIDを決めたら、次にプラグイン名を決めましょう。ここでは例として、TutorialPlugin と名付けます。
+
# プロジェクトエクスプローラ上で、Bukkitのjarファイルを右クリックして、メニューを開く。
 +
# "プロパティ"を選択し、表示されるポップアップ左側の"Javadoc ロケーション"項目を選択する。
 +
# "Javadoc URL"の下部にあるテキストボックスに、"http://jd.bukkit.org/apidocs/" (ダブルクォートは除く)を貼り付ける。
 +
# "検証"ボタンを押下し、URLがJavadocとして正しく識別される事をチェックしてから、OKボタンを押す。
  
ダイアログには次のように入力し、完了 を押してください。
+
:次ような画面になります:<br/>
 +
:[[Image:Bukkitjavadocs.png]]
  
[[Image:create_project3.png]]
+
=== Plugin開発の開始 ===
  
Eclipseの画面に戻ると、左側に TutorialPlugin プロジェクトが作成されているはずです。ここまで、うまく作成できましたか?
+
[[Image:MakePackage.png]]
  
「ビルドプランを計算できませんでした。」と出た場合、作成したパッケージを右クリックし「実行」から「Maven install」をクリックすることにより、
+
== onEnable() and onDisable() ==
Mavenのインストールが始まりエラーが出なくなります。
+
このファンクションは、プラグインが有効/無効になったときに呼ばれます。<br />
 
 
=== 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/>
 
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()は、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 {
 +
 +
Logger log;
 
   
 
   
    @Override
+
public void onEnable(){
    public void onEnable() {
+
log = this.getLogger();
        getLogger().info("onEnableメソッドが呼び出されたよ!!");
+
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行目: 161行目:
 
さて、どのようにイベントを登録しいつ発生するかについて理解しました。
 
さて、どのようにイベントを登録しいつ発生するかについて理解しました。
 
しかし、コマンドを利用して何かを起こしたい場合はどのようなデータ型を利用すればよいのでしょうか?それには、onCommand()メソッドを利用します。
 
しかし、コマンドを利用して何かを起こしたい場合はどのようなデータ型を利用すればよいのでしょうか?それには、onCommand()メソッドを利用します。
このメソッドは、plugin.ymlに設定したプラグインのコマンドが実行されたときに呼び出されます。
+
このメソッドは、プレイヤーが文字"/"を入力する度に実行されます。
 +
具体的には、"/do something"とコマンドを実行した場合にonCommand()が呼び出されます。
 
今のところ、onCommand()はまだプログラムしていないので何も起こらないでしょう。
 
今のところ、onCommand()はまだプログラムしていないので何も起こらないでしょう。
  
373行目: 170行目:
 
具体的には、"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行目: 181行目:
 
* 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行目: 210行目:
 
       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行目: 234行目:
 
記述例:  
 
記述例:  
 
<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行目: 253行目:
 
return false;
 
return false;
 
}
 
}
</source> </blockquote>
+
</source> </blockquote>  
  
 
この例では、'''basic''' コマンドはログインプレイヤーとサーバコンソールのどちらからでも実行できます。しかし、'''basic2''' コマンドは、ログインプレイヤーしか実行できません。
 
この例では、'''basic''' コマンドはログインプレイヤーとサーバコンソールのどちらからでも実行できます。しかし、'''basic2''' コマンドは、ログインプレイヤーしか実行できません。
478行目: 274行目:
  
 
:'''Note''': <code>'''"basic"'''</code>は実行されたコマンドであり、<code>'''myExecutor'''</code>はMyPluginCommandExecutorインスタンスです。
 
:'''Note''': <code>'''"basic"'''</code>は実行されたコマンドであり、<code>'''myExecutor'''</code>はMyPluginCommandExecutorインスタンスです。
 +
 
とっても良い具体例:
 
とっても良い具体例:
 +
<blockquote><source lang="java">MyPlugin.java (the main plugin class):
  
MyPlugin.java (プラグインのメインクラス):
+
private MyPluginCommandExecutor myExecutor;
<blockquote><source lang="java">
 
 
@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>
  
 +
<blockquote><source lang="java">
 
MyPluginCommandExecutor.java:
 
MyPluginCommandExecutor.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行目: 322行目:
 
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行目: 338行目:
 
<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行目: 348行目:
  
 
=== プレイヤーがオンラインである事を確認する ===
 
=== プレイヤーがオンラインである事を確認する ===
特定のプレイヤーの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行目: 367行目:
  
 
== 権限 ==
 
== 権限 ==
:'''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行目: 431行目:
 
         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行目: 450行目:
 
==== 子権限 ====
 
==== 子権限 ====
  
権限を設定するときに''* 権限''を利用すると、その権限の子権限すべてを操作することができます。<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行目: 462行目:
 
             doorman.knock: true
 
             doorman.knock: true
 
             doorman.denied: false</source>
 
             doorman.denied: false</source>
</syntaxhighlight>
+
 
 
''doorman.*''権限は、いくつかの子権限を含んでいます。
 
''doorman.*''権限は、いくつかの子権限を含んでいます。
 
''doorman.*''権限がtrueである場合に、
 
''doorman.*''権限がtrueである場合に、
676行目: 469行目:
 
その子権限のデフォルト権限は、全て反転(trueが定義値ならfalseになる)された状態で機能します。
 
その子権限のデフォルト権限は、全て反転(trueが定義値ならfalseになる)された状態で機能します。
 
</blockquote>  
 
</blockquote>  
 
ねっ、簡単でしょ?
 
  
 
=== 独自の権限設定 ===
 
=== 独自の権限設定 ===
696行目: 487行目:
 
といった複数の方法で、長大なタスクをゲームロジックと並行で処理させる事ができます。
 
といった複数の方法で、長大なタスクをゲームロジックと並行で処理させる事ができます。
  
詳しくは、[[スケジューラのプログラミング]]中の、同期タスクと非同期タスクのスケジューリング方法に関する解説を参考にして下さい。
+
詳しくは、[[スケジューラのプログラミング方法]]中の、同期タスクと非同期タスクのスケジューリング方法に関する解説を参考にして下さい。
  
 
== ブロックの操作 ==
 
== ブロックの操作 ==
 
 
ブロックを生成する簡単な方法は、既存のブロックを取得して変更する事です。<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行目: 587行目:
 
=== エンチャント ===
 
=== エンチャント ===
  
アイテムにエンチャントを付与するには、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の応用 ==
<blockquote><source lang="java">
+
 
// 新しい木の棒を生成します。
+
:'''Note''': このセクションは原文が独特な書き回しだったため意訳を行っています。
ItemStack myItem = new ItemStack(Material.STICK);
 
  
// 木の棒にFireAspectレベル100を付与します。
+
複数のプレイヤーが発するアクションやイベントを処理するためには、<br/>
// ただしFireAspectレベル100は通常存在しないので、addEnchantment を使うと失敗します。
+
イベントの発生や状態を保持するための領域として単一の変数を利用していたのでは不十分です。
// ありえないエンチャントを設定したい場合は、addUnsafeEnchantment を使ってください。
 
myItem.addUnsafeEnchantment(Enchantment.FIRE_ASPECT, 100); 
 
</source></blockquote>
 
  
=== ItemMeta ===
+
例を挙げると、<br/>
 +
私が作った古いプラグインのZones(今はRegionsに改名して処理も改善しています)では、<br/>
 +
プラグインがサーバ上で実際にどのように振舞うかに関して考慮し切れていなかったために、<br/>
 +
多くのエラーに直面しました。<br/>
 +
具体的には、プレイヤーが特定の領域内に居たかどうかをチェックする処理で<br/>
 +
チェック結果の格納先として単一のboolean値を利用したため、<br/>
 +
複数のプレイヤーそれぞれに対するチェックを個別に保持する事が出来ずに膨大なエラーを引き起こしたのです。
  
アイテムの表示名を変更するには、次のようにします。
+
HashMapはこのような問題を解決する素晴らしい方法です。<br/>
<blockquote><source lang="java">
+
HashMapは、データを「'''キー'''と'''値'''のペア」の集まりとして保管できるデータ保持機能です。<br/>
String myDisplayName = "すごい剣";
+
これを利用するメリットは、HashMapが下記の仕様を備えている事です。<br/>
+
* 1つのキーに対して、必ず1つの値が対応する
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //アイテムを生成します。
+
* 同じ内容のキーが、同一のインスタンス内に重複して存在できない
ItemMeta im = myItem.getItemMeta(); //ItemStackから、ItemMetaを取得します。
+
つまり、キーがプレイヤー、値がプレイヤーに紐付く情報の保管先になるようにHashMapを利用する事で、<br/>
im.setDisplayName(myDisplayName); //アイテム表示名を設定します。
+
プレイヤーとプレイヤーに紐付く値が強制的にペア(対)で管理できますし、<br/>
myItem.setItemMeta(im); //元のItemStackに、変更したItemMetaを設定します。
+
プレイヤーがインスタンス内で重複し得ない(ゲーム内で同一のプレイヤーは重複し得ない)事を保証できるのです。
</source></blockquote>
 
  
次に、loreを設定してみましょう。loreは、ゲーム内でアイテムにカーソルを合わせたときに表示される説明文のことです。
+
具体的には、"TaroYamada"がキーとなり、"False"が値となります。<br/>
 +
また、後からキー"TaroYamada"に対応する値を"True"に変更する事もできます:
 
<blockquote><source lang="java">
 
<blockquote><source lang="java">
List<String> lores = new ArrayList<String>();
+
import java.util.HashMap;
lores.add("loreのテストです。");
+
public class Sample{
lores.add("これは2行目です。");
+
  public Sample(){
 
+
    HashMap<String, Boolean> playerFlags = new HashMap<String, Boolean>();
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //アイテムを生成します。
+
    playerFlags.put("TaroYamada", false);
ItemMeta im = myItem.getItemMeta(); //ItemStackから、ItemMetaを取得します。
+
    playerFlags.put("TaroYamada", true);
im.setLore(lores); //loreを設定します。
+
    playerFlags.put("GorillaMatsui", true);
myItem.setItemMeta(im); //元のItemStackに、変更したItemMetaを設定します。
+
  }
 +
}
 
</source></blockquote>
 
</source></blockquote>
 +
:'''Note''': 理解のために独自に追加したコードです。
  
== Metadata ==
+
=== HashMapの定義 ===
 +
<blockquote><source lang="java">public Map<Key, DataType> HashMapName = new HashMap<Key, Datatype>(); //Example syntax
  
Bukkit では、プラグインの開発をより簡単にするため、Playerクラス、Entityクラス、Worldクラスに紐づく追加データをMetadataという形式で管理できるようになっています。
+
//Example Declaration
今までは、それぞれのプラグイン内で、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を使うメリット ===
+
public Map<Player, Boolean> pluginEnabled = new HashMap<Player, Boolean>();
 +
public Map<Player, Boolean> isGodMode = new HashMap<Player, Boolean>();
 +
</source> </blockquote>
  
* Metadataは全てBukkit側で管理されます。プラグイン側で管理する必要がありません。
+
このコードは、以降のHashMapsの説明で使うので覚えてください。
* プラグイン間で共通にアクセスできるので、データの共有に使用できます。
+
さて、例としてプラグインが有効・無効化する時に、天候を切り替える簡単な処理を作りましょう。
 +
まずはonCommand()の中に、前述の説明と同様にプレイヤーの状態に応じて、プレイヤー名を送信する処理を記述します。
  
=== Metadataを使うデメリット ===
+
onCommand()には下記を記述します。
 
+
内部で呼んでいるメソッドの名前は変えても良いですが、意味が通じるシンプルなものにして下さい。
* データの取得・設定を行うときに、ひと手間が必要になります。
+
<blockquote><source lang="java">Player player = (Player) sender;
* Bukkitが停止すると、全てのMetadataが消えます。
+
togglePluginState(player);</source> </blockquote>
 
+
上記のコードは、senderをPlayer型にキャストしたものをパラメタとして'''togglePluginState()'''に与えています。
=== Metadataの使い方 ===
+
では、'''togglePluginState()'''を実装しましょう。
 
+
<blockquote><source lang="java">public void togglePluginState(Player player){
<source lang="java">
+
   
 
+
    if(pluginEnabled.containsKey(player)){
    /**
+
        if(pluginEnabled.get(player)){
    * PlayerにMetadataを設定するサンプルメソッドです。
+
            pluginEnabled.put(player, false);
    * @param player 対象プレイヤー
+
            player.sendMessage("Plugin disabled");
    * @param key Metadataのキー名
+
        } else {
    * @param value Metadataの値
+
            pluginEnabled.put(player, true);
    * @param plugin プラグインクラス
+
            player.sendMessage("Plugin enabled");
    */
 
    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;
+
    } else {
 +
         pluginEnabled.put(player, true); //If you want plugin enabled by default change this value to false.
 +
        player.sendMessage("Plugin enabled");
 
     }
 
     }
</source>
 
  
:'''Note''': もしあなたが、必ず boolean、int、String としてMetadataの値を取得したいのであれば、asBoolean()、asInt()、asString() メソッドを使うことで、キャストせずに直接取得が可能です。
+
}</source> </blockquote>  
 
 
== データベース ==
 
時にフラットファイルは、データベースとして利用するには不足があるでしょう。
 
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>
+
まず、HashMapの'''pluginEnabled'''が'''player'''をキーとして持っているかと、
[ERROR] Unable to locate the Javac Compiler in:<br/>
+
次に、値が'''true'''と'''false'''のどちらなのかを調べています。
[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ファイルがあります。
+
playerがキーに含まれており、かつ
 +
値がtrueであれば、falseにリセットしてプレイヤーへメッセージを送信します。
 +
falseであれば、trueにリセットして同様にメッセージを送信します。
  
[[Image:Plugin_deploy2.png]]
+
playerがキーに含まれていない場合、
 +
今処理を'''player'''が初めて行った当処理の実行とみなして、
 +
HashMapへプレイヤーと値のペアを追加し、メッセージを送信します。
  
 +
=== まだあるHashMapsの用途 ===
  
プラグインのコードとplugin.ymlに不備が無ければ、エクスポートしたJarファイルはすぐにBukkitプラグインとして動作します。Jarファイルを、Bukkitサーバの'''"plugins"'''フォルダの中に配置し、Bukkitサーバを起動し、プラグインの動作確認をしてみましょう。なお、ローカルマシン上で起動するBukkitサーバへは、Minecraftクライアントのマルチプレイヤーサーバの接続先IPアドレスに'''"localhost"'''を指定して接続する事でログインできます。
+
HashMap(よく似たMapも)は、データの集合体です。
 +
集合の中から、一意な'''キー'''を使って対応する''''''を参照する処理を、高速・効率的に行います。
  
もしプラグインが上手く動かず、それがどうしても自分で解決できない場合は、当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ユーザ・開発者に貢献する事ができます。
+
これに類する処理は付き物ですが、Mapはこれらを解決します。
 +
Mapの理想的な用途の例はいくつもあります。
 +
下記を見れば分かる通り、
 +
用途は個々のプレイヤーに対応したデータである事にこだわる必要もありません。
 +
必要に応じて、対象を置き換えて他の用途にも活用して下さい。
  
== ヒントとノウハウ ==
+
==== 値の参照 ====
CraftBukkit APIは、すばらしい機能をたくさん提供しています。
 
下記に、面白い効果を実現するコードを示します。
 
  
=== プレイヤーに着火する ===
 
下記は、指定されたプレイヤーに着火するサンプルです。例えば '''/ignite Notch''' と実行すると、Notchが燃えます!
 
 
<blockquote><source lang="java">
 
<blockquote><source lang="java">
@Override
+
public Map<String, Integer> wool_colors = new HashMap<String, Integer>();
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>
 
  
上記の拡張版として、プレイヤーを爆死させる処理を下記に示します:  
+
// Run this on plugin startup (ideally reading from a file instead of copied out row by row):
<blockquote><source lang="java">
+
wool_colors("orange", 1);
@Override
+
wool_colors("magenta", 2);
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
+
wool_colors("light blue", 3);
    if(cmd.getName().equalsIgnoreCase("KillPlayer")){
+
  ...
        Player target = Bukkit.getPlayerExact(args[0]);
+
wool_colors("black", 15);
        // 対象プレイヤーがオンラインかどうかを確認します。
 
        if (target == null) {
 
            sender.sendMessage(args[0] + " というプレイヤーは見つかりません!");
 
            return true;
 
        }
 
        float explosionPower = 4F; // 爆発の大きさです。1Fでガストの火球、3Fでクリーパーの爆発、4FでTNTの爆発 に相当します。
 
  
        // 爆発を起こしつつ、対象に1000ダメージを与えます。
+
// Run this in response to user commands - turn "green" into 13
        target.getWorld().createExplosion(target.getLocation(), explosionPower);
+
int datavalue = 0;
        target.damage(1000);
+
if (wool_colors.containsKey(argument)
        return true;
+
    datavalue = wool_colors.get(argument);
    }
+
else {
     return false;
+
    try { datavalue = Integer.parseInt(argument); }
 +
     catch (Exception e) { ; }
 
}
 
}
 
</source> </blockquote>
 
</source> </blockquote>
  
=== 爆発を起こす ===
+
=== Saving/Loading a HashMap  ===
このコードは、TNTの爆発と同様の音とヴィジュアルを再現します。
+
HashMapがどのように動作するかを学びましたが、
これは、TNTの爆発効果は無効化しつつ、音とヴィジュアル効果を発生させる処理に転用できます。
+
次は恐らく、HashMapのデータを読み書きする方法を知りたくなるかと思います。
<blockquote><source lang="java">
+
もし次のような要求があれば、HashMapが適しています。
@EventHandler
+
* 管理者に手作業でデータ編集をさせたくない
public void onExplosionPrime(ExplosionPrimeEvent event) {
+
* バイナリ形式でデータを保管したい(YAMLで扱うには複雑すぎるデータ)
    Entity entity = event.getEntity();
+
* ブロック等のオブジェクトにの引き当てに、任意の文字列を使いたい
  
    // このイベントは、点火されたTNTにより発生したのかどうかを確認します。
+
これらは、HasMapの定義(HashMap&lt;Player,Boolean&gt;)を、任意のデータ型に変更することでシンプルに行えます。
    // (つまり、TNTの爆発はこれで無効化されますが、クリーパーの爆発は無効化されません)
+
引き続き、前節で出てきた"pluginEnabled"インスタンスの例を使って説明をします。
    if (entity instanceof TNTPrimed) {
+
このコードは、与えられたパスに存在するファイルに対して、HashMapの値を保存します。
        event.setCancelled(true); // イベントをキャンセルして、爆発を無かったことにする
+
<blockquote><source lang="java">public void save(HashMap<Player,Boolean> pluginEnabled, String path)
        entity.getWorld().createExplosion(entity.getLocation(), 0); // 偽物の爆発を発生させる
+
{
    }
+
try{
}
+
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
</source></blockquote>
+
oos.writeObject(pluginEnabled);
 +
oos.flush();
 +
oos.close();
 +
//Handle I/O exceptions
 +
}catch(Exception e){
 +
e.printStackTrace();
 +
}
 +
}</source> </blockquote>  
  
=== プレイヤーを非表示にする ===
 
  
これは指定したプレイヤーから自分を非表示にするサンプルです。
+
You can see it's really easy. Loading works very very similar but we use ObjectInputStream instead of ObjectOutputStream ,FileInputStream instead of FileOutputStream,readObject() instead of writeObject() and we return the HashMap.
指定したプレイヤー以外のプレイヤーからは、自分が見えたままになっています。
+
<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>
  
<blockquote>
+
You can use this "API" for saving/loading HashMaps, ArrayLists, Blocks, Players... and all Objects you know&nbsp;;) . Please credit me (Tomsik68) if you use this in your plugin.
<source lang="java">
+
<blockquote><source lang="java">/** SLAPI = Saving/Loading API
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
+
* API for Saving and Loading Objects.
    if(cmd.getName().equalsIgnoreCase("HideMe") && args.length == 1) {
+
* @author Tomsik68
 
+
*/
        // コマンド実行者がプレイヤーかどうかを確認します。
+
public class SLAPI
        if (!(sender instanceof Player)) {
+
{
            sender.sendMessage("このコマンドはゲーム内から実行してください!");
+
public static void save(Object obj,String path) throws Exception
            return true;
+
{
        }
+
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
        // sender instanceof Player の検査が終わっているので、Playerクラスへ安全にキャストできます。
+
oos.writeObject(obj);
        Player s = (Player) sender;
+
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>
 +
Example implementation of this API: '''I'm skipping some part of code in this source'''
 +
<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>
 +
A minor note about this SLAPI and Java's ObjectOutputStream class. This will work un-modified if you are saving almost all well-known Java types like Integer, String, HashMap. This will work un-modified for some Bukkit types as well. If you're writing your own data object classes, and you may want to save their state using this technique, you should read up on Java's Serializable interface. It's easy to add to your code, and it will make your data persistent with very little work on your part. No more parsing!
  
        // コマンドのパラメータに、対象のプレイヤーが指定されているかどうかを確認します。
+
== Map・Set・Listの応用 ==
        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チュートリアル|*]]
 

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

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

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

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