跳到主要内容

取消传送

现在,玩家只要输入了命令后,到时间就一定会被传送走。但是我希望像ESS中传送那样,玩家可以在传送前的三秒内通过移动来取消传送。

在这里我给出的思路是添加一个临时监听器,具体实现如下。

public class MoveListener implements Listener {
public MoveListener(UUID uuid,ScheduledTask task){
this.uuid=uuid;
this.task=task;
}
private final UUID uuid;
private final ScheduledTask task;
@EventHandler
public void cancelTeleport(PlayerMoveEvent e){
if(e.getPlayer().getUniqueId().equals(uuid)){
if(!task.getExecutionState().equals(ScheduledTask.ExecutionState.CANCELLED)&&!task.getExecutionState().equals(ScheduledTask.ExecutionState.FINISHED)){
task.cancel();
e.getPlayer().sendMessage("你移动了!");
e.getPlayer().sendTitle("传送已取消","");
}
HandlerList.unregisterAll(this);
}
}
}

其中,getExecutionState()用于获取任务状态,这是Folia中提供的API,这里我用来判断调度任务是否取消或结束。

同时,为了防止一些误判,我希望在玩家输入命令0.5秒后才可以取消。

于是在上一步中的代码便修改成了这样

        Random random = new Random();
Location target = player.getLocation().set(random.nextInt(500),100,random.nextInt(500));
player.sendTitle("三秒后传送","...");
//随机传送实现
task = player.getScheduler().runDelayed(this, new Consumer<ScheduledTask>() {
@Override
public void accept(ScheduledTask scheduledTask) {
player.teleportAsync(target);
//播报
Bukkit.getGlobalRegionScheduler().run(instance, new Consumer<ScheduledTask>() {
@Override
public void accept(ScheduledTask scheduledTask) {
getServer().dispatchCommand(getServer().getConsoleSender(),"say "+player.getName()+"刚刚随机传送了!");
}
});
}
}, new Runnable() {
@Override
public void run() {
System.out.println("错误:玩家不存在!");
}
}, 60L);

//移动取消
Bukkit.getAsyncScheduler().runDelayed(instance, new Consumer<ScheduledTask>() {
@Override
public void accept(ScheduledTask t) {
player.sendMessage("尝试注册监听器");
Bukkit.getPluginManager().registerEvents(new MoveListener(player.getUniqueId(),task),instance);
}
},50*10L,TimeUnit.MILLISECONDS);

AsyncScheduler

聪明的读者肯定想到了,我肯定会用一个新的调度方式!这里使用的是AsyncScheduler,它就类似BukkitAPI中的runXXXAsynchronously,不像Region与Entity,它有一种“Global”的感觉,但GlobalRegion又不是什么调度都能塞给他,如果你用GlobalRegion注册例子中的监听器,你会发现监听器是无法工作的。

同样的,像前面的玩家传送,你也可以使用这个调度程序来实现:

        Bukkit.getAsyncScheduler().runDelayed(this,new Consumer<ScheduledTask>() {
@Override
public void accept(ScheduledTask scheduledTask) {
player.teleportAsync(target);
}
}, 60L*50, TimeUnit.MILLISECONDS);

但是考虑上文说到的实体调度的优点以及传送中玩家实体消失的问题,我不建议你这么使用。

哦!差点忘了,看到这里有一个TimeUnit了吗?

这是一个时间单位的枚举类

    NANOSECONDS(TimeUnit.NANO_SCALE),     //纳秒
MICROSECONDS(TimeUnit.MICRO_SCALE), //微秒
MILLISECONDS(TimeUnit.MILLI_SCALE), //毫秒
SECONDS(TimeUnit.SECOND_SCALE), ///秒
MINUTES(TimeUnit.MINUTE_SCALE), //分
HOURS(TimeUnit.HOUR_SCALE), //时
DAYS(TimeUnit.DAY_SCALE); //日

由于1s = 20tick , 于是1t = 50 TimeUnit.MILLISECONDS。 如果你想要时间为x t的话, 你需要填填50*x, 最后填入TimeUnit.MILLISECONDS。

这是一个奇怪的设定,我不太理解为什么这个地方不统一用tick...