bevnet/
lib.rs

1use bevy::prelude::*;
2pub use packet::Packet;
3use std::{
4    io,
5    net::{SocketAddr, ToSocketAddrs},
6    sync::Arc,
7};
8use tcp::{Connection, Listener};
9
10mod packet;
11mod tcp;
12
13/// A connection to a server.
14#[derive(Resource)]
15pub struct ServerConnection(Connection);
16
17impl ServerConnection {
18    /// Creates a [ServerConnection] to the given address.
19    pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<Self> {
20        Ok(Self(Connection::connect(addr)?))
21    }
22
23    /// Sends a [Packet] to the server.
24    pub fn send<P: Packet>(&self, packet: P) {
25        self.0.send(packet);
26    }
27
28    /// Gets the address of the server.
29    pub fn address(&self) -> SocketAddr {
30        self.0.address()
31    }
32}
33
34/// Used to listen for incoming [ClientConnection]s.
35#[derive(Resource)]
36pub struct ClientListener(Listener);
37
38impl ClientListener {
39    /// Creates a [ClientListener] binded to the given address.
40    pub fn bind<A: ToSocketAddrs>(address: A) -> io::Result<Self> {
41        Ok(Self(Listener::bind(address)?))
42    }
43
44    /// Returns the address the [ClientListener] is bound to.
45    pub fn address(&self) -> SocketAddr {
46        self.0.address()
47    }
48}
49
50/// A connection to a client.
51#[derive(Component)]
52pub struct ClientConnection(Arc<Connection>);
53
54impl ClientConnection {
55    /// Sends a [Packet] to the client.
56    pub fn send<P: Packet>(&self, packet: P) {
57        self.0.send(packet);
58    }
59
60    /// Gets the address of the client.
61    pub fn address(&self) -> SocketAddr {
62        self.0.address()
63    }
64}
65
66/// A [Plugin] for client networking.
67pub struct ClientNetworkPlugin;
68
69impl ClientNetworkPlugin {
70    /// Removes the [ServerConnection] resource when it's disconnected.
71    fn remove_disconnected(mut commands: Commands, connection: Res<ServerConnection>) {
72        if !connection.0.connected() {
73            commands.remove_resource::<ServerConnection>();
74        }
75    }
76
77    /// Clears the packet cache of the [ServerConnection].
78    fn clear_cache(connection: Res<ServerConnection>) {
79        connection.0.clear();
80    }
81}
82
83impl Plugin for ClientNetworkPlugin {
84    fn build(&self, app: &mut App) {
85        app.add_systems((
86            Self::remove_disconnected.run_if(resource_exists::<ServerConnection>()),
87            Self::clear_cache
88                .run_if(resource_exists::<ServerConnection>())
89                .after(Self::remove_disconnected),
90        ));
91    }
92}
93
94/// A [Plugin] for server networking.
95pub struct ServerNetworkPlugin;
96
97impl ServerNetworkPlugin {
98    /// Removes the [ClientConnection] components when it's disconnected.
99    fn remove_disconnected(
100        mut commands: Commands,
101        connections: Query<(Entity, &ClientConnection)>,
102    ) {
103        for (entity, connection) in connections.iter() {
104            if !connection.0.connected() {
105                commands.entity(entity).remove::<ClientConnection>();
106            }
107        }
108    }
109
110    /// Clears the packet cache of the [ClientConnection]s.
111    fn clear_cache(connections: Query<&ClientConnection>) {
112        for connection in connections.iter() {
113            connection.0.clear();
114        }
115    }
116
117    /// Removes the [ClientListener] resource when it stop listening.
118    fn remove_not_listening(mut commands: Commands, listener: Res<ClientListener>) {
119        if !listener.0.listening() {
120            commands.remove_resource::<ClientListener>();
121        }
122    }
123
124    /// Accepts incoming connections.
125    fn accept_connections(mut commands: Commands, listener: Res<ClientListener>) {
126        while let Some(connection) = listener.0.accept() {
127            commands.spawn(ClientConnection(Arc::new(connection)));
128        }
129    }
130}
131
132impl Plugin for ServerNetworkPlugin {
133    fn build(&self, app: &mut App) {
134        app.add_systems((
135            Self::remove_disconnected,
136            Self::clear_cache.after(Self::remove_disconnected),
137            Self::remove_not_listening.run_if(resource_exists::<ClientListener>()),
138            Self::accept_connections
139                .run_if(resource_exists::<ClientListener>())
140                .after(Self::remove_not_listening),
141        ));
142    }
143}
144
145/// Receives [Packet]s and sends them as [PacketEvent]s.
146fn receive_server_packets<P: Packet>(
147    mut writer: EventWriter<PacketEvent<P>>,
148    connection: Query<(Entity, &ClientConnection)>,
149) {
150    for (entity, connection) in connection.iter() {
151        for packet in connection.0.recv() {
152            writer.send(PacketEvent {
153                connection: ClientConnection(Arc::clone(&connection.0)),
154                entity,
155                packet,
156            });
157        }
158    }
159}
160
161/// An extention trait to easily register a [Packet] to the server.
162pub trait AppServerNetwork {
163    /// Registers a [Packet] for the server.
164    fn register_server_packet<P: Packet>(&mut self) -> &mut Self;
165}
166
167impl AppServerNetwork for App {
168    fn register_server_packet<P: Packet>(&mut self) -> &mut Self {
169        self.add_event::<PacketEvent<P>>();
170        self.add_system(
171            receive_server_packets::<P>
172                .after(ServerNetworkPlugin::remove_disconnected)
173                .before(ServerNetworkPlugin::clear_cache),
174        );
175        self
176    }
177}
178
179/// An event for received [Packet]s on the server.
180pub struct PacketEvent<P: Packet> {
181    /// The [ClientConnection] from which the [Packet] was received.
182    pub connection: ClientConnection,
183
184    /// The [Entity] of the [ClientConnection].
185    pub entity: Entity,
186
187    /// The [Packet]
188    pub packet: P,
189}
190
191/// Receives [Packet]s and sends them as [Event]s.
192fn receive_client_packets<P: Packet>(
193    mut writer: EventWriter<P>,
194    connection: Res<ServerConnection>,
195) {
196    for packet in connection.0.recv() {
197        writer.send(packet);
198    }
199}
200
201/// An extention trait to easily register a [Packet] to the client.
202pub trait AppClientNetwork {
203    /// Registers a [Packet] for the client.
204    fn register_client_packet<P: Packet>(&mut self) -> &mut Self;
205}
206
207impl AppClientNetwork for App {
208    fn register_client_packet<P: Packet>(&mut self) -> &mut Self {
209        self.add_event::<P>();
210        self.add_system(
211            receive_client_packets::<P>
212                .run_if(resource_exists::<ServerConnection>())
213                .after(ClientNetworkPlugin::remove_disconnected)
214                .before(ClientNetworkPlugin::clear_cache),
215        );
216        self
217    }
218}