客户端模组编写 
本节将主要介绍如何制作客户端模组与Java服插件进行通信。
语言基础 
编写中国版基岩版客户端模组需要掌握Python2.7、模组SDK。
该部分较为基础,需要开发者自行安装并学习。推荐提前安装补全库。
项目创建 
在开始代码编写之前,首先需要创建项目。
切换到插件标签页,点击新建插件。

然后我们按照团队名,插件名称来填写信息,并且勾选大厅服/游戏服。
因为我们在之前已经在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,并打印信息到控制台。
 
会用到以下两个函数:
除此之外,还有更多的事件相关的接口,可以参考官方文档。
向服务器发送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解码,是我们传输的消息