MineCraft-握手篇

文章出处: MineCraft协议-握手篇

与服务器会话的状态

mc客户端与服务器的会话,可能有这么几种状态.

  • Handshaking正在握手
  • Status 查询服务器状态
  • Login正在登入
  • Play正在进行游戏

与服务器建立连接后, 总是进入握手状态, 在握手过程中, 可以选择接下来进入Status状态或者Login状态, 登录成功后, 将进入Play状态.

常用的字段及和编码格式

定长整数

定长整数: Byte(一个字节), Sort(两个字节), Int(四个字节), Long(8个字节)和UnsignedByte(一个字节), UnsignedSort(两个字节), UnsignedInt(4个字节), UnsignedLong(8个字节).

均用大端法排列, 即表示高有效位的字节排在前面, 第一位是符号位, 负数用补码

变长整数

变长整数: VarInt, VarLong
VarInt用于编码int类型, 占1~5字节, 每个字节用低7位来编码数字. 最高一位如果位为1, 表示还有下一个字节. 最高位为0时, 表示没有下一字节了. 表示低有效位的字节排在前面, 表示高有效位的字节排在后面。

符号位无需单独处理, 不把符号位看成符号位即可

变长整数也是用大端法

比如:
127 = 0x00 00 00 80 =》 0x80 01
-1 = 0xFF FF FF FF =》 0xFF FF FF FF 0F

Varint表示比较小的整数时很节约字节,对于比较大的整数也能表示。但是对于任何负数, 它始终会占用5个字节

VarLong用于编码long类型,转化方法同理

字符串

字符串由两部分组成, 一个VarInt前缀, 表示字符串按UTF-8编码之后的长度, 接下来是用UTF-8编码的字符串本身

比如:
“FML”按UTF-8编码-》0x46 4D 4C-》长度为三字节, 3按Varint编码:0x03-》完整的字符串:0x03 46 4D 4C

包格式

mc将要发送的数据封装成包(packet)再发送给服务器或客户端

域名称 域类型 注释
长度 Varint 包数据的长度 + 包序号的长度
包序号 VarInt
数据 Byte Array 依赖于连接状态和包序号, 详见下面的部分

包序号(Packet ID), 或者更正确的叫包ID, 表示这个包的类型, 这个包是干什么的, 有哪些字段. 注意. 包可分为发往客户端的包和发往服务器的包两类. 这两类包的id是分开数据, 由字段依次连接而成, 具体包含哪些字段取决于包ID

mc握手和SLP

通过Server List Ping(SLP)可以得到服务器的有关信息(由几哥玩家在线, 服务器的名字版本号, 装了哪些mod)和连接的时延

具体过程如下:

Packet ID | Field Name Field Type | Notes
:- | :- | :- |:-
0x00 | protoco Version | Varint | the vers that the client plans on using to the server(which is not important for the ping). if the client is
pinging to determine what version to use, by convertion -1 shouled be set.
| Server Address | String | homstname or IP, e.g localhost or 127.0.0.1
| Server Port | Unsigned Short | default is 25565
| Next state | Varint | should be 1, for status

例如

1
2
3
4
5
00
D4 02
0E 31 32 37 2E 30 31 00 46 4D 4C 00
27 66
02

分行方便阅读

上面那串数字为原始的16进制数, 若要以byte形式发出服务器, 先把上面的16进制数换成10进制形式
包ID = 0, 对应第一行

字段

协议号Varint: 参考文章基于mc 1.12.2 协议号340, 对应第二行D4 02

服务器地址String: 字符串, ip或者域名, 比如127.0.0.1, 对应第三行. 测试用的forge客户端, 实际发送的是”127.0.0.1\0FML\0”, \0表示00, ASCII码空字符, FML显然是forge mod loader的缩写.

端口号UnsignedSort: 默认25565, 对应第四行27 66

下一个状态Varint: 只能是1或者2, 1表示进入Status状态, 2表示进入Login状态, 例子中是02, 要获取服务器信息则应该填01

客户端发Request包

Packet ID | Field Name Field Type | Notes
:- | :- | :- |:-
0x00 | no fields | |

对应的二进制应该是0x100. 第一个字节是包长度, 第二个字节包id, 数据部分没有

服务器回应Response包

Packet ID | Field Name Field Type | Notes
:- | :- | :- |:-
0x00 | JSON Response | String | see blow

一个字段, string, json格式的服务器信息