use crate::{
change_detection::{traits::*, ComponentTickCells, MaybeLocation, Tick},
ptr::PtrMut,
resource::Resource,
};
use bevy_ptr::{Ptr, UnsafeCellDeref};
use core::{
ops::{Deref, DerefMut},
panic::Location,
};
#[derive(Clone)]
pub(crate) struct ComponentTicksRef<'w> {
pub(crate) added: &'w Tick,
pub(crate) changed: &'w Tick,
pub(crate) changed_by: MaybeLocation<&'w &'static Location<'static>>,
pub(crate) last_run: Tick,
pub(crate) this_run: Tick,
}
impl<'w> ComponentTicksRef<'w> {
#[inline]
pub(crate) unsafe fn from_tick_cells(
cells: ComponentTickCells<'w>,
last_run: Tick,
this_run: Tick,
) -> Self {
Self {
added: unsafe { cells.added.deref() },
changed: unsafe { cells.changed.deref() },
changed_by: unsafe { cells.changed_by.map(|changed_by| changed_by.deref()) },
last_run,
this_run,
}
}
}
pub(crate) struct ComponentTicksMut<'w> {
pub(crate) added: &'w mut Tick,
pub(crate) changed: &'w mut Tick,
pub(crate) changed_by: MaybeLocation<&'w mut &'static Location<'static>>,
pub(crate) last_run: Tick,
pub(crate) this_run: Tick,
}
impl<'w> ComponentTicksMut<'w> {
#[inline]
pub(crate) unsafe fn from_tick_cells(
cells: ComponentTickCells<'w>,
last_run: Tick,
this_run: Tick,
) -> Self {
Self {
added: unsafe { cells.added.deref_mut() },
changed: unsafe { cells.changed.deref_mut() },
changed_by: unsafe { cells.changed_by.map(|changed_by| changed_by.deref_mut()) },
last_run,
this_run,
}
}
}
impl<'w> From<ComponentTicksMut<'w>> for ComponentTicksRef<'w> {
fn from(ticks: ComponentTicksMut<'w>) -> Self {
ComponentTicksRef {
added: ticks.added,
changed: ticks.changed,
changed_by: ticks.changed_by.map(|changed_by| &*changed_by),
last_run: ticks.last_run,
this_run: ticks.this_run,
}
}
}
pub struct Res<'w, T: ?Sized + Resource> {
pub(crate) value: &'w T,
pub(crate) ticks: ComponentTicksRef<'w>,
}
impl<'w, T: Resource> Res<'w, T> {
#[expect(
clippy::should_implement_trait,
reason = "As this struct derefs to the inner resource, a `Clone` trait implementation would interfere with the common case of cloning the inner content. (A similar case of this happening can be found with `std::cell::Ref::clone()`.)"
)]
pub fn clone(this: &Self) -> Self {
Self {
value: this.value,
ticks: this.ticks.clone(),
}
}
pub fn into_inner(self) -> &'w T {
self.value
}
}
impl<'w, T: Resource> From<ResMut<'w, T>> for Res<'w, T> {
fn from(res: ResMut<'w, T>) -> Self {
Self {
value: res.value,
ticks: res.ticks.into(),
}
}
}
impl<'w, T: Resource> From<Res<'w, T>> for Ref<'w, T> {
fn from(res: Res<'w, T>) -> Self {
Self {
value: res.value,
ticks: res.ticks,
}
}
}
impl<'w, 'a, T: Resource> IntoIterator for &'a Res<'w, T>
where
&'a T: IntoIterator,
{
type Item = <&'a T as IntoIterator>::Item;
type IntoIter = <&'a T as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.value.into_iter()
}
}
change_detection_impl!(Res<'w, T>, T, Resource);
impl_debug!(Res<'w, T>, Resource);
pub struct ResMut<'w, T: ?Sized + Resource> {
pub(crate) value: &'w mut T,
pub(crate) ticks: ComponentTicksMut<'w>,
}
impl<'w, 'a, T: Resource> IntoIterator for &'a ResMut<'w, T>
where
&'a T: IntoIterator,
{
type Item = <&'a T as IntoIterator>::Item;
type IntoIter = <&'a T as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.value.into_iter()
}
}
impl<'w, 'a, T: Resource> IntoIterator for &'a mut ResMut<'w, T>
where
&'a mut T: IntoIterator,
{
type Item = <&'a mut T as IntoIterator>::Item;
type IntoIter = <&'a mut T as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.set_changed();
self.value.into_iter()
}
}
change_detection_impl!(ResMut<'w, T>, T, Resource);
change_detection_mut_impl!(ResMut<'w, T>, T, Resource);
impl_methods!(ResMut<'w, T>, T, Resource);
impl_debug!(ResMut<'w, T>, Resource);
impl<'w, T: Resource> From<ResMut<'w, T>> for Mut<'w, T> {
fn from(other: ResMut<'w, T>) -> Mut<'w, T> {
Mut {
value: other.value,
ticks: other.ticks,
}
}
}
pub struct NonSend<'w, T: ?Sized + 'static> {
pub(crate) value: &'w T,
pub(crate) ticks: ComponentTicksRef<'w>,
}
change_detection_impl!(NonSend<'w, T>, T,);
impl_debug!(NonSend<'w, T>,);
impl<'w, T> From<NonSendMut<'w, T>> for NonSend<'w, T> {
fn from(other: NonSendMut<'w, T>) -> Self {
Self {
value: other.value,
ticks: other.ticks.into(),
}
}
}
pub struct NonSendMut<'w, T: ?Sized + 'static> {
pub(crate) value: &'w mut T,
pub(crate) ticks: ComponentTicksMut<'w>,
}
change_detection_impl!(NonSendMut<'w, T>, T,);
change_detection_mut_impl!(NonSendMut<'w, T>, T,);
impl_methods!(NonSendMut<'w, T>, T,);
impl_debug!(NonSendMut<'w, T>,);
impl<'w, T: 'static> From<NonSendMut<'w, T>> for Mut<'w, T> {
fn from(other: NonSendMut<'w, T>) -> Mut<'w, T> {
Mut {
value: other.value,
ticks: other.ticks,
}
}
}
pub struct Ref<'w, T: ?Sized> {
pub(crate) value: &'w T,
pub(crate) ticks: ComponentTicksRef<'w>,
}
impl<'w, T: ?Sized> Ref<'w, T> {
pub fn into_inner(self) -> &'w T {
self.value
}
pub fn map<U: ?Sized>(self, f: impl FnOnce(&T) -> &U) -> Ref<'w, U> {
Ref {
value: f(self.value),
ticks: self.ticks,
}
}
pub fn new(
value: &'w T,
added: &'w Tick,
changed: &'w Tick,
last_run: Tick,
this_run: Tick,
caller: MaybeLocation<&'w &'static Location<'static>>,
) -> Ref<'w, T> {
Ref {
value,
ticks: ComponentTicksRef {
added,
changed,
changed_by: caller,
last_run,
this_run,
},
}
}
pub fn set_ticks(&mut self, last_run: Tick, this_run: Tick) {
self.ticks.last_run = last_run;
self.ticks.this_run = this_run;
}
}
impl<'w, 'a, T> IntoIterator for &'a Ref<'w, T>
where
&'a T: IntoIterator,
{
type Item = <&'a T as IntoIterator>::Item;
type IntoIter = <&'a T as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.value.into_iter()
}
}
change_detection_impl!(Ref<'w, T>, T,);
impl_debug!(Ref<'w, T>,);
pub struct Mut<'w, T: ?Sized> {
pub(crate) value: &'w mut T,
pub(crate) ticks: ComponentTicksMut<'w>,
}
impl<'w, T: ?Sized> Mut<'w, T> {
pub fn new(
value: &'w mut T,
added: &'w mut Tick,
last_changed: &'w mut Tick,
last_run: Tick,
this_run: Tick,
caller: MaybeLocation<&'w mut &'static Location<'static>>,
) -> Self {
Self {
value,
ticks: ComponentTicksMut {
added,
changed: last_changed,
changed_by: caller,
last_run,
this_run,
},
}
}
pub fn set_ticks(&mut self, last_run: Tick, this_run: Tick) {
self.ticks.last_run = last_run;
self.ticks.this_run = this_run;
}
}
impl<'w, T: ?Sized> From<Mut<'w, T>> for Ref<'w, T> {
fn from(mut_ref: Mut<'w, T>) -> Self {
Self {
value: mut_ref.value,
ticks: mut_ref.ticks.into(),
}
}
}
impl<'w, 'a, T> IntoIterator for &'a Mut<'w, T>
where
&'a T: IntoIterator,
{
type Item = <&'a T as IntoIterator>::Item;
type IntoIter = <&'a T as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.value.into_iter()
}
}
impl<'w, 'a, T> IntoIterator for &'a mut Mut<'w, T>
where
&'a mut T: IntoIterator,
{
type Item = <&'a mut T as IntoIterator>::Item;
type IntoIter = <&'a mut T as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.set_changed();
self.value.into_iter()
}
}
change_detection_impl!(Mut<'w, T>, T,);
change_detection_mut_impl!(Mut<'w, T>, T,);
impl_methods!(Mut<'w, T>, T,);
impl_debug!(Mut<'w, T>,);
pub struct MutUntyped<'w> {
pub(crate) value: PtrMut<'w>,
pub(crate) ticks: ComponentTicksMut<'w>,
}
impl<'w> MutUntyped<'w> {
#[inline]
pub fn into_inner(mut self) -> PtrMut<'w> {
self.set_changed();
self.value
}
#[inline]
pub fn reborrow(&mut self) -> MutUntyped<'_> {
MutUntyped {
value: self.value.reborrow(),
ticks: ComponentTicksMut {
added: self.ticks.added,
changed: self.ticks.changed,
changed_by: self.ticks.changed_by.as_deref_mut(),
last_run: self.ticks.last_run,
this_run: self.ticks.this_run,
},
}
}
pub fn has_changed_since(&self, tick: Tick) -> bool {
self.ticks.changed.is_newer_than(tick, self.ticks.this_run)
}
#[inline]
pub fn as_mut(&mut self) -> PtrMut<'_> {
self.set_changed();
self.value.reborrow()
}
#[inline]
pub fn as_ref(&self) -> Ptr<'_> {
self.value.as_ref()
}
pub fn map_unchanged<T: ?Sized>(self, f: impl FnOnce(PtrMut<'w>) -> &'w mut T) -> Mut<'w, T> {
Mut {
value: f(self.value),
ticks: self.ticks,
}
}
pub unsafe fn with_type<T>(self) -> Mut<'w, T> {
Mut {
value: unsafe { self.value.deref_mut() },
ticks: self.ticks,
}
}
}
impl<'w> DetectChanges for MutUntyped<'w> {
#[inline]
fn is_added(&self) -> bool {
self.ticks
.added
.is_newer_than(self.ticks.last_run, self.ticks.this_run)
}
#[inline]
fn is_changed(&self) -> bool {
self.ticks
.changed
.is_newer_than(self.ticks.last_run, self.ticks.this_run)
}
#[inline]
fn last_changed(&self) -> Tick {
*self.ticks.changed
}
#[inline]
fn changed_by(&self) -> MaybeLocation {
self.ticks.changed_by.copied()
}
#[inline]
fn added(&self) -> Tick {
*self.ticks.added
}
}
impl<'w> DetectChangesMut for MutUntyped<'w> {
type Inner = PtrMut<'w>;
#[inline]
#[track_caller]
fn set_changed(&mut self) {
*self.ticks.changed = self.ticks.this_run;
self.ticks.changed_by.assign(MaybeLocation::caller());
}
#[inline]
#[track_caller]
fn set_added(&mut self) {
*self.ticks.changed = self.ticks.this_run;
*self.ticks.added = self.ticks.this_run;
self.ticks.changed_by.assign(MaybeLocation::caller());
}
#[inline]
#[track_caller]
fn set_last_changed(&mut self, last_changed: Tick) {
*self.ticks.changed = last_changed;
self.ticks.changed_by.assign(MaybeLocation::caller());
}
#[inline]
#[track_caller]
fn set_last_added(&mut self, last_added: Tick) {
*self.ticks.added = last_added;
*self.ticks.changed = last_added;
self.ticks.changed_by.assign(MaybeLocation::caller());
}
#[inline]
#[track_caller]
fn bypass_change_detection(&mut self) -> &mut Self::Inner {
&mut self.value
}
}
impl core::fmt::Debug for MutUntyped<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("MutUntyped")
.field(&self.value.as_ptr())
.finish()
}
}
impl<'w, T> From<Mut<'w, T>> for MutUntyped<'w> {
fn from(value: Mut<'w, T>) -> Self {
MutUntyped {
value: value.value.into(),
ticks: value.ticks,
}
}
}