Skip to main content

bevy_replicon/
lib.rs

1/*!
2A server-authoritative replication crate for [Bevy](https://bevyengine.org).
3
4# Quick start
5
6The library doesn't provide any I/O, so you need to add a
7[messaging backend](https://github.com/simgine/bevy_replicon#messaging-backends).
8If you want to write an integration yourself, see [`shared::backend`] module.
9
10## Prelude
11
12We provide a [`prelude`] module, which exports most of the typically used traits and types.
13
14## Plugins
15
16Add [`RepliconPlugins`] and plugins for your chosen messaging backend to your app:
17
18```
19use bevy::{prelude::*, state::app::StatesPlugin};
20use bevy_replicon::prelude::*;
21# use bevy::app::PluginGroupBuilder;
22
23let mut app = App::new();
24app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins, MyMessagingPlugins));
25# struct MyMessagingPlugins;
26# impl PluginGroup for MyMessagingPlugins {
27#     fn build(self) -> PluginGroupBuilder {
28#         PluginGroupBuilder::start::<Self>()
29#     }
30# }
31```
32
33If you use [`MinimalPlugins`], you need to add [`StatesPlugin`](bevy::state::app::StatesPlugin)
34manually. It's included by default with [`DefaultPlugins`].
35
36## Server and client creation
37
38This part is specific to your messaging backend. For `bevy_replicon_renet`,
39see [this section](https://docs.rs/bevy_replicon_renet#server-and-client-creation).
40
41On server connected clients represented as entities with [`ConnectedClient`] component.
42Their data represented as components, such as [`ConnectedClientStats`]. Users can also attach their
43own metadata to them or even replicate these entities back to clients.
44
45These entities are automatically spawned and despawned by the messaging backend. You can
46also despawn them yourself to trigger a disconnect or use the [`DisconnectRequest`] message
47to disconnect after sending messages.
48
49You can use [`On<Add, ConnectedClient>`] to react to new connections,
50or use backend-provided events if you need the disconnect reason.
51
52## States
53
54We provide [`ClientState`] and [`ServerState`], which are Bevy [`States`].
55These are managed by your messaging backend, and you can use them to control when your systems run.
56
57For systems that should run continuously while in a specific state, use [`IntoScheduleConfigs::run_if`]
58with the [`in_state`] run condition:
59
60```
61# use bevy::prelude::*;
62# use bevy_replicon::prelude::*;
63# let mut app = App::new();
64app.add_systems(
65    Update,
66    (
67        apply_damage.run_if(in_state(ServerState::Running)), // Runs every frame on the server.
68        display_vfx.run_if(in_state(ClientState::Connected)), // Runs every frame on the client.
69    ),
70);
71# fn apply_damage() {}
72# fn display_vfx() {}
73```
74
75To run systems when entering or exiting a state, use the [`OnEnter`] or [`OnExit`] schedules:
76
77```
78# use bevy::prelude::*;
79# use bevy_replicon::prelude::*;
80# let mut app = App::new();
81app.add_systems(OnEnter(ClientState::Connecting), display_connection_message) // Runs when the client starts connecting.
82    .add_systems(OnExit(ClientState::Connected), show_disconnected_message) // Runs when the client disconnects.
83    .add_systems(OnEnter(ServerState::Running), initialize_match); // Runs when the server starts.
84# fn display_connection_message() {}
85# fn show_disconnected_message() {}
86# fn initialize_match() {}
87```
88
89You can also use these states with [`DespawnOnExit`] to control the lifetime of entities.
90
91Read more about system patterns in the [Abstracting over configurations](#abstracting-over-configurations)
92section.
93
94## Replication
95
96It's a process of exchanging data in order to keep the world in sync. Replicon
97provides a high-level API to automate server-authoritative replication.
98
99Replication happens only from server to clients. It's necessary to prevent cheating.
100If you need to send information from clients to the server, use
101[messages](#network-messages-and-events).
102
103For implementation details see [`ServerChannel`](shared::backend::channels::ServerChannel).
104
105### Tick rate
106
107By default, updates are not sent every frame in order to save bandwidth. Replication runs
108in [`ServerSystems::Send`] whenever the [`ServerTick`](server::server_tick::ServerTick) resource
109changes and if the state is [`ServerState::Running`].
110
111By default, the tick is incremented in [`FixedPostUpdate`] each time [`FixedMain`](bevy::app::FixedMain)
112runs. You can configure how often it runs by inserting [`Time<Fixed>`], which is 64 Hz by default.
113
114```rust
115use bevy::{prelude::*, state::app::StatesPlugin};
116use bevy_replicon::prelude::*;
117
118# let mut app = App::new();
119// `Time::from_hz` is implemented only for `Fixed`, so the type is inferred.
120app.insert_resource(Time::from_hz(30.0)) // Run 30 times per second.
121    .add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins));
122```
123
124You can also configure the schedule via [`ServerPlugin::tick_schedule`].
125
126### Entities
127
128By default no entities are replicated. Add the [`Replicated`] marker
129component on the server for entities you want to replicate.
130
131On clients [`Remote`] will be automatically inserted for every entity spawned by replication.
132
133If you remove the [`Replicated`] component from an entity on the server, it will be despawned on all clients.
134
135Entity IDs differ between clients and server. As a result, clients maps server entities to local entities
136on receive. These mappings are stored in the [`ServerEntityMap`](shared::server_entity_map::ServerEntityMap)
137resource.
138
139### Components
140
141Components will be replicated only on entities marked for replication.
142By default no components are replicated, you need to define rules for it.
143
144Use [`AppRuleExt::replicate`] to create a replication rule for a single component:
145
146```
147# use bevy::{prelude::*, state::app::StatesPlugin};
148# use bevy_replicon::prelude::*;
149# use serde::{Deserialize, Serialize};
150# let mut app = App::new();
151# app.add_plugins((StatesPlugin, RepliconPlugins));
152app.replicate::<ExampleComponent>();
153
154#[derive(Component, Deserialize, Serialize)]
155struct ExampleComponent;
156```
157
158If your component contains an entity, it cannot be deserialized as is
159because entities inside components also need to be mapped. Therefore,
160to properly replicate such components, mark fields containing entities with
161`#[entities]`. See [`Component::map_entities`] for details.
162
163By default all components are serialized with [`postcard`].
164In order to serialize Bevy components you need to enable the `serialize` feature on Bevy.
165
166If your component doesn't implement serde traits or you want to customize the serialization
167(for example, quantize, skip some fields or apply compression), you can use
168[`AppRuleExt::replicate_as`] or [`AppRuleExt::replicate_with`].
169
170You can also create a rule for multiple components. Use [`AppRuleExt::replicate_bundle`],
171or pass a tuple of [`RuleFns`] to [`AppRuleExt::replicate_with`]. The components will only
172be replicated if all of them are present on the entity. This also allows you to specialize
173serialization and deserialization based on specific entity components.
174
175All functions also have `_filtered` variants that additionally accept archetypal filters,
176similar to [`Query`]. They are separate functions because Rust doesn't allow default generics
177for functions. For more details, see [`AppRuleExt::replicate_filtered`].
178
179#### Required components
180
181You don't want to replicate all components because not all of them are
182necessary to send over the network. Components that can be calculated on the client can
183be inserted using Bevy's required components feature. You can also mark [`Replicated`]
184as required to automatically replicate all entities with this component.
185
186```
187# use bevy::{prelude::*, state::app::StatesPlugin};
188# use bevy_replicon::prelude::*;
189# use serde::{Deserialize, Serialize};
190# let mut app = App::new();
191# app.add_plugins((StatesPlugin, RepliconPlugins));
192// Replicate only transform and player marker.
193app.replicate::<Transform>()
194    .replicate::<Player>()
195    .add_observer(init_player_mesh);
196
197fn init_player_mesh(
198    add: On<Add, Mesh2d>,
199    mut meshes: ResMut<Assets<Mesh>>,
200    mut players: Query<&mut Mesh2d>,
201) {
202    let mut mesh = players.get_mut(add.entity).unwrap();
203    **mesh = meshes.add(Capsule2d::default());
204}
205
206#[derive(Component, Deserialize, Serialize)]
207#[require(Replicated, Mesh2d)]
208struct Player;
209```
210
211This pairs nicely with server state serialization and keeps saves clean.
212You can use [`scene::replicate_into`] to fill [`DynamicScene`] with replicated entities and their components.
213On deserialization all missing required components will be inserted, and initialization
214systems will restore the correct game state.
215
216If a component can't be used with the required components due to the inability to insert it
217without world access, you can create an observer for a replicated marker and insert the actual
218component inside it. However, it's preferred to use required components when possible. For example,
219it's better to require a [`Handle<T>`] with a default value that doesn't point to any asset
220and initialize it later in a hook or observer. This way you avoid archetype moves in ECS.
221
222#### Mutability
223
224There are two ways to change a component value on an entity: re-inserting it or mutating it.
225
226We use Bevy’s change detection to track and send changes. However, it does not distinguish between modifications
227and re-insertions. This is why we simply send the list of changes and decide how to apply them on the client.
228By default, this behavior is based on [`Component::Mutability`].
229
230When a component is [`Mutable`](bevy::ecs::component::Mutable), we check whether it already exists on the entity.
231If it doesn’t, we insert it. If it does, we mutate it. This means that if you insert a component into an entity
232that already has it on the server, the client will treat it as a mutation. As a result, triggers may behave
233differently on the client and server. If your game logic relies on this semantic, mark your component as
234[`Immutable`](bevy::ecs::component::Immutable). For such components, replication will always be applied via insertion.
235
236A single mutation on the server may result in multiple change detection triggers on a client.
237Clients track the last applied tick for each entity and apply a mutation only if the tick for the received data is greater.
238However, mutation messages include only the server tick on which the message was sent. This is because it would be too expensive
239to include the change tick for each entity. So, if a client does not acknowledge the received message in time, the server
240will resend the data (because it might have been lost). Since the tick for this message will be greater, the
241client will write the component again, even if it is the same. If your game logic relies on this behavior, you can set
242[`write_if_neq`](shared::replication::registry::receive_fns::write_if_neq) as your writing function for
243the desired components. See [receive markers](#receive-markers) for more details.
244
245#### Component relations
246
247Some components depend on each other. For example, [`ChildOf`] and [`Children`]. You can enable
248replication only for [`ChildOf`] so that [`Children`] will be updated automatically on insertion.
249Related entities replicate like any others, so children should also have [`Replicated`].
250
251Currently `ChildOf` replication emits a [`B0004`](https://bevy.org/learn/errors/b0004) warning which can be safely ignored.
252See [#19776](https://github.com/bevyengine/bevy/issues/19776) for more details.
253
254You can also ensure that their mutations arrive in sync by using [`SyncRelatedAppExt::sync_related_entities`].
255
256#### Deterministic replication
257
258Up until now, we've covered only authoritative replication (AR), where the server is the source of truth
259and continuously sends changes. However, sometimes you may want to send data only once and simulate independently,
260relying on determinism. This approach is called deterministic replication (DR).
261
262For example, you might use AR for things like player health, and DR for moving platform positions to reduce
263network traffic.
264
265Use [`AppRuleExt::replicate_once`] to replicate only the initial value of a component.
266You can configure this per-component within a replication rule using [`AppRuleExt::replicate_with`].
267
268See also [server messages](#from-server-to-client), which are also useful for DR.
269
270## Network messages and events
271
272This replaces RPCs (remote procedure calls) in other engines and,
273unlike replication, can be sent both from server to clients and from clients to
274server.
275
276### From client to server
277
278To send a message from client to server, you need to register the message
279with [`ClientMessageAppExt::add_client_message`] instead of [`App::add_message`].
280
281Messages include [`Channel`] to configure delivery guarantees (reliability and
282ordering).
283
284These messages will appear on server as [`FromClient`] wrapper message that
285contains sender ID and the message.
286
287```
288# use bevy::{prelude::*, state::app::StatesPlugin};
289# use bevy_replicon::prelude::*;
290# use serde::{Deserialize, Serialize};
291# let mut app = App::new();
292# app.add_plugins((StatesPlugin, RepliconPlugins));
293app.add_client_message::<Ping>(Channel::Ordered)
294    .add_systems(
295        PreUpdate,
296        receive
297            .after(ServerSystems::Receive)
298            .run_if(in_state(ServerState::Running)),
299    )
300    .add_systems(
301        PostUpdate,
302        send.before(ClientSystems::Send).run_if(in_state(ClientState::Connected)),
303    );
304
305fn send(mut ping: MessageWriter<Ping>) {
306    ping.write(Ping);
307}
308
309fn receive(mut pings: MessageReader<FromClient<Ping>>) {
310    for ping in pings.read() {
311        info!("received ping from client `{}`", ping.client_id);
312    }
313}
314
315#[derive(Message, Deserialize, Serialize)]
316struct Ping;
317```
318
319If a message contains an entity, implement
320[`MapEntities`](bevy::ecs::entity::MapEntities) for it and use use
321[`ClientMessageAppExt::add_mapped_client_message`] instead.
322
323There is also [`ClientMessageAppExt::add_client_message_with`] to register a message with special serialization and
324deserialization functions. This could be used for sending messages that contain [`Box<dyn PartialReflect>`], which
325require access to the [`AppTypeRegistry`] resource. Don't forget to validate the contents of every
326[`Box<dyn PartialReflect>`] from a client, it could be anything!
327
328Alternatively, you can use events with a similar API. First, you need to register the event
329using [`ClientEventAppExt::add_client_event`], and then use [`ClientTriggerExt::client_trigger`].
330
331```
332# use bevy::{prelude::*, state::app::StatesPlugin};
333# use bevy_replicon::prelude::*;
334# use serde::{Deserialize, Serialize};
335# let mut app = App::new();
336# app.add_plugins((StatesPlugin, RepliconPlugins));
337app.add_client_event::<Ping>(Channel::Ordered)
338    .add_observer(receive)
339    .add_systems(Update, send.run_if(in_state(ClientState::Connected)));
340
341fn send(mut commands: Commands) {
342    commands.client_trigger(Ping);
343}
344
345fn receive(ping: On<FromClient<Ping>>) {
346    info!("received ping from client `{}`", ping.client_id);
347}
348# #[derive(Event, Deserialize, Serialize)]
349# struct Ping;
350```
351
352For events with entities inside use [`ClientEventAppExt::add_mapped_client_event`].
353Similar to messages, serialization can also be customized with [`ClientEventAppExt::add_client_event_with`].
354
355If you need to react to the same message on both the client and the server (for example,
356to share logic between client-side prediction and authoritative server processing), use
357[`SharedMessageAppExt::add_shared_message`] or
358[`SharedEventAppExt::add_shared_event`]. The message is emitted as [`LocalOrRemote<M>`]
359on both sides, with the [`Sender`] field indicating its origin.
360
361```
362# use bevy::{prelude::*, state::app::StatesPlugin};
363# use bevy_replicon::prelude::*;
364# use serde::{Deserialize, Serialize};
365# let mut app = App::new();
366# app.add_plugins((StatesPlugin, RepliconPlugins));
367app.add_shared_event::<Attack>(Channel::Ordered)
368    .add_observer(on_attack);
369
370fn on_attack(attack: On<LocalOrRemote<Attack>>) {
371    info!("attack from `{:?}`", attack.sender);
372}
373
374#[derive(Event, Deserialize, Serialize)]
375struct Attack;
376```
377
378Similar to regular client messages, we also provide [`SharedEventAppExt::add_mapped_shared_event`].
379and [`SharedEventAppExt::add_shared_event_with`].
380
381### From server to client
382
383A similar technique is used to send messages from server to clients. To do this,
384register a message with [`ServerMessageAppExt::add_server_message`]
385and send it from server using [`ToClients`]. This wrapper contains send parameters
386and the message itself.
387
388```
389# use bevy::{prelude::*, state::app::StatesPlugin};
390# use bevy_replicon::prelude::*;
391# use serde::{Deserialize, Serialize};
392# let mut app = App::new();
393# app.add_plugins((StatesPlugin, RepliconPlugins));
394app.add_server_message::<Pong>(Channel::Ordered)
395    .add_systems(
396        PreUpdate,
397        receive
398            .after(ClientSystems::Receive)
399            .run_if(in_state(ClientState::Connected)),
400    )
401    .add_systems(
402        PostUpdate,
403        send.before(ServerSystems::Send).run_if(in_state(ServerState::Running)),
404    );
405
406fn send(mut pongs: MessageWriter<ToClients<Pong>>) {
407    pongs.write(ToClients {
408        targets: SendTargets::All,
409        message: Pong,
410    });
411}
412
413fn receive(mut pongs: MessageReader<Pong>) {
414    for pong in pongs.read() {
415        info!("received pong from the server");
416    }
417}
418
419#[derive(Message, Deserialize, Serialize)]
420struct Pong;
421```
422
423Just like for client messages, we provide [`ServerMessageAppExt::add_mapped_server_message`]
424and [`ServerMessageAppExt::add_server_message_with`].
425
426Event API is available. First, you need to register an event with [`ServerEventAppExt::add_server_event`]
427and then use [`ServerTriggerExt::server_trigger`]:
428
429```
430# use bevy::{prelude::*, state::app::StatesPlugin};
431# use bevy_replicon::prelude::*;
432# use serde::{Deserialize, Serialize};
433# let mut app = App::new();
434# app.add_plugins((StatesPlugin, RepliconPlugins));
435app.add_server_event::<Pong>(Channel::Ordered)
436    .add_observer(receive)
437    .add_systems(Update, send.run_if(in_state(ServerState::Running)));
438
439fn send(mut commands: Commands) {
440    commands.server_trigger(ToClients {
441        targets: SendTargets::All,
442        message: Pong,
443    });
444}
445
446fn receive(_on: On<Pong>) {
447    info!("received pong from the server");
448}
449# #[derive(Event, Deserialize, Serialize)]
450# struct Pong;
451```
452
453And just like for client events, we provide [`ServerEventAppExt::add_mapped_server_event`]
454and [`ServerEventAppExt::add_server_event_with`].
455
456We guarantee that clients will never receive events or messages that point to an entity or require specific
457component to be presentt which client haven't received yet. For more details see the documentation on
458[`ServerMessageAppExt::make_message_independent`].
459
460## Abstracting over configurations
461
462Depending on the game, you may need to support some of these configurations:
463
464- Client
465- Dedicated (headless) server
466- Listen server (where the server is also a client)
467- Singleplayer
468
469There are 2 ways to support multiple configurations at the same time.
470
471### Classic way
472
473Just split client and server logic. Then for listen server and singleplayer run both the server and client,
474just don't accept outside connections for singleplayer.
475
476However, you can't simply run both the client and server in a single app for a listen server. Since they operate
477on the same world, any changes will trigger replication, which will be sent with a delay and then applied to the
478same entities, overriding the latest values and triggering changes again.
479
480- Two Bevy apps inside a single process, running in separate threads.
481- Two executables. After starting the client app, the server starts in the background.
482- Use [receive markers](#receive-markers) to discard replication on listen server.
483
484It's not easy to set up and requires more resources due to the synchronization between two worlds.
485This is why, while it's possible to use Replicon this way, we recommend a different approach.
486
487### The recommended way
488
489Instead of recreating full client-server logic, we provide a way to emulate client and server functionality
490without actually running them:
491
492- Client configuration runs only the client and its logic.
493- Dedicated server configuration runs only the server and its logic (all client logic usually compiled out).
494- Listen server configuration runs only the server and both logics.
495- Singleplayer configuration doesn't run the client or server but runs both logics.
496
497To achieve this, use the provided [`ClientState`] and [`ServerState`] states:
498
499- Use [`ClientState::Disconnected`] for systems that require server authority.
500  For example, systems that apply damage or send server events. This basically means "run when not a client",
501  which applies to both server **and** singleplayer.
502- Use [`ClientState::Connecting`], [`ClientState::Connected`], [`ServerState::Running`], etc.
503  **only** for miscellaneous things, like display a connection message or a menu to kick connected players
504  (things that actually require server or client running)
505- For everything else don't use Replicon's states.
506
507Everything else is done automatically by the crate. All provided
508[examples](https://github.com/simgine/bevy_replicon/tree/master/example_backend/examples)
509use this approach.
510
511Internally we run replication sending system only in [`ServerState::Running`] and replication receiving
512only in [`ClientState::Connected`]. This way for singleplayer replication systems won't run at all and
513for listen server replication will only be sending (server world is already in the correct state).
514
515For messages, it's a bit trickier. For all client messages, we internally drain messages as `E` and send
516them as [`FromClient<E>`] locally with [`ClientId::Server`] in [`ClientState::Disconnected`].
517For server messages we drain [`ToClients<E>`] and, if the [`ClientId::Server`] is the recipient of the message,
518re-emit it as `E` locally. The same applies to the event API. This emulates message receiving for both server
519and singleplayer without actually transmitting data over the network.
520
521We also provide [`ClientSystems`] and [`ServerSystems`] to schedule your system at specific time in the frame.
522For example, you can run your systems right after receive using [`ClientSystems::Receive`] or [`ServerSystems::Receive`].
523
524## Organizing your game code
525
526If you support a dedicated server, it's recommended to split your game logic into "client", "server", and "shared"
527crates. This way, you can compile out all unnecessary code for the dedicated server configuration.
528Alternatively, you can use [Cargo features](https://doc.rust-lang.org/cargo/reference/features.html) and split
529the logic into modules.
530
531We provide `client` and `server` features to disable unneeded functionality for this use case.
532You will need to disable similar features on your messaging backend crate too.
533
534<div class="warning">
535
536Make sure that the component, event and message registration order is the same on the client and server. Simply put all
537registration code in your "shared" crate.
538
539</div>
540
541If you don't need to support a dedicated server configuration, it's easier to keep the logic grouped together.
542Splitting your game into crates is still useful, but it should be done logically rather than on server/client
543basis.
544
545## Advanced features
546
547### Authorization
548
549Connected clients are considered authorized when they have the [`AuthorizedClient`] component. Without this
550component, clients can only receive events and messages marked as independent via [`ServerMessageAppExt::make_message_independent`]
551or [`ServerEventAppExt::make_event_independent`]. Additionally, some components on connected clients are only present
552after replication starts. See the required components for [`AuthorizedClient`] for details.
553
554By default, this component is automatically inserted when the client and server [`ProtocolHash`] matches.
555This behavior can be customized via [`RepliconSharedPlugin::auth_method`].
556
557### Client visibility
558
559You can control which parts of the world are visible to each client by using components registered as visibility filters.
560This works similarly to collision layers in physics: you insert filters to both the client and gameplay entities.
561See [`AppVisibilityExt`] for API details.
562
563The server always sees the entire world, even in listen-server mode.
564
565### Prioritization
566
567By default, all unacknowledged mutations are sent every tick. This can be expensive if you
568have many entities with mutating components. The [`PriorityMap`] component lets you configure
569how often mutations are sent for each entity on authorized clients. See its documentation for
570more details.
571
572In addition, [client visibility](#client-visibility) can be used to further reduce bandwidth by hiding entities
573that are irrelevant to a given client.
574
575### Interpolation and/or client-side prediction
576
577Due to network round-trip time and [tick rate](#tick-rate), you may notice that the state isn't updated
578immediately. This might be fine depending on your type of game. However, sometimes this needs to be visually hidden.
579
580To make value updates look smooth, you can just to interpolate the received state. If the input delay doesn't matter
581for your type of game, it can be enough.
582
583But if your game is fast-paced, waiting for server to receive your inputs and replicate the state back might
584might be unacceptable. The solution is to predict simulation on the client.
585
586However, it introduces another problem - misspredictions. For example, player 1 might predict movement to point X,
587while player 2 might have stunned it, the player 1 just didn't receive it yet. To solve this, client must apply
588the received state from server and replay its inputs.
589
590How much to predict also depends on the game. Common approaches are:
591
592- Predict individual entities. Common for shooters or games where you don't have many entities. In the case of
593  a misprediction or non-deterministic mismatch, the state will be corrected. Determinism is important for this
594  approach to reduce the number of rollbacks.
595- Predict the entire world. Common for physics-based games or games with many entities. With this approach,
596  all predicted entities are rolled back to the oldest received tick. For example, if one entity have confirmed tick 1
597  and another entity have confirmed tick 2, both entities are rolled back to tick 1. This approach is usually more expensive
598  but produces better results for physics. Additionally, if there are many predicted entities, it might even be faster
599  since there's no need to check each entity for misprediction. The more entities you predict, the more likely it is
600  that at least one will trigger a world rollback. So with this approach client usually just always rollbacks.
601
602We don't have these features built-in, but we provide a low-level API to implement these abstractions on top.
603Check the [corresponding section](https://github.com/simgine/bevy_replicon#interpolation-andor-rollback)
604in our README for existing implementations.
605
606### Predicting spawns
607
608Sometimes it's necessary to spawn an entity on the client before receiving replication from the server.
609A common example is projectiles. By default, the client will end up with two entities: one locally spawned
610and one replicated from the server.
611
612We provide the [`Signature`] component, which allows replicated entities to be matched with entities
613previously spawned on the client.
614
615This is also useful for synchronizing scenes. Both the client and the server can load a level independently
616and then match level entities to synchronize certain things, such as opened doors.
617
618#### Receive markers
619
620This is similar to replication rules, except markers are client-specific and only define how components
621are applied to entities. This allows you to customize writing based on components that might not be known to the server.
622For example, on clients some entities might be predicted, while others might be interpolated, and their values need to be written
623differently. The server does not need to know which entities the client interpolates and which it predicts.
624
625[`AppMarkerExt::set_receive_fns<C>`] allows you to override how the component `C` is written by default.
626To select a different writing function for a specific entity, you need to register a marker via [`AppMarkerExt::register_marker<M>`]
627and associate functions for components with this marker using [`AppMarkerExt::set_marker_fns<M, C>`]. These functions will be called for `C`
628if the marker `M` is present. You can also control marker priority or enable processing of old values using
629[`AppMarkerExt::register_marker_with<M>`].
630
631It's also possible to override despawns received from the server via
632[`ReplicationRegistry::despawn`](shared::replication::registry::ReplicationRegistry::despawn), which is also often used together
633with receive markers.
634
635### Ticks information
636
637This requires an understanding of how replication works. See the documentation on
638[`ServerChannel`](shared::backend::channels::ServerChannel) and [this section](#eventual-consistency) for more details.
639
640To get information about confirmed ticks for individual entities, we provide
641[`ConfirmHistory`](client::confirm_history::ConfirmHistory). This component is updated when any replication for its entity is received.
642For convenience, we also trigger the [`EntityReplicated`](client::confirm_history::EntityReplicated) to ergonomically react on it.
643
644However, an entity might not have any mutations at a given tick. To address this, you need to call
645[`TrackAppExt::track_mutate_messages`](shared::replication::track_mutate_messages::TrackAppExt::track_mutate_messages) during the [`App`]
646setup and use [`ServerMutateTicks`](client::server_mutate_ticks::ServerMutateTicks) resource to check whether all mutation messages were
647received for this tick.
648
649So, a tick for an entity is confirmed if one of the following is true:
650- [`ConfirmHistory`](client::confirm_history::ConfirmHistory) reports that the tick is received.
651- [`ServerMutateTicks`](client::server_mutate_ticks::ServerMutateTicks) reports that for at least one of the next ticks, all update
652  messages have been received.
653
654### Optimizing entity serialization
655
656Serialization of [`Entity`] is optimized for use in scenes, but it’s not very efficient for networking. Because of this, we use our own implementation.
657See the [`compact_entity`] module if you want to apply this optimization to entities in your own types. You can also use
658[`postcard_utils::entity_to_extend_mut`] and [`postcard_utils::entity_from_buf`] for manual writing.
659
660# Eventual consistency
661
662All events, messages, inserts, removals and despawns will be applied to clients in the same order as on the server.
663
664However, if you insert/mutate a component and immediately remove it, the client will only receive the removal because the component value
665won't exist in the [`World`] during the replication process. But removal followed by insertion will work as expected since we buffer removals.
666
667Entity component mutations may be applied to clients in a different order than on the server.
668For example, if two entities are spawned in tick 1 on the server and their components are mutated in tick 2,
669then the client is guaranteed to see the spawns at the same tick, but the component mutations may appear later (but not earlier).
670
671If a component is dependent on other data, mutations to the component will only be applied to the client when that data has arrived.
672So if your component references another entity, mutations to that component will only be applied when the referenced entity has been spawned on the client.
673
674Mutations for despawned entities will be discarded automatically, but events, messages or components may reference despawned entities and should be handled with that in mind.
675
676Clients should never assume their world state is the same as the server's on any given tick value-wise.
677World state on the client is only "eventually consistent" with the server's.
678
679# Troubleshooting
680
681If you face any issue, try to enable logging to see what is going on.
682To enable logging, you can temporarily set `RUST_LOG` environment variable to `bevy_replicon=debug`
683(or `bevy_replicon=trace` for more noisy output) like this:
684
685```bash
686RUST_LOG=bevy_replicon=debug cargo run
687```
688
689You can also filter by specific module like this:
690
691```bash
692RUST_LOG=bevy_replicon::shared::protocol=debug cargo run
693```
694
695The exact method depends on the OS shell.
696
697Alternatively you can configure `LogPlugin` from Bevy to make it permanent.
698
699For deserialization errors on client we use `error` level which should be visible by default.
700But on server we use `debug` for it to avoid flooding server logs with errors caused by clients.
701*/
702#![cfg_attr(docsrs, feature(doc_cfg))]
703#![no_std]
704
705extern crate alloc;
706
707#[cfg(feature = "client")]
708pub mod client;
709pub mod compact_entity;
710pub mod postcard_utils;
711#[cfg(feature = "scene")]
712pub mod scene;
713#[cfg(feature = "server")]
714pub mod server;
715pub mod shared;
716#[cfg(all(feature = "server", feature = "client"))]
717pub mod test_app;
718
719pub mod prelude {
720    #[expect(deprecated, reason = "Re-export of deprecated aliases")]
721    pub use super::{
722        RepliconPlugins,
723        shared::{
724            AuthMethod, RepliconSharedPlugin,
725            backend::{
726                ClientState, ClientStats, ConnectedClientStats, DisconnectRequest, ServerState,
727                channels::{Channel, RepliconChannels},
728                client_messages::ClientMessages,
729                connected_client::ConnectedClient,
730                server_messages::ServerMessages,
731            },
732            client_id::ClientId,
733            message::{
734                client_event::{ClientEventAppExt, ClientTriggerExt},
735                client_message::{ClientMessageAppExt, FromClient},
736                server_event::{ServerEventAppExt, ServerTriggerExt},
737                server_message::{SendMode, SendTargets, ServerMessageAppExt, ToClients},
738                shared_event::{SharedEventAppExt, SharedTriggerExt},
739                shared_message::{LocalOrRemote, Sender, SharedMessageAppExt},
740            },
741            protocol::{ProtocolHash, ProtocolHasher, ProtocolMismatch},
742            replication::{
743                Replicated,
744                receive_markers::AppMarkerExt,
745                registry::rule_fns::RuleFns,
746                rules::{AppRuleExt, component::ReplicationMode},
747                signature::Signature,
748                visibility::{ComponentScope, FilterScope, SingleComponent, VisibilityFilter},
749            },
750            replicon_tick::RepliconTick,
751        },
752    };
753
754    #[cfg(feature = "client")]
755    pub use super::client::{
756        ClientPlugin, ClientReplicationStats, ClientSystems, Remote, message::ClientMessagePlugin,
757    };
758
759    #[cfg(feature = "server")]
760    pub use super::server::{
761        AuthorizedClient, PriorityMap, ServerPlugin, ServerSystems, message::ServerMessagePlugin,
762        related_entities::SyncRelatedAppExt, visibility::AppVisibilityExt,
763    };
764
765    #[cfg(feature = "client_diagnostics")]
766    pub use super::client::diagnostics::ClientDiagnosticsPlugin;
767}
768
769pub use bytes;
770pub use postcard;
771
772use bevy::{app::PluginGroupBuilder, prelude::*};
773use prelude::*;
774
775/// Plugin group for all replicon plugins.
776///
777/// Contains the following:
778/// * [`RepliconSharedPlugin`].
779/// * [`ServerPlugin`] - with feature `server`.
780/// * [`ServerMessagePlugin`] - with feature `server`.
781/// * [`ClientPlugin`] - with feature `client`.
782/// * [`ClientMessagePlugin`] - with feature `client`.
783/// * [`ClientDiagnosticsPlugin`] - with feature `client_diagnostics`.
784pub struct RepliconPlugins;
785
786impl PluginGroup for RepliconPlugins {
787    fn build(self) -> PluginGroupBuilder {
788        let mut group = PluginGroupBuilder::start::<Self>();
789        group = group.add(RepliconSharedPlugin::default());
790
791        #[cfg(feature = "server")]
792        {
793            group = group.add(ServerPlugin::default()).add(ServerMessagePlugin);
794        }
795
796        #[cfg(feature = "client")]
797        {
798            group = group.add(ClientPlugin).add(ClientMessagePlugin);
799        }
800
801        #[cfg(feature = "client_diagnostics")]
802        {
803            group = group.add(ClientDiagnosticsPlugin);
804        }
805
806        group
807    }
808}