From c77ca299b017619506bdb0680e5809e36364520f Mon Sep 17 00:00:00 2001 From: CosminPerRam Date: Wed, 25 Sep 2024 18:56:15 +0300 Subject: [PATCH] feat: update Soldat protocol (#642) * feat: add Soldat support * feat: update protocol in games.js and CHANGELOG * feat: add gamemode in the raw object * remove debug console log * misspell * docs: add server config requirements * fix games list formatting issue * fix: players list --- CHANGELOG.md | 1 + GAMES_LIST.md | 5 ++++- lib/games.js | 9 ++++++--- protocols/index.js | 3 ++- protocols/soldat.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 protocols/soldat.js diff --git a/CHANGELOG.md b/CHANGELOG.md index aa0c6457..9340dc92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 5.X.Y * Feat: Replaced `punycode` package usage with `url.domainToASCII` (#630). * Feat: World of Padman (2007) - Added support (#636) +* Feat: Update Soldat protocol (#642) ## 5.1.3 * Fix: `Deus Ex` using the wrong protocol (#621) diff --git a/GAMES_LIST.md b/GAMES_LIST.md index 4502dca7..0dfa39f2 100644 --- a/GAMES_LIST.md +++ b/GAMES_LIST.md @@ -267,7 +267,7 @@ | sinepisodes | SiN Episodes | [Valve Protocol](#valve) | | sof | Soldier of Fortune | | | sof2 | Soldier of Fortune 2 | | -| soldat | Soldat | | +| soldat | Soldat | [Notes](#soldat) | | sotf | Sons Of The Forest | [Valve Protocol](#valve) | | soulmask | Soulmask | [Valve Protocol](#valve) | | spaceengineers | Space Engineers | [Valve Protocol](#valve) | @@ -490,3 +490,6 @@ EOS does not provide players data. ### Palworld Palworld support can be unstable, the devs mention the api is currently experimental. To query Palworld servers, the `RESTAPIEnabled` setting must be `True` in the configuration file, and you need to pass the `username` (currently always `admin`) and the `adminpassword` (from the server config) as the `password` parameter. + +### Soldat +Requires `Allow_Download` and `Logging` to be `1` in the server config. diff --git a/lib/games.js b/lib/games.js index 54e82f4c..1bec7153 100644 --- a/lib/games.js +++ b/lib/games.js @@ -2648,9 +2648,12 @@ export const games = { name: 'Soldat', release_year: 2002, options: { - port: 13073, - port_query_offset: 123, - protocol: 'ase' + port: 23073, + port_query_offset: 10, + protocol: 'soldat' + }, + extra: { + doc_notes: 'soldat' } }, sof: { diff --git a/protocols/index.js b/protocols/index.js index fa91dc5b..b1bf4851 100644 --- a/protocols/index.js +++ b/protocols/index.js @@ -62,6 +62,7 @@ import xonotic from './xonotic.js' import altvmp from './altvmp.js' import vintagestorymaster from './vintagestorymaster.js' import vintagestory from './vintagestory.js' +import soldat from './soldat.js' export { armagetron, ase, asa, assettocorsa, battlefield, buildandshoot, cs2d, discord, doom3, eco, epic, factorio, farmingsimulator, ffow, @@ -69,5 +70,5 @@ export { minecraftbedrock, minecraftvanilla, minetest, mumble, mumbleping, nadeo, openttd, palworld, quake1, quake2, quake3, rfactor, ragemp, samp, savage2, starmade, starsiege, teamspeak2, teamspeak3, terraria, tribes1, tribes1master, unreal2, ut3, valve, vcmp, ventrilo, warsow, eldewrito, beammpmaster, beammp, dayz, theisleevrima, xonotic, altvmp, vintagestorymaster, - vintagestory + vintagestory, soldat } diff --git a/protocols/soldat.js b/protocols/soldat.js new file mode 100644 index 00000000..413f62d1 --- /dev/null +++ b/protocols/soldat.js @@ -0,0 +1,45 @@ +import Core from './core.js' + +const extractValue = (text, regex, defaultValue, parser = (val) => val) => { + const match = text.match(regex) + return match ? parser(match[1] || defaultValue) : defaultValue +} + +export default class soldat extends Core { + async run (state) { + const data = await this.withTcp(async socket => { + return await this.tcpSend(socket, 'STARTFILES\r\nlogs/gamestat.txt\r\nENDFILES\r\n', (data) => { + const asString = data.toString() + if (asString.endsWith('\r\n') && !asString.endsWith('ENDFILES\r\n')) { + return undefined + } + return data + }) + }) + + const string = data.toString() + + state.numplayers = extractValue(string, /Players:\s*(\d+)/, 0, Number) + state.map = extractValue(string, /Map:\s*(.+)/, '') + + const lines = string.trim().split('\n') + const playersIndex = lines.findIndex(line => line.startsWith('Players list')) + + if (playersIndex > -1) { + for (let i = playersIndex + 1; i < lines.length - 1; i += 5) { + state.players.push({ + name: lines[i].trim(), + raw: { + kills: parseInt(lines[i + 1].trim()), + deaths: parseInt(lines[i + 2].trim()), + team: parseInt(lines[i + 3].trim()), + ping: parseInt(lines[i + 4].trim()) + } + }) + } + } + + state.raw.response = string + state.raw.gamemode = extractValue(string, /Gamemode:\s*(.+)/, '') + } +}