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}