feat: port caching (#478)

* feat: port caching

* feat: dont store in cache if disabled
This commit is contained in:
CosminPerRam 2024-01-17 23:23:20 +02:00 committed by GitHub
parent 1f0563f7d2
commit b48a4398cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 24 additions and 7 deletions

View file

@ -31,10 +31,12 @@
* Added code examples. * Added code examples.
* New stable field: `queryPort` - this number indicates what was the port that the query was done on, 0 indicates none if not applicable. * New stable field: `queryPort` - this number indicates what was the port that the query was done on, 0 indicates none if not applicable.
* Fixed `numplayers` not having a default value. * Fixed `numplayers` not having a default value.
* New option: `stripColors` (defaults to `true`) for protocols that strips colors: unreal2, savage2, quake3, nadeo, gamespy2, doom3, armagetron. * New options:
* New option: `requestRulesRequired` (defaults to `false`) Valve games only. `requestRules` is always required to have a response or the query will timeout. * `portCache` (defaults to `true`) after you queried a server, the second time you query that exact server (identified by specified ip and port), first add an attempt to query with the last successful port.
* New option: `requestPlayersRequired` (defaults to `false`) Valve games only. Querying players is always required to have a response or the query will timeout. Some [games](GAMES_LIST.md) may not provide a players response. * `stripColors` (defaults to `true`) for protocols that strips colors: unreal2, savage2, quake3, nadeo, gamespy2, doom3, armagetron.
* New option: `address` (defaults to `undefined`) Override the IP address of the server skipping DNS resolution. When set, host will not be resolved, instead address will be connected to. However, some protocols still use host for other reasons e.g. as part of the query. * `requestRulesRequired` (defaults to `false`) Valve games only. `requestRules` is always required to have a response or the query will timeout.
* `requestPlayersRequired` (defaults to `false`) Valve games only. Querying players is always required to have a response or the query will timeout. Some [games](GAMES_LIST.md) may not provide a players response.
* `address` (defaults to `undefined`) Override the IP address of the server skipping DNS resolution. When set, host will not be resolved, instead address will be connected to. However, some protocols still use host for other reasons e.g. as part of the query.
#### Games #### Games
* Removed the players::setNum method, the library will no longer add empty players as * Removed the players::setNum method, the library will no longer add empty players as

View file

@ -51,6 +51,7 @@ Confused on how this works, or you want to see more? Checkout the [examples](/ex
| **requestRulesRequired** | boolean | false | Valve games only. `requestRules` is always required to have a response or the query will timeout. | | **requestRulesRequired** | boolean | false | Valve games only. `requestRules` is always required to have a response or the query will timeout. |
| **requestPlayersRequired** | boolean | false | Valve games only. Querying players is always required to have a response or the query will timeout. Some [games](GAMES_LIST.md) may not provide a players response. | | **requestPlayersRequired** | boolean | false | Valve games only. Querying players is always required to have a response or the query will timeout. Some [games](GAMES_LIST.md) may not provide a players response. |
| **stripColors** | boolean | true | Enables stripping colors for protocols: unreal2, savage2, quake3, nadeo, gamespy2, doom3, armagetron. | | **stripColors** | boolean | true | Enables stripping colors for protocols: unreal2, savage2, quake3, nadeo, gamespy2, doom3, armagetron. |
| **portCache** | boolean | true | After you queried a server, the second time you query that exact server (identified by specified ip and port), first add an attempt to query with the last successful port. |
## Query Response ## Query Response

View file

@ -6,10 +6,11 @@ import Minimist from 'minimist'
import { GameDig } from './../lib/index.js' import { GameDig } from './../lib/index.js'
const argv = Minimist(process.argv.slice(2), { const argv = Minimist(process.argv.slice(2), {
boolean: ['pretty', 'debug', 'givenPortOnly', 'requestRules', 'requestRulesRequired', 'requestPlayersRequired', 'stripColors'], boolean: ['pretty', 'debug', 'givenPortOnly', 'requestRules', 'requestRulesRequired', 'requestPlayersRequired', 'stripColors', 'portCache'],
string: ['guildId', 'listenUdpPort', 'ipFamily'], string: ['guildId', 'listenUdpPort', 'ipFamily'],
default: { default: {
stripColors: true stripColors: true,
portCache: true
} }
}) })

View file

@ -7,6 +7,7 @@ const defaultOptions = {
attemptTimeout: 10000, attemptTimeout: 10000,
maxAttempts: 1, maxAttempts: 1,
stripColors: true, stripColors: true,
portCache: true,
ipFamily: 0 ipFamily: 0
} }
@ -15,6 +16,7 @@ export default class QueryRunner {
this.udpSocket = new GlobalUdpSocket({ this.udpSocket = new GlobalUdpSocket({
port: runnerOpts.listenUdpPort port: runnerOpts.listenUdpPort
}) })
this.portCache = {}
} }
async run (userOptions) { async run (userOptions) {
@ -50,11 +52,18 @@ export default class QueryRunner {
gameQueryPortOffset ? portOffsetArray = [gameQueryPortOffset] : portOffsetArray = [0] gameQueryPortOffset ? portOffsetArray = [gameQueryPortOffset] : portOffsetArray = [0]
} }
const cachedPort = this.portCache[`${userOptions.address}:${userOptions.port}`]
if (cachedPort && optionsCollection.portCache) {
addAttemptWithPort(cachedPort)
}
if (userOptions.port) { if (userOptions.port) {
if (!userOptions.givenPortOnly) { if (!userOptions.givenPortOnly) {
portOffsetArray.forEach((portOffset) => { addAttemptWithPort(userOptions.port + portOffset) }) portOffsetArray.forEach((portOffset) => { addAttemptWithPort(userOptions.port + portOffset) })
if (userOptions.port === gameOptions.port && gameQueryPort) { addAttemptWithPort(gameQueryPort) } if (userOptions.port === gameOptions.port && gameQueryPort) { addAttemptWithPort(gameQueryPort) }
} }
attempts.push(optionsCollection) attempts.push(optionsCollection)
} else if (gameQueryPort) { } else if (gameQueryPort) {
addAttemptWithPort(gameQueryPort) addAttemptWithPort(gameQueryPort)
@ -74,7 +83,11 @@ export default class QueryRunner {
attemptNum++ attemptNum++
try { try {
return await this._attempt(attempt) const response = await this._attempt(attempt)
if (attempt.portCache) {
this.portCache[`${userOptions.address}:${userOptions.port}`] = attempt.port
}
return response
} catch (e) { } catch (e) {
e.stack = 'Attempt #' + attemptNum + ' - Port=' + attempt.port + ' Retry=' + (retry) + ':\n' + e.stack e.stack = 'Attempt #' + attemptNum + ' - Port=' + attempt.port + ' Retry=' + (retry) + ':\n' + e.stack
errors.push(e) errors.push(e)