mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-08-03 13:55:36 +02:00
init defaults
This commit is contained in:
parent
2af1fc16ee
commit
e2e199a093
5 changed files with 104 additions and 101 deletions
|
@ -165,6 +165,10 @@ export class Agent {
|
|||
}
|
||||
});
|
||||
|
||||
this.bot.on('idle', () => {
|
||||
this.coder.executeDefault();
|
||||
});
|
||||
|
||||
this.self_defense = true;
|
||||
this.defending = false;
|
||||
this._pause_defending = false;
|
||||
|
|
|
@ -12,6 +12,9 @@ export class Coder {
|
|||
this.executing = false;
|
||||
this.code_template = '';
|
||||
this.timedout = false;
|
||||
this.default_func = null;
|
||||
this.default_name = null;
|
||||
this.interruptible = false;
|
||||
}
|
||||
|
||||
async load() {
|
||||
|
@ -85,7 +88,6 @@ export class Coder {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
async generateCode(agent_history) {
|
||||
let system_message = "You are a minecraft mineflayer bot that plays minecraft by writing javascript codeblocks. Given the conversation between you and the user, use the provided skills and world functions to write your code in a codeblock. Example response: ``` // your code here ``` You will then be given a response to your code. If you are satisfied with the response, respond without a codeblock in a conversational way. If something went wrong, write another codeblock and try to fix the problem.";
|
||||
system_message += getSkillDocs();
|
||||
|
@ -150,6 +152,21 @@ export class Coder {
|
|||
return;
|
||||
}
|
||||
|
||||
async executeDefault(func=null, name=null, timeout=10) {
|
||||
if (func != null) {
|
||||
this.default_func = func;
|
||||
this.default_name = name;
|
||||
}
|
||||
if (this.default_func != null) {
|
||||
this.interruptible = true;
|
||||
let res = await this.execute(this.default_func, timeout);
|
||||
this.interruptible = false;
|
||||
return res;
|
||||
} else {
|
||||
return {success: false, message: null, interrupted: false, timedout: false};
|
||||
}
|
||||
}
|
||||
|
||||
// returns {success: bool, message: string, interrupted: bool, timedout: false}
|
||||
async execute(func, timeout=10) {
|
||||
if (!this.code_template) return {success: false, message: "Code template not loaded.", interrupted: false, timedout: false};
|
||||
|
@ -171,6 +188,7 @@ export class Coder {
|
|||
let interrupted = this.agent.bot.interrupt_code;
|
||||
let timedout = this.timedout;
|
||||
this.clear();
|
||||
if (!interrupted) this.agent.bot.emit('idle');
|
||||
return {success:true, message: output, interrupted, timedout};
|
||||
} catch (err) {
|
||||
console.error("Code execution triggered catch: " + err);
|
||||
|
@ -180,6 +198,7 @@ export class Coder {
|
|||
let message = this.formatOutput(this.agent.bot) + '!!Code threw exception!! Error: ' + err;
|
||||
let interrupted = this.agent.bot.interrupt_code;
|
||||
this.clear();
|
||||
if (!interrupted) this.agent.bot.emit('idle');
|
||||
return {success: false, message, interrupted, timedout: false};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
import * as skills from '../library/skills.js';
|
||||
|
||||
|
||||
function wrapExecution(func, timeout=-1) {
|
||||
function wrapExecution(func, timeout=-1, default_name=null) {
|
||||
return async function (agent, ...args) {
|
||||
let code_return = await agent.coder.execute(async () => {
|
||||
await func(agent, ...args);
|
||||
}, timeout);
|
||||
let code_return;
|
||||
if (default_name != null) {
|
||||
code_return = await agent.coder.executeDefault(async () => {
|
||||
await func(agent, ...args);
|
||||
}, default_name, timeout);
|
||||
} else {
|
||||
code_return = await agent.coder.execute(async () => {
|
||||
await func(agent, ...args);
|
||||
}, timeout);
|
||||
}
|
||||
if (code_return.interrupted && !code_return.timedout)
|
||||
return;
|
||||
return code_return.message;
|
||||
|
@ -26,6 +33,8 @@ export const actionsList = [
|
|||
perform: async function (agent) {
|
||||
await agent.coder.stop();
|
||||
agent.coder.clear();
|
||||
agent.coder.default_func = null;
|
||||
agent.coder.default_name = null;
|
||||
return 'Agent stopped.';
|
||||
}
|
||||
},
|
||||
|
@ -60,7 +69,7 @@ export const actionsList = [
|
|||
params: {'player_name': '(string) The name of the player to follow.'},
|
||||
perform: wrapExecution(async (agent, player_name) => {
|
||||
await skills.followPlayer(agent.bot, player_name);
|
||||
})
|
||||
}, -1, 'followPlayer')
|
||||
},
|
||||
{
|
||||
name: '!givePlayer',
|
||||
|
@ -81,6 +90,16 @@ export const actionsList = [
|
|||
await skills.collectBlock(agent.bot, type, num);
|
||||
}, 10) // 10 minute timeout
|
||||
},
|
||||
{
|
||||
name: '!collectAllBlocks',
|
||||
description: 'Collect all the nearest blocks of a given type until told to stop.',
|
||||
params: {
|
||||
'type': '(string) The block type to collect. Ex: !collectAllBlocks("stone")'
|
||||
},
|
||||
perform: wrapExecution(async (agent, type) => {
|
||||
await skills.collectBlock(agent.bot, type, 1);
|
||||
}, 10, 'collectAllBlocks') // 10 minute timeout
|
||||
},
|
||||
{
|
||||
name: '!craftRecipe',
|
||||
description: 'Craft the given recipe a given number of times. Ex: I will craft 8 sticks !craftRecipe("stick", 2)',
|
||||
|
|
|
@ -643,49 +643,18 @@ export async function followPlayer(bot, username) {
|
|||
* @example
|
||||
* await skills.followPlayer(bot, "player");
|
||||
**/
|
||||
bot.modes.pause('self_defense');
|
||||
bot.modes.pause('hunting');
|
||||
|
||||
let player = bot.players[username].entity
|
||||
if (!player)
|
||||
return false;
|
||||
|
||||
const follow_distance = 4;
|
||||
const attack_distance = 8;
|
||||
|
||||
bot.pathfinder.setMovements(new pf.Movements(bot));
|
||||
bot.pathfinder.setGoal(new pf.goals.GoalFollow(player, follow_distance), true);
|
||||
log(bot, `You are now actively following player ${username}.`);
|
||||
|
||||
while (!bot.interrupt_code) {
|
||||
let acted = false;
|
||||
if (bot.modes.isOn('self_defense')) {
|
||||
const enemy = world.getNearestEntityWhere(bot, entity => mc.isHostile(entity), attack_distance);
|
||||
if (enemy) {
|
||||
log(bot, `Found ${enemy.name}, attacking!`, true);
|
||||
await defendSelf(bot, 8);
|
||||
acted = true;
|
||||
}
|
||||
}
|
||||
if (bot.modes.isOn('hunting')) {
|
||||
const animal = world.getNearestEntityWhere(bot, entity => mc.isHuntable(entity), attack_distance);
|
||||
if (animal) {
|
||||
log(bot, `Hunting ${animal.name}!`, true);
|
||||
await attackEntity(bot, animal, true);
|
||||
acted = true;
|
||||
}
|
||||
}
|
||||
if (bot.entity.position.distanceTo(player.position) < follow_distance) {
|
||||
acted = autoLight(bot);
|
||||
}
|
||||
|
||||
if (acted) { // if we did something then resume following
|
||||
bot.pathfinder.setMovements(new pf.Movements(bot));
|
||||
bot.pathfinder.setGoal(new pf.goals.GoalFollow(player, follow_distance), true);
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,10 +15,10 @@ const modes = [
|
|||
{
|
||||
name: 'self_defense',
|
||||
description: 'Automatically attack nearby enemies. Interrupts other actions.',
|
||||
interrupts: ['all'],
|
||||
on: true,
|
||||
active: false,
|
||||
update: function (agent) {
|
||||
if (this.active) return;
|
||||
const enemy = world.getNearestEntityWhere(agent.bot, entity => mc.isHostile(entity), 8);
|
||||
if (enemy) {
|
||||
agent.bot.chat(`Fighting ${enemy.name}!`);
|
||||
|
@ -31,57 +31,53 @@ const modes = [
|
|||
{
|
||||
name: 'hunting',
|
||||
description: 'Automatically hunt nearby animals when idle.',
|
||||
interrupts: ['defaults'],
|
||||
on: true,
|
||||
active: false,
|
||||
update: function (agent) {
|
||||
if (!agent.coder.executing) {
|
||||
const huntable = world.getNearestEntityWhere(agent.bot, entity => mc.isHuntable(entity), 8);
|
||||
if (huntable) {
|
||||
execute(this, agent, async () => {
|
||||
agent.bot.chat(`Hunting ${huntable.name}!`);
|
||||
await skills.attackEntity(agent.bot, huntable);
|
||||
});
|
||||
}
|
||||
const huntable = world.getNearestEntityWhere(agent.bot, entity => mc.isHuntable(entity), 8);
|
||||
if (huntable) {
|
||||
execute(this, agent, async () => {
|
||||
agent.bot.chat(`Hunting ${huntable.name}!`);
|
||||
await skills.attackEntity(agent.bot, huntable);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'item_collecting',
|
||||
description: 'Automatically collect nearby items when idle.',
|
||||
interrupts: ['followPlayer'],
|
||||
on: true,
|
||||
active: false,
|
||||
update: function (agent) {
|
||||
if (!agent.coder.executing) {
|
||||
let item = world.getNearestEntityWhere(agent.bot, entity => entity.name === 'item', 8);
|
||||
if (item) {
|
||||
execute(this, agent, async () => {
|
||||
// wait 2 seconds for the item to settle
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await skills.pickupNearbyItem(agent.bot);
|
||||
});
|
||||
}
|
||||
let item = world.getNearestEntityWhere(agent.bot, entity => entity.name === 'item', 8);
|
||||
if (item) {
|
||||
execute(this, agent, async () => {
|
||||
// wait 2 seconds for the item to settle
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await skills.pickupNearbyItem(agent.bot);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'torch_placing',
|
||||
description: 'Automatically place torches when idle and there are no torches nearby.',
|
||||
interrupts: ['followPlayer'],
|
||||
on: true,
|
||||
active: false,
|
||||
update: function (agent) {
|
||||
if (this.active) return;
|
||||
if (!agent.coder.executing) {
|
||||
// TODO: check light level instead of nearby torches, block.light is broken
|
||||
const near_torch = world.getNearestBlock(agent.bot, 'torch', 8);
|
||||
if (!near_torch) {
|
||||
let torches = agent.bot.inventory.items().filter(item => item.name.includes('torch'));
|
||||
if (torches.length > 0) {
|
||||
const torch = torches[0];
|
||||
const pos = agent.bot.entity.position;
|
||||
execute(this, agent, async () => {
|
||||
await skills.placeBlock(agent.bot, torch.name, pos.x, pos.y, pos.z);
|
||||
});
|
||||
}
|
||||
// TODO: check light level instead of nearby torches, block.light is broken
|
||||
const near_torch = world.getNearestBlock(agent.bot, 'torch', 8);
|
||||
if (!near_torch) {
|
||||
let torches = agent.bot.inventory.items().filter(item => item.name.includes('torch'));
|
||||
if (torches.length > 0) {
|
||||
const torch = torches[0];
|
||||
const pos = agent.bot.entity.position;
|
||||
execute(this, agent, async () => {
|
||||
await skills.placeBlock(agent.bot, torch.name, pos.x, pos.y, pos.z);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +85,7 @@ const modes = [
|
|||
{
|
||||
name: 'idle_staring',
|
||||
description: 'Non-functional animation to look around at entities when idle.',
|
||||
interrupts: [],
|
||||
on: true,
|
||||
active: false,
|
||||
|
||||
|
@ -96,35 +93,30 @@ const modes = [
|
|||
last_entity: null,
|
||||
next_change: 0,
|
||||
update: function (agent) {
|
||||
if (!agent.coder.executing) {
|
||||
this.active = true;
|
||||
const entity = agent.bot.nearestEntity();
|
||||
let entity_in_view = entity && entity.position.distanceTo(agent.bot.entity.position) < 10 && entity.name !== 'enderman';
|
||||
if (entity_in_view && entity !== this.last_entity) {
|
||||
this.staring = true;
|
||||
this.last_entity = entity;
|
||||
this.next_change = Date.now() + Math.random() * 1000 + 4000;
|
||||
}
|
||||
if (entity_in_view && this.staring) {
|
||||
let isbaby = entity.type !== 'player' && entity.metadata[16];
|
||||
let height = isbaby ? entity.height/2 : entity.height;
|
||||
agent.bot.lookAt(entity.position.offset(0, height, 0));
|
||||
}
|
||||
if (!entity_in_view)
|
||||
this.last_entity = null;
|
||||
if (Date.now() > this.next_change) {
|
||||
// look in random direction
|
||||
this.staring = Math.random() < 0.3;
|
||||
if (!this.staring) {
|
||||
const yaw = Math.random() * Math.PI * 2;
|
||||
const pitch = (Math.random() * Math.PI/2) - Math.PI/4;
|
||||
agent.bot.look(yaw, pitch, false);
|
||||
}
|
||||
this.next_change = Date.now() + Math.random() * 10000 + 2000;
|
||||
}
|
||||
const entity = agent.bot.nearestEntity();
|
||||
let entity_in_view = entity && entity.position.distanceTo(agent.bot.entity.position) < 10 && entity.name !== 'enderman';
|
||||
if (entity_in_view && entity !== this.last_entity) {
|
||||
this.staring = true;
|
||||
this.last_entity = entity;
|
||||
this.next_change = Date.now() + Math.random() * 1000 + 4000;
|
||||
}
|
||||
if (entity_in_view && this.staring) {
|
||||
let isbaby = entity.type !== 'player' && entity.metadata[16];
|
||||
let height = isbaby ? entity.height/2 : entity.height;
|
||||
agent.bot.lookAt(entity.position.offset(0, height, 0));
|
||||
}
|
||||
if (!entity_in_view)
|
||||
this.last_entity = null;
|
||||
if (Date.now() > this.next_change) {
|
||||
// look in random direction
|
||||
this.staring = Math.random() < 0.3;
|
||||
if (!this.staring) {
|
||||
const yaw = Math.random() * Math.PI * 2;
|
||||
const pitch = (Math.random() * Math.PI/2) - Math.PI/4;
|
||||
agent.bot.look(yaw, pitch, false);
|
||||
}
|
||||
this.next_change = Date.now() + Math.random() * 10000 + 2000;
|
||||
}
|
||||
else
|
||||
this.active = false;
|
||||
}
|
||||
},
|
||||
];
|
||||
|
@ -183,12 +175,12 @@ class ModeController {
|
|||
}
|
||||
}
|
||||
for (let mode of this.modes_list) {
|
||||
if (mode.on && !mode.paused) {
|
||||
let available = mode.interrupts.includes('all') || !this.agent.coder.executing;
|
||||
let interruptible = this.agent.coder.interruptible && (mode.interrupts.includes('defaults') || mode.interrupts.includes(this.agent.coder.default_name));
|
||||
if (mode.on && !mode.paused && !mode.active && (available || interruptible)) {
|
||||
mode.update(this.agent);
|
||||
if (mode.active) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mode.active) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue