提供: Minecraft Modding Wiki
この編集を取り消せます。
下記の差分を確認して、本当に取り消していいか検証してください。よろしければ変更を保存して取り消しを完了してください。
最新版 | 編集中の文章 | ||
3行目: | 3行目: | ||
== 始めに == | == 始めに == | ||
+ | :'''Note''': 当ページの内容は、[[Bukkit用MODの作成方法]]の内容とは異なるアプローチによるBukkitプラグイン開発手法の解説です。 | ||
:'''重要''': 当ページには、原文の翻訳ではない、日本語読者向けの独自の記述を行っている箇所が少量あります。そのような内容を含む節ではNoteにて通知しています。 | :'''重要''': 当ページには、原文の翻訳ではない、日本語読者向けの独自の記述を行っている箇所が少量あります。そのような内容を含む節ではNoteにて通知しています。 | ||
:'''重要''': 当ページでは、技術的な解説やたとえ話の原意を理解し易くするために、一部意訳を行っています。 | :'''重要''': 当ページでは、技術的な解説やたとえ話の原意を理解し易くするために、一部意訳を行っています。 | ||
:'''重要''': 当ページは訳文であるため、訳文自体に対する文責は原著者にありません。当ページの内容を受けて原著者へ何らかのアクションを行う場合、その点を考慮して必ず原文にも目を通して下さい。 | :'''重要''': 当ページは訳文であるため、訳文自体に対する文責は原著者にありません。当ページの内容を受けて原著者へ何らかのアクションを行う場合、その点を考慮して必ず原文にも目を通して下さい。 | ||
− | + | このチュートリアルは、[http://forums.bukkit.org/threads/plugin-development-a-huge-tutorial-status-under-development.15167/ こちらのスレッド]からの転載です。当ページの原著者は、'''Adamki11s'''氏ですが、多くの編集を受けて今に至ります。 | |
− | + | [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の一次配布元が提供するパッケージではありません。 | ||
− | + | どのIDEも気に入らなければ、BlueJも利用できます。<br/> | |
− | + | プラグイン開発が初めてであるなら、とにかく一度Eclipseを使ってみて、<br/> | |
+ | 良ければNetBeansを試してみると良いでしょう。 | ||
− | + | [http://www.okapiproject.com/java/java_tools/eclipse/index.html 優良なEclipseの解説]。お勧めのものより古いバージョンの解説ですが、ほぼ同様です。 | |
− | |||
− | |||
− | |||
− | |||
== Plugin用プロジェクトを始めるために == | == Plugin用プロジェクトを始めるために == | ||
=== プロジェクトの作成 === | === プロジェクトの作成 === | ||
− | + | 始める前にEclipseのワークスペースとファイルの設定を行う必要があります。<br /> | |
− | + | Eclipseを起動し、ファイル>新規>Java プロジェクトと選択して下さい。<br /> | |
− | [[Image: | + | [[Image:NewJavaProject.png]] |
− | + | プロジェクト名を入力し、新規プロジェクトウィザード画面の指示に従って、プロジェクトを作成してください。 | |
− | + | === Bukkit APIの参照 === | |
+ | 開発を始める前にbukkit APIライブラリをプロジェクトに追加する必要があります。 | ||
+ | 使用したい他のAPIも同じように追加することが可能です。 | ||
− | + | 最新のBukkit APIはここからダウンロードが可能です。 {{BDownload}} | |
− | + | 画面の左手にあるパッケージエクスプローラの(先ほど名前を付けた)プロジェクトを右クリックし、プロパティーを選択します。<br /> | |
+ | 開いた画面のJavaビルド・パスを選択し、ライブラリータブの中から、外部 Jar 追加ボタンを押して、ダウンロードしたBukkit APIを指定します。<br /> | ||
− | + | [[Image:BuildPathPic.png|800px]] | |
− | |||
− | + | === 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"フォルダを右クリックし、''新規 > パッケージ''を選択します: | ||
+ | <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 | ||
− | |||
</blockquote> | </blockquote> | ||
− | + | ベースとなるパッケージ名(例: '''com.github.<username>''')を決めたら、末尾にプラグイン名称(例: '''TestPlugin''')を追加したものを、パッケージ名称としましょう。つまり、'''com.github.<username>.TestPlugin'''になります。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | プラグイン開発のためのプロジェクトに、クラスファイルを追加する準備ができました。メインのクラス名は、プラグインの名称と同一にすると良いでしょう(つまりこの例では'''TestPlugin.java'''が良いでしょう)。パッケージを右クリックし、 ''新規 > クラス''を選択して追加します。 | |
− | |||
− | |||
− | + | 以上で、プロジェクトとメインファイルの準備ができました。次に、bukkitが'''[[Plugin_YAML|plugin.yml]]'''ファイルを参照できるようにします。このファイルは、Javaプログラムがプラグインとして動作するための必須情報を記述するファイルであり、これ無しではプラグインは動作しません。'''ソースフォルダ以外'''(この例では"src"以外)のプロジェクトフォルダを右クリックし、''新規 > ファイル''を選択し、ファイル名を"'''plugin.yml'''"としてOKを押下します。Eclipseは、作成した空の'''plugin.yml'''を、標準テキストエディタで開きます。 | |
− | + | ''(Hint: ワークスペースのファイル構成を維持したければ、テキストエディタを閉じて'''plugin.yml'''ファイルをワークスペースへドラッグ・ドロップして移動させてください。当ファイルに必須の内容は、メインクラスへの参照と、プラグインのコマンドの情報です。最もシンプルな'''plugin.yml'''は、次のような内容になります : | |
+ | <blockquote><code><pre>name: <PluginName> | ||
+ | main: <packagename>.<MainClass> | ||
+ | version: <Version Number></pre></code></blockquote> | ||
− | + | :'''Note''': メインクラスへの参照として記入されたパッケージ名が、'''<pluginname>.<pluginname>'''のように、パッケージ名がプラグイン名を含んでいる事がありますが、間違いではありません。 | |
− | + | :'''Note''': クラスの名称には、大文字小文字の区別があることに留意してください。 | |
− | |||
− | |||
− | '' | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
== onEnable()メソッドとonDisable()メソッド == | == onEnable()メソッドとonDisable()メソッド == | ||
− | + | このファンクションは、プラグインが有効/無効になったときに呼ばれます。<br /> | |
デフォルトでは、プラグインは自動的に読み込まれたときに、イベントを登録やデバッグ出力を行うことが出来ます。<br /> | デフォルトでは、プラグインは自動的に読み込まれたときに、イベントを登録やデバッグ出力を行うことが出来ます。<br /> | ||
− | onEnable() | + | onEnable()は、プラグインがBukkitから読み込まれるときに最初に呼ばれ、プラグインを実行するために必須です。<br /> |
=== onEnable()とonDisable()の基本 === | === onEnable()とonDisable()の基本 === | ||
前のセクションで作成したメインクラスに、onEnable()とonDisable()のメソッドを作成します。<br /> | 前のセクションで作成したメインクラスに、onEnable()とonDisable()のメソッドを作成します。<br /> | ||
<blockquote><source lang="java"> | <blockquote><source lang="java"> | ||
− | + | public void onEnable(){ | |
− | + | } | |
− | |||
− | |||
− | + | public void onDisable(){ | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
</source></blockquote> | </source></blockquote> | ||
− | + | 現時点では、このメソッドは何も行いません。また、エラーが発生する事にも気付くでしょう。このエラーは、メインクラスが、プラグインの機能を継承(extends)する必要がある事を示しています。当クラスの定義文を、下記のように変更しましょう。 | |
+ | |||
+ | これを・・・ | ||
+ | <blockquote><source lang="java">Class <classname> {}</source></blockquote> | ||
− | = | + | このように変更する。 |
+ | <blockquote><source lang="java">Class <classname> extends JavaPlugin {}</source></blockquote> | ||
+ | |||
+ | すると、前述の追加コードが赤の下線でハイライト表示され、何かが間違っていることを通知してくるはずです。このハイライト部にマウスを重ねると、下記のようなポップアップが表示されるので、「'JavaPlugin'をインポートします(org.bukkit.plugin.java)」を選択します。 | ||
+ | |||
+ | <blockquote>[[ファイル:to_need_import_package.png]]</blockquote> | ||
− | + | もしくは、 | |
− | + | <blockquote><source lang="java">import org.bukkit.plugin.java.JavaPlugin;</source></blockquote> | |
+ | をコード上部の定義部に記述する事でも同様の事が行えます。 | ||
− | + | === Loggerを利用したメッセージ出力 === | |
− | |||
− | |||
− | + | 始めに、プラグインが実際に動作するかどうかを確認するため、シンプルなメッセージをサーバコンソールに表示させてみましょう。この処理として、ログ出力機能(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 | + | package me.<yourname>.<pluginname>; |
+ | import java.util.logging.Logger; | ||
import org.bukkit.plugin.java.JavaPlugin; | import org.bukkit.plugin.java.JavaPlugin; | ||
− | public class | + | public class <classname> extends JavaPlugin { |
− | + | Logger log; | |
− | + | ||
− | + | public void onEnable(){ | |
− | + | log = this.getLogger(); | |
+ | log.info("Your plugin has been enabled!"); | ||
+ | } | ||
− | + | public void onDisable(){ | |
− | + | log.info("Your plugin has been disabled."); | |
− | + | } | |
− | |||
} | } | ||
</source></blockquote> | </source></blockquote> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
== イベントとリスナ == | == イベントとリスナ == | ||
365行目: | 195行目: | ||
さて、どのようにイベントを登録しいつ発生するかについて理解しました。 | さて、どのようにイベントを登録しいつ発生するかについて理解しました。 | ||
しかし、コマンドを利用して何かを起こしたい場合はどのようなデータ型を利用すればよいのでしょうか?それには、onCommand()メソッドを利用します。 | しかし、コマンドを利用して何かを起こしたい場合はどのようなデータ型を利用すればよいのでしょうか?それには、onCommand()メソッドを利用します。 | ||
− | + | このメソッドは、プレイヤーが文字"/"を入力する度に実行されます。 | |
+ | 具体的には、"/do something"とコマンドを実行した場合にonCommand()が呼び出されます。 | ||
今のところ、onCommand()はまだプログラムしていないので何も起こらないでしょう。 | 今のところ、onCommand()はまだプログラムしていないので何も起こらないでしょう。 | ||
373行目: | 204行目: | ||
具体的には、"give"コマンドは既にいくつかのプラグインで利用されています。 | 具体的には、"give"コマンドは既にいくつかのプラグインで利用されています。 | ||
もし独自に"give"コマンドを実装した場合は、 | もし独自に"give"コマンドを実装した場合は、 | ||
− | "give"コマンドを実装している他のプラグインとの互換性は無くなります。 | + | "give"コマンドを実装している他のプラグインとの互換性は無くなります。 |
− | onCommand() | + | 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となる) | + | * 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: | + | 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 - | + | * usage - onCommand()がfalseを返した際に、コマンド実行ユーザに向けて表示されるメッセージの内容。コマンドの使い方を明解に書いてください。 |
− | * permission - | + | * permission - 当コマンドの動作に必要なプラグインの設定を、コマンド実行ユーザに知らせるメッセージ。(主に、コマンド実行に必要なpermissionを書きます) |
− | * permission-message - | + | * permission-message - コマンド実行権限を持たないユーザがコマンドを実行した場合に、その旨を同ユーザに知らせるメッセージ。 |
</blockquote> | </blockquote> | ||
− | + | :'''Note''': ymlファイルには、1個タブを2個のスペースで記述する必要があります。タブ文字は構文エラーとなるため利用できません。 | |
− | |||
− | |||
− | :'''Note''': | ||
=== コンソールコマンドとプレイヤーコマンド === | === コンソールコマンドとプレイヤーコマンド === | ||
440行目: | 268行目: | ||
記述例: | 記述例: | ||
<blockquote><source lang="java"> | <blockquote><source lang="java"> | ||
− | |||
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; |
− | // | + | 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")) { | ||
− | + | if (player == null) { | |
− | if ( | + | sender.sendMessage("this command can only be run by a player"); |
− | sender.sendMessage(" | ||
} else { | } else { | ||
− | + | // 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() { | ||
− | // | + | // .... |
− | + | ||
− | getCommand("basic").setExecutor( | + | 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 | + | |
− | |||
public MyPluginCommandExecutor(MyPlugin plugin) { | public MyPluginCommandExecutor(MyPlugin plugin) { | ||
this.plugin = plugin; | this.plugin = plugin; | ||
} | } | ||
− | + | ||
@Override | @Override | ||
− | public boolean onCommand(CommandSender sender, Command | + | public boolean onCommand(CommandSender sender, Command 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){ |
− | + | if ((sender instanceof Player)) { | |
− | public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){ | + | // doSomething |
− | + | } else { | |
+ | sender.sendMessage(ChatColor.RED + "You must be a player!"); | ||
+ | return false; | ||
+ | } | ||
Player player = (Player) sender; | Player player = (Player) sender; | ||
− | |||
− | |||
− | |||
− | |||
return false; | return false; | ||
− | + | }</source> </blockquote> | |
− | |||
− | |||
− | </source> </blockquote> | ||
− | |||
− | |||
=== コマンドのパラメタ長をチェックする === | === コマンドのパラメタ長をチェックする === | ||
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/> | |
+ | 必ずそのプレイヤーがオンラインである必要があります。<br/> | ||
オンラインであるかどうかをチェックして下さい。<br/> | オンラインであるかどうかをチェックして下さい。<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])); |
− | + | if (other == null) { | |
− | public boolean onCommand(CommandSender sender, Command | + | sender.sendMessage(ChatColor.RED + args[0] + " is not online!"); |
− | + | return false; | |
− | + | } | |
return false; | return false; | ||
− | + | }</source> </blockquote> | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | </source> </blockquote> | ||
オンラインではないプレイヤーのインスタンスに操作を加えたい場合は、一般的には<code>'''OfflinePlayer'''</code>クラスを利用して行う事ができます。 | オンラインではないプレイヤーのインスタンスに操作を加えたい場合は、一般的には<code>'''OfflinePlayer'''</code>クラスを利用して行う事ができます。 | ||
586行目: | 402行目: | ||
== 権限 == | == 権限 == | ||
− | + | Bukkitのパーミッション(権限)APIの利用は、簡単ではありません。 | |
− | + | プレイヤーが特定の権限を持っているかどうかを調べる処理は次のようになります: | |
+ | <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"> | + | そもそも、権限にはグルーピングの概念は不要なのです。 |
+ | |||
+ | 元々、グループの主な用途はチャットメッセージをフォーマッティングする事にありました。<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")) { | |
− | + | player.sendMessage("You were sent a message"); | |
− | + | } | |
− | |||
− | |||
− | } | ||
− | + | }</source> </blockquote> | |
− | |||
− | + | さて、依然として<br/> | |
− | + | 「グループを利用せずに、複数のプレイヤーに権限をセットする良い方法は何なのか?」が疑問かと思いますが・・・<br/> | |
+ | BukkitのAPI自体は、グループの概念を提供していません。<br/> | ||
+ | グループの概念を利用するためには、permissionsBukkitのような<br/> | ||
+ | グループ権限の機能を提供するプラグインを利用する事になります。<br/> | ||
+ | とどのつまり、'''このAPIはインタフェース(Interface)であり、実装(Implementation)ではない'''のです。 | ||
=== 権限の設定 === | === 権限の設定 === | ||
− | + | 権限を利用して細かな制御を行いたい場合は、<br/> | |
− | ''plugin.yml'' | + | デフォルト権限と、子権限の設定を''plugin.yml''に追記する事を検討して下さい。<br/> |
+ | この2種類の設定は、オプション(必須ではなく完全に任意で利用される)項目ではありますが、お勧めします。<br/> | ||
下記は、''plugin.yml''の最後に追加する形で設定する権限です: | 下記は、''plugin.yml''の最後に追加する形で設定する権限です: | ||
− | <blockquote> | + | <blockquote><code><source lang="yaml">permissions: |
− | < | ||
− | 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> |
− | </ | + | </code></blockquote> |
− | </blockquote> | ||
655行目: | 485行目: | ||
==== 子権限 ==== | ==== 子権限 ==== | ||
− | + | 恐らく今までに、''* 権限''を利用してサブ権限を割り当てた事があると思います。<br/> | |
これは、変更されたBukkit APIと、子権限の定義によって実現した機能であり、<br/> | これは、変更されたBukkit APIと、子権限の定義によって実現した機能であり、<br/> | ||
高い柔軟性を提供しています。<br/> | 高い柔軟性を提供しています。<br/> | ||
下記はその実装例です: | 下記はその実装例です: | ||
− | <blockquote> | + | <blockquote><source lang="yaml">permissions: |
− | < | ||
− | 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> | ||
− | + | ||
''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) { |
− | + | Location loc = evt.getPlayer().getLocation(); // プレイヤーからその位置を得る | |
− | public void onPlayerMove(PlayerMoveEvent evt) { | + | World w = loc.getWorld(); // 位置からワールドを得る |
− | + | loc.setY(loc.getY() + 5); // 位置のY座標を+5する | |
− | + | Block b = w.getBlockAt(loc); // ワールド上の指定位置のブロックを得る | |
− | + | b.setTypeId(1); // ブロックのタイプIDに1をセットする | |
− | |||
− | |||
− | |||
− | |||
− | |||
}</source></blockquote> | }</source></blockquote> | ||
このコードの処理は、プレイヤーが移動する(=PlayerMoveイベントが発生する)度に、<br/> | このコードの処理は、プレイヤーが移動する(=PlayerMoveイベントが発生する)度に、<br/> | ||
− | + | プレイヤーの5ブロック上方のブロックがStone(TypeIdが1のブロック)に変化する動作として見えるでしょう。<br/> | |
ブロック取得までの流れは・・・ | ブロック取得までの流れは・・・ | ||
# 取得したプレイヤーの位置から、ワールドを取得する | # 取得したプレイヤーの位置から、ワールドを取得する | ||
# 位置のY座標を+5する | # 位置のY座標を+5する | ||
− | # <code>''' | + | # <code>'''w.getBlockAt(loc);'''</code>で、ワールドにおける位置に存在するブロックを得る |
となります。<br/> | となります。<br/> | ||
位置(''loc'')中のブロックインスタンス(''b'')に対して、<br/> | 位置(''loc'')中のブロックインスタンス(''b'')に対して、<br/> | ||
− | + | IDを変えたり、ブロックデータ自体を変更する事もできます。 | |
− | |||
− | + | また、ブロックの種類を表すデータは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 |
− | + | World world = point.getWorld(); | |
− | + | ||
− | + | int x_start = point.getBlockX(); // Set the startpoints to the coordinates of the given location | |
− | + | 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 z_start = point.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; | |
− | + | ||
− | + | for(int x_operate = x_start; x_operate <= x_length; x_operate++){ | |
− | + | // Loop 1 for the X-Dimension "for x_operate (which is set to x_start) | |
− | + | //do whats inside the loop while x_operate is | |
− | + | //<= x_length and after each loop increase | |
− | + | //x_operate by 1 (x_operate++ is the same as x_operate=x_operate+1;) | |
− | + | for(int y_operate = y_start; y_operate <= y_length; y_operate++){// Loop 2 for the Y-Dimension | |
− | + | for(int z_operate = z_start; z_operate <= z_length; z_operate++){// Loop 3 for the Z-Dimension | |
− | + | ||
− | + | Block blockToChange = world.getBlockAt(x_operate,y_operate,z_operate); // get the block with the current coordinates | |
− | + | blockToChange.setTypeId(34); // set the block to Type 34 | |
− | + | } | |
− | + | } | |
− | + | } | |
− | + | }</source></blockquote> | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | </source></blockquote> | ||
このメソッドは、辺の長さと開始点の指定を受けて、任意のサイズと位置を持つ直方体を生成する処理です。<br/> | このメソッドは、辺の長さと開始点の指定を受けて、任意のサイズと位置を持つ直方体を生成する処理です。<br/> | ||
ブロックの削除処理の場合も、このロジックを真似て同様のアルゴリズムで実装する事ができます。<br/> | ブロックの削除処理の場合も、このロジックを真似て同様のアルゴリズムで実装する事ができます。<br/> | ||
− | + | ただし、セットするブロックのIDはゼロ(=air)になりますね。 | |
== プレイヤーインベントリの操作 == | == プレイヤーインベントリの操作 == | ||
804行目: | 622行目: | ||
=== エンチャント === | === エンチャント === | ||
− | + | アイテムに対するエンチャントに触れる前に、[http://ja.minecraftwiki.net/wiki/Data_values Item Code] と [http://ja.minecraftwiki.net/wiki/Enchanting EID]を見てから以下の解説を読んでください。 | |
− | + | エンチャントは、Enchantmentクラスが受け持っている機能ですが、<br/> | |
− | + | Enchantmentクラス自体は抽象クラスであるため、インスタンス化('''new Enchantment()''')出来ません。 | |
+ | エンチャントはEnchantmentWrapperクラスから利用する必要があるからです。 | ||
− | + | 元々エンチャントが出来ないアイテムに対して、独自にエンチャントを付与する処理は書けません。<br/> | |
+ | Bukkitサーバ自体が、エンチャント不可能なアイテムに対して、エンチャント情報を付与する仕組みを備えていないからです。<br/> | ||
+ | つまり、Fire AspectedなStick(火のエンチャントが付いた木の棒)はあり得ません。 | ||
<blockquote><source lang="java"> | <blockquote><source lang="java"> | ||
− | // | + | int itemCode = 280; // StickのItemID |
− | + | int effectId = 20; // 効果Fire AspectのEID | |
+ | int enchantmentLevel = 100; // エンチャントのレベル | ||
− | // | + | ItemStack myItem = new ItemStack(itemCode); // 木の棒のインスタンスを生成する |
− | myItem.addEnchantment( | + | Enchantment myEnchantment = new EnchantmentWrapper(effectId); // エンチャント効果のインスタンスを生成する |
+ | myItem.addEnchantment(myEnchantment, enchantmentLevel); // 木の棒にFireAspectを付与する(ただし付与は成功しない) | ||
</source></blockquote> | </source></blockquote> | ||
− | + | == 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; |
− | + | 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()の中に、前述の説明と同様にプレイヤーの状態に応じて、プレイヤー名を送信する処理を記述します。 | ||
− | // | + | onCommand()には下記を記述します。 |
− | + | 内部で呼んでいるメソッドの名前は変えても良いですが、意味が通じるシンプルなものにして下さい。 | |
− | // | + | <blockquote><source lang="java">Player player = (Player) sender; |
− | + | 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'''のどちらなのかを調べています。 | ||
− | + | playerがキーに含まれており、かつ | |
+ | 値がtrueであれば、falseにリセットしてプレイヤーへメッセージを送信します。 | ||
+ | falseであれば、trueにリセットして同様にメッセージを送信します。 | ||
− | + | playerがキーに含まれていない場合、 | |
− | + | 今処理を'''player'''が初めて行った当処理の実行とみなして、 | |
− | + | HashMapへプレイヤーと値のペアを追加し、メッセージを送信します。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | === まだあるHashMapsの用途 === | |
− | |||
− | |||
− | |||
− | |||
− | + | HashMap(よく似たMapも)は、データの集合体です。 | |
− | + | 集合の中から、一意な'''キー'''を使って対応する'''値'''を参照する処理を、高速・効率的に行います。 | |
− | |||
− | |||
− | |||
− | + | これに類する処理は付き物ですが、Mapはこれらを解決します。 | |
+ | Mapの理想的な用途の例はいくつもあります。 | ||
+ | 下記を見れば分かる通り、 | ||
+ | 用途は個々のプレイヤーに対応したデータである事にこだわる必要もありません。 | ||
+ | 必要に応じて、対象を置き換えて他の用途にも活用して下さい。 | ||
− | + | ==== 値の参照 ==== | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | == | + | <blockquote><source lang="java"> |
+ | public Map<String, Integer> wool_colors = new HashMap<String, Integer>(); | ||
− | + | // 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); | ||
− | === | + | // 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データのセーブ/ロード === |
− | * | + | HashMapがどのように動作するかを学びましたが、 |
+ | 次は恐らく、HashMapのデータを読み書きする方法を知りたくなるかと思います。 | ||
+ | もし次のような要求があれば、HashMapが適しています。 | ||
+ | * 管理者に手作業でデータ編集をさせたくない | ||
+ | * バイナリ形式でデータを保管したい(YAMLで扱うには複雑すぎるデータ) | ||
+ | * ブロック等のオブジェクトの引き当てに、任意の文字列を使いたい | ||
− | == | + | これらは、HasMapの定義(HashMap<Player,Boolean>)を、任意のデータ型に変更することでシンプルに行えます。 |
+ | 引き続き、前節で出てきた"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)の著作である事を明記して下さい。 | |
− | + | <blockquote><source lang="java">/** SLAPI = Saving/Loading API | |
− | + | * API for Saving and Loading Objects. | |
− | + | * @author Tomsik68 | |
− | + | */ | |
− | + | public class SLAPI | |
− | + | { | |
− | + | public static void save(Object obj,String path) throws Exception | |
− | + | { | |
− | + | 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インタフェースについて読んで下さい。それにより、簡単に少量の変更で動かす事ができます。コードを丁寧に解析する必要はありません。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | == Map・Set・Listの応用 == | |
+ | HashMapクラスのもうひとつの機能は、Javaの持つ多くのデータ構造を提供する事にあります。<br/> | ||
+ | しかし、クラスの種類によってMapは時に適切ではないケースも存在します。<br/> | ||
+ | こちらに、そのようなケースについて詳しく記述するためのページとして、[[Javaのデータ構造クラス]]を用意しています。 | ||
== データベース == | == データベース == | ||
940行目: | 874行目: | ||
== プラグインの配布 == | == プラグインの配布 == | ||
+ | :'''Note''': 当セクションは、日本語圏の読者向けの資料として外部の紹介サイトを独自に追加しています。 | ||
− | + | プラグインのコーディング後に行うべき事は、サーバへインストールするためのJarファイルを、ソースコードから作成する事です。これはどのように行えば良いでしょう? | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
+ | # まずは、[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を例にした場合、プラグインのプロジェクトを選択した状態で、'''パッケージエクスプローラ上のプロジェクト'''の右クリックか、'''ファイル > エクスポート'''の選択で表示される機能) | ||
+ | # 開いたエクスポート画面上で'''"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行目: | ||
=== プレイヤーに着火する === | === プレイヤーに着火する === | ||
− | + | プレイヤーを燃やすコマンド: | |
<blockquote><source lang="java"> | <blockquote><source lang="java"> | ||
− | + | 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")){ |
− | + | Player s = (Player)sender; | |
− | + | Player target = s.getWorld().getPlayer(args[0]); // 誰がコマンドを実行したか | |
− | if (cmd.getName().equalsIgnoreCase("ignite")) { | + | // "/ignite notch"コマンドが打たれた場合、targetインスタンスは"notch"を対象とします。 |
− | + | // Note: 最初の引数は[0]で、[1]ではないので、arg[0]がプレイヤー名を格納しています。 | |
− | + | target.setFireTicks(10000); | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | Player target = | ||
− | |||
− | // | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | // | ||
− | target.setFireTicks( | ||
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]); | |
− | + | target.setHealth(0); | |
− | |||
− | |||
− | |||
− | |||
− | Player target = | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | target. | ||
− | |||
} | } | ||
return false; | return false; | ||
− | } | + | }</source></blockquote> |
− | </source></blockquote> | ||
上記の拡張版として、プレイヤーを爆死させる処理を下記に示します: | 上記の拡張版として、プレイヤーを爆死させる処理を下記に示します: | ||
<blockquote><source lang="java"> | <blockquote><source lang="java"> | ||
− | + | float explosionPower = 4F; //This is the explosion power - TNT explosions are 4F by default | |
− | + | Player target = sender.getWorld().getPlayer(args[0]); | |
− | + | target.getWorld().createExplosion(target.getLocation(), explosionPower); | |
− | + | target.setHealth(0); | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
</source> </blockquote> | </source> </blockquote> | ||
=== 爆発を起こす === | === 爆発を起こす === | ||
− | + | このコードは、TNTやクリーパーの爆発と同様の音とヴィジュアルを再現します。 | |
これは、TNTの爆発効果は無効化しつつ、音とヴィジュアル効果を発生させる処理に転用できます。 | これは、TNTの爆発効果は無効化しつつ、音とヴィジュアル効果を発生させる処理に転用できます。 | ||
<blockquote><source lang="java"> | <blockquote><source lang="java"> | ||
− | + | public void onExplosionPrime(ExplosionPrimeEvent event){ | |
− | public void onExplosionPrime(ExplosionPrimeEvent event) { | + | |
− | + | Entity entity = event.getEntity(); | |
− | + | ||
− | + | if (entity instanceof TNTPrimed){ | |
− | + | TNTPrimed tnt = (TNTPrimed) entity; | |
− | + | event.getEntity().getWorld().createExplosion(tnt.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=" | + | <blockquote><source lang="bash">mvn archetype:generate -DarchetypeCatalog=local</source></blockquote> |
− | |||
− | |||
− | + | プロンプトに表示されたリストから下記を選択します: | |
− | + | <blockquote>uk.co.dbyz:bukkitplugin (bukkitplugin) </blockquote> | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | GroupIDには筆頭(プラグインのトップ階層)としたいJavaパッケージ名を指定し、ArtifactIDにはパッケージ名の末端の名称(Jarファイルのような成果物の名称として利用されます)を入力します。そして、確認メッセージに'''Y<enter>'''で応答します。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | = | + | 例: |
+ | <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/<package>'''フォルダに存在する'''<archetypeid>CommandExecuter.java'''ファイルを開き、コード<source lang="java">//Do Something</source>の部分に書きのコードを記述します: |
− | <source lang="java"> | + | <blockquote><source lang="java"> |
− | + | Player player = (Player) sender; | |
− | + | player.setHealth(1000); | |
+ | </source></blockquote> | ||
− | + | ベースのフォルダに移動して下記を実行します: | |
− | + | <blockquote><source lang="bash"> | |
− | + | mvn clean package | |
− | + | </source></blockquote> | |
− | + | ダウンロード処理が走りますが、他の作業を並行して行っても大丈夫です。 | |
− | + | ''clean package''処理が完了すると、targetフォルダの中に'''<archetypeid>-1.0-SNAPSHOT.jar'''ファイルが生成されます(これがビルドされたプラグインのJarファイルです)。このファイルをBukkitのpluginsフォルダへコピーして、Bukkitサーバを起動して下さい。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | </source> | ||
− | </blockquote> | ||
+ | ゲーム内で'''/<archetypeid>'''コマンドを実行すると、そのプレイヤーの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''': 当ページは訳文であるため、訳文自体に対する文責は原著者にありません。その点を考慮して必ず原文にも目を通してから質問して下さい。 | ||
− | |||
− |