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}