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

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

この編集を取り消せます。 下記の差分を確認して、本当に取り消していいか検証してください。よろしければ変更を保存して取り消しを完了してください。
最新版 編集中の文章
1行目: 1行目:
== スケジューラのプログラミング ==
+
== スケジューラの解説 ==
 +
この解説では、Bukkitのスケジューラの利用方法を説明しています。
  
このチュートリアルでは、bukkit によって提供されているスケジューラの使用方法をガイドします。<br>
+
== 主な注意事項  ==
スケジューラは、指定の時間後に実行される処理を書くことができます。<br>
 
これはリスナーやイベントへの応答で実行されるコードの記述方法とは異なります。<br>
 
また、繰り返しや、遅延の有無を設定したりすることもできます。この繰り返し処理は、処理が完了するか、キャンセルされるか、プラグインが無効になるまで実行できます。
 
  
スケジューラを利用した実装は、次のような手順を踏んでください。
+
主な注意事項は次の2点です。
# まず、スケジューラにより実行される処理内容を、[[#BukkitRunnable|BukkitRunnable]] を継承したクラスの run()メソッドで実装します。
+
* 他のスレッドからAPIのメソッドを(一部例外あり)を呼び出さない
# 次に、前のステップで実装したクラスをインスタンス化し、スケジュールをしたい箇所で、runTaskLaterメソッドやrunTaskTimerメソッドなどを実行するように設定します。
+
* メインのスレッドをスリープ状態にしてはならない
  
== BukkitRunnable ==
+
== 解説 ==
  
BukkitRunnableは、 抽象クラスRunnableの実装クラスです。<br>
+
スケジューラは次の3種類のタスクをオプションとして提供しています。
Runnableに無い便利な機能として、自身の処理を再スケジュールしたり、キャンセルしたりできることです。
+
*遅延タスク(Delayed Task)
 +
*連続タスク(Repeating Task)
 +
*戻り値を返すタスク
  
=== 処理の実装例 ===
+
これらのタスクは2種類のスレッドとして実行する事ができます。
 +
*メインサーバスレッド
 +
*専用スレッド
  
スケジューラの処理を定義するには、まず BukkitRunnable を extends しましょう。
+
=== スレッドの種類  ===
  
これはスケジュール処理のタスク定義の例です。
+
スケジューラにどのスレッドタイプを利用すれば良いかが、最も重要な問題です。<br>
<blockquote><source lang="java">
+
間違ったスレッドタイプを利用すると、検出が困難な障害を断続的に発生させる原因となります。<br>
import org.bukkit.Bukkit;
+
また、そのような障害は試験サーバでは発生せず、頻繁に連続稼動しているサーバでのみ発生する場合がある等、<br>
import org.bukkit.scheduler.BukkitRunnable;
+
再現性が低い障害となる場合もあるため、原因の特定を更に困難にします。
  
public class ExampleTask extends BukkitRunnable {
+
Minecraftサーバはマルチスレッドであり、<br>
 +
一つのメインサーバーのスレッドと、他のヘルパースレッドが存在します。
  
    @Override
+
多くのデータ構造は、サーバのメインスレッドからのみアクセスされます。<br>
    public void run() {
+
別のスレッドからこれらのデータを変更しようとすると、<br>
        // スケジュールで実行する処理の内容をここに書きます。
+
2つのスレッドが同時にデータにアクセスする事になるため、<br>
        Bukkit.broadcastMessage("サーバーへようこそ!説明文をちゃんと読んでね!");
+
データ破損を引き起こす可能性があります。
    }
 
}
 
</source></blockquote>
 
  
=== 実装した処理のスケジューリング ===
+
メインスレッドは、サーバーのCPU時間の配分を処理します。<br>
 +
このスレッドは、他のタスクへCPU時間を配分するために、秒間20回のスリープを実行します。
  
タスクを定義した後、プラグインは、タスクをスケジュールする必要があります。<br>
+
少数のBukkitAPIはスレッドセーフです。<br>
BukkitRunnable は、指定の時間になったら、タスクのインスタンスが起動され、メソッドが実行されます。<br>
+
あなたのプラグインが使っているAPIがスレッドセーフなものである事を確認していない限り、<br>
詳細は、BukkitRunnableのjavadoc を参照してください。<br>
+
基本的にBukkitAPIの命令はスレッドセーフではないものとして開発して下さい
これらのメソッドは共通して、BukkitTaskクラスのオブジェクトを返します。
 
  
これは、プレイヤーがログインしたら、20ticks(=1秒)後にタスクを実行するスケジューラを登録する実装例です。
+
==== 同期タスク ====
<blockquote><source lang="java">
+
サーバのメインスレッドが実行するタスクです。
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
+
<br> メインスレッドは下記の用途に'''使えます'''
    public void onEnable() {
+
* スケジューラメソッドの呼び出し
        getServer().getPluginManager().registerEvents(this, this);
+
** スケジューラの戻り値を取得する際は注意を要します(後方の説明に記載)
    }
