# 客户端模组编写

本节将主要介绍如何制作客户端模组与Java服插件进行通信。

# 语言基础

编写中国版基岩版客户端模组需要掌握Python2.7、模组SDK。

该部分较为基础,需要开发者自行安装并学习。推荐提前安装补全库 (opens new window)

# 项目创建

在开始代码编写之前,首先需要创建项目。

切换到插件标签页,点击新建插件。

然后我们按照团队名,插件名称来填写信息,并且勾选大厅服/游戏服。

因为我们在之前已经在Spigot插件中定义好了插件的命名空间和系统名,所以我们这里按照下方截图填写,方便后面直接和Java服通信。

创建完成后,对插件右键,打开目录。就可以看到插件的目录结构。

  • behavior_packs - 行为包目录
  • developer_mods - 在开服工具2.0中无用
  • resource_packs - 资源包目录
  • worlds - 存档(在开服工具2.0中仅用来配置行为包和资源包)

在这里,我们主要需要编写的地方就是行为包目录、资源包目录

  • 行为包主要用来存放客户端模组的代码、物品定义、实体定义等等。
  • 资源包主要用来存放客户端模组的美术资源,文本资源等等。

因为developer_mods在开服工具2.0中没有用途,所以我们可以打开文件夹,将截图所示内容删除。

完成删除后,我们可以将整个testMod文件夹剪切到服务器配置中的Mod目录文件夹。

接下来,打开装有Python插件的IDEA或者PyCharm,对客户端模组进行脚本编辑。

在File->Open中复制文件路径,打开这个模组文件夹。

然后对testModBehavior右键,将其标记为Sources Root,这样补全库才能正常工作。

接下来,我们可以打开modConst.py,在这里可以看到这个模组的一些常量。

  • ModName 代表 模组命名空间
  • ClientSystemName 代表 模组客户端系统名

可以回顾一下Java服插件中的命名空间和客户端系统名,可以看到这它们是一一对应的。

只有在服务器和客户端通信时使用相同命名空间和系统名,通信数据才会被成功处理。

# -*- coding: utf-8 -*-

# 整个Mod的一些绑定配置
ModVersion = "1.0.0"
ModName = "testMod"
ClientSystemName = "testModBeh"
ClientSystemClsPath = "testModScript.modClientSystem.ModClientSystem"
ServerSystemName = "testModDev"
ServerSystemClsPath = "testModScript.modServerSystem.ModServerSystem"

# 引擎事件
UiInitFinishedEvent = "UiInitFinished"

接下来打开modClientSystem.py

import client.extraClientApi as clientApi

将文件顶部的代码修改为,方便正常使用补全库。

import mod.client.extraClientApi as clientApi

# 代码编写

功能需求:

  • 在玩家客户端UI初始化完成时,向服务器发送TestEvent事件,参数任意。
  • 监听服务器TestServerEvent,并打印信息到控制台。

会用到以下两个函数:

除此之外,还有更多的事件相关的接口,可以参考官方文档 (opens new window)

# 向服务器发送TestEvent事件

在创建项目后的模板中,已经生成了监听UI初始化完成的事件,我们可以直接在这个事件的回调函数中向服务器通信。

直接使用NotifyToServer函数即可。发送的数据是一个Python字典。

    # UI加载完成
    def OnUiInitFinished(self, args):
        logger.info("%s OnUiInitFinished", ModConst.ClientSystemName)
        self.NotifyToServer("TestEvent", {"data": "测试数据"})

Python的类型会被转换成Java的类型,对照表如下:

Python类型 Java类型
None null
bool Boolean
int/long(-2^31到2^31-1) Integer
int/long(-2^63到-2^31-1,2^31到2^63-1) Long
int/long(2^63到2^64-1) BigInteger
float Double
str String
list List<Object>
dict(key必须为str) Map<String, Object>

# 监听服务器TestServerEvent事件

我们可以在客户端系统初始化时,监听这个事件并注册回调函数。在Destroy时注销监听。

    def __init__(self, namespace, systemName):
        ClientSystem.__init__(self, namespace, systemName)
        self.mUIMgr = uiMgr.UIMgr()

        self.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), ModConst.UiInitFinishedEvent, self, self.OnUiInitFinished)
        self.ListenForEvent(ModConst.ModName, ModConst.ServerSystemName, "TestServerEvent", self, self.OnServerEvent)

    def OnServerEvent(self, args):
        print "OnServerEvent", args

    def Destroy(self):
        self.UnListenForEvent(ModConst.ModName, ModConst.ServerSystemName, "TestServerEvent", self, self.OnServerEvent)
        self.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), ModConst.UiInitFinishedEvent, self, self.OnUiInitFinished)
        if self.mUIMgr:
            self.mUIMgr.Destroy()

在这里,我们监听的命名空间,引用了ModConst中的ModName,对应Spigot插件中的命名空间testMod。还引用了ModConst中的ServerSystemName,对应Spigot插件中的系统名testModDev。因此这个监听函数将会正常监听来自服务器的信息。

# 部署测试

至此我们完成了客户端与服务端之间双端通信的最基础的实现。接下来将客户端模组进行部署,进入服务器测试。

找到服务器配置->游戏配置->协议服,勾选testMod。进行部署。

随后点击启动测试,进入游戏。并输入指令/apollotest

可以看到服务器控制台正常输出

客户端控制台也正常输出。

Python命令行执行:

"OnServerEvent {'msg': '\xe8\xbf\x99\xe6\x98\xaf\xe4\xb8\x80\xe6\x9d\xa1\xe6\x9d\xa5\xe8\x87\xaaJava\xe6\x9c\x8d\xe7\x9a\x84\xe6\xb6\x88\xe6\x81\xaf'}".decode("utf-8")
u"OnServerEvent {'msg': '\u8fd9\u662f\u4e00\u6761\u6765\u81eaJava\u670d\u7684\u6d88\u606f'}"

消息经过utf8解码,是我们传输的消息

语言基础

项目创建

代码编写

向服务器发送TestEvent事件

监听服务器TestServerEvent事件

部署测试