Zh:Query
Query是一个在1.9pre4引入用于查询服务端属性的UDP协议。它注定兼容UT3(或GameSpot)Query协议
一个稍简单的查询协议的替代方案包含连接到Minecraft主TCP端口并发送一个服务端列表Ping数据包,它将会返回MOTD、玩家的数量以及槽的数量。更多细节请见主协议页面。
所有的数据类型都是大字节序的,short
除外。
Contents
服务端配置
enable-query=true query.port=<1-65535>
默认的端口是建立在server-port
上的。
基础数据包格式
客户端至服务端数据包格式
字段名称 | 字段类型 | 注释 |
---|---|---|
Magic | short | 总是65277(0xFEFD) |
Type | byte | 9 代表握手,0 代表状态
|
Session ID | int32 | |
Payload | 可变 | 见下 |
服务端至客户端数据包格式
字段名称 | 字段类型 | 注释 |
---|---|---|
Type | byte | 9 代表握手,0 代表状态
|
Session ID | int32 | |
Payload | 可变 | 见下 |
握手
生成Session ID
会话ID(Session ID)用于区分你的请求。接下来的示例使用会话ID = 1(编码为十六进制转储00 00 00 01
)。
只有会话ID每一字节的低四位才会被使用,而高四位则不会被Minecraft处理。要将任何4字节的会话ID转换为有效的Minecraft会话ID,只需使用sessionId & 0x0F0F0F0F
进行位掩码。
请求
使用空负载发送一个请求。
字段名称 | 字段类型 | 示例 |
---|---|---|
Magic | short | FE FD
|
Type | byte | 09
|
Session ID | int32 | 00 00 00 01
|
Payload | (empty) |
转储:
FE FD 09 00 00 00 01
响应
该响应负载会是一个challenge token,编码为空终止字符串。你应该将其转换为int32并存储它。
字段名称 | 字段类型 | 示例 |
---|---|---|
Type | byte | 09
|
Session ID | int32 | 00 00 00 01
|
Challenge令牌字符串 | 空终止字符串 | "9513307\0"
|
在这个示例中,解析字符串"9513307\0"为整型并将他导报为大字节序的int32后,结果应为00 91 29 5B。
转储:
09 00 00 00 01 39 35 31 33 33 30 37 00 | .....9513307.
challenge令牌过期
引用有关Minecraft查询协议的Dinnerbone的原始文章:
注意challenge令牌绑定至你的IP和端口(与[session ID]相反),并持续达30秒。对,你没看错,这取决于;这不是“你的令牌将在将在30秒后过期”,而是“每个令牌都在30秒后过期”。这意味着你完全有可能得到一个令牌并在同一秒内使用它并使它过期了。
另外:
你需要提供你的challenge令牌,否则将不会收到任何回复。如果你提供了一个错误的令牌,那么仍不会收到任何回复。记住,如果你想存储你的challenge令牌并在以后使用,那么你需要在等待回复时设置一些超时,以防在服务器重启后你的令牌不再有效。不可能在没有任何额外请求的情况下区分离线服务器和拒绝你challenge的服务器,所以你需要尝试另一个challenge令牌,如果这也失败了,那么就标记它为不可用。
标准状态
请求
你的负载应该时你的challenge令牌,以int32打包。
字段名称 | 字段类型 | 示例 |
---|---|---|
Magic | short | FE FD
|
Type | byte | 00
|
Session ID | int32 | 00 00 00 01
|
Challenge令牌 | int32 | 00 91 29 5B
|
转储:
FE FD 00 00 00 00 01 00 91 29 5B
响应
字段名称 | 字段类型 | 示例 |
---|---|---|
Type | byte | 00
|
Session ID | int32 | 00 00 00 01
|
MOTD | Null-terminated string | "A Minecraft Server\0"
|
gametype | Null-terminated string | "SMP\0"
|
map | Null-terminated string | "world\0"
|
numplayers | Null-terminated string | "2\0"
|
maxplayers | Null-terminated string | "20\0"
|
hostport | Little endian short | DD 63 ( = 25565)
|
hostip | Null-terminated string | "127.0.0.1\0"
|
转储:
00 00 00 00 01 41 20 4D 69 6E 65 63 72 61 66 74 | .....A Minecraft 20 53 65 72 76 65 72 00 53 4D 50 00 77 6F 72 6C | Server.SMP.worl 64 00 32 00 32 30 00 DD 63 31 32 37 2E 30 2E 30 | d.2.20.##127.0.0 2E 31 00 | .1.
完整状态
本方法没5秒缓存一次。
请求
该请求与基础状态中时一样的,除了负载必须填充至8字节。最后发送0x00 0x00 0x00 0x00。
字段名称 | 字段类型 | 示例 |
---|---|---|
Magic | short | FE FD
|
Type | byte | 00
|
Session ID | int32 | 00 00 00 01
|
Challenge令牌 | int32 | 00 91 29 5B
|
Padding | 00 00 00 00
|
转储:
FE FD 00 00 00 00 01 00 91 29 5B 00 00 00 00
响应
该响应分为两个部分。第一部分是一个零终止字符串列表,代表了(key1、value1、key2、value2…)。第二部分是另一个零终止字符串列表,每一项代表一个玩家。
一个解析该负载的简单方法是忽略最前面的11字节,然后使用令牌\x00\x01player_\x00\x00
拆分响应。最后面是多余的零字节。
字段名称 | 字段类型 | 示例 | 注释 |
---|---|---|---|
Type | byte | 00
|
|
Session ID | int32 | 00 00 00 01
|
|
Padding | 11字节,常量 | 73 70 6C 69 74 6E 75 6D 00 80 00
|
无意义 |
K、V部分 | 多对零终止字符串 | hostname\0A Minecraft Server\0gametype\0SMP\0 ... hostip\0127.0.0.1\0\0
|
当你读取到0长度的键时终止(退出循环,不再读取值) |
Padding | 10字节,常量 | 01 70 6C 61 79 65 72 5F 00 00
|
无意义 |
Players部分 | 多个零终止字符串 | "barneygale\0TkTech\0 ... sadimusi\0\0"
|
当你读取到0长度的玩家名称时终止 |
转储:
00 00 00 00 01 73 70 6C 69 74 6E 75 6D 00 80 00 | .....splitnum... 68 6F 73 74 6E 61 6D 65 00 41 20 4D 69 6E 65 63 | hostname.A minec 72 61 66 74 20 53 65 72 76 65 72 00 67 61 6D 65 | raft Server.game 74 79 70 65 00 53 4D 50 00 67 61 6D 65 5F 69 64 | type.SMP.game_id 00 4D 49 4E 45 43 52 41 46 54 00 76 65 72 73 69 | .MINECRAFT.versi 6F 6E 00 42 65 74 61 20 31 2E 39 20 50 72 65 72 | on.Beta 1.9 Prer 65 6C 65 61 73 65 20 34 00 70 6C 75 67 69 6E 73 | elease 4.plugins 00 00 6D 61 70 00 77 6F 72 6C 64 00 6E 75 6D 70 | ..map.world.nump 6C 61 79 65 72 73 00 32 00 6D 61 78 70 6C 61 79 | layers.2.maxplay 65 72 73 00 32 30 00 68 6F 73 74 70 6F 72 74 00 | ers.20.hostport. 32 35 35 36 35 00 68 6F 73 74 69 70 00 31 32 37 | 25565.hostip.127 2E 30 2E 30 2E 31 00 00 01 70 6C 61 79 65 72 5F | .0.0.1...player_ 00 00 62 61 72 6E 65 79 67 61 6C 65 00 56 69 76 | ..barneygale.Viv 61 6C 61 68 65 6C 76 69 67 00 00 | alahelvig..
K、V部分
键 | 示例值 | 描述 |
---|---|---|
hostname | 'A Minecraft Server' | 当前服务端的MOTD |
game type | 'SMP' | 硬编码为SMP |
game_id | 'MINECRAFT' | 硬编码为MINECRAFT |
version | '1.2.5' | 服务端版本 |
plugins | 'CraftBukkit on Bukkit 1.2.5-R4.0:
WorldEdit 5.3; CommandBook 2.1' |
插件列表,原版服务端不使用,而只是一个空字符串(但仍然是零终止的,见上面的十六进制转储)。
这是由Dinnerbone提议的格式,且当前用于Bukkit: |
map | 'world' | 当前地图的名称 |
numplayers | '1' | 在线玩家的数量。该字符串可以解析至数字。 |
max players | '20' | 服务端的最大玩家数量。该字符串可以解析至数字。 |
host port | '25565' | 服务端端口。该字符串可以解析至数字。 |
host ip | '127.0.0.1' | 服务端监听/联系的IP地址。 |
示例实现
- https://github.com/barneygale/MCQuery (python)
- https://github.com/kmpm/node-mcquery (node.js)
- https://github.com/Dinnerbone/mcstatus (python)
- https://github.com/A2PLab/minelib (scala)
- https://github.com/ryanshawty/MCJQuery (java)
- https://github.com/Erikvv/Minecraft-Query-cpp (c++)
- https://github.com/winny-/mcstat (php)
- https://github.com/coNQP/mcipc (python 3.6)