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,446 @@
1
+ import * as skills from './library/skills.js';
2
+ import * as world from './library/world.js';
3
+ import * as mc from '../utils/mcdata.js';
4
+ import settings from './settings.js'
5
+ import convoManager from './conversation.js';
6
+
7
+ async function say(agent, message) {
8
+ agent.bot.modes.behavior_log += message + '\n';
9
+ if (agent.shut_up || !settings.narrate_behavior) return;
10
+ agent.openChat(message);
11
+ }
12
+
13
+ // a mode is a function that is called every tick to respond immediately to the world
14
+ // it has the following fields:
15
+ // on: whether 'update' is called every tick
16
+ // active: whether an action has been triggered by the mode and hasn't yet finished
17
+ // paused: whether the mode is paused by another action that overrides the behavior (eg followplayer implements its own self defense)
18
+ // update: the function that is called every tick (if on is true)
19
+ // when a mode is active, it will trigger an action to be performed but won't wait for it to return output
20
+
21
+ // the order of this list matters! first modes will be prioritized
22
+ // while update functions are async, they should *not* be awaited longer than ~100ms as it will block the update loop
23
+ // to perform longer actions, use the execute function which won't block the update loop
24
+ const modes_list = [
25
+ {
26
+ name: 'self_preservation',
27
+ description: 'Respond to drowning, burning, and damage at low health. Interrupts all actions.',
28
+ interrupts: ['all'],
29
+ on: true,
30
+ active: false,
31
+ fall_blocks: ['sand', 'gravel', 'concrete_powder'], // includes matching substrings like 'sandstone' and 'red_sand'
32
+ update: async function (agent) {
33
+ const bot = agent.bot;
34
+ let block = bot.blockAt(bot.entity.position);
35
+ let blockAbove = bot.blockAt(bot.entity.position.offset(0, 1, 0));
36
+ if (!block) block = {name: 'air'}; // hacky fix when blocks are not loaded
37
+ if (!blockAbove) blockAbove = {name: 'air'};
38
+ if (blockAbove.name === 'water') {
39
+ // does not call execute so does not interrupt other actions
40
+ if (!bot.pathfinder.goal) {
41
+ bot.setControlState('jump', true);
42
+ }
43
+ }
44
+ else if (this.fall_blocks.some(name => blockAbove.name.includes(name))) {
45
+ execute(this, agent, async () => {
46
+ await skills.moveAway(bot, 2);
47
+ });
48
+ }
49
+ else if (block.name === 'lava' || block.name === 'fire' ||
50
+ blockAbove.name === 'lava' || blockAbove.name === 'fire') {
51
+ say(agent, 'I\'m on fire!');
52
+ // if you have a water bucket, use it
53
+ let waterBucket = bot.inventory.findInventoryItem('water_bucket');
54
+ if (waterBucket) {
55
+ execute(this, agent, async () => {
56
+ let success = await skills.placeBlock(bot, 'water_bucket', block.position.x, block.position.y, block.position.z);
57
+ if (success) say(agent, 'Placed some water, ahhhh that\'s better!');
58
+ });
59
+ }
60
+ else {
61
+ execute(this, agent, async () => {
62
+ let waterBucket = bot.inventory.findInventoryItem('water_bucket');
63
+ if (waterBucket) {
64
+ let success = await skills.placeBlock(bot, 'water_bucket', block.position.x, block.position.y, block.position.z);
65
+ if (success) say(agent, 'Placed some water, ahhhh that\'s better!');
66
+ return;
67
+ }
68
+ let nearestWater = world.getNearestBlock(bot, 'water', 20);
69
+ if (nearestWater) {
70
+ const pos = nearestWater.position;
71
+ let success = await skills.goToPosition(bot, pos.x, pos.y, pos.z, 0.2);
72
+ if (success) say(agent, 'Found some water, ahhhh that\'s better!');
73
+ return;
74
+ }
75
+ await skills.moveAway(bot, 5);
76
+ });
77
+ }
78
+ }
79
+ else if (Date.now() - bot.lastDamageTime < 3000 && (bot.health < 5 || bot.lastDamageTaken >= bot.health)) {
80
+ say(agent, 'I\'m dying!');
81
+ execute(this, agent, async () => {
82
+ await skills.moveAway(bot, 20);
83
+ });
84
+ }
85
+ else if (agent.isIdle()) {
86
+ bot.clearControlStates(); // clear jump if not in danger or doing anything else
87
+ }
88
+ }
89
+ },
90
+ {
91
+ name: 'unstuck',
92
+ description: 'Attempt to get unstuck when in the same place for a while. Interrupts some actions.',
93
+ interrupts: ['all'],
94
+ on: true,
95
+ active: false,
96
+ prev_location: null,
97
+ distance: 2,
98
+ stuck_time: 0,
99
+ last_time: Date.now(),
100
+ max_stuck_time: 20,
101
+ prev_dig_block: null,
102
+ update: async function (agent) {
103
+ if (agent.isIdle()) {
104
+ this.prev_location = null;
105
+ this.stuck_time = 0;
106
+ return; // don't get stuck when idle
107
+ }
108
+ const bot = agent.bot;
109
+ const cur_dig_block = bot.targetDigBlock;
110
+ if (cur_dig_block && !this.prev_dig_block) {
111
+ this.prev_dig_block = cur_dig_block;
112
+ }
113
+ if (this.prev_location && this.prev_location.distanceTo(bot.entity.position) < this.distance && cur_dig_block == this.prev_dig_block) {
114
+ this.stuck_time += (Date.now() - this.last_time) / 1000;
115
+ }
116
+ else {
117
+ this.prev_location = bot.entity.position.clone();
118
+ this.stuck_time = 0;
119
+ this.prev_dig_block = null;
120
+ }
121
+ const max_stuck_time = cur_dig_block?.name === 'obsidian' ? this.max_stuck_time * 2 : this.max_stuck_time;
122
+ if (this.stuck_time > max_stuck_time) {
123
+ say(agent, 'I\'m stuck!');
124
+ this.stuck_time = 0;
125
+ execute(this, agent, async () => {
126
+ const crashTimeout = setTimeout(() => { agent.cleanKill("Got stuck and couldn't get unstuck") }, 10000);
127
+ await skills.moveAway(bot, 5);
128
+ clearTimeout(crashTimeout);
129
+ say(agent, 'I\'m free.');
130
+ });
131
+ }
132
+ this.last_time = Date.now();
133
+ },
134
+ unpause: function () {
135
+ this.prev_location = null;
136
+ this.stuck_time = 0;
137
+ this.prev_dig_block = null;
138
+ }
139
+ },
140
+ {
141
+ name: 'cowardice',
142
+ description: 'Run away from enemies. Interrupts all actions.',
143
+ interrupts: ['all'],
144
+ on: true,
145
+ active: false,
146
+ update: async function (agent) {
147
+ const enemy = world.getNearestEntityWhere(agent.bot, entity => mc.isHostile(entity), 16);
148
+ if (enemy && await world.isClearPath(agent.bot, enemy)) {
149
+ say(agent, `Aaa! A ${enemy.name.replace("_", " ")}!`);
150
+ execute(this, agent, async () => {
151
+ await skills.avoidEnemies(agent.bot, 24);
152
+ });
153
+ }
154
+ }
155
+ },
156
+ {
157
+ name: 'self_defense',
158
+ description: 'Attack nearby enemies. Interrupts all actions.',
159
+ interrupts: ['all'],
160
+ on: true,
161
+ active: false,
162
+ update: async function (agent) {
163
+ const enemy = world.getNearestEntityWhere(agent.bot, entity => mc.isHostile(entity), 8);
164
+ if (enemy && await world.isClearPath(agent.bot, enemy)) {
165
+ say(agent, `Fighting ${enemy.name}!`);
166
+ execute(this, agent, async () => {
167
+ await skills.defendSelf(agent.bot, 8);
168
+ });
169
+ }
170
+ }
171
+ },
172
+ {
173
+ name: 'hunting',
174
+ description: 'Hunt nearby animals when idle.',
175
+ interrupts: ['action:followPlayer'],
176
+ on: true,
177
+ active: false,
178
+ update: async function (agent) {
179
+ const huntable = world.getNearestEntityWhere(agent.bot, entity => mc.isHuntable(entity), 8);
180
+ if (huntable && await world.isClearPath(agent.bot, huntable)) {
181
+ execute(this, agent, async () => {
182
+ say(agent, `Hunting ${huntable.name}!`);
183
+ await skills.attackEntity(agent.bot, huntable);
184
+ });
185
+ }
186
+ }
187
+ },
188
+ {
189
+ name: 'item_collecting',
190
+ description: 'Collect nearby items when idle.',
191
+ interrupts: ['action:followPlayer'],
192
+ on: true,
193
+ active: false,
194
+
195
+ wait: 2, // number of seconds to wait after noticing an item to pick it up
196
+ prev_item: null,
197
+ noticed_at: -1,
198
+ update: async function (agent) {
199
+ let item = world.getNearestEntityWhere(agent.bot, entity => entity.name === 'item', 8);
200
+ let empty_inv_slots = agent.bot.inventory.emptySlotCount();
201
+ if (item && item !== this.prev_item && await world.isClearPath(agent.bot, item) && empty_inv_slots > 1) {
202
+ if (this.noticed_at === -1) {
203
+ this.noticed_at = Date.now();
204
+ }
205
+ if (Date.now() - this.noticed_at > this.wait * 1000) {
206
+ say(agent, `Picking up item!`);
207
+ this.prev_item = item;
208
+ execute(this, agent, async () => {
209
+ await skills.pickupNearbyItems(agent.bot);
210
+ });
211
+ this.noticed_at = -1;
212
+ }
213
+ }
214
+ else {
215
+ this.noticed_at = -1;
216
+ }
217
+ }
218
+ },
219
+ {
220
+ name: 'torch_placing',
221
+ description: 'Place torches when idle and there are no torches nearby.',
222
+ interrupts: ['action:followPlayer'],
223
+ on: true,
224
+ active: false,
225
+ cooldown: 5,
226
+ last_place: Date.now(),
227
+ update: function (agent) {
228
+ if (world.shouldPlaceTorch(agent.bot)) {
229
+ if (Date.now() - this.last_place < this.cooldown * 1000) return;
230
+ execute(this, agent, async () => {
231
+ const pos = agent.bot.entity.position;
232
+ await skills.placeBlock(agent.bot, 'torch', pos.x, pos.y, pos.z, 'bottom', true);
233
+ });
234
+ this.last_place = Date.now();
235
+ }
236
+ }
237
+ },
238
+ {
239
+ name: 'elbow_room',
240
+ description: 'Move away from nearby players when idle.',
241
+ interrupts: ['action:followPlayer'],
242
+ on: true,
243
+ active: false,
244
+ distance: 0.5,
245
+ update: async function (agent) {
246
+ const player = world.getNearestEntityWhere(agent.bot, entity => entity.type === 'player', this.distance);
247
+ if (player) {
248
+ execute(this, agent, async () => {
249
+ // wait a random amount of time to avoid identical movements with other bots
250
+ const wait_time = Math.random() * 1000;
251
+ await new Promise(resolve => setTimeout(resolve, wait_time));
252
+ if (player.position.distanceTo(agent.bot.entity.position) < this.distance) {
253
+ await skills.moveAwayFromEntity(agent.bot, player, this.distance);
254
+ }
255
+ });
256
+ }
257
+ }
258
+ },
259
+ {
260
+ name: 'idle_staring',
261
+ description: 'Animation to look around at entities when idle.',
262
+ interrupts: [],
263
+ on: true,
264
+ active: false,
265
+
266
+ staring: false,
267
+ last_entity: null,
268
+ next_change: 0,
269
+ update: function (agent) {
270
+ const entity = agent.bot.nearestEntity();
271
+ let entity_in_view = entity && entity.position.distanceTo(agent.bot.entity.position) < 10 && entity.name !== 'enderman';
272
+ if (entity_in_view && entity !== this.last_entity) {
273
+ this.staring = true;
274
+ this.last_entity = entity;
275
+ this.next_change = Date.now() + Math.random() * 1000 + 4000;
276
+ }
277
+ if (entity_in_view && this.staring) {
278
+ let isbaby = entity.type !== 'player' && entity.metadata[16];
279
+ let height = isbaby ? entity.height/2 : entity.height;
280
+ agent.bot.lookAt(entity.position.offset(0, height, 0));
281
+ }
282
+ if (!entity_in_view)
283
+ this.last_entity = null;
284
+ if (Date.now() > this.next_change) {
285
+ // look in random direction
286
+ this.staring = Math.random() < 0.3;
287
+ if (!this.staring) {
288
+ const yaw = Math.random() * Math.PI * 2;
289
+ const pitch = (Math.random() * Math.PI/2) - Math.PI/4;
290
+ agent.bot.look(yaw, pitch, false);
291
+ }
292
+ this.next_change = Date.now() + Math.random() * 10000 + 2000;
293
+ }
294
+ }
295
+ },
296
+ {
297
+ name: 'cheat',
298
+ description: 'Use cheats to instantly place blocks and teleport.',
299
+ interrupts: [],
300
+ on: false,
301
+ active: false,
302
+ update: function (agent) { /* do nothing */ }
303
+ }
304
+ ];
305
+
306
+ async function execute(mode, agent, func, timeout=-1) {
307
+ if (agent.self_prompter.isActive())
308
+ agent.self_prompter.stopLoop();
309
+ let interrupted_action = agent.actions.currentActionLabel;
310
+ mode.active = true;
311
+ let code_return = await agent.actions.runAction(`mode:${mode.name}`, async () => {
312
+ await func();
313
+ }, { timeout });
314
+ mode.active = false;
315
+ console.log(`Mode ${mode.name} finished executing, code_return: ${code_return.message}`);
316
+
317
+ let should_reprompt =
318
+ interrupted_action && // it interrupted a previous action
319
+ !agent.actions.resume_func && // there is no resume function
320
+ !agent.self_prompter.isActive() && // self prompting is not on
321
+ !code_return.interrupted; // this mode action was not interrupted by something else
322
+
323
+ if (should_reprompt) {
324
+ // auto prompt to respond to the interruption
325
+ let role = convoManager.inConversation() ? agent.last_sender : 'system';
326
+ let logs = agent.bot.modes.flushBehaviorLog();
327
+ agent.handleMessage(role, `(AUTO MESSAGE)Your previous action '${interrupted_action}' was interrupted by ${mode.name}.
328
+ Your behavior log: ${logs}\nRespond accordingly.`);
329
+ }
330
+ }
331
+
332
+ let _agent = null;
333
+ const modes_map = {};
334
+ for (let mode of modes_list) {
335
+ modes_map[mode.name] = mode;
336
+ }
337
+
338
+ class ModeController {
339
+ /*
340
+ SECURITY WARNING:
341
+ ModesController must be reference isolated. Do not store references to external objects like `agent`.
342
+ This object is accessible by LLM generated code, so any stored references are also accessible.
343
+ This can be used to expose sensitive information by malicious prompters.
344
+ */
345
+ constructor() {
346
+ this.behavior_log = '';
347
+ }
348
+
349
+ exists(mode_name) {
350
+ return modes_map[mode_name] != null;
351
+ }
352
+
353
+ setOn(mode_name, on) {
354
+ modes_map[mode_name].on = on;
355
+ }
356
+
357
+ isOn(mode_name) {
358
+ return modes_map[mode_name].on;
359
+ }
360
+
361
+ pause(mode_name) {
362
+ modes_map[mode_name].paused = true;
363
+ }
364
+
365
+ unpause(mode_name) {
366
+ const mode = modes_map[mode_name];
367
+ //if unpause func is defined and mode is currently paused
368
+ if (mode.unpause && mode.paused) {
369
+ mode.unpause();
370
+ }
371
+ mode.paused = false;
372
+ }
373
+
374
+ unPauseAll() {
375
+ for (let mode of modes_list) {
376
+ if (mode.paused) console.log(`Unpausing mode ${mode.name}`);
377
+ this.unpause(mode.name);
378
+ }
379
+ }
380
+
381
+ getMiniDocs() { // no descriptions
382
+ let res = 'Agent Modes:';
383
+ for (let mode of modes_list) {
384
+ let on = mode.on ? 'ON' : 'OFF';
385
+ res += `\n- ${mode.name}(${on})`;
386
+ }
387
+ return res;
388
+ }
389
+
390
+ getDocs() {
391
+ let res = 'Agent Modes:';
392
+ for (let mode of modes_list) {
393
+ let on = mode.on ? 'ON' : 'OFF';
394
+ res += `\n- ${mode.name}(${on}): ${mode.description}`;
395
+ }
396
+ return res;
397
+ }
398
+
399
+ async update() {
400
+ if (_agent.isIdle()) {
401
+ this.unPauseAll();
402
+ }
403
+ for (let mode of modes_list) {
404
+ let interruptible = mode.interrupts.some(i => i === 'all') || mode.interrupts.some(i => i === _agent.actions.currentActionLabel);
405
+ if (mode.on && !mode.paused && !mode.active && (_agent.isIdle() || interruptible)) {
406
+ await mode.update(_agent);
407
+ }
408
+ if (mode.active) break;
409
+ }
410
+ }
411
+
412
+ flushBehaviorLog() {
413
+ const log = this.behavior_log;
414
+ this.behavior_log = '';
415
+ return log;
416
+ }
417
+
418
+ getJson() {
419
+ let res = {};
420
+ for (let mode of modes_list) {
421
+ res[mode.name] = mode.on;
422
+ }
423
+ return res;
424
+ }
425
+
426
+ loadJson(json) {
427
+ for (let mode of modes_list) {
428
+ if (json[mode.name] != undefined) {
429
+ mode.on = json[mode.name];
430
+ }
431
+ }
432
+ }
433
+ }
434
+
435
+ export function initModes(agent) {
436
+ _agent = agent;
437
+ // the mode controller is added to the bot object so it is accessible from anywhere the bot is used
438
+ agent.bot.modes = new ModeController();
439
+ if (agent.task) {
440
+ agent.bot.restrict_to_inventory = agent.task.restrict_to_inventory;
441
+ }
442
+ let modes_json = agent.prompter.getInitModes();
443
+ if (modes_json) {
444
+ agent.bot.modes.loadJson(modes_json);
445
+ }
446
+ }
@@ -0,0 +1,80 @@
1
+ import { Vec3 } from 'vec3';
2
+ import * as skills from '../library/skills.js';
3
+ import * as world from '../library/world.js';
4
+ import * as mc from '../../utils/mcdata.js';
5
+ import { blockSatisfied, getTypeOfGeneric, rotateXZ } from './utils.js';
6
+
7
+
8
+ export class BuildGoal {
9
+ constructor(agent) {
10
+ this.agent = agent;
11
+ }
12
+
13
+ async wrapSkill(func) {
14
+ if (!this.agent.isIdle())
15
+ return false;
16
+ let res = await this.agent.actions.runAction('BuildGoal', func);
17
+ return !res.interrupted;
18
+ }
19
+
20
+ async executeNext(goal, position=null, orientation=null) {
21
+ let sizex = goal.blocks[0][0].length;
22
+ let sizez = goal.blocks[0].length;
23
+ let sizey = goal.blocks.length;
24
+ if (!position) {
25
+ for (let x = 0; x < sizex - 1; x++) {
26
+ position = world.getNearestFreeSpace(this.agent.bot, sizex - x, 16);
27
+ if (position) break;
28
+ }
29
+ }
30
+ if (orientation === null) {
31
+ orientation = Math.floor(Math.random() * 4);
32
+ }
33
+
34
+ let inventory = world.getInventoryCounts(this.agent.bot);
35
+ let missing = {};
36
+ let acted = false;
37
+ for (let y = goal.offset; y < sizey+goal.offset; y++) {
38
+ for (let z = 0; z < sizez; z++) {
39
+ for (let x = 0; x < sizex; x++) {
40
+
41
+ let [rx, rz] = rotateXZ(x, z, orientation, sizex, sizez);
42
+ let ry = y - goal.offset;
43
+ let block_name = goal.blocks[ry][rz][rx];
44
+ if (block_name === null || block_name === '') continue;
45
+
46
+ let world_pos = new Vec3(position.x + x, position.y + y, position.z + z);
47
+ let current_block = this.agent.bot.blockAt(world_pos);
48
+
49
+ let res = null;
50
+ if (current_block !== null && !blockSatisfied(block_name, current_block)) {
51
+ acted = true;
52
+
53
+ if (current_block.name !== 'air') {
54
+ res = await this.wrapSkill(async () => {
55
+ await skills.breakBlockAt(this.agent.bot, world_pos.x, world_pos.y, world_pos.z);
56
+ });
57
+ if (!res) return {missing: missing, acted: acted, position: position, orientation: orientation};
58
+ }
59
+
60
+ if (block_name !== 'air') {
61
+ let block_typed = getTypeOfGeneric(this.agent.bot, block_name);
62
+ if (inventory[block_typed] > 0) {
63
+ res = await this.wrapSkill(async () => {
64
+ await skills.placeBlock(this.agent.bot, block_typed, world_pos.x, world_pos.y, world_pos.z);
65
+ });
66
+ if (!res) return {missing: missing, acted: acted, position: position, orientation: orientation};
67
+ } else {
68
+ if (missing[block_typed] === undefined)
69
+ missing[block_typed] = 0;
70
+ missing[block_typed]++;
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+ return {missing: missing, acted: acted, position: position, orientation: orientation};
78
+ }
79
+
80
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "dirt_shelter",
3
+ "offset": -2,
4
+ "blocks": [
5
+ [
6
+ ["", "", "", "", ""],
7
+ ["", "dirt", "dirt", "dirt", ""],
8
+ ["", "dirt", "dirt", "dirt", ""],
9
+ ["", "dirt", "dirt", "dirt", ""],
10
+ ["", "", "dirt", "", ""],
11
+ ["", "", "dirt", "", ""]
12
+ ],
13
+ [
14
+ ["dirt", "dirt", "dirt", "dirt", "dirt"],
15
+ ["dirt", "chest", "bed", "air", "dirt"],
16
+ ["dirt", "air", "bed", "air", "dirt"],
17
+ ["dirt", "air", "air", "air", "dirt"],
18
+ ["dirt", "dirt", "door", "dirt", "dirt"],
19
+ ["dirt", "dirt", "air", "dirt", "dirt"]
20
+ ],
21
+ [
22
+ ["dirt", "dirt", "dirt", "dirt", "dirt"],
23
+ ["dirt", "air", "air", "air", "dirt"],
24
+ ["dirt", "torch", "air", "air", "dirt"],
25
+ ["dirt", "air", "air", "air", "dirt"],
26
+ ["dirt", "dirt", "door", "dirt", "dirt"],
27
+ ["air", "air", "air", "air", "air"]
28
+ ],
29
+ [
30
+ ["air", "air", "air", "air", "air"],
31
+ ["dirt", "dirt", "dirt", "dirt", "dirt"],
32
+ ["dirt", "dirt", "dirt", "dirt", "dirt"],
33
+ ["dirt", "dirt", "dirt", "dirt", "dirt"],
34
+ ["air", "air", "air", "air", "air"],
35
+ ["air", "air", "air", "air", "air"]
36
+ ]
37
+ ]
38
+ }