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,259 @@
|
|
|
1
|
+
import { getBlockId, getItemId } from "../../utils/mcdata.js";
|
|
2
|
+
import { actionsList } from './actions.js';
|
|
3
|
+
import { queryList } from './queries.js';
|
|
4
|
+
|
|
5
|
+
let suppressNoDomainWarning = true;
|
|
6
|
+
|
|
7
|
+
const commandList = queryList.concat(actionsList);
|
|
8
|
+
const commandMap = {};
|
|
9
|
+
for (let command of commandList) {
|
|
10
|
+
commandMap[command.name] = command;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getCommand(name) {
|
|
14
|
+
return commandMap[name];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function blacklistCommands(commands) {
|
|
18
|
+
const unblockable = ['!stop', '!stats', '!inventory', '!goal'];
|
|
19
|
+
for (let command_name of commands) {
|
|
20
|
+
if (unblockable.includes(command_name)){
|
|
21
|
+
console.warn(`Command ${command_name} is unblockable`);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
delete commandMap[command_name];
|
|
25
|
+
delete commandList.find(command => command.name === command_name);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const commandRegex = /!(\w+)(?:\(((?:-?\d+(?:\.\d+)?|true|false|"[^"]*")(?:\s*,\s*(?:-?\d+(?:\.\d+)?|true|false|"[^"]*"))*)\))?/
|
|
30
|
+
const argRegex = /-?\d+(?:\.\d+)?|true|false|"[^"]*"/g;
|
|
31
|
+
|
|
32
|
+
export function containsCommand(message) {
|
|
33
|
+
const commandMatch = message.match(commandRegex);
|
|
34
|
+
if (commandMatch)
|
|
35
|
+
return "!" + commandMatch[1];
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function commandExists(commandName) {
|
|
40
|
+
if (!commandName.startsWith("!"))
|
|
41
|
+
commandName = "!" + commandName;
|
|
42
|
+
return commandMap[commandName] !== undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Converts a string into a boolean.
|
|
47
|
+
* @param {string} input
|
|
48
|
+
* @returns {boolean | null} the boolean or `null` if it could not be parsed.
|
|
49
|
+
* */
|
|
50
|
+
function parseBoolean(input) {
|
|
51
|
+
switch(input.toLowerCase()) {
|
|
52
|
+
case 'false': //These are interpreted as flase;
|
|
53
|
+
case 'f':
|
|
54
|
+
case '0':
|
|
55
|
+
case 'off':
|
|
56
|
+
return false;
|
|
57
|
+
case 'true': //These are interpreted as true;
|
|
58
|
+
case 't':
|
|
59
|
+
case '1':
|
|
60
|
+
case 'on':
|
|
61
|
+
return true;
|
|
62
|
+
default:
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {number} value - the value to check
|
|
69
|
+
* @param {number} lowerBound
|
|
70
|
+
* @param {number} upperBound
|
|
71
|
+
* @param {string} endpointType - The type of the endpoints represented as a two character string. `'[)'` `'()'`
|
|
72
|
+
*/
|
|
73
|
+
function checkInInterval(number, lowerBound, upperBound, endpointType) {
|
|
74
|
+
switch (endpointType) {
|
|
75
|
+
case '[)':
|
|
76
|
+
return lowerBound <= number && number < upperBound;
|
|
77
|
+
case '()':
|
|
78
|
+
return lowerBound < number && number < upperBound;
|
|
79
|
+
case '(]':
|
|
80
|
+
return lowerBound < number && number <= upperBound;
|
|
81
|
+
case '[]':
|
|
82
|
+
return lowerBound <= number && number <= upperBound;
|
|
83
|
+
default:
|
|
84
|
+
throw new Error('Unknown endpoint type:', endpointType)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
// todo: handle arrays?
|
|
91
|
+
/**
|
|
92
|
+
* Returns an object containing the command, the command name, and the comand parameters.
|
|
93
|
+
* If parsing unsuccessful, returns an error message as a string.
|
|
94
|
+
* @param {string} message - A message from a player or language model containing a command.
|
|
95
|
+
* @returns {string | Object}
|
|
96
|
+
*/
|
|
97
|
+
export function parseCommandMessage(message) {
|
|
98
|
+
const commandMatch = message.match(commandRegex);
|
|
99
|
+
if (!commandMatch) return `Command is incorrectly formatted`;
|
|
100
|
+
|
|
101
|
+
const commandName = "!"+commandMatch[1];
|
|
102
|
+
|
|
103
|
+
let args;
|
|
104
|
+
if (commandMatch[2]) args = commandMatch[2].match(argRegex);
|
|
105
|
+
else args = [];
|
|
106
|
+
|
|
107
|
+
const command = getCommand(commandName);
|
|
108
|
+
if(!command) return `${commandName} is not a command.`
|
|
109
|
+
|
|
110
|
+
const params = commandParams(command);
|
|
111
|
+
const paramNames = commandParamNames(command);
|
|
112
|
+
|
|
113
|
+
if (args.length !== params.length)
|
|
114
|
+
return `Command ${command.name} was given ${args.length} args, but requires ${params.length} args.`;
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
for (let i = 0; i < args.length; i++) {
|
|
118
|
+
const param = params[i];
|
|
119
|
+
//Remove any extra characters
|
|
120
|
+
let arg = args[i].trim();
|
|
121
|
+
if ((arg.startsWith('"') && arg.endsWith('"')) || (arg.startsWith("'") && arg.endsWith("'"))) {
|
|
122
|
+
arg = arg.substring(1, arg.length-1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
//Convert to the correct type
|
|
126
|
+
switch(param.type) {
|
|
127
|
+
case 'int':
|
|
128
|
+
arg = Number.parseInt(arg); break;
|
|
129
|
+
case 'float':
|
|
130
|
+
arg = Number.parseFloat(arg); break;
|
|
131
|
+
case 'boolean':
|
|
132
|
+
arg = parseBoolean(arg); break;
|
|
133
|
+
case 'BlockName':
|
|
134
|
+
case 'BlockOrItemName':
|
|
135
|
+
case 'ItemName':
|
|
136
|
+
if (arg.endsWith('plank') || arg.endsWith('seed'))
|
|
137
|
+
arg += 's'; // add 's' to for common mistakes like "oak_plank" or "wheat_seed"
|
|
138
|
+
case 'string':
|
|
139
|
+
break;
|
|
140
|
+
default:
|
|
141
|
+
throw new Error(`Command '${commandName}' parameter '${paramNames[i]}' has an unknown type: ${param.type}`);
|
|
142
|
+
}
|
|
143
|
+
if(arg === null || Number.isNaN(arg))
|
|
144
|
+
return `Error: Param '${paramNames[i]}' must be of type ${param.type}.`
|
|
145
|
+
|
|
146
|
+
if(typeof arg === 'number') { //Check the domain of numbers
|
|
147
|
+
const domain = param.domain;
|
|
148
|
+
if(domain) {
|
|
149
|
+
/**
|
|
150
|
+
* Javascript has a built in object for sets but not intervals.
|
|
151
|
+
* Currently the interval (lowerbound,upperbound] is represented as an Array: `[lowerbound, upperbound, '(]']`
|
|
152
|
+
*/
|
|
153
|
+
if (!domain[2]) domain[2] = '[)'; //By default, lower bound is included. Upper is not.
|
|
154
|
+
|
|
155
|
+
if(!checkInInterval(arg, ...domain)) {
|
|
156
|
+
return `Error: Param '${paramNames[i]}' must be an element of ${domain[2][0]}${domain[0]}, ${domain[1]}${domain[2][1]}.`;
|
|
157
|
+
//Alternatively arg could be set to the nearest value in the domain.
|
|
158
|
+
}
|
|
159
|
+
} else if (!suppressNoDomainWarning) {
|
|
160
|
+
console.warn(`Command '${commandName}' parameter '${paramNames[i]}' has no domain set. Expect any value [-Infinity, Infinity].`)
|
|
161
|
+
suppressNoDomainWarning = true; //Don't spam console. Only give the warning once.
|
|
162
|
+
}
|
|
163
|
+
} else if(param.type === 'BlockName') { //Check that there is a block with this name
|
|
164
|
+
if(getBlockId(arg) == null) return `Invalid block type: ${arg}.`
|
|
165
|
+
} else if(param.type === 'ItemName') { //Check that there is an item with this name
|
|
166
|
+
if(getItemId(arg) == null) return `Invalid item type: ${arg}.`
|
|
167
|
+
} else if(param.type === 'BlockOrItemName') {
|
|
168
|
+
if(getBlockId(arg) == null && getItemId(arg) == null) return `Invalid block or item type: ${arg}.`
|
|
169
|
+
}
|
|
170
|
+
args[i] = arg;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return { commandName, args };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function truncCommandMessage(message) {
|
|
177
|
+
const commandMatch = message.match(commandRegex);
|
|
178
|
+
if (commandMatch) {
|
|
179
|
+
return message.substring(0, commandMatch.index + commandMatch[0].length);
|
|
180
|
+
}
|
|
181
|
+
return message;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function isAction(name) {
|
|
185
|
+
return actionsList.find(action => action.name === name) !== undefined;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @param {Object} command
|
|
190
|
+
* @returns {Object[]} The command's parameters.
|
|
191
|
+
*/
|
|
192
|
+
function commandParams(command) {
|
|
193
|
+
if (!command.params)
|
|
194
|
+
return [];
|
|
195
|
+
return Object.values(command.params);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @param {Object} command
|
|
200
|
+
* @returns {string[]} The names of the command's parameters.
|
|
201
|
+
*/
|
|
202
|
+
function commandParamNames(command) {
|
|
203
|
+
if (!command.params)
|
|
204
|
+
return [];
|
|
205
|
+
return Object.keys(command.params);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function numParams(command) {
|
|
209
|
+
return commandParams(command).length;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export async function executeCommand(agent, message) {
|
|
213
|
+
let parsed = parseCommandMessage(message);
|
|
214
|
+
if (typeof parsed === 'string')
|
|
215
|
+
return parsed; //The command was incorrectly formatted or an invalid input was given.
|
|
216
|
+
else {
|
|
217
|
+
console.log('parsed command:', parsed);
|
|
218
|
+
const command = getCommand(parsed.commandName);
|
|
219
|
+
let numArgs = 0;
|
|
220
|
+
if (parsed.args) {
|
|
221
|
+
numArgs = parsed.args.length;
|
|
222
|
+
}
|
|
223
|
+
if (numArgs !== numParams(command))
|
|
224
|
+
return `Command ${command.name} was given ${numArgs} args, but requires ${numParams(command)} args.`;
|
|
225
|
+
else {
|
|
226
|
+
const result = await command.perform(agent, ...parsed.args);
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function getCommandDocs(agent) {
|
|
233
|
+
const typeTranslations = {
|
|
234
|
+
//This was added to keep the prompt the same as before type checks were implemented.
|
|
235
|
+
//If the language model is giving invalid inputs changing this might help.
|
|
236
|
+
'float': 'number',
|
|
237
|
+
'int': 'number',
|
|
238
|
+
'BlockName': 'string',
|
|
239
|
+
'ItemName': 'string',
|
|
240
|
+
'BlockOrItemName': 'string',
|
|
241
|
+
'boolean': 'bool'
|
|
242
|
+
}
|
|
243
|
+
let docs = `\n*COMMAND DOCS\n You can use the following commands to perform actions and get information about the world.
|
|
244
|
+
Use the commands with the syntax: !commandName or !commandName("arg1", 1.2, ...) if the command takes arguments.\n
|
|
245
|
+
Do not use codeblocks. Use double quotes for strings. Only use one command in each response, trailing commands and comments will be ignored.\n`;
|
|
246
|
+
for (let command of commandList) {
|
|
247
|
+
if (agent.blocked_actions.includes(command.name)) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
docs += command.name + ': ' + command.description + '\n';
|
|
251
|
+
if (command.params) {
|
|
252
|
+
docs += 'Params:\n';
|
|
253
|
+
for (let param in command.params) {
|
|
254
|
+
docs += `${param}: (${typeTranslations[command.params[param].type]??command.params[param].type}) ${command.params[param].description}\n`;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return docs + '*\n';
|
|
259
|
+
}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import * as world from '../library/world.js';
|
|
2
|
+
import * as mc from '../../utils/mcdata.js';
|
|
3
|
+
import { getCommandDocs } from './index.js';
|
|
4
|
+
import convoManager from '../conversation.js';
|
|
5
|
+
import { checkLevelBlueprint, checkBlueprint } from '../tasks/construction_tasks.js';
|
|
6
|
+
import { load } from 'cheerio';
|
|
7
|
+
|
|
8
|
+
const pad = (str) => {
|
|
9
|
+
return '\n' + str + '\n';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// queries are commands that just return strings and don't affect anything in the world
|
|
13
|
+
export const queryList = [
|
|
14
|
+
{
|
|
15
|
+
name: "!stats",
|
|
16
|
+
description: "Get your bot's location, health, hunger, and time of day.",
|
|
17
|
+
perform: function (agent) {
|
|
18
|
+
let bot = agent.bot;
|
|
19
|
+
let res = 'STATS';
|
|
20
|
+
let pos = bot.entity.position;
|
|
21
|
+
// display position to 2 decimal places
|
|
22
|
+
res += `\n- Position: x: ${pos.x.toFixed(2)}, y: ${pos.y.toFixed(2)}, z: ${pos.z.toFixed(2)}`;
|
|
23
|
+
// Gameplay
|
|
24
|
+
res += `\n- Gamemode: ${bot.game.gameMode}`;
|
|
25
|
+
res += `\n- Health: ${Math.round(bot.health)} / 20`;
|
|
26
|
+
res += `\n- Hunger: ${Math.round(bot.food)} / 20`;
|
|
27
|
+
res += `\n- Biome: ${world.getBiomeName(bot)}`;
|
|
28
|
+
let weather = "Clear";
|
|
29
|
+
if (bot.rainState > 0)
|
|
30
|
+
weather = "Rain";
|
|
31
|
+
if (bot.thunderState > 0)
|
|
32
|
+
weather = "Thunderstorm";
|
|
33
|
+
res += `\n- Weather: ${weather}`;
|
|
34
|
+
// let block = bot.blockAt(pos);
|
|
35
|
+
// res += `\n- Artficial light: ${block.skyLight}`;
|
|
36
|
+
// res += `\n- Sky light: ${block.light}`;
|
|
37
|
+
// light properties are bugged, they are not accurate
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if (bot.time.timeOfDay < 6000) {
|
|
41
|
+
res += '\n- Time: Morning';
|
|
42
|
+
} else if (bot.time.timeOfDay < 12000) {
|
|
43
|
+
res += '\n- Time: Afternoon';
|
|
44
|
+
} else {
|
|
45
|
+
res += '\n- Time: Night';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// get the bot's current action
|
|
49
|
+
let action = agent.actions.currentActionLabel;
|
|
50
|
+
if (agent.isIdle())
|
|
51
|
+
action = 'Idle';
|
|
52
|
+
res += `\- Current Action: ${action}`;
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
let players = world.getNearbyPlayerNames(bot);
|
|
56
|
+
let bots = convoManager.getInGameAgents().filter(b => b !== agent.name);
|
|
57
|
+
players = players.filter(p => !bots.includes(p));
|
|
58
|
+
|
|
59
|
+
res += '\n- Nearby Human Players: ' + (players.length > 0 ? players.join(', ') : 'None.');
|
|
60
|
+
res += '\n- Nearby Bot Players: ' + (bots.length > 0 ? bots.join(', ') : 'None.');
|
|
61
|
+
|
|
62
|
+
res += '\n' + agent.bot.modes.getMiniDocs() + '\n';
|
|
63
|
+
return pad(res);
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "!inventory",
|
|
68
|
+
description: "Get your bot's inventory.",
|
|
69
|
+
perform: function (agent) {
|
|
70
|
+
let bot = agent.bot;
|
|
71
|
+
let inventory = world.getInventoryCounts(bot);
|
|
72
|
+
let res = 'INVENTORY';
|
|
73
|
+
for (const item in inventory) {
|
|
74
|
+
if (inventory[item] && inventory[item] > 0)
|
|
75
|
+
res += `\n- ${item}: ${inventory[item]}`;
|
|
76
|
+
}
|
|
77
|
+
if (res === 'INVENTORY') {
|
|
78
|
+
res += ': Nothing';
|
|
79
|
+
}
|
|
80
|
+
else if (agent.bot.game.gameMode === 'creative') {
|
|
81
|
+
res += '\n(You have infinite items in creative mode. You do not need to gather resources!!)';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let helmet = bot.inventory.slots[5];
|
|
85
|
+
let chestplate = bot.inventory.slots[6];
|
|
86
|
+
let leggings = bot.inventory.slots[7];
|
|
87
|
+
let boots = bot.inventory.slots[8];
|
|
88
|
+
res += '\nWEARING: ';
|
|
89
|
+
if (helmet)
|
|
90
|
+
res += `\nHead: ${helmet.name}`;
|
|
91
|
+
if (chestplate)
|
|
92
|
+
res += `\nTorso: ${chestplate.name}`;
|
|
93
|
+
if (leggings)
|
|
94
|
+
res += `\nLegs: ${leggings.name}`;
|
|
95
|
+
if (boots)
|
|
96
|
+
res += `\nFeet: ${boots.name}`;
|
|
97
|
+
if (!helmet && !chestplate && !leggings && !boots)
|
|
98
|
+
res += 'Nothing';
|
|
99
|
+
|
|
100
|
+
return pad(res);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: "!nearbyBlocks",
|
|
105
|
+
description: "Get the blocks near the bot.",
|
|
106
|
+
perform: function (agent) {
|
|
107
|
+
let bot = agent.bot;
|
|
108
|
+
let res = 'NEARBY_BLOCKS';
|
|
109
|
+
let blocks = world.getNearestBlocks(bot);
|
|
110
|
+
let block_details = new Set();
|
|
111
|
+
|
|
112
|
+
for (let block of blocks) {
|
|
113
|
+
let details = block.name;
|
|
114
|
+
if (block.name === 'water' || block.name === 'lava') {
|
|
115
|
+
details += block.metadata === 0 ? ' (source)' : ' (flowing)';
|
|
116
|
+
}
|
|
117
|
+
block_details.add(details);
|
|
118
|
+
}
|
|
119
|
+
for (let details of block_details) {
|
|
120
|
+
res += `\n- ${details}`;
|
|
121
|
+
}
|
|
122
|
+
if (block_details.size === 0) {
|
|
123
|
+
res += ': none';
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
res += '\n- ' + world.getSurroundingBlocks(bot).join('\n- ');
|
|
127
|
+
res += `\n- First Solid Block Above Head: ${world.getFirstBlockAboveHead(bot, null, 32)}`;
|
|
128
|
+
}
|
|
129
|
+
return pad(res);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "!craftable",
|
|
134
|
+
description: "Get the craftable items with the bot's inventory.",
|
|
135
|
+
perform: function (agent) {
|
|
136
|
+
let craftable = world.getCraftableItems(agent.bot);
|
|
137
|
+
let res = 'CRAFTABLE_ITEMS';
|
|
138
|
+
for (const item of craftable) {
|
|
139
|
+
res += `\n- ${item}`;
|
|
140
|
+
}
|
|
141
|
+
if (res == 'CRAFTABLE_ITEMS') {
|
|
142
|
+
res += ': none';
|
|
143
|
+
}
|
|
144
|
+
return pad(res);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "!entities",
|
|
149
|
+
description: "Get the nearby players and entities.",
|
|
150
|
+
perform: function (agent) {
|
|
151
|
+
let bot = agent.bot;
|
|
152
|
+
let res = 'NEARBY_ENTITIES';
|
|
153
|
+
let players = world.getNearbyPlayerNames(bot);
|
|
154
|
+
let bots = convoManager.getInGameAgents().filter(b => b !== agent.name);
|
|
155
|
+
players = players.filter(p => !bots.includes(p));
|
|
156
|
+
|
|
157
|
+
for (const player of players) {
|
|
158
|
+
res += `\n- Human player: ${player}`;
|
|
159
|
+
}
|
|
160
|
+
for (const bot of bots) {
|
|
161
|
+
res += `\n- Bot player: ${bot}`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let nearbyEntities = world.getNearbyEntities(bot);
|
|
165
|
+
let entityCounts = {};
|
|
166
|
+
let villagerIds = [];
|
|
167
|
+
let babyVillagerIds = [];
|
|
168
|
+
let villagerDetails = []; // Store detailed villager info including profession
|
|
169
|
+
|
|
170
|
+
for (const entity of nearbyEntities) {
|
|
171
|
+
if (entity.type === 'player' || entity.name === 'item')
|
|
172
|
+
continue;
|
|
173
|
+
|
|
174
|
+
if (!entityCounts[entity.name]) {
|
|
175
|
+
entityCounts[entity.name] = 0;
|
|
176
|
+
}
|
|
177
|
+
entityCounts[entity.name]++;
|
|
178
|
+
|
|
179
|
+
if (entity.name === 'villager') {
|
|
180
|
+
if (entity.metadata && entity.metadata[16] === 1) {
|
|
181
|
+
babyVillagerIds.push(entity.id);
|
|
182
|
+
} else {
|
|
183
|
+
const profession = world.getVillagerProfession(entity);
|
|
184
|
+
villagerIds.push(entity.id);
|
|
185
|
+
villagerDetails.push({
|
|
186
|
+
id: entity.id,
|
|
187
|
+
profession: profession
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
for (const [entityType, count] of Object.entries(entityCounts)) {
|
|
194
|
+
if (entityType === 'villager') {
|
|
195
|
+
let villagerInfo = `${count} ${entityType}(s)`;
|
|
196
|
+
if (villagerDetails.length > 0) {
|
|
197
|
+
const detailStrings = villagerDetails.map(v => `(${v.id}:${v.profession})`);
|
|
198
|
+
villagerInfo += ` - Adults: ${detailStrings.join(', ')}`;
|
|
199
|
+
}
|
|
200
|
+
if (babyVillagerIds.length > 0) {
|
|
201
|
+
villagerInfo += ` - Baby IDs: ${babyVillagerIds.join(', ')} (babies cannot trade)`;
|
|
202
|
+
}
|
|
203
|
+
res += `\n- entities: ${villagerInfo}`;
|
|
204
|
+
} else {
|
|
205
|
+
res += `\n- entities: ${count} ${entityType}(s)`;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (res == 'NEARBY_ENTITIES') {
|
|
210
|
+
res += ': none';
|
|
211
|
+
}
|
|
212
|
+
return pad(res);
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: "!modes",
|
|
217
|
+
description: "Get all available modes and their docs and see which are on/off.",
|
|
218
|
+
perform: function (agent) {
|
|
219
|
+
return agent.bot.modes.getDocs();
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: '!savedPlaces',
|
|
224
|
+
description: 'List all saved locations.',
|
|
225
|
+
perform: async function (agent) {
|
|
226
|
+
return "Saved place names: " + agent.memory_bank.getKeys();
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
name: '!checkBlueprintLevel',
|
|
231
|
+
description: 'Check if the level is complete and what blocks still need to be placed for the blueprint',
|
|
232
|
+
params: {
|
|
233
|
+
'levelNum': { type: 'int', description: 'The level number to check.', domain: [0, Number.MAX_SAFE_INTEGER] }
|
|
234
|
+
},
|
|
235
|
+
perform: function (agent, levelNum) {
|
|
236
|
+
let res = checkLevelBlueprint(agent, levelNum);
|
|
237
|
+
console.log(res);
|
|
238
|
+
return pad(res);
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: '!checkBlueprint',
|
|
243
|
+
description: 'Check what blocks still need to be placed for the blueprint',
|
|
244
|
+
perform: function (agent) {
|
|
245
|
+
let res = checkBlueprint(agent);
|
|
246
|
+
return pad(res);
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: '!getBlueprint',
|
|
251
|
+
description: 'Get the blueprint for the building',
|
|
252
|
+
perform: function (agent) {
|
|
253
|
+
let res = agent.task.blueprint.explain();
|
|
254
|
+
return pad(res);
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: '!getBlueprintLevel',
|
|
259
|
+
description: 'Get the blueprint for the building',
|
|
260
|
+
params: {
|
|
261
|
+
'levelNum': { type: 'int', description: 'The level number to check.', domain: [0, Number.MAX_SAFE_INTEGER] }
|
|
262
|
+
},
|
|
263
|
+
perform: function (agent, levelNum) {
|
|
264
|
+
let res = agent.task.blueprint.explainLevel(levelNum);
|
|
265
|
+
console.log(res);
|
|
266
|
+
return pad(res);
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: '!getCraftingPlan',
|
|
271
|
+
description: "Provides a comprehensive crafting plan for a specified item. This includes a breakdown of required ingredients, the exact quantities needed, and an analysis of missing ingredients or extra items needed based on the bot's current inventory.",
|
|
272
|
+
params: {
|
|
273
|
+
targetItem: {
|
|
274
|
+
type: 'string',
|
|
275
|
+
description: 'The item that we are trying to craft'
|
|
276
|
+
},
|
|
277
|
+
quantity: {
|
|
278
|
+
type: 'int',
|
|
279
|
+
description: 'The quantity of the item that we are trying to craft',
|
|
280
|
+
optional: true,
|
|
281
|
+
domain: [1, Infinity, '[)'], // Quantity must be at least 1,
|
|
282
|
+
default: 1
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
perform: function (agent, targetItem, quantity = 1) {
|
|
286
|
+
let bot = agent.bot;
|
|
287
|
+
|
|
288
|
+
// Fetch the bot's inventory
|
|
289
|
+
const curr_inventory = world.getInventoryCounts(bot);
|
|
290
|
+
const target_item = targetItem;
|
|
291
|
+
let existingCount = curr_inventory[target_item] || 0;
|
|
292
|
+
let prefixMessage = '';
|
|
293
|
+
if (existingCount > 0) {
|
|
294
|
+
curr_inventory[target_item] -= existingCount;
|
|
295
|
+
prefixMessage = `You already have ${existingCount} ${target_item} in your inventory. If you need to craft more,\n`;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Generate crafting plan
|
|
299
|
+
try {
|
|
300
|
+
let craftingPlan = mc.getDetailedCraftingPlan(target_item, quantity, curr_inventory);
|
|
301
|
+
craftingPlan = prefixMessage + craftingPlan;
|
|
302
|
+
return pad(craftingPlan);
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.error("Error generating crafting plan:", error);
|
|
305
|
+
return `An error occurred while generating the crafting plan: ${error.message}`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
name: '!searchWiki',
|
|
313
|
+
description: 'Search the Minecraft Wiki for the given query.',
|
|
314
|
+
params: {
|
|
315
|
+
'query': { type: 'string', description: 'The query to search for.' }
|
|
316
|
+
},
|
|
317
|
+
perform: async function (agent, query) {
|
|
318
|
+
const url = `https://minecraft.wiki/w/${query}`
|
|
319
|
+
try {
|
|
320
|
+
const response = await fetch(url);
|
|
321
|
+
if (response.status === 404) {
|
|
322
|
+
return `${query} was not found on the Minecraft Wiki. Try adjusting your search term.`;
|
|
323
|
+
}
|
|
324
|
+
const html = await response.text();
|
|
325
|
+
const $ = load(html);
|
|
326
|
+
|
|
327
|
+
const parserOutput = $("div.mw-parser-output");
|
|
328
|
+
|
|
329
|
+
parserOutput.find("table.navbox").remove();
|
|
330
|
+
|
|
331
|
+
const divContent = parserOutput.text();
|
|
332
|
+
|
|
333
|
+
return divContent.trim();
|
|
334
|
+
} catch (error) {
|
|
335
|
+
console.error("Error fetching or parsing HTML:", error);
|
|
336
|
+
return `The following error occurred: ${error}`
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
name: '!help',
|
|
342
|
+
description: 'Lists all available commands and their descriptions.',
|
|
343
|
+
perform: async function (agent) {
|
|
344
|
+
return getCommandDocs(agent);
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
];
|