mirror of
https://github.com/minescope/mineping.git
synced 2025-07-12 12:24:36 +03:00
docs: update references to wiki.vg
wiki.vg has been shut down and is in the process of being merged into minecraft.wiki
This commit is contained in:
183
lib/bedrock.js
183
lib/bedrock.js
@ -1,12 +1,12 @@
|
||||
/**
|
||||
* Implementation of the RakNet ping/pong protocol.
|
||||
* @see https://wiki.vg/Raknet_Protocol
|
||||
* @see https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Raknet_Protocol
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
"use strict";
|
||||
|
||||
import dgram from 'node:dgram';
|
||||
import crypto from 'node:crypto'
|
||||
import dgram from "node:dgram";
|
||||
import crypto from "node:crypto";
|
||||
|
||||
const MAGIC = "00ffff00fefefefefdfdfdfd12345678";
|
||||
const START_TIME = new Date().getTime();
|
||||
@ -15,47 +15,51 @@ const START_TIME = new Date().getTime();
|
||||
* Creates an Unconnected Ping packet.
|
||||
* @param {number} pingId
|
||||
* @returns {Buffer}
|
||||
* @see {@link https://wiki.vg/Raknet_Protocol#Unconnected_Ping}
|
||||
* @see {@link https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Raknet_Protocol#Unconnected_Ping}
|
||||
*/
|
||||
const createUnconnectedPingFrame = (timestamp) => {
|
||||
const buffer = Buffer.alloc(33);
|
||||
buffer.writeUInt8(0x01, 0); // Packet ID
|
||||
buffer.writeBigInt64LE(BigInt(timestamp), 1); // Timestamp
|
||||
Buffer.from(MAGIC, "hex").copy(buffer, 9); // OFFLINE_MESSAGE_DATA_ID (Magic)
|
||||
Buffer.from(crypto.randomBytes(8)).copy(buffer, 25); // Client GUID
|
||||
return buffer;
|
||||
const buffer = Buffer.alloc(33);
|
||||
buffer.writeUInt8(0x01, 0); // Packet ID
|
||||
buffer.writeBigInt64LE(BigInt(timestamp), 1); // Timestamp
|
||||
Buffer.from(MAGIC, "hex").copy(buffer, 9); // OFFLINE_MESSAGE_DATA_ID (Magic)
|
||||
Buffer.from(crypto.randomBytes(8)).copy(buffer, 25); // Client GUID
|
||||
return buffer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract Modt from Unconnected Pong Packet and convert to an object
|
||||
* @param {Buffer} unconnectedPongPacket
|
||||
* @returns {Object}
|
||||
* @see {@link https://wiki.vg/Raknet_Protocol#Unconnected_Pong}
|
||||
* @see {@link https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Raknet_Protocol#Unconnected_Pong}
|
||||
*/
|
||||
const extractModt = (unconnectedPongPacket) => {
|
||||
// Skip everything to Modt
|
||||
const offset = 33;
|
||||
const length = unconnectedPongPacket.readUInt16BE(offset);
|
||||
let modt = unconnectedPongPacket.toString("utf-8", offset + 2, offset + 2 + length);
|
||||
// Skip everything to Modt
|
||||
const offset = 33;
|
||||
const length = unconnectedPongPacket.readUInt16BE(offset);
|
||||
let modt = unconnectedPongPacket.toString(
|
||||
"utf-8",
|
||||
offset + 2,
|
||||
offset + 2 + length
|
||||
);
|
||||
|
||||
const components = modt.split(';');
|
||||
const parsedComponents = {
|
||||
edition: components[0],
|
||||
name: components[1],
|
||||
version: {
|
||||
protocolVersion: Number(components[2]),
|
||||
minecraftVersion: components[3],
|
||||
},
|
||||
players: {
|
||||
online: Number(components[4]),
|
||||
max: Number(components[5])
|
||||
},
|
||||
serverId: components[6],
|
||||
mapName: components[7],
|
||||
gameMode: components[8]
|
||||
};
|
||||
const components = modt.split(";");
|
||||
const parsedComponents = {
|
||||
edition: components[0],
|
||||
name: components[1],
|
||||
version: {
|
||||
protocolVersion: Number(components[2]),
|
||||
minecraftVersion: components[3],
|
||||
},
|
||||
players: {
|
||||
online: Number(components[4]),
|
||||
max: Number(components[5]),
|
||||
},
|
||||
serverId: components[6],
|
||||
mapName: components[7],
|
||||
gameMode: components[8],
|
||||
};
|
||||
|
||||
return parsedComponents;
|
||||
return parsedComponents;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -66,63 +70,63 @@ const extractModt = (unconnectedPongPacket) => {
|
||||
* @param {number} [timeout=5000] - The timeout duration in milliseconds.
|
||||
*/
|
||||
const ping = (host, port = 19132, cb, timeout = 5000) => {
|
||||
const socket = dgram.createSocket('udp4');
|
||||
const socket = dgram.createSocket("udp4");
|
||||
|
||||
// Set manual timeout interval.
|
||||
// This ensures the connection will NEVER hang regardless of internal state
|
||||
const timeoutTask = setTimeout(() => {
|
||||
socket.emit('error', new Error('Socket timeout'));
|
||||
}, timeout);
|
||||
// Set manual timeout interval.
|
||||
// This ensures the connection will NEVER hang regardless of internal state
|
||||
const timeoutTask = setTimeout(() => {
|
||||
socket.emit("error", new Error("Socket timeout"));
|
||||
}, timeout);
|
||||
|
||||
const closeSocket = () => {
|
||||
socket.close();
|
||||
clearTimeout(timeoutTask);
|
||||
};
|
||||
const closeSocket = () => {
|
||||
socket.close();
|
||||
clearTimeout(timeoutTask);
|
||||
};
|
||||
|
||||
// Generic error handler
|
||||
// This protects multiple error callbacks given the complex socket state
|
||||
// This is mostly dangerous since it can swallow errors
|
||||
let didFireError = false;
|
||||
// Generic error handler
|
||||
// This protects multiple error callbacks given the complex socket state
|
||||
// This is mostly dangerous since it can swallow errors
|
||||
let didFireError = false;
|
||||
|
||||
/**
|
||||
* Handle any error that occurs during the ping process.
|
||||
* @param {Error} err The error that occurred.
|
||||
*/
|
||||
const handleError = (err) => {
|
||||
closeSocket();
|
||||
/**
|
||||
* Handle any error that occurs during the ping process.
|
||||
* @param {Error} err The error that occurred.
|
||||
*/
|
||||
const handleError = (err) => {
|
||||
closeSocket();
|
||||
|
||||
if (!didFireError) {
|
||||
didFireError = true;
|
||||
cb(null, err);
|
||||
}
|
||||
};
|
||||
if (!didFireError) {
|
||||
didFireError = true;
|
||||
cb(null, err);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const ping = createUnconnectedPingFrame(new Date().getTime() - START_TIME);
|
||||
socket.send(ping, 0, ping.length, port, host);
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
}
|
||||
try {
|
||||
const ping = createUnconnectedPingFrame(new Date().getTime() - START_TIME);
|
||||
socket.send(ping, 0, ping.length, port, host);
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
}
|
||||
|
||||
socket.on('message', (pongPacket) => {
|
||||
const id = pongPacket[0];
|
||||
socket.on("message", (pongPacket) => {
|
||||
const id = pongPacket[0];
|
||||
|
||||
switch (id) {
|
||||
case 0x1c: {
|
||||
const modtObject = extractModt(pongPacket);
|
||||
closeSocket();
|
||||
cb(modtObject, null);
|
||||
break;
|
||||
}
|
||||
switch (id) {
|
||||
case 0x1c: {
|
||||
const modtObject = extractModt(pongPacket);
|
||||
closeSocket();
|
||||
cb(modtObject, null);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
handleError(new Error('Received unexpected packet'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
default: {
|
||||
handleError(new Error("Received unexpected packet"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('error', handleError);
|
||||
socket.on("error", handleError);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -133,13 +137,18 @@ const ping = (host, port = 19132, cb, timeout = 5000) => {
|
||||
* @returns {Promise<import('../types/index.js').BedrockPingResponse>}
|
||||
*/
|
||||
export const pingBedrock = (host, options = {}) => {
|
||||
if (!host) throw new Error('Host argument is not provided');
|
||||
if (!host) throw new Error("Host argument is not provided");
|
||||
|
||||
const { port = 19132, timeout = 5000 } = options;
|
||||
const { port = 19132, timeout = 5000 } = options;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
ping(host, port, (res, err) => {
|
||||
err ? reject(err) : resolve(res);
|
||||
}, timeout);
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
ping(
|
||||
host,
|
||||
port,
|
||||
(res, err) => {
|
||||
err ? reject(err) : resolve(res);
|
||||
},
|
||||
timeout
|
||||
);
|
||||
});
|
||||
};
|
||||
|
Reference in New Issue
Block a user