Skip to content

Commit

Permalink
feat: Add version as a top level field (#532)
Browse files Browse the repository at this point in the history
* add top level version on existing entries

* start adding version on new protocols WIP

* add version to more games

* more games with version

* add more games

* more version

* even more games with version

* add 'delete state.raw.version'

* fix delete version

* Update CHANGELOG.md

* add version in Results.js

* more games

* add new game

* more games

* add version on README

* add new game

* other game

* new game

* add unreal2 version

* add ventrilo version

* add eldewrito eldewrito

* add beammp version

* fix starmade version

* add new version in samp protocol

* docs: tweak the changelog line a bit

---------

Co-authored-by: CosminPerRam <[email protected]>
  • Loading branch information
podrivo and CosminPerRam authored Feb 24, 2024
1 parent fb6a5a1 commit a7c3b54
Show file tree
Hide file tree
Showing 40 changed files with 52 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

## To Be Released...
## 5.0.0-beta.3
* Added a new stabilized field `version` in the query response (By @podrivo #532)
* Euro Truck Simulator 2 (2012) - Added support (By @podrivo #523)
* Eco - Fixed querying servers using reverse queries and player names (By @Vito0912 #526)
* Factorio (2016) - Added support (By @Vito0912 #527)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ The returned state object will contain the following keys:
| **connect** | string | This will typically include the game's `IP:PORT`. The port will reflect the server's game port, even if your request specified the game's query port in the request. For some games, this may be a server ID or connection URL if an IP:PORT is not appropriate for end-users. |
| **ping** | number | Round trip time to the server (in milliseconds). Note that this is not the RTT of an ICMP echo, as ICMP packets are often blocked by NATs and node has poor support for raw sockets. This value is derived from the RTT of one of the query packets, which is usually quite accurate, but may add a bit due to server lag. |
| **queryPort** | number | Indicates on which port the query was done on, 0 if this is not applicable. |
| **version** | string | Game version that is running on the server. Empty if not present. |
| **raw** | object | Contains all information received from the server in a disorganized format. |

Note that `raw` (or **unstable**) objects contents MAY change on a per-protocol basis between GameDig patch releases (although not typical).
Expand Down
1 change: 1 addition & 0 deletions lib/Results.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class Results {
password = false

raw = {}
version = ''

maxplayers = 0
numplayers = 0
Expand Down
2 changes: 1 addition & 1 deletion protocols/armagetron.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default class armagetron extends Core {
state.numplayers = this.readUInt(reader)
state.raw.versionmin = this.readUInt(reader)
state.raw.versionmax = this.readUInt(reader)
state.raw.version = this.readString(reader)
state.version = this.readString(reader)
state.maxplayers = this.readUInt(reader)

const players = this.readString(reader)
Expand Down
5 changes: 5 additions & 0 deletions protocols/asa.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ export default class asa extends Epic {
this.clientSecret = 'PP5UGxysEieNfSrEicaD1N2Bb3TdXuD7xHYcsdUHZ7s'
this.deploymentId = 'ad9a8feffb3b4b2ca315546f038c3ae2'
}

async run(state) {
await super.run(state)
state.version = state.raw.attributes.BUILDID_s + '.' + state.raw.attributes.MINORBUILDID_s
}
}
2 changes: 1 addition & 1 deletion protocols/ase.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default class ase extends Core {
state.name = this.readString(reader)
state.raw.gametype = this.readString(reader)
state.map = this.readString(reader)
state.raw.version = this.readString(reader)
state.version = this.readString(reader)
state.password = this.readString(reader) === '1'
state.numplayers = parseInt(this.readString(reader))
state.maxplayers = parseInt(this.readString(reader))
Expand Down
1 change: 1 addition & 0 deletions protocols/assettocorsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default class assettocorsa extends Core {
state.gamePort = serverInfo.port
state.raw.carInfo = carInfo.Cars
state.raw.serverInfo = serverInfo
state.version = state.raw.serverInfo.poweredBy

for (const car of carInfo.Cars) {
if (car.IsConnected) {
Expand Down
2 changes: 1 addition & 1 deletion protocols/battlefield.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export default class battlefield extends Core {
{
const data = await this.query(socket, ['version'])
data.shift()
state.raw.version = data.shift()
state.version = data.shift()
}

{
Expand Down
1 change: 1 addition & 0 deletions protocols/beammp.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ export default class beammp extends Core {
})

state.raw = server
if ('version' in state.raw) state.version = state.raw.version
}
}
1 change: 1 addition & 0 deletions protocols/doom3.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default class doom3 extends Core {
let reader = this.reader(body)
const protoVersion = reader.uint(4)
state.raw.protocolVersion = (protoVersion >> 16) + '.' + (protoVersion & 0xffff)
state.version = state.raw.protocolVersion

// some doom implementations send us a packet size here, some don't (etqw does this)
// we can tell if this is a packet size, because the third and fourth byte will be 0 (no packets are that massive)
Expand Down
1 change: 1 addition & 0 deletions protocols/eco.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ export default class eco extends Core {
state.gamePort = serverInfo.GamePort
state.players = serverInfo.OnlinePlayersNames?.map(name => ({ name, raw: {} })) || []
state.raw = serverInfo
state.version = state.raw.Version
}
}
1 change: 1 addition & 0 deletions protocols/eldewrito.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ export default class eldewrito extends Core {
state.connect = this.options.address + ':' + json.port

state.raw = json
if ('eldewritoVersion' in state.raw) state.version = state.raw.eldewritoVersion
}
}
1 change: 1 addition & 0 deletions protocols/factorio.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ export default class factorio extends Core {
state.players = players.map(player => ({ name: player, raw: {} }))

state.raw = serverInfo
state.version = state.raw.application_version.game_version + '.' + state.raw.application_version.build_version
}
}
2 changes: 1 addition & 1 deletion protocols/farmingsimulator.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default class farmingsimulator extends Core {
}
})

state.raw.version = serverInfo.attr('version')
state.version = serverInfo.attr('version')

// TODO: Add state.raw
}
Expand Down
2 changes: 1 addition & 1 deletion protocols/ffow.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default class ffow extends valve {
state.raw.mod = reader.string()
state.raw.gamemode = reader.string()
state.raw.description = reader.string()
state.raw.version = reader.string()
state.version = reader.string()
state.gamePort = reader.uint(2)
state.numplayers = reader.uint(1)
state.maxplayers = reader.uint(1)
Expand Down
1 change: 1 addition & 0 deletions protocols/fivem.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default class fivem extends quake2 {
responseType: 'json'
})
state.raw.info = json
if ('version' in state.raw.info) state.version = state.raw.info.version
}

{
Expand Down
1 change: 1 addition & 0 deletions protocols/gamespy1.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export default class gamespy1 extends Core {
}

state.numplayers = state.players.length
state.version = state.raw.gamever
}

async sendPacket (type) {
Expand Down
1 change: 1 addition & 0 deletions protocols/gamespy2.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default class gamespy2 extends Core {
if (this.trueTest(state.raw.password)) state.password = true
if ('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers)
if ('hostport' in state.raw) state.gamePort = parseInt(state.raw.hostport)
if ('gamever' in state.raw) state.version = state.raw.gamever
}

// Parse players
Expand Down
1 change: 1 addition & 0 deletions protocols/gamespy3.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export default class gamespy3 extends Core {
if (state.raw.password === '1') state.password = true
if ('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers)
if ('hostport' in state.raw) state.gamePort = parseInt(state.raw.hostport)
if ('gamever' in state.raw) state.version = state.raw.gamever

if ('' in state.raw.playerTeamInfo) {
for (const playerInfo of state.raw.playerTeamInfo['']) {
Expand Down
2 changes: 1 addition & 1 deletion protocols/geneshift.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ export default class geneshift extends Core {
state.raw.friendlyfire = !!parseInt(found[16])
state.raw.mercs = !!parseInt(found[17])
// fields[18] is unknown? listen server?
state.raw.version = found[19]
state.version = found[19]
}
}
2 changes: 2 additions & 0 deletions protocols/jc2mp.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ export default class jc2mp extends gamespy3 {

async run (state) {
await super.run(state)

state.version = state.raw.version
}
}
1 change: 1 addition & 0 deletions protocols/mafia2mp.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default class mafia2mp extends Core {
state.numplayers = parseInt(this.readString(reader))
state.maxplayers = parseInt(this.readString(reader))
state.raw.gamemode = this.readString(reader)
state.version = state.raw.gamemode
state.password = !!reader.uint(1)
state.gamePort = this.options.port - 1

Expand Down
2 changes: 2 additions & 0 deletions protocols/minecraft.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export default class minecraft extends Core {
if (vanillaState.maxplayers) state.maxplayers = vanillaState.maxplayers
if (vanillaState.players.length) state.players = vanillaState.players
if (vanillaState.ping) this.registerRtt(vanillaState.ping)
if (vanillaState.raw.version) state.version = vanillaState.raw.version.name
}
if (gamespyState) {
if (gamespyState.name) state.name = gamespyState.name
Expand All @@ -93,6 +94,7 @@ export default class minecraft extends Core {
if (bedrockState.maxplayers) state.maxplayers = bedrockState.maxplayers
if (bedrockState.map) state.map = bedrockState.map
if (bedrockState.ping) this.registerRtt(bedrockState.ping)
if (bedrockState.raw.mcVersion) state.version = bedrockState.raw.mcVersion
}
// remove dupe spaces from name
state.name = state.name.replace(/\s+/g, ' ')
Expand Down
1 change: 1 addition & 0 deletions protocols/minecraftbedrock.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export default class minecraftbedrock extends Core {
state.name = split.shift()
state.raw.protocolVersion = split.shift()
state.raw.mcVersion = split.shift()
state.version = state.raw.mcVersion
state.numplayers = parseInt(split.shift())
state.maxplayers = parseInt(split.shift())
if (split.length) state.raw.serverId = split.shift()
Expand Down
1 change: 1 addition & 0 deletions protocols/mumbleping.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default class mumbleping extends Core {
state.raw.versionMajor = reader.uint(1)
state.raw.versionMinor = reader.uint(1)
state.raw.versionPatch = reader.uint(1)
state.version = state.raw.versionMajor + '.' + state.raw.versionMinor + '.' + state.raw.versionPatch
reader.skip(8)
state.numplayers = reader.uint(4)
state.maxplayers = reader.uint(4)
Expand Down
2 changes: 1 addition & 1 deletion protocols/openttd.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default class openttd extends Core {
}

state.name = reader.string()
state.raw.version = reader.string()
state.version = reader.string()

state.raw.language = this.decode(
reader.uint(1),
Expand Down
1 change: 1 addition & 0 deletions protocols/palworld.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export default class palworld extends Epic {
await super.run(state)
state.name = state.raw.attributes.NAME_s
state.numplayers = state.raw.attributes.PLAYERS_l
state.version = state.raw.attributes.VERSION_S
}
}
5 changes: 5 additions & 0 deletions protocols/quake1.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ export default class quake1 extends quake2 {
this.responseHeader = 'n'
this.isQuake1 = true
}

async run(state) {
await super.run(state)
if ('*version' in state.raw) state.version = state.raw['*version']
}
}
1 change: 1 addition & 0 deletions protocols/quake2.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export default class quake2 extends Core {
if ('sv_hostname' in state.raw) state.name = state.raw.sv_hostname
if ('hostname' in state.raw) state.name = state.raw.hostname
if ('clients' in state.raw) state.numplayers = state.raw.clients
if ('iv' in state.raw) state.version = state.raw.iv
else state.numplayers = state.players.length + state.bots.length
}
}
1 change: 1 addition & 0 deletions protocols/quake3.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default class quake3 extends quake2 {
state.name = this.stripColors(state.name)
for (const key of Object.keys(state.raw)) {
state.raw[key] = this.stripColors(state.raw[key])
if ('version' in state.raw) state.version = state.raw.version
}
for (const player of state.players) {
player.name = this.stripColors(player.name)
Expand Down
2 changes: 1 addition & 1 deletion protocols/rfactor.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default class rfactor extends Core {
state.raw.region = reader.uint(2)
state.raw.ip = reader.part(4)
state.raw.size = reader.uint(2)
state.raw.version = reader.uint(2)
state.version = reader.uint(2)
state.raw.versionRaceCast = reader.uint(2)
state.gamePort = reader.uint(2)
state.raw.queryPort = reader.uint(2)
Expand Down
3 changes: 2 additions & 1 deletion protocols/samp.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default class samp extends Core {
const reader = await this.sendPacket('i')
if (this.isVcmp) {
const consumed = reader.part(12)
state.raw.version = this.reader(consumed).string()
state.version = this.reader(consumed).string()
}
state.password = !!reader.uint(1)
state.numplayers = reader.uint(2)
Expand All @@ -35,6 +35,7 @@ export default class samp extends Core {
const key = reader.pascalString(1)
const value = reader.pascalString(1)
state.raw.rules[key] = value
if ('version' in state.raw.rules) state.version = state.raw.rules.version
}
}

Expand Down
2 changes: 1 addition & 1 deletion protocols/savage2.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default class savage2 extends Core {
state.raw.location = reader.string()
state.raw.minplayers = reader.uint(1)
state.raw.gametype = reader.string()
state.raw.version = reader.string()
state.version = reader.string()
state.raw.minlevel = reader.uint(1)
}

Expand Down
2 changes: 1 addition & 1 deletion protocols/starmade.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default class starmade extends Core {
this.logger.debug('Received raw data array', data)

if (typeof data[0] === 'number') state.raw.infoVersion = data[0]
if (typeof data[1] === 'number') state.raw.version = data[1]
if (typeof data[1] === 'string') state.version = data[1]
if (typeof data[2] === 'string') state.name = data[2]
if (typeof data[3] === 'string') state.raw.description = data[3]
if (typeof data[4] === 'number') state.raw.startTime = data[4]
Expand Down
1 change: 1 addition & 0 deletions protocols/teamspeak3.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default class teamspeak3 extends Core {
if ('virtualserver_name' in state.raw) state.name = state.raw.virtualserver_name
if ('virtualserver_maxclients' in state.raw) state.maxplayers = state.raw.virtualserver_maxclients
if ('virtualserver_clientsonline' in state.raw) state.numplayers = state.raw.virtualserver_clientsonline
if ('virtualserver_version' in state.raw) state.version = state.raw.virtualserver_version
}

{
Expand Down
1 change: 1 addition & 0 deletions protocols/theisleevrima.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ export default class theisleevrima extends Epic {
await super.run(state)
state.name = state.raw.attributes.SERVERNAME_s
state.map = state.raw.attributes.MAP_NAME_s
state.version = state.raw.attributes.SERVER_VERSION_s
}
}
2 changes: 1 addition & 1 deletion protocols/tribes1.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default class tribes1 extends Core {

state.raw.gametype = this.readString(reader)
const isStarsiege2009 = state.raw.gametype === 'Starsiege'
state.raw.version = this.readString(reader)
state.version = this.readString(reader)
state.name = this.readString(reader)

if (isStarsiege2009) {
Expand Down
1 change: 1 addition & 0 deletions protocols/unreal2.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export default class unreal2 extends Core {
}
}
if ('GamePassword' in state.raw.rules) { state.password = state.raw.rules.GamePassword !== 'True' }
if ('UTComp_Version' in state.raw.rules) { state.version = state.raw.rules.UTComp_Version }
}

if (state.raw.mutators.includes('KillingFloorMut') ||
Expand Down
3 changes: 2 additions & 1 deletion protocols/valve.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ export default class valve extends Core {
state.raw.shipwitnesses = reader.uint(1)
state.raw.shipduration = reader.uint(1)
}
state.raw.version = reader.string()
state.version = reader.string()

const extraFlag = reader.uint(1)
if (extraFlag & 0x80) state.gamePort = reader.uint(2)
if (extraFlag & 0x10) state.raw.steamid = reader.uint(8).toString()
Expand Down
1 change: 1 addition & 0 deletions protocols/ventrilo.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default class ventrilo extends Core {

if ('NAME' in state.raw) state.name = state.raw.NAME
if ('MAXCLIENTS' in state.raw) state.maxplayers = state.raw.MAXCLIENTS
if ('VERSION' in state.raw) state.version = state.raw.VERSION
if (this.trueTest(state.raw.AUTH)) state.password = true
}

Expand Down

0 comments on commit a7c3b54

Please sign in to comment.