lightyear/lib.rs
1/*! # Lightyear
2
3Lightyear is a networking library for Bevy.
4It provides a set of plugins that can be used to create multiplayer games.
5
6It has been tested mostly in the server-client topology, but the API should be flexible
7enough to support other topologies such as peer-to-peer.
8
9You can find more information in the [book](https://cbournhonesque.github.io/lightyear/book/) or check out the [examples](https://github.com/cBournhonesque/lightyear/tree/main/examples)!
10
11
12## Getting started
13
14### Adding the Plugins
15
16Similarly to Bevy, lightyear is composed of several sub crates that each provide a set of features.
17A wrapper crate `lightyear` is provided for convenience, which contain two main PluginGroups: [`ClientPlugins`](client::ClientPlugins) and [`ServerPlugins`](server::ServerPlugins) that you can add
18to your app.
19
20```rust,ignore
21use bevy_app::App;
22use core::time::Duration;
23use lightyear::prelude::*;
24
25pub const FIXED_TIMESTEP_HZ: f64 = 60.0;
26
27fn main() {
28 let mut app = App::new();
29 app.add_plugins(client::ClientPlugins {
30 tick_duration: Duration::from_secs_f64(1.0 / FIXED_TIMESTEP_HZ),
31 });
32 app.add_plugins(server::ServerPlugins {
33 tick_duration: Duration::from_secs_f64(1.0 / FIXED_TIMESTEP_HZ),
34 });
35}
36```
37
38It is also possible to use the subcrates directly:
39
40**IO** (How to send bytes over the network)
41- [`lightyear_link`]: provides a transport-agnostic `Link` component which is responsible for sending and receiving bytes over the network.
42- [`lightyear_crossbeam`]: IO layer that uses crossbeam channels. Useful for testing or for local networking (by having a server process and a client process on the same machine).
43- [`lightyear_udp`] / [`lightyear_webtransport`]: IO layers for the UDP protocol and WebTransport protocol respectively.
44
45**Connection**
46- [`lightyear_connection`]: this layer wraps the IO layer by providing a long-running `PeerId` identifier component that is used to identify a peer in the network.
47Also provides the `Client` and `Server` components for client-server topologies.
48- [`lightyear_netcode`]: a connection layer that uses the [netcode.io](https://github.com/mas-bandwidth/netcode/blob/main/STANDARD.md) standard for creating secure connections over an unreliable IO
49such as UDP.
50- [`lightyear_steam`]: a connection layer that uses the Steam networking API to both send the bytes and to provide a long-running identifier. This layer operates at both the IO and
51the connection
52level.
53
54Currently it is not possible to use an IO layer without a connection layer.
55
56**Messages**
57- [`lightyear_transport`]: provides a [`Transport`](prelude::Transport) component that is provides several channels with different reliability/ordering guarantees when sending raw bytes. This crate
58also organizes
59the
60raw
61bytes into messages that are assembled into packets.
62- [`lightyear_messages`]: provides a [`MessageManager`](prelude::MessageManager) component responsible for handling the serialization of `Messages` (serializable structs) into raw bytes that can be
63sent over
64the network.
65
66**Replication**
67- [`lightyear_replication`]: provides utilities to replicate the state of the Bevy World between two peers.
68- [`lightyear_sync`]: helps synchronize the timelines between two peers.
69- [`lightyear_prediction`]: provides client-side prediction and rollback to help hide latency
70- [`lightyear_interpolation`]: provides interpolation for replicated entities to smooth out the network updates received from the remote peer.
71- [`lightyear_frame_interpolation`]: most of the game logic should run in the FixedMain schedule, but the rendering is done in the PostUpdate schedule. To avoid visual artifacts, we need some
72interpolation to interpolate the rendering between the FixedMain states.
73
74**Inputs**
75- [`lightyear_inputs`]: backend-agnostic general input queue plugin to network client inputs
76- [`lightyear_inputs_native`]: provides support to use any user-defined struct as an input type
77- [`lightyear_inputs_leafwing`]: provides support to network [leafwing_input_manager](https://github.com/Leafwing-Studios/leafwing-input-manager) inputs
78- [`lightyear_inputs_bei`]: provides support to network [bevy_enhanced_input](https://github.com/projectharmonia/bevy_enhanced_input) inputs
79
80**Extra**
81- [`lightyear_avian2d`]/[`lightyear_avian3d`]: provides a plugin to help handle networking Avian components. This sets the correct system ordering, etc.
82
83
84### Implement the Protocol
85
86The `Protocol` is a shared configuration between the local and remote peers that defines which types will be sent over the network.
87
88You will have to define your protocol in a shared module that is accessible to both the client and the server.
89
90There are several steps:
91- [Adding messages](prelude::MessageRegistry#adding-messages)
92- [Adding components](prelude::ComponentRegistry#adding-components)
93- [Adding channels](prelude::ChannelRegistry#adding-channels)
94- [Adding leafwing inputs](lightyear_inputs_leafwing#adding-leafwing-inputs) or [Adding inputs](lightyear_inputs_native#adding-a-new-input-type)
95
96NOTE: the protocol must currently be added AFTER the Client/Server Plugins, but BEFORE any `Client` or `Server` entity is spawned.
97
98
99### Spawn your Link entity
100
101In lightyear, your Client or Server will simply be an entity with the right sets of components.
102
103The [`Link`](prelude::Link) component can be added on an entity to make it able to send and receive data over the network.
104
105Usually you will add more components on that entity to customize its behavior:
106- define its role using the [`Client`](prelude::Client) or [`Server`](prelude::Server) components. Most of the lightyear plugins currently
107expect the [`Link`](prelude::Link) entity to have one of these components.
108- make it able to receive/send replication data using the [`ReplicationSender`](prelude::ReplicationSender) or [`ReplicationReceiver`](prelude::ReplicationReceiver) components.
109- add a [`MessageManager`](prelude::MessageManager) component to handle the serialization and deserialization of messages.
110- etc.
111
112The `Server` entity works a bit differently. It starts a server that listens for incoming connections. When a new client connects, a new entity is spawned with the [`LinkOf`](prelude::LinkOf)
113component.
114You can add a trigger to listen to this event and add the extra components to customize the behaviour of this connection.
115
116```rust
117# use bevy_ecs::prelude::*;
118# use lightyear::prelude::*;
119# use core::time::Duration;
120fn handle_new_client(trigger: Trigger<OnAdd, LinkOf>, mut commands: Commands) {
121 commands.entity(trigger.target()).insert((
122 ReplicationSender::new(Duration::from_millis(100), SendUpdatesMode::SinceLastAck, false),
123 Name::from("Client"),
124 ));
125}
126```
127
128## Using lightyear
129
130### Linking
131
132There is a set that reflects if the [`Link`](prelude::Link) is established. The link represents an IO connection to send bytes to a remote peer.
133You can trigger [`LinkStart`](prelude::LinkStart) to start the link, and [`Unlink`](prelude::Unlink) to stop it.
134
135The [`Unlinked`](prelude::Unlinked), [`Linking`](prelude::Linking), [`Linked`](prelude::Linked) components represent the current state of the link.
136
137### Connections
138
139A connection is a wrapper around a [`Link`](prelude::Link) that provides a long-running identifier for the peer.
140You can use the [`PeerId`](prelude::PeerId) component to identify the remote peer that the link is connected to, and [`LocalId`](prelude::LocalId) to identify the local peer.
141
142The lifecycle of a connection is controlled by several sets of components.
143
144You can trigger [`Connect`](prelude::Connect) to start the connection, and [`Disconnect`](prelude::Disconnect) to stop it.
145
146The [`Disconnected`](prelude::Disconnected), [`Connecting`](prelude::Connecting), [`Connected`](prelude::Connected) components represent the current state of the connection.
147
148On the server, [`Start`](prelude::server::Start) and [`Stop`](prelude::server::Stop) components are used to control the server's listening state.
149The [`Stopped`](prelude::server::Stopped), [`Starting`](prelude::server::Starting), [`Started`](prelude::server::Started) components represent the current state of the connection.
150
151While a client is disconnected, you can update its configuration (`ReplicationSender`, `MessageManager`, etc.), it will be applied on the next connection attempt.
152
153
154### Sending messages
155
156The [`MessageSender`](prelude::MessageSender) component is used to send messages that you have defined in your protocol.
157
158```rust
159# use bevy_ecs::prelude::*;
160use lightyear::prelude::*;
161use lightyear::prelude::server::*;
162use serde::{Serialize, Deserialize};
163
164#[derive(Serialize, Deserialize)]
165struct MyMessage;
166
167struct MyChannel;
168
169fn send_message(mut sender: Single<&mut MessageSender<MyMessage>>) {
170 let _ = sender.send::<MyChannel>(MyMessage);
171}
172```
173
174### Receiving messages
175
176The [`MessageReceiver`](prelude::MessageReceiver) component is used to receive messages that you have defined in your protocol.
177
178```rust
179# use bevy_ecs::prelude::*;
180# use lightyear::prelude::*;
181# use serde::{Serialize, Deserialize};
182
183# #[derive(Serialize, Deserialize)]
184# struct MyMessage;
185
186fn send_message(mut receivers: Query<&mut MessageReceiver<MyMessage>>) {
187 for mut receiver in receivers.iter_mut() {
188 let _ = receiver.receive().for_each(|message| {});
189 }
190}
191```
192
193### Starting replication
194
195To replicate an entity from the local world to the remote world, you can just add the [`Replicate`](prelude::Replicate) component to the entity.
196
197The marker component [`Replicating`](prelude::Replicating) indicates that the entity is getting replicated to a remote peer.
198You can remove the [`Replicating`](prelude::Replicating) component to pause the replication. This will not despawn the entity on the remote world; it will simply stop sending replication updates.
199
200
201### Reacting to replication events
202
203On the receiver side, entities that are replicated from a remote peer will have the [`Replicated`](prelude::Replicated) marker component.
204
205You can use to react to components being inserted via replication.
206```rust
207# use bevy_ecs::prelude::*;
208# use lightyear::prelude::*;
209# use lightyear::prelude::client::*;
210# use serde::{Serialize, Deserialize};
211
212# #[derive(Component, Serialize, Deserialize)]
213# struct MyComponent;
214
215fn component_inserted(query: Query<Entity, (With<Replicated>, Added<MyComponent>)>) {
216 for entity in query.iter() {
217 println!("MyComponent was inserted via replication on {entity:?}");
218 }
219}
220```
221
222[`Replicated`]: prelude::Replicated
223[`Replicating`]: prelude::Replicating
224[`lightyear_steam`]: lightyear_steam
225 */
226//!
227//! ### Feature Flags
228#![doc = document_features::document_features!()]
229#![cfg_attr(docsrs, feature(doc_auto_cfg))]
230#![allow(ambiguous_glob_reexports)]
231
232#[cfg(feature = "client")]
233mod client;
234
235#[cfg(all(feature = "server", not(target_family = "wasm")))]
236mod server;
237mod shared;
238
239#[cfg(feature = "replication")]
240mod protocol;
241#[cfg(target_family = "wasm")]
242mod web;
243
244pub mod core {
245 pub use lightyear_core::*;
246}
247
248#[cfg(feature = "crossbeam")]
249pub mod crossbeam {
250 pub use lightyear_crossbeam::*;
251}
252
253pub mod link {
254 pub use lightyear_link::*;
255}
256
257#[cfg(feature = "frame_interpolation")]
258pub mod frame_interpolation {
259 pub use lightyear_frame_interpolation::*;
260}
261
262#[cfg(feature = "netcode")]
263pub mod netcode {
264 pub use lightyear_netcode::*;
265}
266
267#[cfg(feature = "interpolation")]
268pub mod interpolation {
269 pub use lightyear_interpolation::*;
270}
271
272#[cfg(feature = "prediction")]
273pub mod prediction {
274 pub use lightyear_prediction::*;
275}
276
277#[cfg(feature = "steam")]
278pub mod steam {
279 pub use lightyear_steam::*;
280}
281
282#[cfg(feature = "webtransport")]
283pub mod webtransport {
284 pub use lightyear_webtransport::*;
285}
286
287#[cfg(feature = "avian2d")]
288pub mod avian2d {
289 pub use lightyear_avian2d::*;
290}
291
292#[cfg(feature = "avian3d")]
293pub mod avian3d {
294 pub use lightyear_avian3d::*;
295}
296
297#[cfg(any(feature = "input_native", feature = "leafwing", feature = "input_bei"))]
298pub mod input {
299 pub use lightyear_inputs::*;
300 #[cfg(feature = "input_native")]
301 pub mod native {
302 pub use lightyear_inputs_native::*;
303 }
304
305 #[cfg(feature = "input_bei")]
306 pub mod bei {
307 pub use lightyear_inputs_bei::*;
308 }
309
310 #[cfg(feature = "leafwing")]
311 pub mod leafwing {
312 pub use lightyear_inputs_leafwing::*;
313 }
314}
315
316pub mod connection {
317 pub use lightyear_connection::*;
318}
319
320pub mod utils {
321 pub use lightyear_utils::*;
322}
323
324pub mod prelude {
325 pub use aeronet_io::connection::{LocalAddr, PeerAddr};
326 pub use lightyear_connection::prelude::*;
327 pub use lightyear_core::prelude::*;
328 pub use lightyear_link::prelude::*;
329 pub use lightyear_messages::prelude::*;
330 #[cfg(feature = "replication")]
331 pub use lightyear_replication::prelude::*;
332 pub use lightyear_sync::prelude::*;
333 pub use lightyear_transport::prelude::*;
334
335 #[cfg(all(not(target_family = "wasm"), feature = "udp"))]
336 pub use lightyear_udp::prelude::*;
337
338 #[allow(unused_imports)]
339 #[cfg(feature = "webtransport")]
340 pub use lightyear_webtransport::prelude::*;
341
342 #[cfg(feature = "steam")]
343 pub use lightyear_steam::prelude::*;
344
345 #[cfg(feature = "netcode")]
346 pub use lightyear_netcode::prelude::*;
347
348 // TODO: maybe put this in prelude::client?
349 #[cfg(feature = "prediction")]
350 pub use lightyear_prediction::prelude::*;
351
352 #[cfg(feature = "interpolation")]
353 pub use lightyear_interpolation::prelude::*;
354
355 #[cfg(any(feature = "input_native", feature = "leafwing", feature = "input_bei"))]
356 pub mod input {
357 pub use lightyear_inputs::prelude::*;
358
359 #[cfg(feature = "input_native")]
360 pub mod native {
361 pub use lightyear_inputs_native::prelude::*;
362 }
363
364 #[cfg(feature = "input_bei")]
365 pub mod bei {
366 pub use lightyear_inputs_bei::prelude::*;
367 }
368 #[cfg(feature = "input_bei")]
369 pub use lightyear_inputs_bei::prelude::InputRegistryExt;
370
371 #[cfg(feature = "leafwing")]
372 pub mod leafwing {
373 pub use lightyear_inputs_leafwing::prelude::*;
374 }
375 }
376
377 #[cfg(feature = "client")]
378 pub mod client {
379 pub use crate::client::ClientPlugins;
380
381 pub use lightyear_sync::prelude::client::*;
382
383 #[cfg(feature = "netcode")]
384 pub use lightyear_netcode::prelude::client::*;
385 #[cfg(feature = "steam")]
386 pub use lightyear_steam::prelude::client::*;
387 #[cfg(feature = "webtransport")]
388 pub use lightyear_webtransport::prelude::client::*;
389
390 #[cfg(any(feature = "input_native", feature = "leafwing", feature = "input_bei"))]
391 pub mod input {
392 pub use lightyear_inputs::prelude::client::*;
393 }
394 }
395
396 #[cfg(all(feature = "server", not(target_family = "wasm")))]
397 pub mod server {
398 pub use crate::server::ServerPlugins;
399 pub use lightyear_connection::prelude::server::*;
400 pub use lightyear_link::prelude::server::*;
401
402 #[cfg(all(not(target_family = "wasm"), feature = "udp", feature = "server"))]
403 pub use lightyear_udp::prelude::server::*;
404
405 #[cfg(feature = "netcode")]
406 pub use lightyear_netcode::prelude::server::*;
407 #[cfg(feature = "steam")]
408 pub use lightyear_steam::prelude::server::*;
409 #[cfg(feature = "webtransport")]
410 pub use lightyear_webtransport::prelude::server::*;
411
412 #[cfg(any(feature = "input_native", feature = "leafwing", feature = "input_bei"))]
413 pub mod input {
414 pub use lightyear_inputs::prelude::server::*;
415 }
416 }
417}