Room class


Room class represents a single room. It allows you to broadcast messages to all players in the room, and create actors.

Properties

Type Name Description
string Name Name of the room
bool IsPublic True if the room is public
bool IsJoined True if the player is in the room
int MaxPlayers Maximum number of players in the room. If negative, there is no limit.
Players IReadOnlyCollection\<Player> Collection of players in the room.
Actors IReadOnlyCollection\<Actor> Collection of actors in the room.

Methods

CreateRoom

Creates a new room if room with same name does not exist already. This method accepts custom properties, which can be used for matchmaking.

Use CreateRoomOptions to configure the room's visibility and number of players.

Note: Properties must be a simple POCO class. It can use nested classes, but cannot use any collections, or circular references.

void CreateRoom<T>(string name, T properties, CreateRoomOptions opts);
void CreateRoom(string name, CreateRoomOptions opts);

CreateOrJoinRoom

Joins the room if it exists and max number of players is not reached, or creates a new one if it does not. If the room is created, it will have the properties passed to this method.

Use CreateRoomOptions to configure the room's visibility and number of players.

Note: Properties must be a simple POCO class. It can use nested classes, but cannot use any collections, or circular references.

void CreateOrJoinRoom<T>(string name, T properties, CreateRoomOptions opts);
void CreateOrJoinRoom(string name, CreateRoomOptions opts);

JoinRoom

Joins the room with given name.

void Join(string name);

LeaveRoom

Leaves the room.

void LeaveRoom();

Broadcast

Broadcasts a message to all players in the room.

void Broadcast<T>(T message);

RegisterRoomBroadcastHandler

Register a handler for messages broadcasted by a player in the room.

void RegisterRoomBroadcastHandler<TMessage>(Action<Room, Player, TMessage> callback);

CreateActor

Creates an actor with a given state.

void CreateActor<T>(T state);

RegisterActorBroadcastHandler

Register a handler for messages broadcasted by an actor in the room.

void RegisterActorBroadcastHandler<TMessage>(Action<Room, Actor, TMessage> callback);

Events

RoomCreated

This event is invoked when a room is successfuly created.

delegate void RoomCreated(Room room);

FailedToCreateRoom

This event is invoked when failed to create a room.

delegate void FailedToCreateRoom(Room room);

FailedToJoinRoom

This event is invoked when failed to join a room.

delegate void FailedToJoinRoom(Room room);

PlayerJoined

This event is invoked when a player joins a room.

delegate void PlayerJoined(Room room, Player player);

PlayerLeft

This event is invoked when a player has left a room. When a player leaves the room, every actor he created also leaves a room.

delegate void PlayerLeft(Room room, Player player);

ActorCreated

This event is invoked when an actor is created in the room.

delegate void ActorCreated(Room room, Actor actor);

ActorDeleted

This event is invoked when actor is deleted from a room.

delegate void ActorDeleted(Room room, Actor actor);

ActorStateUpdated

This event is invoked when actor state is updated.

delegate void ActorStateUpdated(Room room, Actor actor);

Examples

In this example, we want to create a car racing multiplayer, where players can customize their car.

Initially, we want every player to be in a same room, but we will add matchmaking later on.

// setup the client and create factories for plugins
IPluginHost host = ClientFactory.GetPluginHost("my-app-key", "Development");
_factory = new MultiplayerFactory(host);

// ..
// after the connection has been established, create a room:

private void Client_OnConnected()
{
    var mpService = _factory.Create(_client);
    var room = new Room(mpService);
    // register event handlers
    room.PlayerJoined += Room_PlayerJoined;
    room.PlayerLeft += Room_PlayerLeft;

    // Create or join a room with name 'all-players'
    room.CreateOrJoinRoom("all-players");
}

private void Room_PlayerJoined(Room room, Player player)
{
    Debug.Log($"{player.DisplayName} joined the room");
    if (player.IsMine)
    {
        Debug.Log("This is me!");
    }
}

private void Room_PlayerLeft(Room room, Player player)
{
    Debug.Log($"{player.DisplayName} left the room");
}

Now that we are in a room, we need to communicate with other players. There are 2 ways to do that: by broadcasting a message as a player, or by creating an actor, which represents a gameobject, and broadcasting the message as an actor.

The second approach is better in cases where you have multiple game objects, or the game objects have state.

In this example, we want to have a player customize his car, so we will go with the second approach. Let's create 2 POCO classes, one for the car state (e.g. color, acceleration, etc), and one for position sync messages.

Additionally, we need to create serializers for the classes, and finally, to register these serializers. Note: Serializers need to implement IBinarySerializer<T>, and they need to have different TypeKeys.

public class CarState
{
    public string Color { get; set; }
    public float Acceleration { get; set; }
    public float MaxSpeed { get; set; }

