mirror of
https://github.com/tribufu/node-gamedig
synced 2026-05-06 07:07:33 +00:00
feat(protocols/sdtd): add telnet support for players (#692)
* feat(protocols/sdtd): add telnet support * docs: update CHANGELOG.md
This commit is contained in:
parent
4db8a473ff
commit
71ce074866
9 changed files with 159 additions and 5 deletions
|
|
@ -2,6 +2,7 @@
|
|||
## To Be Released...
|
||||
## 5.X.Y
|
||||
* Fix: HTTP requests would end up making more retries than needed due to got's internal retry mechanism (#690, thanks @RattleSN4K3)
|
||||
* Feat: 7 Days to Die add Telnet support for missing player names (#692, thanks @jammsen)
|
||||
|
||||
## 5.3.0
|
||||
* Docs: Arma Reforger query setup note (#670, thanks @xCausxn)
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@
|
|||
| rust | Rust | [Valve Protocol](#valve) |
|
||||
| s2ats | Savage 2: A Tortured Soul | |
|
||||
| satisfactory | Satisfactory | [Notes](#satisfactory) |
|
||||
| sdtd | 7 Days to Die | [Valve Protocol](#valve) |
|
||||
| sdtd | 7 Days to Die | [Notes](#sdtd), [Valve Protocol](#valve) |
|
||||
| serioussam | Serious Sam | |
|
||||
| serioussam2 | Serious Sam 2 | |
|
||||
| shatteredhorizon | Shattered Horizon | [Valve Protocol](#valve) |
|
||||
|
|
@ -547,3 +547,8 @@ Does not provide players names, using a plugin like this [one](https://github.co
|
|||
|
||||
### <a name="aosc-buildandshoot"></a>Ace of Spades / Build and Shoot
|
||||
Requires usage of the status query server enabled in the config.txt. `status_server.enabled` to `true`
|
||||
|
||||
### <a name="sdtd"></a>7 Days to Die
|
||||
Does not provide player names but can be provided via telnet commands, to use these make sure in `serverconfig.xml` you
|
||||
have configured `TelnetEnabled` to `true`, `TelnetPort` and `TelnetPassword` and pass `telnetPort` and `telnetPassword`
|
||||
to the query parameters.
|
||||
|
|
|
|||
|
|
@ -2602,10 +2602,11 @@ export const games = {
|
|||
options: {
|
||||
port: 26900,
|
||||
port_query_offset: 1,
|
||||
protocol: 'valve'
|
||||
protocol: 'sdtd'
|
||||
},
|
||||
extra: {
|
||||
old_id: '7d2d'
|
||||
old_id: '7d2d',
|
||||
doc_notes: 'sdtd'
|
||||
}
|
||||
},
|
||||
satisfactory: {
|
||||
|
|
|
|||
62
package-lock.json
generated
62
package-lock.json
generated
|
|
@ -16,6 +16,7 @@
|
|||
"long": "5.3.2",
|
||||
"minimist": "1.2.8",
|
||||
"seek-bzip": "2.0.0",
|
||||
"telnet-client": "2.2.5",
|
||||
"varint": "6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -1020,6 +1021,14 @@
|
|||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/emitter-component": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz",
|
||||
"integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/es-abstract": {
|
||||
"version": "1.22.2",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz",
|
||||
|
|
@ -2444,6 +2453,11 @@
|
|||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/net": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/net/-/net-1.0.2.tgz",
|
||||
"integrity": "sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ=="
|
||||
},
|
||||
"node_modules/normalize-url": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz",
|
||||
|
|
@ -2964,6 +2978,14 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/stream": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz",
|
||||
"integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==",
|
||||
"dependencies": {
|
||||
"emitter-component": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
|
|
@ -3118,6 +3140,19 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/telnet-client": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/telnet-client/-/telnet-client-2.2.5.tgz",
|
||||
"integrity": "sha512-X5xEkmKHgpCpngnH7QnkFX87UyBErauHsjzlCGVp87MbhnmHoaAeacuALGfqovHh3MXAfrpPs+g30PgyBpy8Jw==",
|
||||
"dependencies": {
|
||||
"net": "^1.0.2",
|
||||
"stream": "^0.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/kozjak"
|
||||
}
|
||||
},
|
||||
"node_modules/text-table": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||
|
|
@ -3970,6 +4005,11 @@
|
|||
"esutils": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"emitter-component": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz",
|
||||
"integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw=="
|
||||
},
|
||||
"es-abstract": {
|
||||
"version": "1.22.2",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz",
|
||||
|
|
@ -4997,6 +5037,11 @@
|
|||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"net": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/net/-/net-1.0.2.tgz",
|
||||
"integrity": "sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ=="
|
||||
},
|
||||
"normalize-url": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz",
|
||||
|
|
@ -5353,6 +5398,14 @@
|
|||
"object-inspect": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"stream": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz",
|
||||
"integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==",
|
||||
"requires": {
|
||||
"emitter-component": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
|
|
@ -5470,6 +5523,15 @@
|
|||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true
|
||||
},
|
||||
"telnet-client": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/telnet-client/-/telnet-client-2.2.5.tgz",
|
||||
"integrity": "sha512-X5xEkmKHgpCpngnH7QnkFX87UyBErauHsjzlCGVp87MbhnmHoaAeacuALGfqovHh3MXAfrpPs+g30PgyBpy8Jw==",
|
||||
"requires": {
|
||||
"net": "^1.0.2",
|
||||
"stream": "^0.0.2"
|
||||
}
|
||||
},
|
||||
"text-table": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"long": "5.3.2",
|
||||
"minimist": "1.2.8",
|
||||
"seek-bzip": "2.0.0",
|
||||
"telnet-client": "2.2.5",
|
||||
"varint": "6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import Logger from '../lib/Logger.js'
|
|||
import DnsResolver from '../lib/DnsResolver.js'
|
||||
import { Results } from '../lib/Results.js'
|
||||
import Promises from '../lib/Promises.js'
|
||||
import { Telnet } from 'telnet-client'
|
||||
|
||||
let uid = 0
|
||||
|
||||
|
|
@ -27,6 +28,8 @@ export default class Core extends EventEmitter {
|
|||
this.udpSocket = null
|
||||
this.shortestRTT = 0
|
||||
this.usedTcp = false
|
||||
|
||||
this.telnetClient = new Telnet()
|
||||
}
|
||||
|
||||
// Runs a single attempt with a timeout and cleans up afterward
|
||||
|
|
@ -336,4 +339,23 @@ export default class Core extends EventEmitter {
|
|||
requestPromise?.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
async telnetConnect (params) {
|
||||
await this.telnetClient.connect({
|
||||
timeout: 2000,
|
||||
execTimeout: 2000,
|
||||
host: this.options.host,
|
||||
debug: this.debugEnabled,
|
||||
...params
|
||||
})
|
||||
}
|
||||
|
||||
async telnetExecute (command) {
|
||||
return await this.telnetClient.exec(command)
|
||||
}
|
||||
|
||||
async telnetClose () {
|
||||
await this.telnetClient.end()
|
||||
await this.telnetClient.destroy()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,11 +72,12 @@ import xonotic from './xonotic.js'
|
|||
import altvmp from './altvmp.js'
|
||||
import vintagestorymaster from './vintagestorymaster.js'
|
||||
import vintagestory from './vintagestory.js'
|
||||
import sdtd from './sdtd.js'
|
||||
|
||||
export {
|
||||
armagetron, ase, asa, assettocorsa, battlefield, brokeprotocol, brokeprotocolmaster, buildandshoot, cs2d, discord, doom3, eco, epic, factorio, farmingsimulator, ffow,
|
||||
fivem, gamespy1, gamespy2, gamespy3, geneshift, goldsrc, gtasao, hawakening, hawakeningmaster, hexen2, jc2mp, kspdmp, mafia2mp, mafia2online, minecraft,
|
||||
minecraftbedrock, minecraftvanilla, minetest, mumble, mumbleping, nadeo, openttd, palworld, quake1, quake2, quake3, renegadex, renegadexmaster, renown, rfactor, ragemp, samp,
|
||||
satisfactory, soldat, savage2, starmade, starsiege, teamspeak2, teamspeak3, terraria, toxikk, tribes1, tribes1master, unreal2, ut3, valve,
|
||||
vcmp, ventrilo, warsow, eldewrito, beammpmaster, beammp, dayz, theisleevrima, xonotic, altvmp, vintagestorymaster, vintagestory
|
||||
vcmp, ventrilo, warsow, eldewrito, beammpmaster, beammp, dayz, theisleevrima, xonotic, altvmp, vintagestorymaster, vintagestory, sdtd
|
||||
}
|
||||
|
|
|
|||
61
protocols/sdtd.js
Normal file
61
protocols/sdtd.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import Valve from './valve.js'
|
||||
import { Players } from '../lib/Results.js'
|
||||
|
||||
const playerLineRegex = /(?<=id=\d+,\s*)(?<name>\S[^,]*)(?=,)/
|
||||
|
||||
const sanitizeTelnetResponse = response => {
|
||||
return response
|
||||
.split(/\r?\n/)
|
||||
.map(l => l.replace(/\r$/, '').trim())
|
||||
.filter(l => l.length > 0)
|
||||
}
|
||||
|
||||
export default class sdtd extends Valve {
|
||||
async run (state) {
|
||||
await super.run(state)
|
||||
await this.telnetCalls(state)
|
||||
}
|
||||
|
||||
async telnetCalls (state) {
|
||||
const telnetPort = this.options.telnetPort
|
||||
const telnetPassword = this.options.telnetPassword
|
||||
|
||||
if (!telnetPort || !telnetPassword) {
|
||||
this.logger.debug('No telnet args given, skipping.')
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.options.requestPlayers) {
|
||||
return
|
||||
}
|
||||
|
||||
await this.telnetConnect({
|
||||
port: telnetPort,
|
||||
password: telnetPassword,
|
||||
passwordPrompt: /Please enter password:/i,
|
||||
shellPrompt: /\r\n$/
|
||||
})
|
||||
|
||||
await this.telnetCallPlayers(state)
|
||||
|
||||
await this.telnetClose()
|
||||
}
|
||||
|
||||
async telnetCallPlayers (state) {
|
||||
const playersResponse = await this.telnetExecute('listplayers')
|
||||
state.players = new Players()
|
||||
for (const possiblePlayerLine of sanitizeTelnetResponse(playersResponse)) {
|
||||
const match = possiblePlayerLine.match(playerLineRegex)
|
||||
|
||||
const name = match?.groups?.name
|
||||
if (name) {
|
||||
state.players.push({
|
||||
name,
|
||||
responseLine: possiblePlayerLine
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
state.raw.telnetPlayersResponse = playersResponse
|
||||
}
|
||||
}
|
||||
|
|
@ -55,7 +55,7 @@ Object.entries(sortedGames).forEach(([id, game]) => {
|
|||
if (game?.extra?.doc_notes) {
|
||||
notes.push('[Notes](#' + game.extra.doc_notes + ')')
|
||||
}
|
||||
if (['valve', 'dayz'].includes(game.options.protocol)) {
|
||||
if (['valve', 'dayz', 'sdtd'].includes(game.options.protocol)) {
|
||||
notes.push('[Valve Protocol](#valve)')
|
||||
}
|
||||
if (['epic', 'asa', 'theisleevrima', 'renown'].includes(game.options.protocol)) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue