streamduck_core/modules/
plugins.rs

1//! Plugin API for loading dynamic library files
2
3use std::collections::HashMap;
4use std::ffi::OsStr;
5use std::fs;
6use std::hash::Hasher;
7use std::path::Path;
8use std::sync::{Arc, Mutex};
9use dlopen::Error;
10use crate::modules::{ModuleManager, PluginMetadata, SDModule, UniqueSDModule};
11use dlopen::wrapper::{Container, WrapperApi};
12use dlopen_derive::WrapperApi;
13use image::DynamicImage;
14use tokio::task::{JoinError, spawn_blocking};
15use crate::core::button::Button;
16use crate::core::manager::CoreManager;
17use crate::core::{check_feature_list_for_feature, CoreHandle, UniqueButton, warn_for_feature};
18use crate::modules::components::{ComponentDefinition, UIValue};
19use crate::modules::events::{SDCoreEvent, SDGlobalEvent};
20use crate::{Config, RenderingManager};
21use crate::socket::{SocketManager, UniqueSocketListener};
22use crate::thread::rendering::custom::UniqueRenderer;
23use crate::versions::SUPPORTED_FEATURES;
24
25#[derive(WrapperApi)]
26struct PluginApi {
27    get_metadata: extern fn() -> PluginMetadata,
28    register: fn(socket_manager: Arc<PluginSocketManager>, render_manager: Arc<PluginRenderingManager>, module_manager: Arc<PluginModuleManager>),
29}
30
31#[allow(dead_code)]
32struct PluginProxy {
33    pub wrapper: Arc<Container<PluginApi>>,
34    pub metadata: PluginMetadata,
35    pub plugin: UniqueSDModule
36}
37
38#[async_trait]
39impl SDModule for PluginProxy {
40    fn name(&self) -> String {
41        self.plugin.name()
42    }
43
44    fn components(&self) -> HashMap<String, ComponentDefinition> {
45        self.plugin.components()
46    }
47
48    async fn add_component(&self, core: CoreHandle, button: &mut Button, name: &str) {
49        self.plugin.add_component(core, button, name).await
50    }
51
52    async fn remove_component(&self, core: CoreHandle, button: &mut Button, name: &str) {
53        self.plugin.remove_component(core, button, name).await
54    }
55
56    async fn paste_component(&self, core: CoreHandle, reference_button: &Button, new_button: &mut Button) {
57        self.plugin.paste_component(core, reference_button, new_button).await
58    }
59
60    async fn component_values(&self, core: CoreHandle, button: &Button, name: &str) -> Vec<UIValue> {
61        self.plugin.component_values(core, button, name).await
62    }
63
64    async fn set_component_value(&self, core: CoreHandle, button: &mut Button, name: &str, value: Vec<UIValue>) {
65        self.plugin.set_component_value(core, button, name, value).await
66    }
67
68    fn listening_for(&self) -> Vec<String> {
69        self.plugin.listening_for()
70    }
71
72    async fn settings(&self, core: Arc<CoreManager>) -> Vec<UIValue> {
73        self.plugin.settings(core).await
74    }
75
76    async fn set_setting(&self, core: Arc<CoreManager>, value: Vec<UIValue>) {
77        self.plugin.set_setting(core, value).await
78    }
79
80    async fn global_event(&self, event: SDGlobalEvent) {
81        if check_feature_list_for_feature(&self.metadata.used_features, "global_events") {
82            self.plugin.global_event(event).await
83        }
84    }
85
86    async fn event(&self, core: CoreHandle, event: SDCoreEvent) {
87        if core.check_for_feature("core_events") {
88            self.plugin.event(core, event).await
89        }
90    }
91
92    async fn render(&self, core: CoreHandle, button: &UniqueButton, frame: &mut DynamicImage) {
93        if core.check_for_feature("rendering") {
94            self.plugin.render(core, button, frame).await
95        }
96    }
97
98    fn render_hash(&self, core: CoreHandle, button: &UniqueButton, hash: &mut Box<dyn Hasher>) {
99        if core.check_for_feature("rendering") {
100            self.plugin.render_hash(core, button, hash)
101        }
102    }
103
104    fn metadata(&self) -> PluginMetadata {
105        self.metadata.clone()
106    }
107}
108
109/// Wrapper of socket manager for plugin initialization to use
110pub struct PluginSocketManager {
111    socket_manager: Arc<SocketManager>,
112
113    listeners: Arc<Mutex<Vec<UniqueSocketListener>>>
114}
115
116impl PluginSocketManager {
117    /// Add listener to daemon
118    pub fn add_listener(&self, listener: UniqueSocketListener) {
119        self.listeners.lock().unwrap().push(listener);
120    }
121
122    async fn load_listeners(&self) -> Result<(), JoinError> {
123        let listeners = self.listeners.clone();
124        let listeners = spawn_blocking(move || listeners.lock().unwrap().clone()).await?;
125
126        for listener in listeners {
127            self.socket_manager.add_listener(listener).await;
128        }
129
130        Ok(())
131    }
132}
133
134/// Wrapper of rendering manager for plugin initialization to use
135pub struct PluginRenderingManager {
136    rendering_manager: Arc<RenderingManager>,
137
138    renderers: Arc<Mutex<Vec<UniqueRenderer>>>
139}
140
141impl PluginRenderingManager {
142    /// Add renderer to daemon
143    pub fn add_renderer(&self, renderer: UniqueRenderer) {
144        self.renderers.lock().unwrap().push(renderer);
145    }
146
147    async fn load_renderers(&self) -> Result<(), JoinError> {
148        let renderers = self.renderers.clone();
149        let renderers = spawn_blocking(move || renderers.lock().unwrap().clone()).await?;
150
151        for renderer in renderers {
152            self.rendering_manager.add_custom_renderer(renderer).await;
153        }
154
155        Ok(())
156    }
157}
158
159
160/// Wrapper of module manager for plugin initialization to use
161pub struct PluginModuleManager {
162    // Things required to register modules in core
163    module_manager: Arc<ModuleManager>,
164    metadata: PluginMetadata,
165    wrapper: Arc<Container<PluginApi>>,
166
167    // Collection that plugin will be writing to
168    modules: Arc<Mutex<Vec<UniqueSDModule>>>,
169}
170
171impl PluginModuleManager {
172    /// Add module to daemon
173    pub fn add_module(&self, module: UniqueSDModule) {
174        self.modules.lock().unwrap().push(module);
175    }
176
177    async fn load_modules(&self) -> Result<(), PluginError> {
178        let modules = self.modules.clone();
179        let modules = spawn_blocking(move || modules.lock().unwrap().clone()).await?;
180
181        if modules.is_empty() {
182            return Err(PluginError::NoModulesFound);
183        }
184
185        for module in modules {
186            for component in module.components().keys() {
187                if self.module_manager.get_component(component).await.is_some() {
188                    return Err(PluginError::ComponentConflict(module.name(), component.to_string()))
189                }
190            }
191
192            self.module_manager.add_module(Arc::new(PluginProxy {
193                wrapper: self.wrapper.clone(),
194                metadata: self.metadata.clone(),
195                plugin: module
196            })).await;
197        }
198
199        Ok(())
200    }
201}
202
203/// Returns error if plugin is incompatible
204pub fn compare_plugin_versions(versions: &Vec<(String, String)>) -> Result<(), PluginError> {
205    let core_versions = SUPPORTED_FEATURES.clone().into_iter()
206        .map(|(n, v)| (*n, *v))
207        .collect::<HashMap<&str, &str>>();
208
209    for (name, version) in versions {
210        if let Some(software_version) = core_versions.get(name.as_str()) {
211            if software_version != version {
212                return Err(PluginError::WrongVersion(format!("{} {}", name, version), format!("{} {}", name, software_version)))
213            }
214        } else {
215            return Err(PluginError::TooNew(format!("{} {}", name, version)))
216        }
217    }
218
219    Ok(())
220}
221
222/// Warns about essential features
223fn warn_about_essential_features(meta: &PluginMetadata) {
224    let name = &meta.name;
225    let features = &meta.used_features;
226
227    warn_for_feature(name, &features, "compiler_version");
228    warn_for_feature(name, &features, "plugin_api");
229    warn_for_feature(name, &features, "sdmodule_trait");
230}
231
232/// Loads a plugin into module manager
233pub async fn load_plugin<T: AsRef<OsStr>>(config: Arc<Config>, module_manager: Arc<ModuleManager>, socket_manager: Arc<SocketManager>, render_manager: Arc<RenderingManager>, path: T) -> Result<(), PluginError> {
234    // Loading file as a library, error if cannot load
235    let wrapper: Container<PluginApi> = unsafe { Container::load(path) }?;
236
237    let wrapper = Arc::new(wrapper);
238
239    // Retrieving metadata and comparing versions
240    let metadata = wrapper.get_metadata();
241
242    // Performing checks if enabled
243    if config.plugin_compatibility_checks() {
244        compare_plugin_versions(&metadata.used_features)?;
245    }
246
247    // Warn plugin if metadata doesn't contain essential plugins
248    warn_about_essential_features(&metadata);
249
250    // Adding module if it wasn't defined before
251    if module_manager.get_module(&metadata.name).await.is_none() {
252        let plugin_manager = Arc::new(PluginModuleManager {
253            module_manager,
254            metadata: metadata.clone(),
255            wrapper,
256            modules: Default::default()
257        });
258
259        let plugin_socket_manager = Arc::new(PluginSocketManager {
260                socket_manager,
261                listeners: Default::default()
262        });
263
264        let plugin_rendering_manager = Arc::new(PluginRenderingManager {
265            rendering_manager: render_manager,
266            renderers: Default::default()
267        });
268
269        // Calling register after all checks were done
270        plugin_manager.wrapper.register(plugin_socket_manager.clone(), plugin_rendering_manager.clone(), plugin_manager.clone());
271
272        plugin_manager.load_modules().await?;
273        plugin_socket_manager.load_listeners().await?;
274        plugin_rendering_manager.load_renderers().await?;
275
276        Ok(())
277    } else {
278        Err(PluginError::AlreadyExists(metadata.name))
279    }
280}
281
282/// Loads plugins into module manager from path
283pub async fn load_plugins_from_folder<T: AsRef<OsStr>>(config: Arc<Config>, module_manager: Arc<ModuleManager>, socket_manager: Arc<SocketManager>, render_manager: Arc<RenderingManager>, path: T) {
284    let path = Path::new(&path);
285    match fs::read_dir(path) {
286        Ok(read_dir) => {
287            for item in read_dir {
288                match item {
289                    Ok(entry) => {
290                        if entry.path().is_file() {
291                            if let Some(file_name) = entry.path().file_name() {
292                                log::info!("Loading plugin {:?}", file_name);
293                                match load_plugin(config.clone(), module_manager.clone(), socket_manager.clone(), render_manager.clone(), entry.path()).await {
294                                    Err(err) => match err {
295                                        PluginError::LoadError(err) => log::error!("Failed to load plugin: {}", err),
296                                        PluginError::WrongVersion(plugin, software) => log::error!("Failed to load plugin: Plugin is using unsupported version of '{}', software's using '{}'", plugin, software),
297                                        PluginError::TooNew(version) => log::error!("Failed to load plugin: Software doesn't support '{}', try updating the software", version),
298                                        PluginError::AlreadyExists(name) => log::error!("Failed to load plugin: Module '{}' was already defined", name),
299                                        PluginError::ComponentConflict(name, component_name) => log::error!("Failed to load plugin: Module '{}' is declaring '{}' component, but it was already previously declared by other module", name, component_name),
300                                        PluginError::JoinError(err) => log::error!("Failed to load plugin: {}", err),
301                                        PluginError::NoModulesFound => log::error!("Failed to load plugin: No modules found")
302                                    },
303                                    _ => {}
304                                }
305                            }
306                        }
307                    }
308                    Err(err) => log::error!("Failed to reach entry. {}", err),
309                }
310            }
311        }
312        Err(e) => {
313            if let std::io::ErrorKind::NotFound = e.kind() {
314                log::info!("Loaded no plugins, missing plugins folder")
315            } else {
316                log::error!("Plugins folder is unreachable: {:?}", path);
317            }
318        }
319    }
320}
321
322/// Enum for anything wrong that might happen during plugin loading
323#[derive(Debug)]
324pub enum PluginError {
325    /// Plugin didn't register any modules
326    NoModulesFound,
327    /// Failed to load the plugin library
328    LoadError(dlopen::Error),
329    /// Plugin uses different version of a feature
330    WrongVersion(String, String),
331    /// Plugin uses unknown feature for the daemon
332    TooNew(String),
333    /// Module name already exists
334    AlreadyExists(String),
335    /// Component with the name was already declared (Soon to be removed due to better naming)
336    ComponentConflict(String, String),
337    /// Error spawning a blocking task
338    JoinError(tokio::task::JoinError)
339}
340
341impl From<dlopen::Error> for PluginError {
342    fn from(err: Error) -> Self {
343        PluginError::LoadError(err)
344    }
345}
346
347impl From<tokio::task::JoinError> for PluginError {
348    fn from(err: JoinError) -> Self {
349        PluginError::JoinError(err)
350    }
351}