mirror of
https://github.com/minescope/mineping.git
synced 2025-07-12 22:54:38 +03:00
Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
0b5c5e2938
|
|||
73a2fffe8b | |||
9469564736
|
|||
838ffc497a
|
|||
502029869a
|
|||
e3e7e293ed
|
|||
88ad92e59d
|
|||
009f542c55
|
|||
0b0bed4e71
|
|||
fa4c34d896
|
|||
296294ca96
|
|||
9d25aaf4ea
|
|||
c735604c38
|
|||
afdaa9eb3e
|
|||
435899309f | |||
13e6b8c6ff | |||
d7256eabe7
|
|||
afa2c3025f
|
|||
6c297d0b8c
|
|||
283e9b32c6 | |||
354fa212a6 | |||
d9bf4cfb3f | |||
9dace3748b
|
|||
0aa73655b1
|
@ -1,6 +1,6 @@
|
|||||||
# mineping
|
# mineping
|
||||||
|
|
||||||
`mineping` is a Javasript library thar provides Minecraft server ping protocol implementation. It can be used to collect information about the server, such as MODT, current online, server icon (java edition only) and etc.
|
This JavaScript library provides an implementation of the Minecraft server ping protocol. **It allows you to gather information about a Minecraft server**, such as the MOTD, current online players, server icon (Java Edition only), and more.
|
||||||
|
|
||||||
Mirror on my [<img src="https://git.zeldon.ru/assets/img/logo.svg" align="center" width="20" height="20"/> Git](https://git.zeldon.ru/zeldon/mineping)
|
Mirror on my [<img src="https://git.zeldon.ru/assets/img/logo.svg" align="center" width="20" height="20"/> Git](https://git.zeldon.ru/zeldon/mineping)
|
||||||
|
|
||||||
@ -10,6 +10,8 @@ Mirror on my [<img src="https://git.zeldon.ru/assets/img/logo.svg" align="center
|
|||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
|
To install `mineping`, simply run the following command:
|
||||||
|
|
||||||
```
|
```
|
||||||
npm i @minescope/mineping
|
npm i @minescope/mineping
|
||||||
```
|
```
|
||||||
@ -17,6 +19,7 @@ npm i @minescope/mineping
|
|||||||
## Loading and configuration the module
|
## Loading and configuration the module
|
||||||
|
|
||||||
### ES Modules (ESM)
|
### ES Modules (ESM)
|
||||||
|
If you are using ES Modules, you can import the library like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { pingJava, pingBedrock } from '@minescope/mineping';
|
import { pingJava, pingBedrock } from '@minescope/mineping';
|
||||||
@ -59,5 +62,7 @@ console.log(data);
|
|||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
|
Special thanks to the following projects:
|
||||||
|
|
||||||
- [mcping](https://github.com/Scetch/mcping) crate for Rust
|
- [mcping](https://github.com/Scetch/mcping) crate for Rust
|
||||||
- [mcping-js](https://github.com/Cryptkeeper/mcping-js) library for quering Minecraft Java Edition servers
|
- [mcping-js](https://github.com/Cryptkeeper/mcping-js) library for quering Minecraft Java Edition servers
|
||||||
|
@ -13,6 +13,7 @@ const results = await Promise.allSettled(pingPromises);
|
|||||||
for (let result of results) {
|
for (let result of results) {
|
||||||
if (result.status === 'rejected') {
|
if (result.status === 'rejected') {
|
||||||
console.error(result.reason);
|
console.error(result.reason);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(result.value);
|
console.log(result.value);
|
||||||
|
5
example/single.js
Normal file
5
example/single.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { pingBedrock } from "../index.js";
|
||||||
|
|
||||||
|
const host = "mc.nevertime.su";
|
||||||
|
const ping = await pingBedrock(host);
|
||||||
|
console.log(ping);
|
191
lib/bedrock.js
191
lib/bedrock.js
@ -1,124 +1,65 @@
|
|||||||
/**
|
/**
|
||||||
* Implementation of the RakNet ping/pong protocol.
|
* Implementation of the RakNet ping/pong protocol.
|
||||||
* @see https://wiki.vg/Raknet_Protocol#Unconnected_Ping
|
* @see https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Raknet_Protocol
|
||||||
*
|
|
||||||
* Data types:
|
|
||||||
* @see https://wiki.vg/Raknet_Protocol#Data_types
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
import dgram from 'dgram';
|
import dgram from "node:dgram";
|
||||||
|
import crypto from "node:crypto";
|
||||||
|
|
||||||
|
const MAGIC = "00ffff00fefefefefdfdfdfd12345678";
|
||||||
const START_TIME = new Date().getTime();
|
const START_TIME = new Date().getTime();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a buffer with the specified length.
|
* Creates an Unconnected Ping packet.
|
||||||
* @param {number} length - The length of the buffer.
|
* @param {number} pingId
|
||||||
* @returns {Buffer} - The created buffer.
|
* @returns {Buffer}
|
||||||
|
* @see {@link https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Raknet_Protocol#Unconnected_Ping}
|
||||||
*/
|
*/
|
||||||
const createBuffer = (length) => {
|
const createUnconnectedPingFrame = (timestamp) => {
|
||||||
const buffer = Buffer.alloc(length);
|
const buffer = Buffer.alloc(33);
|
||||||
buffer[0] = 0x01;
|
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;
|
return buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a BigInt value to the buffer at the specified offset using big-endian byte order.
|
* Extract Modt from Unconnected Pong Packet and convert to an object
|
||||||
* @param {Buffer} buffer - The buffer to write to.
|
* @param {Buffer} unconnectedPongPacket
|
||||||
* @param {number} value - The BigInt value to write.
|
* @returns {Object}
|
||||||
* @param {number} offset - The offset in the buffer to write the value.
|
* @see {@link https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Raknet_Protocol#Unconnected_Pong}
|
||||||
*/
|
*/
|
||||||
const writeBigInt64BE = (buffer, value, offset) => {
|
const extractModt = (unconnectedPongPacket) => {
|
||||||
buffer.writeBigInt64BE(BigInt(value), offset);
|
// 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],
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
return parsedComponents;
|
||||||
* Copies the specified hex value to the buffer at the specified offset.
|
|
||||||
* @param {Buffer} buffer - The buffer to copy to.
|
|
||||||
* @param {string} hex - The hex value to copy.
|
|
||||||
* @param {number} offset - The offset in the buffer to copy the value.
|
|
||||||
*/
|
|
||||||
const copyHexToBuffer = (buffer, hex, offset) => {
|
|
||||||
Buffer.from(hex, 'hex').copy(buffer, offset);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a BigInt value from the buffer at the specified offset using big-endian byte order.
|
|
||||||
* @param {Buffer} buffer - The buffer to read from.
|
|
||||||
* @param {number} offset - The offset in the buffer to read the value.
|
|
||||||
* @returns {BigInt} - The read BigInt value.
|
|
||||||
*/
|
|
||||||
const readBigInt64BE = (buffer, offset) => {
|
|
||||||
return buffer.readBigInt64BE(offset);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a string from the buffer at the specified offset.
|
|
||||||
* @param {Buffer} buffer - The buffer to read from.
|
|
||||||
* @param {number} offset - The offset in the buffer to read the string.
|
|
||||||
* @returns {string} - The read string.
|
|
||||||
*/
|
|
||||||
const readStringFromBuffer = (buffer, offset) => {
|
|
||||||
const length = buffer.readUInt16BE(offset);
|
|
||||||
return buffer.toString('utf8', offset + 2, offset + 2 + length);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the advertise string into an object with properties.
|
|
||||||
* @param {string} advertiseStr - The advertise string to parse.
|
|
||||||
* @returns {Object} - The parsed object with properties.
|
|
||||||
*/
|
|
||||||
const parseAdvertiseString = (advertiseStr) => {
|
|
||||||
const parts = advertiseStr.split(';');
|
|
||||||
return {
|
|
||||||
gameId: parts[0],
|
|
||||||
description: parts[1],
|
|
||||||
protocolVersion: parts[2],
|
|
||||||
gameVersion: parts[3],
|
|
||||||
currentPlayers: parts[4],
|
|
||||||
maxPlayers: parts[5],
|
|
||||||
name: parts[7],
|
|
||||||
mode: parts[8]
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an Unconnected Ping buffer.
|
|
||||||
* @param {number} pingId - The ping ID.
|
|
||||||
* @returns {Buffer} - The Unconnected Ping buffer.
|
|
||||||
* @see {@link https://wiki.vg/Raknet_Protocol#Unconnected_Ping}
|
|
||||||
*/
|
|
||||||
const UNCONNECTED_PING = (pingId) => {
|
|
||||||
const buffer = createBuffer(35);
|
|
||||||
writeBigInt64BE(buffer, pingId, 1);
|
|
||||||
copyHexToBuffer(buffer, '00ffff00fefefefefdfdfdfd12345678', 9);
|
|
||||||
writeBigInt64BE(buffer, 0, 25);
|
|
||||||
return buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decodes an Unconnected Pong buffer and returns the parsed data.
|
|
||||||
* @param {Buffer} buffer - The Unconnected Pong buffer.
|
|
||||||
* @returns {Object} - The parsed Unconnected Pong data.
|
|
||||||
* @see {@link https://wiki.vg/Raknet_Protocol#Unconnected_Pong}
|
|
||||||
*/
|
|
||||||
const UNCONNECTED_PONG = (buffer) => {
|
|
||||||
const pingId = readBigInt64BE(buffer, 1);
|
|
||||||
const serverId = readBigInt64BE(buffer, 9);
|
|
||||||
let offset = 25;
|
|
||||||
let advertiseStr;
|
|
||||||
|
|
||||||
try {
|
|
||||||
advertiseStr = readStringFromBuffer(buffer, offset);
|
|
||||||
} catch (err) {
|
|
||||||
const length = parseInt(err.message.substr(err.message.indexOf(',') + 2, 3));
|
|
||||||
advertiseStr = buffer.toString('utf8', offset, offset + length);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedAdvertiseStr = parseAdvertiseString(advertiseStr);
|
|
||||||
|
|
||||||
return { pingId, advertiseStr, serverId, offset, ...parsedAdvertiseStr };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,12 +70,12 @@ const UNCONNECTED_PONG = (buffer) => {
|
|||||||
* @param {number} [timeout=5000] - The timeout duration in milliseconds.
|
* @param {number} [timeout=5000] - The timeout duration in milliseconds.
|
||||||
*/
|
*/
|
||||||
const ping = (host, port = 19132, cb, timeout = 5000) => {
|
const ping = (host, port = 19132, cb, timeout = 5000) => {
|
||||||
const socket = dgram.createSocket('udp4');
|
const socket = dgram.createSocket("udp4");
|
||||||
|
|
||||||
// Set manual timeout interval.
|
// Set manual timeout interval.
|
||||||
// This ensures the connection will NEVER hang regardless of internal state
|
// This ensures the connection will NEVER hang regardless of internal state
|
||||||
const timeoutTask = setTimeout(() => {
|
const timeoutTask = setTimeout(() => {
|
||||||
socket.emit('error', new Error('Socket timeout'));
|
socket.emit("error", new Error("Socket timeout"));
|
||||||
}, timeout);
|
}, timeout);
|
||||||
|
|
||||||
const closeSocket = () => {
|
const closeSocket = () => {
|
||||||
@ -161,44 +102,31 @@ const ping = (host, port = 19132, cb, timeout = 5000) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const ping = UNCONNECTED_PING(new Date().getTime() - START_TIME);
|
const ping = createUnconnectedPingFrame(new Date().getTime() - START_TIME);
|
||||||
socket.send(ping, 0, ping.length, port, host);
|
socket.send(ping, 0, ping.length, port, host);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
handleError(err);
|
handleError(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.on('message', (msg) => {
|
socket.on("message", (pongPacket) => {
|
||||||
const id = msg[0];
|
const id = pongPacket[0];
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case 0x1c: {
|
case 0x1c: {
|
||||||
const pong = UNCONNECTED_PONG(msg);
|
const modtObject = extractModt(pongPacket);
|
||||||
const clientData = {
|
|
||||||
version: {
|
|
||||||
name: pong.name,
|
|
||||||
protocol: pong.protocolVersion
|
|
||||||
},
|
|
||||||
players: {
|
|
||||||
max: pong.maxPlayers,
|
|
||||||
online: pong.currentPlayers
|
|
||||||
},
|
|
||||||
description: pong.description.replace(/\xA7[0-9A-FK-OR]/ig, ''),
|
|
||||||
gamemode: pong.mode
|
|
||||||
};
|
|
||||||
|
|
||||||
closeSocket();
|
closeSocket();
|
||||||
cb(clientData, null);
|
cb(modtObject, null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
handleError(new Error('Received unexpected packet'));
|
handleError(new Error("Received unexpected packet"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('error', handleError);
|
socket.on("error", handleError);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -209,13 +137,18 @@ const ping = (host, port = 19132, cb, timeout = 5000) => {
|
|||||||
* @returns {Promise<import('../types/index.js').BedrockPingResponse>}
|
* @returns {Promise<import('../types/index.js').BedrockPingResponse>}
|
||||||
*/
|
*/
|
||||||
export const pingBedrock = (host, options = {}) => {
|
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) => {
|
return new Promise((resolve, reject) => {
|
||||||
ping(host, port, (res, err) => {
|
ping(
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
(res, err) => {
|
||||||
err ? reject(err) : resolve(res);
|
err ? reject(err) : resolve(res);
|
||||||
}, timeout);
|
},
|
||||||
|
timeout
|
||||||
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
71
lib/java.js
71
lib/java.js
@ -1,29 +1,29 @@
|
|||||||
/**
|
/**
|
||||||
* Implementation of the Java Minecraft ping protocol.
|
* Implementation of the Java Minecraft ping protocol.
|
||||||
* @see https://wiki.vg/Server_List_Ping
|
* @see https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Server_List_Ping
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
import net from 'net';
|
import net from "node:net";
|
||||||
import varint from './varint.js';
|
import varint from "./varint.js";
|
||||||
|
|
||||||
const PROTOCOL_VERSION = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ping a Minecraft Java server.
|
* Ping a Minecraft Java server.
|
||||||
* @param {string} host The host of the Java server.
|
* @param {string} host The host of the Java server.
|
||||||
|
* @param {string} virtualHost The host sent in handshake.
|
||||||
* @param {number} [port=25565] The port of the Java server.
|
* @param {number} [port=25565] The port of the Java server.
|
||||||
* @param {function} cb The callback function to handle the ping response.
|
* @param {function} cb The callback function to handle the ping response.
|
||||||
* @param {number} [timeout=5000] The timeout duration in milliseconds.
|
* @param {number} [timeout=5000] The timeout duration in milliseconds.
|
||||||
|
* @param {number} [protocolVersion=-1] The protocol version of the Java client.
|
||||||
*/
|
*/
|
||||||
function ping(host, port = 25565, cb, timeout = 5000) {
|
function ping(host, virtualHost, port = 25565, cb, timeout = 5000, protocolVersion = -1) {
|
||||||
const socket = net.createConnection(({ host, port }));
|
const socket = net.createConnection({ host, port });
|
||||||
|
|
||||||
// Set manual timeout interval.
|
// Set manual timeout interval.
|
||||||
// This ensures the connection will NEVER hang regardless of internal state
|
// This ensures the connection will NEVER hang regardless of internal state
|
||||||
const timeoutTask = setTimeout(() => {
|
const timeoutTask = setTimeout(() => {
|
||||||
socket.emit('error', new Error('Socket timeout'));
|
socket.emit("error", new Error("Socket timeout"));
|
||||||
}, timeout);
|
}, timeout);
|
||||||
|
|
||||||
const closeSocket = () => {
|
const closeSocket = () => {
|
||||||
@ -53,34 +53,32 @@ function ping(host, port = 25565, cb, timeout = 5000) {
|
|||||||
// This prevents the runtime from delaying the write at all
|
// This prevents the runtime from delaying the write at all
|
||||||
socket.setNoDelay(true);
|
socket.setNoDelay(true);
|
||||||
|
|
||||||
socket.on('connect', () => {
|
socket.on("connect", () => {
|
||||||
const handshake = varint.concat([
|
const handshake = varint.concat([
|
||||||
varint.encodeInt(0),
|
varint.encodeInt(0),
|
||||||
varint.encodeInt(PROTOCOL_VERSION),
|
varint.encodeInt(protocolVersion),
|
||||||
varint.encodeInt(host.length),
|
varint.encodeInt(virtualHost.length),
|
||||||
varint.encodeString(host),
|
varint.encodeString(virtualHost),
|
||||||
varint.encodeUShort(port),
|
varint.encodeUShort(port),
|
||||||
varint.encodeInt(1)
|
varint.encodeInt(1),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
socket.write(handshake);
|
socket.write(handshake);
|
||||||
|
|
||||||
const request = varint.concat([
|
const request = varint.concat([varint.encodeInt(0)]);
|
||||||
varint.encodeInt(0)
|
|
||||||
]);
|
|
||||||
|
|
||||||
socket.write(request);
|
socket.write(request);
|
||||||
});
|
});
|
||||||
|
|
||||||
let incomingBuffer = Buffer.alloc(0);
|
let incomingBuffer = Buffer.alloc(0);
|
||||||
|
|
||||||
socket.on('data', (data) => {
|
socket.on("data", (data) => {
|
||||||
incomingBuffer = Buffer.concat([incomingBuffer, data]);
|
incomingBuffer = Buffer.concat([incomingBuffer, data]);
|
||||||
|
|
||||||
// Wait until incomingBuffer is at least 5 bytes long to ensure it has captured the first VarInt value
|
// Wait until incomingBuffer is at least 5 bytes long to ensure it has captured the first VarInt value
|
||||||
// This value is used to determine the full read length of the response
|
// This value is used to determine the full read length of the response
|
||||||
// "VarInts are never longer than 5 bytes"
|
// "VarInts are never longer than 5 bytes"
|
||||||
// https://wiki.vg/Data_types#VarInt_and_VarLong
|
// https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Data_types#VarInt_and_VarLong
|
||||||
if (incomingBuffer.length < 5) {
|
if (incomingBuffer.length < 5) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -93,12 +91,20 @@ function ping(host, port = 25565, cb, timeout = 5000) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const packetId = varint.decodeInt(incomingBuffer, varint.decodeLength(packetLength));
|
const packetId = varint.decodeInt(
|
||||||
|
incomingBuffer,
|
||||||
|
varint.decodeLength(packetLength)
|
||||||
|
);
|
||||||
|
|
||||||
if (packetId === 0) {
|
if (packetId === 0) {
|
||||||
const data = incomingBuffer.subarray(varint.decodeLength(packetLength) + varint.decodeLength(packetId));
|
const data = incomingBuffer.subarray(
|
||||||
|
varint.decodeLength(packetLength) + varint.decodeLength(packetId)
|
||||||
|
);
|
||||||
const responseLength = varint.decodeInt(data, 0);
|
const responseLength = varint.decodeInt(data, 0);
|
||||||
const response = data.subarray(varint.decodeLength(responseLength), varint.decodeLength(responseLength) + responseLength);
|
const response = data.subarray(
|
||||||
|
varint.decodeLength(responseLength),
|
||||||
|
varint.decodeLength(responseLength) + responseLength
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const message = JSON.parse(response);
|
const message = JSON.parse(response);
|
||||||
@ -109,28 +115,35 @@ function ping(host, port = 25565, cb, timeout = 5000) {
|
|||||||
handleError(err);
|
handleError(err);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
handleError(new Error('Received unexpected packet'));
|
handleError(new Error("Received unexpected packet"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('error', handleError);
|
socket.on("error", handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronously ping Minecraft Java server.
|
* Asynchronously ping Minecraft Java server.
|
||||||
* The optional `options` argument can be an object with a `ping` (default is `25565`) or/and `timeout` (default is `5000`) property.
|
* The optional `options` argument can be an object with a `port` (default is `25565`) or/and `timeout` (default is `5000`) or/and `protocolVersion` (default is `-1`) property.
|
||||||
* @param {string} host The Java server address.
|
* @param {string} host The Java server address.
|
||||||
* @param {import('../types/index.js').PingOptions} options The configuration for pinging Minecraft Java server.
|
* @param {import('../types/index.js').PingOptions} options The configuration for pinging Minecraft Java server.
|
||||||
* @returns {Promise<import('../types/index.js').JavaPingResponse>}
|
* @returns {Promise<import('../types/index.js').JavaPingResponse>}
|
||||||
*/
|
*/
|
||||||
export function pingJava(host, options = {}) {
|
export function pingJava(host, options = {}) {
|
||||||
if (!host) throw new Error('Host argument is not provided');
|
if (!host) throw new Error("Host argument is not provided");
|
||||||
|
|
||||||
const { port = 25565, timeout = 5000 } = options;
|
const { port = 25565, timeout = 5000, protocolVersion = -1, virtualHost = null } = options;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
ping(host, port, (res, err) => {
|
ping(
|
||||||
|
host,
|
||||||
|
virtualHost || host,
|
||||||
|
port,
|
||||||
|
(res, err) => {
|
||||||
err ? reject(err) : resolve(res);
|
err ? reject(err) : resolve(res);
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
protocolVersion
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// https://wiki.vg/Data_types
|
// https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol#Data_types
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A utility object for encoding and decoding varints.
|
* A utility object for encoding and decoding varints.
|
||||||
@ -10,13 +10,13 @@ const varint = {
|
|||||||
* @returns {Buffer}
|
* @returns {Buffer}
|
||||||
*/
|
*/
|
||||||
encodeInt: (val) => {
|
encodeInt: (val) => {
|
||||||
// "constInts are never longer than 5 bytes"
|
// "VarInts are never longer than 5 bytes"
|
||||||
// https://wiki.vg/Data_types#constInt_and_constLong
|
// https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Data_types#VarInt_and_VarLong
|
||||||
const buf = Buffer.alloc(5);
|
const buf = Buffer.alloc(5);
|
||||||
let written = 0;
|
let written = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const byte = val & 0x7F;
|
const byte = val & 0x7f;
|
||||||
val >>>= 7;
|
val >>>= 7;
|
||||||
|
|
||||||
if (val === 0) {
|
if (val === 0) {
|
||||||
@ -36,7 +36,7 @@ const varint = {
|
|||||||
* @returns {Buffer}
|
* @returns {Buffer}
|
||||||
*/
|
*/
|
||||||
encodeString: (val) => {
|
encodeString: (val) => {
|
||||||
return Buffer.from(val, 'utf-8');
|
return Buffer.from(val, "utf-8");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,7 +45,7 @@ const varint = {
|
|||||||
* @returns {Buffer}
|
* @returns {Buffer}
|
||||||
*/
|
*/
|
||||||
encodeUShort: (val) => {
|
encodeUShort: (val) => {
|
||||||
return Buffer.from([val >> 8, val & 0xFF]);
|
return Buffer.from([val >> 8, val & 0xff]);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,10 +60,7 @@ const varint = {
|
|||||||
length += chunk.length;
|
length += chunk.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
const buffer = [
|
const buffer = [varint.encodeInt(length), ...chunks];
|
||||||
varint.encodeInt(length),
|
|
||||||
...chunks
|
|
||||||
];
|
|
||||||
|
|
||||||
return Buffer.concat(buffer);
|
return Buffer.concat(buffer);
|
||||||
},
|
},
|
||||||
@ -81,7 +78,7 @@ const varint = {
|
|||||||
while (true) {
|
while (true) {
|
||||||
const byte = buffer.readUInt8(offset++);
|
const byte = buffer.readUInt8(offset++);
|
||||||
|
|
||||||
val |= (byte & 0x7F) << count++ * 7;
|
val |= (byte & 0x7f) << (count++ * 7);
|
||||||
|
|
||||||
if ((byte & 0x80) !== 0x80) {
|
if ((byte & 0x80) !== 0x80) {
|
||||||
break;
|
break;
|
||||||
@ -109,19 +106,26 @@ const varint = {
|
|||||||
const N9 = Math.pow(2, 63);
|
const N9 = Math.pow(2, 63);
|
||||||
|
|
||||||
// Return the number of bytes required based on the value
|
// Return the number of bytes required based on the value
|
||||||
return (
|
return val < N1
|
||||||
val < N1 ? 1
|
? 1
|
||||||
: val < N2 ? 2
|
: val < N2
|
||||||
: val < N3 ? 3
|
? 2
|
||||||
: val < N4 ? 4
|
: val < N3
|
||||||
: val < N5 ? 5
|
? 3
|
||||||
: val < N6 ? 6
|
: val < N4
|
||||||
: val < N7 ? 7
|
? 4
|
||||||
: val < N8 ? 8
|
: val < N5
|
||||||
: val < N9 ? 9
|
? 5
|
||||||
: 10
|
: val < N6
|
||||||
);
|
? 6
|
||||||
}
|
: val < N7
|
||||||
|
? 7
|
||||||
|
: val < N8
|
||||||
|
? 8
|
||||||
|
: val < N9
|
||||||
|
? 9
|
||||||
|
: 10;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default varint;
|
export default varint;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@minescope/mineping",
|
"name": "@minescope/mineping",
|
||||||
"version": "1.1.1",
|
"version": "1.6.0",
|
||||||
"description": "Ping both Minecraft Bedrock and Java servers.",
|
"description": "Ping both Minecraft Bedrock and Java servers.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"types": "types/index.d.ts",
|
"types": "types/index.d.ts",
|
||||||
|
24
types/lib/bedrock.d.ts
vendored
24
types/lib/bedrock.d.ts
vendored
@ -2,22 +2,25 @@
|
|||||||
* @param port The server port.
|
* @param port The server port.
|
||||||
* @param timeout The read/write socket timeout.
|
* @param timeout The read/write socket timeout.
|
||||||
*/
|
*/
|
||||||
export type PingOptions = {
|
export type BedrockPingOptions = {
|
||||||
port: number,
|
port?: number;
|
||||||
timeout: number;
|
timeout?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BedrockPingResponse = {
|
export type BedrockPingResponse = {
|
||||||
version: {
|
edition: string;
|
||||||
name: string;
|
name: string;
|
||||||
protocol: string;
|
version: {
|
||||||
|
protocolVersion: number;
|
||||||
|
minecraftVersion: string;
|
||||||
};
|
};
|
||||||
players: {
|
players: {
|
||||||
max: string;
|
online: number;
|
||||||
online: string;
|
max: number;
|
||||||
};
|
};
|
||||||
description: string;
|
serverId: string;
|
||||||
gamemode: string;
|
mapName: string;
|
||||||
|
gameMode: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,6 +29,7 @@ export type BedrockPingResponse = {
|
|||||||
* The optional `options` argument can be an object with a `ping` (default is `19132`) or/and `timeout` (default is `5000`) property.
|
* The optional `options` argument can be an object with a `ping` (default is `19132`) or/and `timeout` (default is `5000`) property.
|
||||||
*
|
*
|
||||||
* @param host The Bedrock server address.
|
* @param host The Bedrock server address.
|
||||||
|
* @param options The configuration for pinging Minecraft Bedrock server.
|
||||||
*
|
*
|
||||||
* ```js
|
* ```js
|
||||||
* import { pingBedrock } from '@minescope/mineping';
|
* import { pingBedrock } from '@minescope/mineping';
|
||||||
@ -45,5 +49,5 @@ export type BedrockPingResponse = {
|
|||||||
* ```
|
* ```
|
||||||
* @see [source](https://github.com/minescope/mineping/blob/915edbec9c9ad811459458600af3531ec0836911/lib/bedrock.js#L204)
|
* @see [source](https://github.com/minescope/mineping/blob/915edbec9c9ad811459458600af3531ec0836911/lib/bedrock.js#L204)
|
||||||
*/
|
*/
|
||||||
export function pingBedrock(host: string, options?: PingOptions): Promise<BedrockPingResponse>;
|
export function pingBedrock(host: string, options?: BedrockPingOptions): Promise<BedrockPingResponse>;
|
||||||
|
|
||||||
|
24
types/lib/java.d.ts
vendored
24
types/lib/java.d.ts
vendored
@ -1,8 +1,18 @@
|
|||||||
import { PingOptions } from "./bedrock";
|
/**
|
||||||
|
* @param port The server port.
|
||||||
|
* @param timeout The read/write socket timeout.
|
||||||
|
* @param protocolVersion The protocol version.
|
||||||
|
*/
|
||||||
|
export type JavaPingOptions = {
|
||||||
|
port?: number | undefined;
|
||||||
|
timeout?: number | undefined;
|
||||||
|
protocolVersion?: number | undefined;
|
||||||
|
virtualHost?: string | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSON format chat component used for description field.
|
* JSON format chat component used for description field.
|
||||||
* @see https://wiki.vg/Chat
|
* @see https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Chat
|
||||||
*/
|
*/
|
||||||
export type ChatComponent = {
|
export type ChatComponent = {
|
||||||
text: string;
|
text: string;
|
||||||
@ -16,13 +26,13 @@ export type ChatComponent = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type SampleProp = {
|
export type SampleProp = {
|
||||||
name: string,
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `JSON Response` field of Response packet.
|
* `JSON Response` field of Response packet.
|
||||||
* @see https://wiki.vg/Server_List_Ping#Response
|
* @see https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Server_List_Ping#Status_Response
|
||||||
*/
|
*/
|
||||||
export type JavaPingResponse = {
|
export type JavaPingResponse = {
|
||||||
version: {
|
version: {
|
||||||
@ -67,5 +77,7 @@ export type JavaPingResponse = {
|
|||||||
* ```
|
* ```
|
||||||
* @see [source](https://github.com/minescope/mineping/blob/915edbec9c9ad811459458600af3531ec0836911/lib/java.js#L117)
|
* @see [source](https://github.com/minescope/mineping/blob/915edbec9c9ad811459458600af3531ec0836911/lib/java.js#L117)
|
||||||
*/
|
*/
|
||||||
export function pingJava(host: string, options?: PingOptions): Promise<JavaPingResponse>;
|
export function pingJava(
|
||||||
|
host: string,
|
||||||
|
options?: JavaPingOptions
|
||||||
|
): Promise<JavaPingResponse>;
|
||||||
|
Reference in New Issue
Block a user