Merge remote-tracking branch 'Nimikita/join-auto-detect' into auto-server

This commit is contained in:
MaxRobinsonTheGreat 2025-08-25 14:03:18 -05:00
commit 61dafbc77d
4 changed files with 161 additions and 4 deletions

View file

@ -10,7 +10,7 @@ Do not connect this bot to public servers with coding enabled. This project allo
## Requirements ## Requirements
- [Minecraft Java Edition](https://www.minecraft.net/en-us/store/minecraft-java-bedrock-edition-pc) (up to v1.21.1, recommend v1.21.1) - [Minecraft Java Edition](https://www.minecraft.net/en-us/store/minecraft-java-bedrock-edition-pc) (up to v1.21.4, recommend v1.21.1)
- [Node.js Installed](https://nodejs.org/) (at least v18) - [Node.js Installed](https://nodejs.org/) (at least v18)
- One of these: [OpenAI API Key](https://openai.com/blog/openai-api) | [Gemini API Key](https://aistudio.google.com/app/apikey) | [Anthropic API Key](https://docs.anthropic.com/claude/docs/getting-access-to-claude) | [Replicate API Key](https://replicate.com/) | [Hugging Face API Key](https://huggingface.co/) | [Groq API Key](https://console.groq.com/keys) | [Ollama Installed](https://ollama.com/download). | [Mistral API Key](https://docs.mistral.ai/getting-started/models/models_overview/) | [Qwen API Key [Intl.]](https://www.alibabacloud.com/help/en/model-studio/developer-reference/get-api-key)/[[cn]](https://help.aliyun.com/zh/model-studio/getting-started/first-api-call-to-qwen?) | [Novita AI API Key](https://novita.ai/settings?utm_source=github_mindcraft&utm_medium=github_readme&utm_campaign=link#key-management) | [Cerebras API Key](https://cloud.cerebras.ai) | [Mercury API](https://platform.inceptionlabs.ai/docs) - One of these: [OpenAI API Key](https://openai.com/blog/openai-api) | [Gemini API Key](https://aistudio.google.com/app/apikey) | [Anthropic API Key](https://docs.anthropic.com/claude/docs/getting-access-to-claude) | [Replicate API Key](https://replicate.com/) | [Hugging Face API Key](https://huggingface.co/) | [Groq API Key](https://console.groq.com/keys) | [Ollama Installed](https://ollama.com/download). | [Mistral API Key](https://docs.mistral.ai/getting-started/models/models_overview/) | [Qwen API Key [Intl.]](https://www.alibabacloud.com/help/en/model-studio/developer-reference/get-api-key)/[[cn]](https://help.aliyun.com/zh/model-studio/getting-started/first-api-call-to-qwen?) | [Novita AI API Key](https://novita.ai/settings?utm_source=github_mindcraft&utm_medium=github_readme&utm_campaign=link#key-management) | [Cerebras API Key](https://cloud.cerebras.ai) | [Mercury API](https://platform.inceptionlabs.ai/docs)

View file

@ -1,5 +1,5 @@
const settings = { const settings = {
"minecraft_version": "1.21.1", // supports up to 1.21.1 "minecraft_version": "auto", // or specific version like "1.21.1"
"host": "127.0.0.1", // or "localhost", "your.ip.address.here" "host": "127.0.0.1", // or "localhost", "your.ip.address.here"
"port": 55916, "port": 55916,
"auth": "offline", // or "microsoft" "auth": "offline", // or "microsoft"
@ -7,7 +7,7 @@ const settings = {
// the mindserver manages all agents and hosts the UI // the mindserver manages all agents and hosts the UI
"mindserver_port": 8080, "mindserver_port": 8080,
"base_profile": "survival", // survival, assistant, creative, or god_mode "base_profile": "assistant", // survival, assistant, creative, or god_mode
"profiles": [ "profiles": [
"./andy.json", "./andy.json",
// "./profiles/gpt.json", // "./profiles/gpt.json",

151
src/mindcraft/mcserver.js Normal file
View file

@ -0,0 +1,151 @@
import net from 'net';
import mc from 'minecraft-protocol';
/**
* Scans the IP address for Minecraft LAN servers and collects their info.
* @param {string} ip - The IP address to scan.
* @param {number} port - The port to check.
* @param {number} timeout - The connection timeout in ms.
* @param {boolean} verbose - Whether to print output on connection errors.
* @returns {Promise<Array>} - A Promise that resolves to an array of server info objects.
*/
export async function serverInfo(ip, port, timeout = 1000, verbose = false) {
return new Promise((resolve) => {
let timeoutId = setTimeout(() => {
if (verbose)
console.error(`Timeout pinging server ${ip}:${port}`);
resolve(null); // Resolve as null if no response within timeout
}, timeout);
mc.ping({
host: ip,
port
}, (err, response) => {
clearTimeout(timeoutId);
if (err) {
if (verbose)
console.error(`Error pinging server ${ip}:${port}`, err);
return resolve(null);
}
// extract version number from modded servers like "Paper 1.21.4"
const version = response?.version?.name || '';
const match = String(version).match(/\d+\.\d+(?:\.\d+)?/);
const numericVersion = match ? match[0] : null;
if (numericVersion !== version) {
console.log(`Modded server found (${version}), attempting to use ${numericVersion}...`);
}
const serverInfo = {
host: ip,
port,
name: response.description.text || 'No description provided.',
ping: response.latency,
version: numericVersion
};
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<Array>} - 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);
});
});
};
// This supresses a lot of annoying console output from the mc library
// TODO: find a better way to do this, it supresses other useful 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, 200, false);
if (server) {
servers.push(server);
if (earlyExit) break;
}
}
}
// Restore console output
console.log = originalConsoleLog;
return servers;
}
/**
* Gets the MC server info from the host and port.
* @param {string} host - The host to search for.
* @param {number} port - The port to search for.
* @param {string} version - The version to search for.
* @returns {Promise<Object>} - A Promise that resolves to the server info object.
*/
export async function getServer(host, port, version) {
let server = null;
let serverString = "";
let serverVersion = "";
// Search for server
if (port == -1)
{
console.log(`No port provided. Searching for LAN server on host ${host}...`);
await findServers(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(host, port, 1000, true);
// Server not found
if (server == null)
throw new Error(`MC server not found. (Host: ${host}, Port: ${port}) Check the host and port in settings.js, and ensure the server is running and open to public or LAN.`);
serverString = `(Host: ${server.host}, Port: ${server.port}, Version: ${server.version})`;
if (version === "auto")
serverVersion = server.version;
else
serverVersion = version;
// Server version unsupported / mismatch
if (mc.supportedVersions.indexOf(serverVersion) === -1)
throw new Error(`MC server was found ${serverString}, but version is unsupported. Supported versions are: ${mc.supportedVersions.join(", ")}.`);
else if (version !== "auto" && server.version !== version)
throw new Error(`MC server was found ${serverString}, but version is incorrect. Expected ${version}, but found ${server.version}. Check the server version in settings.js.`);
else
console.log(`MC server found. ${serverString}`);
return server;
}

View file

@ -1,11 +1,11 @@
import { createMindServer, registerAgent } from './mindserver.js'; import { createMindServer, registerAgent } from './mindserver.js';
import { AgentProcess } from '../process/agent_process.js'; import { AgentProcess } from '../process/agent_process.js';
import { getServer } from './mcserver.js';
let mindserver; let mindserver;
let connected = false; let connected = false;
let agent_processes = {}; let agent_processes = {};
let agent_count = 0; let agent_count = 0;
let host = 'localhost';
let port = 8080; let port = 8080;
export async function init(host_public=false, port=8080) { export async function init(host_public=false, port=8080) {
@ -28,6 +28,12 @@ export async function createAgent(settings) {
registerAgent(settings); registerAgent(settings);
let load_memory = settings.load_memory || false; let load_memory = settings.load_memory || false;
let init_message = settings.init_message || null; let init_message = settings.init_message || null;
const server = await getServer(settings.host, settings.port, settings.minecraft_version);
settings.host = server.host;
settings.port = server.port;
settings.minecraft_version = server.version;
const agentProcess = new AgentProcess(agent_name, port); const agentProcess = new AgentProcess(agent_name, port);
agentProcess.start(load_memory, init_message, agent_count); agentProcess.start(load_memory, init_message, agent_count);
agent_count++; agent_count++;