use bevy_app::{App, Plugin};
use bevy_ecs::error::{BevyError, Result};
use bevy_ecs::prelude::*;
use core::time::Duration;
use lightyear_connection::client::Connected;
use lightyear_connection::client_of::ClientOf;
use lightyear_connection::direction::NetworkDirection;
use lightyear_connection::host::HostClient;
use lightyear_messages::prelude::{AppTriggerExt, EventSender, RemoteEvent};
use lightyear_messages::registry::MessageRegistry;
use lightyear_replication::message::MetadataChannel;
use lightyear_replication::registry::registry::ComponentRegistry;
use lightyear_transport::prelude::{
AppChannelExt, ChannelMode, ChannelRegistry, ChannelSettings, ReliableSettings,
};
use serde::{Deserialize, Serialize};
use tracing::trace;
#[derive(Serialize, Deserialize, Debug, PartialEq, Default, Clone, Copy, Event)]
pub struct ProtocolCheck {
messages: Option<u64>,
components: Option<u64>,
channels: Option<u64>,
}
pub struct ProtocolCheckPlugin;
#[derive(thiserror::Error, Debug)]
pub enum ProtocolCheckError {
#[error("the message protocol doesn't match")]
Message,
#[error("the component protocol doesn't match")]
Component,
#[error("the channel protocol doesn't match")]
Channel,
}
impl ProtocolCheckPlugin {
fn send_verify_protocol(
trigger: On<Add, Connected>,
mut sender: Query<&mut EventSender<ProtocolCheck>, (With<ClientOf>, Without<HostClient>)>,
messages: Option<ResMut<MessageRegistry>>,
components: Option<ResMut<ComponentRegistry>>,
channels: Option<ResMut<ChannelRegistry>>,
) {
let check_message = ProtocolCheck {
messages: messages.map(|mut m| m.finish()),
components: components.map(|mut c| c.finish()),
channels: channels.map(|mut c| c.finish()),
};
if let Ok(mut s) = sender.get_mut(trigger.entity) {
s.trigger::<MetadataChannel>(check_message);
}
}
fn receive_verify_protocol(
trigger: On<RemoteEvent<ProtocolCheck>>,
messages: Option<ResMut<MessageRegistry>>,
components: Option<ResMut<ComponentRegistry>>,
channels: Option<ResMut<ChannelRegistry>>,
) -> Result {
let message = trigger.trigger;
trace!("Received protocol check from server: {message:?}");
if message.messages != messages.map(|mut m| m.finish()) {
return Err(BevyError::from(ProtocolCheckError::Message));
}
if message.components != components.map(|mut c| c.finish()) {
return Err(BevyError::from(ProtocolCheckError::Component));
}
if message.channels != channels.map(|mut c| c.finish()) {
return Err(BevyError::from(ProtocolCheckError::Channel));
}
Ok(())
}
}
impl Plugin for ProtocolCheckPlugin {
fn build(&self, app: &mut App) {
app.add_channel::<MetadataChannel>(ChannelSettings {
mode: ChannelMode::UnorderedReliable(ReliableSettings::default()),
send_frequency: Duration::default(),
priority: 10.0,
})
.add_direction(NetworkDirection::Bidirectional);
app.register_event::<ProtocolCheck>()
.add_direction(NetworkDirection::ServerToClient);
}
fn finish(&self, app: &mut App) {
app.add_observer(Self::send_verify_protocol);
app.add_observer(Self::receive_verify_protocol);
}
}