注意:本页上的内容适用于 Clanforge。如果使用 Multiplay Hosting,请参阅 Multiplay Hosting 文档。
服务器查询协议
通过服务器查询协议 (SQP),客户端可以使用 UDP/IP 数据包来检索关于运行中游戏服务器的信息。
客户端通过向服务器发送 ChallengeRequest
来发起查询。服务器通过发送包含 ChallengeId
的 ChallengeResponse
来进行响应。收到 ChallengeResponse
后,客户端可以继续查询服务器来获取信息。
以下各节介绍了 SQP 的技术规范,其中包括数据类型、请求类型和包格式。
SQP 支持的游戏服务器状态信息包括:
- 服务器名称:游戏服务器的名称。
- 版本 ID:游戏服务器所运行游戏镜像的标识符。
- 当前玩家数量:当前已连接的玩家数量。
- 最大玩家数量:可以加入游戏服务器的最大玩家数量。
- IP 地址:游戏服务器所在机器的 IP 地址。
- 端口:游戏服务器的端口,供客户端连接到主机上的特定游戏服务器。
- 游戏类型:游戏服务器所运行的游戏。
- 地图:游戏服务器上当前加载的游戏地图。
注意:SQP 与 Clanforge 游戏服务器监控进程协作,以确保游戏服务器报告的信息与 Clanforge 数据库中的信息一致。
要求
在实现 SQP 作为游戏服务器查询协议之前,您必须在游戏客户端一侧使用所有游戏服务器信息填充 ServerInfoData
对象。这样可以确保游戏服务器报告正确的信息。
参考实现
请参阅 go-svrquery中的一个 SQP 示例实现。go-svrquery 是一个 Golang 客户端,用于通过各种查询协议(包括 SQP)与游戏服务器通信。
数据类型
所有服务器查询都包含五种基本数据类型,这些数据都打包在一个数据流中。下表描述了每种数据类型。所有这些类型都是大端字节序。
Name | 描述 |
---|---|
byte | 8 位字符或无符号整数。 |
ushort | 16 位无符号整数。 |
uint | 32 位无符号整数。 |
ulong | 64 位无符号整数。 |
string | 可变长度的字节字段,采用 UTF-8 编码并以一个包含字符串长度的字节为前缀。字符串长度限制为 255 字节。 |
请求类型
客户端可以向服务器发出五种类型的请求。下表说明了客户端针对每种请求类型预期收到的服务器响应。
请求 | 响应 |
---|---|
ChallengeRequest | 服务器会返回一个挑战码,供客户端在查询请求响应中使用。 |
带有 ServerInfo 数据块的 QueryRequest | 服务器会返回关于服务器的基本信息,例如当前玩家数量、最大玩家数量、游戏类型和版本 ID。 |
数据包类型
下表列出了 SQP 数据包的各个不同类型。
数据包类型 | 字节值 |
---|---|
ChallengeRequest | 0 |
ChallengeResponse | 0 |
QueryRequest | 1 |
QueryResponse | 1 |
标头
无论是请求还是响应,所有 SQP 数据包都包含一个带有以下数据的标头。
Data | 类型 | 注释 |
---|---|---|
类型 | byte | 包含数据包类型。请参阅数据包类型。 |
ChallengeToken | uint | ChallengeToken 包含挑战码。QueryRequest 和 QueryResponse 数据包类型需要挑战码。请参阅挑战码。 |
有效负载 | 包含数据包的正文(如果数据包具有正文的话) |
挑战码
SQP 使用挑战码(随机生成的 32 位整数)来确保发出查询请求的客户端与收到挑战码的客户端是一致的。
当客户端首次向服务器发送请求时,服务器会随机选择挑战码并将其包含在数据包标头中。然后,客户端必须在所有后续请求的标头中包含相同的挑战码。
挑战数据包
客户端可以使用挑战数据包来检索用于后续请求的可用挑战码。
> 注意:挑战数据包只有一个标头,而没有有效负载。
请求格式
Data | 注释 |
---|---|
标头 | 请参阅标头。 |
以下代码片段提供了一个 ChallengeRequest
数据包示例:
[0x00000000] 0x00 '\0' unsigned char
[0x00000001] 0x00 '\0' unsigned char
[0x00000002] 0x00 '\0' unsigned char
[0x00000003] 0x00 '\0' unsigned char
[0x00000004] 0x00 '\0' unsigned char
响应格式
Data | 注释 |
---|---|
标头 | 请参阅标头。 |
以下代码片段提供了一个 ChallengeResponse
数据包示例:
[0x00000000] 0x00 '\0' unsigned char
[0x00000001] 0x80 '€' unsigned char
[0x00000002] 0x90 '•' unsigned char
[0x00000003] 0x23 '#' unsigned char
[0x00000004] 0x48 'H' unsigned char
查询数据包
客户端可以发送查询数据包来检索关于服务器的信息。Clanforge 只需要游戏服务器使用 ServerInfo
数据块来响应查询。您可以使用其他请求类型,但对于 Clanforge 而言并不是必需的。
数据块类型
客户端可以通过查询数据包来请求四种不同类型的数据块。不过,本文档只介绍了 ServerInfo
数据块类型,因为 Clanforge 仅支持该数据块类型。
每个数据块类型的字节值表示请求客户端期望收到的响应数据块。收到查询数据包时,服务器会检查请求中的 RequestedChunks
字段并使用数据块类型的字节值执行按位 AND
运算。如果该运算的结果大于零,则服务器便知道已请求了某个数据块类型。
例如,如果 RequestedChunks&1 > 0
,服务器会在响应时返回 ServerInfo
数据块。请参阅下面的注意事项。
> 注意:尽管 SQP 协议中提供了四种数据块类型,但是 Clanforge 仅支持 ServerInfo
数据块类型。
RequestedChunk 示例 | 含义 | 检查 |
---|---|---|
00000001 | ServerInfo | RequestedChunks & 0x01 > 0 |
00000010 | 保留 | RequestedChunks & 0x02 > 0 |
00000100 | 保留 | RequestedChunks & 0x04 > 0 |
00000010 | 保留 | RequestedChunks & 0x08 > 0 |
注意:RequestedChunks
可以同时请求多个数据块,因此您的实现必须能够对 RequestedChunks
值执行正确的按位检查。
请求格式
所有查询请求都采用相同的请求格式。
注意:Clanforge Hosting 只需要服务器响应 ServerInfo
请求。因此,这里忽略了所有其他请求类型。
Data | 类型 | 注释 |
---|---|---|
标头 | 请参阅标头。 | |
版本 | ushort | 包含要使用的 SQP 版本。 |
RequestedChunks | byte | 包含所请求的 ChunkTypes 。请参阅数据块类型。 |
以下代码片段提供了一个 QueryRequest
数据包示例:
[0x00000000] 0x01 '\x1' unsigned char
[0x00000001] 0x80 '€' unsigned char
[0x00000002] 0x31 '1' unsigned char
[0x00000003] 0xbe '¾' unsigned char
[0x00000004] 0x18 '\x18' unsigned char
[0x00000005] 0x00 '\0' unsigned char
[0x00000006] 0x01 '\x1' unsigned char
[0x00000007] 0x01 '\x1' unsigned char
响应格式
所有数据块类型的前四个字段(Version
、CurrentPacket
、LastPacket
和 PacketLength
)均遵循相同的标准响应格式。数据包 PacketLength
字段之后的信息因请求类型而异。
标准响应格式
下表列出了对于所有数据块类型都相同的响应部分。
Data | 类型 | 注释 |
---|---|---|
标头 | 请参阅标头。 | |
版本 | ushort | 包含要使用的 SQP 版本。 |
CurrentPacket | byte | 不支持。该值应该始终为 0。 |
LastPacket | byte | 不支持。该值应该始终为 0。 |
PacketLength | ushort | 包含数据包在此点之后的长度。 |
ServerInfo 响应格式
Data | 类型 | 注释 |
---|---|---|
对于在不同数据块类型之间保持不变的字段,请参阅标准响应格式。 | ||
ChunkLength | uint | 包含 ServerInfo 数据块在此点之后的长度。 |
CurrentPlayers | ushort | 包含服务器上的玩家数量。 |
MaxPlayers | ushort | 包含服务器支持的最大玩家数量。 |
ServerName | string | 包含服务器的名称。 |
GameType | string | 包含服务器上的游戏。 |
BuildId | string | 包含服务器的版本编号或版本 ID。 |
Map | string | 包含服务器当前加载的地图。 |
Port | ushort | 包含服务器所开放的游戏端口。 |
以下代码片段提供了一个 QueryResponse
数据包示例:
[0x00000000] 0x01 '\x1' unsigned char
[0x00000001] 0xc0 'À' unsigned char
[0x00000002] 0x7a 'z' unsigned char
[0x00000003] 0x6c 'l' unsigned char
[0x00000004] 0x3d '=' unsigned char
[0x00000005] 0x00 '\0' unsigned char
[0x00000006] 0x01 '\x1' unsigned char
[0x00000007] 0x00 '\0' unsigned char
[0x00000008] 0x00 '\0' unsigned char
[0x00000009] 0x00 '\0' unsigned char
[0x0000000a] 0x5b '[' unsigned char
[0x0000000b] 0x00 '\0' unsigned char
[0x0000000c] 0x00 '\0' unsigned char
[0x0000000d] 0x00 '\0' unsigned char
[0x0000000e] 0x57 'W' unsigned char
[0x0000000f] 0x00 '\0' unsigned char
[0x00000010] 0x00 '\0' unsigned char
[0x00000011] 0x00 '\0' unsigned char
[0x00000012] 0x10 '\x10' unsigned char
[0x00000013] 0x14 '\x14' unsigned char
[0x00000014] 0x55 'U' unsigned char
[0x00000015] 0x45 'E' unsigned char
[0x00000016] 0x34 '4' unsigned char
[0x00000017] 0x20 ' ' unsigned char
[0x00000018] 0x44 'D' unsigned char
[0x00000019] 0x65 'e' unsigned char
[0x0000001a] 0x64 'd' unsigned char
[0x0000001b] 0x69 'i' unsigned char
[0x0000001c] 0x63 'c' unsigned char
[0x0000001d] 0x61 'a' unsigned char
[0x0000001e] 0x74 't' unsigned char
[0x0000001f] 0x65 'e' unsigned char
[0x00000020] 0x64 'd' unsigned char
[0x00000021] 0x20 ' ' unsigned char
[0x00000022] 0x53 'S' unsigned char
[0x00000023] 0x65 'e' unsigned char
[0x00000024] 0x72 'r' unsigned char
[0x00000025] 0x76 'v' unsigned char
[0x00000026] 0x65 'e' unsigned char
[0x00000027] 0x72 'r' unsigned char
[0x00000028] 0x2e '.' unsigned char
[0x00000029] 0x2f '/' unsigned char
[0x0000002a] 0x53 'S' unsigned char
[0x0000002b] 0x63 'c' unsigned char
[0x0000002c] 0x72 'r' unsigned char
[0x0000002d] 0x69 'i' unsigned char
[0x0000002e] 0x70 'p' unsigned char
[0x0000002f] 0x74 't' unsigned char
[0x00000030] 0x2f '/' unsigned char
[0x00000031] 0x53 'S' unsigned char
[0x00000032] 0x68 'h' unsigned char
[0x00000033] 0x6f 'o' unsigned char
[0x00000034] 0x6f 'o' unsigned char
[0x00000035] 0x74 't' unsigned char
[0x00000036] 0x65 'e' unsigned char
[0x00000037] 0x72 'r' unsigned char
[0x00000038] 0x47 'G' unsigned char
[0x00000039] 0x61 'a' unsigned char
[0x0000003a] 0x6d 'm' unsigned char
[0x0000003b] 0x65 'e' unsigned char
[0x0000003c] 0x2e '.' unsigned char
[0x0000003d] 0x53 'S' unsigned char
[0x0000003e] 0x68 'h' unsigned char
[0x0000003f] 0x6f 'o' unsigned char
[0x00000040] 0x6f 'o' unsigned char
[0x00000041] 0x74 't' unsigned char
[0x00000042] 0x65 'e' unsigned char
[0x00000043] 0x72 'r' unsigned char
[0x00000044] 0x47 'G' unsigned char
[0x00000045] 0x61 'a' unsigned char
[0x00000046] 0x6d 'm' unsigned char
[0x00000047] 0x65 'e' unsigned char
[0x00000048] 0x5f '_' unsigned char
[0x00000049] 0x54 'T' unsigned char
[0x0000004a] 0x65 'e' unsigned char
[0x0000004b] 0x61 'a' unsigned char
[0x0000004c] 0x6d 'm' unsigned char
[0x0000004d] 0x44 'D' unsigned char
[0x0000004e] 0x65 'e' unsigned char
[0x0000004f] 0x61 'a' unsigned char
[0x00000050] 0x74 't' unsigned char
[0x00000051] 0x68 'h' unsigned char
[0x00000052] 0x4d 'M' unsigned char
[0x00000053] 0x61 'a' unsigned char
[0x00000054] 0x74 't' unsigned char
[0x00000055] 0x63 'c' unsigned char
[0x00000056] 0x68 'h' unsigned char
[0x00000057] 0x03 '\x3' unsigned char
[0x00000058] 0x30 '0' unsigned char
[0x00000059] 0x30 '0' unsigned char
[0x0000005a] 0x31 '1' unsigned char
[0x0000005b] 0x08 '\b' unsigned char
[0x0000005c] 0x48 'H' unsigned char
[0x0000005d] 0x69 'i' unsigned char
[0x0000005e] 0x67 'g' unsigned char
[0x0000005f] 0x68 'h' unsigned char
[0x00000060] 0x72 'r' unsigned char
[0x00000061] 0x69 'i' unsigned char
[0x00000062] 0x73 's' unsigned char
[0x00000063] 0x65 'e' unsigned char
[0x00000064] 0x1e '\x1e' unsigned char
[0x00000065] 0x61 'a' unsigned char