From 44a0e3f947c1c6be6743a80642cb4db0c7cdf856 Mon Sep 17 00:00:00 2001 From: BD103 <59022059+BD103@users.noreply.github.com> Date: Thu, 22 Feb 2024 08:28:52 -0500 Subject: [PATCH] Document all members of `bevy_dynamic_plugin` (#12029) # Objective - Some members of `bevy_dynamic_plugin` are not documented. - Part of #3492. ## Solution - Add documentation to members missing it in `bevy_dynamic_plugin`. - Update existing documentation for clarity and formatting. --- ## Changelog - Completely document `bevy_dynamic_plugin`. --------- Co-authored-by: Alice Cecile Co-authored-by: James Liu --- crates/bevy_dynamic_plugin/src/lib.rs | 22 ++++++++++++++++++++-- crates/bevy_dynamic_plugin/src/loader.rs | 23 +++++++++++++++-------- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/crates/bevy_dynamic_plugin/src/lib.rs b/crates/bevy_dynamic_plugin/src/lib.rs index 961ae5a19f5b2..3a620cee1f6be 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 656e3778490e3..94c283a886e78 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; }