fix: world and query
This commit is contained in:
@@ -1,32 +1,11 @@
|
|||||||
import type { Bot } from 'mineflayer'
|
|
||||||
import type { BotContext } from '../composables/bot'
|
import type { BotContext } from '../composables/bot'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { getStatus } from '../components/status'
|
import { getStatusToString } from '../components/status'
|
||||||
|
import * as world from '../composables/world'
|
||||||
|
|
||||||
// Core types
|
// Core types
|
||||||
type QueryResult = string | Promise<string>
|
type QueryResult = string | Promise<string>
|
||||||
|
|
||||||
// BotContext management
|
|
||||||
let ctx: QueryBotContext
|
|
||||||
|
|
||||||
export function initQueryBotContext(BotContext: QueryBotContext): void {
|
|
||||||
ctx = BotContext
|
|
||||||
}
|
|
||||||
|
|
||||||
interface QueryBotContext {
|
|
||||||
world: {
|
|
||||||
getBiomeName: (bot: Bot) => string
|
|
||||||
getNearbyPlayerNames: (bot: Bot) => string[]
|
|
||||||
getInventoryCounts: (bot: Bot) => Record<string, number>
|
|
||||||
getNearbyBlockTypes: (bot: Bot) => string[]
|
|
||||||
getCraftableItems: (bot: Bot) => string[]
|
|
||||||
getNearbyEntityTypes: (bot: Bot) => string[]
|
|
||||||
}
|
|
||||||
convoManager: {
|
|
||||||
getInGameAgents: () => string[]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Query {
|
interface Query {
|
||||||
readonly name: string
|
readonly name: string
|
||||||
readonly description: string
|
readonly description: string
|
||||||
@@ -51,9 +30,7 @@ function createStatsQuery(): Query {
|
|||||||
name: 'stats',
|
name: 'stats',
|
||||||
description: 'Get your bot\'s location, health, hunger, and time of day.',
|
description: 'Get your bot\'s location, health, hunger, and time of day.',
|
||||||
schema: z.object({}),
|
schema: z.object({}),
|
||||||
perform: (ctx: BotContext) => (): string => {
|
perform: (ctx: BotContext) => (): string => getStatusToString(ctx),
|
||||||
return Array.from(getStatus(ctx).entries()).map(([key, value]) => `${key}: ${value}`).join('\n')
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,9 +39,9 @@ function createInventoryQuery(): Query {
|
|||||||
name: 'inventory',
|
name: 'inventory',
|
||||||
description: 'Get your bot\'s inventory.',
|
description: 'Get your bot\'s inventory.',
|
||||||
schema: z.object({}),
|
schema: z.object({}),
|
||||||
perform: (agent: QueryAgentBotContext) => (): string => {
|
perform: (ctx: BotContext) => (): string => {
|
||||||
const { bot } = agent
|
const { bot } = ctx
|
||||||
const inventory = ctx.world.getInventoryCounts(bot)
|
const inventory = world.getInventoryCounts({ bot, botCtx: ctx })
|
||||||
const items = Object.entries(inventory)
|
const items = Object.entries(inventory)
|
||||||
.map(([item, count]) => formatInventoryItem(item, count))
|
.map(([item, count]) => formatInventoryItem(item, count))
|
||||||
.join('')
|
.join('')
|
||||||
@@ -77,7 +54,7 @@ function createInventoryQuery(): Query {
|
|||||||
].filter(Boolean).join('')
|
].filter(Boolean).join('')
|
||||||
|
|
||||||
return pad(`INVENTORY${items || ': Nothing'}
|
return pad(`INVENTORY${items || ': Nothing'}
|
||||||
${agent.bot.game.gameMode === 'creative' ? '\n(You have infinite items in creative mode. You do not need to gather resources!!)' : ''}
|
${bot.game.gameMode === 'creative' ? '\n(You have infinite items in creative mode. You do not need to gather resources!!)' : ''}
|
||||||
WEARING: ${wearing || 'Nothing'}`)
|
WEARING: ${wearing || 'Nothing'}`)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -88,8 +65,8 @@ function createNearbyBlocksQuery(): Query {
|
|||||||
name: 'nearbyBlocks',
|
name: 'nearbyBlocks',
|
||||||
description: 'Get the blocks near the bot.',
|
description: 'Get the blocks near the bot.',
|
||||||
schema: z.object({}),
|
schema: z.object({}),
|
||||||
perform: (agent: QueryAgentBotContext) => (): string => {
|
perform: (ctx: BotContext) => (): string => {
|
||||||
const blocks = ctx.world.getNearbyBlockTypes(agent.bot)
|
const blocks = world.getNearbyBlockTypes({ bot: ctx.bot, botCtx: ctx })
|
||||||
return pad(`NEARBY_BLOCKS${blocks.map(b => `\n- ${b}`).join('') || ': none'}`)
|
return pad(`NEARBY_BLOCKS${blocks.map(b => `\n- ${b}`).join('') || ': none'}`)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -100,8 +77,8 @@ function createCraftableQuery(): Query {
|
|||||||
name: 'craftable',
|
name: 'craftable',
|
||||||
description: 'Get the craftable items with the bot\'s inventory.',
|
description: 'Get the craftable items with the bot\'s inventory.',
|
||||||
schema: z.object({}),
|
schema: z.object({}),
|
||||||
perform: (agent: QueryAgentBotContext) => (): string => {
|
perform: (ctx: BotContext) => (): string => {
|
||||||
const craftable = ctx.world.getCraftableItems(agent.bot)
|
const craftable = world.getCraftableItems({ bot: ctx.bot, botCtx: ctx })
|
||||||
return pad(`CRAFTABLE_ITEMS${craftable.map(i => `\n- ${i}`).join('') || ': none'}`)
|
return pad(`CRAFTABLE_ITEMS${craftable.map(i => `\n- ${i}`).join('') || ': none'}`)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -112,19 +89,16 @@ function createEntitiesQuery(): Query {
|
|||||||
name: 'entities',
|
name: 'entities',
|
||||||
description: 'Get the nearby players and entities.',
|
description: 'Get the nearby players and entities.',
|
||||||
schema: z.object({}),
|
schema: z.object({}),
|
||||||
perform: (agent: QueryAgentBotContext) => (): string => {
|
perform: (ctx: BotContext) => (): string => {
|
||||||
const { bot } = agent
|
const { bot } = ctx
|
||||||
const players = ctx.world.getNearbyPlayerNames(bot)
|
const worldCtx = { bot, botCtx: ctx }
|
||||||
.filter(p => !ctx.convoManager.getInGameAgents().includes(p))
|
const players = world.getNearbyPlayerNames(worldCtx)
|
||||||
const bots = ctx.convoManager.getInGameAgents()
|
const entities = world.getNearbyEntityTypes(worldCtx)
|
||||||
.filter(b => b !== agent.name)
|
.filter((e: string) => e !== 'player' && e !== 'item')
|
||||||
const entities = ctx.world.getNearbyEntityTypes(bot)
|
|
||||||
.filter(e => e !== 'player' && e !== 'item')
|
|
||||||
|
|
||||||
const result = [
|
const result = [
|
||||||
...players.map(p => `- Human player: ${p}`),
|
...players.map((p: string) => `- Human player: ${p}`),
|
||||||
...bots.map(b => `- Bot player: ${b}`),
|
...entities.map((e: string) => `- entities: ${e}`),
|
||||||
...entities.map(e => `- entities: ${e}`),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
return pad(`NEARBY_ENTITIES${result.length ? `\n${result.join('\n')}` : ': none'}`)
|
return pad(`NEARBY_ENTITIES${result.length ? `\n${result.join('\n')}` : ': none'}`)
|
||||||
@@ -132,25 +106,6 @@ function createEntitiesQuery(): Query {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createModesQuery(): Query {
|
|
||||||
return {
|
|
||||||
name: 'modes',
|
|
||||||
description: 'Get all available modes and their docs and see which are on/off.',
|
|
||||||
schema: z.object({}),
|
|
||||||
perform: (agent: QueryAgentBotContext) => (): string => agent.bot.modes.getDocs(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createSavedPlacesQuery(): Query {
|
|
||||||
return {
|
|
||||||
name: 'savedPlaces',
|
|
||||||
description: 'List all saved locations.',
|
|
||||||
schema: z.object({}),
|
|
||||||
perform: (agent: QueryAgentBotContext) => (): string =>
|
|
||||||
`Saved place names: ${agent.memory_bank.getKeys()}`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export query list
|
// Export query list
|
||||||
export const queryList: readonly Query[] = [
|
export const queryList: readonly Query[] = [
|
||||||
createStatsQuery(),
|
createStatsQuery(),
|
||||||
@@ -158,6 +113,4 @@ export const queryList: readonly Query[] = [
|
|||||||
createNearbyBlocksQuery(),
|
createNearbyBlocksQuery(),
|
||||||
createCraftableQuery(),
|
createCraftableQuery(),
|
||||||
createEntitiesQuery(),
|
createEntitiesQuery(),
|
||||||
createModesQuery(),
|
|
||||||
createSavedPlacesQuery(),
|
|
||||||
] as const
|
] as const
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ import type { BotContext, ComponentLifecycle } from '../composables/bot'
|
|||||||
import { useLogg } from '@guiiai/logg'
|
import { useLogg } from '@guiiai/logg'
|
||||||
import { registerCommand } from '../composables/command'
|
import { registerCommand } from '../composables/command'
|
||||||
|
|
||||||
|
export function getStatusToString(ctx: BotContext): string {
|
||||||
|
return Array.from(getStatus(ctx).entries()).map(([key, value]) => `${key}: ${value}`).join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
export function getStatus(ctx: BotContext): Map<string, string> {
|
export function getStatus(ctx: BotContext): Map<string, string> {
|
||||||
const status = new Map<string, string>()
|
const status = new Map<string, string>()
|
||||||
const pos = ctx.bot.entity.position
|
const pos = ctx.bot.entity.position
|
||||||
|
|||||||
@@ -1,305 +1,174 @@
|
|||||||
|
import type { Bot } from 'mineflayer'
|
||||||
|
import type { Block } from 'prismarine-block'
|
||||||
|
import type { Entity } from 'prismarine-entity'
|
||||||
|
import type { Item } from 'prismarine-item'
|
||||||
|
import type { Vec3 } from 'vec3'
|
||||||
|
import type { BotContext } from './bot'
|
||||||
import pf from 'mineflayer-pathfinder'
|
import pf from 'mineflayer-pathfinder'
|
||||||
|
|
||||||
import * as mc from '../utils/mcdata'
|
import * as mc from '../utils/mcdata'
|
||||||
|
|
||||||
export function getNearestFreeSpace(bot, size = 1, distance = 8) {
|
interface WorldContext {
|
||||||
/**
|
bot: Bot
|
||||||
* Get the nearest empty space with solid blocks beneath it of the given size.
|
botCtx: BotContext
|
||||||
* @param {Bot} bot - The bot to get the nearest free space for.
|
}
|
||||||
* @param {number} size - The (size x size) of the space to find, default 1.
|
|
||||||
* @param {number} distance - The maximum distance to search, default 8.
|
export function getNearestFreeSpace(ctx: WorldContext, size: number = 1, distance: number = 8): Vec3 | undefined {
|
||||||
* @returns {Vec3} - The south west corner position of the nearest free space.
|
const emptyPositions = ctx.bot.findBlocks({
|
||||||
* @example
|
matching: (block: Block) => block?.name === 'air',
|
||||||
* let position = world.getNearestFreeSpace(bot, 1, 8);
|
|
||||||
*/
|
|
||||||
const empty_pos = bot.findBlocks({
|
|
||||||
matching: (block) => {
|
|
||||||
return block && block.name == 'air'
|
|
||||||
},
|
|
||||||
maxDistance: distance,
|
maxDistance: distance,
|
||||||
count: 1000,
|
count: 1000,
|
||||||
})
|
})
|
||||||
for (let i = 0; i < empty_pos.length; i++) {
|
|
||||||
let empty = true
|
return emptyPositions.find((pos) => {
|
||||||
for (let x = 0; x < size; x++) {
|
for (let x = 0; x < size; x++) {
|
||||||
for (let z = 0; z < size; z++) {
|
for (let z = 0; z < size; z++) {
|
||||||
const top = bot.blockAt(empty_pos[i].offset(x, 0, z))
|
const top = ctx.bot.blockAt(pos.offset(x, 0, z))
|
||||||
const bottom = bot.blockAt(empty_pos[i].offset(x, -1, z))
|
const bottom = ctx.bot.blockAt(pos.offset(x, -1, z))
|
||||||
if (!top || !top.name == 'air' || !bottom || bottom.drops.length == 0 || !bottom.diggable) {
|
if (!top || top.name !== 'air' || !bottom?.drops?.length || !bottom.diggable) {
|
||||||
empty = false
|
return false
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!empty)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
if (empty) {
|
return true
|
||||||
return empty_pos[i]
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNearestBlocks(bot, block_types = null, distance = 16, count = 10000) {
|
export function getNearestBlocks(ctx: WorldContext, blockTypes: string[] | string | null = null, distance: number = 16, count: number = 10000): Block[] {
|
||||||
/**
|
const blockIds = blockTypes === null
|
||||||
* Get a list of the nearest blocks of the given types.
|
? mc.getAllBlockIds(['air'])
|
||||||
* @param {Bot} bot - The bot to get the nearest block for.
|
: (Array.isArray(blockTypes) ? blockTypes : [blockTypes]).map(mc.getBlockId)
|
||||||
* @param {string[]} block_types - The names of the blocks to search for.
|
|
||||||
* @param {number} distance - The maximum distance to search, default 16.
|
|
||||||
* @param {number} count - The maximum number of blocks to find, default 10000.
|
|
||||||
* @returns {Block[]} - The nearest blocks of the given type.
|
|
||||||
* @example
|
|
||||||
* let woodBlocks = world.getNearestBlocks(bot, ['oak_log', 'birch_log'], 16, 1);
|
|
||||||
*/
|
|
||||||
// if blocktypes is not a list, make it a list
|
|
||||||
let block_ids = []
|
|
||||||
if (block_types === null) {
|
|
||||||
block_ids = mc.getAllBlockIds(['air'])
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!Array.isArray(block_types))
|
|
||||||
block_types = [block_types]
|
|
||||||
for (const block_type of block_types) {
|
|
||||||
block_ids.push(mc.getBlockId(block_type))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const positions = bot.findBlocks({ matching: block_ids, maxDistance: distance, count })
|
const positions = ctx.bot.findBlocks({ matching: blockIds, maxDistance: distance, count })
|
||||||
const blocks = []
|
|
||||||
for (let i = 0; i < positions.length; i++) {
|
|
||||||
const block = bot.blockAt(positions[i])
|
|
||||||
const distance = positions[i].distanceTo(bot.entity.position)
|
|
||||||
blocks.push({ block, distance })
|
|
||||||
}
|
|
||||||
blocks.sort((a, b) => a.distance - b.distance)
|
|
||||||
|
|
||||||
const res = []
|
return positions
|
||||||
for (let i = 0; i < blocks.length; i++) {
|
.map((pos) => {
|
||||||
res.push(blocks[i].block)
|
const block = ctx.bot.blockAt(pos)
|
||||||
}
|
const dist = pos.distanceTo(ctx.bot.entity.position)
|
||||||
return res
|
return block ? { block, distance: dist } : null
|
||||||
|
})
|
||||||
|
.filter((item): item is { block: Block, distance: number } => item !== null)
|
||||||
|
.sort((a, b) => a.distance - b.distance)
|
||||||
|
.map(item => item.block)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNearestBlock(bot, block_type, distance = 16) {
|
export function getNearestBlock(ctx: WorldContext, blockType: string, distance: number = 16): Block | null {
|
||||||
/**
|
const blocks = getNearestBlocks(ctx, blockType, distance, 1)
|
||||||
* Get the nearest block of the given type.
|
return blocks[0] || null
|
||||||
* @param {Bot} bot - The bot to get the nearest block for.
|
|
||||||
* @param {string} block_type - The name of the block to search for.
|
|
||||||
* @param {number} distance - The maximum distance to search, default 16.
|
|
||||||
* @returns {Block} - The nearest block of the given type.
|
|
||||||
* @example
|
|
||||||
* let coalBlock = world.getNearestBlock(bot, 'coal_ore', 16);
|
|
||||||
*/
|
|
||||||
const blocks = getNearestBlocks(bot, block_type, distance, 1)
|
|
||||||
if (blocks.length > 0) {
|
|
||||||
return blocks[0]
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNearbyEntities(bot, maxDistance = 16) {
|
export function getNearbyEntities(ctx: WorldContext, maxDistance: number = 16): Entity[] {
|
||||||
const entities = []
|
return Object.values(ctx.bot.entities)
|
||||||
for (const entity of Object.values(bot.entities)) {
|
.filter((entity): entity is Entity =>
|
||||||
const distance = entity.position.distanceTo(bot.entity.position)
|
entity !== null
|
||||||
if (distance > maxDistance)
|
&& entity.position.distanceTo(ctx.bot.entity.position) <= maxDistance,
|
||||||
continue
|
)
|
||||||
entities.push({ entity, distance })
|
.sort((a, b) =>
|
||||||
}
|
a.position.distanceTo(ctx.bot.entity.position)
|
||||||
entities.sort((a, b) => a.distance - b.distance)
|
- b.position.distanceTo(ctx.bot.entity.position),
|
||||||
const res = []
|
)
|
||||||
for (let i = 0; i < entities.length; i++) {
|
|
||||||
res.push(entities[i].entity)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNearestEntityWhere(bot, predicate, maxDistance = 16) {
|
export function getNearestEntityWhere(ctx: WorldContext, predicate: (entity: Entity) => boolean, maxDistance: number = 16): Entity | null {
|
||||||
return bot.nearestEntity(entity => predicate(entity) && bot.entity.position.distanceTo(entity.position) < maxDistance)
|
return ctx.bot.nearestEntity(entity =>
|
||||||
|
predicate(entity)
|
||||||
|
&& ctx.bot.entity.position.distanceTo(entity.position) < maxDistance,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNearbyPlayers(bot, maxDistance) {
|
export function getNearbyPlayers(ctx: WorldContext, maxDistance: number = 16): Entity[] {
|
||||||
if (maxDistance == null)
|
return getNearbyEntities(ctx, maxDistance)
|
||||||
maxDistance = 16
|
.filter(entity =>
|
||||||
const players = []
|
entity.type === 'player'
|
||||||
for (const entity of Object.values(bot.entities)) {
|
&& entity.username !== ctx.bot.username,
|
||||||
const distance = entity.position.distanceTo(bot.entity.position)
|
)
|
||||||
if (distance > maxDistance)
|
|
||||||
continue
|
|
||||||
if (entity.type == 'player' && entity.username != bot.username) {
|
|
||||||
players.push({ entity, distance })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
players.sort((a, b) => a.distance - b.distance)
|
|
||||||
const res = []
|
|
||||||
for (let i = 0; i < players.length; i++) {
|
|
||||||
res.push(players[i].entity)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getInventoryStacks(bot) {
|
export function getInventoryStacks(ctx: WorldContext): Item[] {
|
||||||
const inventory = []
|
return ctx.bot.inventory.items().filter((item): item is Item => item !== null)
|
||||||
for (const item of bot.inventory.items()) {
|
|
||||||
if (item != null) {
|
|
||||||
inventory.push(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return inventory
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getInventoryCounts(bot) {
|
export function getInventoryCounts(ctx: WorldContext): Record<string, number> {
|
||||||
/**
|
return getInventoryStacks(ctx).reduce((counts, item) => {
|
||||||
* Get an object representing the bot's inventory.
|
counts[item.name] = (counts[item.name] || 0) + item.count
|
||||||
* @param {Bot} bot - The bot to get the inventory for.
|
return counts
|
||||||
* @returns {object} - An object with item names as keys and counts as values.
|
}, {} as Record<string, number>)
|
||||||
* @example
|
|
||||||
* let inventory = world.getInventoryCounts(bot);
|
|
||||||
* let oakLogCount = inventory['oak_log'];
|
|
||||||
* let hasWoodenPickaxe = inventory['wooden_pickaxe'] > 0;
|
|
||||||
*/
|
|
||||||
const inventory = {}
|
|
||||||
for (const item of bot.inventory.items()) {
|
|
||||||
if (item != null) {
|
|
||||||
if (inventory[item.name] == null) {
|
|
||||||
inventory[item.name] = 0
|
|
||||||
}
|
|
||||||
inventory[item.name] += item.count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return inventory
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCraftableItems(bot) {
|
export function getCraftableItems(ctx: WorldContext): string[] {
|
||||||
/**
|
const table = getNearestBlock(ctx, 'crafting_table')
|
||||||
* Get a list of all items that can be crafted with the bot's current inventory.
|
|| getInventoryStacks(ctx).find(item => item.name === 'crafting_table')
|
||||||
* @param {Bot} bot - The bot to get the craftable items for.
|
|
||||||
* @returns {string[]} - A list of all items that can be crafted.
|
return mc.getAllItems()
|
||||||
* @example
|
.filter(item => ctx.bot.recipesFor(item.id, null, 1, table).length > 0)
|
||||||
* let craftableItems = world.getCraftableItems(bot);
|
.map(item => item.name)
|
||||||
*/
|
|
||||||
let table = getNearestBlock(bot, 'crafting_table')
|
|
||||||
if (!table) {
|
|
||||||
for (const item of bot.inventory.items()) {
|
|
||||||
if (item != null && item.name === 'crafting_table') {
|
|
||||||
table = item
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const res = []
|
|
||||||
for (const item of mc.getAllItems()) {
|
|
||||||
const recipes = bot.recipesFor(item.id, null, 1, table)
|
|
||||||
if (recipes.length > 0)
|
|
||||||
res.push(item.name)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPosition(bot) {
|
export function getPosition(ctx: WorldContext): Vec3 {
|
||||||
/**
|
return ctx.bot.entity.position
|
||||||
* Get your position in the world (Note that y is vertical).
|
|
||||||
* @param {Bot} bot - The bot to get the position for.
|
|
||||||
* @returns {Vec3} - An object with x, y, and x attributes representing the position of the bot.
|
|
||||||
* @example
|
|
||||||
* let position = world.getPosition(bot);
|
|
||||||
* let x = position.x;
|
|
||||||
*/
|
|
||||||
return bot.entity.position
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNearbyEntityTypes(bot) {
|
export function getNearbyEntityTypes(ctx: WorldContext): string[] {
|
||||||
/**
|
return [...new Set(
|
||||||
* Get a list of all nearby mob types.
|
getNearbyEntities(ctx, 16)
|
||||||
* @param {Bot} bot - The bot to get nearby mobs for.
|
.map(mob => mob.name)
|
||||||
* @returns {string[]} - A list of all nearby mobs.
|
.filter((name): name is string => name !== undefined),
|
||||||
* @example
|
)]
|
||||||
* let mobs = world.getNearbyEntityTypes(bot);
|
|
||||||
*/
|
|
||||||
const mobs = getNearbyEntities(bot, 16)
|
|
||||||
const found = []
|
|
||||||
for (let i = 0; i < mobs.length; i++) {
|
|
||||||
if (!found.includes(mobs[i].name)) {
|
|
||||||
found.push(mobs[i].name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNearbyPlayerNames(bot) {
|
export function getNearbyPlayerNames(ctx: WorldContext): string[] {
|
||||||
/**
|
return [...new Set(
|
||||||
* Get a list of all nearby player names.
|
getNearbyPlayers(ctx, 64)
|
||||||
* @param {Bot} bot - The bot to get nearby players for.
|
.map(player => player.username)
|
||||||
* @returns {string[]} - A list of all nearby players.
|
.filter((name): name is string =>
|
||||||
* @example
|
name !== undefined
|
||||||
* let players = world.getNearbyPlayerNames(bot);
|
&& name !== ctx.bot.username,
|
||||||
*/
|
),
|
||||||
const players = getNearbyPlayers(bot, 64)
|
)]
|
||||||
const found = []
|
|
||||||
for (let i = 0; i < players.length; i++) {
|
|
||||||
if (!found.includes(players[i].username) && players[i].username != bot.username) {
|
|
||||||
found.push(players[i].username)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNearbyBlockTypes(bot, distance = 16) {
|
export function getNearbyBlockTypes(ctx: WorldContext, distance: number = 16): string[] {
|
||||||
/**
|
return [...new Set(
|
||||||
* Get a list of all nearby block names.
|
getNearestBlocks(ctx, null, distance)
|
||||||
* @param {Bot} bot - The bot to get nearby blocks for.
|
.map(block => block.name),
|
||||||
* @param {number} distance - The maximum distance to search, default 16.
|
)]
|
||||||
* @returns {string[]} - A list of all nearby blocks.
|
|
||||||
* @example
|
|
||||||
* let blocks = world.getNearbyBlockTypes(bot);
|
|
||||||
*/
|
|
||||||
const blocks = getNearestBlocks(bot, null, distance)
|
|
||||||
const found = []
|
|
||||||
for (let i = 0; i < blocks.length; i++) {
|
|
||||||
if (!found.includes(blocks[i].name)) {
|
|
||||||
found.push(blocks[i].name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isClearPath(bot, target) {
|
export async function isClearPath(ctx: WorldContext, target: Entity): Promise<boolean> {
|
||||||
/**
|
const movements = new pf.Movements(ctx.bot)
|
||||||
* Check if there is a path to the target that requires no digging or placing blocks.
|
|
||||||
* @param {Bot} bot - The bot to get the path for.
|
|
||||||
* @param {Entity} target - The target to path to.
|
|
||||||
* @returns {boolean} - True if there is a clear path, false otherwise.
|
|
||||||
*/
|
|
||||||
const movements = new pf.Movements(bot)
|
|
||||||
movements.canDig = false
|
movements.canDig = false
|
||||||
movements.canPlaceOn = false
|
movements.canPlaceOn = false
|
||||||
const goal = new pf.goals.GoalNear(target.position.x, target.position.y, target.position.z, 1)
|
|
||||||
const path = await bot.pathfinder.getPathTo(movements, goal, 100)
|
const goal = new pf.goals.GoalNear(
|
||||||
|
target.position.x,
|
||||||
|
target.position.y,
|
||||||
|
target.position.z,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
|
||||||
|
const path = await ctx.bot.pathfinder.getPathTo(movements, goal, 100)
|
||||||
return path.status === 'success'
|
return path.status === 'success'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shouldPlaceTorch(bot) {
|
export function shouldPlaceTorch(ctx: WorldContext): boolean {
|
||||||
if (!bot.modes.isOn('torch_placing') || bot.interrupt_code)
|
// if (!ctx.bot.modes.isOn('torch_placing') || ctx.bot.interrupt_code) {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
|
||||||
|
const pos = getPosition(ctx)
|
||||||
|
const nearestTorch = getNearestBlock(ctx, 'torch', 6)
|
||||||
|
|| getNearestBlock(ctx, 'wall_torch', 6)
|
||||||
|
|
||||||
|
if (nearestTorch) {
|
||||||
return false
|
return false
|
||||||
const pos = getPosition(bot)
|
|
||||||
// TODO: check light level instead of nearby torches, block.light is broken
|
|
||||||
let nearest_torch = getNearestBlock(bot, 'torch', 6)
|
|
||||||
if (!nearest_torch)
|
|
||||||
nearest_torch = getNearestBlock(bot, 'wall_torch', 6)
|
|
||||||
if (!nearest_torch) {
|
|
||||||
const block = bot.blockAt(pos)
|
|
||||||
const has_torch = bot.inventory.items().find(item => item.name === 'torch')
|
|
||||||
return has_torch && block?.name === 'air'
|
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
|
const block = ctx.bot.blockAt(pos)
|
||||||
|
const hasTorch = ctx.bot.inventory.items().some(item => item?.name === 'torch')
|
||||||
|
|
||||||
|
return Boolean(hasTorch && block?.name === 'air')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBiomeName(bot) {
|
export function getBiomeName(ctx: WorldContext): string {
|
||||||
/**
|
const biomeId = ctx.bot.world.getBiome(ctx.bot.entity.position)
|
||||||
* Get the name of the biome the bot is in.
|
|
||||||
* @param {Bot} bot - The bot to get the biome for.
|
|
||||||
* @returns {string} - The name of the biome.
|
|
||||||
* @example
|
|
||||||
* let biome = world.getBiomeName(bot);
|
|
||||||
*/
|
|
||||||
const biomeId = bot.world.getBiome(bot.entity.position)
|
|
||||||
return mc.getAllBiomes()[biomeId].name
|
return mc.getAllBiomes()[biomeId].name
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ async function main() {
|
|||||||
registerComponent('command', createCommandComponent)
|
registerComponent('command', createCommandComponent)
|
||||||
})
|
})
|
||||||
|
|
||||||
initAgent()
|
initAgent(ctx)
|
||||||
|
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', () => {
|
||||||
cleanup()
|
cleanup()
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { BotContext } from '../composables/bot'
|
import type { BotContext } from '../composables/bot'
|
||||||
|
import { getStatusToString } from '../components/status'
|
||||||
|
|
||||||
export function basicSystemPrompt(botName: string): string {
|
export function basicSystemPrompt(botName: string): string {
|
||||||
return `You are a playful Minecraft bot named ${botName} that can converse with players, see, move,
|
return `You are a playful Minecraft bot named ${botName} that can converse with players, see, move,
|
||||||
@@ -40,13 +41,7 @@ task to do in Minecraft. My ultimate goal is to discover as many things as possi
|
|||||||
accomplish as many tasks as possible and become the best Minecraft player in the world.
|
accomplish as many tasks as possible and become the best Minecraft player in the world.
|
||||||
|
|
||||||
I will give you the following information:
|
I will give you the following information:
|
||||||
${Array.from(ctx.status.entries()).map(([key, value]) => `${key}: ${value}`).join('\n')}
|
${getStatusToString(ctx)}
|
||||||
|
|
||||||
Then you can choose some of the tools to use. Use the valid JS call function to call the tool.
|
|
||||||
|
|
||||||
## For example:
|
|
||||||
### Get the stats
|
|
||||||
stats()
|
|
||||||
`
|
`
|
||||||
|
|
||||||
return prompt
|
return prompt
|
||||||
|
|||||||
Reference in New Issue
Block a user