diff --git a/crates/bevy_dynamic_plugin/src/lib.rs b/crates/bevy_dynamic_plugin/src/lib.rs index 961ae5a19f..3a620cee1f 100644 --- a/crates/bevy_dynamic_plugin/src/lib.rs +++ b/crates/bevy_dynamic_plugin/src/lib.rs @@ -1,5 +1,23 @@ -// FIXME(3492): remove once docs are ready -#![allow(missing_docs)] +//! Bevy's dynamic plugin loading functionality. +//! +//! This crate allows loading dynamic libraries (`.dylib`, `.so`) that export a single +//! [`Plugin`](bevy_app::Plugin). For usage, see [`dynamically_load_plugin`]. +//! +//! Note that dynamic linking and loading is inherently unsafe because it allows executing foreign +//! code. Additionally, Rust does not have a stable ABI and may produce +//! incompatible libraries across Rust versions, or even subsequent compilations. This will not work +//! well in scenarios such as modding, but can work if the dynamic plugins and the main app are +//! built at the same time, such as with Downloadable Content (DLC) packs. +//! +//! You may be interested in these safer alternatives: +//! +//! - [Bevy Assets - Scripting]: Scripting and modding libraries for Bevy +//! - [Bevy Assets - Development tools]: Hot reloading and other development functionality +//! - [`stabby`]: Stable Rust ABI +//! +//! [Bevy Assets - Scripting]: https://bevyengine.org/assets/#scripting +//! [Bevy Assets - Development tools]: https://bevyengine.org/assets/#development-tools +//! [`stabby`]: https://github.com/ZettaScaleLabs/stabby mod loader; diff --git a/crates/bevy_dynamic_plugin/src/loader.rs b/crates/bevy_dynamic_plugin/src/loader.rs index 656e377849..94c283a886 100644 --- a/crates/bevy_dynamic_plugin/src/loader.rs +++ b/crates/bevy_dynamic_plugin/src/loader.rs @@ -7,8 +7,10 @@ use bevy_app::{App, CreatePlugin, Plugin}; /// Errors that can occur when loading a dynamic plugin #[derive(Debug, Error)] pub enum DynamicPluginLoadError { + /// An error occurred when loading a dynamic library. #[error("cannot load library for dynamic plugin: {0}")] Library(#[source] libloading::Error), + /// An error occurred when loading a library without a valid Bevy plugin. #[error("dynamic library does not contain a valid Bevy dynamic plugin")] Plugin(#[source] libloading::Error), } @@ -18,22 +20,22 @@ pub enum DynamicPluginLoadError { /// /// # Safety /// -/// The specified plugin must be linked against the exact same libbevy.so as this program. +/// The specified plugin must be linked against the exact same `libbevy.so` as this program. /// In addition the `_bevy_create_plugin` symbol must not be manually created, but instead created /// by deriving `DynamicPlugin` on a unit struct implementing [`Plugin`]. /// -/// Dynamically loading plugins is orchestrated through dynamic linking. When linking against foreign -/// code, initialization routines may be run (as well as termination routines when the program exits). -/// The caller of this function is responsible for ensuring these routines are sound. For more -/// information, please see the safety section of [`libloading::Library::new`]. +/// Dynamically loading plugins is orchestrated through dynamic linking. When linking against +/// foreign code, initialization routines may be run (as well as termination routines when the +/// program exits). The caller of this function is responsible for ensuring these routines are +/// sound. For more information, please see the safety section of [`libloading::Library::new`]. pub unsafe fn dynamically_load_plugin>( path: P, ) -> Result<(Library, Box), DynamicPluginLoadError> { // SAFETY: Caller must follow the safety requirements of Library::new. let lib = unsafe { Library::new(path).map_err(DynamicPluginLoadError::Library)? }; - // SAFETY: Loaded plugins are not allowed to specify `_bevy_create_plugin` symbol manually, but must - // instead automatically generate it through `DynamicPlugin`. + // SAFETY: Loaded plugins are not allowed to specify `_bevy_create_plugin` symbol manually, but + // must instead automatically generate it through `DynamicPlugin`. let func: Symbol = unsafe { lib.get(b"_bevy_create_plugin") .map_err(DynamicPluginLoadError::Plugin)? @@ -46,10 +48,15 @@ pub unsafe fn dynamically_load_plugin>( Ok((lib, plugin)) } +/// An extension trait for [`App`] that allows loading dynamic plugins. pub trait DynamicPluginExt { + /// Dynamically links a plugin at the given path, registering the plugin. + /// + /// For more details, see [`dynamically_load_plugin`]. + /// /// # Safety /// - /// Same as [`dynamically_load_plugin`]. + /// See [`dynamically_load_plugin`]'s safety section. unsafe fn load_plugin>(&mut self, path: P) -> &mut Self; }