Architecture
ποΈ Architecture
Technical overview of nExBot's internal architecture and design patterns.
π Overview
nExBot is a modular, event-driven bot built in Lua for OTClient. It uses a layered architecture with clear separation between client abstraction, core systems, and feature modules.
π§© System Architecture
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β _Loader.lua β
β (Entry point, load phases) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Anti-Corruption Layer (ACL) β β
β β Auto-detects client: vBot or OTCR β β
β β Loads appropriate adapter β β
β β Exposes unified ClientService API β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β ββββββββββββ¬ββββββββββββΌββββββββββββ¬ββββββββββββββ β
β β EventBus β UnifiedTickβUnifiedStorageβCreatureCacheβ β
β ββββββββββββ΄ββββββββββββ΄ββββββββββββ΄ββββββββββββββ β
β β β
β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββ β
β β Feature Modules (independent) β β
β β HealBot AttackBot CaveBot TargetBot Extras β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββ β
β β Analytics Layer β β
β β Hunt Analyzer Spy Level Supplies Monitor β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π‘οΈ Anti-Corruption Layer (ACL)
The ACL provides a unified API regardless of whether nExBot is running on vBot (OTClientV8) or OTCR (OpenTibiaBR).
Client Detection
At startup, the ACL detects the client by:
- Checking for OTCR-exclusive module files (
game_cyclopedia,game_forge) - Probing for OTCR-exclusive APIs (
g_game.forceWalk()) - Checking for vBot-exclusive APIs (
g_game.moveRaw()) - A deferred re-detection runs 1.5 seconds later to catch late-loading APIs
Adapters
| Adapter | Client | Extra APIs |
|---|---|---|
| Base | OTClientV8 (vBot) | Standard game operations |
| OpenTibiaBR | OTCR | forceWalk, stash, imbuements, prey, forge, market |
ClientService API
The global getClient() function returns a unified interface:
local Client = getClient()
Client.getLocalPlayer()
Client.attack(creature)
Client.walk(direction)
-- OTCR-specific (gracefully degrade on vBot)
Client.stashWithdraw(itemId, count)
Client.applyImbuement(slotId, imbuementId, useProtection)
π‘ EventBus
The centralized event dispatcher connects game callbacks to module handlers. All native OTClient callbacks are routed through EventBus so modules can subscribe without interfering with each other.
Key Events
| Event | Source | Consumers |
|---|---|---|
creature:appear |
Native callback | TargetBot, Monster AI |
creature:disappear |
Native callback | TargetBot, Looting |
creature:health |
Native callback | TargetBot, Hunt Analyzer |
player:health |
Native callback | HealBot |
player:position |
Native callback | CaveBot, Spy Level |
effect:missile |
Native callback | Monster AI Spell Tracker |
containers:open_all_complete |
Container Opener | Other modules |
Z-Change Burst Detection
During floor transitions, hundreds of creature events fire in a single frame. EventBus detects this burst (threshold: 5 events per frame) and sets _zBlocked = true, suppressing expensive callbacks for 150 ms.
β±οΈ Unified Tick System
Instead of each module running its own macro() timer, UnifiedTick provides a single 50 ms master tick. Modules register callbacks at their desired intervals:
UnifiedTick.register("myModule", 250, function()
-- runs every 250ms
end)
This reduces timer overhead from 30+ separate timers to one.
πΎ Unified Storage
Per-character persistent storage using JSON serialization:
- Namespace-based access:
UnifiedStorage.get("healing.enabled") - Batch updates for atomicity
- Migration helpers from legacy storage patterns
- Handles sparse array sanitization on startup
π Loading Order
The _Loader.lua organizes initialization into phases:
| Phase | Modules |
|---|---|
| 1 | ACL + Client abstraction |
| 2 | Constants (floor items, food, directions) |
| 3 | Utils (shared, ring buffer, path utils, path strategy) |
| 4 | Core libraries (lib, items, configs, database) |
| 5 | Architecture (EventBus, UnifiedStorage, UnifiedTick) |
| 6 | Feature modules (HealBot, AttackBot, CaveBot, TargetBot, etc.) |
| 7 | Tools (Containers, Dropper, antiRs, etc.) |
| 8 | Analytics (Analyzer, Hunt Analyzer, Spy Level) |
| 9 | Private scripts (user custom scripts) |
| 10 | Activate UnifiedTick |
Each module is error-isolated β a failure in one module doesn't prevent others from loading.
π Design Patterns
| Pattern | Purpose | Where Used |
|---|---|---|
| Event-Driven | Efficient reactivity | EventBus, HealBot, TargetBot |
| State Machine | Deterministic attacks | AttackStateMachine |
| State Machine | Stuck detection + recovery | CaveBot WaypointEngine (NORMALβRECOVERING) |
| Intent Voting | Conflict-free movement | MovementCoordinator |
| LRU Cache | Bounded memory | Creature configs, pathfinding (4-entry) |
| Negative Cache | Skip proven-unreachable paths | PathUtils findPath (500ms TTL) |
| PathCursor Preservation | Avoid redundant A* per tick | Walking engine (cursor survives across ticks for same WP) |
| Step Pipelining | Smooth keyboard walking | Walking engine (2-step lookahead dispatch) |
| One-time Backend Detection | Zero per-call overhead | PathStrategy (resolves API once at init) |
| Adaptive Blacklist Decay | Prevent cascading exclusion | CaveBot recovery (exponential TTL: 15s base, 120s cap) |
| EWMA | Smooth statistics | Monster tracking, cooldowns |
| BFS Traversal | Container opening/looting | ContainerOpener, Looting |
| Engagement Lock | Anti-zigzag targeting | ScenarioManager |
| Lazy Evaluation | Skip unnecessary work | Safety checks, pathfinding |
| Burst Detection | Z-change protection | EventBus |
| SSoT Constants | DRY direction/floor data | Directions, FloorItems modules |
π Module Communication
Modules communicate through three mechanisms:
- EventBus β loose coupling via named events
- Direct API β modules expose public functions (e.g.
CaveBot.isOn()) - Shared state β
nExBotglobal namespace for cross-module data
Circular dependencies are avoided by loading modules in a strict phase order and using deferred event subscriptions.
π¨ Error Handling
- Each module loads inside
pcall()β failures are logged but don't crash the bot - Optional modules (marked in
OPTIONAL_MODULES) fail silently if missing - Load times are tracked per module for profiling
- Errors are collected in
nExBot.loadErrorsfor debugging
π Private Scripts
Users can place custom .lua files in a private/ folder. These are auto-loaded after all core modules, giving them access to the full nExBot API. Private scripts are recursively discovered and sorted alphabetically.