+
*停止・待機処理を行わないタスク
  
    @EventHandler
+
<br> メインスレッドは下記の用途に'''使わなくてはいけません'''
    public void onJoin(PlayerJoinEvent event) {
+
*Bukkit APIのメソッドを呼び出すタスク(スレッドセーフな方法を除く)
        // BukkitRunnableを継承したExampleTaskを生成し、
 
        // runTaskLater メソッドで20ticks後に実行するように設定します。
 
        new ExampleTask().runTaskLater(this, 20);
 
    }
 
}
 
</source></blockquote>
 
  
== 自己キャンセルの実装例 ==
+
<br> メインスレッドは下記の用途に'''使ってはいけません'''
 +
*自スレッドをスリープ状態にするタスク
 +
**停止・待機処理を含むスレッド。(例:ネットワーク通信の受信タスク)
  
今度は、プレイヤーがログインしたら、10ticks待った後、20ticksごとに5回実行し、その後スケジューラの処理をキャンセルする例を示します。
 
  
'''ExampleSelfCancelingTask.java'''
+
メインスレッド上で同期タスクを実行する事自体は良いのですが、<br>
 +
待機や停止の時間がほぼゼロであるものに限ります。<br>
 +
:'''訳者注''': 1つの処理ステップが長時間を費やす処理は、サーバ自体をその間待機状態に置いてしまうため、メインスレッド上で行うべきではない事を説明しています。
  
<blockquote><source lang="java">
+
==== 非同期タスク ====
import org.bukkit.plugin.java.JavaPlugin;
 
import org.bukkit.scheduler.BukkitRunnable;
 
  
public class ExampleSelfCancelingTask extends BukkitRunnable {
+
非同期タスクは、サーバのメインスレッド以外のスレッドによって実行されます。専用のタスクとして実行されるため、同期タスクで説明したような問題を引き起こすことなく、自タスクを停止・スリープ状態にする事ができます。この特性は、同期タスクの真逆に相当します。
  
    private final JavaPlugin plugin;
+
<br> 非同期タスクは下記の用途に'''利用できます'''
  
    private int counter;
+
*スケジューラメソッドの呼び出し
 +
*プラグインAPIを利用しないタスク
  
    public ExampleSelfCancelingTask(JavaPlugin plugin, int counter) {
+
<br> 非同期タスクは下記の用途に'''利用しなければなりません'''
        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();
 
        }
 
    }
 
}
 
</source></blockquote>
 
  
 +
<br> 非同期タスクは下記の用途に'''利用してはいけません'''
  
'''ExamplePlugin.java'''
+
*Bukkit APIのメソッドを呼び出すタスク(スレッドセーフな方法を除いては、下記参照)
  
