use bevy_reflect::Reflect;
use std::{
any::TypeId,
collections::{HashMap, HashSet},
marker::PhantomData,
};
use bevy_ecs::{
prelude::*,
system::{EntityCommands, SystemParam},
};
use rig_core::{
effect::{EffectKind, Family, FamilyDescriptor, HandlerDescriptor, HandlerKey, Key, Outcome},
error::{ErrorKind, ErrorReport},
serve::{ErasedHandler, Serve},
};
use serde::{Deserialize, Serialize};
use super::effect::{Answer, Asked, WorldEffect};
#[derive(Component, Debug, Clone, Serialize, Deserialize, Reflect)]
#[component(immutable, on_insert = index_bound, on_discard = unindex_bound)]
#[reflect(Component)]
pub struct Bound {
#[reflect(remote = crate::bus::reflect::HandlerKeyReflect)]
pub key: HandlerKey,
#[reflect(remote = crate::bus::reflect::HandlerDescriptorReflect)]
pub descriptor: HandlerDescriptor,
}
#[derive(Resource, Debug, Default)]
pub struct HandlerIndex {
keys: HashMap<HandlerKey, (Entity, rig_core::effect::EffectFamily)>,
}
impl HandlerIndex {
pub fn entity(&self, key: &HandlerKey) -> Option<Entity> {
self.keys.get(key).map(|(entity, _)| *entity)
}
pub fn len(&self) -> usize {
self.keys.len()
}
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
}
fn index_bound(
mut world: bevy_ecs::world::DeferredWorld<'_>,
context: bevy_ecs::lifecycle::HookContext,
) {
let Some(bound) = world.get::<Bound>(context.entity) else {
return;
};
let (key, family) = (bound.key.clone(), bound.family());
if let Some(mut index) = world.get_resource_mut::<HandlerIndex>() {
index.keys.insert(key, (context.entity, family));
}
}
fn unindex_bound(
mut world: bevy_ecs::world::DeferredWorld<'_>,
context: bevy_ecs::lifecycle::HookContext,
) {
let Some(bound) = world.get::<Bound>(context.entity) else {
return;
};
let key = bound.key.clone();
if let Some(mut index) = world.get_resource_mut::<HandlerIndex>()
&& index
.keys
.get(&key)
.is_some_and(|(entity, _)| *entity == context.entity)
{
index.keys.remove(&key);
}
}
#[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Reflect)]
#[relationship(relationship_target = Serves)]
#[reflect(Component)]
pub struct ServedBy(pub Entity);
#[derive(Component, Debug, Default, Reflect)]
#[relationship_target(relationship = ServedBy)]
#[reflect(Component)]
pub struct Serves(Vec<Entity>);
impl Bound {
pub fn family(&self) -> rig_core::effect::EffectFamily {
self.descriptor.family.family()
}
}
pub enum Served {
Task(ErasedHandler),
World(WorldServe),
}
#[derive(Clone)]
pub struct WorldServe {
pub family: FamilyDescriptor,
pub ask: fn(&mut EntityCommands<'_>, &EffectKind) -> Result<(), ErrorReport>,
}
fn open(_entity: &mut EntityCommands<'_>, _kind: &EffectKind) -> Result<(), ErrorReport> {
Ok(())
}
pub struct WorldHandler<E: WorldEffect>(PhantomData<fn() -> E>);
impl<E: WorldEffect> WorldHandler<E> {
pub fn descriptor(key: HandlerKey) -> HandlerDescriptor {
HandlerDescriptor {
key,
family: FamilyDescriptor::Custom {
kind: E::KIND.to_owned(),
},
layers: Vec::new(),
}
}
pub fn served() -> Served {
Served::World(WorldServe {
family: FamilyDescriptor::Custom {
kind: E::KIND.to_owned(),
},
ask: ask::<E>,
})
}
}
fn ask<E: WorldEffect>(
entity: &mut EntityCommands<'_>,
kind: &EffectKind,
) -> Result<(), ErrorReport> {
let EffectKind::Custom {
kind: label,
payload,
} = kind
else {
return Err(ErrorReport::new(
ErrorKind::Request,
format!(
"`{}` is served by a system for `{}` effects; a {} effect cannot be asked of it",
E::KIND,
E::KIND,
kind.family()
),
));
};
if &**label != E::KIND {
return Err(ErrorReport::new(
ErrorKind::Request,
format!(
"a `{label}` effect reached the system serving `{}`",
E::KIND
),
));
}
let effect: E = serde_json::from_value(payload.clone()).map_err(|error| {
ErrorReport::new(
ErrorKind::Request,
format!("the payload of `{}` did not deserialize: {error}", E::KIND),
)
})?;
entity.insert(Asked(effect));
Ok(())
}
pub fn answered<E: WorldEffect>(
added: On<Add, Answer<E>>,
answers: Query<&Answer<E>, With<Asked<E>>>,
mut commands: Commands,
) {
let entity = added.event().entity;
let Ok(answer) = answers.get(entity) else {
commands.entity(entity).remove::<Answer<E>>();
return;
};
let outcome = serde_json::to_value(&answer.0)
.map(|payload| Outcome::Custom { payload })
.map_err(|error| {
ErrorReport::new(
ErrorKind::Response,
format!("the answer to `{}` did not serialize: {error}", E::KIND),
)
});
commands
.entity(entity)
.remove::<(Asked<E>, Answer<E>)>()
.insert(super::effect::WorldOutcome::new(outcome));
}
#[derive(Component)]
pub struct Handler;
#[derive(Default)]
pub struct HandlerTable {
served: HashMap<Entity, Served>,
}
impl HandlerTable {
pub(crate) fn contains(&self, entity: Entity) -> bool {
self.served.contains_key(&entity)
}
pub(crate) fn install(world: &mut World, entity: Entity, handler: ErasedHandler) {
world
.non_send_mut::<Self>()
.served
.insert(entity, Served::Task(handler));
world.entity_mut(entity).insert(Handler);
}
}
pub fn unbound(removed: On<Remove, Handler>, mut table: NonSendMut<HandlerTable>) {
table.served.remove(&removed.event().entity);
}
#[derive(SystemParam)]
pub struct Registry<'w> {
table: NonSend<'w, HandlerTable>,
}
impl Registry<'_> {
pub fn served(&self, entity: Entity) -> Option<&Served> {
self.table.served.get(&entity)
}
}
#[derive(SystemParam)]
pub struct Handlers<'w, 's> {
commands: Commands<'w, 's>,
index: ResMut<'w, HandlerIndex>,
world_kinds: ResMut<'w, WorldKinds>,
bound: Query<'w, 's, &'static Bound>,
table: NonSendMut<'w, HandlerTable>,
}
#[derive(Resource, Default)]
pub struct WorldKinds(HashSet<TypeId>);
impl Handlers<'_, '_> {
pub fn with<T>(
world: &mut World,
f: impl FnOnce(&mut Handlers<'_, '_>) -> T,
) -> Result<T, ErrorReport> {
let mut state = bevy_ecs::system::SystemState::<Handlers>::new(world);
let out = match state.get_mut(world) {
Ok(mut handlers) => f(&mut handlers),
Err(error) => {
return Err(ErrorReport::new(
ErrorKind::Internal,
format!("the world has no bus: {error}"),
));
}
};
state.apply(world);
Ok(out)
}
pub fn register(
&mut self,
key: impl Into<HandlerKey>,
handler: impl Serve + 'static,
) -> Result<Entity, ErrorReport> {
self.register_erased(key, ErasedHandler::new(handler))
}
pub fn register_erased(
&mut self,
key: impl Into<HandlerKey>,
handler: ErasedHandler,
) -> Result<Entity, ErrorReport> {
let key = key.into();
let described = handler.descriptor();
let descriptor = HandlerDescriptor {
key: key.clone(),
family: described.family,
layers: described.layers,
};
self.bind(key, descriptor, Served::Task(handler))
}
pub fn register_typed<F: Family>(
&mut self,
key: impl Into<HandlerKey>,
handler: impl Serve + 'static,
) -> Result<Key<F>, ErrorReport> {
let key = key.into();
let handler = ErasedHandler::new(handler);
let descriptor = handler.descriptor();
if descriptor.family.family() != F::FAMILY {
return Err(ErrorReport::new(
ErrorKind::HandlerUnavailable,
format!(
"`{key}` was registered as {} but the handler serves {}",
F::FAMILY,
descriptor.family.family()
),
));
}
self.register_erased(key.clone(), handler)?;
Ok(Key::new_unchecked(key))
}
pub fn register_world<E: WorldEffect>(
&mut self,
key: impl Into<HandlerKey>,
) -> Result<Entity, ErrorReport> {
let key = key.into();
let descriptor = WorldHandler::<E>::descriptor(key.clone());
let entity = self.bind(key, descriptor, WorldHandler::<E>::served())?;
if self.world_kinds.0.insert(TypeId::of::<E>()) {
self.commands.add_observer(answered::<E>);
}
Ok(entity)
}
pub fn register_open(
&mut self,
key: impl Into<HandlerKey>,
family: FamilyDescriptor,
) -> Result<Entity, ErrorReport> {
let key = key.into();
let descriptor = HandlerDescriptor {
key: key.clone(),
family: family.clone(),
layers: Vec::new(),
};
self.bind(
key,
descriptor,
Served::World(WorldServe { family, ask: open }),
)
}
fn bind(
&mut self,
key: HandlerKey,
descriptor: HandlerDescriptor,
served: Served,
) -> Result<Entity, ErrorReport> {
let family = descriptor.family.family();
let entity = match self.index.keys.get(&key).copied() {
Some((entity, bound)) if bound == family => {
self.commands.entity(entity).insert((
Bound {
key: key.clone(),
descriptor,
},
Name::new(key.to_string()),
));
entity
}
Some((_, bound)) => {
return Err(ErrorReport::new(
ErrorKind::HandlerUnavailable,
format!(
"`{key}` is bound to a {bound} handler; a {family} handler cannot take its place while it is bound — deregister it first"
),
));
}
None => self
.commands
.spawn((
Bound {
key: key.clone(),
descriptor,
},
Name::new(key.to_string()),
))
.id(),
};
self.index.keys.insert(key, (entity, family));
self.serve(entity, served);
Ok(entity)
}
fn serve(&mut self, entity: Entity, served: Served) {
self.table.served.insert(entity, served);
self.commands.entity(entity).insert(Handler);
}
pub fn deregister(&mut self, key: &HandlerKey) -> bool {
match self.index.keys.remove(key) {
Some((entity, _)) => {
self.commands.entity(entity).despawn();
true
}
None => false,
}
}
pub fn entity(&self, key: &HandlerKey) -> Option<Entity> {
self.index.entity(key)
}
pub fn descriptor(&self, key: &HandlerKey) -> Option<HandlerDescriptor> {
let entity = self.index.entity(key)?;
self.bound
.get(entity)
.ok()
.map(|bound| bound.descriptor.clone())
}
pub fn keys(&self) -> Vec<HandlerKey> {
let mut keys: Vec<HandlerKey> = self.bound.iter().map(|bound| bound.key.clone()).collect();
keys.sort();
keys
}
pub fn descriptors(&self) -> Vec<HandlerDescriptor> {
let mut described: Vec<HandlerDescriptor> = self
.bound
.iter()
.map(|bound| bound.descriptor.clone())
.collect();
described.sort_by(|a, b| a.key.cmp(&b.key));
described
}
}