mirror of
https://github.com/tribufu/node-gamedig
synced 2026-05-06 15:17:36 +00:00
chore: Convert all files to LF endings (#400)
* Convert to LF? * Modify gitattributes * Force LF * Git --renormalize * Update .gitattributes to enforce eol=lf * Redo CRLF -> LF on remaining files
This commit is contained in:
parent
a8bc7521f6
commit
cee42e7a88
65 changed files with 5697 additions and 5697 deletions
|
|
@ -1,74 +1,74 @@
|
|||
import dns from 'node:dns'
|
||||
import punycode from 'punycode/punycode.js'
|
||||
|
||||
export default class DnsResolver {
|
||||
/**
|
||||
* @param {Logger} logger
|
||||
*/
|
||||
constructor (logger) {
|
||||
this.logger = logger
|
||||
}
|
||||
|
||||
isIp (host) {
|
||||
return !!host.match(/\d+\.\d+\.\d+\.\d+/)
|
||||
}
|
||||
|
||||
/**
|
||||
* Response port will only be present if srv record was involved.
|
||||
* @param {string} host
|
||||
* @param {number} ipFamily
|
||||
* @param {string=} srvRecordPrefix
|
||||
* @returns {Promise<{address:string, port:number=}>}
|
||||
*/
|
||||
async resolve (host, ipFamily, srvRecordPrefix) {
|
||||
this.logger.debug('DNS Lookup: ' + host)
|
||||
|
||||
if (this.isIp(host)) {
|
||||
this.logger.debug('Raw IP Address: ' + host)
|
||||
return { address: host }
|
||||
}
|
||||
|
||||
const asciiForm = punycode.toASCII(host)
|
||||
if (asciiForm !== host) {
|
||||
this.logger.debug('Encoded punycode: ' + host + ' -> ' + asciiForm)
|
||||
host = asciiForm
|
||||
}
|
||||
|
||||
if (srvRecordPrefix) {
|
||||
this.logger.debug('SRV Resolve: ' + srvRecordPrefix + '.' + host)
|
||||
let records
|
||||
try {
|
||||
records = await dns.promises.resolve(srvRecordPrefix + '.' + host, 'SRV')
|
||||
if (records.length >= 1) {
|
||||
this.logger.debug('Found SRV Records: ', records)
|
||||
const record = records[0]
|
||||
const srvPort = record.port
|
||||
const srvHost = record.name
|
||||
if (srvHost === host) {
|
||||
throw new Error('Loop in DNS SRV records')
|
||||
}
|
||||
return {
|
||||
port: srvPort,
|
||||
...await this.resolve(srvHost, ipFamily, srvRecordPrefix)
|
||||
}
|
||||
}
|
||||
this.logger.debug('No SRV Record')
|
||||
} catch (e) {
|
||||
this.logger.debug(e)
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.debug('Standard Resolve: ' + host)
|
||||
const dnsResult = await dns.promises.lookup(host, ipFamily)
|
||||
// For some reason, this sometimes returns a string address rather than an object.
|
||||
// I haven't been able to reproduce, but it's been reported on the issue tracker.
|
||||
let address
|
||||
if (typeof dnsResult === 'string') {
|
||||
address = dnsResult
|
||||
} else {
|
||||
address = dnsResult.address
|
||||
}
|
||||
this.logger.debug('Found address: ' + address)
|
||||
return { address }
|
||||
}
|
||||
}
|
||||
import dns from 'node:dns'
|
||||
import punycode from 'punycode/punycode.js'
|
||||
|
||||
export default class DnsResolver {
|
||||
/**
|
||||
* @param {Logger} logger
|
||||
*/
|
||||
constructor (logger) {
|
||||
this.logger = logger
|
||||
}
|
||||
|
||||
isIp (host) {
|
||||
return !!host.match(/\d+\.\d+\.\d+\.\d+/)
|
||||
}
|
||||
|
||||
/**
|
||||
* Response port will only be present if srv record was involved.
|
||||
* @param {string} host
|
||||
* @param {number} ipFamily
|
||||
* @param {string=} srvRecordPrefix
|
||||
* @returns {Promise<{address:string, port:number=}>}
|
||||
*/
|
||||
async resolve (host, ipFamily, srvRecordPrefix) {
|
||||
this.logger.debug('DNS Lookup: ' + host)
|
||||
|
||||
if (this.isIp(host)) {
|
||||
this.logger.debug('Raw IP Address: ' + host)
|
||||
return { address: host }
|
||||
}
|
||||
|
||||
const asciiForm = punycode.toASCII(host)
|
||||
if (asciiForm !== host) {
|
||||
this.logger.debug('Encoded punycode: ' + host + ' -> ' + asciiForm)
|
||||
host = asciiForm
|
||||
}
|
||||
|
||||
if (srvRecordPrefix) {
|
||||
this.logger.debug('SRV Resolve: ' + srvRecordPrefix + '.' + host)
|
||||
let records
|
||||
try {
|
||||
records = await dns.promises.resolve(srvRecordPrefix + '.' + host, 'SRV')
|
||||
if (records.length >= 1) {
|
||||
this.logger.debug('Found SRV Records: ', records)
|
||||
const record = records[0]
|
||||
const srvPort = record.port
|
||||
const srvHost = record.name
|
||||
if (srvHost === host) {
|
||||
throw new Error('Loop in DNS SRV records')
|
||||
}
|
||||
return {
|
||||
port: srvPort,
|
||||
...await this.resolve(srvHost, ipFamily, srvRecordPrefix)
|
||||
}
|
||||
}
|
||||
this.logger.debug('No SRV Record')
|
||||
} catch (e) {
|
||||
this.logger.debug(e)
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.debug('Standard Resolve: ' + host)
|
||||
const dnsResult = await dns.promises.lookup(host, ipFamily)
|
||||
// For some reason, this sometimes returns a string address rather than an object.
|
||||
// I haven't been able to reproduce, but it's been reported on the issue tracker.
|
||||
let address
|
||||
if (typeof dnsResult === 'string') {
|
||||
address = dnsResult
|
||||
} else {
|
||||
address = dnsResult.address
|
||||
}
|
||||
this.logger.debug('Found address: ' + address)
|
||||
return { address }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,114 +1,114 @@
|
|||
import * as path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import * as fs from 'node:fs'
|
||||
|
||||
export default class GameResolver {
|
||||
constructor () {
|
||||
const loaded = this._readGames()
|
||||
this.gamesByKey = loaded.gamesByKey
|
||||
this.games = loaded.games
|
||||
}
|
||||
|
||||
lookup (type) {
|
||||
if (!type) { throw Error('No game specified') }
|
||||
|
||||
if (type.startsWith('protocol-')) {
|
||||
return {
|
||||
protocol: type.substring(9)
|
||||
}
|
||||
}
|
||||
|
||||
const game = this.gamesByKey.get(type)
|
||||
|
||||
if (!game) { throw Error('Invalid game: ' + type) }
|
||||
|
||||
return game.options
|
||||
}
|
||||
|
||||
printReadme () {
|
||||
let out = ''
|
||||
out += '| GameDig Type ID | Name | See Also\n'
|
||||
out += '|---|---|---\n'
|
||||
|
||||
const sorted = this.games
|
||||
.filter(game => game.pretty)
|
||||
.sort((a, b) => {
|
||||
return a.pretty.localeCompare(b.pretty)
|
||||
})
|
||||
for (const game of sorted) {
|
||||
const keysOut = game.keys.map(key => '`' + key + '`').join('<br>')
|
||||
out += '| ' + keysOut.padEnd(10, ' ') + ' ' +
|
||||
'| ' + game.pretty
|
||||
const 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
|
||||
}
|
||||
|
||||
_readGames () {
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
const gamesFile = path.normalize(__dirname + '/../games.txt')
|
||||
const lines = fs.readFileSync(gamesFile, 'utf8').split('\n')
|
||||
|
||||
const gamesByKey = new Map()
|
||||
const games = []
|
||||
|
||||
for (let line of lines) {
|
||||
// strip comments
|
||||
const comment = line.indexOf('#')
|
||||
if (comment !== -1) line = line.substring(0, comment)
|
||||
line = line.trim()
|
||||
if (!line) continue
|
||||
|
||||
const split = line.split('|')
|
||||
const keys = split[0].trim().split(',')
|
||||
const name = split[1].trim()
|
||||
const options = this._parseList(split[3])
|
||||
options.protocol = split[2].trim()
|
||||
const extra = this._parseList(split[4])
|
||||
|
||||
const game = {
|
||||
keys,
|
||||
pretty: name,
|
||||
options,
|
||||
extra
|
||||
}
|
||||
|
||||
for (const key of keys) {
|
||||
gamesByKey.set(key, game)
|
||||
}
|
||||
|
||||
games.push(game)
|
||||
}
|
||||
return { gamesByKey, games }
|
||||
}
|
||||
|
||||
_parseList (str) {
|
||||
if (!str) { return {} }
|
||||
|
||||
const out = {}
|
||||
for (const one of str.split(',')) {
|
||||
const equals = one.indexOf('=')
|
||||
const key = equals === -1 ? one : one.substring(0, equals)
|
||||
|
||||
/** @type {string|number|boolean} */
|
||||
let value = equals === -1 ? '' : one.substring(equals + 1)
|
||||
|
||||
if (value === 'true' || value === '') { value = true } else if (value === 'false') { value = false } else if (!isNaN(parseInt(value))) { value = parseInt(value) }
|
||||
|
||||
out[key] = value
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
}
|
||||
import * as path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import * as fs from 'node:fs'
|
||||
|
||||
export default class GameResolver {
|
||||
constructor () {
|
||||
const loaded = this._readGames()
|
||||
this.gamesByKey = loaded.gamesByKey
|
||||
this.games = loaded.games
|
||||
}
|
||||
|
||||
lookup (type) {
|
||||
if (!type) { throw Error('No game specified') }
|
||||
|
||||
if (type.startsWith('protocol-')) {
|
||||
return {
|
||||
protocol: type.substring(9)
|
||||
}
|
||||
}
|
||||
|
||||
const game = this.gamesByKey.get(type)
|
||||
|
||||
if (!game) { throw Error('Invalid game: ' + type) }
|
||||
|
||||
return game.options
|
||||
}
|
||||
|
||||
printReadme () {
|
||||
let out = ''
|
||||
out += '| GameDig Type ID | Name | See Also\n'
|
||||
out += '|---|---|---\n'
|
||||
|
||||
const sorted = this.games
|
||||
.filter(game => game.pretty)
|
||||
.sort((a, b) => {
|
||||
return a.pretty.localeCompare(b.pretty)
|
||||
})
|
||||
for (const game of sorted) {
|
||||
const keysOut = game.keys.map(key => '`' + key + '`').join('<br>')
|
||||
out += '| ' + keysOut.padEnd(10, ' ') + ' ' +
|
||||
'| ' + game.pretty
|
||||
const 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
|
||||
}
|
||||
|
||||
_readGames () {
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
const gamesFile = path.normalize(__dirname + '/../games.txt')
|
||||
const lines = fs.readFileSync(gamesFile, 'utf8').split('\n')
|
||||
|
||||
const gamesByKey = new Map()
|
||||
const games = []
|
||||
|
||||
for (let line of lines) {
|
||||
// strip comments
|
||||
const comment = line.indexOf('#')
|
||||
if (comment !== -1) line = line.substring(0, comment)
|
||||
line = line.trim()
|
||||
if (!line) continue
|
||||
|
||||
const split = line.split('|')
|
||||
const keys = split[0].trim().split(',')
|
||||
const name = split[1].trim()
|
||||
const options = this._parseList(split[3])
|
||||
options.protocol = split[2].trim()
|
||||
const extra = this._parseList(split[4])
|
||||
|
||||
const game = {
|
||||
keys,
|
||||
pretty: name,
|
||||
options,
|
||||
extra
|
||||
}
|
||||
|
||||
for (const key of keys) {
|
||||
gamesByKey.set(key, game)
|
||||
}
|
||||
|
||||
games.push(game)
|
||||
}
|
||||
return { gamesByKey, games }
|
||||
}
|
||||
|
||||
_parseList (str) {
|
||||
if (!str) { return {} }
|
||||
|
||||
const out = {}
|
||||
for (const one of str.split(',')) {
|
||||
const equals = one.indexOf('=')
|
||||
const key = equals === -1 ? one : one.substring(0, equals)
|
||||
|
||||
/** @type {string|number|boolean} */
|
||||
let value = equals === -1 ? '' : one.substring(equals + 1)
|
||||
|
||||
if (value === 'true' || value === '') { value = true } else if (value === 'false') { value = false } else if (!isNaN(parseInt(value))) { value = parseInt(value) }
|
||||
|
||||
out[key] = value
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,72 +1,72 @@
|
|||
import { createSocket } from 'node:dgram'
|
||||
import { debugDump } from './HexUtil.js'
|
||||
import { promisify } from 'node:util'
|
||||
import Logger from './Logger.js'
|
||||
|
||||
export default class GlobalUdpSocket {
|
||||
constructor ({ port }) {
|
||||
this.socket = null
|
||||
this.callbacks = new Set()
|
||||
this.debuggingCallbacks = new Set()
|
||||
this.logger = new Logger()
|
||||
this.port = port
|
||||
}
|
||||
|
||||
async _getSocket () {
|
||||
if (!this.socket) {
|
||||
const udpSocket = createSocket({
|
||||
type: 'udp4',
|
||||
reuseAddr: true
|
||||
})
|
||||
// https://github.com/denoland/deno/issues/20138
|
||||
if (typeof Deno === "undefined") {
|
||||
udpSocket.unref();
|
||||
}
|
||||
udpSocket.on('message', (buffer, rinfo) => {
|
||||
const fromAddress = rinfo.address
|
||||
const fromPort = rinfo.port
|
||||
this.logger.debug(log => {
|
||||
log(fromAddress + ':' + fromPort + ' <--UDP(' + this.port + ')')
|
||||
log(debugDump(buffer))
|
||||
})
|
||||
for (const callback of this.callbacks) {
|
||||
callback(fromAddress, fromPort, buffer)
|
||||
}
|
||||
})
|
||||
udpSocket.on('error', e => {
|
||||
this.logger.debug('UDP ERROR:', e)
|
||||
})
|
||||
await promisify(udpSocket.bind).bind(udpSocket)(this.port)
|
||||
this.port = udpSocket.address().port
|
||||
this.socket = udpSocket
|
||||
}
|
||||
return this.socket
|
||||
}
|
||||
|
||||
async send (buffer, address, port, debug) {
|
||||
const socket = await this._getSocket()
|
||||
|
||||
if (debug) {
|
||||
this.logger._print(log => {
|
||||
log(address + ':' + port + ' UDP(' + this.port + ')-->')
|
||||
log(debugDump(buffer))
|
||||
})
|
||||
}
|
||||
|
||||
await promisify(socket.send).bind(socket)(buffer, 0, buffer.length, port, address)
|
||||
}
|
||||
|
||||
addCallback (callback, debug) {
|
||||
this.callbacks.add(callback)
|
||||
if (debug) {
|
||||
this.debuggingCallbacks.add(callback)
|
||||
this.logger.debugEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
removeCallback (callback) {
|
||||
this.callbacks.delete(callback)
|
||||
this.debuggingCallbacks.delete(callback)
|
||||
this.logger.debugEnabled = this.debuggingCallbacks.size > 0
|
||||
}
|
||||
}
|
||||
import { createSocket } from 'node:dgram'
|
||||
import { debugDump } from './HexUtil.js'
|
||||
import { promisify } from 'node:util'
|
||||
import Logger from './Logger.js'
|
||||
|
||||
export default class GlobalUdpSocket {
|
||||
constructor ({ port }) {
|
||||
this.socket = null
|
||||
this.callbacks = new Set()
|
||||
this.debuggingCallbacks = new Set()
|
||||
this.logger = new Logger()
|
||||
this.port = port
|
||||
}
|
||||
|
||||
async _getSocket () {
|
||||
if (!this.socket) {
|
||||
const udpSocket = createSocket({
|
||||
type: 'udp4',
|
||||
reuseAddr: true
|
||||
})
|
||||
// https://github.com/denoland/deno/issues/20138
|
||||
if (typeof Deno === "undefined") {
|
||||
udpSocket.unref();
|
||||
}
|
||||
udpSocket.on('message', (buffer, rinfo) => {
|
||||
const fromAddress = rinfo.address
|
||||
const fromPort = rinfo.port
|
||||
this.logger.debug(log => {
|
||||
log(fromAddress + ':' + fromPort + ' <--UDP(' + this.port + ')')
|
||||
log(debugDump(buffer))
|
||||
})
|
||||
for (const callback of this.callbacks) {
|
||||
callback(fromAddress, fromPort, buffer)
|
||||
}
|
||||
})
|
||||
udpSocket.on('error', e => {
|
||||
this.logger.debug('UDP ERROR:', e)
|
||||
})
|
||||
await promisify(udpSocket.bind).bind(udpSocket)(this.port)
|
||||
this.port = udpSocket.address().port
|
||||
this.socket = udpSocket
|
||||
}
|
||||
return this.socket
|
||||
}
|
||||
|
||||
async send (buffer, address, port, debug) {
|
||||
const socket = await this._getSocket()
|
||||
|
||||
if (debug) {
|
||||
this.logger._print(log => {
|
||||
log(address + ':' + port + ' UDP(' + this.port + ')-->')
|
||||
log(debugDump(buffer))
|
||||
})
|
||||
}
|
||||
|
||||
await promisify(socket.send).bind(socket)(buffer, 0, buffer.length, port, address)
|
||||
}
|
||||
|
||||
addCallback (callback, debug) {
|
||||
this.callbacks.add(callback)
|
||||
if (debug) {
|
||||
this.debuggingCallbacks.add(callback)
|
||||
this.logger.debugEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
removeCallback (callback) {
|
||||
this.callbacks.delete(callback)
|
||||
this.debuggingCallbacks.delete(callback)
|
||||
this.logger.debugEnabled = this.debuggingCallbacks.size > 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
/** @param {Buffer} buffer */
|
||||
export const debugDump = (buffer) => {
|
||||
let hexLine = ''
|
||||
let chrLine = ''
|
||||
let out = ''
|
||||
out += 'Buffer length: ' + buffer.length + ' bytes\n'
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
const sliced = buffer.slice(i, i + 1)
|
||||
hexLine += sliced.toString('hex') + ' '
|
||||
let chr = sliced.toString()
|
||||
if (chr < ' ' || chr > '~') chr = ' '
|
||||
chrLine += chr + ' '
|
||||
if (hexLine.length > 60 || i === buffer.length - 1) {
|
||||
out += hexLine + '\n'
|
||||
out += chrLine + '\n'
|
||||
hexLine = chrLine = ''
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
/** @param {Buffer} buffer */
|
||||
export const debugDump = (buffer) => {
|
||||
let hexLine = ''
|
||||
let chrLine = ''
|
||||
let out = ''
|
||||
out += 'Buffer length: ' + buffer.length + ' bytes\n'
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
const sliced = buffer.slice(i, i + 1)
|
||||
hexLine += sliced.toString('hex') + ' '
|
||||
let chr = sliced.toString()
|
||||
if (chr < ' ' || chr > '~') chr = ' '
|
||||
chrLine += chr + ' '
|
||||
if (hexLine.length > 60 || i === buffer.length - 1) {
|
||||
out += hexLine + '\n'
|
||||
out += chrLine + '\n'
|
||||
hexLine = chrLine = ''
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,45 @@
|
|||
import { debugDump } from './HexUtil.js'
|
||||
import { Buffer} from 'node:buffer'
|
||||
|
||||
export default class Logger {
|
||||
constructor () {
|
||||
this.debugEnabled = false
|
||||
this.prefix = ''
|
||||
}
|
||||
|
||||
debug (...args) {
|
||||
if (!this.debugEnabled) return
|
||||
this._print(...args)
|
||||
}
|
||||
|
||||
_print (...args) {
|
||||
try {
|
||||
const strings = this._convertArgsToStrings(...args)
|
||||
if (strings.length) {
|
||||
if (this.prefix) {
|
||||
strings.unshift(this.prefix)
|
||||
}
|
||||
console.log(...strings)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Error while logging: ' + e)
|
||||
}
|
||||
}
|
||||
|
||||
_convertArgsToStrings (...args) {
|
||||
const out = []
|
||||
for (const arg of args) {
|
||||
if (arg instanceof Error) {
|
||||
out.push(arg.stack)
|
||||
} else if (arg instanceof Buffer) {
|
||||
out.push(debugDump(arg))
|
||||
} else if (typeof arg === 'function') {
|
||||
const result = arg.call(undefined, (...args) => this._print(...args))
|
||||
if (result !== undefined) out.push(...this._convertArgsToStrings(result))
|
||||
} else {
|
||||
out.push(arg)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
import { debugDump } from './HexUtil.js'
|
||||
import { Buffer} from 'node:buffer'
|
||||
|
||||
export default class Logger {
|
||||
constructor () {
|
||||
this.debugEnabled = false
|
||||
this.prefix = ''
|
||||
}
|
||||
|
||||
debug (...args) {
|
||||
if (!this.debugEnabled) return
|
||||
this._print(...args)
|
||||
}
|
||||
|
||||
_print (...args) {
|
||||
try {
|
||||
const strings = this._convertArgsToStrings(...args)
|
||||
if (strings.length) {
|
||||
if (this.prefix) {
|
||||
strings.unshift(this.prefix)
|
||||
}
|
||||
console.log(...strings)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Error while logging: ' + e)
|
||||
}
|
||||
}
|
||||
|
||||
_convertArgsToStrings (...args) {
|
||||
const out = []
|
||||
for (const arg of args) {
|
||||
if (arg instanceof Error) {
|
||||
out.push(arg.stack)
|
||||
} else if (arg instanceof Buffer) {
|
||||
out.push(debugDump(arg))
|
||||
} else if (typeof arg === 'function') {
|
||||
const result = arg.call(undefined, (...args) => this._print(...args))
|
||||
if (result !== undefined) out.push(...this._convertArgsToStrings(result))
|
||||
} else {
|
||||
out.push(arg)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
export default class Promises {
|
||||
static createTimeout (timeoutMs, timeoutMsg) {
|
||||
let cancel = null
|
||||
const wrapped = new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(
|
||||
() => {
|
||||
reject(new Error(timeoutMsg + ' - Timed out after ' + timeoutMs + 'ms'))
|
||||
},
|
||||
timeoutMs
|
||||
)
|
||||
cancel = () => {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
})
|
||||
wrapped.cancel = cancel
|
||||
return wrapped
|
||||
}
|
||||
}
|
||||
export default class Promises {
|
||||
static createTimeout (timeoutMs, timeoutMsg) {
|
||||
let cancel = null
|
||||
const wrapped = new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(
|
||||
() => {
|
||||
reject(new Error(timeoutMsg + ' - Timed out after ' + timeoutMs + 'ms'))
|
||||
},
|
||||
timeoutMs
|
||||
)
|
||||
cancel = () => {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
})
|
||||
wrapped.cancel = cancel
|
||||
return wrapped
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import * as Protocols from '../protocols/index.js'
|
||||
|
||||
export const getProtocol = (protocolId) => {
|
||||
if (!(protocolId in Protocols)) { throw Error('Protocol definition file missing: ' + protocolId) }
|
||||
|
||||
return new Protocols[protocolId]()
|
||||
}
|
||||
import * as Protocols from '../protocols/index.js'
|
||||
|
||||
export const getProtocol = (protocolId) => {
|
||||
if (!(protocolId in Protocols)) { throw Error('Protocol definition file missing: ' + protocolId) }
|
||||
|
||||
return new Protocols[protocolId]()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,104 +1,104 @@
|
|||
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()
|
||||
}
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,32 @@
|
|||
export 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Players extends Array {
|
||||
push (data) {
|
||||
super.push(new Player(data))
|
||||
}
|
||||
}
|
||||
|
||||
export class Results {
|
||||
name = ''
|
||||
map = ''
|
||||
password = false
|
||||
|
||||
raw = {}
|
||||
|
||||
maxplayers = 0
|
||||
players = new Players()
|
||||
bots = new Players()
|
||||
}
|
||||
export 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Players extends Array {
|
||||
push (data) {
|
||||
super.push(new Player(data))
|
||||
}
|
||||
}
|
||||
|
||||
export class Results {
|
||||
name = ''
|
||||
map = ''
|
||||
password = false
|
||||
|
||||
raw = {}
|
||||
|
||||
maxplayers = 0
|
||||
players = new Players()
|
||||
bots = new Players()
|
||||
}
|
||||
|
|
|
|||
46
lib/index.js
46
lib/index.js
|
|
@ -1,23 +1,23 @@
|
|||
import QueryRunner from './QueryRunner.js'
|
||||
|
||||
let singleton = null
|
||||
|
||||
export default class Gamedig {
|
||||
constructor (runnerOpts) {
|
||||
this.queryRunner = new QueryRunner(runnerOpts)
|
||||
}
|
||||
|
||||
async query (userOptions) {
|
||||
return await this.queryRunner.run(userOptions)
|
||||
}
|
||||
|
||||
static getInstance () {
|
||||
if (!singleton) { singleton = new Gamedig() }
|
||||
|
||||
return singleton
|
||||
}
|
||||
|
||||
static async query (...args) {
|
||||
return await Gamedig.getInstance().query(...args)
|
||||
}
|
||||
}
|
||||
import QueryRunner from './QueryRunner.js'
|
||||
|
||||
let singleton = null
|
||||
|
||||
export default class Gamedig {
|
||||
constructor (runnerOpts) {
|
||||
this.queryRunner = new QueryRunner(runnerOpts)
|
||||
}
|
||||
|
||||
async query (userOptions) {
|
||||
return await this.queryRunner.run(userOptions)
|
||||
}
|
||||
|
||||
static getInstance () {
|
||||
if (!singleton) { singleton = new Gamedig() }
|
||||
|
||||
return singleton
|
||||
}
|
||||
|
||||
static async query (...args) {
|
||||
return await Gamedig.getInstance().query(...args)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
344
lib/reader.js
344
lib/reader.js
|
|
@ -1,172 +1,172 @@
|
|||
import Iconv from 'iconv-lite'
|
||||
import Long from 'long'
|
||||
import { Buffer } from 'node:buffer'
|
||||
import Varint from 'varint'
|
||||
|
||||
function readUInt64BE (buffer, offset) {
|
||||
const high = buffer.readUInt32BE(offset)
|
||||
const low = buffer.readUInt32BE(offset + 4)
|
||||
return new Long(low, high, true)
|
||||
}
|
||||
function readUInt64LE (buffer, offset) {
|
||||
const low = buffer.readUInt32LE(offset)
|
||||
const high = buffer.readUInt32LE(offset + 4)
|
||||
return new Long(low, high, true)
|
||||
}
|
||||
|
||||
export default class Reader {
|
||||
/**
|
||||
* @param {Core} query
|
||||
* @param {Buffer} buffer
|
||||
**/
|
||||
constructor (query, buffer) {
|
||||
this.defaultEncoding = query.options.encoding || query.encoding
|
||||
this.defaultDelimiter = query.delimiter
|
||||
this.defaultByteOrder = query.byteorder
|
||||
this.buffer = buffer
|
||||
this.i = 0
|
||||
}
|
||||
|
||||
setOffset (offset) {
|
||||
this.i = offset
|
||||
}
|
||||
|
||||
offset () {
|
||||
return this.i
|
||||
}
|
||||
|
||||
skip (i) {
|
||||
this.i += i
|
||||
}
|
||||
|
||||
pascalString (bytesForSize, adjustment = 0) {
|
||||
const length = this.uint(bytesForSize) + adjustment
|
||||
return this.string(length)
|
||||
}
|
||||
|
||||
string (arg) {
|
||||
let encoding = this.defaultEncoding
|
||||
let length = null
|
||||
let delimiter = this.defaultDelimiter
|
||||
|
||||
if (typeof arg === 'string') delimiter = arg
|
||||
else if (typeof arg === 'number') length = arg
|
||||
else if (typeof arg === 'object') {
|
||||
if ('encoding' in arg) encoding = arg.encoding
|
||||
if ('length' in arg) length = arg.length
|
||||
if ('delimiter' in arg) delimiter = arg.delimiter
|
||||
}
|
||||
|
||||
if (encoding === 'latin1') encoding = 'win1252'
|
||||
|
||||
const start = this.i
|
||||
let end = start
|
||||
if (length === null) {
|
||||
// terminated by the delimiter
|
||||
let delim = delimiter
|
||||
if (typeof delim === 'string') delim = delim.charCodeAt(0)
|
||||
while (true) {
|
||||
if (end >= this.buffer.length) {
|
||||
end = this.buffer.length
|
||||
break
|
||||
}
|
||||
if (this.buffer.readUInt8(end) === delim) break
|
||||
end++
|
||||
}
|
||||
this.i = end + 1
|
||||
} else if (length <= 0) {
|
||||
return ''
|
||||
} else {
|
||||
end = start + length
|
||||
if (end >= this.buffer.length) {
|
||||
end = this.buffer.length
|
||||
}
|
||||
this.i = end
|
||||
}
|
||||
|
||||
const slice = this.buffer.slice(start, end)
|
||||
const enc = encoding
|
||||
if (enc === 'utf8' || enc === 'ucs2' || enc === 'binary') {
|
||||
return slice.toString(enc)
|
||||
} else {
|
||||
return Iconv.decode(slice, enc)
|
||||
}
|
||||
}
|
||||
|
||||
int (bytes) {
|
||||
let r = 0
|
||||
if (this.remaining() >= bytes) {
|
||||
if (this.defaultByteOrder === 'be') {
|
||||
if (bytes === 1) r = this.buffer.readInt8(this.i)
|
||||
else if (bytes === 2) r = this.buffer.readInt16BE(this.i)
|
||||
else if (bytes === 4) r = this.buffer.readInt32BE(this.i)
|
||||
} else {
|
||||
if (bytes === 1) r = this.buffer.readInt8(this.i)
|
||||
else if (bytes === 2) r = this.buffer.readInt16LE(this.i)
|
||||
else if (bytes === 4) r = this.buffer.readInt32LE(this.i)
|
||||
}
|
||||
}
|
||||
this.i += bytes
|
||||
return r
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
uint (bytes) {
|
||||
let r = 0
|
||||
if (this.remaining() >= bytes) {
|
||||
if (this.defaultByteOrder === 'be') {
|
||||
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)
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
this.i += bytes
|
||||
return r
|
||||
}
|
||||
|
||||
float () {
|
||||
let r = 0
|
||||
if (this.remaining() >= 4) {
|
||||
if (this.defaultByteOrder === 'be') r = this.buffer.readFloatBE(this.i)
|
||||
else r = this.buffer.readFloatLE(this.i)
|
||||
}
|
||||
this.i += 4
|
||||
return r
|
||||
}
|
||||
|
||||
varint () {
|
||||
const out = Varint.decode(this.buffer, this.i)
|
||||
this.i += Varint.decode.bytes
|
||||
return out
|
||||
}
|
||||
|
||||
/** @returns Buffer */
|
||||
part (bytes) {
|
||||
let r
|
||||
if (this.remaining() >= bytes) {
|
||||
r = this.buffer.slice(this.i, this.i + bytes)
|
||||
} else {
|
||||
r = Buffer.from([])
|
||||
}
|
||||
this.i += bytes
|
||||
return r
|
||||
}
|
||||
|
||||
remaining () {
|
||||
return this.buffer.length - this.i
|
||||
}
|
||||
|
||||
rest () {
|
||||
return this.buffer.slice(this.i)
|
||||
}
|
||||
|
||||
done () {
|
||||
return this.i >= this.buffer.length
|
||||
}
|
||||
}
|
||||
import Iconv from 'iconv-lite'
|
||||
import Long from 'long'
|
||||
import { Buffer } from 'node:buffer'
|
||||
import Varint from 'varint'
|
||||
|
||||
function readUInt64BE (buffer, offset) {
|
||||
const high = buffer.readUInt32BE(offset)
|
||||
const low = buffer.readUInt32BE(offset + 4)
|
||||
return new Long(low, high, true)
|
||||
}
|
||||
function readUInt64LE (buffer, offset) {
|
||||
const low = buffer.readUInt32LE(offset)
|
||||
const high = buffer.readUInt32LE(offset + 4)
|
||||
return new Long(low, high, true)
|
||||
}
|
||||
|
||||
export default class Reader {
|
||||
/**
|
||||
* @param {Core} query
|
||||
* @param {Buffer} buffer
|
||||
**/
|
||||
constructor (query, buffer) {
|
||||
this.defaultEncoding = query.options.encoding || query.encoding
|
||||
this.defaultDelimiter = query.delimiter
|
||||
this.defaultByteOrder = query.byteorder
|
||||
this.buffer = buffer
|
||||
this.i = 0
|
||||
}
|
||||
|
||||
setOffset (offset) {
|
||||
this.i = offset
|
||||
}
|
||||
|
||||
offset () {
|
||||
return this.i
|
||||
}
|
||||
|
||||
skip (i) {
|
||||
this.i += i
|
||||
}
|
||||
|
||||
pascalString (bytesForSize, adjustment = 0) {
|
||||
const length = this.uint(bytesForSize) + adjustment
|
||||
return this.string(length)
|
||||
}
|
||||
|
||||
string (arg) {
|
||||
let encoding = this.defaultEncoding
|
||||
let length = null
|
||||
let delimiter = this.defaultDelimiter
|
||||
|
||||
if (typeof arg === 'string') delimiter = arg
|
||||
else if (typeof arg === 'number') length = arg
|
||||
else if (typeof arg === 'object') {
|
||||
if ('encoding' in arg) encoding = arg.encoding
|
||||
if ('length' in arg) length = arg.length
|
||||
if ('delimiter' in arg) delimiter = arg.delimiter
|
||||
}
|
||||
|
||||
if (encoding === 'latin1') encoding = 'win1252'
|
||||
|
||||
const start = this.i
|
||||
let end = start
|
||||
if (length === null) {
|
||||
// terminated by the delimiter
|
||||
let delim = delimiter
|
||||
if (typeof delim === 'string') delim = delim.charCodeAt(0)
|
||||
while (true) {
|
||||
if (end >= this.buffer.length) {
|
||||
end = this.buffer.length
|
||||
break
|
||||
}
|
||||
if (this.buffer.readUInt8(end) === delim) break
|
||||
end++
|
||||
}
|
||||
this.i = end + 1
|
||||
} else if (length <= 0) {
|
||||
return ''
|
||||
} else {
|
||||
end = start + length
|
||||
if (end >= this.buffer.length) {
|
||||
end = this.buffer.length
|
||||
}
|
||||
this.i = end
|
||||
}
|
||||
|
||||
const slice = this.buffer.slice(start, end)
|
||||
const enc = encoding
|
||||
if (enc === 'utf8' || enc === 'ucs2' || enc === 'binary') {
|
||||
return slice.toString(enc)
|
||||
} else {
|
||||
return Iconv.decode(slice, enc)
|
||||
}
|
||||
}
|
||||
|
||||
int (bytes) {
|
||||
let r = 0
|
||||
if (this.remaining() >= bytes) {
|
||||
if (this.defaultByteOrder === 'be') {
|
||||
if (bytes === 1) r = this.buffer.readInt8(this.i)
|
||||
else if (bytes === 2) r = this.buffer.readInt16BE(this.i)
|
||||
else if (bytes === 4) r = this.buffer.readInt32BE(this.i)
|
||||
} else {
|
||||
if (bytes === 1) r = this.buffer.readInt8(this.i)
|
||||
else if (bytes === 2) r = this.buffer.readInt16LE(this.i)
|
||||
else if (bytes === 4) r = this.buffer.readInt32LE(this.i)
|
||||
}
|
||||
}
|
||||
this.i += bytes
|
||||
return r
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
uint (bytes) {
|
||||
let r = 0
|
||||
if (this.remaining() >= bytes) {
|
||||
if (this.defaultByteOrder === 'be') {
|
||||
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)
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
this.i += bytes
|
||||
return r
|
||||
}
|
||||
|
||||
float () {
|
||||
let r = 0
|
||||
if (this.remaining() >= 4) {
|
||||
if (this.defaultByteOrder === 'be') r = this.buffer.readFloatBE(this.i)
|
||||
else r = this.buffer.readFloatLE(this.i)
|
||||
}
|
||||
this.i += 4
|
||||
return r
|
||||
}
|
||||
|
||||
varint () {
|
||||
const out = Varint.decode(this.buffer, this.i)
|
||||
this.i += Varint.decode.bytes
|
||||
return out
|
||||
}
|
||||
|
||||
/** @returns Buffer */
|
||||
part (bytes) {
|
||||
let r
|
||||
if (this.remaining() >= bytes) {
|
||||
r = this.buffer.slice(this.i, this.i + bytes)
|
||||
} else {
|
||||
r = Buffer.from([])
|
||||
}
|
||||
this.i += bytes
|
||||
return r
|
||||
}
|
||||
|
||||
remaining () {
|
||||
return this.buffer.length - this.i
|
||||
}
|
||||
|
||||
rest () {
|
||||
return this.buffer.slice(this.i)
|
||||
}
|
||||
|
||||
done () {
|
||||
return this.i >= this.buffer.length
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue