This repository has been archived by the owner on Feb 5, 2023. It is now read-only.
forked from randunel/node-srcds-rcon
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.js
157 lines (142 loc) · 5.23 KB
/
index.js
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
'use strict';
let Connection = require('./lib/connection');
let packet = require('./lib/packet');
let util = require('./lib/util');
module.exports = params => {
let address = params.address;
let password = params.password;
let _connection;
let nextPacketId;
return Object.freeze({
connect: connect,
command: command,
disconnect: disconnect
});
function connect() {
let connection = Connection(address);
return connection.create().then(() => _auth(connection));
}
function disconnect() {
return _connection.destroy().then(() => {
_connection = undefined;
});
}
function _auth(connection) {
let buf = packet.request({
id: 1,
type: packet.SERVERDATA_AUTH,
body: password
});
connection.send(buf);
return Promise.race([
util.promiseTimeout(3000).then(() => {
let err = new Error('Auth timeout');
return Promise.reject(err);
}),
connection.getData(dataHandler)
]).then(data => {
// TODO: data as a single type, not string/object
let res = packet.response(data);
if (res.id === -1) {
let err = new Error('Wrong rcon password');
return Promise.reject(err);
}
// Auth successful, but continue after receiving packet index
return connection.getData(dataHandler).then(() => {
_init(connection);
});
});
function dataHandler() {
// Auth response should only return 1 packet
return false;
}
}
function _init(connection) {
_connection = connection;
nextPacketId = 1;
}
function _getNextPacketId() {
return nextPacketId += 1;
}
function command(text, timeout) {
return Promise.race([
new Promise((resolve, reject) => {
if (!_connection) {
reject(new Error('not connected'));
}
let unexpectedPackets;
let responseData = new Buffer(0);
let reqId = _getNextPacketId();
let req = packet.request({
id: reqId,
type: packet.SERVERDATA_EXECCOMMAND,
body: text
});
let ackId = _getNextPacketId();
let ack = packet.request({
id: ackId,
type: packet.SERVERDATA_EXECCOMMAND,
body: ''
});
_connection.send(req);
_connection.send(ack);
_connection.getData(dataHandler).then(done);
function dataHandler(data) {
let res = packet.response(data);
if (res.id === ackId) {
return false;
} else if (res.id === reqId) {
// More data to come
responseData = Buffer.concat([responseData, res.payload], responseData.length + res.payload.length);
return true;
} else {
return handleUnexpectedData(res.id);
}
}
function done() {
let text = packet.convertPayload(responseData);
resolve(text);
}
function handleUnexpectedData(id) {
// Unexpected res.id, possibly from other commands
if (reqId > id) {
// Do nothing and keep listening, packets from older
// commands are still coming in
return true;
}
if ('undefined' === typeof unexpectedPackets) {
unexpectedPackets = new Map();
}
if (!unexpectedPackets.has(id)) {
if (unexpectedPackets.size >= 2) {
let err = new Error('Command lost');
err.details = {
reqId: reqId
};
if (responseData.length > 0) {
err.details.partialResponse = packet.convertPayload(responseData);
}
reject(err);
return false;
}
unexpectedPackets.set(id, 1);
return true;
}
unexpectedPackets.set(id, unexpectedPackets.get(id) + 1);
return true;
}
}),
new Promise((resolve, reject) => {
if ('number' === typeof timeout) {
return util.promiseTimeout(timeout).then(() => {
let err = new Error('Command timeout');
err.details = {
timeout: timeout
};
reject(err);
});
}
})
]);
}
};