Warcraft局域网内数据格式
魔兽争霸III数据包规范
v0.3 by Soar Qin
(麦德三世 译)
1. 本文涉及的数据包种类
a) 魔兽争霸III拥有以下类型的数据包
i. 局域网UDP数据包。三月份去哪里旅游好
这类数据包都用于在游戏准备阶段广播/检测游戏信息。
ii. 游戏中TCP数据包。
这类数据包在游戏准备阶段和实际游戏阶段都会被用到。
我会分两部分来介绍这类数据包。
iii. Battle TCP/UDP 数据包。
有时间的话,我会用一份单独的doc文档来介绍这部分。
目前可暂且参考 .
b) 本规范中仅讨论前二类数据包。
2. 数据包头
魔兽争霸III中使用的所有数据包都拥有四字节的包头,具体如下表:

字节 / 类型
用途
1 / uint8
魔数标志位。
0xF7 – 局域网UDP或游戏中TCP数据包(本文中所有数据包都采用这个标志)
0xFF -- Battle战网数据包
1 / uint8
操作码。详情参考第345部分。
2 / uint16
数据包长度(包括4字节的包头部分)。
3. 局域网UDP数据包
a) 操作码0x2F
这种数据包用于查询局域网游戏,它可用于两种场合:1.应答0x310x32数据包以查询指定游戏的信息。2.玩家进入局域网游戏界面时发送。向255.255.255.255广播以查询所有可加入的游戏。
字节 / 类型
用途
4 / uint32
以低位在前的方式表示游戏类型。
'W3XP' = TFT    'WAR3' = ROC
4 / uint32
游戏版本。
比如War3 1.24,这里就填24
4 / uint32
游戏ID, 广播时置零。
b) 操作码0x30
用于应答UDP 0x2F数据包,该数据包包含完整的游戏信息。
字节 / 类型
用途
4 / uint32
以低位在前的方式表示操作系统信息。
'IX86' = Windows    'XMAC' = Mac OS X
4 / uint32
以低位在前的方式表示游戏类型。
4 / uint32
游戏ID
4 / uint32
系统时钟(比如Windows就用GetTickCount()来获得)
N/'\0'结束的字符串
这是一个经过编码的字符串,包含着大量重要的游戏信息。参考附注。
4 / uint32
玩家位置数。
4 / uint32
游戏标志。据我所知 0x01 = 剧情游戏;0x09 = 自定义游戏。
4 / uint32
游戏内玩家数。
4 / uint32
非电脑玩家位置数。
  综上所述,我们可以得出这样的公式:显示的游戏内玩家数=游戏内玩家数+(位置数-非电脑位置数)
4 / uint32
未知。通常是0-0x80
2 / uint16
以低位在前方式表示用于侦听连接的TCP游戏端口。

