Crate bevy_replicon_attributes

Source
Expand description

§Bevy Replicon Attributes

Extends bevy_replicon with attributes-based visibility control for server entities and events.

Assumes all client entities are assigned a stable NetworkId on the server.

§Basic example

use bevy::prelude::*;
use bevy_replicon::prelude::Replicated;
use bevy_replicon_attributes::prelude::*;

#[derive(Component)]
struct Bat;

#[derive(Event)]
struct SpawnBat;
#[derive(Event, Copy, Clone)]
struct BatAlert;
#[derive(Event)]
struct GainedNightVision(ClientId);

#[derive(VisibilityAttribute, Default, PartialEq)]
struct HasNightVision;
#[derive(VisibilityAttribute, Default, PartialEq)]
struct IsAwake;
#[derive(VisibilityAttribute, Default, PartialEq)]
struct HatesBats;

fn spawn_bats(
    mut commands : Commands,
    mut events   : EventReader<SpawnBat>,
    mut sender   : ServerEventSender<BatAlert>,
    attributes   : ClientAttributes,
){
    for _ in events.read()
    {
        // Entity
        commands.spawn((Bat, Replicated, vis!(HasNightVision)));

        // Server event
        sender.send(&attributes, BatAlert, vis!(all!(HasNightVision, IsAwake, HatesBats)));
    }
}

fn gain_night_vision(
    mut events     : EventReader<GainedNightVision>,
    mut attributes : ClientAttributes,
){
    for client_id in events.read()
    {
        // Client attribute
        attributes.add(client_id, HasNightVision);
    }
}

§Usage

§Setup

Add replicon to your server app. This crate only works with VisibilityPolicy::All and VisibilityPolicy::Whitelist.

See renet for how to set up a renet server.

use bevy::prelude::*;
use bevy_replicon::prelude::*;

app.add_plugins(bevy::time::TimePlugin)  //required by bevy_renet
    .add_plugins(RepliconPlugins
        .build()
        .disable::<ClientPlugin>()
        .set(ServerPlugin{
            visibility_policy: VisibilityPolicy::Whitelist,
            ..Default::default(),
        })
    );

Add VisibilityAttributesPlugin to your server app after the replicon plugins. The plugin will panic if you used VisibilityPolicy::Blacklist. You must specify a ReconnectPolicy:

use bevy_replicon_attributes::prelude::*;

app.add_plugins(VisibilityAttributesPlugin{ reconnect_policy: ReconnectPolicy::Reset });

If you choose ReconnectPolicy::Repair, we recommend also using bevy_replicon_repair for preserving replicated state on clients.

§Define attributes

Attributes can be derived with VisibilityAttribute, which requires Default and PartialEq. Only zero-sized types should use this derive.

#[derive(VisibilityAttribute, Default, PartialEq)]
struct InStartingArea;

More complex attributes should implement VisibilityAttribute manually.

struct InLocation(x: u32, y: u32);

impl VisibilityAttribute for InLocation
{
    fn inner_attribute_id(&self) -> u64
    {
        ((self.x as u64) << 32) + (self.y as u64)
    }
}

The inner_attribute_id defined here is used to differentiate attribute instances of the same type.

§Add attributes to a client

Add attributes to clients with the ClientAttributes system parameter.

Client attributes are used when evaluating entity VisibilityConditions to determine if entities should be replicated to a client.

use bevy::prelude::*;
use bevy_replicon::prelude::ServerEvent;
use bevy_replicon_attributes::prelude::*;

#[derive(VisibilityAttribute, Default, PartialEq)]
struct IsDisconnected;

fn update_visibility_on_connect_events(
    mut server_events : EventReader<ServerEvent>,
    mut attributes    : ClientAttributes,
){
    for event in server_events.read()
    {
        match event
        {
            ServerEvent::ClientConnected{ id } =>
            {
                attributes.remove(id, IsDisconnected);
                attributes.add(id, InStartingArea);
            }
            ServerEvent::ClientDisconnected{ id, _ } =>
            {
                attributes.add(id, IsDisconnected);
            }
        }
    }
}
§Default client attributes

All clients are given the Global and Client builtin attributes each time they connect.

§Entity visibility

Entity visibility is controlled by VisibilityConditions, which are arbitrary combinations of VisibilityAttributes and not()/and()/or() logic.

Entity visibility conditions are evaluated against client attribute lists to determine if entities can be seen by clients.

For convenience we have a vis!() macro which produces new VisibilityCondition components. The any!()/all!()/none!() macros can be used inside the vis!() macro in addition to not()/and()/or().

