gamedigz 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/GAMES_LIST.md +453 -0
  2. package/LICENSE +21 -0
  3. package/README.md +144 -0
  4. package/bin/gamedig.js +79 -0
  5. package/lib/DnsResolver.js +76 -0
  6. package/lib/GlobalUdpSocket.js +69 -0
  7. package/lib/HexUtil.js +20 -0
  8. package/lib/Logger.js +45 -0
  9. package/lib/Promises.js +18 -0
  10. package/lib/ProtocolResolver.js +7 -0
  11. package/lib/QueryRunner.js +95 -0
  12. package/lib/Results.js +32 -0
  13. package/lib/game-resolver.js +17 -0
  14. package/lib/gamedig.js +23 -0
  15. package/lib/games.js +2747 -0
  16. package/lib/index.js +5 -0
  17. package/lib/reader.js +172 -0
  18. package/package.json +74 -0
  19. package/protocols/armagetron.js +65 -0
  20. package/protocols/asa.js +12 -0
  21. package/protocols/ase.js +45 -0
  22. package/protocols/assettocorsa.js +40 -0
  23. package/protocols/battlefield.js +162 -0
  24. package/protocols/beammp.js +32 -0
  25. package/protocols/beammpmaster.js +17 -0
  26. package/protocols/buildandshoot.js +55 -0
  27. package/protocols/core.js +349 -0
  28. package/protocols/cs2d.js +65 -0
  29. package/protocols/dayz.js +196 -0
  30. package/protocols/discord.js +29 -0
  31. package/protocols/doom3.js +148 -0
  32. package/protocols/eco.js +20 -0
  33. package/protocols/eldewrito.js +21 -0
  34. package/protocols/epic.js +95 -0
  35. package/protocols/ffow.js +38 -0
  36. package/protocols/fivem.js +33 -0
  37. package/protocols/gamespy1.js +181 -0
  38. package/protocols/gamespy2.js +144 -0
  39. package/protocols/gamespy3.js +197 -0
  40. package/protocols/geneshift.js +46 -0
  41. package/protocols/goldsrc.js +8 -0
  42. package/protocols/hexen2.js +14 -0
  43. package/protocols/index.js +61 -0
  44. package/protocols/jc2mp.js +16 -0
  45. package/protocols/kspdmp.js +28 -0
  46. package/protocols/mafia2mp.js +41 -0
  47. package/protocols/mafia2online.js +9 -0
  48. package/protocols/minecraft.js +102 -0
  49. package/protocols/minecraftbedrock.js +72 -0
  50. package/protocols/minecraftvanilla.js +87 -0
  51. package/protocols/mumble.js +39 -0
  52. package/protocols/mumbleping.js +24 -0
  53. package/protocols/nadeo.js +86 -0
  54. package/protocols/openttd.js +127 -0
  55. package/protocols/quake1.js +9 -0
  56. package/protocols/quake2.js +88 -0
  57. package/protocols/quake3.js +24 -0
  58. package/protocols/rfactor.js +69 -0
  59. package/protocols/samp.js +102 -0
  60. package/protocols/savage2.js +25 -0
  61. package/protocols/starmade.js +67 -0
  62. package/protocols/starsiege.js +10 -0
  63. package/protocols/teamspeak2.js +71 -0
  64. package/protocols/teamspeak3.js +69 -0
  65. package/protocols/terraria.js +24 -0
  66. package/protocols/tribes1.js +153 -0
  67. package/protocols/tribes1master.js +80 -0
  68. package/protocols/unreal2.js +150 -0
  69. package/protocols/ut3.js +45 -0
  70. package/protocols/valve.js +455 -0
  71. package/protocols/vcmp.js +10 -0
  72. package/protocols/ventrilo.js +237 -0
  73. package/protocols/warsow.js +13 -0
