pub mod codec;
pub mod transform;
pub mod wire;
mod apply;
mod send;
use std::collections::HashSet;
use bevy::{ecs::change_detection::Tick, platform::collections::HashMap, prelude::*};
use iroh::EndpointId;
use iroh_gossip::proto::TopicId;
use serde::{Deserialize, Serialize, de::DeserializeOwned};
pub use codec::{Codec, DecodeCx, EncodeCx, Rate, Rejected, Serde};
pub use transform::{Glide, TransformCodec};
use crate::{IrohSet, net::Kind, node::Receive};
use codec::{Erased, ErasedCodec};
#[derive(Component, Debug, Clone, Default)]
#[require(NetId)]
pub struct Shared {
pub authority: Authority,
}
impl Shared {
pub fn anyone() -> Self {
Shared {
authority: Authority::Anyone,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum Authority {
#[default]
Owner,
Anyone,
}
#[derive(
Component, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
)]
pub struct NetId(pub u64);
impl NetId {
pub fn random() -> Self {
NetId(rand::random())
}
pub fn keyed(key: &str) -> Self {
NetId(crate::net::proto::fnv1a(key.as_bytes()))
}
}
impl Default for NetId {
fn default() -> Self {
Self::random()
}
}
impl std::fmt::Display for NetId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:016x}", self.0)
}
}
#[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Deref)]
pub struct Owner(pub EndpointId);
#[derive(Component, Debug, Clone, Copy, Default)]
pub struct Remote;
#[derive(Component, Debug, Clone, Copy, PartialEq, Eq)]
#[relationship(relationship_target = SharedEntities)]
pub struct InRoom(pub Entity);
#[derive(Component, Debug, Default)]
#[relationship_target(relationship = InRoom)]
pub struct SharedEntities(Vec<Entity>);
impl SharedEntities {
pub fn iter(&self) -> impl Iterator<Item = Entity> + '_ {
self.0.iter().copied()
}
}
#[derive(Component, Debug)]
pub struct Replica {
pub lamport: u64,
pub(crate) announced: HashSet<TopicId>,
pub(crate) applied_tick: Option<Tick>,
pub(crate) from_topic: Option<TopicId>,
pub(crate) placeholder: bool,
}
impl Replica {
fn local() -> Self {
Replica {
lamport: 0,
announced: HashSet::new(),
applied_tick: None,
from_topic: None,
placeholder: false,
}
}
fn remote(lamport: u64, topic: TopicId) -> Self {
Replica {
lamport,
announced: HashSet::new(),
applied_tick: None,
from_topic: Some(topic),
placeholder: false,
}
}
pub(crate) fn placeholder() -> Self {
Replica {
lamport: 0,
announced: HashSet::new(),
applied_tick: None,
from_topic: None,
placeholder: true,
}
}
}
#[derive(Resource, Default)]
pub struct NetIds {
map: HashMap<NetId, Entity>,
}
impl NetIds {
pub fn get(&self, id: NetId) -> Option<Entity> {
self.map.get(&id).copied()
}
pub(crate) fn insert(&mut self, id: NetId, entity: Entity) {
self.map.insert(id, entity);
}
pub(crate) fn remove(&mut self, id: NetId) {
self.map.remove(&id);
}
}
#[derive(Resource, Default)]
pub(crate) struct Registry {
entries: Vec<Entry>,
by_key: HashMap<u64, usize>,
by_source: HashMap<std::any::TypeId, u64>,
}
pub(crate) struct Entry {
pub key: u64,
pub name: &'static str,
pub rate: Rate,
pub last_sent: f64,
pub codec: Box<dyn ErasedCodec>,
}
impl Registry {
fn add<C: Codec>(&mut self, codec: C) {
let name = codec.name();
let key = Kind::named(name).0;
self.by_source
.insert(std::any::TypeId::of::<C::Source>(), key);
if let Some(&i) = self.by_key.get(&key) {
warn!(
"bevy_iroh: {} is registered twice; keeping the second",
self.entries[i].name
);
self.entries.remove(i);
self.by_key.clear();
for (i, e) in self.entries.iter().enumerate() {
self.by_key.insert(e.key, i);
}
}
self.by_key.insert(key, self.entries.len());
self.entries.push(Entry {
key,
name,
rate: codec.rate(),
last_sent: f64::NEG_INFINITY,
codec: Box::new(Erased::new(codec)),
});
}
pub fn key_of<T: 'static>(&self) -> Option<u64> {
self.by_source.get(&std::any::TypeId::of::<T>()).copied()
}
pub fn by_key(&self, key: u64) -> Option<&Entry> {
self.by_key.get(&key).map(|&i| &self.entries[i])
}
pub fn entries(&self) -> &[Entry] {
&self.entries
}
pub fn entries_mut(&mut self) -> &mut [Entry] {
&mut self.entries
}
}
#[derive(Resource, Default)]
pub(crate) struct Outbox {
pub despawned: Vec<(NetId, u64, Vec<TopicId>)>,
pub removed: Vec<(NetId, u64, u64, Vec<TopicId>)>,
}
#[derive(Resource, Default)]
pub(crate) struct Snapshots {
pub pending: Vec<(TopicId, EndpointId, Option<Vec<NetId>>)>,
pub asked: HashMap<NetId, f64>,
}
pub trait ReplicateAppExt {
fn replicate<T>(&mut self) -> &mut Self
where
T: Component + Clone + Serialize + DeserializeOwned;
fn replicate_with<C: Codec>(&mut self, codec: C) -> &mut Self;
}
impl ReplicateAppExt for App {
fn replicate<T>(&mut self) -> &mut Self
where
T: Component + Clone + Serialize + DeserializeOwned,
{
self.replicate_with(Serde::<T>::new())
}
fn replicate_with<C: Codec>(&mut self, codec: C) -> &mut Self {
self.init_resource::<Registry>();
self.world_mut().resource_mut::<Registry>().add(codec);
self.add_observer(send::on_remove_source::<C>);
self
}
}
pub(crate) struct ReplicatePlugin {
pub transform: bool,
}
impl Plugin for ReplicatePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<Registry>()
.init_resource::<NetIds>()
.init_resource::<Outbox>()
.init_resource::<Snapshots>()
.add_systems(PreUpdate, apply::apply.in_set(Receive::Replicate))
.add_systems(Update, transform::glide)
.add_systems(
PostUpdate,
(send::queue_snapshots, send::send)
.chain()
.in_set(IrohSet::Send),
)
.add_observer(send::on_remove_shared)
.add_observer(apply::on_room_left);
if self.transform {
app.replicate_with(TransformCodec::default());
}
}
}