From 7a3a5b3f222b8c7990a07f30c27e0d2214a20425 Mon Sep 17 00:00:00 2001 From: Nimi Date: Tue, 12 Nov 2024 11:20:30 -0600 Subject: [PATCH] LAN search, better error handling for joining servers --- main.js | 50 +++++++++++++++++++++++++- src/utils/mcserver.js | 84 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/utils/mcserver.js diff --git a/main.js b/main.js index 5e8cc97..0c93d2e 100644 --- a/main.js +++ b/main.js @@ -2,6 +2,8 @@ import { AgentProcess } from './src/process/agent-process.js'; import settings from './settings.js'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; +import { serverInfo, findServers } from './src/utils/mcserver.js'; +import mc from 'minecraft-protocol'; function parseArguments() { return yargs(hideBin(process.argv)) @@ -18,12 +20,58 @@ function getProfiles(args) { return args.profiles || settings.profiles; } -function main() { +async function getServer() { + let server = null; + let serverString = ""; + let serverVersion = ""; + + // Search for server + if (settings.port == -1) + { + console.log("No port provided. Searching for LAN server..."); + + await findServers(settings.host, true).then((servers) => { + if (servers.length > 0) + server = servers[0]; + }); + + if (server == null) + throw new Error(`No server found on LAN.`); + } + else + server = await serverInfo(settings.host, settings.port); + + // Server not found + if (server == null) + throw new Error(`Server not found. (Host: ${settings.host}, Port: ${settings.port}) Check the host and port in settings.js.`); + + serverString = `(Host: ${server.host}, Port: ${server.port}, Version: ${server.version})`; + + if (settings.minecraft_version === "auto") + serverVersion = server.version; + else + serverVersion = settings.minecraft_version; + + // Server version unsupported / mismatch + if (mc.supportedVersions.indexOf(serverVersion) === -1) + throw new Error(`A server was found ${serverString}, but version is unsupported. Supported versions are: ${mc.supportedVersions.join(", ")}.`); + else if (settings.minecraft_version !== "auto" && server.version !== settings.minecraft_version) + throw new Error(`A server was found ${serverString}, but version is incorrect. Expected ${settings.minecraft_version}, but found ${server.version}.`); + else + console.log(`Server found. ${serverString}`); + + return server; +} + +async function main() { const args = parseArguments(); const profiles = getProfiles(args); console.log(profiles); const { load_memory, init_message } = settings; + // Get server + const server = await getServer(); + for (let i=0; i} - A Promise that resolves to an array of server info objects. + */ +export async function serverInfo(ip, port, timeout = 100) { + return new Promise((resolve) => { + + setTimeout(() => { + resolve(null); // Resolve as null if no response within timeout + }, timeout); + + mc.ping({ + host: ip, + port + }, (err, response) => { + if (err) return resolve(null); + + const serverInfo = { + host: ip, + port, + name: response.description.text || 'No description provided.', + ping: response.latency, + version: response.version.name + }; + + resolve(serverInfo); + }); + }); +} + +/** + * Scans the IP address for Minecraft LAN servers and collects their info. + * @param {string} ip - The IP address to scan. + * @param {boolean} earlyExit - Whether to exit early after finding a server. + * @param {number} timeout - The connection timeout in ms. + * @returns {Promise} - A Promise that resolves to an array of server info objects. + */ +export async function findServers(ip, earlyExit = false, timeout = 100) { + const servers = []; + const startPort = 49000; + const endPort = 65000; + + const checkPort = (port) => { + return new Promise((resolve) => { + const socket = net.createConnection({ host: ip, port, timeout }, () => { + socket.end(); + resolve(port); // Port is open + }); + + socket.on('error', () => resolve(null)); // Port is closed + socket.on('timeout', () => { + socket.destroy(); + resolve(null); + }); + }); + }; + + // Surpress console output + const originalConsoleLog = console.log; + console.log = () => { }; + + for (let port = startPort; port <= endPort; port++) { + const openPort = await checkPort(port); + if (openPort) { + const server = await serverInfo(ip, port); + if (server) { + servers.push(server); + + if (earlyExit) break; + } + } + } + + // Restore console output + console.log = originalConsoleLog; + + return servers; +} \ No newline at end of file