use std::marker::PhantomData;
use bevy::{ecs::change_detection::Tick, prelude::*};
use iroh::EndpointId;
use serde::{Serialize, de::DeserializeOwned};
use super::{NetId, NetIds, Remote, Replica};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Rate {
EveryChange,
Hz(f32),
}
impl Rate {
pub fn hz(hz: f32) -> Rate {
Rate::Hz(hz)
}
pub(crate) fn interval(self) -> f64 {
match self {
Rate::EveryChange => 0.0,
Rate::Hz(hz) => 1.0 / hz.max(0.001) as f64,
}
}
}
#[derive(Debug, Clone, thiserror::Error)]
#[error("{0}")]
pub struct Rejected(pub String);
impl From<String> for Rejected {
fn from(s: String) -> Self {
Rejected(s)
}
}
impl From<&str> for Rejected {
fn from(s: &str) -> Self {
Rejected(s.to_string())
}
}
pub struct EncodeCx<'w> {
pub world: &'w World,
pub entity: Entity,
}
impl EncodeCx<'_> {
pub fn net_id(&self, entity: Entity) -> Option<NetId> {
self.world.get::<NetId>(entity).copied()
}
}
pub struct DecodeCx<'w> {
pub world: &'w mut World,
pub entity: Entity,
pub from: EndpointId,
}
impl DecodeCx<'_> {
pub fn entity(&mut self, id: NetId) -> Entity {
if let Some(e) = self.world.resource::<NetIds>().get(id)
&& self.world.get_entity(e).is_ok()
{
return e;
}
let e = self.world.spawn((id, Remote, Replica::placeholder())).id();
self.world.resource_mut::<NetIds>().insert(id, e);
e
}
}
pub trait Codec: Send + Sync + 'static {
type Source: Component;
type Wire: Serialize + DeserializeOwned;
type Target: Bundle;
fn name(&self) -> &'static str {
std::any::type_name::<Self::Source>()
}
fn rate(&self) -> Rate {
Rate::EveryChange
}
fn encode(&self, src: &Self::Source, cx: &EncodeCx) -> Option<Self::Wire>;
fn verify(&self, _wire: &Self::Wire) -> Result<(), Rejected> {
Ok(())
}
fn decode(&self, wire: Self::Wire, cx: &mut DecodeCx) -> Result<Self::Target, Rejected>;
}
pub struct Serde<T> {
rate: Rate,
_marker: PhantomData<fn() -> T>,
}
impl<T> Serde<T> {
pub fn new() -> Self {
Self {
rate: Rate::EveryChange,
_marker: PhantomData,
}
}
pub fn with_rate(mut self, rate: Rate) -> Self {
self.rate = rate;
self
}
}
impl<T> Default for Serde<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Codec for Serde<T>
where
T: Component + Clone + Serialize + DeserializeOwned,
{
type Source = T;
type Wire = T;
type Target = T;
fn rate(&self) -> Rate {
self.rate
}
fn encode(&self, src: &T, _cx: &EncodeCx) -> Option<T> {
Some(src.clone())
}
fn decode(&self, wire: T, _cx: &mut DecodeCx) -> Result<T, Rejected> {
Ok(wire)
}
}
pub(crate) trait ErasedCodec: Send + Sync {
fn collect(&mut self, world: &mut World, out: &mut Vec<(Entity, Vec<u8>)>);
fn encode(&self, world: &World, entity: Entity) -> Option<Vec<u8>>;
fn apply(
&self,
world: &mut World,
entity: Entity,
from: EndpointId,
bytes: &[u8],
) -> Result<(), Rejected>;
fn remove(&self, world: &mut World, entity: Entity);
}
pub(crate) struct Erased<C: Codec> {
codec: C,
name: &'static str,
query: Option<
QueryState<
(Entity, Ref<'static, C::Source>, &'static Replica),
(With<super::Shared>, Without<Remote>),
>,
>,
last_collect: Tick,
}
impl<C: Codec> Erased<C> {
pub fn new(codec: C) -> Self {
Self {
name: codec.name(),
codec,
query: None,
last_collect: Tick::new(0),
}
}
}
impl<C: Codec> ErasedCodec for Erased<C> {
fn collect(&mut self, world: &mut World, out: &mut Vec<(Entity, Vec<u8>)>) {
if self.query.is_none() {
self.query = Some(world.query_filtered());
self.last_collect = world.change_tick();
}
let query = self.query.as_mut().expect("just initialised");
let last = self.last_collect;
let now = world.change_tick();
world.last_change_tick_scope(last, |world| {
for (entity, source, replica) in query.iter(world) {
if !source.is_changed() || replica.announced.is_empty() {
continue;
}
if replica.applied_tick == Some(source.last_changed()) {
continue;
}
let cx = EncodeCx { world, entity };
if let Some(wire) = self.codec.encode(&source, &cx)
&& let Ok(bytes) = postcard::to_stdvec(&wire)
{
out.push((entity, bytes));
}
}
});
self.last_collect = now;
}
fn encode(&self, world: &World, entity: Entity) -> Option<Vec<u8>> {
let source = world.get::<C::Source>(entity)?;
let cx = EncodeCx { world, entity };
let wire = self.codec.encode(source, &cx)?;
postcard::to_stdvec(&wire).ok()
}
fn apply(
&self,
world: &mut World,
entity: Entity,
from: EndpointId,
bytes: &[u8],
) -> Result<(), Rejected> {
let wire: C::Wire =
postcard::from_bytes(bytes).map_err(|e| Rejected(format!("{}: {e}", self.name)))?;
self.codec.verify(&wire)?;
let mut cx = DecodeCx {
world,
entity,
from,
};
let target = self.codec.decode(wire, &mut cx)?;
if let Ok(mut e) = world.get_entity_mut(entity) {
e.insert(target);
}
Ok(())
}
fn remove(&self, world: &mut World, entity: Entity) {
if let Ok(mut e) = world.get_entity_mut(entity) {
e.remove::<C::Target>();
e.remove::<C::Source>();
}
}
}