2
1

More than 3 years have passed since last update.

【Minecraft】Bukkit/Spigotプラグインで時間操作

Last updated at Posted at 2020-05-04

2020年5月5日:コード上に些細なミスが有りましたので修正しました。


Bukkitのプラグイン作成の初めの方で、つまずいた点をメモ。

したいこと

Minecraftのチャット欄に特定のワードを入れると、トリガーがひかれてゲーム内時間を設定する。ここでは例として「朝」と入力すれば時刻0(朝)に変更するようなプラグインを作成する。

ダメな例

main.java
//パッケージ文、インポート文は省略
public class main extends JavaPlugin{
    @Override
    public void onEnable() {
        getServer().getPluginManager().registerEvents(new eventListener(this), this);
    }
}
eventListener.java
//パッケージ文、インポート文は省略
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);は同期的に実行しなければならない。

解決方法

いくつか解決方法はあるが、一番簡単な匿名クラスの実装でエラーを回避する。

main.java
//そのままで良い
eventListener.java
//パッケージ文、インポート文は省略
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を使う。




以上。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1