mirror of
https://github.com/gamedig/node-gamedig.git
synced 2024-11-18 01:30:39 +01:00
0b8ebc871f
* feat: add valve requestRulesRequired * missing delete requestRulesRequired * feat: add valve requestPlayersRequired * update documentation * update games list
196 lines
5.3 KiB
JavaScript
196 lines
5.3 KiB
JavaScript
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 && !this.options.requestRulesRequired) 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
|
|
state.raw.privateHive = false
|
|
state.raw.external = false
|
|
state.raw.official = 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('privHive')) {
|
|
state.raw.privateHive = true;
|
|
}
|
|
if (tag.includes('external')) {
|
|
state.raw.external = 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 (!state.raw.external && !state.raw.privateHive) {
|
|
state.raw.official = true
|
|
}
|
|
}
|
|
|
|
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')
|
|
}
|
|
}
|