1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
//! _"This library stinks!" ... "Unless you like durian"_
//!
//! `durian` is a client-server networking library built on top of the [QUIC](https://en.wikipedia.org/wiki/QUIC) protocol which is
//! implemented in Rust by [quinn](https://github.com/quinn-rs/quinn).
//!
//! It provides a thin abstraction layer above the lower-level details of connection management, byte management,
//! framing, and more, to make writing netcode easier and allow the user to focus on the messaging
//! contents instead.  Serialization and deserialization are built into the APIs so you can send and receive exact
//! Packets as structs for ease of development.
//!
//! ### Disclaimer
//! This library is in very early (but very active!) development, meaning a LOT of it will change rapidly.  
//! In its current state, it's usable to create a quick multiplayer demo.  I use it myself to learn [game
//! development](https://github.com/spoorn/multisnakegame).
//!
//! This __is not production ready__, and is missing a lot of features to make it production ready.
//!
//! However, if you are trying to build something and want to avoid a lot of the headache of lower-level netcode details,
//! and don't need the "production" features, such as a multiplayer game demo, LAN sandbox applications, etc., then feel
//! free to try it out!  
//!
//! `durian`'s goal is to make it as simple as possible to setup netcode (See the examples below).
//!
//! # Cargo.toml Dependency
//!
//! Add `durian` to your Cargo.toml via `cargo add durian` or manually:
//!
//! ```toml
//! [dependencies]
//! durian = "0.3"
//! ```
//!
//! # Packet/PacketBuilder
//!
//! There are 2 steps needed to create a `Packet` to be used with `durian`:
//!
//! 1. `durian` allows for structuring `Packets` as simple structs.  The structs must implement
//! Trait [`Packet`], which has a single function [`Packet::as_bytes()`] which will be called for
//! serializing the [`Packet`] into bytes to be sent over the wire between client and server.
//!
//! 2. There also needs to be a struct that implements [`PacketBuilder`] which is used to
//! deserialize from bytes back into your [`Packet`] struct via the [`PacketBuilder::read()`]
//! function.
//!
//! For your convenience, `durian` is bundled with [`durian_macros`] which contains a few macros that
//! help autogenerate Impl blocks for a struct for both [`Packet`] and [`PacketBuilder`].  The only
//! requirement is the struct must be de/serializable, meaning all nested fields also need to be
//! de/serializable.
//!
//! [`bincode_packet`] will de/serialize your Packet using [`bincode`] and applies necessary derive
//! macros automatically for you.
//! ```rust
//! use durian::bincode_packet;
//!
//! // Automatically implements Packet, and generates a PositionPacketBuilder that implements
//! // PacketBuilder.  You can also add other macros such as derive macros so long as they don't
//! // conflict with what #[bincode_packet] adds (See bincode_packet documentation).
//! #[bincode_packet]
//! #[derive(Debug)]
//! struct Position {
//!     x: i32,
//!     y: i32
//! }
//!
//! // Works for Unit (empty) structs as well
//! #[bincode_packet]
//! struct Ack;
//! ```
//!
//! You can also use the derive macros ([`BinPacket`] and [`UnitPacket`]) manually:
//!
//! ```rust
//! use durian::serde::{Deserialize, Serialize};
//! use durian::{BinPacket, UnitPacket};
//!
//! #[derive(Serialize, Deserialize, BinPacket)]
//! #[serde(crate = "durian::serde")]
//! struct Position { x: i32, y: i32 }
//!
//! #[derive(UnitPacket)]
//! struct Ack;
//! ```
//!
//!
//! # PacketManager
//!
//! [`PacketManager`] is what you will use to initiate connections between clients/servers, and
//! send/receive `Packets`.  
//!
//! A [`PacketManager`] would be created on each client to connect to a
//! single server, and one created on the server to connect to multiple clients. It contains both
//! synchronous and asynchronous APIs, so you can call the functions both from a synchronous
//! context, or within an async runtime (_Note: the synchronous path will create a separate
//! isolated async runtime context per `PacketManager` instance._)
//!
//! There are 4 basic steps to using the [`PacketManager`], which would be done on both the client
//! and server side:
//!
//! 1. Create a [`PacketManager`] via [`new()`](`PacketManager::new()`) or, if calling from an async context, [`new_for_async()`](`PacketManager::new_for_async()`)
//!
//! 2. Register the [`Packets`](`Packet`) and [`PacketBuilders`](`PacketBuilder`) that the [`PacketManager`] will __receive__
//! and __send__ using [`register_receive_packet()`](`PacketManager::register_receive_packet()`) and [`register_send_packet()`](`PacketManager::register_send_packet()`).  
//! The ordering of `Packet` registration matters for the `receive` channel and
//! `send` channel each - the client and server must register the same packets in the same order,
//! for the opposite channels.  
//!     - In other words, the client must register `receive` packets in the
//! same order the server registers the same as `send` packets, and vice versa, the client must
//! register `send` packets in the same order the server registers the same as `receive` packets.
//! This helps to ensure the client and servers are in sync on what Packets to send/receive, almost
//! like ensuring they are on the same "version" so to speak, and is used to properly identify
//! Packets.
//!     - For your convenience, `durian_macros` comes bundled with 2 macros that simplify registration of send and
//! receive packets: [`register_receive!()`](https://docs.rs/durian_macros/latest/durian_macros/#declarative-macros), and [`register_send!()`](https://docs.rs/durian_macros/latest/durian_macros/#declarative-macros)
//!
//! 3. Initiate connection(s) with [`init_client()`](`PacketManager::init_client()`) (or the async variant [`async_init_client()`](`PacketManager::async_init_client()`)
//! if on the client side, else use [`init_server()`](`PacketManager::init_server()`) (or the async variant [`async_init_server)`](`PacketManager::async_init_server()`)
//! if on the server side.
//!
//! 4. Send packets using any of [`broadcast()`](`PacketManager::broadcast()`), [`send()`](`PacketManager::send()`), [`send_to()`](`PacketManager::send_to()`)
//! or the respective `async` variants if calling from an async context already.  Receive packets
//! using any of [`received_all()`](`PacketManager::received_all()`) , [`received()`](`PacketManager::received()`), or the respective
//! `async` variants.
//!
//! Putting these together:
//!
//! ```rust
//! use durian::{ClientConfig, PacketManager, bincode_packet, register_receive, register_send};
//!
//! #[bincode_packet]
//! struct Position { x: i32, y: i32 }
//! #[bincode_packet]
//! struct ServerAck;
//! #[bincode_packet]
//! struct ClientAck;
//! #[bincode_packet]
//! struct InputMovement { direction: String }
//!
//! fn packet_manager_example() {
//!     // Create PacketManager
//!     let mut manager = PacketManager::new();
//!
//!     // Register send and receive packets
//!     // Using macros
//!     let receive_results = register_receive!(
//!         manager,
//!         (Position, PositionPacketBuilder),
//!         (ServerAck, ServerAckPacketBuilder)
//!     );
//!     let send_results = register_send!(manager, ClientAck, InputMovement);
//!     // Validate registrations succeeded
//!     assert!(receive_results.iter().all(|r| r.is_ok()));  
//!     assert!(send_results.iter().all(|r| r.is_ok()));
//!
//!     // Or equivalently with manual registrations:
//!     // manager.register_receive_packet::<Position>(PositionPacketBuilder).unwrap();
//!     // manager.register_receive_packet::<ServerAck>(ServerAckPacketBuilder).unwrap();
//!     // manager.register_send_packet::<ClientAck>().unwrap();
//!     // manager.register_send_packet::<InputMovement>().unwrap();
//!
//!     // Initialize a client
//!     let client_config = ClientConfig::new("127.0.0.1:5001", "127.0.0.1:5000", 2, 2);
//!     manager.init_client(client_config).unwrap();
//!
//!     // Send and receive packets
//!     manager.broadcast(InputMovement { direction: "North".to_string() }).unwrap();
//!     manager.received_all::<Position, PositionPacketBuilder>(false).unwrap();
//!
//!     // The above PacketManager is for the client.  Server side is similar except the packets
//!     // are swapped between receive vs send channels:
//!
//!     // Create PacketManager
//!     let mut server_manager = PacketManager::new();
//!
//!     // Register send and receive packets
//!     let server_receive_results = register_receive!(
//!         server_manager,
//!         (ClientAck, ClientAckPacketBuilder),
//!         (InputMovement, InputMovementPacketBuilder)
//!     );
//!     let server_send_results = register_send!(server_manager, Position, ServerAck);
//!     // Validate registrations succeeded
//!     assert!(server_receive_results.iter().all(|r| r.is_ok()));  
//!     assert!(server_send_results.iter().all(|r| r.is_ok()));
//!
//!     // Initialize a client
//!     let client_config = ClientConfig::new("127.0.0.1:5001", "127.0.0.1:5000", 2, 2);
//!     server_manager.init_client(client_config).unwrap();
//!
//!     // Send and receive packets
//!     server_manager.broadcast(Position { x: 1, y: 3 }).unwrap();
//!     server_manager.received_all::<InputMovement, InputMovementPacketBuilder>(false).unwrap();
//! }
//! ```
//!
//! # durian_macros
//!
//! Rust docs don't properly link documentation of re-exported crates.  If you want to learn more about how the
//! [`bincode_packet`] and other macros used above work, see the [`durian_macros`] docs.
//!
//! # Examples
//!
//! For beginners, creating packets to be sent between clients/server should be extremely straight-forward
//! and the above examples should covers most of what you'd need.  For more complex scenarios, such as
//! serializing/deserializing packets in a custom way, can be done by implementing the various Traits
//! yourself, or through extra configurations in the [`PacketManager`](PacketManager).
//!
//! For a comprehensive minimal example, see the [`example crate`](https://github.com/spoorn/durian/tree/main/example).
//!
//! I also use this library myself for simple game development.  See the [`multisnakegame repo`](https://github.com/spoorn/multisnakegame).
//!
//!
//! # Debugging
//!
//! `durian` uses the `log` API with debug and trace logs.  Enable debug logging to see update logs
//! from `durian`, and enable trace logging to see packet byte transmissions.

mod error;
mod packet;
mod quinn_helpers;

pub use error::*;
pub use packet::*;

#[doc(inline)]
#[allow(unused_imports)]
pub use durian_macros;

#[allow(unused_imports)]
pub use durian_macros::*;

#[allow(unused_imports)]
pub use bytes;

#[allow(unused_imports)]
pub use serde;

#[allow(unused_imports)]
pub use bincode;

/// For [`PacketManager::get_source()`]
#[allow(unused_imports)]
pub use quinn::Endpoint;