Skip to main content

nevy_messages/
writer.rs

1use std::{any::TypeId, collections::VecDeque, marker::PhantomData};
2
3use bevy::{
4    ecs::system::SystemParam,
5    platform::collections::{HashMap, hash_map::Entry},
6    prelude::*,
7};
8use bytes::Bytes;
9use nevy_transport::prelude::*;
10use serde::Serialize;
11
12use crate::{
13    protocol::{ConnectionProtocolEntity, Protocol},
14    varint::VarInt,
15};
16
17/// A state machine for a stream that sends messages.
18///
19/// For most use cases that involve sending messages use either [`LocalMessageSender`] or [`SharedMessageSender`].
20pub struct MessageStreamState {
21    stream: Stream,
22    /// Each chunk of data must be sent entirely.
23    /// This is why they are kept separately.
24    buffer: VecDeque<Bytes>,
25}
26
27impl MessageStreamState {
28    pub fn new(mut connection: Connection, requirements: StreamRequirements) -> Result<Self> {
29        let stream = connection.new_stream(requirements)?;
30
31        Ok(MessageStreamState {
32            stream,
33            buffer: VecDeque::new(),
34        })
35    }
36
37    /// Writes as much data as possible, returns `true` if all data was written.
38    pub fn flush(&mut self, mut connection: Connection) -> Result<bool> {
39        while let Some(message) = self.buffer.front_mut() {
40            let written = connection.write(&self.stream, message.clone(), true)?;
41            let _ = message.split_to(written);
42
43            if message.is_empty() {
44                self.buffer.pop_front();
45            } else {
46                return Ok(false);
47            }
48        }
49
50        Ok(true)
51    }
52
53    pub fn write<T>(
54        &mut self,
55        mut connection: Connection,
56        message_id: usize,
57        queue: bool,
58        message: &T,
59    ) -> Result<bool>
60    where
61        T: Serialize,
62    {
63        let buffer_empty = self.flush(connection.reborrow())?;
64
65        // Only attempt to send the message if there is no congestion or if `queue` is true.
66        if !(queue || buffer_empty) {
67            return Ok(false);
68        }
69
70        let message = postcard::to_stdvec(message)?;
71
72        let mut buffer = Vec::with_capacity(message.len() + 16);
73
74        VarInt::from_u64(message_id as u64)
75            .ok_or("Message id was too big for VarInt")?
76            .encode(&mut buffer);
77
78        VarInt::from_u64(message.len() as u64)
79            .ok_or("Message length was too big for VarInt")?
80            .encode(&mut buffer);
81
82        buffer.extend(message);
83
84        self.buffer.push_back(buffer.into());
85
86        self.flush(connection)?;
87
88        Ok(true)
89    }
90
91    pub fn close(&mut self, mut connection: Connection, graceful: bool) -> Result {
92        connection.close_send_stream(&self.stream, graceful)
93    }
94}
95
96/// Holds an optional [`MessageStreamState`] for each connection.
97/// Used by either a [`LocalMessageSender`] or [`SharedMessageSender`] for sending messages from systems.
98#[derive(Default)]
99pub struct MessageSenderState {
100    streams: HashMap<Entity, MessageStreamState>,
101}
102
103/// System parameters needed for either a [`LocalMessageSender`] or [`SharedMessageSender`] to send messages.
104#[derive(SystemParam)]
105pub struct MessageSenderParams<'w, 's> {
106    connection_q: Query<'w, 's, (&'static ConnectionOf, &'static ConnectionProtocolEntity)>,
107    endpoint_q: Query<'w, 's, &'static mut Endpoint>,
108    protocol_q: Query<'w, 's, &'static Protocol>,
109}
110
111/// Holds a [`MessageSenderState`] in a system [`Local`].
112/// Used for sending messages when ordering requirements between systems aren't needed.
113///
114/// [`Self::flush`](MessageSender::flush) needs to be called in any system that uses this sender,
115/// or partially written messages will not be sent.
116///
117/// By default this sender is reliable and ordered. There are shorthands for less strict [`StreamRequirements`].
118/// - [`LocalMessageSenderUnrel`]
119/// - [`LocalMessageSenderUnord`]
120/// - [`LocalMessageSenderUnordUnrel`]
121#[derive(SystemParam)]
122pub struct LocalMessageSender<'w, 's, const RELIABLE: bool = true, const ORDERED: bool = true> {
123    state: Local<'s, MessageSenderState>,
124    params: MessageSenderParams<'w, 's>,
125}
126
127pub type LocalMessageSenderUnrel<'w, 's> = LocalMessageSender<'w, 's, true, false>;
128pub type LocalMessageSenderUnord<'w, 's> = LocalMessageSender<'w, 's, false, true>;
129pub type LocalMessageSenderUnordUnrel<'w, 's> = LocalMessageSender<'w, 's, false, false>;
130
131/// Holds a [`MessageSenderState`] in a shared resource.
132/// Used for sending messages when ordering requirements between systems are needed.
133///
134/// Unlike a [`LocalMessageSender`] the shared state needs to be initialized by calling
135/// [`App::add_shared_message_sender<S>`](AddSharedMessageSender::add_shared_message_sender)
136/// with the desired [`StreamRequirements`].
137///
138/// Each shared sender is marked by a unique `S`.
139///
140/// [`Self::flush`](MessageSender::flush) does not need to be called manually for shared senders, it is done automatically once per tick.
141#[derive(SystemParam)]
142pub struct SharedMessageSender<'w, 's, S>
143where
144    S: Send + Sync + 'static,
145{
146    state: ResMut<'w, SharedMessageSenderState<S>>,
147    params: MessageSenderParams<'w, 's>,
148}
149
150/// The shared state used by a [`SharedMessageSender`].
151#[derive(Resource)]
152struct SharedMessageSenderState<S> {
153    _p: PhantomData<S>,
154    requirements: StreamRequirements,
155    state: MessageSenderState,
156}
157
158pub trait MessageSender<'w, 's> {
159    fn context(
160        &mut self,
161    ) -> (
162        &mut MessageSenderState,
163        &mut MessageSenderParams<'w, 's>,
164        StreamRequirements,
165    );
166
167    /// Attempts to flush any partially written messages.
168    fn flush(&mut self) -> Result {
169        let (state, params, _) = self.context();
170
171        let mut remove_streams = Vec::new();
172
173        for (&connection_entity, stream_state) in &mut state.streams {
174            let Ok((&ConnectionOf(endpoint_entity), _)) =
175                params.connection_q.get(connection_entity)
176            else {
177                // If the connection no longer exists remove the stream.
178                remove_streams.push(connection_entity);
179                continue;
180            };
181
182            let mut endpoint = params.endpoint_q.get_mut(endpoint_entity)?;
183            let connection = endpoint.get_connection(connection_entity)?;
184
185            stream_state.flush(connection)?;
186        }
187
188        for connection_entity in remove_streams {
189            state.streams.remove(&connection_entity);
190        }
191
192        Ok(())
193    }
194
195    /// Attempts to write a message on the current stream.
196    ///
197    /// If `queue` is true the message will be queued to send even if the stream is congested.
198    /// Shared senders share a queue.
199    fn write<T>(&mut self, connection_entity: Entity, queue: bool, message: &T) -> Result<bool>
200    where
201        T: Serialize + 'static,
202    {
203        let (state, params, requirements) = self.context();
204
205        let (&ConnectionOf(endpoint_entity), protocol_entity) = params
206            .connection_q
207            .get(connection_entity)
208            .ok()
209            .ok_or("Entity is not a connection with a messaging protocol.")?;
210
211        let protocol = params.protocol_q.get(**protocol_entity)?;
212        let &message_id = protocol.lookup.get(&TypeId::of::<T>()).ok_or_else(|| {
213            format!(
214                "This connection's protocol doesn't have an id assigned for message `{}`",
215                std::any::type_name::<T>()
216            )
217        })?;
218
219        let mut endpoint = params.endpoint_q.get_mut(endpoint_entity)?;
220        let mut connection = endpoint.get_connection(connection_entity)?;
221
222        let stream_state = match state.streams.entry(connection_entity) {
223            Entry::Occupied(entry) => entry.into_mut(),
224            Entry::Vacant(entry) => entry.insert(MessageStreamState::new(
225                connection.reborrow(),
226                requirements,
227            )?),
228        };
229
230        stream_state.write(connection, message_id, queue, message)
231    }
232
233    /// Returns the number of buffered messages for a connection.
234    fn queue_size(&mut self, connection_entity: Entity) -> usize {
235        let (state, _, _) = self.context();
236        state
237            .streams
238            .get(&connection_entity)
239            .map(|state| state.buffer.len())
240            .unwrap_or(0)
241    }
242
243    /// Closes all streams that don't have queued data gracefully.
244    fn close_unused_streams(&mut self) -> Result {
245        let (state, params, _) = self.context();
246
247        let mut remove_streams = Vec::new();
248
249        for (&connection_entity, state) in &mut state.streams {
250            if !state.buffer.is_empty() {
251                continue;
252            }
253
254            let Ok((&ConnectionOf(endpoint_entity), _)) =
255                params.connection_q.get(connection_entity)
256            else {
257                remove_streams.push(connection_entity);
258                continue;
259            };
260
261            let mut endpoint = params.endpoint_q.get_mut(endpoint_entity)?;
262            let connection = endpoint.get_connection(connection_entity)?;
263
264            state.close(connection, true)?;
265
266            remove_streams.push(connection_entity);
267        }
268
269        for connection_entity in remove_streams {
270            state.streams.remove(&connection_entity);
271        }
272
273        Ok(())
274    }
275
276    /// Closes all streams, dropping any queued messages that haven't been sent.
277    fn close_all_streams(&mut self, graceful: bool) -> Result {
278        let (state, params, _) = self.context();
279
280        for (connection_entity, mut state) in std::mem::take(&mut state.streams) {
281            let Ok((&ConnectionOf(endpoint_entity), _)) =
282                params.connection_q.get(connection_entity)
283            else {
284                continue;
285            };
286
287            let mut endpoint = params.endpoint_q.get_mut(endpoint_entity)?;
288            let connection = endpoint.get_connection(connection_entity)?;
289
290            state.close(connection, graceful)?;
291        }
292
293        Ok(())
294    }
295}
296
297impl<'w, 's, const RELIABLE: bool, const ORDERED: bool> MessageSender<'w, 's>
298    for LocalMessageSender<'w, 's, RELIABLE, ORDERED>
299{
300    fn context(
301        &mut self,
302    ) -> (
303        &mut MessageSenderState,
304        &mut MessageSenderParams<'w, 's>,
305        StreamRequirements,
306    ) {
307        let requirements = StreamRequirements {
308            reliable: RELIABLE,
309            ordered: ORDERED,
310            bidirectional: false,
311        };
312
313        (&mut *self.state, &mut self.params, requirements)
314    }
315}
316
317impl<'w, 's, S> MessageSender<'w, 's> for SharedMessageSender<'w, 's, S>
318where
319    S: Send + Sync + 'static,
320{
321    fn context(
322        &mut self,
323    ) -> (
324        &mut MessageSenderState,
325        &mut MessageSenderParams<'w, 's>,
326        StreamRequirements,
327    ) {
328        let requirements = self.state.requirements;
329
330        (&mut self.state.state, &mut self.params, requirements)
331    }
332}
333
334pub trait AddSharedMessageSender {
335    fn add_shared_message_sender<S>(&mut self, requirements: StreamRequirements)
336    where
337        S: Send + Sync + 'static;
338}
339
340impl AddSharedMessageSender for App {
341    /// Inserts the shared state for a [`SharedMessageSender<S>`].
342    fn add_shared_message_sender<S>(&mut self, requirements: StreamRequirements)
343    where
344        S: Send + Sync + 'static,
345    {
346        self.insert_resource(SharedMessageSenderState::<S> {
347            _p: PhantomData,
348            requirements: requirements.with_ordered(true),
349            state: MessageSenderState::default(),
350        });
351
352        self.add_systems(
353            DEFAULT_TRANSPORT_SCHEDULE,
354            flush_shared_message_sender::<S>.before(TransportUpdateSystems),
355        );
356    }
357}
358
359fn flush_shared_message_sender<S>(mut sender: SharedMessageSender<'_, '_, S>) -> Result
360where
361    S: Send + Sync + 'static,
362{
363    sender.flush()
364}