Skip to content

Instantly share code, notes, and snippets.

@adrian154
Created June 10, 2021 04:05
Show Gist options
  • Select an option

  • Save adrian154/702e88bbaa831ce6df999a425ae5d764 to your computer and use it in GitHub Desktop.

Select an option

Save adrian154/702e88bbaa831ce6df999a425ae5d764 to your computer and use it in GitHub Desktop.
// 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