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:

  1. Checking for OTCR-exclusive module files (game_cyclopedia, game_forge)
  2. Probing for OTCR-exclusive APIs (g_game.forceWalk())
  3. Checking for vBot-exclusive APIs (g_game.moveRaw())
  4. 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:

  1. EventBus β€” loose coupling via named events
  2. Direct API β€” modules expose public functions (e.g. CaveBot.isOn())
  3. Shared state β€” nExBot global 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.loadErrors for 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.