Your first script

Lua · Updated 09 November 2026

Set up, write a script table with a manifest, and run it.

Lua scripts run in bwu_host, the BotWithUs native host, which embeds Lua 5.4. LuaJIT and Lua 5.1 to 5.3 are not supported.

Setup

  • Nothing to install. bwu_host brings its own Lua 5.4 and the botwithus library.
  • Put your scripts in ~/.botwithus/scripts/lua (%USERPROFILE%\.botwithus\scripts\lua). That is the folder the bwu_host GUI lists.

The script table

A Lua script is a file that builds a script table and returns it:

local bot = require("botwithus")

local TREE_IDS = { [38782] = true, [38783] = true, [38785] = true, [38787] = true }

local OBJECT_OPTION_1 = 3
local CHOP_TICKS = 5

local script = {
  manifest = { name = "Woodcutter", author = "example", version = "1.0.0" },
}

function script.on_start(ctx)
  local me = ctx.game:self()
  io.write(("woodcutter: starting at %s (cb %d)\n"):format(tostring(me.tile), me.combat_level))
end

function script.on_loop(ctx)
  local me   = ctx.game:self()
  local tree = bot.objects(ctx.game):where(function(o) return TREE_IDS[o.id] end):nearest(me.tile)
  if not tree then
    io.write("woodcutter: no tree nearby; stopping\n")
    return -1
  end
  ctx.game:do_action({ id = OBJECT_OPTION_1, p1 = tree.id, p2 = tree.tile.x, p3 = tree.tile.y })
  io.write(("woodcutter: chopping tree %d at %s\n"):format(tree.id, tostring(tree.tile)))
  return CHOP_TICKS
end

function script.on_stop(ctx)
  io.write("woodcutter: stopped\n")
end

if select("#", ...) == 0 then
  bot.run(script)
end

return script

The action { id = 3, p1 = loc id, p2 = x, p3 = y } is "object option 1" ("Chop down" on a tree). Like a click, it makes the client walk the player to the tree first.

The manifest

manifest = { name, version, author, description, category }. Every field is optional, but each one that is set must be a string (version = 1.0 is refused; write "1.0"). The limits, in bytes:

Field Default Length
name the file name 1 to 128
version "1.0" 1 to 64
author "" 0 to 128
description "" 0 to 4096
category none 1 to 64

Use one of the Java host's category names, such as Skilling, Combat or Woodcutting, to group the script in the GUI.

The self-run guard

The last lines of the example matter:

if select("#", ...) == 0 then
  bot.run(script)
end

return script
  • When bwu_host runs the file directly, it calls it with no arguments, so the script runs itself.
  • When the GUI loads the file to list it, or another file requires it, there is an argument. The file then only returns its table.

Without the guard, listing the script in the GUI would start it. Attaching to a client while the file is being listed fails with attach during load: guard the self-run.

Running it

In the GUI. Start bwu_host with no arguments. It lists the .lua files in ~/.botwithus/scripts/lua by their manifests and runs each one in a worker process of its own against the client you pick.

From a console.

bwu_host --lua $HOME\.botwithus\scripts\lua\woodcutter.lua

On the command line a script gets its default settings, and no stop request reaches it. It ends when on_loop returns a negative number, when it fails, or when its client exits.

Next: Lifecycle, pacing and settings.