Bevy Replicon

repository·master·Indexed 20 days ago

https://github.com/simgine/bevy_replicon

A server-authoritative replication framework for the Bevy game engine designed to synchronize ECS state and events across a network. It provides automatic world replication, remote events, and authorization support while remaining agnostic to the I/O transport layer. Key features include control over client visibility, state serialization based on replication rules, and support for various messaging backends such as bevy_renet, bevy_quinnet, and matchbox.

Tokens
13K
Snippets
41
Records
52
Agent score
69%

What's inside bevy_replicon

  1. What is Bevy Replicon?

    master

    Bevy Replicon is a server-authoritative replication crate for the Bevy game engine. It provides automatic world replication, remote events and triggers, and authorization support. It is designed to abstract game logic so that the same code can support singleplayer, client, dedicated server, and listen server configurations simultaneously.

    Key technical features include:

    • Control over client visibility of entities and components.
    • Synchronization of entities via ECS relationships.
    • State serialization based on replication rules.
    • Customizable serialization/deserialization (including support for types like Box<dyn Reflect> that do not implement serde).
    • No built-in I/O: It is agnostic to the messaging layer and can be used with any messaging library.
  2. Run the Bevy Replicon Example Backend

    master

    The bevy_replicon_example_backend is a simple TCP backend designed for running examples, testing the backend API, and serving as a reference implementation.

    Warning: This backend is intended for testing and examples only. DO NOT USE this in a real project. For production environments, select a proper messaging backend from the main repository documentation.

    To run a specific example, use the following command structure. Note that you must start the server first because the TCP connection implementation in the Rust standard library used here is blocking. Real backends will not have this requirement.

    cargo run -p bevy_replicon_example_backend --example <example name> -- <example CLI args>
  3. Get started with Bevy Replicon

    master

    To begin using Bevy Replicon, follow the official quick start guide.

    Because Bevy Replicon does not include built-in I/O, you must use a messaging backend to run functional examples. You can find examples in the example_backend directory of this repository. Note that messaging backend repositories (like bevy_replicon_renet) typically contain the same examples but with the backend initialization adapted to their specific APIs.

  4. Understand the ServerSystems execution order

    master

    The ServerSystems enum defines the various system sets used by the server-side replication logic. Understanding these helps in placing custom systems correctly within the replication lifecycle:

    • ReceivePackets: (Runs in PreUpdate) Used by the messaging backend to receive packets and update ServerState.
    • Receive: (Runs in PreUpdate) Systems that read data from ServerMessages.
    • ReadRelations: (Runs in OnEnter for ServerState::Running) Builds the initial graph of related entities.
    • IncrementTick: (Runs in ServerPlugin::tick_schedule) Increments the ServerTick.
    • Send: (Runs in PostUpdate if ServerTick changes) Systems that write data to ServerMessages.
    • SendPackets: (Runs in PostUpdate if ServerTick changes) Used by the messaging backend to send packets.
    use bevy_replicon::server::ServerSystems;
    
    // Example: Adding a system to the server's receive phase
    app.add_systems(PreUpdate, my_custom_receive_system.in_set(ServerSystems::Receive));
    // Example: Adding a system to the server's send phase
    app.add_systems(PostUpdate, my_custom_send_system.in_set(ServerSystems::Send));
  5. Configure replication frequency with ReplicatePriority and PriorityMap

    master

    You can control how often mutations (component changes) are sent for entities to optimize network bandwidth. This is achieved through an accumulation mechanism: the difference between the last acknowledged tick and the current ServerTick is multiplied by the priority. When the result is $\ge 1.0$, the mutation is sent.

    There are two ways to apply this:

    1. Per-Entity (ReplicatePriority): Attach this component to a specific entity to set its base replication frequency. A priority of 0.5 means mutations are sent at most once every 2 ticks. A priority of 1.0 (default) sends mutations every tick.
    2. Per-Client (PriorityMap): Attach this to an AuthorizedClient entity. It maps specific entities to priority values. This allows you to give certain clients higher or lower update rates for specific objects (e.g., a player sees their own character more frequently than distant NPCs).

    Note: This only affects mutations. Component insertions and removals are always sent via ServerChannel::Updates immediately.

    use bevy_replicon::server::{ReplicatePriority, PriorityMap};
    
    // Set a low priority for a distant object (updates every 10 ticks)
    commands.entity(entity).insert(ReplicatePriority(0.1));
    
    // Or, override priorities for a specific client
    commands.entity(client_entity).insert(PriorityMap::from_iter([(entity, 1.0), (other_entity, 0.2)]));
  6. How `sync_related_entities` works

    master

    When you call sync_related_entities::<C>(), bevy_replicon sets up a mechanism to group entities into disconnected subgraphs (Strongly Connected Components) based on the relationship C.

    1. Graph Maintenance: The server maintains a graph of all entities connected by the specified relationship. This graph is updated automatically via observers when relationships are added, removed, or when the Replicated component is inserted/removed.
    2. Bundling: During replication, instead of treating every entity independently, the system identifies which subgraph an entity belongs to. All mutations for entities within the same subgraph are included in the same message.
    3. Lifecycle: The relationship tracking only occurs while the ServerState is Running.
  7. Manage application logic using ClientState and ServerState

    master

    Bevy Replicon provides ClientState and ServerState as Bevy States. These are managed by your messaging backend and allow you to control when specific systems run based on the connection status.

    Common States

    • ClientState::Connecting / ClientState::Connected / ClientState::Disconnected
    • ServerState::Running

    Usage Patterns

    • Continuous systems: Use .run_if(in_state(...)) to run systems every frame while in a specific state.
    • Lifecycle systems: Use OnEnter(State) or OnExit(State) schedules to react to state transitions.
    • Entity lifetimes: Use DespawnOnExit with these states to control entity lifetimes.
    // Running systems based on state
    app.add_systems(
        Update,
        (
            apply_damage.run_if(in_state(ServerState::Running)), // Server-only logic
            display_vfx.run_if(in_state(ClientState::Connected)), // Client-only logic
        ),
    );
    
    // Reacting to state changes
    app.add_systems(OnEnter(ClientState::Connecting), display_connection_message)
       .add_systems(OnExit(ClientState::Connected), show_disconnected_message)
       .add_systems(OnEnter(ServerState::Running), initialize_match);
  8. Understand ClientSystems execution order

    master

    The ClientPlugin organizes its logic into several SystemSets. Understanding these helps when scheduling your own systems relative to replication:

    SetStagePurpose
    ClientSystems::ReceivePacketsPreUpdateReceives raw packets from the messaging backend.
    ClientSystems::ReceivePreUpdate / OnEnter(Connected)Processes received messages and applies replication.
    ClientSystems::DiagnosticsPreUpdate / OnEnter(Connected)Populates Bevy diagnostics.
    ClientSystems::SendHashOnEnter(Connected)Sends the protocol hash to the server for validation.
    ClientSystems::SendPostUpdatePrepares outgoing replication messages.
    ClientSystems::SendPacketsPostUpdateSends packets to the messaging backend.
    ClientSystems::ResetOnExit(Connected)Resets client resources upon disconnection.
  9. Customize entity despawning behavior

    master

    The ReplicationRegistry allows you to override the default entity despawning logic by providing a custom DespawnFn. By default, the registry uses the despawn function, which simply calls entity.despawn(). You can replace this with a custom function if you need to intercept despawns to perform special cleanup or logic during the replication process.

    // Define a custom despawn function
    fn custom_despawn(ctx: &DespawnCtx, mut entity: EntityWorldMut) {
        // Custom logic here
        entity.despawn();
    }
    
    // Apply it to the registry
    let mut registry = ReplicationRegistry::default();
    registry.despawn = custom_despawn;
  10. How Replicon channels work and how to use them

    master

    RepliconChannels is a resource that manages the communication channels used by Replicon. It distinguishes between channels used for sending and receiving based on whether the application is running as a client or a server.

    Directionality

    • On a Client:
      • RepliconChannels::client_channels() are used for sending data to the server.
      • RepliconChannels::server_channels() are used for receiving data from the server.
    • On a Server:
      • RepliconChannels::server_channels() are used for sending data to clients.
      • RepliconChannels::client_channels() are used for receiving data from clients.

    Note: If your backend does not distinguish between sending and receiving, you should create channels for both client and server by chaining them.

    Custom Channels

    Backends can define their own channels by writing an extension trait for RepliconChannels. Any custom channels created should have a delivery guarantee equal to or stronger than the ones they are replacing.

    Reserved Channels

    Replicon uses specific reserved channels for core replication logic:

    Server-to-Client (ServerChannel)

    • ServerChannel::Updates: An ordered reliable channel used for entity mappings, inserts, removals, and despawns. This ensures atomic updates and prevents outdated state.
    • ServerChannel::Mutations: An unreliable channel used for component mutations. This allows for eventual consistency where the latest values are prioritized over missing intermediate ticks.

    Client-to-Server (ClientChannel)

    • ClientChannel::MutationAcks: An ordered reliable channel used by the client to acknowledge mutation messages received via ServerChannel::Mutations.

    Channel Delivery Guarantees

    All channels are defined by a Channel type specifying their reliability and ordering:

    • Channel::Unreliable: Unreliable and unordered.
    • Channel::Unordered: Reliable and unordered.
    • Channel::Ordered: Reliable and ordered.
    // Example of accessing channels (conceptual usage)
    fn check_channels(channels: Res<RepliconChannels>) {
        let server_to_client = channels.server_channels();
        let client_to_server = channels.client_channels();
    }
  11. How to implement a custom messaging backend

    master

    To integrate a custom messaging layer (like Renet or a custom UDP implementation) with Bevy Replicon, you must implement a backend that manages the communication flow between client and server. Because of Rust's orphan rules, Bevy Replicon does not provide a trait to implement; instead, your backend must interact with the following components:

    1. Channels: Create channels defined in the RepliconChannels resource. You typically use an extension trait to convert your backend's configuration into the required channels.
    2. State Management: Manage the ClientState (on the client) and ServerState (on the server).
    3. Message Buffering: Update the ServerMessages and ClientMessages resources to move data between the network and the Replicon systems.
    4. Client Lifecycle: Spawn and despawn entities with the ConnectedClient component.
    5. Disconnection: React to DisconnectRequest messages sent by the server to queue a disconnection for a specific client.
    6. Statistics (Optional): Update the ClientStats resource and ConnectedClientStats components to provide network telemetry.

    Best Practices:

    • Split your integration into separate client and server plugins.
    • Use server and client features to allow users to disable unused parts at compile time.
    • Refer to bevy_replicon_renet for a production-grade implementation reference.