12 Commits

Author SHA1 Message Date
skylord123 5bacdb685d Release v2.2.1
- Update gamedig from 4.0.6 to 4.0.7
2023-09-05 15:10:50 -06:00
skylord123 2bf5cc5f48 Update deps 2023-05-10 22:41:08 -06:00
skylord123 f0d4b8e269 Set version to 2.2.0 2023-05-10 22:39:57 -06:00
skylord123 5f80a0dbed Merge pull request #12 from skylord123/dev
Release 2.2.0
2023-05-10 22:37:11 -06:00
skylord123 787ee61c4e - You can now pass any option to the underlying GameDig library when querying a server by setting msg.options object on the input. Can be used for example to set msg.options.guildId that is required for querying Discord servers. Note that this will override even the config settings from the UI.
- New option "Output options" that you can enable from config to output `msg.options` object that contains all options we passed to GameDig to query the server. This overrides all other options (whether set from the UI or from `msg.*` variables) so take note of that when chaining multiple server query nodes together with this enabled.
- Breaking Change: This node no longer returns `msg.max_attempts`, `msg.socket_timeout`, and `msg.attempt_timeout`, instead see "Output options" mentioned above if you need this.
2023-05-10 22:12:26 -06:00
skylord123 8bdb043923 Closes #11 - Add autocomplete for server types instead of having to search at bottom of config page 2023-05-10 19:38:10 -06:00
skylord123 a5498a4ba9 #10 - Work on adding all available options (including advanced) from damedig 2023-05-09 23:35:48 -06:00
skylord123 bb5cb45cd9 Updated readme 2023-02-23 23:24:23 -07:00
skylord123 facf77e1fa Merge pull request #9 from skylord123/dev
Update version to 2.1.2
2023-02-23 23:09:44 -07:00
skylord123 9d200b60a1 - update version to 2.1.2
- fixed server type list
- bump gamedig to 4.0.6
2023-02-23 23:06:48 -07:00
skylord123 f6078118c0 Merge pull request #2 from skylord123/dev
update version to 2.1.1
2021-09-04 23:01:58 -06:00
skylord123 e2e8119d7f - update version to 2.1.1
- node description updated
- node now errors if msg.host or msg.port is left blank and not configured on the node
- server type & host is not required when configuring the node since it can be passed in via the inputs
- when modifying a node in the editor you can now view available server types at the bottom of the properties. This is parsed from the gamedig readme.
- Update gamedig version
- package-lock.json added
- palette label changed to "Query Game Server"
2021-09-04 23:00:32 -06:00
6 changed files with 1453 additions and 108 deletions
+3
View File
@@ -1,3 +1,6 @@
# ide
.idea
# Logs
logs
*.log
+13 -2
View File
@@ -1,17 +1,28 @@
# node-red-contrib-gamedig
Query for the status of any game server using Node-RED.
Query for server information of most game/voice servers using Node-RED.
This package adds the node "Query Game Server" that uses the NPM package [GameDig](https://www.npmjs.com/package/gamedig) to query if a server is online or not and if so returns the data of the server.
You can pass the server type, host, and port on the input message or define them on the node (settings defined on the node will override msg values).
You can also specify manual GameDig options using `msg.options` as an input. This will override any other options. For example: you can set `msg.options.guildId` that is required for querying Discord servers.
Visit the [GameDig GitLab page](https://github.com/gamedig/node-gamedig#return-value) if you want more information about what this library parses and standardizes from the server response.
### Usage Examples
#### Inserting query data into InfluxDB and using Grafana to view results
- #### Inserting query data into InfluxDB and using Grafana to view results
![Flow Preview](https://skylar.tech/content/images/2019/12/image-2.png)
I created a post on my website about how to use this node to query gameservers and store the results in InfluxDB. I then give a dashboard in Grafana that can be used to display the data. Check it out here:
https://skylar.tech/tracking-game-server-statistics-using-node-red-influxdb-and-grafana/
- #### Automatically restarting servers when unavailable
Ever host a server and have it stop responding but the process doesn't crash so it doesn't auto restart? If you pair this with something like [node-red-contrib-dockerode](https://flows.nodered.org/node/node-red-contrib-dockerode) you can automatically restart the container/process if the query fails X times to respond.
### Other Packages
- [node-red-contrib-matrix-chat](https://www.npmjs.com/package/node-red-contrib-gamedig) - Matrix chat server client for Node-RED
### Developers
So far me (skylord123) is the only person that has contributed towards this package. Feel free to do any pull-requests to get your name here!
+1152
View File
File diff suppressed because it is too large Load Diff
+7 -3
View File
@@ -1,6 +1,6 @@
{
"name": "node-red-contrib-gamedig",
"version": "2.0.1",
"version": "2.2.1",
"description": "Query for the status of any game server using node-red",
"repository": {
"type": "git",
@@ -12,13 +12,17 @@
"url": "https://github.com/skylord123/node-red-contrib-gamedig/issues"
},
"homepage": "https://github.com/skylord123/node-red-contrib-gamedig#readme",
"keywords": [ "node-red", "gamedig", "query game server" ],
"keywords": [
"node-red",
"gamedig",
"query game server"
],
"node-red": {
"nodes": {
"query-game-server": "query-game-server.js"
}
},
"dependencies": {
"gamedig": "^2.0.0"
"gamedig": "^4.0.7"
}
}
+198 -68
View File
@@ -4,17 +4,23 @@
color: '#a6bbcf',
defaults: {
name: { value: '' },
server_type: { value: '', required: true },
host: { value: '', required: true },
server_type: { value: '' },
host: { value: '' },
port: { value: '' },
halt_if: { value: '' },
max_attempts: { value: '' },
socket_timeout: { value: '' },
attempt_timeout: { value: '' },
given_port_only: { value: '' },
ip_family: { value: '0' },
debug: { value: '' },
request_rules: { value: '' },
output_options: { value: '' }
},
inputs:1,
outputs:1,
icon: "icons/server.png",
paletteLabel: "Query Game Server",
label: function() {
if(this.name){
return this.name;
@@ -25,11 +31,55 @@
}
return 'Query Game Server';
},
oneditprepare: function() {
let server_types = null;
$.getJSON('/gamedig/types', function(data) {
if(data.result !== 'ok' || !data.hasOwnProperty("server_types"))
{
console.error("server_types failed to load");
return;
}
server_types = data.server_types;
});
$("#node-input-server_type").autoComplete({
search: function(val) {
if(!server_types) return false; // ignore until we have the types loaded
let matches = [];
server_types.forEach(v => {
if (
v.name.toLowerCase().indexOf(val.toLowerCase()) > -1 ||
v.type.toLowerCase().indexOf(val.toLowerCase()) > -1 ||
v.protocol.toLowerCase().indexOf(val.toLowerCase()) > -1
) {
matches.push({
value: v.type,
label: `${v.name} (${v.type})`,
protocol: v.protocol
});
}
});
return matches;
}
});
}
});
</script>
<script type="text/x-red" data-template-name="query-game-server">
<script type="text/html" data-template-name="query-game-server">
<style>
#query-game-server-types table, #query-game-server-types th, #query-game-server-types td {
border: 1px solid black;
border-collapse: collapse;
}
#query-game-server-types td {
padding: 3px;
}
</style>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
@@ -37,29 +87,28 @@
<div class="form-row">
<label for="node-input-server_type"><i class="fa fa-cube"></i> Server Type</label>
<input type="text" id="node-input-server_type">
<input type="text" id="node-input-server_type" placeholder="msg.server_type">
</div>
<div class="form-row">
<label>&nbsp;</label>
<span>Check <a href="https://github.com/sonicsnes/node-gamedig#games-list" style="color:#0000EE;" target="_blank">here</a> for a list of supported server types.</span>
<div style="margin-left: 105px;width: 50%;margin-bottom: 10px;margin-top: -10px;">
Recommend visiting the <a href="https://github.com/gamedig/node-gamedig#games-list" target="_blank" style="color:#0000EE;text-decoration: underline;">GameDig GitHub page</a> for more information about the server type you are trying to query. Some types require extra setup.
</div>
<div class="form-row">
<label for="node-input-host"><i class="fa fa-server"></i> Host</label>
<input type="text" id="node-input-host" />
<input type="text" id="node-input-host" placeholder="msg.host" />
</div>
<div class="form-row">
<label>&nbsp;</label>
<span>Host without port</span>
<div style="margin-left: 105px;width: 50%;margin-bottom: 10px;margin-top: -10px;">
Host without port. Uses <code>msg.host</code> if left blank.
</div>
<div class="form-row">
<label for="node-input-port"><i class="fa fa-server"></i> Port</label>
<input type="text" id="node-input-port" />
<input type="text" id="node-input-port" placeholder="msg.port" />
</div>
<div class="form-row">
<label>&nbsp;</label>
<span>Query port for the server (not always the same as the join port). If this is left blank it will use the default port for the server type specified.</span>
<div style="margin-left: 105px;width: 50%;margin-bottom: 10px;margin-top: -10px;">
Query port for the server (join and query port may differ).
If this is left blank it will use the default port for the server type specified.
Uses <code>msg.port</code> if left blank.
</div>
<div class="form-row">
@@ -70,112 +119,193 @@
<option value="offline">Offline</option>
</select>
</div>
<div class="form-row">
<label>&nbsp;</label>
<span>Enter "online" or "offline" (without quotes) to filter out the state you don't want.</span>
<div style="margin-left: 105px;width: 50%;margin-bottom: 10px;margin-top: -10px;">
Filter messages based on server status
</div>
<div class="form-row">
<label for="node-input-max_attempts"><i class="fa fa-server"></i> Max Attempts</label>
<input type="text" id="node-input-max_attempts" placeholder="1" />
<label for="node-input-max_attempts"><i class="fa fa-cogs"></i> Max Attempts</label>
<input type="text" id="node-input-max_attempts" placeholder="msg.max_attempts (default: 1)" />
</div>
<div class="form-row">
<label>&nbsp;</label>
<span>Number of attempts to query server in case of failure.</span>
<div style="margin-left: 105px;width: 50%;margin-bottom: 10px;margin-top: -10px;">
Number of attempts to query server in case of failure.
</div>
<div class="form-row">
<label for="node-input-socket_timeout"><i class="fa fa-server"></i> Socket Timeout</label>
<input type="text" id="node-input-socket_timeout" placeholder="2000" />
<label for="node-input-socket_timeout"><i class="fa fa-cogs"></i> Socket Timeout</label>
<input type="text" id="node-input-socket_timeout" placeholder="msg.socket_timeout (default: 2000)" />
</div>
<div class="form-row">
<label>&nbsp;</label>
<span>Milliseconds to wait for a single packet. Beware that increasing this will cause many queries to take longer even if the server is online.</span>
<div style="margin-left: 105px;width: 50%;margin-bottom: 10px;margin-top: -10px;">
Milliseconds to wait for a single packet. Beware that increasing this will cause many queries to take longer even if the server is online.
</div>
<div class="form-row">
<label for="node-input-attempt_timeout"><i class="fa fa-server"></i> Attempt Timeout</label>
<input type="text" id="node-input-attempt_timeout" placeholder="10000" />
<label for="node-input-attempt_timeout"><i class="fa fa-cogs"></i> Attempt Timeout</label>
<input type="text" id="node-input-attempt_timeout" placeholder="msg.attempt_timeout (default: 10000)" />
</div>
<div style="margin-left: 105px;width: 50%;margin-bottom: 10px;margin-top: -10px;">
Milliseconds allowed for an entire query attempt. This timeout is not commonly hit, as the socketTimeout typically fires first.
</div>
<div class="form-row">
<label>&nbsp;</label>
<span>Milliseconds allowed for an entire query attempt. This timeout is not commonly hit, as the socketTimeout typically fires first.</span>
<label for="node-input-given_port_only"><i class="fa fa-lock"></i> Lock port</label>
<input
type="checkbox"
id="node-input-given_port_only"
style="width: auto; vertical-align: top"
/>
<span>
Only attempt to query server on given port (default: false).
</span>
</div>
<div class="form-row">
<label for="node-input-debug"><i class="fa fa-bug"></i> Debug mode</label>
<input
type="checkbox"
id="node-input-debug"
style="width: auto; vertical-align: top"
/>
<span>
Enables massive amounts of debug logging to stdout.
</span>
</div>
<div class="form-row">
<label for="node-input-request_rules" style="vertical-align: top"><i class="fa fa-server"></i> Request rules</label>
<div style="width: 50%;display: inline-block;">
<input
type="checkbox"
id="node-input-request_rules"
style="width: auto; vertical-align: top"
/>
For many valve games, additional 'rules' may be fetched into the unstable raw field by setting this to true. Beware that this may increase query time and this is for Valve games only.
</div>
</div>
<div class="form-row">
<label for="node-input-output_options" style="vertical-align: top"><i class="fa fa-server"></i> Output options</label>
<div style="width: 50%;display: inline-block;">
<input
type="checkbox"
id="node-input-output_options"
style="width: auto; vertical-align: top"
/>
Outputs <code style="white-space: normal;">msg.options</code> as an object that contains all the options used to query the server using GameDig. Note: If you pass <code style="white-space: normal;">msg.options</code> as an input it will override all set options so make sure you unset it if chaining multiple server query nodes together unless that is what you want.
</div>
</div>
<div class="form-row">
<label for="node-input-ip_family"><i class="fa fa-server"></i> IP Rules</label>
<select
id="node-input-ip_family"
style="width: auto; vertical-align: top">
<option value="0">IPv4 and IPv6</option>
<option value="4">IPv4</option>
<option value="6">IPv6</option>
</select>
</div>
<div style="margin-left: 105px;width: 50%;margin-bottom: 10px;margin-top: -10px;">
IP family/version returned when looking up hostnames via DNS, can be IPv4 and IPv6, IPv4 only or IPv6 only.
</div>
</script>
<script type="text/x-red" data-help-name="query-game-server">
<p>Query a Game Server</p>
<h3>Config</h3>
<dl class="message-properties">
<dt>Server Type <span class="property-type">string</span></dt>
<dt>Host <span class="property-type">string</span></dt>
<dt>Port <span class="property-type">integer</span></dt>
<dt>Halt If <span class="property-type">string</span></dt>
</dl>
<script type="text/html" data-help-name="query-game-server">
<p>Query most Game/Voice server's using the <a href="https://github.com/gamedig/node-gamedig" target="_blank">GameDig</a> library. I recommend visiting the <a href="https://github.com/gamedig/node-gamedig" target="_blank">node-gamedig GitHub page</a> for more documentation.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt class="optional">
msg.server_type <span class="property-type">string</span>
msg.server_type <span class="property-type">string | null</span>
</dt>
<dd>Server type to query. Check <a href="https://github.com/sonicsnes/node-gamedig#games-list" style="color:#0000EE;" target="_blank">here</a> for a list of supported server types</dd>
<dd>Server type to query. View available types either at the bottom of the node editor or <a href="https://github.com/sonicsnes/node-gamedig#games-list" style="color:#0000EE;" target="_blank">click here</a>. Ignored if configured on the node.</dd>
<dt class="optional">
msg.host <span class="property-type">string</span>
msg.host <span class="property-type">string | null</span>
</dt>
<dd>Server IP/Hostname</dd>
<dd>Server IP/Hostname. Ignored if configured on the node.</dd>
<dt class="optional">
msg.port <span class="property-type">integer</span>
msg.port <span class="property-type">integer | null</span>
</dt>
<dd>Query port of the server</dd>
<dd>Query port of the server. Ignored if configured on the node. Uses default query port for the server type if left empty.</dd>
<dt class="optional">
msg.max_attempts <span class="property-type">integer</span>
msg.options <span class="property-type">object | null</span>
</dt>
<dd>Number of attempts to query server in case of failure.</dd>
<dd>Set additional GameDig options. This overrides all other methods of setting options. Can be used for example to set <code style="white-space: normal;">msg.options.guildId</code> that is required for querying Discord servers.</dd>
<dt class="optional">
msg.socket_timeout <span class="property-type">integer</span>
msg.max_attempts <span class="property-type">integer | null</span>
</dt>
<dd>Milliseconds to wait for a single packet. Beware that increasing this will cause many queries to take longer even if the server is online.</dd>
<dd>Number of attempts to query server in case of failure. Ignored if configured on the node.</dd>
<dt class="optional">
msg.attempt_timeout <span class="property-type">integer</span>
msg.socket_timeout <span class="property-type">integer | null</span>
</dt>
<dd>Milliseconds allowed for an entire query attempt. This timeout is not commonly hit, as the socketTimeout typically fires first.</dd>
<dd>Milliseconds to wait for a single packet. Beware that increasing this will cause many queries to take longer even if the server is online. Ignored if configured on the node.</dd>
<dt class="optional">
msg.attempt_timeout <span class="property-type">integer | null</span>
</dt>
<dd>Milliseconds allowed for an entire query attempt. This timeout is not commonly hit, as the socketTimeout typically fires first. Ignored if configured on the node.</dd>
<dt class="optional">
msg.given_port_only <span class="property-type">boolean</span>
</dt>
<dd>Only attempt to query server on given port. Ignored if configured on the node.</dd>
<dt class="optional">
msg.ip_family <span class="property-type">number</span>
</dt>
<dd>IP family/version returned when looking up hostnames via DNS, can be 0 (IPv4 and IPv6), 4 (IPv4 only) or 6 (IPv6 only). (default 0).</dd>
<dt class="optional">
msg.debug <span class="property-type">boolean</span>
</dt>
<dd>Enables massive amounts of debug logging to stdout. (default false)</dd>
<dt class="optional">
msg.request_rules <span class="property-type">boolean</span>
</dt>
<dd>For many valve games, additional 'rules' may be fetched into the unstable raw field by setting this to true. Beware that this may increase query time and this is for Valve games only.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>
msg.payload <span class="property-type">string</span>
</dt>
<dd>Returns either <code>online</code> or <code>offline</code> depending on if we successfully query the server or not.</dd>
<dd>Returns either <code>online</code> or <code>offline</code>.</dd>
<dt>
msg.data <span class="property-type">object</span>
</dt>
<dd>Returns back the data we got from GameDig. Click <a href="https://github.com/sonicsnes/node-gamedig#return-value" style="color:#0000EE;">here</a> for more information on what this contains.</dd>
<dd>Returns back the results we got from GameDig. Click <a href="https://github.com/sonicsnes/node-gamedig#return-value" target="_blank" style="color:#0000EE;">here</a> for more information on what this contains.</dd>
<dt>
msg.data.error <span class="property-type">string</span>
</dt>
<dd>Reason the server failed to query. Only returns if <code>msg.payload</code> is <code>offline</code>.</dd>
<dt>
msg.server_type <span class="property-type">string</span>
</dt>
<dt>
msg.server_type <span class="property-type">string</span>
</dt>
<dd>Server type to query. View available types either at the bottom of the node editor or <a href="https://github.com/sonicsnes/node-gamedig#games-list" style="color:#0000EE;" target="_blank">click here</a>. Ignored if configured on the node.</dd>
<dt>
msg.host <span class="property-type">string</span>
</dt>
<dd>Server IP/Hostname. Ignored if configured on the node.</dd>
<dt>
msg.port <span class="property-type">integer</span>
</dt>
<dd>Query port of the server. Ignored if configured on the node.</dd>
<dt>
msg.halt_if <span class="property-type">string</span>
</dt>
<dt>
msg.max_attempts <span class="property-type">integer</span>
</dt>
<dt>
msg.socket_timeout <span class="property-type">integer</span>
</dt>
<dt>
msg.attempt_timeout <span class="property-type">integer</span>
msg.options <span class="property-type">object | undefined</span>
</dt>
<dd>Only set if configured to do so. Will return all the options passed to GameDig to query the server.</dd>
</dl>
</script>
+76 -31
View File
@@ -1,8 +1,10 @@
var Gamedig = require('gamedig');
module.exports = function(RED) {
const gamedig = require('gamedig');
const fs = require('fs');
function QueryGameServer(config) {
RED.nodes.createNode(this, config);
let node = this;
this.server_type = config.server_type;
this.host = config.host;
this.port = config.port;
@@ -10,45 +12,50 @@ module.exports = function(RED) {
this.max_attempts = config.max_attempts || 1;
this.socket_timeout = config.socket_timeout || 2000;
this.attempt_timeout = config.attempt_timeout || 10000;
var node = this;
this.given_port_only = config.given_port_only || false;
this.ip_family = config.ip_family || 0;
this.debug = config.debug || false;
this.request_rules = config.request_rules || false;
this.output_options = config.output_options || false;
node.on('input', function(msg) {
let options = {
'type': node.server_type || msg.server_type || undefined,
'host': node.host || msg.host || undefined,
'port': node.port || msg.port || undefined,
'maxAttempts': node.max_attempts || msg.max_attempts || undefined,
'socketTimeout': node.socket_timeout || msg.socket_timeout || undefined,
'attemptTimeout': node.attempt_timeout || msg.attempt_timeout || undefined,
'givenPortOnly': node.given_port_only || msg.given_port_only || undefined,
'ipFamily': node.ip_family || msg.ip_family || undefined,
'debug': node.debug || msg.config || undefined,
'requestRules': node.request_rules || msg.request_rules || undefined
};
if(node.server_type) {
msg.server_type = node.server_type;
if(typeof msg.options === 'object' && msg.options)
{
options = {...options, ...msg.options};
}
if(node.host) {
msg.host = node.host;
// set the things we want to return
msg.server_type = options.type;
msg.host = options.host;
msg.port = options.port;
if(node.output_options)
{
msg.options = options;
}
if(node.port) {
msg.port = node.port;
if(!options.host) {
node.error("host missing from input.");
return;
}
if(node.halt_if) {
msg.halt_if = node.halt_if;
if(!options.type) {
node.error("server_type missing from input.");
return;
}
if(node.max_attempts) {
msg.max_attempts = node.max_attempts;
}
if(node.socket_timeout) {
msg.socket_timeout = node.socket_timeout;
}
if(node.attempt_timeout) {
msg.attempt_timeout = node.attempt_timeout;
}
Gamedig.query({
'type': msg.server_type,
'host': msg.host,
'port': msg.port,
'maxAttempts': msg.max_attempts,
'socketTimeout': msg.socket_timeout,
'attemptTimeout': msg.attempt_timeout
})
gamedig.query(options)
.then(function(state) {
msg.payload = 'online';
msg.data = state;
@@ -71,4 +78,42 @@ module.exports = function(RED) {
});
}
RED.nodes.registerType("query-game-server", QueryGameServer);
RED.httpAdmin.get(
"/gamedig/types",
RED.auth.needsPermission('gamedig.types'),
function(req, res) {
// gamedig has no way of listing available server types
// so we just use regex to parse the info from the README
// this could break so we also reference the gamedig repo
let availableTypesContent = fs.readFileSync(require.resolve("gamedig/games.txt"), 'utf-8')
server_types = [];
availableTypesContent
.split(/\r?\n/)
.forEach(line => {
if(
line.trim().length === 0
|| line.trim().length === 0
|| line.trim().startsWith('#')
) {
return;
}
// examples:
// avp2|Aliens versus Predator 2 (2001)|gamespy1|port=27888
// avp2010|Aliens vs. Predator (2010)|valve|port=27015
let [game_type, game_name, game_protocol] = line.split('|');
server_types.push({
'name': game_name,
'type': game_type,
'protocol': game_protocol
});
});
res.json({
'result': 'ok',
'server_types': server_types
});
});
};