この連載が書籍になりました! 『マインクラフトでマルチサーバーを立てよう!』好評発売中です。

プラグイン開発の基礎

2016年9月27日(火)
ecolight

前回は全体的なサーバー運用に関連した作業について解説しました。今回は前回よりも一歩進んで、サーバー運営において比較的重要な「プラグインの開発」について解説したいと思います。

プラグイン開発を学ぶ理由とは

サーバー管理者にとって、プラグインの構成や開発方法は非常に重要な知識の1つです。その理由は、例えば以下のような場合に対応が求められるためです。

  • サーバーアップデート時にプラグインがエラーとなり起動しないため代替物を用意したい
  • サーバー構築の際に目的を達成できるプラグインが見つからないため自分で用意したい
  • 他にプラグインをメンテナンスする人が居てもプラグインで可能なこと、不可能なことを管理者が把握しておいたほうが良い

サーバー管理者がプラグインの知識を持っているだけで、Minecraftサーバーの応用範囲は無限に広がります。ここでは、プラグイン開発の基礎的な部分について解説していきます。

開発環境を用意しよう

まず、プラグインを作るための開発環境を整えましょう。spigotプラグインはJava言語で作られているため、Java言語でコーディングできる開発環境を用意します。Javaの有名な開発環境には「Eclipse」「IntelliJ IDEA」「NetBeans」などがありますが、ここでは筆者が普段使用しているNetBeansで説明します。

他の開発環境でも構築方法やプラグインの開発方法がインターネット上に多く公開されているので、ご自身の環境に合わせて調べてみてください。

さて、まずはNetBeansの公式サイトにアクセスし、画面左側のメニューから最新版の「ダウンロード(日本語版)」をクリックします(図1)。

