Created
June 10, 2021 04:05
-
-
Save adrian154/702e88bbaa831ce6df999a425ae5d764 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
| // deps | |
| const net = require("net"); | |
| const makeVarInt = (value) => { | |
| const buf = []; | |
| do { | |
| let cur = value & 0b01111111; | |
| value >>>= 7; | |
| if(value != 0) { | |
| cur |= 0b10000000; | |
| } | |
| buf.push(cur); | |
| } while(value != 0); | |
| return Buffer.from(buf); | |
| }; | |
| const makeString = (string) => Buffer.concat([makeVarInt(string.length), Buffer.from(string, "utf-8")]); | |
| // :-/ | |
| const makeUShort = (value) => { | |
| const buffer = Buffer.alloc(2); | |
| buffer.writeUInt16LE(value); | |
| return buffer; | |
| }; | |
| const sendPacket = (socket, ...parts) => { | |
| socket.write(makeVarInt(parts.reduce((a, c) => a + c.length, 0))); | |
| parts.forEach(part => socket.write(part)); | |
| }; | |
| const skipVarInt = (buffer, offset) => { | |
| while((buffer[offset] & 0b10000000) != 0) { | |
| offset++; | |
| } | |
| return offset + 1; | |
| }; | |
| module.exports = (host, port, options) => new Promise((resolve, reject) => { | |
| const socket = new net.Socket(); | |
| socket.setTimeout(options?.timeout ?? 5000); | |
| let ready = false, chunks =[]; | |
| // net.Socket docs wrt timeout event: | |
| // > The user must manually close the connection. | |
| socket.on("timeout", () => { | |
| socket.destroy(); | |
| }); | |
| socket.on("error", (error) => { | |
| reject("Network error occurred: " + error); | |
| }); | |
| // catch-all error handling :) | |
| socket.on("close", () => { | |
| if(!ready) { | |
| reject("The socket closed before enough data could be received"); | |
| } else { | |
| const buffer = Buffer.concat(chunks); | |
| const dataStart = skipVarInt(buffer, skipVarInt(buffer, skipVarInt(buffer, 0))); // evil stupidity | |
| const data = buffer.slice(dataStart); | |
| try { | |
| resolve(JSON.parse(data)); | |
| } catch(error) { | |
| reject("Could not parse response or none received"); | |
| } | |
| } | |
| }); | |
| socket.on("data", (data) => { | |
| chunks.push(data); | |
| }); | |
| socket.connect({host, port}); | |
| // build handshake packet | |
| sendPacket( | |
| socket, | |
| makeVarInt(0), // packet ID | |
| makeVarInt(-1), // protocol version (-1 = pinging) | |
| makeString(host), // hostname + port even though vanilla server ignores these... | |
| makeUShort(port), | |
| makeVarInt(1), // target state (1 = status ping) | |
| ); | |
| sendPacket( | |
| socket, | |
| makeVarInt(0) // packet ID | |
| ); | |
| ready = true; | |
| // *crickets* | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment