魔兽争霸跨网段对战原理与实现

1.魔兽争霸III冰封王座局域网联网过程

为了描述方便,先定义几个术语:

服务器:建立了游戏的主机

客户机:准备加入游戏的主机

分两种情况描述:

1.1客户机点击局域网时,服务器已经建立好了游戏

魔兽争霸启动后,当玩家点击“局域网”按钮时,会主动发送一次广播UDP包,内容如下:

F7 2F 10 00 50 58 33 57 14 00 00 00 00 00 00 00(客户询问包)

上述包数据,随魔兽争霸的版本不同而不同,但是只要是一个版本,这个数据是不变的。

目的就是询问本网段内所有的机器,看看是否有人建立了游戏。如果有人建立了游戏,那么建立游戏的机器就会返回一个地图信息的UDP单播包,地图数据随建立的游戏的不同而不同,下面仅举一例:

F7 30 8C 00 50 58 33 57 14 00 00 00 01 00 00 00
39 69 C2 00 E5 BD 93 E5 9C B0 E5 B1 80 E5 9F 9F
E7 BD 91 E5 86 85 E7 9A 84 E6 B8 B8 E6 88 8F 20
28 76 73 00 00 01 03 49 07 01 01 77 01 B9 79 01
99 D5 B9 31 4D CB 61 71 73 5D 45 6F 77 19 6F 6D
6F 61 65 5D 45 2B 6F 75 41 21 41 6D 6D 2B 73 75
61 73 73 21 77 B1 37 2F 37 31 63 2F 77 23 33 79
01 77 73 01 01 00 0A 00 00 00 01 00 00 00 01 00
00 00 0A 00 00 00 1B 00 00 00 E0 17 (地图包)

地图数据同时说明了,游戏的端口,本例就是E017,也就是6112.

客户机收到这个地图信息后就会显示出这个游戏,点击加入就可以进入了。

1.2客户机点击“局域网”时,服务器尚未建立游戏

此时客户机发送客户询问包后,没有任何回应,以后也不会主动发送询问包了。

待服务器建立好了游戏以后,会每隔大概3秒就在本网段广播一次UDP包,内容如下:

F7 32 10 00 00 00 00 00 01 00 00 00 00 00 00 00(建主信息包)

注意,上述内容的第五个字节,每次建立游戏都会不同。

客户端收到这个包后,会向服务器单播一个询问包,服务器收到后,单播返回地图包,客户机如果已经显示了这个游戏就会更新游戏信息,如果还没有就增加这个游戏显示,玩家点击即可进入。

2.跨网段联网的三个切入点与我的选择

2.1 跨网段发起主动包

从联网过程可以看到,联网过程可以从客户机发起,也可以从服务器发起,而且发起过程都是在本网段广播UDP包,客户机主动发起的时候是广播“询问包”,服务器发起的时候是广播“建主信息包”,以后的过程都是单播UDP。这样一来,由于广播智能在本网段进行,所以其他网段的机器无法通过广播来主动发起联网,也无法收到本网段机器主动发送的广播包,从而无法连入游戏。

知道了这个过程,就很容易想到跨网段连接过程,假设有两个网段A和B,用A(x)表示A网段的机器,B(x)表示B网段的机器,要让A(x)和B(x)互联,那么主动发起的一方就要把包发送到对方,显然通过广播是不行的,那就用单播吧。但问题又来了,UDP的端口要和魔兽游戏一样才行,这样魔兽才能收到回来的包。可魔兽的UDP端口是独占的没有办法和其他软件共享。看来一般的socket编程是不行了,可以采用winpcap构造底层包来完成这个工作。我用的是C#的封装Sharppcap。

2.2 三种联网方式

谁来发起主动连接呢,答案是不一定。客户机和服务器都可以。

(1)客户机发起主动连接

前提是必须知道服务器的IP地址,这样就可以向服务器主动发送"询问包”,从而引发以后的联网过程。

(2)服务器发起主动连接

前提是必须知道客户机的IP地址,这服务器端软件截获广播到本网段的“建主信息包”,转发到其他网段的客户机,从而引发以后的联网过程。

(3)服务器直接发送地图

前提是必须知道客户机的IP地址,服务器端软件截获发网本网段客户机的地图信息包,转发到其他网段的客户机。

上述方式中,(2),(3)都必须要截获后转发,而(1)只要发送就可以,因为对于固定版本的魔兽来说,“询问包”是恒定的。