    public class Serializer : IBinarySerializer<CarState>
    {
        public int TypeKey => 0;
        public void Write(IBinaryWriter writer, CarState data)
        {
            writer.WriteString(data.Color);
            writer.WriteFloat(data.Acceleration);
            writer.WriteFloat(data.MaxSpeed);
        }

        public CarState Read(IBinaryReader reader)
        {
            return new CarState
            {
                Color = reader.ReadString(),
                Acceleration = reader.ReadFloat(),
                MaxSpeed = reader.ReadFloat()
            };
        }
    }
}

public class CarSyncData
{
    public float PositionX { get; set; }
    public float CurrentSpeed { get; set; }

    public class Serializer : IBinarySerializer<CarSyncData>
    {
        public int TypeKey => 1;

        public void Write(IBinaryWriter writer, CarSyncData data)
        {
            writer.WriteFloat(data.PositionX);
            writer.WriteFloat(data.CurrentSpeed);
        }

        public CarSyncData Read(IBinaryReader reader)
        {
            return new CarSyncData
            {
                PositionX = reader.ReadFloat(),
                CurrentSpeed = reader.ReadFloat()
            };
        }
    }
}

// register the serializers in the multiplayer factory
_factory.RegisterSerializer(new CarState.Serializer());
_factory.RegisterSerializer(new CarSyncData.Serializer());

The next step is to create an actor with a given state, and periodically send data updates about position. We will create the actor when our player has joined to room:

private void Room_PlayerJoined(Room room, Player player)
{
    Debug.Log($"{player.DisplayName} joined the room");
    if (player.IsMine)
    {
        Debug.Log("This is me!");
        room.CreateActor(new CarState
        {
            MaxSpeed = 10,
            Acceleration = 3,
            Color = "red"
        });
    }
}

Once the actor is created, we want to send periodic updates to all players. Let's handle the ActorCreated and ActorDeleted events, add a handler for actor message broadcasts, and send position broadcasts every 100ms.

// register event handlers
room.ActorCreated += Room_ActorCreated;
room.ActorDeleted += Room_ActorDeleted;
// register actor broadcast handler
room.RegisterActorBroadcastHandler<CarSyncData>(Room_ActorBroadcastReceived);

//...

// create a dictionary which contains game objects (cars) for each actor
_cars = new Dictionary<int, CarController>();
// define the methods


private void Room_ActorDeleted(Room room, Actor actor)
{
    var carController = _cars[actor.Id];
    _cars.Remove(actor.Id);
    // handle car removed from the game..
}

private void Room_ActorCreated(Room room, Actor actor)
{
    var car = GameObject.Instantiate(CarPrefab);
    // initialize the car from the state
    var carController = car.GetComponent<CarController>();
    // actor.State property will be of type CarState
    carController.Initialize((CarState)actor.State);
    _car[actor.Id] = carController;

    if (actor.Owner.IsMine)
    {
        Task.Run(() => StartBroadcast(actor));
    }
}

private async Task StartBroadcast(Actor actor)
{
    const int BroadcastDelayMs = 100;
    while (true)
    {
        var carController = _cars[actor.Id];
        var data = carController.GetSyncData();
        actor.Broadcast(data);
        await Task.Delay(BroadcastDelayMs);
    }
}

private void Room_ActorBroadcastReceived(Room room, Actor actor, CarSyncData data)
{
    var carController = _cars[actor.Id];
    carController.UpdateSyncData(data); // update the gameobject position based on the data
}

Matchmaking

The final piece of the puzzle is to add matchmaking. If the game is to be fun for everyone, players need to be matched with players of the similar skills. We could also filter maps we want to play by name and other properties.

To do that, let's create a POCO class which contains matchmaking parameters.

public class MatchmakingInfo
{
    public int Level { get; set; }
    public string Map { get; set; }
    public int Laps { get; set; }
}

Now, we want to search rooms with specific properties and join the first one, or create a new one if none is found.

In this example, we will search for a map where players with level 5 play.

var criterion = new QueryBuilder<MatchmakingInfo>().Equal(x => x.Level, 5);
var query = new ListRoomsQuery<TestData> { Take = 1, Restriction = criterion };
var result = await _service.ListRooms(query);

var room = new Room(_service);
// attach room event handlers..

if(result.IsSuccessful && result.Data.Any())
{
    // join the room by name
    room.Join(result.Data.First().Name);
}
else
{
    // create the room
    var roomName = ... ;// generate unique/random room name
    var matchmakingInfo = new MatchmakingInfo
    {
        Level = 5,
        Map = "SpeedCity",
        Laps = 3
    };

    room.Create(roomName, matchmakingInfo);
}

results matching ""

    No results matching ""