commonlibsse_ng\skse/
api.rs

1use crate::re::BSTEvent::BSTEventSource;
2use crate::rel::version::Version;
3use crate::skse::impls::stab::{
4    PluginHandle, SKSEDelayFunctorManager, SKSEObjectRegistry, SKSEPersistentObjectStorage,
5};
6use crate::skse::interfaces::{
7    PluginVersionData,
8    load::LoadInterface,
9    messaging::{self, MessagingInterface},
10    object::ObjectInterface,
11    papyrus::PapyrusInterface,
12    scaleform::ScaleformInterface,
13    serialization::SerializationInterface,
14    task::TaskInterface,
15    trampoline::TrampolineInterface,
16};
17use snafu::Snafu;
18use std::sync::OnceLock;
19
20#[derive(Debug, Clone, PartialEq, Snafu)]
21pub enum ApiStorageError {
22    /// Global API storage has not yet been initialized. We must call `skse::init` first in the `SKSE_PluginLoad` function.
23    Uninitialized,
24
25    /// Could not find the definition of the `SKSEPlugin_Version` symbol in this SKSE plugin dll. Therefore, the plugin information could not be retrieved.
26    MissingSymbolSKSEPluginVersion,
27}
28
29#[derive(Debug)]
30pub struct ModCallbackEvent;
31#[derive(Debug)]
32pub struct CameraEvent;
33#[derive(Debug)]
34pub struct CrosshairRefEvent;
35#[derive(Debug)]
36pub struct ActionEvent;
37#[derive(Debug)]
38pub struct NiNodeUpdateEvent;
39
40#[derive(Debug)]
41pub struct APIStorage {
42    /// Your SKSE Plugin name
43    ///
44    /// If the `SKSEPlugin_Version` symbol is not defined, [`Option::None`] is always included.
45    pub plugin_name: Option<&'static str>,
46    /// Your SKSE Plugin author name
47    ///
48    /// If the `SKSEPlugin_Version` symbol is not defined, [`Option::None`] is always included.
49    pub plugin_author: Option<&'static str>,
50    /// Your SKSE Plugin version
51    ///
52    /// If the `SKSEPlugin_Version` symbol is not defined, [`Option::None`] is always included.
53    pub plugin_version: Option<Version>,
54
55    /// The plugin handle (index of how many dlls SKSE has loaded) of this SKSE plugin dll.
56    pub plugin_handle: PluginHandle,
57    pub release_index: u32,
58
59    pub scaleform_interface: ScaleformInterface,
60    pub papyrus_interface: PapyrusInterface,
61    pub serialization_interface: SerializationInterface,
62    pub task_interface: TaskInterface,
63    pub trampoline_interface: TrampolineInterface,
64
65    pub messaging_interface: MessagingInterface,
66    pub mod_callback_event_source: *mut BSTEventSource<ModCallbackEvent>,
67    pub camera_event_source: *mut BSTEventSource<CameraEvent>,
68    pub crosshair_ref_event_source: *mut BSTEventSource<CrosshairRefEvent>,
69    pub action_event_source: *mut BSTEventSource<ActionEvent>,
70    pub ni_node_update_event_source: *mut BSTEventSource<NiNodeUpdateEvent>,
71
72    pub object_interface: ObjectInterface,
73    pub delay_functor_manager: *mut SKSEDelayFunctorManager,
74    pub object_registry: *mut SKSEObjectRegistry,
75    pub persistent_object_storage: *mut SKSEPersistentObjectStorage,
76}
77
78unsafe impl Send for APIStorage {}
79unsafe impl Sync for APIStorage {}
80
81static INSTANCE: OnceLock<APIStorage> = OnceLock::new();
82
83impl APIStorage {
84    /// Returns a reference to the `APIStorage` instance.
85    ///
86    /// # Errors
87    /// Returns an error if the `APIStorage` is not initialized.
88    ///
89    /// # Example
90    /// ```
91    /// use commonlibsse_ng::skse::api::APIStorage;
92    /// let storage = APIStorage::get();
93    /// ```
94    #[inline]
95    pub fn get() -> Result<&'static Self, ApiStorageError> {
96        INSTANCE.get().ok_or(ApiStorageError::Uninitialized)
97    }
98
99    /// Maps over the `APIStorage` instance if it exists and returns a result of `Option<R>`.
100    ///
101    /// # Errors
102    /// Returns an error if the `APIStorage` is not initialized.
103    ///
104    /// # Example
105    /// ```
106    /// use commonlibsse_ng::skse::api::APIStorage;
107    /// let result = APIStorage::map(|storage| storage.plugin_version.clone());
108    /// ```
109    pub fn map<F, R>(f: F) -> Result<R, ApiStorageError>
110    where
111        F: FnOnce(&Self) -> R,
112    {
113        Self::get().map(f)
114    }
115}
116
117/// Initializes global API interfaces through the `QueryInterface` method of `LoadInterface`.
118/// Ensures all interfaces are initialized before use.
119///
120/// # Example
121/// ```
122/// #[unsafe(no_mangle)]
123/// pub extern "C" fn SKSEPlugin_Load(skse: &commonlibsse_ng::skse::interfaces::load::LoadInterface) -> bool {
124///     commonlibsse_ng::skse::init(skse);
125///     true
126/// }
127/// ```
128pub fn init(load_interface: &LoadInterface) {
129    let plugin_handle = load_interface.get_plugin_handle();
130    let release_index = load_interface.get_release_index();
131
132    let scaleform_interface = ScaleformInterface::new(load_interface.query_interface());
133    let papyrus_interface = PapyrusInterface::new(load_interface.query_interface());
134    let serialization_interface = SerializationInterface::new(load_interface.query_interface());
135    let task_interface = TaskInterface::new(load_interface.query_interface());
136    let trampoline_interface = TrampolineInterface::new(load_interface.query_interface());
137    let messaging_interface = MessagingInterface::new(load_interface.query_interface());
138
139    let mod_callback_event_source =
140        messaging_interface.get_event_dispatcher(messaging::Dispatcher::ModEvent);
141
142    let camera_event_source =
143        messaging_interface.get_event_dispatcher(messaging::Dispatcher::CameraEvent);
144
145    let crosshair_ref_event_source =
146        messaging_interface.get_event_dispatcher(messaging::Dispatcher::CrosshairEvent);
147
148    let action_event_source =
149        messaging_interface.get_event_dispatcher(messaging::Dispatcher::ActionEvent);
150
151    let ni_node_update_event_source =
152        messaging_interface.get_event_dispatcher(messaging::Dispatcher::NiNodeUpdateEvent);
153
154    let object_interface = ObjectInterface::new(load_interface.query_interface());
155    let delay_functor_manager = object_interface.get_delay_functor_manager();
156    let object_registry = object_interface.get_object_registry();
157    let persistent_object_storage = object_interface.get_persistent_object_storage();
158
159    let (plugin_name, plugin_author, plugin_version) =
160        PluginVersionData::get_singleton().map_or((None, None, None), |plugin_ver| {
161            (
162                Some(plugin_ver.get_author_name()),
163                Some(plugin_ver.get_author_name()),
164                Some(Version::unpack(plugin_ver.get_plugin_version())),
165            )
166        });
167
168    // ignore double insert
169    let _ = INSTANCE.set(APIStorage {
170        plugin_name,
171        plugin_author,
172        plugin_version,
173
174        plugin_handle,
175        release_index,
176
177        scaleform_interface,
178        papyrus_interface,
179        serialization_interface,
180        task_interface,
181        trampoline_interface,
182
183        messaging_interface,
184        mod_callback_event_source: mod_callback_event_source.cast(),
185        camera_event_source: camera_event_source.cast(),
186        crosshair_ref_event_source: crosshair_ref_event_source.cast(),
187        action_event_source: action_event_source.cast(),
188        ni_node_update_event_source: ni_node_update_event_source.cast(),
189
190        object_interface,
191        delay_functor_manager,
192        object_registry,
193        persistent_object_storage,
194    });
195}
196
197/// Get the plugin's name.
198///
199/// # Errors
200/// - If the internal global API storage is uninitialized because forgot to call `skse::init`
201/// - Returns an error if forgot to define the `SKSEPlugin_Version` symbol in this SKSE plugin dll
202///
203/// # Example
204/// ```
205/// use commonlibsse_ng::skse::api::{get_plugin_name};
206/// match get_plugin_name() {
207///     Ok(name) => println!("Plugin name: {}", name),
208///     Err(e) => println!("Error: {}", e),
209/// }
210/// ```
211#[inline]
212pub fn get_plugin_name() -> Result<&'static str, ApiStorageError> {
213    APIStorage::get()?.plugin_name.ok_or(ApiStorageError::MissingSymbolSKSEPluginVersion)
214}
215
216/// Get the plugin's author.
217///
218/// # Errors
219/// - If the internal global API storage is uninitialized because forgot to call `skse::init`
220/// - Returns an error if forgot to define the `SKSEPlugin_Version` symbol in this SKSE plugin dll
221///
222/// # Example
223/// ```
224/// use commonlibsse_ng::skse::api::{get_plugin_author, ApiStorageError};
225///
226/// assert_eq!(get_plugin_author(), Err(ApiStorageError::Uninitialized));
227/// ```
228#[inline]
229pub fn get_plugin_author() -> Result<&'static str, ApiStorageError> {
230    APIStorage::get()?.plugin_author.ok_or(ApiStorageError::MissingSymbolSKSEPluginVersion)
231}
232
233/// Get the plugin's version.
234///
235/// # Errors
236/// - If the internal global API storage is uninitialized because forgot to call `skse::init`
237/// - Returns an error if forgot to define the `SKSEPlugin_Version` symbol in this SKSE plugin dll
238///
239/// # Example
240/// ```
241/// use commonlibsse_ng::skse::api::get_plugin_version;
242///
243/// match get_plugin_version() {
244///     Ok(version) => println!("Plugin version: {:?}", version),
245///     Err(e) => println!("Error: {}", e),
246/// }
247/// ```
248#[inline]
249pub fn get_plugin_version() -> Result<Version, ApiStorageError> {
250    APIStorage::get()?.plugin_version.clone().ok_or(ApiStorageError::MissingSymbolSKSEPluginVersion)
251}
252
253/// Get the plugin handle (index of how many dlls SKSE has loaded) of this SKSE plugin dll.
254///
255/// # Errors
256/// If the internal global API storage is uninitialized because forgot to call `skse::init`
257///
258/// # Example
259/// ```
260/// use commonlibsse_ng::skse::api::get_plugin_handle;
261///
262/// match get_plugin_handle() {
263///     Ok(handle) => println!("Plugin handle(dll index): {:?}", handle),
264///     Err(e) => println!("Error: {}", e),
265/// }
266/// ```
267#[inline]
268pub fn get_plugin_handle() -> Result<PluginHandle, ApiStorageError> {
269    APIStorage::map(|storage| storage.plugin_handle.clone())
270}
271
272/// Retrieves the release index.
273///
274/// # Errors
275/// If the internal global API storage is uninitialized because forgot to call `skse::init`
276///
277/// # Example
278/// ```
279/// use commonlibsse_ng::skse::api::get_release_index;
280///
281/// match get_release_index() {
282///     Ok(index) => println!("Release index: {}", index),
283///     Err(e) => println!("Error: {}", e),
284/// }
285/// ```
286#[inline]
287pub fn get_release_index() -> Result<u32, ApiStorageError> {
288    APIStorage::map(|storage| storage.release_index)
289}
290
291/// Retrieves the `ScaleformInterface` instance.
292///
293/// # Errors
294/// If the internal global API storage is uninitialized because forgot to call `skse::init`
295///
296/// # Example
297/// ```
298/// use commonlibsse_ng::skse::api::get_scaleform_interface;
299///
300/// match get_scaleform_interface() {
301///     Ok(interface) => println!("Scaleform Interface: {:?}", interface),
302///     Err(e) => println!("Error: {}", e),
303/// }
304/// ```
305#[inline]
306pub fn get_scaleform_interface() -> Result<ScaleformInterface, ApiStorageError> {
307    APIStorage::map(|storage| storage.scaleform_interface.clone())
308}
309
310/// Retrieves the `PapyrusInterface` instance.
311///
312/// # Errors
313/// If the internal global API storage is uninitialized because forgot to call `skse::init`
314///
315/// # Example
316/// ```
317/// use commonlibsse_ng::skse::api::get_papyrus_interface;
318///
319/// match get_papyrus_interface() {
320///     Ok(interface) => println!("Papyrus Interface: {:?}", interface),
321///     Err(e) => println!("Error: {}", e),
322/// }
323/// ```
324#[inline]
325pub fn get_papyrus_interface() -> Result<PapyrusInterface, ApiStorageError> {
326    APIStorage::map(|storage| storage.papyrus_interface.clone())
327}
328
329/// Retrieves the `SerializationInterface` instance.
330///
331/// # Errors
332/// If the internal global API storage is uninitialized because forgot to call `skse::init`
333///
334/// # Example
335/// ```
336/// use commonlibsse_ng::skse::api::get_serialization_interface;
337///
338/// match get_serialization_interface() {
339///     Ok(interface) => println!("Serialization Interface: {:?}", interface),
340///     Err(e) => println!("Error: {}", e),
341/// }
342/// ```
343#[inline]
344pub fn get_serialization_interface() -> Result<SerializationInterface, ApiStorageError> {
345    APIStorage::map(|storage| storage.serialization_interface.clone())
346}
347
348/// Retrieves the `TaskInterface` instance.
349///
350/// # Errors
351/// If the internal global API storage is uninitialized because forgot to call `skse::init`
352///
353/// # Example
354/// ```
355/// use commonlibsse_ng::skse::api::get_task_interface;
356///
357/// match get_task_interface() {
358///     Ok(interface) => println!("Task Interface: {:?}", interface),
359///     Err(e) => println!("Error: {}", e),
360/// }
361/// ```
362#[inline]
363pub fn get_task_interface() -> Result<TaskInterface, ApiStorageError> {
364    APIStorage::map(|storage| storage.task_interface.clone())
365}
366
367/// Retrieves the `MessagingInterface` instance.
368///
369/// # Errors
370/// If the internal global API storage is uninitialized because forgot to call `skse::init`
371///
372/// # Example
373/// ```
374/// use commonlibsse_ng::skse::api::get_messaging_interface;
375///
376/// match get_messaging_interface() {
377///     Ok(interface) => println!("Messaging Interface: {:?}", interface),
378///     Err(e) => println!("Error: {}", e),
379/// }
380/// ```
381#[inline]
382pub fn get_messaging_interface() -> Result<MessagingInterface, ApiStorageError> {
383    APIStorage::map(|storage| storage.messaging_interface.clone())
384}