1use alloc::collections::{BTreeMap, BTreeSet};
4use alloc::sync::{Arc, Weak};
5use alloc::vec::Vec;
6use core::any::{Any, TypeId};
7use core::fmt;
8use core::mem;
9use core::ops;
10use core::task::Waker;
11use core::time::Duration;
12use manyfmt::Refmt as _;
13
14use bevy_ecs::prelude as ecs;
15use bevy_platform::{sync::Mutex, time::Instant};
16
17use crate::time::Tick;
18use crate::transaction::{self, Merge as _, Transaction};
19use crate::universe::{self, HandleVisitor, ReadTicket, UniverseTransaction, VisitHandles};
20
21#[cfg(doc)]
22use crate::universe::Universe;
23use crate::util::StatusText;
24
25pub trait Behavior<H: Host>: fmt::Debug + Any + Send + Sync + VisitHandles + 'static {
29 fn step(&self, context: &Context<'_, '_, H>) -> (UniverseTransaction, Then);
34
35 fn persistence(&self) -> Option<Persistence>;
43}
44
45pub trait Host: universe::UniverseMember + transaction::Transactional + 'static {
47 type Attachment: fmt::Debug + Clone + Eq + 'static;
50}
51
52#[non_exhaustive]
57pub struct Context<'ctx, 'read, H: Host> {
58 pub tick: Tick,
60
61 pub read_ticket: ReadTicket<'ctx>,
63
64 pub host: &'ctx H::Read<'read>,
66
67 pub attachment: &'ctx H::Attachment,
70
71 waker: &'ctx Waker,
72
73 host_transaction_binder: &'ctx dyn Fn(H::Transaction) -> UniverseTransaction,
74 self_transaction_binder: &'ctx dyn Fn(Arc<dyn Behavior<H>>) -> UniverseTransaction,
75}
76
77impl<'a, H: Host> Context<'a, '_, H> {
78 pub fn waker(&self) -> &'a Waker {
84 self.waker
85 }
86
87 pub fn bind_host(&self, transaction: H::Transaction) -> UniverseTransaction {
90 (self.host_transaction_binder)(transaction)
91 }
92
93 pub fn replace_self<B: Behavior<H> + 'static>(&self, new_behavior: B) -> UniverseTransaction {
98 (self.self_transaction_binder)(Arc::new(new_behavior))
99 }
100}
101
102impl<H: Host + fmt::Debug> fmt::Debug for Context<'_, '_, H> {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 f.debug_struct("Context").finish_non_exhaustive()
106 }
107}
108
109#[derive(Debug, Clone, PartialEq)]
113#[non_exhaustive]
114pub enum Then {
115 Drop,
117
118 Step,
121
122 Sleep,
124}
125
126#[doc = include_str!("save/serde-warning.md")]
134#[expect(clippy::module_name_repetitions)] #[derive(ecs::Component)]
136pub struct BehaviorSet<H: Host> {
137 members: BTreeMap<Key, BehaviorSetEntry<H>>,
142
143 woken: Arc<WokenSet>,
145}
146
147impl<H: Host> BehaviorSet<H> {
148 pub fn new() -> Self {
150 BehaviorSet {
151 members: BTreeMap::new(),
152 woken: Default::default(),
153 }
154 }
155
156 pub fn query<T: Behavior<H>>(&self) -> impl Iterator<Item = QueryItem<'_, H, T>> + '_ {
163 self.query_any(Some(TypeId::of::<T>())).map(
164 |QueryItem {
165 attachment,
166 behavior,
167 }| QueryItem {
168 attachment,
169 behavior: <dyn Any>::downcast_ref::<T>(behavior).unwrap(),
170 },
171 )
172 }
173
174 pub fn query_any<'a>(
181 &'a self,
182 type_filter: Option<TypeId>,
183 ) -> impl Iterator<Item = QueryItem<'a, H, dyn Behavior<H> + 'static>> + 'a {
184 self.members
185 .values()
186 .map(
187 move |entry: &'a BehaviorSetEntry<H>| -> QueryItem<'a, H, dyn Behavior<H> + 'static> {
188 QueryItem {
189 attachment: &entry.attachment,
190 behavior: &*entry.behavior,
191 }
192 },
193 )
194 .filter(move |qi| type_filter.is_none_or(|t| (*qi.behavior).type_id() == t))
195 }
196
197 pub(crate) fn step(
198 &self,
199 read_ticket: ReadTicket<'_>,
200 host: &H::Read<'_>,
201 host_transaction_binder: &dyn Fn(H::Transaction) -> UniverseTransaction,
202 set_transaction_binder: impl Fn(BehaviorSetTransaction<H>) -> H::Transaction,
206 tick: Tick,
207 ) -> (UniverseTransaction, BehaviorSetStepInfo) {
208 let mut transactions = Vec::new();
209 let mut info = BehaviorSetStepInfo {
210 stepped: 0,
211 acted: 0,
212 total_count: self.members.len(),
213 total_time: Duration::ZERO, };
215 let start_time = Instant::now();
216
217 let woken: BTreeSet<_> = mem::take(&mut self.woken.lock().unwrap());
220
221 for key in woken {
222 let Some(entry) = self.members.get(&key) else {
223 continue;
225 };
226
227 let context = &Context {
228 tick,
229 read_ticket,
230 host,
231 attachment: &entry.attachment,
232 waker: entry.waker.as_ref().unwrap(),
233 host_transaction_binder,
234 self_transaction_binder: &|new_behavior| {
235 host_transaction_binder(set_transaction_binder(
236 BehaviorSetTransaction::replace(
237 key,
238 Replace {
239 old: entry.clone(),
240 new: Some(BehaviorSetEntry {
241 attachment: entry.attachment.clone(),
242 behavior: new_behavior,
243 waker: None,
244 }),
245 },
246 ),
247 ))
248 },
249 };
250 info.stepped += 1;
251 let (txn, then) = entry.behavior.step(context);
252 if !txn.is_empty() {
253 info.acted += 1;
254 transactions.push(txn);
255 }
256 match then {
257 Then::Drop => transactions.push(host_transaction_binder(set_transaction_binder(
258 BehaviorSetTransaction::delete(key, entry.clone()),
259 ))),
260
261 Then::Step => context.waker.wake_by_ref(),
263
264 Then::Sleep => { }
265 }
266 }
267 let transaction = transactions
268 .into_iter()
269 .reduce(|a, b| a.merge(b).expect("TODO: handle merge failure"))
270 .unwrap_or_default();
271
272 info.total_time = start_time.elapsed();
273 (transaction, info)
274 }
275
276 #[allow(unused)] pub(crate) fn iter(&self) -> impl Iterator<Item = &BehaviorSetEntry<H>> + '_ {
278 self.members.values()
279 }
280
281 #[allow(unused)] pub(crate) fn is_empty(&self) -> bool {
283 self.members.is_empty()
284 }
285}
286
287impl<H: Host> Clone for BehaviorSet<H> {
288 fn clone(&self) -> Self {
289 let woken = Arc::new(Mutex::new(self.members.keys().copied().collect()));
290
291 let members: BTreeMap<Key, _> = self
294 .members
295 .values()
296 .map(|entry| {
297 let mut entry = entry.clone();
298 let key = Key::new();
299 entry.waker = Some(BehaviorWakerInner::create_waker(key, &woken));
300 (key, entry)
301 })
302 .collect();
303
304 Self { members, woken }
305 }
306}
307
308impl<H: Host> Default for BehaviorSet<H> {
309 fn default() -> Self {
310 Self::new()
311 }
312}
313
314impl<H: Host> fmt::Debug for BehaviorSet<H> {
315 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316 write!(f, "BehaviorSet(")?;
317 f.debug_map().entries(self.members.iter()).finish()?;
318 write!(f, ")")?;
319 Ok(())
320 }
321}
322
323impl<H: Host> VisitHandles for BehaviorSet<H> {
324 fn visit_handles(&self, visitor: &mut dyn HandleVisitor) {
325 let Self { members, woken: _ } = self;
326 for entry in members.values() {
327 entry.behavior.visit_handles(visitor);
328 }
329 }
330}
331
332impl<H: Host> transaction::Transactional for BehaviorSet<H> {
333 type Transaction = BehaviorSetTransaction<H>;
334}
335
336#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
339struct Key(u64);
340
341impl Key {
342 fn new() -> Self {
343 #![allow(
344 clippy::useless_conversion,
345 clippy::unnecessary_fallible_conversions,
346 reason = "depends on pointer width and atomic support"
347 )]
348
349 use core::sync::atomic::{self, Ordering};
350
351 cfg_if::cfg_if! {
352 if #[cfg(target_has_atomic = "64")] {
355 static ID_COUNTER: atomic::AtomicU64 = atomic::AtomicU64::new(0);
356 } else if #[cfg(target_has_atomic = "32")] {
357 static ID_COUNTER: atomic::AtomicU32 = atomic::AtomicU32::new(0);
358 } else {
359 static ID_COUNTER: atomic::AtomicUsize = atomic::AtomicUsize::new(0);
361 }
362 }
363
364 let id = ID_COUNTER
365 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |counter| {
366 counter.checked_add(1)
367 })
368 .expect("behavior id overflow");
369
370 Self(id.try_into().unwrap()) }
372}
373
374impl fmt::Display for Key {
375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 fmt::Display::fmt(&self.0, f)
377 }
378}
379impl fmt::Debug for Key {
380 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
381 fmt::Display::fmt(&self.0, f)
382 }
383}
384
385pub(crate) struct BehaviorSetEntry<H: Host> {
386 pub(crate) attachment: H::Attachment,
387 pub(crate) behavior: Arc<dyn Behavior<H>>,
390 waker: Option<Waker>,
393}
394
395impl<H: Host> Clone for BehaviorSetEntry<H> {
396 fn clone(&self) -> Self {
397 Self {
399 attachment: self.attachment.clone(),
400 behavior: self.behavior.clone(),
401 waker: self.waker.clone(),
402 }
403 }
404}
405
406impl<H: Host> fmt::Debug for BehaviorSetEntry<H> {
407 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408 let BehaviorSetEntry {
409 attachment,
410 behavior,
411 waker: _,
412 } = self;
413 behavior.fmt(f)?; write!(f, " @ {attachment:?}")?; Ok(())
416 }
417}
418
419impl<H: Host> PartialEq for BehaviorSetEntry<H> {
420 fn eq(&self, other: &Self) -> bool {
421 self.attachment == other.attachment && Arc::ptr_eq(&self.behavior, &other.behavior)
422 }
423}
424
425impl<H: Host> VisitHandles for BehaviorSetEntry<H> {
426 fn visit_handles(&self, visitor: &mut dyn HandleVisitor) {
427 let Self {
428 attachment: _,
429 behavior,
430 waker: _,
431 } = self;
432 behavior.visit_handles(visitor);
433 }
434}
435
436type WokenSet = Mutex<BTreeSet<Key>>;
437
438#[derive(Debug)]
440struct BehaviorWakerInner {
441 key: Key,
442 set: Weak<WokenSet>,
443}
444
445impl alloc::task::Wake for BehaviorWakerInner {
446 fn wake(self: Arc<Self>) {
447 self.wake_by_ref()
448 }
449
450 fn wake_by_ref(self: &Arc<Self>) {
451 let Some(strong_set) = self.set.upgrade() else {
452 return;
454 };
455 let Ok(mut mut_set) = strong_set.lock() else {
456 return;
458 };
459 mut_set.insert(self.key);
460 }
461}
462
463impl BehaviorWakerInner {
464 fn create_waker(key: Key, woken: &Arc<WokenSet>) -> Waker {
465 Waker::from(Arc::new(BehaviorWakerInner {
466 key,
467 set: Arc::downgrade(woken),
468 }))
469 }
470}
471
472#[non_exhaustive]
474pub struct QueryItem<'a, H: Host, B: Behavior<H> + ?Sized> {
475 pub behavior: &'a B,
477 pub attachment: &'a H::Attachment,
482}
483
484impl<H: Host, B: Behavior<H> + ?Sized> Clone for QueryItem<'_, H, B> {
485 fn clone(&self) -> Self {
486 Self {
488 attachment: self.attachment,
489 behavior: self.behavior,
490 }
491 }
492}
493
494impl<H: Host, B: Behavior<H> + ?Sized> fmt::Debug for QueryItem<'_, H, B> {
495 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
496 let QueryItem {
497 attachment,
498 behavior,
499 } = self;
500 behavior.fmt(f)?; write!(f, " @ {attachment:?}")?; Ok(())
503 }
504}
505
506#[derive(Debug)]
508#[expect(clippy::module_name_repetitions)] pub struct BehaviorSetTransaction<H: Host> {
510 replace: BTreeMap<Key, Replace<H>>,
512 insert: Vec<BehaviorSetEntry<H>>,
514}
515
516#[derive(Debug)]
517struct Replace<H: Host> {
518 old: BehaviorSetEntry<H>,
519 new: Option<BehaviorSetEntry<H>>,
521}
522
523impl<H: Host> BehaviorSetTransaction<H> {
524 pub(crate) fn is_empty(&self) -> bool {
526 self.replace.is_empty() && self.insert.is_empty()
527 }
528
529 fn replace(key: Key, replacement: Replace<H>) -> Self {
532 BehaviorSetTransaction {
533 replace: BTreeMap::from([(key, replacement)]),
534 ..Default::default()
535 }
536 }
537
538 pub fn insert(attachment: H::Attachment, behavior: Arc<dyn Behavior<H>>) -> Self {
540 BehaviorSetTransaction {
541 insert: vec![BehaviorSetEntry {
542 attachment,
543 behavior,
544 waker: None,
545 }],
546 ..Default::default()
547 }
548 }
549
550 fn delete(key: Key, existing_entry: BehaviorSetEntry<H>) -> Self {
553 Self::replace(
554 key,
555 Replace {
556 old: existing_entry,
557 new: None,
558 },
559 )
560 }
561
562 pub(crate) fn attachments_affected(&self) -> impl Iterator<Item = &H::Attachment> {
565 let replace = self.replace.values().flat_map(|Replace { old, new }| {
566 [
567 Some(&old.attachment),
568 new.as_ref().map(|entry| &entry.attachment),
569 ]
570 .into_iter()
571 .flatten()
572 });
573 let insert = self.insert.iter().map(|entry| &entry.attachment);
574 replace.chain(insert)
575 }
576}
577
578impl<H: Host> Transaction for BehaviorSetTransaction<H> {
579 type Target = BehaviorSet<H>;
580 type Context<'a> = ();
581 type CommitCheck = CommitCheck;
582 type Output = transaction::NoOutput;
583 type Mismatch = BehaviorTransactionMismatch;
584
585 fn check(
586 &self,
587 target: &BehaviorSet<H>,
588 (): Self::Context<'_>,
589 ) -> Result<Self::CommitCheck, Self::Mismatch> {
590 let Self { replace, insert } = self;
591 for (&key, Replace { old, new: _ }) in replace {
593 if let Some(BehaviorSetEntry {
594 attachment,
595 behavior,
596 waker: _,
597 }) = target.members.get(&key)
598 {
599 let wrong_attachment = attachment != &old.attachment;
600 let wrong_value = !Arc::ptr_eq(behavior, &old.behavior);
601 if wrong_attachment || wrong_value {
602 return Err(BehaviorTransactionMismatch {
603 key,
604 key_not_found: false,
605 wrong_attachment,
606 wrong_value,
607 });
608 }
609 } else {
610 return Err(BehaviorTransactionMismatch {
611 key,
612 key_not_found: true,
613 wrong_attachment: false,
614 wrong_value: false,
615 });
616 }
617 }
618
619 let _ = insert;
621
622 Ok(CommitCheck { _private: () })
623 }
624
625 fn commit(
626 self,
627 target: &mut BehaviorSet<H>,
628 _: Self::CommitCheck,
629 _outputs: &mut dyn FnMut(Self::Output),
630 ) -> Result<(), transaction::CommitError> {
631 for (key, replacement) in self.replace {
632 match replacement.new {
633 Some(new) => {
634 let Some(entry) = target.members.get_mut(&key) else {
635 return Err(transaction::CommitError::message::<Self>(format!(
636 "behavior set does not contain key {key}"
637 )));
638 };
639 let BehaviorSetEntry {
640 attachment,
641 behavior,
642 waker,
643 } = new;
644 assert!(
645 waker.is_none(),
646 "transaction entries should not have wakers"
647 );
648 entry.attachment = attachment;
649 entry.behavior = behavior;
650 }
651 None => {
652 let Some(_) = target.members.remove(&key) else {
653 return Err(transaction::CommitError::message::<Self>(format!(
654 "behavior set does not contain key {key}"
655 )));
656 };
657 }
658 }
659 }
660
661 let mut woken = target.woken.lock().map_err(|_| {
663 transaction::CommitError::message::<Self>("behavior set wake lock poisoned".into())
664 })?;
665
666 target.members.extend(self.insert.iter().cloned().map(|mut entry| {
667 let key = Key::new();
670
671 woken.insert(key);
673
674 entry.waker = Some(BehaviorWakerInner::create_waker(key, &target.woken));
676
677 (key, entry)
678 }));
679 Ok(())
680 }
681}
682
683impl<H: Host> transaction::Merge for BehaviorSetTransaction<H> {
684 type MergeCheck = MergeCheck;
685 type Conflict = BehaviorTransactionConflict;
686
687 fn check_merge(&self, other: &Self) -> Result<Self::MergeCheck, Self::Conflict> {
688 if let Some(&key) = self.replace.keys().find(|key| other.replace.contains_key(key)) {
690 return Err(BehaviorTransactionConflict { key });
691 }
692 Ok(MergeCheck { _private: () })
693 }
694
695 fn commit_merge(&mut self, other: Self, _: Self::MergeCheck) {
696 self.replace.extend(other.replace);
697 self.insert.extend(other.insert);
698 }
699}
700
701impl<H: Host> Clone for BehaviorSetTransaction<H> {
702 fn clone(&self) -> Self {
704 Self {
705 replace: self.replace.clone(),
706 insert: self.insert.clone(),
707 }
708 }
709}
710
711impl<H: Host> Default for BehaviorSetTransaction<H> {
712 fn default() -> Self {
714 Self {
715 replace: Default::default(),
716 insert: Default::default(),
717 }
718 }
719}
720
721impl<H: Host> PartialEq for BehaviorSetTransaction<H> {
722 fn eq(&self, other: &Self) -> bool {
724 let Self {
725 replace: r1,
726 insert: i1,
727 } = self;
728 let Self {
729 replace: r2,
730 insert: i2,
731 } = other;
732 r1 == r2 && i1 == i2
733 }
734}
735impl<H: Host> PartialEq for Replace<H> {
736 fn eq(&self, other: &Self) -> bool {
738 let Self {
739 old: old1,
740 new: new1,
741 } = self;
742 let Self {
743 old: old2,
744 new: new2,
745 } = other;
746 old1 == old2 && new1 == new2
747 }
748}
749
750impl<H: Host> Eq for BehaviorSetTransaction<H> {}
751impl<H: Host> Eq for Replace<H> {}
752
753impl<H: Host> Clone for Replace<H> {
754 fn clone(&self) -> Self {
756 Self {
757 old: self.old.clone(),
758 new: self.new.clone(),
759 }
760 }
761}
762
763impl<H: Host> VisitHandles for BehaviorSetTransaction<H> {
764 fn visit_handles(&self, visitor: &mut dyn HandleVisitor) {
765 let Self { replace, insert } = self;
766 for Replace { old, new } in replace.values() {
767 new.visit_handles(visitor);
768 old.visit_handles(visitor);
769 }
770 insert.visit_handles(visitor);
771 }
772}
773
774#[derive(Debug)]
775#[doc(hidden)] pub struct CommitCheck {
777 _private: (),
778}
779#[derive(Debug)]
780#[doc(hidden)] pub struct MergeCheck {
782 _private: (),
783}
784
785#[derive(Clone, Debug, Eq, PartialEq)]
787#[expect(clippy::module_name_repetitions)]
788pub struct BehaviorTransactionMismatch {
789 key: Key,
790 key_not_found: bool,
792 wrong_attachment: bool,
793 wrong_value: bool,
794}
795
796#[derive(Clone, Debug, Eq, PartialEq, displaydoc::Display)]
798#[non_exhaustive]
799#[displaydoc("tried to replace the same behavior slot, {key}, twice")]
800#[expect(clippy::module_name_repetitions)]
801pub struct BehaviorTransactionConflict {
802 key: Key,
803}
804
805impl core::error::Error for BehaviorTransactionMismatch {}
806impl core::error::Error for BehaviorTransactionConflict {}
807
808impl fmt::Display for BehaviorTransactionMismatch {
809 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
810 let &Self {
811 key,
812 key_not_found,
813 wrong_attachment,
814 wrong_value,
815 } = self;
816 write!(f, "behavior {key} ")?;
817 if key_not_found {
818 write!(f, "not found")?;
819 } else {
820 write!(f, "does not have a matching ")?;
821 if wrong_attachment && wrong_value {
822 write!(f, "attachment or value")?;
823 } else if wrong_attachment {
824 write!(f, "attachment")?;
825 } else if wrong_value {
826 write!(f, "value")?;
827 } else {
828 write!(f, "<error in error details>")?;
829 }
830 }
831 Ok(())
832 }
833}
834
835#[cfg(test)]
836pub(crate) use testing::*;
837#[cfg(test)]
838mod testing {
839 use super::*;
840
841 #[derive(Clone, Eq, PartialEq)]
843 pub(crate) struct NoopBehavior<D>(pub D);
844
845 impl<D: fmt::Debug> fmt::Debug for NoopBehavior<D> {
846 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
847 write!(f, "NoopBehavior(")?;
848 self.0.fmt(f)?; write!(f, ")")?;
850 Ok(())
851 }
852 }
853
854 impl<H: Host, D: fmt::Debug + Send + Sync + 'static> Behavior<H> for NoopBehavior<D> {
855 fn step(&self, _context: &Context<'_, '_, H>) -> (UniverseTransaction, Then) {
856 (UniverseTransaction::default(), Then::Step)
857 }
858 fn persistence(&self) -> Option<Persistence> {
859 None
860 }
861 }
862
863 impl<D> VisitHandles for NoopBehavior<D> {
864 fn visit_handles(&self, _visitor: &mut dyn HandleVisitor) {}
865 }
866}
867
868#[derive(Debug)]
874pub struct Persistence(
875 #[cfg(feature = "save")] pub(crate) crate::save::schema::BehaviorV1Ser,
876 #[cfg(not(feature = "save"))] (),
877);
878
879#[derive(Clone, Copy, Debug, Default, PartialEq)]
883pub(crate) struct BehaviorSetStepInfo {
884 total_count: usize,
886 total_time: Duration,
888 stepped: usize,
890 acted: usize,
892}
893
894impl ops::AddAssign for BehaviorSetStepInfo {
895 fn add_assign(&mut self, other: Self) {
896 let Self {
897 total_count,
898 total_time,
899 stepped,
900 acted,
901 } = self;
902 *total_count += other.total_count;
903 *total_time += other.total_time;
904 *stepped += other.stepped;
905 *acted += other.acted;
906 }
907}
908
909impl crate::util::Fmt<StatusText> for BehaviorSetStepInfo {
910 fn fmt(&self, f: &mut fmt::Formatter<'_>, fopt: &StatusText) -> fmt::Result {
911 let Self {
912 total_count,
913 total_time,
914 stepped,
915 acted,
916 } = self;
917 write!(
918 f,
919 "{acted} acted of {stepped} stepped of {total_count} in {total_time}",
920 total_time = total_time.refmt(fopt)
921 )
922 }
923}
924
925#[cfg(test)]
926#[allow(
927 unused,
928 reason = "TODO(ecs): BehaviorSet is either going away or getting its tests reworked"
929)]
930mod tests {
931 use super::*;
932 use crate::character::{Character, CharacterTransaction};
933 use crate::math::{FreeCoordinate, GridAab};
934 use crate::physics::BodyTransaction;
935 use crate::space::{Space, SpaceBehaviorAttachment, SpaceTransaction};
936 use crate::time;
937 use crate::transaction::no_outputs;
938 use crate::universe::Universe;
939 use euclid::point3;
940
941 #[cfg(false)] #[test]
943 fn behavior_set_debug() {
944 use pretty_assertions::assert_eq;
945
946 let mut set = BehaviorSet::<Character>::new();
947
948 assert_eq!(format!("{set:?}"), "BehaviorSet({})");
950 assert_eq!(format!("{set:#?}"), "BehaviorSet({})");
951
952 BehaviorSetTransaction::insert((), Arc::new(NoopBehavior(1)))
953 .execute(&mut set, (), &mut no_outputs)
954 .unwrap();
955 let key = *set.members.keys().next().unwrap();
956
957 assert_eq!(
959 format!("{set:?}"),
960 format!("BehaviorSet({{{key:?}: NoopBehavior(1) @ ()}})")
961 );
962 assert_eq!(
963 format!("{set:#?}\n"),
964 indoc::formatdoc!(
965 "
966 BehaviorSet({{
967 {key:?}: NoopBehavior(1) @ (),
968 }})
969 "
970 ),
971 );
972 }
973
974 #[derive(Clone, Debug, PartialEq)]
975 struct SelfModifyingBehavior {
976 foo: u32,
977 then: Then,
978 }
979 #[cfg(false)] impl Behavior<Character> for SelfModifyingBehavior {
981 fn step(&self, context: &Context<'_, Character>) -> (UniverseTransaction, Then) {
982 let mut txn = context.bind_host(CharacterTransaction::body(
983 BodyTransaction::default().with_position(point3(
984 FreeCoordinate::from(self.foo),
985 0.,
986 0.,
987 )),
988 ));
989 if self.then != Then::Drop {
990 txn.merge_from(context.replace_self(SelfModifyingBehavior {
991 foo: self.foo + 1,
992 ..self.clone()
993 }))
994 .unwrap();
995 }
996 (txn, self.then.clone())
997 }
998 fn persistence(&self) -> Option<Persistence> {
999 None
1000 }
1001 }
1002
1003 impl VisitHandles for SelfModifyingBehavior {
1004 fn visit_handles(&self, _visitor: &mut dyn HandleVisitor) {}
1006 }
1007
1008 #[cfg(false)] #[test]
1010 fn self_transaction() {
1011 let mut u = Universe::new();
1012 let space = u.insert_anonymous(Space::empty_positive(1, 1, 1));
1014 let mut character = Character::spawn_default(u.read_ticket(), space);
1015 character.add_behavior(SelfModifyingBehavior {
1016 foo: 1,
1017 then: Then::Step,
1018 });
1019 let character = u.insert("character".into(), character).unwrap();
1020
1021 u.step(false, time::Deadline::Whenever);
1022 u.step(false, time::Deadline::Whenever);
1023
1024 let character = character.read(u.read_ticket()).unwrap();
1025 assert_eq!(
1026 character
1027 .behaviors()
1028 .query::<SelfModifyingBehavior>()
1029 .map(|qi| qi.behavior)
1030 .collect::<Vec<_>>(),
1031 vec![&SelfModifyingBehavior {
1032 foo: 3,
1033 then: Then::Step,
1034 }]
1035 );
1036 assert_eq!(character.body().position().x, 2.0);
1037 }
1038
1039 #[cfg(false)] #[test]
1041 fn dropped_when_requested() {
1042 let mut u = Universe::new();
1043 let space = u.insert_anonymous(Space::empty_positive(1, 1, 1));
1045 let mut character = Character::spawn_default(u.read_ticket(), space);
1046 character.add_behavior(SelfModifyingBehavior {
1047 foo: 1,
1048 then: Then::Drop,
1049 });
1050 let character = u.insert("character".into(), character).unwrap();
1051
1052 assert_eq!(
1053 character
1054 .read(u.read_ticket())
1055 .unwrap()
1056 .behaviors()
1057 .query::<SelfModifyingBehavior>()
1058 .count(),
1059 1
1060 );
1061 u.step(false, time::Deadline::Whenever);
1062 assert_eq!(
1063 character
1064 .read(u.read_ticket())
1065 .unwrap()
1066 .behaviors()
1067 .query::<SelfModifyingBehavior>()
1068 .count(),
1069 0
1070 );
1071 }
1072
1073 #[cfg(false)] #[test]
1075 fn query() {
1076 #[derive(Debug, Eq, PartialEq)]
1077 struct Expected;
1078 #[derive(Debug, Eq, PartialEq)]
1079 struct Unexpected;
1080
1081 let mut set = BehaviorSet::<Character>::new();
1082 let arc_qe = Arc::new(NoopBehavior(Expected));
1083 BehaviorSetTransaction::insert((), arc_qe.clone())
1084 .execute(&mut set, (), &mut no_outputs)
1085 .unwrap();
1086 let arc_qu = Arc::new(NoopBehavior(Unexpected));
1088 BehaviorSetTransaction::insert((), arc_qu.clone())
1089 .execute(&mut set, (), &mut no_outputs)
1090 .unwrap();
1091
1092 assert_eq!(
1094 set.query::<NoopBehavior<Expected>>().map(|qi| qi.behavior).collect::<Vec<_>>(),
1095 vec![&NoopBehavior(Expected)],
1096 );
1097
1098 assert_eq!(
1100 set.query_any(None)
1101 .map(|qi| core::ptr::from_ref(qi.behavior))
1102 .collect::<Vec<_>>(),
1103 vec![
1104 Arc::as_ptr(&arc_qe) as *const dyn Behavior<Character>,
1105 Arc::as_ptr(&arc_qu) as *const dyn Behavior<Character>
1106 ],
1107 )
1108 }
1109
1110 #[test]
1111 fn sleep_and_wake() {
1112 use std::sync::mpsc;
1113
1114 #[derive(Debug)]
1115 struct SleepBehavior {
1116 tx: mpsc::Sender<Waker>,
1117 }
1118 impl Behavior<Space> for SleepBehavior {
1119 fn step(&self, context: &Context<'_, '_, Space>) -> (UniverseTransaction, Then) {
1120 self.tx.send(context.waker().clone()).unwrap();
1121 (UniverseTransaction::default(), Then::Sleep)
1122 }
1123 fn persistence(&self) -> Option<Persistence> {
1124 None
1125 }
1126 }
1127 impl VisitHandles for SleepBehavior {
1128 fn visit_handles(&self, _: &mut dyn HandleVisitor) {}
1129 }
1130
1131 let (tx, rx) = mpsc::channel();
1133 let mut u = Universe::new();
1134 let space = u.insert("space".into(), Space::empty_positive(1, 1, 1)).unwrap();
1135 SpaceTransaction::add_behavior(GridAab::ORIGIN_CUBE, SleepBehavior { tx })
1136 .bind(space)
1137 .execute(&mut u, (), &mut no_outputs)
1138 .unwrap();
1139 assert_eq!(mpsc::TryRecvError::Empty, rx.try_recv().unwrap_err());
1140
1141 u.step(false, time::Deadline::Whenever);
1143 let waker: Waker = rx.try_recv().unwrap();
1144
1145 u.step(false, time::Deadline::Whenever);
1147 assert_eq!(mpsc::TryRecvError::Empty, rx.try_recv().unwrap_err());
1148
1149 waker.wake();
1151 u.step(false, time::Deadline::Whenever);
1152 rx.try_recv().unwrap();
1153 }
1154
1155 #[test]
1156 fn txn_attachments_insert() {
1157 let attachment =
1158 SpaceBehaviorAttachment::new(GridAab::from_lower_size([0, 0, 0], [1, 1, 1]));
1159 let transaction =
1160 BehaviorSetTransaction::<Space>::insert(attachment, Arc::new(NoopBehavior(1)));
1161 assert_eq!(
1162 transaction.attachments_affected().collect::<Vec<_>>(),
1163 vec![&attachment]
1164 );
1165 }
1166
1167 #[test]
1168 fn txn_attachments_replace() {
1169 let attachment1 =
1170 SpaceBehaviorAttachment::new(GridAab::from_lower_size([0, 0, 0], [1, 1, 1]));
1171 let attachment2 =
1172 SpaceBehaviorAttachment::new(GridAab::from_lower_size([10, 0, 0], [1, 1, 1]));
1173 let transaction = BehaviorSetTransaction::<Space>::replace(
1174 Key::new(),
1175 Replace {
1176 old: BehaviorSetEntry {
1177 attachment: attachment1,
1178 behavior: Arc::new(NoopBehavior(1)),
1179 waker: None,
1180 },
1181 new: Some(BehaviorSetEntry {
1182 attachment: attachment2,
1183 behavior: Arc::new(NoopBehavior(1)),
1184 waker: None,
1185 }),
1186 },
1187 );
1188 assert_eq!(
1189 transaction.attachments_affected().collect::<Vec<_>>(),
1190 vec![&attachment1, &attachment2]
1191 );
1192 }
1193
1194 #[test]
1195 fn txn_check_non_matching_old() {
1196 let mut set = BehaviorSet::<Space>::new();
1198 let attachment =
1199 SpaceBehaviorAttachment::new(GridAab::from_lower_size([0, 0, 0], [1, 1, 1]));
1200 let correct_old_behavior = Arc::new(NoopBehavior(1));
1201 BehaviorSetTransaction::insert(attachment, correct_old_behavior.clone())
1202 .execute(&mut set, (), &mut no_outputs)
1203 .unwrap();
1204 let key = *set.members.keys().next().unwrap();
1205
1206 {
1208 let transaction = BehaviorSetTransaction::<Space>::replace(
1209 key,
1210 Replace {
1211 old: BehaviorSetEntry {
1212 attachment,
1213 behavior: Arc::new(NoopBehavior(2)), waker: None,
1215 },
1216 new: Some(BehaviorSetEntry {
1217 attachment,
1218 behavior: Arc::new(NoopBehavior(3)),
1219 waker: None,
1220 }),
1221 },
1222 );
1223 assert_eq!(
1224 transaction.check(&set, ()).unwrap_err(),
1225 BehaviorTransactionMismatch {
1226 key,
1227 key_not_found: false,
1228 wrong_attachment: false,
1229 wrong_value: true,
1230 }
1231 );
1232 }
1233
1234 {
1236 let transaction = BehaviorSetTransaction::<Space>::replace(
1237 key,
1238 Replace {
1239 old: BehaviorSetEntry {
1240 attachment: SpaceBehaviorAttachment::new(GridAab::from_lower_size(
1242 [100, 0, 0],
1243 [1, 1, 1],
1244 )),
1245 behavior: correct_old_behavior,
1246 waker: None,
1247 },
1248 new: Some(BehaviorSetEntry {
1249 attachment,
1250 behavior: Arc::new(NoopBehavior(4)),
1251 waker: None,
1252 }),
1253 },
1254 );
1255 assert_eq!(
1256 transaction.check(&set, ()).unwrap_err(),
1257 BehaviorTransactionMismatch {
1258 key,
1259 key_not_found: false,
1260 wrong_attachment: true,
1261 wrong_value: false,
1262 }
1263 );
1264 }
1265 }
1266
1267 #[cfg(false)] #[test]
1269 fn txn_systematic() {
1270 let b1 = Arc::new(SelfModifyingBehavior {
1271 foo: 100,
1272 then: Then::Step,
1273 });
1274 let b2 = Arc::new(SelfModifyingBehavior {
1275 foo: 200,
1276 then: Then::Step,
1277 });
1278
1279 transaction::TransactionTester::new()
1282 .transaction(BehaviorSetTransaction::default(), |_, _| Ok(()))
1283 .transaction(BehaviorSetTransaction::insert((), b1), |_, after| {
1284 after
1285 .query::<SelfModifyingBehavior>()
1286 .map(|item| item.behavior)
1287 .find(|b| b.foo == 100)
1288 .ok_or("expected b1")?;
1289 Ok(())
1290 })
1291 .transaction(BehaviorSetTransaction::insert((), b2), |_, after| {
1292 after
1293 .query::<SelfModifyingBehavior>()
1294 .map(|item| item.behavior)
1295 .find(|b| b.foo == 200)
1296 .ok_or("expected b2")?;
1297 Ok(())
1298 })
1299 .target(BehaviorSet::new)
1300 .test(())
1301 }
1302}