浅析Minecraft直播弹幕模组BakaDanmaku源码

写在一切之前

写在一切之前:从第一次玩Minecraft到现在大概有十年的光景了,那时候常见的版本号还是1.2.5,不禁令人感慨。而Minecraft本身的经久不衰,也离不开源源不断的UGC产出供玩家不断游玩、探索和讨论,其中及其重要的一环,就是mods(Modifications 的缩写,也叫模组,是指任何基于 Minecraft 原本游戏内容所作出的任何修改或在原本游戏内容中增加新的内容)。看了一些教程之后,觉得有必要了解一下Minecraft独特的mod文化背后,支撑mod本身的编写/运作方式了。

一、上下文

先简单介绍一下本次分析的主角,直播弹幕模组BakaDanmaku,开门见山地说,因为与我的工作内容有关联,所以挑选了他作为本次分析的主角。
下面是mcmod.cn给出的介绍

用于在游戏内聊天栏显示哔哩哔哩直播间弹幕的模组。只需要丢进模组文件夹、启动游戏、设定房间号即可连接直播弹幕服务器,实现在游戏内输出直播弹幕功能。
具体效果如下图所示:
在这里插入图片描述

该mod的GitHub地址:https://github.com/TartaricAcid/BakaDanmaku

二、分析前的一些假设 / 猜测

由于我工作涉及直播,对直播弹幕系统稍有了解,有以下的猜测/待验证

  1. 猜测1:使用长链与弹幕服务器连接
  2. 猜测2:制作者自行解析了websocket中包含的弹幕内容(协议)
  3. 疑问3:作者对协议的解析是否形成了接口定义,以供后续二次开发?
  4. 疑问4:相较于市面上成熟的第三方弹幕姬,它对于弹幕的解析有什么区别?
  5. 疑问5:对于与mc本身的交互,仅限于在左下角聊天框显示吗?

三、分析

1. 依赖/构建:

使用标准的forge开发规范forge gradle。同时可以看到,该mod是并没有前置依赖的。
. 依赖forge版本

2. 总体项目结构

在这里插入图片描述
看完代码结构,对于疑问3我们心里大概是有个底了:该mod并没有打算作为api将弹幕解析能力提供给二次开发或其他mod使用的想法,而是只服务于mod内部的弹幕展示。
这里不再给大家打哑谜,直接放出整体的时序图:
mod时序图

3. mod入口

BakaDanmaku
好家伙,入口类只做对象管理,说明不需要在此初始化很多变量。

4. 事件总线

in a file
作者也比较开门见山,把事件总线的消费和生产都放在了一处代码里。

  • 事件生产:
    SendDanmakuEvent类可以理解为一个消息类,描述了这个消息类应该在构造时含有一条只读的String信息。它在是实际向该mod的事件总线发送时被构造调用的。
    需要特别说明的是,这里的@Mod.EventBusSubscriber注解即定义了该事件总线是该mod专属的。
    实际用例:
    实际用例-进房
    SendDanmakuEvent类构造为对象,然后被发送进入mod的事件总线。

事件消费:
消费者的逻辑即为onSendDanmaku(SendDanmakuEvent event)这一个消费方法。

5. websocket / 弹幕协议解析

弹幕长链需要主动请求连接,连接后的规则是发起长链后,30s发送一次心跳包,以证明弹幕长链的客户端是存活的。

  • 发起长链&维持
    长链维持
    总体来说,长链的发起是通过发送了请求包发起的,而维持则是通过定时任务进行了定时的心跳包发送实现的。
    BakaDanmaku.HEART_BEAT_TASK = SERVICE.scheduleAtFixedRate(() -> sendMessage(site.getHeartBeat()),site.getHeartBeatInterval(), site.getHeartBeatInterval(), TimeUnit.MILLISECONDS);
    这一行代码即为维持心跳的逻辑。

  • 长链的消息结构:(摘自https://github.com/lovelyyoshino/Bilibili-Live-API/blob/master/API.WebSocket.md)
    在这里插入图片描述
    作者按照弹幕的类型进行了下发数据包内包含json的解析:
    解析
    最终返回的是String,然后通过事件总线,传递给GUI层。
    传递

7. GUI交互

最后的工作也就非常简单了,发送到聊天框

    @SubscribeEvent
    public static void onSendDanmaku(SendDanmakuEvent event) {
    
    
        Minecraft minecraft = Minecraft.getInstance();
        if (minecraft.gui != null) {
    
    
            minecraft.gui.handleChat(ChatType.CHAT, new TextComponent(event.getMessage()), Util.NIL_UUID);
        }
    }

四、回到问题上来

  1. 猜测1:使用长链与弹幕服务器连接
    正确的,按照长链Websocket协议
  2. 猜测2:制作者自行解析了websocket中包含的弹幕内容(协议)
    正确的,自行实现了一套解析逻辑
  3. 疑问3:作者对协议的解析是否形成了接口定义,以供后续二次开发?
    形成了一些interface,但是并不是对外开放的能力,不具备二次开发的能力,只考虑了兼容其他直播网站的情况(并且未实现,仍然只支持b站直播)
  4. 疑问4:相较于市面上成熟的第三方弹幕姬,它对于弹幕的解析有什么区别?
    编码层次感不足,没有规范化接口,性能上未测试
  5. 疑问5:对于与mc本身的交互,仅限于在左下角聊天框显示吗?
    还真是仅聊天框

五、Better?

  1. 实现一套更加OO的代码,暴露出对外的事件总线/interface定义。
  2. 更多交互?
  3. 目前仅支持单个房间的弹幕读取,可以考虑跨房间(多主播互动场景or多房间看板)。
  4. 需要手动配置json文件,老实说比较不方便,考虑其他配置模式。

猜你喜欢

转载自blog.csdn.net/qq_42739587/article/details/126375440