12 KiB
Nexus — Architecture Overview
What Is This
A modular C# automation framework for POE2. The system reads game memory, builds a structured game state, runs AI systems (combat, navigation, threat assessment), and emits input commands. A separate trade pipeline monitors the trade site via a Node.js daemon and executes buy flows.
When the real game isn't available, a standalone Simulator replaces the memory layer with a procedural game world — same bot systems, same interfaces, zero game dependency.
Solution Structure
Nexus.sln (net8.0-windows10.0.19041.0)
│
├── Core Layer (shared types, no dependencies)
│ └── Nexus.Core
│
├── Infrastructure Layer (reads from external sources)
│ ├── Nexus.GameOffsets Pure offset structs for memory reading
│ ├── Nexus.Memory Process memory reading (RPM, pattern scan)
│ ├── Nexus.Screen Screen capture, OCR, grid detection
│ ├── Nexus.Log Client.txt game log watcher
│ └── Nexus.Input Win32 SendInput / Interception driver
│
├── Data Layer (transforms raw data into typed game state)
│ └── Nexus.Data EntityMapper, EntityClassifier, GameDataCache, MemoryPoller, GameStateEnricher
│
├── Logic Layer (AI systems that decide what to do)
│ ├── Nexus.Systems ThreatSystem, MovementSystem, CombatSystem, ResourceSystem, LootSystem
│ ├── Nexus.Engine BotEngine (orchestrator), AreaProgressionSystem, MovementKeyTracker
│ └── Nexus.Pathfinding NavigationController, A* PathFinder
│
├── Game Interaction Layer (acts on the game)
│ ├── Nexus.Game Window focus, input sending, clipboard
│ ├── Nexus.Items Item parsing via Sidekick
│ ├── Nexus.Inventory Stash/inventory grid management
│ ├── Nexus.Navigation Minimap-based real-time navigation
│ └── Nexus.Trade Trade daemon IPC (Node.js Playwright)
│
├── Orchestration Layer (top-level coordination)
│ ├── Nexus.Bot BotOrchestrator, Trade/Mapping/Crafting executors
│ └── Nexus.Ui Avalonia 11.2 desktop GUI (entry point)
│
└── Testing Layer
└── Nexus.Simulator Standalone game world for bot testing
Dependency Flow
Nexus.Core
│
├── Nexus.GameOffsets ──→ Nexus.Memory ──→ Nexus.Data ──→ Nexus.Engine
│ │ │
├── Nexus.Input │ Nexus.Systems
│ │ │
├── Nexus.Screen ◄───────────────────────────────┘ │
├── Nexus.Game │
├── Nexus.Log │
│ │
├── Nexus.Pathfinding ◄─────────────────────────────────────────┘
│
├── Nexus.Items, Nexus.Inventory, Nexus.Navigation, Nexus.Trade
│
├── Nexus.Bot (consumes all above)
│
├── Nexus.Ui (DI hub, entry point, consumes all)
│
└── Nexus.Simulator (Core, Data, Systems, Pathfinding — NOT Memory/Input/Screen)
Data Flow — Per Tick
┌─────────────────────────────────────────────────────────────────────┐
│ MemoryPoller Thread (60Hz hot / 10Hz cold) │
│ │
│ Hot tick (4 RPM calls): │
│ Camera matrix, Player position, Player vitals, Loading state │
│ │
│ Cold tick (full hierarchical read): │
│ Entity tree traversal → classification → EntitySnapshot[] │
│ Terrain grid, Skills, Quests, UI elements │
│ │
│ Writes to GameDataCache (volatile references, lock-free) │
└────────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ BotEngine Logic Thread (25Hz default) │
│ │
│ 1. Read latest GameState from cache │
│ 2. GameStateEnricher → NearestEnemies, ThreatMap, DangerLevel │
│ 3. Clear ActionQueue │
│ 4. NavigationController.Update() → path, DesiredDirection │
│ 5. Run systems in priority order: │
│ ThreatSystem (50) → MovementSystem (100) → │
│ AreaProgressionSystem (199) → NavigationSystem (200) → │
│ CombatSystem (300) → ResourceSystem (400) → LootSystem (500) │
│ 6. ActionQueue.Resolve() → conflict resolution │
│ 7. ExecuteActions() → IInputController │
└────────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ IInputController (Win32 SendInput or Interception driver) │
│ │
│ WASD keys (scan codes), mouse movement (Bézier curves), │
│ skill casts, flask presses, clicks │
└─────────────────────────────────────────────────────────────────────┘
Action Resolution
Systems submit actions to a shared ActionQueue. The queue resolves conflicts:
| Action Type | Behavior |
|---|---|
| FlaskAction | Always passes through |
| MoveAction (priority ≤ 10) | Urgent flee — blocks CastAction |
| MoveAction (priority > 10) | Normal move — allows CastAction alongside |
| CastAction | Passes unless blocked by urgent flee |
| Other (Key, Click, Chat) | Always passes through |
Priority values: lower number = higher priority. ThreatSystem uses priority 5 for emergency flee (blocks all casting).
System Priority Table
| Priority | System | Purpose |
|---|---|---|
| 50 | ThreatSystem | Emergency flee on High/Critical danger |
| 100 | MovementSystem | Soft avoidance via inverse-square repulsion |
| 199 | AreaProgressionSystem | Quest-driven area traversal and transitions |
| 200 | NavigationSystem | Submits NavigationController's direction |
| 300 | CombatSystem | Skill rotation, target selection, kiting |
| 400 | ResourceSystem | Flask usage on life/mana thresholds |
| 500 | LootSystem | Item pickup (stub) |
Thread Model
| Thread | Rate | Responsibility |
|---|---|---|
| MemoryPoller | 60Hz hot, 10Hz cold | Read game memory → GameDataCache |
| BotEngine Logic | 25Hz | Run AI systems → emit actions |
| Render (Simulator only) | vsync | ImGui + Veldrid drawing |
| Trade Daemon | event-driven | Node.js Playwright → stdin/stdout JSON IPC |
| Log Watcher | 200ms poll | Client.txt → area/whisper/trade events |
Cross-thread safety: GameDataCache uses volatile references. No locks — writer (MemoryPoller) atomically swaps reference types, readers (BotEngine) get consistent snapshots.
Simulator Architecture
When the real game isn't available, Nexus.Simulator replaces the memory pipeline:
┌─────────────────────────────────────────────────────┐
│ Nexus.Simulator.exe │
│ │
│ ┌──────────┐ GameState ┌──────────────────┐ │
│ │ SimWorld │───────────────►│ GameDataCache │ │
│ │ (terrain, │ SimPoller + │ (same as prod) │ │
│ │ enemies, │ StateBuilder └────────┬─────────┘ │
│ │ player) │ │ │
│ └─────┬────┘ ┌────────▼─────────┐ │
│ │ │ Bot Systems │ │
│ │◄─────────────────────│ (unchanged) │ │
│ │ SimInputController └──────────────────┘ │
│ │ │
│ ┌─────▼───────────────────────────────────────┐ │
│ │ ImGui + Veldrid Renderer (isometric 2D) │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
Bot systems don't know they're in a simulation — they see identical GameState objects and emit actions to IInputController.
Key Design Decisions
| Decision | Rationale |
|---|---|
| Immutable records (GameState, EntitySnapshot) | Thread-safe sharing without locks |
| Priority-based ActionQueue | Natural conflict resolution without hardcoded if-else |
| Two-tier memory polling (hot/cold) | Balance responsiveness (position at 60Hz) with CPU cost (entities at 10Hz) |
| Scan codes over virtual keys | Games read hardware scan codes, not VK codes |
| Separate Memory → Data layers | Memory reads raw bytes; Data interprets and classifies. Clean testability. |
| IInputController interface | Swap real Win32 input for simulated input without changing bot logic |
| BFS exploration + A* pathfinding | BFS finds what to explore; A* finds how to get there |
| CharacterProfile auto-detection | Automatically applies combat/flask settings per character name |
| External daemons (Trade, OCR) | Isolate browser/OCR concerns from main process |