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: On<Add, LinkOf>, mut commands: Commands) {
121 commands.entity(trigger.entity).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_cfg))]
230#![allow(ambiguous_glob_reexports)]
231
232#[cfg(feature = "client")]
233mod client;
234
235#[cfg(feature = "server")]
236mod server;
237
238mod shared;
239
240#[cfg(feature = "replication")]
241mod protocol;
242#[cfg(target_family = "wasm")]
243mod web;
244
245pub mod core {
246 pub use lightyear_core::*;
247}
248
249#[cfg(feature = "crossbeam")]
250pub mod crossbeam {
251 pub use lightyear_crossbeam::*;
252}
253
254pub mod link {
255 pub use lightyear_link::*;
256}
257
258#[cfg(feature = "frame_interpolation")]
259pub mod frame_interpolation {
260 pub use lightyear_frame_interpolation::*;
261}
262
263#[cfg(feature = "netcode")]
264pub mod netcode {
265 pub use lightyear_netcode::*;
266}
267
268#[cfg(feature = "interpolation")]
269pub mod interpolation {
270 pub use lightyear_interpolation::*;
271}
272
273#[cfg(feature = "prediction")]
274pub mod prediction {
275 pub use lightyear_prediction::*;
276}
277
278#[cfg(feature = "steam")]
279pub mod steam {
280 pub use lightyear_steam::*;
281}
282
283#[cfg(feature = "webtransport")]
284pub mod webtransport {
285 pub use lightyear_webtransport::*;
286}
287
288#[cfg(feature = "websocket")]
289pub mod websocket {
290 pub use lightyear_websocket::*;
291}
292
293#[cfg(feature = "avian2d")]
294pub mod avian2d {
295 pub use lightyear_avian2d::*;
296}
297
298#[cfg(feature = "avian3d")]
299pub mod avian3d {
300 pub use lightyear_avian3d::*;
301}
302
303#[cfg(any(feature = "input_native", feature = "leafwing", feature = "input_bei"))]
304pub mod input {
305 pub use lightyear_inputs::*;
306 #[cfg(feature = "input_native")]
307 pub mod native {
308 pub use lightyear_inputs_native::*;
309 }
310
311 #[cfg(feature = "input_bei")]
312 pub mod bei {
313 pub use lightyear_inputs_bei::*;
314 }
315
316 #[cfg(feature = "leafwing")]
317 pub mod leafwing {
318 pub use lightyear_inputs_leafwing::*;
319 }
320}
321
322pub mod connection {
323 pub use lightyear_connection::*;
324}
325
326pub mod utils {
327 pub use lightyear_utils::*;
328}
329
330pub mod prelude {
331 pub use aeronet_io::connection::{LocalAddr, PeerAddr};
332 pub use lightyear_connection::prelude::*;
333 pub use lightyear_core::prelude::*;
334 pub use lightyear_link::prelude::*;
335 pub use lightyear_messages::prelude::*;
336 #[cfg(feature = "replication")]
337 pub use lightyear_replication::prelude::*;
338 pub use lightyear_sync::prelude::*;
339 pub use lightyear_transport::prelude::*;
340 pub use lightyear_serde::prelude::*;
341
342 #[cfg(all(not(target_family = "wasm"), feature = "udp"))]
343 pub use lightyear_udp::prelude::*;
344
345 #[allow(unused_imports)]
346 #[cfg(feature = "webtransport")]
347 pub use lightyear_webtransport::prelude::*;
348
349 #[allow(unused_imports)]
350 #[cfg(feature = "websocket")]
351 pub use lightyear_websocket::prelude::*;
352
353 #[cfg(feature = "steam")]
354 pub use lightyear_steam::prelude::*;
355
356 #[cfg(feature = "netcode")]
357 pub use lightyear_netcode::prelude::*;
358
359 // TODO: maybe put this in prelude::client?
360 #[cfg(feature = "prediction")]
361 pub use lightyear_prediction::prelude::*;
362
363 #[cfg(feature = "interpolation")]
364 pub use lightyear_interpolation::prelude::*;
365
366 #[cfg(feature = "metrics")]
367 pub use lightyear_ui::prelude::*;
368
369 #[cfg(any(feature = "input_native", feature = "leafwing", feature = "input_bei"))]
370 pub mod input {
371 pub use lightyear_inputs::prelude::*;
372
373 #[cfg(feature = "input_native")]
374 pub mod native {
375 pub use lightyear_inputs_native::prelude::*;
376 }
377
378 #[cfg(feature = "input_bei")]
379 pub mod bei {
380 pub use lightyear_inputs_bei::prelude::*;
381 }
382 #[cfg(feature = "input_bei")]
383 pub use lightyear_inputs_bei::prelude::InputRegistryExt;
384
385 #[cfg(feature = "leafwing")]
386 pub mod leafwing {
387 pub use lightyear_inputs_leafwing::prelude::*;
388 }
389 }
390
391 #[cfg(feature = "client")]
392 pub mod client {
393 pub use crate::client::ClientPlugins;
394
395 pub use lightyear_connection::prelude::client::*;
396 pub use lightyear_sync::prelude::client::*;
397
398 #[cfg(feature = "netcode")]
399 pub use lightyear_netcode::prelude::client::*;
400 #[cfg(feature = "raw_connection")]
401 pub use lightyear_raw_connection::prelude::client::*;
402 #[cfg(feature = "steam")]
403 pub use lightyear_steam::prelude::client::*;
404 #[cfg(feature = "websocket")]
405 pub use lightyear_websocket::prelude::client::*;
406 #[cfg(feature = "webtransport")]
407 pub use lightyear_webtransport::prelude::client::*;
408
409 #[cfg(any(feature = "input_native", feature = "leafwing", feature = "input_bei"))]
410 pub mod input {
411 pub use lightyear_inputs::prelude::client::*;
412 }
413 }
414
415 #[cfg(feature = "server")]
416 pub mod server {
417 pub use crate::server::ServerPlugins;
418 pub use lightyear_connection::prelude::server::*;
419 pub use lightyear_link::prelude::server::*;
420
421 #[cfg(all(not(target_family = "wasm"), feature = "udp", feature = "server"))]
422 pub use lightyear_udp::prelude::server::*;
423
424 #[cfg(feature = "netcode")]
425 pub use lightyear_netcode::prelude::server::*;
426 #[cfg(feature = "raw_connection")]
427 pub use lightyear_raw_connection::prelude::server::*;
428 #[cfg(feature = "steam")]
429 pub use lightyear_steam::prelude::server::*;
430 #[cfg(all(feature = "websocket", not(target_family = "wasm")))]
431 pub use lightyear_websocket::prelude::server::*;
432 #[cfg(all(feature = "webtransport", not(target_family = "wasm")))]
433 pub use lightyear_webtransport::prelude::server::*;
434
435 #[cfg(any(feature = "input_native", feature = "leafwing", feature = "input_bei"))]
436 pub mod input {
437 pub use lightyear_inputs::prelude::server::*;
438 }
439 }
440}