From a3fca5d590fd16994f0ab2352bec51b1c8e8f7e0 Mon Sep 17 00:00:00 2001 From: CosminPerRam Date: Tue, 6 May 2025 10:53:30 +0300 Subject: [PATCH] feat(protocol/sdtd): add optional more data via telnet (#697) * feat(protocol/sdtd): add optional more data via telnet * docs: update CHANGELOG.md * feat: some refactoring * feat: add hordeDay to raw * docs: mention what extra data it is * docs: formatting --- CHANGELOG.md | 1 + GAMES_LIST.md | 2 ++ protocols/sdtd.js | 55 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db4fa7f..c449624 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 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) +* Feat: 7 Days to Die get more optional Telnet data via an option (#693) ## 5.3.0 * Docs: Arma Reforger query setup note (#670, thanks @xCausxn) diff --git a/GAMES_LIST.md b/GAMES_LIST.md index b17c556..b1bb3f2 100644 --- a/GAMES_LIST.md +++ b/GAMES_LIST.md @@ -552,3 +552,5 @@ Requires usage of the status query server enabled in the config.txt. `status_ser 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. + +To fetch more info via telnet (game and mods versions, date and time), at the cost of a longer query time, set `moreData` to `true`. diff --git a/protocols/sdtd.js b/protocols/sdtd.js index d11e233..8c1f507 100644 --- a/protocols/sdtd.js +++ b/protocols/sdtd.js @@ -1,7 +1,10 @@ import Valve from './valve.js' import { Players } from '../lib/Results.js' -const playerLineRegex = /(?<=id=\d+,\s*)(?\S[^,]*)(?=,)/ +const playerRegex = /(?<=id=\d+,\s*)(?\S[^,]*)(?=,)/ +const gameVersionsRegex = /V \d+\.\d+(?: \(b\d+\))?/g +const modRegex = /^Mod\s+([^:]+):\s*([\d.]+)$/ +const dateTimeRegex = /Day\s+(\d+),\s*(\d{2}:\d{2})/ const sanitizeTelnetResponse = response => { return response @@ -25,7 +28,7 @@ export default class sdtd extends Valve { return } - if (!this.options.requestPlayers) { + if (!this.options.requestPlayers && !this.options.moreData) { return } @@ -37,7 +40,13 @@ export default class sdtd extends Valve { echoLines: 0 }) - await this.telnetCallPlayers(state) + if (this.options.requestPlayers) { + await this.telnetCallPlayers(state) + } + + if (this.options.moreData) { + await this.telnetMoreData(state) + } await this.telnetClose() } @@ -46,7 +55,7 @@ export default class sdtd extends Valve { const playersResponse = await this.telnetExecute('listplayers') state.players = new Players() for (const possiblePlayerLine of sanitizeTelnetResponse(playersResponse)) { - const match = possiblePlayerLine.match(playerLineRegex) + const match = possiblePlayerLine.match(playerRegex) const name = match?.groups?.name if (name) { @@ -59,4 +68,42 @@ export default class sdtd extends Valve { state.raw.telnetPlayersResponse = playersResponse } + + async telnetMoreData (state) { + const gettimeResponse = await this.telnetExecute('gettime') + const dateTime = sanitizeTelnetResponse(gettimeResponse)[0] || '' + const match = dateTime.match(dateTimeRegex) + if (match) { + state.raw.day = Number(match[1]) + state.raw.time = match[2] + state.raw.hordeDay = state.raw.day % 7 === 0 + } else { + state.raw.hordeDay = false + } + + state.raw.telnetGettimeResponse = gettimeResponse + + const versionResponse = await this.telnetExecute('version') + const versions = sanitizeTelnetResponse(versionResponse) + const gameVersions = versions[0] || '' + const gameVersionsMatch = gameVersions.match(gameVersionsRegex) + if (gameVersionsMatch) { + state.raw.gameVersion = gameVersionsMatch[0] + state.raw.compatibilityVersion = gameVersionsMatch[1] + } + + const mods = [] + for (const possibleMod of versions.slice(1)) { + const match = possibleMod.match(modRegex) + if (match) { + mods.push({ + name: match[1], + version: match[2] + }) + } + } + + state.raw.mods = mods + state.raw.telnetVersionResponse = versionResponse + } }