use std::sync::Arc;
use bevy_ecs::{
lifecycle::{Despawn, Insert, Remove},
prelude::*,
};
use rig_core::observe::{
Action, Emitter, Observation, OutcomeSummary, Reason, Stage, Subject, Witness,
};
use super::effect::{EffectOutcome, Held, InFlight, Issued, PendingEffect, Scope, Seq};
pub const BUS_EMITTER: &str = "rig-ecs/bus";
pub fn bus_emitter() -> Emitter {
Emitter::versioned(BUS_EMITTER, env!("CARGO_PKG_VERSION"))
}
const WALK_LIMIT: usize = 4096;
#[derive(Resource, Clone)]
pub struct Witnessing(Arc<dyn Witness + Send + Sync>);
#[derive(Component, Clone, Debug)]
pub struct AdapterOperation {
pub context: rig_core::observe::AdapterContext,
pub host_attempt: std::num::NonZeroU64,
}
#[derive(Resource, Debug, Default)]
struct WitnessObserversInstalled;
impl Witnessing {
fn new(sink: impl Witness + Send + Sync) -> Self {
Self(Arc::new(sink))
}
pub fn install(world: &mut World, sink: impl Witness + Send + Sync) {
world.insert_resource(Self::new(sink));
if world.contains_resource::<WitnessObserversInstalled>() {
return;
}
world.init_resource::<WitnessObserversInstalled>();
world.init_resource::<Despawning>();
world.add_observer(observe_despawn);
world.add_observer(observe_held);
world.add_observer(observe_released);
world.add_observer(observe_hold_transition);
world.add_observer(observe_preflight_outcome);
world.add_observer(observe_outcome_replaced);
world.add_observer(observe_pending_despawned);
}
pub fn observe(&self, observation: Observation) {
self.0.observe(observation);
}
pub fn emit(&self, subject: Subject, stage: Stage, emitter: Emitter, action: Action) {
self.0
.observe(Observation::new(subject, stage, emitter, action));
}
pub fn sink(&self) -> &Arc<dyn Witness + Send + Sync> {
&self.0
}
}
#[derive(Component, Debug, Clone, Copy, Default)]
pub struct Refused;
#[derive(Component, Debug, Clone)]
pub struct SeenOutcome {
pub summary: OutcomeSummary,
pub fingerprint: u64,
}
impl SeenOutcome {
pub fn of(outcome: &Result<rig_core::effect::Outcome, rig_core::error::ErrorReport>) -> Self {
Self {
summary: OutcomeSummary::of(outcome),
fingerprint: fingerprint(outcome),
}
}
}
pub fn fingerprint<T: serde::Serialize>(value: &T) -> u64 {
let mut hash: u64 = 0xcbf2_9ce4_8422_2325;
if let Ok(bytes) = serde_json::to_vec(value) {
for byte in bytes {
hash ^= u64::from(byte);
hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
}
}
hash
}
#[derive(Resource, Debug, Default)]
pub struct Despawning(std::collections::HashSet<Entity>);
#[derive(bevy_ecs::system::SystemParam)]
pub struct DispatchWitness<'w, 's> {
pub witness: Option<Res<'w, Witnessing>>,
pub subjects: Subjects<'w, 's>,
}
#[derive(bevy_ecs::system::SystemParam)]
pub struct SubjectWalk<'w, 's> {
seqs: Query<'w, 's, &'static Seq>,
issued: Query<'w, 's, &'static Issued>,
parents: Query<'w, 's, &'static ChildOf>,
scopes: Query<'w, 's, &'static Scope>,
}
impl SubjectWalk<'_, '_> {
pub fn of_intent(
&self,
entity: Entity,
key: &rig_core::effect::HandlerKey,
family: rig_core::effect::EffectFamily,
) -> Subject {
let mut subject = self.of_entity(entity);
subject.key = Some(key.clone());
subject.family = Some(family);
subject
}
pub fn of_entity(&self, entity: Entity) -> Subject {
let mut subject = Subject {
order: self.seqs.get(entity).ok().map(|seq| seq.0),
effect: self.issued.get(entity).ok().map(|issued| issued.0),
..Subject::default()
};
let mut current = entity;
for _ in 0..WALK_LIMIT {
if subject.scope.is_none()
&& let Ok(Scope(scope)) = self.scopes.get(current)
{
subject.scope = Some(scope.clone());
}
match self.parents.get(current) {
Ok(parent) => {
current = parent.parent();
if subject.parent.is_none()
&& let Ok(Issued(id)) = self.issued.get(current)
{
subject.parent = Some(*id);
}
}
Err(_) => break,
}
}
subject
}
pub fn of_scope(&self, entity: Entity) -> Subject {
let mut current = entity;
for _ in 0..WALK_LIMIT {
if let Ok(Scope(scope)) = self.scopes.get(current) {
return Subject::scoped(scope.clone());
}
match self.parents.get(current) {
Ok(parent) => current = parent.parent(),
Err(_) => break,
}
}
Subject::default()
}
}
#[derive(bevy_ecs::system::SystemParam)]
pub struct Subjects<'w, 's> {
walk: SubjectWalk<'w, 's>,
pending: Query<'w, 's, &'static PendingEffect>,
}
impl Subjects<'_, '_> {
pub fn of(&self, entity: Entity) -> Subject {
let mut subject = self.walk.of_entity(entity);
if let Ok(pending) = self.pending.get(entity) {
subject.key = Some(pending.key.clone());
subject.family = Some(pending.kind.family());
}
subject
}
pub fn of_scope(&self, entity: Entity) -> Subject {
self.walk.of_scope(entity)
}
}
fn observe_despawn(despawned: On<Despawn, Held>, mut despawning: ResMut<Despawning>) {
despawning.0.insert(despawned.event().entity);
}
fn observe_held(
added: On<bevy_ecs::lifecycle::Add, Held>,
subjects: Subjects,
witness: Option<Res<Witnessing>>,
owned: Query<(), With<super::HoldOwners>>,
) {
if owned.contains(added.event().entity) {
return;
}
let Some(witness) = witness else {
return;
};
witness.emit(
subjects.of(added.event().entity),
Stage::Gate,
Emitter::unknown(),
Action::Held {
reason: Reason::unknown(),
},
);
}
fn observe_released(
removed: On<Remove, Held>,
outcomes: Query<(), With<EffectOutcome>>,
subjects: Subjects,
mut despawning: ResMut<Despawning>,
witness: Option<Res<Witnessing>>,
owned: Query<(), With<super::HoldOwners>>,
) {
let entity = removed.event().entity;
let despawned = despawning.0.remove(&entity);
let Some(witness) = witness else {
return;
};
if outcomes.get(entity).is_ok() || despawned || owned.contains(entity) {
return;
}
witness.emit(
subjects.of(entity),
Stage::Gate,
Emitter::unknown(),
Action::Released,
);
}
fn observe_hold_transition(
event: On<super::hold::HoldTransition>,
outcomes: Query<(), With<EffectOutcome>>,
subjects: Subjects,
witness: Option<Res<Witnessing>>,
) {
let event = event.event();
if !event.acquired && outcomes.contains(event.entity) {
return;
}
if let Some(witness) = witness {
witness.emit(
subjects.of(event.entity),
Stage::Gate,
event.owner.clone(),
if event.acquired {
Action::Held {
reason: Reason::unknown(),
}
} else {
Action::Released
},
);
}
}
fn observe_preflight_outcome(
added: On<bevy_ecs::lifecycle::Add, EffectOutcome>,
outcomes: Query<(&EffectOutcome, Has<Issued>, Has<Refused>), With<PendingEffect>>,
subjects: Subjects,
witness: Option<Res<Witnessing>>,
) {
let Some(witness) = witness else {
return;
};
let entity = added.event().entity;
let Ok((outcome, issued, refused)) = outcomes.get(entity) else {
return;
};
if issued || refused {
return;
}
let action = match &outcome.0 {
Err(report) => Action::Denied {
reason: Reason::from_report(report),
},
Ok(outcome) => Action::Replaced {
recorded: OutcomeSummary::Err {
reason: Reason::code("never_served"),
retryable: false,
},
consumed: OutcomeSummary::Ok {
family: outcome.family(),
},
},
};
witness.emit(subjects.of(entity), Stage::Gate, Emitter::unknown(), action);
}
fn observe_outcome_replaced(
inserted: On<Insert, EffectOutcome>,
mut replaced: Query<(&EffectOutcome, &mut SeenOutcome), Without<InFlight>>,
subjects: Subjects,
witness: Option<Res<Witnessing>>,
) {
let Some(witness) = witness else {
return;
};
let entity = inserted.event().entity;
let Ok((outcome, mut seen)) = replaced.get_mut(entity) else {
return;
};
let now = SeenOutcome::of(&outcome.0);
if now.fingerprint == seen.fingerprint {
return;
}
witness.emit(
subjects.of(entity),
Stage::Judge,
Emitter::unknown(),
Action::Replaced {
recorded: seen.summary.clone(),
consumed: now.summary.clone(),
},
);
*seen = now;
}
fn observe_pending_despawned(
removed: On<Remove, PendingEffect>,
state: Query<(Has<Issued>, Has<EffectOutcome>), With<PendingEffect>>,
subjects: Subjects,
witness: Option<Res<Witnessing>>,
) {
let entity = removed.event().entity;
let Some(witness) = witness else {
return;
};
let Ok((issued, answered)) = state.get(entity) else {
return;
};
if issued || answered {
return;
}
witness.emit(
subjects.of(entity),
Stage::Dispatch,
Emitter::unknown(),
Action::Cancelled {
reason: Reason::code("despawned_before_dispatch"),
},
);
}