1use alloc::boxed::Box;
8use core::any::Any;
9use core::{fmt, hash};
10
11use bevy_ecs::prelude as ecs;
12use bevy_ecs::query::QueryData;
13
14use crate::block::BlockDef;
15use crate::character::Character;
16use crate::sound::SoundDef;
17use crate::space::Space;
18use crate::tag::TagDef;
19use crate::transaction;
20use crate::universe::{
21 self, ErasedHandle, Handle, InsertError, Name, Universe, handle::HandlePtr, universe_txn as ut,
22};
23
24pub(crate) trait SealedMember: Sized {
31 type Bundle: ecs::Bundle;
33
34 type ReadQueryData: bevy_ecs::query::ReadOnlyQueryData + Clone;
35
36 fn register_all_member_components(world: &mut ecs::World);
38
39 fn read_from_standalone(value: &Self) -> Self::Read<'_>
42 where
43 Self: UniverseMember;
44
45 fn read_from_query(data: <Self::ReadQueryData as QueryData>::Item<'_>) -> Self::Read<'_>
47 where
48 Self: UniverseMember;
49
50 #[expect(
56 unused,
57 reason = "TODO(ecs): remove this if we turn out to never need it"
58 )]
59 fn read_from_entity_ref(entity: ecs::EntityRef<'_>) -> Option<Self::Read<'_>>
60 where
61 Self: UniverseMember;
62
63 fn into_bundle(value: Box<Self>) -> Self::Bundle;
66}
67
68#[expect(private_bounds)]
78pub trait UniverseMember: Sized + 'static + fmt::Debug + SealedMember + MemberBoilerplate {
79 type Read<'ticket>: Clone;
85}
86
87pub(in crate::universe) trait MemberBoilerplate: Sized {
90 fn into_any_handle(handle: Handle<Self>) -> AnyHandle;
92
93 fn into_any_pending(handle: Handle<Self>, value: Option<Box<Self>>) -> AnyPending;
95
96 fn member_read_query_state(
97 queries: &MemberReadQueryStates,
98 ) -> &ecs::QueryState<Self::ReadQueryData>
99 where
100 Self: UniverseMember;
101
102 fn member_mutation_query_state(
103 queries: &mut MemberWriteQueryStates,
104 ) -> &mut ecs::QueryState<<<Self as universe::Transactional>::Transaction as universe::TransactionOnEcs>::WriteQueryData>
105 where
106 Self: UniverseMember + transaction::Transactional<Transaction: universe::TransactionOnEcs>;
107
108 fn member_read_query<'q, 'w, 's>(
111 queries: &'q MemberReadQueries<'w, 's>,
112 ) -> Option<&'q ecs::Query<'w, 's, Self::ReadQueryData>>
113 where
114 Self: UniverseMember;
115}
116
117macro_rules! impl_universe_member_for_single_component_type {
124 ($member_type:path) => {
125 impl $crate::universe::SealedMember for $member_type {
126 type Bundle = (Self,);
127 type ReadQueryData = &'static Self;
128
129 fn register_all_member_components(world: &mut ::bevy_ecs::world::World) {
130 $crate::universe::VisitableComponents::register::<$member_type>(world);
131 }
132
133 fn read_from_standalone(
134 value: &Self,
135 ) -> <Self as $crate::universe::UniverseMember>::Read<'_> {
136 value
139 }
140
141 fn read_from_query(
142 data: <Self::ReadQueryData as ::bevy_ecs::query::QueryData>::Item<'_>,
143 ) -> <Self as $crate::universe::UniverseMember>::Read<'_> {
144 data
147 }
148
149 fn read_from_entity_ref(
150 entity: ::bevy_ecs::world::EntityRef<'_>,
151 ) -> Option<<Self as $crate::universe::UniverseMember>::Read<'_>> {
152 entity.get::<$member_type>()
155 }
156
157 fn into_bundle(value: ::alloc::boxed::Box<Self>) -> Self::Bundle {
158 (*value,)
161 }
162 }
163
164 impl $crate::universe::UniverseMember for $member_type {
165 type Read<'ticket> = &'ticket $member_type;
168 }
169 };
170}
171pub(crate) use impl_universe_member_for_single_component_type;
172
173macro_rules! impl_universe_for_member {
175 ($member_type:ident, $table:ident) => {
176 impl MemberBoilerplate for $member_type {
177 fn into_any_handle(handle: Handle<Self>) -> AnyHandle {
178 AnyHandle::$member_type(handle)
179 }
180
181 fn into_any_pending(handle: Handle<Self>, value: Option<Box<Self>>) -> AnyPending {
182 AnyPending::$member_type { handle, value }
183 }
184
185 fn member_read_query_state(
186 queries: &MemberReadQueryStates,
187 ) -> &ecs::QueryState<<Self as SealedMember>::ReadQueryData> {
188 &queries.$table
189 }
190
191 fn member_mutation_query_state(
192 queries: &mut MemberWriteQueryStates,
193 ) -> &mut ecs::QueryState<<<Self as universe::Transactional>::Transaction as universe::TransactionOnEcs>::WriteQueryData> {
194 &mut queries.$table
195 }
196
197 fn member_read_query<'q, 'w, 's>(
198 queries: &'q MemberReadQueries<'w, 's>,
199 ) -> Option<&'q ecs::Query<'w, 's, <Self as SealedMember>::ReadQueryData>>
200 {
201 queries.$table.as_ref()
202 }
203 }
204
205
206 impl ut::UTransactional for $member_type {
207 fn bind(
208 target: Handle<Self>,
209 transaction: Self::Transaction,
210 ) -> ut::UniverseTransaction {
211 ut::UniverseTransaction::from(AnyTransaction::$member_type(
212 ut::TransactionInUniverse {
213 target,
214 transaction,
215 },
216 ))
217 }
218 }
219 };
220}
221
222macro_rules! member_enums_and_impls {
224 ( $( ($member_type:ident, $table_name:ident), )* ) => {
225 impl Universe {
226 pub fn iter(&self) -> impl Iterator<Item = AnyHandle> {
230 core::iter::empty()
231 $(
232 .chain(self.iter_by_type::<$member_type>()
233 .map(|(_name, handle)| AnyHandle::$member_type(handle)))
234 )*
235 }
236
237 pub(in crate::universe) fn register_all_member_components(world: &mut ecs::World) {
238 $(
239 <$member_type as SealedMember>::register_all_member_components(world);
240 )*
241 }
242 }
243
244 #[derive(Clone, Debug)]
250 #[non_exhaustive]
251 #[allow(missing_docs, reason = "variant meanings are obvious from name")]
252 pub enum AnyHandle {
253 $( $member_type(Handle<$member_type>), )*
254 }
255
256 impl AnyHandle {
257 #[doc(hidden)] pub fn member_type_name(&self) -> &'static str {
260 match self {
261 $( AnyHandle::$member_type(_) => {
262 core::any::type_name::<$member_type>()
263 } )*
264 }
265 }
266
267 #[cfg(feature = "save")]
269 pub(crate) fn not_still_deserializing(
270 &self,
271 read_ticket: $crate::universe::ReadTicket<'_>,
272 ) -> bool {
273 match self {
274 $( AnyHandle::$member_type(h) => {
275 !matches!(
276 h.read(read_ticket),
277 Err(handle_error) if matches!(
278 handle_error.kind,
279 $crate::universe::HandleErrorKind::NotReady,
280 )
281 )
282 } )*
283 }
284 }
285
286 pub(in crate::universe) fn set_state_to_gone(
288 &self,
289 reason: $crate::universe::GoneReason,
290 ) -> () {
291 match self {
292 $( AnyHandle::$member_type(handle) => handle.set_state_to_gone(reason), )*
293 }
294 }
295
296 pub(in crate::universe) fn has_strong_handles(
297 &self,
298 ) -> bool {
299 match self {
300 $( AnyHandle::$member_type(handle) => handle.has_strong_handles(), )*
301 }
302 }
303 }
304
305 impl core::ops::Deref for AnyHandle {
306 type Target = dyn ErasedHandle;
307
308 fn deref(&self) -> &Self::Target {
309 match self {
310 $( Self::$member_type(r) => r, )*
311 }
312 }
313 }
314
315 $( impl_universe_for_member!($member_type, $table_name); )*
316
317 #[derive(Clone, Default, PartialEq)]
320 #[non_exhaustive]
321 pub(in crate::universe) enum AnyTransaction {
322 #[default]
323 Noop,
324 $( $member_type(ut::TransactionInUniverse<$member_type>), )*
325 }
326
327 impl AnyTransaction {
328 pub(in crate::universe) fn target_erased(&self) -> Option<&dyn ErasedHandle> {
329 use AnyTransaction::*;
330 match self {
331 Noop => None,
332 $( $member_type(t) => Some(&t.target), )*
333 }
334 }
335
336 pub(in crate::universe) fn transaction_as_debug(&self) -> &dyn fmt::Debug {
338 use AnyTransaction::*;
339 match self {
340 Noop => &"AnyTransaction::Noop",
341 $( Self::$member_type(t) => &t.transaction, )*
342 }
343 }
344 }
345
346 impl transaction::Transaction for AnyTransaction {
347 type Target = Universe;
348 type Context<'a> = ();
349 type CommitCheck = ut::AnyTransactionCheck;
350 type Output = transaction::NoOutput;
351 type Mismatch = AnyTransactionMismatch;
352
353 fn check(
354 &self,
355 universe: &Universe, (): Self::Context<'_>
356 ) -> Result<Self::CommitCheck, Self::Mismatch> {
357 Ok::<Self::CommitCheck, Self::Mismatch>(match self {
358 Self::Noop => Box::new(()),
359 $(
360 Self::$member_type(t) => Box::new(t.check(universe, ())
361 .map_err(AnyTransactionMismatch::$member_type)?),
362 )*
363 })
364 }
365
366 fn commit(
367 self,
368 universe: &mut Universe,
369 check: Self::CommitCheck,
370 outputs: &mut dyn FnMut(Self::Output),
371 ) -> Result<(), transaction::CommitError> {
372 match self {
373 Self::Noop => Ok(()),
374 $( Self::$member_type(t) => ut::anytxn_commit_helper(t, universe, check, outputs), )*
375 }
376 }
377 }
378
379 impl transaction::Merge for AnyTransaction {
380 type MergeCheck = ut::AnyTransactionCheck;
381 type Conflict = AnyTransactionConflict;
382
383 fn check_merge(&self, other: &Self) -> Result<Self::MergeCheck, Self::Conflict> {
384 match (self, other) {
385 (Self::Noop, _) => Ok(Box::new(())),
386 (_, Self::Noop) => Ok(Box::new(())),
387 $( (Self::$member_type(t1), Self::$member_type(t2)) => {
388 let check =
389 t1.check_merge(t2)
390 .map_err(AnyTransactionConflict::$member_type)?;
391 Ok(Box::new(check))
392 } )*
393 (_, _) => Err(AnyTransactionConflict::TypeMismatch),
394 }
395 }
396
397 fn commit_merge(&mut self, other: Self, check: Self::MergeCheck) {
398 match (self, other) {
399 (_t1, Self::Noop) => {},
400 (t1 @ Self::Noop, t2) => *t1 = t2,
401 $( (Self::$member_type(t1), Self::$member_type(t2)) => {
402 ut::anytxn_merge_helper(t1, t2, check)
403 } )*
404 (_, _) => panic!("Mismatched transaction target types"),
405 }
406 }
407 }
408
409 #[derive(Clone, Debug, Eq, PartialEq)]
411 #[non_exhaustive]
412 pub(in crate::universe) enum AnyTransactionMismatch {
413 $(
414 $member_type(
415 <
416 <$member_type as transaction::Transactional>::Transaction
417 as transaction::Transaction
418 >::Mismatch
419 ),
420 )*
421 }
422
423 #[derive(Clone, Debug, Eq, PartialEq)]
425 #[non_exhaustive]
426 pub(in crate::universe) enum AnyTransactionConflict {
427 TypeMismatch,
428 $(
429 $member_type(
430 <
431 <$member_type as transaction::Transactional>::Transaction
432 as transaction::Merge
433 >::Conflict
434 ),
435 )*
436 }
437
438 impl core::error::Error for AnyTransactionMismatch {
439 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
440 match self {
441 $( Self::$member_type(e) => Some(e), )*
442 }
443 }
444 }
445
446 impl core::error::Error for AnyTransactionConflict {
447 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
448 match self {
449 Self::TypeMismatch => None,
450 $( Self::$member_type(e) => Some(e), )*
451 }
452 }
453 }
454
455 impl fmt::Display for AnyTransactionMismatch {
456 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
457 match self {
458 $( Self::$member_type(e) => e.fmt(f), )*
459 }
460 }
461 }
462 impl fmt::Display for AnyTransactionConflict {
463 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
464 match self {
465 Self::TypeMismatch => write!(f, "Mismatched transaction target types"),
466 $( Self::$member_type(e) => e.fmt(f), )*
467 }
468 }
469 }
470
471 pub(in crate::universe) enum AnyPending {
476 $(
477 $member_type {
478 handle: Handle<$member_type>,
479
480 value: Option<Box<$member_type>>,
485 },
486 )*
487 }
488
489 impl AnyPending {
490 pub(crate) fn handle(&self) -> &dyn ErasedHandle {
491 match self {
492 $( AnyPending::$member_type { handle, .. } => handle, )*
493 }
494 }
495
496 pub(crate) fn check_upgrade_pending(
501 &self,
502 read_ticket_for_self: $crate::universe::ReadTicket<'_>,
503 universe_id: $crate::universe::UniverseId,
504 ) -> Result<(), $crate::universe::InsertError> {
505 match self {
506 $(
507 Self::$member_type { handle, value: _ } => {
508 handle.check_upgrade_pending(read_ticket_for_self, universe_id)
509 }
510 )*
511 }
512 }
513
514 pub(crate) fn value_as_any_option_box(&self) -> &dyn Any {
517 match self {
518 $(
519 Self::$member_type { handle: _, value } => value,
520 )*
521 }
522 }
523 pub(crate) fn value_as_mut_any_option_box(&mut self) -> &mut dyn Any {
526 match self {
527 $(
528 Self::$member_type { handle: _, value } => value,
529 )*
530 }
531 }
532
533 pub(crate) fn insert_pending_into_universe(
535 self,
536 universe: &mut Universe
537 ) -> Result<(), InsertError> {
538 match self {
539 $(
540 AnyPending::$member_type { handle, value } => {
541 if let Some(value) = value {
542 handle.insert_pending_into_universe(universe, value)
543 } else {
544 Err(InsertError {
545 name: handle.name(),
546 kind: $crate::universe::InsertErrorKind::ValueMissing,
547 })
548 }
549
550 }
551 )*
552 }
553 }
554 }
555
556 impl fmt::Debug for AnyPending {
557 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
558 write!(f, "{:?} = ", self.handle())?;
559 match self {
560 $(
561 Self::$member_type { handle: _, value: Some(value) } => {
562 value.fmt(f)?;
563 },
564 )*
565 $( Self::$member_type { handle: _, value: None } )|* => {
566 write!(f, "<value not set>")?;
567 }
568 }
569 Ok(())
570 }
571 }
572
573 impl PartialEq for AnyPending {
574 fn eq(&self, rhs: &Self) -> bool {
575 match (self, rhs) {
576 $(
577 (
578 AnyPending::$member_type { handle: h1, value: _ },
579 AnyPending::$member_type { handle: h2, value: _ },
580 ) => {
581 h1 == h2
584 }
585 )*
586 (_, _) => false,
587 }
588 }
589 }
590 impl Eq for AnyPending {}
591
592 #[derive(ecs::FromWorld)]
596 #[macro_rules_attribute::derive($crate::universe::ecs_details::derive_manual_query_bundle!)]
597 pub(crate) struct MemberReadQueryStates {
598 $(
599 pub(in crate::universe) $table_name:
600 ::bevy_ecs::query::QueryState<
601 <$member_type as universe::SealedMember>::ReadQueryData
602 >,
603 )*
604 }
605
606 impl MemberReadQueryStates {
607 #[expect(dead_code, reason = "TODO(ecs): going to use this in systems")]
608 pub fn query_manual<'w, 's>(
609 &'s self,
610 world: &'w ecs::World,
611 ) -> MemberReadQueries<'w, 's> {
612 MemberReadQueries {
613 universe_id: *world.resource(),
614 $(
615 $table_name:
616 Some(self.$table_name.query_manual(world)),
617 )*
618 }
619 }
620 }
621
622 #[derive(ecs::FromWorld)]
626 #[macro_rules_attribute::derive($crate::universe::ecs_details::derive_manual_query_bundle!)]
627 pub(in crate::universe) struct MemberWriteQueryStates {
628 $(
629 pub(in crate::universe) $table_name:
630 ::bevy_ecs::query::QueryState<
631 <
632 <$member_type as universe::Transactional>::Transaction
633 as universe::TransactionOnEcs
634 >::WriteQueryData
635 >,
636 )*
637 }
638
639 pub(crate) struct MemberReadQueries<'w, 's> {
651 pub(crate) universe_id: universe::UniverseId,
652 $(
653 pub(crate) $table_name:
654 ::core::option::Option<::bevy_ecs::prelude::Query<'w, 's,
655 <$member_type as universe::SealedMember>::ReadQueryData
656 >>,
657 )*
658 }
659
660 #[derive(bevy_ecs::system::SystemParam)]
662 pub(crate) struct AllMemberReadQueries<'w, 's> {
663 pub(crate) universe_id: ecs::Res<'w, universe::UniverseId>,
664 $(
665 pub(crate) $table_name:
666 ::bevy_ecs::prelude::Query<'w, 's,
667 <$member_type as universe::SealedMember>::ReadQueryData
668 >,
669 )*
670 }
671
672 impl<'w, 's> AllMemberReadQueries<'w, 's> {
673 pub fn get(self) -> MemberReadQueries<'w, 's> {
674 MemberReadQueries {
675 universe_id: *self.universe_id,
676 $(
677 $table_name: Some(self.$table_name),
678 )*
679 }
680 }
681 }
682 }
683}
684
685member_enums_and_impls!(
696 (BlockDef, blocks),
697 (Character, characters),
698 (SoundDef, sounds),
699 (Space, spaces),
700 (TagDef, tags),
701);
702
703impl AnyHandle {
706 pub fn downcast_ref<T: 'static>(&self) -> Option<&Handle<T>> {
708 let handle: &dyn ErasedHandle = self.as_ref();
709 let handle: &dyn Any = handle;
710 handle.downcast_ref()
711 }
712
713 pub fn downcast<T: 'static>(self) -> Result<Handle<T>, AnyHandle> {
715 match self.downcast_ref() {
718 Some(handle) => Ok(handle.clone()),
719 None => Err(self),
720 }
721 }
722}
723
724impl AsRef<dyn ErasedHandle> for AnyHandle {
725 fn as_ref(&self) -> &dyn ErasedHandle {
726 &**self
727 }
728}
729impl core::borrow::Borrow<dyn ErasedHandle> for AnyHandle {
730 fn borrow(&self) -> &dyn ErasedHandle {
731 &**self
732 }
733}
734
735impl HandlePtr for AnyHandle {
736 fn as_erased_shared_pointer(&self) -> *const () {
737 let r: &dyn ErasedHandle = &**self;
738 r.as_erased_shared_pointer()
739 }
740}
741
742impl ErasedHandle for AnyHandle {
743 fn name(&self) -> Name {
744 let r: &dyn ErasedHandle = &**self;
745 r.name()
746 }
747
748 fn universe_id(&self) -> Option<super::UniverseId> {
749 let r: &dyn ErasedHandle = &**self;
750 r.universe_id()
751 }
752
753 fn as_entity(
754 &self,
755 expected_universe: super::UniverseId,
756 ) -> Result<ecs::Entity, universe::handle::HandleError> {
757 let r: &dyn ErasedHandle = &**self;
758 r.as_entity(expected_universe)
759 }
760
761 fn to_any_handle(&self) -> AnyHandle {
762 self.clone()
763 }
764}
765
766impl PartialEq for AnyHandle {
767 fn eq(&self, other: &AnyHandle) -> bool {
768 self.as_erased_shared_pointer() == other.as_erased_shared_pointer()
769 }
770}
771impl Eq for AnyHandle {}
772impl hash::Hash for AnyHandle {
773 fn hash<H: hash::Hasher>(&self, state: &mut H) {
774 self.as_erased_shared_pointer().hash(state);
776 }
777}
778
779impl PartialEq<dyn ErasedHandle> for AnyHandle {
780 fn eq(&self, other: &dyn ErasedHandle) -> bool {
781 self.as_erased_shared_pointer() == other.as_erased_shared_pointer()
782 }
783}
784impl hashbrown::Equivalent<AnyHandle> for dyn ErasedHandle {
785 fn equivalent(&self, other: &AnyHandle) -> bool {
786 self.as_erased_shared_pointer() == other.as_erased_shared_pointer()
787 }
788}
789
790impl<T> PartialEq<Handle<T>> for AnyHandle
791where
792 Handle<T>: HandlePtr,
793{
794 fn eq(&self, other: &Handle<T>) -> bool {
795 self.as_erased_shared_pointer() == other.as_erased_shared_pointer()
796 }
797}
798impl<T> PartialEq<AnyHandle> for Handle<T>
799where
800 Handle<T>: HandlePtr,
801{
802 fn eq(&self, other: &AnyHandle) -> bool {
803 *other == *self
804 }
805}
806
807impl bevy_ecs::system::ExclusiveSystemParam for &mut MemberReadQueryStates {
809 type State = MemberReadQueryStates;
810 type Item<'s> = &'s mut MemberReadQueryStates;
811
812 fn init(world: &mut ecs::World, _: &mut bevy_ecs::system::SystemMeta) -> Self::State {
813 <MemberReadQueryStates as bevy_ecs::world::FromWorld>::from_world(world)
814 }
815
816 fn get_param<'s>(
817 state: &'s mut Self::State,
818 _: &bevy_ecs::system::SystemMeta,
819 ) -> Self::Item<'s> {
820 state
821 }
822}