Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- [GoalCompositeAny](#goalcompositeany)
- [GoalCompositeAll](#goalcompositeall)
- [Settings](#settings)
- [Exclusion Zones](#exclusion-zones)
- [Events](#events)
- [pathGenerated](#pathGenerated)
- [goalSet](#goalSet)
Expand Down Expand Up @@ -560,6 +561,9 @@ These are the currently available settings.
| `infiniteLiquidDropdownDistance` | `boolean` | Whether or not to have an infinite liquid dropdown distance. | `true` |
| `allowSprinting` | `boolean` | Whether or not to allow sprinting. | `true` |
| `careAboutLookAlignment` | `boolean` | Whether or not to care about look alignment. | `true` |
| `exclusionAreasStep` | `ExclusionArea[]` | "Keep out" rules for blocks the bot would **stand in**. See [Exclusion Zones](#exclusion-zones). | `[]` |
| `exclusionAreasBreak` | `ExclusionArea[]` | "Keep out" rules for blocks the bot would **break** (mine). | `[]` |
| `exclusionAreasPlace` | `ExclusionArea[]` | "Keep out" rules for blocks the bot would **place** (build on). | `[]` |


```ts
Expand All @@ -583,12 +587,64 @@ interface MovementOptions {
infiniteLiquidDropdownDistance: boolean
allowSprinting: boolean
careAboutLookAlignment: boolean

movementTimeoutMs: number

// "Keep out" zones. Empty by default. See the Exclusion Zones section below.
exclusionAreasStep: ExclusionArea[]
exclusionAreasBreak: ExclusionArea[]
exclusionAreasPlace: ExclusionArea[]
}

```



<h1 align="center">Exclusion Zones</h1>

Exclusion zones mark areas as off-limits. An exclusion area is a function
returning the extra cost of a block; the same idea as upstream
[`PrismarineJS/mineflayer-pathfinder`](https://github.com/PrismarineJS/mineflayer-pathfinder).

```ts
type ExclusionArea = (block: BlockInfo) => number
```

- `0` — no opinion.
- a positive number — soft zone: allowed, but avoided when a cheaper route exists.
- `Infinity` (`>= COST_INF`) — hard zone: never used.

Areas live in three settings; each sums the cost of every function in its list:

| Setting | Checked on every block the bot would… |
| --- | --- |
| `exclusionAreasStep` | stand in (the foot block of each move — walk, jump, drop, parkour, tower; optimized paths included). |
| `exclusionAreasBreak` | break (mine). |
| `exclusionAreasPlace` | place (build on). |

Empty by default, so there is zero overhead when unused. The library ships no
ready-made shapes — write your own. Box/radius helpers are in
[`examples/exclusionZones.js`](../examples/exclusionZones.js).

```ts
// Never break or place inside a protected box; soft-avoid stepping on farmland.
const farmlandId = bot.registry.blocksByName.farmland.id
const inBox = (block) =>
block.position.x >= -10 && block.position.x <= 10 &&
block.position.z >= -10 && block.position.z <= 10 ? Infinity : 0

bot.pathfinder.setMoveOptions({
exclusionAreasStep: [(block) => block.type === farmlandId ? 100 : 0],
exclusionAreasBreak: [inBox],
exclusionAreasPlace: [inBox]
})
```

`exclusionAreasStep` is checked on the block the bot's **feet** land in; extend a
box one block lower if you also need the head kept out.



<h1 align="center">Events</h1>


Expand Down
35 changes: 35 additions & 0 deletions docs/AdvancedUsage.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [Custom Movement Providers](#custom-movement-providers)
- [Custom Movement Executors](#custom-movement-executors)
- [Custom Movement Optimizers](#custom-movement-optimizers)
- [Exclusion Zones (Keep-Out Areas)](#exclusion-zones-keep-out-areas)



Expand Down Expand Up @@ -311,3 +312,37 @@ class MyMovementOptimizer extends MovementOptimizer {
}
}
```

<h1 align="center">Exclusion Zones (Keep-Out Areas)</h1>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These docs are too conversational. Make this more concise.


To keep the bot out of an area without writing a custom `MovementProvider`, add
an **exclusion area** to the move settings. It is a function returning the extra
cost of a block: `0` (no opinion), a positive number (soft — avoided when a
cheaper route exists), or `Infinity` (hard — never used).

Three lists, each summing the cost of every function in it:

- `exclusionAreasStep` — blocks the bot would stand in.
- `exclusionAreasBreak` — blocks the bot would break (mine).
- `exclusionAreasPlace` — blocks the bot would place (build on).

All empty by default (no overhead). Any `(block) => number` works:

```ts
// Never mine valuable ores or anything below y=0.
const diamondId = bot.registry.blocksByName.diamond_ore.id
const protectOres = (block) =>
(block.type === diamondId || block.position.y < 0) ? Infinity : 0

bot.pathfinder.setMoveOptions({ exclusionAreasBreak: [protectOres] })
```

Notes:

- Costs from multiple areas in a list add up.
- `exclusionAreasStep` is checked on the foot block of every move (walk, jump,
drop, parkour, tower), and optimized/straight-lined paths are blocked from
cutting through hard zones too.
- Pass a fresh array to `setMoveOptions({ ... })` rather than mutating the
existing one.
- Ready-to-copy box/radius helpers live in `examples/exclusionZones.js`.
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

`basic.js` shows off the basic functionality of this pathfinder, while `example.js` goes into more depth.
`bridging/bridge.ts` is the bridge demo, and `neos/neo.ts` is a neo-jump-focused variant based on the same bot setup.
`exclusionZones.js` shows how to add "keep out" areas (exclusion zones) to the movement settings.
122 changes: 122 additions & 0 deletions examples/exclusionZones.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
'use strict'

// ---------------------------------------------------------------------------
// Exclusion zones example
//
// An "exclusion area" is just a function (block) => number that returns the
// extra cost of using a block: 0 = fine, a positive number = soft avoid,
// Infinity = hard "keep out". The library ships no ready-made shapes on
// purpose, so the small box/radius builders below are yours to copy and adapt.
//
// They go into three movement settings:
// exclusionAreasStep -> blocks the bot may stand in / walk into
// exclusionAreasBreak -> blocks the bot may break (mine)
// exclusionAreasPlace -> blocks the bot may place (build on)
//
// Run a local server, then: node examples/exclusionZones.js
// In chat: "goto <x> <y> <z>", "zones on", "zones off".
// ---------------------------------------------------------------------------

const { createBot } = require('mineflayer')
const { Vec3 } = require('vec3')
const { createPlugin, goals } = require('../dist')

const { GoalBlock } = goals

// --- copy these helpers into your own project ------------------------------

// A box between two opposite corners (inclusive, any order).
function boxExclusion (corner1, corner2, cost = Infinity) {
const minX = Math.min(corner1.x, corner2.x)
const minY = Math.min(corner1.y, corner2.y)
const minZ = Math.min(corner1.z, corner2.z)
const maxX = Math.max(corner1.x, corner2.x)
const maxY = Math.max(corner1.y, corner2.y)
const maxZ = Math.max(corner1.z, corner2.z)
return (block) => {
const p = block.position
const inside =
p.x >= minX && p.x <= maxX &&
p.y >= minY && p.y <= maxY &&
p.z >= minZ && p.z <= maxZ
return inside ? cost : 0
}
}

// A ball (sphere) of the given radius around a center point.
function radiusExclusion (center, radius, cost = Infinity) {
const r2 = radius * radius
return (block) => {
const dx = block.position.x - center.x
const dy = block.position.y - center.y
const dz = block.position.z - center.z
return dx * dx + dy * dy + dz * dz <= r2 ? cost : 0
}
}

// ---------------------------------------------------------------------------

const bot = createBot({
username: 'exclusion-demo',
auth: 'offline',
host: 'localhost',
port: 25565
})

bot.loadPlugin(createPlugin())

// Example zones (tweak the coordinates to match your world):
const noGoBox = boxExclusion(new Vec3(-8, 60, -8), new Vec3(8, 80, 8)) // hard
const softBall = radiusExclusion(new Vec3(30, 64, 30), 6, 50) // soft

function enableZones () {
bot.pathfinder.setMoveOptions({
exclusionAreasStep: [noGoBox, softBall]
})
bot.chat('Exclusion zones: ON')
}

function disableZones () {
bot.pathfinder.setMoveOptions({
exclusionAreasStep: [],
exclusionAreasBreak: [],
exclusionAreasPlace: []
})
bot.chat('Exclusion zones: OFF')
}

bot.once('spawn', () => {
enableZones()
bot.chat('Ready. Try: "goto <x> <y> <z>", "zones on", "zones off".')
})

bot.on('chat', async (username, message) => {
if (username === bot.username) return

const [cmd, ...args] = message.trim().split(/\s+/)

if (cmd === 'zones') {
if (args[0] === 'off') disableZones()
else enableZones()
return
}

if (cmd === 'goto') {
const [x, y, z] = args.map(Number)
if ([x, y, z].some(Number.isNaN)) {
bot.chat('Usage: goto <x> <y> <z>')
return
}

bot.chat(`Heading to ${x} ${y} ${z}, avoiding the zones...`)
try {
await bot.pathfinder.goto(new GoalBlock(x, y, z))
bot.chat('Arrived!')
} catch (err) {
bot.chat(`Could not get there: ${err.message}`)
}
}
})

bot.on('kicked', console.log)
bot.on('error', console.log)
6 changes: 3 additions & 3 deletions src/ThePathfinder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type {
import {
MovementHandler,
MovementExecutor,
DEFAULT_MOVEMENT_OPTS
buildMovementOptions
} from './mineflayer-specific/movements'

import {
Expand Down Expand Up @@ -209,7 +209,7 @@ export class ThePathfinder {
const optimizers = opts.optimizers ?? DEFAULT_OPTIMIZATION
const moveSetup = opts.movements ?? DEFAULT_SETUP

Object.assign(moveSettings, { ...DEFAULT_MOVEMENT_OPTS, ...opts.moveSettings })
Object.assign(moveSettings, buildMovementOptions(opts.moveSettings))
Object.assign(pathfinderSettings, { ...DEFAULT_PATHFINDER_OPTS, ...opts.pathfinderSettings })

const moves = new Map<BuildableMoveProvider, MovementExecutor>()
Expand Down Expand Up @@ -279,7 +279,7 @@ export class ThePathfinder {
}

setMoveOptions(settings: Partial<MovementOptions>): void {
this.defaultMoveSettings = Object.assign({}, DEFAULT_MOVEMENT_OPTS, settings)
this.defaultMoveSettings = buildMovementOptions(settings)
this.optimizerRegistry.setSettings(this.defaultMoveSettings)
for (const [, executor] of this.movements) {
executor.settings = this.defaultMoveSettings
Expand Down
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ export * as goals from './mineflayer-specific/goals'

export { MovementExecutor, MovementProvider } from './mineflayer-specific/movements'
export type { BuildableMoveExecutor, BuildableMoveProvider, MovementSetup } from './mineflayer-specific/movements'
export type { MovementOptions } from './mineflayer-specific/movements'

// Exclusion zones ("keep out" areas), like upstream mineflayer-pathfinder.
// Only the type is exported; users write their own zone functions
// (see examples/exclusionZones.js for ready-to-copy box/radius helpers).
export type { ExclusionArea } from './mineflayer-specific/movements/exclusionZones'
export { MovementOptimizer } from './mineflayer-specific/post'
export type { BuildableMoveOptimizer, OptimizationSetup, OptimizationMap } from './mineflayer-specific/post'
export { Move } from './mineflayer-specific/move'
Expand Down
16 changes: 16 additions & 0 deletions src/mineflayer-specific/movements/exclusionZones.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { BlockInfo } from '../world/cacheWorld'

/**
* An exclusion area: a function that returns the extra cost of letting the bot
* use a given block.
*
* - `0` -> no opinion on this block.
* - a positive number -> a soft penalty; the bot avoids the block when it can.
* - `>= COST_INF` -> a hard "keep out"; the bot will never use the block.
*
* These are stored in the three movement settings `exclusionAreasStep`,
* `exclusionAreasBreak` and `exclusionAreasPlace`. The pathfinder intentionally
* ships no ready-made shapes — write your own, or copy the box/radius helpers
* from `examples/exclusionZones.js`. This mirrors upstream mineflayer-pathfinder.
*/
export type ExclusionArea = (block: BlockInfo) => number
1 change: 1 addition & 0 deletions src/mineflayer-specific/movements/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export * from './movementExecutors'
export * from './movementProviders'
export * from './movementExecutor'
export * from './movementProvider'
export * from './exclusionZones'
// export * from './pp'
Loading