@threadmc/minecraft-api
Version:
An advanced Minecraft API client for interacting with piston-meta, minecraft servers, and more
238 lines • 9.55 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import * as net from "net";
/**
* Provides methods for interacting with Minecraft servers (ping, status, query, legacy ping).
*/
export class Server {
/**
* Get the status of a Minecraft server using the server list ping protocol.
* @param host The server hostname or IP.
* @param port The server port (default 25565).
* @returns The server status response.
*/
getStatus(host_1) {
return __awaiter(this, arguments, void 0, function* (host, port = 25565) {
return yield this.ping(host, port, true);
});
}
/**
* Ping a Minecraft server and return latency (ms).
* @param host The server hostname or IP.
* @param port The server port (default 25565).
* @param returnStatus If true, return status JSON instead of latency.
* @returns The latency in ms or the status JSON.
*/
ping(host_1) {
return __awaiter(this, arguments, void 0, function* (host, port = 25565, returnStatus = false) {
return new Promise((resolve, reject) => {
const socket = new net.Socket();
let start = Date.now();
let receivedData = Buffer.alloc(0);
socket.setTimeout(5000);
socket.connect(port, host, () => {
const hostBuf = Buffer.from(host, "utf8");
const handshake = Buffer.concat([
Buffer.from([0x00]),
Buffer.from([0x00]),
Buffer.from([hostBuf.length]),
hostBuf,
Buffer.from([(port >> 8) & 0xff, port & 0xff]),
Buffer.from([0x01]),
]);
const handshakePacket = Buffer.concat([
Buffer.from([handshake.length]),
handshake,
]);
socket.write(handshakePacket);
socket.write(Buffer.from([0x01, 0x00]));
});
socket.on("data", (data) => {
receivedData = Buffer.concat([receivedData, data]);
try {
let offset = 0;
const length = receivedData.readVarInt(offset);
offset += getVarIntSize(receivedData, offset);
const packetId = receivedData.readVarInt(offset);
offset += getVarIntSize(receivedData, offset);
if (packetId === 0x00) {
const jsonLength = receivedData.readVarInt(offset);
offset += getVarIntSize(receivedData, offset);
const json = receivedData
.slice(offset, offset + jsonLength)
.toString("utf8");
socket.destroy();
if (returnStatus) {
resolve(JSON.parse(json));
}
else {
resolve(Date.now() - start);
}
}
}
catch (_a) {
// Wait for more data
}
});
socket.on("timeout", () => {
socket.destroy();
reject(new Error("Timeout connecting to server"));
});
socket.on("error", (err) => {
socket.destroy();
reject(err);
});
});
});
}
/**
* Query a Minecraft server for player/sample info (requires server.properties enable-query=true).
* @param host The server hostname or IP.
* @param port The query port (default 25565).
* @returns The query response JSON.
*/
query(host_1) {
return __awaiter(this, arguments, void 0, function* (host, port = 25565) {
const dgram = yield import("dgram");
return new Promise((resolve, reject) => {
const client = dgram.createSocket("udp4");
const sessionId = Math.floor(Math.random() * 0xffffffff);
const handshake = Buffer.from([
0xfe,
0xfd,
0x09,
(sessionId >> 24) & 0xff,
(sessionId >> 16) & 0xff,
(sessionId >> 8) & 0xff,
sessionId & 0xff,
]);
let token = null;
let timeout;
client.send(handshake, 0, handshake.length, port, host);
client.on("message", (msg) => {
if (msg[0] === 0x09 && token === null) {
token = parseInt(msg.slice(5, msg.length - 1).toString());
const req = Buffer.concat([
Buffer.from([
0xfe,
0xfd,
0x00,
(sessionId >> 24) & 0xff,
(sessionId >> 16) & 0xff,
(sessionId >> 8) & 0xff,
sessionId & 0xff,
]),
Buffer.from(token.toString()),
Buffer.alloc(4),
]);
client.send(req, 0, req.length, port, host);
}
else if (msg[0] === 0x00 && token !== null) {
client.close();
clearTimeout(timeout);
resolve(parseQueryResponse(msg));
}
});
timeout = setTimeout(() => {
client.close();
reject(new Error("Timeout querying server"));
}, 5000);
client.on("error", (err) => {
client.close();
clearTimeout(timeout);
reject(err);
});
});
});
}
/**
* Legacy ping for Minecraft servers (1.6 and below).
* @param host The server hostname or IP.
* @param port The server port (default 25565).
* @returns The legacy ping response.
*/
legacyPing(host_1) {
return __awaiter(this, arguments, void 0, function* (host, port = 25565) {
return new Promise((resolve, reject) => {
const socket = new net.Socket();
socket.setTimeout(5000);
socket.connect(port, host, () => {
socket.write(Buffer.from([0xfe, 0x01]));
});
socket.on("data", (data) => {
socket.destroy();
// Parse legacy ping response
const str = data.toString("ucs2", 3);
const parts = str.split("\0");
resolve({
protocol: parts[1],
version: parts[2],
motd: parts[3],
onlinePlayers: parts[4],
maxPlayers: parts[5],
});
});
socket.on("timeout", () => {
socket.destroy();
reject(new Error("Timeout connecting to server"));
});
socket.on("error", (err) => {
socket.destroy();
reject(err);
});
});
});
}
}
/**
* Helper to get the size of a VarInt in a buffer.
* @param buf The buffer.
* @param offset The offset to start reading.
* @returns The size in bytes.
*/
function getVarIntSize(buf, offset) {
let size = 0;
while (true) {
if ((buf[offset + size] & 0x80) !== 0x80)
break;
size++;
}
return size + 1;
}
/**
* Parse the response from a Minecraft server query.
* @param msg The response buffer.
* @returns The parsed query response.
*/
function parseQueryResponse(msg) {
let offset = 5;
const data = {};
const str = msg.slice(offset).toString("utf8");
const parts = str.split("\0");
for (let i = 0; i < parts.length - 1; i += 2) {
if (parts[i].length === 0)
break;
data[parts[i]] = parts[i + 1];
}
return data;
}
Buffer.prototype.readVarInt = function (offset) {
let num = 0;
let shift = 0;
let pos = offset;
let byte = 0;
do {
byte = this[pos++];
num |= (byte & 0x7f) << shift;
shift += 7;
} while (byte & 0x80);
return num;
};
//# sourceMappingURL=server.js.map