diff --git a/pumpkin-api-macros/src/lib.rs b/pumpkin-api-macros/src/lib.rs index 478bc5920b..55c455d6f8 100644 --- a/pumpkin-api-macros/src/lib.rs +++ b/pumpkin-api-macros/src/lib.rs @@ -66,6 +66,9 @@ pub fn plugin_impl(_attr: TokenStream, item: TokenStream) -> TokenStream { description: env!("CARGO_PKG_DESCRIPTION"), }; + #[unsafe(no_mangle)] + pub static PUMPKIN_API_VERSION: u32 = pumpkin::plugin::PLUGIN_API_VERSION; + #input_struct impl pumpkin::plugin::Plugin for #struct_ident { diff --git a/pumpkin/src/plugin/loader/mod.rs b/pumpkin/src/plugin/loader/mod.rs index 5f9e2ebd55..807aa3cac6 100644 --- a/pumpkin/src/plugin/loader/mod.rs +++ b/pumpkin/src/plugin/loader/mod.rs @@ -56,4 +56,17 @@ pub enum LoaderError { #[error("Invalid loader data")] InvalidLoaderData, + + #[error( + "Plugin was built for an incompatible API version. Please rebuild it against this Pumpkin build." + )] + ApiVersionMissing, + + #[error( + "Plugin API version mismatch (plugin {plugin_version}, server {server_version}). Please rebuild it against this Pumpkin build." + )] + ApiVersionMismatch { + plugin_version: u32, + server_version: u32, + }, } diff --git a/pumpkin/src/plugin/loader/native.rs b/pumpkin/src/plugin/loader/native.rs index 61b0da3e69..fec386473d 100644 --- a/pumpkin/src/plugin/loader/native.rs +++ b/pumpkin/src/plugin/loader/native.rs @@ -2,7 +2,10 @@ use std::any::Any; use libloading::Library; -use crate::plugin::loader::{PluginLoadFuture, PluginUnloadFuture}; +use crate::plugin::{ + PLUGIN_API_VERSION, + loader::{PluginLoadFuture, PluginUnloadFuture}, +}; use super::{LoaderError, Path, Plugin, PluginLoader, PluginMetadata}; @@ -16,6 +19,21 @@ impl PluginLoader for NativePluginLoader { let library = unsafe { Library::new(&path) } .map_err(|e| LoaderError::LibraryLoad(e.to_string()))?; + // Ensure this plugin was built against a compatible Pumpkin plugin API version + let plugin_api_version = unsafe { + match library.get::<*const u32>(b"PUMPKIN_API_VERSION") { + Ok(symbol) => **symbol, + Err(_) => return Err(LoaderError::ApiVersionMissing), + } + }; + + if plugin_api_version != PLUGIN_API_VERSION { + return Err(LoaderError::ApiVersionMismatch { + plugin_version: plugin_api_version, + server_version: PLUGIN_API_VERSION, + }); + } + // 2. Extract Metadata (METADATA) let metadata = unsafe { &**library diff --git a/pumpkin/src/plugin/mod.rs b/pumpkin/src/plugin/mod.rs index 1fa6684d10..549272943c 100644 --- a/pumpkin/src/plugin/mod.rs +++ b/pumpkin/src/plugin/mod.rs @@ -18,6 +18,10 @@ pub use api::*; type BoxFuture<'a, T> = Pin + Send + 'a>>; +/// Bump this whenever the public plugin API or any event layout changes in a way +/// that makes old binary plugins incompatible. +pub const PLUGIN_API_VERSION: u32 = 2; + /// A trait for handling events dynamically. /// /// This trait allows for handling events of any type that implements the `Event` trait.