Created
October 21, 2023 17:07
-
-
Save kennarddh/d8d3860f991f241f197f486fd8f88c4e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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