mirror of
https://github.com/minescope/mineping.git
synced 2025-07-12 17:04:36 +03:00
feat: add debug logging
Add debug logs using the `debug` library. To use this new feature, set the `DEBUG` environment variable: - `DEBUG=mineping:*` enables all logs from this library. - `DEBUG=mineping:java` enables logs for the Java module only. - `DEBUG=mineping:bedrock` enables logs for the Bedrock module only.
This commit is contained in:
@ -7,6 +7,9 @@
|
||||
|
||||
import dgram from "node:dgram";
|
||||
import crypto from "node:crypto";
|
||||
import createDebug from "debug";
|
||||
|
||||
const debug = createDebug("mineping:bedrock");
|
||||
|
||||
const MAGIC = "00ffff00fefefefefdfdfdfd12345678";
|
||||
const START_TIME = Date.now();
|
||||
@ -177,6 +180,7 @@ const parseUnconnectedPong = (pongPacket) => {
|
||||
motdOffset,
|
||||
motdOffset + motdLength
|
||||
);
|
||||
debug("received raw MOTD string: %s", motdString);
|
||||
|
||||
const rawMotd = parseMotd(motdString);
|
||||
const motd = transformMotd(rawMotd);
|
||||
@ -197,6 +201,7 @@ export const pingBedrock = (host, options = {}) => {
|
||||
}
|
||||
|
||||
const { port = 19132, timeout = 5000 } = options;
|
||||
debug("pinging Bedrock server %s:%d with %dms timeout", host, port, timeout);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const socket = dgram.createSocket("udp4");
|
||||
@ -215,17 +220,20 @@ export const pingBedrock = (host, options = {}) => {
|
||||
const cleanup = () => {
|
||||
if (isCleanupCompleted) return;
|
||||
isCleanupCompleted = true;
|
||||
debug("cleaning up resources for %s:%d", host, port);
|
||||
clearTimeout(timeoutTask);
|
||||
socket.close();
|
||||
};
|
||||
|
||||
// Generic error handler
|
||||
socket.on("error", (err) => {
|
||||
debug("socket error for %s:%d - %s", host, port, err.message);
|
||||
cleanup();
|
||||
reject(err);
|
||||
});
|
||||
|
||||
socket.on("message", (pongPacket) => {
|
||||
debug("received %d bytes from %s:%d", pongPacket.length, host, port);
|
||||
try {
|
||||
const motd = parseUnconnectedPong(pongPacket);
|
||||
cleanup();
|
||||
@ -237,6 +245,8 @@ export const pingBedrock = (host, options = {}) => {
|
||||
|
||||
try {
|
||||
const pingPacket = createUnconnectedPingFrame(Date.now() - START_TIME);
|
||||
debug("sending Unconnected Ping packet to %s:%d", host, port);
|
||||
debug("packet: %o", pingPacket);
|
||||
socket.send(pingPacket, 0, pingPacket.length, port, host);
|
||||
} catch (err) {
|
||||
// Handle any immediate, synchronous errors that might occur when sending the ping packet
|
||||
|
26
lib/java.js
26
lib/java.js
@ -7,8 +7,11 @@
|
||||
|
||||
import net from "node:net";
|
||||
import dns from "node:dns/promises";
|
||||
import createDebug from "debug";
|
||||
import * as varint from "./varint.js";
|
||||
|
||||
const debug = createDebug("mineping:java");
|
||||
|
||||
/**
|
||||
* Represents the structured and user-friendly response from a server ping.
|
||||
* The fields and their optionality are based on the official protocol documentation.
|
||||
@ -70,6 +73,7 @@ function processResponse(buffer) {
|
||||
|
||||
// Check if the full packet has arrived yet.
|
||||
if (buffer.length < offset + packetLength) {
|
||||
debug("packet incomplete, waiting for more data");
|
||||
return null; // Incomplete packet, wait for more data.
|
||||
}
|
||||
|
||||
@ -86,12 +90,14 @@ function processResponse(buffer) {
|
||||
offset += jsonLengthResult.bytesRead;
|
||||
|
||||
if (buffer.length < offset + jsonLength) {
|
||||
debug("JSON string incomplete, waiting for more data");
|
||||
return null; // Incomplete JSON string, wait for more data.
|
||||
}
|
||||
|
||||
const jsonString = buffer
|
||||
.subarray(offset, offset + jsonLength)
|
||||
.toString("utf8");
|
||||
debug("received raw JSON response");
|
||||
const response = JSON.parse(jsonString);
|
||||
|
||||
// Return the response and any data that came after this packet.
|
||||
@ -101,6 +107,7 @@ function processResponse(buffer) {
|
||||
} catch (err) {
|
||||
// If the buffer is too short for a VarInt, it's a recoverable state.
|
||||
if (err.code === varint.ERR_VARINT_BUFFER_UNDERFLOW) {
|
||||
debug("buffer underflow while parsing VarInt, waiting for more data");
|
||||
return null; // Wait for more data.
|
||||
}
|
||||
// For malformed VarInts or JSON, throw the error to reject the promise.
|
||||
@ -128,26 +135,31 @@ export async function pingJava(host, options = {}) {
|
||||
timeout = 5000,
|
||||
protocolVersion = -1,
|
||||
} = options;
|
||||
debug("pinging Java server %s with options: %o", host, options);
|
||||
|
||||
let targetHost = host;
|
||||
let targetPort = fallbackPort;
|
||||
|
||||
try {
|
||||
debug("attempting SRV lookup for _minecraft._tcp.%s", host);
|
||||
const srvRecords = await dns.resolveSrv(`_minecraft._tcp.${host}`);
|
||||
if (srvRecords.length > 0) {
|
||||
targetHost = srvRecords[0].name;
|
||||
targetPort = srvRecords[0].port;
|
||||
debug("SRV lookup successful, new target: %s:%d", targetHost, targetPort);
|
||||
}
|
||||
} catch (err) {
|
||||
// Common errors like ENODATA or ENOTFOUND are expected when a server
|
||||
// does not have an SRV record, so we ignore them and proceed.
|
||||
if (!["ENODATA", "ENOTFOUND"].includes(err.code)) {
|
||||
debug("SRV lookup for %s failed (%s), using fallback", host, err.code);
|
||||
// For other errors we should re-throw.
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
debug("creating TCP connection to %s:%d", targetHost, targetPort);
|
||||
const socket = net.createConnection({ host: targetHost, port: targetPort });
|
||||
|
||||
// Prevent cleanup tasks from running more than once
|
||||
@ -164,6 +176,7 @@ export async function pingJava(host, options = {}) {
|
||||
const cleanup = () => {
|
||||
if (isCleanupCompleted) return;
|
||||
isCleanupCompleted = true;
|
||||
debug("cleaning up resources for %s:%d", targetHost, targetPort);
|
||||
clearTimeout(timeoutTask);
|
||||
socket.destroy();
|
||||
};
|
||||
@ -174,18 +187,25 @@ export async function pingJava(host, options = {}) {
|
||||
|
||||
// Generic error handler
|
||||
socket.on("error", (err) => {
|
||||
debug("socket error for %s:%d - %s", targetHost, targetPort, err.message);
|
||||
cleanup();
|
||||
reject(err);
|
||||
});
|
||||
|
||||
socket.on("close", () => {
|
||||
if (!isCleanupCompleted) {
|
||||
debug("socket for %s:%d closed prematurely", targetHost, targetPort);
|
||||
cleanup();
|
||||
reject(new Error("Socket closed unexpectedly without a response."));
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("connect", () => {
|
||||
debug(
|
||||
"socket connected to %s:%d, sending packets...",
|
||||
targetHost,
|
||||
targetPort
|
||||
);
|
||||
try {
|
||||
const handshakePacket = createHandshakePacket(
|
||||
host,
|
||||
@ -204,11 +224,17 @@ export async function pingJava(host, options = {}) {
|
||||
let incomingBuffer = Buffer.alloc(0);
|
||||
|
||||
socket.on("data", (data) => {
|
||||
debug(
|
||||
"received %d bytes of data, total buffer size is now %d bytes",
|
||||
data.length,
|
||||
incomingBuffer.length + data.length
|
||||
);
|
||||
incomingBuffer = Buffer.concat([incomingBuffer, data]);
|
||||
|
||||
try {
|
||||
const result = processResponse(incomingBuffer);
|
||||
if (result) {
|
||||
debug("successfully parsed full response");
|
||||
// We successfully parsed a response. Clean up before resolving.
|
||||
cleanup();
|
||||
resolve(result.response);
|
||||
|
9
package-lock.json
generated
9
package-lock.json
generated
@ -1,13 +1,16 @@
|
||||
{
|
||||
"name": "@minescope/mineping",
|
||||
"version": "1.6.1",
|
||||
"version": "1.7.0-beta.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@minescope/mineping",
|
||||
"version": "1.6.1",
|
||||
"version": "1.7.0-beta.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitest": "^3.2.3"
|
||||
},
|
||||
@ -917,7 +920,6 @@
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
@ -1067,7 +1069,6 @@
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@minescope/mineping",
|
||||
"version": "1.7.0-beta.0",
|
||||
"version": "1.7.0-beta.1",
|
||||
"description": "Ping both Minecraft Bedrock and Java servers.",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
@ -23,6 +23,9 @@
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": "^4.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitest": "^3.2.3"
|
||||
}
|
||||
|
Reference in New Issue
Block a user