This document is for developers who want to contribute to BetterNTE or build on top of its engine.
- Prerequisites
- Project Structure
- Quick Start
- Crate Architecture
- Frontend Architecture
- Tauri IPC Commands
- Scripting
- Flow Engine
- Build & Release
- Debugging Tips
| Dependency | Version | Notes |
|---|---|---|
| Rust | edition 2021+ | Install via rustup |
| Node.js | 18+ | Frontend build |
| pnpm | 8+ | Package manager (npm i -g pnpm) |
| OpenCV | 4.x | Vision recognition; see opencv-rust install guide |
| ONNX Runtime | — | Auto-downloaded by the ort crate; supports CUDA / DirectML acceleration |
| Windows SDK | — | Win32 API calls (screenshots, input simulation, overlay) |
Tip: OpenCV linking configuration is the most common compilation issue. For detailed installation steps and environment variable setup, refer to the opencv-rust install guide. Key environment variables:
OPENCV_LINK_PATHS(lib directory),OPENCV_LINK_LIBS(library names),OPENCV_INCLUDE_PATHS(header directory).
The model files required by the project (OCR, feature matching, etc.) are not distributed with the source code and must be downloaded separately:
- Go to the BetterNTE ModelScope files page and download
assets.zip - Extract it to the project root directory to get the
assets/directory
BetterNTE/
├── assets/ # ← Extract here
│ └── models/
│ ├── paddleocr/ # OCR models
│ ├── superpoint/ # Feature point detection models
│ ├── yolo/ # Object detection models
│ ├── mobilenet_v3_small-onnx-float/
│ └── test.png
├── crates/
├── data/
└── ...
Note: The
assets/directory is excluded in.gitignoreand will not be committed to the repository. Tauri builds will bundle it as bundled resources.
BetterNTE/
├── Cargo.toml # Workspace root config
├── Cargo.lock
├── data/ # Runtime data (scripts, config templates, etc.)
│ ├── main/ # Built-in scripts
│ └── plugins/ # Plugin manifests
├── crates/
│ ├── betternte-core/ # Base types, trait abstractions, config structures
│ ├── betternte-capture/ # Screenshot engine (WGC / DXGI / PrintWindow / ScreenDC / BitBlt)
│ ├── betternte-vision/ # Vision recognition (template matching, OCR, color detection, contour analysis)
│ ├── betternte-input/ # Input simulation (Win32 foreground/background, ADB emulator)
│ ├── betternte-helper/ # Standalone utility library (encoding, geometry, process, regex)
│ ├── betternte-notify/ # Multi-channel push notifications (ServerChan, Telegram, Bark, Webhook)
│ ├── betternte-overlay/ # Win32 overlay window and drawing API
│ ├── betternte-runtime/ # Flow Engine (Step / Transition / Condition)
│ ├── betternte-script/ # Script engine (QuickJS + async bridge)
│ ├── betternte-engine/ # Engine facade, assembles all subsystems
│ └── betternte-client/ # Tauri desktop client
│ ├── src-tauri/ # Rust backend (Tauri commands, engine integration)
│ ├── src/ # React frontend
│ ├── package.json
│ └── vite.config.ts
└── README.md
betternte-helper betternte-core
(no internal deps) (no internal deps)
│ │
│ ┌────────────────┼────────────────┐
│ │ │ │
│ betternte- betternte- betternte-
│ capture vision input
│ │ │ │
│ │ betternte- betternte-
│ │ notify overlay
│ │ │ │
│ └────────┬───────┘ │
│ │ │
│ betternte-runtime │
│ │ │
│ betternte-script ───────────────┘
│ │
│ betternte-engine
│ │
└──────── betternte-client
# 1. Clone the repository
git clone https://github.com/719733328/BetterNTE.git
cd BetterNTE
# 2. Enter the client directory, install dependencies, and start dev mode
cd crates/betternte-client
pnpm install
pnpm tauri devNote:
pnpm tauri devmust be executed from thecrates/betternte-clientdirectory, not the project root.
On first launch, Tauri will automatically compile the Rust backend. Subsequent frontend changes will trigger HMR hot-reload, and Rust code changes will trigger automatic recompilation.
cargo build # Debug mode
cargo build --release # Release mode
cargo check # Type check only, no binary outputcd crates/betternte-client
pnpm dev # Vite dev server on localhost:1420Common dependency for all other crates, defining core types and trait abstractions for the entire project.
Key Types:
EngineConfig— Top-level config structure, deserialized fromengine.yaml, containing 15+ sub-configs (screenshot, hotkeys, overlay, notifications, API, game window, etc.)CaptureFrame— Generic image container, supports cropping, scaling, format conversion, PNG/JPEG/BMP exportGameWindow— Game window info (handle, title, process, DPI, region)EngineEvent— Event bus event enum (TaskStarted, TaskStopped, ScriptLoaded, ConfigChanged, Error, LogMessage)
Key Traits:
ScreenCapture— Screenshot interfaceInputController— Input simulation interfaceTemplateMatcher— Template matching interfaceOcrEngine— OCR interfaceColorDetector— Color detection interfaceWindowFinder— Window finding interface
Concrete implementations of all traits are distributed across
betternte-capture,betternte-input,betternte-vision, etc.
Provides 5 Windows screenshot backends with no third-party screenshot library dependencies:
| Backend | API | Characteristics |
|---|---|---|
WgcCapture |
Windows Graphics Capture | GPU accelerated, persistent session |
DxgiDupCapture |
DXGI Desktop Duplication | GPU, desktop-level capture |
PrintWindowCapture |
GDI PrintWindow | Can capture occluded windows |
ScreenDCCapture |
Screen DC + BitBlt | GDI, can capture occluded windows |
BitBltCapture |
GDI BitBlt | Best compatibility, cannot capture occluded windows |
Factory Functions:
create_capture_engine()— Auto-selects backend based on configresolve_auto_capture_method()— Probes the best available screenshot method on the current system
Vision pipeline based on OpenCV and ONNX Runtime:
- Template Matching:
OpenCvTemplateMatcher— NCC normalized cross-correlation with template caching - OCR:
PaddleOcrEngine— PaddleOCR ONNX models (detection + recognition) - Color Detection:
ColorDetectorImpl— Color range matching - Contour Analysis:
ContourFinder/ContourAnalyzer - Morphology:
Morphology— Erosion, dilation, etc. - Feature Matching:
SuperPointDetector/LightGlueMatcher— ONNX feature detection and matching - Image Preprocessing:
ImagePreprocessor— General image processing utilities
Supports input simulation for both PC native windows and Android emulators:
Win32Input— Win32 implementation (foreground SendInput / background PostMessage)AdbInput— ADB implementation for Android emulatorsInputQueue— Input queue with rate limitingFailoverInputController— Automatic primary/backup switchingInputRecorder/MacroPlayer— Macro recording and playback
Zero-internal-dependency standalone utility crate providing:
- Directory operations (create, copy, delete, size calculation)
- Encoding (Base64, MD5)
- Geometry (point, rectangle, distance, intersection)
- Process info (PID, debug mode, elevation status)
- String processing (Chinese detection, number extraction)
- Windows-specific (DPI, foreground window)
Multi-channel push notification system defining a Notifier trait:
| Channel | Implementation | Platform |
|---|---|---|
| ServerChan | ServerChanNotifier |
|
| Telegram | TelegramNotifier |
Telegram |
| Bark | BarkNotifier |
iOS |
| Webhook | WebhookNotifier |
WeCom, DingTalk, Feishu, Discord, Slack |
NotificationManager manages all channels, providing send_all(), send_to(), test_channel().
Win32 layered transparent window (WS_EX_TRANSPARENT + WS_EX_LAYERED), can overlay on the game window to display debug information:
OverlayWindow— Low-level window operationsOverlayManager— High-level manager, binds to game window, syncs positionOverlayRenderer— Frame rendering (begin_frame → draw → end_frame)DrawingApi— Pixel drawing (rectangles, lines, text, crosshairs, circles, progress bars)
Unified flow execution engine based on the Flow / Step / Transition model:
Flow— Directed graph composed ofStepandTransitionStep— Execution unit, types include script, click, swipe, key_press, wait, flow (nested), group, set_variableTransition— Connection between steps, withConditionconditionsCondition— Condition enum (Always, Template, Ocr, Color, Variable, Hotkey, Script, And/Or/Not)FlowExecutor— Executor main loopVariableStore— Two-layer variable system (default values + persistence), supports reference resolution ($variables.x,$result.y)PermissionGuard— Manifest-based permission sandbox
JavaScript script runtime based on QuickJS, integrated via the rquickjs crate.
Script Types:
SoloTask— One-shot task script (callsstart())Trigger— Frame-driven trigger (callson_enable()+ per-frameon_capture())Library— Reusable module (callscall_function())
QuickJS Async Bridge:
QuickJS itself is synchronous, but all ScriptContext methods are async. The solution:
- Dedicated background thread
qjs-async-bridgewith its own tokio runtime - JS global
__invoke(method, args_json)synchronous function registered - JS wrapper functions wrap
__invokeintoPromise, scripts can directlyawait ctx.click(100, 200) __invokesends async closures to the bridge thread viampsc::channel, pollsrecv_timeout(50ms)to check cancellation- Nested calls (library calling ctx methods) use
dispatch_ctx_method_nested_blocking, spawning independent threads + single-threaded tokio runtime to avoid deadlocks
ScriptContext API (~45 methods):
- Screenshot:
capture(),capture_region() - Recognition:
find_template(),ocr(),get_color(),color_match() - Input:
click(),key_press(),swipe(),type_text() - Wait:
sleep(),wait_for_template(),wait_for_color() - Window:
find_window(),activate_window() - Storage:
storage_get(),storage_set() - File:
read_file(),write_file() - Network:
http_get(),http_post() - IPC:
run_script(),call_library() - Notification:
notify()
Top-level facade; the client only interacts with this crate:
// Lifecycle
EngineBuilder::new(config, base_dir).build() // -> Engine (Idle)
engine.start() // -> Running
engine.stop() // -> IdleOn start():
- Load scripts from all subscription directories
- Sync trigger states
- Bind to target game window
- Start screenshot loop (spawns tokio task at configured FPS)
- Start replay recording (if configured)
EngineBuilder supports custom StepHandler, ConditionHandler, InputRunner for extensibility.
Tauri v2 desktop application. The Rust backend holds Option<Engine> via AppState (protected by tokio::sync::RwLock).
Modules:
lib.rs— Plugin registration, system tray, window management, event bridging, log initializationhotkeys.rs— Global hotkeys (emergency stop, toggle overlay, script/task group triggers)commands/— 55 Tauri IPC commands across 6 modules
Tauri Plugins:
tauri-plugin-shell— Shell command executiontauri-plugin-notification— System notificationstauri-plugin-dialog— File dialogstauri-plugin-clipboard-manager— Clipboardtauri-plugin-global-shortcut— Global hotkeystauri-plugin-single-instance— Single instancetauri-plugin-updater— Auto-update (desktop only)
| Layer | Technology |
|---|---|
| UI Framework | React 19 + TypeScript 6 |
| Build Tool | Vite 8 |
| Routing | react-router-dom 7 |
| State Management | Zustand 5 |
| Styling | Tailwind CSS 4 |
| Icons | lucide-react |
| Code Editor | CodeMirror 6 |
| Flow Editor | @xyflow/react 12 + @dagrejs/dagre |
| Path | Page | Description |
|---|---|---|
/ |
HomePage | Launch page |
/triggers |
TriggerPage | Trigger management |
/scripts |
TaskPage | Script management and execution |
/one-dragon |
OneDragonFlow | Task group orchestration |
/workflow |
FlowEditorPage | Visual flow editor |
/debug |
ScriptDebugPage | Script debug tracing |
/settings |
Settings | Engine configuration |
/input-test |
InputTestPage | Input debugging (dev mode only) |
| Slice | Responsibility |
|---|---|
EngineSlice |
Engine lifecycle, config, status, screenshot testing |
ScriptSlice |
Script/trigger CRUD, run/stop, source read/write |
FlowSlice |
Flow / TaskGroup CRUD, run/stop/progress |
UISlice |
Logs, recent tasks, error dialogs, event listening |
DebugSlice |
Script call tracing |
Rust EventBus sends events to the frontend via app.emit("engine-event", ...). The frontend UISlice.setupEventListener() uses listen() from @tauri-apps/api/event to receive and dispatch events to Zustand state.
A total of 55 commands, grouped by module:
| Command | Description |
|---|---|
init_engine |
Initialize engine (idempotent), loads config, injects data, registers hotkeys |
start_engine |
Start engine |
stop_engine |
Stop engine, release resources |
get_status |
Get engine status (idle/running, current task, script count, version) |
stop_all |
Emergency stop all running tasks |
| Command | Description |
|---|---|
reload_scripts |
Reload scripts from disk |
list_scripts |
List all loaded scripts |
run_script |
Run script (auto-starts engine) |
stop_task |
Stop current task |
enable_trigger / disable_trigger |
Enable/disable trigger |
reload_triggers / list_triggers |
Reload/list triggers |
create_script / delete_script |
Create/delete script |
list_script_files |
List files in script directory |
read_script_source / save_script_source |
Read/write script source (with path traversal protection) |
import_script_asset |
Import asset file to script directory |
| Command | Description |
|---|---|
list_task_groups / save_task_group / delete_task_group |
Task group CRUD |
run_task_group / stop_task_group / get_task_group_progress |
Task group run control |
list_flows / save_flow / delete_flow |
Flow CRUD |
run_flow / stop_flow / get_flow_progress |
Flow run control |
| Command | Description |
|---|---|
input_list_windows / input_bind_window |
Window list and binding |
input_key_down / input_key_up / input_key_tap |
Keyboard simulation |
input_mouse_move / input_mouse_scroll / input_mouse_button / input_mouse_click |
Mouse simulation |
input_demo_* |
Composite input demos |
input_run_js_snippet |
Execute JS code snippet |
| Command | Description |
|---|---|
get_config / save_config_cmd |
Read/write engine config |
get_capture_methods |
List available screenshot methods |
list_subscriptions / save_subscription / delete_subscription |
Script subscription management |
list_windows / find_game_window |
System window enumeration |
test_screenshot |
Test screenshot (returns base64) |
test_notification_channel |
Test push notification channel |
list_game_plugins |
List game plugins |
export_logs |
Export logs |
better_nte_debug_enabled |
Check debug mode |
| Command | Description |
|---|---|
replay_verify_session |
Verify replay session |
replay_verify_artifacts |
Verify replay artifacts |
scripts/
└── my-script/
├── manifest.json # Script metadata
├── main.js # Entry file
└── assets/ # Asset files (template images, etc.)
{
"name": "my-script",
"version": "1.0.0",
"type": "solo_task",
"entry": "main.js",
"permissions": ["capture", "input", "template", "ocr"],
"dependencies": [],
"engine_version": ">=0.0.1"
}type field:
solo_task— One-shot tasktrigger— Frame triggerlibrary— Reusable library
Scripts call engine capabilities through the global ctx object:
// Screenshot
const frame = await ctx.capture();
const region = await ctx.capture_region(x, y, w, h);
// Template matching
const result = await ctx.find_template("button.png", { threshold: 0.8 });
const results = await ctx.find_templates(["a.png", "b.png"]);
// OCR
const text = await ctx.ocr({ x: 0, y: 0, w: 200, h: 50 });
// Color
const color = await ctx.get_color(100, 200);
const matched = await ctx.color_match(100, 200, { r: 255, g: 0, b: 0 }, 30);
// Input
await ctx.click(500, 300);
await ctx.key_press("enter");
await ctx.swipe(100, 200, 300, 200, 500);
// Wait
await ctx.sleep(1000);
await ctx.wait_for_template("dialog.png", { timeout: 5000 });
// Storage
await ctx.storage_set("count", "42");
const val = await ctx.storage_get("count");
// Network
const data = await ctx.http_get("https://api.example.com/status");
// IPC
await ctx.run_script("other-script");
const result = await ctx.call_library("utils", "formatDate", [Date.now()]);
// Notification
await ctx.notify("Task Complete", "Script executed successfully");Flow
├── entry: StepId # Entry step
└── steps: Map<StepId, Step>
├── id: StepId
├── kind: StepKind # script / click / swipe / key_press / wait / flow / group / set_variable
├── transitions: [Transition]
│ ├── condition: Condition
│ ├── target: StepId
│ ├── priority: int
│ └── interrupt: bool
├── on_error: StepId? # Error fallback
├── retry: int
└── timeout: Duration
The Condition enum supports composition:
Condition::Always
Condition::Template { image, threshold }
Condition::Ocr { region, pattern }
Condition::Color { x, y, color, tolerance }
Condition::Variable { name, op, value }
Condition::Hotkey { key }
Condition::Script { script, function }
Condition::And(Box<Condition>, Box<Condition>)
Condition::Or(Box<Condition>, Box<Condition>)
Condition::Not(Box<Condition>)
VariableStore provides two-layer variables:
- Default values: Declared in the Flow definition
- Runtime: Modified during execution via
set_variablesteps - Reference resolution:
$variables.x,$result.y,$steps.z.result.w,$flow_output.k
cargo build # Full workspace debug
cargo build -p betternte-engine # Single crate
cargo check # Type check onlycd crates/betternte-client
pnpm tauri build # Production build (NSIS installer)| Variable | Description |
|---|---|
OPENCV_LINK_PATHS |
OpenCV lib directory path |
OPENCV_LINK_LIBS |
OpenCV library names to link |
OPENCV_INCLUDE_PATHS |
OpenCV header path (usually auto-detected) |
BETTER_NTE_DEBUG |
Set to 1 to enable debug mode |
Tauri build produces an NSIS installer (.exe), containing:
- Compiled Rust binary
- Frontend static assets (
dist/) data/andassets/as bundled resources
Set the environment variable BETTER_NTE_DEBUG=1 to enable debug mode:
- Frontend will show the "Input Test" sidebar entry
- Additional debug panels become available
- Log files are located in the app data directory, using
tracing+tracing-subscriberwith automatic rotation - Export log files via the "Export Logs" button on the settings page
- Frontend
FloatingLogLayerandLogDrawerdisplay logs in real time
The overlay can display in real time:
- Template match results (match position, confidence)
- Crosshairs (mouse/touch point)
- Progress bars
- Custom text annotations
Controlled via the overlay field in engine configuration.
The /input-test route (visible only in debug mode) provides:
- Keyboard key testing
- Mouse click/move testing
- Window binding testing
The /debug route provides script call tracing, recording the parameters and return values of each ctx method call.
Via the input_run_js_snippet Tauri command, you can execute arbitrary JS code snippets at engine runtime to quickly test ctx APIs.