node-gamedig/protocols/dayz.js
Pedro Ivo Hudson 0b8ebc871f
feat: Add valve requestRulesRequired & requestPlayersRequired (#458)
* feat: add valve requestRulesRequired

* missing delete requestRulesRequired

* feat: add valve requestPlayersRequired

* update documentation

* update games list
2024-01-11 22:58:50 +02:00

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')
}
}