diff --git a/README.md b/README.md index 27b8c83..9575171 100644 --- a/README.md +++ b/README.md @@ -266,6 +266,7 @@ Games List * Star Wars: Republic Commando (swrc) * Starbound (starbound) * StarMade (starmade) +* Starsiege (2009) (starsiege) * Suicide Survival (suicidesurvival) * SWAT 4 (swat4) * Sven Coop (svencoop) @@ -416,6 +417,9 @@ as well: `--debug`, `--pretty`, `--socketTimeout 5000`, etc. Changelog --- +### 2.0.2 +* Added support for Starsiege 2009 (starsiege) + ### 2.0.1 * Updated reagme games list for 2.0 * Fixed csgo default port diff --git a/games.txt b/games.txt index 5914101..baa1409 100644 --- a/games.txt +++ b/games.txt @@ -249,6 +249,7 @@ swrc|Star Wars: Republic Commando|gamespy2|port=7777,port_query=11138 starbound|Starbound|valve|port=21025 starmade|StarMade|starmade|port=4242 +starsiege|Starsiege (2009)|starsiege|port=29001 suicidesurvival|Suicide Survival|valve|port=27015 swat4|SWAT 4|gamespy2|port=10480,port_query_offset=2 svencoop|Sven Coop|valve|port=27015 diff --git a/lib/HexUtil.js b/lib/HexUtil.js index 483f6b6..eda0540 100644 --- a/lib/HexUtil.js +++ b/lib/HexUtil.js @@ -4,6 +4,7 @@ class HexUtil { let hexLine = ''; let chrLine = ''; let out = ''; + out += "Buffer length: " + buffer.length + " bytes\n"; for(let i = 0; i < buffer.length; i++) { const sliced = buffer.slice(i,i+1); hexLine += sliced.toString('hex')+' '; diff --git a/package.json b/package.json index d7cdda5..99db4dd 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ ], "main": "lib/index.js", "author": "Michael Morrison", - "version": "2.0.1", + "version": "2.0.2", "repository": { "type": "git", "url": "https://github.com/sonicsnes/node-gamedig.git" diff --git a/protocols/starsiege.js b/protocols/starsiege.js new file mode 100644 index 0000000..680b83a --- /dev/null +++ b/protocols/starsiege.js @@ -0,0 +1,12 @@ +const Tribes1 = require('./tribes1'); + +class Starsiege extends Tribes1 { + constructor() { + super(); + this.encoding = 'latin1'; + this.requestByte = 0x72; + this.responseByte = 0x73; + } +} + +module.exports = Starsiege; diff --git a/protocols/tribes1.js b/protocols/tribes1.js index e503b8f..dfd546e 100644 --- a/protocols/tribes1.js +++ b/protocols/tribes1.js @@ -4,28 +4,75 @@ class Tribes1 extends Core { constructor() { super(); this.encoding = 'latin1'; + this.requestByte = 0x62; + this.responseByte = 0x63; + this.challenge = 0x01; } async run(state) { - const queryBuffer = Buffer.from('b++'); - const reader = await this.udpSend(queryBuffer,(buffer) => { + const query = Buffer.alloc(3); + query.writeUInt8(this.requestByte, 0); + query.writeUInt16LE(this.challenge, 1); + const reader = await this.udpSend(query,(buffer) => { const reader = this.reader(buffer); - const header = reader.string({length: 4}); - if (header !== 'c++b') { - this.debugLog('Header response does not match: ' + header); + const responseByte = reader.uint(1); + if (responseByte !== this.responseByte) { + this.debugLog('Unexpected response byte'); + return; + } + const challenge = reader.uint(2); + if (challenge !== this.challenge) { + this.debugLog('Unexpected challenge'); + return; + } + const requestByte = reader.uint(1); + if (requestByte !== this.requestByte) { + this.debugLog('Unexpected request byte'); return; } return reader; }); state.raw.gametype = this.readString(reader); + const isStarsiege2009 = state.raw.gametype === 'Starsiege'; state.raw.version = this.readString(reader); state.name = this.readString(reader); + + if (isStarsiege2009) { + state.password = !!reader.uint(1); + state.raw.dedicated = !!reader.uint(1); + state.raw.dropInProgress = !!reader.uint(1); + state.raw.gameInProgress = !!reader.uint(1); + state.raw.playerCount = reader.uint(4); + state.maxplayers = reader.uint(4); + state.raw.teamPlay = reader.uint(1); + state.map = this.readString(reader); + state.raw.cpuSpeed = reader.uint(2); + state.raw.factoryVeh = reader.uint(1); + state.raw.allowTecmix = reader.uint(1); + state.raw.spawnLimit = reader.uint(4); + state.raw.fragLimit = reader.uint(4); + state.raw.timeLimit = reader.uint(4); + state.raw.techLimit = reader.uint(4); + state.raw.combatLimit = reader.uint(4); + state.raw.massLimit = reader.uint(4); + state.raw.playersSent = reader.uint(4); + const teams = {1:'yellow', 2:'blue', 4:'red', 8:'purple'}; + while (!reader.done()) { + const player = {}; + player.name = this.readString(reader); + const teamId = reader.uint(1); + const team = teams[teamId]; + if (team) player.team = teams[teamId]; + } + return; + } + state.raw.dedicated = !!reader.uint(1); state.password = !!reader.uint(1); state.raw.playerCount = reader.uint(1); state.maxplayers = reader.uint(1); - state.raw.cpu = reader.uint(2); + state.raw.cpuSpeed = reader.uint(2); state.raw.mod = this.readString(reader); state.raw.type = this.readString(reader); state.map = this.readString(reader);