Bukkit服务端插件开发(二)

有点摸了,现在开始复习下学期考试然后每天只修一点bug,所以这一部分的内容不是很多

线程分离

在进行插件的测试过程中,存在一个问题,输入指令/debugvote之后,记分牌能够正常工作

但是由于在这之前插件的运行是单个线程,在插件的相关程序运行的过程中需要等待程序运行结束

这就导致了在客户端看来能够正常移动,但是在这个过程中服务端却是在运行命令程序的

PS:突然想起来之前用一些插件的时候也会出现类似的问题不知道是不是也是这个原因

Bukkit中提供了org.bukkit.scheduler中的相关类用来解决线程的问题,但是进行异步调用的时候,子线程中是不能调用Bukkit中提供的API的

准确来说,是只能调用Bukkit中提供的线程安全的API

关于线程安全的问题,在mcbbs上了解到关于MC本身的一些操作不是线程安全的,具体的函数还不是很清楚

但是,在插件开发的过程中最好是将一些需要调用API的方法放到主线程中进行实现

对于这个问题,可以通过在异步线程中调用Bukkit中的同步调用

在项目中的实现如下:

  • DanmakuListener中开启异步线程对弹幕进行监听
1
2
3
4
5
6
7
8
9
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length > 0 || !(sender instanceof Player)) {
return false;
}

BukkitTask task = new DanmakuListenTask(plugin).runTaskAsynchronously(plugin);
return true;
}

这里是在接收到debugvote命令时运行的程序,开启了一个写好的监听任务异步线程

在之前的实现中,直接在运行过程调用了Bukkit中的API,导致出现了相关的异常错误,下面对于相关的函数调用实现如下:

1
2
3
4
5
6
7
8
objective.unregister();
int index = getResult();
Bukkit.getScheduler().runTask(plugin, () -> {
// 这里是在主线程中实现Bukkit api的调用,具体的实现原理不是很清楚
Bukkit.broadcastMessage("观众选择了事件:" + events[index].getEventName());
Bukkit.broadcastMessage("事件效果:" + events[index].getDescription());
events[index].effect();
});

events[index].effect();的调用是调用了事件的生效方法,让投票最多的事件在服务器世界中进行生效

随机时间进行投票

随机时间的实现是比较容易的,直接调用Random类就可以实现

比较麻烦需要考虑的地方是在一个任务线程结束后/开始前,进行开关状态的检测以及线程的检测

在插件类DanmakuVote中设置一个变量count表示当前运行的任务线程数量,这个指只能为0或1

使用DanmakuTaskMonitor类进行每一次延时任务的启动

每一次任务结束就使用DanmakuTaskMonitor中的方法进行下一次任务的延时启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void addLaterTask() {
FileConfiguration config = plugin.getConfig();
int timeSpace;
if (config.getBoolean("Setting.random"))
timeSpace = RandomChooser.RandomTimeChoose(config.getInt("Setting.floor"), config.getInt("Setting.ceil"));
else
timeSpace = config.getInt("Setting.space");

if (! plugin.getSwitchStatus()) return ;

if (plugin.addThreadCount()) {
DanmakuListenTask task = new DanmakuListenTask(plugin);
task.runTaskLaterAsynchronously(plugin, timeSpace * Constants.onSecond);
Bukkit.getLogger().info(String.format("下一次投票任务定于%d秒后进行", timeSpace));
}
}

pluginaddThreadCount()方法返回一个布尔类型表示count的添加是否成功,同时,在插件任务开关为关的时候直接中断这一次执行

VoterunVote方法的最后,将任务切换到主线程中进行运行,在这里Bukkit.getScheduler().runTask方法是一个异步的操作,不会形成阻塞,所以monitor进行任务的添加放到该部分进行执行,保证当前下一次会进行的投票任务只有一个

在这个地方,考虑增加一个/bvote status命令查看下一次执行任务的信息

20200711 - 20200723 Ver1.0

  • 解决由于单线程导致监听过程中服务器暂停的问题
  • 实现插件对指定世界/玩家的操作
  • 某些情况下天气的修改会失效
  • 随机时间以及固定时间进行弹幕投票的实现
  • 完善 time random/static指令
  • 对于服务器中没有玩家的情况下进行任务的跳过
  • 实现随机选取事件的代码
  • 动物/怪物的随机选取
  • 基本功能完善,bug调试完成,线程问题待处理
  • 存在incorrect header check问题
  • 弹幕接收较快的情况下,对于一些弹幕的解析存在问题(貌似是因为b站会对频繁刷的弹幕进行屏蔽)
  • 进行相关命令的构思与完善
  • 线程数量问题修正