简单的就是最好的,我的选择是通过(1)来完成跨网段联网。

3 联网软件在哪里运行

这个问题比较有意思,因为UDP可以伪造。也就是说,软件可以代表运行自身的主机发起主动包,也可以伪装成别人发起主动包。只要运行软件的主机能访问到接收主动包的机器就可以,可以在任意网段的任意机器上。

4.软件使用说明

4.1安装

本软件采用C#写成,采用.net Framework 4.0。故必须先安装有.net Framework4.0。另外需要winpcap,本软件包已经收录了,用户无需另外下载。安装.net Framework和winpcap后,即可直接运行本软件了。

4.2使用说明

需要填写的信息有:软件运行所在机器的网关IP, 魔兽服务器的IP,端口,客户端的IP,端口。客户端玩家点击“局域网”以后再运行本软件,客户端就可以看到游戏了。

4.3注意打开防火墙

防火墙要对魔兽开放,如果不会,直接关闭防火墙即可。

4.4软件下载

已放到了csdn资源里。http://download.csdn.net/source/3406656,需要源码的朋友QQ联系112633012。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用 C++ 实现魔兽争霸单位寻路的基本步骤: 1. 定义节点结构体,包含节点坐标、通行性、移动代价等信息。 ``` struct Node { int x, y; // 节点坐标 bool pass; // 是否可通行 int cost; // 移动代价 // 其他信息... }; ``` 2. 定义 A* 算法中的开放列表和关闭列表,以及计算估价函数的方法。 ``` // 开放列表和关闭列表 vector<Node> openList; vector<Node> closedList; // 计算估价函数 int heuristic(Node& node, Node& target) { int dx = abs(node.x - target.x); int dy = abs(node.y - target.y); return dx + dy; // 简单使用曼哈顿距离作为估价函数 } ``` 3. 实现 A* 算法的主体部分,包括将起点加入开放列表、从开放列表中取出节点、扩展节点、加入关闭列表等步骤。 ``` // A* 算法主体 bool aStar(Node& start, Node& target, vector<vector<Node>>& map) { openList.push_back(start); while (!openList.empty()) { // 取出估价函数值最小的节点 Node current = openList[0]; int currentIndex = 0; for (int i = 1; i < openList.size(); i++) { if (heuristic(openList[i], target) < heuristic(current, target)) { current = openList[i]; currentIndex = i; } } // 到达目标节点 if (current.x == target.x && current.y == target.y) { // 返回 true 或者记录路径等信息 return true; } // 从开放列表中删除当前节点,加入关闭列表 openList.erase(openList.begin() + currentIndex); closedList.push_back(current); // 扩展当前节点 vector<Node> neighbors; if (current.x > 0) neighbors.push_back(map[current.x - 1][current.y]); if (current.y > 0) neighbors.push_back(map[current.x][current.y - 1]); if (current.x < map.size()) neighbors.push_back(map[current.x + 1][current.y]); if (current.y < map[0].size()) neighbors.push_back(map[current.x][current.y + 1]); for (Node neighbor : neighbors) { // 跳过不可通行的节点和已经在关闭列表中的节点 if (!neighbor.pass || find(closedList.begin(), closedList.end(), neighbor) != closedList.end()) { continue; } // 计算新的移动代价 int newCost = current.cost + neighbor.cost; // 如果新的移动代价更小,或者邻居节点不在开放列表中,则更新邻居节点的代价和父节点 bool inOpenList = find(openList.begin(), openList.end(), neighbor) != openList.end(); if (newCost < neighbor.cost || !inOpenList) { neighbor.cost = newCost; neighbor.parent = &current; if (!inOpenList) { openList.push_back(neighbor); } } } } // 无法到达目标节点,返回 false 或者其他信息 return false; } ``` 4. 在实际使用时,需要根据地图和单位的移动能力等因素,初始化节点的通行性和移动代价等信息。 ``` // 初始化地图 vector<vector<Node>> map; for (int i = 0; i < mapSize; i++) { vector<Node> row; for (int j = 0; j < mapSize; j++) { Node node; node.x = i; node.y = j; node.pass = checkPassability(i, j); // 根据地形信息判断通行性 node.cost = calculateCost(i, j); // 根据地形和单位移动能力计算移动代价 row.push_back(node); } map.push_back(row); } // 初始化起点和终点 Node start = map[startX][startY]; Node target = map[targetX][targetY]; // 调用 A* 算法 bool success = aStar(start, target, map); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值