lightyear 0.26.4

Server-client networking library for the Bevy game engine with modular architecture
Documentation
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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
/*! # Lightyear

Lightyear is a networking library for Bevy.
It provides a set of plugins that can be used to create multiplayer games.

It has been tested mostly in the server-client topology, but the API should be flexible
enough to support other topologies such as peer-to-peer.

You 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)!


## Getting started

### Adding the Plugins

Similarly to Bevy, lightyear is composed of several sub crates that each provide a set of features.
A wrapper crate `lightyear` is provided for convenience, which contain two main PluginGroups: [`ClientPlugins`](client::ClientPlugins) and [`ServerPlugins`](server::ServerPlugins) that you can add
to your app.

```rust,ignore
use bevy_app::App;
use core::time::Duration;
use lightyear::prelude::*;

pub const FIXED_TIMESTEP_HZ: f64 = 60.0;

fn main() {
    let mut app = App::new();
    app.add_plugins(client::ClientPlugins {
        tick_duration: Duration::from_secs_f64(1.0 / FIXED_TIMESTEP_HZ),
    });
    app.add_plugins(server::ServerPlugins {
        tick_duration: Duration::from_secs_f64(1.0 / FIXED_TIMESTEP_HZ),
    });
}
```

It is also possible to use the subcrates directly:

**IO** (How to send bytes over the network)
- [`lightyear_link`]: provides a transport-agnostic `Link` component which is responsible for sending and receiving bytes over the network.
- [`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).
- [`lightyear_udp`] / [`lightyear_webtransport`]: IO layers for the UDP protocol and WebTransport protocol respectively.

**Connection**
- [`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.
Also provides the `Client` and `Server` components for client-server topologies.
- [`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
such as UDP.
- [`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
the connection
level.

Currently it is not possible to use an IO layer without a connection layer.

**Messages**
- [`lightyear_transport`]: provides a [`Transport`](prelude::Transport) component that is provides several channels with different reliability/ordering guarantees when sending raw bytes. This crate
also organizes
the
raw
bytes into messages that are assembled into packets.
- [`lightyear_messages`]: provides a [`MessageManager`](prelude::MessageManager) component responsible for handling the serialization of `Messages` (serializable structs) into raw bytes that can be
sent over
the network.

**Replication**
- [`lightyear_replication`]: provides utilities to replicate the state of the Bevy World between two peers.
- [`lightyear_sync`]: helps synchronize the timelines between two peers.
- [`lightyear_prediction`]: provides client-side prediction and rollback to help hide latency
- [`lightyear_interpolation`]: provides interpolation for replicated entities to smooth out the network updates received from the remote peer.
- [`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
interpolation to interpolate the rendering between the FixedMain states.

**Inputs**
- [`lightyear_inputs`]: backend-agnostic general input queue plugin to network client inputs
- [`lightyear_inputs_native`]: provides support to use any user-defined struct as an input type
- [`lightyear_inputs_leafwing`]: provides support to network [leafwing_input_manager](https://github.com/Leafwing-Studios/leafwing-input-manager) inputs
- [`lightyear_inputs_bei`]: provides support to network [bevy_enhanced_input](https://github.com/projectharmonia/bevy_enhanced_input) inputs

**Extra**
- [`lightyear_avian2d`]/[`lightyear_avian3d`]: provides a plugin to help handle networking Avian components. This sets the correct system ordering, etc.


### Implement the Protocol

The `Protocol` is a shared configuration between the local and remote peers that defines which types will be sent over the network.

You will have to define your protocol in a shared module that is accessible to both the client and the server.

There are several steps:
- [Adding messages](prelude::MessageRegistry#adding-messages)
- [Adding components](prelude::ComponentRegistry#adding-components)
- [Adding channels](prelude::ChannelRegistry#adding-channels)
- [Adding leafwing inputs](lightyear_inputs_leafwing#adding-leafwing-inputs) or [Adding inputs](lightyear_inputs_native#adding-a-new-input-type)

NOTE: the protocol must currently be added AFTER the Client/Server Plugins, but BEFORE any `Client` or `Server` entity is spawned.


### Spawn your Link entity

In lightyear, your Client or Server will simply be an entity with the right sets of components.

The [`Link`](prelude::Link) component can be added on an entity to make it able to send and receive data over the network.

Usually you will add more components on that entity to customize its behavior:
- define its role using the [`Client`](prelude::Client) or [`Server`](prelude::Server) components. Most of the lightyear plugins currently
expect the [`Link`](prelude::Link) entity to have one of these components.
- make it able to receive/send replication data using the [`ReplicationSender`](prelude::ReplicationSender) or [`ReplicationReceiver`](prelude::ReplicationReceiver) components.
- add a [`MessageManager`](prelude::MessageManager) component to handle the serialization and deserialization of messages.
- etc.

The `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)
component.
You can add a trigger to listen to this event and add the extra components to customize the behaviour of this connection.

```rust
# use bevy_ecs::prelude::*;
# use lightyear::prelude::*;
# use core::time::Duration;
fn handle_new_client(trigger: On<Add, LinkOf>, mut commands: Commands) {
    commands.entity(trigger.entity).insert((
        ReplicationSender::new(Duration::from_millis(100), SendUpdatesMode::SinceLastAck, false),
        Name::from("Client"),
    ));
}
```

## Using lightyear

### Linking

There 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.
You can trigger [`LinkStart`](prelude::LinkStart) to start the link, and [`Unlink`](prelude::Unlink) to stop it.

The [`Unlinked`](prelude::Unlinked), [`Linking`](prelude::Linking), [`Linked`](prelude::Linked) components represent the current state of the link.

### Connections

A connection is a wrapper around a [`Link`](prelude::Link) that provides a long-running identifier for the peer.
You 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.

The lifecycle of a connection is controlled by several sets of components.

You can trigger [`Connect`](prelude::Connect) to start the connection, and [`Disconnect`](prelude::Disconnect) to stop it.

The [`Disconnected`](prelude::Disconnected), [`Connecting`](prelude::Connecting), [`Connected`](prelude::Connected) components represent the current state of the connection.

On the server, [`Start`](prelude::server::Start) and [`Stop`](prelude::server::Stop) components are used to control the server's listening state.
The [`Stopped`](prelude::server::Stopped), [`Starting`](prelude::server::Starting), [`Started`](prelude::server::Started) components represent the current state of the connection.

While a client is disconnected, you can update its configuration (`ReplicationSender`, `MessageManager`, etc.), it will be applied on the next connection attempt.


### Sending messages

The [`MessageSender`](prelude::MessageSender) component is used to send messages that you have defined in your protocol.

```rust
# use bevy_ecs::prelude::*;
use lightyear::prelude::*;
use lightyear::prelude::server::*;
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct MyMessage;

struct MyChannel;

fn send_message(mut sender: Single<&mut MessageSender<MyMessage>>) {
    let _ = sender.send::<MyChannel>(MyMessage);
}
```

### Receiving messages

The [`MessageReceiver`](prelude::MessageReceiver) component is used to receive messages that you have defined in your protocol.

```rust
# use bevy_ecs::prelude::*;
# use lightyear::prelude::*;
# use serde::{Serialize, Deserialize};

# #[derive(Serialize, Deserialize)]
# struct MyMessage;

fn send_message(mut receivers: Query<&mut MessageReceiver<MyMessage>>) {
    for mut receiver in receivers.iter_mut() {
        let _ = receiver.receive().for_each(|message| {});
    }
}
```

### Starting replication

To replicate an entity from the local world to the remote world, you can just add the [`Replicate`](prelude::Replicate) component to the entity.

The marker component [`Replicating`](prelude::Replicating) indicates that the entity is getting replicated to a remote peer.
You 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.


### Reacting to replication events

On the receiver side, entities that are replicated from a remote peer will have the [`Replicated`](prelude::Replicated) marker component.

You can use to react to components being inserted via replication.
```rust
# use bevy_ecs::prelude::*;
# use lightyear::prelude::*;
# use lightyear::prelude::client::*;
# use serde::{Serialize, Deserialize};

# #[derive(Component, Serialize, Deserialize)]
# struct MyComponent;

fn component_inserted(query: Query<Entity, (With<Replicated>, Added<MyComponent>)>) {
    for entity in query.iter() {
        println!("MyComponent was inserted via replication on {entity:?}");
    }
}
```

[`Replicated`]: prelude::Replicated
[`Replicating`]: prelude::Replicating
[`lightyear_steam`]: lightyear_steam
 */
