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.
Files changed (116) hide show
  1. package/FAQ.md +38 -0
  2. package/LICENSE +21 -0
  3. package/README.md +255 -0
  4. package/andy.json +6 -0
  5. package/bin/mindcraft.js +80 -0
  6. package/keys.example.json +19 -0
  7. package/main.js +80 -0
  8. package/package.json +78 -0
  9. package/patches/minecraft-data+3.97.0.patch +13 -0
  10. package/patches/mineflayer+4.33.0.patch +54 -0
  11. package/patches/mineflayer-pathfinder+2.4.5.patch +265 -0
  12. package/patches/mineflayer-pvp+1.3.2.patch +13 -0
  13. package/patches/prismarine-viewer+1.33.0.patch +13 -0
  14. package/patches/protodef+1.19.0.patch +15 -0
  15. package/profiles/andy-4-reasoning.json +14 -0
  16. package/profiles/andy-4.json +7 -0
  17. package/profiles/azure.json +19 -0
  18. package/profiles/claude.json +7 -0
  19. package/profiles/claude_thinker.json +15 -0
  20. package/profiles/deepseek.json +7 -0
  21. package/profiles/defaults/_default.json +256 -0
  22. package/profiles/defaults/assistant.json +14 -0
  23. package/profiles/defaults/creative.json +14 -0
  24. package/profiles/defaults/god_mode.json +14 -0
  25. package/profiles/defaults/survival.json +14 -0
  26. package/profiles/freeguy.json +7 -0
  27. package/profiles/gemini.json +9 -0
  28. package/profiles/gpt.json +12 -0
  29. package/profiles/grok.json +7 -0
  30. package/profiles/llama.json +10 -0
  31. package/profiles/mercury.json +9 -0
  32. package/profiles/mistral.json +5 -0
  33. package/profiles/qwen.json +17 -0
  34. package/profiles/tasks/construction_profile.json +42 -0
  35. package/profiles/tasks/cooking_profile.json +11 -0
  36. package/profiles/tasks/crafting_profile.json +71 -0
  37. package/profiles/vllm.json +10 -0
  38. package/settings.js +64 -0
  39. package/src/agent/action_manager.js +177 -0
  40. package/src/agent/agent.js +561 -0
  41. package/src/agent/coder.js +229 -0
  42. package/src/agent/commands/actions.js +504 -0
  43. package/src/agent/commands/index.js +259 -0
  44. package/src/agent/commands/queries.js +347 -0
  45. package/src/agent/connection_handler.js +96 -0
  46. package/src/agent/conversation.js +353 -0
  47. package/src/agent/history.js +122 -0
  48. package/src/agent/library/full_state.js +89 -0
  49. package/src/agent/library/index.js +23 -0
  50. package/src/agent/library/lockdown.js +32 -0
  51. package/src/agent/library/skill_library.js +93 -0
  52. package/src/agent/library/skills.js +2093 -0
  53. package/src/agent/library/world.js +431 -0
  54. package/src/agent/memory_bank.js +25 -0
  55. package/src/agent/mindserver_proxy.js +136 -0
  56. package/src/agent/modes.js +446 -0
  57. package/src/agent/npc/build_goal.js +80 -0
  58. package/src/agent/npc/construction/dirt_shelter.json +38 -0
  59. package/src/agent/npc/construction/large_house.json +230 -0
  60. package/src/agent/npc/construction/small_stone_house.json +42 -0
  61. package/src/agent/npc/construction/small_wood_house.json +42 -0
  62. package/src/agent/npc/controller.js +261 -0
  63. package/src/agent/npc/data.js +50 -0
  64. package/src/agent/npc/item_goal.js +355 -0
  65. package/src/agent/npc/utils.js +126 -0
  66. package/src/agent/self_prompter.js +146 -0
  67. package/src/agent/settings.js +7 -0
  68. package/src/agent/speak.js +150 -0
  69. package/src/agent/tasks/construction_tasks.js +1104 -0
  70. package/src/agent/tasks/cooking_tasks.js +358 -0
  71. package/src/agent/tasks/tasks.js +594 -0
  72. package/src/agent/templates/execTemplate.js +6 -0
  73. package/src/agent/templates/lintTemplate.js +10 -0
  74. package/src/agent/vision/browser_viewer.js +8 -0
  75. package/src/agent/vision/camera.js +78 -0
  76. package/src/agent/vision/vision_interpreter.js +82 -0
  77. package/src/mindcraft/index.js +28 -0
  78. package/src/mindcraft/mcserver.js +154 -0
  79. package/src/mindcraft/mindcraft.js +111 -0
  80. package/src/mindcraft/mindserver.js +328 -0
  81. package/src/mindcraft/public/index.html +1253 -0
  82. package/src/mindcraft/public/settings_spec.json +145 -0
  83. package/src/mindcraft/userconfig.js +72 -0
  84. package/src/mindcraft-py/example.py +27 -0
  85. package/src/mindcraft-py/init-mindcraft.js +24 -0
  86. package/src/mindcraft-py/mindcraft.py +99 -0
  87. package/src/models/_model_map.js +89 -0
  88. package/src/models/azure.js +32 -0
  89. package/src/models/cerebras.js +61 -0
  90. package/src/models/claude.js +87 -0
  91. package/src/models/deepseek.js +59 -0
  92. package/src/models/gemini.js +176 -0
  93. package/src/models/glhf.js +71 -0
  94. package/src/models/gpt.js +147 -0
  95. package/src/models/grok.js +82 -0
  96. package/src/models/groq.js +95 -0
  97. package/src/models/huggingface.js +86 -0
  98. package/src/models/hyperbolic.js +114 -0
  99. package/src/models/lmstudio.js +74 -0
  100. package/src/models/mercury.js +95 -0
  101. package/src/models/mistral.js +94 -0
  102. package/src/models/novita.js +71 -0
  103. package/src/models/ollama.js +115 -0
  104. package/src/models/openrouter.js +77 -0
  105. package/src/models/prompter.js +366 -0
  106. package/src/models/qwen.js +80 -0
  107. package/src/models/replicate.js +60 -0
  108. package/src/models/vllm.js +81 -0
  109. package/src/process/agent_process.js +84 -0
  110. package/src/process/init_agent.js +54 -0
  111. package/src/utils/examples.js +83 -0
  112. package/src/utils/keys.js +34 -0
  113. package/src/utils/math.js +13 -0
  114. package/src/utils/mcdata.js +572 -0
  115. package/src/utils/text.js +78 -0
  116. package/src/utils/translator.js +30 -0
