Skip to content

Instantly share code, notes, and snippets.

@kennarddh
Created October 21, 2023 17:07
Show Gist options
  • Save kennarddh/d8d3860f991f241f197f486fd8f88c4e to your computer and use it in GitHub Desktop.
Save kennarddh/d8d3860f991f241f197f486fd8f88c4e to your computer and use it in GitHub Desktop.
const dgram = require('node:dgram')
class CustomBuffer {
#offset = 0
#buffer
/**
* @returns {number}
*/
get() {
const data = this.#buffer.readInt8(this.#offset)
this.#offset += 1
return data
}
/**
* @returns {number}
*/
getInt4() {
const data = this.#buffer.readInt32BE(this.#offset)
this.#offset += 4
return data
}
/**
* @param {number} value
*/
write(value) {
this.#buffer.writeInt8(value, this.#offset)
this.#offset += 1
}
/**
* @param {Buffer} buffer
* @returns {CustomBuffer}
*/
static fromBuffer(buffer) {
const newBuffer = CustomBuffer.alloc(buffer.length)
newBuffer.copy(buffer)
return newBuffer
}
/**
* @param {number} length
*/
static alloc(length) {
const newBuffer = new CustomBuffer()
newBuffer.#buffer = Buffer.alloc(length)
return newBuffer
}
/**
* @param {Buffer} originalBuffer
*/
copy(originalBuffer) {
originalBuffer.copy(this.#buffer, 0)
}
/**
* @param {BufferEncoding} encoding
* @returns {string}
*/
toString(encoding = 'utf-8') {
return this.#buffer.toString(encoding)
}
get offset() {
return this.#offset
}
get bufferLeft() {
return this.#buffer.slice(this.#offset)
}
resetOffset() {
this.#offset = 0
}
}
class Host {
ping
name
address
players
mapname
wave
version
versionType
playerLimit
mode
description
modeName
/**
*
* @param {number} ping
* @param {string} name
* @param {string} address
* @param {string} mapname
* @param {number} wave
* @param {number} players
* @param {number} version
* @param {string} versionType
* @param {string} mode
* @param {number} playerLimit
* @param {string} description
* @param {string} modeName
*/
constructor(
ping,
name,
address,
mapname,
wave,
players,
version,
versionType,
mode,
playerLimit,
description,
modeName
) {
this.ping = ping
this.name = name
this.address = address
this.players = players
this.mapname = mapname
this.wave = wave
this.version = version
this.versionType = versionType
this.playerLimit = playerLimit
this.mode = mode
this.description = description
this.modeName = modeName
}
}
/**
* @param {number} startOffset
* @param {CustomBuffer} buffer
* @returns {[number, string]}
*/
const readString = buffer => {
const length = buffer.get() & 0xff
const stringBuffer = CustomBuffer.alloc(length)
for (let i = 0; i < length; i++) {
const data = buffer.get()
stringBuffer.write(data)
}
return stringBuffer.toString('utf-8')
}
/**
*
* @param {[number,number]} hrtime
* @returns {number}
*/
const parseHrtimeToSeconds = hrtime => {
return hrtime[0] + hrtime[1] / 1e9
}
/**
* @param {string} host
* @param {number} port
* @param {CustomBuffer} data
* @param {(error: Error | null, response: CustomBuffer | null) => void} callback
*/
const requestUDP = (host, port, data, callback) => {
const client = dgram.createSocket('udp4')
client.on('message', message => {
client.close()
const buffer = CustomBuffer.fromBuffer(message)
callback(null, buffer)
})
client.send(data, port, host, err => {
if (err) callback(err, null)
})
}
const gamemodes = ['survival', 'sandbox', 'attack', 'pvp', 'editor']
/**
* @param {string} host
* @param {number} port
* @returns {Promise<Host>}
*/
const pingMindustryServer = async (host, port) =>
new Promise((resolve, reject) => {
const pingMessage = Buffer.from([-2, 1])
const startPing = process.hrtime()
requestUDP(
mindustryServerHost,
mindustryServerPort,
pingMessage,
(error, response) => {
if (error) reject(error)
const pingDuration = process.hrtime(startPing)
const ping = parseHrtimeToSeconds(pingDuration)
const name = readString(response)
const map = readString(response)
const players = response.getInt4()
const wave = response.getInt4()
const version = response.getInt4()
const verType = readString(response)
const gamemode = gamemodes[response.get()]
const limit = response.getInt4()
const description = readString(response)
const modeName = readString(response)
const hostData = new Host(
ping,
name,
`${host}:${port}`,
map,
wave,
players,
version,
verType,
gamemode,
limit,
description,
modeName
)
resolve(hostData)
}
)
})
const mindustryServerHost = 'alexmindustryv7.servegame.com'
const mindustryServerPort = 41962
pingMindustryServer(mindustryServerHost, mindustryServerPort)
.then(console.log)
.catch(console.error)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment