mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Update App:is_plugin_added
to work inside Plugin::finish
and Plugin::clean
(#12761)
# Objective I have been trying to check for the existing of some plugins via `App::is_plugin_added` to conditionally run some behaviour in the `Plugin::finish` part of my plugin, before realizing that the plugin registry is actually not available during this step. This is because the `App::is_plugin_added` using the plugin registry to check for previous registration. ## Solution - Switch the `App::is_plugin_added` to use the list of plugin names to check for previous registrations - Add a unit test showcasing that `App::is_plugin_added` works during `Plugin::finish`
This commit is contained in:
parent
16531fb3e3
commit
f73950767b
2 changed files with 53 additions and 40 deletions
|
@ -209,15 +209,15 @@ impl App {
|
||||||
let mut overall_plugins_state = match self.main_mut().plugins_state {
|
let mut overall_plugins_state = match self.main_mut().plugins_state {
|
||||||
PluginsState::Adding => {
|
PluginsState::Adding => {
|
||||||
let mut state = PluginsState::Ready;
|
let mut state = PluginsState::Ready;
|
||||||
let plugins = std::mem::take(&mut self.main_mut().plugins);
|
let plugins = std::mem::take(&mut self.main_mut().plugin_registry);
|
||||||
for plugin in &plugins.registry {
|
for plugin in &plugins {
|
||||||
// plugins installed to main need to see all sub-apps
|
// plugins installed to main need to see all sub-apps
|
||||||
if !plugin.ready(self) {
|
if !plugin.ready(self) {
|
||||||
state = PluginsState::Adding;
|
state = PluginsState::Adding;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.main_mut().plugins = plugins;
|
self.main_mut().plugin_registry = plugins;
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
state => state,
|
state => state,
|
||||||
|
@ -235,12 +235,12 @@ impl App {
|
||||||
/// plugins are ready, but can be useful for situations where you want to use [`App::update`].
|
/// plugins are ready, but can be useful for situations where you want to use [`App::update`].
|
||||||
pub fn finish(&mut self) {
|
pub fn finish(&mut self) {
|
||||||
// plugins installed to main should see all sub-apps
|
// plugins installed to main should see all sub-apps
|
||||||
let plugins = std::mem::take(&mut self.main_mut().plugins);
|
let plugins = std::mem::take(&mut self.main_mut().plugin_registry);
|
||||||
for plugin in &plugins.registry {
|
for plugin in &plugins {
|
||||||
plugin.finish(self);
|
plugin.finish(self);
|
||||||
}
|
}
|
||||||
let main = self.main_mut();
|
let main = self.main_mut();
|
||||||
main.plugins = plugins;
|
main.plugin_registry = plugins;
|
||||||
main.plugins_state = PluginsState::Finished;
|
main.plugins_state = PluginsState::Finished;
|
||||||
self.sub_apps.iter_mut().skip(1).for_each(|s| s.finish());
|
self.sub_apps.iter_mut().skip(1).for_each(|s| s.finish());
|
||||||
}
|
}
|
||||||
|
@ -249,12 +249,12 @@ impl App {
|
||||||
/// [`App::finish`], but can be useful for situations where you want to use [`App::update`].
|
/// [`App::finish`], but can be useful for situations where you want to use [`App::update`].
|
||||||
pub fn cleanup(&mut self) {
|
pub fn cleanup(&mut self) {
|
||||||
// plugins installed to main should see all sub-apps
|
// plugins installed to main should see all sub-apps
|
||||||
let plugins = std::mem::take(&mut self.main_mut().plugins);
|
let plugins = std::mem::take(&mut self.main_mut().plugin_registry);
|
||||||
for plugin in &plugins.registry {
|
for plugin in &plugins {
|
||||||
plugin.cleanup(self);
|
plugin.cleanup(self);
|
||||||
}
|
}
|
||||||
let main = self.main_mut();
|
let main = self.main_mut();
|
||||||
main.plugins = plugins;
|
main.plugin_registry = plugins;
|
||||||
main.plugins_state = PluginsState::Cleaned;
|
main.plugins_state = PluginsState::Cleaned;
|
||||||
self.sub_apps.iter_mut().skip(1).for_each(|s| s.cleanup());
|
self.sub_apps.iter_mut().skip(1).for_each(|s| s.cleanup());
|
||||||
}
|
}
|
||||||
|
@ -490,8 +490,7 @@ impl App {
|
||||||
if plugin.is_unique()
|
if plugin.is_unique()
|
||||||
&& !self
|
&& !self
|
||||||
.main_mut()
|
.main_mut()
|
||||||
.plugins
|
.plugin_names
|
||||||
.names
|
|
||||||
.insert(plugin.name().to_string())
|
.insert(plugin.name().to_string())
|
||||||
{
|
{
|
||||||
Err(AppError::DuplicatePlugin {
|
Err(AppError::DuplicatePlugin {
|
||||||
|
@ -501,10 +500,9 @@ impl App {
|
||||||
|
|
||||||
// Reserve position in the plugin registry. If the plugin adds more plugins,
|
// Reserve position in the plugin registry. If the plugin adds more plugins,
|
||||||
// they'll all end up in insertion order.
|
// they'll all end up in insertion order.
|
||||||
let index = self.main().plugins.registry.len();
|
let index = self.main().plugin_registry.len();
|
||||||
self.main_mut()
|
self.main_mut()
|
||||||
.plugins
|
.plugin_registry
|
||||||
.registry
|
|
||||||
.push(Box::new(PlaceholderPlugin));
|
.push(Box::new(PlaceholderPlugin));
|
||||||
|
|
||||||
self.main_mut().plugin_build_depth += 1;
|
self.main_mut().plugin_build_depth += 1;
|
||||||
|
@ -515,7 +513,7 @@ impl App {
|
||||||
resume_unwind(payload);
|
resume_unwind(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.main_mut().plugins.registry[index] = plugin;
|
self.main_mut().plugin_registry[index] = plugin;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1008,6 +1006,18 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PluginE;
|
||||||
|
|
||||||
|
impl Plugin for PluginE {
|
||||||
|
fn build(&self, _app: &mut App) {}
|
||||||
|
|
||||||
|
fn finish(&self, app: &mut App) {
|
||||||
|
if app.is_plugin_added::<PluginA>() {
|
||||||
|
panic!("cannot run if PluginA is already registered");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_add_two_plugins() {
|
fn can_add_two_plugins() {
|
||||||
App::new().add_plugins((PluginA, PluginB));
|
App::new().add_plugins((PluginA, PluginB));
|
||||||
|
@ -1078,6 +1088,15 @@ mod tests {
|
||||||
assert_eq!(app.world().entities().len(), 2);
|
assert_eq!(app.world().entities().len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_is_plugin_added_works_during_finish() {
|
||||||
|
let mut app = App::new();
|
||||||
|
app.add_plugins(PluginA);
|
||||||
|
app.add_plugins(PluginE);
|
||||||
|
app.finish();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_derive_app_label() {
|
fn test_derive_app_label() {
|
||||||
use super::AppLabel;
|
use super::AppLabel;
|
||||||
|
|
|
@ -10,17 +10,11 @@ use bevy_ecs::{
|
||||||
};
|
};
|
||||||
#[cfg(feature = "trace")]
|
#[cfg(feature = "trace")]
|
||||||
use bevy_utils::tracing::info_span;
|
use bevy_utils::tracing::info_span;
|
||||||
use bevy_utils::{default, HashMap, HashSet};
|
use bevy_utils::{HashMap, HashSet};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
type ExtractFn = Box<dyn Fn(&mut World, &mut World) + Send>;
|
type ExtractFn = Box<dyn Fn(&mut World, &mut World) + Send>;
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub(crate) struct PluginStore {
|
|
||||||
pub(crate) registry: Vec<Box<dyn Plugin>>,
|
|
||||||
pub(crate) names: HashSet<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A secondary application with its own [`World`]. These can run independently of each other.
|
/// A secondary application with its own [`World`]. These can run independently of each other.
|
||||||
///
|
///
|
||||||
/// These are useful for situations where certain processes (e.g. a render thread) need to be kept
|
/// These are useful for situations where certain processes (e.g. a render thread) need to be kept
|
||||||
|
@ -67,8 +61,11 @@ pub(crate) struct PluginStore {
|
||||||
pub struct SubApp {
|
pub struct SubApp {
|
||||||
/// The data of this application.
|
/// The data of this application.
|
||||||
world: World,
|
world: World,
|
||||||
/// Metadata for installed plugins.
|
/// List of plugins that have been added.
|
||||||
pub(crate) plugins: PluginStore,
|
pub(crate) plugin_registry: Vec<Box<dyn Plugin>>,
|
||||||
|
/// The names of plugins that have been added to this app. (used to track duplicates and
|
||||||
|
/// already-registered plugins)
|
||||||
|
pub(crate) plugin_names: HashSet<String>,
|
||||||
/// Panics if an update is attempted while plugins are building.
|
/// Panics if an update is attempted while plugins are building.
|
||||||
pub(crate) plugin_build_depth: usize,
|
pub(crate) plugin_build_depth: usize,
|
||||||
pub(crate) plugins_state: PluginsState,
|
pub(crate) plugins_state: PluginsState,
|
||||||
|
@ -91,7 +88,8 @@ impl Default for SubApp {
|
||||||
world.init_resource::<Schedules>();
|
world.init_resource::<Schedules>();
|
||||||
Self {
|
Self {
|
||||||
world,
|
world,
|
||||||
plugins: default(),
|
plugin_registry: Vec::default(),
|
||||||
|
plugin_names: HashSet::default(),
|
||||||
plugin_build_depth: 0,
|
plugin_build_depth: 0,
|
||||||
plugins_state: PluginsState::Adding,
|
plugins_state: PluginsState::Adding,
|
||||||
update_schedule: None,
|
update_schedule: None,
|
||||||
|
@ -380,10 +378,7 @@ impl SubApp {
|
||||||
where
|
where
|
||||||
T: Plugin,
|
T: Plugin,
|
||||||
{
|
{
|
||||||
self.plugins
|
self.plugin_names.contains(std::any::type_name::<T>())
|
||||||
.registry
|
|
||||||
.iter()
|
|
||||||
.any(|p| p.downcast_ref::<T>().is_some())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`App::get_added_plugins`].
|
/// See [`App::get_added_plugins`].
|
||||||
|
@ -391,8 +386,7 @@ impl SubApp {
|
||||||
where
|
where
|
||||||
T: Plugin,
|
T: Plugin,
|
||||||
{
|
{
|
||||||
self.plugins
|
self.plugin_registry
|
||||||
.registry
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|p| p.downcast_ref())
|
.filter_map(|p| p.downcast_ref())
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -409,16 +403,16 @@ impl SubApp {
|
||||||
match self.plugins_state {
|
match self.plugins_state {
|
||||||
PluginsState::Adding => {
|
PluginsState::Adding => {
|
||||||
let mut state = PluginsState::Ready;
|
let mut state = PluginsState::Ready;
|
||||||
let plugins = std::mem::take(&mut self.plugins);
|
let plugins = std::mem::take(&mut self.plugin_registry);
|
||||||
self.run_as_app(|app| {
|
self.run_as_app(|app| {
|
||||||
for plugin in &plugins.registry {
|
for plugin in &plugins {
|
||||||
if !plugin.ready(app) {
|
if !plugin.ready(app) {
|
||||||
state = PluginsState::Adding;
|
state = PluginsState::Adding;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.plugins = plugins;
|
self.plugin_registry = plugins;
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
state => state,
|
state => state,
|
||||||
|
@ -427,25 +421,25 @@ impl SubApp {
|
||||||
|
|
||||||
/// Runs [`Plugin::finish`] for each plugin.
|
/// Runs [`Plugin::finish`] for each plugin.
|
||||||
pub fn finish(&mut self) {
|
pub fn finish(&mut self) {
|
||||||
let plugins = std::mem::take(&mut self.plugins);
|
let plugins = std::mem::take(&mut self.plugin_registry);
|
||||||
self.run_as_app(|app| {
|
self.run_as_app(|app| {
|
||||||
for plugin in &plugins.registry {
|
for plugin in &plugins {
|
||||||
plugin.finish(app);
|
plugin.finish(app);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.plugins = plugins;
|
self.plugin_registry = plugins;
|
||||||
self.plugins_state = PluginsState::Finished;
|
self.plugins_state = PluginsState::Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs [`Plugin::cleanup`] for each plugin.
|
/// Runs [`Plugin::cleanup`] for each plugin.
|
||||||
pub fn cleanup(&mut self) {
|
pub fn cleanup(&mut self) {
|
||||||
let plugins = std::mem::take(&mut self.plugins);
|
let plugins = std::mem::take(&mut self.plugin_registry);
|
||||||
self.run_as_app(|app| {
|
self.run_as_app(|app| {
|
||||||
for plugin in &plugins.registry {
|
for plugin in &plugins {
|
||||||
plugin.cleanup(app);
|
plugin.cleanup(app);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.plugins = plugins;
|
self.plugin_registry = plugins;
|
||||||
self.plugins_state = PluginsState::Cleaned;
|
self.plugins_state = PluginsState::Cleaned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue