chore(minecraft): various legacy code cleanup
This commit is contained in:
@@ -89,21 +89,6 @@ export const actionsList: Action[] = [
|
||||
return pad(`NEARBY_ENTITIES${result.length ? `\n${result.join('\n')}` : ': none'}`)
|
||||
},
|
||||
},
|
||||
// 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: (mineflayer: 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(mineflayer.history)
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
// todo: must 'stop now' can be used to stop the agent
|
||||
{
|
||||
name: 'stop',
|
||||
@@ -113,53 +98,13 @@ export const actionsList: Action[] = [
|
||||
// await ctx.actions.stop()
|
||||
// ctx.clearBotLogs()
|
||||
// ctx.actions.cancelResume()
|
||||
// ctx.bot.emit('idle')
|
||||
|
||||
mineflayer.emit('interrupt')
|
||||
|
||||
const msg = 'Agent stopped.'
|
||||
// if (mineflayer.self_prompter.on)
|
||||
// msg += ' Self-prompting still active.'
|
||||
return msg
|
||||
},
|
||||
},
|
||||
|
||||
// getStfuAction(): Action {
|
||||
// return {
|
||||
// name: 'stfu',
|
||||
// description: 'Stop all chatting and self prompting, but continue current action.',
|
||||
// schema: z.object({}),
|
||||
// perform: (mineflayer: BotContext) => async () => {
|
||||
// ctx.openChat('Shutting up.')
|
||||
// ctx.shutUp()
|
||||
// return 'Shutting up.'
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
// getRestartAction(): Action {
|
||||
// return {
|
||||
// name: 'restart',
|
||||
// description: 'Restart the agent process.',
|
||||
// schema: z.object({}),
|
||||
// perform: (mineflayer: BotContext) => async () => {
|
||||
// ctx.cleanKill()
|
||||
// return 'Restarting agent...'
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
// getClearChatAction(): Action {
|
||||
// return {
|
||||
// name: 'clearChat',
|
||||
// description: 'Clear the chat history.',
|
||||
// schema: z.object({}),
|
||||
// perform: (mineflayer: 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.',
|
||||
@@ -172,7 +117,6 @@ export const actionsList: Action[] = [
|
||||
return 'Moving to player...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'followPlayer',
|
||||
description: 'Endlessly follow the given player.',
|
||||
@@ -185,7 +129,6 @@ export const actionsList: Action[] = [
|
||||
return 'Following player...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'goToCoordinates',
|
||||
description: 'Go to the given x, y, z location.',
|
||||
@@ -200,7 +143,6 @@ export const actionsList: Action[] = [
|
||||
return 'Moving to coordinates...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'searchForBlock',
|
||||
description: 'Find and go to the nearest block of a given type in a given range.',
|
||||
@@ -213,7 +155,6 @@ export const actionsList: Action[] = [
|
||||
return 'Searching for block...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'searchForEntity',
|
||||
description: 'Find and go to the nearest entity of a given type in a given range.',
|
||||
@@ -226,7 +167,6 @@ export const actionsList: Action[] = [
|
||||
return 'Searching for entity...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'moveAway',
|
||||
description: 'Move away from the current location in any direction by a given distance.',
|
||||
@@ -238,7 +178,6 @@ export const actionsList: Action[] = [
|
||||
return 'Moving away...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'givePlayer',
|
||||
description: 'Give the specified item to the given player.',
|
||||
@@ -252,7 +191,6 @@ export const actionsList: Action[] = [
|
||||
return 'Giving items to player...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'consume',
|
||||
description: 'Eat/drink the given item.',
|
||||
@@ -264,7 +202,6 @@ export const actionsList: Action[] = [
|
||||
return 'Consuming item...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'equip',
|
||||
description: 'Equip the given item.',
|
||||
@@ -276,7 +213,6 @@ export const actionsList: Action[] = [
|
||||
return 'Equipping item...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'putInChest',
|
||||
description: 'Put the given item in the nearest chest.',
|
||||
@@ -289,7 +225,6 @@ export const actionsList: Action[] = [
|
||||
return 'Putting items in chest...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'takeFromChest',
|
||||
description: 'Take the given items from the nearest chest.',
|
||||
@@ -302,7 +237,6 @@ export const actionsList: Action[] = [
|
||||
return 'Taking items from chest...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'viewChest',
|
||||
description: 'View the items/counts of the nearest chest.',
|
||||
@@ -312,7 +246,6 @@ export const actionsList: Action[] = [
|
||||
return 'Viewing chest contents...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'discard',
|
||||
description: 'Discard the given item from the inventory.',
|
||||
@@ -325,7 +258,6 @@ export const actionsList: Action[] = [
|
||||
return 'Discarding items...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'collectBlocks',
|
||||
description: 'Collect the nearest blocks of a given type.',
|
||||
@@ -338,7 +270,6 @@ export const actionsList: Action[] = [
|
||||
return 'Collecting blocks...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'craftRecipe',
|
||||
description: 'Craft the given recipe a given number of times.',
|
||||
@@ -351,7 +282,6 @@ export const actionsList: Action[] = [
|
||||
return 'Crafting items...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'smeltItem',
|
||||
description: 'Smelt the given item the given number of times.',
|
||||
@@ -364,7 +294,6 @@ export const actionsList: Action[] = [
|
||||
return 'Smelting items...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'clearFurnace',
|
||||
description: 'Take all items out of the nearest furnace.',
|
||||
@@ -374,7 +303,6 @@ export const actionsList: Action[] = [
|
||||
return 'Clearing furnace...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'placeHere',
|
||||
description: 'Place a given block in the current location. Do NOT use to build structures, only use for single blocks/torches.',
|
||||
@@ -387,7 +315,6 @@ export const actionsList: Action[] = [
|
||||
return 'Placing block...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'attack',
|
||||
description: 'Attack and kill the nearest entity of a given type.',
|
||||
@@ -399,7 +326,6 @@ export const actionsList: Action[] = [
|
||||
return 'Attacking entity...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
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.',
|
||||
@@ -416,7 +342,6 @@ export const actionsList: Action[] = [
|
||||
return 'Attacking player...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'goToBed',
|
||||
description: 'Go to the nearest bed and sleep.',
|
||||
@@ -426,7 +351,6 @@ export const actionsList: Action[] = [
|
||||
return 'Going to bed...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'activate',
|
||||
description: 'Activate the nearest object of a given type.',
|
||||
@@ -438,7 +362,6 @@ export const actionsList: Action[] = [
|
||||
return 'Activating block...'
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'stay',
|
||||
description: 'Stay in the current location no matter what. Pauses all modes.',
|
||||
@@ -450,92 +373,4 @@ export const actionsList: Action[] = [
|
||||
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: (mineflayer: 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'}.`
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
// 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: (mineflayer: BotContext) => async (prompt: string) => {
|
||||
// if (convoManager.inConversation()) {
|
||||
// ctx.self_prompter.setPrompt(prompt)
|
||||
// convoManager.scheduleSelfPrompter()
|
||||
// }
|
||||
// else {
|
||||
// ctx.self_prompter.start(prompt)
|
||||
// }
|
||||
// return 'Goal set...'
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
// 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: (mineflayer: BotContext) => async () => {
|
||||
// ctx.self_prompter.stop()
|
||||
// convoManager.cancelSelfPrompter()
|
||||
// return 'Self-prompting stopped.'
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
// 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: (mineflayer: 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)
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
// 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: (mineflayer: 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.`
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
]
|
||||
|
||||
@@ -30,7 +30,8 @@ export class TaskManager {
|
||||
if (!this.primaryTask) {
|
||||
this.primaryTask = task
|
||||
this.logger.withFields({ taskId: task.id, goal, type: 'primary' }).log('TaskManager: Created new primary task')
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.secondaryTasks.set(task.id, task)
|
||||
this.logger.withFields({ taskId: task.id, goal, type: 'secondary' }).log('TaskManager: Created new secondary task')
|
||||
}
|
||||
@@ -94,7 +95,7 @@ export class TaskManager {
|
||||
|
||||
// We don't remove it yet, we wait for completeTask to be called
|
||||
this.addToHistory(task)
|
||||
|
||||
|
||||
// Cleanup reference immediately to allow new primary tasks if this was primary?
|
||||
// No, we should wait for the orchestrator to call completeTask/cleanup.
|
||||
}
|
||||
@@ -113,14 +114,16 @@ export class TaskManager {
|
||||
*/
|
||||
public completeTask(taskId: string): void {
|
||||
const task = this.getTaskById(taskId)
|
||||
if (!task) return
|
||||
if (!task)
|
||||
return
|
||||
|
||||
this.logger.withFields({ taskId: task.id }).log('TaskManager: Task completed')
|
||||
this.addToHistory(task)
|
||||
|
||||
if (this.primaryTask?.id === taskId) {
|
||||
this.primaryTask = null
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.secondaryTasks.delete(taskId)
|
||||
}
|
||||
}
|
||||
@@ -143,7 +146,8 @@ export class TaskManager {
|
||||
* Get a task by ID
|
||||
*/
|
||||
public getTaskById(taskId: string): TaskContext | null {
|
||||
if (this.primaryTask?.id === taskId) return this.primaryTask
|
||||
if (this.primaryTask?.id === taskId)
|
||||
return this.primaryTask
|
||||
return this.secondaryTasks.get(taskId) || null
|
||||
}
|
||||
|
||||
@@ -160,9 +164,12 @@ export class TaskManager {
|
||||
const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000)
|
||||
lines.push(`[PRIMARY TASK] (${status.toUpperCase()}): "${goal}"`)
|
||||
lines.push(`- Duration: ${elapsedSeconds}s`)
|
||||
if (currentStep) lines.push(`- Step: ${currentStep}`)
|
||||
if (plan) lines.push(`- Plan: ${plan.steps.length} steps (${plan.status})`)
|
||||
} else {
|
||||
if (currentStep)
|
||||
lines.push(`- Step: ${currentStep}`)
|
||||
if (plan)
|
||||
lines.push(`- Plan: ${plan.steps.length} steps (${plan.status})`)
|
||||
}
|
||||
else {
|
||||
lines.push('No primary task active.')
|
||||
}
|
||||
|
||||
@@ -182,7 +189,8 @@ export class TaskManager {
|
||||
*/
|
||||
public getAllActiveTasks(): TaskContext[] {
|
||||
const tasks: TaskContext[] = []
|
||||
if (this.primaryTask) tasks.push(this.primaryTask)
|
||||
if (this.primaryTask)
|
||||
tasks.push(this.primaryTask)
|
||||
tasks.push(...this.secondaryTasks.values())
|
||||
return tasks
|
||||
}
|
||||
@@ -200,8 +208,9 @@ export class TaskManager {
|
||||
|
||||
private addToHistory(task: TaskContext): void {
|
||||
// Only add if not already in history (simple check)
|
||||
if (this.taskHistory.some(t => t.id === task.id)) return
|
||||
|
||||
if (this.taskHistory.some(t => t.id === task.id))
|
||||
return
|
||||
|
||||
this.taskHistory.push(task)
|
||||
if (this.taskHistory.length > this.maxHistorySize) {
|
||||
this.taskHistory.shift()
|
||||
|
||||
@@ -56,26 +56,6 @@ export class Mineflayer extends EventEmitter<EventHandlers> {
|
||||
public static async asyncBuild(options: MineflayerOptions) {
|
||||
const mineflayer = new Mineflayer(options)
|
||||
|
||||
mineflayer.bot.on('messagestr', async (message, _, jsonMsg) => {
|
||||
// jsonMsg.translate:
|
||||
// - death.attack.player
|
||||
// message:
|
||||
// - <bot username> was slain by <player / entity>
|
||||
// - <bot username> drowned
|
||||
if (jsonMsg.translate && jsonMsg.translate.startsWith('death') && message.startsWith(mineflayer.username)) {
|
||||
const deathPos = mineflayer.bot.entity.position
|
||||
|
||||
// mineflayer.memory_bank.rememberPlace('last_death_position', deathPos.x, deathPos.y, deathPos.z)
|
||||
let deathPosStr: string | undefined
|
||||
if (deathPos) {
|
||||
deathPosStr = `x: ${deathPos.x.toFixed(2)}, y: ${deathPos.y.toFixed(2)}, z: ${deathPos.x.toFixed(2)}`
|
||||
}
|
||||
|
||||
const dimension = mineflayer.bot.game.dimension
|
||||
await mineflayer.handleMessage('system', `You died at position ${deathPosStr || 'unknown'} in the ${dimension} dimension with the final message: '${message}'. Your place of death has been saved as 'last_death_position' if you want to return. Previous actions were stopped and you have re-spawned.`)
|
||||
}
|
||||
})
|
||||
|
||||
mineflayer.bot.once('resourcePack', () => {
|
||||
mineflayer.bot.acceptResourcePack()
|
||||
})
|
||||
@@ -223,126 +203,4 @@ export class Mineflayer extends EventEmitter<EventHandlers> {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private async handleMessage(_source: string, _message: string, _maxResponses: number = Infinity) {
|
||||
// if (!source || !message) {
|
||||
// console.warn('Received empty message from', source);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// let used_command = false;
|
||||
// if (maxResponses === null) {
|
||||
// maxResponses = settings.max_commands === -1 ? Infinity : settings.max_commands;
|
||||
// }
|
||||
// if (maxResponses === -1) {
|
||||
// maxResponses = Infinity;
|
||||
// }
|
||||
|
||||
// const self_prompt = source === 'system' || source === ctx.botName;
|
||||
// const from_other_bot = convoManager.isOtherAgent(source);
|
||||
|
||||
// if (!self_prompt && !from_other_bot) { // from user, check for forced commands
|
||||
// const user_command_name = containsCommand(message);
|
||||
// if (user_command_name) {
|
||||
// if (!commandExists(user_command_name)) {
|
||||
// this.routeResponse(source, `Command '${user_command_name}' does not exist.`);
|
||||
// return false;
|
||||
// }
|
||||
// this.routeResponse(source, `*${source} used ${user_command_name.substring(1)}*`);
|
||||
// if (user_command_name === '!newAction') {
|
||||
// // all user-initiated commands are ignored by the bot except for this one
|
||||
// // add the preceding message to the history to give context for newAction
|
||||
// this.history.add(source, message);
|
||||
// }
|
||||
// let execute_res = await executeCommand(this, message);
|
||||
// if (execute_res)
|
||||
// this.routeResponse(source, execute_res);
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (from_other_bot)
|
||||
// this.last_sender = source;
|
||||
|
||||
// // Now translate the message
|
||||
// message = await handleEnglishTranslation(message);
|
||||
// console.log('received message from', source, ':', message);
|
||||
|
||||
// const checkInterrupt = () => this.self_prompter.shouldInterrupt(self_prompt) || this.shut_up || convoManager.responseScheduledFor(source);
|
||||
|
||||
// let behavior_log = this.bot.modes.flushBehaviorLog();
|
||||
// if (behavior_log.trim().length > 0) {
|
||||
// const MAX_LOG = 500;
|
||||
// if (behavior_log.length > MAX_LOG) {
|
||||
// behavior_log = '...' + behavior_log.substring(behavior_log.length - MAX_LOG);
|
||||
// }
|
||||
// behavior_log = 'Recent behaviors log: \n' + behavior_log.substring(behavior_log.indexOf('\n'));
|
||||
// await this.history.add('system', behavior_log);
|
||||
// }
|
||||
|
||||
// // Handle other user messages
|
||||
// await this.history.add(source, message);
|
||||
// this.history.save();
|
||||
|
||||
// if (!self_prompt && this.self_prompter.on) // message is from user during self-prompting
|
||||
// maxResponses = 1; // force only respond to this message, then let self-prompting take over
|
||||
// for (let i=0; i<maxResponses; i++) {
|
||||
// if (checkInterrupt()) break;
|
||||
// let history = this.history.getHistory();
|
||||
// let res = await this.prompter.promptConvo(history);
|
||||
|
||||
// console.log(`${this.name} full response to ${source}: ""${res}""`);
|
||||
|
||||
// if (res.trim().length === 0) {
|
||||
// console.warn('no response')
|
||||
// break; // empty response ends loop
|
||||
// }
|
||||
|
||||
// let command_name = containsCommand(res);
|
||||
|
||||
// if (command_name) { // contains query or command
|
||||
// res = truncCommandMessage(res); // everything after the command is ignored
|
||||
// this.history.add(this.name, res);
|
||||
|
||||
// if (!commandExists(command_name)) {
|
||||
// this.history.add('system', `Command ${command_name} does not exist.`);
|
||||
// console.warn('Agent hallucinated command:', command_name)
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// if (checkInterrupt()) break;
|
||||
// this.self_prompter.handleUserPromptedCmd(self_prompt, isAction(command_name));
|
||||
|
||||
// if (settings.verbose_commands) {
|
||||
// this.routeResponse(source, res);
|
||||
// }
|
||||
// else { // only output command name
|
||||
// let pre_message = res.substring(0, res.indexOf(command_name)).trim();
|
||||
// let chat_message = `*used ${command_name.substring(1)}*`;
|
||||
// if (pre_message.length > 0)
|
||||
// chat_message = `${pre_message} ${chat_message}`;
|
||||
// this.routeResponse(source, chat_message);
|
||||
// }
|
||||
|
||||
// let execute_res = await executeCommand(this, res);
|
||||
|
||||
// console.log('Agent executed:', command_name, 'and got:', execute_res);
|
||||
// used_command = true;
|
||||
|
||||
// if (execute_res)
|
||||
// this.history.add('system', execute_res);
|
||||
// else
|
||||
// break;
|
||||
// }
|
||||
// else { // conversation response
|
||||
// this.history.add(this.name, res);
|
||||
// this.routeResponse(source, res);
|
||||
// break;
|
||||
// }
|
||||
|
||||
// this.history.save();
|
||||
// }
|
||||
|
||||
// return used_command;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +179,8 @@ export async function ensureSticks(mineflayer: Mineflayer, neededAmount: number)
|
||||
sticksCount = getItemCount(mineflayer, 'stick')
|
||||
}
|
||||
|
||||
if (sticksCount >= neededAmount) return true
|
||||
if (sticksCount >= neededAmount)
|
||||
return true
|
||||
|
||||
throw new ActionError('RESOURCE_MISSING', 'Failed to ensure sticks', { needed: neededAmount, current: sticksCount })
|
||||
}
|
||||
@@ -188,7 +189,7 @@ export async function ensureSticks(mineflayer: Mineflayer, neededAmount: number)
|
||||
export async function ensureChests(mineflayer: Mineflayer, quantity: number = 1): Promise<boolean> {
|
||||
logger.log(`Bot: Checking for ${quantity} chest(s)...`)
|
||||
|
||||
let chestCount = getItemCount(mineflayer, 'chest')
|
||||
const chestCount = getItemCount(mineflayer, 'chest')
|
||||
|
||||
if (chestCount >= quantity) {
|
||||
return true
|
||||
@@ -196,11 +197,11 @@ export async function ensureChests(mineflayer: Mineflayer, quantity: number = 1)
|
||||
|
||||
await ensurePlanks(mineflayer, 8 * (quantity - chestCount))
|
||||
const crafted = await craftRecipe(mineflayer, 'chest', quantity - chestCount)
|
||||
|
||||
|
||||
if (!crafted) {
|
||||
throw new ActionError('CRAFTING_FAILED', 'Failed to craft chests')
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -208,7 +209,7 @@ export async function ensureChests(mineflayer: Mineflayer, quantity: number = 1)
|
||||
export async function ensureFurnaces(mineflayer: Mineflayer, quantity: number = 1): Promise<boolean> {
|
||||
logger.log(`Bot: Checking for ${quantity} furnace(s)...`)
|
||||
|
||||
let furnaceCount = getItemCount(mineflayer, 'furnace')
|
||||
const furnaceCount = getItemCount(mineflayer, 'furnace')
|
||||
|
||||
if (furnaceCount >= quantity) {
|
||||
return true
|
||||
@@ -217,15 +218,16 @@ export async function ensureFurnaces(mineflayer: Mineflayer, quantity: number =
|
||||
const stoneNeeded = 8 * (quantity - furnaceCount)
|
||||
try {
|
||||
await ensureCobblestone(mineflayer, stoneNeeded)
|
||||
} catch (e) {
|
||||
throw new ActionError('RESOURCE_MISSING', 'Failed to gather cobblestone for furnace', { error: e })
|
||||
}
|
||||
catch (e) {
|
||||
throw new ActionError('RESOURCE_MISSING', 'Failed to gather cobblestone for furnace', { error: e })
|
||||
}
|
||||
|
||||
const crafted = await craftRecipe(mineflayer, 'furnace', quantity - furnaceCount)
|
||||
if (!crafted) {
|
||||
throw new ActionError('CRAFTING_FAILED', 'Failed to craft furnace')
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -233,7 +235,7 @@ export async function ensureFurnaces(mineflayer: Mineflayer, quantity: number =
|
||||
export async function ensureTorches(mineflayer: Mineflayer, quantity: number = 1): Promise<boolean> {
|
||||
logger.log(`Bot: Checking for ${quantity} torch(es)...`)
|
||||
|
||||
let torchCount = getItemCount(mineflayer, 'torch')
|
||||
const torchCount = getItemCount(mineflayer, 'torch')
|
||||
|
||||
if (torchCount >= quantity) {
|
||||
return true
|
||||
@@ -241,11 +243,12 @@ export async function ensureTorches(mineflayer: Mineflayer, quantity: number = 1
|
||||
|
||||
const needed = quantity - torchCount
|
||||
await ensureSticks(mineflayer, Math.ceil(needed / 4))
|
||||
|
||||
|
||||
try {
|
||||
await ensureCoal(mineflayer, Math.ceil(needed / 4))
|
||||
} catch (e) {
|
||||
throw new ActionError('RESOURCE_MISSING', 'Failed to gather coal for torches', { error: e })
|
||||
}
|
||||
catch (e) {
|
||||
throw new ActionError('RESOURCE_MISSING', 'Failed to gather coal for torches', { error: e })
|
||||
}
|
||||
|
||||
const crafted = await craftRecipe(mineflayer, 'torch', Math.ceil(needed / 4))
|
||||
@@ -261,19 +264,21 @@ export async function ensureCampfire(mineflayer: Mineflayer): Promise<boolean> {
|
||||
logger.log('Bot: Checking for a campfire...')
|
||||
|
||||
const hasCampfire = getItemCount(mineflayer, 'campfire') > 0
|
||||
if (hasCampfire) return true
|
||||
if (hasCampfire)
|
||||
return true
|
||||
|
||||
await ensurePlanks(mineflayer, 3)
|
||||
await ensureSticks(mineflayer, 3)
|
||||
try {
|
||||
await ensureCoal(mineflayer, 1)
|
||||
} catch (e) {
|
||||
throw new ActionError('RESOURCE_MISSING', 'Failed to gather coal/charcoal for campfire', { error: e })
|
||||
}
|
||||
catch (e) {
|
||||
throw new ActionError('RESOURCE_MISSING', 'Failed to gather coal/charcoal for campfire', { error: e })
|
||||
}
|
||||
|
||||
const crafted = await craftRecipe(mineflayer, 'campfire', 1)
|
||||
if (!crafted) {
|
||||
throw new ActionError('CRAFTING_FAILED', 'Failed to craft campfire')
|
||||
throw new ActionError('CRAFTING_FAILED', 'Failed to craft campfire')
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -315,7 +320,8 @@ export async function ensureCobblestone(mineflayer: Mineflayer, requiredCobblest
|
||||
cobblestoneCount = getItemCount(mineflayer, 'cobblestone')
|
||||
}
|
||||
|
||||
if (cobblestoneCount >= requiredCobblestone) return true
|
||||
if (cobblestoneCount >= requiredCobblestone)
|
||||
return true
|
||||
|
||||
throw new ActionError('RESOURCE_MISSING', 'Could not gather enough cobblestone', { required: requiredCobblestone, current: cobblestoneCount })
|
||||
}
|
||||
@@ -347,7 +353,8 @@ export async function ensureCoal(mineflayer: Mineflayer, neededAmount: number, m
|
||||
coalCount = getItemCount(mineflayer, 'coal')
|
||||
}
|
||||
|
||||
if (coalCount >= neededAmount) return true
|
||||
if (coalCount >= neededAmount)
|
||||
return true
|
||||
throw new ActionError('RESOURCE_MISSING', 'Could not gather enough coal', { required: neededAmount, current: coalCount })
|
||||
}
|
||||
|
||||
@@ -404,31 +411,37 @@ async function ensureTool(mineflayer: Mineflayer, toolType: ToolType, quantity:
|
||||
try {
|
||||
await ensureCraftingTable(mineflayer)
|
||||
await ensureSticks(mineflayer, 2)
|
||||
|
||||
|
||||
const crafted = await craftRecipe(mineflayer, toolRecipe, 1)
|
||||
if (crafted) {
|
||||
toolCount++
|
||||
mineflayer.bot.chat(`I have crafted a ${material} ${toolType}.`)
|
||||
if (toolCount >= quantity) return true
|
||||
if (toolCount >= quantity)
|
||||
return true
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof ActionError && err.code === 'RESOURCE_MISSING') {
|
||||
// Just fall through to next material if resources missing
|
||||
} else {
|
||||
logger.error(`Failed to craft ${material} ${toolType}, trying next material.`)
|
||||
}
|
||||
}
|
||||
} else if (material === 'wooden') {
|
||||
catch (err) {
|
||||
if (err instanceof ActionError && err.code === 'RESOURCE_MISSING') {
|
||||
// Just fall through to next material if resources missing
|
||||
}
|
||||
else {
|
||||
logger.error(`Failed to craft ${material} ${toolType}, trying next material.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (material === 'wooden') {
|
||||
// Last resort: make wooden tools
|
||||
// This will try to gather wood if needed, or throw if it fails
|
||||
try {
|
||||
await ensurePlanks(mineflayer, 4)
|
||||
await ensureCraftingTable(mineflayer)
|
||||
await ensureSticks(mineflayer, 2)
|
||||
const crafted = await craftRecipe(mineflayer, `wooden_${toolType}`, 1)
|
||||
if (crafted) return true
|
||||
} catch (err) {
|
||||
throw new ActionError('CRAFTING_FAILED', `Could not craft any ${toolType}`, { error: err })
|
||||
await ensurePlanks(mineflayer, 4)
|
||||
await ensureCraftingTable(mineflayer)
|
||||
await ensureSticks(mineflayer, 2)
|
||||
const crafted = await craftRecipe(mineflayer, `wooden_${toolType}`, 1)
|
||||
if (crafted)
|
||||
return true
|
||||
}
|
||||
catch (err) {
|
||||
throw new ActionError('CRAFTING_FAILED', `Could not craft any ${toolType}`, { error: err })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +70,8 @@ export async function craftRecipe(
|
||||
)
|
||||
const recipes = mineflayer.bot.recipesFor(itemId, null, 1, craftingTable)
|
||||
if (!recipes || recipes.length === 0) {
|
||||
// If we have a crafting table but still no recipes, we are missing materials
|
||||
return false // Let the caller decide or fall through
|
||||
// If we have a crafting table but still no recipes, we are missing materials
|
||||
return false // Let the caller decide or fall through
|
||||
}
|
||||
success = await attemptCraft(recipes, craftingTable)
|
||||
}
|
||||
@@ -81,13 +81,14 @@ export async function craftRecipe(
|
||||
(err as Error).message
|
||||
}`,
|
||||
)
|
||||
if (err instanceof ActionError) throw err
|
||||
if (err instanceof ActionError)
|
||||
throw err
|
||||
}
|
||||
attempts++
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
throw new ActionError('NAVIGATION_FAILED', 'Could not reach crafting table')
|
||||
throw new ActionError('NAVIGATION_FAILED', 'Could not reach crafting table')
|
||||
}
|
||||
|
||||
return success
|
||||
@@ -129,19 +130,19 @@ export async function craftRecipe(
|
||||
logger.log(`Step 1: Try to craft without a crafting table`)
|
||||
const recipes = mineflayer.bot.recipesFor(itemId, null, 1, null)
|
||||
if (recipes && recipes.length > 0) {
|
||||
// We have recipes without table
|
||||
if (await attemptCraft(recipes)) {
|
||||
return true
|
||||
}
|
||||
// We have recipes without table
|
||||
if (await attemptCraft(recipes)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// RECURSION GUARD:
|
||||
// If we failed to craft basic items (planks, sticks) without a table,
|
||||
// If we failed to craft basic items (planks, sticks) without a table,
|
||||
// do NOT try to find a table. These items do not need a table.
|
||||
// Seeking a table often triggers "ensureCraftingTable" -> "ensurePlanks" -> infinite loop.
|
||||
if (itemName.includes('planks') || itemName === 'stick' || itemName === 'crafting_table') {
|
||||
logger.log(`Recursion Guard: Skipping crafting table search for basic item: ${itemName}`)
|
||||
throw new ActionError('RESOURCE_MISSING', `Cannot craft ${itemName} - missing ingredients`, { item: itemName })
|
||||
logger.log(`Recursion Guard: Skipping crafting table search for basic item: ${itemName}`)
|
||||
throw new ActionError('RESOURCE_MISSING', `Cannot craft ${itemName} - missing ingredients`, { item: itemName })
|
||||
}
|
||||
|
||||
// Step 2: Find and use a crafting table
|
||||
@@ -151,7 +152,7 @@ export async function craftRecipe(
|
||||
if (await findAndUseCraftingTable(craftingTableRange)) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
// If we got here, maybe we didn't have recipes even with a table?
|
||||
// Let's verify if resources are missing
|
||||
// We can check recipes again assuming table is available (which we tried to ensure)
|
||||
@@ -172,7 +173,7 @@ export async function smeltItem(mineflayer: Mineflayer, itemName: string, num =
|
||||
]
|
||||
if (!itemName.includes('raw') && !foods.includes(itemName)) {
|
||||
throw new ActionError('CRAFTING_FAILED', `Cannot smelt ${itemName}, must be a "raw" item`)
|
||||
}
|
||||
}
|
||||
|
||||
let placedFurnace = false
|
||||
let furnaceBlock = getNearestBlock(mineflayer, 'furnace', 32)
|
||||
@@ -192,9 +193,9 @@ export async function smeltItem(mineflayer: Mineflayer, itemName: string, num =
|
||||
}
|
||||
}
|
||||
if (!furnaceBlock) {
|
||||
throw new ActionError('RESOURCE_MISSING', 'There is no furnace nearby and I have no furnace to place')
|
||||
throw new ActionError('RESOURCE_MISSING', 'There is no furnace nearby and I have no furnace to place')
|
||||
}
|
||||
|
||||
|
||||
if (mineflayer.bot.entity.position.distanceTo(furnaceBlock.position) > 4) {
|
||||
await goToNearestBlock(mineflayer, 'furnace', 4, 32)
|
||||
}
|
||||
@@ -209,14 +210,16 @@ export async function smeltItem(mineflayer: Mineflayer, itemName: string, num =
|
||||
&& inputItem.type !== getItemId(itemName)
|
||||
&& inputItem.count > 0
|
||||
) {
|
||||
if (placedFurnace) await collectBlock(mineflayer, 'furnace', 1)
|
||||
if (placedFurnace)
|
||||
await collectBlock(mineflayer, 'furnace', 1)
|
||||
throw new ActionError('CRAFTING_FAILED', `The furnace is currently smelting ${getItemName(inputItem.type)}`)
|
||||
}
|
||||
|
||||
|
||||
// Check if the bot has enough items to smelt
|
||||
const invCounts = getInventoryCounts(mineflayer)
|
||||
if (!invCounts[itemName] || invCounts[itemName] < num) {
|
||||
if (placedFurnace) await collectBlock(mineflayer, 'furnace', 1)
|
||||
if (placedFurnace)
|
||||
await collectBlock(mineflayer, 'furnace', 1)
|
||||
throw new ActionError('RESOURCE_MISSING', `I do not have enough ${itemName} to smelt`, { required: num })
|
||||
}
|
||||
|
||||
@@ -227,20 +230,22 @@ export async function smeltItem(mineflayer: Mineflayer, itemName: string, num =
|
||||
.find(item => item.name === 'coal' || item.name === 'charcoal')
|
||||
const putFuel = Math.ceil(num / 8)
|
||||
if (!fuel || fuel.count < putFuel) {
|
||||
if (placedFurnace) await collectBlock(mineflayer, 'furnace', 1)
|
||||
if (placedFurnace)
|
||||
await collectBlock(mineflayer, 'furnace', 1)
|
||||
throw new ActionError('RESOURCE_MISSING', `I do not have enough coal or charcoal to smelt`, { required: putFuel })
|
||||
}
|
||||
await furnace.putFuel(fuel.type, null, putFuel)
|
||||
}
|
||||
|
||||
|
||||
// Put the items in the furnace
|
||||
const itemId = getItemId(itemName)
|
||||
if (itemId === null) {
|
||||
if (placedFurnace) await collectBlock(mineflayer, 'furnace', 1)
|
||||
throw new ActionError('UNKNOWN', `Invalid item name: ${itemName}`)
|
||||
if (placedFurnace)
|
||||
await collectBlock(mineflayer, 'furnace', 1)
|
||||
throw new ActionError('UNKNOWN', `Invalid item name: ${itemName}`)
|
||||
}
|
||||
await furnace.putInput(itemId, null, num)
|
||||
|
||||
|
||||
// Wait for the items to smelt
|
||||
let total = 0
|
||||
let collectedLast = true
|
||||
@@ -249,16 +254,16 @@ export async function smeltItem(mineflayer: Mineflayer, itemName: string, num =
|
||||
// Wait limit 30s per item?
|
||||
const maxWait = num * 12000 // approx 10s per item + buffer
|
||||
let waited = 0
|
||||
|
||||
|
||||
while (total < num) {
|
||||
await new Promise(resolve => setTimeout(resolve, 5000))
|
||||
waited += 5000
|
||||
|
||||
|
||||
// Safety break
|
||||
if (waited > maxWait) {
|
||||
break
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
logger.log('checking...')
|
||||
let collected = false
|
||||
if (furnace.outputItem()) {
|
||||
@@ -272,7 +277,7 @@ export async function smeltItem(mineflayer: Mineflayer, itemName: string, num =
|
||||
// If we didn't collect anything twice in a row, maybe it stopped?
|
||||
// Check input
|
||||
if (!furnace.inputItem() && !furnace.outputItem()) {
|
||||
break // empty?
|
||||
break // empty?
|
||||
}
|
||||
}
|
||||
collectedLast = collected
|
||||
@@ -282,11 +287,11 @@ export async function smeltItem(mineflayer: Mineflayer, itemName: string, num =
|
||||
if (placedFurnace) {
|
||||
await collectBlock(mineflayer, 'furnace', 1)
|
||||
}
|
||||
|
||||
|
||||
if (total < num) {
|
||||
throw new ActionError('CRAFTING_FAILED', `Failed to smelt all items, only got ${total}/${num}`)
|
||||
throw new ActionError('CRAFTING_FAILED', `Failed to smelt all items, only got ${total}/${num}`)
|
||||
}
|
||||
|
||||
|
||||
logger.log(
|
||||
`Successfully smelted ${itemName}, got ${total} ${getItemName(
|
||||
smeltedItem?.type || 0,
|
||||
@@ -311,7 +316,7 @@ export async function clearNearestFurnace(mineflayer: Mineflayer): Promise<boole
|
||||
await furnace.takeInput()
|
||||
if (furnace.fuelItem())
|
||||
await furnace.takeFuel()
|
||||
|
||||
|
||||
await mineflayer.bot.closeWindow(furnace)
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user