本篇教程由作者设定使用 CC BY-NC 协议。

教程背景

PktTweaker 发布后,有整合包作者表示出疑惑,认为其介绍轻描淡写,十分模糊,其作用也是让人稀里糊涂。

本教程会尽可能解释 PktTweaker 的背景和作用。

C/S 设计模式和网络通信

Minecraft 是经典的基于 C/S 设计模式的游戏,由客户端、服务端两部分组成。

整合包作者在编写脚本时,常常要考虑当前游戏位于“哪一侧”,以避免“双侧问题”(只需要执行一次的逻辑在服务端和客户端各被执行一次)和“异侧问题”(在错误侧执行逻辑,导致逻辑无效或游戏崩溃)。

服务端和客户端执行的逻辑显然是不尽相同的,因此网络通信的引入是必要的。网络通信主要有两种目的:

  • 保证客户端和服务端同步;

  • 保证客户端能够告知服务端玩家的变化。

网络通信最简单可靠的实现方式则是通过网络包(Packet)来传递消息(Message),消息由特定的方式存储数据,以方便发送和接收。一侧接收到来自另一侧的消息后,则可以根据消息的类型和数据,在接收侧作出一些改动,以达到同步效果。

Minecraft 中数个需要手动网络通信的经典例子

Minecraft 原版已实现了多种网络包收发,因此,我们在游戏中的大多数操作都是双端同步的,如放置 / 破坏方块等。

然而,部分数据并不自动同步,下面我们列出几个较为经典的例子。

按键绑定的使用状况

玩家按键绑定的使用状况(某按键是否被按下、已被按下多长时间)不会被自动同步到服务端

玩家速度

虽然可能很奇怪,但是大多数时间在服务端试图获取玩家速度都会无功而返。玩家速度是在客户端计算的,且只有在玩家跳跃、被击退等情况下才会被短暂地同步到服务端

实体 NBT

实体 NBT 不会被自动同步到客户端

PktTweaker 的原理

在模组开发中,通常使用 SimpleImpl 管理自定义网络包。

一般地,模组开发者可以轻而易举地创建多个不同的网络包类。但是想让整合包作者也打上这种富裕仗,成本是巨大的。因此,作者仅实现了两种网络包 C2SMessage 和 S2CMessage,分别对应“客户端->服务器”、“服务器->客户端”两种发包方向。

作者在这两种网络包中引入两个信息:网络包的唯一标识符(id)和数据(data)。相应地,添加了由事件注册的网络包处理策略类——同样分为 C2S、S2C 两种类型,包含唯一标识符(id)和处理策略(strategy)。

收到 PktTweaker 定义的网络包时,先会寻找唯一标识符与网络包唯一标识符相同的网络包处理策略,然后利用其策略处理网络包提供的数据。这样就能在有限的两个网络包类中区分不同的使用场景,实现不同的逻辑。