Table of Contents

Session Properties

Overview

Each Session has access to a data storage space in the server that can only be accessed by applications connected to that specific session. This data storage space is implemented as a dictionary consisting of a string key and an object value.

This data structure is not replicated in the Unity side and data is accessed through get, set and clear methods. When doing so, a specific entry of the dictionary can be queried or set, but the data structure can't be retrieved as a whole element.

This entries exist in the server until they are manually deleted by a user or until the session is finished, meaning that it will persist scene changes by default while the application is running. Developers should check (either when joining a session, loading a specific scene or reconnecting after a network disconnection) the current state of properties relevant to their logic.

API

You can use the following interface to run Session Property operations.

public interface ISessionMessageSender
{
    Task<SessionPropertyOperationResponse> ClearSessionProperties(IEnumerable<string> propertyKeys);

    Task<SessionPropertyOperationResponse> GetSessionProperties(IEnumerable<string> propertyKeys);

    Task<SessionPropertyOperationResponse> SetSessionProperties<T>(IEnumerable<SetSessionPropertyData<T>> setPropertyData);
}

This methods allow developers to Get, Set or Clear multiple properties at once.

The SessionPropertyOperationResponse contains the following properties:

SessionPropertyOperationResponse
Field Description
Success Wether the server handled the message or not.
ErrorCode A error code for the case where the Success property is false.
Id A unique id for the operation. Handled by VIROO.
SenderClientId The identifier of the user that ran the operation.
Data A list of SessionPropertyOperationResponseData responses for each of the session properties affected for the operation.
SessionPropertyOperationResponseData
Field Description
Success Wether the server handled the operation or not.
ErrorCode A error code for the case where the Success property is false.
Operation A enum containing the type of operation: Query, SetValue or Delete.
PropertyKey The identifier of property that was affected.
Value The value of the property as a generic object.

Since the most common use case is to Get, Set or Clear a single property. The following methods are also available in the same interface.

public interface ISessionMessageSender
{
    Task<SessionPropertyOperationResponse> ClearSessionProperty(string propertyKey);

    Task<SessionPropertyOperationResponse> GetSessionProperty(string propertyKey);

    Task<SessionPropertyOperationResponse> SetSessionProperty<T>(string propertyKey, T value);
}

For the same reason, the SessionPropertyOperationResponse exposes a method to get the response when the operation affects a single property, simplifying the access to the returned list in the Data property.

public class SessionPropertyOperationResponse
{
    SessionPropertyOperationResponseData GetSingleResponseData()
}

Usage sample

[Serializable]
public class Data
{
    public string Field1 { get; set; }
    public int Field2 { get; set; }
    public SubData Field3 { get; set; }
}

[Serializable]
public class SubData
{
    public string Field1 { get; set; }
    public int Field2 { get; set; }
}

private const string PropertyId = $"MyUniqueId";

protected async Task Save<Data>(Data data)
{
    if (!networkManager.IsInitialized)
    {
        await UniTask.WaitUntil(() => networkManager.IsInitialized);
    }

    string serializedData = System.Text.Json.JsonSerializer.Serialize(data);

    await sessionMessageSender.SetSessionProperty(PropertyId, serializedData);
}

private async Task<Data?> Get()
{
    if (!networkManager.IsInitialized)
    {
        await UniTask.WaitUntil(() => networkManager.IsInitialized);
    }

    SessionPropertyOperationResponse response = await sessionMessageSender.GetSessionProperty(PropertyId);
    SessionPropertyOperationResponseData data = response.GetSingleResponseData();

    if (data.Success)
    {
        string json = data.Value.UntypedValue.ToString();

        Data data = System.Text.Json.JsonSerializer.Deserialize<Data>(json, new System.Text.Json.JsonSerializerOptions()
        {
            PropertyNameCaseInsensitive = true,
        });
        return data;
    }

    return null;
}

private async Task Clear()
{
    if (!networkManager.IsInitialized)
    {
        await UniTask.WaitUntil(() => networkManager.IsInitialized);
    }

    await sessionMessageSender.ClearSessionProperty(PropertyId);
}

Getting notified of changes made by other users

When the value of a property is set or cleared by another instance of the application, a background operation is run by VIROO. If the SessionPropertyOperationResponse's SenderClientId property is different to the local user's ClientId, events are triggered to notify the local user of the changes made to the value of the property.

As a developer, you can subscribe to events of ISessionPropertyEventDispatcher to be notified of changes to all session properties, properties defined both by the application developer and properties defined by VIROO. This means that developers should check the event's parameter's PropertyKey value to see if the modified property is one they are using.

public class MyPropertyListenerBehaviour : MonoBehaviour
{
    [SerializeField]
    // If set to string.Empty, all Session Property operations will be logged
    private string propertyKey = default;

    private ISessionPropertyEventDispatcher eventDispatcher;

    protected void Inject(ISessionPropertyEventDispatcher eventDispatcher)
    {
        this.eventDispatcher = eventDispatcher;
        eventDispatcher.OnPropertyValueChanged += OnPropertyValueChanged;
        eventDispatcher.OnPropertyRemoved += OnPropertyRemoved;
    }

    protected void Awake()
    {
        // we call this method to have the Inject method automatically called by VIROO
        this.QueueForInject();
    }

    protected void OnDestroy()
    {
        // In order not to leave residual code, we will unsubscribe to the events we have previously subscribed to.
        if (eventDispatcher != null)
        {
            eventDispatcher.OnPropertyValueChanged -= OnPropertyValueChanged;
            eventDispatcher.OnPropertyRemoved -= OnPropertyRemoved;
        }
    }

    private void OnPropertyRemoved(object sender, PropertyRemovedEventArgs args)
    {
        string eventProperty = args.PropertyKey;
        if (string.IsNullOrEmpty(eventProperty) || eventProperty.Equals(propertyKey, StringComparison.Ordinal))
        {
            Debug.Log($"Property {eventProperty} value removed");
        }
    }

    private void OnPropertyValueChanged(object sender, PropertyValueChangedEventArgs args)
    {
        string eventProperty = args.PropertyKey;
        if (string.IsNullOrEmpty(eventProperty) || eventProperty.Equals(propertyKey, StringComparison.Ordinal))
        {
            Debug.Log($"Property {eventProperty} value changed by {args.SenderClientId}. New value: {args.Value}");
        }
    }
}

Since these events are triggered for all Session Property update operations, synchronizing data directly through Session Properties is recommended for one-off transactions to get and set values instead of subscribing to this event. Some examples of these kind of operations:

  • Checking some property when joining a session.
  • Setting a boolean value to work as a flag.

If you need to store synchronized values locally, and you would like to be notified of their changes specifically, you should use Network Variables.