An empty visibility condition always evaluates to false. If you want global visibility for an entity, use the builtin Global attribute that is given to clients when they connect.

Here is a low-level example how it works. In practice you only need to add VisibilityAttributes to clients and VisibilityCondition components to entities. This crate will take care of translating that information into entity visibility within bevy_replicon.

use bevy::prelude::*;
use bevy_replicon_attributes::prelude::*;

fn entity_demo(
    mut commands   : Commands,
    mut attributes : ClientAttributes,
){
    let client_id = ClientId::from_raw(0u64);

    // Add location to client.
    attributes.add(client_id, InLocation(0, 20));

    // Make location condition.
    let location = vis!(InLocation(0, 20));

    // Evaluate condition.
    let client_attributes = attributes.get(client_id).unwrap();
    assert!(location.evaluate(|a| client_attributes.contains(&a)));

    // Spawn entity.
    commands.spawn((Replicated, location));
}

Here are examples of more complex visibility conditions:

// Basic
vis!();
vis!(A);
vis!(not(B));
vis!(and(A, B));
vis!(or(A, B));
vis!(and(A, not(B)));

// Composition
vis!(and(A, vis!(B)));

// Helpers
vis!(any!(A, B, C));   // vis!(or(A, or(B, C)))
vis!(all!(A, B, C));   // vis!(and(A, and(B, C)))
vis!(none!(A, B, C));  // vis!(not(or(A, or(B, C)))))

// Modification
vis!()
    .and(A)                           // vis!(A)
    .or(B)                            // vis!(or(A, B))
    .replace(or(A, B), and(C(1), D))  // vis!(and(C(1), D))
    .replace_type::<C>(E(2))          // vis!(and(E(2), D))
    .remove(E(2))                     // vis!(D)
    ;
§Server events

Visibility of server events can be controlled with the ServerEventSender system parameter.

Server events must be registered with bevy_replicon. Clients will receive server events with EventReader<T>.

use bevy::prelude::*;
use bevy_replicon::prelude::*;
use bevy_replicon_attributes::prelude::*;

#[derive(Event, Copy, Clone)]
struct E;

fn setup(app: &mut App)
{
    // Replicon server event registration
    app.add_server_event::<E>(EventType::Ordered);
}

fn send_event(mut sender: ServerEventSender<E>, attributes: ClientAttributes)
{
    sender.send(&attributes, E, vis!(any!(Client::from(1), Client::from(2), Client::from(3))));
}

§bevy_replicon compatability

bevy_repliconbevy_replicon_attributes
0.330.11
0.320.10
0.300.9
0.290.8
0.270.6 - 0.7
0.260.5
0.250.4
0.210.1 - 0.3

Modules§

prelude

Macros§

all
Syntax sugar for and(A, and(B, C)) etc.
any
Syntax sugar for or(A, or(B, C)) etc.
into_condition
Translates a token sequence into a type that implements IntoVisibilityCondition.
none
Syntax sugar for not(or(A, or(B, C))) etc.
vis
Syntax sugar for VisibilityCondition::new.

Structs§

Client
Builtin VisibilityAttribute added to all clients by default.
ClientAttributes
System parameter for updating client visibility attributes.
Global
Builtin VisibilityAttribute added to all clients by default.
ServerEventSender
System parameter for sending server events controlled by visibility conditions.
VisibilityAttributeId
Id associated with a visibility attribute.
VisibilityAttributesPlugin
Plugin that sets up visibility handling systems in a server using bevy_replicon.
VisibilityConditionBuilder
Visibility condition builder.
VisibilityConditionId
Id associated with a visibility condition.
VisibilityUpdateSet
System set that collects entity VisibilityCondition changes and translates them into bevy_replicon client visibility.

Enums§

ReconnectPolicy
Configures handling of reconnects,
VisibilityCondition
A type-erased visibility condition.
VisibilityConditionNode
A node within a visibility condition tree.

Constants§

SMALL_PACK_LEN
Max number of nodes for non-allocating VisibilityConditions.

Traits§

DefaultVisibilityAttribute
Implemented by the derive for VisibilityAttribute.
IntoVisibilityCondition
Represents a type that can convert into a visibility condition.
VisibilityAttribute
Signifies that a type is a visibility attribute.

Functions§

and
Creates an AND visibility condition.
empty
Creates an EMPTY visibility condition.
not
Creates a NOT visibility condition.
or
Creates an OR visibility condition.

Derive Macros§

VisibilityAttribute