#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
pub mod agent_client;
pub mod agent_server;
pub mod buffer;
pub mod callback;
pub mod common;
pub mod context;
pub mod controller;
pub mod custom;
pub mod custom_controller;
pub mod error;
pub mod event_sink;
pub mod job;
pub mod notification;
pub mod pipeline;
pub mod resource;
pub mod tasker;
pub mod toolkit;
pub mod util;
pub use common::AndroidNativeControllerConfig;
pub use common::AndroidScreenResolution;
pub use common::ControllerFeature;
pub use common::MaaStatus;
pub use error::{MaaError, MaaResult};
pub use maa_framework_sys as sys;
use std::ffi::CString;
use std::sync::atomic::{AtomicU8, Ordering};
const RUNTIME_CONTEXT_UNKNOWN: u8 = 0;
#[cfg(feature = "dynamic")]
const RUNTIME_CONTEXT_FRAMEWORK: u8 = 1;
const RUNTIME_CONTEXT_AGENT_SERVER: u8 = 2;
static RUNTIME_CONTEXT: AtomicU8 = AtomicU8::new(RUNTIME_CONTEXT_UNKNOWN);
pub fn maa_version() -> &'static str {
unsafe {
std::ffi::CStr::from_ptr(sys::MaaVersion())
.to_str()
.unwrap_or("unknown")
}
}
pub fn set_global_option(
key: sys::MaaGlobalOption,
value: *mut std::ffi::c_void,
size: u64,
) -> MaaResult<()> {
let ret = unsafe { sys::MaaGlobalSetOption(key, value, size) };
common::check_bool(ret)
}
pub fn configure_logging(log_dir: &str) -> MaaResult<()> {
let c_dir = CString::new(log_dir)?;
set_global_option(
sys::MaaGlobalOptionEnum_MaaGlobalOption_LogDir as i32,
c_dir.as_ptr() as *mut _,
c_dir.as_bytes().len() as u64,
)
}
pub fn set_debug_mode(enable: bool) -> MaaResult<()> {
let mut val_bool = if enable { 1u8 } else { 0u8 };
set_global_option(
sys::MaaGlobalOptionEnum_MaaGlobalOption_DebugMode as i32,
&mut val_bool as *mut _ as *mut _,
std::mem::size_of::<u8>() as u64,
)
}
pub fn set_stdout_level(level: sys::MaaLoggingLevel) -> MaaResult<()> {
let mut val = level;
set_global_option(
sys::MaaGlobalOptionEnum_MaaGlobalOption_StdoutLevel as i32,
&mut val as *mut _ as *mut _,
std::mem::size_of::<sys::MaaLoggingLevel>() as u64,
)
}
pub fn set_save_draw(enable: bool) -> MaaResult<()> {
let mut val: u8 = if enable { 1 } else { 0 };
set_global_option(
sys::MaaGlobalOptionEnum_MaaGlobalOption_SaveDraw as i32,
&mut val as *mut _ as *mut _,
std::mem::size_of::<u8>() as u64,
)
}
pub fn set_save_on_error(enable: bool) -> MaaResult<()> {
let mut val: u8 = if enable { 1 } else { 0 };
set_global_option(
sys::MaaGlobalOptionEnum_MaaGlobalOption_SaveOnError as i32,
&mut val as *mut _ as *mut _,
std::mem::size_of::<u8>() as u64,
)
}
pub fn set_draw_quality(quality: i32) -> MaaResult<()> {
let mut val = quality;
set_global_option(
sys::MaaGlobalOptionEnum_MaaGlobalOption_DrawQuality as i32,
&mut val as *mut _ as *mut _,
std::mem::size_of::<i32>() as u64,
)
}
pub fn set_reco_image_cache_limit(limit: u64) -> MaaResult<()> {
let mut val = limit;
set_global_option(
sys::MaaGlobalOptionEnum_MaaGlobalOption_RecoImageCacheLimit as i32,
&mut val as *mut _ as *mut _,
std::mem::size_of::<u64>() as u64,
)
}
pub fn load_plugin(path: &str) -> MaaResult<()> {
let c_path = CString::new(path)?;
let ret = unsafe { sys::MaaGlobalLoadPlugin(c_path.as_ptr()) };
common::check_bool(ret)
}
#[cfg(feature = "dynamic")]
pub fn load_library(path: &std::path::Path) -> Result<(), String> {
let context = runtime_context_from_library_path(path);
unsafe { sys::load_library(path) }?;
RUNTIME_CONTEXT.store(context, Ordering::Relaxed);
Ok(())
}
#[cfg(feature = "dynamic")]
pub fn ensure_library_loaded() -> Result<(), String> {
let lib_name = if cfg!(target_os = "windows") {
"MaaFramework.dll"
} else if cfg!(target_os = "macos") {
"libMaaFramework.dylib"
} else {
"libMaaFramework.so"
};
let mut candidates: Vec<std::path::PathBuf> = Vec::new();
if let Ok(sdk) = std::env::var("MAA_SDK_PATH") {
let sdk = std::path::PathBuf::from(sdk);
candidates.push(sdk.join("bin").join(lib_name));
candidates.push(sdk.join("lib").join(lib_name));
}
let search_roots: Vec<std::path::PathBuf> = std::env::var("CARGO_MANIFEST_DIR")
.map(std::path::PathBuf::from)
.into_iter()
.chain(std::env::current_dir().ok())
.collect();
for root in &search_roots {
if let Ok(entries) = std::fs::read_dir(root) {
for e in entries.flatten() {
let p = e.path();
if p.is_dir() {
if let Some(name) = p.file_name() {
if name.to_string_lossy().starts_with("MAA-") {
candidates.push(p.join("bin").join(lib_name));
}
}
}
}
}
}
if let Ok(cwd) = std::env::current_dir() {
candidates.push(cwd.join("target/debug").join(lib_name));
candidates.push(cwd.join("target/release").join(lib_name));
candidates.push(cwd.join(lib_name));
}
let chosen = candidates.into_iter().find(|p| p.exists());
match chosen {
Some(path) => load_library(&path),
None => Err(
"MaaFramework library not found. Set MAA_SDK_PATH or place SDK (e.g. MAA-*/bin/)."
.to_string(),
),
}
}
pub(crate) fn mark_agent_server_context() {
RUNTIME_CONTEXT.store(RUNTIME_CONTEXT_AGENT_SERVER, Ordering::Relaxed);
}
pub(crate) fn is_agent_server_context() -> bool {
RUNTIME_CONTEXT.load(Ordering::Relaxed) == RUNTIME_CONTEXT_AGENT_SERVER
}
#[cfg(feature = "dynamic")]
fn runtime_context_from_library_path(path: &std::path::Path) -> u8 {
let Some(file_name) = path.file_name() else {
return RUNTIME_CONTEXT_UNKNOWN;
};
let file_name = file_name.to_string_lossy();
if file_name.is_empty() {
return RUNTIME_CONTEXT_UNKNOWN;
}
if file_name.contains("MaaAgentServer") {
RUNTIME_CONTEXT_AGENT_SERVER
} else {
RUNTIME_CONTEXT_FRAMEWORK
}
}