図1:NetBeansの公式サイト(https://ja.netbeans.org/)

ダウンロードページに移動したら、JavaSEの開発が可能な環境をダウンロードしましょう(図2)。

図2:最新版のダウンロードページ

インストーラをダウンロードしたら、インストーラを起動して画面の指示に従ってインストールしていきます。基本的には「次へ」ボタンを押していき、最後に「インストール」ボタンを押せばインストールが開始されます。

図3:NetBeansのインストーラ画面

インストールが完了したら、IDEを起動してみましょう(図4)。

図4:NetBeansのIDE画面

基本的なファイルを用意してビルドしてみよう

Spigotのプラグイン開発は、クライアント用のMOD開発と比べてかなり楽に開発できます。まずは、新しいプロジェクトを作成しましょう。「ファイル」メニューから「新規プロジェクト」を選択します(図5)。

図5:新規プロジェクトの作成

次に、新規プロジェクトの作成ウィザードで「Maven」の「Javaアプリケーション」を選択して「次へ」をクリックします(図6)。

図6:Mavenプロジェクトを選択

図7の「名前と場所」では、プロジェクト名(任意)を指定してグループIDを設定します。グループIDは独自ドメインを持っていればドメインを逆順で記載したものを使用すると良いでしょう。グループIDは他のプロジェクトと名前の規則を重複させないためなどに使用されるため、ドメインを持っていない方も独特の規則になるようにしてください。

図7:プロジェクトに名前を付ける

これでプロジェクトが作成されました。IDEのプロジェクトウインドウでプロジェクトの構成を確認してみましょう(図8)。標準ではプロジェクトウインドウはIDEの左上に統合されています。階層が深く見にくい場合は枠の位置をドラッグで調整するか、タブを掴んでIDEのウインドウから外して外部ウインドウ化するなどしてください。

図8:プロジェクトウインドウでプロジェクトの構成を見ることができる

以降の手順はどの順番で操作しても問題ありませんが、とりあえず「pom.xml」ファイルを編集しましょう。これはMavenプロジェクトで依存性などを管理するためのファイルです。

プロジェクトウインドウ(図8)で「プロジェクト・ファイル」の左横にある「+」をクリックすると「pom.xml」が表示されるので、ダブルクリックして開きましょう(図9)。

図9:pom.xmlファイルを開く

続いて、pom.xmlにspigotのプラグイン開発のためのAPI定義を追加します。例えば、以下は1.9.4向けのプラグインを開発する場合のAPI定義です。赤字の部分を追加して上書き保存しましょう。<version>の部分にはspigotで公開されているAPIから該当するバージョンを選択して記載します。

<?xml version="1.0" encoding="UTF-8"?>
<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>jp.minecraftuser</groupId>
    <artifactId>test_plugin</artifactId>
    <version>0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <repositories>
        <repository>
            <id>spigot-repo</id>
            <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>org.spigotmc</groupId>
            <artifactId>spigot-api</artifactId>
            <version>1.9.4-R0.1-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>plugin.yml</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>

次に、プラグインに必要な構成ファイルを追加していきます。まずは「plugin.yml」を追加しましょう。プロジェクトウインドウ(図8)で「ソース・パッケージ」を右クリックして、表示されるメニューから「新規」-「YAMLファイル」を選択します(図10)。

図10:YAMLファイルを追加

右クリックで表示される新規メニューにYAMLがない場合は「その他...」を選択し、「ファイル・タイプの選択」ダイアログで「その他」-「YAMLファイル」を選択してください(図11)。

図11:YAMLファイルを選択する

追加するYAMLファイルには「plugin」とファイル名を付けて「終了」ボタンをクリックします(図12)。

図12:追加するYAMLファイルに「plugin」と名前を付ける

追加されたplugin.yamファイルが自動的に開くので、作成するプラグインの記述を追記していきましょう(図13)。

図13:自動的にplugin.ymlが開く

plugin.ymlの基本的な記述は以下のような形式です。記述する内容は「Minecraft Modding Wiki」で解説されているので、そちらを参照してください。必須の記述は「name」「version」「main」です。なお、mainの記述はデフォルトで追加されたパッケージに加えて次の手順で説明するメインクラスの名前に合わせてください。

name: TestPlugin
version: 0.1
description: test plugin.
author: ecolight
website: forum.minecraftuser.jp
#---
main: jp.minecraftuser.test_plugin.TestPlugin

ここまで作業したら、いったん「実行」メニューから「プロジェクト(test_plugin)をビルド」します(図14)。

図14:プロジェクトをビルドする

デフォルトだとIDEの右下部にある「出力 - ビルド」ウインドウに「BUILD SUCCESS」の文字が表示されていることを確認しましょう(図15)。ビルドが失敗している場合はpom.xmlの記述が誤っていることが考えられるので見直してください。

図15:ビルド結果を確認する

ビルドの成功が確認できたら、プラグインのメインクラスを追加します。プロジェクトウインドウ(図8)からデフォルトで追加されたパッケージ(今回はjp.minecraftuser.test_plugin)を右クリックし、「新規」-「Javaクラス」を選択します(図16)。ここではクラス名を「TestPlugin」としました(図17)。

図16:プラグインのメインクラスを追加する

図17:クラス名を「TestPlugin」とした

追加すると自動的にファイルが開くので、プラグインの基本的な記述を追加していきます。まず、メインクラスにJavaPluginを継承する定義を追加します。

public class TestPlugin extends JavaPlugin {

}

赤文字の部分を追加すると、IDEの「JavaPlugin」に赤い波線が引かれるので、該当行にカーソルを合わせてから[Alt]+[Enter]キーを押します。

すると「org.bukkit.plugin.java.JavaPluginをインポートに追加」というヒントが表示される(図18)ので、[Enter]キー(もしくはクリック)して選択してください。なお、このヒントはビルドしていないと表示されません。この操作により自動的にspigotプラグインAPIのパッケージがインポート文として追記されます。

図18:警告文から自動的にインポート文を生成させる

続いて、メインクラスの中に起動時と終了時にサーバーコンソールへログを出力するコードを記述してみましょう。インポート部分が足りない箇所は赤い波線でエラー警告が出ると思いますので、都度「org.bukkit~」で始まるパッケージを補完機能で追加してください。

public class TestPlugin extends JavaPlugin {
    /**
     * 起動時処理
     */
    @Override
    public void onEnable() {
        getLogger().info("test enable");
    }

    /**
     * 終了時処理
     */
    @Override
    public void onDisable() {
        getLogger().info("test disable");
    }
}

コードを追加したら、再度ビルドします。「BUILD SUCCESS」と表示されれば成功です。作成されたファイルを確認してみましょう。ビルド結果はプロジェクトフォルダのtargetフォルダに格納されています(図19)。

プロジェクトフォルダが分からない場合は、プロジェクトウィンドウからプロジェクト名を右クリックして「プロパティ」-「ソース」カテゴリを選択すれば出てきます。

図19:ビルドされたプラグインが生成されている(targetフォルダ)

このプラグインファイルをspigotサーバーのpluginsフォルダに導入し、サーバーを起動・停止してみましょう。以下のような形でログが出力されると思います。

[04:39:59 INFO]: [TestPlugin] Enabling TestPlugin v0.1
[04:39:59 INFO]: [TestPlugin] test enable
[04:39:59 INFO]: Server permissions file permissions.yml is empty, ignoring it
[04:39:59 INFO]: Done (3.288s)! For help, type "help" or "?"
stop
[04:40:30 INFO]: Stopping the server
[04:40:30 INFO]: Stopping server
[04:40:30 INFO]: [TestPlugin] Disabling TestPlugin v0.1
[04:40:30 INFO]: [TestPlugin] test disable

コマンドとイベントを制御する

起動時や停止時にJavaでできる範囲の処理を行うだけであれば前述の手順で問題ないでしょう。しかし、実際にプラグインでやりたいことは「コマンドを打った際に処理を行う」「サーバー内の特定の事象を契機に処理を行う」といったMinecraftサーバー独自の処理だと思います。以降では、それぞれの実装方法について説明していきましょう。

コマンドの処理を追加する

まず、コマンドの処理を追加してみましょう。追加するコマンドは「/test」コマンドにします。メインクラスに以下のようなメソッドを追加します。

    /**
     * コマンド実行処理
     */
    @Override
    public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){
        // test コマンドの処理
        if(cmd.getName().equalsIgnoreCase("test")){
            // 取りあえずログ出力して終わる
            getLogger().info("test command"); 
        }
        // 該当するコマンド無し
        return false;
    }

