# 作业

学习了服务端与客户端之间的基本通信方法之后,我们可以尝试来完成一个使用模组SDK来实现的全息字的功能。

# 要求

在玩家加入游戏之后,在玩家客户端的指定位置生成一个文字面板(全息字)。

整个通信流程应该如下图所示:

sequenceDiagram
          客户端->>服务端: 客户端加载完毕
          服务端-->>客户端: 生成文字面板的参数
          客户端->>服务端: 生成文字面板,返回生成是否成功

为什么需要在客户端加载完毕的时候主动通知服务端,而不是直接监听服务端的PlayerJoinEvent?

因为在PlayerJoinEvent触发的时候,基岩版客户端可能还没有完全加载完毕。

在这个时候给客户端发送事件,有可能客户端模组还没有初始化完成,无法处理请求。

# 实现过程

# Spigot插件

首先新建Spigot项目,操作步骤和之前一致。这里新建了一个名为TutorialHologram的项目,并配置pom.xml,添加SpigotMaster插件的maven依赖。

在开始编写插件之前,我们预先定义好,客户端的命名空间为testHologram,因此,根据开发规范 (opens new window),客户端系统名应为testHologramBeh,服务端系统名应为testHologramDev

  • 客户端通知加载完毕的事件定义为ClientLoadFinishEvent
  • 传输生成文字面板的参数的事件定义为HologramParameterEvent
  • 返回生成结果的事件定义为HologramGeneratedEvent

推荐将这部分命名空间和事件名定义为常量。

教程为了方便将所有代码都写入主类,在实际开发过程中,不推荐将所有代码写入一个类中!!

	private final String NAMESPACE = "testHologram";
    private final String CLIENT_SYSTEM_NAME = "testHologramBeh";
    private final String SERVER_SYSTEM_NAME = "testHologramDev";
    private final String CLIENT_LOAD_FINISH_EVENT = "ClientLoadFinishEvent";
    private final String HOLOGRAM_PARAMETER_EVENT = "HologramParameterEvent";
    private final String HOLOGRAM_GENERATED_EVENT = "HologramGeneratedEvent";

首先,生成一个文字面板,需要提供这个文字面板的坐标,文本的信息,我们在作业中可以简单地硬编码坐标的位置和文本的内容。例如坐标为(0,100,0),内容为这是一个文字面板

在监听ClientLoadFinishEvent后,发送文字面板参数信息。同时监听HologramGeneratedEvent,输出结果。

	@Override
    public void onEnable() {
        spigotMaster = (SpigotMaster) Bukkit.getPluginManager().getPlugin("SpigotMaster");
        spigotMaster.listenForEvent(NAMESPACE, CLIENT_SYSTEM_NAME, CLIENT_LOAD_FINISH_EVENT, (player, map) -> {
            Map<String, Object> data = new HashMap<>();
            data.put("x", 0);
            data.put("y", 100);
            data.put("z", 0);
            data.put("text", "这是一个文字面板");
            spigotMaster.notifyToClient(player, NAMESPACE, SERVER_SYSTEM_NAME, HOLOGRAM_PARAMETER_EVENT, data);
        });
        spigotMaster.listenForEvent(NAMESPACE, CLIENT_SYSTEM_NAME, HOLOGRAM_GENERATED_EVENT, (player, map) -> {
            boolean success = (boolean) map.get("suc");
            getLogger().info("生成全息字 " + player.getName() + " " + success);
        });
    }

在HologramGeneratedEvent中,我们可以监听来自客户端的事件,并从中获取suc的值,来判断生成是否成功,可以继续拓展插件的功能。

这样我们的Spigot插件部分的代码就编写完成了,可以构建后装入服务器。

# 客户端模组

和之前的操作一样,新建一个插件,团队名称填写test,模组名填写hologram,勾选游戏服和大厅服。

生成完成后打开插件文件夹,删除developer_mods文件夹里的内容,并将整个插件文件夹复制到部署设置的模组目录中。

# 文字面板的生成

文字面板的API文档 点我 (opens new window)

通过文档的查阅,我们需要先后

  1. 创建文字面板
  2. 设置文字面板的位置
  3. 如有需要的话 返回服务器文字面板的ID

# 常量定义

之前在Spigot服插件编写的过程中已经定义了一些事件名常量,方便起见,我们也需要在客户端模组的常量文件中定义相同的常量。

hologramConst.py文件中定义下列变量

ClientLoadFinishEvent = "ClientLoadFinishEvent"
HologramParameterEvent = "HologramParameterEvent"
HologramGeneratedEvent = "HologramGeneratedEvent"

# 功能实现

首先在OnUiInitFinished函数中,向服务端系统发送事件。

# UI加载完成
def OnUiInitFinished(self, args):
    logger.info("%s OnUiInitFinished", HologramConst.ClientSystemName)
    self.NotifyToServer(HologramConst.ClientLoadFinishEvent, {})

接下来监听来自服务端系统的HologramParameterEvent事件,并设置回调函数。获取坐标,创建文字面板。

部分代码如下

def __init__(self, namespace, systemName):
    ClientSystem.__init__(self, namespace, systemName)
    self.mUIMgr = uiMgr.UIMgr()
    self.mTextBoardComp = clientApi.GetEngineCompFactory().CreateTextBoard(clientApi.GetLevelId())

    self.ListenForEvent(HologramConst.ModName, HologramConst.ServerSystemName, HologramConst.HologramParameterEvent, self, self.OnHologramParameter)
    self.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), HologramConst.UiInitFinishedEvent, self, self.OnUiInitFinished)

def OnHologramParameter(self, args):
    x = args["x"]
    y = args["y"]
    z = args["z"]
    text = args["text"]
    boardId = self.mTextBoardComp.CreateTextBoardInWorld(text, (1, 1, 1, 1), (0.5, 0.5, 0.5, 0.1), True)
    if not boardId:
        self.NotifyToServer(HologramConst.HologramGeneratedEvent, {"suc": False})
        return
    self.mTextBoardComp.SetBoardPos(boardId, (x, y, z))
    self.NotifyToServer(HologramConst.HologramGeneratedEvent, {"suc": True, "boardId": boardId})

def Destroy(self):
    self.UnListenForEvent(HologramConst.ModName, HologramConst.ServerSystemName, HologramConst.HologramParameterEvent, self, self.OnHologramParameter)
    self.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), HologramConst.UiInitFinishedEvent, self, self.OnUiInitFinished)
    if self.mUIMgr:
        self.mUIMgr.Destroy()

OnHologramParameter函数,会解析来自服务端的数据,在指定xyz坐标创建文字面板,如果失败,返回信息中suc是False。如果成功,suc为True,并附带文字面板的id。

# 部署测试

前往服务器配置,协议服,勾选刚刚编写的testHologram模组,重新部署后进入游戏进行测试。

进入游戏后,传送到坐标(0,100,0)附近,可以看到我们生成的文字面板。

要求

实现过程

Spigot插件

客户端模组

部署测试