use bevy::ecs::system::SystemChangeTick;
use std::ops::Deref;
use crate::connection::events::ConnectionEvents;
use bevy::prelude::{
Added, App, Commands, Component, DetectChanges, Entity, EventReader, IntoSystemConfigs, Mut,
PostUpdate, Query, Ref, RemovedComponents, ResMut,
};
use bevy::utils::HashSet;
use tracing::{debug, info};
use crate::netcode::ClientId;
use crate::prelude::NetworkTarget;
use crate::protocol::component::IntoKind;
use crate::protocol::Protocol;
use crate::server::events::ConnectEvent;
use crate::server::room::ClientVisibility;
use crate::shared::replication::components::{DespawnTracker, Replicate, ReplicationMode};
use crate::shared::replication::resources::ReplicationData;
use crate::shared::replication::ReplicationSend;
use crate::shared::sets::ReplicationSet;
fn add_despawn_tracker(
mut replication: ResMut<ReplicationData>,
mut commands: Commands,
query: Query<(Entity, &Replicate), Added<Replicate>>,
) {
for (entity, replicate) in query.iter() {
commands.entity(entity).insert(DespawnTracker);
replication.owned_entities.insert(entity, replicate.clone());
}
}
fn send_entity_despawn<P: Protocol, R: ReplicationSend<P>>(
mut replication: ResMut<ReplicationData>,
query: Query<(Entity, &Replicate)>,
system_bevy_ticks: SystemChangeTick,
mut despawn_removed: RemovedComponents<DespawnTracker>,
mut sender: ResMut<R>,
) {
query.iter().for_each(|(entity, replicate)| {
if matches!(replicate.replication_mode, ReplicationMode::Room) {
replicate
.replication_clients_cache
.iter()
.for_each(|(client_id, visibility)| {
if replicate.replication_target.should_send_to(client_id)
&& matches!(visibility, ClientVisibility::Lost)
{
debug!("sending entity despawn for entity: {:?}", entity);
sender
.prepare_entity_despawn(
entity,
replicate,
NetworkTarget::Only(vec![*client_id]),
system_bevy_ticks.this_run(),
)
.unwrap();
}
});
}
});
for entity in despawn_removed.read() {
if let Some(replicate) = replication.owned_entities.remove(&entity) {
sender
.prepare_entity_despawn(
entity,
&replicate,
replicate.replication_target.clone(),
system_bevy_ticks.this_run(),
)
.unwrap();
}
}
}
fn send_entity_spawn<P: Protocol, R: ReplicationSend<P>>(
mut replication: ResMut<ReplicationData>,
system_bevy_ticks: SystemChangeTick,
query: Query<(Entity, Ref<Replicate>)>,
mut sender: ResMut<R>,
) {
query.iter().for_each(|(entity, replicate)| {
match replicate.replication_mode {
ReplicationMode::Room => {
replicate
.replication_clients_cache
.iter()
.for_each(|(client_id, visibility)| {
if replicate.replication_target.should_send_to(client_id) {
match visibility {
ClientVisibility::Gained => {
debug!("send entity spawn to gained");
sender
.prepare_entity_spawn(
entity,
&replicate,
NetworkTarget::Only(vec![*client_id]),
system_bevy_ticks.this_run(),
)
.unwrap();
}
ClientVisibility::Lost => {}
ClientVisibility::Maintained => {
if replicate.is_added() {
debug!("send entity spawn to maintained");
replication
.owned_entities
.insert(entity, replicate.clone());
sender
.prepare_entity_spawn(
entity,
replicate.deref(),
NetworkTarget::Only(vec![*client_id]),
system_bevy_ticks.this_run(),
)
.unwrap();
}
}
}
}
});
}
ReplicationMode::NetworkTarget => {
let new_connected_clients = sender.new_connected_clients().clone();
sender
.prepare_entity_spawn(
entity,
&replicate,
NetworkTarget::Only(new_connected_clients.clone()),
system_bevy_ticks.this_run(),
)
.unwrap();
if replicate.is_added() {
debug!("send entity spawn to maintained");
replication.owned_entities.insert(entity, replicate.clone());
let mut target = replicate.replication_target.clone();
target.exclude(new_connected_clients.clone());
sender
.prepare_entity_spawn(
entity,
replicate.deref(),
target,
system_bevy_ticks.this_run(),
)
.unwrap();
}
}
}
})
}
fn send_component_update<C: Component + Clone, P: Protocol, R: ReplicationSend<P>>(
query: Query<(Entity, Ref<C>, &Replicate)>,
system_bevy_ticks: SystemChangeTick,
mut sender: ResMut<R>,
) where
<P as Protocol>::Components: From<C>,
{
query.iter().for_each(|(entity, component, replicate)| {
match replicate.replication_mode {
ReplicationMode::Room => {
replicate
.replication_clients_cache
.iter()
.for_each(|(client_id, visibility)| {
if replicate.replication_target.should_send_to(client_id) {
match visibility {
ClientVisibility::Gained => {
sender
.prepare_component_insert(
entity,
component.clone().into(),
replicate,
NetworkTarget::Only(vec![*client_id]),
system_bevy_ticks.this_run(),
)
.unwrap();
}
ClientVisibility::Lost => {}
ClientVisibility::Maintained => {
if component.is_added() {
sender
.prepare_component_insert(
entity,
component.clone().into(),
replicate,
NetworkTarget::Only(vec![*client_id]),
system_bevy_ticks.this_run(),
)
.unwrap();
} else {
sender
.prepare_entity_update(
entity,
component.clone().into(),
replicate,
NetworkTarget::Only(vec![*client_id]),
component.last_changed(),
system_bevy_ticks.this_run(),
)
.unwrap();
}
}
}
}
})
}
ReplicationMode::NetworkTarget => {
let new_connected_clients = sender.new_connected_clients().clone();
sender
.prepare_component_insert(
entity,
component.clone().into(),
replicate,
NetworkTarget::Only(new_connected_clients.clone()),
system_bevy_ticks.this_run(),
)
.unwrap();
let mut target = replicate.replication_target.clone();
target.exclude(new_connected_clients.clone());
if component.is_added() {
sender
.prepare_component_insert(
entity,
component.clone().into(),
replicate,
target,
system_bevy_ticks.this_run(),
)
.unwrap();
} else {
sender
.prepare_entity_update(
entity,
component.clone().into(),
replicate,
target,
component.last_changed(),
system_bevy_ticks.this_run(),
)
.unwrap();
}
}
}
});
}
fn send_component_removed<C: Component + Clone, P: Protocol, R: ReplicationSend<P>>(
query: Query<&Replicate>,
system_bevy_ticks: SystemChangeTick,
mut removed: RemovedComponents<C>,
mut sender: ResMut<R>,
) where
C: IntoKind<<P as Protocol>::ComponentKinds>,
{
removed.read().for_each(|entity| {
if let Ok(replicate) = query.get(entity) {
match replicate.replication_mode {
ReplicationMode::Room => {
replicate.replication_clients_cache.iter().for_each(
|(client_id, visibility)| {
if replicate.replication_target.should_send_to(client_id) {
if matches!(visibility, ClientVisibility::Maintained) {
sender
.prepare_component_remove(
entity,
C::into_kind(),
replicate,
NetworkTarget::Only(vec![*client_id]),
system_bevy_ticks.this_run(),
)
.unwrap();
}
}
},
)
}
ReplicationMode::NetworkTarget => {
sender
.prepare_component_remove(
entity,
C::into_kind(),
replicate,
replicate.replication_target.clone(),
system_bevy_ticks.this_run(),
)
.unwrap();
}
}
}
})
}
pub fn add_replication_send_systems<P: Protocol, R: ReplicationSend<P>>(app: &mut App) {
app.add_systems(
PostUpdate,
(
send_entity_spawn::<P, R>.in_set(ReplicationSet::SendEntityUpdates),
(add_despawn_tracker, send_entity_despawn::<P, R>)
.in_set(ReplicationSet::ReplicationSystems),
),
);
}
pub fn add_per_component_replication_send_systems<
C: Component + Clone,
P: Protocol,
R: ReplicationSend<P>,
>(
app: &mut App,
) where
<P as Protocol>::Components: From<C>,
C: IntoKind<<P as Protocol>::ComponentKinds>,
{
app.add_systems(
PostUpdate,
(
send_component_removed::<C, P, R>.in_set(ReplicationSet::ReplicationSystems),
send_component_update::<C, P, R>.in_set(ReplicationSet::SendComponentUpdates),
),
);
}