Skip to main content

bevy_extract/
extract_resource.rs

1use core::marker::PhantomData;
2
3use bevy_app::{App, AppLabel, Plugin};
4use bevy_ecs::{component::Mutable, prelude::*};
5pub use bevy_extract_macros::ExtractResource;
6use bevy_utils::once;
7
8use crate::{Extract, ExtractSchedule};
9
10/// Describes how a resource gets extracted for processing.
11///
12/// Therefore the resource is transferred from the "main world" into the "sub world"
13/// in the [`ExtractSchedule`] step.
14///
15/// The marker type `F` is only used as a way to bypass the orphan rules. To
16/// implement the trait for a foreign type you can use a local type as the
17/// marker, e.g. the type of the plugin that calls [`ExtractResourcePlugin`].
18pub trait ExtractResource<L: AppLabel, F = ()>: Resource {
19    type Source: Resource;
20
21    /// Defines how the resource is transferred into the "sub world".
22    fn extract_resource(source: &Self::Source) -> Self;
23}
24
25/// This plugin extracts the resources into the "sub world".
26///
27/// Therefore it sets up the[`ExtractSchedule`] step
28/// for the specified [`Resource`].
29///
30/// The marker type `F` is only used as a way to bypass the orphan rules. To
31/// implement the trait for a foreign type you can use a local type as the
32/// marker, e.g. the type of the plugin that calls [`ExtractResourcePlugin`].
33pub struct ExtractResourcePlugin<R: ExtractResource<L, F>, L: AppLabel, F = ()>(
34    PhantomData<(R, L, F)>,
35);
36
37impl<R: ExtractResource<L, F>, L: AppLabel, F> Default for ExtractResourcePlugin<R, L, F> {
38    fn default() -> Self {
39        Self(PhantomData)
40    }
41}
42
43impl<
44        R: ExtractResource<L, F, Mutability = Mutable>,
45        L: AppLabel + Default,
46        F: 'static + Send + Sync,
47    > Plugin for ExtractResourcePlugin<R, L, F>
48{
49    fn build(&self, app: &mut App) {
50        if let Some(sub_app) = app.get_sub_app_mut(L::default()) {
51            sub_app.add_systems(ExtractSchedule, extract_resource::<R, L, F>);
52        } else {
53            once!(bevy_log::error!(
54                "Sub app did not exist when trying to add `extract_resource` for <{}>.",
55                core::any::type_name::<R>()
56            ));
57        }
58    }
59}
60
61/// This system extracts the resource of the corresponding [`Resource`] type
62pub fn extract_resource<R: ExtractResource<L, F, Mutability = Mutable>, L: AppLabel, F>(
63    mut commands: Commands,
64    main_resource: Extract<Option<Res<R::Source>>>,
65    target_resource: Option<ResMut<R>>,
66) {
67    if let Some(main_resource) = main_resource.as_ref() {
68        if let Some(mut target_resource) = target_resource {
69            if main_resource.is_changed() {
70                *target_resource = R::extract_resource(main_resource);
71            }
72        } else {
73            #[cfg(debug_assertions)]
74            if !main_resource.is_added() {
75                once!(bevy_log::warn!(
76                    "Removing resource {} from sub world not expected, adding using `Commands`.
77                This may decrease performance",
78                    core::any::type_name::<R>()
79                ));
80            }
81
82            commands.insert_resource(R::extract_resource(main_resource));
83        }
84    }
85}