//!
//! ### Feature Flags
#![doc = document_features::document_features!()]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(ambiguous_glob_reexports)]

#[cfg(feature = "client")]
mod client;

#[cfg(feature = "server")]
mod server;

mod shared;

#[cfg(feature = "replication")]
mod protocol;

pub mod core {
    pub use lightyear_core::*;
}

#[cfg(feature = "crossbeam")]
pub mod crossbeam {
    pub use lightyear_crossbeam::*;
}

pub mod link {
    pub use lightyear_link::*;
}

#[cfg(feature = "frame_interpolation")]
pub mod frame_interpolation {
    pub use lightyear_frame_interpolation::*;
}

#[cfg(feature = "metrics")]
pub mod metrics {
    pub use lightyear_metrics::*;
}

#[cfg(feature = "netcode")]
pub mod netcode {
    pub use lightyear_netcode::*;
}

#[cfg(feature = "interpolation")]
pub mod interpolation {
    pub use lightyear_interpolation::*;
}

#[cfg(feature = "prediction")]
pub mod prediction {
    pub use lightyear_prediction::*;
}

#[cfg(feature = "steam")]
pub mod steam {
    pub use lightyear_steam::*;
}

#[cfg(feature = "webtransport")]
pub mod webtransport {
    pub use lightyear_webtransport::*;
}

#[cfg(feature = "websocket")]
pub mod websocket {
    pub use lightyear_websocket::*;
}

#[cfg(feature = "avian2d")]
pub mod avian2d {
    pub use lightyear_avian2d::*;
}

