Java
Anatomy of a script
Java · Updated 09 November 2026
BotScript, @ScriptManifest and the onStart, onLoop, onStop lifecycle.
A script is a class that implements com.botwithus.bot.api.BotScript and carries a @ScriptManifest annotation. The package and com.botwithus imports are left out below; A complete example shows a whole file.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ScriptManifest(
name = "My Script",
version = "1.0",
author = "You",
description = "Does something useful"
)
public class MyScript implements BotScript {
private static final Logger log = LoggerFactory.getLogger(MyScript.class);
private ScriptContext ctx;
@Override
public void onStart(ScriptContext ctx) {
this.ctx = ctx;
log.info("Started!");
}
@Override
public int onLoop() {
GameAPI api = ctx.getGameAPI();
GameSnapshot snap = api.snapshot();
if (snap != null && snap.self() != null) {
// read state from the snapshot, act through api
}
return 1000;
}
@Override
public void onStop() {
log.info("Stopped.");
}
}
@ScriptManifest
| Field | Default | Meaning |
|---|---|---|
name |
required | The name shown in the host |
version |
"1.0" |
Your script's version |
author |
"" |
Your name |
description |
"" |
One line about what it does |
category |
UNCATEGORIZED |
A ScriptCategory, such as UTILITY or WOODCUTTING |
If two different scripts use the same name, only one of them is registered, and the host's Installed page lists the other as an older copy. Keep names unique.
The lifecycle
onStart(ScriptContext ctx)runs once. Keep the context and set up your state here.onLoop()runs again and again. Its return value is the delay in milliseconds before the next call. Return-1to stop the script.onStop()runs once at the end, either becauseonLoopreturned-1or because the user pressed Stop. Clean up here.
If your script declares settings, onConfigUpdate also runs once before the first onLoop (see API tour).
Stopping cleanly from deep inside your code
ctx.stopSelf() asks for the same clean stop as returning -1, from anywhere in your script. The current onLoop finishes, then onStop runs.
If you wait inside onLoop, poll ctx.isStopRequested() and return when it turns true:
@Override
public int onLoop() {
Bank bank = ctx.getGameAPI().bank();
while (!bank.isOpen()) {
if (ctx.isStopRequested()) {
return -1;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return -1;
}
}
log.info("Bank is open.");
return 600;
}
A stop, whether from ctx.stopSelf() or the user's Stop button, also interrupts the script's thread. A sleep or wait then ends early with InterruptedException, so that catch is the stop path: restore the interrupt flag and return.
A script that ignores stop requests is eventually cut off: every further game call throws, and the script's thread stays parked for the rest of the session.
Registering the script
The host only sees classes your module provides:
provides com.botwithus.bot.api.BotScript with my.script.MyScript;
If you forget this line, the JAR loads but no script appears, and the host log says:
Module 'my.script' contains no BotScript providers
The fix is always the provides clause in module-info.java.
Next: API tour.