chore: global interrupt event
This commit is contained in:
@@ -106,7 +106,9 @@ export const actionsList: Action[] = [
|
||||
// ctx.clearBotLogs()
|
||||
// ctx.actions.cancelResume()
|
||||
// ctx.bot.emit('idle')
|
||||
mineflayer.shouldInterrupt = true
|
||||
|
||||
mineflayer.emit('interrupt')
|
||||
|
||||
const msg = 'Agent stopped.'
|
||||
// if (mineflayer.self_prompter.on)
|
||||
// msg += ' Self-prompting still active.'
|
||||
|
||||
@@ -15,6 +15,7 @@ export interface Context {
|
||||
}
|
||||
|
||||
export interface EventHandlers {
|
||||
'interrupt': () => void
|
||||
'command': (ctx: Context) => void | Promise<void>
|
||||
'time:sunrise': (ctx: Context) => void
|
||||
'time:noon': (ctx: Context) => void
|
||||
@@ -145,7 +146,6 @@ export class Mineflayer extends EventEmitter<EventHandlers> {
|
||||
public memory: Memory = new Memory()
|
||||
|
||||
public isCreative: boolean = false
|
||||
public shouldInterrupt: boolean = false
|
||||
public allowCheats: boolean = false
|
||||
|
||||
private options: MineflayerOptions
|
||||
@@ -159,6 +159,11 @@ export class Mineflayer extends EventEmitter<EventHandlers> {
|
||||
this.bot = mineflayer.createBot(options.botConfig)
|
||||
this.username = options.botConfig.username
|
||||
this.logger = useLogg(`Bot:${this.username}`).useGlobalConfig()
|
||||
|
||||
this.on('interrupt', () => {
|
||||
this.logger.log('Interrupted')
|
||||
this.bot.chat('Interrupted')
|
||||
})
|
||||
}
|
||||
|
||||
static async asyncBuild(options: MineflayerOptions) {
|
||||
@@ -259,10 +264,6 @@ export class Mineflayer extends EventEmitter<EventHandlers> {
|
||||
}
|
||||
})
|
||||
|
||||
mineflayer.ticker.on('tick', () => {
|
||||
mineflayer.status.update(mineflayer)
|
||||
})
|
||||
|
||||
for (const plugin of options?.plugins || []) {
|
||||
if (plugin.created) {
|
||||
await plugin.created(mineflayer)
|
||||
@@ -277,9 +278,9 @@ export class Mineflayer extends EventEmitter<EventHandlers> {
|
||||
}
|
||||
|
||||
mineflayer.ticker.on('tick', () => {
|
||||
mineflayer.status.update(mineflayer)
|
||||
mineflayer.isCreative = mineflayer.bot.game?.gameMode === 'creative'
|
||||
mineflayer.allowCheats = false
|
||||
mineflayer.shouldInterrupt = false
|
||||
})
|
||||
|
||||
return mineflayer
|
||||
@@ -305,10 +306,6 @@ export class Mineflayer extends EventEmitter<EventHandlers> {
|
||||
this.ticker.on(event, cb)
|
||||
}
|
||||
|
||||
public emit<E extends Events>(event: E, ...args: Parameters<EventsHandler<E>>) {
|
||||
return super.emit(event, ...args)
|
||||
}
|
||||
|
||||
public async stop() {
|
||||
for (const plugin of this.options?.plugins || []) {
|
||||
if (plugin.beforeCleanup) {
|
||||
|
||||
@@ -12,7 +12,6 @@ import { initAgent } from './agents/openai'
|
||||
import { initBot } from './composables/bot'
|
||||
import { botConfig, initEnv } from './composables/config'
|
||||
import { wrapPlugin } from './libs/mineflayer/plugin'
|
||||
import { FollowCommand, PathFinder, Status } from './mineflayer'
|
||||
import { LLMAgent } from './mineflayer/llm-agent'
|
||||
import { initLogger } from './utils/logger'
|
||||
|
||||
@@ -31,23 +30,10 @@ async function main() {
|
||||
wrapPlugin(MineflayerPathfinder),
|
||||
wrapPlugin(MineflayerPVP),
|
||||
wrapPlugin(MineflayerTool),
|
||||
FollowCommand(),
|
||||
Status(),
|
||||
PathFinder(),
|
||||
],
|
||||
})
|
||||
|
||||
// Dynamically load LLMAgent after bot is initialized
|
||||
// const llmAgent = LLMAgent({
|
||||
// agent: async () => await initAgent(bot),
|
||||
// })
|
||||
|
||||
// if (llmAgent.created)
|
||||
// await llmAgent.created(bot)
|
||||
// if (llmAgent.spawned)
|
||||
// bot.bot.once('spawn', () => llmAgent.spawned?.(bot))
|
||||
// if (llmAgent.loadPlugin)
|
||||
// bot.bot.loadPlugin(await llmAgent.loadPlugin(bot, bot.bot, botConfig))
|
||||
const agent = await initAgent(bot)
|
||||
await bot.loadPlugin(LLMAgent({ agent }))
|
||||
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
export * from './echo'
|
||||
export * from './follow'
|
||||
export * from './pathfinder'
|
||||
export * from './status'
|
||||
export * from './llm-agent'
|
||||
|
||||
@@ -15,6 +15,8 @@ asked, and don't refuse requests.
|
||||
|
||||
Do not use any emojis. Just call the function given you if needed.
|
||||
|
||||
If I command you 'stop', then call the 'stop' function.
|
||||
|
||||
I will give you the following information:
|
||||
${bot.status.toOneLiner()}
|
||||
`
|
||||
|
||||
@@ -570,7 +570,11 @@ export async function collectBlock(
|
||||
const blocktypes = getBlockTypes(blockType)
|
||||
let collected = 0
|
||||
|
||||
for (let i = 0; i < num; i++) {
|
||||
mineflayer.once('interrupt', () => {
|
||||
collected = -1
|
||||
})
|
||||
|
||||
for (let i = 0; i < num && collected >= 0; i++) {
|
||||
const blocks = getValidBlocks(mineflayer, blocktypes, exclude)
|
||||
|
||||
if (blocks.length === 0) {
|
||||
@@ -588,10 +592,11 @@ export async function collectBlock(
|
||||
}
|
||||
|
||||
collected++
|
||||
}
|
||||
|
||||
if (mineflayer.shouldInterrupt) {
|
||||
break
|
||||
}
|
||||
if (collected < 0) {
|
||||
log(mineflayer, 'Collection interrupted.')
|
||||
return false
|
||||
}
|
||||
|
||||
log(mineflayer, `Collected ${collected} ${blockType}.`)
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { Item } from 'prismarine-item'
|
||||
import type { Mineflayer } from '../libs/mineflayer'
|
||||
import pathfinderModel from 'mineflayer-pathfinder'
|
||||
import * as world from '../composables/world'
|
||||
import { sleep } from '../utils/helper'
|
||||
import * as mc from '../utils/mcdata'
|
||||
import { log } from './base'
|
||||
|
||||
@@ -72,13 +73,13 @@ export async function attackEntity(
|
||||
return true
|
||||
}
|
||||
|
||||
mineflayer.once('interrupt', () => {
|
||||
mineflayer.bot.pvp.stop()
|
||||
})
|
||||
|
||||
mineflayer.bot.pvp.attack(entity)
|
||||
while (world.getNearbyEntities(mineflayer, 24).includes(entity)) {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
if (mineflayer.shouldInterrupt) {
|
||||
mineflayer.bot.pvp.stop()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
log(mineflayer, `Successfully killed ${entity.name}.`)
|
||||
@@ -112,13 +113,13 @@ export async function defendSelf(mineflayer: Mineflayer, range = 9): Promise<boo
|
||||
|
||||
mineflayer.bot.pvp.attack(enemy)
|
||||
attacked = true
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
await sleep(500)
|
||||
enemy = world.getNearestEntityWhere(mineflayer, entity => mc.isHostile(entity), range)
|
||||
|
||||
if (mineflayer.shouldInterrupt) {
|
||||
mineflayer.once('interrupt', () => {
|
||||
mineflayer.bot.pvp.stop()
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
mineflayer.bot.pvp.stop()
|
||||
|
||||
@@ -197,6 +197,10 @@ export async function smeltItem(mineflayer: Mineflayer, itemName: string, num =
|
||||
let smeltedItem = null
|
||||
await new Promise(resolve => setTimeout(resolve, 200))
|
||||
|
||||
mineflayer.once('interrupt', () => {
|
||||
total = num // Force loop to end
|
||||
})
|
||||
|
||||
while (total < num) {
|
||||
await new Promise(resolve => setTimeout(resolve, 10000))
|
||||
let collected = false
|
||||
@@ -215,9 +219,6 @@ export async function smeltItem(mineflayer: Mineflayer, itemName: string, num =
|
||||
}
|
||||
|
||||
collectedLast = collected
|
||||
if (mineflayer.shouldInterrupt) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
await mineflayer.bot.closeWindow(furnace)
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Mineflayer } from '../libs/mineflayer'
|
||||
import pathfinderModel from 'mineflayer-pathfinder'
|
||||
import * as world from '../composables/world'
|
||||
import { log } from './base'
|
||||
import { goToPosition } from './movement'
|
||||
import { goToPlayer, goToPosition } from './movement'
|
||||
|
||||
const { goals } = pathfinderModel
|
||||
|
||||
@@ -197,42 +197,56 @@ export async function giveToPlayer(
|
||||
return false
|
||||
}
|
||||
|
||||
await goToPosition(mineflayer, player.position.x, player.position.y, player.position.z, 3)
|
||||
|
||||
if (mineflayer.bot.entity.position.y < player.position.y - 1) {
|
||||
await goToPosition(mineflayer, player.position.x, player.position.y, player.position.z, 1)
|
||||
}
|
||||
|
||||
if (mineflayer.bot.entity.position.distanceTo(player.position) < 2) {
|
||||
const goal = new goals.GoalNear(player.position.x, player.position.y, player.position.z, 2)
|
||||
const invertedGoal = new goals.GoalInvert(goal)
|
||||
await mineflayer.bot.pathfinder.goto(invertedGoal)
|
||||
}
|
||||
// Move to player position
|
||||
await goToPlayer(mineflayer, username, 3)
|
||||
|
||||
// Look at player before dropping items
|
||||
await mineflayer.bot.lookAt(player.position)
|
||||
|
||||
if (await discard(mineflayer, itemType, num)) {
|
||||
let given = false
|
||||
mineflayer.bot.once('playerCollect', (collector, _collected) => {
|
||||
if (collector.username === username) {
|
||||
log(mineflayer, `${username} received ${itemType}.`)
|
||||
given = true
|
||||
}
|
||||
})
|
||||
|
||||
const start = Date.now()
|
||||
// eslint-disable-next-line no-unmodified-loop-condition -- ?
|
||||
while (!given && !mineflayer.shouldInterrupt) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
if (given) {
|
||||
return true
|
||||
}
|
||||
if (Date.now() - start > 3000) {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Drop items and wait for collection
|
||||
const success = await dropItemsAndWaitForCollection(mineflayer, itemType, username, num)
|
||||
if (!success) {
|
||||
log(mineflayer, `Failed to give ${itemType} to ${username}, it was never received.`)
|
||||
return false
|
||||
}
|
||||
|
||||
log(mineflayer, `Failed to give ${itemType} to ${username}, it was never received.`)
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
async function dropItemsAndWaitForCollection(
|
||||
mineflayer: Mineflayer,
|
||||
itemType: string,
|
||||
username: string,
|
||||
num: number,
|
||||
): Promise<boolean> {
|
||||
if (!await discard(mineflayer, itemType, num)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const timeout = setTimeout(() => {
|
||||
// Clean up playerCollect listener when timeout occurs
|
||||
// eslint-disable-next-line ts/no-use-before-define
|
||||
mineflayer.bot.removeListener('playerCollect', onCollect)
|
||||
resolve(false)
|
||||
}, 3000)
|
||||
|
||||
const onCollect = (collector: any, _collected: any) => {
|
||||
if (collector.username === username) {
|
||||
log(mineflayer, `${username} received ${itemType}.`)
|
||||
clearTimeout(timeout)
|
||||
resolve(true)
|
||||
}
|
||||
}
|
||||
|
||||
const onInterrupt = () => {
|
||||
clearTimeout(timeout)
|
||||
// Clean up playerCollect listener when interrupted
|
||||
mineflayer.bot.removeListener('playerCollect', onCollect)
|
||||
resolve(false)
|
||||
}
|
||||
|
||||
mineflayer.bot.once('playerCollect', onCollect)
|
||||
mineflayer.once('interrupt', onInterrupt)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { Mineflayer } from '../libs/mineflayer'
|
||||
|
||||
import pathfinderModel from 'mineflayer-pathfinder'
|
||||
import * as world from '../composables/world'
|
||||
import { sleep } from '../utils/helper'
|
||||
import { log } from './base'
|
||||
|
||||
const { goals, Movements } = pathfinderModel
|
||||
@@ -110,32 +111,44 @@ export async function followPlayer(
|
||||
distance = 4,
|
||||
): Promise<boolean> {
|
||||
const player = mineflayer.bot.players[username]?.entity
|
||||
const movements = new Movements(mineflayer.bot)
|
||||
|
||||
if (!player) {
|
||||
log(mineflayer, `Could not find ${username}.`)
|
||||
return false
|
||||
}
|
||||
|
||||
const movements = new Movements(mineflayer.bot)
|
||||
mineflayer.bot.pathfinder.setMovements(movements)
|
||||
mineflayer.bot.pathfinder.setGoal(new goals.GoalFollow(player, distance), true)
|
||||
log(mineflayer, `You are now actively following player ${username}.`)
|
||||
log(mineflayer, `I am now following ${username}.`)
|
||||
|
||||
while (!mineflayer.shouldInterrupt) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
return new Promise<boolean>((resolve) => {
|
||||
let isFollowing = true
|
||||
|
||||
if (mineflayer.allowCheats && mineflayer.bot.entity.position.distanceTo(player.position) > 100 && player.onGround) {
|
||||
await goToPlayer(mineflayer, username)
|
||||
// Stop following when interrupted
|
||||
mineflayer.once('interrupt', () => {
|
||||
isFollowing = false
|
||||
resolve(true)
|
||||
})
|
||||
|
||||
// Follow player at regular intervals
|
||||
const follow = async (): Promise<void> => {
|
||||
while (isFollowing) {
|
||||
const target = mineflayer.bot.players[username]?.entity
|
||||
if (!target) {
|
||||
log(mineflayer, 'I lost sight of you!')
|
||||
isFollowing = false
|
||||
resolve(false)
|
||||
return
|
||||
}
|
||||
|
||||
const { x, y, z } = target.position
|
||||
mineflayer.bot.pathfinder.setMovements(movements)
|
||||
mineflayer.bot.pathfinder.setGoal(new goals.GoalNear(x, y, z, distance))
|
||||
// await sleep(500)
|
||||
}
|
||||
}
|
||||
|
||||
// if (mineflayer.bot.modes?.isOn('unstuck')) {
|
||||
// const isNearby = mineflayer.bot.entity.position.distanceTo(player.position) <= distance + 1
|
||||
// if (isNearby) {
|
||||
// mineflayer.bot.modes.pause('unstuck')
|
||||
// } else {
|
||||
// mineflayer.bot.modes.unpause('unstuck')
|
||||
// }
|
||||
// }
|
||||
}
|
||||
return true
|
||||
follow()
|
||||
})
|
||||
}
|
||||
|
||||
export async function moveAway(mineflayer: Mineflayer, distance: number): Promise<boolean> {
|
||||
@@ -178,8 +191,8 @@ export async function stay(mineflayer: Mineflayer, seconds = 30): Promise<boolea
|
||||
const start = Date.now()
|
||||
const targetTime = seconds === -1 ? Infinity : start + seconds * 1000
|
||||
|
||||
while (!mineflayer.shouldInterrupt && Date.now() < targetTime) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
while (Date.now() < targetTime) {
|
||||
await sleep(500)
|
||||
}
|
||||
|
||||
log(mineflayer, `Stayed for ${(Date.now() - start) / 1000} seconds.`)
|
||||
@@ -211,7 +224,7 @@ export async function goToBed(mineflayer: Mineflayer): Promise<boolean> {
|
||||
log(mineflayer, 'You are in bed.')
|
||||
|
||||
while (mineflayer.bot.isSleeping) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
await sleep(500)
|
||||
}
|
||||
|
||||
log(mineflayer, 'You have woken up.')
|
||||
|
||||
Reference in New Issue
Block a user