次に、plugin.ymlに下記のようなtestコマンドの定義を追加します。

commands:
    test:
        description: test command.
        usage: '/ : 説明'

testコマンド追加後のplugin.yml全体の内容は以下のような形になるでしょう。

name: TestPlugin
version: 0.1
description: test plugin.
author: ecolight
website: forum.minecraftuser.jp
#---
main: jp.minecraftuser.test_plugin.TestPlugin
#---
commands:
    test:
        description: test command.
        usage: '/ : 説明'

コマンドを追加できたら、プラグインをリビルドしてサーバーで読み込んでみます。サーバー上から「test」と打ち込むと以下のようなログが出力されると思います。

[05:08:30 INFO]: [TestPlugin] test command
[05:08:30 INFO]: /test : 説明

1行目はコマンド処理を実行して出力したログ、2行目はメソッドの最後に「return false」したことで出力されたコマンドの使用方法の説明です。

今度はMinecraftを起動してログインし、ゲーム内から「/test」コマンドを実行してみましょう。サーバーコンソールに以下のようなメッセージが表示されます。ゲーム画面にも出力されていると思います。

[05:09:18 INFO]: ecolight issued server command: /test
[05:09:18 INFO]: [TestPlugin] test command

図20:testコマンドの実行結果(return false)

onCommandメソッドは復帰値のbooleanでコマンドの成否を返します。return falseはコマンドの失敗を意味するため、正しいコマンドの説明文を実行者のコンソール(サーバーコンソールかゲーム内のチャットウインドウ)に返却する動きになっています。

このことから、コマンド実行が成功した場合はreturn trueで終わらせれば説明文のメッセージは表示されなくなります。

コマンドの処理はクラスで分割することもできます。まず以下のようなクラスをパッケージに追加します。

package jp.minecraftuser.test_plugin;

import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;

/**
 * testコマンド実行クラス
 * @author ecolight
 */
public class TestCommandExecutor implements CommandExecutor{
    private final TestPlugin plg;
    
    /**
     * コンストラクタ
     * @param plg_ プラグインメインクラスのインスタンス
     */
    public TestCommandExecutor(TestPlugin plg_) {
        plg = plg_;
    }
    
    /**
     * コマンド実行処理
     * @param sender 送信者
     * @param cmd コマンド名
     * @param commandLabel 
     * @param args
     * @return 
     */
    @Override
    public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args){
        // test コマンドの処理
        if(cmd.getName().equalsIgnoreCase("test")){
            // 取りあえずログ出力して終わる
            plg.getLogger().info("test command:"+commandLabel);       
        }
        // 該当するコマンド無し
        return false;
    }
}

onCommandの内容は先ほどメインクラスで実行したものと同じです。次にメインクラスにonCommandメソッドが残っている場合は削除し、代わりに以下の1文をonEnableメソッドに追加します。

getCommand("test").setExecutor(new TestCommandExecutor(this));

プラグインをリビルドし、再度コマンドをテストしてみましょう。先ほどメインクラスにonCommandメソッドを実装した時と同じ結果になるはずです。

イベントの処理を追加する

続いて、イベントの処理を記述してみましょう。今回はプレイヤーがログインした時のイベントを処理してみます。まず、以下のようなクラスをパッケージに追加します。

package jp.minecraftuser.test_plugin;

import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;

/**
 * イベントリスナクラス
 * @author ecolight
 */
public class TestPluginEventListener implements Listener {
    private final TestPlugin plg;
    
    /**
     * コンストラクタ
     * @param plg_  プラグインメインクラスのインスタンス
     */
    public TestPluginEventListener(TestPlugin plg_) {
        plg = plg_;
    }
    
    /**
     * PlayerLoginイベント処理
     * プレイヤーログイン時のイベントについて実装する
     * @param event ログインイベント情報
     */
    @EventHandler
    public void onPlayerLogin(PlayerLoginEvent event) {
        // プレイヤーがOP権限かどうかログしておく。
        plg.getLogger().info("isOp=" + event.getPlayer().isOp());
    }
    
    /**
     * PlayerJoinイベント処理
     * プレイヤーログイン後のイベントについて実装する
     * @param event JOINイベント情報
     */
    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent event) {
        // ログインしたプレイヤーにようこそメッセージを表示する
        event.getPlayer().sendMessage("ようこそテストサーバーへ!");
    }
}

次に、メインクラスのonEnableメソッドに以下の1文を追加します。

getServer().getPluginManager().registerEvents(new TestPluginEventListener(this), this);

プラグインをリビルドしてspigotサーバーで動作させてみましょう。プレイヤーをログインさせて動作確認してみます。以下のようなサーバーコンソールメッセージとゲーム内のメッセージ(図21)が確認できたでしょうか。

[06:40:59 INFO]: [TestPlugin] isOp=true

図21:ログインイベントの処理メッセージ例(ゲーム内)

プログラムの動作については、コード中のコメントを参考にしてください。

なお、今回作成したプラグイン(test_plugin)のファイル構成と各ファイルのソースコードをサンプルコードとしてまとめました。ぜひ参考にしてください。こちらからダウンロードできます。

プラグインでやりたい事を検討する

さて、ここまでプラグインのコマンドとイベントの実装方法を説明しました。あとはプラグインでやりたい事を考えて実装していくだけです。

spigot APIの全容を把握するのは難しいですが、spigotの開発元がjavadocを公開しているので、これを参考にすると良いでしょう。

例えば、イベント処理の実装例として、今回は「PlayerLoginEvent」と「PlayerJoinEvent」の使い方を示しましたが、実際には他にも多くの種類のイベントを処理できます。前述のjavadocを開き、左上部の「Package」リストから「org.bukkit.event」で始まるパッケージを探してみましょう(図22)。

図22:spigotのjavadocからイベントに関わる項目を見てみよう

いずれかのpackageをクリックすると、左下部にそのパッケージに所属するイベントクラスがリストアップされます。これらはそれぞれイベントハンドラを記載してプラグインから処理を割り込ませる事ができます。

Javadocでどんなイベントが拾えるのか研究するだけでもMinecraftサーバーで色々とできる事が分かると思います。イベント系のクラス以外にも、プラグイン内で扱うクラスの情報が一通り記載されているため、困ったことがあったら都度参照するようにしましょう。

今回は、プラグイン開発の基礎を解説しました。次回はプラグインの応用的な開発方法や勉強方法を解説したいと思います。

本連載について

Minecraftの公式記事ではありません。本連載の内容はMojangから承認されているわけではなく、Mojangとは関係ありません。

Minecraftの非公式日本ユーザーフォーラム管理人

同フォーラムに付帯する生活系マルチサーバーもMinecraft製品版発売以降個人運営を続けている。ご相談などありましたらフォーラム(http://forum.minecraftuser.jp/)やTwitter@ecolightまで。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています