Skip to content
This repository has been archived by the owner on Aug 19, 2018. It is now read-only.

ArbiterClientView

David Daeschler edited this page Apr 7, 2017 · 5 revisions

The Arbiter ClientView Object

Halcyon contains what is known as a ClientView object that processes messages from the simulator and is responsible for general communication between a client program and the simulator.

To reduce the responsibility of the Halcyon server to perform message fan-out, we will create virtual client instances for each user connected to the simulator via an arbiter. This will require a higher level protocol between the simulator and arbiter that tags the clients that should receive a message as part of the message body.

We will send messages in batches at a specified rate (30 hz?). This will ensure that if there are multiple (even dozens of) clients requiring the same message, they are queued together within the same quantum of time and sent out as a single flatbuffer.

Initial message support

We will begin by supporting the ObjectUpdate (full update), KillObject, and the TerseUpdate (movement) messages. This will allow users to see the objects in a scene being streamed live to their view as well as view objects moving around the scene.

ObjectUpdate

The UDP ObjectUpdate describes nearly the entire state of a primitive object existing inside a simulator. Much of this data is not really required to display an object, and so when we create a flatbuffer serialized version of this message, we will strip out unnecessary fields

ObjectUpdate is only the beginning however. When updates happen to even a single primitive in a group, the visual hash for the whole group changes. Each prim change in a group will mean that the arbiter code on the simulator will be responsible for recalculating the prim and group visual hashes.

Currently, optimization on babylon requires us to be able to instance out drawing of every group. When a single primitive changes, it will be necessary to recalculate the group hash and send that and the single primitive hash to the web client along with the prim/group local IDs.

The web client will then be responsible for knowing if it has a cached or live version of the primitive/group. If it does, the group can be loaded from cache/instance. If not, the web client will have to contact the arbiter for a new version of the group by its visual hash.

Since we're not currently having to combine prims into a single mesh for performance reasons, we can replace a single primitive on a group that only has one instance loaded in a scene. This can be used as an optimization for changing groups. Once there is more than one instance of a group present however, our only response will have to be to rebuild a brand new group with the new primitive.

When the client calls out to get a babylon mesh, it will pass not only the primitive and group hashes, but also the PrimUpdate message which contains the simulatorid, localid, the group mesh, and the primitive mesh hash.

If the arbiter doesn't have the primitive or group cached that is being requested, it will call back to the simulator with the PrimUpdate message. If the primitive/group has changed since the PrimUpdate was generated, the simulator will pass back the updated primitive and group ids, using the localid to find the prim.

Message Flow

  • (simulator) SceneObjectPart fires SendPrimitiveToClient or the immediate version
  • (simulator) The virtual client view determines if the client has seen the group already
    • If the client has seen the group already, the hash for the changed primitive is computed along with the new group hash calculated from cached primitive values and the new primitive value.
    • If the client has not seen the group, the entire group hash with all primitive hashes is calculated.
  • The set of hashes is sent to the arbiter via the virtual client view for processing
  • The web client gets the message, and processing depends on its local cache:
    • If the client already has the new group loaded in the simulator, it instances that group
    • If the client already has the new group cached, it loads the group and instances appropriately
    • If the client has the old version of the group cached without the new part, it loads the new part from the transform gateway, replaces the part, and instances/caches appropriately.
    • If the client doesn't have anything cached, it loads the whole group from the transformation gateway and instances/caches appropriately.