mirror of
https://github.com/gamedig/node-gamedig.git
synced 2024-11-14 07:51:12 +01:00
chore: extract dayz from valve (#448)
This commit is contained in:
parent
6bfbc883be
commit
90b3c6044b
4 changed files with 187 additions and 155 deletions
|
@ -654,7 +654,7 @@ export const games = {
|
||||||
options: {
|
options: {
|
||||||
port: 2302,
|
port: 2302,
|
||||||
port_query_offset: 24714,
|
port_query_offset: 24714,
|
||||||
protocol: 'valve'
|
protocol: 'dayz'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dayzmod: {
|
dayzmod: {
|
||||||
|
|
182
protocols/dayz.js
Normal file
182
protocols/dayz.js
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
import valve from './valve.js'
|
||||||
|
import { Buffer } from 'node:buffer'
|
||||||
|
|
||||||
|
export default class dayz extends valve {
|
||||||
|
async run (state) {
|
||||||
|
if (!this.options.port) this.options.port = 27016
|
||||||
|
await super.queryInfo(state)
|
||||||
|
await super.queryChallenge()
|
||||||
|
await super.queryPlayers(state)
|
||||||
|
await this.queryRules(state)
|
||||||
|
|
||||||
|
this.processQueryInfo(state)
|
||||||
|
await super.cleanup(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
async queryRules (state) {
|
||||||
|
if (!this.options.requestRules) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const rules = {}
|
||||||
|
state.raw.rules = rules
|
||||||
|
const dayZPayload = []
|
||||||
|
|
||||||
|
this.logger.debug('Requesting rules ...')
|
||||||
|
|
||||||
|
const b = await this.sendPacket(0x56, null, 0x45, true)
|
||||||
|
if (b === null) return // timed out - the server probably has rules disabled
|
||||||
|
|
||||||
|
let dayZPayloadEnded = false
|
||||||
|
|
||||||
|
const reader = this.reader(b)
|
||||||
|
const num = reader.uint(2)
|
||||||
|
for (let i = 0; i < num; i++) {
|
||||||
|
if (!dayZPayloadEnded) {
|
||||||
|
const one = reader.uint(1)
|
||||||
|
const two = reader.uint(1)
|
||||||
|
const three = reader.uint(1)
|
||||||
|
if (one !== 0 && two !== 0 && three === 0) {
|
||||||
|
while (true) {
|
||||||
|
const byte = reader.uint(1)
|
||||||
|
if (byte === 0) break
|
||||||
|
dayZPayload.push(byte)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
reader.skip(-3)
|
||||||
|
dayZPayloadEnded = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = reader.string()
|
||||||
|
rules[key] = reader.string()
|
||||||
|
}
|
||||||
|
|
||||||
|
state.raw.dayzMods = this.readDayzMods(Buffer.from(dayZPayload))
|
||||||
|
}
|
||||||
|
|
||||||
|
processQueryInfo (state) {
|
||||||
|
// DayZ embeds some of the server information inside the tags attribute
|
||||||
|
if (!state.raw.tags) { return }
|
||||||
|
|
||||||
|
state.raw.dlcEnabled = false
|
||||||
|
state.raw.firstPerson = false
|
||||||
|
for (const tag of state.raw.tags) {
|
||||||
|
if (tag.startsWith('lqs')) {
|
||||||
|
const value = parseInt(tag.replace('lqs', ''))
|
||||||
|
if (!isNaN(value)) {
|
||||||
|
state.raw.queue = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tag.includes('no3rd')) {
|
||||||
|
state.raw.firstPerson = true
|
||||||
|
}
|
||||||
|
if (tag.includes('isDLC')) {
|
||||||
|
state.raw.dlcEnabled = true
|
||||||
|
}
|
||||||
|
if (tag.includes(':')) {
|
||||||
|
state.raw.time = tag
|
||||||
|
}
|
||||||
|
if (tag.startsWith('etm')) {
|
||||||
|
const value = parseInt(tag.replace('etm', ''))
|
||||||
|
if (!isNaN(value)) {
|
||||||
|
state.raw.dayAcceleration = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tag.startsWith('entm')) {
|
||||||
|
const value = parseInt(tag.replace('entm', ''))
|
||||||
|
if (!isNaN(value)) {
|
||||||
|
state.raw.nightAcceleration = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readDayzMods (/** Buffer */ buffer) {
|
||||||
|
if (!buffer.length) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.debug('DAYZ BUFFER')
|
||||||
|
this.logger.debug(buffer)
|
||||||
|
|
||||||
|
const reader = this.reader(buffer)
|
||||||
|
const version = this.readDayzByte(reader)
|
||||||
|
const overflow = this.readDayzByte(reader)
|
||||||
|
const dlc1 = this.readDayzByte(reader)
|
||||||
|
const dlc2 = this.readDayzByte(reader)
|
||||||
|
this.logger.debug('version ' + version)
|
||||||
|
this.logger.debug('overflow ' + overflow)
|
||||||
|
this.logger.debug('dlc1 ' + dlc1)
|
||||||
|
this.logger.debug('dlc2 ' + dlc2)
|
||||||
|
if (dlc1) {
|
||||||
|
const unknown = this.readDayzUint(reader, 4) // ?
|
||||||
|
this.logger.debug('unknown ' + unknown)
|
||||||
|
}
|
||||||
|
if (dlc2) {
|
||||||
|
const unknown = this.readDayzUint(reader, 4) // ?
|
||||||
|
this.logger.debug('unknown ' + unknown)
|
||||||
|
}
|
||||||
|
const mods = []
|
||||||
|
mods.push(...this.readDayzModsSection(reader, true))
|
||||||
|
mods.push(...this.readDayzModsSection(reader, false))
|
||||||
|
this.logger.debug('dayz buffer rest:', reader.rest())
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
readDayzModsSection (/** Reader */ reader, withHeader) {
|
||||||
|
const out = []
|
||||||
|
const count = this.readDayzByte(reader)
|
||||||
|
this.logger.debug('dayz mod section withHeader:' + withHeader + ' count:' + count)
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
if (reader.done()) break
|
||||||
|
const mod = {}
|
||||||
|
if (withHeader) {
|
||||||
|
mod.unknown = this.readDayzUint(reader, 4) // ?
|
||||||
|
|
||||||
|
// For some reason this is 4 on all of them, but doesn't exist on the last one? but only sometimes?
|
||||||
|
const offset = reader.offset()
|
||||||
|
const flag = this.readDayzByte(reader)
|
||||||
|
if (flag !== 4) reader.setOffset(offset)
|
||||||
|
|
||||||
|
mod.workshopId = this.readDayzUint(reader, 4)
|
||||||
|
}
|
||||||
|
mod.title = this.readDayzString(reader)
|
||||||
|
this.logger.debug(mod)
|
||||||
|
out.push(mod)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
readDayzUint (reader, bytes) {
|
||||||
|
const out = []
|
||||||
|
for (let i = 0; i < bytes; i++) {
|
||||||
|
out.push(this.readDayzByte(reader))
|
||||||
|
}
|
||||||
|
const buf = Buffer.from(out)
|
||||||
|
const r2 = this.reader(buf)
|
||||||
|
return r2.uint(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
readDayzByte (reader) {
|
||||||
|
const byte = reader.uint(1)
|
||||||
|
if (byte === 1) {
|
||||||
|
const byte2 = reader.uint(1)
|
||||||
|
if (byte2 === 1) return 1
|
||||||
|
if (byte2 === 2) return 0
|
||||||
|
if (byte2 === 3) return 0xff
|
||||||
|
return 0 // ?
|
||||||
|
}
|
||||||
|
return byte
|
||||||
|
}
|
||||||
|
|
||||||
|
readDayzString (reader) {
|
||||||
|
const length = this.readDayzByte(reader)
|
||||||
|
const out = []
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
out.push(this.readDayzByte(reader))
|
||||||
|
}
|
||||||
|
return Buffer.from(out).toString('utf8')
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,11 +50,12 @@ import ventrilo from './ventrilo.js'
|
||||||
import warsow from './warsow.js'
|
import warsow from './warsow.js'
|
||||||
import beammpmaster from './beammpmaster.js'
|
import beammpmaster from './beammpmaster.js'
|
||||||
import beammp from './beammp.js'
|
import beammp from './beammp.js'
|
||||||
|
import dayz from './dayz.js'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
armagetron, ase, asa, assettocorsa, battlefield, buildandshoot, cs2d, discord, doom3, eco, epic, ffow, fivem, gamespy1,
|
armagetron, ase, asa, assettocorsa, battlefield, buildandshoot, cs2d, discord, doom3, eco, epic, ffow, fivem, gamespy1,
|
||||||
gamespy2, gamespy3, geneshift, goldsrc, hexen2, jc2mp, kspdmp, mafia2mp, mafia2online, minecraft,
|
gamespy2, gamespy3, geneshift, goldsrc, hexen2, jc2mp, kspdmp, mafia2mp, mafia2online, minecraft,
|
||||||
minecraftbedrock, minecraftvanilla, mumble, mumbleping, nadeo, openttd, quake1, quake2, quake3, rfactor, samp,
|
minecraftbedrock, minecraftvanilla, mumble, mumbleping, nadeo, openttd, quake1, quake2, quake3, rfactor, samp,
|
||||||
savage2, starmade, starsiege, teamspeak2, teamspeak3, terraria, tribes1, tribes1master, unreal2, ut3, valve,
|
savage2, starmade, starsiege, teamspeak2, teamspeak3, terraria, tribes1, tribes1master, unreal2, ut3, valve,
|
||||||
vcmp, ventrilo, warsow, eldewrito, beammpmaster, beammp
|
vcmp, ventrilo, warsow, eldewrito, beammpmaster, beammp, dayz
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ const AppId = {
|
||||||
Squad: 393380,
|
Squad: 393380,
|
||||||
Bat1944: 489940,
|
Bat1944: 489940,
|
||||||
Ship: 2400,
|
Ship: 2400,
|
||||||
DayZ: 221100,
|
|
||||||
Rust: 252490,
|
Rust: 252490,
|
||||||
CSGO: 730,
|
CSGO: 730,
|
||||||
CS_Source: 240,
|
CS_Source: 240,
|
||||||
|
@ -132,43 +131,6 @@ export default class valve extends Core {
|
||||||
this.goldsrcSplits = true
|
this.goldsrcSplits = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// DayZ embeds some of the server information inside the tags attribute
|
|
||||||
if (appId === AppId.DayZ) {
|
|
||||||
if (state.raw.tags) {
|
|
||||||
state.raw.dlcEnabled = false
|
|
||||||
state.raw.firstPerson = false
|
|
||||||
for (const tag of state.raw.tags) {
|
|
||||||
if (tag.startsWith('lqs')) {
|
|
||||||
const value = parseInt(tag.replace('lqs', ''))
|
|
||||||
if (!isNaN(value)) {
|
|
||||||
state.raw.queue = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tag.includes('no3rd')) {
|
|
||||||
state.raw.firstPerson = true
|
|
||||||
}
|
|
||||||
if (tag.includes('isDLC')) {
|
|
||||||
state.raw.dlcEnabled = true
|
|
||||||
}
|
|
||||||
if (tag.includes(':')) {
|
|
||||||
state.raw.time = tag
|
|
||||||
}
|
|
||||||
if (tag.startsWith('etm')) {
|
|
||||||
const value = parseInt(tag.replace('etm', ''))
|
|
||||||
if (!isNaN(value)) {
|
|
||||||
state.raw.dayAcceleration = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tag.startsWith('entm')) {
|
|
||||||
const value = parseInt(tag.replace('entm', ''))
|
|
||||||
if (!isNaN(value)) {
|
|
||||||
state.raw.nightAcceleration = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appId === AppId.Rust) {
|
if (appId === AppId.Rust) {
|
||||||
if (state.raw.tags) {
|
if (state.raw.tags) {
|
||||||
for (const tag of state.raw.tags) {
|
for (const tag of state.raw.tags) {
|
||||||
|
@ -250,7 +212,6 @@ export default class valve extends Core {
|
||||||
|
|
||||||
const rules = {}
|
const rules = {}
|
||||||
state.raw.rules = rules
|
state.raw.rules = rules
|
||||||
const dayZPayload = []
|
|
||||||
|
|
||||||
this.logger.debug('Requesting rules ...')
|
this.logger.debug('Requesting rules ...')
|
||||||
|
|
||||||
|
@ -260,38 +221,17 @@ export default class valve extends Core {
|
||||||
const reader = this.reader(b)
|
const reader = this.reader(b)
|
||||||
while (!reader.done()) {
|
while (!reader.done()) {
|
||||||
const key = reader.string()
|
const key = reader.string()
|
||||||
const value = reader.string()
|
rules[key] = reader.string()
|
||||||
rules[key] = value
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const b = await this.sendPacket(0x56, null, 0x45, true)
|
const b = await this.sendPacket(0x56, null, 0x45, true)
|
||||||
if (b === null) return // timed out - the server probably has rules disabled
|
if (b === null) return // timed out - the server probably has rules disabled
|
||||||
|
|
||||||
let dayZPayloadEnded = false
|
|
||||||
|
|
||||||
const reader = this.reader(b)
|
const reader = this.reader(b)
|
||||||
const num = reader.uint(2)
|
const num = reader.uint(2)
|
||||||
for (let i = 0; i < num; i++) {
|
for (let i = 0; i < num; i++) {
|
||||||
if (appId === AppId.DayZ && !dayZPayloadEnded) {
|
|
||||||
const one = reader.uint(1)
|
|
||||||
const two = reader.uint(1)
|
|
||||||
const three = reader.uint(1)
|
|
||||||
if (one !== 0 && two !== 0 && three === 0) {
|
|
||||||
while (true) {
|
|
||||||
const byte = reader.uint(1)
|
|
||||||
if (byte === 0) break
|
|
||||||
dayZPayload.push(byte)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
reader.skip(-3)
|
|
||||||
dayZPayloadEnded = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const key = reader.string()
|
const key = reader.string()
|
||||||
const value = reader.string()
|
rules[key] = reader.string()
|
||||||
rules[key] = value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,97 +263,6 @@ export default class valve extends Core {
|
||||||
state.password = true
|
state.password = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appId === AppId.DayZ) {
|
|
||||||
state.raw.dayzMods = this.readDayzMods(Buffer.from(dayZPayload))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readDayzMods (/** Buffer */ buffer) {
|
|
||||||
if (!buffer.length) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.debug('DAYZ BUFFER')
|
|
||||||
this.logger.debug(buffer)
|
|
||||||
|
|
||||||
const reader = this.reader(buffer)
|
|
||||||
const version = this.readDayzByte(reader)
|
|
||||||
const overflow = this.readDayzByte(reader)
|
|
||||||
const dlc1 = this.readDayzByte(reader)
|
|
||||||
const dlc2 = this.readDayzByte(reader)
|
|
||||||
this.logger.debug('version ' + version)
|
|
||||||
this.logger.debug('overflow ' + overflow)
|
|
||||||
this.logger.debug('dlc1 ' + dlc1)
|
|
||||||
this.logger.debug('dlc2 ' + dlc2)
|
|
||||||
if (dlc1) {
|
|
||||||
const unknown = this.readDayzUint(reader, 4) // ?
|
|
||||||
this.logger.debug('unknown ' + unknown)
|
|
||||||
}
|
|
||||||
if (dlc2) {
|
|
||||||
const unknown = this.readDayzUint(reader, 4) // ?
|
|
||||||
this.logger.debug('unknown ' + unknown)
|
|
||||||
}
|
|
||||||
const mods = []
|
|
||||||
mods.push(...this.readDayzModsSection(reader, true))
|
|
||||||
mods.push(...this.readDayzModsSection(reader, false))
|
|
||||||
this.logger.debug('dayz buffer rest:', reader.rest())
|
|
||||||
return mods
|
|
||||||
}
|
|
||||||
|
|
||||||
readDayzModsSection (/** Reader */ reader, withHeader) {
|
|
||||||
const out = []
|
|
||||||
const count = this.readDayzByte(reader)
|
|
||||||
this.logger.debug('dayz mod section withHeader:' + withHeader + ' count:' + count)
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
if (reader.done()) break
|
|
||||||
const mod = {}
|
|
||||||
if (withHeader) {
|
|
||||||
mod.unknown = this.readDayzUint(reader, 4) // ?
|
|
||||||
|
|
||||||
// For some reason this is 4 on all of them, but doesn't exist on the last one? but only sometimes?
|
|
||||||
const offset = reader.offset()
|
|
||||||
const flag = this.readDayzByte(reader)
|
|
||||||
if (flag !== 4) reader.setOffset(offset)
|
|
||||||
|
|
||||||
mod.workshopId = this.readDayzUint(reader, 4)
|
|
||||||
}
|
|
||||||
mod.title = this.readDayzString(reader)
|
|
||||||
this.logger.debug(mod)
|
|
||||||
out.push(mod)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
readDayzUint (reader, bytes) {
|
|
||||||
const out = []
|
|
||||||
for (let i = 0; i < bytes; i++) {
|
|
||||||
out.push(this.readDayzByte(reader))
|
|
||||||
}
|
|
||||||
const buf = Buffer.from(out)
|
|
||||||
const r2 = this.reader(buf)
|
|
||||||
return r2.uint(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
readDayzByte (reader) {
|
|
||||||
const byte = reader.uint(1)
|
|
||||||
if (byte === 1) {
|
|
||||||
const byte2 = reader.uint(1)
|
|
||||||
if (byte2 === 1) return 1
|
|
||||||
if (byte2 === 2) return 0
|
|
||||||
if (byte2 === 3) return 0xff
|
|
||||||
return 0 // ?
|
|
||||||
}
|
|
||||||
return byte
|
|
||||||
}
|
|
||||||
|
|
||||||
readDayzString (reader) {
|
|
||||||
const length = this.readDayzByte(reader)
|
|
||||||
const out = []
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
out.push(this.readDayzByte(reader))
|
|
||||||
}
|
|
||||||
return Buffer.from(out).toString('utf8')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async cleanup (/** Results */ state) {
|
async cleanup (/** Results */ state) {
|
||||||
|
|
Loading…
Reference in a new issue