#[cfg(feature = "avian3d")]
pub mod avian3d {
    pub use lightyear_avian3d::*;
}

#[cfg(any(feature = "input_native", feature = "leafwing", feature = "input_bei"))]
pub mod input {
    pub use lightyear_inputs::*;
    #[cfg(feature = "input_native")]
    pub mod native {
        pub use lightyear_inputs_native::*;
    }

    #[cfg(feature = "input_bei")]
    pub mod bei {
        pub use lightyear_inputs_bei::*;
    }

    #[cfg(feature = "leafwing")]
    pub mod leafwing {
        pub use lightyear_inputs_leafwing::*;
    }
}

pub mod connection {
    pub use lightyear_connection::*;
}

pub mod utils {
    pub use lightyear_utils::*;
}

pub mod prelude {
    pub use aeronet_io::connection::{LocalAddr, PeerAddr};
    pub use lightyear_connection::prelude::*;
    pub use lightyear_core::prelude::*;
    pub use lightyear_link::prelude::*;
    pub use lightyear_messages::prelude::*;
    #[cfg(feature = "metrics")]
    pub use lightyear_metrics::prelude::*;
    #[cfg(feature = "replication")]
    pub use lightyear_replication::prelude::*;
    pub use lightyear_serde::prelude::*;
    pub use lightyear_sync::prelude::*;
    pub use lightyear_transport::prelude::*;

    #[cfg(all(not(target_family = "wasm"), feature = "udp"))]
    pub use lightyear_udp::prelude::*;

    #[allow(unused_imports)]
    #[cfg(feature = "webtransport")]
    pub use lightyear_webtransport::prelude::*;

    #[allow(unused_imports)]
    #[cfg(feature = "websocket")]
    pub use lightyear_websocket::prelude::*;

    #[cfg(feature = "steam")]
    pub use lightyear_steam::prelude::*;

    #[cfg(feature = "netcode")]
    pub use lightyear_netcode::prelude::*;

    // TODO: maybe put this in prelude::client?
    #[cfg(feature = "prediction")]
    pub use lightyear_prediction::prelude::*;

    #[cfg(feature = "interpolation")]
    pub use lightyear_interpolation::prelude::*;

    #[cfg(feature = "debug")]
    pub use lightyear_ui::prelude::*;

    #[cfg(any(feature = "input_native", feature = "leafwing", feature = "input_bei"))]
    pub mod input {
        pub use lightyear_inputs::prelude::*;

        #[cfg(feature = "input_native")]
        pub mod native {
            pub use lightyear_inputs_native::prelude::*;
        }

        #[cfg(feature = "input_bei")]
        pub mod bei {
            pub use lightyear_inputs_bei::prelude::*;
        }
        #[cfg(feature = "input_bei")]
        pub use lightyear_inputs_bei::prelude::InputRegistryExt;

        #[cfg(feature = "leafwing")]
        pub mod leafwing {
            pub use lightyear_inputs_leafwing::prelude::*;
        }
    }

    #[cfg(feature = "client")]
    pub mod client {
        pub use crate::client::ClientPlugins;

        pub use lightyear_connection::prelude::client::*;
        pub use lightyear_sync::prelude::client::*;

        #[cfg(feature = "netcode")]
        pub use lightyear_netcode::prelude::client::*;
        #[cfg(feature = "raw_connection")]
        pub use lightyear_raw_connection::prelude::client::*;
        #[cfg(feature = "steam")]
        pub use lightyear_steam::prelude::client::*;
        #[cfg(feature = "websocket")]
        pub use lightyear_websocket::prelude::client::*;
        #[cfg(feature = "webtransport")]
        pub use lightyear_webtransport::prelude::client::*;

        #[cfg(any(feature = "input_native", feature = "leafwing", feature = "input_bei"))]
        pub mod input {
            pub use lightyear_inputs::prelude::client::*;
        }
    }

    #[cfg(feature = "server")]
    pub mod server {
        pub use crate::server::ServerPlugins;
        pub use lightyear_connection::prelude::server::*;
        pub use lightyear_link::prelude::server::*;

        #[cfg(all(not(target_family = "wasm"), feature = "udp", feature = "server"))]
        pub use lightyear_udp::prelude::server::*;

        #[cfg(feature = "netcode")]
        pub use lightyear_netcode::prelude::server::*;
        #[cfg(feature = "raw_connection")]
        pub use lightyear_raw_connection::prelude::server::*;
        #[cfg(feature = "steam")]
        pub use lightyear_steam::prelude::server::*;
        #[cfg(all(feature = "websocket", not(target_family = "wasm")))]
        pub use lightyear_websocket::prelude::server::*;
        #[cfg(all(feature = "webtransport", not(target_family = "wasm")))]
        pub use lightyear_webtransport::prelude::server::*;

        #[cfg(any(feature = "input_native", feature = "leafwing", feature = "input_bei"))]
        pub mod input {
            pub use lightyear_inputs::prelude::server::*;
        }
    }
}