mirror of
https://github.com/skylord123/node-red-contrib-gamedig.git
synced 2026-05-24 08:03:17 -06:00
Compare commits
19 Commits
73850eaba8
...
2.1.2
| Author | SHA1 | Date | |
|---|---|---|---|
| facf77e1fa | |||
| 9d200b60a1 | |||
| f6078118c0 | |||
| e2e8119d7f | |||
| 09c763a605 | |||
| 67ad3de10b | |||
| 1885f7b8a1 | |||
| 072c85f78a | |||
| 36afa961a3 | |||
| a2a4f47604 | |||
| 6836b82b0b | |||
| 52bddfa603 | |||
| 4770cfdc53 | |||
| 3fdd0c1539 | |||
| b8ec64d854 | |||
| 560d24f068 | |||
| 539f7fe21f | |||
| 6f806ca727 | |||
| d3c076bb6a |
@@ -1,3 +1,6 @@
|
||||
# ide
|
||||
.idea
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
@@ -1,2 +1,23 @@
|
||||
# node-red-contrib-gamedig
|
||||
Query for the status of any game server using Node Red
|
||||
|
||||
Query for the status 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).
|
||||
|
||||
### Usage Examples
|
||||
#### Inserting query data into InfluxDB and using Grafana to view results
|
||||