附注(编码字符串):
所有值为偶数的字节都+1。因此所有编码后的字节都是奇数。并用一个控制字节来保存接下去7字节的转换方式。
因为所有空字节都会变成1,因此编码字符串内不会包含空字节。但空字节用于表征字符串的结束。
编码字符串以一个控制字节开头。
控制字节中第1-7位(不包括第0位)分别依次对应控制字节后的7个字节。
0位无作用,永远设为1
解码方式如下:
如果对应位为'1',那么该字符不作修改。
如果对应位为'0',那么该字符的值要减去1
解码完一个控制字节其后的七个字节后,接下去的字节又是一个新的控制字节。
重复操作直到数据流中出现NULL字符。
C语言解码的样例:
char* EncodedString;
char* DecodedString;
char  mask;
int  pos=0, dpos=0;
杨晓京while (EncodedString[pos] != 0)
{
  if (pos%8 == 0) mask=EncodedString[pos];
  else
  {
    if ((mask & (0x1 << (pos%8))) == 0)
      DecodedString[dpos++] = EncodedString[pos] - 1;
    else
      DecodedString[dpos++] = EncodedString[pos];
  }
  pos++;
}
编码方案的另一种理解方式如下:
每个字符的第0位都移到控制字节,然后本身设为1
解码后字符串的结构:
字节 / 类型
用途
4 / uint32
游戏设置。
1 / uint8
永远为0
2 / uint16
地图宽度。
2 / uint16
地图高度。
4 / uint32
地图本地校验码。
N/'\0'结束的字符串
地图名。
N/'\0'结束的字符串
主机用户名。
1 / uint8
永远为0(也算个以'\0'结束的字符串?)
20 / uint8[20]
地图本地SHA-1哈希值,这一段是1.23版后添加的,以防止地图互通。
关于SHA-1哈希和地图校验的算法,我会在另一篇文章里讨论。
c) 操作码0x31
主机建立游戏后会向255.255.255.255广播该数据包。
字节 / 类型
用途
4 / uint32
以低位在前的方式表示游戏类型。
4 / uint32
游戏版本。
4 / uint32
游戏ID(初始为1,随着逐个游戏的创建而自增)。
d) 操作码0x32
游戏内玩家数发生变更后会向255.255.255.255广播该数据包。
字节 / 类型
用途
鬼泣 加点4 / uint32
游戏ID
4 / uint32
显示的游戏内玩家数。
4 / uint32
玩家位置总数。
e) 操作码0x33
游戏取消后会向255.255.255.255广播该数据包。
字节 / 类型
用途
4 / uint32
游戏ID
4. 游戏准备阶段的TCP
a) 操作码0x01
保持连接存活,也用于游戏进行中。
字节 / 类型
用途
4 / uint32
系统时钟(比如Windows就用GetTickCount()来获得)
b) 操作码0x04
向新加入的玩家发送,告诉他们各个玩家位置的状态及他们所处的位置。
字节 / 类型
用途
2 / uint16
接下去5段信息的总长度(到“开始位置数”为止,不包括本段的2个字节。)
1 / uint8
玩家位记录的条数,这里记为RN
9 * RN / 玩家位记录 * RN
每条玩家位记录代表每个玩家位的详细信息。每条记录的结构如下:
uint8  玩家ID0为电脑,0xFF为空。
uint8  地图下载进度。0-1000xFF代表'?'
uint8  玩家位状态。0-空,1-关闭,2-已有玩家。
uint8  是否电脑。0-活人,1-电脑。
uint8  队伍。0-1112代表ob/裁判。
uint8  颜。0-1112代表ob/裁判。
uint8  种族。
0x01-人类,0x02-兽人,
0x04-暗夜精灵,0x08-亡灵,
0x20-随机,0x40-地图未指定。
uint8  AI等级。0-简单,1-普通,2-疯狂
uint8  生命百分比。
通常为50,60,70,80,90,100。用GHost++可设为其他值。
4 / uint32
游戏的初始随机种子。
1 / uint8
队伍和种族锁定标记:
0x01 – 队伍已锁定
0x02 – 种族已锁定
0x04 – 种族为随机(无法与0x02共用)
1 / uint8
地图开始位置数。
1 / uint8
为新加入玩家分配的玩家位。
16 / sockaddr_in
主机用getpeername()获得的目标玩家地址。
c) 操作码0x06
向新加入的玩家发送,告诉他们已经在游戏中的玩家列表,每个对应玩家会发一个0x06的包。
字节 / 类型
用途
4 / uint32
始终为0x02,可能是sockaddr_in的结尾数字?
1 / uint8
在玩家位信息中显示的玩家ID
N/'\0'结束的字符串
玩家名。最长16字节(包括结尾的'\0')。
1 / uint8
后续的额外字节数。局域网游戏中总是1
N / uint8[N]
额外字节。局域网游戏中总是一个\0字节。
16 / sockaddr_in
缝肛门事件从getpeername()获得的该玩家地址。
16 / sockaddr_in
NAT路由(如果有)获得的该玩家地址(似乎只用于Battle战网)。
d) 操作码0x07
玩家离开游戏时向所有玩家发送。也用于游戏进行中。
字节 / 类型
用途
1 / uint8
玩家ID
4 / uint32
离开原因标志。这部分还需进一步研究。
e) 操作码0x08
当玩家的地图载入完成后向所有玩家发送(此时主机将会从客户端处收到0x23数据包),这样所有玩家的读取界面上,该玩家的姓名板背景会变成绿。
字节 / 类型
用途
1 / uint8
玩家ID
f) 操作码0x09
0x04基本相同,但缺少最后两段。当任何玩家位相关信息发生改变时向所有玩家发送。(比如说正在下载地图,玩家改变位置等等。)
字节 / 类型
用途
2 / uint16
接下去5段信息的总长度(到“开始位置数”为止,不包括本段的2个字节。)
1 / uint8
玩家位记录的条数,这里记为RN
9 * RN / 玩家位记录 * RN
每条玩家位记录代表每个玩家位的详细信息。每条记录的结构如下:
uint8  玩家ID0为电脑,0xFF为空。
uint8  地图下载进度。0-1000xFF代表'?'
uint8  玩家位状态。0-空,1-关闭,2-已有玩家。
uint8  是否电脑。0-活人,1-电脑。
uint8  队伍。0-1112代表ob/裁判。
uint8  颜。0-1112代表辐射新维加斯秘籍ob/裁判。
uint8  种族。
0x01-人类,0x02-兽人,
0x04-暗夜精灵,0x08-亡灵,
0x20-随机,0x40-地图未指定。
uint8  AI等级。0-简单,1-普通,2-疯狂
uint8  生命百分比。
通常为50,60,70,80,90,100。用GHost++可设为其他值。
4 / uint32
游戏的初始随机种子。
1 / uint8
队伍和种族锁定标记:
0x01 – 队伍已锁定
0x02 – 种族已锁定
0x04 – 种族为随机(无法与0x02共用)
1 / uint8
地图开始位置数。
g) 操作码0x0A
只包含4字节的包头,当主机按下开始游戏,并开始倒数5秒时发送。
h) 操作码0x0B
只包含4字节的包头,当5秒计时结束时发送,提醒所有玩家进入载入界面。
i) 操作码0x0F
向聊天信息的接收者发送。
如果是私密/小队频道的聊天,而且发送者和接收者可通过TCP连接,那么这个数据包是直接发送的。
否则玩家会先向主机发送一个0x28数据包,主机再发给所有接收者。
注意某些平台会阻断客户端之间的连接,因此所有聊天信息都要通过主机来传递,这就存在潜在的安全问题,可能导致私聊信息泄露。
字节 / 类型
用途
1 / uint8
需发送聊天信息的目标玩家数,这里记为PN
PN / uint8 * PN
每个uint8保存一个目标玩家ID
1 / uint8
聊天信息发送者的玩家ID
1 / uint8
聊天信息标志。
0x10-准备阶段,0x20-游戏阶段
4 / uint32
发送目标标志。
0x00 – 向所有玩家发送
0x01 – 向盟友发送
0x02 – OB/裁判发送
0x03-0x0E – 向特定玩家发送(玩家ID=-2
当聊天信息标志为0x10(即游戏准备阶段)时,这段信息将被排除。
N/'\0'结束的字符串
聊天字符串。
j) 操作码0x1B
只包含4字节的包头,向玩家发布,告诉他们你已断开游戏,即使是主动退出游戏的情况。同样适用于游戏进行阶段。
k) 操作码0x1E
这是当一个玩家加入游戏的时候向主机发送的第一份数据包,包含了该玩家的信息。
字节 / 类型
用途
4 / uint32
游戏ID
4 / uint32
游戏开始后经过的毫秒数。
1 / uint8
始终为0
2 / uint16
客户端的游戏端口。用于地图信息交换、直接私聊、主机变更等等。
4 / uint32
古戈尔
客户端会话计数器。初始值为1,每加入一个游戏都会自增。
N/'\0'结束的字符串
用户名。
2 / uint16
可用的公共端口。这里记为P
16 * P / sockaddr_in * P
每个公共端口都有一个套接字sockaddr_in结构。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。