From superpowers
Use when implementing new bot scripts in 2009scape, debugging bot behavior, or understanding how bots interact with game systems - covers AIPlayer architecture, Script class, GeneralBotCreator, and state machines
How this skill is triggered — by the user, by Claude, or both
Slash command
/superpowers:2009scape-bot-architectureThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
2009scape uses **AIPlayer** (Artificial Intelligent Player) objects to create server-sided bots that simulate real player behavior. These bots run scripts that control their actions through the `Script` abstract class and are managed by the `GeneralBotCreator` system.
2009scape uses AIPlayer (Artificial Intelligent Player) objects to create server-sided bots that simulate real player behavior. These bots run scripts that control their actions through the Script abstract class and are managed by the GeneralBotCreator system.
Location: Server/src/main/core/game/bots/AIPlayer.java
AIPlayer extends the Player class, making bots full-fledged entities in the game world:
ArtificialSession instead of network sessionsRepository.getPlayers() like real playersKey characteristics:
public AIPlayer(Location l) {
super.setLocation(startLocation = l);
super.artificial = true; // Marks as bot
super.getDetails().setSession(ArtificialSession.getSingleton());
Repository.getPlayers().add(this);
this.updateRandomValues(); // Randomize appearance/stats
this.init();
}
Location: Server/src/main/core/game/bots/Script.java
The abstract Script class defines bot behavior:
Core components:
scriptAPI - Helper methods for common bot actions (teleport, attack, walk, bank)inventory - Starting inventory itemsequipment - Starting equipped itemsskills - Skill levels to set on initializationquests - Quest completion statestick() - Abstract method called every game tickLifecycle:
init(boolean isPlayer) sets skills, quests, equipment, inventorytick() called every game tick (doesn't run if bot has active pulse)scriptAPI and direct bot referenceLocation: Server/src/main/core/game/bots/GeneralBotCreator.kt
Creates bots and submits their scripts to the game pulse system:
Constructor patterns:
// Create bot with script at location
GeneralBotCreator(botScript: Script, loc: Location?)
// Create bot with pre-existing AIPlayer
GeneralBotCreator(botScript: Script, bot: AIPlayer?)
// Turn real player into bot (for botting commands)
GeneralBotCreator(botScript: Script, player: Player, isPlayer: Boolean)
BotScriptPulse:
tick() method every game tickAIRepository.PulseRepository for trackingbotPulsesTriggeredThisTickclass MyBot : Script() {
var state = State.IDLE
init {
// Set starting stats, inventory, equipment
skills[Skills.ATTACK] = 60
skills[Skills.STRENGTH] = 65
inventory.add(Item(Items.LOBSTER_379, 10))
equipment.add(Item(Items.RUNE_SCIMITAR_1333))
}
override fun tick() {
// Only runs when bot doesn't have active pulse
scriptAPI.eat(Items.LOBSTER_379)
when(state) {
State.IDLE -> { /* logic */ }
State.COMBAT -> { /* logic */ }
}
}
override fun newInstance(): Script {
return MyBot()
}
enum class State { IDLE, COMBAT }
}
Movement:
walkTo(Location) - Walk to specific locationteleport(Location) - Instant teleportCombat:
attackNpcInRadius(bot, npcName, radius) - Attack nearest NPC by namebot.attack(target) directlyItems:
eat(foodId) - Eat food if HP is lowforceEat(foodId) - Eat regardless of HPtakeNearestGroundItem(itemId) - Pick up ground itemswithdraw(itemId, amount) - Withdraw from bankObjects:
getNearestNode(id, true) - Find nearest object/NPC by IDgetNearestNode(name, true) - Find nearest by nameGrand Exchange:
sellAllOnGe() - Sell all tradeable itemsBots typically use state machines for complex behavior:
enum class State {
GETTING_TASK, // Initialize new task
GOING_TO_HUB, // Travel to area
KILLING_ENEMY, // Combat + looting
GOING_TO_BANK, // Travel to bank
BANKING, // Bank items
GOING_TO_GE, // Travel to GE
SELLING // Sell items
}
Each state handles specific logic and transitions to the next state when complete.
Use Pulse for actions that need delays:
bot.pulseManager.run(object: Pulse(delayTicks) {
override fun pulse(): Boolean {
// Action after delay
return true // Stop pulse
}
})
Use MovementPulse for pathfinding:
bot.pulseManager.run(object: MovementPulse(bot, destination, Pathfinder.SMART) {
override fun pulse(): Boolean {
// Reached destination
return true
}
})
Location: Server/src/main/core/game/world/ImmerseWorld.kt
StartupListener that spawns bots on server start:
CombatBotAssembler - Spawns combat bots in various areasSkillingBotAssembler - Spawns skilling botsGameWorld.settings.max_adv_bots and enable_botsSpawning methods:
fun spawnBots() {
immerseSeersAndCatherby()
immerseLumbridgeDraynor()
immerseVarrock()
// etc...
}
CombatBotAssembler - Builds combat-focused bots SkillingBotAssembler - Builds skilling-focused bots
Both use builders/factories to create diverse bot populations with randomized:
Location: Server/src/main/core/game/bots/AIRepository.kt
Central registry for bot management:
PulseRepository - Maps bot username to BotScriptPulsegroundItems - Tracks items that belong to specific botsLocation: Server/src/main/core/game/system/command/sets/BottingCommandSet.kt
Admin/testing commands:
::script <identifier> - Start bot script on player (requires warning acknowledgement)::stopscript - Stop currently running scriptPlayerScripts.identifierMapImportant: Running a bot script permanently removes player from highscores.
val items = AIRepository.groundItems.get(bot)
if (items != null && items.isNotEmpty()) {
scriptAPI.takeNearestGroundItem(items[0].id)
}
if (taskZone.insideBorder(bot)) {
// Do task
} else {
scriptAPI.walkTo(taskZone.randomLoc)
}
var teleportFlag = false
if (!teleportFlag) {
scriptAPI.teleport(destination)
teleportFlag = true
} else {
// Do actions at destination
}
for (item in bot.inventory.toArray()) {
item ?: continue
if (item.shouldKeep()) continue
bot.bank.add(item)
}
bot.inventory.clear()
// Re-add essential items
Bots respect server configuration:
enable_bots - Master switch for bot spawningmax_adv_bots - Maximum number of adventurer botsenabled_botting - Allow players to use bot scripts| Task | Method/Pattern |
|---|---|
| Create bot | GeneralBotCreator(script, location) |
| Walk | scriptAPI.walkTo(location) |
| Attack NPC | scriptAPI.attackNpcInRadius(bot, name, radius) |
| Eat food | scriptAPI.eat(foodId) |
| Pick up item | scriptAPI.takeNearestGroundItem(itemId) |
| Find object | scriptAPI.getNearestNode(id, true) |
| Delay action | Pulse(delayTicks) |
| Check in zone | zoneBorders.insideBorder(bot) |
| Set skill level | skills[Skills.ATTACK] = 99 |
class SimpleCombatBot : Script() {
val FOOD = Items.LOBSTER_379
val TARGET_NAME = "Cow"
val COMBAT_ZONE = ZoneBorders(3200, 3200, 3250, 3250)
init {
skills[Skills.ATTACK] = 40
skills[Skills.STRENGTH] = 40
skills[Skills.DEFENCE] = 40
inventory.add(Item(FOOD, 10))
}
override fun tick() {
scriptAPI.eat(FOOD)
// Loot first
val items = AIRepository.groundItems.get(bot)
if (items?.isNotEmpty() == true) {
scriptAPI.takeNearestGroundItem(items[0].id)
return
}
// Stay in zone
if (!COMBAT_ZONE.insideBorder(bot)) {
scriptAPI.walkTo(COMBAT_ZONE.randomLoc)
return
}
// Attack
scriptAPI.attackNpcInRadius(bot, TARGET_NAME, 20)
}
override fun newInstance() = SimpleCombatBot()
}
AIPlayer.java - Bot entity implementationScript.java - Bot script base classScriptAPI.kt - Helper methods for bot actionsGeneralBotCreator.kt - Bot launching systemAIRepository.kt - Bot registry and trackingImmerseWorld.kt - Global bot spawningGenericSlayerBot.kt - Complete bot exampleBottingCommandSet.kt - Bot commandsnpx claudepluginhub realdam/superpowersCreates structured, bite-sized implementation plans from specs or requirements before writing code. Useful for breaking down multi-step tasks into testable steps with file structure and task boundaries.