Skip to main content
Version: Next

Messaging

What for?

In virtual spaces where people can gather and communicate, they often want to form groups to exchange messages, such as multiplayer or text chat.

Therefore, Extreal provides a messaging feature that allows groups to exchange messages. This module provides a default implementation of messaging features using Socket.IO. The server used for messaging, like Socket.IO, is called a messaging server in this module.

Specification

  • You can exchange messages in groups.
  • You can controls the timing of sending and receiving messages (queuing).
  • You can add processing triggered by client state.
  • Messaging with Socket.IO.
    • Native (C#) and WebGL (JavaScript) support.
tip

When many messages are exchanged in a short period of time, such as when realizing a multiplayer game, the communication load remains high. In such cases, queuing can be used to reduce the communication load.

Architecture

Messaging

classDiagram DisposableBase <|-- MessagingClient DisposableBase <|-- QueuingMessagingClient QueuingMessagingClient --> MessagingClient class MessagingClient { <<abstract>> +OnJoined IObservable +OnLeaving IObservable +OnUnexpectedLeft IObservable +OnJoiningApprovalRejected IObservable +OnClientJoined IObservable +OnClientLeaving IObservable +OnMessageReceived IObservable +ListGroupsAsync() List +JoinAsync(connectionConfig) void +LeaveAsync() void +SendMessageAsync(message, to) void } class QueuingMessagingClient { +OnJoined IObservable +OnLeaving IObservable +OnUnexpectedLeft IObservable +OnJoiningApprovalRejected IObservable +OnClientJoined IObservable +OnClientLeaving IObservable +QueuingMessagingClient(messagingClient) +EnqueueRequest(message, to) void +ResponseQueueCount() int +DequeueResponse() from, message +ListGroupsAsync() List +JoinAsync(connectionConfig) void +LeaveAsync() void } class DisposableBase { <<extreal>> }

Socket.IO

Unity

classDiagram SocketIOMessagingClientProvider ..> SocketIOMessagingClient SocketIOMessagingClient <|-- NativeSocketIOMessagingClient SocketIOMessagingClient <|-- WebGLSocketIOMessagingClient NativeSocketIOMessagingClient ..> SocketIOMessagingConfig WebGLSocketIOMessagingClient ..> SocketIOMessagingConfig MessagingClient <|-- SocketIOMessagingClient DisposableBase <|-- SocketIOMessagingClient class SocketIOMessagingClientProvider { +Provide(messagingConfig)$ SocketIOMessagingClient } class SocketIOMessagingClient { <<abstract>> +SocketIOMessagingClient() } class SocketIOMessagingConfig { +Url string +SocketIOOptions SocketIOOptions +SocketIOMessagingConfig(url, socketIOOptions) } class NativeSocketIOMessagingClient { +NativeSocketIOMessagingClient(messagingConfig) } class WebGLSocketIOMessagingClient { +WebGLSocketIOMessagingClient(messagingConfig) } class MessagingClient { <<extreal, abstract>> } class DisposableBase { <<extreal>> }

JavaScript

classDiagram WebGLSocketIOMessagingClient ..> WebGLHelper WebGLHelper ..> SocketIOMessagingClient SocketIOMessagingClientAdapter ..> SocketIOMessagingClient class WebGLHelper { <<C#>> } class WebGLSocketIOMessagingClient { <<C#>> } class SocketIOMessagingClientAdapter { <<TypeScript>> } class SocketIOMessagingClient { <<TypeScript>> }

Installation

Package

Messaging

https://github.com/extreal-dev/Extreal.Integration.Messaging.git

Socket.IO

Unity
https://github.com/extreal-dev/Extreal.Integration.Messaging.Socket.IO.git
npm
@extreal-dev/extreal.integration.messaging.socket.io

Dependencies

Messaging uses the following packages.

Messaging

Socket.IO

Unity
npm

Please refer to Release for the correspondence between module version and each package version.

Settings

Messaging server

Messaging servers are provided by Docker Compose.

Rooms of Socket.IO are used for group messaging.

Sets the maximum number of people per group when starting up the messaging server. If more than the maximum number of clients attempt to join a group, the client will be denied participation. The maximum number of people per group is specified by MESSAGING_MAX_CAPACITY in the compose.yaml file.

environment:
# If "on" is logging, otherwise is not. In production, set it to "off".
MESSAGING_LOGGING: ${MESSAGING_LOGGING:-on}
# Capacity of one room
MESSAGING_MAX_CAPACITY: ${MESSAGING_MAX_CAPACITY:-80} # Change here
# In production, change it to suit the environment.
MESSAGING_CORS_ORIGIN: ${MESSAGING_CORS_ORIGIN:-*}

Please refer to README to prepare your messaging server.

tip

If you wish to scale out by adding more messaging servers, you can do so by using the Redis Adapter provided by Socket.IO. See Redis Adapter for details.

Application

Create a SocketIOMessagingClient using the SocketIOMessagingClientProvider.

public class ClientControlScope : LifetimeScope
{
protected override void Configure(IContainerBuilder builder)
{
var messagingConfig = new SocketIOMessagingConfig("url", socketIOOptions);
var messagingClient = SocketIOMessagingClientProvider.Provide(messagingConfig);
builder.RegisterComponent<MessagingClient>(messagingClient);

builder.RegisterEntryPoint<ClientControlPresenter>();
}
}

When used with WebGL, JavaScript initialization is required. Create a SocketIOMessagingAdapter and call the adapt function.

import { SocketIOMessagingAdapter } from "@extreal-dev/extreal.integration.messaging.socket.io";

const messagingAdapter = new SocketIOMessagingAdapter();
messagingAdapter.adapt();

Usage

Exchange messages in groups

The feature to exchange messages in groups is provided by MessagingClient.

To join an existing group, use ListGroupsAsync to get a list of groups.

var groups = await messagingClient.ListGroupsAsync();

A list of Groups will be returned, so join the group using the Group obtained here.

var group = /* Select group from groups */;
var joiningConfig = new MessagingJoiningConfig(group.Name);
await messagingClient.JoinAsync(joiningConfig);

If you want to create a new group, join it by specifying a group name that is not part of existing groups.

var joiningConfig = new MessagingJoiningConfig("NewGroupName");
await messagingClient.JoinAsync(joiningConfig);

To send a message, use SendMessageAsync. If you want to send a message to the members of a group, specify only the message.

await messagingClient.SendMessageAsync("message");

If you want to send a message to a specific member of a group, specify the client ID in addition to the message.

await messagingClient.SendMessageAsync("message", toClientId);

The IDs of the clients participating in the group can be obtained from the OnClientJoined event parameter.

private readonly List<string> joinedClients = new List<string>();
messagingClient.OnClientJoined
.Subscribe(joinedClients.Add)

The OnMessageReceived event is used to receive messages.

messagingClient.OnMessageReceived
.Subscribe(HandleReceivedMessage)
.AddTo(disposables);

private void HandleReceivedMessage((string clientId, string message) tuple)
{
// Handle message
}

To leave the group, use LeaveAsync.

await messagingClient.LeaveAsync();

Control the timing of sending and receiving messages (queuing)

Queuing feature is provided by QueuingMessagingClient. QueuingMessagingClient is a wrapper class for MessagingClient. To use the queuing feature, initialize QueuingMessagingClient.

public class ClientControlScope : LifetimeScope
{
protected override void Configure(IContainerBuilder builder)
{
var messagingConfig = new SocketIOMessagingConfig("url", socketIOOptions);
var messagingClient = SocketIOMessagingClientProvider.Provide(messagingConfig);
var queuingMessagingClient = new QueuingMessagingClient(messagingClient);
builder.RegisterComponent(queuingMessagingClient);

builder.RegisterEntryPoint<ClientControlPresenter>();
}
}

QueuingMessagingClient controls the timing of sending and receiving by holding messages to be sent and received in the request and response queues, respectively.

If you want to send a message, add the message to the request queue.

If you want to send a message to a group member, specify only the message.

await queuingMessagingClient.EnqueueRequest("message");

If you want to send a message to a specific member of a group, specify the client ID in addition to the message.

await queuingMessagingClient.EnqueueRequest("message", toClientId);

Received messages are received from the response queue. The number of messages in the response queue can be checked with ResponseQueueCount.

while (queuingMessagingClient.ResponseQueueCount() > 0)
{
(var from, var message) = queuingMessagingClient.DequeueResponse();
// Handle message
}

Add processing triggered by client state

MessagingClient/QueuingMessagingClient has the following event notifications.

  • OnJoined
    • Timing: Immediately after joining a group
    • Type: IObservable
    • Parameters: Own client ID
  • OnLeaving
    • Timing: Just before leaving the group
    • Type: IObservable
    • Parameters: None
  • OnUnexpectedLeft
    • Timing: Immediately after an unexpected server disconnection occurs
    • Type: IObservable
    • Parameters: Reason for disconnection
  • OnJoiningApprovalRejected
    • Timing: Immediately after a joining was rejected
    • Type: IObservable
    • Parameters: None
  • OnClientJoined
    • Timing: Immediately after a client joins
    • Type: IObservable
    • Parameters: Joined client ID
  • OnClientLeaving
    • Timing: Just before a client leaves
    • Type: IObservable
    • Parameter: Client ID to be disconnected
  • OnMessageReceived
    • Timing: Immediately after a message is received
    • Type: IObservable
    • Parameters: ID of the client who sent the message and the message