Bot¶
Inherits from: Player
Represents a bot: a player without a client, controlled by spells, inheriting from Player and
adding bot-specific attributes and actions. For the server, the other players and all mods, a
bot is a player like any other, so everything a Player offers works for a bot too. Use bots to
play-test a mini-game, or as players a spell controls.
Acting like a player
A bot acts as a player does at the keyboard: it holds keys down, looks around, and clicks. Each
action reaches the server exactly as a player's would, so the server checks it the same way
(reach, attack strength, cooldowns) and fires the same events, like PlayerAttackEntityEvent
or PlayerUseBlockEvent.
An action happens right away, but some take time to show their effect: a block breaks after
the bot has dug at it long enough, and a step forward takes a tick. Use sleep to wait.
Keys stay down until they are released. A bot whose spell has ended keeps walking if it walked
before, so set input to nil before letting go of a bot.
Make a bot sprint or sneak with input as well, not with the fields sprinting and sneaking
it has as an entity: its keys decide, as a player's do. It stops sprinting on the next tick
unless it holds the sprint key and walks forward, and sneaks only until its keys change.
Tasks
A task lets the bot pursue a goal on its own, the way a player would: startPathTo makes it
walk to a place, finding the way and steering around obstacles with its keys, startFollow
makes it follow an entity, startFleeFrom makes it run away from one, startAttack makes it
fight one, startBreakBlock makes it break a block, startPlaceBlock makes it place one,
startPutInto and startTakeFrom make it put items into a container or take them out, and
startStay keeps it where it is. findPath only searches the way to a place and returns it as
a BotPath to look at, and startPathAlong walks the bot along such a path. Tasks work like
those of a MobEntity: busy tells whether a task is running, task describes the task the
bot executes or executed last, awaitTask waits until it has ended, and stopTask stops it.
While a task runs, it holds the bot's movement keys and turns it the way it walks. Assigning
input stops the task, so a spell can take over at any time. Other actions, like attack or
lookAt, leave the task running. When a task that walks ends, or waits, it lets go of the keys,
but as a player does, a bot on a ladder in the air keeps holding on by sneaking, rather than
sliding down, and a bot in water too deep to stand in holds the jump key, rather than sinking.
A bot's life
A bot stays online until it leaves, is kicked, or the server stops, whichever spell spawned it.
Any spell can take control of it, for example after finding it with
spell:findEntities("@a[name=Bob]"). Mobs attack a bot, and it gets hungry; a dead bot stays
dead until respawn is called, unless the game rule immediate_respawn is on: then it
respawns right away, as a player does who sees no death screen. Nothing of a bot is saved: after
a restart it's gone.
A reference to a bot follows it when it respawns, as a reference to any player does.
A bot that has left is gone for good: Bot:spawn with the same name makes a new bot, and an old
reference does not follow it there. The old reference still tells how the bot was when it left,
and its online is false. Any action on it, like useOnBlock, chat, teleport, assigning
input or starting a task, raises an error; only leave, stopTask and awaitTask are
harmless.
What a bot sees and hears
A bot has no screen and no speakers. What a player would be shown there, a spell can collect as
events: BotMessageEvent for chat, whispers, system messages, titles, subtitles and the text
above the hotbar, and BotSoundEvent for sounds. So a spell that play-tests a mini-game can
check that a player gets the title at the start, and a bot can answer what it is told.
Example
local bot = Bot:spawn("Bob", spell.pos)
bot:lookAt(spell.pos + Vec3(10, 1.6, 0))
bot.input = { forward = true, sprint = true }
sleep(40)
bot.input = nil
bot:chat("I'm here!")
Fields¶
busy: boolean — Read-Only¶
Indicates whether the bot is currently executing a task.
digging: boolean — Read-Only¶
Indicates whether the bot is still digging at the block it started to dig with dig.
input: BotInput — Read/Write¶
A copy of the movement keys the bot holds down. Assign a table with the keys to hold down, which releases all others, or nil to release all keys. Assigning it stops a running task.
menu: BotMenu — Read-Only¶
A copy of what describes the menu the bot has open.
task: BotTask? — Read-Only¶
A copy of the task the bot is executing, or of the last task it executed. nil if it has not executed one yet.
Methods¶
spawn(name: string, pos: Vec3, options?: {skin?:Skin|false}) → bot: Bot¶
Spawns a new bot, which joins the server like a player who logs in.
A bot can't have the name of a player who is online, be it a real player or another bot: the function then raises an error. Upper and lower case make no difference, so "bob" is refused while "Bob" is online. Once that player has left, the name is free. The other way round, a real player who logs in takes their name from a bot: the bot of that name leaves the server.
A bot named like a real player wears that player's skin. The skin is looked up in the background, so the bot appears with a default skin and changes to the other one when it arrives, usually within a second. If no player of that name exists, or the server has no internet access, the bot keeps the default skin.
With the option skin, the bot wears that Skin from the moment it appears, without a lookup,
or, with false, a default skin. Assigning Player.skin changes the skin later.
Example
A bot that looks like another player from the moment it appears:
local skin = Skin:load("skin/guard.json")
local bot = Bot:spawn("Guard", spell.pos, { skin = skin })
Parameters
- name: string — The player name of the bot: 1 to 16 characters, without spaces. No other player of that name may be online.
- pos: Vec3 — The position where the bot appears, in the spell's world.
- options?: {skin?:Skin|false} —
skinis the skin the bot wears, orfalsefor a default skin; defaults to the skin of the player named like the bot, looked up in the background.
Returns
- bot: Bot — A reference to the new bot.
attack(target: Entity)¶
Attacks the entity, like a left click at it. The bot turns to the middle of the entity first, since a player can only hit what it looks at. The server only lets the hit land if the entity is within reach, and the damage depends on how far the weapon has recharged.
Example
local pig = spell:summon("pig") --[[@as MobEntity]]
while pig.alive do
bot:attack(pig)
sleep(15) -- a sword recharges in about 13 ticks
end
Parameters
- target: Entity — The entity to attack. Items, experience orbs and the bot itself can't be attacked.
awaitTask(timeout?: number) → ok: boolean, reason: TaskReason|"timeout"|nil¶
Waits until the task the bot is executing has ended, and tells how it ended.
Returns true if the task finished, and false and the reason if it failed or was interrupted,
the same way a start... function reports a task that could not start. It waits for the task
that runs when it is called: if something replaces that task, for example another spell, the
wait ends with false and "replaced". A task that has already ended gives its result at once,
and a bot that never had a task gives true.
With a timeout, the wait ends after that many ticks with false and "timeout", but the task
itself keeps running. A timeout of 0 only looks.
Parameters
- timeout?: number — The maximum number of ticks to wait, an integer. Defaults to no limit.
Returns
- ok: boolean —
trueif the task finished. - reason: TaskReason|"timeout"|nil — Why the task did not finish, or "timeout" if the wait ended first.
chat(message: string)¶
Sends a chat message, like a player typing it. Chat listeners get it as a ChatMessageEvent.
Parameters
- message: string — The message.
clickSlot(slot: number, button?: number, mode?: ClickMode)¶
Clicks a slot of the menu the bot has open, like a click in an inventory or container screen. The bot's own inventory is open whenever no other menu is.
Example
-- Moves the items of a chest's first slot into the bot's inventory.
bot:useOnBlock(chestPos)
sleep(1)
bot:clickSlot(0, 0, "quick_move")
bot:closeMenu()
Parameters
- slot: number — The index of the menu slot, from 0 to
menu.size - 1, or -999 for outside the window, which drops what the cursor holds. - button?: number — The mouse button or key, depending on the mode; see
ClickMode. Defaults to 0. - mode?: ClickMode — How to click. Defaults to "pickup".
closeMenu()¶
Closes the menu the bot has open, like pressing Escape.
dig(pos: Vec3, side?: Side)¶
Digs at the block, like holding the left mouse button down on it. The bot turns to the middle
of the side first, since a player can only hit what it looks at. It keeps digging until the
block breaks or stopDigging is called; digging tells whether it still digs.
The bot digs from where it stands, so the block must be within its reach. To walk to a block
and break it, use the task startBreakBlock. Like a player, the bot doesn't dig at all in
adventure mode (unless its tool may break the block) or beyond the world border.
Example
Parameters
- pos: Vec3 — The position of the block.
- side?: Side — The side of the block to hit. Defaults to the side facing the bot's eyes.
dropSelectedItem(wholeStack?: boolean)¶
Drops the selected item, like pressing Q.
Parameters
- wholeStack?: boolean — Whether to drop the whole stack, like Ctrl+Q. Defaults to
false.
execute(command: string, trace?: Trace) → result: number¶
Runs a command as the bot, like a player typing it with a leading "/", and waits until it has
finished. The bot needs the permission for it, like any player, and selectors like @s and
relative coordinates refer to the bot.
The command's output goes to the bot's chat, where nobody sees it. To inspect it, pass a
Trace object: it collects the messages the bot receives while the command runs, error messages
included. A spell the command casts, like /lua ..., keeps adding its output to the trace until
it ends.
The return value is the command result value as defined by the Minecraft command system:
usually the number of entities or blocks affected by the command, and 0 if it failed. A command
that runs once per entity, like execute as @a run ..., returns the sum of its results.
On a server with online-mode and enforce-secure-profile, commands that send a chat
message, like /say, /msg and /me, need a signature that a bot can't give. They don't run;
the bot gets an error message instead.
Example
Example with Trace
local trace = Trace:new()
local result = bot:execute("clear @s minecraft:dirt", trace)
print("Removed " .. result .. " dirt")
print(trace.content)
Parameters
- command: string — The command, with or without the leading "/".
- trace?: Trace — A
Traceobject to collect the command's output.
Returns
- result: number — The command result value, usually the number of entities or blocks affected.
findPath(target: Vec3|Vec3[ ], options?: BotFindPathOptions) → path: BotPath?¶
Searches for the shortest path from where the bot is to the given position, and returns it without walking: the bot stays where it is, and its task, if it has one, goes on.
The path is the one startPathTo would start on with the same options, and it fits the bot as
it is when the path is searched: its size, its jump, its hunger, its breath. So a spell can look
before the bot goes: how long the way is, whether it gets there at all, and where it leads.
startPathAlong then walks the bot along it.
A search on foot takes no time. With jumps, the jumps are worked out in the background where
the way on foot doesn't reach the target or takes a detour, and this function waits meanwhile,
usually less than a second, as sleep does.
Given a list of positions, the path leads to each of them in order, and ends at the last: a
route of the spell's own choice, like a patrol, or a way around a place the bot should avoid.
The positions only say where the path comes by. The way from each to the next is searched like
any path, with the same options, so it leads around walls, up ladders, and, with jumps,
across gaps, and the bot walks all of it in one go. targetRadius tells how close the path
comes to each position; with 0 it leads through their blocks. If a position can't be reached,
the path ends there, short of it, and reachable is false.
Example: The nearer of two chests
local a, b = bot:findPath(chestA), bot:findPath(chestB)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
bot:startPathAlong(path, { sprint = true })
bot:awaitTask()
end
Example: A patrol around a square
local corner = bot.pos
local route = { corner + Vec3(8, 0, 0), corner + Vec3(8, 0, 8), corner + Vec3(0, 0, 8), corner }
while true do
local path = bot:findPath(route, { targetRadius = 0 })
if not (path and path.reachable and bot:startPathAlong(path) and bot:awaitTask()) then
break
end
end
Parameters
- target: Vec3|Vec3[ ] — The position to search a path to, or a list of positions to come by in order, the last being the target.
- options?: BotFindPathOptions — The options that tell where a path may lead, as for
startPathTo.
Returns
- path: BotPath? — The path, or
nilif there is none at all. A path that stops short of the target hasreachableset tofalse.
getMenuItem(slot: number) → item: Item?¶
Returns the item in a slot of the menu the bot has open.
Parameters
- slot: number — The index of the menu slot, from 0 to
menu.size - 1.
Returns
- item: Item? — A copy of the item, or
nilif the slot is empty.
leave()¶
Lets the bot leave the server, like a player who logs out. Calling it on a bot that has left already does nothing.
respawn() → ok: boolean¶
Respawns the dead bot, like a player who clicks "Respawn" on the death screen. With the game
rule immediate_respawn on, a bot respawns by itself.
Example
local queue = spell:collect("AfterLivingEntityDeathEvent")
while true do
local evt = queue:next() --[[@as AfterLivingEntityDeathEvent]]
if evt.entity == bot then
sleep(20)
bot:respawn()
end
end
Returns
- ok: boolean —
trueif the bot respawned,falseif it was alive.
selectSlot(slot: number)¶
Selects a hotbar slot. Its item goes into the main hand.
Parameters
- slot: number — The hotbar slot, from 0 to 8.
startAttack(target: LivingEntity, options?: BotAttackOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that makes the bot run to the given entity and attack it with the item in its main hand: it hits, or, with a bow or crossbow, shoots.
The bot fights like a player who clicks at the entity, so its hits are the ones attack makes:
the damage depends on the weapon, its enchantments and how far it has recharged, and each hit
fires PlayerAttackEntityEvent. The bot hits only when the entity is within its attack range
and nothing blocks its view; otherwise it runs closer, the way startPathTo walks.
By default the bot waits before each hit until its weapon has recharged and the entity has
stopped flashing red from the last hit, as a good player does, so that each hit deals full
damage. A cooldown makes it hit at that fixed interval instead; quick hits are weak.
With a bow or a crossbow in the main hand and arrows to shoot (or fireworks, for a crossbow),
the bot shoots instead, like a player using the item: each shot uses up an arrow, and each draw
or load fires PlayerUseItemEvent. It shoots from up to distance blocks away, aims high
enough for the arrow to drop onto the entity and ahead of an entity that moves, and shoots only
when nothing is in the arrow's way; otherwise it runs closer. It draws a bow fully before each
shot, or for cooldown ticks (at least 3); it loads a crossbow right after each shot, and waits
cooldown ticks between shots. It does not back away from an entity that comes close. Out of
arrows, it hits with the bow or crossbow. A trident is a melee weapon in this task: the bot
doesn't throw it.
The task finishes when the entity is dead. It fails with "invalid_target" when the entity is
removed, and with "other_dimension" when it changes world. It does not end by itself if the bot
can't reach the entity: then the bot gets as close as it can and waits there, and
task.reachable is false. A spell that doesn't want to wait can stop the task then.
While it is on its way and needs a new path, the bot searches one every searchInterval ticks:
also while it waits where it can't get on, since the world may change. With searchInterval
set to false it searches only once, and stays at the end of that path; it still hits an
entity that comes within reach.
With watchTarget, the bot keeps looking at an entity or at a position while it runs closer, as for
startPathTo, and with chase set to false also while it waits for the entity to come near.
To hit or to shoot it turns to the entity it attacks, as a player must. watchTarget may be that
entity itself: then the bot faces it all the way, for example behind a shield, which only
covers what is in front of the bot. When the entity to look at dies, is removed or changes
world, the bot looks where it goes again.
While this task runs, task also carries target, mode ("melee" or "ranged", depending on
what the bot holds), cooldown (if given), distance, chase, searchRange,
searchInterval, sprint, ladders, doors, closeDoors, maxFall, jumps, watchTarget,
reachable, which is false when the last path the bot got stops short of the entity,
searching, as for startPathTo, hits, the number of hits and arrows that hurt the entity
so far, and shots, the number of arrows the bot fired.
Example: Giving up on an entity the bot can't get to
bot:startAttack(enemy)
local outOfReach = 0
while bot.busy and outOfReach < 40 do
local task = bot.task
local stuck = task.reachable == false and not task.searching
outOfReach = stuck and outOfReach + 1 or 0
sleep(1)
end
bot:stopTask()
Example
bot.mainHandItem = Item:new("iron_sword")
local zombie = spell:summon("zombie") --[[@as MobEntity]]
local ok, reason = bot:startAttack(zombie, { sprint = true })
if ok then
ok, reason = bot:awaitTask(20 * 30)
end
print(ok and "Won after " .. bot.task.hits .. " hits." or "Did not win: " .. reason)
Example: An archer
bot.mainHandItem = Item:new("bow")
bot.inventory.slots[9] = Item:new("arrow", { count = 32 })
local skeleton = spell:summon("skeleton") --[[@as MobEntity]]
bot:startAttack(skeleton, { distance = 20 })
bot:awaitTask()
print(bot.task.hits .. " of " .. bot.task.shots .. " arrows hit.")
Parameters
- target: LivingEntity — The entity to attack.
- options?: BotAttackOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started. - reason: TaskReason? — The reason the task did not start: "invalid_target" for the bot itself, a dead entity, or one the game protects from attacks, like a player in creative or spectator mode; "other_dimension" for an entity in another world.
startBreakBlock(pos: Vec3, options?: BotJumpOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that walks the bot to the given block and breaks it.
The bot walks the way startPathTo walks, until it sees the block within reach. There it stops,
turns its eyes to the block, at most 45 degrees per tick, and digs at it with what it holds in
its main hand, as dig does, until the block breaks. So the game decides about everything as
for any player: how long it takes, what drops, how much the tool wears out, and whether the
block may be broken at all. Each dig fires PlayerAttackBlockEvent, and the break fires
BeforePlayerBlockBreakEvent and AfterPlayerBlockBreakEvent. Select the right tool before,
with selectSlot.
The task finishes when the block is gone, also when something else removed it. It fails with "target_changed" if the block is replaced by a different one, with "unreachable" if the bot can't get to a place from where it sees the block, and with "not_allowed" if the block is still there after the bot dug it long enough, for example because a spell cancelled the break. Gravel and sand above the block fall into its place right after it breaks, so check the block again before moving on.
While this task runs, task also carries target, searchRange, sprint, ladders, doors,
closeDoors, maxFall and jumps, and progress, how far the bot has dug the block, from 0
to 1.
Example
bot.mainHandItem = Item:new("iron_pickaxe")
local ok, reason = bot:startBreakBlock(spell.pos + Vec3(5, 0, 0))
if ok then
ok, reason = bot:awaitTask()
end
print(ok and "Broken." or "The bot could not break it: " .. reason)
Parameters
- pos: Vec3 — The position of the block to break.
- options?: BotJumpOptions — The options that the walking tasks share.
Returns
- ok: boolean —
trueif the task started. - reason: TaskReason? — The reason the task did not start: "invalid_target" for air and fluids, "unbreakable" for blocks like bedrock, "not_allowed" for a block the bot may not break, like one in adventure mode, or one near the world spawn for a bot that is no operator, or "no_path".
startFleeFrom(entity: Entity, options?: BotFleeFromOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that makes the bot run away from the given entity.
The bot sprints, unless sprint is false, and goes in legs of about 10 blocks, each a path as
startPathTo searches it, but on foot only, never across gaps. A leg leads straight away from
the entity where that gets the bot away, else to one side, and never past the entity. The bot
starts on a new leg at the end of one, and when the entity has moved so that the leg no longer
leads away from it.
The task finishes as soon as the two are distance blocks apart, and also when the entity
dies, is removed or changes world. It fails with "unreachable" when there is nowhere to run,
for example in a closed room or at the end of a corridor.
With watchTarget, the bot keeps looking at an entity or a position while it runs, as for
startPathTo: looking at the entity it flees from, it backs away without turning its back.
A bot only sprints forward, so one that looks back is slower.
While this task runs, task also carries target, distance, searchRange, sprint,
sprintJumps, ladders, doors, closeDoors, maxFall and watchTarget.
Example
local bot = Bot:spawn("Buddy", spell.pos)
local enemy = spell.owner
bot:startFleeFrom(enemy, { distance = 15 })
if bot:awaitTask() then
print("Buddy is safe again.")
end
Example
Backing away from an enemy, without losing sight of it.
local bot = Bot:spawn("Buddy", spell.pos)
local enemy = spell.owner
bot:startFleeFrom(enemy, { watchTarget = enemy })
Parameters
- entity: Entity — The entity to run away from.
- options?: BotFleeFromOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started. - reason: TaskReason? — The reason the task did not start: "no_path" when there is nowhere to run, "invalid_target" for the bot itself or a dead entity, "other_dimension" for an entity in another world.
startFollow(target: Entity, options?: BotFollowOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that makes the bot follow the given entity, keeping a distance.
The bot walks after the entity the way startPathTo walks to a place, and searches a new path
when the entity has moved two blocks away from where the path leads. Once it is within
distance of the entity, it stops and faces it, until the entity moves away again. If the
entity can't be reached, the bot gets as close as it can and waits there; task.reachable is
false then.
While it is on its way and needs a new path, the bot searches one every searchInterval ticks:
also while it waits where it can't get on, since the world may change. With searchInterval
set to false it searches only once, and stays at the end of that path.
A bot with jumps that ends up at the same dead end again searches wider from there: the way
around may then be up to 8 times as long as searchRange, but it still stays within about
searchRange blocks of the bot. Where even the widest search finds no way on, the bot lets more
and more time pass between two searches, up to 10 seconds, until the entity moves.
The task runs until it is stopped or replaced. It fails with "invalid_target" when the entity dies or is removed, and with "other_dimension" when it changes world.
With watchTarget, the bot keeps looking at another entity or at a position, on its way as for
startPathTo, and also while it waits near the entity it follows. When that other entity dies,
is removed or changes world, the bot looks where it goes again, and faces the entity it follows.
While this task runs, task also carries target, distance, searchRange,
searchInterval, sprint, ladders, doors, closeDoors, maxFall, jumps, watchTarget,
reachable, which is false when the last path the bot got stops short of the entity, and
searching, as for startPathTo.
Example
local bot = Bot:spawn("Buddy", spell.pos)
bot:startFollow(spell.owner, { distance = 3, sprint = true })
Example: A bodyguard that keeps an eye on the enemy
local enemy = spell:findEntities("@e[type=zombie,sort=nearest,limit=1]")[1]
bot:startFollow(spell.owner, { distance = 3, watchTarget = enemy })
Parameters
- target: Entity — The entity to follow.
- options?: BotFollowOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started. - reason: TaskReason? — The reason the task did not start: "invalid_target" for the bot itself or a dead entity, "other_dimension" for an entity in another world.
startPathAlong(path: BotPath, options?: BotPathAlongOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that walks the bot along a path that findPath returned, the way startPathTo
walks: it jumps, climbs, swims and clicks doors where the path leads it.
The bot walks this path and no other. It doesn't search again: if it is held up, for example
by a block placed in its way, or misses a jump, the task fails with "unreachable", and the
spell decides what to do next. The task finishes when the bot stands at the end of the path,
also at the end of a path that stops short of its target, as path.reachable tells.
The bot must stand on the path to start: at its first node, or at any other, from where it walks the rest. So a path can be walked again, or by another bot of the same size and strength, as long as the blocks along it stay as they were.
Where the path may lead was decided when it was searched, so only the options that tell how
the bot walks are left. While this task runs, task carries the same fields as for
startPathTo, with the target and the search options of the path.
Example
local path = bot:findPath(spell.pos + Vec3(20, 0, 0))
if path and path.reachable and path.length < 40 then
local ok, reason = bot:startPathAlong(path)
if ok then
ok, reason = bot:awaitTask()
end
print(ok and "The bot got there." or "The bot did not get there: " .. reason)
end
Parameters
- path: BotPath — The path to walk along.
- options?: BotPathAlongOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started. - reason: TaskReason? — The reason the task did not start: "off_path" if the bot doesn't stand on the path, or "other_dimension" if the path was searched in another world.
startPathTo(target: Vec3, options?: BotPathToOptions) → ok: boolean, reason: TaskReason?¶
Searches for the shortest path to the given position and starts a task that walks the bot along
it. The bot steers with its keys like a player: it turns, walks forward, jumps up steps, drops
down ledges no higher than maxFall, and swims.
The path fits the bot as it is: its size, which the scale attribute changes, and how high it
gets up onto a block, by stepping up (step_height) or by jumping (jump_strength and Jump
Boost). A bot of half size crawls through a hole one block high; one that can't jump a block
high goes up by stairs and slabs only. Low blocks count as well: a bot that neither steps nor
jumps high enough goes around a carpet or a layer of snow.
It swims across water and under water, down to the bottom and through passages two blocks high.
Under water it sprints if it isn't too hungry to, which makes it swim fast where it looks, lying
flat, also through passages one block high. Since a player can only start sprinting with its
eyes under water, the bot dives before a longer stretch of water at least two blocks deep, comes
up again, and swims on fast at the surface. Without sprinting it swims up with the jump key and
down by sneaking. It goes under water only as far as its air lasts, with its oxygen_bonus
attribute (Respiration), coming up for air where it must; a path that stops short of the target
under water ends where the bot can still breathe. With water breathing, or in creative mode, any
distance. Water stops a fall, so the bot drops into water from any height, deeper than maxFall
too. A way through water costs more than one on foot, by the time swimming takes, so the bot
walks around a pool unless that is much longer.
It wades through powder snow, cobwebs and sweet berry bushes that are on solid ground, slowly, as a player does, and goes around them unless that is much longer: around a cobweb rather than around a bush, and around a bush rather than around snow. A while in the snow and it starts to freeze, and a bush hurts. It doesn't step onto snow or a cobweb it would sink into, and from within no way leads a block up, since nobody jumps that high in there: a bot in a pit of powder snow has no way out. In leather boots it walks on top of powder snow, as a player does: it steps into the side of a block of snow and up through it onto its top, rather than wade through, and a bot that follows or attacks somebody climbs up out of snow of any depth.
It climbs ladders and vines with the jump key and slides down them, unless ladders is
false. It gets on a ladder from the ground, from a ledge above its top, or, with jumps, by
jumping at it, and off at the top onto the block beside it, anywhere on the way with a drop
no deeper than maxFall, or, with jumps, by jumping off it across a gap.
It also climbs through a hatch: a trapdoor right above a ladder that faces the same way as the
ladder, which the game lets anyone climb like the ladder while it is open. Open, a hatch stands
upright at one side of its block; one at the bottom of its block lies flat when closed, so the
bot walks across it there as well: coming up, it climbs until it is above the hatch, closes it
under itself and walks on; going down, it walks onto the closed hatch, opens it and drops onto
the ladder.
Scaffolding is not climbed.
It opens wooden doors, fence gates and hatches on its way with a click, like a player, and
closes them behind itself; one it still stands in when the task ends stays open. Both clicks
fire PlayerUseBlockEvent. Like a player, it clicks only what it looks at: it turns its eyes to
a door ahead while walking on, and to put one back it stops and turns around to it, at most 45
degrees per tick; a path task ends only after that, while a task that is stopped or replaced
leaves them as they are. Trapdoors in a doorway it clicks into whatever state lets it through,
since an open trapdoor stands upright at one side of the doorway, and puts them back behind
itself; where there is no room to stand, it crouches through. With closeDoors set to false
it leaves what it clicked as it is. With doors set to false it clicks none of them: a closed
door blocks its way, an open one doesn't. Iron doors and iron trapdoors are never clicked.
maxFall lets the bot jump down from higher up, at the cost of fall damage, as for any player:
about one point of health per block beyond its safe_fall_distance attribute (3 for a player),
less with Feather Falling. The bot does not check whether it survives the fall.
With jumps set to true, the path may also lead across gaps, as in a jump-and-run: from a
block the bot stands on to another one further away, in any direction, as high up as it jumps
and as far down as maxFall, counted from the top of the jump, as the game counts the fall.
Slabs and closed trapdoors count as such blocks as well. It takes only the jumps it can make,
with a little room to spare, worked out from how the game moves a player, with the bot's
attributes and effects as they are when the path is searched: sprinting, a bot jumps a gap of
three blocks on the level, or of two blocks one block up; a bot too hungry to sprint, a gap of
two blocks. Like a good player, it times its run-up, jumps at the last moment, or earlier where
it would bump into the side of a block above, and brakes or speeds up in the air with its keys,
to come down near the middle of the block. It sprints for a jump even when sprint is false.
If it misses a block anyway, it searches a new path from where it came down, and gives up after
three missed jumps. A jump may also end on a ladder or vine that hangs in the air, out of reach
on foot: the bot grabs hold of it as soon as it is in the ladder's block, as a player does, and
climbs on. A jump may start from a ladder as well: the bot holds on in the middle of the ladder,
turns away from it, and walks off, climbing on, keeping its height, holding on by sneaking or
sliding down meanwhile, whichever brings it onto the block. It takes only jumps off a ladder
that work even when it lets go a little earlier or later. A jump may lead around a corner: onto
a block diagonally ahead, one block up, behind the corner of a wall, where the bot slides along
the wall and gets onto the block over the corner. Jumps under a ceiling too low for the
arc are not taken.
Working out jumps takes much longer than walking, so the bot searches a path on foot first. If
that reaches the target, it sets off at once; if the way is much longer than the straight line,
it looks for a shortcut with jumps meanwhile, and takes it if it is much shorter. If the path on
foot doesn't reach the target, the bot works out the jumps first, and meanwhile stands, or goes
on along the path it has, for up to a second or so. Either way the jumps are worked out in the
background, which doesn't hold up the server, and task.searching is true meanwhile. The
search tries 10000 jumps at most, then it goes on as on foot; set jumpTries to change
that limit: more tries find longer and harder ways, but take longer.
With sprintJumps set to true, the bot sprints, and jumps along straight, level stretches of
its way, as players often do. It gets there a bit faster, and hungry a bit sooner.
The task finishes as soon as the bot stands within targetRadius of the target. A path search
looks searchRange blocks ahead, so a path to a target further away, or along a long, winding
way, may end short of it. Then the bot searches again from where that path ends, and so on, as
long as each such end is at least a block nearer the target than the last. It does so a few
blocks before it gets there, so that it can walk on without a stop. The task fails if the
bot gets stuck, or if it gets no nearer, which happens when the target cannot be reached. On a
winding way, the end of a short path can be a dead end, which costs a detour or fails; a larger
searchRange finds the better way at once, but takes longer to search.
With watchTarget, the bot keeps looking at an entity's eyes or at a position while it walks, for
example at an enemy while it retreats. It then walks backward and sideways as well, like a
player who holds those keys, as fast as forward. Sprinting takes the forward key, so the bot
sprints only where its way leads roughly where it looks. It still looks where it goes for what
takes that: jumps across gaps, ladders, swimming, and the doors it clicks; then it turns back.
When the entity dies, is removed or changes world, the bot looks where it goes from then on.
While this task runs, task also carries target, targetRadius, searchRange, sprint,
sprintJumps, ladders, doors, closeDoors, maxFall, jumps, jumpTries and watchTarget as
they were passed, traveled, the distance in blocks the bot has come along its paths so far,
remainingDistance, the length in blocks of the rest of the path, reachable, which is false
when the path stops short of the target (the bot may still get there, see above), and
searching. While the bot works out jumps, remainingDistance and reachable tell of the path
on foot, and change once it is done. The path is computed anew when the bot is held up, for
example by a block placed in its way.
Misspelling an option is an error, not a wish: { sprnt = true } is rejected rather than
ignored.
Example
local ok, reason = bot:startPathTo(spell.pos + Vec3(20, 0, 0), { sprint = true })
if ok then
ok, reason = bot:awaitTask() -- sleeps until the task has ended
end
print(ok and "The bot got there." or "The bot did not get there: " .. reason)
Example: Retreating with an eye on the enemy
local enemy = spell:findEntities("@e[type=zombie,sort=nearest,limit=1]")[1]
bot:startPathTo(shelter, { watchTarget = enemy })
Parameters
- target: Vec3 — The position to walk to.
- options?: BotPathToOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started. - reason: TaskReason? — The reason the task did not start: "no_path", only without
jumps; with them, the task fails with "unreachable" instead.
startPlaceBlock(pos: Vec3, options?: BotPlaceBlockOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that walks the bot to the given position and places the block it holds there.
The bot walks the way startPathTo walks, until it sees, within reach, a side of a neighboring
block that faces the position. There it stops, turns its eyes to that side, at most 45 degrees
per tick, and clicks it with the item in its hand, as useOnBlock does. So the game decides about
everything as for any player: which way the block faces (a furnace or stairs face the bot),
what is used up, and whether the block can be placed at all. The click fires
PlayerUseBlockEvent. Against a block that reacts to a click, like a chest or a door, the bot
sneaks for the click, as a player does, so the block stays shut.
Like a player, the bot needs a neighboring block to place a block against: it can't put a
stone in mid-air. against tells which neighbor that is, which decides between a torch on the
floor and one on a wall, for example. Without it the bot takes the first neighbor it sees,
preferring the block below. If the bot stands where the block goes, it steps aside first.
The task finishes when the block is placed. It fails with "occupied" if a block was put there in the meantime or another entity is in the way, with "cannot_place" if the click placed nothing, because the block can't stay there, like a sapling on stone, or because a spell cancelled the click, or if the neighbors to place it against are gone, and with "unreachable" if the bot can't get to a place from where it sees a side to click. Nothing is used up then.
While this task runs, task also carries target, hand, against if it was given,
searchRange, sprint, ladders, doors, closeDoors, maxFall and jumps.
Example: A torch on the wall, from the off hand
bot.inventory.slots[40] = Item:new("torch")
local ok, reason = bot:startPlaceBlock(spell.pos + Vec3(2, 1, 0), { hand = "offhand", against = "east" })
if ok then
ok, reason = bot:awaitTask()
end
print(ok and "Placed." or "The bot could not place it: " .. reason)
Parameters
- pos: Vec3 — The position to place the block at.
- options?: BotPlaceBlockOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started. - reason: TaskReason? — The reason the task did not start: "hand_empty", "not_a_block", "occupied" for a position that holds a block already, "cannot_place" for a position without a block to place it against, or beyond the build height, "not_allowed" for a position where the bot may not build, like one in adventure mode, or one near the world spawn for a bot that is no operator, or "no_path".
startPutInto(pos: Vec3, options?: BotPutIntoOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that walks the bot to the given container and puts items from its inventory into it.
The bot walks the way startPathTo walks, until it sees the container within reach. There it
stops, turns its eyes to it, opens it with a click, as useOnBlock does, shift-clicks the stacks,
one click per tick, as clickSlot does, and closes it. So the container's menu decides where an
item goes: in a furnace, fuel goes to the fuel slot and everything else to the slot of the item
to smelt. The click fires PlayerUseBlockEvent.
Without item, the bot puts in the stack it has selected in its hotbar. With item, it puts
in all matching stacks from its hotbar and the rest of its inventory, but not its armor or what
its off hand holds. With count, it puts in no more than that many items: for a part of a
stack it picks the stack up, puts the items down one by one, and puts the rest back.
A log is both fuel and an item to smelt, and the furnace's menu smelts it. To decide yourself,
give slot, the slot of the container as clickSlot counts it: in a furnace 0 is the item to
smelt and 1 the fuel. The bot then puts the items into that slot with plain clicks, as long as
the slot takes them: it is empty, or holds the same item and has room.
The task finishes when at least one item went in; task.moved tells how many. What doesn't fit
stays with the bot. The task fails with "container_full" if nothing fit, with "not_allowed" if
the container doesn't open, like a chest with a block on top, with "invalid_slot" if the
container has no such slot, with "target_changed" if the container is gone, and with
"unreachable" if the bot can't get to a place from where it sees the container.
While this task runs, task also carries target, count and slot if they were given,
moved, searchRange, sprint, ladders, doors, closeDoors, maxFall and jumps.
Example: Fueling a furnace
bot.inventory.slots[9] = Item:new("coal", { count = 16 })
local ok, reason = bot:startPutInto(spell.pos + Vec3(3, 0, 0), { item = "coal", count = 8 })
if ok then
ok, reason = bot:awaitTask()
end
print(ok and "Put in " .. bot.task.moved .. " coal." or "The bot could not do it: " .. reason)
Example: Logs as fuel
Parameters
- pos: Vec3 — The position of the container.
- options?: BotPutIntoOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started. - reason: TaskReason? — The reason the task did not start: "no_items" if the bot has no such item, "no_container", or "no_path".
startStay(options?: BotStayOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that keeps the bot where it is, or at a given position.
The bot lets go of all movement keys and stands still until the task is stopped or replaced, or,
with a duration, until that many ticks have passed. Only to stay where it is it holds keys, as
a player does: on a ladder in the air it holds on by sneaking, and in water too deep to stand in
it holds the jump key and stays at the surface. Like a player who takes their hands off the
keys, it still falls, gets pushed and takes damage. Other actions, like
lookAt, useOnBlock or dig, leave the task running, so the bot can stand and work. Assigning
input stops the task.
With pos or radius, the bot holds its spot: when something moves it further away than
radius, like a hit, a push or a current, it goes back once it has landed, along a path as
startPathTo walks, and the last bit straight onto the spot, sneaking, to stand on it within a
tenth of a block. With radius = 0 it goes back as soon as it is a quarter of a block off, so
that a brush in passing doesn't move it. Its spot is the middle of the block at pos, where it
walks first, or else the exact place where it stands when the task starts. When it finds no way
back, it waits where it is and tries again every two seconds; only on its way to pos at the
start the task fails, with "unreachable", as a path task does.
Unlike a mob, a bot has no behavior of its own that takes over when it has no task: it stands
still then too, unless it holds keys. The task makes waiting visible to other spells: the bot is
busy, and its task says "stay".
With watchTarget, the bot keeps looking at an entity's eyes or at a position while it stays, and on
its way to its spot as for startPathTo; only for the last bit, straight onto its spot, it
looks where it goes. It turns as a player does, not at once, and follows an entity that moves.
When the entity dies, is removed or changes world, the bot stops turning.
While this task runs, task also carries duration and remaining, the ticks left, if a
duration was given, and watchTarget, if it was given. A bot that holds its spot also carries pos, its spot, radius, away,
which is true while it is on its way to the spot, and searchRange, sprint, ladders,
doors, closeDoors, maxFall and jumps.
Example
Without options, the bot stays where it stands, with no time limit, until the task is stopped or replaced. It doesn't hold its spot: when something moves it, it stays where it ends up.
A bot that walks somewhere, then stays for five seconds and looks at the spell's owner:
bot:startPathTo(spell.pos + Vec3(8, 0, 0))
bot:awaitTask()
bot:startStay({ duration = 5 * 20, watchTarget = spell.owner })
bot:awaitTask()
A guard that walks to its post and keeps it, and one that gives way up to 3 blocks:
Parameters
- options?: BotStayOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started. - reason: TaskReason? — The reason the task did not start: "no_path" if no path leads to
pos.
startTakeFrom(pos: Vec3, options?: BotTakeFromOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that walks the bot to the given container and takes items out of it, into its inventory.
The bot walks to the container and opens it as for startPutInto, shift-clicks the matching
stacks, one click per tick, and closes it. item chooses what to take: an item id like "coal",
a tag like "#minecraft:logs", or a list of them. Without it the bot takes everything, from a
furnace also the fuel and the item being smelted. With count, it takes no more than that
many items. With slot, it takes only from that slot of the container, as clickSlot counts
it: 2 is a furnace's output.
The task finishes when at least one item was taken; task.moved tells how many. It fails
with "nothing_to_take" if nothing in the container matched, with "inventory_full" if the bot's
inventory took none of it, with "not_allowed" if the container doesn't open, with
"invalid_slot" if the container has no such slot, and with "target_changed" if the
container is gone.
While this task runs, task also carries target, count and slot if they were given,
moved, searchRange, sprint, ladders, doors, closeDoors, maxFall and jumps.
Example: Fetching a pickaxe
local chest = spell.pos + Vec3(3, 0, 0)
bot:startTakeFrom(chest, { item = "#minecraft:pickaxes", count = 1 })
if bot:awaitTask() then
print("The bot has a pickaxe now.")
end
Parameters
- pos: Vec3 — The position of the container.
- options?: BotTakeFromOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started. - reason: TaskReason? — The reason the task did not start: "no_container", or "no_path".
stopDigging()¶
Stops digging, like letting go of the left mouse button before the block broke.
stopTask()¶
Stops the task the bot is executing, if any, and releases the keys it held; on a ladder in the air, the bot holds on by sneaking, and in water too deep to stand in it holds the jump key. The task ends "interrupted" with the reason "stopped".
stopUsingItem()¶
Stops using the item in use, like letting go of the right mouse button: shoots a drawn bow, for example.
swing()¶
Swings the main hand at nothing, like a left click into the air. Players nearby see the swing,
PlayerHandSwingEvent fires, and the attack charge of the weapon in hand starts over, as for a
player.
Like a player, the bot cannot swing its off hand on its own. The game swings a hand when the bot uses the item in it with success, the off hand too: placing a block, feeding an animal, and so on. Starting to eat or to draw a bow doesn't swing.
Example
Play-tests a wand spell that casts a fireball when a player swings a blaze rod.
Example
With a torch in the off hand: places it on top of the block and swings the off hand.
useItem(hand?: Hand)¶
Uses the item in the hand, like a right click into the air: eats, throws, casts a fishing rod,
starts to draw a bow, and so on. An item used over time, like a bow or food, stays in use until
stopUsingItem is called or its use is complete.
Parameters
- hand?: Hand — The hand to use. Defaults to "mainhand".
useOnBlock(pos: Vec3, side?: Side, hand?: Hand)¶
Uses the item in the hand on the block, like a right click at the block's side: pulls a lever, opens a chest, places a block, and so on. The bot turns to the middle of that side first, since a player can only click what it looks at.
Example
Parameters
- pos: Vec3 — The position of the block.
- side?: Side — The side of the block to click. Defaults to the side facing the bot's eyes.
- hand?: Hand — The hand to use. Defaults to "mainhand".
useOnEntity(target: Entity, hand?: Hand)¶
Uses the item in the hand on the entity, like a right click at it: trades with a villager, puts armor on an armor stand, and so on. The bot turns to the middle of the entity first, since a player can only click what it looks at.
Parameters