package/lib/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import { GameDig } from './gamedig.js'
2
+ import { games } from './games.js'
3
+ import * as protocols from '../protocols/index.js'
4
+
5
+ export { GameDig, games, protocols }
package/lib/reader.js ADDED
@@ -0,0 +1,172 @@
1
+ import Iconv from 'iconv-lite'
2
+ import Long from 'long'
3
+ import { Buffer } from 'node:buffer'
4
+ import Varint from 'varint'
5
+
6
+ function readUInt64BE (buffer, offset) {
7
+ const high = buffer.readUInt32BE(offset)
8
+ const low = buffer.readUInt32BE(offset + 4)
9
+ return new Long(low, high, true)
10
+ }
11
+ function readUInt64LE (buffer, offset) {
12
+ const low = buffer.readUInt32LE(offset)
13
+ const high = buffer.readUInt32LE(offset + 4)
14
+ return new Long(low, high, true)
15
+ }
16
+
17
+ export default class Reader {
18
+ /**
19
+ * @param {Core} query
20
+ * @param {Buffer} buffer
21
+ **/
22
+ constructor (query, buffer) {
23
+ this.defaultEncoding = query.options.encoding || query.encoding
24
+ this.defaultDelimiter = query.delimiter
25
+ this.defaultByteOrder = query.byteorder
26
+ this.buffer = buffer
27
+ this.i = 0
28
+ }
29
+
30
+ setOffset (offset) {
31
+ this.i = offset
32
+ }
33
+
34
+ offset () {
35
+ return this.i
36
+ }
37
+
38
+ skip (i) {
39
+ this.i += i
40
+ }
41
+
42
+ pascalString (bytesForSize, adjustment = 0) {
43
+ const length = this.uint(bytesForSize) + adjustment
44
+ return this.string(length)
45
+ }
46
+
47
+ string (arg) {
48
+ let encoding = this.defaultEncoding
49
+ let length = null
50
+ let delimiter = this.defaultDelimiter
51
+
52
+ if (typeof arg === 'string') delimiter = arg
53
+ else if (typeof arg === 'number') length = arg
54
+ else if (typeof arg === 'object') {
55
+ if ('encoding' in arg) encoding = arg.encoding
56
+ if ('length' in arg) length = arg.length
57
+ if ('delimiter' in arg) delimiter = arg.delimiter
58
+ }
59
+
60
+ if (encoding === 'latin1') encoding = 'win1252'
61
+
62
+ const start = this.i
63
+ let end = start
64
+ if (length === null) {
65
+ // terminated by the delimiter
66
+ let delim = delimiter
67
+ if (typeof delim === 'string') delim = delim.charCodeAt(0)
68
+ while (true) {
69
+ if (end >= this.buffer.length) {
70
+ end = this.buffer.length
71
+ break
72
+ }
73
+ if (this.buffer.readUInt8(end) === delim) break
74
+ end++
75
+ }
76
+ this.i = end + 1
77
+ } else if (length <= 0) {
78
+ return ''
79
+ } else {
80
+ end = start + length
81
+ if (end >= this.buffer.length) {
82
+ end = this.buffer.length
83
+ }
84
+ this.i = end
85
+ }
86
+
87
+ const slice = this.buffer.slice(start, end)
88
+ const enc = encoding
89
+ if (enc === 'utf8' || enc === 'ucs2' || enc === 'binary') {
90
+ return slice.toString(enc)
91
+ } else {
92
+ return Iconv.decode(slice, enc)
93
+ }
94
+ }
95
+
96
+ int (bytes) {
97
+ let r = 0
98
+ if (this.remaining() >= bytes) {
99
+ if (this.defaultByteOrder === 'be') {
100
+ if (bytes === 1) r = this.buffer.readInt8(this.i)
101
+ else if (bytes === 2) r = this.buffer.readInt16BE(this.i)
102
+ else if (bytes === 4) r = this.buffer.readInt32BE(this.i)
103
+ } else {
104
+ if (bytes === 1) r = this.buffer.readInt8(this.i)
105
+ else if (bytes === 2) r = this.buffer.readInt16LE(this.i)
106
+ else if (bytes === 4) r = this.buffer.readInt32LE(this.i)
107
+ }
108
+ }
109
+ this.i += bytes
110
+ return r
111
+ }
112
+
113
+ /** @returns {number} */
114
+ uint (bytes) {
115
+ let r = 0
116
+ if (this.remaining() >= bytes) {
117
+ if (this.defaultByteOrder === 'be') {
118
+ if (bytes === 1) r = this.buffer.readUInt8(this.i)
119
+ else if (bytes === 2) r = this.buffer.readUInt16BE(this.i)
120
+ else if (bytes === 4) r = this.buffer.readUInt32BE(this.i)
121
+ else if (bytes === 8) r = readUInt64BE(this.buffer, this.i)
122
+ } else {
123
+ if (bytes === 1) r = this.buffer.readUInt8(this.i)
124
+ else if (bytes === 2) r = this.buffer.readUInt16LE(this.i)
125
+ else if (bytes === 4) r = this.buffer.readUInt32LE(this.i)
126
+ else if (bytes === 8) r = readUInt64LE(this.buffer, this.i)
127
+ }
128
+ }
129
+ this.i += bytes
130
+ return r
131
+ }
132
+
133
+ float () {
134
+ let r = 0
135
+ if (this.remaining() >= 4) {
136
+ if (this.defaultByteOrder === 'be') r = this.buffer.readFloatBE(this.i)
137
+ else r = this.buffer.readFloatLE(this.i)
138
+ }
139
+ this.i += 4
140
+ return r
141
+ }
142
+
143
+ varint () {
144
+ const out = Varint.decode(this.buffer, this.i)
145
+ this.i += Varint.decode.bytes
146
+ return out
147
+ }
148
+
149
+ /** @returns Buffer */
150
+ part (bytes) {
151
+ let r
152
+ if (this.remaining() >= bytes) {
153
+ r = this.buffer.slice(this.i, this.i + bytes)
154
+ } else {
155
+ r = Buffer.from([])
156
+ }
157
+ this.i += bytes
158
+ return r
159
+ }
160
+
161
+ remaining () {
162
+ return this.buffer.length - this.i
163
+ }
164
+
165
+ rest () {
166
+ return this.buffer.slice(this.i)
167
+ }
168
+
169
+ done () {
170
+ return this.i >= this.buffer.length
171
+ }
172
+ }
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "gamedigz",
3
+ "description": "Query for the status of any game server in Node.JS",
4
+ "scripts": {
5
+ "lint:check": "eslint .",
6
+ "lint:fix": "eslint --fix ."
7
+ },
8
+ "keywords": [
9
+ "srcds",
10
+ "query",
11
+ "game",
12
+ "utility",
13
+ "util",
14
+ "server",
15
+ "gameserver",
16
+ "game-server-query",
17
+ "game server query",
18
+ "server query",
19
+ "game server",
20
+ "gameserverquery",
21
+ "serverquery",
22
+ "terraria",
23
+ "counter strike",
24
+ "csgo",
25
+ "minecraft"
26
+ ],
27
+ "type": "module",
28
+ "main": "lib/index.js",
29
+ "author": "GameDig Contributors",
30
+ "version": "0.1.0",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/gamedig/node-gamedig.git"
34
+ },
35
+ "bugs": {
36
+ "url": "https://github.com/gamedig/node-gamedig/issues"
37
+ },
38
+ "license": "MIT",
39
+ "engines": {
40
+ "node": ">=16.20.0"
41
+ },
42
+ "bin": {
43
+ "gamedig": "bin/gamedig.js"
44
+ },
45
+ "files": [
46
+ "bin/gamedig.js",
47
+ "lib/",
48
+ "protocols/",
49
+ "games.txt",
50
+ "LICENSE",
51
+ "GAMES_LIST.md",
52
+ "README.md"
53
+ ],
54
+ "dependencies": {
55
+ "cheerio": "^1.0.0-rc.12",
56
+ "gbxremote": "^0.2.1",
57
+ "got": "^13.0.0",
58
+ "iconv-lite": "^0.6.3",
59
+ "long": "^5.2.3",
60
+ "minimist": "^1.2.8",
61
+ "punycode": "^2.3.0",
62
+ "seek-bzip": "^2.0.0",
63
+ "varint": "^6.0.0"
64
+ },
65
+ "devDependencies": {
66
+ "@types/cheerio": "^0.22.31",
67
+ "@types/node": "^16.18.58",
68
+ "eslint": "^8.49.0",
69
+ "eslint-config-standard": "^17.1.0",
70
+ "eslint-plugin-import": "^2.28.1",
71
+ "eslint-plugin-n": "15.7.0",
72
+ "eslint-plugin-promise": "^6.1.1"
73
+ }
74
+ }
@@ -0,0 +1,65 @@
1
+ import Core from './core.js'
2
+
3
+ export default class armagetron extends Core {
4
+ constructor () {
5
+ super()
6
+ this.encoding = 'latin1'
7
+ this.byteorder = 'be'
8
+ }
9
+
10
+ async run (state) {
11
+ const b = Buffer.from([0, 0x35, 0, 0, 0, 0, 0, 0x11])
12
+
13
+ const buffer = await this.udpSend(b, b => b)
14
+ const reader = this.reader(buffer)
15
+
16
+ reader.skip(6)
17
+
18
+ state.gamePort = this.readUInt(reader)
19
+ state.raw.hostname = this.readString(reader)
20
+ state.name = this.stripColorCodes(this.readString(reader))
21
+ state.numplayers = this.readUInt(reader)
22
+ state.raw.versionmin = this.readUInt(reader)
23
+ state.raw.versionmax = this.readUInt(reader)
24
+ state.raw.version = this.readString(reader)
25
+ state.maxplayers = this.readUInt(reader)
26
+
27
+ const players = this.readString(reader)
28
+ const list = players.split('\n')
29
+ for (const name of list) {
30
+ if (!name) continue
31
+ state.players.push({
32
+ name: this.stripColorCodes(name)
33
+ })
34
+ }
35
+
36
+ state.raw.options = this.stripColorCodes(this.readString(reader))
37
+ state.raw.uri = this.readString(reader)
38
+ state.raw.globalids = this.readString(reader)
39
+ }
40
+
41
+ readUInt (reader) {
42
+ const a = reader.uint(2)
43
+ const b = reader.uint(2)
44
+ return (b << 16) + a
45
+ }
46
+
47
+ readString (reader) {
48
+ const len = reader.uint(2)
49
+ if (!len) return ''
50
+
51
+ let out = ''
52
+ for (let i = 0; i < len; i += 2) {
53
+ const hi = reader.uint(1)
54
+ const lo = reader.uint(1)
55
+ if (i + 1 < len) out += String.fromCharCode(lo)
56
+ if (i + 2 < len) out += String.fromCharCode(hi)
57
+ }
58
+
59
+ return out
60
+ }
61
+
62
+ stripColorCodes (str) {
63
+ return str.replace(/0x[0-9a-f]{6}/g, '')
64
+ }
65
+ }
@@ -0,0 +1,12 @@
1
+ import Epic from './epic.js'
2
+
3
+ export default class asa extends Epic {
4
+ constructor () {
5
+ super()
6
+
7
+ // OAuth2 credentials extracted from ARK: Survival Ascended files.
8
+ this.clientId = 'xyza7891muomRmynIIHaJB9COBKkwj6n'
9
+ this.clientSecret = 'PP5UGxysEieNfSrEicaD1N2Bb3TdXuD7xHYcsdUHZ7s'
10
+ this.deploymentId = 'ad9a8feffb3b4b2ca315546f038c3ae2'
11
+ }
12
+ }
@@ -0,0 +1,45 @@
1
+ import Core from './core.js'
2
+
3
+ export default class ase extends Core {
4
+ async run (state) {
5
+ const buffer = await this.udpSend('s', (buffer) => {
6
+ const reader = this.reader(buffer)
7
+ const header = reader.string(4)
8
+ if (header === 'EYE1') return reader.rest()
9
+ })
10
+
11
+ const reader = this.reader(buffer)
12
+ state.raw.gamename = this.readString(reader)
13
+ state.gamePort = parseInt(this.readString(reader))
14
+ state.name = this.readString(reader)
15
+ state.raw.gametype = this.readString(reader)
16
+ state.map = this.readString(reader)
17
+ state.raw.version = this.readString(reader)
18
+ state.password = this.readString(reader) === '1'
19
+ state.numplayers = parseInt(this.readString(reader))
20
+ state.maxplayers = parseInt(this.readString(reader))
21
+
22
+ while (!reader.done()) {
23
+ const key = this.readString(reader)
24
+ if (!key) break
25
+ const value = this.readString(reader)
26
+ state.raw[key] = value
27
+ }
28
+
29
+ while (!reader.done()) {
30
+ const flags = reader.uint(1)
31
+ const player = {}
32
+ if (flags & 1) player.name = this.readString(reader)
33
+ if (flags & 2) player.team = this.readString(reader)
34
+ if (flags & 4) player.skin = this.readString(reader)
35
+ if (flags & 8) player.score = parseInt(this.readString(reader))
36
+ if (flags & 16) player.ping = parseInt(this.readString(reader))
37
+ if (flags & 32) player.time = parseInt(this.readString(reader))
38
+ state.players.push(player)
39
+ }
40
+ }
41
+
42
+ readString (reader) {
43
+ return reader.pascalString(1, -1)
44
+ }
45
+ }
@@ -0,0 +1,40 @@
1
+ import Core from './core.js'
2
+
3
+ export default class assettocorsa extends Core {
4
+ async run (state) {
5
+ const serverInfo = await this.request({
6
+ url: `http://${this.options.address}:${this.options.port}/INFO`,
7
+ responseType: 'json'
8
+ })
9
+ const carInfo = await this.request({
10
+ url: `http://${this.options.address}:${this.options.port}/JSON|${parseInt(Math.random() * 999999999999999, 10)}`,
11
+ responseType: 'json'
12
+ })
13
+
14
+ if (!serverInfo || !carInfo || !carInfo.Cars) {
15
+ throw new Error('Query not successful')
16
+ }
17
+
18
+ state.maxplayers = serverInfo.maxclients
19
+ state.name = serverInfo.name
20
+ state.map = serverInfo.track
21
+ state.password = serverInfo.pass
22
+ state.gamePort = serverInfo.port
23
+ state.raw.carInfo = carInfo.Cars
24
+ state.raw.serverInfo = serverInfo
25
+
26
+ for (const car of carInfo.Cars) {
27
+ if (car.IsConnected) {
28
+ state.players.push({
29
+ name: car.DriverName,
30
+ car: car.Model,
31
+ skin: car.Skin,
32
+ nation: car.DriverNation,
33
+ team: car.DriverTeam
34
+ })
35
+ }
36
+ }
37
+
38
+ state.numplayers = carInfo.Cars.length
39
+ }
40
+ }
@@ -0,0 +1,162 @@
1
+ import Core from './core.js'
2
+
3
+ export default class battlefield extends Core {
4
+ constructor () {
5
+ super()
6
+ this.encoding = 'latin1'
7
+ }
8
+
9
+ async run (state) {
10
+ await this.withTcp(async socket => {
11
+ {
12
+ const data = await this.query(socket, ['serverInfo'])
13
+ state.name = data.shift()
14
+ state.numplayers = parseInt(data.shift())
15
+ state.maxplayers = parseInt(data.shift())
16
+ state.raw.gametype = data.shift()
17
+ state.map = data.shift()
18
+ state.raw.roundsplayed = parseInt(data.shift())
19
+ state.raw.roundstotal = parseInt(data.shift())
20
+
21
+ const teamCount = data.shift()
22
+ state.raw.teams = []
23
+ for (let i = 0; i < teamCount; i++) {
24
+ const tickets = parseFloat(data.shift())
25
+ state.raw.teams.push({
26
+ tickets
27
+ })
28
+ }
29
+
30
+ state.raw.targetscore = parseInt(data.shift())
31
+ state.raw.status = data.shift()
32
+
33
+ // Seems like the fields end at random places beyond this point
34
+ // depending on the server version
35
+
36
+ if (data.length) state.raw.ranked = (data.shift() === 'true')
37
+ if (data.length) state.raw.punkbuster = (data.shift() === 'true')
38
+ if (data.length) state.password = (data.shift() === 'true')
39
+ if (data.length) state.raw.uptime = parseInt(data.shift())
40
+ if (data.length) state.raw.roundtime = parseInt(data.shift())
41
+
42
+ const isBadCompany2 = data[0] === 'BC2'
43
+ if (isBadCompany2) {
44
+ if (data.length) data.shift()
45
+ if (data.length) data.shift()
46
+ }
47
+ if (data.length) {
48
+ state.raw.ip = data.shift()
49
+ const split = state.raw.ip.split(':')
50
+ state.gameHost = split[0]
51
+ state.gamePort = split[1]
52
+ } else {
53
+ // best guess if the server doesn't tell us what the server port is
54
+ // these are just the default game ports for different default query ports
55
+ if (this.options.port === 48888) state.gamePort = 7673
56
+ if (this.options.port === 22000) state.gamePort = 25200
57
+ }
58
+ if (data.length) state.raw.punkbusterversion = data.shift()
59
+ if (data.length) state.raw.joinqueue = (data.shift() === 'true')
60
+ if (data.length) state.raw.region = data.shift()
61
+ if (data.length) state.raw.pingsite = data.shift()
62
+ if (data.length) state.raw.country = data.shift()
63
+ if (data.length) state.raw.quickmatch = (data.shift() === 'true')
64
+ }
65
+
66
+ {
67
+ const data = await this.query(socket, ['version'])
68
+ data.shift()
69
+ state.raw.version = data.shift()
70
+ }
71
+
72
+ {
73
+ const data = await this.query(socket, ['listPlayers', 'all'])
74
+ const fieldCount = parseInt(data.shift())
75
+ const fields = []
76
+ for (let i = 0; i < fieldCount; i++) {
77
+ fields.push(data.shift())
78
+ }
79
+ const numplayers = data.shift()
80
+ for (let i = 0; i < numplayers; i++) {
81
+ const player = {}
82
+ for (let key of fields) {
83
+ let value = data.shift()
84
+
85
+ if (key === 'teamId') key = 'team'
86
+ else if (key === 'squadId') key = 'squad'
87
+
88
+ if (
89
+ key === 'kills' ||
90
+ key === 'deaths' ||
91
+ key === 'score' ||
92
+ key === 'rank' ||
93
+ key === 'team' ||
94
+ key === 'squad' ||
95
+ key === 'ping' ||
96
+ key === 'type'
97
+ ) {
98
+ value = parseInt(value)
99
+ }
100
+
101
+ player[key] = value
102
+ }
103
+ state.players.push(player)
104
+ }
105
+ }
106
+ })
107
+ }
108
+
109
+ async query (socket, params) {
110
+ const outPacket = this.buildPacket(params)
111
+ return await this.tcpSend(socket, outPacket, (data) => {
112
+ const decoded = this.decodePacket(data)
113
+ if (decoded) {
114
+ this.logger.debug(decoded)
115
+ if (decoded.shift() !== 'OK') throw new Error('Missing OK')
116
+ return decoded
117
+ }
118
+ })
119
+ }
120
+
121
+ buildPacket (params) {
122
+ const paramBuffers = []
123
+ for (const param of params) {
124
+ paramBuffers.push(Buffer.from(param, 'utf8'))
125
+ }
126
+
127
+ let totalLength = 12
128
+ for (const paramBuffer of paramBuffers) {
129
+ totalLength += paramBuffer.length + 1 + 4
130
+ }
131
+
132
+ const b = Buffer.alloc(totalLength)
133
+ b.writeUInt32LE(0, 0)
134
+ b.writeUInt32LE(totalLength, 4)
135
+ b.writeUInt32LE(params.length, 8)
136
+ let offset = 12
137
+ for (const paramBuffer of paramBuffers) {
138
+ b.writeUInt32LE(paramBuffer.length, offset); offset += 4
139
+ paramBuffer.copy(b, offset); offset += paramBuffer.length
140
+ b.writeUInt8(0, offset); offset += 1
141
+ }
142
+
143
+ return b
144
+ }
145
+
146
+ decodePacket (buffer) {
147
+ if (buffer.length < 8) return false
148
+ const reader = this.reader(buffer)
149
+ reader.uint(4) // header
150
+ const totalLength = reader.uint(4)
151
+ if (buffer.length < totalLength) return false
152
+ this.logger.debug('Expected ' + totalLength + ' bytes, have ' + buffer.length)
153
+
154
+ const paramCount = reader.uint(4)
155
+ const params = []
156
+ for (let i = 0; i < paramCount; i++) {
157
+ params.push(reader.pascalString(4))
158
+ reader.uint(1) // strNull
159
+ }
160
+ return params
161
+ }
162
+ }
@@ -0,0 +1,32 @@
1
+ import Core from './core.js'
2
+ import beammpmaster from './beammpmaster.js'
3
+
4
+ export default class beammp extends Core {
5
+ async run (state) {
6
+ const master = new beammpmaster()
7
+ master.options = this.options
8
+ const masterState = await master.runOnceSafe()
9
+ const servers = masterState.raw.servers
10
+ const server = servers.find(s => s.ip === this.options.host)
11
+
12
+ if (!server) {
13
+ throw new Error('Server not found in the master list')
14
+ }
15
+
16
+ state.name = server.sname.replace(/\^./g, '')
17
+ state.map = server.map
18
+ state.password = server.password
19
+ state.numplayers = parseInt(server.players)
20
+ state.maxplayers = parseInt(server.players)
21
+
22
+ const players = server.playerslist.split(';')
23
+ if (players[players.length - 1] === '') {
24
+ players.pop()
25
+ }
26
+ players.forEach(player => {
27
+ state.players.push({ name: player })
28
+ })
29
+
30
+ state.raw = server
31
+ }
32
+ }
@@ -0,0 +1,17 @@
1
+ import Core from './core.js'
2
+
3
+ export default class beammpmaster extends Core {
4
+ constructor () {
5
+ super()
6
+
7
+ // Don't use the tcp ping probing
8
+ this.usedTcp = true
9
+ }
10
+
11
+ async run (state) {
12
+ state.raw.servers = await this.request({
13
+ url: 'https://backend.beammp.com/servers-info',
14
+ responseType: 'json'
15
+ })
16
+ }
17
+ }
@@ -0,0 +1,55 @@
1
+ import Core from './core.js'
2
+ import * as cheerio from 'cheerio'
3
+
4
+ export default class buildandshoot extends Core {
5
+ async run (state) {
6
+ const body = await this.request({
7
+ url: 'http://' + this.options.address + ':' + this.options.port + '/'
8
+ })
9
+
10
+ let m
11
+
12
+ m = body.match(/status server for (.*?)\.?[\r\n]/)
13
+ if (m) state.name = m[1]
14
+
15
+ m = body.match(/Current uptime: (\d+)/)
16
+ if (m) state.raw.uptime = m[1]
17
+
18
+ m = body.match(/currently running (.*?) by /)
19
+ if (m) state.map = m[1]
20
+
21
+ m = body.match(/Current players: (\d+)\/(\d+)/)
22
+ if (m) {
23
+ state.numplayers = parseInt(m[1])
24
+ state.maxplayers = m[2]
25
+ }
26
+
27
+ m = body.match(/aos:\/\/[0-9]+:[0-9]+/)
28
+ if (m) {
29
+ state.connect = m[0]
30
+ }
31
+
32
+ const $ = cheerio.load(body)
33
+ $('#playerlist tbody tr').each((i, tr) => {
34
+ if (!$(tr).find('td').first().attr('colspan')) {
35
+ state.players.push({
36
+ name: $(tr).find('td').eq(2).text(),
37
+ ping: $(tr).find('td').eq(3).text().trim(),
38
+ team: $(tr).find('td').eq(4).text().toLowerCase(),
39
+ score: parseInt($(tr).find('td').eq(5).text())
40
+ })
41
+ }
42
+ })
43
+ /*
44
+ var m = this.options.address.match(/(\d+)\.(\d+)\.(\d+)\.(\d+)/);
45
+ if(m) {
46
+ var o1 = parseInt(m[1]);
47
+ var o2 = parseInt(m[2]);
48
+ var o3 = parseInt(m[3]);
49
+ var o4 = parseInt(m[4]);
50
+ var addr = o1+(o2<<8)+(o3<<16)+(o4<<24);
51
+ state.raw.url = 'aos://'+addr;
52
+ }
53
+ */
54
+ }
55
+ }