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}