BotHub Framework
A comprehensive C# .NET 8 game automation framework with 150+ features spanning anti-detection, combat, skilling, market automation, farm management, visual scripting, and analytics. Built with a unified service architecture via BotContext.
Script Creation Tutorial STEP-BY-STEP
A complete walkthrough for creating your first custom C# script, from opening Visual Studio to building, loading in-game, and running via the Script Manager.
-
Open Visual Studio
Launch Visual Studio 2022 (Community edition or higher). If you don't have it installed, download it from
visualstudio.microsoft.comand make sure the .NET desktop development workload is selected during installation. -
Create a Class Library Project
Go to
File → New → Project, search for "Class Library", and select the C# one targeting .NET 8.0. Name it something descriptive likeMyBotScripts.This gives you full IntelliSense, autocomplete, and compile-time error checking. When you build the project, Visual Studio produces a
.dllfile that BotHub loads directly — no runtime compilation needed. -
Add BotCore Reference
To get autocomplete on
AbstractScript,BotContext,Actions, and everything else:- Right-click your project in Solution Explorer → Add → Project Reference (or Add Reference → Browse)
- Navigate to BotHub's install directory and select
BotCore.dll - Click OK — you now have full IntelliSense for the entire framework
Important: After adding the reference, right-clickBotCoreunder your project's Dependencies → Assemblies, select Properties, and set Copy Local toNo. This prevents Visual Studio from copying a secondBotCore.dllinto your build output, which would cause a type mismatch when loading your script.Also addHexa.NET.ImGui.dll(same folder, alsoCopy Local = No) if you want to create custom ImGui displays (Part 4). -
Rename the Default Class
Visual Studio automatically creates a file called
Class1.cswith a class namedClass1inside it. You need to rename both to something descriptive:- In Solution Explorer, right-click
Class1.csand select Rename - Type a descriptive name for your script, e.g.
WillowChopper.cs - Visual Studio will ask "Would you also like to rename all references to Class1?" — click Yes. This renames the class inside the file to match.
Some examples of good script names:
WillowChopper.cs— a woodcutting scriptCowKiller.cs— a combat scriptFlaxSpinner.cs— a crafting script
The file name and class name should always match. Visual Studio handles this for you when you use Rename. Use PascalCase with no spaces (e.g.DraynorWillowChopper, notdraynor willow chopper). - In Solution Explorer, right-click
-
Replace the Default Code
After renaming, your file will look something like this — an empty class inside the namespace Visual Studio created from your project name:
namespace MyBotScripts { public class WillowChopper { } }Replace the entire contents of the file with the template below. Notice we keep the same
namespaceandclass name— just add the BotHub imports, extendAbstractScript, and fill in the required properties:using System.Threading.Tasks; using BotCore.Engine; using BotCore.API; namespace MyBotScripts { public class WillowChopper : AbstractScript { public override string Name => "Willow Chopper"; public override string Description => "Chops willows and banks logs"; public override string Author => "YourName"; public override string Version => "1.0.0"; public override string Category => "Woodcutting"; protected override async Task Run() { while (!ShouldStop) { // Your logic goes here await Delay(500); } } } }What changed? We added threeusingimports at the top, made the class extendAbstractScript, added the five required properties (Name,Description,Author,Version,Category), and added theRun()method. The namespace and class name stay the same as what Visual Studio gave you. -
Customize Your Script Metadata
Now update the five properties at the top of the class to match your script. These are what users see in the Script Manager panel:
Name— Human-readable name shown in the UI. Change"Willow Chopper"to whatever fits your script.Description— Short summary of what the script doesAuthor— Replace"YourName"with your username or aliasVersion— Semantic version string, start with"1.0.0"Category— One of:Combat,Skilling,MoneyMaking,Minigame,Questing,Utility,AIO,Prayer,Magic,Agility,Mining,Fishing,Woodcutting,Farming,Hunter,Slayer,Thieving,Other
-
Write the Main Loop
The
Run()method is your script's main loop. Use thewhile (!ShouldStop)pattern so the framework can cleanly stop your script. Here's a real example — a tree chopper with banking:protected override async Task Run() { while (!ShouldStop) { await CheckBreak(); // Auto break scheduling await CheckRandomEvents(); // Handle genie, etc. await MaybeMistake(); // Anti-detection misclicks if (InventoryFull) { Log("Inventory full, banking..."); await WalkTo(2886, 3534); // InteractObject(action, objectId, tileX, tileY, offset) await Actions.InteractObject(46, 134036, 2886, 3535, 3808); await Actions.WaitUntilIdle(1500); Interface.Interact(-1, 1, 1, 517, 39, -1, 80); await WalkTo(2882, 3532); } else { Log("Chopping tree..."); await Actions.InteractObject(59, 38785, 2881, 3534, 3712); await Actions.WaitUntilIdle(10000); } await Delay(300); // 300ms base mean - actual wait varies (e.g. 150-500ms) } }About Delay(): The number you pass (e.g. 300) is NOT a fixed wait — it's a base mean in milliseconds. Under the hood,Delay()callsCtx.SmartDelay()which applies Gaussian randomization, fatigue scaling, circadian rhythm, and entropy matching. SoDelay(300)might actually wait 180ms, 420ms, 350ms, or 510ms — never the same value twice. -
Use Built-in Convenience Methods
AbstractScriptgives you these methods out of the box — no extra imports needed:Log("message")/LogWarn("msg")/LogError("msg")— Structured loggingDelay(baseMeanMs)— Randomized humanized delay (NOT a fixed wait)WalkTo(x, y)— Pathfind and walk to a tileIsNear(x, y, radius)— Check proximity to a tileInventoryFull/HasItem(id)/InvCount— Inventory checksCheckBreak()— Handles break scheduling automaticallyCheckRandomEvents()— Solves genies, strange plants, etc.MaybeMistake()— Injects humanized misclicksRecordAction("what")— Anti-AFK activity recordingShouldStop— True when user clicks Stop or cancellation is requested
Access the full framework viaCtx— e.g.Ctx.Combat,Ctx.Banking,Ctx.Stats,Ctx.WorldState, and 40+ more systems.
Every in-game object (trees, banks, NPCs) has a unique ID that can change between game patches. You need the correct IDs for your script to interact with objects. There are two ways to find them:
-
Method 1: Use the Console Log
With BotHub injected, manually click on the object you want to interact with (e.g. click a tree to chop it). Watch the BotHub console window — it logs every action the game processes:
** OBJECT INTERACTION DETECTED id=0x3B at (2881,3534) ** Sending to C#: OBJECT(action: 59 id: 38785 x: 2881 y: 3534 offset: 3712)This tells you exactly what to put in your script:
action: 59— the action code (e.g. "Chop down")id: 38785— the object ID for that specific treex: 2881, y: 3534— the tile coordinatesoffset: 3712— the action queue offset
Use these values in your script:
await Actions.InteractObject(59, 38785, 2881, 3534, 3712);Common mistake: Object IDs can differ between seemingly identical objects. A willow tree on one tile may have id38785while the same-looking willow one tile over has id38783. Always verify the ID by clicking the exact object you want your script to use. -
Method 2: Use Record New Script
Click the "Record New Script" button in the Script Manager. This starts recording your clicks and captures all the action codes, object IDs, and tile coordinates automatically. When you stop recording, the captured data can be used to generate a script or just to copy the correct IDs into your own code.
-
Build the DLL
In Visual Studio, press
Ctrl+Shift+B(orBuild → Build Solution). This compiles your script into a DLL file. Look for:MyBotScripts/ bin/ Debug/ net8.0/ MyBotScripts.dll ← THIS is what you load in-game BotCore.dll ← delete this (or set Copy Local = No)Check the Output window at the bottom of Visual Studio — it should show "Build succeeded" with 0 errors.
Important: If you see aBotCore.dllin your output folder, delete it. If the bot loads YOUR copy of BotCore alongside the real one, types won't match and loading will fail with "No IScript class found". Prevent this permanently by setting Copy Local = No on the BotCore reference (see Part 1 Step 3). -
Verify Build Output
Make sure your DLL exists and has a recent timestamp. The file path will be something like:
C:\Users\YourName\source\repos\MyBotScripts\bin\Debug\net8.0\MyBotScripts.dllRemember this path — you'll navigate to it in the next step.
-
Open the Script Manager In-Game
With BotHub injected into the game client, open the Script Manager panel from the in-game toolbar.
-
Click "Load Script"
At the top of the Script Manager, click the blue "Load Script" button. This opens a file browser with:
- Drive buttons (C:, D:, etc.) for quick navigation
- Desktop / Documents shortcut buttons
- ".." (up) to go to the parent folder
- [FolderName] entries (blue) to navigate into folders
- FileName.dll / FileName.cs entries (green) to select a script
Navigate to your Visual Studio project's build output folder (e.g.
MyBotScripts\bin\Debug\net8.0\) and click onMyBotScripts.dll. -
Click "Compile & Load"
With your DLL selected, click the green "Compile & Load" button. The loader:
- Loads the assembly directly into memory (reads bytes, no file lock)
- Discovers scripts — finds all classes that extend
AbstractScript - Registers them in the local library (
Scripts/library.json) with the full file path
The status line shows: "Loaded 1 script(s): WillowChopper"
Your script now appears in the Installed Scripts list below.
Persisted across restarts: The DLL path is saved inlibrary.json. Next time you inject, the script stays in the Installed list. Clicking Run re-loads the DLL from the same path — so if you rebuild in Visual Studio, the next Run picks up your changes automatically. -
Run Your Script
In the Installed Scripts list, click the green Run button next to your script. The status shows "Running WillowChopper" and your script's
Run()method starts executing.To stop: click the red Stop button (same position). This sets
ShouldStop = true, which exits yourwhile (!ShouldStop)loop cleanly. -
Update Your Script
The edit-test cycle is simple:
- Edit your code in Visual Studio
- Press
Ctrl+Shift+Bto rebuild - In-game, click Stop (if running) then Run again — it reloads the latest DLL automatically
No need to click "Load Script" again — the path is already saved. Just rebuild and re-run.
Scripts can draw custom in-game UI by implementing the IScriptPainter interface. Your OnPaintImGui() method is called every frame while the script is running. Add Hexa.NET.ImGui.dll as a reference (Copy Local = No) to use this feature.
-
Implement IScriptPainter
Add
IScriptPainterto your class declaration and implementOnPaintImGui():using System.Numerics; using System.Threading.Tasks; using Hexa.NET.ImGui; using BotCore.Engine; using BotCore.API; namespace MyBotScripts { public class WillowChopper : AbstractScript, IScriptPainter { public override string Name => "Willow Chopper"; // ... other properties ... private int _logsChopped = 0; public void OnPaintImGui() { ImGui.SetNextWindowSize(new Vector2(250, 120), ImGuiCond.FirstUseEver); if (ImGui.Begin("Willow Chopper")) { ImGui.Text($"Logs: {_logsChopped}"); float inv = InvCount / 28f; ImGui.ProgressBar(inv, new Vector2(-1, 18), $"Inventory: {InvCount}/28"); var col = InventoryFull ? new Vector4(1f,0.4f,0.4f,1f) : new Vector4(0.3f,1f,0.3f,1f); ImGui.TextColored(col, InventoryFull ? "Banking..." : "Chopping..."); } ImGui.End(); } protected override async Task Run() { while (!ShouldStop) { _logsChopped++; await Delay(300); } } } }Registration is automatic.AbstractScriptregisters your painter on start and unregisters on stop. -
Important Rules
- Thread safety —
OnPaintImGui()runs on the render thread. Use simple types (int,bool) for shared state. - Always call ImGui.End() after every
ImGui.Begin() - No heavy work — runs every frame (~16ms). Just read state and draw.
- Thread safety —
- Always use
while (!ShouldStop)as your main loop guard - Always use
Delay(baseMean)instead ofTask.Delay()— SmartDelay adds Gaussian noise, fatigue, circadian, and entropy variation so the actual wait is different every time - Call
await CheckBreak()at the top of each loop iteration - Use
RecordAction()to prevent anti-AFK timeouts - Use
Log()generously for debugging
- Build with
Ctrl+Shift+Bin Visual Studio to produce your.dll - Load via Load Script in the in-game Script Manager — navigate to your
bin/Debug/net8.0/folder - The DLL path is saved — after rebuild, just click Run again (no need to re-load)
- Set Copy Local = No on BotCore.dll reference to prevent type identity mismatches
- Delete any stale
BotCore.dllfrom your build output folder if loading fails
- Call
await MaybeMistake()periodically to inject humanized misclicks and pauses - Don't hard-code exact delays — always use
Delay(baseMean)which adds Gaussian noise - Use
Ctx.BanRisk.GetScore()to monitor your ban risk score and adjust behavior - Access the full humanization suite via
Ctx.Fatigue,Ctx.Circadian,Ctx.Entropy,Ctx.Mistakes
- "No IScript class found" — You're loading the wrong DLL (e.g.
BotCore.dllinstead of your script DLL), OR a staleBotCore.dllsits next to your DLL causing a type mismatch. Fix: delete the extraBotCore.dllfrom your output folder. - Action queued but nothing happens — The object ID is wrong. Manually click the object and check the console log for the correct ID. IDs can differ between identical-looking objects.
- Script loads but crashes immediately — Check the status bar for the error. Common:
NullReferenceExceptionfrom callingCtxmethods beforeRun()is entered. - Script not in Installed list after load — The load may have silently failed. Check the status line at the bottom of the Script Manager for diagnostic messages.
Architecture
All systems are accessed through BotContext, wired together via SystemWiring, and fed live data by GameStatePoller. Scripts extend AbstractScript and get the full framework for free.
Quick Start
Create a script by extending AbstractScript. You get access to every system through Ctx.
BotContext CORE
Central service container that constructs, holds, and exposes every subsystem. Script authors get ONE object that provides access to the entire framework. Handles lifecycle (start/stop), background service management, and cross-system wiring.
BotRuntime
Static bridge between BotHubInject (entry point) and BotContext (framework). Call BotRuntime.Initialize() once to make the entire framework live.
IScript / AbstractScript
Standard contract for all bot scripts with proper lifecycle management. AbstractScript provides convenience methods for logging, delays, inventory checks, breaks, and random events.
EventBus
Central pub/sub event system. Any component can publish events and any component can subscribe to them by type. 15+ built-in event types.
GameStatePoller
Background loop that polls game state every 600ms and publishes events to the EventBus. This is the heartbeat that makes the entire event-driven architecture work.
SystemWiring
Wires cross-system event subscriptions so all 150+ systems work together as an integrated platform. Called once from BotContext constructor.
TaskPipeline
Async task pipeline with proper cancellation, pause/resume, and state tracking. Replaces ad-hoc while(true) loops with structured execution.
Resilience
Retry with backoff, safe wrappers for memory reads, and circuit breaker for APIs that start failing. Prevents a single failed memory read from crashing scripts.
ScriptChainer
Chains multiple scripts with conditions. "Mine 1000 ores, then smith, then bank." Scripts execute in sequence with completion conditions, skip conditions, repeat counts, and timeouts.
ScriptHotReload
Hot-reload for bot scripts using Roslyn. Edit .cs files and they recompile at runtime without restart. FileSystemWatcher triggers recompilation on save.
BehaviorTree
12 node types for hierarchical behavior composition: Sequence, Selector, Repeater, Inverter, Parallel, Cooldown, TimeLimit, RandomSelector, ConditionalGuard, AlwaysSucceed, ActionNode, ConditionNode.
OffsetHealer
When the game updates and memory offsets break, this system detects invalid reads, re-scans for known signatures, validates against invariants, and hot-patches the offset table without restart.
Actions (High-Level API) NEW
High-level action facade wrapping low-level API calls with proper error handling, humanized delays, and event publishing. Use these instead of raw Interface.Interact() calls.
Humanization Suite 11 SYSTEMS
The most comprehensive anti-detection layer of any botting framework. Covers mouse movement, timing, attention, fatigue, circadian rhythms, click patterns, entropy matching, and deliberate mistakes.
Combat Suite 8 SYSTEMS
Complete combat automation: FSM, ability rotation, auto-eat, death handling, boss mechanics, PvP escape, prayer switching, tick manipulation.
Skills Suite 8 SYSTEMS
Skill-specific automation modules: XP calculator, prayer flicking, potion timers, farming planner, agility courses, clue scrolls, smart alching, herblore, hunter traps, bone burying.
Items & Market
Loot management, equipment loadouts, drop tables, price caching, GE tracking, and autonomous flipping.
Interaction Handlers
Banking, dialogue, random events, auto-login, player interaction policies.
Analytics & Monitoring
Session stats, structured logging, SQLite database, profit tracking, ban risk scoring, screenshots, Discord integration.
Scheduling
Session scheduler, break generator, anti-AFK, crash recovery.
Rules Engine
Declarative condition-action rules, priority action queuing, goal planning, config profiles.
Farm Management
Multi-account lifecycle, world hopping, proxy rotation, trade coordination, progression optimization.
Vision & Detection
Pixel scanning, template matching, animation detection, orb reading - pure visual detection without memory reading.
World Systems
Security
UI & Overlay 10 SYSTEMS
Paint overlay API, live stats HUD, mouse trail, tile highlighting, global hotkeys, notifications, script selector, session history.
Diagnostics
Plugins
Quest Automation
Debug Tools
Node Editor
Visual scripting with 16+ node types, graph serialization, and in-game ImGui rendering.
Core API (Memory Reading)
Low-level game interaction through memory reading and DLL injection. These are the foundational APIs that everything else builds on.
Generated from source code analysis