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

スケジューラのプログラミング

このチュートリアルでは、bukkit によって提供されているスケジューラの使用方法をガイドします。
スケジューラは、指定の時間後に実行される処理を書くことができます。
これはリスナーやイベントへの応答で実行されるコードの記述方法とは異なります。
また、繰り返しや、遅延の有無を設定したりすることもできます。この繰り返し処理は、処理が完了するか、キャンセルされるか、プラグインが無効になるまで実行できます。

スケジューラを利用した実装は、次のような手順を踏んでください。

  1. まず、スケジューラにより実行される処理内容を、BukkitRunnable を継承したクラスの run()メソッドで実装します。
  2. 次に、前のステップで実装したクラスをインスタンス化し、スケジュールをしたい箇所で、runTaskLaterメソッドやrunTaskTimerメソッドなどを実行するように設定します。

BukkitRunnable

BukkitRunnableは、 抽象クラスRunnableの実装クラスです。
Runnableに無い便利な機能として、自身の処理を再スケジュールしたり、キャンセルしたりできることです。

処理の実装例

スケジューラの処理を定義するには、まず BukkitRunnable を extends しましょう。

これはスケジュール処理のタスク定義の例です。

import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitRunnable;

public class ExampleTask extends BukkitRunnable {

    @Override
    public void run() {
        // スケジュールで実行する処理の内容をここに書きます。
        Bukkit.broadcastMessage("サーバーへようこそ!説明文をちゃんと読んでね!");
    }
}

実装した処理のスケジューリング

タスクを定義した後、プラグインは、タスクをスケジュールする必要があります。
BukkitRunnable は、指定の時間になったら、タスクのインスタンスが起動され、メソッドが実行されます。
詳細は、BukkitRunnableのjavadoc を参照してください。
これらのメソッドは共通して、BukkitTaskクラスのオブジェクトを返します。

これは、プレイヤーがログインしたら、20ticks(=1秒)後にタスクを実行するスケジューラを登録する実装例です。

import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;

public class ExamplePlugin extends JavaPlugin implements Listener {

    @Override
    public void onEnable() {
        getServer().getPluginManager().registerEvents(this, this);
    }

    @EventHandler
    public void onJoin(PlayerJoinEvent event) {
        // BukkitRunnableを継承したExampleTaskを生成し、
        // runTaskLater メソッドで20ticks後に実行するように設定します。
        new ExampleTask().runTaskLater(this, 20);
    }
}

自己キャンセルの実装例

今度は、プレイヤーがログインしたら、10ticks待った後、20ticksごとに5回実行し、その後スケジューラの処理をキャンセルする例を示します。

ExampleSelfCancelingTask.java

import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;

public class ExampleSelfCancelingTask extends BukkitRunnable {

    private final JavaPlugin plugin;

    private int counter;

    public ExampleSelfCancelingTask(JavaPlugin plugin, int counter) {
        this.plugin = plugin;
        if (counter < 1) {
            throw new IllegalArgumentException("counter には1以上を指定してください。");
        } else {
            this.counter = counter;
        }
    }

    @Override
    public void run() {
        // ここに、スケジュールの処理内容を実装します。
        if (counter > 0) {
            plugin.getServer().broadcastMessage("広告が表示されるまで、あと " + counter-- + "秒");
        } else {
            plugin.getServer().broadcastMessage("サーバーへようこそ!説明文をちゃんと読んでね!");
            this.cancel();
        }
    }
}


ExamplePlugin.java

import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;

public final class ExamplePlugin extends JavaPlugin implements Listener {

    @Override
    public void onEnable() {
        getServer().getPluginManager().registerEvents(this, this);
    }

    @EventHandler
    public void onJoin(PlayerJoinEvent event) {
        // スケジューリングする
        new ExampleSelfCancelingTask(this, 5).runTaskTimer(this, 10, 20);
    }
}

無名クラスを使用した実装例

毎回BukkitRunnableを継承したクラスを作成していると大変なので、BukkitRunnableを無名クラスとして内部に実装した例も示します。
無名クラスを理解している場合は、こちらを使っても構いません。

import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;

public final class ExamplePlugin extends JavaPlugin implements Listener {

    @Override
    public void onEnable() {
        getServer().getPluginManager().registerEvents(this, this);
    }

    @EventHandler
    public void onJoin(PlayerJoinEvent event) {
        // 20ticks後に1度だけ実行される処理を、実装しつつ、そのままスケジュールします。
        
        new BukkitRunnable() {

            @Override
            public void run() {
                // スケジューラで実行される処理内容を、ここに実装します。
                getServer().broadcastMessage(
                    "サーバーへようこそ!説明文をちゃんと読んでね!");
            }
        }.runTaskLater(this, 20);
        // ↑そのままスケジュールします。
    }
}

BukkitScheduler

BukkitSchedulerは、RunnableクラスやCallableクラスの、どちらかまたは両方を実装したクラスを、一定時間後に実行する機能を提供します。
詳細は、BukkitSchedulerのjavadoc を参照してください。
なお、BukkitSchedulerのメソッドで実行できる内容は、BukkitRunnableのメソッドで実行できる内容と全く同じです。

Attention.pngWarning: ただし、BukkitSchedulerでBukkitRunnableを実行することも可能ですが、BukkitRunnableのcancel()メソッドを使ってスケジューラをキャンセルすることができないことに注意してください。
Attention.pngWarning: この実装方法は、CraftBukkit 1.7.10-R0.1 で非推奨に設定されました。今後は前述のBukkitRunnableのメソッドを呼び出す方法を使用してください。

実装例1

無名クラスでRunnableを実装し、20ticks後に実行される例を示します。

import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitScheduler;

public final class ExamplePlugin extends JavaPlugin {
    public void onEnable() {
        BukkitScheduler scheduler = Bukkit.getServer().getScheduler();
        scheduler.scheduleSyncDelayedTask(this, new Runnable() {
            @Override
            public void run() {
                // ここに処理を実装する。
            }
        }, 20L);
    }
}

実行例2

無名クラスでRunnableを実装し、20ticksごとにプラグインが終了するまでずっと実行され続ける例を示します。

import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitScheduler;

public final class ExamplePlugin extends JavaPlugin {
    public void onEnable() {
        BukkitScheduler scheduler = Bukkit.getServer().getScheduler();
        scheduler.scheduleSyncRepeatingTask(this, new Runnable() {
            @Override
            public void run() {
                // ここに処理を実装する。
            }
        }, 0L, 20L);
    }
}

BukkitTask

BukkitTaskクラスのオブジェクトは、Runnableがスケジュールされたときに返されます。
このオブジェクトは、スケジューラにより実行されるスケジュールタスクを表現しています。
詳細は、BukkitTaskのjavadoc を参照してください。

Callable と Future

Callableクラスは、同期処理を呼び出したときに、Futureクラスのオブジェクトを返します。
これはJavaで提供されている機能やクラスです。
詳細は、Callableのjavadoc や、Futureのjavadoc を参照してください。

スレッドの安全性のためのヒント

1. 非同期実行タスクは、BukkitのAPIを直接実行してはいけません。
2. 非同期タスクのコレクションにアクセスしたり、内容を変更しないでください。通常のコレクションはスレッドセーフではありません。
3. 非同期タスクは、同期タスクをスケジュールすることができます。
4. 同期タスクは、非同期タスクをスケジュールすることができます。