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}