Skip to main content

tapsdk_pc/
sdk.rs

1//! Core SDK functionality
2
3use std::ffi::{CStr, CString};
4use std::sync::atomic::{AtomicBool, Ordering};
5
6use crate::callback::{self, TapEvent};
7use crate::error::{InitResult, Result, TapSdkError};
8
9/// Global flag to track if SDK is initialized
10static SDK_INITIALIZED: AtomicBool = AtomicBool::new(false);
11
12/// Check if the SDK has been initialized
13pub fn is_initialized() -> bool {
14    SDK_INITIALIZED.load(Ordering::SeqCst)
15}
16
17/// Check if the app needs to restart (should be called before init)
18///
19/// This function should be called before `TapSdk::init()` to check if the game
20/// was launched directly instead of through TapTap. If it returns `true`,
21/// TapTap will relaunch the game and you should exit immediately.
22///
23/// # Arguments
24/// * `client_id` - The client ID from TapTap developer center
25///
26/// # Returns
27/// `true` if the app needs to restart (exit immediately), `false` otherwise
28pub fn restart_app_if_necessary(client_id: &str) -> Result<bool> {
29    let client_id_c = CString::new(client_id)?;
30    let result = unsafe { tapsdk_pc_sys::TapSDK_RestartAppIfNecessary(client_id_c.as_ptr()) };
31    Ok(result)
32}
33
34/// Main TapTap PC SDK wrapper
35///
36/// This struct represents an initialized SDK instance. Only one instance
37/// can exist at a time. When dropped, it will shut down the SDK.
38#[derive(Debug)]
39pub struct TapSdk {
40    _private: (), // Prevent direct construction
41}
42
43impl TapSdk {
44    /// Initialize the TapTap PC SDK
45    ///
46    /// # Arguments
47    /// * `pub_key` - The public key from TapTap developer center
48    ///
49    /// # Returns
50    /// A `TapSdk` instance on success, or an error if initialization failed
51    ///
52    /// # Example
53    /// ```no_run
54    /// use tapsdk_pc::TapSdk;
55    ///
56    /// let sdk = TapSdk::init("your_public_key_here").expect("Failed to init SDK");
57    /// ```
58    pub fn init(pub_key: &str) -> Result<Self> {
59        if SDK_INITIALIZED.swap(true, Ordering::SeqCst) {
60            return Err(TapSdkError::InvalidArgument(
61                "SDK already initialized".to_string(),
62            ));
63        }
64
65        let pub_key_c = CString::new(pub_key)?;
66        let mut err_msg: [std::os::raw::c_char; 1024] = [0; 1024];
67
68        let result = unsafe {
69            tapsdk_pc_sys::TapSDK_Init(err_msg.as_mut_ptr() as *mut _, pub_key_c.as_ptr())
70        };
71
72        let init_result = InitResult::from(result);
73
74        if init_result != InitResult::Ok {
75            SDK_INITIALIZED.store(false, Ordering::SeqCst);
76
77            let error_message = unsafe {
78                CStr::from_ptr(err_msg.as_ptr())
79                    .to_string_lossy()
80                    .into_owned()
81            };
82
83            return Err(TapSdkError::InitFailed {
84                result: init_result,
85                message: error_message,
86            });
87        }
88
89        // Register our callback handlers
90        callback::register_callbacks();
91
92        Ok(TapSdk { _private: () })
93    }
94
95    /// Get the client ID
96    ///
97    /// # Returns
98    /// The client ID string, or `None` if not available
99    pub fn get_client_id(&self) -> Option<String> {
100        let mut buffer: [std::os::raw::c_char; 256] = [0; 256];
101
102        let success = unsafe { tapsdk_pc_sys::TapSDK_GetClientID(buffer.as_mut_ptr()) };
103
104        if success {
105            let client_id = unsafe {
106                CStr::from_ptr(buffer.as_ptr())
107                    .to_string_lossy()
108                    .into_owned()
109            };
110            if client_id.is_empty() {
111                None
112            } else {
113                Some(client_id)
114            }
115        } else {
116            None
117        }
118    }
119
120    /// Poll for events from the SDK
121    ///
122    /// This should be called regularly (e.g., in your game loop) to process
123    /// pending callbacks and receive events.
124    ///
125    /// # Returns
126    /// A vector of events that have occurred since the last poll
127    pub fn run_callbacks(&self) -> Vec<TapEvent> {
128        callback::poll_events()
129    }
130
131    /// Shut down the SDK
132    ///
133    /// This is called automatically when the `TapSdk` instance is dropped,
134    /// but can be called explicitly if needed.
135    pub fn shutdown(self) {
136        // The Drop implementation will handle cleanup
137        drop(self);
138    }
139}
140
141impl Drop for TapSdk {
142    fn drop(&mut self) {
143        // Unregister callbacks first
144        callback::unregister_callbacks();
145
146        // Shut down the SDK
147        unsafe {
148            tapsdk_pc_sys::TapSDK_Shutdown();
149        }
150
151        // Mark SDK as not initialized
152        SDK_INITIALIZED.store(false, Ordering::SeqCst);
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    #[test]
161    fn test_not_initialized() {
162        assert!(!is_initialized());
163    }
164}