<blockquote><source lang="java">
+
<br>
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 {
+
== タスクの種類 ==
 +
タスクには、連続タスク, 遅延タスク, 戻り値を返すタスク, の3種類があります。
  
    @Override
+
APIのインタフェースの仕様については[[https://github.com/Bukkit/Bukkit/blob/master/src/main/java/org/bukkit/scheduler/BukkitScheduler.java こちら]]を参照して下さい。
    public void onEnable() {
 
        getServer().getPluginManager().registerEvents(this, this);
 
    }
 
  
    @EventHandler
+
=== 遅延タスク ===
    public void onJoin(PlayerJoinEvent event) {
 
        // スケジューリングする
 
        new ExampleSelfCancelingTask(this, 5).runTaskTimer(this, 10, 20);
 
    }
 
}
 
</source></blockquote>
 
  
== 無名クラスを使用した実装例 ==
+
一定の遅延を置いてから実行されるタスクです。遅延時間にはゼロを指定可能です。
  
毎回BukkitRunnableを継承したクラスを作成していると大変なので、BukkitRunnableを無名クラスとして内部に実装した例も示します。<br>
+
==== 具体例 ====
無名クラスを理解している場合は、こちらを使っても構いません。
 
  
 +
This submits a task to the main thread to be executed after 3 seconds.
 
<blockquote><source lang="java">
 
<blockquote><source lang="java">
import org.bukkit.event.EventHandler;
+
myPlugin.getServer().getScheduler().scheduleSyncDelayedTask(myPlugin, new Runnable() {
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 {
+
  public void run() {
 +
      getServer().broadcastMessage("This message is broadcast by the main thread");
 +
  }
 +
}, 60L);
 +
</source></blockquote>
 +
The time delay is 60. This counts in server ticks. There are 20 ticks per second, so the delay is 60/20 = 3.
  
    @Override
+
This code could be executed by another thread. If the code
    public void onEnable() {
+
<blockquote><source lang="java">
        getServer().getPluginManager().registerEvents(this, this);
+
getServer().broadcastMessage("This message is broadcast by an async thread");
    }
+
</source></blockquote>
 +
was executed by another thread, it could cause problems since the broadcastMessage method is being executed by another thread.
 +
 
 +
=== 繰り返しタスク ===
 +
 
 +
繰り返し実行されるタスクです。遅延時間も設定可能です。
  
    @EventHandler
+
==== 具体例 ====
    public void onJoin(PlayerJoinEvent event) {
 
        // 20ticks後に1度だけ実行される処理を、実装しつつ、そのままスケジュールします。
 
       
 
        new BukkitRunnable() {
 
  
            @Override
+
This submits a task to an async thread to be executed after 3 seconds with a period of 10 seconds.
            public void run() {
+
<blockquote><source lang="java">
                // スケジューラで実行される処理内容を、ここに実装します。
+
myPlugin.getServer().getScheduler().scheduleAsyncRepeatingTask(myPlugin, new Runnable() {
                getServer().broadcastMessage(
+
 
                    "サーバーへようこそ!説明文をちゃんと読んでね!");
+
  public void run() {
            }
+
      System.out.println("This message is printed by an async thread");
        }.runTaskLater(this, 20);
+
  }
        // ↑そのままスケジュールします。
+
}, 60L, 200L);
    }
 
}
 
 
</source></blockquote>
 
</source></blockquote>
  
== BukkitScheduler ==
+
Both times count in server ticks. The initial delay is 60/20 = 3 seconds and the period is 200/20 = 10 seconds.
  
BukkitSchedulerは、[http://docs.oracle.com/javase/1.5.0/docs/api/index.html?java/lang/Runnable.html Runnable]クラスや[http://docs.oracle.com/javase/1.5.0/docs/api/index.html?java/util/concurrent/Callable.html Callable]クラスの、どちらかまたは両方を実装したクラスを、一定時間後に実行する機能を提供します。<br>
+
The above code will print "This message is printed by an async thread" every 10 seconds.
詳細は、[http://jd.bukkit.org/rb/apidocs/index.html?org/bukkit/scheduler/BukkitScheduler.html BukkitSchedulerのjavadoc] を参照してください。<br>
 
なお、BukkitSchedulerのメソッドで実行できる内容は、BukkitRunnableのメソッドで実行できる内容と全く同じです。
 
  
{{warning}} ただし、BukkitSchedulerでBukkitRunnableを実行することも可能ですが、BukkitRunnableのcancel()メソッドを使ってスケジューラをキャンセルすることができないことに注意してください。<br>
+
=== 戻り値を返すタスク ===
{{warning}} この実装方法は、CraftBukkit 1.7.10-R0.1 で非推奨に設定されました。今後は前述のBukkitRunnableのメソッドを呼び出す方法を使用してください。
 
  
=== 実装例1 ===
+
スケジューラから戻り値を返す事ができるタスクです。メインスレッドから実行可能なタスクです。
  
無名クラスでRunnableを実装し、20ticks後に実行される例を示します。
+
この仕組みの目的は、他タスクから呼ばれたAPIのメソッドに戻り値を返させる事です。
 +
 
 +
スケジューラは、戻り値として返されるであろうオブジェクトを取得可能にします。
 +
 
 +
==== 具体例 ====
 
<blockquote><source lang="java">
 
<blockquote><source lang="java">
import org.bukkit.Bukkit;
+
Future<String> returnFuture = myPlugin.getServer().getScheduler().callSyncMethod(myPlugin, new Callable<String>() {
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);
 
    }
 
}
 
</source></blockquote>
 
  
=== 実行例2 ===
+
  public String call() {
 +
      return "This is the string to return";
 +
  }
  
無名クラスでRunnableを実装し、20ticksごとにプラグインが終了するまでずっと実行され続ける例を示します。
+
});
<blockquote><source lang="java">
 
import org.bukkit.Bukkit;
 
import org.bukkit.plugin.java.JavaPlugin;
 
import org.bukkit.scheduler.BukkitScheduler;
 
  
public final class ExamplePlugin extends JavaPlugin {
+
try {
     public void onEnable() {
+
     // This will block the current thread
        BukkitScheduler scheduler = Bukkit.getServer().getScheduler();
+
    String returnValue = returnFuture.get();
        scheduler.scheduleSyncRepeatingTask(this, new Runnable() {
+
    System.out.println(returnValue);
            @Override
+
} catch (InterruptedException e) {
            public void run() {
+
    System.out.println("Interrupt triggered which waiting on callable to return");
                // ここに処理を実装する。
+
} catch (ExecutionException e) {
            }
+
    System.out.println("Callable task threw an exception");
        }, 0L, 20L);
+
     ee.getCause().printStackTrace();
     }
 
 
}
 
}
 
</source></blockquote>
 
</source></blockquote>
  
=== BukkitTask ===
+
This will submit the callable and then use the Future.get() method. This method sleeps the current thread until the Callable has returned a value and gets the result.
 +
 
 +
===== メインスレッドからの利用 =====
 +
この方法は'''非推奨'''です。
 +
この仕組みは、メインスレッドから利用される事を想定したものではありません。
  
BukkitTaskクラスのオブジェクトは、Runnableがスケジュールされたときに返されます。<br>
+
<code>''.get()''</code>メソッドを利用している場合、メインスレッドがこのタスクを完了するまでの間、現行スレッド(=メインスレッド)をスリープ状態にします。
このオブジェクトは、スケジューラにより実行されるスケジュールタスクを表現しています。<br>
 
詳細は、[http://jd.bukkit.org/rb/apidocs/index.html?org/bukkit/scheduler/BukkitTask.html BukkitTaskのjavadoc] を参照してください。
 
  
=== Callable と Future  ===
+
どうしてもメインスレッド上で使用する必要がある場合は、返されるであろうスケジューラの戻り値に対する<code>''.get()''</code>メソッドを呼び出す前に、<code>''.isDone()''</code>メソッドをチェックしなければなりません。さもなくばメインスレッドは、メインスレッド自身がタスクを完了させるまでスリープ状態になります。
  
Callableクラスは、同期処理を呼び出したときに、[http://docs.oracle.com/javase/1.5.0/docs/api/index.html?java/util/concurrent/Future.html Future]クラスのオブジェクトを返します。<br>
+
== Thread Safe API Methods  ==
これはJavaで提供されている機能やクラスです。<br>
 
詳細は、[http://docs.oracle.com/javase/1.5.0/docs/api/index.html?java/util/concurrent/Callable.html Callableのjavadoc] や、[http://docs.oracle.com/javase/1.5.0/docs/api/index.html?java/util/concurrent/Future.html Futureのjavadoc] を参照してください。
 
  
== スレッドの安全性のためのヒント ==
+
Bukkit API methods which are thread safe are:
  
1. 非同期実行タスクは、BukkitのAPIを直接実行してはいけません。<br>
+
*All the scheduler methods
2. 非同期タスクのコレクションにアクセスしたり、内容を変更しないでください。通常のコレクションはスレッドセーフではありません。<br>
+
*[http://jd.bukkit.org/apidocs/org/bukkit/Bukkit.html#getServer() Bukkit.getServer()]
3. 非同期タスクは、同期タスクをスケジュールすることができます。<br>
+
*[http://jd.bukkit.org/apidocs/org/bukkit/Server.html#getBukkitVersion() Server.getBukkitVersion()]
4. 同期タスクは、非同期タスクをスケジュールすることができます。
+
*[http://jd.bukkit.org/apidocs/org/bukkit/World.html#getUID() World.getUID()]
 +
*[http://jd.bukkit.org/apidocs/org/bukkit/World.html#getMaxHeight() World.getMaxHeight()]
 +
*[http://jd.bukkit.org/apidocs/org/bukkit/World.html#getSeed() World.getSeed()]
 +
*[http://jd.bukkit.org/apidocs/src-html/org/bukkit/World.html#line.51 World.getBlockTypeIdAt(int x, int y, int z)]
 +
*[http://jd.bukkit.org/apidocs/org/bukkit/entity/Entity.html#getEntityId() Entity.getEntityId()]
 +
*[http://jd.bukkit.org/apidocs/org/bukkit/entity/Entity.html#getUniqueId() Entity.getUniqueId()]
 +
**This list is not complete....

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

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

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

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