diff --git a/examples/example.js b/examples/example.js index 54ba128..b5ef7fc 100644 --- a/examples/example.js +++ b/examples/example.js @@ -1,7 +1,7 @@ "use strict"; const { createBot } = require("mineflayer"); const { createPlugin, goals } = require("../dist"); -const { GoalBlock, GoalLookAt, GoalPlaceBlock } = goals; +const { GoalBlock, GoalLookAt, GoalPlaceBlock, GoalInvert } = goals; const { Vec3 } = require("vec3"); const rl = require('readline') const { default: loader, EntityState, EPhysicsCtx, EntityPhysics } = require("@nxg-org/mineflayer-physics-util"); @@ -136,6 +136,7 @@ async function cmdHandler(username, msg) { if (!author) return bot.whisper(username, "failed to find player"); const dist = parseInt(args[0]) || 1; const goal = GoalFollowEntity.fromEntity(author, dist, {neverfinish: true}); + const goal1 = GoalInvert.fromDyn(goal); await bot.pathfinder.goto(goal); break; } diff --git a/src/ThePathfinder.ts b/src/ThePathfinder.ts index 7206076..b9d94b9 100644 --- a/src/ThePathfinder.ts +++ b/src/ThePathfinder.ts @@ -387,19 +387,20 @@ export class ThePathfinder { const ret = bounded(...args) if (ret) { void this.reset('goalUpdated') - this.bot.off(goal.eventKey, list1) + for (const key of goal._eventKeys) this.bot.off(key, list1) } } - this.bot.on(goal.eventKey, list1) + for (const key of goal._eventKeys) this.bot.on(key, list1) + goal.cleanup = () => { - this.bot.off(goal.eventKey, list1) + for (const key of goal._eventKeys) this.bot.off(key, list1) delete goal.cleanup } cleanup = () => { old() - this.bot.off(goal.eventKey, list1) + for (const key of goal._eventKeys) this.bot.off(key, list1) } } @@ -516,13 +517,12 @@ export class ThePathfinder { const bounded = refGoal.hasChanged.bind(refGoal) const listener = (...args: any[]): void => { if (this.userAborted || bounded(...args)) { - console.log('hi', bounded(...args), this.bot.listenerCount(refGoal.eventKey)) refGoal.update() - this.bot.off(refGoal.eventKey, listener) + for (const key of refGoal._eventKeys) this.bot.off(key, listener) resolve() } } - this.bot.on(refGoal.eventKey, listener) + for (const key of refGoal._eventKeys) this.bot.on(key, listener) }) } } diff --git a/src/abstract/algorithms/astar.ts b/src/abstract/algorithms/astar.ts index 820af8a..d46c223 100644 --- a/src/abstract/algorithms/astar.ts +++ b/src/abstract/algorithms/astar.ts @@ -36,7 +36,7 @@ export class AStar implements Algorithm movements: MovementProvider, goal: Goal, timeout: number, - tickTimeout = 45, + tickTimeout = 40, searchRadius = -1, differential = 0 ) { diff --git a/src/mineflayer-specific/goals.ts b/src/mineflayer-specific/goals.ts index 47d54ab..b24621d 100644 --- a/src/mineflayer-specific/goals.ts +++ b/src/mineflayer-specific/goals.ts @@ -19,14 +19,84 @@ export abstract class Goal implements AGoal { async onFinish (node: MovementExecutor): Promise {} } -export abstract class GoalDynamic extends Goal { +type EasyKeys = keyof BotEvents | Array +export abstract class GoalDynamic< + Change extends EasyKeys = Array, + Valid extends EasyKeys = Array, + ChKey extends Change extends keyof BotEvents ? [Change] : Change = Change extends keyof BotEvents ? [Change] : Change, + VlKey extends Valid extends keyof BotEvents ? [Valid] : Valid = Valid extends keyof BotEvents ? [Valid] : Valid +> extends Goal { dynamic = true neverfinish = false - readonly abstract eventKey: Key - abstract hasChanged (...args: Parameters): boolean + abstract readonly eventKeys: Readonly + abstract readonly validKeys: Readonly + abstract hasChanged (...args: Parameters): boolean + abstract isValid (...args: Parameters): boolean abstract update (): void cleanup?: () => void // will be assigned later. + + get _eventKeys (): ChKey { + if (this.eventKeys instanceof Array) return this.eventKeys as ChKey + return [this.eventKeys] as ChKey + } + + get _validKeys (): VlKey { + if (this.validKeys instanceof Array) return this.validKeys as VlKey + return [this.validKeys] as VlKey + } +} + +export class GoalInvert extends GoalDynamic { + eventKeys = [] as unknown as Et + validKeys = [] as unknown as Val // to be set later. + + isDynamic = false + + private constructor (private readonly goal: Goal) { + super() + + if (goal instanceof GoalDynamic) { + this.eventKeys = goal._eventKeys + this.validKeys = goal._validKeys + this.isDynamic = true + } + } + + static from (goal: Goal): GoalInvert { + return new GoalInvert(goal) + } + + static fromDyn< + G extends GoalDynamic, + K0 extends EasyKeys = G extends GoalDynamic ? K : never, + K1 extends EasyKeys = G extends GoalDynamic ? V : never + >(goal: GoalDynamic): GoalInvert { + return new GoalInvert(goal) + } + + isEnd (node: Move): boolean { + return !this.goal.isEnd(node) + } + + heuristic (node: Move): number { + return -this.goal.heuristic(node) + } + + hasChanged (...args: Parameters): boolean { + if (!this.isDynamic) return false + return (this.goal as GoalDynamic).hasChanged(...args) + } + + isValid (...args: Parameters): boolean { + if (!this.isDynamic) return false + return (this.goal as GoalDynamic).isValid(...args) + } + + update (): void { + if (this.goal instanceof GoalDynamic) this.goal.update() + } } + /** * A goal to be directly at a specific coordinate. */ @@ -265,10 +335,11 @@ export class GoalPlaceBlock extends GoalLookAt { interface GoalFollowEntityOpts { neverfinish?: boolean dynamic?: boolean - } -export class GoalFollowEntity extends GoalDynamic<'entityMoved'> { - readonly eventKey = 'entityMoved' as const + +export class GoalFollowEntity extends GoalDynamic<'entityMoved', 'entityGone'> { + readonly eventKeys = 'entityMoved' as const + readonly validKeys = 'entityGone' as const public x: number public y: number @@ -306,7 +377,7 @@ export class GoalFollowEntity extends GoalDynamic<'entityMoved'> { } hasChanged (e: Entity): boolean { - // if (e.position !== this.refVec) return false; + if (e.position !== this.refVec) return false const dx = this.x - this.refVec.x const dy = this.y - this.refVec.y const dz = this.z - this.refVec.z @@ -319,6 +390,10 @@ export class GoalFollowEntity extends GoalDynamic<'entityMoved'> { return ret } + isValid (entity: Entity): boolean { + return entity.position === this.refVec + } + update (): void { this.x = this.refVec.x this.y = this.refVec.y diff --git a/src/mineflayer-specific/movements/movementProviders.ts b/src/mineflayer-specific/movements/movementProviders.ts index 3cdbadd..42c911f 100644 --- a/src/mineflayer-specific/movements/movementProviders.ts +++ b/src/mineflayer-specific/movements/movementProviders.ts @@ -430,15 +430,29 @@ export class ParkourForward extends MovementProvider { // if (blockC.safe) cost += this.getNumEntitiesAt(blockC.position, 0, 0, 0) * this.entityCost - if (ceilingClear && blockB.safe && blockC.safe && blockD.physical) { + if ((ceilingClear || d === 2) && blockB.safe && blockC.safe && blockD.safe && floorCleared) { + const off = blockD.position + if (closed.has(`${off.x},${off.y},${off.z}`)) continue + + // Down + const blockE = this.getBlockInfo(node, dx, -2, dz) + if (blockE.physical) { + // cost += this.exclusionStep(blockD) + // cost += this.getNumEntitiesAt(blockD.position, 0, 0, 0) * this.entityCost + neighbors.push(Move.fromPrevious(cost, blockD.position.offset(0.5, 0, 0.5), node, this)) + // neighbors.push(new Move(blockD.position.x, blockD.position.y, blockD.position.z, node.remainingBlocks, cost, [], [], true)) + } + floorCleared = floorCleared && !blockE.physical + } else if (ceilingClear && blockB.safe && blockC.safe && blockD.physical) { if (d === 5) continue + const cost1 = cost + 3 // potential slowdown (will fix later.) // cost += this.exclusionStep(blockB) // Forward const off = blockC.position if (closed.has(`${off.x},${off.y},${off.z}`)) continue - neighbors.push(Move.fromPrevious(cost, blockC.position.offset(0.5, 0, 0.5), node, this)) + neighbors.push(Move.fromPrevious(cost1, blockC.position.offset(0.5, 0, 0.5), node, this)) // neighbors.push(new Move(blockC.position.x, blockC.position.y, blockC.position.z, node.remainingBlocks, cost, [], [], true)) break } else if (ceilingClear && blockA.safe && blockB.safe && blockC.physical) { @@ -456,19 +470,6 @@ export class ParkourForward extends MovementProvider { // neighbors.push(new Move(blockB.position.x, blockB.position.y, blockB.position.z, node.remainingBlocks, cost, [], [], true)) break // } - } else if ((ceilingClear || d === 2) && blockB.safe && blockC.safe && blockD.safe && floorCleared) { - const off = blockD.position - if (closed.has(`${off.x},${off.y},${off.z}`)) continue - - // Down - const blockE = this.getBlockInfo(node, dx, -2, dz) - if (blockE.physical) { - // cost += this.exclusionStep(blockD) - // cost += this.getNumEntitiesAt(blockD.position, 0, 0, 0) * this.entityCost - neighbors.push(Move.fromPrevious(cost, blockD.position.offset(0.5, 0, 0.5), node, this)) - // neighbors.push(new Move(blockD.position.x, blockD.position.y, blockD.position.z, node.remainingBlocks, cost, [], [], true)) - } - floorCleared = floorCleared && !blockE.physical } else if (!blockB.safe || !blockC.safe) { break }