|
||||
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/
|
||||
|
||||
### 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!
|
||||
|
||||
### License
|
||||
This project is licensed under the MIT License. Check [here](LICENSE) for more information.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 256 B |
Binary file not shown.
|
After Width: | Height: | Size: 913 B |
Generated
+1152
File diff suppressed because it is too large
Load Diff
+8
-7
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"name": "node-red-contrib-gamedig",
|
||||
"version": "0.1.0",
|
||||
"version": "2.1.2",
|
||||
"description": "Query for the status of any game server using node-red",
|
||||
"main": "query-game-server/query-game-server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/skylord123/node-red-contrib-gamedig.git"
|
||||
@@ -16,12 +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"
|
||||
],
|
||||
"node-red": {
|
||||
"nodes": {
|
||||
"query-game-server": "query-game-server/query-game-server.js"
|
||||
"query-game-server": "query-game-server.js"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"gamedig": "^2.0.13"
|
||||
"gamedig": "^4.0.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('query-game-server', {
|
||||
category: 'advanced',
|
||||
color: '#a6bbcf',
|
||||
defaults: {
|
||||
name: { value: '' },
|
||||
server_type: { value: '' },
|
||||
host: { value: '' },
|
||||
port: { value: '' },
|
||||
halt_if: { value: '' },
|
||||
max_attempts: { value: '' },
|
||||
socket_timeout: { value: '' },
|
||||
attempt_timeout: { value: '' },
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
icon: "icons/server.png",
|
||||
paletteLabel: "Query Game Server",
|
||||
label: function() {
|
||||
if(this.name){
|
||||
return this.name;
|
||||
}
|
||||
|
||||
if(this.host) {
|
||||
return (this.server_type ? this.server_type : 'Query') + ': ' + this.host + (this.port ? ":" + this.port : '');
|
||||
}
|
||||
|
||||
return 'Query Game Server';
|
||||
},
|
||||
oneditprepare: function() {
|
||||
$.getJSON('/gamedig/types', function(data) {
|
||||
let html = '<table>' +
|
||||
'<thead id="query-game-server-types-table"><tr><td><strong>Type</strong></td><td><strong>Name</strong></td><td><strong>Protocol</strong></td></tr></thead>' +
|
||||
'<tbody id="query-game-server-type-rows">';
|
||||
for(let game of data) {
|
||||
html += "<tr class=\"query-game-server-type-row\">" +
|
||||
"<td>"+game['type']+"</td>" +
|
||||
"<td>"+game['name']+"</td>" +
|
||||
"<td>"+game['protocol']+"</td>" +
|
||||
"</tr>";
|
||||
}
|
||||
html += '</tbody>' +
|
||||
'</table>';
|
||||
$("#query-game-server-types").html(html);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<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">
|
||||
</div>
|
||||
|
||||
<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">
|
||||
</div>
|
||||
<div style="margin-left: 105px;width: 50%;margin-bottom: 10px;margin-top: -10px;">
|
||||
View server types <a href="#gamdig-types" style="color:#0000EE;text-decoration: underline;">below</a>.
|
||||
</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" placeholder="msg.host" />
|
||||
</div>
|
||||
<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-ethernet"></i> Port</label>
|
||||
<input type="text" id="node-input-port" placeholder="msg.host" />
|
||||
</div>
|
||||
<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">
|
||||
<label for="node-input-halt_if"><i class="fa fa-hand-paper-o"></i> Halt If</label>
|
||||
<select type="text" id="node-input-halt_if">
|
||||
<option value="">Do not halt</option>
|
||||
<option value="online">Online</option>
|
||||
<option value="offline">Offline</option>
|
||||
</select>
|
||||
</div>
|
||||
<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-cogs"></i> Max Attempts</label>
|
||||
<input type="text" id="node-input-max_attempts" placeholder="1" />
|
||||
</div>
|
||||
<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-cogs"></i> Socket Timeout</label>
|
||||
<input type="text" id="node-input-socket_timeout" placeholder="2000" />
|
||||
</div>
|
||||
<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="fas fa-cogs"></i> Attempt Timeout</label>
|
||||
<input type="text" id="node-input-attempt_timeout" placeholder="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>
|
||||
|
||||
<h3 id="gamdig-types">Server Types</h3>
|
||||
<p>
|
||||
Search available types below.<br>
|
||||
You can also view the list <a href="https://github.com/gamedig/node-gamedig#games-list" target="_blank" style="color:#0000EE;text-decoration: underline;">here</a>.
|
||||
</p>
|
||||
|
||||
<div class="row">
|
||||
<input type="text" id="query-game-server-types-search" placeholder="Search types.." style="margin-bottom: 10px;" />
|
||||
</div>
|
||||
<div id="query-game-server-types"></div>
|
||||
<script type="text/javascript">
|
||||
$("#query-game-server-types-search").on("input", function(e) {
|
||||
let value = $(this).val();
|
||||
if(value.length) {
|
||||
$(".query-game-server-type-row").each(function(i, elem){
|
||||
console.log('yay', $(elem).text(), value, $(elem).text().indexOf(value));
|
||||
if($(elem).text().toLowerCase().indexOf(value.toLowerCase()) > -1) {
|
||||
$(elem).show();
|
||||
} else {
|
||||
$(elem).hide();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
$(".query-game-server-type-row").show();
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<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.</p>
|
||||
|
||||
<h3>Inputs</h3>
|
||||
<dl class="message-properties">
|
||||
<dt class="optional">
|
||||
msg.server_type <span class="property-type">string | null</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 class="optional">
|
||||
msg.host <span class="property-type">string | null</span>
|
||||
</dt>
|
||||
<dd>Server IP/Hostname. Ignored if configured on the node.</dd>
|
||||
|
||||
<dt class="optional">
|
||||
msg.port <span class="property-type">integer | null</span>
|
||||
</dt>
|
||||
<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 | null</span>
|
||||
</dt>
|
||||
<dd>Number of attempts to query server in case of failure. Ignored if configured on the node.</dd>
|
||||
|
||||
<dt class="optional">
|
||||
msg.socket_timeout <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. 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>
|
||||
</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>.</dd>
|
||||
|
||||
<dt>
|
||||
msg.data <span class="property-type">object</span>
|
||||
</dt>
|
||||
<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>
|
||||
<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.max_attempts <span class="property-type">integer</span>
|
||||
</dt>
|
||||
<dd>Number of attempts to query server in case of failure. Ignored if configured on the node.</dd>
|
||||
|
||||
<dt>
|
||||
msg.socket_timeout <span class="property-type">integer</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. Ignored if configured on the node.</dd>
|
||||
|
||||
<dt>
|
||||
msg.attempt_timeout <span class="property-type">integer</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>
|
||||
</dl>
|
||||
</script>
|
||||
@@ -0,0 +1,113 @@
|
||||
module.exports = function(RED) {
|
||||
const gamedig = require('gamedig');
|
||||
const fs = require('fs');
|
||||
|
||||
function QueryGameServer(config) {
|
||||
RED.nodes.createNode(this, config);
|
||||
var node = this;
|
||||
this.server_type = config.server_type;
|
||||
this.host = config.host;
|
||||
this.port = config.port;
|
||||
this.halt_if = config.halt_if;
|
||||
this.max_attempts = config.max_attempts || 1;
|
||||
this.socket_timeout = config.socket_timeout || 2000;
|
||||
this.attempt_timeout = config.attempt_timeout || 10000;
|
||||
node.on('input', function(msg) {
|
||||
if(node.server_type) {
|
||||
msg.server_type = node.server_type;
|
||||
}
|
||||
|
||||
if(node.host) {
|
||||
msg.host = node.host;
|
||||
}
|
||||
if(!msg.host) {
|
||||
node.error("msg.host missing from input.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(node.port) {
|
||||
msg.port = node.port;
|
||||
}
|
||||
|
||||
if(node.halt_if) {
|
||||
msg.halt_if = node.halt_if;
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
.then(function(state) {
|
||||
msg.payload = 'online';
|
||||
msg.data = state;
|
||||
if (msg.payload === msg.halt_if) {
|
||||
return null;
|
||||
}
|
||||
node.status({fill:"green",shape:"dot",text: 'Online ' + msg.data.players.length + ' players' });
|
||||
node.send(msg);
|
||||
}).catch(function(error) {
|
||||
msg.payload = 'offline';
|
||||
msg.data = {
|
||||
'error': error
|
||||
};
|
||||
if (msg.payload === msg.halt_if) {
|
||||
return null;
|
||||
}
|
||||
node.status({fill:"red", shape:"dot", text: 'Offline'});
|
||||
node.send(msg);
|
||||
});
|
||||
});
|
||||
}
|
||||
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')
|
||||
results = [];
|
||||
|
||||
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('|');
|
||||
results.push({
|
||||
'name': game_type,
|
||||
'type': game_type,
|
||||
'protocol': game_protocol
|
||||
});
|
||||
});
|
||||
res.json(results);
|
||||
});
|
||||
};
|
||||
@@ -1,47 +0,0 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('query-game-server', {
|
||||
category: 'advanced',
|
||||
color: '#a6bbcf',
|
||||
defaults: {
|
||||
name: { value: '' },
|
||||
type: { value: '', required: true },
|
||||
host: { value: '', type: 'server', required: true },
|
||||
port: { value: '' },
|
||||
halt_if: { value: '' }
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
icon: "icons/down_arrow.png",
|
||||
label: function() {
|
||||
return this.name || 'query-game-server' || `query-game-server: ${this.server}`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-template-name="query-game-server">
|
||||
<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">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-type"><i class="fa fa-cube"></i> Type</label>
|
||||
<input type="text" id="node-input-type">
|
||||
<span>Check <a href="https://github.com/sonicsnes/node-gamedig">node-gamedig</a> README for supported types</span>
|
||||
</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" />
|
||||
<span>Host without port</span>
|
||||
</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" />
|
||||
<span>Query port for the server (not always the same as the join port)</span>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-halt_if"><i class="fa fa-hand-paper-o"></i> Halt If</label>
|
||||
<input type="text" id="node-input-halt_if" placeholder="off"/>
|
||||
<span>Enter "online" or "offline" (without quotes) to filter out the state you don't want.</span>
|
||||
</div>
|
||||
</script>
|
||||
@@ -1,39 +0,0 @@
|
||||
module.exports = function(RED) {
|
||||
var Gamedig = require('gamedig');
|
||||
function queryGameServer(config) {
|
||||
RED.nodes.createNode(this, config);
|
||||
var node = this;
|
||||
node.on('input', function(msg) {
|
||||
var serverInfo = {
|
||||
'type': this.nodeConfig.type,
|
||||
'host': this.nodeConfig.host
|
||||
};
|
||||
|
||||
if(this.nodeConfig.port) {
|
||||
serverInfo['port'] = this.nodeConfig.port;
|
||||
}
|
||||
|
||||
Gamedig.query(serverInfo)
|
||||
.then(function(state) {
|
||||
msg.payload = 'online';
|
||||
msg.data = state;
|
||||
const shouldHaltIfState = this.nodeConfig.halt_if && ('online' === this.nodeConfig.halt_if);
|
||||
if (shouldHaltIfState) {
|
||||
return null;
|
||||
}
|
||||
node.send(msg);
|
||||
}).catch(function(error) {
|
||||
msg.payload = 'offline';
|
||||
msg.data = {
|
||||
'error': error
|
||||
};
|
||||
const shouldHaltIfState = this.nodeConfig.halt_if && ('offline' === this.nodeConfig.halt_if);
|
||||
if (shouldHaltIfState) {
|
||||
return null;
|
||||
}
|
||||
node.send(msg);
|
||||
});
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("query-game-server", queryGameServer);
|
||||
};
|
||||
Reference in New Issue
Block a user