use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bytes::Bytes;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub type OnEvent = fn(Bytes, &mut Commands<'_, '_>);
#[derive(Component)]
pub struct NetWorld {
pub id: u64,
}
#[derive(Resource)]
pub struct Context {
pub ch: crate::base::Chan,
events: HashMap<String, OnEvent>,
entitys: HashMap<Entity, u64>,
}
impl Default for Context {
fn default() -> Self {
Self {
ch: crate::base::RUNTIME.chan(),
events: HashMap::new(),
entitys: HashMap::new(),
}
}
}
impl Drop for Context {
fn drop(&mut self) {
self.ch.close();
}
}
#[derive(Default)]
pub struct SiloPlugin;
impl Plugin for SiloPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<Context>();
app.add_systems(PreUpdate, update_system);
app.configure_sets(
PostUpdate,
(
NetSystemSet::Spawn,
NetSystemSet::Change,
NetSystemSet::Despawn,
)
.chain(),
);
}
}
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
enum NetSystemSet {
Spawn,
Change,
Despawn,
}
pub trait SiloApp {
fn add_net_event<T: Event + for<'a> Deserialize<'a>>(&mut self)
where
for<'a> <T as Event>::Trigger<'a>: Default;
fn add_net_object<T: Component>(&mut self);
fn add_net_variant<T: Component + Serialize>(&mut self);
}
impl SiloApp for App {
fn add_net_event<T: Event + for<'a> Deserialize<'a>>(&mut self)
where
for<'a> <T as Event>::Trigger<'a>: Default,
{
if let Some(mut ctx) = self.world_mut().get_resource_mut::<Context>() {
ctx.events
.insert(std::any::type_name::<T>().to_string(), on_event::<T>);
}
}
fn add_net_object<T: Component>(&mut self) {
self.add_systems(
PostUpdate,
(
reg_object_add::<T>.in_set(NetSystemSet::Spawn),
reg_object_removed::<T>.in_set(NetSystemSet::Despawn),
),
);
}
fn add_net_variant<T: Component + Serialize>(&mut self) {
self.add_systems(
PostUpdate,
reg_variant_change::<T>.in_set(NetSystemSet::Change),
);
}
}
fn on_event<T: Event + for<'a> Deserialize<'a>>(v: Bytes, commands: &mut Commands<'_, '_>)
where
for<'a> <T as Event>::Trigger<'a>: Default,
{
let mut de = serde_cbor::de::Deserializer::from_slice(&v);
if let Ok(event) = T::deserialize(&mut de) {
commands.trigger(event);
}
}
fn update_system(mut commands: Commands, mut ctx: ResMut<Context>) {
while let Ok(v) = ctx.ch.rx.try_recv() {
match v {
crate::base::Message::Event(e) => match e {
crate::base::Event::Invoke { t, v, world: _ } => {
if let Some(f) = ctx.events.get(&t) {
f(v, &mut commands)
}
}
_ => {}
},
_ => {}
}
}
}
fn reg_object_add<E: Component>(
query: Query<(Entity, &NetWorld), Added<E>>,
mut ctx: ResMut<Context>,
) {
for (entity, w) in query.iter() {
let id: u64 = entity.to_bits();
ctx.entitys.insert(entity, w.id);
let _ = ctx
.ch
.tx
.send(crate::base::Message::Event(crate::base::Event::Spawn {
id,
t: std::any::type_name::<E>().to_string(),
world: w.id,
}));
}
}
fn reg_object_removed<E: Component>(mut removed: RemovedComponents<E>, mut ctx: ResMut<Context>) {
for entity in removed.read() {
if let Some(w) = ctx.entitys.remove(&entity) {
let id: u64 = entity.to_bits();
let _ = ctx
.ch
.tx
.send(crate::base::Message::Event(crate::base::Event::Despawn {
id,
world: w,
}));
}
}
}
fn reg_variant_change<V: Component + Serialize>(
changed: Query<(Entity, &V, &NetWorld), Changed<V>>,
ctx: Res<Context>,
) {
for (entity, v, w) in changed.iter() {
let id: u64 = entity.to_bits();
if let Ok(v) = serde_cbor::to_vec(v) {
let _ = ctx
.ch
.tx
.send(crate::base::Message::Event(crate::base::Event::Change {
id,
t: std::any::type_name::<V>().to_string(),
v: Bytes::from(v),
world: w.id,
}));
}
}
}