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,561 @@
|
|
|
1
|
+
import { History } from './history.js';
|
|
2
|
+
import { Coder } from './coder.js';
|
|
3
|
+
import { Prompter } from '../models/prompter.js';
|
|
4
|
+
import { initModes } from './modes.js';
|
|
5
|
+
import { initBot } from '../utils/mcdata.js';
|
|
6
|
+
import { containsCommand, commandExists, executeCommand, truncCommandMessage, isAction, blacklistCommands } from './commands/index.js';
|
|
7
|
+
import { ActionManager } from './action_manager.js';
|
|
8
|
+
import { NPCContoller } from './npc/controller.js';
|
|
9
|
+
import { MemoryBank } from './memory_bank.js';
|
|
10
|
+
import { SelfPrompter } from './self_prompter.js';
|
|
11
|
+
import convoManager from './conversation.js';
|
|
12
|
+
import { handleTranslation, handleEnglishTranslation } from '../utils/translator.js';
|
|
13
|
+
import { addBrowserViewer } from './vision/browser_viewer.js';
|
|
14
|
+
import { serverProxy, sendOutputToServer } from './mindserver_proxy.js';
|
|
15
|
+
import settings from './settings.js';
|
|
16
|
+
import { Task } from './tasks/tasks.js';
|
|
17
|
+
import { speak } from './speak.js';
|
|
18
|
+
import { log, validateNameFormat, handleDisconnection } from './connection_handler.js';
|
|
19
|
+
|
|
20
|
+
export class Agent {
|
|
21
|
+
async start(load_mem=false, init_message=null, count_id=0) {
|
|
22
|
+
this.last_sender = null;
|
|
23
|
+
this.count_id = count_id;
|
|
24
|
+
this._disconnectHandled = false;
|
|
25
|
+
|
|
26
|
+
// Initialize components
|
|
27
|
+
this.actions = new ActionManager(this);
|
|
28
|
+
this.prompter = new Prompter(this, settings.profile);
|
|
29
|
+
this.name = (this.prompter.getName() || '').trim();
|
|
30
|
+
console.log(`Initializing agent ${this.name}...`);
|
|
31
|
+
|
|
32
|
+
// Validate Name Format
|
|
33
|
+
// connection_handler now ensures the message has [LoginGuard] prefix
|
|
34
|
+
const nameCheck = validateNameFormat(this.name);
|
|
35
|
+
if (!nameCheck.success) {
|
|
36
|
+
log(this.name, nameCheck.msg);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.history = new History(this);
|
|
42
|
+
this.coder = new Coder(this);
|
|
43
|
+
this.npc = new NPCContoller(this);
|
|
44
|
+
this.memory_bank = new MemoryBank();
|
|
45
|
+
this.self_prompter = new SelfPrompter(this);
|
|
46
|
+
convoManager.initAgent(this);
|
|
47
|
+
await this.prompter.initExamples();
|
|
48
|
+
|
|
49
|
+
// load mem first before doing task
|
|
50
|
+
let save_data = null;
|
|
51
|
+
if (load_mem) {
|
|
52
|
+
save_data = this.history.load();
|
|
53
|
+
}
|
|
54
|
+
let taskStart = null;
|
|
55
|
+
if (save_data) {
|
|
56
|
+
taskStart = save_data.taskStart;
|
|
57
|
+
} else {
|
|
58
|
+
taskStart = Date.now();
|
|
59
|
+
}
|
|
60
|
+
this.task = new Task(this, settings.task, taskStart);
|
|
61
|
+
this.blocked_actions = settings.blocked_actions.concat(this.task.blocked_actions || []);
|
|
62
|
+
blacklistCommands(this.blocked_actions);
|
|
63
|
+
|
|
64
|
+
console.log(this.name, 'logging into minecraft...');
|
|
65
|
+
this.bot = initBot(this.name);
|
|
66
|
+
|
|
67
|
+
// Connection Handler
|
|
68
|
+
const onDisconnect = (event, reason) => {
|
|
69
|
+
if (this._disconnectHandled) return;
|
|
70
|
+
this._disconnectHandled = true;
|
|
71
|
+
|
|
72
|
+
// Log and Analyze
|
|
73
|
+
// handleDisconnection handles logging to console and server
|
|
74
|
+
const { type } = handleDisconnection(this.name, reason);
|
|
75
|
+
|
|
76
|
+
process.exit(1);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Bind events
|
|
80
|
+
this.bot.once('kicked', (reason) => onDisconnect('Kicked', reason));
|
|
81
|
+
this.bot.once('end', (reason) => onDisconnect('Disconnected', reason));
|
|
82
|
+
this.bot.on('error', (err) => {
|
|
83
|
+
if (String(err).includes('Duplicate') || String(err).includes('ECONNREFUSED')) {
|
|
84
|
+
onDisconnect('Error', err);
|
|
85
|
+
} else {
|
|
86
|
+
log(this.name, `[LoginGuard] Connection Error: ${String(err)}`);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
initModes(this);
|
|
91
|
+
|
|
92
|
+
this.bot.on('login', () => {
|
|
93
|
+
console.log(this.name, 'logged in!');
|
|
94
|
+
serverProxy.login();
|
|
95
|
+
|
|
96
|
+
// Set skin for profile, requires Fabric Tailor. (https://modrinth.com/mod/fabrictailor)
|
|
97
|
+
if (this.prompter.profile.skin)
|
|
98
|
+
this.bot.chat(`/skin set URL ${this.prompter.profile.skin.model} ${this.prompter.profile.skin.path}`);
|
|
99
|
+
else
|
|
100
|
+
this.bot.chat(`/skin clear`);
|
|
101
|
+
});
|
|
102
|
+
const spawnTimeoutDuration = settings.spawn_timeout;
|
|
103
|
+
const spawnTimeout = setTimeout(() => {
|
|
104
|
+
const msg = `Bot has not spawned after ${spawnTimeoutDuration} seconds. Exiting.`;
|
|
105
|
+
log(this.name, msg);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}, spawnTimeoutDuration * 1000);
|
|
108
|
+
this.bot.once('spawn', async () => {
|
|
109
|
+
try {
|
|
110
|
+
clearTimeout(spawnTimeout);
|
|
111
|
+
addBrowserViewer(this.bot, count_id);
|
|
112
|
+
console.log('Initializing vision intepreter...');
|
|
113
|
+
// VisionInterpreter -> camera.js -> node-canvas-webgl, whose native
|
|
114
|
+
// deps (gl, canvas) are optionalDependencies and may not have built.
|
|
115
|
+
// Import lazily so the bot still works without screenshot vision.
|
|
116
|
+
try {
|
|
117
|
+
const { VisionInterpreter } = await import('./vision/vision_interpreter.js');
|
|
118
|
+
this.vision_interpreter = new VisionInterpreter(this, settings.allow_vision);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.warn('Vision module unavailable (optional native deps not built):', e.message);
|
|
121
|
+
this.vision_interpreter = null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// wait for a bit so stats are not undefined
|
|
125
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
126
|
+
|
|
127
|
+
console.log(`${this.name} spawned.`);
|
|
128
|
+
this.clearBotLogs();
|
|
129
|
+
|
|
130
|
+
this._setupEventHandlers(save_data, init_message);
|
|
131
|
+
this.startEvents();
|
|
132
|
+
|
|
133
|
+
if (!load_mem) {
|
|
134
|
+
if (settings.task) {
|
|
135
|
+
this.task.initBotTask();
|
|
136
|
+
this.task.setAgentGoal();
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
// set the goal without initializing the rest of the task
|
|
140
|
+
if (settings.task) {
|
|
141
|
+
this.task.setAgentGoal();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
await new Promise((resolve) => setTimeout(resolve, 10000));
|
|
146
|
+
this.checkAllPlayersPresent();
|
|
147
|
+
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error('Error in spawn event:', error);
|
|
150
|
+
process.exit(0);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async _setupEventHandlers(save_data, init_message) {
|
|
156
|
+
const ignore_messages = [
|
|
157
|
+
"Set own game mode to",
|
|
158
|
+
"Set the time to",
|
|
159
|
+
"Set the difficulty to",
|
|
160
|
+
"Teleported ",
|
|
161
|
+
"Set the weather to",
|
|
162
|
+
"Gamerule "
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
const respondFunc = async (username, message) => {
|
|
166
|
+
if (message === "") return;
|
|
167
|
+
if (username === this.name) return;
|
|
168
|
+
if (settings.only_chat_with.length > 0 && !settings.only_chat_with.includes(username)) return;
|
|
169
|
+
try {
|
|
170
|
+
if (ignore_messages.some((m) => message.startsWith(m))) return;
|
|
171
|
+
|
|
172
|
+
this.shut_up = false;
|
|
173
|
+
|
|
174
|
+
console.log(this.name, 'received message from', username, ':', message);
|
|
175
|
+
|
|
176
|
+
if (convoManager.isOtherAgent(username)) {
|
|
177
|
+
console.warn('received whisper from other bot??')
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
let translation = await handleEnglishTranslation(message);
|
|
181
|
+
this.handleMessage(username, translation);
|
|
182
|
+
}
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.error('Error handling message:', error);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
this.respondFunc = respondFunc;
|
|
189
|
+
|
|
190
|
+
this.bot.on('whisper', respondFunc);
|
|
191
|
+
|
|
192
|
+
this.bot.on('chat', (username, message) => {
|
|
193
|
+
if (serverProxy.getNumOtherAgents() > 0) return;
|
|
194
|
+
// only respond to open chat messages when there are no other agents
|
|
195
|
+
respondFunc(username, message);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Set up auto-eat
|
|
199
|
+
this.bot.autoEat.options = {
|
|
200
|
+
priority: 'foodPoints',
|
|
201
|
+
startAt: 14,
|
|
202
|
+
bannedFood: ["rotten_flesh", "spider_eye", "poisonous_potato", "pufferfish", "chicken"]
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
if (save_data?.self_prompt) {
|
|
206
|
+
if (init_message) {
|
|
207
|
+
this.history.add('system', init_message);
|
|
208
|
+
}
|
|
209
|
+
await this.self_prompter.handleLoad(save_data.self_prompt, save_data.self_prompting_state);
|
|
210
|
+
}
|
|
211
|
+
if (save_data?.last_sender) {
|
|
212
|
+
this.last_sender = save_data.last_sender;
|
|
213
|
+
if (convoManager.otherAgentInGame(this.last_sender)) {
|
|
214
|
+
const msg_package = {
|
|
215
|
+
message: `You have restarted and this message is auto-generated. Continue the conversation with me.`,
|
|
216
|
+
start: true
|
|
217
|
+
};
|
|
218
|
+
convoManager.receiveFromBot(this.last_sender, msg_package);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else if (init_message) {
|
|
222
|
+
await this.handleMessage('system', init_message, 2);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
this.openChat("Hello world! I am "+this.name);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
checkAllPlayersPresent() {
|
|
230
|
+
if (!this.task || !this.task.agent_names) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const missingPlayers = this.task.agent_names.filter(name => !this.bot.players[name]);
|
|
235
|
+
if (missingPlayers.length > 0) {
|
|
236
|
+
console.log(`Missing players/bots: ${missingPlayers.join(', ')}`);
|
|
237
|
+
this.cleanKill('Not all required players/bots are present in the world. Exiting.', 4);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
requestInterrupt() {
|
|
242
|
+
this.bot.interrupt_code = true;
|
|
243
|
+
this.bot.stopDigging();
|
|
244
|
+
this.bot.collectBlock.cancelTask();
|
|
245
|
+
this.bot.pathfinder.stop();
|
|
246
|
+
this.bot.pvp.stop();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
clearBotLogs() {
|
|
250
|
+
this.bot.output = '';
|
|
251
|
+
this.bot.interrupt_code = false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
shutUp() {
|
|
255
|
+
this.shut_up = true;
|
|
256
|
+
if (this.self_prompter.isActive()) {
|
|
257
|
+
this.self_prompter.stop(false);
|
|
258
|
+
}
|
|
259
|
+
convoManager.endAllConversations();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async handleMessage(source, message, max_responses=null) {
|
|
263
|
+
await this.checkTaskDone();
|
|
264
|
+
if (!source || !message) {
|
|
265
|
+
console.warn('Received empty message from', source);
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
let used_command = false;
|
|
270
|
+
if (max_responses === null) {
|
|
271
|
+
max_responses = settings.max_commands === -1 ? Infinity : settings.max_commands;
|
|
272
|
+
}
|
|
273
|
+
if (max_responses === -1) {
|
|
274
|
+
max_responses = Infinity;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const self_prompt = source === 'system' || source === this.name;
|
|
278
|
+
const from_other_bot = convoManager.isOtherAgent(source);
|
|
279
|
+
|
|
280
|
+
if (!self_prompt && !from_other_bot) { // from user, check for forced commands
|
|
281
|
+
const user_command_name = containsCommand(message);
|
|
282
|
+
if (user_command_name) {
|
|
283
|
+
if (!commandExists(user_command_name)) {
|
|
284
|
+
this.routeResponse(source, `Command '${user_command_name}' does not exist.`);
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
this.routeResponse(source, `*${source} used ${user_command_name.substring(1)}*`);
|
|
288
|
+
if (user_command_name === '!newAction') {
|
|
289
|
+
// all user-initiated commands are ignored by the bot except for this one
|
|
290
|
+
// add the preceding message to the history to give context for newAction
|
|
291
|
+
this.history.add(source, message);
|
|
292
|
+
}
|
|
293
|
+
let execute_res = await executeCommand(this, message);
|
|
294
|
+
if (execute_res)
|
|
295
|
+
this.routeResponse(source, execute_res);
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (from_other_bot)
|
|
301
|
+
this.last_sender = source;
|
|
302
|
+
|
|
303
|
+
// Now translate the message
|
|
304
|
+
message = await handleEnglishTranslation(message);
|
|
305
|
+
console.log('received message from', source, ':', message);
|
|
306
|
+
|
|
307
|
+
const checkInterrupt = () => this.self_prompter.shouldInterrupt(self_prompt) || this.shut_up || convoManager.responseScheduledFor(source);
|
|
308
|
+
|
|
309
|
+
let behavior_log = this.bot.modes.flushBehaviorLog().trim();
|
|
310
|
+
if (behavior_log.length > 0) {
|
|
311
|
+
const MAX_LOG = 500;
|
|
312
|
+
if (behavior_log.length > MAX_LOG) {
|
|
313
|
+
behavior_log = '...' + behavior_log.substring(behavior_log.length - MAX_LOG);
|
|
314
|
+
}
|
|
315
|
+
behavior_log = 'Recent behaviors log: \n' + behavior_log;
|
|
316
|
+
await this.history.add('system', behavior_log);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Handle other user messages
|
|
320
|
+
await this.history.add(source, message);
|
|
321
|
+
this.history.save();
|
|
322
|
+
|
|
323
|
+
if (!self_prompt && this.self_prompter.isActive()) // message is from user during self-prompting
|
|
324
|
+
max_responses = 1; // force only respond to this message, then let self-prompting take over
|
|
325
|
+
for (let i=0; i<max_responses; i++) {
|
|
326
|
+
if (checkInterrupt()) break;
|
|
327
|
+
let history = this.history.getHistory();
|
|
328
|
+
let res = await this.prompter.promptConvo(history);
|
|
329
|
+
|
|
330
|
+
console.log(`${this.name} full response to ${source}: ""${res}""`);
|
|
331
|
+
|
|
332
|
+
if (res.trim().length === 0) {
|
|
333
|
+
console.warn('no response')
|
|
334
|
+
break; // empty response ends loop
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
let command_name = containsCommand(res);
|
|
338
|
+
|
|
339
|
+
if (command_name) { // contains query or command
|
|
340
|
+
res = truncCommandMessage(res); // everything after the command is ignored
|
|
341
|
+
this.history.add(this.name, res);
|
|
342
|
+
|
|
343
|
+
if (!commandExists(command_name)) {
|
|
344
|
+
this.history.add('system', `Command ${command_name} does not exist.`);
|
|
345
|
+
console.warn('Agent hallucinated command:', command_name)
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (checkInterrupt()) break;
|
|
350
|
+
this.self_prompter.handleUserPromptedCmd(self_prompt, isAction(command_name));
|
|
351
|
+
|
|
352
|
+
if (settings.show_command_syntax === "full") {
|
|
353
|
+
this.routeResponse(source, res);
|
|
354
|
+
}
|
|
355
|
+
else if (settings.show_command_syntax === "shortened") {
|
|
356
|
+
// show only "used !commandname"
|
|
357
|
+
let pre_message = res.substring(0, res.indexOf(command_name)).trim();
|
|
358
|
+
let chat_message = `*used ${command_name.substring(1)}*`;
|
|
359
|
+
if (pre_message.length > 0)
|
|
360
|
+
chat_message = `${pre_message} ${chat_message}`;
|
|
361
|
+
this.routeResponse(source, chat_message);
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
// no command at all
|
|
365
|
+
let pre_message = res.substring(0, res.indexOf(command_name)).trim();
|
|
366
|
+
if (pre_message.trim().length > 0)
|
|
367
|
+
this.routeResponse(source, pre_message);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
let execute_res = await executeCommand(this, res);
|
|
371
|
+
|
|
372
|
+
console.log('Agent executed:', command_name, 'and got:', execute_res);
|
|
373
|
+
used_command = true;
|
|
374
|
+
|
|
375
|
+
if (execute_res)
|
|
376
|
+
this.history.add('system', execute_res);
|
|
377
|
+
else
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
else { // conversation response
|
|
381
|
+
this.history.add(this.name, res);
|
|
382
|
+
this.routeResponse(source, res);
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
this.history.save();
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return used_command;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async routeResponse(to_player, message) {
|
|
393
|
+
if (this.shut_up) return;
|
|
394
|
+
let self_prompt = to_player === 'system' || to_player === this.name;
|
|
395
|
+
if (self_prompt && this.last_sender) {
|
|
396
|
+
// this is for when the agent is prompted by system while still in conversation
|
|
397
|
+
// so it can respond to events like death but be routed back to the last sender
|
|
398
|
+
to_player = this.last_sender;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (convoManager.isOtherAgent(to_player) && convoManager.inConversation(to_player)) {
|
|
402
|
+
// if we're in an ongoing conversation with the other bot, send the response to it
|
|
403
|
+
convoManager.sendToBot(to_player, message);
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
// otherwise, use open chat
|
|
407
|
+
this.openChat(message);
|
|
408
|
+
// note that to_player could be another bot, but if we get here the conversation has ended
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async openChat(message) {
|
|
413
|
+
let to_translate = message;
|
|
414
|
+
let remaining = '';
|
|
415
|
+
let command_name = containsCommand(message);
|
|
416
|
+
let translate_up_to = command_name ? message.indexOf(command_name) : -1;
|
|
417
|
+
if (translate_up_to != -1) { // don't translate the command
|
|
418
|
+
to_translate = to_translate.substring(0, translate_up_to);
|
|
419
|
+
remaining = message.substring(translate_up_to);
|
|
420
|
+
}
|
|
421
|
+
message = (await handleTranslation(to_translate)).trim() + " " + remaining;
|
|
422
|
+
// newlines are interpreted as separate chats, which triggers spam filters. replace them with spaces
|
|
423
|
+
message = message.replaceAll('\n', ' ');
|
|
424
|
+
|
|
425
|
+
if (settings.only_chat_with.length > 0) {
|
|
426
|
+
for (let username of settings.only_chat_with) {
|
|
427
|
+
this.bot.whisper(username, message);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
if (settings.speak) {
|
|
432
|
+
speak(to_translate, this.prompter.profile.speak_model);
|
|
433
|
+
}
|
|
434
|
+
if (settings.chat_ingame) {this.bot.chat(message);}
|
|
435
|
+
sendOutputToServer(this.name, message);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
startEvents() {
|
|
440
|
+
// Custom events
|
|
441
|
+
this.bot.on('time', () => {
|
|
442
|
+
if (this.bot.time.timeOfDay == 0)
|
|
443
|
+
this.bot.emit('sunrise');
|
|
444
|
+
else if (this.bot.time.timeOfDay == 6000)
|
|
445
|
+
this.bot.emit('noon');
|
|
446
|
+
else if (this.bot.time.timeOfDay == 12000)
|
|
447
|
+
this.bot.emit('sunset');
|
|
448
|
+
else if (this.bot.time.timeOfDay == 18000)
|
|
449
|
+
this.bot.emit('midnight');
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
let prev_health = this.bot.health;
|
|
453
|
+
this.bot.lastDamageTime = 0;
|
|
454
|
+
this.bot.lastDamageTaken = 0;
|
|
455
|
+
this.bot.on('health', () => {
|
|
456
|
+
if (this.bot.health < prev_health) {
|
|
457
|
+
this.bot.lastDamageTime = Date.now();
|
|
458
|
+
this.bot.lastDamageTaken = prev_health - this.bot.health;
|
|
459
|
+
}
|
|
460
|
+
prev_health = this.bot.health;
|
|
461
|
+
});
|
|
462
|
+
// Logging callbacks
|
|
463
|
+
this.bot.on('error' , (err) => {
|
|
464
|
+
console.error('Error event!', err);
|
|
465
|
+
});
|
|
466
|
+
// Use connection handler for runtime disconnects
|
|
467
|
+
this.bot.on('end', (reason) => {
|
|
468
|
+
if (!this._disconnectHandled) {
|
|
469
|
+
const { msg } = handleDisconnection(this.name, reason);
|
|
470
|
+
this.cleanKill(msg);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
this.bot.on('death', () => {
|
|
474
|
+
this.actions.cancelResume();
|
|
475
|
+
this.actions.stop();
|
|
476
|
+
});
|
|
477
|
+
this.bot.on('kicked', (reason) => {
|
|
478
|
+
if (!this._disconnectHandled) {
|
|
479
|
+
const { msg } = handleDisconnection(this.name, reason);
|
|
480
|
+
this.cleanKill(msg);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
this.bot.on('messagestr', async (message, _, jsonMsg) => {
|
|
484
|
+
if (jsonMsg.translate && jsonMsg.translate.startsWith('death') && message.startsWith(this.name)) {
|
|
485
|
+
console.log('Agent died: ', message);
|
|
486
|
+
let death_pos = this.bot.entity.position;
|
|
487
|
+
this.memory_bank.rememberPlace('last_death_position', death_pos.x, death_pos.y, death_pos.z);
|
|
488
|
+
let death_pos_text = null;
|
|
489
|
+
if (death_pos) {
|
|
490
|
+
death_pos_text = `x: ${death_pos.x.toFixed(2)}, y: ${death_pos.y.toFixed(2)}, z: ${death_pos.z.toFixed(2)}`;
|
|
491
|
+
}
|
|
492
|
+
let dimention = this.bot.game.dimension;
|
|
493
|
+
this.handleMessage('system', `You died at position ${death_pos_text || "unknown"} in the ${dimention} dimension with the final message: '${message}'. Your place of death is saved as 'last_death_position' if you want to return. Previous actions were stopped and you have respawned.`);
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
this.bot.on('idle', () => {
|
|
497
|
+
this.bot.clearControlStates();
|
|
498
|
+
this.bot.pathfinder.stop(); // clear any lingering pathfinder
|
|
499
|
+
this.bot.modes.unPauseAll();
|
|
500
|
+
setTimeout(() => {
|
|
501
|
+
if (this.isIdle()) {
|
|
502
|
+
this.actions.resumeAction();
|
|
503
|
+
}
|
|
504
|
+
}, 1000);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
// Init NPC controller
|
|
508
|
+
this.npc.init();
|
|
509
|
+
|
|
510
|
+
// This update loop ensures that each update() is called one at a time, even if it takes longer than the interval
|
|
511
|
+
const INTERVAL = 300;
|
|
512
|
+
let last = Date.now();
|
|
513
|
+
setTimeout(async () => {
|
|
514
|
+
while (true) {
|
|
515
|
+
let start = Date.now();
|
|
516
|
+
await this.update(start - last);
|
|
517
|
+
let remaining = INTERVAL - (Date.now() - start);
|
|
518
|
+
if (remaining > 0) {
|
|
519
|
+
await new Promise((resolve) => setTimeout(resolve, remaining));
|
|
520
|
+
}
|
|
521
|
+
last = start;
|
|
522
|
+
}
|
|
523
|
+
}, INTERVAL);
|
|
524
|
+
|
|
525
|
+
this.bot.emit('idle');
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
async update(delta) {
|
|
529
|
+
await this.bot.modes.update();
|
|
530
|
+
this.self_prompter.update(delta);
|
|
531
|
+
await this.checkTaskDone();
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
isIdle() {
|
|
535
|
+
return !this.actions.executing;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
cleanKill(msg='Killing agent process...', code=1) {
|
|
540
|
+
this.history.add('system', msg);
|
|
541
|
+
this.bot.chat(code > 1 ? 'Restarting.': 'Exiting.');
|
|
542
|
+
this.history.save();
|
|
543
|
+
process.exit(code);
|
|
544
|
+
}
|
|
545
|
+
async checkTaskDone() {
|
|
546
|
+
if (this.task.data) {
|
|
547
|
+
let res = this.task.isDone();
|
|
548
|
+
if (res) {
|
|
549
|
+
await this.history.add('system', `Task ended with score : ${res.score}`);
|
|
550
|
+
await this.history.save();
|
|
551
|
+
// await new Promise(resolve => setTimeout(resolve, 3000)); // Wait 3 second for save to complete
|
|
552
|
+
console.log('Task finished:', res.message);
|
|
553
|
+
this.killAll();
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
killAll() {
|
|
559
|
+
serverProxy.shutdown();
|
|
560
|
+
}
|
|
561
|
+
}
|