mindcraft 0.1.4-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FAQ.md +38 -0
- package/LICENSE +21 -0
- package/README.md +255 -0
- package/andy.json +6 -0
- package/bin/mindcraft.js +80 -0
- package/keys.example.json +19 -0
- package/main.js +80 -0
- package/package.json +78 -0
- package/patches/minecraft-data+3.97.0.patch +13 -0
- package/patches/mineflayer+4.33.0.patch +54 -0
- package/patches/mineflayer-pathfinder+2.4.5.patch +265 -0
- package/patches/mineflayer-pvp+1.3.2.patch +13 -0
- package/patches/prismarine-viewer+1.33.0.patch +13 -0
- package/patches/protodef+1.19.0.patch +15 -0
- package/profiles/andy-4-reasoning.json +14 -0
- package/profiles/andy-4.json +7 -0
- package/profiles/azure.json +19 -0
- package/profiles/claude.json +7 -0
- package/profiles/claude_thinker.json +15 -0
- package/profiles/deepseek.json +7 -0
- package/profiles/defaults/_default.json +256 -0
- package/profiles/defaults/assistant.json +14 -0
- package/profiles/defaults/creative.json +14 -0
- package/profiles/defaults/god_mode.json +14 -0
- package/profiles/defaults/survival.json +14 -0
- package/profiles/freeguy.json +7 -0
- package/profiles/gemini.json +9 -0
- package/profiles/gpt.json +12 -0
- package/profiles/grok.json +7 -0
- package/profiles/llama.json +10 -0
- package/profiles/mercury.json +9 -0
- package/profiles/mistral.json +5 -0
- package/profiles/qwen.json +17 -0
- package/profiles/tasks/construction_profile.json +42 -0
- package/profiles/tasks/cooking_profile.json +11 -0
- package/profiles/tasks/crafting_profile.json +71 -0
- package/profiles/vllm.json +10 -0
- package/settings.js +64 -0
- package/src/agent/action_manager.js +177 -0
- package/src/agent/agent.js +561 -0
- package/src/agent/coder.js +229 -0
- package/src/agent/commands/actions.js +504 -0
- package/src/agent/commands/index.js +259 -0
- package/src/agent/commands/queries.js +347 -0
- package/src/agent/connection_handler.js +96 -0
- package/src/agent/conversation.js +353 -0
- package/src/agent/history.js +122 -0
- package/src/agent/library/full_state.js +89 -0
- package/src/agent/library/index.js +23 -0
- package/src/agent/library/lockdown.js +32 -0
- package/src/agent/library/skill_library.js +93 -0
- package/src/agent/library/skills.js +2093 -0
- package/src/agent/library/world.js +431 -0
- package/src/agent/memory_bank.js +25 -0
- package/src/agent/mindserver_proxy.js +136 -0
- package/src/agent/modes.js +446 -0
- package/src/agent/npc/build_goal.js +80 -0
- package/src/agent/npc/construction/dirt_shelter.json +38 -0
- package/src/agent/npc/construction/large_house.json +230 -0
- package/src/agent/npc/construction/small_stone_house.json +42 -0
- package/src/agent/npc/construction/small_wood_house.json +42 -0
- package/src/agent/npc/controller.js +261 -0
- package/src/agent/npc/data.js +50 -0
- package/src/agent/npc/item_goal.js +355 -0
- package/src/agent/npc/utils.js +126 -0
- package/src/agent/self_prompter.js +146 -0
- package/src/agent/settings.js +7 -0
- package/src/agent/speak.js +150 -0
- package/src/agent/tasks/construction_tasks.js +1104 -0
- package/src/agent/tasks/cooking_tasks.js +358 -0
- package/src/agent/tasks/tasks.js +594 -0
- package/src/agent/templates/execTemplate.js +6 -0
- package/src/agent/templates/lintTemplate.js +10 -0
- package/src/agent/vision/browser_viewer.js +8 -0
- package/src/agent/vision/camera.js +78 -0
- package/src/agent/vision/vision_interpreter.js +82 -0
- package/src/mindcraft/index.js +28 -0
- package/src/mindcraft/mcserver.js +154 -0
- package/src/mindcraft/mindcraft.js +111 -0
- package/src/mindcraft/mindserver.js +328 -0
- package/src/mindcraft/public/index.html +1253 -0
- package/src/mindcraft/public/settings_spec.json +145 -0
- package/src/mindcraft/userconfig.js +72 -0
- package/src/mindcraft-py/example.py +27 -0
- package/src/mindcraft-py/init-mindcraft.js +24 -0
- package/src/mindcraft-py/mindcraft.py +99 -0
- package/src/models/_model_map.js +89 -0
- package/src/models/azure.js +32 -0
- package/src/models/cerebras.js +61 -0
- package/src/models/claude.js +87 -0
- package/src/models/deepseek.js +59 -0
- package/src/models/gemini.js +176 -0
- package/src/models/glhf.js +71 -0
- package/src/models/gpt.js +147 -0
- package/src/models/grok.js +82 -0
- package/src/models/groq.js +95 -0
- package/src/models/huggingface.js +86 -0
- package/src/models/hyperbolic.js +114 -0
- package/src/models/lmstudio.js +74 -0
- package/src/models/mercury.js +95 -0
- package/src/models/mistral.js +94 -0
- package/src/models/novita.js +71 -0
- package/src/models/ollama.js +115 -0
- package/src/models/openrouter.js +77 -0
- package/src/models/prompter.js +366 -0
- package/src/models/qwen.js +80 -0
- package/src/models/replicate.js +60 -0
- package/src/models/vllm.js +81 -0
- package/src/process/agent_process.js +84 -0
- package/src/process/init_agent.js +54 -0
- package/src/utils/examples.js +83 -0
- package/src/utils/keys.js +34 -0
- package/src/utils/math.js +13 -0
- package/src/utils/mcdata.js +572 -0
- package/src/utils/text.js +78 -0
- package/src/utils/translator.js +30 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Viewer } from 'prismarine-viewer/viewer/lib/viewer.js';
|
|
2
|
+
import { WorldView } from 'prismarine-viewer/viewer/lib/worldView.js';
|
|
3
|
+
import { getBufferFromStream } from 'prismarine-viewer/viewer/lib/simpleUtils.js';
|
|
4
|
+
|
|
5
|
+
import THREE from 'three';
|
|
6
|
+
import { createCanvas } from 'node-canvas-webgl/lib/index.js';
|
|
7
|
+
import fs from 'fs/promises';
|
|
8
|
+
import { Vec3 } from 'vec3';
|
|
9
|
+
import { EventEmitter } from 'events';
|
|
10
|
+
|
|
11
|
+
import worker_threads from 'worker_threads';
|
|
12
|
+
global.Worker = worker_threads.Worker;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export class Camera extends EventEmitter {
|
|
16
|
+
constructor (bot, fp) {
|
|
17
|
+
super();
|
|
18
|
+
this.bot = bot;
|
|
19
|
+
this.fp = fp;
|
|
20
|
+
this.viewDistance = 12;
|
|
21
|
+
this.width = 800;
|
|
22
|
+
this.height = 512;
|
|
23
|
+
this.canvas = createCanvas(this.width, this.height);
|
|
24
|
+
this.renderer = new THREE.WebGLRenderer({ canvas: this.canvas });
|
|
25
|
+
this.viewer = new Viewer(this.renderer);
|
|
26
|
+
this._init().then(() => {
|
|
27
|
+
this.emit('ready');
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async _init () {
|
|
32
|
+
const botPos = this.bot.entity.position;
|
|
33
|
+
const center = new Vec3(botPos.x, botPos.y+this.bot.entity.height, botPos.z);
|
|
34
|
+
this.viewer.setVersion(this.bot.version);
|
|
35
|
+
// Load world
|
|
36
|
+
const worldView = new WorldView(this.bot.world, this.viewDistance, center);
|
|
37
|
+
this.viewer.listen(worldView);
|
|
38
|
+
worldView.listenToBot(this.bot);
|
|
39
|
+
await worldView.init(center);
|
|
40
|
+
this.worldView = worldView;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async capture() {
|
|
44
|
+
const center = new Vec3(this.bot.entity.position.x, this.bot.entity.position.y+this.bot.entity.height, this.bot.entity.position.z);
|
|
45
|
+
this.viewer.camera.position.set(center.x, center.y, center.z);
|
|
46
|
+
await this.worldView.updatePosition(center);
|
|
47
|
+
this.viewer.setFirstPersonCamera(this.bot.entity.position, this.bot.entity.yaw, this.bot.entity.pitch);
|
|
48
|
+
this.viewer.update();
|
|
49
|
+
this.renderer.render(this.viewer.scene, this.viewer.camera);
|
|
50
|
+
|
|
51
|
+
const imageStream = this.canvas.createJPEGStream({
|
|
52
|
+
bufsize: 4096,
|
|
53
|
+
quality: 100,
|
|
54
|
+
progressive: false
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
58
|
+
const filename = `screenshot_${timestamp}`;
|
|
59
|
+
|
|
60
|
+
const buf = await getBufferFromStream(imageStream);
|
|
61
|
+
await this._ensureScreenshotDirectory();
|
|
62
|
+
await fs.writeFile(`${this.fp}/${filename}.jpg`, buf);
|
|
63
|
+
console.log('saved', filename);
|
|
64
|
+
return filename;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async _ensureScreenshotDirectory() {
|
|
68
|
+
let stats;
|
|
69
|
+
try {
|
|
70
|
+
stats = await fs.stat(this.fp);
|
|
71
|
+
} catch (e) {
|
|
72
|
+
if (!stats?.isDirectory()) {
|
|
73
|
+
await fs.mkdir(this.fp);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Vec3 } from 'vec3';
|
|
2
|
+
import { Camera } from "./camera.js";
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import settings from '../settings.js';
|
|
5
|
+
|
|
6
|
+
export class VisionInterpreter {
|
|
7
|
+
constructor(agent, allow_vision) {
|
|
8
|
+
this.agent = agent;
|
|
9
|
+
this.allow_vision = allow_vision;
|
|
10
|
+
this.fp = `${settings.data_dir}/${agent.name}/screenshots/`;
|
|
11
|
+
if (allow_vision) {
|
|
12
|
+
this.camera = new Camera(agent.bot, this.fp);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async lookAtPlayer(player_name, direction) {
|
|
17
|
+
if (!this.allow_vision || !this.agent.prompter.vision_model.sendVisionRequest) {
|
|
18
|
+
return "Vision is disabled. Use other methods to describe the environment.";
|
|
19
|
+
}
|
|
20
|
+
let result = "";
|
|
21
|
+
const bot = this.agent.bot;
|
|
22
|
+
const player = bot.players[player_name]?.entity;
|
|
23
|
+
if (!player) {
|
|
24
|
+
return `Could not find player ${player_name}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let filename;
|
|
28
|
+
if (direction === 'with') {
|
|
29
|
+
await bot.look(player.yaw, player.pitch);
|
|
30
|
+
result = `Looking in the same direction as ${player_name}\n`;
|
|
31
|
+
filename = await this.camera.capture();
|
|
32
|
+
} else {
|
|
33
|
+
await bot.lookAt(new Vec3(player.position.x, player.position.y + player.height, player.position.z));
|
|
34
|
+
result = `Looking at player ${player_name}\n`;
|
|
35
|
+
filename = await this.camera.capture();
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return result + `Image analysis: "${await this.analyzeImage(filename)}"`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async lookAtPosition(x, y, z) {
|
|
43
|
+
if (!this.allow_vision || !this.agent.prompter.vision_model.sendVisionRequest) {
|
|
44
|
+
return "Vision is disabled. Use other methods to describe the environment.";
|
|
45
|
+
}
|
|
46
|
+
let result = "";
|
|
47
|
+
const bot = this.agent.bot;
|
|
48
|
+
await bot.lookAt(new Vec3(x, y + 2, z));
|
|
49
|
+
result = `Looking at coordinate ${x}, ${y}, ${z}\n`;
|
|
50
|
+
|
|
51
|
+
let filename = await this.camera.capture();
|
|
52
|
+
|
|
53
|
+
return result + `Image analysis: "${await this.analyzeImage(filename)}"`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
getCenterBlockInfo() {
|
|
57
|
+
const bot = this.agent.bot;
|
|
58
|
+
const maxDistance = 128; // Maximum distance to check for blocks
|
|
59
|
+
const targetBlock = bot.blockAtCursor(maxDistance);
|
|
60
|
+
|
|
61
|
+
if (targetBlock) {
|
|
62
|
+
return `Block at center view: ${targetBlock.name} at (${targetBlock.position.x}, ${targetBlock.position.y}, ${targetBlock.position.z})`;
|
|
63
|
+
} else {
|
|
64
|
+
return "No block in center view";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async analyzeImage(filename) {
|
|
69
|
+
try {
|
|
70
|
+
const imageBuffer = fs.readFileSync(`${this.fp}/${filename}.jpg`);
|
|
71
|
+
const messages = this.agent.history.getHistory();
|
|
72
|
+
|
|
73
|
+
const blockInfo = this.getCenterBlockInfo();
|
|
74
|
+
const result = await this.agent.prompter.promptVision(messages, imageBuffer);
|
|
75
|
+
return result + `\n${blockInfo}`;
|
|
76
|
+
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.warn('Error reading image:', error);
|
|
79
|
+
return `Error reading image: ${error.message}`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import defaultSettings from '../../settings.js';
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
init,
|
|
7
|
+
createAgent,
|
|
8
|
+
getAgentProcess,
|
|
9
|
+
startAgent,
|
|
10
|
+
stopAgent,
|
|
11
|
+
destroyAgent,
|
|
12
|
+
shutdown,
|
|
13
|
+
} from './mindcraft.js';
|
|
14
|
+
|
|
15
|
+
export { createMindServer, registerAgent, getIO, getServer } from './mindserver.js';
|
|
16
|
+
|
|
17
|
+
export { defaultSettings };
|
|
18
|
+
|
|
19
|
+
const settings_spec_path = fileURLToPath(new URL('./public/settings_spec.json', import.meta.url));
|
|
20
|
+
export const settingsSpec = JSON.parse(readFileSync(settings_spec_path, 'utf8'));
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Build a minimal Claude profile. Omits `embedding` so mindcraft falls back to
|
|
24
|
+
* word-overlap similarity, meaning only ANTHROPIC_API_KEY is required.
|
|
25
|
+
*/
|
|
26
|
+
export function claudeProfile({ name, model = 'claude-sonnet-4-6' }) {
|
|
27
|
+
return { name, model };
|
|
28
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import net from 'net';
|
|
2
|
+
import mc from 'minecraft-protocol';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scans the IP address for Minecraft LAN servers and collects their info.
|
|
6
|
+
* @param {string} ip - The IP address to scan.
|
|
7
|
+
* @param {number} port - The port to check.
|
|
8
|
+
* @param {number} timeout - The connection timeout in ms.
|
|
9
|
+
* @param {boolean} verbose - Whether to print output on connection errors.
|
|
10
|
+
* @returns {Promise<Array>} - A Promise that resolves to an array of server info objects.
|
|
11
|
+
*/
|
|
12
|
+
export async function serverInfo(ip, port, timeout = 1000, verbose = false) {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
|
|
15
|
+
let timeoutId = setTimeout(() => {
|
|
16
|
+
if (verbose)
|
|
17
|
+
console.error(`Timeout pinging server ${ip}:${port}`);
|
|
18
|
+
resolve(null); // Resolve as null if no response within timeout
|
|
19
|
+
}, timeout);
|
|
20
|
+
|
|
21
|
+
mc.ping({
|
|
22
|
+
host: ip,
|
|
23
|
+
port
|
|
24
|
+
}, (err, response) => {
|
|
25
|
+
clearTimeout(timeoutId);
|
|
26
|
+
|
|
27
|
+
if (err) {
|
|
28
|
+
if (verbose)
|
|
29
|
+
console.error(`Error pinging server ${ip}:${port}`, err);
|
|
30
|
+
return resolve(null);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// extract version number from modded servers like "Paper 1.21.4"
|
|
34
|
+
const version = response?.version?.name || '';
|
|
35
|
+
const match = String(version).match(/\d+\.\d+(?:\.\d+)?/);
|
|
36
|
+
const numericVersion = match ? match[0] : null;
|
|
37
|
+
if (numericVersion !== version) {
|
|
38
|
+
console.log(`Modded server found (${version}), attempting to use ${numericVersion}...`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const serverInfo = {
|
|
42
|
+
host: ip,
|
|
43
|
+
port,
|
|
44
|
+
name: response.description.text || 'No description provided.',
|
|
45
|
+
ping: response.latency,
|
|
46
|
+
version: numericVersion
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
resolve(serverInfo);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Scans the IP address for Minecraft LAN servers and collects their info.
|
|
56
|
+
* @param {string} ip - The IP address to scan.
|
|
57
|
+
* @param {boolean} earlyExit - Whether to exit early after finding a server.
|
|
58
|
+
* @param {number} timeout - The connection timeout in ms.
|
|
59
|
+
* @returns {Promise<Array>} - A Promise that resolves to an array of server info objects.
|
|
60
|
+
*/
|
|
61
|
+
export async function findServers(ip, earlyExit = false, timeout = 100) {
|
|
62
|
+
const servers = [];
|
|
63
|
+
const startPort = 49000;
|
|
64
|
+
const endPort = 65000;
|
|
65
|
+
|
|
66
|
+
const checkPort = (port) => {
|
|
67
|
+
return new Promise((resolve) => {
|
|
68
|
+
const socket = net.createConnection({ host: ip, port, timeout }, () => {
|
|
69
|
+
socket.end();
|
|
70
|
+
resolve(port); // Port is open
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
socket.on('error', () => resolve(null)); // Port is closed
|
|
74
|
+
socket.on('timeout', () => {
|
|
75
|
+
socket.destroy();
|
|
76
|
+
resolve(null);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// This supresses a lot of annoying console output from the mc library
|
|
82
|
+
// TODO: find a better way to do this, it supresses other useful output
|
|
83
|
+
const originalConsoleLog = console.log;
|
|
84
|
+
console.log = () => { };
|
|
85
|
+
|
|
86
|
+
for (let port = startPort; port <= endPort; port++) {
|
|
87
|
+
const openPort = await checkPort(port);
|
|
88
|
+
if (openPort) {
|
|
89
|
+
const server = await serverInfo(ip, port, 200, false);
|
|
90
|
+
if (server) {
|
|
91
|
+
servers.push(server);
|
|
92
|
+
|
|
93
|
+
if (earlyExit) break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Restore console output
|
|
99
|
+
console.log = originalConsoleLog;
|
|
100
|
+
|
|
101
|
+
return servers;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Gets the MC server info from the host and port.
|
|
106
|
+
* @param {string} host - The host to search for.
|
|
107
|
+
* @param {number} port - The port to search for.
|
|
108
|
+
* @param {string} version - The version to search for.
|
|
109
|
+
* @returns {Promise<Object>} - A Promise that resolves to the server info object.
|
|
110
|
+
*/
|
|
111
|
+
export async function getServer(host, port, version) {
|
|
112
|
+
let server = null;
|
|
113
|
+
let serverString = "";
|
|
114
|
+
let serverVersion = "";
|
|
115
|
+
|
|
116
|
+
// Search for server
|
|
117
|
+
if (port == -1)
|
|
118
|
+
{
|
|
119
|
+
console.log(`No port provided. Searching for LAN server on host ${host}...`);
|
|
120
|
+
|
|
121
|
+
await findServers(host, true).then((servers) => {
|
|
122
|
+
if (servers.length > 0)
|
|
123
|
+
server = servers[0];
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (server == null)
|
|
127
|
+
throw new Error(`No server found on LAN.`);
|
|
128
|
+
}
|
|
129
|
+
else
|
|
130
|
+
server = await serverInfo(host, port, 1000, true);
|
|
131
|
+
|
|
132
|
+
// Server not found
|
|
133
|
+
if (server == null)
|
|
134
|
+
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.`);
|
|
135
|
+
|
|
136
|
+
serverString = `(Host: ${server.host}, Port: ${server.port}, Version: ${server.version})`;
|
|
137
|
+
|
|
138
|
+
if (version === "auto")
|
|
139
|
+
serverVersion = server.version;
|
|
140
|
+
else
|
|
141
|
+
serverVersion = version;
|
|
142
|
+
// Server version unsupported / mismatch
|
|
143
|
+
const isSupported = mc.supportedVersions.some(v =>
|
|
144
|
+
serverVersion === v || (serverVersion.startsWith(v) && serverVersion.charAt(v.length) === '.')
|
|
145
|
+
); // Checks version or parent version (e.g. if 1.7 is supported then 1.7.2 will be allowed)
|
|
146
|
+
if (!isSupported)
|
|
147
|
+
throw new Error(`MC server was found ${serverString}, but version is unsupported. Supported versions are: ${mc.supportedVersions.join(", ")}.`);
|
|
148
|
+
else if (version !== "auto" && server.version !== version)
|
|
149
|
+
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.`);
|
|
150
|
+
else
|
|
151
|
+
console.log(`MC server found. ${serverString}`);
|
|
152
|
+
|
|
153
|
+
return server;
|
|
154
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { createMindServer, registerAgent, numStateListeners } from './mindserver.js';
|
|
2
|
+
import { AgentProcess } from '../process/agent_process.js';
|
|
3
|
+
import { getServer } from './mcserver.js';
|
|
4
|
+
import open from 'open';
|
|
5
|
+
|
|
6
|
+
let mindserver;
|
|
7
|
+
let connected = false;
|
|
8
|
+
let agent_processes = {};
|
|
9
|
+
let agent_count = 0;
|
|
10
|
+
let mindserver_port = 8080;
|
|
11
|
+
|
|
12
|
+
export async function init(host_public=false, port=8080, auto_open_ui=true) {
|
|
13
|
+
if (connected) {
|
|
14
|
+
console.error('Already initiliazed!');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
mindserver = createMindServer(host_public, port);
|
|
18
|
+
mindserver_port = port;
|
|
19
|
+
connected = true;
|
|
20
|
+
if (auto_open_ui) {
|
|
21
|
+
setTimeout(() => {
|
|
22
|
+
// check if browser listener is already open
|
|
23
|
+
if (numStateListeners() === 0) {
|
|
24
|
+
open('http://localhost:'+port);
|
|
25
|
+
}
|
|
26
|
+
}, 3000);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function createAgent(settings) {
|
|
31
|
+
if (!settings.profile.name) {
|
|
32
|
+
console.error('Agent name is required in profile');
|
|
33
|
+
return {
|
|
34
|
+
success: false,
|
|
35
|
+
error: 'Agent name is required in profile'
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
settings = JSON.parse(JSON.stringify(settings));
|
|
39
|
+
let agent_name = settings.profile.name;
|
|
40
|
+
const agentIndex = agent_count++;
|
|
41
|
+
const viewer_port = 3000 + agentIndex;
|
|
42
|
+
registerAgent(settings, viewer_port);
|
|
43
|
+
let load_memory = settings.load_memory || false;
|
|
44
|
+
let init_message = settings.init_message || null;
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
try {
|
|
48
|
+
const server = await getServer(settings.host, settings.port, settings.minecraft_version);
|
|
49
|
+
settings.host = server.host;
|
|
50
|
+
settings.port = server.port;
|
|
51
|
+
settings.minecraft_version = server.version;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.warn(`Error getting server:`, error);
|
|
54
|
+
if (settings.minecraft_version === "auto") {
|
|
55
|
+
settings.minecraft_version = null;
|
|
56
|
+
}
|
|
57
|
+
console.warn(`Attempting to connect anyway...`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const agentProcess = new AgentProcess(agent_name, mindserver_port);
|
|
61
|
+
agentProcess.start(load_memory, init_message, agentIndex);
|
|
62
|
+
agent_processes[settings.profile.name] = agentProcess;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(`Error creating agent ${agent_name}:`, error);
|
|
65
|
+
destroyAgent(agent_name);
|
|
66
|
+
return {
|
|
67
|
+
success: false,
|
|
68
|
+
error: error.message
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
success: true,
|
|
73
|
+
error: null
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function getAgentProcess(agentName) {
|
|
78
|
+
return agent_processes[agentName];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function startAgent(agentName) {
|
|
82
|
+
if (agent_processes[agentName]) {
|
|
83
|
+
agent_processes[agentName].forceRestart();
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
console.error(`Cannot start agent ${agentName}; not found`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function stopAgent(agentName) {
|
|
91
|
+
if (agent_processes[agentName]) {
|
|
92
|
+
agent_processes[agentName].stop();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function destroyAgent(agentName) {
|
|
97
|
+
if (agent_processes[agentName]) {
|
|
98
|
+
agent_processes[agentName].stop();
|
|
99
|
+
delete agent_processes[agentName];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function shutdown() {
|
|
104
|
+
console.log('Shutting down');
|
|
105
|
+
for (let agentName in agent_processes) {
|
|
106
|
+
agent_processes[agentName].stop();
|
|
107
|
+
}
|
|
108
|
+
setTimeout(() => {
|
|
109
|
+
process.exit(0);
|
|
110
|
+
}, 2000);
|
|
111
|
+
}
|