mirror of
https://github.com/gamedig/node-gamedig.git
synced 2024-10-07 17:27:25 +02:00
01794f6339
* Add missing CRLF line ending * Add support for running using deno Prefix node imports with "node:" and gate a socket API that is not implemented in [deno](https://deno.land) so that the library can be used there. This should not break node and doesn't in my brief testing.
105 lines
3.0 KiB
JavaScript
105 lines
3.0 KiB
JavaScript
import GameResolver from './GameResolver.js'
|
|
import { getProtocol } from './ProtocolResolver.js'
|
|
import GlobalUdpSocket from './GlobalUdpSocket.js'
|
|
|
|
const defaultOptions = {
|
|
socketTimeout: 2000,
|
|
attemptTimeout: 10000,
|
|
maxAttempts: 1,
|
|
ipFamily: 0
|
|
}
|
|
|
|
export default class QueryRunner {
|
|
constructor (runnerOpts = {}) {
|
|
this.udpSocket = new GlobalUdpSocket({
|
|
port: runnerOpts.listenUdpPort
|
|
})
|
|
this.gameResolver = new GameResolver()
|
|
}
|
|
|
|
async run (userOptions) {
|
|
for (const key of Object.keys(userOptions)) {
|
|
const value = userOptions[key]
|
|
if (['port', 'ipFamily'].includes(key)) {
|
|
userOptions[key] = parseInt(value)
|
|
}
|
|
}
|
|
|
|
const {
|
|
port_query: gameQueryPort,
|
|
port_query_offset: gameQueryPortOffset,
|
|
...gameOptions
|
|
} = this.gameResolver.lookup(userOptions.type)
|
|
const attempts = []
|
|
|
|
const optionsCollection = {
|
|
...defaultOptions,
|
|
...gameOptions,
|
|
...userOptions
|
|
}
|
|
|
|
const addAttemptWithPort = port => {
|
|
attempts.push({
|
|
...optionsCollection,
|
|
port
|
|
})
|
|
}
|
|
|
|
if (userOptions.port) {
|
|
if (!userOptions.givenPortOnly) {
|
|
if (gameQueryPortOffset) { addAttemptWithPort(userOptions.port + gameQueryPortOffset) }
|
|
|
|
if (userOptions.port === gameOptions.port && gameQueryPort) { addAttemptWithPort(gameQueryPort) }
|
|
}
|
|
|
|
attempts.push(optionsCollection)
|
|
} else if (gameQueryPort) {
|
|
addAttemptWithPort(gameQueryPort)
|
|
} else if (gameOptions.port) {
|
|
addAttemptWithPort(gameOptions.port + (gameQueryPortOffset || 0))
|
|
} else {
|
|
// Hopefully the request doesn't need a port. If it does, it'll fail when making the request.
|
|
attempts.push(optionsCollection)
|
|
}
|
|
|
|
const numRetries = userOptions.maxAttempts || gameOptions.maxAttempts || defaultOptions.maxAttempts
|
|
|
|
let attemptNum = 0
|
|
const errors = []
|
|
for (const attempt of attempts) {
|
|
for (let retry = 0; retry < numRetries; retry++) {
|
|
attemptNum++
|
|
let result
|
|
try {
|
|
result = await this._attempt(attempt)
|
|
} catch (e) {
|
|
e.stack = 'Attempt #' + attemptNum + ' - Port=' + attempt.port + ' Retry=' + (retry) + ':\n' + e.stack
|
|
errors.push(e)
|
|
} finally {
|
|
// Deno doesn't support unref, so we must close the socket after every connection
|
|
// https://github.com/denoland/deno/issues/20138
|
|
if (typeof Deno !== "undefined") {
|
|
this.udpSocket?.socket?.close()
|
|
delete this.udpSocket
|
|
}
|
|
}
|
|
if (result) return result
|
|
}
|
|
}
|
|
|
|
const err = new Error('Failed all ' + errors.length + ' attempts')
|
|
for (const e of errors) {
|
|
err.stack += '\n' + e.stack
|
|
}
|
|
|
|
throw err
|
|
}
|
|
|
|
async _attempt (options) {
|
|
const core = getProtocol(options.protocol)
|
|
core.options = options
|
|
core.udpSocket = this.udpSocket
|
|
return await core.runOnceSafe()
|
|
}
|
|
}
|