Shared State Service
Within 2-Immerse we have adopted the MediaScape shared state service. This page details how we are using it, and in particular the mappings that we are using.
Service documentation: https://github.com/mediascape/shared-state
State is logically divided into a number of different ‘scopes’. Each scope is identified by a unique name string resembling a file path. Data can be shared between components using a pre-agreed scope name, specified in the timeline document.
A session describes a group of layout contexts taking part in a shared, synchronised experience. This is typically used for inter-home synchronised. Urls for timeline and layout documents must be shared together with a wall clock tuple for synchronisation purposes. The session scope may also contain one or more lobbyIds and a list of layoutContextIds. The name of the scope is defined as follows:
Where sessionId is a pre-determined identifier allocated during the on-boarding process, or hard-coded for trial purposes. The first layoutContext to join the session will initialise the shared state.
A DMApp stores application data in the global scope, allowing it to be shared by any number of different DMApp components. A simple example might be a state governing the audio volume of the application. The shared state service maintains the authoritative volume level, which other DMApp components can use. Servers are a more reliable source of authority than client devices.
A single global scope is created per layoutContextId. Any DMApp component can add/remove/change global state and these changes are propagated to all subscribers. The name of the global scope is defined as follows:
Where layoutContextId is the layout context associated with the DMApp.
A group scope provides a way for several related components to communicate with each other. A DMApp component such as a control surface, might mirror a subset of its attributes to a group scope. Another DMApp component may then listen to value changes in the group scope. This provide some of the much needed application ‘glue-code’, that would otherwise be implemented using a combination of global variables and event handlers in a non-distributed app.
Group scopes enable data-binding functionality to be constructed, permitting a more declarative style of programming.
For example, an control panel component could mirror attribute values such as paused, muted and volume to a group state. Then an HTML video element could listen to these value changes by subscribing to the group scope and adjust its volume accordingly.
The name of a group state is defined as follows:
Where groupStateId is an arbitrary identifier string used to define a group.
The shared state service also has pre-defined scopes for storing user preferences on a per-DMApp or DMApp independent basis.
Keys, Values and Data-binding
A scope stores a list of (key, value) pairs defined by the application and its components. Values can be any JSON value, but it is recommended that values are kept simple and atomic updates are performed using the shared state client’s transactional API.
There is a UpgradeCustomElement() function for mirroring nominated HTML custom element attributes to the shared state service. This is the functionality that makes it possible to hook up DMApp control surface components to DMApp view components. See:
The code deals with incoming parameter changes from the timeline service which it sets as HTML attributes on the upgraded custom element. Changes to these attributes are detected using a DOM mutation observer and then automatically propagated to the shared-state service. Similarly, the code listens to changes in the shared state and updates the HTML attributes. The mutation observer is cleaned up when the DMApp is destroyed.
The timeline author must define a parameter called ‘groupStateId’ which is the all important “magic cookie” that we use to ‘rendezvous’ components with each other. This parameter can be omitted, in which case no state is uploaded to the service. The value of groupStateId is used to build a unique name for the scope into which the state is stored.
The groupStateId attribute is also attached to the HTML custom element as an attribute and can be changed at any time by the application or the timeline to bind a DMApp control surface to a different DMApp view component.
The code has the beginnings of a data-binding implementation that will allow us to define mappings between component attribute names and state data using evaluated expressions.