Skip to main content

maa_framework/
agent_server.rs

1//! Agent server for hosting custom components.
2//!
3//! This module allows hosting custom recognition and action components
4//! in a separate process, which can be connected to by AgentClient.
5
6use crate::{MaaError, MaaResult, callback, common, sys};
7use std::ffi::CString;
8
9/// Static functions for AgentServer management.
10///
11/// AgentServer hosts custom recognitions and actions that can be
12/// accessed remotely by AgentClient instances.
13pub struct AgentServer;
14
15impl AgentServer {
16    #[inline]
17    fn mark_context() {
18        crate::mark_agent_server_context();
19    }
20
21    /// Register a custom recognition with the AgentServer.
22    ///
23    /// The recognition will be available to connected AgentClients.
24    ///
25    /// The framework rejects an empty name and any name already taken by a custom
26    /// recognition or action on this server; `Err(MaaError::InvalidArgument)` is returned.
27    pub fn register_custom_recognition(
28        name: &str,
29        reco: Box<dyn crate::custom::CustomRecognition>,
30    ) -> MaaResult<()> {
31        Self::mark_context();
32        let c_name = CString::new(name)?;
33        let reco_ptr = Box::into_raw(Box::new(reco));
34        let reco_ptr_void = reco_ptr as *mut std::ffi::c_void;
35
36        unsafe {
37            let ret = sys::MaaAgentServerRegisterCustomRecognition(
38                c_name.as_ptr(),
39                Some(crate::custom::custom_recognition_trampoline),
40                reco_ptr_void,
41            );
42            if ret == 0 {
43                let _ = Box::from_raw(reco_ptr);
44                return Err(MaaError::InvalidArgument(format!(
45                    "custom recognition name '{name}' is empty or already registered"
46                )));
47            }
48        }
49
50        Ok(())
51    }
52
53    /// Register a custom action with the AgentServer.
54    ///
55    /// The action will be available to connected AgentClients.
56    ///
57    /// The framework rejects an empty name and any name already taken by a custom
58    /// recognition or action on this server; `Err(MaaError::InvalidArgument)` is returned.
59    pub fn register_custom_action(
60        name: &str,
61        action: Box<dyn crate::custom::CustomAction>,
62    ) -> MaaResult<()> {
63        Self::mark_context();
64        let c_name = CString::new(name)?;
65        let action_ptr = Box::into_raw(Box::new(action));
66        let action_ptr_void = action_ptr as *mut std::ffi::c_void;
67
68        unsafe {
69            let ret = sys::MaaAgentServerRegisterCustomAction(
70                c_name.as_ptr(),
71                Some(crate::custom::custom_action_trampoline),
72                action_ptr_void,
73            );
74            if ret == 0 {
75                let _ = Box::from_raw(action_ptr);
76                return Err(MaaError::InvalidArgument(format!(
77                    "custom action name '{name}' is empty or already registered"
78                )));
79            }
80        }
81        Ok(())
82    }
83
84    /// Add a resource event sink.
85    pub fn add_resource_sink<F>(callback: F) -> MaaResult<sys::MaaSinkId>
86    where
87        F: Fn(&str, &str) + Send + Sync + 'static,
88    {
89        Self::mark_context();
90        let (cb, arg) = callback::EventCallback::new(callback);
91        let sink_id = unsafe { sys::MaaAgentServerAddResourceSink(cb, arg) };
92        if sink_id != 0 {
93            Ok(sink_id)
94        } else {
95            unsafe { callback::EventCallback::drop_callback(arg) };
96            Err(MaaError::FrameworkError(0))
97        }
98    }
99
100    /// Add a controller event sink.
101    pub fn add_controller_sink<F>(callback: F) -> MaaResult<sys::MaaSinkId>
102    where
103        F: Fn(&str, &str) + Send + Sync + 'static,
104    {
105        Self::mark_context();
106        let (cb, arg) = callback::EventCallback::new(callback);
107        let sink_id = unsafe { sys::MaaAgentServerAddControllerSink(cb, arg) };
108        if sink_id != 0 {
109            Ok(sink_id)
110        } else {
111            unsafe { callback::EventCallback::drop_callback(arg) };
112            Err(MaaError::FrameworkError(0))
113        }
114    }
115
116    /// Add a tasker event sink.
117    pub fn add_tasker_sink<F>(callback: F) -> MaaResult<sys::MaaSinkId>
118    where
119        F: Fn(&str, &str) + Send + Sync + 'static,
120    {
121        Self::mark_context();
122        let (cb, arg) = callback::EventCallback::new(callback);
123        let sink_id = unsafe { sys::MaaAgentServerAddTaskerSink(cb, arg) };
124        if sink_id != 0 {
125            Ok(sink_id)
126        } else {
127            unsafe { callback::EventCallback::drop_callback(arg) };
128            Err(MaaError::FrameworkError(0))
129        }
130    }
131
132    /// Add a context event sink.
133    pub fn add_context_sink<F>(callback: F) -> MaaResult<sys::MaaSinkId>
134    where
135        F: Fn(&str, &str) + Send + Sync + 'static,
136    {
137        Self::mark_context();
138        let (cb, arg) = callback::EventCallback::new(callback);
139        let sink_id = unsafe { sys::MaaAgentServerAddContextSink(cb, arg) };
140        if sink_id != 0 {
141            Ok(sink_id)
142        } else {
143            unsafe { callback::EventCallback::drop_callback(arg) };
144            Err(MaaError::FrameworkError(0))
145        }
146    }
147
148    /// Start the AgentServer.
149    ///
150    /// # Arguments
151    /// * `identifier` - Connection identifier for clients to connect to
152    pub fn start_up(identifier: &str) -> MaaResult<()> {
153        Self::mark_context();
154        let c_id = CString::new(identifier)?;
155        let ret = unsafe { sys::MaaAgentServerStartUp(c_id.as_ptr()) };
156        common::check_bool(ret)
157    }
158
159    /// Shut down the AgentServer.
160    pub fn shut_down() {
161        Self::mark_context();
162        unsafe { sys::MaaAgentServerShutDown() }
163    }
164
165    /// Block until the server shuts down.
166    pub fn join() {
167        Self::mark_context();
168        unsafe { sys::MaaAgentServerJoin() }
169    }
170
171    /// Detach the server to run in background.
172    pub fn detach() {
173        Self::mark_context();
174        unsafe { sys::MaaAgentServerDetach() }
175    }
176}