From 8ad0fbc87b1ca779918a1244f4ea5a09a1140a83 Mon Sep 17 00:00:00 2001 From: Tom <25043847+Douile@users.noreply.github.com> Date: Sat, 4 Jan 2020 01:03:02 +0000 Subject: [PATCH 01/41] Add discord protocol --- README.md | 7 ++++++- games.txt | 1 + protocols/discord.js | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 protocols/discord.js diff --git a/README.md b/README.md index 8f961db..5348f93 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,7 @@ Games List | `devastation` | Devastation (2003) | `dinodday` | Dino D-Day (2011) | `dirttrackracing2` | Dirt Track Racing 2 (2002) +| `discord` | Discord | [Notes](#discord) | `doom3` | Doom 3 (2004) | `dota2` | Dota 2 (2013) | `drakan` | Drakan: Order of the Flame (1999) @@ -451,6 +452,10 @@ For teamspeak 3 queries to work correctly, the following permissions must be ava Requires tshock server mod, and a REST user token, which can be passed to GameDig with the additional option: token +### Discord +You must set host to the server's guild ID instead of IP, this can be found in server widget settings (Server ID) or by enabling developer mode in client settings and right-clicking the server's icon. +In order to retrieve information from discord server's they must have the Enable server widget option enabled. + Usage from Command Line --- @@ -547,7 +552,7 @@ Changelog ##### Breaking API 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 +* 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 diff --git a/games.txt b/games.txt index 9f31c25..7d19884 100644 --- a/games.txt +++ b/games.txt @@ -87,6 +87,7 @@ deusex|Deus Ex (2000)|gamespy2|port=7791,port_query_offset=1 devastation|Devastation (2003)|unreal2|port=7777,port_query_offset=1 dinodday|Dino D-Day (2011)|valve|port=27015 dirttrackracing2|Dirt Track Racing 2 (2002)|gamespy1|port=32240,port_query_offset=-100 +discord|Discord|discord|port=443 dnl|Dark and Light (2017)|valve|port=7777,port_query=27015 dod|Day of Defeat (2003)|valve|port=27015 dods|Day of Defeat: Source (2005)|valve|port=27015 diff --git a/protocols/discord.js b/protocols/discord.js new file mode 100644 index 0000000..d79c140 --- /dev/null +++ b/protocols/discord.js @@ -0,0 +1,35 @@ +const Core = require('./core'); + +class Discord extends Core { + constructor() { + super(); + this.dnsResolver = { resolve: function(address) {return {address: address} } }; + } + + async run(state) { + console.log('SENDING HTTP Request'); + this.usedTcp = true; + console.log(this.options); + const raw = await this.request({ + uri: 'https://discordapp.com/api/guilds/' + this.options.address + '/widget.json', + }); + const json = JSON.parse(raw); + state.name = json.name; + if (json.instant_invite) { + state.connect = json.instant_invite; + } else { + state.connect = 'https://discordapp.com/channels/' + this.options.address + } + state.players = json.members.map(v => { + return { + name: v.username + '#' + v.discriminator, + username: v.username, + discriminator: v.discriminator, + team: v.status + } + }); + state.raw = json; + } +} + +module.exports = Discord; From 5dd7446eaeb8416f97f66185a638ff728bb7942f Mon Sep 17 00:00:00 2001 From: Tom <25043847+Douile@users.noreply.github.com> Date: Sat, 4 Jan 2020 01:03:48 +0000 Subject: [PATCH 02/41] Force host argument to be string (Int causes error) --- bin/gamedig.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/gamedig.js b/bin/gamedig.js index 720928e..f0e0126 100644 --- a/bin/gamedig.js +++ b/bin/gamedig.js @@ -4,7 +4,8 @@ const Minimist = require('minimist'), Gamedig = require('..'); const argv = Minimist(process.argv.slice(2), { - boolean: ['pretty','debug'] + boolean: ['pretty','debug'], + string: ['_'] }); const debug = argv.debug; From 0128e4d97226179b2696a3256b9e685fce4f35de Mon Sep 17 00:00:00 2001 From: Tom <25043847+Douile@users.noreply.github.com> Date: Sat, 4 Jan 2020 13:43:35 +0000 Subject: [PATCH 03/41] Remove extra logging Whoops --- protocols/discord.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/protocols/discord.js b/protocols/discord.js index d79c140..b428ab3 100644 --- a/protocols/discord.js +++ b/protocols/discord.js @@ -7,9 +7,7 @@ class Discord extends Core { } async run(state) { - console.log('SENDING HTTP Request'); this.usedTcp = true; - console.log(this.options); const raw = await this.request({ uri: 'https://discordapp.com/api/guilds/' + this.options.address + '/widget.json', }); From 3a8164cfbccaf05a95b0b2f953afaf7d69e68a93 Mon Sep 17 00:00:00 2001 From: Tom <25043847+Douile@users.noreply.github.com> Date: Sat, 4 Jan 2020 13:44:09 +0000 Subject: [PATCH 04/41] Set max players to total amount of members in server --- protocols/discord.js | 1 + 1 file changed, 1 insertion(+) diff --git a/protocols/discord.js b/protocols/discord.js index b428ab3..717f50b 100644 --- a/protocols/discord.js +++ b/protocols/discord.js @@ -26,6 +26,7 @@ class Discord extends Core { team: v.status } }); + state.maxplayers = json.presence_count; state.raw = json; } } From f337cf6f58d0b0da1435e49023a2e7cf5d9d33d9 Mon Sep 17 00:00:00 2001 From: Tom <25043847+Douile@users.noreply.github.com> Date: Sat, 4 Jan 2020 13:53:36 +0000 Subject: [PATCH 05/41] Remove discriminator's from player names The widget API seems to always set discriminator to 0000 --- protocols/discord.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/protocols/discord.js b/protocols/discord.js index 717f50b..c70b846 100644 --- a/protocols/discord.js +++ b/protocols/discord.js @@ -20,9 +20,7 @@ class Discord extends Core { } state.players = json.members.map(v => { return { - name: v.username + '#' + v.discriminator, - username: v.username, - discriminator: v.discriminator, + name: v.username, team: v.status } }); From f2177204beedbbacc71bd3bd52b87b4c7bd19ebc Mon Sep 17 00:00:00 2001 From: Pascal Sthamer Date: Sat, 15 Feb 2020 22:05:13 +0100 Subject: [PATCH 06/41] Fix wrong player count in minecraftvanilla protocol --- protocols/minecraftvanilla.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/protocols/minecraftvanilla.js b/protocols/minecraftvanilla.js index c55294e..7ea0a4b 100644 --- a/protocols/minecraftvanilla.js +++ b/protocols/minecraftvanilla.js @@ -54,9 +54,10 @@ class MinecraftVanilla extends Core { name: player.name }); } - } - for (let i = 0; i < Math.min(json.players.online, 10000); i++) { - state.players.push({}); + } else { + for (let i = 0; i < Math.min(json.players.online, 10000); i++) { + state.players.push({}); + } } } From f5238027c75515b5cc4cb87f06f67be39bbcda93 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer Date: Tue, 18 Feb 2020 14:52:04 +0100 Subject: [PATCH 07/41] Handle situtation where sample exists, but does not contain all players --- protocols/minecraftvanilla.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/protocols/minecraftvanilla.js b/protocols/minecraftvanilla.js index 7ea0a4b..f8b051c 100644 --- a/protocols/minecraftvanilla.js +++ b/protocols/minecraftvanilla.js @@ -47,6 +47,7 @@ class MinecraftVanilla extends Core { state.raw = json; state.maxplayers = json.players.max; + if(json.players.sample) { for(const player of json.players.sample) { state.players.push({ @@ -54,10 +55,13 @@ class MinecraftVanilla extends Core { name: player.name }); } - } else { - for (let i = 0; i < Math.min(json.players.online, 10000); i++) { - state.players.push({}); - } + } + + // players.sample may not contain all players or no players at all, depending on how many players are only. + // Insert a dummy player object for every online player that is not listed in players.sample. + // Limit player amount to 10.000 players for performance reasons. + for (let i = state.players.length; i < Math.min(json.players.online, 10000); i++) { + state.players.push({}); } } From f3b04d52d55a7242fe1cbbb3b93f629b9bd32c54 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer Date: Tue, 18 Feb 2020 14:53:39 +0100 Subject: [PATCH 08/41] Fix typo --- protocols/minecraftvanilla.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/minecraftvanilla.js b/protocols/minecraftvanilla.js index f8b051c..45dd6c5 100644 --- a/protocols/minecraftvanilla.js +++ b/protocols/minecraftvanilla.js @@ -57,7 +57,7 @@ class MinecraftVanilla extends Core { } } - // players.sample may not contain all players or no players at all, depending on how many players are only. + // players.sample may not contain all players or no players at all, depending on how many players are online. // Insert a dummy player object for every online player that is not listed in players.sample. // Limit player amount to 10.000 players for performance reasons. for (let i = state.players.length; i < Math.min(json.players.online, 10000); i++) { From 1c9ca2764e55303645f8a3fd058a52c9f191727f Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Sat, 4 Jul 2020 22:20:42 -0500 Subject: [PATCH 09/41] Update repo location --- README.md | 2 +- package.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7c3a79b..1ba1d9f 100644 --- a/README.md +++ b/README.md @@ -401,7 +401,7 @@ Games List > Want support for one of these games? Please open an issue to show your interest! > __Know how to code?__ Protocol details for many of the games above are documented -> at https://github.com/sonicsnes/legacy-query-library-archive +> at https://github.com/gamedig/legacy-query-library-archive > , ready for you to develop into GameDig! > Don't see your game listed here? diff --git a/package.json b/package.json index 2b9915b..ce74dc0 100644 --- a/package.json +++ b/package.json @@ -10,14 +10,14 @@ "server" ], "main": "lib/index.js", - "author": "Michael Morrison", + "author": "GameDig Contributors", "version": "2.0.20", "repository": { "type": "git", - "url": "https://github.com/sonicsnes/node-gamedig.git" + "url": "https://github.com/gamedig/node-gamedig.git" }, "bugs": { - "url": "https://github.com/sonicsnes/node-gamedig/issues" + "url": "https://github.com/gamedig/node-gamedig/issues" }, "license": "MIT", "engines": { From 67563c0f1e3aeecd0e9d56383c61066ca1082145 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Sat, 4 Jul 2020 22:36:29 -0500 Subject: [PATCH 10/41] Dependency updates --- package-lock.json | 155 +++++++++++++++++++--------------------------- package.json | 14 ++--- 2 files changed, 72 insertions(+), 97 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4aae7ac..b4beb96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,29 +1,29 @@ { "name": "gamedig", - "version": "2.0.14", + "version": "2.0.20", "lockfileVersion": 1, "requires": true, "dependencies": { "@types/cheerio": { - "version": "0.22.13", - "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.13.tgz", - "integrity": "sha512-OZd7dCUOUkiTorf97vJKwZnSja/DmHfuBAroe1kREZZTCf/tlFecwHhsOos3uVHxeKGZDwzolIrCUApClkdLuA==", + "version": "0.22.18", + "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.18.tgz", + "integrity": "sha512-Fq7R3fINAPSdUEhOyjG4iVxgHrOnqDJbY0/BUuiN0pvD/rfmZWekVZnv+vcs8TtpA2XF50uv50LaE4EnpEL/Hw==", "dev": true, "requires": { "@types/node": "*" } }, "@types/node": { - "version": "8.10.54", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.54.tgz", - "integrity": "sha512-kaYyLYf6ICn6/isAyD4K1MyWWd5Q3JgH6bnMN089LUx88+s4W8GvK9Q6JMBVu5vsFFp7pMdSxdKmlBXwH/VFRg==" + "version": "8.10.61", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.61.tgz", + "integrity": "sha512-l+zSbvT8TPRaCxL1l9cwHCb0tSqGAGcjPJFItGGYat5oCTiq1uQQKYg5m7AF1mgnEBzFXGLJ2LRmNjtreRX76Q==" }, "ajv": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz", - "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==", + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" @@ -63,9 +63,9 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" }, "barse": { "version": "0.4.3", @@ -84,9 +84,9 @@ } }, "bluebird": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", - "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==" + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, "boolbase": { "version": "1.0.0", @@ -112,9 +112,9 @@ } }, "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "requires": { "delayed-stream": "~1.0.0" } @@ -231,14 +231,14 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "forever-agent": { "version": "0.6.1", @@ -343,11 +343,11 @@ } }, "iconv-lite": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.0.tgz", - "integrity": "sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.1.tgz", + "integrity": "sha512-Gjcihg3Bi6PI+5V7JlqWmXyVDyX5UQuwulJcbb3btuSoXIoGUy8zwJpRIOpRSzHz0IVnsT2FkceLlM8mm72d3w==", "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "inherits": { @@ -412,27 +412,27 @@ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, "mime-db": { - "version": "1.37.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", - "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" }, "mime-types": { - "version": "2.1.21", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", - "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", "requires": { - "mime-db": "~1.37.0" + "mime-db": "1.44.0" } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", + "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==" }, "nth-check": { "version": "1.0.2", @@ -466,9 +466,9 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" }, "punycode": { "version": "2.1.1", @@ -492,9 +492,9 @@ } }, "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -503,7 +503,7 @@ "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", - "har-validator": "~5.1.0", + "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", @@ -513,46 +513,28 @@ "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", + "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" - }, - "dependencies": { - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } - } - } } }, "request-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.4.tgz", - "integrity": "sha512-8wgMrvE546PzbR5WbYxUQogUnUDfM0S7QIFZMID+J73vdFARkFy+HElj4T+MWYhpXwlLp0EQ8Zoj8xUA0he4Vg==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.5.tgz", + "integrity": "sha512-ZgnepCykFdmpq86fKGwqntyTiUrHycALuGggpyCZwMvGaZWgxW6yagT0FHkgo5LzYvOaCNvxYwWYIjevSH1EDg==", "requires": { "bluebird": "^3.5.0", - "request-promise-core": "1.1.2", + "request-promise-core": "1.1.3", "stealthy-require": "^1.1.1", "tough-cookie": "^2.3.3" } }, "request-promise-core": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", - "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", + "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", "requires": { - "lodash": "^4.17.11" + "lodash": "^4.17.15" } }, "safe-buffer": { @@ -571,9 +553,9 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "sshpk": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz", - "integrity": "sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -662,13 +644,6 @@ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "requires": { "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - } } }, "util-deprecate": { @@ -677,9 +652,9 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, "varint": { "version": "5.0.0", diff --git a/package.json b/package.json index ce74dc0..65db92b 100644 --- a/package.json +++ b/package.json @@ -27,13 +27,13 @@ "cheerio": "^1.0.0-rc.3", "compressjs": "^1.0.2", "gbxremote": "^0.2.1", - "iconv-lite": "^0.5.0", + "iconv-lite": "^0.6.1", "long": "^4.0.0", - "minimist": "^1.2.0", - "moment": "^2.24.0", + "minimist": "^1.2.5", + "moment": "^2.27.0", "punycode": "^2.1.1", - "request": "^2.88.0", - "request-promise": "^4.2.4", + "request": "^2.88.2", + "request-promise": "^4.2.5", "varint": "^5.0.0" }, "bin": { @@ -48,7 +48,7 @@ "README.md" ], "devDependencies": { - "@types/cheerio": "^0.22.13", - "@types/node": "^8.10.54" + "@types/cheerio": "^0.22.18", + "@types/node": "^8.10.61" } } From 487b17dd7384a26d4fb5195fdc583d394d12dd31 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Sat, 4 Jul 2020 23:28:03 -0500 Subject: [PATCH 11/41] Swap from request to got --- package-lock.json | 485 ++++++++++++++----------------------- package.json | 3 +- protocols/assettocorsa.js | 8 +- protocols/buildandshoot.js | 2 +- protocols/core.js | 25 +- protocols/fivem.js | 12 +- protocols/geneshift.js | 4 +- protocols/kspdmp.js | 6 +- protocols/terraria.js | 10 +- 9 files changed, 215 insertions(+), 340 deletions(-) diff --git a/package-lock.json b/package-lock.json index b4beb96..484f6e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,30 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@sindresorhus/is": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz", + "integrity": "sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==" + }, + "@szmarczak/http-timer": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", + "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@types/cacheable-request": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", + "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, "@types/cheerio": { "version": "0.22.18", "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.18.tgz", @@ -13,20 +37,30 @@ "@types/node": "*" } }, + "@types/http-cache-semantics": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", + "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==" + }, + "@types/keyv": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", + "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", + "requires": { + "@types/node": "*" + } + }, "@types/node": { "version": "8.10.61", "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.61.tgz", "integrity": "sha512-l+zSbvT8TPRaCxL1l9cwHCb0tSqGAGcjPJFItGGYat5oCTiq1uQQKYg5m7AF1mgnEBzFXGLJ2LRmNjtreRX76Q==" }, - "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "@types/node": "*" } }, "amdefine": { @@ -39,34 +73,6 @@ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", - "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" - }, "barse": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/barse/-/barse-0.4.3.tgz", @@ -75,28 +81,29 @@ "readable-stream": "~1.0.2" } }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "cacheable-lookup": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.3.tgz", + "integrity": "sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w==" + }, + "cacheable-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", + "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^2.0.0" + } }, "cheerio": { "version": "1.0.0-rc.3", @@ -111,12 +118,12 @@ "parse5": "^3.0.1" } }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", "requires": { - "delayed-stream": "~1.0.0" + "mimic-response": "^1.0.0" } }, "commander": { @@ -157,18 +164,25 @@ "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "requires": { - "assert-plus": "^1.0.0" + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "defer-to-connect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", + "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==" }, "dom-serializer": { "version": "0.1.1", @@ -201,13 +215,12 @@ "domelementtype": "1" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "once": "^1.4.0" } }, "entities": { @@ -220,41 +233,6 @@ "resolved": "https://registry.npmjs.org/event-to-promise/-/event-to-promise-0.7.0.tgz", "integrity": "sha1-ywffzUGNoiIdkPd+q3E7wjXiCQ8=" }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, "gbxremote": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/gbxremote/-/gbxremote-0.2.1.tgz", @@ -267,12 +245,30 @@ "xmlrpc": "^1.3.1" } }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", "requires": { - "assert-plus": "^1.0.0" + "pump": "^3.0.0" + } + }, + "got": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/got/-/got-11.4.0.tgz", + "integrity": "sha512-XysJZuZNVpaQ37Oo2LV90MIkPeYITehyy1A0QzO1JwOXm8EWuEf9eeGk2XuHePvLEGnm9AVOI37bHwD6KYyBtg==", + "requires": { + "@sindresorhus/is": "^2.1.1", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.4.5", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" } }, "graceful-readlink": { @@ -280,20 +276,6 @@ "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, "htmlparser2": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", @@ -332,14 +314,18 @@ } } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "http2-wrapper": { + "version": "1.0.0-beta.4.6", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.4.6.tgz", + "integrity": "sha512-9oB4BiGDTI1FmIBlOF9OJ5hwJvcBEmPCqk/hy314Uhy2uq5TjekUZM8w8SPLLlUEM+mxNhXdPAXfrJN2Zbb/GQ==", "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "quick-lru": "^5.0.0", + "resolve-alpn": "^1.0.0" } }, "iconv-lite": { @@ -355,50 +341,22 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "keyv": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz", + "integrity": "sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw==", "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" + "json-buffer": "3.0.1" } }, "lodash": { @@ -411,18 +369,15 @@ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" }, "minimist": { "version": "1.2.5", @@ -434,6 +389,11 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==" }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" + }, "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", @@ -442,10 +402,18 @@ "boolbase": "~1.0.0" } }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "p-cancelable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==" }, "parse5": { "version": "3.0.3", @@ -455,30 +423,29 @@ "@types/node": "*" } }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" }, "readable-stream": { "version": "1.0.34", @@ -491,50 +458,17 @@ "string_decoder": "~0.10.x" } }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } + "resolve-alpn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz", + "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==" }, - "request-promise": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.5.tgz", - "integrity": "sha512-ZgnepCykFdmpq86fKGwqntyTiUrHycALuGggpyCZwMvGaZWgxW6yagT0FHkgo5LzYvOaCNvxYwWYIjevSH1EDg==", + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", "requires": { - "bluebird": "^3.5.0", - "request-promise-core": "1.1.3", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - } - }, - "request-promise-core": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", - "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", - "requires": { - "lodash": "^4.17.15" + "lowercase-keys": "^2.0.0" } }, "safe-buffer": { @@ -552,27 +486,6 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" - }, "string-to-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string-to-stream/-/string-to-stream-1.1.1.tgz", @@ -616,60 +529,20 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "^2.1.0" - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, "varint": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz", "integrity": "sha1-2Ca4n3SQcy+rwMDtaT7Uddyynr8=" }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "xmlbuilder": { "version": "8.2.2", diff --git a/package.json b/package.json index 65db92b..f4add8f 100644 --- a/package.json +++ b/package.json @@ -27,13 +27,12 @@ "cheerio": "^1.0.0-rc.3", "compressjs": "^1.0.2", "gbxremote": "^0.2.1", + "got": "^11.4.0", "iconv-lite": "^0.6.1", "long": "^4.0.0", "minimist": "^1.2.5", "moment": "^2.27.0", "punycode": "^2.1.1", - "request": "^2.88.2", - "request-promise": "^4.2.5", "varint": "^5.0.0" }, "bin": { diff --git a/protocols/assettocorsa.js b/protocols/assettocorsa.js index e3fe845..1bbb9ba 100644 --- a/protocols/assettocorsa.js +++ b/protocols/assettocorsa.js @@ -3,12 +3,12 @@ const Core = require('./core'); class AssettoCorsa extends Core { async run(state) { const serverInfo = await this.request({ - json: true, - uri: `http://${this.options.address}:${this.options.port}/INFO` + url: `http://${this.options.address}:${this.options.port}/INFO`, + responseType: 'json' }); const carInfo = await this.request({ - json: true, - uri: `http://${this.options.address}:${this.options.port}/JSON|${parseInt(Math.random() * 999999999999999, 10)}` + url: `http://${this.options.address}:${this.options.port}/JSON|${parseInt(Math.random() * 999999999999999, 10)}`, + responseType: 'json' }); if (!serverInfo || !carInfo || !carInfo.Cars) { diff --git a/protocols/buildandshoot.js b/protocols/buildandshoot.js index 7598ae5..824eedc 100644 --- a/protocols/buildandshoot.js +++ b/protocols/buildandshoot.js @@ -4,7 +4,7 @@ const Core = require('./core'), class BuildAndShoot extends Core { async run(state) { const body = await this.request({ - uri: 'http://'+this.options.address+':'+this.options.port+'/', + url: 'http://'+this.options.address+':'+this.options.port+'/', }); let m; diff --git a/protocols/core.js b/protocols/core.js index 9791970..fb9e1ed 100644 --- a/protocols/core.js +++ b/protocols/core.js @@ -2,7 +2,7 @@ const EventEmitter = require('events').EventEmitter, net = require('net'), Reader = require('../lib/reader'), HexUtil = require('../lib/HexUtil'), - requestAsync = require('request-promise'), + got = require('got'), Promises = require('../lib/Promises'), Logger = require('../lib/Logger'), DnsResolver = require('../lib/DnsResolver'); @@ -40,11 +40,10 @@ class Core extends EventEmitter { let abortCall = null; this.abortedPromise = new Promise((resolve,reject) => { abortCall = () => reject(new Error("Query is finished -- cancelling outstanding promises")); + }).catch(() => { + // Make sure that if this promise isn't attached to, it doesn't throw a unhandled promise rejection }); - // Make sure that if this promise isn't attached to, it doesn't throw a unhandled promise rejection - this.abortedPromise.catch(() => {}); - let timeout; try { const promise = this.runOnce(); @@ -342,24 +341,26 @@ 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. + async tcpPing() { + // This will give a much more accurate RTT than using the rtt of an http request. if (!this.usedTcp) { await this.withTcp(() => {}); } + } + + async request(params) { + await this.tcpPing(); let requestPromise; try { - requestPromise = requestAsync({ + requestPromise = got({ ...params, - timeout: this.options.socketTimeout, - resolveWithFullResponse: true + timeout: this.options.socketTimeout }); this.debugLog(log => { - log(() => params.uri + " HTTP-->"); + log(() => params.url + " HTTP-->"); requestPromise - .then((response) => log(params.uri + " <--HTTP " + response.statusCode)) + .then((response) => log(params.url + " <--HTTP " + response.statusCode)) .catch(() => {}); }); const wrappedPromise = requestPromise.then(response => { diff --git a/protocols/fivem.js b/protocols/fivem.js index b091ccc..20af4c2 100644 --- a/protocols/fivem.js +++ b/protocols/fivem.js @@ -12,18 +12,18 @@ class FiveM extends Quake2 { await super.run(state); { - const raw = await this.request({ - uri: 'http://' + this.options.address + ':' + this.options.port + '/info.json' + const json = await this.request({ + url: 'http://' + this.options.address + ':' + this.options.port + '/info.json', + responseType: 'json' }); - const json = JSON.parse(raw); state.raw.info = json; } { - const raw = await this.request({ - uri: 'http://' + this.options.address + ':' + this.options.port + '/players.json' + const json = await this.request({ + url: 'http://' + this.options.address + ':' + this.options.port + '/players.json', + responseType: 'json' }); - const json = JSON.parse(raw); state.raw.players = json; state.players = []; for (const player of json) { diff --git a/protocols/geneshift.js b/protocols/geneshift.js index 891db5e..ce0946f 100644 --- a/protocols/geneshift.js +++ b/protocols/geneshift.js @@ -2,8 +2,10 @@ const Core = require('./core'); class GeneShift extends Core { async run(state) { + await this.tcpPing(); + const body = await this.request({ - uri: 'http://geneshift.net/game/receiveLobby.php' + url: 'http://geneshift.net/game/receiveLobby.php' }); const split = body.split('
'); diff --git a/protocols/kspdmp.js b/protocols/kspdmp.js index 29e402b..9d107f8 100644 --- a/protocols/kspdmp.js +++ b/protocols/kspdmp.js @@ -2,11 +2,11 @@ const Core = require('./core'); class Kspdmp extends Core { async run(state) { - const body = await this.request({ - uri: 'http://'+this.options.address+':'+this.options.port + const json = await this.request({ + url: 'http://'+this.options.address+':'+this.options.port, + responseType: 'json' }); - const json = JSON.parse(body); for (const one of json.players) { state.players.push({name:one.nickname,team:one.team}); } diff --git a/protocols/terraria.js b/protocols/terraria.js index 8daf47e..f9afaa0 100644 --- a/protocols/terraria.js +++ b/protocols/terraria.js @@ -2,15 +2,15 @@ const Core = require('./core'); class Terraria extends Core { async run(state) { - const body = await this.request({ - uri: 'http://'+this.options.address+':'+this.options.port+'/v2/server/status', - qs: { + const json = await this.request({ + url: 'http://'+this.options.address+':'+this.options.port+'/v2/server/status', + searchParams: { players: 'true', token: this.options.token - } + }, + responseType: 'json' }); - const json = JSON.parse(body); if(json.status !== '200') throw new Error('Invalid status'); for (const one of json.players) { From 16bb64b12faf75859e3151e384553c887bb9868b Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Sat, 4 Jul 2020 23:30:43 -0500 Subject: [PATCH 12/41] 2.0.21 --- CHANGELOG.md | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 118 ------------------------------------------------- package.json | 2 +- 3 files changed, 123 insertions(+), 119 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..822c57f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,122 @@ +### 2.0.21 +* Added Assetto Corsa (2014) +* Fixed password flag for Squad +* Added Mordhau (2019) +* Fixed player count being incorrect in minecraftvanilla protocol in some cases +* Updated dependencies +* Replaced deprecated Request http library with Got + +### 2.0.20 +* Fixed minecraft protocol never throwing exceptions + +### 2.0.19 +* Added Days of War (2017) +* Added The Forrest (2014) +* Added Just Cause 3 Multiplayer (2017) +* Added Project Reality: Battlefield 2 (2005) +* Added Quake Live (2010) +* Added Contagion (2011) +* Added Empyrion: Galactic Survival (2015) +* Added PixARK (2018) + +### 2.0.16, 2.0.17, 2.0.18 +* Various improvements to killing floor / unreal2 protocol + +### 2.0.15 +* Added Hell Let Loose +* Added Rising Storm 2: Vietnam +* Added Squad +* Fixed DNS lookup not working in some situations when dns.lookup unexpectedly returns a string +* Improved minecraft protocol for non-vanilla server implementations (bedrock, waterfall, bungeecord) +* Updated dependencies + +### 2.0.14 +* Node 8 compatibility fixes + +### 2.0.13 +* Improved logging + +### 2.0.12 +* Servers are now limited to 10000 players to prevent OOM +* Improvements to Starmade (2012) +* Added Atlas (2018) + +### 2.0.11 +* Added Acra Sim Racing +* Added Mafia 2: Online + +### 2.0.10 +* Added rFactor + +### 2.0.9 +* Added Vice City: Multiplayer + +### 2.0.8 +* Improve out-of-order packet handling for gamespy1 protocol +* Work-around for buggy duplicate player reporting from bf1942 servers +* Report team names rather than IDs when possible for gamespy1 protocol + +### 2.0.7 +* Prevent tcp socket errors from dumping straight to console + +### 2.0.6 +* Added support for host domains requiring Punycode encoding (special characters) + +### 2.0.5 +* Added support for Counter-Strike: 2D + +### 2.0.4 +* Added details about new 2.0 reponse fields to the README. + +### 2.0.3 +* Added support for Insurgency: Sandstorm + +### 2.0.2 +* Added support for Starsiege 2009 (starsiege) + +### 2.0.1 +* Updated readme games list for 2.0 +* Fixed csgo default port + +### 2.0.0 + +##### Breaking API 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. +* Removed `notes` field from options / response object. Data can be passed through a standard javascript context if needed. + +##### 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 [:]` +* 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). +* 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. As 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, or the time required to open a TCP socket +during the query. +* Improved debug logging across all parts of GameDig +* Removed global `Gamedig.debug`. `debug` is now an option on each query. + +##### Protocol Changes +* Added support for games using older versions of battlefield protocol. +* Simplified detection of BC2 when using battlefield protocol. +* Fixed buildandshoot not reading player list +* Standardized all doom3 games into a single protocol, which can discover protocol discrepancies automatically. +* Standardized all gamespy2 games into a single protocol, which can discover protocol discrepancies automatically. +* Standardized all gamespy3 games into a single protocol, which can discover protocol discrepancies automatically. +* Improved valve protocol challenge key retry process + +### 1.0.0 +* First official release +* Node.js 6 is now required diff --git a/README.md b/README.md index 1ba1d9f..0493302 100644 --- a/README.md +++ b/README.md @@ -469,121 +469,3 @@ gamedig --type minecraft mc.example.com:11234 The output of the command will be in JSON format. Additional advanced parameters can be passed in as well: `--debug`, `--pretty`, `--socketTimeout 5000`, etc. - -Changelog ---- - -### 2.0.20 -* Fixed minecraft protocol never throwing exceptions - -### 2.0.19 -* Added Days of War (2017) -* Added The Forrest (2014) -* Added Just Cause 3 Multiplayer (2017) -* Added Project Reality: Battlefield 2 (2005) -* Added Quake Live (2010) -* Added Contagion (2011) -* Added Empyrion: Galactic Survival (2015) -* Added PixARK (2018) - -### 2.0.16, 2.0.17, 2.0.18 -* Various improvements to killing floor / unreal2 protocol - -### 2.0.15 -* Added Hell Let Loose -* Added Rising Storm 2: Vietnam -* Added Squad -* Fixed DNS lookup not working in some situations when dns.lookup unexpectedly returns a string -* Improved minecraft protocol for non-vanilla server implementations (bedrock, waterfall, bungeecord) -* Updated dependencies - -### 2.0.14 -* Node 8 compatibility fixes - -### 2.0.13 -* Improved logging - -### 2.0.12 -* Servers are now limited to 10000 players to prevent OOM -* Improvements to Starmade (2012) -* Added Atlas (2018) - -### 2.0.11 -* Added Acra Sim Racing -* Added Mafia 2: Online - -### 2.0.10 -* Added rFactor - -### 2.0.9 -* Added Vice City: Multiplayer - -### 2.0.8 -* Improve out-of-order packet handling for gamespy1 protocol -* Work-around for buggy duplicate player reporting from bf1942 servers -* Report team names rather than IDs when possible for gamespy1 protocol - -### 2.0.7 -* Prevent tcp socket errors from dumping straight to console - -### 2.0.6 -* Added support for host domains requiring Punycode encoding (special characters) - -### 2.0.5 -* Added support for Counter-Strike: 2D - -### 2.0.4 -* Added details about new 2.0 reponse fields to the README. - -### 2.0.3 -* Added support for Insurgency: Sandstorm - -### 2.0.2 -* Added support for Starsiege 2009 (starsiege) - -### 2.0.1 -* Updated readme games list for 2.0 -* Fixed csgo default port - -### 2.0.0 - -##### Breaking API 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. -* Removed `notes` field from options / response object. Data can be passed through a standard javascript context if needed. - -##### 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 [:]` -* 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). -* 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. As 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, or the time required to open a TCP socket -during the query. -* Improved debug logging across all parts of GameDig -* Removed global `Gamedig.debug`. `debug` is now an option on each query. - -##### Protocol Changes -* Added support for games using older versions of battlefield protocol. -* Simplified detection of BC2 when using battlefield protocol. -* Fixed buildandshoot not reading player list -* Standardized all doom3 games into a single protocol, which can discover protocol discrepancies automatically. -* Standardized all gamespy2 games into a single protocol, which can discover protocol discrepancies automatically. -* Standardized all gamespy3 games into a single protocol, which can discover protocol discrepancies automatically. -* Improved valve protocol challenge key retry process - -### 1.0.0 -* First official release -* Node.js 6 is now required diff --git a/package.json b/package.json index f4add8f..c8c790e 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "2.0.20", + "version": "2.0.21", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" From b99abcd8b3c2b7ec8b2ec6de9234847ed4fad9cc Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Sat, 1 Aug 2020 20:44:00 -0500 Subject: [PATCH 13/41] Updated dependencies (2.0.22) --- CHANGELOG.md | 3 +++ package-lock.json | 44 ++++++++++++++++++++++---------------------- package.json | 23 ++++++++++++++++++----- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 822c57f..40f71b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### 2.0.22 +* Updated dependencies + ### 2.0.21 * Added Assetto Corsa (2014) * Fixed password flag for Squad diff --git a/package-lock.json b/package-lock.json index 484f6e3..9948560 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "gamedig", - "version": "2.0.20", + "version": "2.0.21", "lockfileVersion": 1, "requires": true, "dependencies": { "@sindresorhus/is": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz", - "integrity": "sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.1.0.tgz", + "integrity": "sha512-n4J+zu52VdY43kdi/XdI9DzuMr1Mur8zFL5ZRG2opCans9aiFwkPxHYFEb5Xgy7n1Z4K6WfI4FpqUqsh3E8BPQ==" }, "@szmarczak/http-timer": { "version": "4.0.5", @@ -29,9 +29,9 @@ } }, "@types/cheerio": { - "version": "0.22.18", - "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.18.tgz", - "integrity": "sha512-Fq7R3fINAPSdUEhOyjG4iVxgHrOnqDJbY0/BUuiN0pvD/rfmZWekVZnv+vcs8TtpA2XF50uv50LaE4EnpEL/Hw==", + "version": "0.22.21", + "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.21.tgz", + "integrity": "sha512-aGI3DfswwqgKPiEOTaiHV2ZPC9KEhprpgEbJnv0fZl3SGX0cGgEva1126dGrMC6AJM6v/aihlUgJn9M5DbDZ/Q==", "dev": true, "requires": { "@types/node": "*" @@ -254,18 +254,18 @@ } }, "got": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/got/-/got-11.4.0.tgz", - "integrity": "sha512-XysJZuZNVpaQ37Oo2LV90MIkPeYITehyy1A0QzO1JwOXm8EWuEf9eeGk2XuHePvLEGnm9AVOI37bHwD6KYyBtg==", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/got/-/got-11.5.1.tgz", + "integrity": "sha512-reQEZcEBMTGnujmQ+Wm97mJs/OK6INtO6HmLI+xt3+9CvnRwWjXutUvb2mqr+Ao4Lu05Rx6+udx9sOQAmExMxA==", "requires": { - "@sindresorhus/is": "^2.1.1", + "@sindresorhus/is": "^3.0.0", "@szmarczak/http-timer": "^4.0.5", "@types/cacheable-request": "^6.0.1", "@types/responselike": "^1.0.0", "cacheable-lookup": "^5.0.3", "cacheable-request": "^7.0.1", "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.4.5", + "http2-wrapper": "^1.0.0-beta.5.0", "lowercase-keys": "^2.0.0", "p-cancelable": "^2.0.0", "responselike": "^2.0.0" @@ -320,18 +320,18 @@ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" }, "http2-wrapper": { - "version": "1.0.0-beta.4.6", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.4.6.tgz", - "integrity": "sha512-9oB4BiGDTI1FmIBlOF9OJ5hwJvcBEmPCqk/hy314Uhy2uq5TjekUZM8w8SPLLlUEM+mxNhXdPAXfrJN2Zbb/GQ==", + "version": "1.0.0-beta.5.2", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", + "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", "requires": { - "quick-lru": "^5.0.0", + "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" } }, "iconv-lite": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.1.tgz", - "integrity": "sha512-Gjcihg3Bi6PI+5V7JlqWmXyVDyX5UQuwulJcbb3btuSoXIoGUy8zwJpRIOpRSzHz0IVnsT2FkceLlM8mm72d3w==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" } @@ -360,9 +360,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" }, "long": { "version": "4.0.0", diff --git a/package.json b/package.json index c8c790e..7391083 100644 --- a/package.json +++ b/package.json @@ -7,11 +7,24 @@ "game", "utility", "util", - "server" + "server", + "gameserver", + "node", + "nodejs", + "game-server-query", + "game server query", + "server query", + "game server", + "gameserverquery", + "serverquery", + "terraria", + "counter strike", + "csgo", + "minecraft" ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "2.0.21", + "version": "2.0.22", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" @@ -27,8 +40,8 @@ "cheerio": "^1.0.0-rc.3", "compressjs": "^1.0.2", "gbxremote": "^0.2.1", - "got": "^11.4.0", - "iconv-lite": "^0.6.1", + "got": "^11.5.1", + "iconv-lite": "^0.6.2", "long": "^4.0.0", "minimist": "^1.2.5", "moment": "^2.27.0", @@ -47,7 +60,7 @@ "README.md" ], "devDependencies": { - "@types/cheerio": "^0.22.18", + "@types/cheerio": "^0.22.21", "@types/node": "^8.10.61" } } From a362d1d11303796188296b60d66584551333b0c8 Mon Sep 17 00:00:00 2001 From: cetteup Date: Mon, 24 Aug 2020 20:27:44 +0200 Subject: [PATCH 14/41] Added givenPortOnly user option Allows user to disable gamedig's default behavior of adding query attempts using the default query port(-offset) --- README.md | 1 + bin/gamedig.js | 7 ++++++- lib/QueryRunner.js | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) mode change 100644 => 100755 bin/gamedig.js diff --git a/README.md b/README.md index 0493302..a1e8e9c 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ this query port may work instead. (defaults to protocol default port) will cause many queries to take longer even if the server is online. (default 2000) * **attemptTimeout**: number - Milliseconds allowed for an entire query attempt. This timeout is not commonly hit, as the socketTimeout typically fires first. (default 10000) +* **givenPortOnly**: boolean - Only attempt to query server on given port. (default false) * **debug**: boolean - Enables massive amounts of debug logging to stdout. (default false) ### Return Value diff --git a/bin/gamedig.js b/bin/gamedig.js old mode 100644 new mode 100755 index 720928e..040af19 --- a/bin/gamedig.js +++ b/bin/gamedig.js @@ -4,13 +4,15 @@ const Minimist = require('minimist'), Gamedig = require('..'); const argv = Minimist(process.argv.slice(2), { - boolean: ['pretty','debug'] + boolean: ['pretty','debug','givenPortOnly'] }); const debug = argv.debug; delete argv.debug; const pretty = !!argv.pretty || debug; delete argv.pretty; +const givenPortOnly = argv.givenPortOnly; +delete argv.givenPortOnly; const options = {}; for(const key of Object.keys(argv)) { @@ -34,6 +36,9 @@ if (argv._.length >= 1) { if (debug) { options.debug = true; } +if (givenPortOnly) { + options.givenPortOnly = true; +} Gamedig.query(options) .then((state) => { diff --git a/lib/QueryRunner.js b/lib/QueryRunner.js index 5919dee..062496a 100644 --- a/lib/QueryRunner.js +++ b/lib/QueryRunner.js @@ -30,7 +30,7 @@ class QueryRunner { const attempts = []; if (userOptions.port) { - if (gameQueryPortOffset) { + if (gameQueryPortOffset && !userOptions.givenPortOnly) { attempts.push({ ...defaultOptions, ...gameOptions, @@ -38,7 +38,7 @@ class QueryRunner { port: userOptions.port + gameQueryPortOffset }); } - if (userOptions.port === gameOptions.port && gameQueryPort) { + if (userOptions.port === gameOptions.port && gameQueryPort && !userOptions.givenPortOnly) { attempts.push({ ...defaultOptions, ...gameOptions, From 81a3ec10193fd3f5752689a226c51d8d29dbab25 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Wed, 2 Sep 2020 05:58:35 -0500 Subject: [PATCH 15/41] Always treat missing valve player response as non-fatal (2.0.23) --- CHANGELOG.md | 4 ++++ package.json | 2 +- protocols/valve.js | 22 ++++++++++++---------- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40f71b8..fc5aa5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### 2.0.23 +* Fix Conan Exiles and other games which don't respond to the valve player query +* Add givenPortOnly query option for users that require extreme optimization + ### 2.0.22 * Updated dependencies diff --git a/package.json b/package.json index 7391083..25fc7dc 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "2.0.22", + "version": "2.0.23", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" diff --git a/protocols/valve.js b/protocols/valve.js index d78ebfc..56cf53c 100644 --- a/protocols/valve.js +++ b/protocols/valve.js @@ -113,10 +113,9 @@ class Valve extends Core { ) { this._skipSizeInSplitHeader = true; } - this.debugLog("STEAM APPID: "+state.raw.steamappid); - this.debugLog("PROTOCOL: "+state.raw.protocol); + this.logger.debug("INFO: ", state.raw); if(state.raw.protocol === 48) { - this.debugLog("GOLDSRC DETECTED - USING MODIFIED SPLIT FORMAT"); + this.logger.debug("GOLDSRC DETECTED - USING MODIFIED SPLIT FORMAT"); this.goldsrcSplits = true; } } @@ -139,19 +138,22 @@ class Valve extends Core { async queryPlayers(state) { state.raw.players = []; - // CSGO doesn't even respond sometimes if host_players_show is not 2 - // Ignore timeouts in only this case - const allowTimeout = state.raw.steamappid === 730; - this.debugLog("Requesting player list ..."); const b = await this.sendPacket( 0x55, true, null, 0x44, - allowTimeout + true ); - if (b === null) return; // timed out + + if (b === null) { + // Player query timed out + // CSGO doesn't respond to player query if host_players_show is not 2 + // Conan Exiles never responds to player query + // Just skip it, and we'll fill with dummy objects in cleanup() + return; + } const reader = this.reader(b); const num = reader.uint(1); @@ -179,7 +181,7 @@ class Valve extends Core { state.raw.rules = {}; this.debugLog("Requesting rules ..."); const b = await this.sendPacket(0x56,true,null,0x45,true); - if (b === null) return; // timed out - the server probably just has rules disabled + if (b === null) return; // timed out - the server probably has rules disabled const reader = this.reader(b); const num = reader.uint(2); From e99b3c1812f104cd1a253d6980c25865714bf4ac Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Fri, 8 Jan 2021 20:02:23 -0600 Subject: [PATCH 16/41] Add Savage 2: A Tortured Soul (2008) (2.0.24) --- CHANGELOG.md | 3 +++ README.md | 4 ++-- games.txt | 1 + package.json | 2 +- protocols/savage2.js | 31 +++++++++++++++++++++++++++++++ 5 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 protocols/savage2.js diff --git a/CHANGELOG.md b/CHANGELOG.md index fc5aa5f..b066805 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### 2.0.24 +* Add Savage 2: A Tortured Soul (2008) + ### 2.0.23 * Fix Conan Exiles and other games which don't respond to the valve player query * Add givenPortOnly query option for users that require extreme optimization diff --git a/README.md b/README.md index a1e8e9c..ec06072 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,7 @@ Games List | `minecraft`
`minecraftping` | Minecraft (2009) | `minecraftpe`
`minecraftbe` | Minecraft: Bedrock Edition (2011) | `mnc` | Monday Night Combat (2011) -| `mordhau` | Mordhau (2019) +| `mordhau` | Mordhau (2019) | `mumble` | Mumble - GTmurmur Plugin (2005) | [Notes](#mumble) | `mumbleping` | Mumble - Lightweight (2005) | [Notes](#mumble) | `nascarthunder2004` | NASCAR Thunder 2004 (2003) @@ -274,6 +274,7 @@ Games List | `rust` | Rust | `stalker` | S.T.A.L.K.E.R. | `samp` | San Andreas Multiplayer +| `savage2` | Savage 2: A Tortured Soul (2008) | `ss` | Serious Sam | `ss2` | Serious Sam 2 | `shatteredhorizon` | Shattered Horizon @@ -385,7 +386,6 @@ Games List * Red Faction * S.T.A.L.K.E.R. Clear Sky * Savage: The Battle For Newerth -* Savage 2: A Tortured Soul * SiN 1 Multiplayer * South Park * Star Wars Jedi Knight: Dark Forces II diff --git a/games.txt b/games.txt index acc0747..b8f6e1a 100644 --- a/games.txt +++ b/games.txt @@ -216,6 +216,7 @@ rs2|Rising Storm 2: Vietnam|valve|port=27015 rune|Rune|gamespy1|port=7777,port_query_offset=1 rust|Rust|valve|port=28015 samp|San Andreas Multiplayer|samp|port=7777 +savage2|Savage 2: A Tortured Soul (2008)|savage2|port_query=11235 spaceengineers|Space Engineers|valve|port=27015 ss|Serious Sam|gamespy1|port=25600,port_query_offset=1 ss2|Serious Sam 2|gamespy2|port=25600 diff --git a/package.json b/package.json index 25fc7dc..bd68f22 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "2.0.23", + "version": "2.0.24", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" diff --git a/protocols/savage2.js b/protocols/savage2.js new file mode 100644 index 0000000..8dae2eb --- /dev/null +++ b/protocols/savage2.js @@ -0,0 +1,31 @@ +const Core = require('./core'); + +class Savage2 extends Core { + constructor() { + super(); + } + + async run(state) { + const buffer = await this.udpSend('\x01',b => b); + const reader = this.reader(buffer); + + reader.skip(12); + state.name = this.stripColorCodes(reader.string()); + state.players = reader.uint(1); + state.maxplayers = reader.uint(1); + state.raw.time = reader.string(); + state.map = reader.string(); + state.raw.nextmap = reader.string(); + state.raw.location = reader.string(); + state.raw.minplayers = reader.uint(1); + state.raw.gametype = reader.string(); + state.raw.version = reader.string(); + state.raw.minlevel = reader.uint(1); + } + + stripColorCodes(str) { + return str.replace(/\^./g,''); + } +} + +module.exports = Savage2; From e4c29f9cbc9c8dff64ae0a6d2cfa0ad7d2b763e3 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Sat, 6 Feb 2021 00:50:06 -0600 Subject: [PATCH 17/41] Support challenges in A2S_INFO (upcoming change to valve protocol) Fixes #210 (2.0.25) --- CHANGELOG.md | 3 ++ package.json | 2 +- protocols/ffow.js | 1 - protocols/valve.js | 70 +++++++++++++++++++++++++++------------------- 4 files changed, 46 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b066805..94de092 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### 2.0.25 +* Support challenges in A2S_INFO (upcoming change to valve protocol) + ### 2.0.24 * Add Savage 2: A Tortured Soul (2008) diff --git a/package.json b/package.json index bd68f22..e1218bf 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "2.0.24", + "version": "2.0.25", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" diff --git a/protocols/ffow.js b/protocols/ffow.js index ca82d55..5960beb 100644 --- a/protocols/ffow.js +++ b/protocols/ffow.js @@ -10,7 +10,6 @@ class Ffow extends Valve { this.debugLog("Requesting ffow info ..."); const b = await this.sendPacket( 0x46, - false, 'LSQ', 0x49 ); diff --git a/protocols/valve.js b/protocols/valve.js index 56cf53c..3cc72ba 100644 --- a/protocols/valve.js +++ b/protocols/valve.js @@ -38,7 +38,6 @@ class Valve extends Core { this.debugLog("Requesting info ..."); const b = await this.sendPacket( 0x54, - false, 'Source Engine Query\0', this.goldsrcInfo ? 0x6D : 0x49, false @@ -127,7 +126,6 @@ class Valve extends Core { this.debugLog("Requesting legacy challenge key ..."); await this.sendPacket( 0x57, - false, null, 0x41, false @@ -141,7 +139,6 @@ class Valve extends Core { this.debugLog("Requesting player list ..."); const b = await this.sendPacket( 0x55, - true, null, 0x44, true @@ -180,7 +177,7 @@ class Valve extends Core { async queryRules(state) { state.raw.rules = {}; this.debugLog("Requesting rules ..."); - const b = await this.sendPacket(0x56,true,null,0x45,true); + const b = await this.sendPacket(0x56,null,0x45,true); if (b === null) return; // timed out - the server probably has rules disabled const reader = this.reader(b); @@ -245,33 +242,29 @@ class Valve extends Core { **/ async sendPacket( type, - sendChallenge, payload, expect, allowTimeout ) { for (let keyRetry = 0; keyRetry < 3; keyRetry++) { - let requestKeyChanged = false; + let receivedNewChallengeKey = false; const response = await this.sendPacketRaw( - type, sendChallenge, payload, + type, payload, (payload) => { const reader = this.reader(payload); const type = reader.uint(1); - this.debugLog(() => "Received " + type.toString(16) + " expected " + expect.toString(16)); + this.debugLog(() => "Received 0x" + type.toString(16) + " expected 0x" + expect.toString(16)); if (type === 0x41) { const key = reader.uint(4); if (this._challenge !== key) { - this.debugLog('Received new challenge key: ' + key); + this.debugLog('Received new challenge key: 0x' + key.toString(16)); this._challenge = key; - if (sendChallenge) { - this.debugLog('Challenge key changed -- allowing query retry if needed'); - requestKeyChanged = true; - } + receivedNewChallengeKey = true; } } if (type === expect) { return reader.rest(); - } else if (requestKeyChanged) { + } else if (receivedNewChallengeKey) { return null; } }, @@ -279,7 +272,7 @@ class Valve extends Core { if (allowTimeout) return null; } ); - if (!requestKeyChanged) { + if (!receivedNewChallengeKey) { return response; } } @@ -296,26 +289,47 @@ class Valve extends Core { **/ async sendPacketRaw( type, - sendChallenge, payload, onResponse, onTimeout ) { + const challengeAtBeginning = type === 0x55 || type === 0x56; + const challengeAtEnd = type === 0x54 && !!this._challenge; + if (typeof payload === 'string') payload = Buffer.from(payload, 'binary'); - const challengeLength = sendChallenge ? 4 : 0; - const payloadLength = payload ? payload.length : 0; - const b = Buffer.alloc(5 + challengeLength + payloadLength); - b.writeInt32LE(-1, 0); - b.writeUInt8(type, 4); + const b = Buffer.alloc(5 + + (challengeAtBeginning ? 4 : 0) + + (challengeAtEnd ? 4 : 0) + + (payload ? payload.length : 0) + ); + let offset = 0; - if (sendChallenge) { - let challenge = this._challenge; - if (!challenge) challenge = 0xffffffff; - if (this.byteorder === 'le') b.writeUInt32LE(challenge, 5); - else b.writeUInt32BE(challenge, 5); + let challenge = this._challenge; + if (!challenge) challenge = 0xffffffff; + + b.writeInt32LE(-1, offset); + offset += 4; + + b.writeUInt8(type, offset); + offset += 1; + + if (challengeAtBeginning) { + if (this.byteorder === 'le') b.writeUInt32LE(challenge, offset); + else b.writeUInt32BE(challenge, offset); + offset += 4; + } + + if (payload) { + payload.copy(b, offset); + offset += payload.length; + } + + if (challengeAtEnd) { + if (this.byteorder === 'le') b.writeUInt32LE(challenge, offset); + else b.writeUInt32BE(challenge, offset); + offset += 4; } - if (payloadLength) payload.copy(b, 5 + challengeLength); const packetStorage = {}; return await this.udpSend( @@ -353,7 +367,7 @@ class Valve extends Core { packets[packetNum] = payload; - this.debugLog(() => "Received partial packet uid:"+uid+" num:"+packetNum); + this.debugLog(() => "Received partial packet uid: 0x"+uid.toString(16)+" num: "+packetNum); this.debugLog(() => "Received "+Object.keys(packets).length+'/'+numPackets+" packets for this UID"); if(Object.keys(packets).length !== numPackets) return; From 4ecce4eff8f593369ad42c6b4611f842cb7620e4 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Fri, 12 Feb 2021 11:07:50 -0600 Subject: [PATCH 18/41] Add support for native minecraft bedrock protocol, as some bedrock servers apparently don't respond to gamespy3. Fixes #211 (2.0.26) --- CHANGELOG.md | 4 +++ package.json | 2 +- protocols/minecraft.js | 40 ++++++++++++++++----- protocols/minecraftbedrock.js | 67 +++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 9 deletions(-) create mode 100644 protocols/minecraftbedrock.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 94de092..2938d94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### 2.0.26 +* Added support for the native minecraft bedrock protocol, since some +bedrock servers apparently do not respond to the gamespy3 protocol. + ### 2.0.25 * Support challenges in A2S_INFO (upcoming change to valve protocol) diff --git a/package.json b/package.json index e1218bf..528b39a 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "2.0.25", + "version": "2.0.26", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" diff --git a/protocols/minecraft.js b/protocols/minecraft.js index b21fefe..e5720c9 100644 --- a/protocols/minecraft.js +++ b/protocols/minecraft.js @@ -1,7 +1,16 @@ const Core = require('./core'), MinecraftVanilla = require('./minecraftvanilla'), + MinecraftBedrock = require('./minecraftbedrock'), Gamespy3 = require('./gamespy3'); +/* +Vanilla servers respond to minecraftvanilla only +Some modded vanilla servers respond to minecraftvanilla and gamespy3, or gamespy3 only +Some bedrock servers respond to gamespy3 only +Some bedrock servers respond to minecraftbedrock only +Unsure if any bedrock servers respond to gamespy3 and minecraftbedrock + */ + class Minecraft extends Core { constructor() { super(); @@ -17,25 +26,40 @@ class Minecraft extends Core { try { return await vanillaResolver.runOnceSafe(); } catch(e) {} })()); - const bedrockResolver = new Gamespy3(); - bedrockResolver.options = { + const gamespyResolver = new Gamespy3(); + gamespyResolver.options = { ...this.options, encoding: 'utf8', }; + gamespyResolver.udpSocket = this.udpSocket; + promises.push((async () => { + try { return await gamespyResolver.runOnceSafe(); } catch(e) {} + })()); + + const bedrockResolver = new MinecraftBedrock(); + bedrockResolver.options = this.options; bedrockResolver.udpSocket = this.udpSocket; promises.push((async () => { try { return await bedrockResolver.runOnceSafe(); } catch(e) {} })()); - const [ vanillaState, bedrockState ] = await Promise.all(promises); + const [ vanillaState, gamespyState, bedrockState ] = await Promise.all(promises); state.raw.vanilla = vanillaState; + state.raw.gamespy = gamespyState; state.raw.bedrock = bedrockState; - if (!vanillaState && !bedrockState) { + if (!vanillaState && !gamespyState && !bedrockState) { throw new Error('No protocols succeeded'); } + // Ordered from least worth to most worth (player names / etc) + if (bedrockState) { + if (bedrockState.name) state.name = bedrockState.name; + if (bedrockState.maxplayers) state.maxplayers = bedrockState.maxplayers; + if (bedrockState.players) state.players = bedrockState.players; + if (bedrockState.map) state.map = bedrockState.map; + } if (vanillaState) { try { let name = ''; @@ -54,10 +78,10 @@ class Minecraft extends Core { if (vanillaState.maxplayers) state.maxplayers = vanillaState.maxplayers; if (vanillaState.players) state.players = vanillaState.players; } - if (bedrockState) { - if (bedrockState.name) state.name = bedrockState.name; - if (bedrockState.maxplayers) state.maxplayers = bedrockState.maxplayers; - if (bedrockState.players) state.players = bedrockState.players; + if (gamespyState) { + if (gamespyState.name) state.name = gamespyState.name; + if (gamespyState.maxplayers) state.maxplayers = gamespyState.maxplayers; + if (gamespyState.players) state.players = gamespyState.players; } // remove dupe spaces from name state.name = state.name.replace(/\s+/g, ' '); diff --git a/protocols/minecraftbedrock.js b/protocols/minecraftbedrock.js new file mode 100644 index 0000000..0b762c5 --- /dev/null +++ b/protocols/minecraftbedrock.js @@ -0,0 +1,67 @@ +const Core = require('./core'); + +class MinecraftBedrock extends Core { + constructor() { + super(); + this.byteorder = 'be'; + } + + async run(state) { + const bufs = [ + Buffer.from([0x01]), // Message ID, ID_UNCONNECTED_PING + Buffer.from('0000000000000000', 'hex'), // Nonce / timestamp + Buffer.from('00ffff00fefefefefdfdfdfd12345678', 'hex'), // Magic + Buffer.from('0000000000000000', 'hex') // Cliend GUID + ]; + + return await this.udpSend(Buffer.concat(bufs), buffer => { + const reader = this.reader(buffer); + + const messageId = reader.uint(1); + if (messageId !== 0x1c) { + throw new Error('Invalid message id'); + } + + const nonce = reader.part(8).toString('hex'); // should match the nonce we sent + this.logger.debug('Nonce: ' + nonce); + + state.raw.guid = reader.part(8).toString('hex'); + + const magic = reader.part(16).toString('hex'); + this.logger.debug('Magic value: ' + magic); + + const statusLen = reader.uint(2); + if (reader.remaining() !== statusLen) { + throw new Error('Invalid status length: ' + reader.remaining() + ' vs ' + statusLen); + } + + const statusStr = reader.rest().toString('utf8'); + this.logger.debug('Raw status str: ' + statusStr); + + const split = statusStr.split(';'); + if (split.length < 12) { + throw new Error('Missing enough chunks in status str'); + } + + let i = 0; + state.raw.edition = split[i++]; + state.name = split[i++]; + state.raw.protocolVersion = split[i++]; + state.raw.mcVersion = split[i++]; + state.players = parseInt(split[i++]); + state.maxplayers = parseInt(split[i++]); + state.raw.serverId = split[i++]; + state.map = split[i++]; + state.raw.gameMode = split[i++]; + state.raw.nintendoOnly = !!parseInt(split[i++]); + state.raw.ipv4Port = split[i++]; + state.raw.ipv6Port = split[i++]; + + return true; + }); + + } + +} + +module.exports = MinecraftBedrock; From 883700d7dd0f6160a0ffc9d66aeaa3b88e72e3fe Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Fri, 12 Feb 2021 11:15:13 -0600 Subject: [PATCH 19/41] Remove redundant guid field from minecraftbedrock --- protocols/minecraftbedrock.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/minecraftbedrock.js b/protocols/minecraftbedrock.js index 0b762c5..c1d2ba8 100644 --- a/protocols/minecraftbedrock.js +++ b/protocols/minecraftbedrock.js @@ -25,7 +25,8 @@ class MinecraftBedrock extends Core { const nonce = reader.part(8).toString('hex'); // should match the nonce we sent this.logger.debug('Nonce: ' + nonce); - state.raw.guid = reader.part(8).toString('hex'); + // These 8 bytes are identical to the serverId string we receive in decimal below + reader.skip(8); const magic = reader.part(16).toString('hex'); this.logger.debug('Magic value: ' + magic); From 052736edd7a2368a033d80c09c79bb53fc8a9ae4 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Fri, 12 Feb 2021 11:36:51 -0600 Subject: [PATCH 20/41] Reduced chance of protocol collisions between gamespy3 and minecraftbedrock (2.0.27) --- CHANGELOG.md | 3 +++ package.json | 2 +- protocols/core.js | 4 +++- protocols/gamespy3.js | 10 +++++++-- protocols/minecraft.js | 3 ++- protocols/minecraftbedrock.js | 40 +++++++++++++++++++++-------------- 6 files changed, 41 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2938d94..ddd83a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### 2.0.27 +* Reduced chance of protocol collisions between gamespy3 and minecraftbedrock + ### 2.0.26 * Added support for the native minecraft bedrock protocol, since some bedrock servers apparently do not respond to the gamespy3 protocol. diff --git a/package.json b/package.json index 528b39a..1a18779 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "2.0.26", + "version": "2.0.27", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" diff --git a/protocols/core.js b/protocols/core.js index fb9e1ed..0989495 100644 --- a/protocols/core.js +++ b/protocols/core.js @@ -35,7 +35,9 @@ class Core extends EventEmitter { } this.logger.prefix = 'Q#' + (uid++); - this.logger.debug("Query is running with options:", this.options); + this.logger.debug("Starting"); + this.logger.debug("Protocol: " + this.constructor.name); + this.logger.debug("Options:", this.options); let abortCall = null; this.abortedPromise = new Promise((resolve,reject) => { diff --git a/protocols/gamespy3.js b/protocols/gamespy3.js index 853c900..8372265 100644 --- a/protocols/gamespy3.js +++ b/protocols/gamespy3.js @@ -148,9 +148,15 @@ class Gamespy3 extends Core { return await this.udpSend(b,(buffer) => { const reader = this.reader(buffer); const iType = reader.uint(1); - if(iType !== type) return; + if(iType !== type) { + this.logger.debug('Skipping packet, type mismatch'); + return; + } const iSessionId = reader.uint(4); - if(iSessionId !== this.sessionId) return; + if(iSessionId !== this.sessionId) { + this.logger.debug('Skipping packet, session id mismatch'); + return; + } if(!assemble) { return reader.rest(); diff --git a/protocols/minecraft.js b/protocols/minecraft.js index e5720c9..785f39e 100644 --- a/protocols/minecraft.js +++ b/protocols/minecraft.js @@ -81,7 +81,8 @@ class Minecraft extends Core { if (gamespyState) { if (gamespyState.name) state.name = gamespyState.name; if (gamespyState.maxplayers) state.maxplayers = gamespyState.maxplayers; - if (gamespyState.players) state.players = gamespyState.players; + if (gamespyState.players.length) state.players = gamespyState.players; + else if (gamespyState.raw.numplayers) state.players = parseInt(gamespyState.raw.numplayers); } // remove dupe spaces from name state.name = state.name.replace(/\s+/g, ' '); diff --git a/protocols/minecraftbedrock.js b/protocols/minecraftbedrock.js index c1d2ba8..4c166fc 100644 --- a/protocols/minecraftbedrock.js +++ b/protocols/minecraftbedrock.js @@ -9,7 +9,7 @@ class MinecraftBedrock extends Core { async run(state) { const bufs = [ Buffer.from([0x01]), // Message ID, ID_UNCONNECTED_PING - Buffer.from('0000000000000000', 'hex'), // Nonce / timestamp + Buffer.from('1122334455667788', 'hex'), // Nonce / timestamp Buffer.from('00ffff00fefefefefdfdfdfd12345678', 'hex'), // Magic Buffer.from('0000000000000000', 'hex') // Cliend GUID ]; @@ -19,17 +19,26 @@ class MinecraftBedrock extends Core { const messageId = reader.uint(1); if (messageId !== 0x1c) { - throw new Error('Invalid message id'); + this.logger.debug('Skipping packet, invalid message id'); + return; } const nonce = reader.part(8).toString('hex'); // should match the nonce we sent this.logger.debug('Nonce: ' + nonce); + if (nonce !== '1122334455667788') { + this.logger.debug('Skipping packet, invalid nonce'); + return; + } // These 8 bytes are identical to the serverId string we receive in decimal below reader.skip(8); const magic = reader.part(16).toString('hex'); this.logger.debug('Magic value: ' + magic); + if (magic !== '00ffff00fefefefefdfdfdfd12345678') { + this.logger.debug('Skipping packet, invalid magic'); + return; + } const statusLen = reader.uint(2); if (reader.remaining() !== statusLen) { @@ -40,23 +49,22 @@ class MinecraftBedrock extends Core { this.logger.debug('Raw status str: ' + statusStr); const split = statusStr.split(';'); - if (split.length < 12) { + if (split.length < 6) { throw new Error('Missing enough chunks in status str'); } - let i = 0; - state.raw.edition = split[i++]; - state.name = split[i++]; - state.raw.protocolVersion = split[i++]; - state.raw.mcVersion = split[i++]; - state.players = parseInt(split[i++]); - state.maxplayers = parseInt(split[i++]); - state.raw.serverId = split[i++]; - state.map = split[i++]; - state.raw.gameMode = split[i++]; - state.raw.nintendoOnly = !!parseInt(split[i++]); - state.raw.ipv4Port = split[i++]; - state.raw.ipv6Port = split[i++]; + state.raw.edition = split.shift(); + state.name = split.shift(); + state.raw.protocolVersion = split.shift(); + state.raw.mcVersion = split.shift(); + state.players = parseInt(split.shift()); + state.maxplayers = parseInt(split.shift()); + if (split.length) state.raw.serverId = split.shift(); + if (split.length) state.map = split.shift(); + if (split.length) state.raw.gameMode = split.shift(); + if (split.length) state.raw.nintendoOnly = !!parseInt(split.shift()); + if (split.length) state.raw.ipv4Port = split.shift(); + if (split.length) state.raw.ipv6Port = split.shift(); return true; }); From 88dd1a897acc5bc4b9836ca165b97bab46e9a64c Mon Sep 17 00:00:00 2001 From: rylinj <7265642+rylinj@users.noreply.github.com> Date: Sat, 20 Feb 2021 13:48:21 -0500 Subject: [PATCH 21/41] Added Valheim support --- games.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/games.txt b/games.txt index b8f6e1a..f277517 100644 --- a/games.txt +++ b/games.txt @@ -279,6 +279,7 @@ ut3|Unreal Tournament 3|ut3|port=7777,port_query_offset=-1277 urbanterror|Urban Terror|quake3|port_query=27960 v8supercar|V8 Supercar Challenge|gamespy1|port_query=16700 +valheim|Valheim|valve|port=2456,port_query_offset=1 vcmp|Vice City Multiplayer|vcmp|port=8192 ventrilo|Ventrilo|ventrilo|port=3784 vietcong|Vietcong|gamespy1|port=5425,port_query=15425 From 91056533282a60fb644917c8c00242ac5fd46666 Mon Sep 17 00:00:00 2001 From: rylinj <7265642+rylinj@users.noreply.github.com> Date: Sat, 20 Feb 2021 13:50:58 -0500 Subject: [PATCH 22/41] Add Valheim support to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ec06072..7417354 100644 --- a/README.md +++ b/README.md @@ -337,6 +337,7 @@ Games List | `unturned` | unturned | `urbanterror` | Urban Terror | `v8supercar` | V8 Supercar Challenge +| `valheim` | Valheim | `ventrilo` | Ventrilo | `vcmp` | Vice City Multiplayer | `vietcong` | Vietcong From 7f08381b17e309247be9bb669436f93072b0f9be Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Wed, 24 Feb 2021 23:54:19 -0600 Subject: [PATCH 23/41] Release 2.0.28 --- CHANGELOG.md | 3 +++ README.md | 2 +- games.txt | 2 +- package.json | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddd83a3..fd9d4b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### 2.0.28 +* Added Valheim (2021) + ### 2.0.27 * Reduced chance of protocol collisions between gamespy3 and minecraftbedrock diff --git a/README.md b/README.md index 7417354..6699159 100644 --- a/README.md +++ b/README.md @@ -337,7 +337,7 @@ Games List | `unturned` | unturned | `urbanterror` | Urban Terror | `v8supercar` | V8 Supercar Challenge -| `valheim` | Valheim +| `valheim` | Valheim (2021) | `ventrilo` | Ventrilo | `vcmp` | Vice City Multiplayer | `vietcong` | Vietcong diff --git a/games.txt b/games.txt index f277517..a5c19ba 100644 --- a/games.txt +++ b/games.txt @@ -279,7 +279,7 @@ ut3|Unreal Tournament 3|ut3|port=7777,port_query_offset=-1277 urbanterror|Urban Terror|quake3|port_query=27960 v8supercar|V8 Supercar Challenge|gamespy1|port_query=16700 -valheim|Valheim|valve|port=2456,port_query_offset=1 +valheim|Valheim (2021)|valve|port=2456,port_query_offset=1 vcmp|Vice City Multiplayer|vcmp|port=8192 ventrilo|Ventrilo|ventrilo|port=3784 vietcong|Vietcong|gamespy1|port=5425,port_query=15425 diff --git a/package.json b/package.json index 1a18779..5d4040e 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "2.0.27", + "version": "2.0.28", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" From f70112d0928b3e8faf04c92717c47a8c1843bca1 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Thu, 25 Feb 2021 01:58:35 -0600 Subject: [PATCH 24/41] * Properly handle non-indexed team names in gamespy1 Fixes #213 * Rename raw.steamappid and raw.gameid to raw.appId in steam protocol * Don't query valve rules by default, unless requestRules option is set Fixes #176 --- games.txt | 2 +- lib/Results.js | 51 +++++++++++++++++++++ lib/reader.js | 4 +- protocols/core.js | 38 ++-------------- protocols/gamespy1.js | 44 ++++++------------ protocols/openttd.js | 6 +-- protocols/squad.js | 16 ------- protocols/starmade.js | 2 +- protocols/valve.js | 102 +++++++++++++++++++++++++++--------------- 9 files changed, 143 insertions(+), 122 deletions(-) create mode 100644 lib/Results.js delete mode 100644 protocols/squad.js diff --git a/games.txt b/games.txt index a5c19ba..2978c46 100644 --- a/games.txt +++ b/games.txt @@ -234,7 +234,7 @@ stalker|S.T.A.L.K.E.R.|gamespy3|port=5445,port_query_offset=2 stbc|Star Trek: Bridge Commander|gamespy1|port_query=22101 stvef|Star Trek: Voyager - Elite Force|quake3|port_query=27960 stvef2|Star Trek: Voyager - Elite Force 2|quake3|port_query=29253 -squad|Squad|squad|port=7787,port_query=27165 +squad|Squad|valve|port=7787,port_query=27165 swbf|Star Wars: Battlefront|gamespy2|port_query=3658 swbf2|Star Wars: Battlefront 2|gamespy2|port_query=3658 swjk|Star Wars Jedi Knight: Jedi Academy (2003)|quake3|port_query=29070 diff --git a/lib/Results.js b/lib/Results.js new file mode 100644 index 0000000..2ca54ba --- /dev/null +++ b/lib/Results.js @@ -0,0 +1,51 @@ +class Player { + name = ''; + raw = {}; + + constructor(data) { + if (typeof data === 'string') { + this.name = data; + } else { + const {name, ...raw} = data; + if (name) this.name = name; + if (raw) this.raw = raw; + } + } +} + +class Players extends Array { + setNum(num) { + // If the server specified some ridiculous number of players (billions), we don't want to + // run out of ram allocating these objects. + num = Math.min(num, 10000); + + while(this.players.length < num) { + this.push({}); + } + } + + push(data) { + super.push(new Player(data)); + } +} + +class Results { + name = ''; + map = ''; + password = false; + + raw = {}; + + maxplayers = 0; + players = new Players(); + bots = new Players(); + + set players(num) { + this.players.setNum(num); + } + set bots(num) { + this.bots.setNum(num); + } +} + +module.exports = Results; diff --git a/lib/reader.js b/lib/reader.js index 5dbc2f2..0ab184e 100644 --- a/lib/reader.js +++ b/lib/reader.js @@ -115,12 +115,12 @@ class Reader { if(bytes === 1) r = this.buffer.readUInt8(this.i); else if(bytes === 2) r = this.buffer.readUInt16BE(this.i); else if(bytes === 4) r = this.buffer.readUInt32BE(this.i); - else if(bytes === 8) r = readUInt64BE(this.buffer,this.i).toString(); + else if(bytes === 8) r = readUInt64BE(this.buffer,this.i); } else { if(bytes === 1) r = this.buffer.readUInt8(this.i); else if(bytes === 2) r = this.buffer.readUInt16LE(this.i); else if(bytes === 4) r = this.buffer.readUInt32LE(this.i); - else if(bytes === 8) r = readUInt64LE(this.buffer,this.i).toString(); + else if(bytes === 8) r = readUInt64LE(this.buffer,this.i); } } this.i += bytes; diff --git a/protocols/core.js b/protocols/core.js index 0989495..d2535ff 100644 --- a/protocols/core.js +++ b/protocols/core.js @@ -5,7 +5,8 @@ const EventEmitter = require('events').EventEmitter, got = require('got'), Promises = require('../lib/Promises'), Logger = require('../lib/Logger'), - DnsResolver = require('../lib/DnsResolver'); + DnsResolver = require('../lib/DnsResolver'), + Results = require('../lib/Results'); let uid = 0; @@ -74,44 +75,13 @@ class Core extends EventEmitter { if (resolved.port) options.port = resolved.port; } - const state = { - name: '', - map: '', - password: false, - - raw: {}, - - maxplayers: 0, - players: [], - bots: [] - }; + const state = new Results(); await this.run(state); // because lots of servers prefix with spaces to try to appear first state.name = (state.name || '').trim(); - if (typeof state.players === 'number') { - const num = state.players; - state.players = []; - state.raw.rcvNumPlayers = num; - if (num < 10000) { - for (let i = 0; i < num; i++) { - state.players.push({}); - } - } - } - if (typeof state.bots === 'number') { - const num = state.bots; - state.bots = []; - state.raw.rcvNumBots = num; - if (num < 10000) { - for (let i = 0; i < num; i++) { - state.bots.push({}); - } - } - } - if (!('connect' in state)) { state.connect = '' + (state.gameHost || this.options.host || this.options.address) @@ -130,7 +100,7 @@ class Core extends EventEmitter { return state; } - async run(state) {} + async run(/** Results */ state) {} /** Param can be a time in ms, or a promise (which will be timed) */ registerRtt(param) { diff --git a/protocols/gamespy1.js b/protocols/gamespy1.js index 3721152..206bf54 100644 --- a/protocols/gamespy1.js +++ b/protocols/gamespy1.js @@ -35,9 +35,14 @@ class Gamespy1 extends Core { teamNamesById[id] = value; } else { if (!(id in playersById)) playersById[id] = {}; - if (key === 'playername') key = 'name'; - else if (key === 'team') value = parseInt(value); - else if (key === 'score' || key === 'ping' || key === 'deaths' || key === 'kills') value = parseInt(value); + if (key === 'playername') { + key = 'name'; + } else if (key === 'team' && !isNaN(parseInt(value))) { + key = 'teamId'; + value = parseInt(value); + } else if (key === 'score' || key === 'ping' || key === 'deaths' || key === 'kills') { + value = parseInt(value); + } playersById[id][key] = value; } } @@ -45,28 +50,6 @@ class Gamespy1 extends Core { const players = Object.values(playersById); - // Determine which team id might be for spectators - let specTeamId = null; - for (const player of players) { - if (!player.team) { - continue; - } else if (teamNamesById[player.team]) { - continue; - } else if (teamNamesById[player.team-1] && (specTeamId === null || specTeamId === player.team)) { - specTeamId = player.team; - } else { - specTeamId = null; - break; - } - } - this.logger.debug(log => { - if (specTeamId === null) { - log("Could not detect a team ID for spectators"); - } else { - log("Detected that team ID " + specTeamId + " is probably for spectators"); - } - }); - const seenHashes = new Set(); for (const player of players) { // Some servers (bf1942) report the same player multiple times (bug?) @@ -81,11 +64,12 @@ class Gamespy1 extends Core { } // Convert player's team ID to team name if possible - if (player.team) { - if (teamNamesById[player.team]) { - player.team = teamNamesById[player.team]; - } else if (player.team === specTeamId) { - player.team = "spec"; + if (player.hasOwnProperty('teamId')) { + if (Object.keys(teamNamesById).length) { + player.team = teamNamesById[player.teamId - 1] || ''; + } else { + player.team = player.teamId; + delete player.teamId; } } diff --git a/protocols/openttd.js b/protocols/openttd.js index 9bf336f..5fc2e11 100644 --- a/protocols/openttd.js +++ b/protocols/openttd.js @@ -60,9 +60,9 @@ class OpenTtd extends Core { company.id = reader.uint(1); company.name = reader.string(); company.year_start = reader.uint(4); - company.value = reader.uint(8); - company.money = reader.uint(8); - company.income = reader.uint(8); + company.value = reader.uint(8).toString(); + company.money = reader.uint(8).toString(); + company.income = reader.uint(8).toString(); company.performance = reader.uint(2); company.password = !!reader.uint(1); diff --git a/protocols/squad.js b/protocols/squad.js deleted file mode 100644 index 0be9e02..0000000 --- a/protocols/squad.js +++ /dev/null @@ -1,16 +0,0 @@ -const Valve = require('./valve'); - -class Squad extends Valve { - constructor() { - super(); - } - - async cleanup(state) { - await super.cleanup(state); - if (state.raw.rules != null && state.raw.rules.Password_b === "true") { - state.password = true; - } - } -} - -module.exports = Squad; diff --git a/protocols/starmade.js b/protocols/starmade.js index 091fdf6..d9e4f10 100644 --- a/protocols/starmade.js +++ b/protocols/starmade.js @@ -16,7 +16,7 @@ class Starmade extends Core { const reader = this.reader(buffer); const packetLength = reader.uint(4); this.logger.debug("Received packet length: " + packetLength); - const timestamp = reader.uint(8); + const timestamp = reader.uint(8).toString(); this.logger.debug("Received timestamp: " + timestamp); if (reader.remaining() < packetLength || reader.remaining() < 5) return; diff --git a/protocols/valve.js b/protocols/valve.js index 3cc72ba..3d2fea0 100644 --- a/protocols/valve.js +++ b/protocols/valve.js @@ -1,5 +1,12 @@ const Bzip2 = require('compressjs').Bzip2, - Core = require('./core'); + Core = require('./core'), + Results = require('../lib/Results'); + +const AppId = { + Squad: 393380, + Bat1944: 489940, + Ship: 2400 +}; class Valve extends Core { constructor() { @@ -34,7 +41,7 @@ class Valve extends Core { await this.cleanup(state); } - async queryInfo(state) { + async queryInfo(/** Results */ state) { this.debugLog("Requesting info ..."); const b = await this.sendPacket( 0x54, @@ -52,7 +59,7 @@ class Valve extends Core { state.map = reader.string(); state.raw.folder = reader.string(); state.raw.game = reader.string(); - state.raw.steamappid = reader.uint(2); + state.raw.appId = reader.uint(2); state.raw.numplayers = reader.uint(1); state.maxplayers = reader.uint(1); @@ -84,7 +91,7 @@ class Valve extends Core { if(this.goldsrcInfo) { state.raw.numbots = reader.uint(1); } else { - if(state.raw.folder === 'ship') { + if(state.raw.appId === AppId.Ship) { state.raw.shipmode = reader.uint(1); state.raw.shipwitnesses = reader.uint(1); state.raw.shipduration = reader.uint(1); @@ -92,22 +99,28 @@ class Valve extends Core { state.raw.version = reader.string(); const extraFlag = reader.uint(1); if(extraFlag & 0x80) state.gamePort = reader.uint(2); - if(extraFlag & 0x10) state.raw.steamid = reader.uint(8); + if(extraFlag & 0x10) state.raw.steamid = reader.uint(8).toString(); if(extraFlag & 0x40) { state.raw.sourcetvport = reader.uint(2); state.raw.sourcetvname = reader.string(); } if(extraFlag & 0x20) state.raw.tags = reader.string(); - if(extraFlag & 0x01) state.raw.gameid = reader.uint(8); + if(extraFlag & 0x01) { + const gameId = reader.uint(8); + const betterAppId = gameId.getLowBitsUnsigned() & 0xffffff; + if (betterAppId) { + state.raw.appId = betterAppId; + } + } } // from https://developer.valvesoftware.com/wiki/Server_queries if( state.raw.protocol === 7 && ( - state.raw.steamappid === 215 - || state.raw.steamappid === 17550 - || state.raw.steamappid === 17700 - || state.raw.steamappid === 240 + state.raw.appId === 215 + || state.raw.appId === 17550 + || state.raw.appId === 17700 + || state.raw.appId === 240 ) ) { this._skipSizeInSplitHeader = true; @@ -133,7 +146,7 @@ class Valve extends Core { } } - async queryPlayers(state) { + async queryPlayers(/** Results */ state) { state.raw.players = []; this.debugLog("Requesting player list ..."); @@ -174,8 +187,18 @@ class Valve extends Core { } } - async queryRules(state) { - state.raw.rules = {}; + async queryRules(/** Results */ state) { + const appId = state.raw.appId; + if (appId === AppId.Squad + || appId === AppId.Bat1944 + || this.options.requestRules) { + // let's get 'em + } else { + return; + } + + const rules = {}; + state.raw.rules = rules; this.debugLog("Requesting rules ..."); const b = await this.sendPacket(0x56,null,0x45,true); if (b === null) return; // timed out - the server probably has rules disabled @@ -185,31 +208,40 @@ class Valve extends Core { for(let i = 0; i < num; i++) { const key = reader.string(); const value = reader.string(); - state.raw.rules[key] = value; + rules[key] = value; + } + + // Battalion 1944 puts its info into rules fields for some reason + if (appId === AppId.Bat1944) { + if ('bat_name_s' in rules) { + state.name = rules.bat_name_s; + delete rules.bat_name_s; + if ('bat_player_count_s' in rules) { + state.raw.numplayers = parseInt(rules.bat_player_count_s); + delete rules.bat_player_count_s; + } + if ('bat_max_players_i' in rules) { + state.maxplayers = parseInt(rules.bat_max_players_i); + delete rules.bat_max_players_i; + } + if ('bat_has_password_s' in rules) { + state.password = rules.bat_has_password_s === 'Y'; + delete rules.bat_has_password_s; + } + // apparently map is already right, and this var is often wrong + delete rules.bat_map_s; + } + } + + // Squad keeps its password in a separate field + if (appId === AppId.Squad) { + if (rules.Password_b === "true") { + state.password = true; + } } } - async cleanup(state) { - // Battalion 1944 puts its info into rules fields for some reason - if ('bat_name_s' in state.raw.rules) { - state.name = state.raw.rules.bat_name_s; - delete state.raw.rules.bat_name_s; - if ('bat_player_count_s' in state.raw.rules) { - state.raw.numplayers = parseInt(state.raw.rules.bat_player_count_s); - delete state.raw.rules.bat_player_count_s; - } - if ('bat_max_players_i' in state.raw.rules) { - state.maxplayers = parseInt(state.raw.rules.bat_max_players_i); - delete state.raw.rules.bat_max_players_i; - } - if ('bat_has_password_s' in state.raw.rules) { - state.password = state.raw.rules.bat_has_password_s === 'Y'; - delete state.raw.rules.bat_has_password_s; - } - // apparently map is already right, and this var is often wrong - delete state.raw.rules.bat_map_s; - } - + async cleanup(/** Results */ state) { // Organize players / hidden players into player / bot arrays const botProbability = (p) => { if (p.time === -1) return Number.MAX_VALUE; From d65a24dc1850229dc1d1eb99b7d5683bd0634862 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Thu, 25 Feb 2021 02:15:53 -0600 Subject: [PATCH 25/41] Release 3.0.0 --- CHANGELOG.md | 10 +++ README.md | 181 ++++++++++++++++++++++---------------------- lib/GameResolver.js | 14 +++- package.json | 2 +- 4 files changed, 114 insertions(+), 93 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd9d4b9..1f49af4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +### 3.0.0 +Major Changes: +* The `name` field is now guaranteed to exist on all player objects. If a player's name is unknown, the `name` will be an empty string. +* All non-`name` player fields have been moved into a `raw` sub-field. This means that, like the `raw` subobject of the parent + response, all non-`name` fields are now considered to be unstable and may be changed during minor releases of GameDig. +* "Rules" are no longer queried for `valve` protocol games by default. Many games do not respond to this query anyways (meaning we have to wait + for timeout), and its contents is often not even used since it only exists in the raw subfield. If you depend on rules, + you may pass the `requestRules: true` option to re-enable them. +* The `raw.steamappid` and `raw.gameid` fields for valve games have been consolidated into `raw.appId`. + ### 2.0.28 * Added Valheim (2021) diff --git a/README.md b/README.md index 6699159..d693cf4 100644 --- a/README.md +++ b/README.md @@ -55,10 +55,9 @@ The returned state object will contain the following keys: * **password**: boolean - If a password is required * **maxplayers**: number * **players**: array of objects - * Each object **may or may not** contain name, ping, score, team, address. - * The number of players online can be determined by `players.length`. - * For servers which do not provide player names, this may be an array -of empty objects (ex. `[{},{},{}]`), one for each player without a name. + * **name**: string - If the player's name is unknown, the string will be empty. + * **raw**: object - Additional information about the player if available (unstable) + * The content of this field MAY change on a per-protocol basis between GameDig patch releases (although not typical). * **bots**: array of objects - Same schema as `players` * **connect**: string * This will typically include the game's `ip:port` @@ -79,32 +78,32 @@ Games List ### Supported -| GameDig Type ID | Name | Notes +| GameDig Type ID | Name | See Also |---|---|--- -| `7d2d` | 7 Days to Die (2013) -| `ageofchivalry` | Age of Chivalry (2007) +| `7d2d` | 7 Days to Die (2013) | [Valve Protocol](#valve) +| `ageofchivalry` | Age of Chivalry (2007) | [Valve Protocol](#valve) | `aoe2` | Age of Empires 2 (1999) | `alienarena` | Alien Arena (2004) -| `alienswarm` | Alien Swarm (2010) +| `alienswarm` | Alien Swarm (2010) | [Valve Protocol](#valve) | `avp2` | Aliens versus Predator 2 (2001) -| `avp2010` | Aliens vs. Predator (2010) +| `avp2010` | Aliens vs. Predator (2010) | [Valve Protocol](#valve) | `americasarmy` | America's Army (2002) | `americasarmy2` | America's Army 2 (2003) -| `americasarmy3` | America's Army 3 (2009) -| `americasarmypg` | America's Army: Proving Grounds (2015) +| `americasarmy3` | America's Army 3 (2009) | [Valve Protocol](#valve) +| `americasarmypg` | America's Army: Proving Grounds (2015) | [Valve Protocol](#valve) | `arcasimracing` | Arca Sim Racing (2008) -| `arkse` | Ark: Survival Evolved (2017) -| `arma2` | ARMA 2 (2009) -| `arma2oa` | ARMA 2: Operation Arrowhead (2010) -| `arma3` | ARMA 3 (2013) +| `arkse` | Ark: Survival Evolved (2017) | [Valve Protocol](#valve) +| `arma2` | ARMA 2 (2009) | [Valve Protocol](#valve) +| `arma2oa` | ARMA 2: Operation Arrowhead (2010) | [Valve Protocol](#valve) +| `arma3` | ARMA 3 (2013) | [Valve Protocol](#valve) | `arma` | ARMA: Armed Assault (2007) | `armacwa` | ARMA: Cold War Assault (2011) | `armar` | ARMA: Resistance (2011) | `armagetron` | Armagetron Advanced (2001) | `assettocorsa` | Assetto Corsa (2014) -| `atlas` | Atlas (2018) +| `atlas` | Atlas (2018) | [Valve Protocol](#valve) | `baldursgate` | Baldur's Gate (1998) -| `bat1944` | Battalion 1944 (2018) +| `bat1944` | Battalion 1944 (2018) | [Valve Protocol](#valve) | `bf1942` | Battlefield 1942 (2002) | `bf2` | Battlefield 2 (2005) | `bf2142` | Battlefield 2142 (2006) @@ -113,16 +112,16 @@ Games List | `bfh` | Battlefield Hardline (2015) | `bfv` | Battlefield Vietnam (2004) | `bfbc2` | Battlefield: Bad Company 2 (2010) -| `breach` | Breach (2011) +| `breach` | Breach (2011) | [Valve Protocol](#valve) | `breed` | Breed (2004) -| `brink` | Brink (2011) +| `brink` | Brink (2011) | [Valve Protocol](#valve) | `buildandshoot` | Build and Shoot / Ace of Spades Classic (2012) | `cod` | Call of Duty (2003) | `cod2` | Call of Duty 2 (2005) | `cod3` | Call of Duty 3 (2006) | `cod4` | Call of Duty 4: Modern Warfare (2007) | `codmw2` | Call of Duty: Modern Warfare 2 (2009) -| `codmw3` | Call of Duty: Modern Warfare 3 (2011) +| `codmw3` | Call of Duty: Modern Warfare 3 (2011) | [Valve Protocol](#valve) | `coduo` | Call of Duty: United Offensive (2004) | `codwaw` | Call of Duty: World at War (2008) | `callofjuarez` | Call of Juarez (2006) @@ -131,86 +130,86 @@ Games List | `codenameeagle` | Codename Eagle (2000) | `cacrenegade` | Command and Conquer: Renegade (2002) | `commandos3` | Commandos 3: Destination Berlin (2003) -| `conanexiles` | Conan Exiles (2018) -| `contagion` | Contagion (2011) +| `conanexiles` | Conan Exiles (2018) | [Valve Protocol](#valve) +| `contagion` | Contagion (2011) | [Valve Protocol](#valve) | `contactjack` | Contract J.A.C.K. (2003) -| `cs15` | Counter-Strike 1.5 (2002) -| `cs16` | Counter-Strike 1.6 (2003) +| `cs15` | Counter-Strike 1.5 (2002) | [Valve Protocol](#valve) +| `cs16` | Counter-Strike 1.6 (2003) | [Valve Protocol](#valve) | `cs2d` | Counter-Strike: 2D (2004) -| `cscz` | Counter-Strike: Condition Zero (2004) -| `csgo` | Counter-Strike: Global Offensive (2012) | [Notes](#csgo) -| `css` | Counter-Strike: Source (2004) +| `cscz` | Counter-Strike: Condition Zero (2004) | [Valve Protocol](#valve) +| `csgo` | Counter-Strike: Global Offensive (2012) | [Notes](#csgo), [Valve Protocol](#valve) +| `css` | Counter-Strike: Source (2004) | [Valve Protocol](#valve) | `crossracing` | Cross Racing Championship Extreme 2005 (2005) | `crysis` | Crysis (2007) | `crysis2` | Crysis 2 (2011) | `crysiswars` | Crysis Wars (2008) | `daikatana` | Daikatana (2000) -| `dnl` | Dark and Light (2017) -| `dmomam` | Dark Messiah of Might and Magic (2006) +| `dnl` | Dark and Light (2017) | [Valve Protocol](#valve) +| `dmomam` | Dark Messiah of Might and Magic (2006) | [Valve Protocol](#valve) | `darkesthour` | Darkest Hour: Europe '44-'45 (2008) -| `dod` | Day of Defeat (2003) -| `dods` | Day of Defeat: Source (2005) -| `doi` | Day of Infamy (2017) -| `daysofwar` | Days of War (2017) -| `dayz` | DayZ (2018) -| `dayzmod` | DayZ Mod (2013) +| `dod` | Day of Defeat (2003) | [Valve Protocol](#valve) +| `dods` | Day of Defeat: Source (2005) | [Valve Protocol](#valve) +| `doi` | Day of Infamy (2017) | [Valve Protocol](#valve) +| `daysofwar` | Days of War (2017) | [Valve Protocol](#valve) +| `dayz` | DayZ (2018) | [Valve Protocol](#valve) +| `dayzmod` | DayZ Mod (2013) | [Valve Protocol](#valve) | `deadlydozenpt` | Deadly Dozen: Pacific Theater (2002) | `dh2005` | Deer Hunter 2005 (2004) | `descent3` | Descent 3 (1999) | `deusex` | Deus Ex (2000) | `devastation` | Devastation (2003) -| `dinodday` | Dino D-Day (2011) +| `dinodday` | Dino D-Day (2011) | [Valve Protocol](#valve) | `dirttrackracing2` | Dirt Track Racing 2 (2002) | `doom3` | Doom 3 (2004) -| `dota2` | Dota 2 (2013) +| `dota2` | Dota 2 (2013) | [Valve Protocol](#valve) | `drakan` | Drakan: Order of the Flame (1999) -| `empyrion` | Empyrion - Galactic Survival (2015) +| `empyrion` | Empyrion - Galactic Survival (2015) | [Valve Protocol](#valve) | `etqw` | Enemy Territory: Quake Wars (2007) | `fear` | F.E.A.R. (2005) | `f1c9902` | F1 Challenge '99-'02 (2002) | `farcry` | Far Cry (2004) | `farcry2` | Far Cry 2 (2008) | `f12002` | Formula One 2002 (2002) -| `fortressforever` | Fortress Forever (2007) +| `fortressforever` | Fortress Forever (2007) | [Valve Protocol](#valve) | `ffow` | Frontlines: Fuel of War (2008) -| `garrysmod` | Garry's Mod (2004) +| `garrysmod` | Garry's Mod (2004) | [Valve Protocol](#valve) | `geneshift`
`mutantfactions` | Geneshift (2017) | `giantscitizenkabuto` | Giants: Citizen Kabuto (2000) | `globaloperations` | Global Operations (2002) -| `ges` | GoldenEye: Source (2010) +| `ges` | GoldenEye: Source (2010) | [Valve Protocol](#valve) | `gore` | Gore: Ultimate Soldier (2002) | `fivem` | Grand Theft Auto V - FiveM (2013) | `mtasa` | Grand Theft Auto: San Andreas - Multi Theft Auto (2004) | `mtavc` | Grand Theft Auto: Vice City - Multi Theft Auto (2002) -| `gunmanchronicles` | Gunman Chronicles (2000) -| `hl2dm` | Half-Life 2: Deathmatch (2004) -| `hldm` | Half-Life Deathmatch (1998) -| `hldms` | Half-Life Deathmatch: Source (2005) +| `gunmanchronicles` | Gunman Chronicles (2000) | [Valve Protocol](#valve) +| `hl2dm` | Half-Life 2: Deathmatch (2004) | [Valve Protocol](#valve) +| `hldm` | Half-Life Deathmatch (1998) | [Valve Protocol](#valve) +| `hldms` | Half-Life Deathmatch: Source (2005) | [Valve Protocol](#valve) | `halo` | Halo (2003) | `halo2` | Halo 2 (2007) -| `hll` | Hell Let Loose +| `hll` | Hell Let Loose | [Valve Protocol](#valve) | `heretic2` | Heretic II (1998) | `hexen2` | Hexen II (1997) | `had2` | Hidden & Dangerous 2 (2003) -| `homefront` | Homefront (2011) +| `homefront` | Homefront (2011) | [Valve Protocol](#valve) | `homeworld2` | Homeworld 2 (2003) -| `hurtworld` | Hurtworld (2015) +| `hurtworld` | Hurtworld (2015) | [Valve Protocol](#valve) | `igi2` | I.G.I.-2: Covert Strike (2003) | `il2` | IL-2 Sturmovik (2001) -| `insurgency` | Insurgency (2014) -| `insurgencysandstorm` | Insurgency: Sandstorm (2018) +| `insurgency` | Insurgency (2014) | [Valve Protocol](#valve) +| `insurgencysandstorm` | Insurgency: Sandstorm (2018) | [Valve Protocol](#valve) | `ironstorm` | Iron Storm (2002) | `jamesbondnightfire` | James Bond 007: Nightfire (2002) | `jc2mp` | Just Cause 2 - Multiplayer (2010) -| `jc3mp` | Just Cause 3 - Multiplayer (2017) +| `jc3mp` | Just Cause 3 - Multiplayer (2017) | [Valve Protocol](#valve) | `kspdmp` | Kerbal Space Program - DMP Multiplayer (2015) | `killingfloor` | Killing Floor (2009) -| `killingfloor2` | Killing Floor 2 (2016) +| `killingfloor2` | Killing Floor 2 (2016) | [Valve Protocol](#valve) | `kingpin` | Kingpin: Life of Crime (1999) | `kisspc` | Kiss: Psycho Circus: The Nightmare Child (2000) -| `kzmod` | Kreedz Climbing (2017) -| `left4dead` | Left 4 Dead (2008) -| `left4dead2` | Left 4 Dead 2 (2009) +| `kzmod` | Kreedz Climbing (2017) | [Valve Protocol](#valve) +| `left4dead` | Left 4 Dead (2008) | [Valve Protocol](#valve) +| `left4dead2` | Left 4 Dead 2 (2009) | [Valve Protocol](#valve) | `m2mp` | Mafia II - Multiplayer (2010) | `m2o` | Mafia II - Online (2010) | `moh2010` | Medal of Honor (2010) @@ -220,16 +219,16 @@ Games List | `mohsh` | Medal of Honor: Allied Assault Spearhead (2002) | `mohpa` | Medal of Honor: Pacific Assault (2004) | `mohwf` | Medal of Honor: Warfighter (2012) -| `medievalengineers` | Medieval Engineers (2015) +| `medievalengineers` | Medieval Engineers (2015) | [Valve Protocol](#valve) | `minecraft`
`minecraftping` | Minecraft (2009) | `minecraftpe`
`minecraftbe` | Minecraft: Bedrock Edition (2011) -| `mnc` | Monday Night Combat (2011) -| `mordhau` | Mordhau (2019) +| `mnc` | Monday Night Combat (2011) | [Valve Protocol](#valve) +| `mordhau` | Mordhau (2019) | [Valve Protocol](#valve) | `mumble` | Mumble - GTmurmur Plugin (2005) | [Notes](#mumble) | `mumbleping` | Mumble - Lightweight (2005) | [Notes](#mumble) | `nascarthunder2004` | NASCAR Thunder 2004 (2003) -| `ns` | Natural Selection (2002) -| `ns2` | Natural Selection 2 (2012) +| `ns` | Natural Selection (2002) | [Valve Protocol](#valve) +| `ns2` | Natural Selection 2 (2012) | [Valve Protocol](#valve) | `nfshp2` | Need for Speed: Hot Pursuit 2 (2002) | `nab` | Nerf Arena Blast (1999) | `netpanzer` | netPanzer (2002) @@ -237,56 +236,56 @@ Games List | `nwn2` | Neverwinter Nights 2 (2006) | `nexuiz` | Nexuiz (2005) | `nitrofamily` | Nitro Family (2004) -| `nmrih` | No More Room in Hell (2011) +| `nmrih` | No More Room in Hell (2011) | [Valve Protocol](#valve) | `nolf2` | No One Lives Forever 2: A Spy in H.A.R.M.'s Way (2002) -| `nucleardawn` | Nuclear Dawn (2011) +| `nucleardawn` | Nuclear Dawn (2011) | [Valve Protocol](#valve) | `openarena` | OpenArena (2005) | `openttd` | OpenTTD (2004) | `operationflashpoint`
`flashpoint` | Operation Flashpoint: Cold War Crisis (2001) | `flashpointresistance` | Operation Flashpoint: Resistance (2002) | `painkiller` | Painkiller -| `pixark` | PixARK (2018) +| `pixark` | PixARK (2018) | [Valve Protocol](#valve) | `postal2` | Postal 2 | `prey` | Prey -| `primalcarnage` | Primal Carnage: Extinction +| `primalcarnage` | Primal Carnage: Extinction | [Valve Protocol](#valve) | `prbf2` | Project Reality: Battlefield 2 (2005) | `quake1` | Quake 1: QuakeWorld (1996) | `quake2` | Quake 2 (1997) | `quake3` | Quake 3: Arena (1999) | `quake4` | Quake 4 (2005) -| `quakelive` | Quake Live (2010) -| `ragdollkungfu` | Rag Doll Kung Fu +| `quakelive` | Quake Live (2010) | [Valve Protocol](#valve) +| `ragdollkungfu` | Rag Doll Kung Fu | [Valve Protocol](#valve) | `r6` | Rainbow Six | `r6roguespear` | Rainbow Six 2: Rogue Spear | `r6ravenshield` | Rainbow Six 3: Raven Shield | `rallisportchallenge` | RalliSport Challenge | `rallymasters` | Rally Masters | `redorchestra` | Red Orchestra -| `redorchestra2` | Red Orchestra 2 +| `redorchestra2` | Red Orchestra 2 | [Valve Protocol](#valve) | `redorchestraost` | Red Orchestra: Ostfront 41-45 | `redline` | Redline | `rtcw` | Return to Castle Wolfenstein | `rfactor` | rFactor -| `ricochet` | Ricochet +| `ricochet` | Ricochet | [Valve Protocol](#valve) | `riseofnations` | Rise of Nations -| `rs2` | Rising Storm 2: Vietnam +| `rs2` | Rising Storm 2: Vietnam | [Valve Protocol](#valve) | `rune` | Rune -| `rust` | Rust +| `rust` | Rust | [Valve Protocol](#valve) | `stalker` | S.T.A.L.K.E.R. | `samp` | San Andreas Multiplayer | `savage2` | Savage 2: A Tortured Soul (2008) | `ss` | Serious Sam | `ss2` | Serious Sam 2 -| `shatteredhorizon` | Shattered Horizon +| `shatteredhorizon` | Shattered Horizon | [Valve Protocol](#valve) | `shogo` | Shogo | `shootmania` | Shootmania | [Notes](#nadeo-shootmania--trackmania--etc) | `sin` | SiN -| `sinep` | SiN Episodes +| `sinep` | SiN Episodes | [Valve Protocol](#valve) | `soldat` | Soldat | `sof` | Soldier of Fortune | `sof2` | Soldier of Fortune 2 -| `spaceengineers` | Space Engineers -| `squad` | Squad +| `spaceengineers` | Space Engineers | [Valve Protocol](#valve) +| `squad` | Squad | [Valve Protocol](#valve) | `stbc` | Star Trek: Bridge Commander | `stvef` | Star Trek: Voyager - Elite Force | `stvef2` | Star Trek: Voyager - Elite Force 2 @@ -295,32 +294,32 @@ Games List | `swbf` | Star Wars: Battlefront | `swbf2` | Star Wars: Battlefront 2 | `swrc` | Star Wars: Republic Commando -| `starbound` | Starbound +| `starbound` | Starbound | [Valve Protocol](#valve) | `starmade` | StarMade | `starsiege` | Starsiege (2009) -| `suicidesurvival` | Suicide Survival -| `svencoop` | Sven Coop +| `suicidesurvival` | Suicide Survival | [Valve Protocol](#valve) +| `svencoop` | Sven Coop | [Valve Protocol](#valve) | `swat4` | SWAT 4 -| `synergy` | Synergy +| `synergy` | Synergy | [Valve Protocol](#valve) | `tacticalops` | Tactical Ops | `takeonhelicopters` | Take On Helicopters (2011) | `teamfactor` | Team Factor -| `tf2` | Team Fortress 2 -| `tfc` | Team Fortress Classic +| `tf2` | Team Fortress 2 | [Valve Protocol](#valve) +| `tfc` | Team Fortress Classic | [Valve Protocol](#valve) | `teamspeak2` | Teamspeak 2 | `teamspeak3` | Teamspeak 3 | [Notes](#teamspeak3) | `terminus` | Terminus | `terraria`
`tshock` | Terraria - TShock (2011) | [Notes](#terraria) -| `forrest` | The Forrest (2014) -| `hidden` | The Hidden (2005) +| `forrest` | The Forrest (2014) | [Valve Protocol](#valve) +| `hidden` | The Hidden (2005) | [Valve Protocol](#valve) | `nolf` | The Operative: No One Lives Forever (2000) -| `ship` | The Ship +| `ship` | The Ship | [Valve Protocol](#valve) | `graw` | Tom Clancy's Ghost Recon Advanced Warfighter (2006) | `graw2` | Tom Clancy's Ghost Recon Advanced Warfighter 2 (2007) | `thps3` | Tony Hawk's Pro Skater 3 | `thps4` | Tony Hawk's Pro Skater 4 | `thu2` | Tony Hawk's Underground 2 -| `towerunite` | Tower Unite +| `towerunite` | Tower Unite | [Valve Protocol](#valve) | `trackmania2` | Trackmania 2 | [Notes](#nadeo-shootmania--trackmania--etc) | `trackmaniaforever` | Trackmania Forever | [Notes](#nadeo-shootmania--trackmania--etc) | `tremulous` | Tremulous @@ -334,10 +333,10 @@ Games List | `ut2003` | Unreal Tournament 2003 | `ut2004` | Unreal Tournament 2004 | `ut3` | Unreal Tournament 3 -| `unturned` | unturned +| `unturned` | unturned | [Valve Protocol](#valve) | `urbanterror` | Urban Terror | `v8supercar` | V8 Supercar Challenge -| `valheim` | Valheim (2021) +| `valheim` | Valheim (2021) | [Valve Protocol](#valve) | `ventrilo` | Ventrilo | `vcmp` | Vice City Multiplayer | `vietcong` | Vietcong @@ -347,8 +346,8 @@ Games List | `wolfenstein2009` | Wolfenstein 2009 | `wolfensteinet` | Wolfenstein: Enemy Territory | `xpandrally` | Xpand Rally -| `zombiemaster` | Zombie Master -| `zps` | Zombie Panic: Source +| `zombiemaster` | Zombie Master | [Valve Protocol](#valve) +| `zps` | Zombie Panic: Source | [Valve Protocol](#valve) @@ -453,7 +452,11 @@ For teamspeak 3 queries to work correctly, the following permissions must be ava ### Terraria Requires tshock server mod, and a REST user token, which can be passed to GameDig with the -additional option: token +additional option: `token` + +### Valve Protocol +For many valve games, additional 'rules' may be fetched into the unstable `raw` field by passing the additional +option: `requestRules: true`. Beware that this may increase query time. Usage from Command Line --- diff --git a/lib/GameResolver.js b/lib/GameResolver.js index b75ad33..4856b9f 100644 --- a/lib/GameResolver.js +++ b/lib/GameResolver.js @@ -24,7 +24,7 @@ class GameResolver { printReadme() { let out = ''; - out += '| GameDig Type ID | Name | Notes\n'; + out += '| GameDig Type ID | Name | See Also\n'; out += '|---|---|---\n'; const sorted = this.games @@ -36,8 +36,16 @@ class GameResolver { let keysOut = game.keys.map(key => '`'+key+'`').join('
'); out += "| " + keysOut.padEnd(10, " ") + " " + "| " + game.pretty; - if(game.extra.doc_notes) - out += " | [Notes](#"+game.extra.doc_notes+")"; + let notes = []; + if(game.extra.doc_notes) { + notes.push("[Notes](#" + game.extra.doc_notes + ")"); + } + if(game.options.protocol === 'valve') { + notes.push('[Valve Protocol](#valve)'); + } + if(notes.length) { + out += " | " + notes.join(', '); + } out += "\n"; } return out; diff --git a/package.json b/package.json index 5d4040e..63e5dd7 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "2.0.28", + "version": "3.0.0", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" From fec5a1fac6b3012ec860b51cc7c39cb0b97d5676 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Sat, 10 Apr 2021 22:27:33 -0500 Subject: [PATCH 26/41] Fix raw player subobject in fivem, assettocorsa, gamespy2 Fixes #222 Clarify nodejs 12 requirement for gamedig 3 Fixes #220 Release 3.0.1 --- CHANGELOG.md | 5 +++++ package-lock.json | 8 ++++---- package.json | 6 +++--- protocols/assettocorsa.js | 21 ++++++++++----------- protocols/fivem.js | 1 - protocols/gamespy2.js | 4 +++- 6 files changed, 25 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f49af4..88b7468 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ +### 3.0.1 +* Clarified that nodejs 12 is now required for gamedig 3 +* Fixed misc player fields not going into `raw` subobject in `assettocorsa`, `fivem`, and `gamespy2` + ### 3.0.0 Major Changes: +* **NodeJS 12 is now required** * The `name` field is now guaranteed to exist on all player objects. If a player's name is unknown, the `name` will be an empty string. * All non-`name` player fields have been moved into a `raw` sub-field. This means that, like the `raw` subobject of the parent response, all non-`name` fields are now considered to be unstable and may be changed during minor releases of GameDig. diff --git a/package-lock.json b/package-lock.json index 9948560..4a2b8dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gamedig", - "version": "2.0.21", + "version": "3.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -51,9 +51,9 @@ } }, "@types/node": { - "version": "8.10.61", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.61.tgz", - "integrity": "sha512-l+zSbvT8TPRaCxL1l9cwHCb0tSqGAGcjPJFItGGYat5oCTiq1uQQKYg5m7AF1mgnEBzFXGLJ2LRmNjtreRX76Q==" + "version": "12.20.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.7.tgz", + "integrity": "sha512-gWL8VUkg8VRaCAUgG9WmhefMqHmMblxe2rVpMF86nZY/+ZysU+BkAp+3cz03AixWDSSz0ks5WX59yAhv/cDwFA==" }, "@types/responselike": { "version": "1.0.0", diff --git a/package.json b/package.json index 63e5dd7..65010ab 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "3.0.0", + "version": "3.0.1", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" @@ -34,7 +34,7 @@ }, "license": "MIT", "engines": { - "node": ">=8.0.0" + "node": ">=12.0.0" }, "dependencies": { "cheerio": "^1.0.0-rc.3", @@ -61,6 +61,6 @@ ], "devDependencies": { "@types/cheerio": "^0.22.21", - "@types/node": "^8.10.61" + "@types/node": "^12.20.7" } } diff --git a/protocols/assettocorsa.js b/protocols/assettocorsa.js index 1bbb9ba..98d9fac 100644 --- a/protocols/assettocorsa.js +++ b/protocols/assettocorsa.js @@ -23,19 +23,18 @@ class AssettoCorsa extends Core { state.raw.carInfo = carInfo.Cars; state.raw.serverInfo = serverInfo; - state.players = carInfo.Cars.reduce((r, e) => { - if (e.IsConnected) { - r.push({ - name: e.DriverName, - car: e.Model, - skin: e.Skin, - nation: e.DriverNation, - team: e.DriverTeam + for (const car of carInfo.Cars) { + if (car.IsConnected) { + state.players.push({ + name: car.DriverName, + car: car.Model, + skin: car.Skin, + nation: car.DriverNation, + team: car.DriverTeam }); } - return r; - }, state.players); + } } } -module.exports = AssettoCorsa; \ No newline at end of file +module.exports = AssettoCorsa; diff --git a/protocols/fivem.js b/protocols/fivem.js index 20af4c2..8fc0288 100644 --- a/protocols/fivem.js +++ b/protocols/fivem.js @@ -25,7 +25,6 @@ class FiveM extends Quake2 { responseType: 'json' }); state.raw.players = json; - state.players = []; for (const player of json) { state.players.push({name: player.name, ping: player.ping}); } diff --git a/protocols/gamespy2.js b/protocols/gamespy2.js index 2936762..b913859 100644 --- a/protocols/gamespy2.js +++ b/protocols/gamespy2.js @@ -29,7 +29,9 @@ class Gamespy2 extends Core { { const body = await this.sendPacket([0, 0xff, 0]); const reader = this.reader(body); - state.players = this.readFieldData(reader); + for (const rawPlayer of this.readFieldData(reader)) { + state.players.push(rawPlayer); + } } // Parse teams From 1b11a132b99dda5e6047a231597b61252f18b45c Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Tue, 27 Apr 2021 00:27:14 -0500 Subject: [PATCH 27/41] Fix player name extraction for Unreal Tournament (1999) and possibly other gamespy1 games. (3.0.2) --- CHANGELOG.md | 4 ++++ package.json | 2 +- protocols/gamespy1.js | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88b7468..83e872e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### 3.0.2 +* Fix player name extraction for Unreal Tournament (1999) and possibly + other gamespy1 games. + ### 3.0.1 * Clarified that nodejs 12 is now required for gamedig 3 * Fixed misc player fields not going into `raw` subobject in `assettocorsa`, `fivem`, and `gamespy2` diff --git a/package.json b/package.json index 65010ab..cc51ee7 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "3.0.1", + "version": "3.0.2", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" diff --git a/protocols/gamespy1.js b/protocols/gamespy1.js index 206bf54..d1d4934 100644 --- a/protocols/gamespy1.js +++ b/protocols/gamespy1.js @@ -37,10 +37,12 @@ class Gamespy1 extends Core { if (!(id in playersById)) playersById[id] = {}; if (key === 'playername') { key = 'name'; + } else if (key === 'player') { + key = 'name'; } else if (key === 'team' && !isNaN(parseInt(value))) { key = 'teamId'; value = parseInt(value); - } else if (key === 'score' || key === 'ping' || key === 'deaths' || key === 'kills') { + } else if (key === 'score' || key === 'ping' || key === 'deaths' || key === 'kills' || key === 'frags') { value = parseInt(value); } playersById[id][key] = value; From e8f2c174fb5fb9c6069aa3df09fb3a40b0f85ecc Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Tue, 27 Apr 2021 18:12:22 -0500 Subject: [PATCH 28/41] Improve gamespy1 (3.0.3) --- CHANGELOG.md | 3 + package.json | 2 +- protocols/gamespy1.js | 155 +++++++++++++++++++++++++----------------- 3 files changed, 98 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83e872e..74d0e7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### 3.0.3 +* Greatly improve gamespy1 protocol, with additional error handling and xserverquery support. + ### 3.0.2 * Fix player name extraction for Unreal Tournament (1999) and possibly other gamespy1 games. diff --git a/package.json b/package.json index cc51ee7..f80e7a0 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "3.0.2", + "version": "3.0.3", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" diff --git a/protocols/gamespy1.js b/protocols/gamespy1.js index d1d4934..64330f9 100644 --- a/protocols/gamespy1.js +++ b/protocols/gamespy1.js @@ -1,5 +1,33 @@ const Core = require('./core'); +const stringKeys = new Set([ + 'website', + 'gametype', + 'gamemode', + 'player' +]); + +function normalizeEntry([key,value]) { + key = key.toLowerCase(); + const split = key.split('_'); + let keyType; + if (split.length === 2 && !isNaN(parseInt(split[1]))) { + keyType = split[0]; + } else { + keyType = key; + } + if (!stringKeys.has(keyType) && !keyType.includes('name')) { + if (value.toLowerCase() === 'true') { + value = true; + } else if (value.toLowerCase() === 'false') { + value = false; + } else if (!isNaN(parseInt(value))) { + value = parseInt(value); + } + } + return [key,value]; +} + class Gamespy1 extends Core { constructor() { super(); @@ -8,75 +36,80 @@ class Gamespy1 extends Core { } async run(state) { - { - const data = await this.sendPacket('info'); - state.raw = data; - if ('hostname' in state.raw) state.name = state.raw.hostname; - if ('mapname' in state.raw) state.map = state.raw.mapname; - if (this.trueTest(state.raw.password)) state.password = true; - if ('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers); - if ('hostport' in state.raw) state.gamePort = parseInt(state.raw.hostport); - } - { - const data = await this.sendPacket('rules'); - state.raw.rules = data; - } - { - const data = await this.sendPacket('players'); - const playersById = {}; - const teamNamesById = {}; - for (const ident of Object.keys(data)) { - const split = ident.split('_'); - let key = split[0]; - const id = split[1]; - let value = data[ident]; + const raw = await this.sendPacket('\\status\\xserverquery'); + // Convert all keys to lowercase and normalize value types + const data = Object.fromEntries(Object.entries(raw).map(entry => normalizeEntry(entry))); + state.raw = data; + if ('hostname' in data) state.name = data.hostname; + if ('mapname' in data) state.map = data.mapname; + if (this.trueTest(data.password)) state.password = true; + if ('maxplayers' in data) state.maxplayers = parseInt(data.maxplayers); + if ('hostport' in data) state.gamePort = parseInt(data.hostport); + const teamOffByOne = data.gameid === 'bf1942'; + const playersById = {}; + const teamNamesById = {}; + for (const ident of Object.keys(data)) { + const split = ident.split('_'); + if (split.length !== 2) continue; + let key = split[0].toLowerCase(); + const id = parseInt(split[1]); + if (isNaN(id)) continue; + let value = data[ident]; + + delete data[ident]; + + if (key !== 'team' && key.startsWith('team')) { + // Info about a team if (key === 'teamname') { teamNamesById[id] = value; } else { - if (!(id in playersById)) playersById[id] = {}; - if (key === 'playername') { - key = 'name'; - } else if (key === 'player') { - key = 'name'; - } else if (key === 'team' && !isNaN(parseInt(value))) { - key = 'teamId'; - value = parseInt(value); - } else if (key === 'score' || key === 'ping' || key === 'deaths' || key === 'kills' || key === 'frags') { - value = parseInt(value); - } - playersById[id][key] = value; + // other team info which we don't track + } + } else { + // Info about a player + if (!(id in playersById)) playersById[id] = {}; + if (key === 'playername' || key === 'player') { + key = 'name'; + } + if (key === 'team' && !isNaN(parseInt(value))) { + key = 'teamId'; + value = parseInt(value) + (teamOffByOne ? -1 : 0); + } + if (key !== 'name' && !isNaN(parseInt(value))) { + value = parseInt(value); + } + playersById[id][key] = value; + } + } + state.raw.teams = teamNamesById; + + const players = Object.values(playersById); + + const seenHashes = new Set(); + for (const player of players) { + // Some servers (bf1942) report the same player multiple times (bug?) + // Ignore these duplicates + if (player.keyhash) { + if (seenHashes.has(player.keyhash)) { + this.logger.debug("Rejected player with hash " + player.keyhash + " (Duplicate keyhash)"); + continue; + } else { + seenHashes.add(player.keyhash); } } - state.raw.teams = teamNamesById; - const players = Object.values(playersById); - - const seenHashes = new Set(); - for (const player of players) { - // Some servers (bf1942) report the same player multiple times (bug?) - // Ignore these duplicates - if (player.keyhash) { - if (seenHashes.has(player.keyhash)) { - this.logger.debug("Rejected player with hash " + player.keyhash + " (Duplicate keyhash)"); - continue; - } else { - seenHashes.add(player.keyhash); - } + // Convert player's team ID to team name if possible + if (player.hasOwnProperty('teamId')) { + if (Object.keys(teamNamesById).length) { + player.team = teamNamesById[player.teamId] || ''; + } else { + player.team = player.teamId; + delete player.teamId; } - - // Convert player's team ID to team name if possible - if (player.hasOwnProperty('teamId')) { - if (Object.keys(teamNamesById).length) { - player.team = teamNamesById[player.teamId - 1] || ''; - } else { - player.team = player.teamId; - delete player.teamId; - } - } - - state.players.push(player); } + + state.players.push(player); } } @@ -86,7 +119,7 @@ class Gamespy1 extends Core { const parts = new Set(); let maxPartNum = 0; - return await this.udpSend('\\'+type+'\\', buffer => { + return await this.udpSend(type, buffer => { const reader = this.reader(buffer); const str = reader.string(buffer.length); const split = str.split('\\'); From c20f3cc7ba83b93be61d05b310dd737954190bd8 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Tue, 27 Apr 2021 18:18:26 -0500 Subject: [PATCH 29/41] Add note about valheim --- README.md | 5 ++++- games.txt | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d693cf4..28bf245 100644 --- a/README.md +++ b/README.md @@ -336,7 +336,7 @@ Games List | `unturned` | unturned | [Valve Protocol](#valve) | `urbanterror` | Urban Terror | `v8supercar` | V8 Supercar Challenge -| `valheim` | Valheim (2021) | [Valve Protocol](#valve) +| `valheim` | Valheim (2021) | [Notes](#valheim), [Valve Protocol](#valve) | `ventrilo` | Ventrilo | `vcmp` | Vice City Multiplayer | `vietcong` | Vietcong @@ -454,6 +454,9 @@ For teamspeak 3 queries to work correctly, the following permissions must be ava Requires tshock server mod, and a REST user token, which can be passed to GameDig with the additional option: `token` +### Valheim +Valheim servers will only respond to queries if they are started in public mode (`-public 1`). + ### Valve Protocol For many valve games, additional 'rules' may be fetched into the unstable `raw` field by passing the additional option: `requestRules: true`. Beware that this may increase query time. diff --git a/games.txt b/games.txt index 2978c46..4acb7ea 100644 --- a/games.txt +++ b/games.txt @@ -279,7 +279,7 @@ ut3|Unreal Tournament 3|ut3|port=7777,port_query_offset=-1277 urbanterror|Urban Terror|quake3|port_query=27960 v8supercar|V8 Supercar Challenge|gamespy1|port_query=16700 -valheim|Valheim (2021)|valve|port=2456,port_query_offset=1 +valheim|Valheim (2021)|valve|port=2456,port_query_offset=1|doc_notes=valheim vcmp|Vice City Multiplayer|vcmp|port=8192 ventrilo|Ventrilo|ventrilo|port=3784 vietcong|Vietcong|gamespy1|port=5425,port_query=15425 From 2e84f3ac4869791c36bd034e808b3c41327720d2 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Tue, 18 May 2021 07:25:23 -0500 Subject: [PATCH 30/41] Add some FAQ about why queries may fail --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 28bf245..77a16b0 100644 --- a/README.md +++ b/README.md @@ -461,6 +461,25 @@ Valheim servers will only respond to queries if they are started in public mode For many valve games, additional 'rules' may be fetched into the unstable `raw` field by passing the additional option: `requestRules: true`. Beware that this may increase query time. +Important note about Firewalls (replit / docker / some VPS providers) +--- +Most game query protocols require a UDP request and response. This means that in some environments, gamedig may not be able to receive the reponse required due to environmental restrictions. + +Some examples include: +* Docker containers + * You may need to run the container in `--network host` mode so that gamedig can bind a UDP listen port +* replit + * Most online IDEs run in an isolated container, which will never receive UDP responses from outside networks. +* Various VPS / server providers + * Even if your server provider doesn't explicitly block incoming UDP packets, some server hosts block other server hosts from connecting to them for DDOS-mitigation and anti-botting purposes. + +Important note about gamedig in the browser +--- +Gamedig cannot operate within a browser. This means you cannot package it as part of your webpack / browserify / rollup / parcel package. +Even if you were able to get it packaged into a bundle, unfortunately no browsers support the UDP protocols required to query server status +from most game servers. As an alternative, we'd recommend using gamedig on your server-side, then expose your own API to your webapp's frontend +displaying the status information. If your application is thin (with no constant server component), you may wish to investigate a server-less lambda provider. + Usage from Command Line --- From 65851920d126afa3487d22ff5a21fe71461de289 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Tue, 18 May 2021 07:26:13 -0500 Subject: [PATCH 31/41] Update readme formatting --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 77a16b0..35fefd9 100644 --- a/README.md +++ b/README.md @@ -467,11 +467,11 @@ Most game query protocols require a UDP request and response. This means that in Some examples include: * Docker containers - * You may need to run the container in `--network host` mode so that gamedig can bind a UDP listen port + * You may need to run the container in `--network host` mode so that gamedig can bind a UDP listen port * replit - * Most online IDEs run in an isolated container, which will never receive UDP responses from outside networks. + * Most online IDEs run in an isolated container, which will never receive UDP responses from outside networks. * Various VPS / server providers - * Even if your server provider doesn't explicitly block incoming UDP packets, some server hosts block other server hosts from connecting to them for DDOS-mitigation and anti-botting purposes. + * Even if your server provider doesn't explicitly block incoming UDP packets, some server hosts block other server hosts from connecting to them for DDOS-mitigation and anti-botting purposes. Important note about gamedig in the browser --- From d9310db38b392ffc1983117ea9a79a003511b897 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Tue, 18 May 2021 22:11:58 -0500 Subject: [PATCH 32/41] Restore executable bit on gamedig.js --- bin/gamedig.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 bin/gamedig.js diff --git a/bin/gamedig.js b/bin/gamedig.js old mode 100644 new mode 100755 From fe124a44875f0fcc8b3f7302679eee5d2b04811f Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Tue, 18 May 2021 22:33:36 -0500 Subject: [PATCH 33/41] Bring discord protocol up to date with gamedig 3.0 --- README.md | 6 ++++++ bin/gamedig.js | 3 ++- games.txt | 2 +- lib/QueryRunner.js | 7 ++++++- protocols/core.js | 5 ++++- protocols/discord.js | 49 ++++++++++++++++++++++---------------------- 6 files changed, 43 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 28bf245..bd393af 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,7 @@ Games List | `devastation` | Devastation (2003) | `dinodday` | Dino D-Day (2011) | [Valve Protocol](#valve) | `dirttrackracing2` | Dirt Track Racing 2 (2002) +| `discord` | Discord | [Notes](#discord) | `doom3` | Doom 3 (2004) | `dota2` | Dota 2 (2013) | [Valve Protocol](#valve) | `drakan` | Drakan: Order of the Flame (1999) @@ -428,6 +429,11 @@ Games with Additional Notes To receive a full player list response from CS:GO servers, the server must have set the cvar: host_players_show 2 +### Discord +You must set the `guildId` request field to the server's guild ID. Do not provide an IP. +The Guild ID can be found in server widget settings (Server ID) or by enabling developer mode in client settings and right-clicking the server's icon. +In order to retrieve information from discord server's they must have the `Enable server widget` option enabled. + ### Mumble For full query results from Mumble, you must be running the [GTmurmur plugin](http://www.gametracker.com/downloads/gtmurmurplugin.php). diff --git a/bin/gamedig.js b/bin/gamedig.js index 040af19..e4b53e9 100755 --- a/bin/gamedig.js +++ b/bin/gamedig.js @@ -4,7 +4,8 @@ const Minimist = require('minimist'), Gamedig = require('..'); const argv = Minimist(process.argv.slice(2), { - boolean: ['pretty','debug','givenPortOnly'] + boolean: ['pretty','debug','givenPortOnly'], + string: ['guildId'] }); const debug = argv.debug; diff --git a/games.txt b/games.txt index a912011..ee834cc 100644 --- a/games.txt +++ b/games.txt @@ -88,7 +88,7 @@ deusex|Deus Ex (2000)|gamespy2|port=7791,port_query_offset=1 devastation|Devastation (2003)|unreal2|port=7777,port_query_offset=1 dinodday|Dino D-Day (2011)|valve|port=27015 dirttrackracing2|Dirt Track Racing 2 (2002)|gamespy1|port=32240,port_query_offset=-100 -discord|Discord|discord|port=443 +discord|Discord|discord||doc_notes=discord dnl|Dark and Light (2017)|valve|port=7777,port_query=27015 dod|Day of Defeat (2003)|valve|port=27015 dods|Day of Defeat: Source (2005)|valve|port=27015 diff --git a/lib/QueryRunner.js b/lib/QueryRunner.js index 062496a..31eaac0 100644 --- a/lib/QueryRunner.js +++ b/lib/QueryRunner.js @@ -66,7 +66,12 @@ class QueryRunner { port: gameOptions.port + (gameQueryPortOffset || 0) }); } else { - throw new Error("Could not determine port to query. Did you provide a port or gameid?"); + // Hopefully the request doesn't need a port. If it does, it'll fail when making the request. + attempts.push({ + ...defaultOptions, + ...gameOptions, + ...userOptions + }); } const numRetries = userOptions.maxAttempts || gameOptions.maxAttempts || defaultOptions.maxAttempts; diff --git a/protocols/core.js b/protocols/core.js index d2535ff..3e7d1e1 100644 --- a/protocols/core.js +++ b/protocols/core.js @@ -146,7 +146,10 @@ class Core extends EventEmitter { } assertValidPort(port) { - if (!port || port < 1 || port > 65535) { + if (!port) { + throw new Error("Could not determine port to query. Did you provide a port?"); + } + if (port < 1 || port > 65535) { throw new Error("Invalid tcp/ip port: " + port); } } diff --git a/protocols/discord.js b/protocols/discord.js index c70b846..739086e 100644 --- a/protocols/discord.js +++ b/protocols/discord.js @@ -1,32 +1,31 @@ const Core = require('./core'); class Discord extends Core { - constructor() { - super(); - this.dnsResolver = { resolve: function(address) {return {address: address} } }; - } - - async run(state) { - this.usedTcp = true; - const raw = await this.request({ - uri: 'https://discordapp.com/api/guilds/' + this.options.address + '/widget.json', - }); - const json = JSON.parse(raw); - state.name = json.name; - if (json.instant_invite) { - state.connect = json.instant_invite; - } else { - state.connect = 'https://discordapp.com/channels/' + this.options.address + async run(state) { + const guildId = this.options.guildId; + if (typeof guildId !== 'string') { + throw new Error('guildId option must be set when querying discord. Ensure the guildId is a string and not a number.' + + " (It's too large of a number for javascript to store without losing precision)"); + } + this.usedTcp = true; + const raw = await this.request({ + url: 'https://discordapp.com/api/guilds/' + guildId + '/widget.json', + }); + const json = JSON.parse(raw); + state.name = json.name; + if (json.instant_invite) { + state.connect = json.instant_invite; + } else { + state.connect = 'https://discordapp.com/channels/' + guildId; + } + for (const member of json.members) { + const {username: name, ...rest} = member; + state.players.push({ name, ...rest }); + } + delete json.members; + state.maxplayers = 500000; + state.raw = json; } - state.players = json.members.map(v => { - return { - name: v.username, - team: v.status - } - }); - state.maxplayers = json.presence_count; - state.raw = json; - } } module.exports = Discord; From 68b8dfd684c2efac42cd3209c480a0a1936e0631 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Tue, 18 May 2021 22:35:11 -0500 Subject: [PATCH 34/41] 3.0.4 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74d0e7e..ca0e18e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### 3.0.4 +* Add support for Discord widget + ### 3.0.3 * Greatly improve gamespy1 protocol, with additional error handling and xserverquery support. diff --git a/package.json b/package.json index f80e7a0..d965d6b 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "3.0.3", + "version": "3.0.4", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" From ce4e72849386b5771959de7d6e024cf775090279 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Tue, 18 May 2021 23:13:18 -0500 Subject: [PATCH 35/41] Add support for udp bind port override (3.0.5) Fixes #149 --- CHANGELOG.md | 4 ++++ README.md | 24 ++++++++++++++++++++---- bin/gamedig.js | 5 +++-- lib/GlobalUdpSocket.js | 34 ++++++++++++++++++++++++---------- lib/QueryRunner.js | 6 ++++-- lib/index.js | 4 ++-- package.json | 2 +- protocols/core.js | 6 +----- 8 files changed, 59 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca0e18e..84abc29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### 3.0.5 +* Add support for `listenUdpPort` to specify a fixed bind port. +* Improved udp bind failure detection. + ### 3.0.4 * Add support for Discord widget diff --git a/README.md b/README.md index ed25f0c..f5dadb2 100644 --- a/README.md +++ b/README.md @@ -467,25 +467,41 @@ Valheim servers will only respond to queries if they are started in public mode For many valve games, additional 'rules' may be fetched into the unstable `raw` field by passing the additional option: `requestRules: true`. Beware that this may increase query time. -Important note about Firewalls (replit / docker / some VPS providers) +Common Issues --- + +### Firewalls block incoming UDP +*(replit / docker / some VPS providers)* + Most game query protocols require a UDP request and response. This means that in some environments, gamedig may not be able to receive the reponse required due to environmental restrictions. Some examples include: * Docker containers - * You may need to run the container in `--network host` mode so that gamedig can bind a UDP listen port + * You may need to run the container in `--network host` mode so that gamedig can bind a UDP listen port. + * Alternatively, you can forward a single UDP port to your container, and force gamedig to listen on that port using the + instructions in the section down below. * replit * Most online IDEs run in an isolated container, which will never receive UDP responses from outside networks. * Various VPS / server providers * Even if your server provider doesn't explicitly block incoming UDP packets, some server hosts block other server hosts from connecting to them for DDOS-mitigation and anti-botting purposes. -Important note about gamedig in the browser ---- +### Gamedig doesn't work in the browser Gamedig cannot operate within a browser. This means you cannot package it as part of your webpack / browserify / rollup / parcel package. Even if you were able to get it packaged into a bundle, unfortunately no browsers support the UDP protocols required to query server status from most game servers. As an alternative, we'd recommend using gamedig on your server-side, then expose your own API to your webapp's frontend displaying the status information. If your application is thin (with no constant server component), you may wish to investigate a server-less lambda provider. +### Specifying a listen UDP port override +In some very rare scenarios, you may need to bind / listen on a fixed local UDP port. The is usually not needed except behind +some extremely strict firewalls, or within a docker container (where you only wish to forward a single UDP port). +To use a fixed listen udp port, construct a new Gamedig object like this: +``` +const gamedig = new Gamedig({ + listenUdpPort: 13337 +}); +gamedig.query(...) +``` + Usage from Command Line --- diff --git a/bin/gamedig.js b/bin/gamedig.js index e4b53e9..8fd9a91 100755 --- a/bin/gamedig.js +++ b/bin/gamedig.js @@ -5,7 +5,7 @@ const Minimist = require('minimist'), const argv = Minimist(process.argv.slice(2), { boolean: ['pretty','debug','givenPortOnly'], - string: ['guildId'] + string: ['guildId','listenUdpPort'] }); const debug = argv.debug; @@ -41,7 +41,8 @@ if (givenPortOnly) { options.givenPortOnly = true; } -Gamedig.query(options) +const gamedig = new Gamedig(options); +gamedig.query(options) .then((state) => { if(pretty) { console.log(JSON.stringify(state,null,' ')); diff --git a/lib/GlobalUdpSocket.js b/lib/GlobalUdpSocket.js index 590ee19..99a6c13 100644 --- a/lib/GlobalUdpSocket.js +++ b/lib/GlobalUdpSocket.js @@ -1,25 +1,29 @@ -const dgram = require('dgram'), - HexUtil = require('./HexUtil'), - Logger = require('./Logger'); +const dgram = require('dgram'); +const HexUtil = require('./HexUtil'); +const Logger = require('./Logger'); +const util = require('util'); class GlobalUdpSocket { - constructor() { + constructor({port}) { this.socket = null; this.callbacks = new Set(); this.debuggingCallbacks = new Set(); this.logger = new Logger(); + this.port = port; } - _getSocket() { + async _getSocket() { if (!this.socket) { - const udpSocket = this.socket = dgram.createSocket('udp4'); + const udpSocket = dgram.createSocket({ + type: 'udp4', + reuseAddr: true + }); udpSocket.unref(); - udpSocket.bind(); udpSocket.on('message', (buffer, rinfo) => { const fromAddress = rinfo.address; const fromPort = rinfo.port; this.logger.debug(log => { - log(fromAddress + ':' + fromPort + " <--UDP"); + log(fromAddress + ':' + fromPort + " <--UDP(" + this.port + ")"); log(HexUtil.debugDump(buffer)); }); for (const cb of this.callbacks) { @@ -29,12 +33,22 @@ class GlobalUdpSocket { udpSocket.on('error', e => { this.logger.debug("UDP ERROR:", e); }); + await util.promisify(udpSocket.bind).bind(udpSocket)(this.port); + this.port = udpSocket.address().port; + this.socket = udpSocket; } return this.socket; } - send(buffer, address, port) { - this._getSocket().send(buffer,0,buffer.length,port,address); + async send(buffer, address, port, debug) { + const socket = await this._getSocket(); + if (debug) { + this.logger._print(log => { + log(address + ':' + port + " UDP(" + this.port + ")-->"); + log(HexUtil.debugDump(buffer)); + }); + } + await util.promisify(socket.send).bind(socket)(buffer,0,buffer.length,port,address); } addCallback(callback, debug) { diff --git a/lib/QueryRunner.js b/lib/QueryRunner.js index 31eaac0..47244b7 100644 --- a/lib/QueryRunner.js +++ b/lib/QueryRunner.js @@ -9,8 +9,10 @@ const defaultOptions = { }; class QueryRunner { - constructor() { - this.udpSocket = new GlobalUdpSocket(); + constructor(runnerOpts = {}) { + this.udpSocket = new GlobalUdpSocket({ + port: runnerOpts.listenUdpPort + }); this.gameResolver = new GameResolver(); this.protocolResolver = new ProtocolResolver(); } diff --git a/lib/index.js b/lib/index.js index 4ded359..b1a1f3f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,8 +3,8 @@ const QueryRunner = require('./QueryRunner'); let singleton = null; class Gamedig { - constructor() { - this.queryRunner = new QueryRunner(); + constructor(runnerOpts) { + this.queryRunner = new QueryRunner(runnerOpts); } async query(userOptions) { diff --git a/package.json b/package.json index d965d6b..0372cef 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "3.0.4", + "version": "3.0.5", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" diff --git a/protocols/core.js b/protocols/core.js index 3e7d1e1..a2c7465 100644 --- a/protocols/core.js +++ b/protocols/core.js @@ -252,13 +252,9 @@ class Core extends EventEmitter { this.assertValidPort(port); if(typeof buffer === 'string') buffer = Buffer.from(buffer,'binary'); - this.debugLog(log => { - log(address+':'+port+" UDP-->"); - log(HexUtil.debugDump(buffer)); - }); const socket = this.udpSocket; - socket.send(buffer, address, port); + await socket.send(buffer, address, port, this.options.debug); if (!onPacket && !onTimeout) { return null; From 1d9cf90259f54ffb0286d4aca83f3c5823a7d6a3 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Thu, 8 Jul 2021 21:51:34 -0500 Subject: [PATCH 36/41] Parse dayz mods, queue length, and time acceleration (3.0.6) Fixes #232 Fixes #234 --- CHANGELOG.md | 5 +++ bin/gamedig.js | 2 +- package.json | 2 +- protocols/valve.js | 105 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 110 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84abc29..1e53523 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +### 3.0.6 +* raw.tags for valve servers is now an array rather than a string +* The special mod list for dayz servers is now parsed into raw.dayzMods is requestRules is set to true +* DayZ queue length, day and night acceleration are now parsed into raw as well + ### 3.0.5 * Add support for `listenUdpPort` to specify a fixed bind port. * Improved udp bind failure detection. diff --git a/bin/gamedig.js b/bin/gamedig.js index 8fd9a91..4351b37 100755 --- a/bin/gamedig.js +++ b/bin/gamedig.js @@ -4,7 +4,7 @@ const Minimist = require('minimist'), Gamedig = require('..'); const argv = Minimist(process.argv.slice(2), { - boolean: ['pretty','debug','givenPortOnly'], + boolean: ['pretty','debug','givenPortOnly','requestRules'], string: ['guildId','listenUdpPort'] }); diff --git a/package.json b/package.json index 0372cef..a28a807 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "3.0.5", + "version": "3.0.6", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" diff --git a/protocols/valve.js b/protocols/valve.js index 3d2fea0..19919e1 100644 --- a/protocols/valve.js +++ b/protocols/valve.js @@ -5,7 +5,8 @@ const Bzip2 = require('compressjs').Bzip2, const AppId = { Squad: 393380, Bat1944: 489940, - Ship: 2400 + Ship: 2400, + DayZ: 221100 }; class Valve extends Core { @@ -104,7 +105,7 @@ class Valve extends Core { state.raw.sourcetvport = reader.uint(2); state.raw.sourcetvname = reader.string(); } - if(extraFlag & 0x20) state.raw.tags = reader.string(); + if(extraFlag & 0x20) state.raw.tags = reader.string().split(','); if(extraFlag & 0x01) { const gameId = reader.uint(8); const betterAppId = gameId.getLowBitsUnsigned() & 0xffffff; @@ -203,9 +204,29 @@ class Valve extends Core { const b = await this.sendPacket(0x56,null,0x45,true); if (b === null) return; // timed out - the server probably has rules disabled + const dayZPayload = []; + let dayZPayloadEnded = false; + const reader = this.reader(b); const num = reader.uint(2); for(let i = 0; i < num; i++) { + if (appId === AppId.DayZ && !dayZPayloadEnded) { + const one = reader.uint(1); + const two = reader.uint(1); + const three = reader.uint(1); + if (one !== 0 && two !== 0 && three === 0) { + while (true) { + const byte = reader.uint(1); + if (byte === 0) break; + dayZPayload.push(byte); + } + continue; + } else { + reader.skip(-3); + dayZPayloadEnded = true; + } + } + const key = reader.string(); const value = reader.string(); rules[key] = value; @@ -239,6 +260,86 @@ class Valve extends Core { state.password = true; } } + + if (appId === AppId.DayZ) { + state.raw.dayzMods = this.readDayzMods(Buffer.from(dayZPayload)); + + if (state.raw.tags) { + for (const tag of state.raw.tags) { + if (tag.startsWith('lqs')) { + const value = parseInt(tag.replace('lqs', '')); + if (!isNaN(value)) { + state.raw.queue = value; + } + } + if (tag.startsWith('etm')) { + const value = parseInt(tag.replace('etm', '')); + if (!isNaN(value)) { + state.raw.dayAcceleration = value; + } + } + if (tag.startsWith('entm')) { + const value = parseInt(tag.replace('entm', '')); + if (!isNaN(value)) { + state.raw.nightAcceleration = value; + } + } + } + } + } + } + + readDayzMods(/** Buffer */ buffer) { + if (!buffer.length) { + return {}; + } + + this.logger.debug("DAYZ BUFFER"); + this.logger.debug(buffer); + + const reader = this.reader(buffer); + reader.skip(8); // always 01 01 01 02 01 02 01 02 + const mods = []; + mods.push(...this.readDayzModsSection(reader, true)); + mods.push(...this.readDayzModsSection(reader, false)); + return mods; + } + readDayzModsSection(reader, withHeader) { + const out = []; + const count = reader.uint(1); + for(let i = 0; i < count; i++) { + const mod = {}; + if (withHeader) { + mod.unknown = this.readDayzNum(reader); + if (i !== count - 1) { + // For some reason this is 4 on all of them, but doesn't exist on the last one? + reader.skip(1); + } + mod.workshopId = this.readDayzNum(reader); + } + mod.title = reader.pascalString(1); + out.push(mod); + } + return out; + } + readDayzNum(reader) { + const out = []; + for (let i = 0; i < 4; i++) { + out.push(this.readDayzByte(reader)); + } + const buf = Buffer.from(out); + const r2 = this.reader(buf); + return r2.uint(4); + } + readDayzByte(reader) { + const byte = reader.uint(1); + if (byte === 1) { + const byte2 = reader.uint(1); + if (byte2 === 1) return 1; + if (byte2 === 2) return 0; + return 0; // ? + } + return byte; } async cleanup(/** Results */ state) { From a8b7cad002a2a45dad152bbf34572ba640f30f6c Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Fri, 9 Jul 2021 02:23:36 -0500 Subject: [PATCH 37/41] Fix corrupted dayzMods when packet overflow is used (3.0.7) --- CHANGELOG.md | 3 +++ package.json | 2 +- protocols/valve.js | 35 ++++++++++++++++++++++++++--------- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e53523..1e9b9f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### 3.0.7 +* Fixes corrupted dayzMods when packet overflow is present + ### 3.0.6 * raw.tags for valve servers is now an array rather than a string * The special mod list for dayz servers is now parsed into raw.dayzMods is requestRules is set to true diff --git a/package.json b/package.json index a28a807..fa901e8 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "3.0.6", + "version": "3.0.7", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" diff --git a/protocols/valve.js b/protocols/valve.js index 19919e1..f82803c 100644 --- a/protocols/valve.js +++ b/protocols/valve.js @@ -298,7 +298,14 @@ class Valve extends Core { this.logger.debug(buffer); const reader = this.reader(buffer); - reader.skip(8); // always 01 01 01 02 01 02 01 02 + const version = this.readDayzByte(reader); + const overflow = this.readDayzByte(reader); + const dlc1 = this.readDayzByte(reader); + const dlc2 = this.readDayzByte(reader); + this.logger.debug("version " + version); + this.logger.debug("overflow " + overflow); + this.logger.debug("dlc1 " + dlc1); + this.logger.debug("dlc2 " + dlc2); const mods = []; mods.push(...this.readDayzModsSection(reader, true)); mods.push(...this.readDayzModsSection(reader, false)); @@ -306,30 +313,31 @@ class Valve extends Core { } readDayzModsSection(reader, withHeader) { const out = []; - const count = reader.uint(1); + const count = this.readDayzByte(reader); for(let i = 0; i < count; i++) { const mod = {}; if (withHeader) { - mod.unknown = this.readDayzNum(reader); + const unknown = this.readDayzUint(reader, 4); // mod hash? if (i !== count - 1) { // For some reason this is 4 on all of them, but doesn't exist on the last one? - reader.skip(1); + const flag = this.readDayzByte(reader); + //mod.flag = flag; } - mod.workshopId = this.readDayzNum(reader); + mod.workshopId = this.readDayzUint(reader, 4); } - mod.title = reader.pascalString(1); + mod.title = this.readDayzString(reader); out.push(mod); } return out; } - readDayzNum(reader) { + readDayzUint(reader, bytes) { const out = []; - for (let i = 0; i < 4; i++) { + for (let i = 0; i < bytes; i++) { out.push(this.readDayzByte(reader)); } const buf = Buffer.from(out); const r2 = this.reader(buf); - return r2.uint(4); + return r2.uint(bytes); } readDayzByte(reader) { const byte = reader.uint(1); @@ -337,10 +345,19 @@ class Valve extends Core { const byte2 = reader.uint(1); if (byte2 === 1) return 1; if (byte2 === 2) return 0; + if (byte2 === 3) return 0xff; return 0; // ? } return byte; } + readDayzString(reader) { + const length = this.readDayzByte(reader); + const out = []; + for (let i = 0; i < length; i++) { + out.push(this.readDayzByte(reader)); + } + return Buffer.from(out).toString('utf8'); + } async cleanup(/** Results */ state) { // Organize players / hidden players into player / bot arrays From c331eac5bbaaaed8edfadf430d78df09fab1dffb Mon Sep 17 00:00:00 2001 From: cetteup Date: Wed, 4 Aug 2021 19:52:32 +0200 Subject: [PATCH 38/41] Fix teamOffByOne condition for Battlefield 1942 --- protocols/gamespy1.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/gamespy1.js b/protocols/gamespy1.js index 64330f9..0b04e9a 100644 --- a/protocols/gamespy1.js +++ b/protocols/gamespy1.js @@ -46,7 +46,7 @@ class Gamespy1 extends Core { if ('maxplayers' in data) state.maxplayers = parseInt(data.maxplayers); if ('hostport' in data) state.gamePort = parseInt(data.hostport); - const teamOffByOne = data.gameid === 'bf1942'; + const teamOffByOne = data.gamename === 'bfield1942'; const playersById = {}; const teamNamesById = {}; for (const ident of Object.keys(data)) { From e6db2a9b723a43eb7d650c53795cecff30c56889 Mon Sep 17 00:00:00 2001 From: Smith Date: Thu, 9 Dec 2021 22:25:38 +0100 Subject: [PATCH 39/41] fix undef prop --- lib/Results.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Results.js b/lib/Results.js index 2ca54ba..fc7a252 100644 --- a/lib/Results.js +++ b/lib/Results.js @@ -19,7 +19,7 @@ class Players extends Array { // run out of ram allocating these objects. num = Math.min(num, 10000); - while(this.players.length < num) { + while(this.length < num) { this.push({}); } } From 6354e34d182229bafcb3f52a6dc08efca9dd8d74 Mon Sep 17 00:00:00 2001 From: Smith Date: Thu, 9 Dec 2021 22:41:12 +0100 Subject: [PATCH 40/41] handle ambiguity of the setters (players, bots) * cases where the protocol overwrites the property with a prepared Players instance (eg.: minecraft & gamespy3) --- lib/Results.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/Results.js b/lib/Results.js index fc7a252..fafa54a 100644 --- a/lib/Results.js +++ b/lib/Results.js @@ -40,11 +40,19 @@ class Results { players = new Players(); bots = new Players(); - set players(num) { - this.players.setNum(num); + set players(val) { + if (typeof val === 'number') { + this.players.setNum(val); + } else if (Array.isArray(val)) { + this.players = val; + } } - set bots(num) { - this.bots.setNum(num); + set bots(val) { + if (typeof val === 'number') { + this.bots.setNum(val); + } else if (Array.isArray(val)) { + this.bots = val; + } } } From 5c2d15df495bb0467d31e2d2782584295297b8a3 Mon Sep 17 00:00:00 2001 From: Michael Morrison <517502+mmorrisontx@users.noreply.github.com> Date: Thu, 9 Dec 2021 18:08:36 -0600 Subject: [PATCH 41/41] Remove complex `players` setter overload (3.0.8) --- CHANGELOG.md | 4 ++++ lib/Results.js | 15 --------------- package.json | 2 +- protocols/geneshift.js | 2 +- protocols/jc2mp.js | 2 +- protocols/minecraft.js | 16 +++++++++------- protocols/minecraftbedrock.js | 2 +- protocols/mumbleping.js | 2 +- protocols/openttd.js | 2 +- protocols/rfactor.js | 2 +- protocols/samp.js | 2 +- protocols/savage2.js | 2 +- protocols/starmade.js | 2 +- 13 files changed, 23 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e9b9f6..64387f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### 3.0.8 +* Fixes player array corruption on some protocols which only report player counts without names (Thanks to a-sync) +* Fixes minecraft protocol not using player list from bedrock protocol in some cases + ### 3.0.7 * Fixes corrupted dayzMods when packet overflow is present diff --git a/lib/Results.js b/lib/Results.js index fafa54a..b2a9ae3 100644 --- a/lib/Results.js +++ b/lib/Results.js @@ -39,21 +39,6 @@ class Results { maxplayers = 0; players = new Players(); bots = new Players(); - - set players(val) { - if (typeof val === 'number') { - this.players.setNum(val); - } else if (Array.isArray(val)) { - this.players = val; - } - } - set bots(val) { - if (typeof val === 'number') { - this.bots.setNum(val); - } else if (Array.isArray(val)) { - this.bots = val; - } - } } module.exports = Results; diff --git a/package.json b/package.json index fa901e8..74ed642 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "main": "lib/index.js", "author": "GameDig Contributors", - "version": "3.0.7", + "version": "3.0.8", "repository": { "type": "git", "url": "https://github.com/gamedig/node-gamedig.git" diff --git a/protocols/geneshift.js b/protocols/geneshift.js index ce0946f..3c9c45c 100644 --- a/protocols/geneshift.js +++ b/protocols/geneshift.js @@ -28,7 +28,7 @@ class GeneShift extends Core { state.raw.country = found[1]; state.name = found[4]; state.map = found[5]; - state.players = parseInt(found[6]); + state.players.setNum(parseInt(found[6])); state.maxplayers = parseInt(found[7]); // fields[8] is unknown? state.raw.rules = found[9]; diff --git a/protocols/jc2mp.js b/protocols/jc2mp.js index 996809c..05f060f 100644 --- a/protocols/jc2mp.js +++ b/protocols/jc2mp.js @@ -12,7 +12,7 @@ class Jc2mp extends Gamespy3 { async run(state) { await super.run(state); if(!state.players.length && parseInt(state.raw.numplayers)) { - state.players = parseInt(state.raw.numplayers); + state.players.setNum(parseInt(state.raw.numplayers)); } } } diff --git a/protocols/minecraft.js b/protocols/minecraft.js index 785f39e..0ea1c1d 100644 --- a/protocols/minecraft.js +++ b/protocols/minecraft.js @@ -1,7 +1,8 @@ -const Core = require('./core'), - MinecraftVanilla = require('./minecraftvanilla'), - MinecraftBedrock = require('./minecraftbedrock'), - Gamespy3 = require('./gamespy3'); +const Core = require('./core'); +const MinecraftVanilla = require('./minecraftvanilla'); +const MinecraftBedrock = require('./minecraftbedrock'); +const Gamespy3 = require('./gamespy3'); +const Results = require('../lib/Results'); /* Vanilla servers respond to minecraftvanilla only @@ -17,6 +18,7 @@ class Minecraft extends Core { this.srvRecord = "_minecraft._tcp"; } async run(state) { + /** @type {Promise[]} */ const promises = []; const vanillaResolver = new MinecraftVanilla(); @@ -57,7 +59,7 @@ class Minecraft extends Core { if (bedrockState) { if (bedrockState.name) state.name = bedrockState.name; if (bedrockState.maxplayers) state.maxplayers = bedrockState.maxplayers; - if (bedrockState.players) state.players = bedrockState.players; + if (bedrockState.players.length) state.players = bedrockState.players; if (bedrockState.map) state.map = bedrockState.map; } if (vanillaState) { @@ -76,13 +78,13 @@ class Minecraft extends Core { state.name = name; } catch(e) {} if (vanillaState.maxplayers) state.maxplayers = vanillaState.maxplayers; - if (vanillaState.players) state.players = vanillaState.players; + if (vanillaState.players.length) state.players = vanillaState.players; } if (gamespyState) { if (gamespyState.name) state.name = gamespyState.name; if (gamespyState.maxplayers) state.maxplayers = gamespyState.maxplayers; if (gamespyState.players.length) state.players = gamespyState.players; - else if (gamespyState.raw.numplayers) state.players = parseInt(gamespyState.raw.numplayers); + else if (gamespyState.raw.numplayers) state.players.setNum(parseInt(gamespyState.raw.numplayers)); } // remove dupe spaces from name state.name = state.name.replace(/\s+/g, ' '); diff --git a/protocols/minecraftbedrock.js b/protocols/minecraftbedrock.js index 4c166fc..3aa658e 100644 --- a/protocols/minecraftbedrock.js +++ b/protocols/minecraftbedrock.js @@ -57,7 +57,7 @@ class MinecraftBedrock extends Core { state.name = split.shift(); state.raw.protocolVersion = split.shift(); state.raw.mcVersion = split.shift(); - state.players = parseInt(split.shift()); + state.players.setNum(parseInt(split.shift())); state.maxplayers = parseInt(split.shift()); if (split.length) state.raw.serverId = split.shift(); if (split.length) state.map = split.shift(); diff --git a/protocols/mumbleping.js b/protocols/mumbleping.js index eca4a4c..b666178 100644 --- a/protocols/mumbleping.js +++ b/protocols/mumbleping.js @@ -17,7 +17,7 @@ class MumblePing extends Core { state.raw.versionMinor = reader.uint(1); state.raw.versionPatch = reader.uint(1); reader.skip(8); - state.players = reader.uint(4); + state.players.setNum(reader.uint(4)); state.maxplayers = reader.uint(4); state.raw.allowedbandwidth = reader.uint(4); } diff --git a/protocols/openttd.js b/protocols/openttd.js index 5fc2e11..0da2d0d 100644 --- a/protocols/openttd.js +++ b/protocols/openttd.js @@ -35,7 +35,7 @@ class OpenTtd extends Core { state.password = !!reader.uint(1); state.maxplayers = reader.uint(1); - state.players = reader.uint(1); + state.players.setNum(reader.uint(1)); state.raw.numspectators = reader.uint(1); state.map = reader.string(); state.raw.map_width = reader.uint(2); diff --git a/protocols/rfactor.js b/protocols/rfactor.js index 1716a12..d48a559 100644 --- a/protocols/rfactor.js +++ b/protocols/rfactor.js @@ -27,7 +27,7 @@ class Rfactor extends Core { state.raw.ping = reader.uint(2); state.raw.packedFlags = reader.uint(1); state.raw.rate = reader.uint(1); - state.players = reader.uint(1); + state.players.setNum(reader.uint(1)); state.maxplayers = reader.uint(1); state.raw.bots = reader.uint(1); state.raw.packedSpecial = reader.uint(1); diff --git a/protocols/samp.js b/protocols/samp.js index bc452aa..28ad970 100644 --- a/protocols/samp.js +++ b/protocols/samp.js @@ -69,7 +69,7 @@ class Samp extends Core { } } if (!gotPlayerData) { - state.players = state.raw.numplayers; + state.players.setNum(state.raw.numplayers); } } async sendPacket(type,allowTimeout) { diff --git a/protocols/savage2.js b/protocols/savage2.js index 8dae2eb..dfa7a5e 100644 --- a/protocols/savage2.js +++ b/protocols/savage2.js @@ -11,7 +11,7 @@ class Savage2 extends Core { reader.skip(12); state.name = this.stripColorCodes(reader.string()); - state.players = reader.uint(1); + state.players.setNum(reader.uint(1)); state.maxplayers = reader.uint(1); state.raw.time = reader.string(); state.map = reader.string(); diff --git a/protocols/starmade.js b/protocols/starmade.js index d9e4f10..86567d3 100644 --- a/protocols/starmade.js +++ b/protocols/starmade.js @@ -61,7 +61,7 @@ class Starmade extends Core { if(typeof data[2] === 'string') state.name = data[2]; if(typeof data[3] === 'string') state.raw.description = data[3]; if(typeof data[4] === 'number') state.raw.startTime = data[4]; - if(typeof data[5] === 'number') state.players = data[5]; + if(typeof data[5] === 'number') state.players.setNum(data[5]); if(typeof data[6] === 'number') state.maxplayers = data[6]; } }