Interest management

Interest management is the concept of only replicating to clients the entities that they need.

For example: in a MMORPG, replicating only the entities that are "close" to the player.

There are two main advantages:

  • bandwidth savings: it is pointless to replicate entities that are far away from the player, or that the player cannot interact with. Those bandwidth savings become especially important when you have a lot of concurrent connected clients.
  • prevent cheating: if you replicate entities that the player is not supposed to see, there is a risk that clients read that data and use it to cheat. For example, in a RTS, you can avoid replicating units that are in fog-of-war.

Implementation

VisibilityMode

The first step is to think about the NetworkRelevanceMode of your entities. It is defined on the Replicate component.

#[derive(Default)]
pub enum VisibilityMode {
  /// We will replicate this entity to all clients that are present in the [`NetworkTarget`] AND use relevance on top of that
  InterestManagement,
  /// We will replicate this entity to all clients that are present in the [`NetworkTarget`]
  #[default]
  All
}

If NetworkRelevanceMode::All, you have a coarse way of doing interest management, which is to use the replication_target to specify which clients will receive client updates. The replication_target is a NetworkTarget which is a list of clients that we should replicate to.

In some cases, you might want to use NetworkRelevanceMode::InterestManagement, which is a more fine-grained way of doing interest management. This adds additional constraints on top of the replication_target, we will never send updates for a client that is not in the replication_target of your entity.

Interest management

If you set NetworkRelevanceMode::InterestManagement, we will add a ReplicateVisibility component to your entity, which is a cached list of clients that should receive replication updates about this entity.

There are several ways to update the relevance of an entity:

  • you can either update the relevance directly with the RelevanceManager resource
  • we also provide a more static way of updating the relevance with the concept of Rooms and the RoomManager resource.

Immediate relevance update

You can simply directly update the relevance of an entity/client pair with the RelevanceManager resource.

#![allow(unused)]
fn main() {
use bevy::prelude::*;
use lightyear::prelude::*;
use lightyear::prelude::server::*;

fn my_system(
    mut relevance_manager: ResMut<RelevanceManager>,
) {
    // you can update the relevance like so
    relevance_manager.gain_relevance(ClientId::Netcode(1), Entity::PLACEHOLDER);
    relevance_manager.lose_relevance(ClientId::Netcode(2), Entity::PLACEHOLDER);
}
}

Rooms

An entity can join one or more rooms, and clients can similarly join one or more rooms.

We then compute which entities should be replicated to which clients by looking at which rooms they are both in.

To summarize:

  • if a client is in a room but the entity is not (or vice-versa), we will not replicate that entity to that client
  • if the client and entity are both in the same room, we will replicate that entity to that client
  • if a client leaves a room that the entity is in (or an entity leaves a room that the client is in), we will despawn that entity for that client
  • if a client joins a room that the entity is in (or an entity joins a room that the client is in), we will spawn that entity for that client

This can be useful for games where you have physical instances of rooms:

  • a RPG where you can have different rooms (tavern, cave, city, etc.)
  • a server could have multiple lobbies, and each lobby is in its own room
  • a map could be divided into a grid of 2D squares, where each square is its own room
#![allow(unused)]
fn main() {
use bevy::prelude::*;
use lightyear::prelude::*;
use lightyear::prelude::server::*;

fn room_system(mut manager: ResMut<RoomManager>) {
   // the entity will now be visible to the client
   manager.add_client(ClientId::Netcode(0), RoomId(0));
   manager.add_entity(Entity::PLACEHOLDER, RoomId(0));
}
}