1use alloc::collections::{BTreeMap, BTreeSet};
4use alloc::sync::{Arc, Weak};
5use alloc::vec::Vec;
6use core::any::TypeId;
7use core::fmt;
8use core::mem;
9use core::ops;
10
11#[cfg(doc)]
12use core::{future::Future, task::Waker};
13
14use downcast_rs::{impl_downcast, Downcast};
15
16use crate::time::Tick;
17use crate::transaction::{self, Merge as _, Transaction};
18use crate::universe::{HandleVisitor, UniverseTransaction, VisitHandles};
19use crate::util::maybe_sync::{Mutex, SendSyncIfStd};
20
21#[cfg(doc)]
22use crate::universe::Universe;
23use crate::util::StatusText;
24
25pub trait Behavior<H: Host>:
29 fmt::Debug + SendSyncIfStd + Downcast + VisitHandles + 'static
30{
31 fn step(&self, context: &Context<'_, H>) -> (UniverseTransaction, Then);
36
37 fn persistence(&self) -> Option<Persistence>;
45}
46
47impl_downcast!(Behavior<H> where H: Host);
48
49pub trait Host: transaction::Transactional + 'static {
51 type Attachment: fmt::Debug + Clone + Eq + 'static;
54}
55
56#[non_exhaustive]
58pub struct Context<'a, H: Host> {
59 pub tick: Tick,
61
62 pub host: &'a H,
64
65 pub attachment: &'a H::Attachment,
68
69 waker: &'a BehaviorWaker,
70
71 host_transaction_binder: &'a dyn Fn(H::Transaction) -> UniverseTransaction,
72 self_transaction_binder: &'a dyn Fn(Arc<dyn Behavior<H>>) -> UniverseTransaction,
73}
74
75impl<'a, H: Host> Context<'a, H> {
76 pub fn waker(&self) -> &'a BehaviorWaker {
83 self.waker
84 }
85
86 pub fn bind_host(&self, transaction: H::Transaction) -> UniverseTransaction {
89 (self.host_transaction_binder)(transaction)
90 }
91
92 pub fn replace_self<B: Behavior<H> + 'static>(&self, new_behavior: B) -> UniverseTransaction {
97 (self.self_transaction_binder)(Arc::new(new_behavior))
98 }
99}
100
101impl<H: Host + fmt::Debug> fmt::Debug for Context<'_, H> {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 f.debug_struct("Context")
105 .field("host", &self.host)
106 .finish_non_exhaustive()
107 }
108}
109
110#[derive(Debug, Clone, PartialEq)]
114#[non_exhaustive]
115pub enum Then {
116 Drop,
118
119 Step,
122
123 Sleep,
125}
126
127#[doc = include_str!("save/serde-warning.md")]
135#[expect(clippy::module_name_repetitions)] pub 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: behavior.downcast_ref::<T>().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 host: &H,
200 host_transaction_binder: &dyn Fn(H::Transaction) -> UniverseTransaction,
201 set_transaction_binder: impl Fn(BehaviorSetTransaction<H>) -> H::Transaction,
205 tick: Tick,
206 ) -> (UniverseTransaction, BehaviorSetStepInfo) {
207 let mut transactions = Vec::new();
208 let mut info = BehaviorSetStepInfo {
209 stepped: 0,
210 acted: 0,
211 total: self.members.len(),
212 };
213
214 let woken: BTreeSet<_> = mem::take(&mut self.woken.lock().unwrap());
217
218 for key in woken {
219 let Some(entry) = self.members.get(&key) else {
220 continue;
222 };
223
224 let context = &Context {
225 tick,
226 host,
227 attachment: &entry.attachment,
228 waker: entry.waker.as_ref().unwrap(),
229 host_transaction_binder,
230 self_transaction_binder: &|new_behavior| {
231 host_transaction_binder(set_transaction_binder(
232 BehaviorSetTransaction::replace(
233 key,
234 Replace {
235 old: entry.clone(),
236 new: Some(BehaviorSetEntry {
237 attachment: entry.attachment.clone(),
238 behavior: new_behavior,
239 waker: None,
240 }),
241 },
242 ),
243 ))
244 },
245 };
246 info.stepped += 1;
247 let (txn, then) = entry.behavior.step(context);
248 if txn != UniverseTransaction::default() {
249 info.acted += 1;
250 transactions.push(txn);
251 }
252 match then {
253 Then::Drop => transactions.push(host_transaction_binder(set_transaction_binder(
254 BehaviorSetTransaction::delete(key, entry.clone()),
255 ))),
256
257 Then::Step => context.waker.wake_by_ref(),
259
260 Then::Sleep => { }
261 }
262 }
263 let transaction = transactions
264 .into_iter()
265 .reduce(|a, b| a.merge(b).expect("TODO: handle merge failure"))
266 .unwrap_or_default();
267
268 (transaction, info)
269 }
270
271 #[allow(unused)] pub(crate) fn iter(&self) -> impl Iterator<Item = &BehaviorSetEntry<H>> + '_ {
273 self.members.values()
274 }
275
276 #[allow(unused)] pub(crate) fn is_empty(&self) -> bool {
278 self.members.is_empty()
279 }
280}
281
282impl<H: Host> Clone for BehaviorSet<H> {
283 fn clone(&self) -> Self {
284 let woken = Arc::new(Mutex::new(self.members.keys().copied().collect()));
285
286 let members: BTreeMap<Key, _> = self
289 .members
290 .values()
291 .map(|entry| {
292 let mut entry = entry.clone();
293 let key = Key::new();
294 entry.waker = Some(BehaviorWakerInner::create_waker(key, &woken));
295 (key, entry)
296 })
297 .collect();
298
299 Self { members, woken }
300 }
301}
302
303impl<H: Host> Default for BehaviorSet<H> {
304 fn default() -> Self {
305 Self::new()
306 }
307}
308
309impl<H: Host> fmt::Debug for BehaviorSet<H> {
310 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311 write!(f, "BehaviorSet(")?;
312 f.debug_map().entries(self.members.iter()).finish()?;
313 write!(f, ")")?;
314 Ok(())
315 }
316}
317
318impl<H: Host> VisitHandles for BehaviorSet<H> {
319 fn visit_handles(&self, visitor: &mut dyn HandleVisitor) {
320 let Self { members, woken: _ } = self;
321 for entry in members.values() {
322 entry.behavior.visit_handles(visitor);
323 }
324 }
325}
326
327impl<H: Host> transaction::Transactional for BehaviorSet<H> {
328 type Transaction = BehaviorSetTransaction<H>;
329}
330
331#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
334struct Key(u64);
335
336impl Key {
337 fn new() -> Self {
338 #![allow(
339 clippy::useless_conversion,
340 clippy::unnecessary_fallible_conversions,
341 reason = "depends on pointer width and atomic support"
342 )]
343
344 use core::sync::atomic::{self, Ordering};
345
346 cfg_if::cfg_if! {
347 if #[cfg(target_has_atomic = "64")] {
350 static ID_COUNTER: atomic::AtomicU64 = atomic::AtomicU64::new(0);
351 } else if #[cfg(target_has_atomic = "32")] {
352 static ID_COUNTER: atomic::AtomicU32 = atomic::AtomicU32::new(0);
353 } else {
354 static ID_COUNTER: atomic::AtomicUsize = atomic::AtomicUsize::new(0);
356 }
357 }
358
359 let id = ID_COUNTER
360 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |counter| {
361 counter.checked_add(1)
362 })
363 .expect("behavior id overflow");
364
365 Self(id.try_into().unwrap()) }
367}
368
369impl fmt::Display for Key {
370 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371 fmt::Display::fmt(&self.0, f)
372 }
373}
374impl fmt::Debug for Key {
375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 fmt::Display::fmt(&self.0, f)
377 }
378}
379
380pub(crate) struct BehaviorSetEntry<H: Host> {
381 pub(crate) attachment: H::Attachment,
382 pub(crate) behavior: Arc<dyn Behavior<H>>,
385 waker: Option<BehaviorWaker>,
388}
389
390impl<H: Host> Clone for BehaviorSetEntry<H> {
391 fn clone(&self) -> Self {
392 Self {
394 attachment: self.attachment.clone(),
395 behavior: self.behavior.clone(),
396 waker: self.waker.clone(),
397 }
398 }
399}
400
401impl<H: Host> fmt::Debug for BehaviorSetEntry<H> {
402 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403 let BehaviorSetEntry {
404 attachment,
405 behavior,
406 waker: _,
407 } = self;
408 behavior.fmt(f)?; write!(f, " @ {attachment:?}")?; Ok(())
411 }
412}
413
414impl<H: Host> PartialEq for BehaviorSetEntry<H> {
415 fn eq(&self, other: &Self) -> bool {
416 self.attachment == other.attachment && Arc::ptr_eq(&self.behavior, &other.behavior)
417 }
418}
419
420type WokenSet = Mutex<BTreeSet<Key>>;
421
422#[derive(Clone, Debug)]
434#[expect(clippy::module_name_repetitions)] pub struct BehaviorWaker(Arc<BehaviorWakerInner>);
436impl BehaviorWaker {
437 pub fn wake(self) {
442 self.wake_by_ref()
443 }
444
445 pub fn wake_by_ref(&self) {
450 let Some(strong_set) = self.0.set.upgrade() else {
451 return;
453 };
454 let Ok(mut mut_set) = strong_set.lock() else {
455 return;
457 };
458 mut_set.insert(self.0.key);
459 }
460}
461
462#[derive(Debug)]
463struct BehaviorWakerInner {
464 key: Key,
465 set: Weak<WokenSet>,
466}
467impl BehaviorWakerInner {
468 fn create_waker(key: Key, woken: &Arc<WokenSet>) -> BehaviorWaker {
469 BehaviorWaker(Arc::new(BehaviorWakerInner {
470 key,
471 set: Arc::downgrade(woken),
472 }))
473 }
474}
475
476#[non_exhaustive]
478pub struct QueryItem<'a, H: Host, B: Behavior<H> + ?Sized> {
479 pub behavior: &'a B,
481 pub attachment: &'a H::Attachment,
486}
487
488impl<H: Host, B: Behavior<H> + ?Sized> Clone for QueryItem<'_, H, B> {
489 fn clone(&self) -> Self {
490 Self {
492 attachment: self.attachment,
493 behavior: self.behavior,
494 }
495 }
496}
497
498impl<H: Host, B: Behavior<H> + ?Sized> fmt::Debug for QueryItem<'_, H, B> {
499 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500 let QueryItem {
501 attachment,
502 behavior,
503 } = self;
504 behavior.fmt(f)?; write!(f, " @ {attachment:?}")?; Ok(())
507 }
508}
509
510#[derive(Debug)]
512#[expect(clippy::module_name_repetitions)] pub struct BehaviorSetTransaction<H: Host> {
514 replace: BTreeMap<Key, Replace<H>>,
516 insert: Vec<BehaviorSetEntry<H>>,
518}
519
520#[derive(Debug)]
521struct Replace<H: Host> {
522 old: BehaviorSetEntry<H>,
523 new: Option<BehaviorSetEntry<H>>,
525}
526
527impl<H: Host> BehaviorSetTransaction<H> {
528 pub(crate) fn is_empty(&self) -> bool {
530 self.replace.is_empty() && self.insert.is_empty()
531 }
532
533 fn replace(key: Key, replacement: Replace<H>) -> Self {
536 BehaviorSetTransaction {
537 replace: BTreeMap::from([(key, replacement)]),
538 ..Default::default()
539 }
540 }
541
542 pub fn insert(attachment: H::Attachment, behavior: Arc<dyn Behavior<H>>) -> Self {
544 BehaviorSetTransaction {
545 insert: vec![BehaviorSetEntry {
546 attachment,
547 behavior,
548 waker: None,
549 }],
550 ..Default::default()
551 }
552 }
553
554 fn delete(key: Key, existing_entry: BehaviorSetEntry<H>) -> Self {
557 Self::replace(
558 key,
559 Replace {
560 old: existing_entry,
561 new: None,
562 },
563 )
564 }
565
566 pub(crate) fn attachments_affected(&self) -> impl Iterator<Item = &H::Attachment> {
569 let replace = self.replace.values().flat_map(|Replace { old, new }| {
570 [
571 Some(&old.attachment),
572 new.as_ref().map(|entry| &entry.attachment),
573 ]
574 .into_iter()
575 .flatten()
576 });
577 let insert = self.insert.iter().map(|entry| &entry.attachment);
578 replace.chain(insert)
579 }
580}
581
582impl<H: Host> Transaction for BehaviorSetTransaction<H> {
583 type Target = BehaviorSet<H>;
584 type CommitCheck = CommitCheck;
585 type Output = transaction::NoOutput;
586 type Mismatch = BehaviorTransactionMismatch;
587
588 fn check(&self, target: &BehaviorSet<H>) -> Result<Self::CommitCheck, Self::Mismatch> {
589 let Self { replace, insert } = self;
590 for (&key, Replace { old, new: _ }) in replace {
592 if let Some(BehaviorSetEntry {
593 attachment,
594 behavior,
595 waker: _,
596 }) = target.members.get(&key)
597 {
598 let wrong_attachment = attachment != &old.attachment;
599 let wrong_value = !Arc::ptr_eq(behavior, &old.behavior);
600 if wrong_attachment || wrong_value {
601 return Err(BehaviorTransactionMismatch {
602 key,
603 key_not_found: false,
604 wrong_attachment,
605 wrong_value,
606 });
607 }
608 } else {
609 return Err(BehaviorTransactionMismatch {
610 key,
611 key_not_found: true,
612 wrong_attachment: false,
613 wrong_value: false,
614 });
615 }
616 }
617
618 let _ = insert;
620
621 Ok(CommitCheck { _private: () })
622 }
623
624 fn commit(
625 &self,
626 target: &mut BehaviorSet<H>,
627 _: Self::CommitCheck,
628 _outputs: &mut dyn FnMut(Self::Output),
629 ) -> Result<(), transaction::CommitError> {
630 for (key, replacement) in &self.replace {
631 match &replacement.new {
632 Some(new) => {
633 let Some(entry) = target.members.get_mut(key) else {
634 return Err(transaction::CommitError::message::<Self>(format!(
635 "behavior set does not contain key {key}"
636 )));
637 };
638 let BehaviorSetEntry {
639 attachment,
640 behavior,
641 waker,
642 } = new.clone();
643 assert!(
644 waker.is_none(),
645 "transaction entries should not have wakers"
646 );
647 entry.attachment = attachment;
648 entry.behavior = behavior;
649 }
650 None => {
651 let Some(_) = target.members.remove(key) else {
652 return Err(transaction::CommitError::message::<Self>(format!(
653 "behavior set does not contain key {key}"
654 )));
655 };
656 }
657 }
658 }
659
660 let mut woken = target.woken.lock().map_err(|_| {
662 transaction::CommitError::message::<Self>("behavior set wake lock poisoned".into())
663 })?;
664
665 target
666 .members
667 .extend(self.insert.iter().cloned().map(|mut entry| {
668 let key = Key::new();
671
672 woken.insert(key);
674
675 entry.waker = Some(BehaviorWakerInner::create_waker(key, &target.woken));
677
678 (key, entry)
679 }));
680 Ok(())
681 }
682}
683
684impl<H: Host> transaction::Merge for BehaviorSetTransaction<H> {
685 type MergeCheck = MergeCheck;
686 type Conflict = BehaviorTransactionConflict;
687
688 fn check_merge(&self, other: &Self) -> Result<Self::MergeCheck, Self::Conflict> {
689 if let Some(&key) = self
691 .replace
692 .keys()
693 .find(|key| other.replace.contains_key(key))
694 {
695 return Err(BehaviorTransactionConflict { key });
696 }
697 Ok(MergeCheck { _private: () })
698 }
699
700 fn commit_merge(&mut self, other: Self, _: Self::MergeCheck) {
701 self.replace.extend(other.replace);
702 self.insert.extend(other.insert);
703 }
704}
705
706impl<H: Host> Clone for BehaviorSetTransaction<H> {
707 fn clone(&self) -> Self {
709 Self {
710 replace: self.replace.clone(),
711 insert: self.insert.clone(),
712 }
713 }
714}
715
716impl<H: Host> Default for BehaviorSetTransaction<H> {
717 fn default() -> Self {
719 Self {
720 replace: Default::default(),
721 insert: Default::default(),
722 }
723 }
724}
725
726impl<H: Host> PartialEq for BehaviorSetTransaction<H> {
727 fn eq(&self, other: &Self) -> bool {
729 let Self {
730 replace: r1,
731 insert: i1,
732 } = self;
733 let Self {
734 replace: r2,
735 insert: i2,
736 } = other;
737 r1 == r2 && i1 == i2
738 }
739}
740impl<H: Host> PartialEq for Replace<H> {
741 fn eq(&self, other: &Self) -> bool {
743 let Self {
744 old: old1,
745 new: new1,
746 } = self;
747 let Self {
748 old: old2,
749 new: new2,
750 } = other;
751 old1 == old2 && new1 == new2
752 }
753}
754
755impl<H: Host> Eq for BehaviorSetTransaction<H> {}
756impl<H: Host> Eq for Replace<H> {}
757
758impl<H: Host> Clone for Replace<H> {
759 fn clone(&self) -> Self {
761 Self {
762 old: self.old.clone(),
763 new: self.new.clone(),
764 }
765 }
766}
767
768#[derive(Debug)]
769#[doc(hidden)] pub struct CommitCheck {
771 _private: (),
772}
773#[derive(Debug)]
774#[doc(hidden)] pub struct MergeCheck {
776 _private: (),
777}
778
779#[derive(Clone, Debug, Eq, PartialEq)]
781#[expect(clippy::module_name_repetitions)]
782pub struct BehaviorTransactionMismatch {
783 key: Key,
784 key_not_found: bool,
786 wrong_attachment: bool,
787 wrong_value: bool,
788}
789
790#[derive(Clone, Debug, Eq, PartialEq, displaydoc::Display)]
792#[non_exhaustive]
793#[displaydoc("tried to replace the same behavior slot, {key}, twice")]
794#[expect(clippy::module_name_repetitions)]
795pub struct BehaviorTransactionConflict {
796 key: Key,
797}
798
799impl core::error::Error for BehaviorTransactionMismatch {}
800impl core::error::Error for BehaviorTransactionConflict {}
801
802impl fmt::Display for BehaviorTransactionMismatch {
803 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
804 let &Self {
805 key,
806 key_not_found,
807 wrong_attachment,
808 wrong_value,
809 } = self;
810 write!(f, "behavior {key} ")?;
811 if key_not_found {
812 write!(f, "not found")?;
813 } else {
814 write!(f, "does not have a matching ")?;
815 if wrong_attachment && wrong_value {
816 write!(f, "attachment or value")?;
817 } else if wrong_attachment {
818 write!(f, "attachment")?;
819 } else if wrong_value {
820 write!(f, "value")?;
821 } else {
822 write!(f, "<error in error details>")?;
823 }
824 }
825 Ok(())
826 }
827}
828
829#[cfg(test)]
830pub(crate) use testing::*;
831#[cfg(test)]
832mod testing {
833 use super::*;
834
835 #[derive(Clone, Eq, PartialEq)]
837 pub(crate) struct NoopBehavior<D>(pub D);
838
839 impl<D: fmt::Debug> fmt::Debug for NoopBehavior<D> {
840 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
841 write!(f, "NoopBehavior(")?;
842 self.0.fmt(f)?; write!(f, ")")?;
844 Ok(())
845 }
846 }
847
848 impl<H: Host, D: fmt::Debug + Send + Sync + 'static> Behavior<H> for NoopBehavior<D> {
849 fn step(&self, _context: &Context<'_, H>) -> (UniverseTransaction, Then) {
850 (UniverseTransaction::default(), Then::Step)
851 }
852 fn persistence(&self) -> Option<Persistence> {
853 None
854 }
855 }
856
857 impl<D> VisitHandles for NoopBehavior<D> {
858 fn visit_handles(&self, _visitor: &mut dyn HandleVisitor) {}
859 }
860}
861
862#[derive(Debug)]
868pub struct Persistence(
869 #[cfg(feature = "save")] pub(crate) crate::save::schema::BehaviorV1Ser,
870 #[cfg(not(feature = "save"))] (),
871);
872
873#[derive(Clone, Copy, Debug, Default, PartialEq)]
877pub(crate) struct BehaviorSetStepInfo {
878 total: usize,
880 stepped: usize,
882 acted: usize,
884}
885
886impl ops::AddAssign for BehaviorSetStepInfo {
887 fn add_assign(&mut self, other: Self) {
888 let Self {
889 total,
890 stepped,
891 acted,
892 } = self;
893 *total += other.total;
894 *stepped += other.stepped;
895 *acted += other.acted;
896 }
897}
898
899impl crate::util::Fmt<StatusText> for BehaviorSetStepInfo {
900 fn fmt(&self, f: &mut fmt::Formatter<'_>, _: &StatusText) -> fmt::Result {
901 let Self {
902 total,
903 stepped,
904 acted,
905 } = self;
906 write!(f, "{acted} acted of {stepped} stepped of {total}")
907 }
908}
909
910#[cfg(test)]
911mod tests {
912 use super::*;
913 use crate::character::{Character, CharacterTransaction};
914 use crate::math::{FreeCoordinate, GridAab};
915 use crate::physics::BodyTransaction;
916 use crate::space::{Space, SpaceBehaviorAttachment, SpaceTransaction};
917 use crate::time;
918 use crate::transaction::no_outputs;
919 use crate::universe::Universe;
920 use euclid::point3;
921
922 #[test]
923 fn behavior_set_debug() {
924 use pretty_assertions::assert_eq;
925
926 let mut set = BehaviorSet::<Character>::new();
927
928 assert_eq!(format!("{set:?}"), "BehaviorSet({})");
930 assert_eq!(format!("{set:#?}"), "BehaviorSet({})");
931
932 BehaviorSetTransaction::insert((), Arc::new(NoopBehavior(1)))
933 .execute(&mut set, &mut no_outputs)
934 .unwrap();
935 let key = *set.members.keys().next().unwrap();
936
937 assert_eq!(
939 format!("{set:?}"),
940 format!("BehaviorSet({{{key:?}: NoopBehavior(1) @ ()}})")
941 );
942 assert_eq!(
943 format!("{set:#?}\n"),
944 indoc::formatdoc!(
945 "
946 BehaviorSet({{
947 {key:?}: NoopBehavior(1) @ (),
948 }})
949 "
950 ),
951 );
952 }
953
954 #[derive(Clone, Debug, PartialEq)]
955 struct SelfModifyingBehavior {
956 foo: u32,
957 then: Then,
958 }
959 impl Behavior<Character> for SelfModifyingBehavior {
960 fn step(&self, context: &Context<'_, Character>) -> (UniverseTransaction, Then) {
961 let mut txn = context.bind_host(CharacterTransaction::body(
962 BodyTransaction::default().with_position(point3(
963 FreeCoordinate::from(self.foo),
964 0.,
965 0.,
966 )),
967 ));
968 if self.then != Then::Drop {
969 txn.merge_from(context.replace_self(SelfModifyingBehavior {
970 foo: self.foo + 1,
971 ..self.clone()
972 }))
973 .unwrap();
974 }
975 (txn, self.then.clone())
976 }
977 fn persistence(&self) -> Option<Persistence> {
978 None
979 }
980 }
981
982 impl VisitHandles for SelfModifyingBehavior {
983 fn visit_handles(&self, _visitor: &mut dyn HandleVisitor) {}
985 }
986
987 #[test]
988 fn self_transaction() {
989 let mut u = Universe::new();
990 let space = u.insert_anonymous(Space::empty_positive(1, 1, 1));
992 let mut character = Character::spawn_default(space);
993 character.add_behavior(SelfModifyingBehavior {
994 foo: 1,
995 then: Then::Step,
996 });
997 let character = u.insert_anonymous(character);
998
999 u.step(false, time::DeadlineNt::Whenever);
1000 u.step(false, time::DeadlineNt::Whenever);
1001
1002 let character = character.read().unwrap();
1003 assert_eq!(
1004 character
1005 .behaviors
1006 .query::<SelfModifyingBehavior>()
1007 .map(|qi| qi.behavior)
1008 .collect::<Vec<_>>(),
1009 vec![&SelfModifyingBehavior {
1010 foo: 3,
1011 then: Then::Step,
1012 }]
1013 );
1014 assert_eq!(character.body.position().x, 2.0);
1015 }
1016
1017 #[test]
1018 fn dropped_when_requested() {
1019 let mut u = Universe::new();
1020 let space = u.insert_anonymous(Space::empty_positive(1, 1, 1));
1022 let mut character = Character::spawn_default(space);
1023 character.add_behavior(SelfModifyingBehavior {
1024 foo: 1,
1025 then: Then::Drop,
1026 });
1027 let character = u.insert_anonymous(character);
1028
1029 assert_eq!(
1030 character
1031 .read()
1032 .unwrap()
1033 .behaviors
1034 .query::<SelfModifyingBehavior>()
1035 .count(),
1036 1
1037 );
1038 u.step(false, time::DeadlineNt::Whenever);
1039 assert_eq!(
1040 character
1041 .read()
1042 .unwrap()
1043 .behaviors
1044 .query::<SelfModifyingBehavior>()
1045 .count(),
1046 0
1047 );
1048 }
1049
1050 #[test]
1051 fn query() {
1052 #[derive(Debug, Eq, PartialEq)]
1053 struct Expected;
1054 #[derive(Debug, Eq, PartialEq)]
1055 struct Unexpected;
1056
1057 let mut set = BehaviorSet::<Character>::new();
1058 let arc_qe = Arc::new(NoopBehavior(Expected));
1059 BehaviorSetTransaction::insert((), arc_qe.clone())
1060 .execute(&mut set, &mut no_outputs)
1061 .unwrap();
1062 let arc_qu = Arc::new(NoopBehavior(Unexpected));
1064 BehaviorSetTransaction::insert((), arc_qu.clone())
1065 .execute(&mut set, &mut no_outputs)
1066 .unwrap();
1067
1068 assert_eq!(
1070 set.query::<NoopBehavior<Expected>>()
1071 .map(|qi| qi.behavior)
1072 .collect::<Vec<_>>(),
1073 vec![&NoopBehavior(Expected)],
1074 );
1075
1076 assert_eq!(
1078 set.query_any(None)
1079 .map(|qi| core::ptr::from_ref(qi.behavior))
1080 .collect::<Vec<_>>(),
1081 vec![
1082 Arc::as_ptr(&arc_qe) as *const dyn Behavior<Character>,
1083 Arc::as_ptr(&arc_qu) as *const dyn Behavior<Character>
1084 ],
1085 )
1086 }
1087
1088 #[test]
1089 fn sleep_and_wake() {
1090 use std::sync::mpsc;
1091
1092 #[derive(Debug)]
1093 struct SleepBehavior {
1094 tx: mpsc::Sender<BehaviorWaker>,
1095 }
1096 impl Behavior<Space> for SleepBehavior {
1097 fn step(&self, context: &Context<'_, Space>) -> (UniverseTransaction, Then) {
1098 self.tx.send(context.waker().clone()).unwrap();
1099 (UniverseTransaction::default(), Then::Sleep)
1100 }
1101 fn persistence(&self) -> Option<Persistence> {
1102 None
1103 }
1104 }
1105 impl VisitHandles for SleepBehavior {
1106 fn visit_handles(&self, _: &mut dyn HandleVisitor) {}
1107 }
1108
1109 let (tx, rx) = mpsc::channel();
1111 let mut u = Universe::new();
1112 let space = u
1113 .insert("space".into(), Space::empty_positive(1, 1, 1))
1114 .unwrap();
1115 SpaceTransaction::add_behavior(GridAab::ORIGIN_CUBE, SleepBehavior { tx })
1116 .bind(space)
1117 .execute(&mut u, &mut no_outputs)
1118 .unwrap();
1119 assert_eq!(mpsc::TryRecvError::Empty, rx.try_recv().unwrap_err());
1120
1121 u.step(false, time::DeadlineNt::Whenever);
1123 let waker: BehaviorWaker = rx.try_recv().unwrap();
1124
1125 u.step(false, time::DeadlineNt::Whenever);
1127 assert_eq!(mpsc::TryRecvError::Empty, rx.try_recv().unwrap_err());
1128
1129 waker.wake();
1131 u.step(false, time::DeadlineNt::Whenever);
1132 rx.try_recv().unwrap();
1133 }
1134
1135 #[test]
1136 fn txn_attachments_insert() {
1137 let attachment =
1138 SpaceBehaviorAttachment::new(GridAab::from_lower_size([0, 0, 0], [1, 1, 1]));
1139 let transaction =
1140 BehaviorSetTransaction::<Space>::insert(attachment, Arc::new(NoopBehavior(1)));
1141 assert_eq!(
1142 transaction.attachments_affected().collect::<Vec<_>>(),
1143 vec![&attachment]
1144 );
1145 }
1146
1147 #[test]
1148 fn txn_attachments_replace() {
1149 let attachment1 =
1150 SpaceBehaviorAttachment::new(GridAab::from_lower_size([0, 0, 0], [1, 1, 1]));
1151 let attachment2 =
1152 SpaceBehaviorAttachment::new(GridAab::from_lower_size([10, 0, 0], [1, 1, 1]));
1153 let transaction = BehaviorSetTransaction::<Space>::replace(
1154 Key::new(),
1155 Replace {
1156 old: BehaviorSetEntry {
1157 attachment: attachment1,
1158 behavior: Arc::new(NoopBehavior(1)),
1159 waker: None,
1160 },
1161 new: Some(BehaviorSetEntry {
1162 attachment: attachment2,
1163 behavior: Arc::new(NoopBehavior(1)),
1164 waker: None,
1165 }),
1166 },
1167 );
1168 assert_eq!(
1169 transaction.attachments_affected().collect::<Vec<_>>(),
1170 vec![&attachment1, &attachment2]
1171 );
1172 }
1173
1174 #[test]
1175 fn txn_check_non_matching_old() {
1176 let mut set = BehaviorSet::<Space>::new();
1178 let attachment =
1179 SpaceBehaviorAttachment::new(GridAab::from_lower_size([0, 0, 0], [1, 1, 1]));
1180 let correct_old_behavior = Arc::new(NoopBehavior(1));
1181 BehaviorSetTransaction::insert(attachment, correct_old_behavior.clone())
1182 .execute(&mut set, &mut no_outputs)
1183 .unwrap();
1184 let key = *set.members.keys().next().unwrap();
1185
1186 let transaction = BehaviorSetTransaction::<Space>::replace(
1188 key,
1189 Replace {
1190 old: BehaviorSetEntry {
1191 attachment,
1192 behavior: Arc::new(NoopBehavior(2)), waker: None,
1194 },
1195 new: Some(BehaviorSetEntry {
1196 attachment,
1197 behavior: Arc::new(NoopBehavior(3)),
1198 waker: None,
1199 }),
1200 },
1201 );
1202 assert_eq!(
1203 transaction.check(&set).unwrap_err(),
1204 BehaviorTransactionMismatch {
1205 key,
1206 key_not_found: false,
1207 wrong_attachment: false,
1208 wrong_value: true,
1209 }
1210 );
1211
1212 let transaction = BehaviorSetTransaction::<Space>::replace(
1214 key,
1215 Replace {
1216 old: BehaviorSetEntry {
1217 attachment: SpaceBehaviorAttachment::new(GridAab::from_lower_size(
1219 [100, 0, 0],
1220 [1, 1, 1],
1221 )),
1222 behavior: correct_old_behavior,
1223 waker: None,
1224 },
1225 new: Some(BehaviorSetEntry {
1226 attachment,
1227 behavior: Arc::new(NoopBehavior(4)),
1228 waker: None,
1229 }),
1230 },
1231 );
1232 assert_eq!(
1233 transaction.check(&set).unwrap_err(),
1234 BehaviorTransactionMismatch {
1235 key,
1236 key_not_found: false,
1237 wrong_attachment: true,
1238 wrong_value: false,
1239 }
1240 );
1241 }
1242
1243 #[test]
1244 fn txn_systematic() {
1245 let b1 = Arc::new(SelfModifyingBehavior {
1246 foo: 100,
1247 then: Then::Step,
1248 });
1249 let b2 = Arc::new(SelfModifyingBehavior {
1250 foo: 200,
1251 then: Then::Step,
1252 });
1253
1254 transaction::TransactionTester::new()
1257 .transaction(BehaviorSetTransaction::default(), |_, _| Ok(()))
1258 .transaction(BehaviorSetTransaction::insert((), b1), |_, after| {
1259 after
1260 .query::<SelfModifyingBehavior>()
1261 .map(|item| item.behavior)
1262 .find(|b| b.foo == 100)
1263 .ok_or("expected b1")?;
1264 Ok(())
1265 })
1266 .transaction(BehaviorSetTransaction::insert((), b2), |_, after| {
1267 after
1268 .query::<SelfModifyingBehavior>()
1269 .map(|item| item.behavior)
1270 .find(|b| b.foo == 200)
1271 .ok_or("expected b2")?;
1272 Ok(())
1273 })
1274 .target(BehaviorSet::new)
1275 .test()
1276 }
1277}