mirror of
https://github.com/gamedig/node-gamedig.git
synced 2024-11-17 17:25:19 +01:00
Add ping field, start improving README for 2.0
This commit is contained in:
parent
e937c725bb
commit
6496660633
3 changed files with 73 additions and 5 deletions
32
README.md
32
README.md
|
@ -443,9 +443,39 @@ gamedig --type minecraft --host mc.example.com --port 11234
|
|||
|
||||
The output of the command will be in JSON format.
|
||||
|
||||
Major Version Changes
|
||||
Changelog
|
||||
---
|
||||
|
||||
### 2.0
|
||||
##### Breaking changes
|
||||
* Node 8 is now required
|
||||
* Removed the `port_query` option. You can now pass either the server's game port **or** query port in the `port` option, and
|
||||
GameDig will automatically discover the proper port to query. Passing the query port is more likely be successful in
|
||||
unusual cases, as otherwise it must be automatically derived from the game port.
|
||||
* Removed `callback` parameter from Gamedig.query. Only promises are now supported. If you would like to continue
|
||||
using callbacks, you can use node's `util.callbackify` function to convert the method to callback format.
|
||||
* Removed `query` field from response object, as it was poorly documented and unstable.
|
||||
##### Minor Changes
|
||||
* Rewrote core to use promises extensively for better error-handling. Async chains have been dramatically simplified
|
||||
by using async/await across the codebase, eliminating callback chains and the 'async' dependency.
|
||||
* Replaced `--output pretty` cli parameter with `--pretty`.
|
||||
* You can now query from CLI using shorthand syntax: `gamedig --type <gameid> <ip>[:<port>]`
|
||||
* UDP socket is only opened if needed by a query.
|
||||
* Automatic query port detection -- If provided with a non-standard port, gamedig will attempt to discover if it is a
|
||||
game port or query port by querying twice: once to the port provided, and once to the port including the game's query
|
||||
port offset (if available).
|
||||
* Simplified detection of BC2 when using battlefield protocol.
|
||||
* Fixed buildandshoot not reading player list
|
||||
* Added new `connect` field to the response object. This will typically include the game's `ip:port` (the port will reflect the server's
|
||||
game port, even if you passed in a query port in your request). For some games, this may be a server ID or connection url
|
||||
if an IP:Port is not appropriate.
|
||||
* Added new `ping` field (in milliseconds) to the response object. Since icmp packets are often blocked by NATs, and node has poor support
|
||||
for raw sockets, this time is derived from the rtt of one of the UDP requests, tcp socket connection, or http requests made
|
||||
during the query.
|
||||
|
||||
|
||||
|
||||
|
||||
### 1.0
|
||||
* First official release
|
||||
* Node.js 6.0 is now required
|
||||
|
|
|
@ -21,6 +21,8 @@ class Core extends EventEmitter {
|
|||
// Sent to us by QueryRunner
|
||||
this.options = null;
|
||||
this.udpSocket = null;
|
||||
this.shortestRTT = 0;
|
||||
this.usedTcp = false;
|
||||
}
|
||||
|
||||
async runAllAttempts() {
|
||||
|
@ -68,7 +70,6 @@ class Core extends EventEmitter {
|
|||
}
|
||||
|
||||
async runOnce() {
|
||||
const startMillis = Date.now();
|
||||
const options = this.options;
|
||||
if (('host' in options) && !('address' in options)) {
|
||||
options.address = await this.parseDns(options.host);
|
||||
|
@ -94,13 +95,13 @@ class Core extends EventEmitter {
|
|||
// because lots of servers prefix with spaces to try to appear first
|
||||
state.name = (state.name || '').trim();
|
||||
|
||||
state.duration = Date.now() - startMillis;
|
||||
if (!('connect' in state)) {
|
||||
state.connect = ''
|
||||
+ (state.gameHost || this.options.host || this.options.address)
|
||||
+ ':'
|
||||
+ (state.gamePort || this.options.port)
|
||||
}
|
||||
state.ping = this.shortestRTT;
|
||||
delete state.gameHost;
|
||||
delete state.gamePort;
|
||||
|
||||
|
@ -148,6 +149,23 @@ class Core extends EventEmitter {
|
|||
else return await resolveStandard(host);
|
||||
}
|
||||
|
||||
/** Param can be a time in ms, or a promise (which will be timed) */
|
||||
registerRtt(param) {
|
||||
if (param.then) {
|
||||
const start = Date.now();
|
||||
param.then(() => {
|
||||
const end = Date.now();
|
||||
const rtt = end - start;
|
||||
this.registerRtt(rtt);
|
||||
}).catch(() => {});
|
||||
} else {
|
||||
this.debugLog("Registered RTT: " + param + "ms");
|
||||
if (this.shortestRTT === 0 || param < this.shortestRTT) {
|
||||
this.shortestRTT = param;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// utils
|
||||
/** @returns {Reader} */
|
||||
reader(buffer) {
|
||||
|
@ -186,6 +204,7 @@ class Core extends EventEmitter {
|
|||
* @returns {Promise<T>}
|
||||
*/
|
||||
async withTcp(fn, port) {
|
||||
this.usedTcp = true;
|
||||
const address = this.options.address;
|
||||
if (!port) port = this.options.port;
|
||||
this.assertValidPort(port);
|
||||
|
@ -216,6 +235,7 @@ class Core extends EventEmitter {
|
|||
socket.on('ready', resolve);
|
||||
socket.on('close', () => reject(new Error('TCP Connection Refused')));
|
||||
});
|
||||
this.registerRtt(connectionPromise);
|
||||
connectionTimeout = Promises.createTimeout(this.options.socketTimeout, 'TCP Opening');
|
||||
await Promise.race([
|
||||
connectionPromise,
|
||||
|
@ -284,10 +304,17 @@ class Core extends EventEmitter {
|
|||
let timeout;
|
||||
try {
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const start = Date.now();
|
||||
let end = null;
|
||||
socketCallback = (fromAddress, fromPort, buffer) => {
|
||||
try {
|
||||
if (fromAddress !== address) return;
|
||||
if (fromPort !== port) return;
|
||||
if (end === null) {
|
||||
end = Date.now();
|
||||
const rtt = end-start;
|
||||
this.registerRtt(rtt);
|
||||
}
|
||||
this.debugLog(log => {
|
||||
log(fromAddress + ':' + fromPort + " <--UDP");
|
||||
log(HexUtil.debugDump(buffer));
|
||||
|
@ -307,7 +334,6 @@ class Core extends EventEmitter {
|
|||
const wrappedTimeout = new Promise((resolve, reject) => {
|
||||
timeout.catch((e) => {
|
||||
this.debugLog("UDP timeout detected");
|
||||
let success = false;
|
||||
if (onTimeout) {
|
||||
try {
|
||||
const result = onTimeout();
|
||||
|
@ -331,6 +357,12 @@ class Core extends EventEmitter {
|
|||
}
|
||||
|
||||
async request(params) {
|
||||
// If we haven't opened a raw tcp socket yet during this query, just open one and then immediately close it.
|
||||
// This will give us a much more accurate RTT than using the rtt of the http request.
|
||||
if (!this.usedTcp) {
|
||||
await this.withTcp(() => {});
|
||||
}
|
||||
|
||||
let requestPromise;
|
||||
try {
|
||||
requestPromise = requestAsync({
|
||||
|
@ -344,7 +376,10 @@ class Core extends EventEmitter {
|
|||
.then((response) => log(params.uri + " <--HTTP " + response.statusCode))
|
||||
.catch(() => {});
|
||||
});
|
||||
const wrappedPromise = requestPromise.then(response => response.body);
|
||||
const wrappedPromise = requestPromise.then(response => {
|
||||
if (response.statusCode !== 200) throw new Error("Bad status code: " + response.statusCode);
|
||||
return response.body;
|
||||
});
|
||||
return await Promise.race([wrappedPromise, this.abortedPromise]);
|
||||
} finally {
|
||||
requestPromise && requestPromise.cancel();
|
||||
|
|
|
@ -4,7 +4,10 @@ const gbxremote = require('gbxremote'),
|
|||
class Nadeo extends Core {
|
||||
async run(state) {
|
||||
await this.withClient(async client => {
|
||||
const start = Date.now();
|
||||
await this.methodCall(client, 'Authenticate', this.options.login, this.options.password);
|
||||
this.registerRtt(Date.now()-start);
|
||||
|
||||
//const data = this.methodCall(client, 'GetStatus');
|
||||
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue