Created
September 17, 2022 17:12
-
-
Save zRains/905457b550ef86cc4f7ecd4f99228e9b to your computer and use it in GitHub Desktop.
Get Minecraft server info by LSP using nodejs.
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 { Socket } = require('net') | |
class MineStatus { | |
/** @type {Socket} */ | |
socket | |
/** @type {string} */ | |
host | |
/** @type {number} */ | |
port | |
/** @type {number} */ | |
packageDataLen | |
/** @type {Buffer} */ | |
responseBuffers = Buffer.from('') | |
/** @type {{host: string, ip: string, port: string}} */ | |
debugInfo | |
/** | |
* @param {string} host | |
* @param {number} port | |
* @param {boolean} [immediately] | |
*/ | |
constructor(host, port) { | |
this.host = host | |
this.port = port | |
} | |
onConnect() { | |
const hostUnit8Arr = Uint8Array.from(Buffer.from(this.host)) | |
const dataBody = MineStatus.concatData([ | |
Uint8Array.from([0x00, 0x00]), | |
MineStatus.packVarint(hostUnit8Arr.length), | |
hostUnit8Arr, | |
MineStatus.concatData([ | |
Uint8Array.from(Buffer.from(Uint16Array.from([this.port]).buffer)), | |
Uint8Array.from([0x01]), | |
]), | |
]) | |
this.socket.write(MineStatus.concatData([MineStatus.packVarint(dataBody.length), dataBody])) | |
this.socket.write(Uint8Array.from([0x01, 0x00])) | |
// Set debug info here | |
this.debugInfo = { | |
host: this.host, | |
ip: this.socket.remoteAddress || this.host, | |
port: this.socket.remotePort || this.port, | |
} | |
} | |
/** @return {Promise<Buffer>} */ | |
getResponseData() { | |
if (this.responseBuffers.length >= this.packageDataLen) return Promise.resolve(this.responseBuffers) | |
this.socket = new Socket().connect({ host: this.host, port: this.port }) | |
this.socket.on('connect', this.onConnect.bind(this)) | |
return new Promise((resolve) => { | |
this.socket.on('readable', () => { | |
if (this.socket.readableLength >= 5) { | |
if (this.packageDataLen === undefined) { | |
const packageLen = MineStatus.unPackVarint(this.socket) | |
const packageId = MineStatus.unPackVarint(this.socket) | |
const packageDataLen = MineStatus.unPackVarint(this.socket) | |
this.packageDataLen = packageDataLen | |
} | |
this.responseBuffers = Buffer.concat([this.responseBuffers, this.socket.read()]) | |
if (this.responseBuffers.length >= this.packageDataLen) { | |
this.socket.destroy() | |
resolve(this.responseBuffers) | |
} | |
} | |
}) | |
}) | |
} | |
async getStatus() { | |
const responseData = await this.getResponseData() | |
try { | |
return JSON.parse(responseData.toString()) | |
} catch (error) { | |
console.log(responseData.toString()) | |
return {} | |
} | |
} | |
/** @return {Promise<{host: string, ip: string, port: string}>} */ | |
getDebugInfo() { | |
if (this.debugInfo) return Promise.resolve(this.debugInfo) | |
this.socket = new Socket().connect({ host: this.host, port: this.port }) | |
return new Promise((resolve) => { | |
this.socket.on('connect', () => { | |
this.debugInfo = { | |
host: this.host, | |
ip: this.socket.remoteAddress || this.host, | |
port: this.socket.remotePort || this.port, | |
} | |
this.socket.destroy() | |
resolve(this.debugInfo) | |
}) | |
}) | |
} | |
/** @param {Uint8Array[]} arrays */ | |
static concatData(arrays) { | |
const totalLength = arrays.reduce((acc, value) => acc + value.length, 0) | |
let result = new Uint8Array(totalLength) | |
arrays.reduce((offset, arr) => { | |
result.set(arr, offset) | |
return offset + arr.length | |
}, 0) | |
return result | |
} | |
/** @param {number} num */ | |
static packVarint(num) { | |
const buffers = [] | |
while (true) { | |
if (num > 0x7f) { | |
buffers.push(0x80 | (num & 0x7f)) | |
num >>= 7 | |
} else { | |
buffers.push(num) | |
break | |
} | |
} | |
return Uint8Array.from(buffers) | |
} | |
/** @param {Socket} socket */ | |
static unPackVarint(socket) { | |
let result = 0 | |
for (let idx = 0; idx < 5; idx++) { | |
let bufferVal = socket.read(1)[0] | |
result |= (bufferVal & 0x7f) << (idx * 7) | |
if ((bufferVal & 0x80) === 0) return result | |
} | |
return result | |
} | |
} |
Author
zRains
commented
Sep 18, 2022
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment