use bevy::prelude::*;
pub use packet::Packet;
use std::{
io,
net::{SocketAddr, ToSocketAddrs},
sync::Arc,
};
use tcp::{Connection, Listener};
mod packet;
mod tcp;
#[derive(Resource)]
pub struct ServerConnection(Connection);
impl ServerConnection {
pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<Self> {
Ok(Self(Connection::connect(addr)?))
}
pub fn send<P: Packet>(&self, packet: P) {
self.0.send(packet);
}
pub fn address(&self) -> SocketAddr {
self.0.address()
}
}
#[derive(Resource)]
pub struct ClientListener(Listener);
impl ClientListener {
pub fn bind<A: ToSocketAddrs>(address: A) -> io::Result<Self> {
Ok(Self(Listener::bind(address)?))
}
pub fn address(&self) -> SocketAddr {
self.0.address()
}
}
#[derive(Component)]
pub struct ClientConnection(Arc<Connection>);
impl ClientConnection {
pub fn send<P: Packet>(&self, packet: P) {
self.0.send(packet);
}
pub fn address(&self) -> SocketAddr {
self.0.address()
}
}
pub struct ClientNetworkPlugin;
impl ClientNetworkPlugin {
fn remove_disconnected(mut commands: Commands, connection: Res<ServerConnection>) {
if !connection.0.connected() {
commands.remove_resource::<ServerConnection>();
}
}
fn clear_cache(connection: Res<ServerConnection>) {
connection.0.clear();
}
}
impl Plugin for ClientNetworkPlugin {
fn build(&self, app: &mut App) {
app.add_systems((
Self::remove_disconnected.run_if(resource_exists::<ServerConnection>()),
Self::clear_cache
.run_if(resource_exists::<ServerConnection>())
.after(Self::remove_disconnected),
));
}
}
pub struct ServerNetworkPlugin;
impl ServerNetworkPlugin {
fn remove_disconnected(
mut commands: Commands,
connections: Query<(Entity, &ClientConnection)>,
) {
for (entity, connection) in connections.iter() {
if !connection.0.connected() {
commands.entity(entity).remove::<ClientConnection>();
}
}
}
fn clear_cache(connections: Query<&ClientConnection>) {
for connection in connections.iter() {
connection.0.clear();
}
}
fn remove_not_listening(mut commands: Commands, listener: Res<ClientListener>) {
if !listener.0.listening() {
commands.remove_resource::<ClientListener>();
}
}
fn accept_connections(mut commands: Commands, listener: Res<ClientListener>) {
while let Some(connection) = listener.0.accept() {
commands.spawn(ClientConnection(Arc::new(connection)));
}
}
}
impl Plugin for ServerNetworkPlugin {
fn build(&self, app: &mut App) {
app.add_systems((
Self::remove_disconnected,
Self::clear_cache.after(Self::remove_disconnected),
Self::remove_not_listening.run_if(resource_exists::<ClientListener>()),
Self::accept_connections
.run_if(resource_exists::<ClientListener>())
.after(Self::remove_not_listening),
));
}
}
fn receive_server_packets<P: Packet>(
mut writer: EventWriter<PacketEvent<P>>,
connection: Query<(Entity, &ClientConnection)>,
) {
for (entity, connection) in connection.iter() {
for packet in connection.0.recv() {
writer.send(PacketEvent {
connection: ClientConnection(Arc::clone(&connection.0)),
entity,
packet,
});
}
}
}
pub trait AppServerNetwork {
fn register_server_packet<P: Packet>(&mut self) -> &mut Self;
}
impl AppServerNetwork for App {
fn register_server_packet<P: Packet>(&mut self) -> &mut Self {
self.add_event::<PacketEvent<P>>();
self.add_system(
receive_server_packets::<P>
.after(ServerNetworkPlugin::remove_disconnected)
.before(ServerNetworkPlugin::clear_cache),
);
self
}
}
pub struct PacketEvent<P: Packet> {
pub connection: ClientConnection,
pub entity: Entity,
pub packet: P,
}
fn receive_client_packets<P: Packet>(
mut writer: EventWriter<P>,
connection: Res<ServerConnection>,
) {
for packet in connection.0.recv() {
writer.send(packet);
}
}
pub trait AppClientNetwork {
fn register_client_packet<P: Packet>(&mut self) -> &mut Self;
}
impl AppClientNetwork for App {
fn register_client_packet<P: Packet>(&mut self) -> &mut Self {
self.add_event::<P>();
self.add_system(
receive_client_packets::<P>
.run_if(resource_exists::<ServerConnection>())
.after(ClientNetworkPlugin::remove_disconnected)
.before(ClientNetworkPlugin::clear_cache),
);
self
}
}