前言

可能你会遇到这种情况,在安装盗版的cs1.6客户端后,你可能会刷出来一大堆奇奇怪怪的服务器,甚至什么服务器也刷不出来。这时,我们想要和互联网上的人对战时,就得事先知道人家的服务器IP地址和端口号,不然的话就得使用各种cs1.6的对战平台。这其实极大的限制了cs1.6的可玩性。

那么什么是cs1.6刷服补丁?什么又是MasterServers服务呢?

先简单的说明它们的作用,在我们运行我们的cs1.6客户端浏览互联网服务器时,其首先会读取cs1.6刷服补丁文件里边记录的MasterServers服务器地址,然后再从MasterServers服务器上获取其管理的cs1.6服务器IP和端口号,最后在浏览器列表中展示出来。

cs1.6刷服补丁,通常是指cs1.6客户端安装目录下的config目录下的MasterServers.vdf文件(不同版本可能是不同命名的*.vdf文件),文件的内容是文本,我们可以用记事本打开它,你会发现里边会有一对对的IP和端口号,这些IP和端口号,指向的就是MasterServers服务。

MasterServers服务,按照valve官网制订的MasterServers系列协议而编写的cs1.6服务器管理服务程序,主要功能就是响应cs1.6客户端的请求,返回与服务器相关的数据。当然,你可以在MasterServers系列协议的基础上,加上你个性化的管理功能。

MasterServer简单实现

所谓简单实现,这里是指仅实现MasterServer系列协议中的cs1.6服务器列表协议,即当客户端请求cs1.6服务器列表时,MasterServer服务就会给客户端返回当前已经记录的cs1.6服务器的IP和端口号。当然,MasterServer系列协议中的心跳包协议、单个服务器详情信息协议等,这里不做实现。

本篇文章使用nodejs来实现MasterServer服务,你也可以用php、c++或者其他的编程语言来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
var dgram = require('dgram');

// 返回给客户端的cs1.6服务器列表
var servers = [
"14.18.54.173:27015",
"14.21.37.194:27015",
"1.15.1.5:27015",
];

const startPacket = Buffer.from([0xFF, 0xFF, 0xFF, 0xFF, 0x66, 0x0A]);
const endPacket = Buffer.from([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);

// recieve from client for getting all servers: 31 FF 30 2E 30 2E 30 2E 30 3A 30 00 00

const server = dgram.createSocket('udp4', (msg, rinfo)=>{
// console.log(msg,rinfo.address,rinfo.port,rinfo.size);
// server.send('Hello I\'m server:',rinfo.port, rinfo.address);

if (msg.length == 0) {
console.log('Error Packet: length is 0');
return;
}

console.log(msg.toString('hex'));

var head = msg.readUInt8(0);
if (head != 49) { // 0x31
console.log('Error Packet: head is not 49');
return;
}

// var region = msg.readUInt8(1); // not use for now with sending all servers
// console.log(region.toString(16));

// var fromIPandPort = msg.toString('utf-8', 2); // not use for now with sending all servers
// console.log(fromIPandPort);

var replyBuf = Buffer.allocUnsafe(6+servers.length*6+6);
startPacket.copy(replyBuf, 0, 0, 6);
endPacket.copy(replyBuf, 6+servers.length*6, 0, 6);
// console.log(startPacket);
// console.log(replyBuf);
for (var i = 0; i < servers.length; i++) {
var ipport = servers[i].split(':');
var ipStr = ipport[0];
var portStr = ipport[1];
var ipStrArr = ipStr.split('.');
replyBuf.writeUInt8(Number(ipStrArr[0]), 6+i*6);
replyBuf.writeUInt8(Number(ipStrArr[1]), 6+i*6+1);
replyBuf.writeUInt8(Number(ipStrArr[2]), 6+i*6+2);
replyBuf.writeUInt8(Number(ipStrArr[3]), 6+i*6+3);
replyBuf.writeUInt16BE(Number(portStr), 6+i*6+4); // network ordered
}
console.log(replyBuf);
server.send(replyBuf, rinfo.port, rinfo.address);
});

server.on('error', (err)=>{
console.log(err);
});

server.on('listening', ()=>{
const address = server.address()
console.log(`server listening ${address.address} on ${address.port}`);
});

server.bind(27010);

master服务监听udp的27010端口,数组servers存储的是待返回的cs1.6服务器列表。在本示例中,我们将忽略协议中的过滤分页等字段,一律返回当前servers中的所有服务器列表。

PS: 如果不使用分页字段的话,根据客户端不同版本,最大可以接收3000~5000个服务器的列表
还有,使用分页字段时,服务端或者客户端都不能关闭当前的socket,否则始终会返回第一页
所以客户端第一次请求中的IP和端口参数最好是[31 FF 30 2E 30 2E 30 2E 30 3A 30 00 00]

  1. 首先,master会接收到客户端主动发送来的[31 FF 30 2E 30 2E 30 2E 30 3A 30 00 00]
  2. 然后,因为我们这里忽略了其他协议功能,所以只需要以第一个字节是否为0x31来判断合法请求
  3. 最后,我们将会把servers数据组装成replyBuf并发送给客户端即可

测试

  1. 修改cs1.6客户端安装目录下的config目录下的MasterServers.vdf文件,把里边的IP和端口分别修改为127.0.0.1和27010,若有*.vdf文件中已有多个IP和端口,只需修改其中一个即可;
  2. 运行master.js程序;
  3. 打开cs1.6客户端,刷新服务器,看是否服务器是servers数组中定义的服务器。

参考文献

Master Server Query Protocol