【Minecraft】使用Bukkit/Spigot插件可以控制时间

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()进行获取。

麻煩的是,在这里能成功编译。

为什么不行?

编译成功并且实际安装后没有出现错误。然而,当实际输入「朝」时,服务器端会出现两个错误。

[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会在下一个tick(服务器tick)执行注册的类。如果需要稍等一会儿才执行,可以使用runTaskLater代替。

请用中文将以下句子改述一遍:

以上。

广告
将在 10 秒后关闭
bannerAds