bevy_nest/
events.rs

1use crate::errors::NetworkError;
2use crate::server::ClientId;
3
4use bevy::prelude::*;
5use tokio::net::TcpStream;
6
7#[derive(Debug)]
8pub(crate) struct IncomingConnection {
9    pub(crate) socket: TcpStream,
10}
11
12#[derive(Debug, Event)]
13pub enum NetworkEvent {
14    Connected(ClientId),
15    Disconnected(ClientId),
16    Error(NetworkError),
17}
18
19/// Data to be sent to a client over the GMCP protocol.
20#[derive(Debug, Clone)]
21pub struct Payload {
22    pub package: String,
23    pub subpackage: Option<String>,
24    pub data: Option<String>,
25}
26
27/// A message sent from the server to a client or vice versa.
28#[derive(Debug)]
29pub enum Message {
30    /// Just your regular text message. This is appended with a newline when sent
31    /// to the client.
32    Text(String),
33    /// A command is a sequence of bytes used by the telnet protocol. You can use
34    /// the constants in the [`telnet`](crate::telnet) module to make things easier.
35    ///
36    /// See: <https://users.cs.cf.ac.uk/Dave.Marshall/Internet/node141.html>
37    Command(Vec<u8>),
38    /// A GMCP message is a JSON object serialized into a string. The GMCP
39    /// protocol is used to send structured data to the client.
40    ///
41    /// See: <https://www.gammon.com.au/gmcp>
42    GMCP(Payload),
43}
44
45impl From<&str> for Message {
46    /// Convert a string slice into a [`Message::Text`].
47    fn from(s: &str) -> Self {
48        Message::Text(s.into())
49    }
50}
51
52impl From<String> for Message {
53    /// Convert a string into a [`Message::Text`].
54    fn from(s: String) -> Self {
55        Message::Text(s)
56    }
57}
58
59impl From<Vec<u8>> for Message {
60    /// Convert a vector of bytes into a [`Message::Command`].
61    fn from(v: Vec<u8>) -> Self {
62        Message::Command(v)
63    }
64}
65
66impl From<Payload> for Message {
67    /// Convert a [`Payload`] object into a [`Message::GMCP`].
68    fn from(payload: Payload) -> Self {
69        Message::GMCP(payload)
70    }
71}
72
73/// [`Message`] sent from a client. These are iterated over each
74/// update and sent to Bevy via [`Event<Inbox>`](bevy::ecs::event::Event) to be read over.
75///
76/// ```rust
77/// use bevy::prelude::*;
78/// use bevy_nest::prelude::*;
79///
80/// fn read_inbox(mut inbox: EventReader<Inbox>) {
81///     for message in inbox.read() {
82///         // ...
83///     }
84/// }
85/// ```
86#[derive(Debug, Event)]
87pub struct Inbox {
88    pub from: ClientId,
89    pub content: Message,
90}
91
92/// [`Message`] sent to a client. These are iterated over each
93/// update by the server and sent to the client's socket.
94///
95/// ```rust
96/// use bevy::prelude::*;
97/// use bevy_nest::prelude::*;
98///
99/// fn ping_pong(mut inbox: EventReader<Inbox>, mut outbox: EventWriter<Outbox>) {
100///     for message in inbox.read() {
101///         if let Message::Text(content) = &message.content {
102///             if content == "ping" {
103///                 // There are a few ways to send messages to the outbox:
104///                 // 1. Build the message and send it to the outbox.
105///                 outbox.send(Outbox { to: message.from, content: Message::Text("pong!".into()) });
106///                 // 2. Use the From trait, which is implemented for &str, String, Vec<u8>, and Payload
107///                 // for creating text, commands, and GMCP messages respectively.
108///                 outbox.send(Outbox { to: message.from, content: "pong!".into() });
109///                 // 3. Use the extension trait OutboxWriterExt, which provides convenience methods.
110///                 outbox.send_text(message.from, "pong!");
111///             }
112///         }
113///     }
114/// }
115/// ```
116#[derive(Debug, Event)]
117pub struct Outbox {
118    pub to: ClientId,
119    pub content: Message,
120}
121
122/// Extension trait for [`EventWriter<Outbox>`] to make sending messages easier.
123pub trait OutboxWriterExt {
124    fn send_text(&mut self, to: ClientId, text: impl Into<String>);
125    fn send_command(&mut self, to: ClientId, command: impl Into<Vec<u8>>);
126    fn send_gmcp(&mut self, to: ClientId, payload: Payload);
127}
128
129impl OutboxWriterExt for EventWriter<'_, Outbox> {
130    /// Sends a [`Message::Text`] to a client.
131    fn send_text(&mut self, to: ClientId, text: impl Into<String>) {
132        self.send(Outbox {
133            to,
134            content: Message::Text(text.into()),
135        });
136    }
137
138    /// Sends a [`Message::Command`] to a client.
139    fn send_command(&mut self, to: ClientId, command: impl Into<Vec<u8>>) {
140        self.send(Outbox {
141            to,
142            content: Message::Command(command.into()),
143        });
144    }
145
146    /// Sends a [`Message::GMCP`] to a client.
147    fn send_gmcp(&mut self, to: ClientId, payload: Payload) {
148        self.send(Outbox {
149            to,
150            content: Message::GMCP(payload),
151        });
152    }
153}