use std::marker::PhantomData;
use bevy::prelude::*;
use crate::{ClientEvent, ClientTransport, Message, RecvError, SessionError};
#[derive(Debug, derivative::Derivative)]
#[derivative(Default)]
pub struct ClientTransportPlugin<C2S, S2C, T> {
_phantom_c2s: PhantomData<C2S>,
_phantom_s2c: PhantomData<S2C>,
_phantom_t: PhantomData<T>,
}
impl<C2S, S2C, T> Plugin for ClientTransportPlugin<C2S, S2C, T>
where
C2S: Message + Clone,
S2C: Message,
T: ClientTransport<C2S, S2C> + Resource,
{
fn build(&self, app: &mut App) {
app.add_event::<LocalClientConnecting>()
.add_event::<LocalClientConnected>()
.add_event::<FromServer<S2C>>()
.add_event::<LocalClientDisconnected>()
.add_event::<ToServer<C2S>>()
.configure_set(
PreUpdate,
ClientTransportSet::Recv.run_if(resource_exists::<T>()),
)
.configure_set(
PostUpdate,
ClientTransportSet::Send.run_if(resource_exists::<T>()),
)
.add_systems(
PreUpdate,
recv::<C2S, S2C, T>.in_set(ClientTransportSet::Recv),
)
.add_systems(
PostUpdate,
send::<C2S, S2C, T>.in_set(ClientTransportSet::Send),
);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SystemSet)]
pub enum ClientTransportSet {
Recv,
Send,
}
#[derive(Debug, Clone, Event)]
pub struct LocalClientConnecting;
#[derive(Debug, Clone, Event)]
pub struct LocalClientConnected;
#[derive(Debug, Clone, Event)]
pub struct FromServer<S2C> {
pub msg: S2C,
}
#[derive(Debug, Event)]
pub struct LocalClientDisconnected {
pub reason: SessionError,
}
#[derive(Debug, Event)]
pub struct ToServer<C2S> {
pub msg: C2S,
}
pub fn client_connected<C2S, S2C, T>(client: Option<Res<T>>) -> bool
where
C2S: Message,
S2C: Message,
T: ClientTransport<C2S, S2C> + Resource,
{
if let Some(client) = client {
client.connected()
} else {
false
}
}
fn recv<C2S, S2C, T>(
mut commands: Commands,
mut client: ResMut<T>,
mut connecting: EventWriter<LocalClientConnecting>,
mut connected: EventWriter<LocalClientConnected>,
mut from_server: EventWriter<FromServer<S2C>>,
mut disconnected: EventWriter<LocalClientDisconnected>,
) where
C2S: Message,
S2C: Message,
T: ClientTransport<C2S, S2C> + Resource,
{
loop {
match client.recv() {
Ok(ClientEvent::Connecting) => {
connecting.send(LocalClientConnecting);
}
Ok(ClientEvent::Connected) => {
connected.send(LocalClientConnected);
}
Ok(ClientEvent::Recv { msg }) => {
from_server.send(FromServer { msg });
}
Ok(ClientEvent::Disconnected { reason }) => {
disconnected.send(LocalClientDisconnected { reason });
}
Err(RecvError::Empty) => break,
Err(RecvError::Closed) => {
commands.remove_resource::<T>();
break;
}
}
}
}
fn send<C2S, S2C, T>(mut client: ResMut<T>, mut to_server: EventReader<ToServer<C2S>>)
where
C2S: Message + Clone,
S2C: Message,
T: ClientTransport<C2S, S2C> + Resource,
{
for ToServer { msg } in to_server.iter() {
client.send(msg.clone());
}
}