mirror of
https://github.com/gamedig/node-gamedig.git
synced 2024-11-19 02:00:38 +01:00
Add support for udp bind port override (3.0.5) Fixes #149
This commit is contained in:
parent
68b8dfd684
commit
ce4e728493
8 changed files with 59 additions and 26 deletions
|
@ -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
|
### 3.0.4
|
||||||
* Add support for Discord widget
|
* Add support for Discord widget
|
||||||
|
|
||||||
|
|
24
README.md
24
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
|
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.
|
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.
|
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:
|
Some examples include:
|
||||||
* Docker containers
|
* 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
|
* 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
|
* 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
|
### 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.
|
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
|
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
|
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.
|
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
|
Usage from Command Line
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ const Minimist = require('minimist'),
|
||||||
|
|
||||||
const argv = Minimist(process.argv.slice(2), {
|
const argv = Minimist(process.argv.slice(2), {
|
||||||
boolean: ['pretty','debug','givenPortOnly'],
|
boolean: ['pretty','debug','givenPortOnly'],
|
||||||
string: ['guildId']
|
string: ['guildId','listenUdpPort']
|
||||||
});
|
});
|
||||||
|
|
||||||
const debug = argv.debug;
|
const debug = argv.debug;
|
||||||
|
@ -41,7 +41,8 @@ if (givenPortOnly) {
|
||||||
options.givenPortOnly = true;
|
options.givenPortOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Gamedig.query(options)
|
const gamedig = new Gamedig(options);
|
||||||
|
gamedig.query(options)
|
||||||
.then((state) => {
|
.then((state) => {
|
||||||
if(pretty) {
|
if(pretty) {
|
||||||
console.log(JSON.stringify(state,null,' '));
|
console.log(JSON.stringify(state,null,' '));
|
||||||
|
|
|
@ -1,25 +1,29 @@
|
||||||
const dgram = require('dgram'),
|
const dgram = require('dgram');
|
||||||
HexUtil = require('./HexUtil'),
|
const HexUtil = require('./HexUtil');
|
||||||
Logger = require('./Logger');
|
const Logger = require('./Logger');
|
||||||
|
const util = require('util');
|
||||||
|
|
||||||
class GlobalUdpSocket {
|
class GlobalUdpSocket {
|
||||||
constructor() {
|
constructor({port}) {
|
||||||
this.socket = null;
|
this.socket = null;
|
||||||
this.callbacks = new Set();
|
this.callbacks = new Set();
|
||||||
this.debuggingCallbacks = new Set();
|
this.debuggingCallbacks = new Set();
|
||||||
this.logger = new Logger();
|
this.logger = new Logger();
|
||||||
|
this.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getSocket() {
|
async _getSocket() {
|
||||||
if (!this.socket) {
|
if (!this.socket) {
|
||||||
const udpSocket = this.socket = dgram.createSocket('udp4');
|
const udpSocket = dgram.createSocket({
|
||||||
|
type: 'udp4',
|
||||||
|
reuseAddr: true
|
||||||
|
});
|
||||||
udpSocket.unref();
|
udpSocket.unref();
|
||||||
udpSocket.bind();
|
|
||||||
udpSocket.on('message', (buffer, rinfo) => {
|
udpSocket.on('message', (buffer, rinfo) => {
|
||||||
const fromAddress = rinfo.address;
|
const fromAddress = rinfo.address;
|
||||||
const fromPort = rinfo.port;
|
const fromPort = rinfo.port;
|
||||||
this.logger.debug(log => {
|
this.logger.debug(log => {
|
||||||
log(fromAddress + ':' + fromPort + " <--UDP");
|
log(fromAddress + ':' + fromPort + " <--UDP(" + this.port + ")");
|
||||||
log(HexUtil.debugDump(buffer));
|
log(HexUtil.debugDump(buffer));
|
||||||
});
|
});
|
||||||
for (const cb of this.callbacks) {
|
for (const cb of this.callbacks) {
|
||||||
|
@ -29,12 +33,22 @@ class GlobalUdpSocket {
|
||||||
udpSocket.on('error', e => {
|
udpSocket.on('error', e => {
|
||||||
this.logger.debug("UDP 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;
|
return this.socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
send(buffer, address, port) {
|
async send(buffer, address, port, debug) {
|
||||||
this._getSocket().send(buffer,0,buffer.length,port,address);
|
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) {
|
addCallback(callback, debug) {
|
||||||
|
|
|
@ -9,8 +9,10 @@ const defaultOptions = {
|
||||||
};
|
};
|
||||||
|
|
||||||
class QueryRunner {
|
class QueryRunner {
|
||||||
constructor() {
|
constructor(runnerOpts = {}) {
|
||||||
this.udpSocket = new GlobalUdpSocket();
|
this.udpSocket = new GlobalUdpSocket({
|
||||||
|
port: runnerOpts.listenUdpPort
|
||||||
|
});
|
||||||
this.gameResolver = new GameResolver();
|
this.gameResolver = new GameResolver();
|
||||||
this.protocolResolver = new ProtocolResolver();
|
this.protocolResolver = new ProtocolResolver();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@ const QueryRunner = require('./QueryRunner');
|
||||||
let singleton = null;
|
let singleton = null;
|
||||||
|
|
||||||
class Gamedig {
|
class Gamedig {
|
||||||
constructor() {
|
constructor(runnerOpts) {
|
||||||
this.queryRunner = new QueryRunner();
|
this.queryRunner = new QueryRunner(runnerOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
async query(userOptions) {
|
async query(userOptions) {
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
],
|
],
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"author": "GameDig Contributors",
|
"author": "GameDig Contributors",
|
||||||
"version": "3.0.4",
|
"version": "3.0.5",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/gamedig/node-gamedig.git"
|
"url": "https://github.com/gamedig/node-gamedig.git"
|
||||||
|
|
|
@ -252,13 +252,9 @@ class Core extends EventEmitter {
|
||||||
this.assertValidPort(port);
|
this.assertValidPort(port);
|
||||||
|
|
||||||
if(typeof buffer === 'string') buffer = Buffer.from(buffer,'binary');
|
if(typeof buffer === 'string') buffer = Buffer.from(buffer,'binary');
|
||||||
this.debugLog(log => {
|
|
||||||
log(address+':'+port+" UDP-->");
|
|
||||||
log(HexUtil.debugDump(buffer));
|
|
||||||
});
|
|
||||||
|
|
||||||
const socket = this.udpSocket;
|
const socket = this.udpSocket;
|
||||||
socket.send(buffer, address, port);
|
await socket.send(buffer, address, port, this.options.debug);
|
||||||
|
|
||||||
if (!onPacket && !onTimeout) {
|
if (!onPacket && !onTimeout) {
|
||||||
return null;
|
return null;
|
||||||
|
|
Loading…
Reference in a new issue