74回生 日野
部誌サンプルのタイトルが「Mineraftのサーバー管理を楽で便利なものにしてみる話」になっていますが、正しくは「マインクラフトのサーバー管理を楽で便利なものにしてみる話」です。お詫びして訂正いたします。
初めまして。74回の日野と申します。この度は第72回灘校文化祭・NPCAにお越しいただきありがとうございます。今回の部誌では元あるゲームを改造してみる、ということに関して部誌を書かせてもらいます。
改造するゲームは「マインクラフト」ってやつです。有名ですよね。中一の一年間これに溶かしました。成績が悲惨なことになるので熱中しすぎないようにしましょう。
で、本題の「どのように改造するのか」ですが、マインクラフトのサーバー管理に使える「プラグイン」というやつを作ってみます。(Mod作成だと思った人、ごめんなさい。どうあがいてもまだ作れそうにありません。)作るバージョンは1.11.2で行きます。
いきなりプラグインの話をしても戸惑う人もいるかもしれないので、ブラウザを例にとって説明してみます。
この部誌を手に取ってくれる人はたぶん「Firefox」というウェブブラウザーについて知っていると思います。このブラウザを使う人は多くいます。じゃあなぜこんなに多くの需要があるのか。答えは簡単で、拡張性が高いからです。(拡張機能自体はどのブラウザにも多少は存在するのですが、一番高いのがFirefoxです。)きっと拡張性がないFirefoxは誰からも使われないでしょう。
で、どのように拡張できるのかというと、
のように、様々です。たぶん僕が知らないだけで他にもいっぱいあるんでしょう。詳細はあんまり書きません。
そしてこの拡張機能を提供してくれるのがプラグインなのです。今はFirefoxを例にとって言いましたが、このように拡張性が高いものはブラウザだけではありません。たとえば、プログラムを書く環境であったり、動画や画像の編集ソフトがあったりします。ちなみに今自分はこの部誌をAtomというエディタで書いてるんですが、これも拡張機能つけて便利にしています。そして、一部のゲームはこれらと同様に拡張性が高いのです(拡張性が高いほかのゲームに関してはグーグル先生に聞いてみてください。たぶんいっぱいある)。基本的にシングルプレイメインのゲームは拡張性高いです。
まずは作成に必要な下準備です。
個人的にはEclipceがおすすめです。ここから有志のみなさんが作ってくれた日本語化プラグインつきでEclipceがインストールできます。(プラグインについては分かりますね?割といろいろなところで使われてます。)特に問題がなければ最新バージョンをインストールするのでOKです。
こればっかりはストアで買ってください。間違ってもWindows10版を買わないようにね。最近円に対応して3000円固定になりました。ここで購入できます。
ググってください。解説してるサイトはたくさんあります。解説してると量が膨大に...
まずはプロジェクトを作成します。ファイル→新規→プロジェクトを選択してください。javaプロジェクトを選択して、「次へ」をクリックします。プロジェクト名を設定して、完了をクリックしてください。ここではプロジェクト名をFirstPlugin
にします。
次に、マインクラフトサーバーのプラグインを作成するのに必要なjavaのビルドパスを通します。次に、パッケージ・エクスプローラーの中のFirstPluginを右クリックして、プロパティー→Javaのビルドパス→外部jarの選択をクリックします。出てきたところで使用するバージョン(ここでは1.11.2)のspigotサーバーを選択します。写真のようになってたら成功です。
図6.1: このように表示されていることを確認してください。
javaのビルドパスを通せたら、パッケージを作成します。ツリーを開いて、scrのフォルダを右クリックして、新規作成→パッケージを選択してください。パッケージの名前をfirst.plugin
にします。
次に、ソースコードを書く場所を作成します。さっき作ったパッケージを右クリックして、新規作成→クラスを選択します。名前はここではFirstPlugin
とします。ここで、スーパークラスの参照をクリックします。出てきたウィンドウのボックスにJavaPlugin
と入力して、でてきたリストの中のJavaPlugin-org.bukkit.plugin.java
を選択してOKをクリックします。
すると、画像のようにデフォルトでコードがいくつか生成されます。
図6.2: こんな感じです
次に、サーバー起動時と終了時の処理を実装します。ソース→メソッドのオーバーライド/実装を選択し、開いた画面からonDisable()
とonEnable()
を選択して、OKを押します。入力されたのがわかりましたか?
図6.3: onDisableとonEnableを実装したところ
これで何も機能を持たないプラグインが完成しました。
はい。簡単でしょ?
ちなみに、追加されたonDiable()とonEnable()はメソッドと呼ばれます。詳しくはググって。
ただこれだけでは動作しないので、プラグインの仕様書を作る必要があります。プロジェクトエクスプローラーのFirstPluginを右クリックして、新規→ファイルを選択します。ファイル名にplugin.ymlと入力して完了しましょう。テキストエディタが展開します。そうしたら、plugin.ymlの中に
name: FirstPlugin version: 1.0.0 main: first.plugin.FirstPlugin
と入力します。ここで間違えると動かないので注意しましょう。 plugin.ymlの中身についてはあとで解説してます。
図6.4: イメージ
こんな感じです。
次にプログラムをコンパイルします。FirstPluginを右クリック→エクスポートを選択してください。JARファイルを選択して次へをクリックします。
図6.5: JARファイルを選択
次に、この画面で.classpathと.projectからチェックを外して、plugin.ymlにチェックを入れてください。エクスポートは用意しているサーバーのpluginフォルダに直接してしまいましょう。名前はFirstPlugin.jarにします。この時、エクスポート先にFirstPlugin.jarまで書くようにしてください。画像を参考にしてくださいね。
図6.6: エクスポート設定
エクスポートできたらサーバーを起動して、plコマンドを実行してみましょう。緑色でFirstPluginと表示されていたら成功です!
マインクラフトをプレイしたことがある人なら、一度はマルチプレイのサーバーにつないだことがあると思います。その時に、サーバー独自のコマンドを使ったことはありませんか?実はそのコマンドたちも、プラグインによって実装されています。ここでは、コマンドを実装してみます。
マインクラフトのプラグインを作ってく上で、クラスの概念は重要です。マインクラフトのプラグインでは、コマンドや動作ごとに行う処理一つ一つをそれぞれのクラスを作成して行っています。下の画像のようなイメージです。
図6.7: クラスのイメージ画像
クラスの説明が終わったところでクラスを実装してみます。まずは、onEnableメソッドにgetCommand("first").setExecutor(new FirstCommand());
と入力してください。これは、「firstってコマンドが入力されたらFirstcommandってクラスで対応してね」という意味です。すると、new FirstCommand
に赤線が引かれると思います。これは、「FirstCommandってなんやねんワイ知らんわ」という意味です。なので、FirstCommandというクラスを作成する必要があります。その赤線にマウスを合わせて、クラスを作成を押してください。出てきた画面では設定を変えずに作成してください。これで、first
というコマンドを処理する専用のクラスができました。
因みに、getCommandのsetExcutorの部分をthis
に設定すると、getCommandを記述したクラスで処理することができます。が、クラスのインポートが必要になりますし、何より見た目が汚くなります。お勧めしません。thisに設定する時には、public class FirstPlugin extends JavaPlugin
をpublic class FirstPlugin extends JavaPlugin implements CommandExecutor
に手動で変更してください。クラスを新規作成した時には自動で追加されるので問題ありません。
さて、クラスを生成することはできましたが、この自動で入力されたonCommandとは何でしょうか?その役割を見ていきましょう。ちなみにこれもメソッドですよ~。
onCommandメソッドの引数を見てみるとsender,command,label,args[]
の4種類が存在します。一つずつ紹介していきます。
/test a b c
というコマンドがあったとします。この時、labelにa
が代入されて、args[0]にb
、args[1]にc
が代入されます。これでわかるよね?因みに、クラス作成した直後はarg0,arg1,arg2,arg3[]
になっています。上で紹介したsender,command,label,args[]
のほうが簡単なので、書き換えてください。この部誌の中ではsender,command,label,args[]
で説明します。
図6.8: 作成したすぐのクラス。変数名をそれぞれ変更することを推奨します。
onCommandメソッドの返り値はboolean型です。trueの時には特に何も起こりませんが、falseの時にはコマンドを送った人に、あとで紹介するplugin.ymlで設定したメッセージを送信します。
せっかくクラスとメソッドを作ったのに何もしないのはもったいないので、何か処理を実装してみましょう。ここでは、インベントリをダイヤブロックで満たしてみたいと思います。
ここで、コマンドの送信者について考えてみましょう。もし、このコマンドがコンソールから実行されていたらどうなると思いますか?当然コンソールにアイテムを渡すことができずに、処理に失敗してしまします。無理やり実行するとこのようにエラーが発生します。
図6.9: consoleに無理やりアイテムを渡そうとしたらこうなる
なので、コマンドを実行した人にアイテムを渡す前に、コマンドを実行したのがコンソールかプレイヤーかを確認する必要があります。Javaで、変数の型を比較するには"instanceof"
を使用します。senderがPlayer型の変数ではなかったときはsenderがコンソールなので、return falseで終了させてしまいましょう。実際に書くコードはとなります。
次に、コマンドを送信したプレイヤーの情報を取得して、Player型にキャストします。Player player
で、プレイヤー情報を保存する変数を作成できます。次に、プレイヤー型にキャストしたsenderの情報をplayerに代入します。player = (Player)sender
でプレイヤー型にキャストすることができます。
ここで、あれ?と思った人が多いと思います。なんたって、Playerの下に赤線が引かれてますから。これは、「Player型ってなんやねんワイ知らんぞ」とコンパイラが言っているのです。なので、Player型を処理するパッケージをインポートする必要があります。Ctrl+Shift+Oを押してください。赤線がなくなったらOKです。
次にプレイヤーに渡すアイテムを表す変数を作成します。。まず、ItemStack型の変数itemを作成します。このItemStack型もコンパイラは知らないので、Ctrl+Shift+OでItemStack型をインポートしてください。この時、2つの選択肢を提示されますが、org.bukkit...
を選択してください。
図6.10: こっちを選択してください
次に、itemにダイヤモンドというデータを代入します。アイテムデータはItemStackを使用することで取得できます。あとこのnewをくっつけることでインスタンスの生成をすることができます。インスタンスの生成に関してはググってください。実装すると、下のようになります。
ダイヤモンドのデータを持つ変数の作成
ItemStack item; item = new ItemStack(Material.DIAMOND);
次に、コマンドの実行者のインベントリを取得します。。Inventory型の変数invを作成して、playerのインベントリを保存します。またいつものように赤線が引かれるのでインポートしてください。次に、送信者のインベントリデータをinvに代入します。playerのインベントリデータはplayer.getInventory()
で取得できます。
インベントリの取得
Inventory inv ; inv = player.getInventory();
最後に、インベントリにアイテムを追加する処理です。インベントリにアイテムを追加する処理はaddItem()
とsetItem()
の2種類が存在します。addItemはプレイヤーのインベントリに場所を指定せずにアイテムを追加します。アイテムを投げられる、とするとイメージがわきやすいと思います。それに対して、setItemではインベントリの指定した場所にアイテムを追加します。もとからあったアイテムは上書きされます。今回は、インベントリをダイヤブロックで満たすので、setItemを使用します。setItemの書式はsetItem(インベントリ番号,アイテム)
です。インベントリ番号は、インベントリスロット一つ一つに割り当てられています。割り当てのイメージは下の画像のような感じです。
図6.11: 番号割り振りのイメージ ダイヤブロックの数が番号を表す
今回は、0~35のスロットにダイヤモンドをセットします。36回同じ処理を書くのは面倒なので、For文を使ってみましょう。
プレイヤーにアイテムを渡す処理は以下のようになります。
プレイヤーにアイテムを渡す
for(int i=0;i<36;i++){ inv.setItem(i,item); }
最後に、コマンドを実行したプレイヤーにメッセージを送信してみる機能を実装してみましょう。(プレイヤー型の変数).sendMessage("メッセージの内容")
で、変数で指定されたプレイヤーにメッセージを送ることができます。playerにキャストして代入したのなら、player.sendMessage("メッセージ内容")
で送信できます。
さあ、これでコマンドが完成しました。
図6.12: ここまで
上に示したコードでは変数を作成した後に代入していますが、作成するのと同時に初期化してもかまいません。例えばPlayer型の変数を作成するときにPlayer player = (Player)sender
のようにすることができます。これより後ではこの方法を使っている前提で説明します。====[/column]
ただ、これだけだとコマンドは実行されません。Enable時にplugin.ymlを通じてコマンドの設定をサーバーに読み込ませる必要があります。
まず、plugin.ymlを開きます。開いたら、次の画像のように入力してください。commands:の後が追加されています。空白の部分はすべて半角スペース2つで開けてください。タブではないです。
図6.13: plugin.ymlのイメージ
plugin.ymlは、サーバーにプラグインの情報を伝える役目をしています。それぞれの設定のセットの役割を確認してみましょう。
permissionの設定周りはここでは扱いません。探したらわかりやすいサイトがたくさん出てくると思います。
plugin.ymlの編集が終わったら、前回と同じようにしてエクスポートしてください。上書きしてもらって構いません。
サーバーを起動して、plコマンドを実行して、表示されることを確認してください。プラグインが表示されたら、マインクラフトを起動してサーバーにログインして、コマンドを実行してみてください。
図6.14: 実行結果
インベントリがダイヤブロックで満たされたら成功です!
マインクラフトのプラグインを使ったことがある人は、コンフィグファイルを使ったことがあると思います。ここでは、コンフィグ周りの設定方法を解説したいと思います。
まず最初に元となるコンフィグを作成したいと思います。plugin.ymlと同じようにしてconfig.ymlを作成してしまいましょう。中身はここでは画像のようにしておきます。
図6.15: config.ymlの中身はこのようにしておきます。
configファイルは起動時に読み込んでおきたいので、onEnableメソッドに記述します。
plugin/<プラグイン名>のフォルダにconfig.ymlを保存させるには、saveDefaultConfig();
を使います。この時、このクラスで処理することを明示するためにthisを付けてthis.saveDefaultConfig();
とします。
saveDefaultConfig()の働き方は、config.ymlが存在しているか存在していないかでわけられます。もしconfig.ymlが存在しないときには、plugin/<プラグイン名>の中にjarファイルの中のconfig.ymlと同じものを作成し、config.ymlが存在するときには何もしません。つまり、上書きされることがない、ということです。
マインクラフトのプラグインにはFileConfiguration
という型が存在します。ここでは、configという名前の変数を作成します。御多分に洩れず、こんな型知らねーよと帰ってくるのでインポートしてやってください。この型に対してgetConfig()
の戻り値を代入するとconfigデータを取得できます。2つ合わせてFileConfiguration config = getConfig()
となります。
次に、一つ一つの項目の取り込みです。例を使って説明します。getConfigで取り込んだconfigファイルの中に、<設定名>: <設定内容(TRUE/FALSE)>というコンフィグファイルがあったとします。この時、<設定内容>を取得するには、config.getBoolean("設定名");
を使うことで取得できます。このgetBoolean
の部分は、<設定内容>の方によって変更可能です。ここではtrueとfalseの判別を付けるためにgetBoolean()
を使用します。取得したコンフィグの内容を保存するために変数を一つ作りましょう。ここでは名前をconfig_test
とします。変数の初期化と同時に取得するのであれば、boolean config_test = config.getBoolean("first");
のようになります。
get****()シリーズには多くの種類があって、getBoolean(String),getInt(String),getString(String)
などの簡単に想像できるものから、階層でリストアップされているものすべてを取得するgetList(String),getStringList(String)
のようなものもあります。
これでコンフィグデータを取り込むことはできました。あとはgetLoggerを使用してEnable時にtrueとfalse1を表示する処理を実装してみましょう。
trueかfalseかを取得して出力する
if(config_test){ getLogger().info("TRUE"); }else{ getLogger().info("FALSE"); }
ここまでのソースコードをまとめるとこうなります。
onEnableメソッドの中
this.saveDefaultConfig(); FileConfiguration config = getConfig(); boolean config_test = config.getBoolean("first"); if(config_test){ getLogger().info("TRUE"); }else{ getLogger().info("FALSE"); }
ここまでできたら、エクスポートして実行してみましょう。このときに、エクスポートするフォルダにconfig.ymlを含めてください。
図6.16: エクスポートするフォルダ
エクスポートできたら、サーバーを起動してください。サーバーログにTRUEと出てきましたか?
図6.17: TRUEと出てくる
確認したら、サーバーを一度止めて、plugin/firstpluginの中のconfig.ymlのfirst: true
をfirst: false
に書き換えてください。書き換えた後でサーバーを起動してください。すると...?
図6.18: FALSEと出てくる
先ほどと違ってfalseと出てきました。成功ですね。
いかがだったでしょうか?僕は中3なのでこれが初めて部誌を書く経験になります。難しいですね...部誌書くの...。とても拙く不自由な日本語だったと自分でも思います。来年こそはもっとうまく書くぞ。
この部誌を読んでくれた人が一人でも「マインクラフトのプラグインを作ること」に興味を持ってくれるとうれしいです。