├── book ├── .gitignore ├── src │ ├── guides │ │ └── title.md │ ├── appendix │ │ └── title.md │ ├── concepts │ │ ├── bevy_integration │ │ │ ├── title.md │ │ │ ├── events.md │ │ │ ├── server.md │ │ │ ├── client │ │ │ │ ├── title.md │ │ │ │ └── time_sync.md │ │ │ └── shared_plugin.md │ │ ├── advanced_replication │ │ │ ├── title.md │ │ │ ├── component_sync_mode.md │ │ │ └── inputs.md │ │ ├── replication │ │ │ └── title.md │ │ ├── reliability │ │ │ ├── title.md │ │ │ └── channels.md │ │ ├── transport │ │ │ ├── title.md │ │ │ ├── serialization.md │ │ │ └── packet.md │ │ ├── connection │ │ │ └── multi_connection.md │ │ └── title.md │ ├── tutorial │ │ └── title.md │ └── introduction.md ├── theme │ └── head.hbs ├── book.toml ├── CONTRIBUTING.md └── mermaid-init.js ├── .dockerignore ├── benches ├── criterion │ ├── src │ │ ├── measurements │ │ │ └── mod.rs │ │ ├── lib.rs │ │ └── replication_profiling.rs │ ├── benches │ │ ├── bandwidth │ │ │ └── main.rs │ │ └── timing │ │ │ └── main.rs │ ├── bench.sh │ ├── justfile │ └── Cargo.toml ├── divan │ ├── src │ │ ├── lib.rs │ │ └── message.rs │ ├── bench.sh │ └── Cargo.toml └── compiletime │ ├── run.sh │ └── Cargo.toml ├── certificates ├── digest.txt ├── key.pem ├── generate.sh └── cert.pem ├── lightyear_tests ├── src │ ├── multi_server │ │ └── mod.rs │ ├── client_server │ │ ├── input │ │ │ └── mod.rs │ │ ├── avian │ │ │ └── mod.rs │ │ ├── mod.rs │ │ └── connection.rs │ ├── host_server │ │ ├── input │ │ │ └── mod.rs │ │ └── mod.rs │ ├── lib.rs │ └── bin │ │ └── replicate.rs └── justfile ├── lightyear_replication └── src │ ├── visibility │ ├── mod.rs │ └── error.rs │ ├── send │ └── mod.rs │ ├── impls │ ├── avian3d.rs │ └── avian2d.rs │ ├── error.rs │ └── registry │ └── mod.rs ├── lightyear_deterministic_replication ├── src │ ├── messages.rs │ ├── plugin.rs │ └── lib.rs └── Cargo.toml ├── lightyear_core ├── src │ ├── network.rs │ ├── test.rs │ ├── plugin.rs │ ├── prediction.rs │ ├── interpolation.rs │ └── lib.rs └── Cargo.toml ├── lightyear_prediction ├── src │ └── deterministic.rs └── Cargo.toml ├── .gitignore ├── lightyear_avian └── src │ ├── lag_compensation │ └── mod.rs │ └── lib.rs ├── examples ├── simple_box │ ├── src │ │ ├── lib.rs │ │ ├── renderer.rs │ │ └── shared.rs │ ├── index.html │ ├── .cargo │ │ └── config.toml │ └── Cargo.toml ├── bevy_enhanced_inputs │ ├── src │ │ ├── lib.rs │ │ └── protocol.rs │ ├── index.html │ ├── .cargo │ │ └── config.toml │ ├── Cargo.toml │ └── README.md ├── auth │ ├── index.html │ ├── src │ │ └── shared.rs │ ├── .cargo │ │ └── config.toml │ └── Cargo.toml ├── fps │ ├── index.html │ ├── .cargo │ │ └── config.toml │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── lobby │ ├── index.html │ ├── .cargo │ │ └── config.toml │ ├── src │ │ ├── shared.rs │ │ └── renderer.rs │ └── Cargo.toml ├── priority │ ├── index.html │ ├── .cargo │ │ └── config.toml │ ├── src │ │ ├── shared.rs │ │ └── main.rs │ └── Cargo.toml ├── avian_physics │ ├── index.html │ ├── .cargo │ │ └── config.toml │ └── Cargo.toml ├── projectiles │ ├── index.html │ ├── .cargo │ │ └── config.toml │ └── Cargo.toml ├── avian_3d_character │ ├── index.html │ └── Cargo.toml ├── client_replication │ ├── index.html │ ├── .cargo │ │ └── config.toml │ ├── src │ │ └── renderer.rs │ └── Cargo.toml ├── delta_compression │ ├── index.html │ ├── .cargo │ │ └── config.toml │ ├── src │ │ ├── shared.rs │ │ └── renderer.rs │ └── Cargo.toml ├── network_visibility │ ├── index.html │ ├── .cargo │ │ └── config.toml │ ├── src │ │ ├── shared.rs │ │ └── renderer.rs │ ├── Cargo.toml │ └── README.md ├── replication_groups │ ├── index.html │ ├── .cargo │ │ └── config.toml │ ├── Cargo.toml │ └── src │ │ ├── main.rs │ │ └── renderer.rs ├── distributed_authority │ ├── index.html │ ├── .cargo │ │ └── config.toml │ ├── Cargo.toml │ └── src │ │ └── renderer.rs ├── deterministic_replication │ ├── index.html │ ├── .cargo │ │ └── config.toml │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── server.rs ├── simple_setup │ ├── .cargo │ │ └── config.toml │ ├── Cargo.toml │ ├── src │ │ ├── shared.rs │ │ └── client.rs │ └── README.md ├── common │ ├── src │ │ └── shared.rs │ └── www │ │ └── index.html └── launcher │ ├── README.md │ └── Cargo.toml ├── lightyear_ui ├── src │ └── lib.rs └── Cargo.toml ├── lightyear_connection ├── src │ ├── direction.rs │ ├── client_of.rs │ ├── identity.rs │ └── shared.rs └── Cargo.toml ├── demos └── spaceships │ ├── index.html │ ├── .cargo │ └── config.toml │ └── Cargo.toml ├── lightyear_utils ├── src │ ├── captures.rs │ ├── lib.rs │ └── collections.rs └── Cargo.toml ├── lightyear_transport ├── src │ ├── channel │ │ ├── receivers │ │ │ └── error.rs │ │ ├── mod.rs │ │ ├── stats.rs │ │ └── README.md │ ├── packet │ │ ├── error.rs │ │ ├── packet_type.rs │ │ └── mod.rs │ ├── error.rs │ ├── lib.rs │ ├── client.rs │ └── server.rs └── Cargo.toml ├── .github ├── dependabot.yml ├── FUNDING.yml ├── workflows │ └── website.yaml └── actions │ └── install-linux-deps │ └── action.yml ├── lightyear_sync ├── src │ ├── server.rs │ ├── ping │ │ ├── mod.rs │ │ └── store.rs │ ├── timeline │ │ └── mod.rs │ └── plugin.rs └── Cargo.toml ├── .cargo ├── config_ci.toml └── config.toml ├── lightyear_netcode └── src │ └── bytes.rs ├── lightyear_metrics ├── src │ └── lib.rs └── Cargo.toml ├── lightyear_inputs_bei ├── src │ └── lib.rs └── Cargo.toml ├── lightyear_aeronet └── Cargo.toml ├── lightyear_link └── Cargo.toml ├── lightyear_crossbeam └── Cargo.toml ├── lightyear_udp └── Cargo.toml ├── lightyear_raw_connection ├── Cargo.toml └── src │ └── lib.rs ├── lightyear_inputs_native ├── Cargo.toml └── src │ └── plugin.rs ├── lightyear_frame_interpolation └── Cargo.toml ├── lightyear_interpolation ├── src │ ├── despawn.rs │ └── lib.rs ├── CHANGELOG.md └── Cargo.toml ├── LICENSE-MIT ├── lightyear_serde └── Cargo.toml ├── lightyear_webtransport ├── src │ └── lib.rs └── Cargo.toml ├── lightyear_inputs_leafwing └── Cargo.toml ├── lightyear_websocket ├── src │ └── lib.rs └── Cargo.toml ├── .pre-commit-config.yaml ├── lightyear_steam └── Cargo.toml ├── lightyear └── src │ └── web.rs ├── lightyear_messages ├── Cargo.toml └── src │ └── client.rs ├── lightyear_inputs ├── src │ └── lib.rs └── Cargo.toml ├── lightyear_avian2d └── Cargo.toml ├── lightyear_avian3d └── Cargo.toml └── CHANGELOG.md /book/.gitignore: -------------------------------------------------------------------------------- 1 | booka 2 | -------------------------------------------------------------------------------- /book/src/guides/title.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .git/ 3 | -------------------------------------------------------------------------------- /book/src/appendix/title.md: -------------------------------------------------------------------------------- 1 | # Appendix 2 | -------------------------------------------------------------------------------- /book/src/concepts/bevy_integration/title.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /book/src/concepts/advanced_replication/title.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /book/src/concepts/bevy_integration/events.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /book/src/concepts/bevy_integration/server.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /book/src/concepts/bevy_integration/client/title.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /book/src/concepts/bevy_integration/shared_plugin.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /book/src/concepts/bevy_integration/client/time_sync.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /book/src/concepts/replication/title.md: -------------------------------------------------------------------------------- 1 | # Replication 2 | -------------------------------------------------------------------------------- /book/src/concepts/advanced_replication/component_sync_mode.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /benches/criterion/src/measurements/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bandwidth; 2 | -------------------------------------------------------------------------------- /certificates/digest.txt: -------------------------------------------------------------------------------- 1 | 2E6DF3B559DEB469CD238CA56A903310502DE781427D758BBA0E3B2801556704 -------------------------------------------------------------------------------- /lightyear_tests/src/multi_server/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "steam")] 2 | mod steam; 3 | -------------------------------------------------------------------------------- /lightyear_tests/src/client_server/input/mod.rs: -------------------------------------------------------------------------------- 1 | mod bei; 2 | mod leafwing; 3 | mod native; 4 | -------------------------------------------------------------------------------- /lightyear_tests/src/host_server/input/mod.rs: -------------------------------------------------------------------------------- 1 | mod bei; 2 | mod leafwing; 3 | mod native; 4 | -------------------------------------------------------------------------------- /lightyear_tests/src/host_server/mod.rs: -------------------------------------------------------------------------------- 1 | mod base; 2 | 3 | mod messages; 4 | 5 | mod input; 6 | -------------------------------------------------------------------------------- /lightyear_replication/src/visibility/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod immediate; 2 | 3 | pub mod error; 4 | pub mod room; 5 | -------------------------------------------------------------------------------- /lightyear_tests/src/client_server/avian/mod.rs: -------------------------------------------------------------------------------- 1 | mod position_replication; 2 | mod transform_replication; 3 | -------------------------------------------------------------------------------- /lightyear_deterministic_replication/src/messages.rs: -------------------------------------------------------------------------------- 1 | // pub enum ConnectionEvent { 2 | // Connected(PeerId) 3 | // Disconnected 4 | // } 5 | -------------------------------------------------------------------------------- /lightyear_core/src/network.rs: -------------------------------------------------------------------------------- 1 | // TODO: use newtype instead of u16 2 | /// ID used to serialize IDs over the network efficiently 3 | pub type NetId = u16; 4 | -------------------------------------------------------------------------------- /benches/criterion/benches/bandwidth/main.rs: -------------------------------------------------------------------------------- 1 | use criterion::criterion_main; 2 | 3 | mod replication; 4 | 5 | criterion_main!(replication::replication_bandwidth); 6 | -------------------------------------------------------------------------------- /lightyear_replication/src/send/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod archetypes; 2 | 3 | pub(crate) mod buffer; 4 | 5 | pub mod components; 6 | 7 | pub mod plugin; 8 | 9 | pub mod sender; 10 | -------------------------------------------------------------------------------- /lightyear_prediction/src/deterministic.rs: -------------------------------------------------------------------------------- 1 | //! Prediction primitives when we run in deterministic replication mode (only inputs are replicated) 2 | 3 | struct DeterministicPredicted; 4 | -------------------------------------------------------------------------------- /book/theme/head.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /.idea 4 | /examples/**/dist/ 5 | .DS_Store 6 | /examples/target/ 7 | /examples/Cargo.lock 8 | .aider* 9 | **/CL1 10 | **/CL2 11 | **/SL 12 | **/HL 13 | .vscode 14 | -------------------------------------------------------------------------------- /benches/criterion/benches/timing/main.rs: -------------------------------------------------------------------------------- 1 | use criterion::criterion_main; 2 | 3 | mod message; 4 | 5 | mod replication; 6 | 7 | criterion_main!(message::message_benches, replication::replication_benches,); 8 | -------------------------------------------------------------------------------- /lightyear_avian/src/lag_compensation/mod.rs: -------------------------------------------------------------------------------- 1 | //! Provides helpers to perform lag compensation so that client-predicted entities 2 | //! can interact with interpolated entities. 3 | 4 | pub mod history; 5 | pub mod query; 6 | -------------------------------------------------------------------------------- /lightyear_core/src/test.rs: -------------------------------------------------------------------------------- 1 | use bevy_ecs::component::Component; 2 | 3 | #[derive(Component, Default)] 4 | pub struct TestHelper { 5 | /// If True, we will drop the packets at the IO layer 6 | pub block_send: bool, 7 | } 8 | -------------------------------------------------------------------------------- /lightyear_replication/src/visibility/error.rs: -------------------------------------------------------------------------------- 1 | use bevy_ecs::entity::Entity; 2 | 3 | #[derive(thiserror::Error, Debug)] 4 | pub enum NetworkVisibilityError { 5 | #[error("room {0:?} was not found")] 6 | RoomNotFound(Entity), 7 | } 8 | -------------------------------------------------------------------------------- /examples/simple_box/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod protocol; 2 | 3 | #[cfg(feature = "client")] 4 | pub mod client; 5 | 6 | #[cfg(feature = "server")] 7 | pub mod server; 8 | 9 | #[cfg(feature = "gui")] 10 | pub mod renderer; 11 | 12 | pub mod shared; 13 | -------------------------------------------------------------------------------- /lightyear_ui/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Lightyear UI 2 | #![no_std] 3 | 4 | extern crate alloc; 5 | #[cfg(feature = "std")] 6 | extern crate std; 7 | 8 | pub mod debug; 9 | 10 | pub mod prelude { 11 | pub use crate::debug::DebugUIPlugin; 12 | } 13 | -------------------------------------------------------------------------------- /examples/bevy_enhanced_inputs/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod protocol; 2 | 3 | #[cfg(feature = "client")] 4 | pub mod client; 5 | 6 | #[cfg(feature = "server")] 7 | pub mod server; 8 | 9 | #[cfg(feature = "gui")] 10 | pub mod renderer; 11 | 12 | pub mod shared; 13 | -------------------------------------------------------------------------------- /lightyear_connection/src/direction.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy, PartialEq, Debug)] 2 | /// [`NetworkDirection`] specifies in which direction the packets can be sent 3 | pub enum NetworkDirection { 4 | ClientToServer, 5 | ServerToClient, 6 | Bidirectional, 7 | } 8 | -------------------------------------------------------------------------------- /certificates/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg0nCfP5pPE6Xiafwq 3 | RH8hi4vtRxBsDwhMkKHE9gLcs2+hRANCAAR/6+ya3qAD8UWQKosenUO8XZckDFg4 4 | sRvmav9xL1EbKKBdLAijzMpptUg8Jdlfz4Sk0UFA8HhZ2rzv7SWyuw6C 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /lightyear_tests/src/client_server/mod.rs: -------------------------------------------------------------------------------- 1 | mod base; 2 | 3 | mod authority; 4 | mod avian; 5 | mod connection; 6 | mod delta; 7 | mod hierarchy; 8 | mod input; 9 | mod messages; 10 | mod prediction; 11 | mod replication; 12 | mod replication_advanced; 13 | mod visibility; 14 | -------------------------------------------------------------------------------- /examples/auth/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/fps/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /demos/spaceships/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/lobby/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/priority/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /benches/divan/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate contains benchmarking tests for the Lightyear project, using Divan. 2 | //! It includes modules for profiling and protocol-specific benchmarks. 3 | #![allow(clippy::all)] 4 | #![allow(unused_imports)] 5 | #![allow(unused_variables)] 6 | #![allow(dead_code)] 7 | -------------------------------------------------------------------------------- /examples/avian_physics/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/projectiles/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/simple_box/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/avian_3d_character/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/client_replication/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/delta_compression/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/network_visibility/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/replication_groups/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/bevy_enhanced_inputs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/distributed_authority/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /benches/compiletime/run.sh: -------------------------------------------------------------------------------- 1 | # Rebuild the compiletime crate without rebuilding the dependencies 2 | # Measure the time it takes to compile the crate 3 | rm -rf "target/debug/.fingerprint/compiletime*" && rm -rf "target/debug/incremental/compiletime*" && rm -rf "target/debug/compiletime*" && cargo build -p compiletime -------------------------------------------------------------------------------- /examples/deterministic_replication/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bevy game 7 | 8 | 9 | -------------------------------------------------------------------------------- /lightyear_utils/src/captures.rs: -------------------------------------------------------------------------------- 1 | //! Captures trick to allow for better impl Iterator + 'a 2 | //! See `` 3 | //! and `` 4 | 5 | pub trait Captures {} 6 | impl Captures for T {} 7 | -------------------------------------------------------------------------------- /lightyear_transport/src/channel/receivers/error.rs: -------------------------------------------------------------------------------- 1 | //! Errors for receiving packets 2 | 3 | pub type Result = core::result::Result; 4 | #[derive(thiserror::Error, Debug)] 5 | pub enum ChannelReceiveError { 6 | #[error("A message was received without a message ID")] 7 | MissingMessageId, 8 | } 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | labels: 8 | - "dependencies" 9 | - package-ecosystem: github-actions 10 | directory: / 11 | schedule: 12 | interval: weekly 13 | labels: 14 | - "github actions" -------------------------------------------------------------------------------- /benches/criterion/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate contains benchmarking tests for the Lightyear project, using Criterion. 2 | //! It includes modules for profiling and protocol-specific benchmarks. 3 | #![allow(clippy::all)] 4 | #![allow(unused_imports)] 5 | #![allow(unused_variables)] 6 | #![allow(dead_code)] 7 | pub mod measurements; 8 | pub mod profiler; 9 | -------------------------------------------------------------------------------- /lightyear_sync/src/server.rs: -------------------------------------------------------------------------------- 1 | use bevy_app::{App, Plugin}; 2 | 3 | use crate::plugin::TimelineSyncPlugin; 4 | 5 | pub struct ServerPlugin; 6 | 7 | impl Plugin for ServerPlugin { 8 | fn build(&self, app: &mut App) { 9 | if !app.is_plugin_added::() { 10 | app.add_plugins(TimelineSyncPlugin); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.cargo/config_ci.toml: -------------------------------------------------------------------------------- 1 | # This config is used for the CI workflow. 2 | 3 | # Enable a small amount of optimization in the dev profile. 4 | [profile.dev] 5 | opt-level = 1 6 | 7 | # Enable a large amount of optimization in the dev profile for dependencies. 8 | [profile.dev.package."*"] 9 | opt-level = 3 10 | 11 | [target.x86_64-pc-windows-msvc] 12 | linker = "rust-lld.exe" 13 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Charles Bournhonesque"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Lightyear book" 7 | 8 | [preprocessor] 9 | 10 | [preprocessor.mermaid] 11 | command = "mdbook-mermaid" 12 | 13 | [output] 14 | 15 | #[output.linkcheck] 16 | 17 | [output.html] 18 | additional-js = ["mermaid.min.js", "mermaid-init.js"] 19 | site-url = "/book/" 20 | -------------------------------------------------------------------------------- /lightyear_netcode/src/bytes.rs: -------------------------------------------------------------------------------- 1 | use lightyear_serde::reader::ReadInteger; 2 | use lightyear_serde::writer::WriteInteger; 3 | 4 | pub trait Bytes: Sized { 5 | const SIZE: usize = core::mem::size_of::(); 6 | type Error; 7 | fn write_to(&self, writer: &mut impl WriteInteger) -> Result<(), Self::Error>; 8 | fn read_from(reader: &mut impl ReadInteger) -> Result; 9 | } 10 | -------------------------------------------------------------------------------- /examples/auth/src/shared.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the shared code between the client and the server for the auth example. 2 | use core::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; 3 | 4 | // Define a shared port for the authentication backend 5 | pub const AUTH_BACKEND_PORT: u16 = 4000; 6 | 7 | pub const AUTH_BACKEND_ADDRESS: SocketAddr = 8 | SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, AUTH_BACKEND_PORT)); 9 | -------------------------------------------------------------------------------- /lightyear_deterministic_replication/src/plugin.rs: -------------------------------------------------------------------------------- 1 | use crate::Deterministic; 2 | use bevy_app::{App, Plugin}; 3 | use lightyear_prediction::rollback::DeterministicPredicted; 4 | 5 | pub struct DeterministicReplicationPlugin; 6 | 7 | impl Plugin for DeterministicReplicationPlugin { 8 | fn build(&self, app: &mut App) { 9 | app.register_required_components::(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lightyear_sync/src/ping/mod.rs: -------------------------------------------------------------------------------- 1 | //! Module to handle sending ping/pong messages and compute connection statistics (rtt, jitter, etc.) 2 | 3 | pub mod manager; 4 | 5 | pub mod message; 6 | 7 | pub mod diagnostics; 8 | pub mod estimator; 9 | pub mod plugin; 10 | pub mod store; 11 | 12 | /// Default channel to send pings. This is a Sequenced Unreliable channel, because 13 | /// there is no point in getting older pings. 14 | pub struct PingChannel; 15 | -------------------------------------------------------------------------------- /lightyear_utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Contains a set of useful utilities 2 | 3 | #![no_std] 4 | 5 | extern crate alloc; 6 | extern crate core; 7 | #[cfg(feature = "std")] 8 | extern crate std; 9 | 10 | pub mod free_list; 11 | 12 | pub mod ready_buffer; 13 | 14 | pub mod sequence_buffer; 15 | 16 | pub mod captures; 17 | pub mod collections; 18 | 19 | pub mod ecs; 20 | pub mod registry; 21 | pub mod wrapping_id; 22 | 23 | #[cfg(feature = "metrics")] 24 | pub mod metrics; 25 | -------------------------------------------------------------------------------- /lightyear_sync/src/timeline/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy_ecs::component::Component; 2 | 3 | pub mod input; 4 | pub mod remote; 5 | pub mod sync; 6 | 7 | /// Marker component to identity the timeline that will update the bevy app. 8 | /// 9 | /// [`Time`](bevy_time::Time) will be updated according to the driving timeline's relative_speed. 10 | #[derive(Component, Default)] 11 | pub struct DrivingTimeline { 12 | pub marker: core::marker::PhantomData, 13 | } 14 | -------------------------------------------------------------------------------- /benches/compiletime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "compiletime" 3 | version.workspace = true 4 | authors.workspace = true 5 | publish.workspace = true 6 | edition.workspace = true 7 | rust-version.workspace = true 8 | license.workspace = true 9 | 10 | [dependencies] 11 | lightyear = { workspace = true, features = ["replication"] } 12 | bevy.workspace = true 13 | serde.workspace = true 14 | 15 | [lints] 16 | workspace = true 17 | 18 | [[bin]] 19 | name = "compiletime" 20 | doc = false 21 | -------------------------------------------------------------------------------- /benches/divan/bench.sh: -------------------------------------------------------------------------------- 1 | # Run the benchmark and generate a flamegraph. 2 | # The env variables are needed to have debug symbols even in release mode. 3 | CARGO_PROFILE_RELEASE_DEBUG=true RUSTFLAGS='-C force-frame-pointers=y' cargo bench --bench=replication --profile=release -- send_float_insert/1 --nocapture --profile-time=10 4 | 5 | # Run the flamegraph separately 6 | CARGO_PROFILE_RELEASE_DEBUG=true RUSTFLAGS='-C force-frame-pointers=y' cargo flamegraph --root --bin=replication_profiling --profile=release -------------------------------------------------------------------------------- /lightyear_metrics/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Lightyear UI 2 | #![no_std] 3 | 4 | extern crate alloc; 5 | #[cfg(feature = "std")] 6 | extern crate std; 7 | 8 | pub mod plugin; 9 | pub mod registry; 10 | 11 | pub use metrics; 12 | pub use metrics_util; 13 | 14 | pub mod prelude { 15 | pub use crate::plugin::{ClearBucketsSystem, MetricsPlugin}; 16 | #[cfg(feature = "std")] 17 | pub use crate::registry::GLOBAL_RECORDER; 18 | 19 | pub use crate::registry::{MetricsRegistry, SearchResult}; 20 | } 21 | -------------------------------------------------------------------------------- /benches/criterion/bench.sh: -------------------------------------------------------------------------------- 1 | # Run the benchmark and generate a flamegraph. 2 | # The env variables are needed to have debug symbols even in release mode. 3 | CARGO_PROFILE_RELEASE_DEBUG=true RUSTFLAGS='-C force-frame-pointers=y' cargo bench --bench=replication --profile=release -- send_float_insert/1 --nocapture --profile-time=10 4 | 5 | # Run the flamegraph separately 6 | CARGO_PROFILE_RELEASE_DEBUG=true RUSTFLAGS='-C force-frame-pointers=y' cargo flamegraph --root --bin=replication_profiling --profile=release 7 | 8 | # -------------------------------------------------------------------------------- /certificates/generate.sh: -------------------------------------------------------------------------------- 1 | # Generates a self-signed certificate valid for 14 days, to use for webtransport 2 | # run this from the root folder 3 | OUT=certificates 4 | openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -keyout $OUT/key.pem -out $OUT/cert.pem -days 14 -nodes -subj "/CN=localhost" 5 | 6 | FINGERPRINT=$(openssl x509 -in "$OUT/cert.pem" -noout -sha256 -fingerprint | sed 's/^.*=//' | sed 's/://g') 7 | printf '%s' "$FINGERPRINT" > $OUT/digest.txt 8 | 9 | echo "Wrote new fingerprint $FINGERPRINT to $OUT/digest.txt" -------------------------------------------------------------------------------- /examples/auth/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /examples/fps/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /demos/spaceships/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /examples/lobby/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /examples/priority/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /lightyear_utils/src/collections.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused)] 2 | // these are wrappers around HashMap and HashSet that use the EntityHasher 3 | pub use bevy_ecs::entity::{hash_map::EntityHashMap, hash_set::EntityHashSet}; 4 | 5 | use bevy_platform::hash::FixedHasher; 6 | 7 | // bevy's HashMap is `hashbrown::HashMap` which causes issues with type inference 8 | // Adding this type alias to help with inference 9 | pub type HashMap = hashbrown::HashMap; 10 | pub type HashSet = hashbrown::HashSet; 11 | -------------------------------------------------------------------------------- /book/src/tutorial/title.md: -------------------------------------------------------------------------------- 1 | # Tutorial 2 | 3 | This section will teach you how to quickly setup networking in your bevy game using lightyear. 4 | 5 | You can find many examples in the [examples](https://github.com/cBournhonesque/lightyear/tree/main/examples) folder. 6 | 7 | In this tutorial, we will reproduce the [simple box example](https://github.com/cBournhonesque/lightyear/tree/main/examples/simple_box) that showcases 8 | how to: 9 | - setup a basic client/server app 10 | - replicate an entity from server to clients 11 | - add prediction and interpolation -------------------------------------------------------------------------------- /examples/avian_physics/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /examples/projectiles/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /examples/simple_box/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /examples/simple_setup/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /examples/client_replication/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /examples/delta_compression/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /examples/network_visibility/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /examples/replication_groups/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /lightyear_core/src/plugin.rs: -------------------------------------------------------------------------------- 1 | use crate::timeline::TimelinePlugin; 2 | use bevy_app::{App, Plugin}; 3 | use bevy_time::TimePlugin; 4 | use core::time::Duration; 5 | 6 | pub struct CorePlugins { 7 | pub tick_duration: Duration, 8 | } 9 | 10 | impl Plugin for CorePlugins { 11 | fn build(&self, app: &mut App) { 12 | if !app.is_plugin_added::() { 13 | app.add_plugins(TimePlugin); 14 | } 15 | app.add_plugins(TimelinePlugin { 16 | tick_duration: self.tick_duration, 17 | }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lightyear_tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_must_use)] 2 | /*! # Lightyear IO 3 | 4 | Low-level IO primitives for the lightyear networking library. 5 | This crate provides abstractions for sending and receiving raw bytes over the network. 6 | */ 7 | #![cfg_attr(not(feature = "std"), no_std)] 8 | 9 | extern crate alloc; 10 | extern crate core; 11 | 12 | #[cfg(test)] 13 | #[cfg(feature = "test_utils")] 14 | mod client_server; 15 | pub mod protocol; 16 | pub mod stepper; 17 | 18 | #[cfg(test)] 19 | mod host_server; 20 | 21 | #[cfg(test)] 22 | mod multi_server; 23 | -------------------------------------------------------------------------------- /examples/bevy_enhanced_inputs/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /examples/distributed_authority/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /examples/deterministic_replication/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # it is a good idea to specify your target here, to avoid losing incremental compilation when compiling to wasm 3 | # target = "aarch64-apple-darwin" 4 | rustflags = ["--cfg", "web_sys_unstable_apis"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | runner = "wasm-server-runner" 8 | 9 | # Enable max optimizations for dependencies, but not for our code: 10 | [profile.dev.package."*"] 11 | opt-level = 3 12 | 13 | # Enable only a small amount of optimization in debug mode 14 | [profile.dev] 15 | opt-level = 1 16 | -------------------------------------------------------------------------------- /lightyear_transport/src/packet/error.rs: -------------------------------------------------------------------------------- 1 | //! Errors for building packets 2 | 3 | use crate::channel::receivers::error::ChannelReceiveError; 4 | use lightyear_serde::SerializationError; 5 | 6 | pub type Result = core::result::Result; 7 | #[derive(thiserror::Error, Debug)] 8 | pub enum PacketError { 9 | #[error("serialization error: {0}")] 10 | Serialization(#[from] SerializationError), 11 | #[error("channel was not found")] 12 | ChannelNotFound, 13 | #[error("receiver channel error: {0}")] 14 | ChannelReceiveError(#[from] ChannelReceiveError), 15 | } 16 | -------------------------------------------------------------------------------- /book/src/concepts/reliability/title.md: -------------------------------------------------------------------------------- 1 | # Reliability 2 | 3 | In this layer we add some mechanisms to be able to send and receive messages reliably or in a given order. 4 | 5 | It is similar to the [reliable](https://github.com/networkprotocol/reliable) layer created by Glenn Fiedler on top of his netcode.io code. 6 | 7 | This layer introduces: 8 | - reliability: make sure a packets is received by the remote peer 9 | - ordering: make sure packets are received in the same order they were sent 10 | - channels: allow to send packets on different channels, which can have different reliability and ordering guarantees 11 | 12 | -------------------------------------------------------------------------------- /lightyear_inputs_bei/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Lightyear Inputs BEI 2 | //! 3 | //! This crate provides an integration between `lightyear` and `bevy-enhanced-input`. 4 | #![no_std] 5 | 6 | extern crate alloc; 7 | extern crate core; 8 | #[cfg(feature = "std")] 9 | extern crate std; 10 | 11 | pub mod input_message; 12 | 13 | mod marker; 14 | mod plugin; 15 | mod setup; 16 | 17 | pub mod prelude { 18 | pub use crate::input_message::BEIBuffer; 19 | pub use crate::marker::InputMarker; 20 | pub use crate::plugin::InputPlugin; 21 | pub use crate::setup::InputRegistryExt; 22 | pub use bevy_enhanced_input::prelude::*; 23 | } 24 | -------------------------------------------------------------------------------- /certificates/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBfTCCASOgAwIBAgIUTp0sb9+yFqebXU5Dwq/5o1LCocowCgYIKoZIzj0EAwIw 3 | FDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MTAxMzIyMjU1N1oXDTI1MTAyNzIy 4 | MjU1N1owFDESMBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0D 5 | AQcDQgAEf+vsmt6gA/FFkCqLHp1DvF2XJAxYOLEb5mr/cS9RGyigXSwIo8zKabVI 6 | PCXZX8+EpNFBQPB4Wdq87+0lsrsOgqNTMFEwHQYDVR0OBBYEFP9hzrrzQYvAvzAh 7 | GDdfyUGlbg6IMB8GA1UdIwQYMBaAFP9hzrrzQYvAvzAhGDdfyUGlbg6IMA8GA1Ud 8 | EwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIgN+92ZlxxntlpwOEB9B6pvdvm 9 | HPR7tI1qPN83CZnWjaICIQDrISGOPTLXPJfbsUpcABO48k114AVDasILxT7G1ilb 10 | cw== 11 | -----END CERTIFICATE----- 12 | -------------------------------------------------------------------------------- /lightyear_transport/src/channel/mod.rs: -------------------------------------------------------------------------------- 1 | /*! Channels are used to add reliability/ordering on top of the transport layer 2 | */ 3 | 4 | pub use crate::channel::registry::ChannelKind; 5 | 6 | pub mod builder; 7 | pub mod receivers; 8 | pub mod senders; 9 | 10 | pub mod registry; 11 | #[cfg(feature = "trace")] 12 | pub mod stats; 13 | 14 | /// A Channel is used to specify some properties of how the bytes are sent over the network. 15 | /// 16 | /// The properties can be specified using the [`ChannelSettings`](crate::prelude::ChannelSettings). 17 | pub trait Channel: Send + Sync + 'static {} 18 | impl Channel for T {} 19 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | # we need to run all tests with a single thread otherwise the `mock_instant::global::Instant` gets 3 | # overwritten in parallel tests 4 | t = "test -- --test-threads=1" 5 | clip = "clippy --workspace --features=lightyear_core/not_mock --exclude=compiletime --exclude=avian_3d_character --exclude=launcher --exclude=delta_compression --exclude=distributed_authority --no-deps -- -D warnings -A clippy::needless_lifetimes" 6 | 7 | [unstable] 8 | profile-rustflags = true 9 | 10 | [profile.profiling] 11 | inherits = "release" 12 | # only works in nightly 13 | #rustflags = [ 14 | # "-C", 15 | # "force-frame-pointers=yes", 16 | # "-C", 17 | # "symbol-mangling-version=v0", 18 | #] 19 | debug = true 20 | -------------------------------------------------------------------------------- /lightyear_tests/src/client_server/connection.rs: -------------------------------------------------------------------------------- 1 | //! Check various replication scenarios between 2 peers only 2 | 3 | use crate::stepper::*; 4 | use bevy::prelude::{Entity, With}; 5 | use lightyear_connection::client_of::ClientOf; 6 | use test_log::test; 7 | 8 | #[test] 9 | fn test_disconnection() { 10 | let mut stepper = ClientServerStepper::from_config(StepperConfig::single()); 11 | 12 | stepper.disconnect_client(); 13 | 14 | // check that the client is not present in the server world 15 | assert!( 16 | stepper 17 | .server_app 18 | .world_mut() 19 | .query_filtered::>() 20 | .single(stepper.server_app.world()) 21 | .is_err() 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /lightyear_replication/src/impls/avian3d.rs: -------------------------------------------------------------------------------- 1 | use crate::delta::Diffable; 2 | use avian3d::prelude::{Position, Rotation}; 3 | 4 | impl Diffable for Position { 5 | fn base_value() -> Self { 6 | Position::default() 7 | } 8 | 9 | fn diff(&self, new: &Self) -> Self { 10 | Position::from(new.0 - self.0) 11 | } 12 | 13 | fn apply_diff(&mut self, delta: &Self) { 14 | self.0 += **delta; 15 | } 16 | } 17 | 18 | impl Diffable for Rotation { 19 | fn base_value() -> Self { 20 | Rotation::default() 21 | } 22 | 23 | fn diff(&self, new: &Self) -> Self { 24 | Rotation(new.0 * *self.inverse()) 25 | } 26 | 27 | fn apply_diff(&mut self, delta: &Self) { 28 | self.0 *= delta.0; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /book/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | To contribute to the book, you wil need `mdbook`, `mdbook-mermaid`, and `mdbook-linkcheck` installed. 4 | These can be installed through `cargo install` or through your package manager: 5 | 6 | ```sh 7 | cargo install mdbook mdbook-mermaid mdbook-linkcheck 8 | ``` 9 | 10 | They are also available as packages in Nix: 11 | 12 | ```sh 13 | nix-shell -p mdbook mdbook-mermaid mdbook-linkcheck 14 | ``` 15 | 16 | You will need to install `mdbook-mermaid`'s CSS/JS into the book's `book` directory: 17 | 18 | ```sh 19 | mdbook-mermaid install book 20 | ``` 21 | 22 | You can now serve the book locally: 23 | 24 | ```sh 25 | mdbook serve book 26 | ``` 27 | 28 | For more information, please consult the [mdBook documentation](https://rust-lang.github.io/mdBook/). -------------------------------------------------------------------------------- /lightyear_sync/src/plugin.rs: -------------------------------------------------------------------------------- 1 | use crate::ping::plugin::PingPlugin; 2 | use bevy_app::{App, Plugin, PostUpdate}; 3 | use bevy_ecs::schedule::SystemSet; 4 | 5 | #[deprecated(note = "Use SyncSystems instead")] 6 | pub type SyncSet = SyncSystems; 7 | 8 | #[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone, Copy)] 9 | pub enum SyncSystems { 10 | /// Sync SyncedTimelines to the Remote timelines using networking information (RTT/jitter) from the PingManager 11 | Sync, 12 | } 13 | 14 | pub struct TimelineSyncPlugin; 15 | 16 | impl Plugin for TimelineSyncPlugin { 17 | fn build(&self, app: &mut App) { 18 | if !app.is_plugin_added::() { 19 | app.add_plugins(PingPlugin); 20 | } 21 | app.configure_sets(PostUpdate, SyncSystems::Sync); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lightyear_replication/src/impls/avian2d.rs: -------------------------------------------------------------------------------- 1 | use crate::delta::Diffable; 2 | use avian2d::prelude::{Position, Rotation}; 3 | 4 | impl Diffable for Position { 5 | fn base_value() -> Self { 6 | Position::default() 7 | } 8 | 9 | fn diff(&self, new: &Self) -> Position { 10 | Position(new.0 - self.0) 11 | } 12 | 13 | fn apply_diff(&mut self, delta: &Position) { 14 | self.0 += **delta; 15 | } 16 | } 17 | 18 | impl Diffable for Rotation { 19 | fn base_value() -> Self { 20 | Rotation::default() 21 | } 22 | 23 | fn diff(&self, new: &Self) -> Rotation { 24 | Rotation::radians(self.angle_between(*new)) 25 | } 26 | 27 | fn apply_diff(&mut self, delta: &Rotation) { 28 | *self = self.add_angle_fast(delta.as_radians()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /book/src/concepts/transport/title.md: -------------------------------------------------------------------------------- 1 | # Transport 2 | 3 | The `Transport` trait is the trait that is used to send and receive raw data on the network. 4 | 5 | It is very general: 6 | 7 | ```rust,noplayground 8 | pub trait PacketSender: Send + Sync { 9 | /// Send data on the socket to the remote address 10 | fn send(&mut self, payload: &[u8], address: &SocketAddr) -> Result<()>; 11 | } 12 | pub trait PacketReceiver: Send + Sync { 13 | /// Receive a packet from the socket. Returns the data read and the origin. 14 | /// 15 | /// Returns Ok(None) if no data is available 16 | fn recv(&mut self) -> Result>; 17 | } 18 | ``` 19 | 20 | The trait currently has 4 implementations: 21 | 22 | - UDP sockets 23 | - WebTransport (using QUIC) 24 | - WebSocket 25 | - crossbeam-channels: used for internal testing 26 | -------------------------------------------------------------------------------- /lightyear_aeronet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_aeronet" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "IO primitives for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = ["lightyear_link/std"] 14 | test_utils = ["lightyear_core/test_utils"] 15 | 16 | [dependencies] 17 | lightyear_core.workspace = true 18 | lightyear_link.workspace = true 19 | 20 | aeronet_io.workspace = true 21 | 22 | tracing.workspace = true 23 | 24 | # bevy 25 | bevy_app.workspace = true 26 | bevy_ecs.workspace = true 27 | bevy_reflect.workspace = true 28 | 29 | [lints] 30 | workspace = true 31 | 32 | [package.metadata.docs.rs] 33 | all-features = true 34 | -------------------------------------------------------------------------------- /lightyear_link/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_link" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "IO primitives for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = [] 14 | test_utils = [] 15 | 16 | [dependencies] 17 | lightyear_core.workspace = true 18 | lightyear_utils.workspace = true 19 | 20 | # utils 21 | rand.workspace = true 22 | tracing.workspace = true 23 | bytes.workspace = true 24 | 25 | # bevy 26 | bevy_app.workspace = true 27 | bevy_ecs.workspace = true 28 | bevy_reflect.workspace = true 29 | bevy_utils.workspace = true 30 | 31 | [lints] 32 | workspace = true 33 | 34 | [package.metadata.docs.rs] 35 | all-features = true 36 | -------------------------------------------------------------------------------- /lightyear_replication/src/error.rs: -------------------------------------------------------------------------------- 1 | //! Replication-related errors 2 | 3 | use alloc::string::String; 4 | 5 | use crate::registry::ComponentError; 6 | use lightyear_messages::registry::MessageError; 7 | use lightyear_serde::SerializationError; 8 | use lightyear_transport::error::TransportError; 9 | 10 | pub type Result = core::result::Result; 11 | 12 | #[derive(thiserror::Error, Debug)] 13 | pub enum ReplicationError { 14 | #[error("DeltaCompressionError: {0}")] 15 | DeltaCompressionError(String), 16 | #[error(transparent)] 17 | Serialization(#[from] SerializationError), 18 | #[error(transparent)] 19 | MessageProtocolError(#[from] MessageError), 20 | #[error(transparent)] 21 | ComponentProtocolError(#[from] ComponentError), 22 | #[error(transparent)] 23 | TransportError(#[from] TransportError), 24 | } 25 | -------------------------------------------------------------------------------- /lightyear_transport/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::channel::ChannelKind; 2 | use crate::channel::receivers::error::ChannelReceiveError; 3 | use crate::packet::error::PacketError; 4 | use bytes::Bytes; 5 | use crossbeam_channel::TrySendError; 6 | 7 | pub type Result = core::result::Result; 8 | #[derive(thiserror::Error, Debug)] 9 | pub enum TransportError { 10 | #[error(transparent)] 11 | SerializationError(#[from] lightyear_serde::SerializationError), 12 | #[error(transparent)] 13 | PacketError(#[from] PacketError), 14 | #[error("channel {0:?} was not found")] 15 | ChannelNotFound(ChannelKind), 16 | #[error("receiver channel error: {0}")] 17 | ChannelReceiveError(#[from] ChannelReceiveError), 18 | #[error("error sending data: {0}")] 19 | ChannelSendError(#[from] TrySendError<(ChannelKind, Bytes, f32)>), 20 | } 21 | -------------------------------------------------------------------------------- /lightyear_crossbeam/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_crossbeam" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "IO primitives for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = [] 13 | test_utils = ["lightyear_core/test_utils"] 14 | 15 | [dependencies] 16 | lightyear_core.workspace = true 17 | lightyear_link.workspace = true 18 | 19 | # bevy 20 | bevy_app.workspace = true 21 | bevy_ecs.workspace = true 22 | 23 | # utils 24 | aeronet_io.workspace = true 25 | crossbeam-channel.workspace = true 26 | tracing.workspace = true 27 | 28 | # serde 29 | bytes.workspace = true 30 | 31 | [lints] 32 | workspace = true 33 | 34 | [package.metadata.docs.rs] 35 | all-features = true 36 | -------------------------------------------------------------------------------- /lightyear_transport/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! # Lightyear Packet 2 | 3 | Packet handling for the lightyear networking library. 4 | This crate builds up on top of lightyear-io, to add packet fragmentation, channels, and reliability. 5 | */ 6 | #![no_std] 7 | 8 | extern crate alloc; 9 | #[cfg(feature = "std")] 10 | extern crate std; 11 | 12 | pub mod channel; 13 | 14 | pub mod error; 15 | 16 | #[cfg(feature = "client")] 17 | mod client; 18 | pub mod packet; 19 | pub mod plugin; 20 | #[cfg(feature = "server")] 21 | mod server; 22 | 23 | pub mod prelude { 24 | pub use crate::channel::Channel; 25 | pub use crate::channel::builder::{ChannelMode, ChannelSettings, ReliableSettings, Transport}; 26 | pub use crate::channel::registry::AppChannelExt; 27 | pub use crate::channel::registry::ChannelRegistry; 28 | pub use crate::packet::priority_manager::{PriorityConfig, PriorityManager}; 29 | } 30 | -------------------------------------------------------------------------------- /examples/simple_box/src/renderer.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::*; 2 | use bevy::prelude::*; 3 | 4 | #[derive(Clone)] 5 | pub struct ExampleRendererPlugin; 6 | 7 | impl Plugin for ExampleRendererPlugin { 8 | fn build(&self, app: &mut App) { 9 | app.add_systems(Startup, init); 10 | app.add_systems(Update, draw_boxes); 11 | } 12 | } 13 | 14 | fn init(mut commands: Commands) { 15 | commands.spawn(Camera2d); 16 | } 17 | 18 | /// System that draws the boxes of the player positions. 19 | /// The components should be replicated from the server to the client 20 | pub(crate) fn draw_boxes(mut gizmos: Gizmos, players: Query<(&PlayerPosition, &PlayerColor)>) { 21 | for (position, color) in &players { 22 | gizmos.rect_2d( 23 | Isometry2d::from_translation(position.0), 24 | Vec2::ONE * 50.0, 25 | color.0, 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/delta_compression/src/shared.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the shared code between the client and the server. 2 | //! 3 | //! The simulation logic (movement, etc.) should be shared between client and server to guarantee that there won't be 4 | //! mispredictions/rollbacks. 5 | use bevy::prelude::*; 6 | 7 | use crate::protocol::*; 8 | 9 | // This system defines how we update the player's positions when we receive an input 10 | pub(crate) fn shared_movement_behaviour(mut position: Mut, input: &Inputs) { 11 | const MOVE_SPEED: f32 = 10.0; 12 | let Inputs::Direction(direction) = input; 13 | if direction.up { 14 | position.y += MOVE_SPEED; 15 | } 16 | if direction.down { 17 | position.y -= MOVE_SPEED; 18 | } 19 | if direction.left { 20 | position.x -= MOVE_SPEED; 21 | } 22 | if direction.right { 23 | position.x += MOVE_SPEED; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lightyear_utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_utils" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "Utils shared by lightyear crates" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = [] 13 | std = ["bevy_ecs/std", "bevy_platform/std", "bevy_reflect/std"] 14 | metrics = ["dep:metrics", "std"] 15 | 16 | [dependencies] 17 | # utils 18 | metrics = { workspace = true, optional = true } 19 | hashbrown.workspace = true 20 | paste.workspace = true 21 | seahash.workspace = true 22 | tracing.workspace = true 23 | 24 | # bevy 25 | bevy_ecs.workspace = true 26 | bevy_platform.workspace = true 27 | bevy_reflect.workspace = true 28 | bevy_utils.workspace = true 29 | 30 | [lints] 31 | workspace = true 32 | 33 | [package.metadata.docs.rs] 34 | all-features = true 35 | -------------------------------------------------------------------------------- /lightyear_metrics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_metrics" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "Metrics recorder lightyear" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = [] 14 | test_utils = [] 15 | 16 | [dependencies] 17 | # utils 18 | metrics.workspace = true 19 | metrics-util.workspace = true 20 | tracing.workspace = true 21 | 22 | # bevy 23 | bevy_app.workspace = true 24 | bevy_color.workspace = true 25 | bevy_ecs.workspace = true 26 | bevy_platform.workspace = true 27 | bevy_reflect.workspace = true 28 | bevy_ui.workspace = true 29 | bevy_utils.workspace = true 30 | bevy_text.workspace = true 31 | bevy_time.workspace = true 32 | 33 | [lints] 34 | workspace = true 35 | 36 | [package.metadata.docs.rs] 37 | all-features = true 38 | -------------------------------------------------------------------------------- /examples/delta_compression/src/renderer.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy::render::RenderPlugin; 3 | 4 | use crate::protocol::*; 5 | 6 | #[derive(Clone)] 7 | pub struct ExampleRendererPlugin; 8 | 9 | impl Plugin for ExampleRendererPlugin { 10 | fn build(&self, app: &mut App) { 11 | app.add_systems(Startup, init); 12 | app.add_systems(Update, draw_boxes); 13 | } 14 | } 15 | 16 | fn init(mut commands: Commands) { 17 | commands.spawn(Camera2d); 18 | } 19 | 20 | /// System that draws the boxes of the player positions. 21 | /// The components should be replicated from the server to the client 22 | pub(crate) fn draw_boxes(mut gizmos: Gizmos, mut players: Query<(&PlayerPosition, &PlayerColor)>) { 23 | for (position, color) in &mut players { 24 | gizmos.rect_2d( 25 | Isometry2d::from_translation(position.0), 26 | Vec2::ONE * 50.0, 27 | color.0, 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lightyear_core/src/prediction.rs: -------------------------------------------------------------------------------- 1 | use bevy_ecs::{component::Component, reflect::ReflectComponent}; 2 | use bevy_reflect::Reflect; 3 | 4 | /// Component added to client-side entities that are predicted. 5 | /// 6 | /// Prediction allows the client to simulate the game state locally without waiting for server confirmation, 7 | /// reducing perceived latency. This component links the predicted entity to its server-confirmed counterpart. 8 | /// 9 | /// When an entity is marked as `Predicted`, the `PredictionPlugin` will: 10 | /// - Store its component history. 11 | /// - Rollback and re-simulate the entity when a server correction is received. 12 | /// - Manage the relationship between the predicted entity and its corresponding confirmed entity received from the server. 13 | // NOTE: we create Predicted here because it is used by multiple crates (prediction, replication) 14 | #[derive(Component, Debug, Reflect)] 15 | #[reflect(Component)] 16 | pub struct Predicted; 17 | -------------------------------------------------------------------------------- /book/src/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | For a quick tutorial about how to use the crate, click [here](./tutorial/title.md). 4 | 5 | ## What is lightyear? 6 | 7 | Lightyear is a networking library for games written in Bevy. 8 | It uses a client-server networking architecture, where the server is authoritative over the game state. 9 | 10 | It is heavily inspired by [naia](https://github.com/naia-lib/naia). 11 | 12 | 13 | ## What is this book about? 14 | 15 | This book serves several purposes: 16 | - It contains some explanations of game networking concepts, as well as how they are implemented in this crate 17 | - provide some examples of how to use the crate 18 | - explain some of the design decisions that were made 19 | 20 | This book does not aim to be a polished document, or a comprehensive reference for lightyear. 21 | It is more of a collection of notes and thoughts that I had while developing the crate; a networking-related wiki that I could reference later. -------------------------------------------------------------------------------- /lightyear_ui/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_ui" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "Runtime debug UI for lightyear" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = [] 14 | test_utils = [] 15 | 16 | [dependencies] 17 | lightyear_metrics.workspace = true 18 | 19 | # utils 20 | metrics.workspace = true 21 | metrics-util.workspace = true 22 | tracing.workspace = true 23 | 24 | # bevy 25 | bevy_app.workspace = true 26 | bevy_color.workspace = true 27 | bevy_ecs.workspace = true 28 | bevy_platform.workspace = true 29 | bevy_reflect.workspace = true 30 | bevy_ui.workspace = true 31 | bevy_utils.workspace = true 32 | bevy_text.workspace = true 33 | bevy_time.workspace = true 34 | 35 | [lints] 36 | workspace = true 37 | 38 | [package.metadata.docs.rs] 39 | all-features = true 40 | -------------------------------------------------------------------------------- /lightyear_connection/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_connection" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "Connection handling for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = [] 13 | client = [] 14 | server = [] 15 | 16 | [dependencies] 17 | # local crates 18 | lightyear_core.workspace = true 19 | lightyear_link.workspace = true 20 | lightyear_serde.workspace = true 21 | 22 | # bevy 23 | bevy_app.workspace = true 24 | bevy_ecs.workspace = true 25 | bevy_platform.workspace = true 26 | bevy_reflect.workspace = true 27 | 28 | # utils 29 | bytes.workspace = true 30 | serde.workspace = true 31 | smallvec.workspace = true 32 | thiserror.workspace = true 33 | tracing.workspace = true 34 | 35 | [lints] 36 | workspace = true 37 | 38 | [package.metadata.docs.rs] 39 | all-features = true 40 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [cbournhonesque] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /book/src/concepts/transport/serialization.md: -------------------------------------------------------------------------------- 1 | # Serialization 2 | 3 | We use the bitcode library to serialize and deserialize messages. Bitcode is a very compact serialization format that 4 | uses bit-packing (a bool will be serialized as a single bit). 5 | 6 | When sending messages, we start by serializing the message early into a `Bytes` structure. 7 | 8 | This allows us to: 9 | 10 | - know the size of the message right away (which helps with packet fragmentation) 11 | - cheaply copy the message if we need to send it multiple times (for reliable channels) 12 | However: 13 | - it is much more expensive and inefficient to call `serialize` on each individual message compared with the final 14 | packet, and the serialized bytes compress less efficiently 15 | 16 | ## Buffers 17 | 18 | We use a `Buffer` to serialize/deserialize messages in order to re-use memory allocations. 19 | 20 | When we receive a packet (`&[u8]`), we create a `ReadBuffer` from it, which starts by copying the bytes into the buffer. -------------------------------------------------------------------------------- /lightyear_udp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_udp" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "IO primitives for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = [] 13 | server = ["bevy_platform"] 14 | metrics = ["dep:metrics"] 15 | 16 | [dependencies] 17 | lightyear_core.workspace = true 18 | lightyear_link.workspace = true 19 | 20 | aeronet_io.workspace = true 21 | 22 | metrics = { workspace = true, optional = true } 23 | tracing.workspace = true 24 | 25 | # bevy 26 | bevy_app.workspace = true 27 | bevy_ecs = { workspace = true, features = ["std"] } 28 | bevy_platform = { workspace = true, optional = true } 29 | 30 | # utils 31 | bytes.workspace = true 32 | thiserror.workspace = true 33 | 34 | [lints] 35 | workspace = true 36 | 37 | [package.metadata.docs.rs] 38 | all-features = true 39 | -------------------------------------------------------------------------------- /benches/divan/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_benches_divan" 3 | version.workspace = true 4 | publish = false 5 | authors.workspace = true 6 | edition.workspace = true 7 | rust-version.workspace = true 8 | description = "Benchmark tests for lightyear" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 11 | categories = ["game-development", "network-programming"] 12 | license.workspace = true 13 | 14 | [dependencies] 15 | lightyear = { workspace = true, features = ["interpolation"] } 16 | lightyear_tests = { workspace = true } 17 | 18 | 19 | # enable all the bevy defaults: 20 | bevy = { workspace = true, default-features = true } 21 | tracy-client = { version = "=0.18.0" } 22 | 23 | # crates specific to benchmarks, so not in top-level Cargo workspace 24 | divan = { version = "0.1.21" } 25 | 26 | [lib] 27 | bench = false 28 | 29 | [[bench]] 30 | name = "message" 31 | path = "src/message.rs" 32 | harness = false 33 | -------------------------------------------------------------------------------- /lightyear_raw_connection/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_raw_connection" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "Connection layer that directly uses the underlying IO" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | std = ["lightyear_link/std", "lightyear_transport/std"] 13 | default = [] 14 | client = ["lightyear_connection/client"] 15 | server = ["lightyear_connection/server"] 16 | 17 | [dependencies] 18 | # local crates 19 | lightyear_core.workspace = true 20 | lightyear_connection.workspace = true 21 | lightyear_link.workspace = true 22 | lightyear_transport.workspace = true 23 | 24 | # bevy 25 | bevy_app.workspace = true 26 | bevy_ecs.workspace = true 27 | 28 | # utils 29 | aeronet_io.workspace = true 30 | tracing.workspace = true 31 | 32 | [lints] 33 | workspace = true 34 | 35 | [package.metadata.docs.rs] 36 | all-features = true 37 | -------------------------------------------------------------------------------- /lightyear_inputs_native/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_inputs_native" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "IO primitives for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = ["lightyear_inputs/std"] 14 | client = ["lightyear_inputs/client"] 15 | server = ["lightyear_inputs/server"] 16 | 17 | [dependencies] 18 | lightyear_core.workspace = true 19 | lightyear_inputs.workspace = true 20 | 21 | # utils 22 | tracing.workspace = true 23 | 24 | # serde 25 | serde.workspace = true 26 | 27 | # bevy 28 | bevy_app.workspace = true 29 | bevy_derive.workspace = true 30 | bevy_ecs.workspace = true 31 | bevy_reflect.workspace = true 32 | 33 | [dev-dependencies] 34 | test-log.workspace = true 35 | 36 | [lints] 37 | workspace = true 38 | 39 | [package.metadata.docs.rs] 40 | all-features = true 41 | -------------------------------------------------------------------------------- /lightyear_core/src/interpolation.rs: -------------------------------------------------------------------------------- 1 | use bevy_ecs::{component::Component, reflect::ReflectComponent}; 2 | use bevy_reflect::Reflect; 3 | 4 | /// Component added to client-side entities that are visually interpolated. 5 | /// 6 | /// Interpolation is used to smooth the visual representation of entities received from the server. 7 | /// Instead of snapping to new positions/states upon receiving a server update, the entity's 8 | /// components are smoothly transitioned from their previous state to the new state over time. 9 | /// 10 | /// This component links the interpolated entity to its server-confirmed counterpart. 11 | /// The `InterpolationPlugin` uses this to: 12 | /// - Store the component history of the confirmed entity. 13 | /// - Apply interpolated values to the components of this entity based on the `InterpolationTimeline`. 14 | // NOTE: we create Interpolated here because it is used by multiple crates (interpolation, replication) 15 | #[derive(Debug, Reflect, Component)] 16 | #[reflect(Component)] 17 | pub struct Interpolated; 18 | -------------------------------------------------------------------------------- /book/src/concepts/connection/multi_connection.md: -------------------------------------------------------------------------------- 1 | # Multi connection 2 | 3 | 4 | In lightyear, the server can handle multiple connection protocol as the same time. 5 | This means that the server could: 6 | - open a port to establish steam socket connections 7 | - open another port for UDP connections 8 | - open another port for WebTransport connections 9 | - etc. 10 | 11 | and have all these connections running at the same time. 12 | 13 | You can therefore have cross-play between different platforms. 14 | 15 | Another potential usage is to have a "ListenServer" setup where a client acts as the "host": 16 | - the Client and the Server run in the same process 17 | - the Server has multiple connection protocols: 18 | - one is based on local channels to talk to the Client that is running in the same process 19 | - the other could be for example a UDP connection to allow other clients to connect to the server 20 | 21 | 22 | To achieve this, you can just provide multiple `NetConfig` when creating the `ServerConfig` that will be used to create the server. -------------------------------------------------------------------------------- /lightyear_connection/src/client_of.rs: -------------------------------------------------------------------------------- 1 | use bevy_ecs::{component::Component, reflect::ReflectComponent}; 2 | use bevy_reflect::Reflect; 3 | 4 | /// Marker component to identify this link as a [`LinkOf`](lightyear_link::prelude::LinkOf) 5 | /// 6 | /// This is equivalent to `LinkOf` + [`Connected`](crate::prelude::Connected). 7 | /// 8 | #[derive(Component, Clone, Copy, PartialEq, Eq, Debug, Reflect)] 9 | #[reflect(Component, PartialEq, Debug, Clone)] 10 | pub struct ClientOf; 11 | 12 | /// Marker component to identify a link that should skip netcode processing. 13 | /// 14 | /// For example a Server could have both a ServerNetcode and a SteamServerIO. 15 | /// In which we case we want to skip netcode for the links that come from steam, since they 16 | /// already have a RemoteId. 17 | /// 18 | // TODO: maybe we could also apply netcode for steam links? the RemoteId::Steam would be overridden 19 | // with a RemoteId::Netcode identifier, and the SteamId could simply be some extra metadata component? 20 | #[derive(Component, Default)] 21 | pub struct SkipNetcode; 22 | -------------------------------------------------------------------------------- /lightyear_frame_interpolation/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_frame_interpolation" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "IO primitives for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = ["lightyear_replication/std"] 14 | 15 | [dependencies] 16 | lightyear_core.workspace = true 17 | lightyear_connection.workspace = true 18 | lightyear_replication.workspace = true 19 | lightyear_interpolation.workspace = true 20 | 21 | # utils 22 | tracing.workspace = true 23 | serde.workspace = true 24 | 25 | # bevy 26 | bevy_app.workspace = true 27 | bevy_ecs.workspace = true 28 | bevy_time.workspace = true 29 | bevy_reflect.workspace = true 30 | bevy_transform = { workspace = true, features = ["bevy-support"] } 31 | bevy_utils.workspace = true 32 | 33 | [lints] 34 | workspace = true 35 | 36 | [package.metadata.docs.rs] 37 | all-features = true 38 | -------------------------------------------------------------------------------- /lightyear_transport/src/packet/packet_type.rs: -------------------------------------------------------------------------------- 1 | #[repr(u8)] 2 | #[derive(Copy, Debug, Clone, Eq, PartialEq)] 3 | pub enum PacketType { 4 | /// A packet containing actual data 5 | /// 6 | /// Will be serialized like: 7 | /// - header 8 | /// - channel_id_1 9 | /// - num messages 10 | /// - single_data_1 11 | /// - single_data_2 12 | /// - channel_id_2 13 | /// - num messages 14 | /// - ... 15 | /// - channel_id = 0 = indication of end of packet 16 | Data = 0, 17 | DataFragment = 1, 18 | } 19 | 20 | impl From for u8 { 21 | fn from(packet_type: PacketType) -> u8 { 22 | packet_type as u8 23 | } 24 | } 25 | 26 | impl TryFrom for PacketType { 27 | type Error = lightyear_serde::SerializationError; 28 | 29 | fn try_from(value: u8) -> Result { 30 | match value { 31 | 0 => Ok(PacketType::Data), 32 | 1 => Ok(PacketType::DataFragment), 33 | _ => Err(lightyear_serde::SerializationError::InvalidPacketType), 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/common/src/shared.rs: -------------------------------------------------------------------------------- 1 | use core::net::{IpAddr, Ipv4Addr, SocketAddr}; 2 | use core::time::Duration; 3 | 4 | pub const FIXED_TIMESTEP_HZ: f64 = 64.0; 5 | pub const SERVER_PORT: u16 = 5888; 6 | /// 0 means that the OS will assign any available port 7 | pub const CLIENT_PORT: u16 = 0; 8 | pub const SERVER_ADDR: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), SERVER_PORT); 9 | pub const SHARED_SETTINGS: SharedSettings = SharedSettings { 10 | protocol_id: 0, 11 | private_key: [ 12 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13 | 0, 0, 14 | ], 15 | }; 16 | 17 | pub const SEND_INTERVAL: Duration = Duration::from_millis(100); 18 | 19 | pub const STEAM_APP_ID: u32 = 480; // Steamworks App ID for Spacewar, used for testing 20 | 21 | #[derive(Copy, Clone, Debug)] 22 | pub struct SharedSettings { 23 | /// An id to identify the protocol version 24 | pub protocol_id: u64, 25 | 26 | /// a 32-byte array to authenticate via the Netcode.io protocol 27 | pub private_key: [u8; 32], 28 | } 29 | -------------------------------------------------------------------------------- /lightyear_interpolation/src/despawn.rs: -------------------------------------------------------------------------------- 1 | use crate::interpolation_history::ConfirmedHistory; 2 | use bevy_ecs::prelude::*; 3 | use lightyear_core::interpolation::Interpolated; 4 | use lightyear_replication::prelude::Confirmed; 5 | 6 | /// Remove the component from interpolated entities when the confirmed component gets removed 7 | // TODO: should the removal also be applied with interpolation delay? 8 | pub(crate) fn removed_components( 9 | trigger: On>, 10 | mut commands: Commands, 11 | query: Query<(), (With, With)>, 12 | ) { 13 | if query.get(trigger.entity).is_ok() 14 | && let Ok(mut entity) = commands.get_entity(trigger.entity) 15 | { 16 | entity.try_remove::<(C, ConfirmedHistory)>(); 17 | } 18 | } 19 | 20 | // TODO: we should despawn interpolated only when it reaches the latest confirmed snapshot? 21 | // I suppose we could add a DespawnedMarker, and the entity would get despawned as soon as it reaches the end of interpolation... 22 | // not super priority but would be a nice to have 23 | -------------------------------------------------------------------------------- /examples/lobby/src/shared.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the shared code between the client and the server. 2 | use bevy::prelude::*; 3 | 4 | use crate::protocol::*; 5 | 6 | #[derive(Clone)] 7 | pub struct SharedPlugin; 8 | 9 | impl Plugin for SharedPlugin { 10 | fn build(&self, app: &mut App) { 11 | app.add_plugins(ProtocolPlugin); 12 | } 13 | } 14 | 15 | // This system defines how we update the player's positions when we receive an input 16 | pub(crate) fn shared_movement_behaviour(mut position: Mut, input: &Inputs) { 17 | const MOVE_SPEED: f32 = 10.0; 18 | match input { 19 | Inputs::Direction(direction) => { 20 | if direction.up { 21 | position.y += MOVE_SPEED; 22 | } 23 | if direction.down { 24 | position.y -= MOVE_SPEED; 25 | } 26 | if direction.left { 27 | position.x -= MOVE_SPEED; 28 | } 29 | if direction.right { 30 | position.x += MOVE_SPEED; 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /examples/priority/src/shared.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use leafwing_input_manager::action_state::ActionState; 3 | 4 | use crate::protocol::*; 5 | 6 | const MOVE_SPEED: f32 = 10.0; 7 | pub(crate) const PROP_SIZE: f32 = 5.0; 8 | 9 | // SharedPlugin is no longer needed, ProtocolPlugin is added in main.rs 10 | #[derive(Clone)] 11 | pub struct SharedPlugin; 12 | 13 | impl Plugin for SharedPlugin { 14 | fn build(&self, app: &mut App) { 15 | app.add_plugins(ProtocolPlugin); 16 | } 17 | } 18 | 19 | /// Shared movement logic, controlled by the leafwing ActionState 20 | pub(crate) fn shared_movement_behaviour( 21 | mut position: Mut, 22 | action_state: &ActionState, 23 | ) { 24 | if action_state.pressed(&Inputs::Up) { 25 | position.y += MOVE_SPEED; 26 | } 27 | if action_state.pressed(&Inputs::Down) { 28 | position.y -= MOVE_SPEED; 29 | } 30 | if action_state.pressed(&Inputs::Left) { 31 | position.x -= MOVE_SPEED; 32 | } 33 | if action_state.pressed(&Inputs::Right) { 34 | position.x += MOVE_SPEED; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lightyear_transport/src/channel/stats.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod send { 2 | /// TODO: maybe this should be directly on the ChannelSender? 3 | #[derive(Default, Copy, Clone, Debug, PartialEq)] 4 | pub struct ChannelSendStats { 5 | num_single_messages_sent: usize, 6 | num_fragment_messages_sent: usize, 7 | num_bytes_sent: usize, 8 | } 9 | 10 | impl ChannelSendStats { 11 | pub fn new() -> Self { 12 | Self::default() 13 | } 14 | 15 | pub fn add_single_message_sent(&mut self, num: usize) { 16 | self.num_single_messages_sent += num; 17 | } 18 | 19 | pub fn add_fragment_message_sent(&mut self, num: usize) { 20 | self.num_fragment_messages_sent += num; 21 | } 22 | 23 | pub fn add_bytes_sent(&mut self, num_bytes: usize) { 24 | self.num_bytes_sent = self.num_bytes_sent.saturating_add(num_bytes); 25 | } 26 | 27 | pub fn messages_sent(&self) -> usize { 28 | self.num_single_messages_sent + self.num_fragment_messages_sent 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lightyear_interpolation/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## v0.21.0 (2025-07-03) 9 | 10 | ### Bug Fixes 11 | 12 | - issues with prespawn match 13 | 14 | ### Commit Statistics 15 | 16 | 17 | 18 | - 3 commits contributed to the release. 19 | - 2 commits were understood as [conventional](https://www.conventionalcommits.org). 20 | - 1 unique issue was worked on: [#1064](https://github.com/cBournhonesque/lightyear/issues/1064) 21 | 22 | ### Commit Details 23 | 24 | 25 | 26 |
view details 27 | 28 | * **[#1064](https://github.com/cBournhonesque/lightyear/issues/1064)** 29 | - Issues with prespawn match ([`97d5b9b`](https://github.com/cBournhonesque/lightyear/commit/97d5b9baf349aa8c0245d20432ff333c42b2c04d)) 30 |
31 | -------------------------------------------------------------------------------- /examples/launcher/README.md: -------------------------------------------------------------------------------- 1 | # Server launcher for docker image 2 | 3 | For hosting the example servers, all the server binaries are in a single docker image. 4 | This launcher is the docker entrypoint that decides which one to run, based on $EXAMPLE_NAME env var. 5 | 6 | This could be a bash script: 7 | ```bash 8 | #!/bin/bash 9 | exec /apps/$EXAMPLE_NAME/$EXAMPLE_NAME server 10 | ``` 11 | 12 | ## No shell 13 | 14 | However... in order to keep the resulting docker image as tiny as possible, so deployment is as fast as possible, it's using an absolute bare-bones docker image with just our rust binaries copied in. 15 | https://github.com/GoogleContainerTools/distroless 16 | 17 | it doesn't include a shell like bash or sh. there is a version that includes a shell, but that adds more size 18 | 19 | ## But.. ENTRYPOINT? 20 | 21 | Normally you'd just set the docker ENTRYPOINT when launching the container, depending on which example, to say "/apps/spaceships/spaceships" and not need a launcher, but that's not exposed via the edgegap dashboard, and setting envs is. if they add entry point settings to the dashboard we can delete the launcher. -------------------------------------------------------------------------------- /lightyear_raw_connection/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! # Lightyear Raw Connection 2 | Lightyear separates between the IO layer (Link) which handles transmitting bytes to a remote peer, 3 | and the Connection layer which symbolizes a more long-term connection on top of a Link. 4 | 5 | In particular every Connection entity must be associated with a LocalId and RemoteId that identifies 6 | the peers independently of the underlying Link. 7 | 8 | This crates provide a connection implementation where the establishing the Link is equivalent to establishing the Connection. 9 | The LocalId and RemoteId come from the Link's SocketAddr. 10 | */ 11 | #![no_std] 12 | 13 | extern crate alloc; 14 | extern crate core; 15 | #[cfg(feature = "std")] 16 | extern crate std; 17 | 18 | #[cfg(feature = "client")] 19 | pub mod client; 20 | 21 | #[cfg(feature = "server")] 22 | pub mod server; 23 | 24 | pub mod prelude { 25 | #[cfg(feature = "client")] 26 | pub mod client { 27 | pub use crate::client::RawClient; 28 | } 29 | 30 | #[cfg(feature = "server")] 31 | pub mod server { 32 | pub use crate::server::RawServer; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/network_visibility/src/shared.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use lightyear::prelude::*; 3 | 4 | use crate::protocol::*; 5 | 6 | #[derive(Clone)] 7 | pub struct SharedPlugin; 8 | 9 | impl Plugin for SharedPlugin { 10 | fn build(&self, app: &mut App) { 11 | app.add_plugins(ProtocolPlugin); 12 | } 13 | } 14 | 15 | // This system defines how we update the player's positions when we receive an input 16 | pub(crate) fn shared_movement_behaviour(mut position: Mut, direction: &Inputs) { 17 | const MOVE_SPEED: f32 = 10.0; 18 | if direction.up { 19 | position.y += MOVE_SPEED; 20 | } 21 | if direction.down { 22 | position.y -= MOVE_SPEED; 23 | } 24 | if direction.left { 25 | position.x -= MOVE_SPEED; 26 | } 27 | if direction.right { 28 | position.x += MOVE_SPEED; 29 | } 30 | } 31 | 32 | /// Generate a color from the `ClientId` 33 | pub(crate) fn color_from_id(client_id: PeerId) -> Color { 34 | let h = (((client_id.to_bits().wrapping_mul(30)) % 360) as f32) / 360.0; 35 | let s = 1.0; 36 | let l = 0.5; 37 | Color::hsl(h, s, l) 38 | } 39 | -------------------------------------------------------------------------------- /examples/simple_setup/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simple_setup" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition = "2021" 6 | description = "Examples for the lightyearServer-client networking library for the Bevy game engine" 7 | readme = "README.md" 8 | repository = "https://github.com/cBournhonesque/lightyear" 9 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 10 | categories = ["game-development", "network-programming"] 11 | license = "MIT OR Apache-2.0" 12 | publish = false 13 | 14 | 15 | [dependencies] 16 | clap.workspace = true 17 | lightyear = { workspace = true, features = [ 18 | "client", 19 | "server", 20 | "netcode", 21 | "replication", 22 | "udp", 23 | ] } 24 | bevy = { workspace = true, features = [ 25 | "bevy_render", 26 | "bevy_core_pipeline", 27 | "bevy_winit", 28 | "bevy_window", 29 | ] } 30 | serde.workspace = true 31 | 32 | [package.metadata.bevy_cli.web] 33 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] 34 | default-features = false 35 | features = ["client", "netcode"] 36 | 37 | [lints] 38 | workspace = true 39 | -------------------------------------------------------------------------------- /lightyear_serde/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_serde" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "IO primitives for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = ["bincode/std", "bytes/std", "no_std_io2/std"] 14 | 15 | [dependencies] 16 | # utils 17 | bytes.workspace = true 18 | thiserror.workspace = true 19 | tracing.workspace = true 20 | variadics_please.workspace = true 21 | 22 | # serde 23 | bincode.workspace = true 24 | serde.workspace = true 25 | 26 | # bevy 27 | bevy_derive.workspace = true 28 | bevy_ecs = { workspace = true, features = ["bevy_reflect"] } 29 | bevy_platform.workspace = true 30 | bevy_ptr.workspace = true 31 | bevy_reflect.workspace = true 32 | bevy_utils.workspace = true 33 | 34 | # no_std 35 | no_std_io2.workspace = true 36 | 37 | [dev-dependencies] 38 | test-log.workspace = true 39 | 40 | [lints] 41 | workspace = true 42 | 43 | [package.metadata.docs.rs] 44 | all-features = true 45 | -------------------------------------------------------------------------------- /examples/auth/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "auth" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition = "2021" 6 | description = "Examples for the lightyearServer-client networking library for the Bevy game engine" 7 | readme = "README.md" 8 | repository = "https://github.com/cBournhonesque/lightyear" 9 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 10 | categories = ["game-development", "network-programming"] 11 | license = "MIT OR Apache-2.0" 12 | publish = false 13 | 14 | [dependencies] 15 | lightyear = { "workspace" = true, features = [ 16 | "netcode", 17 | "interpolation", 18 | "prediction", 19 | "replication", 20 | "client", 21 | "server", 22 | "udp", 23 | "std", 24 | ] } 25 | lightyear_examples_common = { "workspace" = true, features = [ 26 | "client", 27 | "server", 28 | "gui", 29 | "netcode", 30 | "udp", 31 | ] } 32 | 33 | async-compat.workspace = true 34 | anyhow.workspace = true 35 | bevy.workspace = true 36 | rand.workspace = true 37 | tokio = { "workspace" = true, features = ["net", "io-util"] } 38 | 39 | [lints] 40 | workspace = true 41 | -------------------------------------------------------------------------------- /lightyear_webtransport/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(docsrs, feature(doc_cfg))] 2 | 3 | extern crate alloc; 4 | 5 | #[cfg(feature = "client")] 6 | pub mod client; 7 | #[cfg(all(feature = "server", not(target_family = "wasm")))] 8 | pub mod server; 9 | 10 | use alloc::string::String; 11 | 12 | #[derive(thiserror::Error, Debug)] 13 | pub enum WebTransportError { 14 | #[error("the certificate hash `{0}` is invalid")] 15 | Certificate(String), 16 | #[error("PeerAddr is required to start the WebTransportClientIo link")] 17 | PeerAddrMissing, 18 | #[error("LocalAddr is required to start the WebTransportServerIo")] 19 | LocalAddrMissing, 20 | } 21 | 22 | pub mod prelude { 23 | pub use crate::WebTransportError; 24 | 25 | #[cfg(not(target_family = "wasm"))] 26 | pub use aeronet_webtransport::wtransport::Identity; 27 | 28 | #[cfg(feature = "client")] 29 | pub mod client { 30 | pub use crate::client::WebTransportClientIo; 31 | } 32 | 33 | #[cfg(all(feature = "server", not(target_family = "wasm")))] 34 | pub mod server { 35 | pub use crate::server::WebTransportServerIo; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lightyear_connection/src/identity.rs: -------------------------------------------------------------------------------- 1 | use crate::client::Client; 2 | use crate::host::HostClient; 3 | use bevy_ecs::query::{With, Without}; 4 | use bevy_ecs::system::Query; 5 | use lightyear_link::server::Server; 6 | 7 | /// Returns true if the peer is a client (host-server counts as a server) 8 | pub fn is_client(query: Query<(), (With, Without)>) -> bool { 9 | !query.is_empty() 10 | } 11 | 12 | /// Returns true if the peer is a server 13 | pub fn is_server(query: Query<(), With>) -> bool { 14 | !query.is_empty() 15 | } 16 | 17 | /// Returns true if we are running in host-server mode, i.e. the server is acting as a client 18 | /// (in which case we can disable the networking/prediction/interpolation systems on the client) 19 | /// 20 | /// We are in HostServer mode if the mode is set to HostServer AND the server is running. 21 | /// (checking if the mode is set to HostServer is not enough, it just means that the server plugin 22 | /// and client plugin are running in the same App) 23 | pub fn is_host_server() -> bool { 24 | todo!(); 25 | // identity.is_some_and(|i| i.get() == &NetworkIdentityState::HostServer) 26 | } 27 | -------------------------------------------------------------------------------- /examples/client_replication/src/renderer.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::*; 2 | use bevy::prelude::*; 3 | 4 | #[derive(Clone)] 5 | pub struct ExampleRendererPlugin; 6 | 7 | impl Plugin for ExampleRendererPlugin { 8 | fn build(&self, app: &mut App) { 9 | app.add_systems(Startup, init); 10 | app.add_systems(PostUpdate, draw_elements); 11 | } 12 | } 13 | 14 | fn init(mut commands: Commands) { 15 | commands.spawn(Camera2d); 16 | } 17 | 18 | /// System that draws the player's boxes and cursors 19 | pub(crate) fn draw_elements( 20 | mut gizmos: Gizmos, 21 | players: Query<(&PlayerPosition, &PlayerColor)>, 22 | cursors: Query<(&CursorPosition, &PlayerColor)>, 23 | ) { 24 | for (position, color) in &players { 25 | gizmos.rect_2d( 26 | Isometry2d::from_translation(Vec2::new(position.x, position.y)), 27 | Vec2::ONE * 40.0, 28 | color.0, 29 | ); 30 | } 31 | for (position, color) in &cursors { 32 | gizmos.circle_2d( 33 | Isometry2d::from_translation(Vec2::new(position.x, position.y)), 34 | 15.0, 35 | color.0, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/lobby/src/renderer.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::*; 2 | use bevy::prelude::*; 3 | use bevy::render::RenderPlugin; 4 | use bevy_egui::EguiPlugin; 5 | use lightyear::interpolation::Interpolated; 6 | use lightyear::prediction::Predicted; 7 | 8 | #[derive(Clone)] 9 | pub struct ExampleRendererPlugin; 10 | 11 | impl Plugin for ExampleRendererPlugin { 12 | fn build(&self, app: &mut App) { 13 | app.add_systems(Startup, init); 14 | app.add_systems(Update, draw_boxes); 15 | } 16 | } 17 | 18 | fn init(mut commands: Commands) { 19 | commands.spawn(Camera2d); 20 | } 21 | 22 | /// System that draws the boxes of the player positions. 23 | /// The components should be replicated from the server to the client 24 | pub(crate) fn draw_boxes( 25 | mut gizmos: Gizmos, 26 | players: Query<(&PlayerPosition, &PlayerColor), Or<(With, With)>>, 27 | ) { 28 | for (position, color) in &players { 29 | debug!("Drawing player at {:?}", position.0); 30 | gizmos.rect_2d( 31 | Isometry2d::from_translation(position.0), 32 | Vec2::ONE * 50.0, 33 | color.0, 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /benches/criterion/justfile: -------------------------------------------------------------------------------- 1 | # run each benchmark once. 2 | test: 3 | cargo test --benches -- --nocapture 4 | 5 | # run a specific benchmark once. This is mostly useful for bandwidth benchmarks, as the bandwidth is deterministic so we only need to run it once 6 | test-target target="bandwidth": 7 | cargo test --bench={{target}} -- --nocapture 8 | 9 | # run a benchmark and generate a flamegraph 10 | # The env variables are needed to have debug symbols even in release mode. 11 | profile bench="timing" target="replication/send_float_insert/1_client": 12 | CARGO_PROFILE_RELEASE_DEBUG=true RUSTFLAGS='-C force-frame-pointers=y' cargo bench --bench {{bench}} --profile=release -- {{target}} --nocapture 13 | 14 | # Run the benchmark and generate a flamegraph. 15 | # The env variables are needed to have debug symbols even in release mode. 16 | bench bench="timing" target="replication/send_float_insert/1_client": 17 | cargo bench --bench {{bench}} --profile=release -- {{target}} 18 | 19 | # Run the flamegraph separately 20 | flamegraph: 21 | CARGO_PROFILE_RELEASE_DEBUG=true RUSTFLAGS='-C force-frame-pointers=y' cargo flamegraph --root --bin=replication_profiling --profile=release -------------------------------------------------------------------------------- /book/src/concepts/transport/packet.md: -------------------------------------------------------------------------------- 1 | # Packet 2 | 3 | On top of the transport layer (which lets us send some arbitrary bytes) we have the packet layer. 4 | 5 | A packet is a structure that contains some data and some metadata (inside the header). 6 | 7 | ## Packet header 8 | 9 | The packet header will contain the same data as described in the Gaffer On Games articles: 10 | 11 | - the packet type (single vs fragmented) 12 | - the packet id (a wrapping u16) 13 | - the last ack-ed packet id received by the sender 14 | - an ack bitfield containing the ack of the last 32 packets before last_ack_packet_id 15 | - the current tick 16 | 17 | ## Packet data 18 | 19 | The data will be a list of Messages that are contained in the packet. 20 | 21 | A message is a structure that knows how to serialize/deserialize itself. 22 | 23 | This is how we store messages into packets: 24 | 25 | - the message get serialized into raw bytes 26 | - if the message is over the packet limit size (roughly 1200 bytes), it gets fragmented into multiple parts 27 | - we build a packet by iterating through the channels in order of priority, and then storing as many messages we can 28 | into the packet 29 | 30 | -------------------------------------------------------------------------------- /lightyear_inputs_leafwing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_inputs_leafwing" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "IO primitives for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = [] 14 | client = ["lightyear_inputs/client"] 15 | server = ["lightyear_inputs/server"] 16 | 17 | [dependencies] 18 | lightyear_inputs.workspace = true 19 | lightyear_core.workspace = true 20 | 21 | leafwing-input-manager.workspace = true 22 | 23 | # utils 24 | tracing.workspace = true 25 | 26 | # serde 27 | serde.workspace = true 28 | 29 | # bevy 30 | bevy_app.workspace = true 31 | bevy_ecs.workspace = true 32 | bevy_derive.workspace = true 33 | bevy_input.workspace = true 34 | bevy_math.workspace = true 35 | bevy_platform.workspace = true 36 | bevy_reflect.workspace = true 37 | bevy_utils.workspace = true 38 | 39 | [dev-dependencies] 40 | test-log.workspace = true 41 | 42 | [lints] 43 | workspace = true 44 | 45 | [package.metadata.docs.rs] 46 | all-features = true 47 | -------------------------------------------------------------------------------- /lightyear_websocket/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(docsrs, feature(doc_cfg))] 2 | 3 | extern crate alloc; 4 | 5 | #[cfg(feature = "client")] 6 | pub mod client; 7 | #[cfg(all(feature = "server", not(target_family = "wasm")))] 8 | pub mod server; 9 | 10 | use alloc::string::String; 11 | 12 | #[derive(thiserror::Error, Debug)] 13 | pub enum WebSocketError { 14 | #[error("the certificate hash `{0}` is invalid")] 15 | Certificate(String), 16 | #[error("PeerAddr is required to start the WebSocketClientIo link")] 17 | PeerAddrMissing, 18 | #[error("LocalAddr is required to start the WebSocketServerIo")] 19 | LocalAddrMissing, 20 | } 21 | 22 | pub mod prelude { 23 | pub use crate::WebSocketError; 24 | pub use aeronet_websocket::*; 25 | 26 | #[cfg(feature = "client")] 27 | pub mod client { 28 | pub use crate::client::{WebSocketClientIo, WebSocketScheme}; 29 | pub use aeronet_websocket::client::ClientConfig; 30 | } 31 | 32 | #[cfg(all(feature = "server", not(target_family = "wasm")))] 33 | pub mod server { 34 | pub use crate::server::WebSocketServerIo; 35 | pub use aeronet_websocket::server::ServerConfig; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lightyear_connection/src/shared.rs: -------------------------------------------------------------------------------- 1 | use alloc::string::String; 2 | use core::fmt::Debug; 3 | use lightyear_core::id::PeerId; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | /// Reasons for denying a connection request 7 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 8 | pub enum DeniedReason { 9 | ServerFull, 10 | Banned, 11 | InternalError, 12 | AlreadyConnected, 13 | TokenAlreadyUsed, 14 | InvalidToken, 15 | Custom(String), 16 | } 17 | 18 | /// Trait for handling connection requests from clients. 19 | pub trait ConnectionRequestHandler: Debug + Send + Sync { 20 | /// Handle a connection request from a client. 21 | /// Returns None if the connection is accepted, 22 | /// Returns Some(reason) if the connection is denied. 23 | fn handle_request(&self, client_id: PeerId) -> Option; 24 | } 25 | 26 | /// By default, all connection requests are accepted by the server. 27 | #[derive(Debug, Clone)] 28 | pub struct DefaultConnectionRequestHandler; 29 | 30 | impl ConnectionRequestHandler for DefaultConnectionRequestHandler { 31 | fn handle_request(&self, _client_id: PeerId) -> Option { 32 | None 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/common/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Lightyear example: {{name}} 10 | 28 | 29 | 30 | 31 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /lightyear_deterministic_replication/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Lightyear Core 2 | //! 3 | //! This crate provides fundamental types and utilities shared across the Lightyear networking library. 4 | //! It includes core concepts such as: 5 | //! - Ticking and time management (`tick`, `time`, `timeline`). 6 | //! - Network identifiers and abstractions (`network`, `id`). 7 | //! - History buffers for state management (`history_buffer`). 8 | //! - Core plugin structures (`plugin`). 9 | 10 | #![no_std] 11 | 12 | extern crate alloc; 13 | extern crate core; 14 | 15 | use bevy_ecs::component::Component; 16 | 17 | mod archetypes; 18 | mod checksum; 19 | /// Messages exchanged between client and server 20 | pub mod messages; 21 | mod plugin; 22 | 23 | /// Commonly used items from the `lightyear_core` crate. 24 | pub mod prelude { 25 | pub use crate::checksum::{ 26 | ChecksumHistory, ChecksumMessage, ChecksumReceivePlugin, ChecksumSendPlugin, 27 | }; 28 | pub use crate::plugin::DeterministicReplicationPlugin; 29 | } 30 | 31 | /// Marker component that indicates that this entity is deterministic. It is not updated via state, but only via inputs. 32 | #[derive(Component, Default)] 33 | pub struct Deterministic; 34 | -------------------------------------------------------------------------------- /book/mermaid-init.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | const darkThemes = ['ayu', 'navy', 'coal']; 3 | const lightThemes = ['light', 'rust']; 4 | 5 | const classList = document.getElementsByTagName('html')[0].classList; 6 | 7 | let lastThemeWasLight = true; 8 | for (const cssClass of classList) { 9 | if (darkThemes.includes(cssClass)) { 10 | lastThemeWasLight = false; 11 | break; 12 | } 13 | } 14 | 15 | const theme = lastThemeWasLight ? 'default' : 'dark'; 16 | mermaid.initialize({ startOnLoad: true, theme }); 17 | 18 | // Simplest way to make mermaid re-render the diagrams in the new theme is via refreshing the page 19 | 20 | for (const darkTheme of darkThemes) { 21 | document.getElementById(darkTheme).addEventListener('click', () => { 22 | if (lastThemeWasLight) { 23 | window.location.reload(); 24 | } 25 | }); 26 | } 27 | 28 | for (const lightTheme of lightThemes) { 29 | document.getElementById(lightTheme).addEventListener('click', () => { 30 | if (!lastThemeWasLight) { 31 | window.location.reload(); 32 | } 33 | }); 34 | } 35 | })(); 36 | -------------------------------------------------------------------------------- /examples/avian_physics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "avian_physics" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [features] 9 | default = ["client", "gui", "server", "netcode", "udp"] 10 | client = ["lightyear_examples_common/client", "gui"] 11 | gui = ["lightyear_examples_common/gui"] 12 | server = ["lightyear_examples_common/server"] 13 | netcode = ["lightyear_examples_common/netcode"] 14 | udp = ["lightyear_examples_common/udp"] 15 | 16 | [dependencies] 17 | lightyear = { workspace = true, features = [ 18 | "interpolation", 19 | "prediction", 20 | "replication", 21 | "leafwing", 22 | "avian2d", 23 | ] } 24 | lightyear_examples_common.workspace = true 25 | lightyear_frame_interpolation.workspace = true 26 | leafwing-input-manager.workspace = true 27 | avian2d = { workspace = true, features = [ 28 | "2d", 29 | "f32", 30 | "parry-f32", 31 | "parallel", 32 | "serialize", 33 | ] } 34 | 35 | serde.workspace = true 36 | bevy.workspace = true 37 | 38 | [package.metadata.bevy_cli.web] 39 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] 40 | default-features = false 41 | features = ["client", "netcode"] 42 | 43 | [lints] 44 | workspace = true 45 | -------------------------------------------------------------------------------- /examples/simple_setup/src/shared.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the shared code between the client and the server. 2 | 3 | use bevy::prelude::*; 4 | use core::net::{IpAddr, Ipv4Addr, SocketAddr}; 5 | use core::time::Duration; 6 | use lightyear::prelude::*; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | pub const FIXED_TIMESTEP_HZ: f64 = 64.0; 10 | 11 | pub const SERVER_REPLICATION_INTERVAL: Duration = Duration::from_millis(100); 12 | 13 | pub const SERVER_ADDR: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5000); 14 | 15 | #[derive(Clone)] 16 | pub struct SharedPlugin; 17 | 18 | pub struct Channel1; 19 | 20 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] 21 | pub struct Message1(pub usize); 22 | 23 | impl Plugin for SharedPlugin { 24 | fn build(&self, app: &mut App) { 25 | // Register your protocol, which is shared between client and server 26 | app.register_message::() 27 | .add_direction(NetworkDirection::Bidirectional); 28 | 29 | app.add_channel::(ChannelSettings { 30 | mode: ChannelMode::OrderedReliable(ReliableSettings::default()), 31 | ..default() 32 | }) 33 | .add_direction(NetworkDirection::Bidirectional); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lightyear_core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_core" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "Core types shared by lightyear crates" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = [] 13 | test_utils = ["dep:mock_instant"] 14 | # special feature to avoid using mock_instant when running cargo doc and cargo clippy 15 | not_mock = [] 16 | 17 | [dependencies] 18 | lightyear_utils.workspace = true 19 | lightyear_serde.workspace = true 20 | 21 | # utils 22 | chrono.workspace = true 23 | fixed.workspace = true 24 | tracing.workspace = true 25 | 26 | # bevy 27 | bevy_app = { workspace = true, features = ["bevy_reflect"] } 28 | bevy_derive.workspace = true 29 | bevy_ecs.workspace = true 30 | bevy_platform.workspace = true 31 | bevy_reflect.workspace = true 32 | bevy_time.workspace = true 33 | 34 | # serde 35 | serde.workspace = true 36 | 37 | # test 38 | mock_instant = { workspace = true, optional = true } 39 | 40 | [dev-dependencies] 41 | test-log.workspace = true 42 | approx.workspace = true 43 | 44 | [lints] 45 | workspace = true 46 | 47 | [package.metadata.docs.rs] 48 | all-features = true 49 | -------------------------------------------------------------------------------- /examples/simple_box/src/shared.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the shared code between the client and the server. 2 | //! 3 | //! The simulation logic (movement, etc.) should be shared between client and server to guarantee that there won't be 4 | //! mispredictions/rollbacks. 5 | use crate::protocol::*; 6 | use bevy::prelude::*; 7 | use lightyear_examples_common::shared::SharedSettings; 8 | 9 | pub struct SharedPlugin; 10 | 11 | impl Plugin for SharedPlugin { 12 | fn build(&self, app: &mut App) { 13 | app.add_plugins(ProtocolPlugin); 14 | } 15 | } 16 | 17 | pub const SHARED_SETTINGS: SharedSettings = SharedSettings { 18 | protocol_id: 0, 19 | private_key: [0; 32], 20 | }; 21 | 22 | // This system defines how we update the player's positions when we receive an input 23 | pub(crate) fn shared_movement_behaviour(mut position: Mut, input: &Inputs) { 24 | const MOVE_SPEED: f32 = 10.0; 25 | let Inputs::Direction(direction) = input; 26 | if direction.up { 27 | position.y += MOVE_SPEED; 28 | } 29 | if direction.down { 30 | position.y -= MOVE_SPEED; 31 | } 32 | if direction.left { 33 | position.x -= MOVE_SPEED; 34 | } 35 | if direction.right { 36 | position.x += MOVE_SPEED; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/launcher/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "launcher" 3 | version = "0.25.5" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | publish = false 7 | 8 | [dependencies] 9 | bevy = { workspace = true, default-features = false, features = [ 10 | "bevy_asset", 11 | "bevy_winit", 12 | "bevy_render", 13 | "bevy_core_pipeline", 14 | "bevy_pbr", 15 | "x11", 16 | "wayland", 17 | ] } 18 | bevy_egui = { workspace = true } 19 | lightyear = { path = "../../lightyear", features = [ 20 | "client", 21 | "server", 22 | "prediction", 23 | "interpolation", 24 | "input_native", 25 | ] } 26 | lightyear_examples_common = { path = "../common", features = [ 27 | "client", 28 | "server", 29 | "gui", 30 | ] } # Added common 31 | 32 | # utils 33 | rand.workspace = true 34 | tracing.workspace = true 35 | serde.workspace = true 36 | 37 | # Add dependencies for specific examples, enabling necessary features 38 | simple_box = { path = "../simple_box", features = ["client", "server", "gui"] } 39 | # fps_new = { path = "../fps_new", features = ["client", "server", "gui"] } # Add other examples as needed 40 | 41 | clap = { version = "4.5.3", features = ["derive"] } 42 | strum = { version = "0.27", features = ["derive"] } 43 | ron = "0.12" 44 | tempfile = "3.19" 45 | 46 | [lints] 47 | workspace = true 48 | -------------------------------------------------------------------------------- /examples/network_visibility/src/renderer.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::*; 2 | use bevy::color::palettes::basic::GREEN; 3 | use bevy::prelude::*; 4 | 5 | #[derive(Clone)] 6 | pub struct ExampleRendererPlugin; 7 | 8 | impl Plugin for ExampleRendererPlugin { 9 | fn build(&self, app: &mut App) { 10 | app.add_systems(Startup, init); 11 | app.add_systems(Update, (draw_boxes, draw_circles)); 12 | } 13 | } 14 | 15 | fn init(mut commands: Commands) { 16 | commands.spawn(Camera2d); 17 | } 18 | 19 | /// System that draws the boxed of the player positions. 20 | /// The components should be replicated from the server to the client 21 | /// This time we will only draw the predicted/interpolated entities 22 | pub(crate) fn draw_boxes(mut gizmos: Gizmos, players: Query<(&Position, &PlayerColor)>) { 23 | for (position, color) in &players { 24 | gizmos.rect( 25 | Isometry3d::from_translation(Vec3::new(position.x, position.y, 0.0)), 26 | Vec2::ONE * 50.0, 27 | color.0, 28 | ); 29 | } 30 | } 31 | 32 | /// System that draws circles 33 | pub(crate) fn draw_circles(mut gizmos: Gizmos, circles: Query<&Position, With>) { 34 | for position in &circles { 35 | gizmos.circle_2d(Isometry2d::from_translation(position.0), 1.0, GREEN); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /benches/criterion/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_benches" 3 | version.workspace = true 4 | publish = false 5 | authors.workspace = true 6 | edition.workspace = true 7 | rust-version.workspace = true 8 | description = "Benchmark tests for lightyear" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 11 | categories = ["game-development", "network-programming"] 12 | license.workspace = true 13 | 14 | [dependencies] 15 | lightyear = { workspace = true, features = ["interpolation", "metrics", "std"] } 16 | lightyear_tests = { workspace = true } 17 | 18 | # enable all the bevy defaults: 19 | bevy = { workspace = true, default-features = true } 20 | tracy-client = { version = "=0.18.0" } 21 | 22 | # crates specific to benchmarks, so not in top-level Cargo workspace 23 | pprof = { version = "0.15.0", features = ["flamegraph", "frame-pointer"] } 24 | criterion = { version = "0.8", features = ["html_reports"] } 25 | 26 | [lib] 27 | bench = false 28 | 29 | [[bin]] 30 | name = "replication_profiling" 31 | path = "src/replication_profiling.rs" 32 | doc = false 33 | 34 | [[bench]] 35 | name = "timing" 36 | path = "benches/timing/main.rs" 37 | harness = false 38 | 39 | [[bench]] 40 | name = "bandwidth" 41 | path = "benches/bandwidth/main.rs" 42 | harness = false 43 | -------------------------------------------------------------------------------- /lightyear_inputs_bei/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_inputs_bei" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "Adds integration to network inputs from the bevy_enhanced_input crate for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = ["lightyear_inputs/std"] 14 | client = ["lightyear_inputs/client"] 15 | server = ["lightyear_inputs/server"] 16 | 17 | [dependencies] 18 | lightyear_core.workspace = true 19 | lightyear_inputs.workspace = true 20 | lightyear_replication.workspace = true 21 | lightyear_serde.workspace = true 22 | lightyear_link.workspace = true 23 | lightyear_connection.workspace = true 24 | lightyear_messages.workspace = true 25 | 26 | # inputs 27 | bevy_enhanced_input.workspace = true 28 | 29 | # utils 30 | tracing.workspace = true 31 | 32 | # serde 33 | serde.workspace = true 34 | 35 | # bevy 36 | bevy_app.workspace = true 37 | bevy_ecs.workspace = true 38 | bevy_reflect.workspace = true 39 | bevy_utils.workspace = true 40 | 41 | [dev-dependencies] 42 | bevy.workspace = true 43 | test-log.workspace = true 44 | 45 | [lints] 46 | workspace = true 47 | 48 | [package.metadata.docs.rs] 49 | all-features = true 50 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: local 3 | hooks: 4 | - id: rustfmt 5 | name: rustfmt 6 | description: Check if all files follow the rustfmt style 7 | entry: cargo fmt --all -- --check --color always 8 | types: [rust] 9 | language: system 10 | pass_filenames: false 11 | 12 | - id: clippy 13 | name: clippy 14 | description: Run clippy linter 15 | entry: cargo clippy --features=lightyear_core/not_mock --workspace --exclude=compiletime --exclude=avian_3d_character --exclude=launcher --exclude=delta_compression --exclude=distributed_authority --no-deps -- -D warnings -A clippy::needless_lifetimes 16 | types: [rust] 17 | language: system 18 | pass_filenames: false 19 | 20 | - id: docs 21 | name: docs 22 | description: Build the documentation to ensure there are no errors 23 | entry: cargo +nightly doc --all-features --workspace --document-private-items --no-deps --keep-going 24 | types: [rust] 25 | language: system 26 | pass_filenames: false 27 | 28 | - id: taplo 29 | name: Taplo TOML formatter 30 | description: Check formatting of all TOML files using Taplo 31 | entry: taplo fmt --check 32 | types: [toml] 33 | language: system 34 | pass_filenames: false 35 | -------------------------------------------------------------------------------- /examples/client_replication/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "client_replication" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition = "2021" 6 | description = "Examples for the lightyearServer-client networking library for the Bevy game engine" 7 | readme = "README.md" 8 | repository = "https://github.com/cBournhonesque/lightyear" 9 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 10 | categories = ["game-development", "network-programming"] 11 | license = "MIT OR Apache-2.0" 12 | publish = false 13 | 14 | [features] 15 | default = ["client", "gui", "server", "netcode", "udp"] 16 | client = ["gui", "lightyear/client", "lightyear_examples_common/client"] 17 | gui = ["lightyear_examples_common/gui"] 18 | server = ["lightyear/server", "lightyear_examples_common/server"] 19 | netcode = ["lightyear_examples_common/netcode"] 20 | udp = [ 21 | "lightyear_examples_common/udp", 22 | # udp requires std 23 | "lightyear/std", 24 | "lightyear/udp", 25 | ] 26 | 27 | [dependencies] 28 | lightyear = { "workspace" = true, features = [ 29 | "interpolation", 30 | "prediction", 31 | "replication", 32 | "input_bei", 33 | ] } 34 | bevy_enhanced_input.workspace = true 35 | lightyear_examples_common.workspace = true 36 | 37 | serde.workspace = true 38 | bevy.workspace = true 39 | 40 | [lints] 41 | workspace = true 42 | -------------------------------------------------------------------------------- /lightyear_interpolation/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_interpolation" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "IO primitives for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = ["lightyear_replication/std"] 14 | metrics = ["dep:metrics", "std"] 15 | 16 | [dependencies] 17 | lightyear_utils.workspace = true 18 | lightyear_serde.workspace = true 19 | lightyear_core.workspace = true 20 | lightyear_connection.workspace = true 21 | lightyear_messages.workspace = true 22 | lightyear_replication = { workspace = true, features = ["interpolation"] } 23 | lightyear_sync = { workspace = true, features = ["client"] } 24 | 25 | # utils 26 | metrics = { workspace = true, optional = true } 27 | tracing.workspace = true 28 | 29 | # serde 30 | serde.workspace = true 31 | 32 | # bevy 33 | bevy_app.workspace = true 34 | bevy_derive.workspace = true 35 | bevy_ecs.workspace = true 36 | bevy_math = { workspace = true, features = ["curve"] } 37 | bevy_platform.workspace = true 38 | bevy_reflect.workspace = true 39 | bevy_time.workspace = true 40 | bevy_utils.workspace = true 41 | 42 | [lints] 43 | workspace = true 44 | 45 | [package.metadata.docs.rs] 46 | all-features = true 47 | -------------------------------------------------------------------------------- /examples/fps/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fps" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [features] 9 | default = ["client", "gui", "server", "netcode", "udp"] 10 | client = ["gui", "lightyear/client", "lightyear_examples_common/client"] 11 | gui = ["lightyear_examples_common/gui"] 12 | server = ["lightyear/server", "lightyear_examples_common/server"] 13 | netcode = ["lightyear_examples_common/netcode"] 14 | udp = ["lightyear_examples_common/udp", "lightyear/std", "lightyear/udp"] 15 | 16 | [dependencies] 17 | avian2d = { workspace = true, features = [ 18 | "2d", 19 | "debug-plugin", 20 | "f32", 21 | "parry-f32", 22 | "parallel", 23 | "serialize", 24 | ] } 25 | lightyear = { workspace = true, features = [ 26 | "interpolation", 27 | "prediction", 28 | "replication", 29 | "avian2d", 30 | "leafwing", 31 | ] } 32 | lightyear_examples_common.workspace = true 33 | lightyear_frame_interpolation.workspace = true 34 | lightyear_avian2d = { workspace = true, features = ["2d", "lag_compensation"] } 35 | 36 | leafwing-input-manager.workspace = true 37 | serde.workspace = true 38 | bevy.workspace = true 39 | 40 | [package.metadata.bevy_cli.web] 41 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] 42 | default-features = false 43 | features = ["client", "netcode"] 44 | 45 | [lints] 46 | workspace = true 47 | -------------------------------------------------------------------------------- /lightyear_avian/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Lightyear Avian Integration 2 | //! 3 | //! This crate provides integration between Lightyear and the Avian physics engine. 4 | //! 5 | //! It currently includes utilities for lag compensation. 6 | #![allow(unexpected_cfgs)] 7 | #![no_std] 8 | 9 | #[cfg(feature = "std")] 10 | extern crate std; 11 | 12 | /// Provides systems and components for lag compensation with Avian. 13 | #[cfg(feature = "lag_compensation")] 14 | pub mod lag_compensation; 15 | 16 | #[cfg(feature = "2d")] 17 | pub mod types_2d; 18 | #[cfg(feature = "2d")] 19 | pub use types_2d as types; 20 | 21 | #[cfg(feature = "3d")] 22 | pub mod types_3d; 23 | 24 | #[cfg(feature = "3d")] 25 | pub use types_3d as types; 26 | 27 | #[cfg(any(feature = "2d", feature = "3d"))] 28 | pub mod plugin; 29 | 30 | #[cfg(feature = "2d")] 31 | mod correction_2d; 32 | #[cfg(feature = "3d")] 33 | mod correction_3d; 34 | /// Commonly used items for Lightyear Avian integration. 35 | pub mod prelude { 36 | #[cfg(feature = "lag_compensation")] 37 | pub use crate::lag_compensation::{ 38 | history::{ 39 | AabbEnvelopeHolder, LagCompensationConfig, LagCompensationHistory, 40 | LagCompensationPlugin, LagCompensationSystems, 41 | }, 42 | query::LagCompensationSpatialQuery, 43 | }; 44 | #[cfg(any(feature = "2d", feature = "3d"))] 45 | pub use crate::plugin::LightyearAvianPlugin; 46 | } 47 | -------------------------------------------------------------------------------- /lightyear_steam/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_steam" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "Connection handling for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = [] 14 | client = [ 15 | "aeronet_io", 16 | "aeronet_steam/client", 17 | "lightyear_aeronet", 18 | "lightyear_core", 19 | "lightyear_link", 20 | "lightyear_connection/client", 21 | ] 22 | server = [ 23 | "aeronet_io", 24 | "aeronet_steam/server", 25 | "lightyear_aeronet", 26 | "lightyear_core", 27 | "lightyear_link", 28 | "lightyear_connection/server", 29 | ] 30 | 31 | [dependencies] 32 | lightyear_aeronet = { workspace = true, optional = true } 33 | lightyear_core = { workspace = true, optional = true } 34 | lightyear_link = { workspace = true, optional = true } 35 | lightyear_connection = { workspace = true, optional = true } 36 | 37 | # bevy 38 | bevy_app.workspace = true 39 | bevy_ecs.workspace = true 40 | 41 | # aeronet 42 | aeronet_steam = { workspace = true } 43 | aeronet_io = { workspace = true, optional = true } 44 | 45 | # utils 46 | thiserror.workspace = true 47 | tracing.workspace = true 48 | 49 | [lints] 50 | workspace = true 51 | 52 | [package.metadata.docs.rs] 53 | all-features = true 54 | -------------------------------------------------------------------------------- /examples/network_visibility/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "network_visibility" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition = "2021" 6 | description = "Examples for the lightyearServer-client networking library for the Bevy game engine" 7 | readme = "README.md" 8 | repository = "https://github.com/cBournhonesque/lightyear" 9 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 10 | categories = ["game-development", "network-programming"] 11 | license = "MIT OR Apache-2.0" 12 | publish = false 13 | 14 | [features] 15 | default = ["client", "gui", "server", "netcode", "udp"] 16 | client = ["gui", "lightyear/client", "lightyear_examples_common/client"] 17 | gui = ["lightyear_examples_common/gui"] 18 | server = ["lightyear/server", "lightyear_examples_common/server"] 19 | netcode = ["lightyear_examples_common/netcode"] 20 | udp = [ 21 | "lightyear_examples_common/udp", 22 | # udp requires std 23 | "lightyear/std", 24 | "lightyear/udp", 25 | ] 26 | 27 | [dependencies] 28 | lightyear = { workspace = true, features = ["input_native"] } 29 | lightyear_examples_common.workspace = true 30 | 31 | serde.workspace = true 32 | bevy.workspace = true 33 | 34 | [package.metadata.bevy_cli.web] 35 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] 36 | default-features = false 37 | features = ["client", "netcode"] 38 | 39 | [lints] 40 | workspace = true 41 | -------------------------------------------------------------------------------- /examples/simple_setup/src/client.rs: -------------------------------------------------------------------------------- 1 | //! The client plugin. 2 | use crate::shared::*; 3 | use bevy::prelude::*; 4 | use core::net::Ipv4Addr; 5 | use core::net::{IpAddr, SocketAddr}; 6 | use lightyear::netcode::Key; 7 | use lightyear::prelude::client::*; 8 | use lightyear::prelude::*; 9 | 10 | pub struct ExampleClientPlugin; 11 | 12 | const CLIENT_ADDR: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 4000); 13 | 14 | impl Plugin for ExampleClientPlugin { 15 | fn build(&self, app: &mut App) { 16 | // add our client-specific logic. Here we will just connect to the server 17 | app.add_systems(Startup, startup); 18 | } 19 | } 20 | 21 | /// Spawn a client that connects to the server 22 | fn startup(mut commands: Commands) -> Result { 23 | commands.spawn(Camera2d); 24 | let auth = Authentication::Manual { 25 | server_addr: SERVER_ADDR, 26 | client_id: 0, 27 | private_key: Key::default(), 28 | protocol_id: 0, 29 | }; 30 | let client = commands 31 | .spawn(( 32 | Client::default(), 33 | LocalAddr(CLIENT_ADDR), 34 | PeerAddr(SERVER_ADDR), 35 | Link::new(None), 36 | ReplicationReceiver::default(), 37 | NetcodeClient::new(auth, NetcodeConfig::default())?, 38 | UdpIo::default(), 39 | )) 40 | .id(); 41 | commands.trigger(Connect { entity: client }); 42 | Ok(()) 43 | } 44 | -------------------------------------------------------------------------------- /examples/deterministic_replication/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deterministic_replication" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition.workspace = true 6 | publish = false 7 | 8 | [features] 9 | default = ["client", "gui", "server", "netcode", "udp"] 10 | client = ["lightyear_examples_common/client", "gui"] 11 | gui = ["lightyear_examples_common/gui"] 12 | server = ["lightyear_examples_common/server"] 13 | netcode = ["lightyear_examples_common/netcode"] 14 | udp = ["lightyear_examples_common/udp"] 15 | 16 | [dependencies] 17 | lightyear = { workspace = true, features = [ 18 | "interpolation", 19 | "prediction", 20 | "replication", 21 | "leafwing", 22 | "deterministic", 23 | ] } 24 | lightyear_examples_common.workspace = true 25 | lightyear_frame_interpolation.workspace = true 26 | leafwing-input-manager.workspace = true 27 | # add avian2d directly to customize the plugin 28 | lightyear_avian2d = { workspace = true, features = ["deterministic", "2d"] } 29 | avian2d = { workspace = true, features = [ 30 | "2d", 31 | "f32", 32 | "parry-f32", 33 | "parallel", 34 | "serialize", 35 | "enhanced-determinism", 36 | ] } 37 | 38 | serde.workspace = true 39 | bevy.workspace = true 40 | 41 | [package.metadata.bevy_cli.web] 42 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] 43 | default-features = false 44 | features = ["client", "netcode"] 45 | 46 | [lints] 47 | workspace = true 48 | -------------------------------------------------------------------------------- /lightyear_tests/src/bin/replicate.rs: -------------------------------------------------------------------------------- 1 | use lightyear_connection::network_target::NetworkTarget; 2 | use lightyear_replication::prelude::Replicate; 3 | use lightyear_tests::protocol::CompFull; 4 | use lightyear_tests::stepper::{ClientServerStepper, StepperConfig}; 5 | 6 | const NUM_ENTITIES: usize = 1000; 7 | 8 | fn main() { 9 | let mut stepper = ClientServerStepper::from_config(StepperConfig::single()); 10 | let entities = vec![(CompFull(0.0), Replicate::to_clients(NetworkTarget::All),); NUM_ENTITIES]; 11 | stepper.server_app.world_mut().spawn_batch(entities); 12 | 13 | stepper.advance_time(stepper.frame_duration); 14 | stepper.server_app.update(); 15 | 16 | // spawn a second batch (allocations should be reused) 17 | let entities = vec![(CompFull(0.0), Replicate::to_clients(NetworkTarget::All),); NUM_ENTITIES]; 18 | stepper.server_app.world_mut().spawn_batch(entities); 19 | 20 | stepper.advance_time(stepper.frame_duration); 21 | stepper.server_app.update(); 22 | } 23 | 24 | // Results: (RUST_LOG=info) 785c170275e39c60e1f588e2c25368af6dee4ea8 25 | // 1st update 26 | // - replicate: 270us 27 | // - send_replication_message: 96us 28 | // - Message::send: 12us 29 | // - Transport::buffer_send: 15us 30 | // - Netcode::send: 35us 31 | 32 | // 2nd update: 33 | // Same, but Message::send: 5us 34 | 35 | // Results: after switching to ReplicateState (61e848e79f89e313f6455936ed99474c87217836) 36 | // Exactly the same results. 37 | -------------------------------------------------------------------------------- /examples/deterministic_replication/README.md: -------------------------------------------------------------------------------- 1 | # Features 2 | 3 | 4 | 5 | 6 | ## Running an example 7 | 8 | - Run the server with a gui: `cargo run -- server` 9 | - Run client with id 1: `cargo run -- client -c 1` 10 | 11 | [//]: # (- Run the client and server in two separate bevy Apps: `cargo run` or `cargo run separate`) 12 | - Run the server without a gui: `cargo run --no-default-features --features=server -- server` 13 | - Run the client and server in "HostClient" mode, where the client also acts as server (both are in the same App) : `cargo run -- host-client -c 0` 14 | 15 | You can control the behaviour of the example by changing the list of features. By default, all features are enabled (client, server, gui). 16 | For example you can run the server in headless mode (without gui) by running `cargo run --no-default-features --features=server,udp,netcode`. 17 | 18 | ### Testing in wasm with webtransport 19 | 20 | NOTE: I am using the [bevy cli](https://github.com/TheBevyFlock/bevy_cli) to build and serve the wasm example. 21 | 22 | To test the example in wasm, you can run the following commands: `bevy run web` 23 | 24 | You will need a valid SSL certificate to test the example in wasm using webtransport. You will need to run the following 25 | commands to generate a self-signed certificate: 26 | - `cd "$(git rev-parse --show-toplevel)" && sh certificates/generate.sh` (to generate the temporary SSL 27 | certificates, they are only valid for 2 weeks) 28 | -------------------------------------------------------------------------------- /lightyear_deterministic_replication/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_deterministic_replication" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "Primitives for deterministic replication (as opposed to state replication) in the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | std = [ 13 | "bevy_ecs/std", 14 | "lightyear_inputs/std", 15 | "lightyear_messages/std", 16 | "lightyear_prediction/std", 17 | ] 18 | default = ["std"] 19 | 20 | [dependencies] 21 | lightyear_utils.workspace = true 22 | lightyear_core.workspace = true 23 | lightyear_inputs = { workspace = true, features = ["client"] } 24 | lightyear_link.workspace = true 25 | lightyear_messages.workspace = true 26 | lightyear_connection = { workspace = true, features = ["server"] } 27 | lightyear_sync.workspace = true 28 | lightyear_prediction = { workspace = true, features = ["deterministic"] } 29 | lightyear_replication = { workspace = true, features = ["deterministic"] } 30 | 31 | # utils 32 | seahash.workspace = true 33 | tracing.workspace = true 34 | 35 | # bevy 36 | bevy_app = { workspace = true, features = ["bevy_reflect"] } 37 | bevy_ecs.workspace = true 38 | 39 | # serde 40 | serde.workspace = true 41 | 42 | [lints] 43 | workspace = true 44 | 45 | [package.metadata.docs.rs] 46 | all-features = true 47 | -------------------------------------------------------------------------------- /lightyear/src/web.rs: -------------------------------------------------------------------------------- 1 | //! Module containing extra behaviour that we need when running in wasm 2 | 3 | use bevy_app::{App, Plugin}; 4 | use bevy_web_keepalive::WebKeepalivePlugin; 5 | 6 | /// In wasm, the main thread gets quickly throttled by the browser when it is hidden (e.g. when the user switches tabs). 7 | /// This means that the app.update() function will not be called, because bevy's scheduler only runs `app.update()` when 8 | /// the browser's requestAnimationFrame is called. (and that happens only when the tab is visible) 9 | /// 10 | /// This is problematic because: 11 | /// - we stop sending packets so the server disconnects the client because it doesn't receive keep-alives 12 | /// - when the client reconnects, it also disconnects because it hasn't been receiving server packets 13 | /// - the internal transport buffers can overflow because they are not being emptied 14 | /// 15 | /// This solution spawns a WebWorker (a background thread) which is not throttled, and which runs 16 | /// `app.update()` at a fixed interval. This way, the client can keep sending and receiving packets, 17 | /// and updating the local World. 18 | pub(crate) struct WebPlugin; 19 | 20 | impl Plugin for WebPlugin { 21 | fn build(&self, app: &mut App) { 22 | app.add_plugins(WebKeepalivePlugin { 23 | // The interval is in milliseconds. We can run app.update() infrequently when in the background 24 | wake_delay: 100.0, 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/priority/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "priority" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition = "2021" 6 | description = "Examples for the lightyear server-client networking library for the Bevy game engine" 7 | readme = "README.md" 8 | repository = "https://github.com/cBournhonesque/lightyear" 9 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 10 | categories = ["game-development", "network-programming"] 11 | license = "MIT OR Apache-2.0" 12 | publish = false 13 | 14 | 15 | [features] 16 | default = ["client", "gui", "server", "netcode", "udp"] 17 | client = ["gui", "lightyear/client", "lightyear_examples_common/client"] 18 | gui = ["lightyear_examples_common/gui"] 19 | server = ["lightyear/server", "lightyear_examples_common/server"] 20 | netcode = ["lightyear_examples_common/netcode"] 21 | udp = ["lightyear_examples_common/udp", "lightyear/std", "lightyear/udp"] 22 | 23 | [dependencies] 24 | lightyear = { workspace = true, features = [ 25 | "interpolation", 26 | "prediction", 27 | "replication", 28 | "leafwing", 29 | ] } 30 | lightyear_examples_common.workspace = true 31 | 32 | leafwing-input-manager.workspace = true 33 | serde.workspace = true 34 | bevy.workspace = true 35 | 36 | [package.metadata.bevy_cli.web] 37 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] 38 | default-features = false 39 | features = ["client", "netcode"] 40 | 41 | [lints] 42 | workspace = true 43 | -------------------------------------------------------------------------------- /lightyear_sync/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_sync" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "IO primitives for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = ["lightyear_transport/std"] 14 | client = [ 15 | "lightyear_connection/client", 16 | "lightyear_messages/client", 17 | "lightyear_transport/client", 18 | ] 19 | server = [ 20 | "lightyear_connection/server", 21 | "lightyear_messages/server", 22 | "lightyear_transport/server", 23 | ] 24 | 25 | [dependencies] 26 | lightyear_utils.workspace = true 27 | lightyear_serde.workspace = true 28 | lightyear_core.workspace = true 29 | lightyear_link.workspace = true 30 | lightyear_connection.workspace = true 31 | lightyear_messages.workspace = true 32 | lightyear_transport.workspace = true 33 | 34 | # utils 35 | tracing.workspace = true 36 | 37 | # serde 38 | serde.workspace = true 39 | 40 | # bevy 41 | bevy_app.workspace = true 42 | bevy_derive.workspace = true 43 | bevy_diagnostic.workspace = true 44 | bevy_ecs.workspace = true 45 | bevy_reflect.workspace = true 46 | bevy_time.workspace = true 47 | bevy_utils.workspace = true 48 | 49 | [dev-dependencies] 50 | test-log.workspace = true 51 | 52 | [lints] 53 | workspace = true 54 | 55 | [package.metadata.docs.rs] 56 | all-features = true 57 | -------------------------------------------------------------------------------- /examples/distributed_authority/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "distributed_authority" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition = "2021" 6 | description = "Examples for the lightyearServer-client networking library for the Bevy game engine" 7 | readme = "README.md" 8 | repository = "https://github.com/cBournhonesque/lightyear" 9 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 10 | categories = ["game-development", "network-programming"] 11 | license = "MIT OR Apache-2.0" 12 | publish = false 13 | 14 | [features] 15 | default = ["client", "gui", "server", "netcode", "udp"] 16 | client = ["gui", "lightyear_examples_common/client"] 17 | gui = ["lightyear_examples_common/gui"] 18 | server = ["lightyear_examples_common/server"] 19 | netcode = ["lightyear_examples_common/netcode"] 20 | udp = ["lightyear_examples_common/udp"] 21 | 22 | [[bin]] 23 | name = "distributed_authority" 24 | doc = false 25 | 26 | [dependencies] 27 | lightyear = { "workspace" = true, features = [ 28 | "interpolation", 29 | "prediction", 30 | "replication", 31 | "input_native", 32 | ] } 33 | lightyear_examples_common.workspace = true 34 | 35 | serde.workspace = true 36 | tracing.workspace = true 37 | bevy.workspace = true 38 | 39 | [package.metadata.bevy_cli.web] 40 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] 41 | default-features = false 42 | features = ["client", "netcode"] 43 | 44 | [lints] 45 | workspace = true 46 | -------------------------------------------------------------------------------- /book/src/concepts/advanced_replication/inputs.md: -------------------------------------------------------------------------------- 1 | # Input handling 2 | 3 | Lightyear handles inputs for you by: 4 | - buffering the last few inputs on both client and server 5 | - re-using the inputs from past ticks during rollback 6 | - sending client inputs to the server with redundancy 7 | 8 | 9 | ## Client-side 10 | 11 | Input handling is currently done in the FixedUpdate schedule. 12 | 13 | There are multiple `SystemSets` involved that should run in the following order: 14 | - `BufferInputs`: the user must add their inputs for the given tick in this system set 15 | - `WriteInputEvents`: we get the inputs for the current tick and return them as the bevy event `InputEvent` 16 | - notably, during rollback we get the inputs for the older rollback tick 17 | - `ClearInputEvents`: we clear the bevy events. 18 | - `SendInputMessage`: we prepare a message with the last few inputs. For redundancy, we will send the inputs of the last few frames, so that the server 19 | can still get the correct input for a given tick even if some packets are lost. 20 | 21 | 22 | 23 | ## Server-side 24 | 25 | Input handling is also done in the FixedUpdate schedule. 26 | These are the relevant `SystemSets`: 27 | - `WriteInputEvents`: we receive the input message from the client, add the inputs into an internal buffer. Then in this 28 | SystemSet we retrieve the inputs for the current tick for the given client. The retrieved inputs will be returned as `InputEvent` 29 | - `ClearInputEvents`: we clear the events -------------------------------------------------------------------------------- /examples/simple_box/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simple_box" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition = "2021" 6 | description = "Examples for the lightyearServer-client networking library for the Bevy game engine" 7 | readme = "README.md" 8 | repository = "https://github.com/cBournhonesque/lightyear" 9 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 10 | categories = ["game-development", "network-programming"] 11 | license = "MIT OR Apache-2.0" 12 | publish = false 13 | 14 | [features] 15 | default = ["client", "gui", "server", "netcode", "udp"] 16 | client = ["gui", "lightyear_examples_common/client"] 17 | gui = ["lightyear_examples_common/gui"] 18 | server = ["lightyear_examples_common/server"] 19 | netcode = ["lightyear_examples_common/netcode"] 20 | udp = ["lightyear_examples_common/udp"] 21 | steam = ["lightyear_examples_common/steam"] 22 | 23 | 24 | [dependencies] 25 | lightyear = { "workspace" = true, features = [ 26 | "interpolation", 27 | "prediction", 28 | "replication", 29 | "input_native", 30 | ] } 31 | lightyear_examples_common.workspace = true 32 | serde.workspace = true 33 | bevy.workspace = true 34 | 35 | 36 | [package.metadata.bevy_cli.web] 37 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] 38 | default-features = false 39 | features = ["client", "netcode"] 40 | 41 | [lints] 42 | workspace = true 43 | 44 | [lib] 45 | doc = false 46 | 47 | [[bin]] 48 | name = "simple_box" 49 | doc = false 50 | -------------------------------------------------------------------------------- /lightyear_transport/src/packet/mod.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Module to group messages into packets 3 | 4 | # Packet 5 | This module defines the concept of a `Packet` which is a byte array that will be sent over the network. 6 | A `Packet` has a maximum size that depends on the transport (around 1400 bytes for UDP), and is 7 | composed of a header and a payload. 8 | 9 | The header will compute important information such as the packet sequence number, the packet type, etc. 10 | as well as information to handle the ack system. 11 | 12 | The payload is a list of messages that are included in the packet. Messages will be included in the packet 13 | in order of [`Channel`] priority. 14 | 15 | Packets that are over the maximum packet size will be fragmented into multiple `FragmentData`. 16 | 17 | [`Channel`]: crate::channel::Channel 18 | */ 19 | 20 | /// Manages the [`PacketHeader`](header::PacketHeader) which includes important packet information 21 | pub(crate) mod header; 22 | 23 | pub mod message; 24 | 25 | // "module has the same name as its containing module" style nit. 26 | // clippy doesn't like this, but not much benefit to changing it now, so silence the warning. 27 | #[allow(clippy::module_inception)] 28 | pub mod packet; 29 | 30 | pub mod error; 31 | /// Manages building a single [`Packet`](packet::Packet) from multiple messages 32 | pub mod packet_builder; 33 | /// Defines the [`PacketType`](packet_type::PacketType) enum 34 | pub(crate) mod packet_type; 35 | pub mod priority_manager; 36 | pub(crate) mod stats_manager; 37 | -------------------------------------------------------------------------------- /lightyear_messages/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_messages" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "IO primitives for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = ["lightyear_transport/std"] 14 | client = ["lightyear_connection/client"] 15 | server = ["lightyear_link", "lightyear_connection/server"] 16 | test_utils = [] 17 | metrics = ["dep:metrics"] 18 | 19 | [dependencies] 20 | lightyear_link = { workspace = true, optional = true } 21 | lightyear_utils.workspace = true 22 | lightyear_serde.workspace = true 23 | lightyear_core.workspace = true 24 | lightyear_transport.workspace = true 25 | lightyear_connection.workspace = true 26 | 27 | # utils 28 | metrics = { workspace = true, optional = true } 29 | tracing.workspace = true 30 | thiserror.workspace = true 31 | 32 | # serde 33 | serde.workspace = true 34 | bytes.workspace = true 35 | 36 | # bevy 37 | bevy_app.workspace = true 38 | bevy_ecs = { workspace = true, features = ["serialize"] } 39 | bevy_reflect.workspace = true 40 | bevy_utils.workspace = true 41 | 42 | [dev-dependencies] 43 | lightyear_link.workspace = true 44 | lightyear_transport = { workspace = true, features = ["test_utils"] } 45 | test-log.workspace = true 46 | 47 | [lints] 48 | workspace = true 49 | 50 | [package.metadata.docs.rs] 51 | all-features = true 52 | -------------------------------------------------------------------------------- /examples/delta_compression/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "delta_compression" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition = "2021" 6 | description = "Examples for the lightyearServer-client networking library for the Bevy game engine" 7 | readme = "README.md" 8 | repository = "https://github.com/cBournhonesque/lightyear" 9 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 10 | categories = ["game-development", "network-programming"] 11 | license = "MIT OR Apache-2.0" 12 | publish = false 13 | 14 | [features] 15 | default = ["client", "gui", "server", "netcode", "udp"] 16 | client = ["gui", "lightyear/client", "lightyear_examples_common/client"] 17 | gui = ["lightyear_examples_common/gui"] 18 | server = ["lightyear/server", "lightyear_examples_common/server"] 19 | netcode = ["lightyear_examples_common/netcode"] 20 | udp = ["lightyear_examples_common/udp", "lightyear/udp"] 21 | 22 | [dependencies] 23 | lightyear = { "workspace" = true, features = [ 24 | "interpolation", 25 | "prediction", 26 | "replication", 27 | "input_native", 28 | ] } 29 | lightyear_examples_common.workspace = true 30 | serde.workspace = true 31 | tracing.workspace = true 32 | bevy.workspace = true 33 | 34 | [package.metadata.bevy_cli.web] 35 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] 36 | default-features = false 37 | features = ["client", "netcode"] 38 | 39 | 40 | [lints] 41 | workspace = true 42 | 43 | [[bin]] 44 | name = "delta_compression" 45 | doc = false 46 | -------------------------------------------------------------------------------- /lightyear_websocket/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_websocket" 3 | version = "0.25.5" 4 | authors = [ 5 | "cbournhonesque ", 7 | ] 8 | edition.workspace = true 9 | rust-version.workspace = true 10 | license.workspace = true 11 | description = "Websocket IO support for lightyear" 12 | repository = "https://github.com/cBournhonesque/lightyear" 13 | 14 | [lints] 15 | workspace = true 16 | 17 | [package.metadata.docs.rs] 18 | all-features = true 19 | rustflags = ["--cfg=web_sys_unstable_apis"] 20 | targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] 21 | 22 | [features] 23 | default = ["self-signed"] 24 | client = ["aeronet_websocket/client"] 25 | server = ["aeronet_websocket/server", "bevy_reflect/std"] 26 | 27 | ## Enables `wtransport/self-signed`, allowing you to generate self-signed certificates easily for 28 | ## use in a server. 29 | ## 30 | ## Note that, without explicitly allowing your server's self-signed certificate, clients will not be able to 31 | ## connect to a server with self-signed certificates. 32 | self-signed = ["aeronet_websocket/self-signed"] 33 | 34 | [dependencies] 35 | aeronet_websocket.workspace = true 36 | aeronet_io.workspace = true 37 | lightyear_link.workspace = true 38 | lightyear_aeronet.workspace = true 39 | 40 | tracing.workspace = true 41 | 42 | # bevy 43 | bevy_app.workspace = true 44 | bevy_ecs.workspace = true 45 | bevy_reflect = { workspace = true, optional = true } 46 | 47 | thiserror.workspace = true 48 | -------------------------------------------------------------------------------- /examples/simple_setup/README.md: -------------------------------------------------------------------------------- 1 | # Simple setup 2 | 3 | This minimal example shows how to create a bevy app with the lightyear client and server plugins. 4 | 5 | 6 | ## Running an example 7 | 8 | - Run the server with a gui: `cargo run -- server` 9 | - Run client with id 1: `cargo run -- client -c 1` 10 | 11 | [//]: # (- Run the client and server in two separate bevy Apps: `cargo run` or `cargo run separate`) 12 | - Run the server without a gui: `cargo run --no-default-features --features=server -- server` 13 | - Run the client and server in "HostClient" mode, where the client also acts as server (both are in the same App) : `cargo run -- host-client -c 0` 14 | 15 | You can control the behaviour of the example by changing the list of features. By default, all features are enabled (client, server, gui). 16 | For example you can run the server in headless mode (without gui) by running `cargo run --no-default-features --features=server,udp,netcode`. 17 | 18 | ### Testing in wasm with webtransport 19 | 20 | NOTE: I am using the [bevy cli](https://github.com/TheBevyFlock/bevy_cli) to build and serve the wasm example. 21 | 22 | To test the example in wasm, you can run the following commands: `bevy run web` 23 | 24 | You will need a valid SSL certificate to test the example in wasm using webtransport. You will need to run the following 25 | commands to generate a self-signed certificate: 26 | - `cd "$(git rev-parse --show-toplevel)" && sh certificates/generate.sh` (to generate the temporary SSL 27 | certificates, they are only valid for 2 weeks) 28 | -------------------------------------------------------------------------------- /examples/projectiles/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "projectiles" 3 | version = "0.25.5" 4 | rust-version.workspace = true 5 | edition.workspace = true 6 | authors = ["Charles Bournhonesque "] 7 | publish = false 8 | 9 | [features] 10 | default = ["client", "gui", "server", "netcode", "udp"] 11 | client = ["lightyear/client", "lightyear_examples_common/client"] 12 | gui = ["lightyear_examples_common/gui"] 13 | server = ["lightyear/server", "lightyear_examples_common/server"] 14 | netcode = ["lightyear_examples_common/netcode"] 15 | udp = ["lightyear_examples_common/udp", "lightyear/std", "lightyear/udp"] 16 | 17 | [dependencies] 18 | avian2d = { workspace = true, features = [ 19 | "2d", 20 | "debug-plugin", 21 | "f32", 22 | "parry-f32", 23 | "parallel", 24 | "serialize", 25 | ] } 26 | lightyear = { workspace = true, features = [ 27 | "crossbeam", 28 | "interpolation", 29 | "prediction", 30 | "replication", 31 | "avian2d", 32 | "input_bei", 33 | ] } 34 | bevy_enhanced_input.workspace = true 35 | lightyear_examples_common.workspace = true 36 | lightyear_frame_interpolation.workspace = true 37 | lightyear_avian2d = { workspace = true, features = ["2d", "lag_compensation"] } 38 | 39 | leafwing-input-manager.workspace = true 40 | serde.workspace = true 41 | bevy.workspace = true 42 | rand.workspace = true 43 | 44 | [package.metadata.bevy_cli.web] 45 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] 46 | default-features = false 47 | features = ["client", "netcode"] 48 | 49 | [lints] 50 | workspace = true 51 | -------------------------------------------------------------------------------- /examples/avian_3d_character/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "avian_3d_character" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [features] 9 | default = ["client", "gui", "server", "netcode", "udp"] 10 | client = ["gui", "lightyear/client", "lightyear_examples_common/client"] 11 | gui = ["lightyear_examples_common/gui"] 12 | server = ["lightyear/server", "lightyear_examples_common/server"] 13 | netcode = ["lightyear_examples_common/netcode"] 14 | udp = ["lightyear_examples_common/udp", "lightyear/udp"] 15 | 16 | [dependencies] 17 | lightyear = { workspace = true, features = [ 18 | "interpolation", 19 | "prediction", 20 | "replication", 21 | "leafwing", 22 | "avian3d", 23 | ] } 24 | lightyear_frame_interpolation.workspace = true 25 | lightyear_examples_common.workspace = true 26 | leafwing-input-manager = { workspace = true, default-features = false, features = [ 27 | "keyboard", 28 | "gamepad", 29 | ] } 30 | avian3d = { workspace = true, features = [ 31 | "3d", 32 | "f32", 33 | "parry-f32", 34 | "parallel", 35 | "serialize", 36 | ] } 37 | serde.workspace = true 38 | bevy.workspace = true 39 | 40 | [target."cfg(target_family = \"wasm\")".dependencies] 41 | console_error_panic_hook.workspace = true 42 | 43 | [package.metadata.bevy_cli.web] 44 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] 45 | default-features = false 46 | features = ["client", "netcode"] 47 | 48 | [lints] 49 | workspace = true 50 | 51 | [[bin]] 52 | name = "avian_3d_character" 53 | doc = false 54 | -------------------------------------------------------------------------------- /lightyear_transport/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_transport" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "Packet handling for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = ["bevy_ecs/std"] 14 | client = ["lightyear_connection/client"] 15 | server = ["lightyear_connection/server"] 16 | metrics = ["dep:metrics", "std", "lightyear_utils/metrics", "bevy_utils/debug"] 17 | trace = [] 18 | 19 | test_utils = [] 20 | 21 | [dependencies] 22 | # local crates 23 | lightyear_core.workspace = true 24 | lightyear_link.workspace = true 25 | lightyear_serde.workspace = true 26 | lightyear_utils.workspace = true 27 | lightyear_connection.workspace = true 28 | 29 | # bevy 30 | bevy_app.workspace = true 31 | bevy_ecs.workspace = true 32 | bevy_platform.workspace = true 33 | bevy_reflect.workspace = true 34 | bevy_time.workspace = true 35 | bevy_utils.workspace = true 36 | 37 | # serde 38 | serde.workspace = true 39 | 40 | # utils 41 | bytes.workspace = true 42 | crossbeam-channel.workspace = true 43 | enum_dispatch.workspace = true 44 | indexmap.workspace = true 45 | governor.workspace = true 46 | metrics = { workspace = true, optional = true } 47 | nonzero_ext.workspace = true 48 | ringbuffer.workspace = true 49 | thiserror.workspace = true 50 | tracing.workspace = true 51 | 52 | [lints] 53 | workspace = true 54 | 55 | [package.metadata.docs.rs] 56 | all-features = true 57 | -------------------------------------------------------------------------------- /examples/lobby/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lobby" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition = "2021" 6 | description = "Examples for the lightyearServer-client networking library for the Bevy game engine" 7 | readme = "README.md" 8 | repository = "https://github.com/cBournhonesque/lightyear" 9 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 10 | categories = ["game-development", "network-programming"] 11 | license = "MIT OR Apache-2.0" 12 | publish = false 13 | 14 | [features] 15 | default = ["client", "gui", "server", "netcode", "udp"] 16 | client = [ 17 | "gui", 18 | "lightyear/client", 19 | "lightyear_examples_common/client", 20 | # Lobby example needs server functionality on the client side too 21 | "lightyear/server", 22 | "lightyear_examples_common/server", 23 | ] 24 | gui = ["lightyear_examples_common/gui"] 25 | server = ["lightyear/server", "lightyear_examples_common/server"] 26 | netcode = ["lightyear_examples_common/netcode"] 27 | udp = ["lightyear_examples_common/udp", "lightyear/std", "lightyear/udp"] 28 | 29 | [dependencies] 30 | lightyear = { workspace = true, features = [ 31 | "interpolation", 32 | "prediction", 33 | "replication", 34 | "leafwing", 35 | "input_native", 36 | ] } 37 | lightyear_examples_common.workspace = true 38 | 39 | bevy_egui.workspace = true 40 | egui_extras.workspace = true 41 | serde.workspace = true 42 | tracing.workspace = true 43 | bevy.workspace = true 44 | 45 | [lints] 46 | workspace = true 47 | 48 | [[bin]] 49 | name = "lobby" 50 | doc = false 51 | -------------------------------------------------------------------------------- /lightyear_inputs/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! # Lightyear IO 2 | 3 | Low-level IO primitives for the lightyear networking library. 4 | This crate provides abstractions for sending and receiving raw bytes over the network. 5 | */ 6 | #![no_std] 7 | 8 | extern crate alloc; 9 | extern crate core; 10 | #[cfg(feature = "std")] 11 | extern crate std; 12 | 13 | #[cfg(feature = "client")] 14 | pub mod client; 15 | 16 | pub mod config; 17 | pub mod input_buffer; 18 | pub mod input_message; 19 | pub mod plugin; 20 | #[cfg(feature = "server")] 21 | pub mod server; 22 | 23 | pub(crate) const HISTORY_DEPTH: u16 = 20; 24 | 25 | /// Default channel to send inputs from client to server. This is a Sequenced Unreliable channel. 26 | /// A marker struct for the default channel used to send inputs from client to server. 27 | /// 28 | /// This channel is typically configured as a Sequenced Unreliable channel, 29 | /// suitable for sending frequent, time-sensitive input data where occasional loss 30 | /// is acceptable and out-of-order delivery is handled by sequencing. 31 | pub struct InputChannel; 32 | 33 | pub mod prelude { 34 | pub use crate::InputChannel; 35 | pub use crate::config::InputConfig; 36 | pub use crate::input_buffer::InputBuffer; 37 | 38 | #[cfg(feature = "client")] 39 | pub mod client { 40 | pub use crate::client::{ClientInputPlugin, InputSystems}; 41 | } 42 | #[cfg(feature = "server")] 43 | pub mod server { 44 | pub use crate::server::{ 45 | InputRebroadcaster, InputSystems, ServerInputConfig, ServerInputPlugin, 46 | }; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lightyear_replication/src/registry/mod.rs: -------------------------------------------------------------------------------- 1 | use alloc::string::String; 2 | use bevy_reflect::Reflect; 3 | use core::any::TypeId; 4 | use lightyear_core::network::NetId; 5 | use lightyear_serde::SerializationError; 6 | use lightyear_utils::registry::TypeKind; 7 | 8 | pub mod buffered; 9 | mod delta; 10 | pub mod registry; 11 | pub mod replication; 12 | 13 | #[cfg(feature = "deterministic")] 14 | pub mod deterministic; 15 | 16 | pub type ComponentNetId = NetId; 17 | 18 | #[derive(thiserror::Error, Debug)] 19 | pub enum ComponentError { 20 | #[error("component is not registered in the protocol")] 21 | NotRegistered, 22 | #[error("missing replication functions for component")] 23 | MissingReplicationFns, 24 | #[error("missing serialization functions for component")] 25 | MissingSerializationFns, 26 | #[error("missing delta compression functions for component")] 27 | MissingDeltaFns, 28 | #[error("delta compression error: {0}")] 29 | DeltaCompressionError(String), 30 | #[error("component error: {0}")] 31 | SerializationError(#[from] SerializationError), 32 | } 33 | 34 | /// [`ComponentKind`] is an internal wrapper around the type of the component 35 | #[derive(Debug, Eq, Hash, Copy, Clone, PartialEq, Reflect)] 36 | pub struct ComponentKind(pub TypeId); 37 | 38 | impl ComponentKind { 39 | pub fn of() -> Self { 40 | Self(TypeId::of::()) 41 | } 42 | } 43 | 44 | impl TypeKind for ComponentKind {} 45 | 46 | impl From for ComponentKind { 47 | fn from(type_id: TypeId) -> Self { 48 | Self(type_id) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /benches/divan/src/message.rs: -------------------------------------------------------------------------------- 1 | //! Benchmark to measure the performance of replicating Entity spawns 2 | #![allow(unused_imports)] 3 | 4 | use lightyear::prelude::MessageSender; 5 | use lightyear_tests::protocol::{Channel1, StringMessage}; 6 | use lightyear_tests::stepper::{ClientServerStepper, StepperConfig}; 7 | 8 | use divan::{AllocProfiler, Bencher}; 9 | 10 | #[global_allocator] 11 | static ALLOC: AllocProfiler = AllocProfiler::system(); 12 | 13 | const NUM_MESSAGE: &[usize] = &[1, 10, 100, 1000]; 14 | const MESSAGE_LEN: &[usize] = &[1, 10, 100, 1000]; 15 | 16 | #[divan::bench( 17 | args = NUM_MESSAGE, 18 | consts = MESSAGE_LEN, 19 | )] 20 | /// Sending N message from server to channel, with a local io 21 | fn send_receive_simple_messages_to_one_client( 22 | bencher: Bencher, 23 | num_message: usize, 24 | ) { 25 | bencher 26 | .with_inputs(|| ClientServerStepper::from_config(StepperConfig::single())) 27 | .bench_values(|mut stepper| { 28 | for _ in 0..num_message { 29 | stepper 30 | .client_of_mut(0) 31 | .get_mut::>() 32 | .unwrap() 33 | .send::(StringMessage(['a'; N].iter().collect())); 34 | } 35 | stepper.frame_step(1); 36 | }); 37 | } 38 | 39 | // TODO: send_receive_long_message_to_one_client 40 | // TODO: send_receive_random_message_to_one_client (with fuzzing) 41 | // TODO: send_receive_simple_message_to_many_clients 42 | 43 | fn main() { 44 | divan::main(); 45 | } 46 | -------------------------------------------------------------------------------- /examples/priority/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_imports)] 2 | #![allow(unused_variables)] 3 | #![allow(dead_code)] 4 | use bevy::prelude::*; 5 | use core::time::Duration; 6 | use lightyear_examples_common::cli::{Cli, Mode}; 7 | use lightyear_examples_common::shared::FIXED_TIMESTEP_HZ; 8 | 9 | #[cfg(feature = "client")] 10 | use crate::client::ExampleClientPlugin; 11 | #[cfg(feature = "server")] 12 | use crate::server::ExampleServerPlugin; 13 | use crate::shared::SharedPlugin; 14 | 15 | #[cfg(feature = "client")] 16 | mod client; 17 | mod protocol; 18 | #[cfg(feature = "gui")] 19 | mod renderer; 20 | #[cfg(feature = "server")] 21 | mod server; 22 | mod shared; 23 | 24 | fn main() { 25 | let cli = Cli::default(); 26 | 27 | let mut app = cli.build_app(Duration::from_secs_f64(1.0 / FIXED_TIMESTEP_HZ), true); 28 | 29 | app.add_plugins(SharedPlugin); 30 | 31 | cli.spawn_connections(&mut app); 32 | 33 | match cli.mode { 34 | #[cfg(feature = "client")] 35 | Some(Mode::Client { .. }) => { 36 | app.add_plugins(ExampleClientPlugin); 37 | } 38 | #[cfg(feature = "server")] 39 | Some(Mode::Server) => { 40 | app.add_plugins(ExampleServerPlugin); 41 | } 42 | #[cfg(all(feature = "client", feature = "server"))] 43 | Some(Mode::HostClient { client_id }) => { 44 | app.add_plugins(ExampleClientPlugin); 45 | app.add_plugins(ExampleServerPlugin); 46 | } 47 | _ => {} 48 | } 49 | 50 | #[cfg(feature = "gui")] 51 | app.add_plugins(renderer::ExampleRendererPlugin); 52 | 53 | app.run(); 54 | } 55 | -------------------------------------------------------------------------------- /.github/workflows/website.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy book 2 | on: 3 | push: 4 | paths: 5 | - "book/**" 6 | branches: 7 | - main 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write # To push a branch 14 | pages: write # To push to a GitHub Pages site 15 | id-token: write # To update the deployment status 16 | steps: 17 | - uses: actions/checkout@v6 18 | with: 19 | fetch-depth: 0 20 | - name: Install latest mdbook 21 | run: | 22 | tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name') 23 | url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz" 24 | mkdir mdbook 25 | curl -sSL $url | tar -xz --directory=./mdbook 26 | echo `pwd`/mdbook >> $GITHUB_PATH 27 | - name: Install stable toolchain 28 | uses: dtolnay/rust-toolchain@stable 29 | - name: Install mdbook-mermaid 30 | run: cargo install mdbook-mermaid 31 | - name: Install mdbook-linkcheck 32 | run: cargo install mdbook-linkcheck 33 | - name: Build Book 34 | run: | 35 | cd book 36 | mdbook build 37 | - name: Setup Pages 38 | uses: actions/configure-pages@v5 39 | - name: Upload artifact 40 | uses: actions/upload-pages-artifact@v4 41 | with: 42 | # Upload entire repository 43 | path: book 44 | - name: Deploy to GitHub Pages 45 | id: deployment 46 | uses: actions/deploy-pages@v4 47 | -------------------------------------------------------------------------------- /examples/bevy_enhanced_inputs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bevy_enhanced_inputs" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition = "2021" 6 | description = "Examples for the lightyear networking library for the Bevy game engine" 7 | readme = "README.md" 8 | repository = "https://github.com/cBournhonesque/lightyear" 9 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 10 | categories = ["game-development", "network-programming"] 11 | license = "MIT OR Apache-2.0" 12 | publish = false 13 | 14 | [features] 15 | default = ["client", "gui", "server", "netcode", "udp"] 16 | client = ["gui", "lightyear/client", "lightyear_examples_common/client"] 17 | gui = ["lightyear_examples_common/gui"] 18 | server = ["lightyear/server", "lightyear_examples_common/server"] 19 | netcode = ["lightyear_examples_common/netcode"] 20 | udp = [ 21 | "lightyear_examples_common/udp", 22 | # udp requires std 23 | "lightyear/std", 24 | "lightyear/udp", 25 | ] 26 | 27 | 28 | [dependencies] 29 | lightyear = { "workspace" = true, features = [ 30 | "interpolation", 31 | "prediction", 32 | "replication", 33 | "input_bei", 34 | ] } 35 | bevy_enhanced_input.workspace = true 36 | lightyear_examples_common.workspace = true 37 | serde.workspace = true 38 | bevy.workspace = true 39 | 40 | [package.metadata.bevy_cli.web] 41 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] 42 | default-features = false 43 | features = ["client", "netcode"] 44 | 45 | [lints] 46 | workspace = true 47 | 48 | [lib] 49 | doc = false 50 | 51 | [[bin]] 52 | name = "bevy_enhanced_inputs" 53 | doc = false 54 | -------------------------------------------------------------------------------- /benches/criterion/src/replication_profiling.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use lightyear::prelude::{NetworkTarget, Replicate}; 3 | use lightyear_tests::protocol::CompFull; 4 | use lightyear_tests::stepper::{ClientServerStepper, StepperConfig}; 5 | use std::fs::File; 6 | 7 | const NUM_FRAMES: usize = 100; 8 | const N: usize = 10; 9 | 10 | const NUM_ENTITIES: usize = 1000; 11 | fn main() { 12 | let mut stepper = ClientServerStepper::from_config(StepperConfig::with_netcode_clients(N)); 13 | let components = vec![(CompFull(0.0), Replicate::to_clients(NetworkTarget::All)); NUM_ENTITIES]; 14 | let entities = stepper 15 | .server_app 16 | .world_mut() 17 | .spawn_batch(components) 18 | .collect::>(); 19 | 20 | // advance time by one frame 21 | stepper.advance_time(stepper.frame_duration); 22 | 23 | let guard = pprof::ProfilerGuardBuilder::default() 24 | .frequency(10000) 25 | .blocklist(&["libc", "libgcc", "pthread", "vdso"]) 26 | .build() 27 | .unwrap(); 28 | 29 | for _ in 0..NUM_FRAMES { 30 | stepper.frame_step_server_first(1); 31 | 32 | // update the component on the server 33 | entities.iter().for_each(|entity| { 34 | stepper 35 | .server_app 36 | .world_mut() 37 | .get_mut::(*entity) 38 | .unwrap() 39 | .0 += 1.0; 40 | }); 41 | } 42 | 43 | if let Ok(report) = guard.report().build() { 44 | let file = File::create("flamegraph.svg").unwrap(); 45 | report.flamegraph(file).unwrap(); 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /lightyear_avian2d/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_avian2d" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | rust-version.workspace = true 6 | edition.workspace = true 7 | description = "Helper library to use Lightyear with Avian2d" 8 | readme = "../README.md" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 11 | categories = ["game-development", "network-programming"] 12 | license.workspace = true 13 | publish = true 14 | 15 | [lib] 16 | name = "lightyear_avian2d" 17 | path = "../lightyear_avian/src/lib.rs" 18 | required-features = ["2d"] 19 | bench = false 20 | 21 | [features] 22 | default = ["std", "2d"] 23 | 2d = ["avian2d/2d", "avian2d/parry-f32"] 24 | std = ["lightyear_prediction/std"] 25 | deterministic = ["dep:seahash"] 26 | lag_compensation = [] 27 | 28 | [dependencies] 29 | lightyear_core.workspace = true 30 | lightyear_link.workspace = true 31 | lightyear_prediction.workspace = true 32 | lightyear_interpolation.workspace = true 33 | lightyear_frame_interpolation.workspace = true 34 | lightyear_replication = { workspace = true, features = ["avian2d"] } 35 | 36 | avian2d.workspace = true 37 | 38 | tracing.workspace = true 39 | 40 | # bevy 41 | bevy_app.workspace = true 42 | bevy_ecs.workspace = true 43 | bevy_math.workspace = true 44 | bevy_time.workspace = true 45 | bevy_transform = { workspace = true, features = ["bevy-support", "libm"] } 46 | bevy_utils.workspace = true 47 | 48 | # utils 49 | seahash = { workspace = true, optional = true } 50 | 51 | [lints] 52 | workspace = true 53 | 54 | [package.metadata.docs.rs] 55 | all-features = true 56 | -------------------------------------------------------------------------------- /lightyear_avian3d/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_avian3d" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | rust-version.workspace = true 6 | edition.workspace = true 7 | description = "Helper library to use Lightyear with Avian3d" 8 | readme = "../README.md" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 11 | categories = ["game-development", "network-programming"] 12 | license.workspace = true 13 | publish = true 14 | 15 | [lib] 16 | name = "lightyear_avian3d" 17 | path = "../lightyear_avian/src/lib.rs" 18 | required-features = ["3d"] 19 | bench = false 20 | 21 | [features] 22 | default = ["std", "3d"] 23 | 3d = ["avian3d/3d", "avian3d/parry-f32"] 24 | std = ["lightyear_prediction/std"] 25 | deterministic = ["dep:seahash"] 26 | lag_compensation = [] 27 | 28 | [dependencies] 29 | lightyear_core.workspace = true 30 | lightyear_link.workspace = true 31 | lightyear_prediction.workspace = true 32 | lightyear_interpolation.workspace = true 33 | lightyear_frame_interpolation.workspace = true 34 | lightyear_replication = { workspace = true, features = ["avian3d"] } 35 | 36 | avian3d.workspace = true 37 | 38 | 39 | # bevy 40 | bevy_app.workspace = true 41 | bevy_ecs.workspace = true 42 | bevy_math.workspace = true 43 | bevy_time.workspace = true 44 | bevy_transform = { workspace = true, features = ["bevy-support", "libm"] } 45 | bevy_utils.workspace = true 46 | 47 | # utils 48 | tracing.workspace = true 49 | seahash = { workspace = true, optional = true } 50 | 51 | [lints] 52 | workspace = true 53 | 54 | [package.metadata.docs.rs] 55 | all-features = true 56 | -------------------------------------------------------------------------------- /lightyear_transport/src/channel/README.md: -------------------------------------------------------------------------------- 1 | Senders: 2 | 3 | - can be reliable (by keeping track of the messages that were not acked in time) 4 | - or unreliable (just send the messages and forget about it) 5 | - sequenced: send the message id 6 | - unordered: don't even include the message id 7 | 8 | Receivers: 9 | 10 | - reliable: make sure we receive every single message. That means we must receive message 0, 1, 2, ... etc. We buffer 11 | all 12 | messages received that are more recent than the next one we need 13 | - ordered: the next one we need progresses sequentially (1, 2, 3). We return messages in order 14 | - sequenced: // TODO? the next one we need progresses sequentially, unless we receive a more recent message; then we 15 | start from there 16 | - unordered: the next one we need progresses sequentially. We return messages from the buffer in any order. 17 | - unreliable: 18 | - sequenced: just receive the messages, but ignore ones that are older than the most recent message 19 | - unordered: just receive the messages 20 | 21 | Fragmentation: 22 | 23 | - let's only store raw bytes in MessageContainer, and read_messages will return a M 24 | - channels are aware of the message size limit, and they will include a message-id if the message will be fragmented 25 | - this indicates that the channels are responsible for fragmentation? 26 | - at least they could choose to fragment a message into fragments 27 | - MessageContainer would then be an enum (fragmented or not fragmented) 28 | - packet manager can then choose all message containers and just add them to packets easily, using bin packing 29 | algorithm? -------------------------------------------------------------------------------- /examples/replication_groups/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "replication_groups" 3 | version = "0.25.5" 4 | authors = ["Charles Bournhonesque "] 5 | edition = "2021" 6 | description = "Examples for the lightyearServer-client networking library for the Bevy game engine" 7 | readme = "README.md" 8 | repository = "https://github.com/cBournhonesque/lightyear" 9 | keywords = ["bevy", "multiplayer", "networking", "netcode", "gamedev"] 10 | categories = ["game-development", "network-programming"] 11 | license = "MIT OR Apache-2.0" 12 | publish = false 13 | 14 | 15 | [features] 16 | default = ["client", "gui", "server", "netcode", "udp"] 17 | client = ["gui", "lightyear/client", "lightyear_examples_common/client"] 18 | gui = ["lightyear_examples_common/gui"] 19 | server = ["lightyear/server", "lightyear_examples_common/server"] 20 | netcode = ["lightyear_examples_common/netcode"] 21 | udp = [ 22 | "lightyear_examples_common/udp", 23 | # udp requires std 24 | "lightyear/std", 25 | "lightyear/udp", 26 | ] 27 | 28 | [dependencies] 29 | lightyear = { "workspace" = true, features = [ 30 | "interpolation", 31 | "prediction", 32 | "replication", 33 | "input_native", 34 | ] } 35 | lightyear_frame_interpolation.workspace = true 36 | lightyear_examples_common.workspace = true 37 | 38 | serde.workspace = true 39 | tracing.workspace = true 40 | bevy.workspace = true 41 | 42 | [package.metadata.bevy_cli.web] 43 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] 44 | default-features = false 45 | features = ["client", "netcode"] 46 | 47 | [lints] 48 | workspace = true 49 | 50 | 51 | [[bin]] 52 | name = "replication_groups" 53 | doc = false 54 | -------------------------------------------------------------------------------- /lightyear_prediction/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_prediction" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "IO primitives for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = ["lightyear_replication/std", "bevy_math/std"] 14 | deterministic = [] 15 | server = ["dep:lightyear_messages"] 16 | metrics = ["dep:metrics", "std"] 17 | 18 | [dependencies] 19 | lightyear_utils.workspace = true 20 | lightyear_core = { workspace = true } 21 | lightyear_connection = { workspace = true } 22 | lightyear_replication = { workspace = true, features = [ 23 | "prediction", 24 | "interpolation", 25 | ] } 26 | lightyear_sync.workspace = true 27 | lightyear_messages = { workspace = true, optional = true } 28 | lightyear_frame_interpolation.workspace = true 29 | lightyear_interpolation.workspace = true 30 | 31 | # utils 32 | metrics = { workspace = true, optional = true } 33 | seahash.workspace = true 34 | parking_lot.workspace = true 35 | tracing.workspace = true 36 | 37 | # serde 38 | serde.workspace = true 39 | 40 | # bevy 41 | bevy_app.workspace = true 42 | bevy_diagnostic.workspace = true 43 | bevy_derive.workspace = true 44 | bevy_ecs.workspace = true 45 | bevy_math = { workspace = true, features = ["curve"] } 46 | bevy_platform.workspace = true 47 | bevy_reflect.workspace = true 48 | bevy_time.workspace = true 49 | bevy_utils.workspace = true 50 | 51 | [lints] 52 | workspace = true 53 | 54 | [package.metadata.docs.rs] 55 | all-features = true 56 | -------------------------------------------------------------------------------- /demos/spaceships/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spaceships" 3 | version.workspace = true 4 | authors = [ 5 | "Charles Bournhonesque ", 6 | "Richard Jones ", 7 | ] 8 | edition.workspace = true 9 | publish = false 10 | 11 | [features] 12 | default = ["client", "gui", "server", "netcode", "udp"] 13 | client = ["gui", "lightyear/client", "lightyear_examples_common/client"] 14 | gui = ["lightyear_examples_common/gui", "bevy/bevy_post_process"] 15 | server = ["lightyear/server", "lightyear_examples_common/server"] 16 | netcode = ["lightyear_examples_common/netcode"] 17 | udp = [ 18 | "lightyear_examples_common/udp", 19 | # udp requires std 20 | "lightyear/std", 21 | "lightyear/udp", 22 | ] 23 | 24 | [dependencies] 25 | lightyear = { workspace = true, features = [ 26 | "interpolation", 27 | "prediction", 28 | "replication", 29 | "leafwing", 30 | "avian2d", 31 | ] } 32 | lightyear_frame_interpolation.workspace = true 33 | lightyear_examples_common.workspace = true 34 | leafwing-input-manager = { workspace = true, features = ["keyboard", "mouse"] } 35 | avian2d = { workspace = true, features = [ 36 | "2d", 37 | "f32", 38 | "parry-f32", 39 | "parallel", 40 | "serialize", 41 | ] } 42 | serde.workspace = true 43 | tracing.workspace = true 44 | tracing-subscriber.workspace = true 45 | bevy = { workspace = true, features = ["bevy_state"] } 46 | 47 | [package.metadata.bevy_cli.web] 48 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] 49 | default-features = false 50 | features = ["client", "netcode"] 51 | 52 | [lints] 53 | workspace = true 54 | 55 | 56 | [[bin]] 57 | name = "spaceships" 58 | doc = false 59 | -------------------------------------------------------------------------------- /examples/bevy_enhanced_inputs/README.md: -------------------------------------------------------------------------------- 1 | # Bevy enhanced inputs 2 | 3 | A simple example that shows how to network inputs from the `bevy_enhanced_input` crate along with lightyear! 4 | 5 | https://github.com/cBournhonesque/lightyear/assets/8112632/7b57d48a-d8b0-4cdd-a16f-f991a394c852 6 | 7 | ## Running an example 8 | 9 | - Run the server with a gui: `cargo run -- server` 10 | - Run client with id 1: `cargo run -- client -c 1` 11 | 12 | [//]: # (- Run the client and server in two separate bevy Apps: `cargo run` or `cargo run separate`) 13 | - Run the server without a gui: `cargo run --no-default-features --features=server -- server` 14 | - Run the client and server in "HostClient" mode, where the client also acts as server (both are in the same App) : `cargo run -- host-client -c 0` 15 | 16 | You can control the behaviour of the example by changing the list of features. By default, all features are enabled (client, server, gui). 17 | For example you can run the server in headless mode (without gui) by running `cargo run --no-default-features --features=server,udp,netcode`. 18 | 19 | ### Testing in wasm with webtransport 20 | 21 | NOTE: I am using the [bevy cli](https://github.com/TheBevyFlock/bevy_cli) to build and serve the wasm example. 22 | 23 | To test the example in wasm, you can run the following commands: `bevy run web` 24 | 25 | You will need a valid SSL certificate to test the example in wasm using webtransport. You will need to run the following 26 | commands to generate a self-signed certificate: 27 | - `cd "$(git rev-parse --show-toplevel)" && sh certificates/generate.sh` (to generate the temporary SSL 28 | certificates, they are only valid for 2 weeks) 29 | -------------------------------------------------------------------------------- /examples/replication_groups/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::all)] 2 | #![allow(unused_imports)] 3 | #![allow(unused_variables)] 4 | #![allow(dead_code)] 5 | 6 | #[cfg(feature = "client")] 7 | use crate::client::ExampleClientPlugin; 8 | #[cfg(feature = "server")] 9 | use crate::server::ExampleServerPlugin; 10 | use crate::shared::SharedPlugin; 11 | use bevy::prelude::*; 12 | use core::time::Duration; 13 | use lightyear_examples_common::cli::{Cli, Mode}; 14 | use lightyear_examples_common::shared::FIXED_TIMESTEP_HZ; 15 | 16 | #[cfg(feature = "client")] 17 | mod client; 18 | mod protocol; 19 | 20 | #[cfg(feature = "gui")] 21 | mod renderer; 22 | #[cfg(feature = "server")] 23 | mod server; 24 | mod shared; 25 | 26 | fn main() { 27 | let cli = Cli::default(); 28 | 29 | let mut app = cli.build_app(Duration::from_secs_f64(1.0 / FIXED_TIMESTEP_HZ), true); 30 | 31 | app.add_plugins(SharedPlugin); 32 | 33 | cli.spawn_connections(&mut app); 34 | 35 | match cli.mode { 36 | #[cfg(feature = "client")] 37 | Some(Mode::Client { .. }) => { 38 | app.add_plugins(ExampleClientPlugin); 39 | } 40 | #[cfg(feature = "server")] 41 | Some(Mode::Server) => { 42 | app.add_plugins(ExampleServerPlugin); 43 | } 44 | #[cfg(all(feature = "client", feature = "server"))] 45 | Some(Mode::HostClient { client_id }) => { 46 | app.add_plugins(ExampleClientPlugin); 47 | app.add_plugins(ExampleServerPlugin); 48 | } 49 | _ => {} 50 | } 51 | 52 | #[cfg(feature = "gui")] 53 | app.add_plugins(renderer::ExampleRendererPlugin); 54 | 55 | app.run(); 56 | } 57 | -------------------------------------------------------------------------------- /lightyear_interpolation/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Handles interpolation of entities between server updates 2 | #![no_std] 3 | 4 | extern crate alloc; 5 | #[cfg(feature = "std")] 6 | extern crate std; 7 | 8 | use bevy_ecs::component::{Component, Mutable}; 9 | 10 | mod despawn; 11 | /// Contains interpolation logic. 12 | pub mod interpolate; 13 | /// Defines `ConfirmedHistory` for storing historical states of confirmed entities. 14 | pub mod interpolation_history; 15 | /// Provides the `InterpolationPlugin` and related systems for Bevy integration. 16 | pub mod plugin; 17 | pub mod registry; 18 | pub mod timeline; 19 | 20 | /// Commonly used items for client-side interpolation. 21 | pub mod prelude { 22 | pub use crate::Interpolated; 23 | pub use crate::interpolate::interpolation_fraction; 24 | pub use crate::interpolation_history::ConfirmedHistory; 25 | pub use crate::plugin::{InterpolationDelay, InterpolationPlugin, InterpolationSystems}; 26 | pub use crate::registry::{InterpolationRegistrationExt, InterpolationRegistry}; 27 | pub use crate::timeline::InterpolationTimeline; 28 | } 29 | 30 | pub use lightyear_core::interpolation::Interpolated; 31 | 32 | /// Trait for components that can be synchronized for interpolation. 33 | /// 34 | /// This is a marker trait, requiring `Component + Clone + PartialEq`. 35 | /// Components implementing this trait can have their state managed by the interpolation systems 36 | /// according to the specified `InterpolationMode`. 37 | pub trait SyncComponent: Component + Clone + PartialEq {} 38 | impl SyncComponent for T where T: Component + Clone + PartialEq {} 39 | -------------------------------------------------------------------------------- /examples/fps/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::all)] 2 | #![allow(unused_imports)] 3 | #![allow(unused_variables)] 4 | #![allow(dead_code)] 5 | use bevy::prelude::*; 6 | use core::time::Duration; 7 | use lightyear_examples_common::cli::{Cli, Mode}; 8 | use lightyear_examples_common::shared::FIXED_TIMESTEP_HZ; 9 | 10 | #[cfg(feature = "client")] 11 | use crate::client::ExampleClientPlugin; 12 | #[cfg(feature = "server")] 13 | use crate::server::ExampleServerPlugin; 14 | use crate::shared::SharedPlugin; 15 | 16 | #[cfg(feature = "client")] 17 | mod client; 18 | mod protocol; 19 | 20 | #[cfg(feature = "gui")] 21 | mod renderer; 22 | 23 | #[cfg(feature = "server")] 24 | mod server; 25 | mod shared; 26 | 27 | fn main() { 28 | let cli = Cli::default(); 29 | 30 | let mut app = cli.build_app(Duration::from_secs_f64(1.0 / FIXED_TIMESTEP_HZ), true); 31 | 32 | app.add_plugins(SharedPlugin); 33 | 34 | cli.spawn_connections(&mut app); 35 | 36 | match cli.mode { 37 | #[cfg(feature = "client")] 38 | Some(Mode::Client { .. }) => { 39 | app.add_plugins(ExampleClientPlugin); 40 | } 41 | #[cfg(feature = "server")] 42 | Some(Mode::Server) => { 43 | app.add_plugins(ExampleServerPlugin); 44 | } 45 | #[cfg(all(feature = "client", feature = "server"))] 46 | Some(Mode::HostClient { client_id }) => { 47 | app.add_plugins(ExampleClientPlugin); 48 | app.add_plugins(ExampleServerPlugin); 49 | } 50 | _ => {} 51 | } 52 | 53 | #[cfg(feature = "gui")] 54 | app.add_plugins(renderer::ExampleRendererPlugin); 55 | 56 | // run the app 57 | app.run(); 58 | } 59 | -------------------------------------------------------------------------------- /lightyear_tests/justfile: -------------------------------------------------------------------------------- 1 | bin := "replicate" 2 | out := "my_capture.tracy" 3 | 4 | # run all tests in a single-threaded manner (necessary for MockTime to work properly) 5 | t: 6 | cargo t 7 | 8 | tracy bin=bin out=out: 9 | rm -f {{out}} 10 | tracy-capture -o {{out}} -f & 11 | RUSTFLAGS='-C force-frame-pointers=y -C symbol-mangling-version=v0' cargo run --profile=profiling --features bevy/trace_tracy -- {{bin}} 12 | just wait_for_file {{out}} 13 | tracy-profiler {{out}} 14 | 15 | tracy-memory bin=bin out=out: 16 | rm -f {{out}} 17 | tracy-capture -o {{out}} -f & 18 | RUSTFLAGS='-C force-frame-pointers=y -C symbol-mangling-version=v0' cargo run --profile=profiling --features bevy/trace_tracy_memory -- {{bin}} 19 | just wait_for_file {{out}} 20 | tracy-profiler {{out}} 21 | 22 | flamegraph bin=bin: 23 | rm -rf cargo-flamegraph.trace 24 | RUSTFLAGS='-C force-frame-pointers=y' cargo flamegraph --open --profile=profiling --no-default-features --features=std,test_utils -- {{bin}} 25 | 26 | samply bin=bin: 27 | RUSTFLAGS='-C force-frame-pointers=y' cargo build --no-default-features --features=std,test_utils --profile=profiling --bin={{bin}} 28 | samply record ../target/profiling/{{bin}} 29 | 30 | # use MacOS Instruments. Profile can be either `time` or `Allocations` 31 | instruments bin=bin profile="time": 32 | RUSTFLAGS='-C force-frame-pointers=y' cargo instruments -t {{profile}} --profile=profiling --no-default-features --features=std,test_utils --bin=replicate 33 | 34 | 35 | 36 | ### Extras 37 | 38 | # Wait until file exists and is non-empty 39 | wait_for_file out: 40 | #!/bin/zsh 41 | until [ -s "{{out}}" ]; do 42 | sleep 0.5 43 | done -------------------------------------------------------------------------------- /examples/distributed_authority/src/renderer.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::*; 2 | use bevy::prelude::*; 3 | use lightyear::prelude::Confirmed; 4 | 5 | #[derive(Clone)] 6 | pub struct ExampleRendererPlugin; 7 | 8 | impl Plugin for ExampleRendererPlugin { 9 | fn build(&self, app: &mut App) { 10 | app.add_systems(Startup, init); 11 | app.add_systems(Update, draw_boxes); 12 | app.add_systems(Update, draw_ball); 13 | } 14 | } 15 | 16 | fn init(mut commands: Commands) { 17 | commands.spawn(Camera2d); 18 | } 19 | 20 | /// We draw: 21 | /// - on the server: we always draw the ball 22 | /// - on the client: we draw the interpolated ball (when the client has authority, 23 | /// the confirmed updates are added to the component history, instead of the server updates) 24 | // TODO: it can be a bit tedious to have the check if we want to draw the interpolated or the confirmed ball. 25 | // if we have authority, should the interpolated ball become the same as Confirmed? 26 | pub(crate) fn draw_ball( 27 | mut gizmos: Gizmos, 28 | balls: Query<(&Position, &PlayerColor), With>, 29 | ) { 30 | for (position, color) in balls.iter() { 31 | gizmos.circle_2d(position.0, 25.0, color.0); 32 | } 33 | } 34 | 35 | /// System that draws the boxes of the player positions. 36 | /// The components should be replicated from the server to the client 37 | pub(crate) fn draw_boxes( 38 | mut gizmos: Gizmos, 39 | players: Query<(&Position, &PlayerColor), Without>, 40 | ) { 41 | for (position, color) in &players { 42 | gizmos.rect_2d( 43 | Isometry2d::from_translation(position.0), 44 | Vec2::ONE * 50.0, 45 | color.0, 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /book/src/concepts/reliability/channels.md: -------------------------------------------------------------------------------- 1 | # Channels 2 | 3 | 4 | Lightyear introduces the concept of a `Channel` to handle reliability. 5 | 6 | A `Channel` is a way to send packets with specific reliability, ordering and priority guarantees. 7 | 8 | You can add a channel to your protocol like so: 9 | ```rust,noplayground 10 | #[derive(Channel)] 11 | struct MyChannel; 12 | 13 | pub fn protocol() -> MyProtocol { 14 | let mut p = MyProtocol::default(); 15 | p.add_channel::(ChannelSettings { 16 | mode: ChannelMode::OrderedReliable(ReliableSettings::default()), 17 | direction: ChannelDirection::Bidirectional, 18 | }); 19 | p 20 | } 21 | ``` 22 | 23 | ## Mode 24 | 25 | The `mode` field of `ChannelSettings` defines the reliability/ordering guarantees of the channel. 26 | 27 | Reliability: 28 | - `Unreliable`: packets are not guaranteed to arrive 29 | - `Reliable`: packets are guaranteed to arrive. We will resend the packet until we receive an acknowledgement from the remote. 30 | You can define how often we resend the packet via the `ReliableSettings` field. 31 | 32 | Ordering: 33 | - `Ordered`: packets are guaranteed to arrive in the order they were sent (*client sends 1,2,3,4,5, server receives 1,2,3,4,5*) 34 | - `Unordered`: packets are not guaranteed to arrive in the order they were sent (*client sends 1,2,3,4,5, server receives 1,3,2,5,4*) 35 | - `Sequenced`: packets are not guaranteed to arrive in the order they were sent, but we will discard packets that are older than the last received packet (*client sends 1,2,3,4,5, server receives 1,3,5 (2 and 4 are discarded)*) 36 | 37 | 38 | ## Direction 39 | 40 | The `direction` field can be used to restrict a `Channel` from sending packets from client->server or server->client. -------------------------------------------------------------------------------- /lightyear_transport/src/client.rs: -------------------------------------------------------------------------------- 1 | use crate::channel::Channel; 2 | use crate::channel::registry::ChannelRegistration; 3 | use crate::prelude::{ChannelRegistry, Transport}; 4 | use bevy_ecs::prelude::On; 5 | use bevy_ecs::prelude::*; 6 | use lightyear_connection::client::Client; 7 | use lightyear_connection::direction::NetworkDirection; 8 | 9 | pub(crate) fn add_sender_channel( 10 | trigger: On, 11 | mut query: Query<&mut Transport, With>, 12 | registry: Res, 13 | ) { 14 | if let Ok(mut transport) = query.get_mut(trigger.entity) { 15 | transport.add_sender_from_registry::(®istry) 16 | } 17 | } 18 | 19 | pub(crate) fn add_receiver_channel( 20 | trigger: On, 21 | mut query: Query<&mut Transport, With>, 22 | registry: Res, 23 | ) { 24 | if let Ok(mut transport) = query.get_mut(trigger.entity) { 25 | transport.add_receiver_from_registry::(®istry) 26 | } 27 | } 28 | 29 | impl ChannelRegistration<'_, C> { 30 | pub(crate) fn add_client_direction(&mut self, direction: NetworkDirection) { 31 | match direction { 32 | NetworkDirection::ClientToServer => { 33 | self.app.add_observer(add_sender_channel::); 34 | } 35 | NetworkDirection::ServerToClient => { 36 | self.app.add_observer(add_receiver_channel::); 37 | } 38 | NetworkDirection::Bidirectional => { 39 | self.add_client_direction(NetworkDirection::ClientToServer); 40 | self.add_client_direction(NetworkDirection::ServerToClient); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lightyear_sync/src/ping/store.rs: -------------------------------------------------------------------------------- 1 | //! Store the latest pings sent to remote 2 | 3 | use lightyear_core::time::Instant; 4 | use lightyear_utils::sequence_buffer::SequenceBuffer; 5 | use lightyear_utils::wrapping_id; 6 | 7 | wrapping_id!(PingId); 8 | 9 | const PING_BUFFER_SIZE: usize = 128; 10 | 11 | /// Data structure to store the latest pings sent to remote 12 | #[derive(Debug)] 13 | pub struct PingStore { 14 | /// ID that will be assigned to the next ping sent 15 | latest_ping_id: PingId, 16 | /// Buffer storing the latest pings sent along with their associated time 17 | /// Older pings will get overwritten by newer pings 18 | buffer: SequenceBuffer, 19 | } 20 | 21 | impl Default for PingStore { 22 | fn default() -> Self { 23 | Self::new() 24 | } 25 | } 26 | 27 | impl PingStore { 28 | pub fn new() -> Self { 29 | PingStore { 30 | latest_ping_id: PingId(0), 31 | buffer: SequenceBuffer::new(), 32 | } 33 | } 34 | 35 | /// Pushes a new ping into the store and returns the corresponding ping id 36 | pub fn push_new(&mut self, now: Instant) -> PingId { 37 | // save current ping index and add a new ping instant associated with it 38 | let ping_id = self.latest_ping_id; 39 | self.latest_ping_id += 1; 40 | self.buffer.push(&ping_id, now); 41 | ping_id 42 | } 43 | 44 | /// Remove a ping from the store and returns the corresponding time if it exists 45 | pub fn remove(&mut self, ping_id: PingId) -> Option { 46 | self.buffer.remove(&ping_id) 47 | } 48 | 49 | pub fn reset(&mut self) { 50 | self.latest_ping_id = PingId(0); 51 | self.buffer.clear(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ## [Unreleased] 5 | 6 | - Simplified the examples 7 | - Added the `visualizer` feature to get a egui dashboard showing a plot of various lightyear metrics 8 | - Removed `DisabledComponent::` in favor of `DisabledComponents` to have more control over 9 | which components are disabled. In particular, it is now possible to express 'disable all components except these'. 10 | - Made the `ServerConnections` resource private. You can now use `commands.disconnect(client_id)` to disconnect a client. 11 | - Enabled replicating events directly! 12 | - Add an `Event` to the protocol with `register_event` 13 | - Replicate an event and buffer it in EventWriter with `send_event` 14 | - Replicate an event and trigger it with `trigger_event` 15 | - Replaced `MessageEvent.context()` with `MessageContext.from()`, which returns the `ClientId` that send the message 16 | - State is correctly cleaned up when the server is stopped (the ControlledBy and Client entities are correctly despawned) 17 | - Parent-Child hierarchy is now synced properly to the Predicted/Interpolated entities 18 | - Fixed how prediction/interpolation interact with authority transfer. 19 | - For example in the use case where we spawn an entity on Client 1, replicate it to the server, then give the server authority over the entity, 20 | a Predicted/Interpolated entities will now get spawned correctly on client 1 21 | - Fixed some edge cases related to InterestManagement 22 | - Fixed a bug where ChannelDirection was not respected (a ClientToServer component would still get replicated from the server to the client) 23 | - Type-erased the receive-message systems so that we only have one `read_messages` system instead of one system per message type 24 | 25 | 26 | 27 | ## 0.18.0 - 2024-12-24 -------------------------------------------------------------------------------- /.github/actions/install-linux-deps/action.yml: -------------------------------------------------------------------------------- 1 | # This action installs a few dependencies necessary to build Bevy on Linux. By default it installs 2 | # alsa and udev, but can be configured depending on which libraries are needed: 3 | # 4 | # ``` 5 | # - uses: ./.github/actions/install-linux-deps 6 | # with: 7 | # alsa: false 8 | # wayland: true 9 | # ``` 10 | # 11 | # See the `inputs` section for all options and their defaults. Note that you must checkout the 12 | # repository before you can use this action. 13 | # 14 | # This action will only install dependencies when the current operating system is Linux. It will do 15 | # nothing on any other OS (macOS, Windows). 16 | 17 | name: Install Linux dependencies 18 | description: Installs the dependencies necessary to build Bevy on Linux. 19 | inputs: 20 | alsa: 21 | description: Install alsa (libasound2-dev) 22 | required: false 23 | default: "true" 24 | udev: 25 | description: Install udev (libudev-dev) 26 | required: false 27 | default: "true" 28 | wayland: 29 | description: Install Wayland (libwayland-dev) 30 | required: false 31 | default: "false" 32 | xkb: 33 | description: Install xkb (libxkbcommon-dev) 34 | required: false 35 | default: "false" 36 | runs: 37 | using: composite 38 | steps: 39 | - name: Install Linux dependencies 40 | shell: bash 41 | if: ${{ runner.os == 'linux' }} 42 | run: > 43 | sudo apt-get update 44 | 45 | sudo apt-get install --no-install-recommends 46 | ${{ fromJSON(inputs.alsa) && 'libasound2-dev' || '' }} 47 | ${{ fromJSON(inputs.udev) && 'libudev-dev' || '' }} 48 | ${{ fromJSON(inputs.wayland) && 'libwayland-dev' || '' }} 49 | ${{ fromJSON(inputs.xkb) && 'libxkbcommon-dev' || '' }} -------------------------------------------------------------------------------- /lightyear_transport/src/server.rs: -------------------------------------------------------------------------------- 1 | use crate::channel::Channel; 2 | use crate::channel::registry::ChannelRegistration; 3 | use crate::prelude::{ChannelRegistry, Transport}; 4 | use bevy_ecs::prelude::*; 5 | use lightyear_connection::client_of::ClientOf; 6 | use lightyear_connection::direction::NetworkDirection; 7 | 8 | pub(crate) fn add_sender_channel( 9 | trigger: On, 10 | mut query: Query<&mut Transport, With>, 11 | registry: Res, 12 | ) { 13 | if let Ok(mut transport) = query.get_mut(trigger.entity) { 14 | transport.add_sender_from_registry::(®istry) 15 | } 16 | } 17 | 18 | pub(crate) fn add_receiver_channel( 19 | trigger: On, 20 | mut query: Query<&mut Transport, With>, 21 | registry: Res, 22 | ) { 23 | if let Ok(mut transport) = query.get_mut(trigger.entity) { 24 | transport.add_receiver_from_registry::(®istry) 25 | } 26 | } 27 | 28 | impl ChannelRegistration<'_, C> { 29 | /// Add a new [`NetworkDirection`] to the registry 30 | pub(crate) fn add_server_direction(&mut self, direction: NetworkDirection) { 31 | match direction { 32 | NetworkDirection::ClientToServer => { 33 | self.app.add_observer(add_receiver_channel::); 34 | } 35 | NetworkDirection::ServerToClient => { 36 | self.app.add_observer(add_sender_channel::); 37 | } 38 | NetworkDirection::Bidirectional => { 39 | self.add_server_direction(NetworkDirection::ClientToServer); 40 | self.add_server_direction(NetworkDirection::ServerToClient); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /book/src/concepts/title.md: -------------------------------------------------------------------------------- 1 | # Concepts 2 | 3 | There are several layers that enable lightyear to act as a games networking library. 4 | Let's list them from the bottom up (closer to the wire): 5 | 6 | - IO: how do we send raw bytes over the network between two peers? 7 | The `Link` component can be added to an entity to interact with the IO layer. Usually you will directly 8 | add the io component itself (`WebTransportClientIO`, `UdpIO`, `CrossbeamIO`, etc.), which will add the `Link` component. 9 | 10 | - Transport: how do provide reliability/ordering guarantees for the bytes we want to send over the `Link`? 11 | The `Transport` component can be added to provide `Channels`. These channels can be used to define the send_frequency, 12 | priority, ordering, reliability characteristics for the bytes you want to send. 13 | 14 | - Messages: how do you go from raw bytes to rust types? 15 | The `MessageManager`/`MessageSender`/`MessageReceiver` components will be required to serialize/deserialize from rust types into 16 | raw bytes that you can send over the `Link` or `Transport`. It is also responsible for mapping Entities from the remote World to 17 | the local World. 18 | 19 | - Connection: how do we get a persistent connection on top of a link? 20 | A Link can be ephemeral, for example if it's simply an UDPSocket. Sometimes you want a more long-term identifier for the different 21 | peers that you are linked to. For example so that when a client disconnects and reconnects you can recognize them as the same client 22 | even if their socket port changed. 23 | Currently we have two layers that can give you a persistent connection: Netcode or Steam. 24 | 25 | - Replication: how do you replicate components between the remote World and the local World 26 | 27 | - advanced replication: prediction, interpolation, etc. 28 | -------------------------------------------------------------------------------- /lightyear_inputs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_inputs" 3 | version = "0.25.5" 4 | authors.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "IO primitives for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [features] 12 | default = ["std"] 13 | std = ["lightyear_transport/std"] 14 | client = [ 15 | "lightyear_connection/client", 16 | "lightyear_messages/client", 17 | "lightyear_sync/client", 18 | "lightyear_replication/client", 19 | "lightyear_transport/client", 20 | ] 21 | server = [ 22 | "lightyear_connection/server", 23 | "lightyear_messages/server", 24 | "lightyear_sync/server", 25 | "lightyear_link", 26 | "lightyear_transport/server", 27 | ] 28 | metrics = ["dep:metrics", "std"] 29 | prediction = [] 30 | interpolation = ["dep:lightyear_interpolation"] 31 | 32 | 33 | [dependencies] 34 | lightyear_connection.workspace = true 35 | lightyear_core.workspace = true 36 | lightyear_sync = { workspace = true, optional = true } 37 | lightyear_link = { workspace = true, optional = true } 38 | lightyear_messages.workspace = true 39 | lightyear_transport.workspace = true 40 | lightyear_replication.workspace = true 41 | lightyear_prediction = { workspace = true } 42 | lightyear_interpolation = { workspace = true, optional = true } 43 | 44 | # utils 45 | metrics = { workspace = true, optional = true } 46 | tracing.workspace = true 47 | 48 | # serde 49 | serde.workspace = true 50 | 51 | # bevy 52 | bevy_app.workspace = true 53 | bevy_ecs.workspace = true 54 | bevy_platform.workspace = true 55 | bevy_reflect.workspace = true 56 | bevy_utils.workspace = true 57 | 58 | [lints] 59 | workspace = true 60 | 61 | [package.metadata.docs.rs] 62 | all-features = true 63 | -------------------------------------------------------------------------------- /lightyear_messages/src/client.rs: -------------------------------------------------------------------------------- 1 | use crate::Message; 2 | use crate::prelude::{MessageReceiver, MessageSender}; 3 | use crate::registry::MessageRegistration; 4 | use crate::send_trigger::EventSender; 5 | use crate::trigger::TriggerRegistration; 6 | use bevy_ecs::event::Event; 7 | use lightyear_connection::client::Client; 8 | use lightyear_connection::direction::NetworkDirection; 9 | 10 | impl MessageRegistration<'_, M> { 11 | pub(crate) fn add_client_direction(&mut self, direction: NetworkDirection) { 12 | match direction { 13 | NetworkDirection::ClientToServer => { 14 | self.app 15 | .register_required_components::>(); 16 | } 17 | NetworkDirection::ServerToClient => { 18 | self.app 19 | .register_required_components::>(); 20 | } 21 | NetworkDirection::Bidirectional => { 22 | self.add_client_direction(NetworkDirection::ClientToServer); 23 | self.add_client_direction(NetworkDirection::ServerToClient); 24 | } 25 | } 26 | } 27 | } 28 | 29 | impl TriggerRegistration<'_, M> { 30 | pub(crate) fn add_client_direction(&mut self, direction: NetworkDirection) { 31 | match direction { 32 | NetworkDirection::ClientToServer => { 33 | self.app 34 | .try_register_required_components::>() 35 | .ok(); 36 | } 37 | NetworkDirection::ServerToClient => {} 38 | NetworkDirection::Bidirectional => { 39 | self.add_client_direction(NetworkDirection::ClientToServer); 40 | self.add_client_direction(NetworkDirection::ServerToClient); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lightyear_core/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Lightyear Core 2 | //! 3 | //! This crate provides fundamental types and utilities shared across the Lightyear networking library. 4 | //! It includes core concepts such as: 5 | //! - Ticking and time management (`tick`, `time`, `timeline`). 6 | //! - Network identifiers and abstractions (`network`, `id`). 7 | //! - History buffers for state management (`history_buffer`). 8 | //! - Core plugin structures (`plugin`). 9 | 10 | #![cfg_attr(not(feature = "test_utils"), no_std)] 11 | 12 | extern crate alloc; 13 | extern crate core; 14 | #[cfg(test)] 15 | extern crate std; 16 | 17 | /// Defines the `Tick` type and related systems for managing discrete time steps. 18 | pub mod tick; 19 | 20 | /// Provides core network-related types and traits. 21 | pub mod network; 22 | 23 | /// Provides `HistoryBuffer` for storing and managing historical state. 24 | pub mod history_buffer; 25 | /// Provides types for network identifiers, such as `PeerId` and `NetId`. 26 | pub mod id; 27 | /// Defines core plugin structures and related utilities. 28 | pub mod plugin; 29 | /// Utilities for time management, including interpolation and synchronization. 30 | pub mod time; 31 | /// Defines [`Timeline`](timeline::Timeline) for managing different views of time (local, network). 32 | pub mod timeline; 33 | 34 | pub mod interpolation; 35 | 36 | pub mod prediction; 37 | 38 | #[cfg(feature = "test_utils")] 39 | pub mod test; 40 | 41 | /// Commonly used items from the `lightyear_core` crate. 42 | pub mod prelude { 43 | pub use crate::interpolation::Interpolated; 44 | 45 | pub use crate::prediction::Predicted; 46 | 47 | pub use crate::id::{LocalId, PeerId, RemoteId}; 48 | pub use crate::tick::Tick; 49 | pub use crate::timeline::{ 50 | LocalTimeline, NetworkTimeline, NetworkTimelinePlugin, Rollback, SyncEvent, Timeline, 51 | is_in_rollback, 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /lightyear_webtransport/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightyear_webtransport" 3 | version = "0.25.5" 4 | authors = ["aecsocket "] 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | license.workspace = true 8 | description = "IO primitives for the lightyear networking library" 9 | repository = "https://github.com/cBournhonesque/lightyear" 10 | 11 | [lints] 12 | workspace = true 13 | 14 | [package.metadata.docs.rs] 15 | all-features = true 16 | rustflags = ["--cfg=web_sys_unstable_apis"] 17 | targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] 18 | 19 | [features] 20 | default = ["self-signed"] 21 | client = ["aeronet_webtransport/client"] 22 | server = ["aeronet_webtransport/server", "bevy_reflect/std"] 23 | 24 | ## Enables `wtransport/self-signed`, allowing you to generate self-signed certificates easily for 25 | ## use in a server. 26 | ## 27 | ## Note that, without explicitly allowing your server's self-signed certificate (or using 28 | ## `dangerous-configuration` and disabling certificate validation), clients will not be able to 29 | ## connect to a server with self-signed certificates. 30 | self-signed = ["aeronet_webtransport/self-signed"] 31 | 32 | ## Enables `wtransport/dangerous-configuration`, allowing you to use potentially dangerous 33 | ## certificate validation configurations. 34 | ## 35 | ## You should not use dangerous configurations in production builds of your app. 36 | dangerous-configuration = ["aeronet_webtransport/dangerous-configuration"] 37 | 38 | [dependencies] 39 | aeronet_webtransport.workspace = true 40 | aeronet_io.workspace = true 41 | lightyear_link.workspace = true 42 | lightyear_aeronet.workspace = true 43 | 44 | tracing.workspace = true 45 | 46 | # bevy 47 | bevy_app.workspace = true 48 | bevy_ecs.workspace = true 49 | bevy_reflect = { workspace = true, optional = true } 50 | 51 | thiserror.workspace = true 52 | -------------------------------------------------------------------------------- /examples/network_visibility/README.md: -------------------------------------------------------------------------------- 1 | # Interest management 2 | 3 | A simple example that shows how to use Lightyear to perform interest management. 4 | 5 | Interest management is a technique to reduce the amount of data that is sent to each client: 6 | we want to send only the data that is relevant to each client. 7 | 8 | In this example, we are going to replicate entities that are within a certain distance of the client. 9 | 10 | https://github.com/cBournhonesque/lightyear/assets/8112632/41a6d102-77a1-4a44-8974-1d208b4ef798 11 | 12 | ## Running an example 13 | 14 | - Run the server with a gui: `cargo run -- server` 15 | - Run client with id 1: `cargo run -- client -c 1` 16 | 17 | [//]: # (- Run the client and server in two separate bevy Apps: `cargo run` or `cargo run separate`) 18 | - Run the server without a gui: `cargo run --no-default-features --features=server -- server` 19 | - Run the client and server in "HostClient" mode, where the client also acts as server (both are in the same App) : `cargo run -- host-client -c 0` 20 | 21 | You can control the behaviour of the example by changing the list of features. By default, all features are enabled (client, server, gui). 22 | For example you can run the server in headless mode (without gui) by running `cargo run --no-default-features --features=server,udp,netcode`. 23 | 24 | ### Testing in wasm with webtransport 25 | 26 | NOTE: I am using the [bevy cli](https://github.com/TheBevyFlock/bevy_cli) to build and serve the wasm example. 27 | 28 | To test the example in wasm, you can run the following commands: `bevy run web` 29 | 30 | You will need a valid SSL certificate to test the example in wasm using webtransport. You will need to run the following 31 | commands to generate a self-signed certificate: 32 | - `cd "$(git rev-parse --show-toplevel)" && sh certificates/generate.sh` (to generate the temporary SSL 33 | certificates, they are only valid for 2 weeks) 34 | -------------------------------------------------------------------------------- /lightyear_inputs_native/src/plugin.rs: -------------------------------------------------------------------------------- 1 | //! Plugin to register and handle user inputs. 2 | use crate::action_state::ActionState; 3 | #[cfg(any(feature = "client", feature = "server"))] 4 | use crate::input_message::NativeStateSequence; 5 | use bevy_app::{App, Plugin}; 6 | use bevy_ecs::entity::MapEntities; 7 | use bevy_reflect::{FromReflect, Reflectable}; 8 | use core::fmt::Debug; 9 | use lightyear_inputs::config::InputConfig; 10 | use lightyear_inputs::input_buffer::InputBuffer; 11 | use serde::Serialize; 12 | use serde::de::DeserializeOwned; 13 | 14 | #[derive(Default)] 15 | pub struct InputPlugin { 16 | pub config: InputConfig, 17 | } 18 | 19 | impl< 20 | A: Serialize 21 | + DeserializeOwned 22 | + Clone 23 | + PartialEq 24 | + Send 25 | + Sync 26 | + Debug 27 | + Default 28 | + 'static 29 | + MapEntities 30 | + Reflectable 31 | + FromReflect, 32 | > Plugin for InputPlugin 33 | { 34 | fn build(&self, app: &mut App) { 35 | app.register_type::, A>>(); 36 | app.register_type::>(); 37 | 38 | // TODO: for simplicity, we currently register both client and server input plugins if both features are enabled 39 | #[cfg(feature = "client")] 40 | { 41 | use lightyear_inputs::client::ClientInputPlugin; 42 | app.add_plugins(ClientInputPlugin::>::new( 43 | self.config, 44 | )); 45 | } 46 | 47 | #[cfg(feature = "server")] 48 | { 49 | use lightyear_inputs::server::ServerInputPlugin; 50 | app.add_plugins(ServerInputPlugin::> { 51 | rebroadcast_inputs: self.config.rebroadcast_inputs, 52 | marker: core::marker::PhantomData, 53 | }); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /examples/deterministic_replication/src/server.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::*; 2 | use crate::shared; 3 | use crate::shared::{ 4 | SharedPlugin, WallBundle, color_from_id, player_bundle, shared_movement_behaviour, 5 | }; 6 | use avian2d::prelude::*; 7 | use bevy::color::palettes::css; 8 | use bevy::platform::collections::HashMap; 9 | use bevy::prelude::*; 10 | use core::time::Duration; 11 | use leafwing_input_manager::prelude::*; 12 | use lightyear::prelude::server::*; 13 | use lightyear::prelude::*; 14 | use lightyear_examples_common::shared::SEND_INTERVAL; 15 | 16 | /// In this example, the server does not simulate anything, it simply acts as a relay server 17 | /// that handles: 18 | /// - receiving and broadcasting player inputs 19 | /// - handling game start 20 | /// - keeping timelines in sync 21 | #[derive(Clone)] 22 | pub struct ExampleServerPlugin; 23 | 24 | impl Plugin for ExampleServerPlugin { 25 | fn build(&self, app: &mut App) { 26 | app.add_observer(handle_new_client); 27 | app.add_observer(handle_connected); 28 | } 29 | } 30 | 31 | pub(crate) fn handle_new_client(trigger: On, mut commands: Commands) { 32 | commands 33 | .entity(trigger.entity) 34 | .insert(ReplicationSender::new( 35 | SEND_INTERVAL, 36 | SendUpdatesMode::SinceLastAck, 37 | false, 38 | )); 39 | } 40 | 41 | pub(crate) fn handle_connected( 42 | trigger: On, 43 | query: Query<&RemoteId, With>, 44 | mut commands: Commands, 45 | ) { 46 | let Ok(remote_id) = query.get(trigger.entity) else { 47 | return; 48 | }; 49 | info!("Spawning player for client {:?}", remote_id); 50 | // we spawn an entity that will be replicated to all clients 51 | commands.spawn(( 52 | Replicate::to_clients(NetworkTarget::All), 53 | PlayerId(remote_id.0), 54 | player_bundle(remote_id.0), 55 | )); 56 | } 57 | -------------------------------------------------------------------------------- /examples/replication_groups/src/renderer.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::*; 2 | use bevy::prelude::*; 3 | 4 | #[derive(Clone)] 5 | pub struct ExampleRendererPlugin; 6 | 7 | impl Plugin for ExampleRendererPlugin { 8 | fn build(&self, app: &mut App) { 9 | app.add_systems(Startup, init); 10 | app.add_systems(Update, draw_snakes); 11 | } 12 | } 13 | 14 | fn init(mut commands: Commands) { 15 | commands.spawn(Camera2d); 16 | } 17 | 18 | /// System that draws the boxes of the player positions. 19 | /// The components should be replicated from the server to the client 20 | pub(crate) fn draw_snakes( 21 | mut gizmos: Gizmos, 22 | players: Query<(Entity, &PlayerPosition, &PlayerColor)>, 23 | tails: Query<(Entity, &PlayerParent, &TailPoints)>, 24 | ) { 25 | for (tail, parent, points) in tails.iter() { 26 | debug!("drawing snake with parent: {:?}", parent.0); 27 | let Ok((head, position, color)) = players.get(parent.0) else { 28 | error!(?tail, parent = ?parent.0, "Tail entity has no parent entity!"); 29 | continue; 30 | }; 31 | // draw the head 32 | gizmos.rect( 33 | Isometry3d::from_translation(Vec3::new(position.x, position.y, 0.0)), 34 | Vec2::ONE * 20.0, 35 | color.0, 36 | ); 37 | // draw the first line 38 | gizmos.line_2d(position.0, points.0.front().unwrap().0, color.0); 39 | if position.0.x != points.0.front().unwrap().0.x 40 | && position.0.y != points.0.front().unwrap().0.y 41 | { 42 | debug!("DIAGONAL"); 43 | } 44 | // draw the rest of the lines 45 | for (start, end) in points.0.iter().zip(points.0.iter().skip(1)) { 46 | gizmos.line_2d(start.0, end.0, color.0); 47 | if start.0.x != end.0.x && start.0.y != end.0.y { 48 | debug!("DIAGONAL"); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/bevy_enhanced_inputs/src/protocol.rs: -------------------------------------------------------------------------------- 1 | use bevy::math::Curve; 2 | use bevy::prelude::*; 3 | use bevy::prelude::{App, Plugin}; 4 | use lightyear::input::prelude::InputConfig; 5 | use lightyear::prelude::input::bei::*; 6 | use lightyear::prelude::*; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | // Components 10 | #[derive(Component, Serialize, Deserialize, Clone, Debug, PartialEq)] 11 | pub struct PlayerId(pub PeerId); 12 | 13 | #[derive(Component, Serialize, Deserialize, Clone, Debug, PartialEq, Deref, DerefMut)] 14 | pub struct PlayerPosition(pub Vec2); 15 | 16 | impl Ease for PlayerPosition { 17 | fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve { 18 | FunctionCurve::new(Interval::UNIT, move |t| { 19 | PlayerPosition(Vec2::lerp(start.0, end.0, t)) 20 | }) 21 | } 22 | } 23 | 24 | #[derive(Component, Deserialize, Serialize, Clone, Debug, PartialEq)] 25 | pub struct PlayerColor(pub(crate) Color); 26 | 27 | // Inputs 28 | 29 | // the context will be replicated 30 | #[derive(Component, Serialize, Deserialize, Reflect, Clone, Debug, PartialEq)] 31 | pub struct Player; 32 | 33 | #[derive(Debug, InputAction)] 34 | #[action_output(Vec2)] 35 | pub struct Movement; 36 | 37 | // Protocol 38 | #[derive(Clone)] 39 | pub struct ProtocolPlugin; 40 | 41 | impl Plugin for ProtocolPlugin { 42 | fn build(&self, app: &mut App) { 43 | // inputs 44 | app.add_plugins(InputPlugin:: { 45 | config: InputConfig:: { 46 | rebroadcast_inputs: true, 47 | ..default() 48 | }, 49 | }); 50 | app.register_input_action::(); 51 | 52 | // components 53 | app.register_component::(); 54 | 55 | app.register_component::() 56 | .add_prediction() 57 | .add_linear_interpolation(); 58 | 59 | app.register_component::(); 60 | } 61 | } 62 | --------------------------------------------------------------------------------