mcvm_plugin/
lib.rs

1#![warn(missing_docs)]
2
3//! This library is used by both MCVM to load plugins, and as a framework for defining
4//! Rust plugins for MCVM to use
5
6use anyhow::{bail, Context};
7use hook_call::HookHandle;
8use hooks::{Hook, OnLoad};
9use mcvm_core::Paths;
10use mcvm_shared::output::MCVMOutput;
11use plugin::Plugin;
12
13/// API for Rust-based plugins to use
14#[cfg(feature = "api")]
15pub mod api;
16/// Implementation for calling hooks
17pub mod hook_call;
18/// Plugin hooks and their definitions
19pub mod hooks;
20/// Serialized output format for plugins
21pub mod output;
22/// Plugins
23pub mod plugin;
24
25pub use mcvm_shared as shared;
26
27/// Environment variable that debugs plugins when set
28pub static PLUGIN_DEBUG_ENV: &str = "MCVM_PLUGIN_DEBUG";
29
30/// Gets whether plugin debugging is enabled
31pub fn plugin_debug_enabled() -> bool {
32	std::env::var(PLUGIN_DEBUG_ENV).unwrap_or_default() == "1"
33}
34
35/// A manager for plugins that is used to call their hooks.
36/// Does not handle actually loading the plugins from files
37#[derive(Debug)]
38pub struct CorePluginManager {
39	plugins: Vec<Plugin>,
40	plugin_list: Vec<String>,
41	mcvm_version: Option<&'static str>,
42}
43
44impl Default for CorePluginManager {
45	fn default() -> Self {
46		Self::new()
47	}
48}
49
50impl CorePluginManager {
51	/// Construct a new PluginManager
52	pub fn new() -> Self {
53		Self {
54			plugins: Vec::new(),
55			plugin_list: Vec::new(),
56			mcvm_version: None,
57		}
58	}
59
60	/// Set the MCVM version of the manager
61	pub fn set_mcvm_version(&mut self, version: &'static str) {
62		self.mcvm_version = Some(version);
63	}
64
65	/// Add a plugin to the manager
66	pub fn add_plugin(
67		&mut self,
68		plugin: Plugin,
69		paths: &Paths,
70		o: &mut impl MCVMOutput,
71	) -> anyhow::Result<()> {
72		// Update the plugin list
73		self.plugin_list.push(plugin.get_id().clone());
74
75		// Call the on_load hook
76		let result = plugin
77			.call_hook(&OnLoad, &(), paths, self.mcvm_version, &self.plugin_list, o)
78			.context("Failed to call on_load hook of plugin")?;
79		if let Some(result) = result {
80			result.result(o)?;
81		}
82
83		self.plugins.push(plugin);
84
85		Ok(())
86	}
87
88	/// Call a plugin hook on the manager and collects the results into a Vec
89	pub fn call_hook<H: Hook>(
90		&self,
91		hook: H,
92		arg: &H::Arg,
93		paths: &Paths,
94		o: &mut impl MCVMOutput,
95	) -> anyhow::Result<Vec<HookHandle<H>>> {
96		let mut out = Vec::new();
97		for plugin in &self.plugins {
98			let result = plugin
99				.call_hook(&hook, arg, paths, self.mcvm_version, &self.plugin_list, o)
100				.with_context(|| format!("Hook failed for plugin {}", plugin.get_id()))?;
101			out.extend(result);
102		}
103
104		Ok(out)
105	}
106
107	/// Call a plugin hook on the manager on a specific plugin
108	pub fn call_hook_on_plugin<H: Hook>(
109		&self,
110		hook: H,
111		plugin_id: &str,
112		arg: &H::Arg,
113		paths: &Paths,
114		o: &mut impl MCVMOutput,
115	) -> anyhow::Result<Option<HookHandle<H>>> {
116		for plugin in &self.plugins {
117			if plugin.get_id() == plugin_id {
118				let result = plugin
119					.call_hook(&hook, arg, paths, self.mcvm_version, &self.plugin_list, o)
120					.context("Plugin hook failed")?;
121				return Ok(result);
122			}
123		}
124
125		bail!("No plugin found that matched the given ID")
126	}
127
128	/// Iterate over the plugins
129	pub fn iter_plugins(&self) -> impl Iterator<Item = &Plugin> {
130		self.plugins.iter()
131	}
132
133	/// Checks whether the given plugin is present and enabled in the manager
134	pub fn has_plugin(&self, plugin_id: &str) -> bool {
135		self.plugin_list.iter().any(|x| x == plugin_id)
136	}
137}