poe2-bot/docs/architecture-overview.md
2026-03-07 09:53:57 -05:00

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