@@ -48,6 +48,46 @@ own metadata to them or even replicate these entiteis back to clients.
4848You can use [`Trigger<OnAdd, ConnectedClient>`] to react to new connections,
4949or use backend-provided events if you need the disconnect reason.
5050
51+ ## States
52+
53+ We provide [`ClientState`] and [`ServerState`], which are Bevy [`States`].
54+ These are managed by your messaging backend, and you can use them to control when your systems run.
55+
56+ For systems that should run continuously while in a specific state, use [`IntoScheduleConfigs::run_if`]
57+ with the [`in_state`] run condition:
58+
59+ ```
60+ # use bevy::prelude::*;
61+ # use bevy_replicon::prelude::*;
62+ # let mut app = App::new();
63+ app.add_systems(
64+ Update,
65+ (
66+ apply_damage.run_if(in_state(ServerState::Running)), // Runs every frame on the server.
67+ display_vfx.run_if(in_state(ClientState::Connected)), // Runs every frame on the client.
68+ ),
69+ );
70+ # fn apply_damage() {}
71+ # fn display_vfx() {}
72+ ```
73+
74+ To run systems when entering or exiting a state, use the [`OnEnter`] or [`OnExit`] schedules:
75+
76+ ```
77+ # use bevy::prelude::*;
78+ # use bevy_replicon::prelude::*;
79+ # let mut app = App::new();
80+ app.add_systems(OnEnter(ClientState::Connecting), display_connection_message) // Runs when the client starts connecting.
81+ .add_systems(OnExit(ClientState::Connected), show_disconnected_message) // Runs when the client disconnects.
82+ .add_systems(OnEnter(ServerState::Running), initialize_match); // Runs when the server starts.
83+ # fn display_connection_message() {}
84+ # fn show_disconnected_message() {}
85+ # fn initialize_match() {}
86+ ```
87+
88+ Read more about system patterns in the [Abstracting over configurations](abstracting-over-configurations)
89+ section.
90+
5191## Replication
5292
5393It's a process of exchanging data in order to keep the world in sync. Replicon
@@ -435,10 +475,11 @@ without actually running them:
435475- Listen server configuration runs only the server and both logics.
436476- Singleplayer configuration doesn't run the client or server but runs both logics.
437477
438- To achieve this, we provide the [`ClientState`] and [`ServerState`] states:
478+ To achieve this, use the provided [`ClientState`] and [`ServerState`] states:
439479
440480- Use [`ClientState::Disconnected`] for systems that require server authority.
441- For example, systems that apply damage or send server events.
481+ For example, systems that apply damage or send server events. This basically means "run when not a client",
482+ which applies to both server **and** singleplayer.
442483- Use [`ClientState::Connecting`], [`ClientState::Connected`], [`ServerState::Running`], etc.
443484 **only** for miscellaneous things, like display a connection message or a menu to kick connected players
444485 (things that actually require server or client running)
@@ -451,9 +492,11 @@ Internally we run replication sending system only in [`ServerState::Running`] an
451492only in [`ClientState::Connected`]. This way for singleplayer replication systems won't run at all and
452493for listen server replication will only be sending (server world is already in the correct state).
453494
454- For events it's a bit trickier. For all client events we internally drain events as `E` and re-emit
455- them as [`FromClient<E>`] locally with a special [`SERVER`] entity in [`ClientState::Disconnected`],
456- regardless of [`ServerState`]. So it works for both server and singleplayer.
495+ For events, it's a bit trickier. For all client events, we internally drain events as `E` and re-emit
496+ them as [`FromClient<E>`] locally with a special [`SERVER`] entity in [`ClientState::Disconnected`].
497+ This emulates event receiving for both server and singleplayer without actually transmitting data
498+ over the network.
499+
457500For server events we drain [`ToClients<E>`] and, if the [`SERVER`] entity is the recipient of the event,
458501re-emit it as `E` locally.
459502
0 commit comments