2020年5月5日:コード上に些細なミスが有りましたので修正しました。
Bukkitのプラグイン作成の初めの方で、つまずいた点をメモ。
したいこと
Minecraftのチャット欄に特定のワードを入れると、トリガーがひかれてゲーム内時間を設定する。ここでは例として「朝」と入力すれば時刻0(朝)に変更するようなプラグインを作成する。
ダメな例
//パッケージ文、インポート文は省略
public class main extends JavaPlugin{
@Override
public void onEnable() {
getServer().getPluginManager().registerEvents(new eventListener(this), this);
}
}
//パッケージ文、インポート文は省略
public class eventListener implements Listener {
private final main m;
public eventListener(main m_){m=m_;}
@EventHandler
public void onPlayerChat(AsyncPlayerChatEvent event){
if(event.getMessage().equals("朝")){
event.getPlayer().getWorld().setTime(0L);
event.getPlayer().sendMessage("ワード「朝」を検知しました。");
m.getLogger().info(event.getPlayer().getName()+"さんがワード「朝」を発言しました");
}
}
}
プレイヤーが送ったチャットはAsyncPlayerChatEvent
でキャッチでき、プラグイン側で自由に操作できる。また、ここに書かれている、Worldは、イベントハンドラ内ではevent.getPlayer().getWorld()
で取得できる。
面倒なのは、これでコンパイルが通るということ。
なぜダメなのか
コンパイルが成功し、実際に導入してみてもエラーは出ない。しかし、実際に「朝」と入力したときに、サーバー側で2つのエラーが出る。
[00:00:00 ERROR]: Could not pass event AsyncPlayerChatEvent to hogehoge_plugin v1.0.0
org.bukkit.event.EventException: null
at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:312) ~[craftbukkit-1.15.2.jar:git-Bukkit-8160e29]
at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) ~[craftbukkit-1.15.2.jar:git-Bukkit-8160e29]
~略~
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:461) [craftbukkit-1.15.2.jar:git-Bukkit-8160e29]
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884) [craftbukkit-1.15.2.jar:git-Bukkit-8160e29]
at java.lang.Thread.run(Thread.java:830) [?:?]
Caused by: java.lang.IllegalStateException: TimeSkipEvent cannot be triggered asynchronously from another thread.
at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:510) ~[craftbukkit-1.15.2.jar:git-Bukkit-8160e29]
at org.bukkit.craftbukkit.v1_15_R1.CraftWorld.setFullTime(CraftWorld.java:803) ~[craftbukkit-1.15.2.jar:git-Bukkit-8160e29]
~略~
このドキュメントでも書かれている通り、AsyncPlayerChatEvent
基本的に非同期で実行される。しかし、event.getPlayer().getWorld().setTime(0L);
は同期的に実行しなければならない。
解決方法
いくつか解決方法はあるが、一番簡単な匿名クラスの実装でエラーを回避する。
//そのままで良い
//パッケージ文、インポート文は省略
public class eventListener implements Listener {
private final main m;
public eventListener(main m_){m=m_;}
@EventHandler
public void onPlayerChat(AsyncPlayerChatEvent event){
if(event.getMessage().equals("朝")){
m.getServer().getScheduler().runTask(m, new Runnable() {
@Override
public void run() {event.getPlayer().getWorld().setTime(0L);}
});
event.getPlayer().sendMessage("ワード「朝」を検知しました。");
m.getLogger().info(event.getPlayer().getName()+"さんがワード「朝」を発言しました");
}
}
}
非同期なAsyncPlayerChatEvent
から同期的な処理を呼び出すために、org.bukkit.scheduler
のスケジューラーを使用している。runTask
は、登録されたクラスを次のティック(サーバーティック)に実行する。実行まで少し待ちたい場合は、かわりにrunTaskLater
を使う。
以上。