@@ -0,0 +1,594 @@
1
+ import { readFileSync , writeFileSync, existsSync} from 'fs';
2
+ import { executeCommand } from '../commands/index.js';
3
+ import { getPosition } from '../library/world.js';
4
+ import { ConstructionTaskValidator, Blueprint } from './construction_tasks.js';
5
+ import { CookingTaskInitiator } from './cooking_tasks.js';
6
+
7
+ const PROGRESS_FILE = './hells_kitchen_progress.json';
8
+
9
+ const hellsKitchenProgressManager = {
10
+ readProgress: function() {
11
+ try {
12
+ if (existsSync(PROGRESS_FILE)) {
13
+ const data = readFileSync(PROGRESS_FILE, 'utf8');
14
+ return JSON.parse(data);
15
+ }
16
+ } catch (err) {
17
+ console.error('Error reading progress file:', err);
18
+ }
19
+ return { taskId: null, agent0Complete: false, agent1Complete: false };
20
+ },
21
+
22
+ writeProgress: function(progress) {
23
+ try {
24
+ writeFileSync(PROGRESS_FILE, JSON.stringify(progress), 'utf8');
25
+ } catch (err) {
26
+ console.error('Error writing progress file:', err);
27
+ }
28
+ },
29
+
30
+ resetTask: function(taskId) {
31
+ const progress = { taskId, agent0Complete: false, agent1Complete: false };
32
+ this.writeProgress(progress);
33
+ return progress;
34
+ },
35
+
36
+ updateAgentProgress: function(taskId, agentId, isComplete) {
37
+ const progress = this.readProgress();
38
+
39
+ // If it's a different task, reset first
40
+ if (progress.taskId !== taskId) {
41
+ progress.taskId = taskId;
42
+ progress.agent0Complete = false;
43
+ progress.agent1Complete = false;
44
+ }
45
+
46
+ // Update the specific agent's status
47
+ if (agentId === 0) progress.agent0Complete = isComplete;
48
+ if (agentId === 1) progress.agent1Complete = isComplete;
49
+
50
+ this.writeProgress(progress);
51
+ return progress;
52
+ },
53
+
54
+ isTaskComplete: function(taskId) {
55
+ const progress = this.readProgress();
56
+ if (progress.taskId !== taskId) return false;
57
+ return progress.agent0Complete && progress.agent1Complete;
58
+ }
59
+ };
60
+
61
+
62
+ //todo: modify validator code to return an object with valid and score -> do more testing hahah
63
+ //todo: figure out how to log these things to the same place as bots/histories
64
+ // export class CraftTaskValidator {
65
+ // constructor(data, agent) {
66
+ // this.target = data.target;
67
+ // this.number_of_target = data.number_of_target;
68
+ // this.agent = agent;
69
+
70
+ /**
71
+ * Validates the presence of required items in an agent's inventory
72
+ * @param {Object} data - Task data containing target and quantity information
73
+ * @param {Object} agent - Agent object with bot inventory
74
+ * @returns {Object} Validation result with success status and missing items
75
+ */
76
+ function checkItemPresence(data, agent) {
77
+
78
+ try {
79
+ // Special handling for hells_kitchen tasks
80
+ if (data.task_id && data.task_id.endsWith('hells_kitchen') && Array.isArray(data.target) && data.target.length === 2) {
81
+
82
+ // Get agent ID and target for this agent
83
+ const agentId = agent.count_id;
84
+
85
+ if (agentId === 0 || agentId === 1) {
86
+ // Use only the corresponding element from the target list
87
+ const targetForThisAgent = data.target[agentId];
88
+ const modifiedData = {
89
+ ...data,
90
+ target: targetForThisAgent
91
+ };
92
+
93
+ // Check if this agent has their required item
94
+ const agentResult = checkItemForSingleAgent(modifiedData, agent);
95
+
96
+ // Update the file-based progress tracker
97
+ const progress = hellsKitchenProgressManager.updateAgentProgress(
98
+ data.task_id,
99
+ agentId,
100
+ agentResult.success
101
+ );
102
+
103
+ // // Log the current state
104
+ // console.log(`Agent ${agentId} has item: ${agentResult.success}`);
105
+ // console.log(`Task state: Agent0=${progress.agent0Complete}, Agent1=${progress.agent1Complete}`);
106
+
107
+ // Return combined result - success only if both agents have their items
108
+ return {
109
+ success: progress.agent0Complete && progress.agent1Complete,
110
+ missingItems: agentResult.missingItems,
111
+ agentComplete: agentResult.success // Individual agent status for debugging
112
+ };
113
+ }
114
+ }
115
+
116
+ // Non-hells_kitchen tasks use the standard check
117
+ return checkItemForSingleAgent(data, agent);
118
+
119
+ } catch (error) {
120
+ console.error('Error checking item presence:', error);
121
+ return {
122
+ success: false,
123
+ missingItems: [],
124
+ error: error.message
125
+ };
126
+ }
127
+ }
128
+
129
+
130
+ /**
131
+ * Helper function to check a single agent's inventory
132
+ * Extracted from the original checkItemPresence logic
133
+ */
134
+ function checkItemForSingleAgent(data, agent) {
135
+ function isTargetDictionaryWithQuantities(target) {
136
+ return typeof target === 'object' &&
137
+ !Array.isArray(target) &&
138
+ target !== null &&
139
+ Object.values(target).every(value => typeof value === 'number');
140
+ }
141
+
142
+ function normalizeTargets(target) {
143
+ if (typeof target === 'string') {
144
+ return { [target]: 1 };
145
+ } else if (Array.isArray(target)) {
146
+ return target.reduce((acc, item) => {
147
+ acc[item] = 1;
148
+ return acc;
149
+ }, {});
150
+ } else if (typeof target === 'object' && target !== null) {
151
+ return target;
152
+ }
153
+ throw new Error('Invalid target format');
154
+ }
155
+
156
+ function normalizeQuantities(targets, quantities) {
157
+ if (quantities === undefined) {
158
+ return Object.keys(targets).reduce((acc, key) => {
159
+ acc[key] = 1;
160
+ return acc;
161
+ }, {});
162
+ } else if (typeof quantities === 'number') {
163
+ return Object.keys(targets).reduce((acc, key) => {
164
+ acc[key] = quantities;
165
+ return acc;
166
+ }, {});
167
+ } else if (typeof quantities === 'object' && quantities !== null) {
168
+ return quantities;
169
+ }
170
+ throw new Error('Invalid number_of_target format');
171
+ }
172
+
173
+ // First normalize targets to always have a consistent format
174
+ const targets = normalizeTargets(data.target);
175
+
176
+ // Determine the required quantities
177
+ const requiredQuantities = isTargetDictionaryWithQuantities(data.target)
178
+ ? data.target
179
+ : normalizeQuantities(targets, data.number_of_target);
180
+
181
+ // Count items in inventory
182
+ const inventoryCount = {};
183
+ agent.bot.inventory.slots.forEach((slot) => {
184
+ if (slot) {
185
+ const itemName = slot.name.toLowerCase();
186
+ inventoryCount[itemName] = (inventoryCount[itemName] || 0) + slot.count;
187
+ }
188
+ });
189
+
190
+ // Check if all required items are present in sufficient quantities
191
+ const missingItems = [];
192
+ let allTargetsMet = true;
193
+
194
+ for (const [item, requiredCount] of Object.entries(requiredQuantities)) {
195
+ const itemName = item.toLowerCase();
196
+ const currentCount = inventoryCount[itemName] || 0;
197
+ if (currentCount < requiredCount) {
198
+ allTargetsMet = false;
199
+ missingItems.push({
200
+ item: itemName,
201
+ required: requiredCount,
202
+ current: currentCount,
203
+ missing: requiredCount - currentCount
204
+ });
205
+ }
206
+ }
207
+
208
+ return {
209
+ success: allTargetsMet,
210
+ missingItems: missingItems
211
+ };
212
+ }
213
+
214
+
215
+
216
+ class CookingCraftingTaskValidator {
217
+ constructor(data, agent) {
218
+ this.data = data;
219
+ this.agent = agent;
220
+ }
221
+ validate() {
222
+ const result = checkItemPresence(this.data, this.agent);
223
+ let score = 0;
224
+ if (result.success) {
225
+ score = 1;
226
+ }
227
+ return {
228
+ "valid": result.success,
229
+ "score": score,
230
+ };
231
+ }
232
+ }
233
+
234
+ export class Task {
235
+ constructor(agent, task_data, taskStartTime = null) {
236
+ this.agent = agent;
237
+ this.data = null;
238
+ if (taskStartTime !== null)
239
+ this.taskStartTime = taskStartTime;
240
+ else
241
+ this.taskStartTime = Date.now();
242
+ this.validator = null;
243
+ this.reset_function = null;
244
+ this.blocked_actions = [];
245
+ this.task_data = task_data;
246
+ if (task_data) {
247
+ console.log('Starting task', task_data.task_id);
248
+ console.log("Task start time set to", this.taskStartTime);
249
+ if (task_data.task_id.endsWith('hells_kitchen')) {
250
+ // Reset hells_kitchen progress when a new task starts
251
+ hellsKitchenProgressManager.resetTask(task_data.task_id);
252
+ console.log('Reset Hells Kitchen progress for new task');
253
+ }
254
+ this.data = task_data;
255
+ this.task_type = this.data.type;
256
+ if (this.task_type === 'construction' && this.data.blueprint) {
257
+ this.blueprint = new Blueprint(this.data.blueprint);
258
+ this.goal = this.data.goal + ' \n' + this.blueprint.explain() + " \n" + "make sure to place the lower levels of the blueprint first";
259
+ this.conversation = this.data.conversation + ' \n' + this.blueprint.explain();
260
+ } else {
261
+ this.goal = this.data.goal;
262
+ this.conversation = this.data.conversation;
263
+ }
264
+ this.taskTimeout = this.data.timeout || 300;
265
+ // Set validator based on task_type
266
+
267
+ // do goal initialization here
268
+
269
+ // let agentGoal = this.getAgentGoal();
270
+ // if (agentGoal) {
271
+ // agentGoal += "You have to collaborate with other agents/bots, namely " + this.available_agents.filter(n => n !== this.name).join(', ') + " to complete the task as soon as possible by dividing the work among yourselves.";
272
+ // console.log(`Setting goal for agent ${this.agent.count_id}: ${agentGoal}`);
273
+ // await executeCommand(this.agent, `!goal("${agentGoal}")`);
274
+ // }
275
+
276
+ if (this.task_type === 'construction') {
277
+ this.validator = new ConstructionTaskValidator(this.data, this.agent);
278
+ } else if (this.task_type === 'cooking' || this.task_type === 'techtree') {
279
+ this.validator = new CookingCraftingTaskValidator(this.data, this.agent);
280
+
281
+ } else {
282
+ this.validator = null;
283
+ }
284
+
285
+ if (this.data.blocked_actions) {
286
+ this.blocked_actions = this.data.blocked_actions[this.agent.count_id.toString()] || [];
287
+ } else {
288
+ this.blocked_actions = [];
289
+ }
290
+ this.restrict_to_inventory = !!this.data.restrict_to_inventory;
291
+ if (this.data.goal)
292
+ this.blocked_actions.push('!endGoal');
293
+ if (this.conversation)
294
+ this.blocked_actions.push('!endConversation');
295
+ }
296
+ else {
297
+ console.log('No task.');
298
+ }
299
+
300
+ this.name = this.agent.name;
301
+ this.available_agents = []
302
+ }
303
+
304
+ updateAvailableAgents(agents) {
305
+ this.available_agents = agents
306
+ }
307
+
308
+ // Add this method if you want to manually reset the hells_kitchen progress
309
+ resetHellsKitchenProgress() {
310
+ if (this.task_id && this.task_id.endsWith('hells_kitchen')) {
311
+ hellsKitchenProgressManager.resetTask(this.task_id);
312
+ console.log('Hells Kitchen progress reset manually');
313
+ }
314
+ }
315
+
316
+ getAgentGoal() {
317
+ if (!this.data || !this.data.goal) {
318
+ return null;
319
+ }
320
+
321
+ let add_string = '';
322
+
323
+ if (this.task_type === 'cooking') {
324
+
325
+
326
+ if (this.data.agent_count > 2) {
327
+
328
+ if (this.name.toLowerCase().startsWith('andy')) {
329
+ add_string = '\nIn the end, all the food items should be given to you by other bots. Make sure to talk to all the agents using startConversation command to coordinate the task instead of talking to just one agent. You can even end current conversation with any agent using endConversation command and then talk to a new agent using startConversation command.';
330
+ }
331
+ else {
332
+ add_string = '\nIn the end, all the food items should be given to one single bot whose name starts with andy or Andy. Make sure to talk to all the agents using startConversation command to coordinate the task instead of talking to just one agent. You can even end current conversation with any agent using endConversation command and then talk to a new agent using startConversation command.';
333
+ }
334
+ }
335
+ else {
336
+ if (this.data.task_id && this.data.task_id.endsWith('hells_kitchen')) {
337
+ add_string = '';
338
+ }
339
+ else {
340
+ add_string = '\nIn the end, all the food items should be given to one single bot.';
341
+ }
342
+ }
343
+ }
344
+
345
+ if (this.task_type === 'techtree') {
346
+ if (this.data.agent_count > 2) {
347
+ add_string = '\nMake sure to share resources among all agents and to talk to all the agents using startConversation command to coordinate the task instead of talking to just one agent. You can even end current conversation with any agent using endConversation command and then talk to a new agent using startConversation command.'
348
+ }
349
+ }
350
+
351
+ // If goal is a string, all agents share the same goal
352
+ if (typeof this.data.goal === 'string') {
353
+ return this.data.goal + add_string;
354
+ }
355
+
356
+ // If goal is an object, get the goal for this agent's count_id
357
+ if (typeof this.data.goal === 'object' && this.data.goal !== null) {
358
+ const agentId = this.agent.count_id.toString();
359
+ return (this.data.goal[agentId] || '') + add_string;
360
+ }
361
+
362
+ return null;
363
+ }
364
+
365
+ isDone() {
366
+ let res = null;
367
+ if (this.validator)
368
+ res = this.validator.validate();
369
+ if (res && res.valid) {
370
+ // Find all the agents and clear their inventories
371
+ for (let agent of this.available_agents) {
372
+ this.agent.bot.chat(`/clear ${agent}`);
373
+ }
374
+ // this.agent.bot.chat(`/clear @a`);
375
+ return {"message": 'Task successful', "score": res.score};
376
+ }
377
+ let other_names = this.available_agents.filter(n => n !== this.name);
378
+ const elapsedTime = (Date.now() - this.taskStartTime) / 1000;
379
+
380
+ if (elapsedTime >= 30 && this.available_agents.length !== this.data.agent_count) {
381
+ console.log('No other agents found. Task unsuccessful.');
382
+ return {"message": 'No other agents found', "score": 0};
383
+ }
384
+
385
+ if (this.taskTimeout) {
386
+ if (elapsedTime >= this.taskTimeout) {
387
+ console.log('Task timeout reached. Task unsuccessful.');
388
+ if (res) {
389
+ return {"message": 'Task timeout reached', "score": res.score};
390
+ } else {
391
+ return {"message": 'Task timeout reached', "score": 0};
392
+ }
393
+
394
+ }
395
+ }
396
+ return false;
397
+ }
398
+
399
+ async setAgentGoal() {
400
+ let agentGoal = this.getAgentGoal();
401
+ if (agentGoal && this.data.agent_count + this.data.human_count > 1) {
402
+ agentGoal += "You have to collaborate with other agents/bots, namely " + this.available_agents.filter(n => n !== this.name).join(', ') + " to complete the task as soon as possible by dividing the work among yourselves.";
403
+ console.log(`Setting goal for agent ${this.agent.count_id}: ${agentGoal}`);
404
+ }
405
+ await executeCommand(this.agent, `!goal("${agentGoal}")`);
406
+ }
407
+
408
+ async initBotTask() {
409
+ await this.agent.bot.chat(`/clear ${this.name}`);
410
+ console.log(`Cleared ${this.name}'s inventory.`);
411
+
412
+ //wait for a bit so inventory is cleared
413
+ await new Promise((resolve) => setTimeout(resolve, 500));
414
+
415
+ if (this.data === null)
416
+ return;
417
+
418
+ if (this.task_type === 'cooking') {
419
+ this.initiator = new CookingTaskInitiator(this.data, this.agent.bot);
420
+ } else {
421
+ this.initiator = null;
422
+ }
423
+
424
+ //wait for a bit so bots are teleported
425
+ await new Promise((resolve) => setTimeout(resolve, 3000));
426
+
427
+ if (this.agent.count_id === 0 && this.data.human_count > 0) {
428
+ console.log('Clearing human player inventories');
429
+ for (let i = 0; i < this.data.human_count; i++) {
430
+ const username = this.data.usernames[i];
431
+ await this.agent.bot.chat(`/clear ${username}`);
432
+ }
433
+ await new Promise((resolve) => setTimeout(resolve, 500));
434
+ }
435
+
436
+ if (this.data.initial_inventory) {
437
+ console.log("Setting inventory...");
438
+ let initialInventory = {};
439
+
440
+ initialInventory = this.data.initial_inventory[this.agent.count_id.toString()] || {};
441
+ console.log("Initial inventory for agent", this.agent.count_id, ":", initialInventory);
442
+ console.log("")
443
+
444
+ if (this.data.human_count > 0 && this.agent.count_id === 0) {
445
+ // this.num_humans = num_keys - this.data.num_agents;
446
+ if (this.data.human_count !== this.data.usernames.length) {
447
+ console.log(`Number of human players ${this.human_count} does not match the number of usernames provided. ${this.data.usernames.length}`);
448
+ throw new Error(`Number of human players ${this.human_count} does not match the number of usernames provided. ${this.data.usernames.length}`);
449
+ return;
450
+ }
451
+
452
+ const starting_idx = this.data.agent_count;
453
+ for (let i = 0; i < this.data.human_count; i++) {
454
+ const username = this.data.usernames[i];
455
+ const inventory = this.data.initial_inventory[starting_idx + i];
456
+ console.log(Object.keys(inventory));
457
+ for (let key of Object.keys(inventory)) {
458
+ const itemName = key.toLowerCase();
459
+ const quantity = inventory[key];
460
+ console.log(`Give ${username} ${quantity} ${itemName}`);
461
+ await this.agent.bot.chat(`/give ${username} ${itemName} ${quantity}`);
462
+ }
463
+ }
464
+ }
465
+ console.log(this.data.initial_inventory);
466
+
467
+ // Assign inventory items
468
+ for (let key of Object.keys(initialInventory)) {
469
+ const itemName = key.toLowerCase();
470
+ const quantity = initialInventory[key];
471
+ await this.agent.bot.chat(`/give ${this.name} ${itemName} ${quantity}`);
472
+ console.log(`Gave ${this.name} ${quantity} ${itemName}`);
473
+ }
474
+
475
+ // Wait briefly for inventory commands to complete
476
+ await new Promise((resolve) => setTimeout(resolve, 500));
477
+ }
478
+
479
+ if (this.initiator && this.agent.count_id === 0) {
480
+ await this.initiator.init();
481
+ }
482
+
483
+ await this.teleportBots();
484
+
485
+ if (this.data.agent_count && this.data.agent_count > 1) {
486
+ // TODO wait for other bots to join
487
+ await new Promise((resolve) => setTimeout(resolve, 10000));
488
+ if (this.available_agents.length < this.data.agent_count) {
489
+ console.log(`Missing ${this.data.agent_count - this.available_agents.length} bot(s).`);
490
+ this.agent.killAll();
491
+ }
492
+ }
493
+ await new Promise((resolve) => setTimeout(resolve, 500));
494
+ if (this.data.conversation && this.agent.count_id === 0) {
495
+ let other_name = this.available_agents.filter(n => n !== this.name)[0];
496
+ let waitCount = 0;
497
+ while (other_name === undefined && waitCount < 20) {
498
+ other_name = this.available_agents.filter(n => n !== this.name)[0];
499
+ await new Promise((resolve) => setTimeout(resolve, 1000));
500
+ waitCount++;
501
+ }
502
+ if (other_name === undefined && this.data.agent_count > 1) {
503
+ console.log('No other agents found. Task unsuccessful.');
504
+ this.agent.killAll();
505
+ }
506
+ await executeCommand(this.agent, `!startConversation("${other_name}", "${this.data.conversation}")`);
507
+ }
508
+ await this.setAgentGoal();
509
+ }
510
+
511
+ async teleportBots() {
512
+ console.log('\n\nTeleporting bots');
513
+ function getRandomOffset(range) {
514
+ return Math.floor(Math.random() * (range * 2 + 1)) - range;
515
+ }
516
+
517
+ let human_player_name = null;
518
+ let bot = this.agent.bot;
519
+
520
+ // Finding if there is a human player on the server
521
+ for (const playerName in bot.players) {
522
+ const player = bot.players[playerName];
523
+ if (!this.available_agents.some((n) => n === playerName)) {
524
+ console.log('Found human player:', player.username);
525
+ human_player_name = player.username
526
+ break;
527
+ }
528
+ }
529
+
530
+ // go the human if there is one and not required for the task
531
+ if (human_player_name && this.data.human_count === 0) {
532
+ console.log(`Teleporting ${this.name} to human ${human_player_name}`)
533
+ bot.chat(`/tp ${this.name} ${human_player_name}`)
534
+ }
535
+ else {
536
+ console.log(`Teleporting ${this.name} to ${this.available_agents[0]}`)
537
+ bot.chat(`/tp ${this.name} ${this.available_agents[0]}`);
538
+ }
539
+
540
+ await new Promise((resolve) => setTimeout(resolve, 200));
541
+
542
+ // now all bots are teleport on top of each other (which kinda looks ugly)
543
+ // Thus, we need to teleport them to random distances to make it look better
544
+
545
+ /*
546
+ Note : We don't want randomness for construction task as the reference point matters a lot.
547
+ Another reason for no randomness for construction task is because, often times the user would fly in the air,
548
+ then set a random block to dirt and teleport the bot to stand on that block for starting the construction,
549
+ */
550
+
551
+
552
+ if (this.data.type !== 'construction') {
553
+ const pos = getPosition(bot);
554
+ const xOffset = getRandomOffset(5);
555
+ const zOffset = getRandomOffset(5);
556
+ bot.chat(`/tp ${this.name} ${Math.floor(pos.x + xOffset)} ${pos.y + 3} ${Math.floor(pos.z + zOffset)}`);
557
+ await new Promise((resolve) => setTimeout(resolve, 200));
558
+ }
559
+
560
+ if (this.data.agent_count && this.data.agent_count > 1) {
561
+ // TODO wait for other bots to join
562
+ await new Promise((resolve) => setTimeout(resolve, 10000));
563
+ if (this.available_agents.length < this.data.agent_count) {
564
+ console.log(`Missing ${this.data.agent_count - this.available_agents.length} bot(s).`);
565
+ this.agent.killAll();
566
+ }
567
+ }
568
+
569
+ if (this.data.type === 'construction'){
570
+ //Ensures construction is cleaned out first. -> relies on cheats which are turned off?
571
+ if (this.blueprint){
572
+ console.log('Cleaning out construction blueprint');
573
+ const result = this.blueprint.autoDelete();
574
+ const commands = result.commands;
575
+ const nearbyPosition = result.nearbyPosition;
576
+ console.log("nearby position", nearbyPosition);
577
+ const first_coord = this.data.blueprint.levels[0].coordinates;
578
+ bot.chat(`/tp @a ${first_coord[0]} ${first_coord[1]} ${first_coord[2]}`);
579
+ if (this.agent.agent_id === 0 && this.data.human_count > 0) {
580
+ for (let i = 0; i < this.data.human_count; i++) {
581
+ const username = this.data.usernames[i];
582
+ await bot.chat(`/tp ${username} ${nearbyPosition.x} ${nearbyPosition.y} ${nearbyPosition.z}`);
583
+ }
584
+ }
585
+ for (const command of commands) {
586
+ bot.chat(command);
587
+ }
588
+ }
589
+ else{
590
+ console.log('no construction blueprint?')
591
+ }
592
+ }
593
+ }
594
+ }
@@ -0,0 +1,6 @@
1
+ (async (bot) => {
2
+
3
+ /* CODE HERE */
4
+ log(bot, 'Code finished.');
5
+
6
+ })
@@ -0,0 +1,10 @@
1
+ import * as skills from '../../../src/agent/library/skills.js';
2
+ import * as world from '../../../src/agent/library/world.js';
3
+ import Vec3 from 'vec3';
4
+
5
+ const log = skills.log;
6
+
7
+ export async function main(bot) {
8
+ /* CODE HERE */
9
+ log(bot, 'Code finished.');
10
+ }
@@ -0,0 +1,8 @@
1
+ import settings from '../settings.js';
2
+ import prismarineViewer from 'prismarine-viewer';
3
+ const mineflayerViewer = prismarineViewer.mineflayer;
4
+
5
+ export function addBrowserViewer(bot, count_id) {
6
+ if (settings.render_bot_view)
7
+ mineflayerViewer(bot, { port: 3000+count_id, firstPerson: true, });
8
+ }