refactor: actions
This commit is contained in:
@@ -2,136 +2,113 @@ import type { BotContext } from 'src/composables/bot'
|
||||
import { z } from 'zod'
|
||||
import * as skills from '../skills'
|
||||
|
||||
function runAsAction(actionFn, resume = false, timeout = -1) {
|
||||
let actionLabel = null // Will be set on first use
|
||||
|
||||
const wrappedAction = async function (agent, ...args) {
|
||||
// Set actionLabel only once, when the action is first created
|
||||
if (!actionLabel) {
|
||||
const actionObj = actionsList.find(a => a.perform === wrappedAction)
|
||||
actionLabel = actionObj.name.substring(1) // Remove the ! prefix
|
||||
}
|
||||
|
||||
const actionFnWithAgent = async () => {
|
||||
await actionFn(agent, ...args)
|
||||
}
|
||||
const code_return = await agent.actions.runAction(`action:${actionLabel}`, actionFnWithAgent, { timeout, resume })
|
||||
if (code_return.interrupted && !code_return.timedout)
|
||||
return
|
||||
return code_return.message
|
||||
}
|
||||
|
||||
return wrappedAction
|
||||
}
|
||||
|
||||
type ActionResult = string | Promise<string>
|
||||
|
||||
interface Action {
|
||||
readonly name: string
|
||||
readonly description: string
|
||||
readonly schema: z.ZodObject<any>
|
||||
readonly perform: (ctx: BotContext) => () => ActionResult
|
||||
readonly perform: (ctx: BotContext) => (...args: any[]) => ActionResult
|
||||
}
|
||||
|
||||
function getNewAction(): Action {
|
||||
return {
|
||||
name: '!newAction',
|
||||
description: 'Perform new and unknown custom behaviors that are not available as a command.',
|
||||
schema: z.object({
|
||||
prompt: z.string().describe('A natural language prompt to guide code generation. Make a detailed step-by-step plan.'),
|
||||
}),
|
||||
perform: (agent: BotContext) => async () => {
|
||||
if (!settings.allow_insecure_coding)
|
||||
return 'newAction not allowed! Code writing is disabled in settings. Notify the user.'
|
||||
return await agent.coder.generateCode(agent.history)
|
||||
},
|
||||
}
|
||||
}
|
||||
export const actionsList: Action[] = [
|
||||
// getNewAction(): Action {
|
||||
// return {
|
||||
// name: 'newAction',
|
||||
// description: 'Perform new and unknown custom behaviors that are not available as a command.',
|
||||
// schema: z.object({
|
||||
// prompt: z.string().describe('A natural language prompt to guide code generation. Make a detailed step-by-step plan.'),
|
||||
// }),
|
||||
// perform: (ctx: BotContext) => async (prompt: string) => {
|
||||
// if (!settings.allow_insecure_coding)
|
||||
// return 'newAction not allowed! Code writing is disabled in settings. Notify the user.'
|
||||
// return await ctx.coder.generateCode(ctx.history)
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
function getStopAction(): Action {
|
||||
return {
|
||||
name: '!stop',
|
||||
description: 'Force stop all actions and commands that are currently executing.',
|
||||
schema: z.object({}),
|
||||
perform: (agent: BotContext) => async () => {
|
||||
await agent.actions.stop()
|
||||
agent.clearBotLogs()
|
||||
agent.actions.cancelResume()
|
||||
agent.bot.emit('idle')
|
||||
let msg = 'Agent stopped.'
|
||||
if (agent.self_prompter.on)
|
||||
msg += ' Self-prompting still active.'
|
||||
return msg
|
||||
},
|
||||
}
|
||||
}
|
||||
// getStopAction(): Action {
|
||||
// return {
|
||||
// name: 'stop',
|
||||
// description: 'Force stop all actions and commands that are currently executing.',
|
||||
// schema: z.object({}),
|
||||
// perform: (ctx: BotContext) => async () => {
|
||||
// await ctx.actions.stop()
|
||||
// ctx.clearBotLogs()
|
||||
// ctx.actions.cancelResume()
|
||||
// ctx.bot.emit('idle')
|
||||
// let msg = 'Agent stopped.'
|
||||
// if (ctx.self_prompter.on)
|
||||
// msg += ' Self-prompting still active.'
|
||||
// return msg
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
function getStfuAction(): Action {
|
||||
return {
|
||||
name: '!stfu',
|
||||
description: 'Stop all chatting and self prompting, but continue current action.',
|
||||
schema: z.object({}),
|
||||
perform: (agent: BotContext) => async () => {
|
||||
agent.openChat('Shutting up.')
|
||||
agent.shutUp()
|
||||
},
|
||||
}
|
||||
}
|
||||
// getStfuAction(): Action {
|
||||
// return {
|
||||
// name: 'stfu',
|
||||
// description: 'Stop all chatting and self prompting, but continue current action.',
|
||||
// schema: z.object({}),
|
||||
// perform: (ctx: BotContext) => async () => {
|
||||
// ctx.openChat('Shutting up.')
|
||||
// ctx.shutUp()
|
||||
// return 'Shutting up.'
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
function getRestartAction(): Action {
|
||||
return {
|
||||
name: '!restart',
|
||||
description: 'Restart the agent process.',
|
||||
schema: z.object({}),
|
||||
perform: (agent: BotContext) => async () => {
|
||||
agent.cleanKill()
|
||||
},
|
||||
}
|
||||
}
|
||||
// getRestartAction(): Action {
|
||||
// return {
|
||||
// name: 'restart',
|
||||
// description: 'Restart the agent process.',
|
||||
// schema: z.object({}),
|
||||
// perform: (ctx: BotContext) => async () => {
|
||||
// ctx.cleanKill()
|
||||
// return 'Restarting agent...'
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
function getClearChatAction(): Action {
|
||||
return {
|
||||
name: '!clearChat',
|
||||
description: 'Clear the chat history.',
|
||||
schema: z.object({}),
|
||||
perform: (agent: BotContext) => async () => {
|
||||
agent.history.clear()
|
||||
return `${agent.name}'s chat history was cleared, starting new conversation from scratch.`
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function getGoToPlayerAction(): Action {
|
||||
return {
|
||||
name: '!goToPlayer',
|
||||
// getClearChatAction(): Action {
|
||||
// return {
|
||||
// name: 'clearChat',
|
||||
// description: 'Clear the chat history.',
|
||||
// schema: z.object({}),
|
||||
// perform: (ctx: BotContext) => async () => {
|
||||
// ctx.history.clear()
|
||||
// return `${ctx.name}'s chat history was cleared, starting new conversation from scratch.`
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
{
|
||||
name: 'goToPlayer',
|
||||
description: 'Go to the given player.',
|
||||
schema: z.object({
|
||||
player_name: z.string().describe('The name of the player to go to.'),
|
||||
closeness: z.number().describe('How close to get to the player.').min(0),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, player_name, closeness) => {
|
||||
return await skills.goToPlayer(agent.bot, player_name, closeness)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (player_name: string, closeness: number) => {
|
||||
await skills.goToPlayer(ctx.bot, player_name, closeness)
|
||||
return 'Moving to player...'
|
||||
},
|
||||
},
|
||||
|
||||
function getFollowPlayerAction(): Action {
|
||||
return {
|
||||
name: '!followPlayer',
|
||||
{
|
||||
name: 'followPlayer',
|
||||
description: 'Endlessly follow the given player.',
|
||||
schema: z.object({
|
||||
player_name: z.string().describe('name of the player to follow.'),
|
||||
follow_dist: z.number().describe('The distance to follow from.').min(0),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, player_name, follow_dist) => {
|
||||
await skills.followPlayer(agent.bot, player_name, follow_dist)
|
||||
}, true),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (player_name: string, follow_dist: number) => {
|
||||
await skills.followPlayer(ctx.bot, player_name, follow_dist)
|
||||
return 'Following player...'
|
||||
},
|
||||
},
|
||||
|
||||
function getGoToCoordinatesAction(): Action {
|
||||
return {
|
||||
name: '!goToCoordinates',
|
||||
{
|
||||
name: 'goToCoordinates',
|
||||
description: 'Go to the given x, y, z location.',
|
||||
schema: z.object({
|
||||
x: z.number().describe('The x coordinate.'),
|
||||
@@ -139,445 +116,350 @@ function getGoToCoordinatesAction(): Action {
|
||||
z: z.number().describe('The z coordinate.'),
|
||||
closeness: z.number().describe('How close to get to the location.').min(0),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, x, y, z, closeness) => {
|
||||
await skills.goToPosition(agent.bot, x, y, z, closeness)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (x: number, y: number, z: number, closeness: number) => {
|
||||
await skills.goToPosition(ctx.bot, x, y, z, closeness)
|
||||
return 'Moving to coordinates...'
|
||||
},
|
||||
},
|
||||
|
||||
function getSearchForBlockAction(): Action {
|
||||
return {
|
||||
name: '!searchForBlock',
|
||||
{
|
||||
name: 'searchForBlock',
|
||||
description: 'Find and go to the nearest block of a given type in a given range.',
|
||||
schema: z.object({
|
||||
type: z.string().describe('The block type to go to.'),
|
||||
search_range: z.number().describe('The range to search for the block.').min(32).max(512),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, block_type, range) => {
|
||||
await skills.goToNearestBlock(agent.bot, block_type, 4, range)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (block_type: string, range: number) => {
|
||||
await skills.goToNearestBlock(ctx.bot, block_type, 4, range)
|
||||
return 'Searching for block...'
|
||||
},
|
||||
},
|
||||
|
||||
function getSearchForEntityAction(): Action {
|
||||
return {
|
||||
name: '!searchForEntity',
|
||||
{
|
||||
name: 'searchForEntity',
|
||||
description: 'Find and go to the nearest entity of a given type in a given range.',
|
||||
schema: z.object({
|
||||
type: z.string().describe('The type of entity to go to.'),
|
||||
search_range: z.number().describe('The range to search for the entity.').min(32).max(512),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, entity_type, range) => {
|
||||
await skills.goToNearestEntity(agent.bot, entity_type, 4, range)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (entity_type: string, range: number) => {
|
||||
await skills.goToNearestEntity(ctx.bot, entity_type, 4, range)
|
||||
return 'Searching for entity...'
|
||||
},
|
||||
},
|
||||
|
||||
function getMoveAwayAction(): Action {
|
||||
return {
|
||||
name: '!moveAway',
|
||||
{
|
||||
name: 'moveAway',
|
||||
description: 'Move away from the current location in any direction by a given distance.',
|
||||
schema: z.object({
|
||||
distance: z.number().describe('The distance to move away.').min(0),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, distance) => {
|
||||
await skills.moveAway(agent.bot, distance)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
function getRememberHereAction(): Action {
|
||||
return {
|
||||
name: '!rememberHere',
|
||||
description: 'Save the current location with a given name.',
|
||||
schema: z.object({
|
||||
name: z.string().describe('The name to remember the location as.'),
|
||||
}),
|
||||
perform: (agent: BotContext) => async (name: string) => {
|
||||
const pos = agent.bot.entity.position
|
||||
agent.memory_bank.rememberPlace(name, pos.x, pos.y, pos.z)
|
||||
return `Location saved as "${name}".`
|
||||
perform: (ctx: BotContext) => async (distance: number) => {
|
||||
await skills.moveAway(ctx.bot, distance)
|
||||
return 'Moving away...'
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
function getGoToRememberedPlaceAction(): Action {
|
||||
return {
|
||||
name: '!goToRememberedPlace',
|
||||
description: 'Go to a saved location.',
|
||||
schema: z.object({
|
||||
name: z.string().describe('The name of the location to go to.'),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, name) => {
|
||||
const pos = agent.memory_bank.recallPlace(name)
|
||||
if (!pos) {
|
||||
skills.log(agent.bot, `No location named "${name}" saved.`)
|
||||
return
|
||||
}
|
||||
await skills.goToPosition(agent.bot, pos[0], pos[1], pos[2], 1)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
function getGivePlayerAction(): Action {
|
||||
return {
|
||||
name: '!givePlayer',
|
||||
{
|
||||
name: 'givePlayer',
|
||||
description: 'Give the specified item to the given player.',
|
||||
schema: z.object({
|
||||
player_name: z.string().describe('The name of the player to give the item to.'),
|
||||
item_name: z.string().describe('The name of the item to give.'),
|
||||
num: z.number().int().describe('The number of items to give.').min(1),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, player_name, item_name, num) => {
|
||||
await skills.giveToPlayer(agent.bot, item_name, player_name, num)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (player_name: string, item_name: string, num: number) => {
|
||||
await skills.giveToPlayer(ctx.bot, item_name, player_name, num)
|
||||
return 'Giving items to player...'
|
||||
},
|
||||
},
|
||||
|
||||
function getConsumeAction(): Action {
|
||||
return {
|
||||
name: '!consume',
|
||||
{
|
||||
name: 'consume',
|
||||
description: 'Eat/drink the given item.',
|
||||
schema: z.object({
|
||||
item_name: z.string().describe('The name of the item to consume.'),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, item_name) => {
|
||||
await skills.consume(agent.bot, item_name)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (item_name: string) => {
|
||||
await skills.consume(ctx.bot, item_name)
|
||||
return 'Consuming item...'
|
||||
},
|
||||
},
|
||||
|
||||
function getEquipAction(): Action {
|
||||
return {
|
||||
name: '!equip',
|
||||
{
|
||||
name: 'equip',
|
||||
description: 'Equip the given item.',
|
||||
schema: z.object({
|
||||
item_name: z.string().describe('The name of the item to equip.'),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, item_name) => {
|
||||
await skills.equip(agent.bot, item_name)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (item_name: string) => {
|
||||
await skills.equip(ctx.bot, item_name)
|
||||
return 'Equipping item...'
|
||||
},
|
||||
},
|
||||
|
||||
function getPutInChestAction(): Action {
|
||||
return {
|
||||
name: '!putInChest',
|
||||
{
|
||||
name: 'putInChest',
|
||||
description: 'Put the given item in the nearest chest.',
|
||||
schema: z.object({
|
||||
item_name: z.string().describe('The name of the item to put in the chest.'),
|
||||
num: z.number().int().describe('The number of items to put in the chest.').min(1),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, item_name, num) => {
|
||||
await skills.putInChest(agent.bot, item_name, num)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (item_name: string, num: number) => {
|
||||
await skills.putInChest(ctx.bot, item_name, num)
|
||||
return 'Putting items in chest...'
|
||||
},
|
||||
},
|
||||
|
||||
function getTakeFromChestAction(): Action {
|
||||
return {
|
||||
name: '!takeFromChest',
|
||||
{
|
||||
name: 'takeFromChest',
|
||||
description: 'Take the given items from the nearest chest.',
|
||||
schema: z.object({
|
||||
item_name: z.string().describe('The name of the item to take.'),
|
||||
num: z.number().int().describe('The number of items to take.').min(1),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, item_name, num) => {
|
||||
await skills.takeFromChest(agent.bot, item_name, num)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (item_name: string, num: number) => {
|
||||
await skills.takeFromChest(ctx.bot, item_name, num)
|
||||
return 'Taking items from chest...'
|
||||
},
|
||||
},
|
||||
|
||||
function getViewChestAction(): Action {
|
||||
return {
|
||||
name: '!viewChest',
|
||||
{
|
||||
name: 'viewChest',
|
||||
description: 'View the items/counts of the nearest chest.',
|
||||
schema: z.object({}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent) => {
|
||||
await skills.viewChest(agent.bot)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async () => {
|
||||
await skills.viewChest(ctx.bot)
|
||||
return 'Viewing chest contents...'
|
||||
},
|
||||
},
|
||||
|
||||
function getDiscardAction(): Action {
|
||||
return {
|
||||
name: '!discard',
|
||||
{
|
||||
name: 'discard',
|
||||
description: 'Discard the given item from the inventory.',
|
||||
schema: z.object({
|
||||
item_name: z.string().describe('The name of the item to discard.'),
|
||||
num: z.number().int().describe('The number of items to discard.').min(1),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, item_name, num) => {
|
||||
const start_loc = agent.bot.entity.position
|
||||
await skills.moveAway(agent.bot, 5)
|
||||
await skills.discard(agent.bot, item_name, num)
|
||||
await skills.goToPosition(agent.bot, start_loc.x, start_loc.y, start_loc.z, 0)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (item_name: string, num: number) => {
|
||||
const start_loc = ctx.bot.entity.position
|
||||
await skills.moveAway(ctx.bot, 5)
|
||||
await skills.discard(ctx.bot, item_name, num)
|
||||
await skills.goToPosition(ctx.bot, start_loc.x, start_loc.y, start_loc.z, 0)
|
||||
return 'Discarding items...'
|
||||
},
|
||||
},
|
||||
|
||||
function getCollectBlocksAction(): Action {
|
||||
return {
|
||||
name: '!collectBlocks',
|
||||
{
|
||||
name: 'collectBlocks',
|
||||
description: 'Collect the nearest blocks of a given type.',
|
||||
schema: z.object({
|
||||
type: z.string().describe('The block type to collect.'),
|
||||
num: z.number().int().describe('The number of blocks to collect.').min(1),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, type, num) => {
|
||||
await skills.collectBlock(agent.bot, type, num)
|
||||
}, false, 10), // 10 minute timeout
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (type: string, num: number) => {
|
||||
await skills.collectBlock(ctx.bot, type, num)
|
||||
return 'Collecting blocks...'
|
||||
},
|
||||
},
|
||||
|
||||
function getCraftRecipeAction(): Action {
|
||||
return {
|
||||
name: '!craftRecipe',
|
||||
{
|
||||
name: 'craftRecipe',
|
||||
description: 'Craft the given recipe a given number of times.',
|
||||
schema: z.object({
|
||||
recipe_name: z.string().describe('The name of the output item to craft.'),
|
||||
num: z.number().int().describe('The number of times to craft the recipe. This is NOT the number of output items, as it may craft many more items depending on the recipe.').min(1),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, recipe_name, num) => {
|
||||
await skills.craftRecipe(agent.bot, recipe_name, num)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (recipe_name: string, num: number) => {
|
||||
await skills.craftRecipe(ctx.bot, recipe_name, num)
|
||||
return 'Crafting items...'
|
||||
},
|
||||
},
|
||||
|
||||
function getSmeltItemAction(): Action {
|
||||
return {
|
||||
name: '!smeltItem',
|
||||
{
|
||||
name: 'smeltItem',
|
||||
description: 'Smelt the given item the given number of times.',
|
||||
schema: z.object({
|
||||
item_name: z.string().describe('The name of the input item to smelt.'),
|
||||
num: z.number().int().describe('The number of times to smelt the item.').min(1),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, item_name, num) => {
|
||||
const success = await skills.smeltItem(agent.bot, item_name, num)
|
||||
if (success) {
|
||||
setTimeout(() => {
|
||||
agent.cleanKill('Safely restarting to update inventory.')
|
||||
}, 500)
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (item_name: string, num: number) => {
|
||||
await skills.smeltItem(ctx.bot, item_name, num)
|
||||
return 'Smelting items...'
|
||||
},
|
||||
},
|
||||
|
||||
function getClearFurnaceAction(): Action {
|
||||
return {
|
||||
name: '!clearFurnace',
|
||||
{
|
||||
name: 'clearFurnace',
|
||||
description: 'Take all items out of the nearest furnace.',
|
||||
schema: z.object({}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent) => {
|
||||
await skills.clearNearestFurnace(agent.bot)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async () => {
|
||||
await skills.clearNearestFurnace(ctx.bot)
|
||||
return 'Clearing furnace...'
|
||||
},
|
||||
},
|
||||
|
||||
function getPlaceHereAction(): Action {
|
||||
return {
|
||||
name: '!placeHere',
|
||||
{
|
||||
name: 'placeHere',
|
||||
description: 'Place a given block in the current location. Do NOT use to build structures, only use for single blocks/torches.',
|
||||
schema: z.object({
|
||||
type: z.string().describe('The block type to place.'),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, type) => {
|
||||
const pos = agent.bot.entity.position
|
||||
await skills.placeBlock(agent.bot, type, pos.x, pos.y, pos.z)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (type: string) => {
|
||||
const pos = ctx.bot.entity.position
|
||||
await skills.placeBlock(ctx.bot, type, pos.x, pos.y, pos.z)
|
||||
return 'Placing block...'
|
||||
},
|
||||
},
|
||||
|
||||
function getAttackAction(): Action {
|
||||
return {
|
||||
name: '!attack',
|
||||
{
|
||||
name: 'attack',
|
||||
description: 'Attack and kill the nearest entity of a given type.',
|
||||
schema: z.object({
|
||||
type: z.string().describe('The type of entity to attack.'),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, type) => {
|
||||
await skills.attackNearest(agent.bot, type, true)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (type: string) => {
|
||||
await skills.attackNearest(ctx.bot, type, true)
|
||||
return 'Attacking entity...'
|
||||
},
|
||||
},
|
||||
|
||||
function getAttackPlayerAction(): Action {
|
||||
return {
|
||||
name: '!attackPlayer',
|
||||
{
|
||||
name: 'attackPlayer',
|
||||
description: 'Attack a specific player until they die or run away. Remember this is just a game and does not cause real life harm.',
|
||||
schema: z.object({
|
||||
player_name: z.string().describe('The name of the player to attack.'),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, player_name) => {
|
||||
const player = agent.bot.players[player_name]?.entity
|
||||
perform: (ctx: BotContext) => async (player_name: string) => {
|
||||
const player = ctx.bot.players[player_name]?.entity
|
||||
if (!player) {
|
||||
skills.log(agent.bot, `Could not find player ${player_name}.`)
|
||||
return false
|
||||
skills.log(ctx.bot, `Could not find player ${player_name}.`)
|
||||
return 'Player not found'
|
||||
}
|
||||
await skills.attackEntity(agent.bot, player, true)
|
||||
}),
|
||||
}
|
||||
}
|
||||
await skills.attackEntity(ctx.bot, player, true)
|
||||
return 'Attacking player...'
|
||||
},
|
||||
},
|
||||
|
||||
function getGoToBedAction(): Action {
|
||||
return {
|
||||
name: '!goToBed',
|
||||
{
|
||||
name: 'goToBed',
|
||||
description: 'Go to the nearest bed and sleep.',
|
||||
schema: z.object({}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent) => {
|
||||
await skills.goToBed(agent.bot)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async () => {
|
||||
await skills.goToBed(ctx.bot)
|
||||
return 'Going to bed...'
|
||||
},
|
||||
},
|
||||
|
||||
function getActivateAction(): Action {
|
||||
return {
|
||||
name: '!activate',
|
||||
{
|
||||
name: 'activate',
|
||||
description: 'Activate the nearest object of a given type.',
|
||||
schema: z.object({
|
||||
type: z.string().describe('The type of object to activate.'),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, type) => {
|
||||
await skills.activateNearestBlock(agent.bot, type)
|
||||
}),
|
||||
}
|
||||
}
|
||||
perform: (ctx: BotContext) => async (type: string) => {
|
||||
await skills.activateNearestBlock(ctx.bot, type)
|
||||
return 'Activating block...'
|
||||
},
|
||||
},
|
||||
|
||||
function getStayAction(): Action {
|
||||
return {
|
||||
name: '!stay',
|
||||
{
|
||||
name: 'stay',
|
||||
description: 'Stay in the current location no matter what. Pauses all modes.',
|
||||
schema: z.object({
|
||||
type: z.number().int().describe('The number of seconds to stay. -1 for forever.').min(-1),
|
||||
}),
|
||||
perform: (agent: BotContext) => runAsAction(async (agent, seconds) => {
|
||||
await skills.stay(agent.bot, seconds)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
function getSetModeAction(): Action {
|
||||
return {
|
||||
name: '!setMode',
|
||||
description: 'Set a mode to on or off. A mode is an automatic behavior that constantly checks and responds to the environment.',
|
||||
schema: z.object({
|
||||
mode_name: z.string().describe('The name of the mode to enable.'),
|
||||
on: z.boolean().describe('Whether to enable or disable the mode.'),
|
||||
}),
|
||||
perform: (agent: BotContext) => async (mode_name: string, on: boolean) => {
|
||||
const modes = agent.bot.modes
|
||||
if (!modes.exists(mode_name))
|
||||
return `Mode ${mode_name} does not exist.${modes.getDocs()}`
|
||||
if (modes.isOn(mode_name) === on)
|
||||
return `Mode ${mode_name} is already ${on ? 'on' : 'off'}.`
|
||||
modes.setOn(mode_name, on)
|
||||
return `Mode ${mode_name} is now ${on ? 'on' : 'off'}.`
|
||||
perform: (ctx: BotContext) => async (seconds: number) => {
|
||||
await skills.stay(ctx.bot, seconds)
|
||||
return 'Staying in place...'
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
// getSetModeAction(): Action {
|
||||
// return {
|
||||
// name: 'setMode',
|
||||
// description: 'Set a mode to on or off. A mode is an automatic behavior that constantly checks and responds to the environment.',
|
||||
// schema: z.object({
|
||||
// mode_name: z.string().describe('The name of the mode to enable.'),
|
||||
// on: z.boolean().describe('Whether to enable or disable the mode.'),
|
||||
// }),
|
||||
// perform: (ctx: BotContext) => async (mode_name: string, on: boolean) => {
|
||||
// const modes = ctx.bot.modes
|
||||
// if (!modes.exists(mode_name))
|
||||
// return `Mode ${mode_name} does not exist.${modes.getDocs()}`
|
||||
// if (modes.isOn(mode_name) === on)
|
||||
// return `Mode ${mode_name} is already ${on ? 'on' : 'off'}.`
|
||||
// modes.setOn(mode_name, on)
|
||||
// return `Mode ${mode_name} is now ${on ? 'on' : 'off'}.`
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
function getGoalAction(): Action {
|
||||
return {
|
||||
name: '!goal',
|
||||
description: 'Set a goal prompt to endlessly work towards with continuous self-prompting.',
|
||||
schema: z.object({
|
||||
selfPrompt: z.string().describe('The goal prompt.'),
|
||||
}),
|
||||
perform: (agent: BotContext) => async (prompt: string) => {
|
||||
if (convoManager.inConversation()) {
|
||||
agent.self_prompter.setPrompt(prompt)
|
||||
convoManager.scheduleSelfPrompter()
|
||||
}
|
||||
else {
|
||||
agent.self_prompter.start(prompt)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
// getGoalAction(): Action {
|
||||
// return {
|
||||
// name: 'goal',
|
||||
// description: 'Set a goal prompt to endlessly work towards with continuous self-prompting.',
|
||||
// schema: z.object({
|
||||
// selfPrompt: z.string().describe('The goal prompt.'),
|
||||
// }),
|
||||
// perform: (ctx: BotContext) => async (prompt: string) => {
|
||||
// if (convoManager.inConversation()) {
|
||||
// ctx.self_prompter.setPrompt(prompt)
|
||||
// convoManager.scheduleSelfPrompter()
|
||||
// }
|
||||
// else {
|
||||
// ctx.self_prompter.start(prompt)
|
||||
// }
|
||||
// return 'Goal set...'
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
function getEndGoalAction(): Action {
|
||||
return {
|
||||
name: '!endGoal',
|
||||
description: 'Call when you have accomplished your goal. It will stop self-prompting and the current action.',
|
||||
schema: z.object({}),
|
||||
perform: (agent: BotContext) => async () => {
|
||||
agent.self_prompter.stop()
|
||||
convoManager.cancelSelfPrompter()
|
||||
return 'Self-prompting stopped.'
|
||||
},
|
||||
}
|
||||
}
|
||||
// getEndGoalAction(): Action {
|
||||
// return {
|
||||
// name: 'endGoal',
|
||||
// description: 'Call when you have accomplished your goal. It will stop self-prompting and the current action.',
|
||||
// schema: z.object({}),
|
||||
// perform: (ctx: BotContext) => async () => {
|
||||
// ctx.self_prompter.stop()
|
||||
// convoManager.cancelSelfPrompter()
|
||||
// return 'Self-prompting stopped.'
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
function getStartConversationAction(): Action {
|
||||
return {
|
||||
name: '!startConversation',
|
||||
description: 'Start a conversation with a player. Use for bots only.',
|
||||
schema: z.object({
|
||||
player_name: z.string().describe('The name of the player to send the message to.'),
|
||||
message: z.string().describe('The message to send.'),
|
||||
}),
|
||||
perform: (agent: BotContext) => async (player_name: string, message: string) => {
|
||||
if (!convoManager.isOtherAgent(player_name))
|
||||
return `${player_name} is not a bot, cannot start conversation.`
|
||||
if (convoManager.inConversation() && !convoManager.inConversation(player_name))
|
||||
convoManager.forceEndCurrentConversation()
|
||||
else if (convoManager.inConversation(player_name))
|
||||
agent.history.add('system', `You are already in conversation with ${player_name}. Don't use this command to talk to them.`)
|
||||
convoManager.startConversation(player_name, message)
|
||||
},
|
||||
}
|
||||
}
|
||||
// getStartConversationAction(): Action {
|
||||
// return {
|
||||
// name: 'startConversation',
|
||||
// description: 'Start a conversation with a player. Use for bots only.',
|
||||
// schema: z.object({
|
||||
// player_name: z.string().describe('The name of the player to send the message to.'),
|
||||
// message: z.string().describe('The message to send.'),
|
||||
// }),
|
||||
// perform: (ctx: BotContext) => async (player_name: string, message: string) => {
|
||||
// if (!convoManager.isOtherAgent(player_name))
|
||||
// return `${player_name} is not a bot, cannot start conversation.`
|
||||
// if (convoManager.inConversation() && !convoManager.inConversation(player_name))
|
||||
// convoManager.forceEndCurrentConversation()
|
||||
// else if (convoManager.inConversation(player_name))
|
||||
// ctx.history.add('system', `You are already in conversation with ${player_name}. Don't use this command to talk to them.`)
|
||||
// convoManager.startConversation(player_name, message)
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
function getEndConversationAction(): Action {
|
||||
return {
|
||||
name: '!endConversation',
|
||||
description: 'End the conversation with the given player.',
|
||||
schema: z.object({
|
||||
player_name: z.string().describe('The name of the player to end the conversation with.'),
|
||||
}),
|
||||
perform: (agent: BotContext) => async (player_name: string) => {
|
||||
if (!convoManager.inConversation(player_name))
|
||||
return `Not in conversation with ${player_name}.`
|
||||
convoManager.endConversation(player_name)
|
||||
return `Converstaion with ${player_name} ended.`
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const actionsList = [
|
||||
getNewAction(),
|
||||
getStopAction(),
|
||||
getStfuAction(),
|
||||
getRestartAction(),
|
||||
getClearChatAction(),
|
||||
getGoToPlayerAction(),
|
||||
getFollowPlayerAction(),
|
||||
getGoToCoordinatesAction(),
|
||||
getSearchForBlockAction(),
|
||||
getSearchForEntityAction(),
|
||||
getMoveAwayAction(),
|
||||
getRememberHereAction(),
|
||||
getGoToRememberedPlaceAction(),
|
||||
getGivePlayerAction(),
|
||||
getConsumeAction(),
|
||||
getEquipAction(),
|
||||
getPutInChestAction(),
|
||||
getTakeFromChestAction(),
|
||||
getViewChestAction(),
|
||||
getDiscardAction(),
|
||||
getCollectBlocksAction(),
|
||||
getCraftRecipeAction(),
|
||||
getSmeltItemAction(),
|
||||
getClearFurnaceAction(),
|
||||
getPlaceHereAction(),
|
||||
getAttackAction(),
|
||||
getAttackPlayerAction(),
|
||||
getGoToBedAction(),
|
||||
getActivateAction(),
|
||||
getStayAction(),
|
||||
getSetModeAction(),
|
||||
getGoalAction(),
|
||||
getEndGoalAction(),
|
||||
getStartConversationAction(),
|
||||
getEndConversationAction(),
|
||||
// getEndConversationAction(): Action {
|
||||
// return {
|
||||
// name: 'endConversation',
|
||||
// description: 'End the conversation with the given player.',
|
||||
// schema: z.object({
|
||||
// player_name: z.string().describe('The name of the player to end the conversation with.'),
|
||||
// }),
|
||||
// perform: (ctx: BotContext) => async (player_name: string) => {
|
||||
// if (!convoManager.inConversation(player_name))
|
||||
// return `Not in conversation with ${player_name}.`
|
||||
// convoManager.endConversation(player_name)
|
||||
// return `Converstaion with ${player_name} ended.`
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
]
|
||||
|
||||
@@ -3,7 +3,8 @@ import type { BotContext } from '../composables/bot'
|
||||
import { useLogg } from '@guiiai/logg'
|
||||
import { agent, neuri } from 'neuri'
|
||||
import { openaiConfig } from '../composables/config'
|
||||
import { queryList } from './queries'
|
||||
import { actionsList } from './actions'
|
||||
import { queriesList } from './queries'
|
||||
|
||||
const agents = new Set<Agent | Promise<Agent>>()
|
||||
|
||||
@@ -14,6 +15,7 @@ export async function initAgent(ctx: BotContext): Promise<Neuri> {
|
||||
let n = neuri()
|
||||
|
||||
agents.add(initQueryAgent(ctx))
|
||||
agents.add(initActionAgent(ctx))
|
||||
|
||||
agents.forEach(agent => n = n.agent(agent))
|
||||
|
||||
@@ -29,7 +31,7 @@ export async function initQueryAgent(ctx: BotContext): Promise<Agent> {
|
||||
logger.log('Initializing query agent')
|
||||
let queryAgent = agent('query')
|
||||
|
||||
queryList.forEach((query) => {
|
||||
Object.values(queriesList).forEach((query) => {
|
||||
queryAgent = queryAgent.tool(
|
||||
query.name,
|
||||
query.schema,
|
||||
@@ -40,3 +42,19 @@ export async function initQueryAgent(ctx: BotContext): Promise<Agent> {
|
||||
|
||||
return queryAgent.build()
|
||||
}
|
||||
|
||||
export async function initActionAgent(ctx: BotContext): Promise<Agent> {
|
||||
logger.log('Initializing action agent')
|
||||
let actionAgent = agent('action')
|
||||
|
||||
Object.values(actionsList).forEach((action) => {
|
||||
actionAgent = actionAgent.tool(
|
||||
action.name,
|
||||
action.schema,
|
||||
action.perform(ctx),
|
||||
{ description: action.description },
|
||||
)
|
||||
})
|
||||
|
||||
return actionAgent.build()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { BotContext } from '../composables/bot'
|
||||
import { z } from 'zod'
|
||||
import { getStatusToString } from '../components/status'
|
||||
import * as world from '../composables/world'
|
||||
|
||||
// Core types
|
||||
type QueryResult = string | Promise<string>
|
||||
@@ -24,93 +23,71 @@ function formatWearingItem(slot: string, item: string | undefined): string {
|
||||
return item ? `\n${slot}: ${item}` : ''
|
||||
}
|
||||
|
||||
// Query implementations
|
||||
function createStatsQuery(): Query {
|
||||
return {
|
||||
export const queriesList: Query[] = [
|
||||
{
|
||||
name: 'stats',
|
||||
description: 'Get your bot\'s location, health, hunger, and time of day.',
|
||||
schema: z.object({}),
|
||||
perform: (ctx: BotContext) => (): string => getStatusToString(ctx),
|
||||
}
|
||||
}
|
||||
},
|
||||
// {
|
||||
// name: 'inventory',
|
||||
// description: 'Get your bot\'s inventory.',
|
||||
// schema: z.object({}),
|
||||
// perform: (ctx: BotContext) => (): string => {
|
||||
// const { bot } = ctx
|
||||
// const inventory = world.getInventoryCounts({ bot, botCtx: ctx })
|
||||
// const items = Object.entries(inventory)
|
||||
// .map(([item, count]) => formatInventoryItem(item, count))
|
||||
// .join('')
|
||||
|
||||
function createInventoryQuery(): Query {
|
||||
return {
|
||||
name: 'inventory',
|
||||
description: 'Get your bot\'s inventory.',
|
||||
schema: z.object({}),
|
||||
perform: (ctx: BotContext) => (): string => {
|
||||
const { bot } = ctx
|
||||
const inventory = world.getInventoryCounts({ bot, botCtx: ctx })
|
||||
const items = Object.entries(inventory)
|
||||
.map(([item, count]) => formatInventoryItem(item, count))
|
||||
.join('')
|
||||
// const wearing = [
|
||||
// formatWearingItem('Head', bot.inventory.slots[5]?.name),
|
||||
// formatWearingItem('Torso', bot.inventory.slots[6]?.name),
|
||||
// formatWearingItem('Legs', bot.inventory.slots[7]?.name),
|
||||
// formatWearingItem('Feet', bot.inventory.slots[8]?.name),
|
||||
// ].filter(Boolean).join('')
|
||||
|
||||
const wearing = [
|
||||
formatWearingItem('Head', bot.inventory.slots[5]?.name),
|
||||
formatWearingItem('Torso', bot.inventory.slots[6]?.name),
|
||||
formatWearingItem('Legs', bot.inventory.slots[7]?.name),
|
||||
formatWearingItem('Feet', bot.inventory.slots[8]?.name),
|
||||
].filter(Boolean).join('')
|
||||
// return pad(`INVENTORY${items || ': Nothing'}
|
||||
// ${bot.game.gameMode === 'creative' ? '\n(You have infinite items in creative mode. You do not need to gather resources!!)' : ''}
|
||||
// WEARING: ${wearing || 'Nothing'}`)
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'nearbyBlocks',
|
||||
// description: 'Get the blocks near the bot.',
|
||||
// schema: z.object({}),
|
||||
// perform: (ctx: BotContext) => (): string => {
|
||||
// const blocks = world.getNearbyBlockTypes({ bot: ctx.bot, botCtx: ctx })
|
||||
// return pad(`NEARBY_BLOCKS${blocks.map(b => `\n- ${b}`).join('') || ': none'}`)
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'craftable',
|
||||
// description: 'Get the craftable items with the bot\'s inventory.',
|
||||
// schema: z.object({}),
|
||||
// perform: (ctx: BotContext) => (): string => {
|
||||
// const craftable = world.getCraftableItems({ bot: ctx.bot, botCtx: ctx })
|
||||
// return pad(`CRAFTABLE_ITEMS${craftable.map(i => `\n- ${i}`).join('') || ': none'}`)
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'entities',
|
||||
// description: 'Get the nearby players and entities.',
|
||||
// schema: z.object({}),
|
||||
// perform: (ctx: BotContext) => (): string => {
|
||||
// const { bot } = ctx
|
||||
// const worldCtx = { bot, botCtx: ctx }
|
||||
// const players = world.getNearbyPlayerNames(worldCtx)
|
||||
// const entities = world.getNearbyEntityTypes(worldCtx)
|
||||
// .filter((e: string) => e !== 'player' && e !== 'item')
|
||||
|
||||
return pad(`INVENTORY${items || ': Nothing'}
|
||||
${bot.game.gameMode === 'creative' ? '\n(You have infinite items in creative mode. You do not need to gather resources!!)' : ''}
|
||||
WEARING: ${wearing || 'Nothing'}`)
|
||||
},
|
||||
}
|
||||
}
|
||||
// const result = [
|
||||
// ...players.map((p: string) => `- Human player: ${p}`),
|
||||
// ...entities.map((e: string) => `- entities: ${e}`),
|
||||
// ]
|
||||
|
||||
function createNearbyBlocksQuery(): Query {
|
||||
return {
|
||||
name: 'nearbyBlocks',
|
||||
description: 'Get the blocks near the bot.',
|
||||
schema: z.object({}),
|
||||
perform: (ctx: BotContext) => (): string => {
|
||||
const blocks = world.getNearbyBlockTypes({ bot: ctx.bot, botCtx: ctx })
|
||||
return pad(`NEARBY_BLOCKS${blocks.map(b => `\n- ${b}`).join('') || ': none'}`)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function createCraftableQuery(): Query {
|
||||
return {
|
||||
name: 'craftable',
|
||||
description: 'Get the craftable items with the bot\'s inventory.',
|
||||
schema: z.object({}),
|
||||
perform: (ctx: BotContext) => (): string => {
|
||||
const craftable = world.getCraftableItems({ bot: ctx.bot, botCtx: ctx })
|
||||
return pad(`CRAFTABLE_ITEMS${craftable.map(i => `\n- ${i}`).join('') || ': none'}`)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function createEntitiesQuery(): Query {
|
||||
return {
|
||||
name: 'entities',
|
||||
description: 'Get the nearby players and entities.',
|
||||
schema: z.object({}),
|
||||
perform: (ctx: BotContext) => (): string => {
|
||||
const { bot } = ctx
|
||||
const worldCtx = { bot, botCtx: ctx }
|
||||
const players = world.getNearbyPlayerNames(worldCtx)
|
||||
const entities = world.getNearbyEntityTypes(worldCtx)
|
||||
.filter((e: string) => e !== 'player' && e !== 'item')
|
||||
|
||||
const result = [
|
||||
...players.map((p: string) => `- Human player: ${p}`),
|
||||
...entities.map((e: string) => `- entities: ${e}`),
|
||||
]
|
||||
|
||||
return pad(`NEARBY_ENTITIES${result.length ? `\n${result.join('\n')}` : ': none'}`)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Export query list
|
||||
export const queryList: readonly Query[] = [
|
||||
createStatsQuery(),
|
||||
// createInventoryQuery(),
|
||||
// createNearbyBlocksQuery(),
|
||||
// createCraftableQuery(),
|
||||
// createEntitiesQuery(),
|
||||
// return pad(`NEARBY_ENTITIES${result.length ? `\n${result.join('\n')}` : ': none'}`)
|
||||
// },
|
||||
// },
|
||||
] as const
|
||||
|
||||
@@ -6,6 +6,87 @@ import * as mc from '../utils/mcdata'
|
||||
import { log } from './base'
|
||||
import { goToPosition } from './movement'
|
||||
|
||||
export async function collectBlock(
|
||||
bot: Bot,
|
||||
blockType: string,
|
||||
num: number = 1,
|
||||
exclude: typeof Vec3[] | null = null,
|
||||
): Promise<boolean> {
|
||||
if (num < 1) {
|
||||
log(bot, `Invalid number of blocks to collect: ${num}.`)
|
||||
return false
|
||||
}
|
||||
|
||||
const blocktypes: string[] = [blockType]
|
||||
if (blockType === 'coal' || blockType === 'diamond' || blockType === 'emerald'
|
||||
|| blockType === 'iron' || blockType === 'gold' || blockType === 'lapis_lazuli'
|
||||
|| blockType === 'redstone') {
|
||||
blocktypes.push(`${blockType}_ore`)
|
||||
}
|
||||
if (blockType.endsWith('ore')) {
|
||||
blocktypes.push(`deepslate_${blockType}`)
|
||||
}
|
||||
if (blockType === 'dirt') {
|
||||
blocktypes.push('grass_block')
|
||||
}
|
||||
|
||||
let collected = 0
|
||||
|
||||
for (let i = 0; i < num; i++) {
|
||||
let blocks = world.getNearestBlocks(bot, blocktypes, 64)
|
||||
if (exclude) {
|
||||
blocks = blocks.filter(
|
||||
block => !exclude.some(pos =>
|
||||
pos.x === block.position.x
|
||||
&& pos.y === block.position.y
|
||||
&& pos.z === block.position.z,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
const movements = new bot.pathfinder.Movements(bot)
|
||||
movements.dontMineUnderFallingBlock = false
|
||||
blocks = blocks.filter(block => movements.safeToBreak(block))
|
||||
|
||||
if (blocks.length === 0) {
|
||||
log(bot, collected === 0
|
||||
? `No ${blockType} nearby to collect.`
|
||||
: `No more ${blockType} nearby to collect.`)
|
||||
break
|
||||
}
|
||||
|
||||
const block = blocks[0]
|
||||
await bot.tool.equipForBlock(block)
|
||||
const itemId = bot.heldItem ? bot.heldItem.type : null
|
||||
|
||||
if (!block.canHarvest(itemId)) {
|
||||
log(bot, `Don't have right tools to harvest ${blockType}.`)
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
await bot.collectBlock.collect(block)
|
||||
collected++
|
||||
await autoLight(bot)
|
||||
}
|
||||
catch (err) {
|
||||
if (err.name === 'NoChests') {
|
||||
log(bot, `Failed to collect ${blockType}: Inventory full, no place to deposit.`)
|
||||
break
|
||||
}
|
||||
log(bot, `Failed to collect ${blockType}: ${err}.`)
|
||||
continue
|
||||
}
|
||||
|
||||
if (bot.interrupt_code) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
log(bot, `Collected ${collected} ${blockType}.`)
|
||||
return collected > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Place a torch if needed
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Bot } from 'mineflayer'
|
||||
import type { Entity } from 'prismarine-entity'
|
||||
import * as mc from '../../../utils/mcdata.js'
|
||||
import * as world from '../world.js'
|
||||
import { log } from './base.js'
|
||||
import * as world from '../composables/world'
|
||||
import * as mc from '../utils/mcdata'
|
||||
import { log } from './base'
|
||||
|
||||
/**
|
||||
* Equip the item with highest attack damage
|
||||
|
||||
@@ -1,50 +1,6 @@
|
||||
// Base utilities
|
||||
export { log } from './base'
|
||||
export type { BlockFace, Position } from './base'
|
||||
|
||||
// Block interaction functions
|
||||
export {
|
||||
activateNearestBlock,
|
||||
breakBlockAt,
|
||||
placeBlock,
|
||||
tillAndSow,
|
||||
useDoor,
|
||||
} from './blocks.js'
|
||||
|
||||
// Combat related functions
|
||||
export {
|
||||
attackEntity,
|
||||
attackNearest,
|
||||
defendSelf,
|
||||
} from './combat.js'
|
||||
|
||||
// Crafting and smelting functions
|
||||
export {
|
||||
clearNearestFurnace,
|
||||
craftRecipe,
|
||||
smeltItem,
|
||||
} from './crafting.js'
|
||||
|
||||
// Inventory management functions
|
||||
export {
|
||||
consume,
|
||||
discard,
|
||||
equip,
|
||||
giveToPlayer,
|
||||
pickupNearbyItems,
|
||||
putInChest,
|
||||
takeFromChest,
|
||||
viewChest,
|
||||
} from './inventory.js'
|
||||
|
||||
// Movement related functions
|
||||
export {
|
||||
followPlayer,
|
||||
goToNearestBlock,
|
||||
goToNearestEntity,
|
||||
goToPlayer,
|
||||
goToPosition,
|
||||
moveAway,
|
||||
moveAwayFromEntity,
|
||||
stay,
|
||||
} from './movement.js'
|
||||
export * from './base'
|
||||
export * from './blocks.js'
|
||||
export * from './combat.js'
|
||||
export * from './crafting.js'
|
||||
export * from './inventory.js'
|
||||
export * from './movement.js'
|
||||
|
||||
@@ -206,3 +206,34 @@ export async function stay(bot: Bot, seconds = 30): Promise<boolean> {
|
||||
log(bot, `Stayed for ${(Date.now() - start) / 1000} seconds.`)
|
||||
return true
|
||||
}
|
||||
/**
|
||||
* Sleep in the nearest bed within 32 blocks
|
||||
*/
|
||||
export async function goToBed(bot: Bot): Promise<boolean> {
|
||||
const beds: Vec3[] = bot.findBlocks({
|
||||
matching: (block: Block) => block.name.includes('bed'),
|
||||
maxDistance: 32,
|
||||
count: 1,
|
||||
})
|
||||
|
||||
if (beds.length === 0) {
|
||||
log(bot, 'Could not find a bed to sleep in.')
|
||||
return false
|
||||
}
|
||||
|
||||
const loc: Vec3 = beds[0]
|
||||
await goToPosition(bot, loc.x, loc.y, loc.z)
|
||||
|
||||
const bed: Block | null = bot.blockAt(loc)
|
||||
await bot.sleep(bed)
|
||||
log(bot, 'You are in bed.')
|
||||
|
||||
bot.modes.pause('unstuck')
|
||||
|
||||
while (bot.isSleeping) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
}
|
||||
|
||||
log(bot, 'You have woken up.')
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user