use core::cmp::Reverse;
use bevy::{ecs::component::ComponentId, prelude::*};
use log::debug;
use super::registry::{
ReplicationRegistry,
receive_fns::{MutWrite, RemoveFn, WriteFn},
};
pub trait AppMarkerExt {
fn register_marker<M: Component>(&mut self) -> &mut Self;
fn register_marker_with<M: Component>(&mut self, config: MarkerConfig) -> &mut Self;
fn set_marker_fns<M: Component, C: Component<Mutability: MutWrite<C>>>(
&mut self,
write: WriteFn<C>,
remove: RemoveFn,
) -> &mut Self;
fn set_receive_fns<C: Component<Mutability: MutWrite<C>>>(
&mut self,
write: WriteFn<C>,
remove: RemoveFn,
) -> &mut Self;
}
impl AppMarkerExt for App {
fn register_marker<M: Component>(&mut self) -> &mut Self {
self.register_marker_with::<M>(MarkerConfig::default())
}
fn register_marker_with<M: Component>(&mut self, config: MarkerConfig) -> &mut Self {
debug!("registering marker `{}`", ShortName::of::<M>());
let component_id = self.world_mut().register_component::<M>();
let mut receive_markers = self.world_mut().resource_mut::<ReceiveMarkers>();
let marker_id = receive_markers.insert(ReceiveMarker {
component_id,
config,
});
let mut replicaton_fns = self.world_mut().resource_mut::<ReplicationRegistry>();
replicaton_fns.register_marker(marker_id);
self
}
fn set_marker_fns<M: Component, C: Component<Mutability: MutWrite<C>>>(
&mut self,
write: WriteFn<C>,
remove: RemoveFn,
) -> &mut Self {
debug!(
"adding fns for marker `{}` for component `{}`",
ShortName::of::<M>(),
ShortName::of::<C>()
);
let component_id = self.world_mut().register_component::<M>();
let receive_markers = self.world().resource::<ReceiveMarkers>();
let marker_id = receive_markers.marker_id(component_id);
self.world_mut()
.resource_scope(|world, mut registry: Mut<ReplicationRegistry>| {
registry.set_marker_fns::<C>(world, marker_id, write, remove);
});
self
}
fn set_receive_fns<C: Component<Mutability: MutWrite<C>>>(
&mut self,
write: WriteFn<C>,
remove: RemoveFn,
) -> &mut Self {
debug!(
"setting receive fns for component `{}`",
ShortName::of::<C>()
);
self.world_mut()
.resource_scope(|world, mut registry: Mut<ReplicationRegistry>| {
registry.set_receive_fns::<C>(world, write, remove);
});
self
}
}
#[derive(Resource, Default)]
pub(crate) struct ReceiveMarkers(Vec<ReceiveMarker>);
impl ReceiveMarkers {
fn insert(&mut self, marker: ReceiveMarker) -> ReceiveMarkerIndex {
let key = Reverse(marker.config.priority);
let index = self
.0
.binary_search_by_key(&key, |marker| Reverse(marker.config.priority))
.unwrap_or_else(|index| index);
self.0.insert(index, marker);
ReceiveMarkerIndex(index)
}
fn marker_id(&self, component_id: ComponentId) -> ReceiveMarkerIndex {
let index = self
.0
.iter()
.position(|marker| marker.component_id == component_id)
.unwrap_or_else(|| panic!("marker {component_id:?} wasn't registered"));
ReceiveMarkerIndex(index)
}
pub(super) fn iter_require_history(&self) -> impl Iterator<Item = bool> + '_ {
self.0.iter().map(|marker| marker.config.need_history)
}
}
struct ReceiveMarker {
component_id: ComponentId,
config: MarkerConfig,
}
#[derive(Default)]
pub struct MarkerConfig {
pub priority: usize,
pub need_history: bool,
}
pub(crate) struct EntityMarkers {
markers: Vec<bool>,
need_history: bool,
}
impl EntityMarkers {
pub(crate) fn read<'a>(
&'a mut self,
markers: &ReceiveMarkers,
entity: impl Into<EntityRef<'a>>,
) {
self.markers.clear();
self.need_history = false;
let entity = entity.into();
for marker in &markers.0 {
let contains = entity.contains_id(marker.component_id);
self.markers.push(contains);
if contains && marker.config.need_history {
self.need_history = true;
}
}
}
pub(super) fn markers(&self) -> &[bool] {
&self.markers
}
pub(crate) fn need_history(&self) -> bool {
self.need_history
}
}
impl FromWorld for EntityMarkers {
fn from_world(world: &mut World) -> Self {
let markers = world.resource::<ReceiveMarkers>();
Self {
markers: Vec::with_capacity(markers.0.len()),
need_history: false,
}
}
}
#[derive(Clone, Copy, Deref, Debug)]
pub(super) struct ReceiveMarkerIndex(usize);
#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};
use super::*;
use crate::shared::replication::registry::receive_fns;
#[test]
#[should_panic]
fn non_registered_marker() {
let mut app = App::new();
app.init_resource::<ReceiveMarkers>()
.init_resource::<ReplicationRegistry>()
.set_marker_fns::<Marker, TestComponent>(
receive_fns::default_write,
receive_fns::default_remove::<TestComponent>,
);
}
#[test]
fn sorting() {
let mut app = App::new();
app.init_resource::<ReceiveMarkers>()
.init_resource::<ReplicationRegistry>()
.register_marker::<MarkerA>()
.register_marker_with::<MarkerB>(MarkerConfig {
priority: 2,
..Default::default()
})
.register_marker_with::<MarkerC>(MarkerConfig {
priority: 1,
..Default::default()
})
.register_marker::<MarkerD>();
let markers = app.world().resource::<ReceiveMarkers>();
let priorities: Vec<_> = markers
.0
.iter()
.map(|marker| marker.config.priority)
.collect();
assert_eq!(priorities, [2, 1, 0, 0]);
}
#[derive(Component)]
struct Marker;
#[derive(Component)]
struct MarkerA;
#[derive(Component)]
struct MarkerB;
#[derive(Component)]
struct MarkerC;
#[derive(Component)]
struct MarkerD;
#[derive(Component, Serialize, Deserialize)]
struct TestComponent;
}