Janus Functionality

From EQUIS Lab Wiki

Jump to: navigation, search

Contents

Creating Timelines

The simplest way to create a timeline is:

   var a = new Timeline<float>("a");

Other syntax may be used such as:

   var b = TimelineManager.Default.Get<float>("b");

Although longer, this is somewhat safer as a check is performed to verify that the name of the timeline is unique. This check is not performed when the new construct is used.

A timeline is associated with a timeline manager and by default the association is made when the timeline is first created. This can be performed in a separate step:

   var c = new Timeline<float>("c", false);
   TimelineManager.Default.Add(c);

Types of Timelines

Default Timeline Types

Timelines are available for any of the following data types:

  • bool
  • int
  • uint
  • short
  • ushort
  • long
  • ulong
  • float
  • double
  • char
  • string
  • byte
  • byte[]

In addition the following timeline types have been created in the Janus Package for Unity.

  • Vector2
  • Vector3
  • Vector4
  • Matrix4x4
  • Quaternion
  • Ray
  • Color
Creating Additional Timeline Types

It is also possible to create timelines for any arbitrary class. However, the user should define the following methods to encode and decode the object. If these methods are not created, default serialization and deserialization will be used and tend to be very verbose.

  • Encode – converts the value to a byte array to transmission over the network
  • Decode – converts a byte array back to an object

If it is possible to interpolate and extrapolate values, these methods should also be created. Otherwise, stepwise interpolation and extrapolation will be used.

  • Interpolate
  • Extrapolate

Here is an example of how to create these methods for the Vector2 class:

   public static byte[] EncodeVector2 (Vector2 value)
   {
       byte[] bytes = new byte[2 * sizeof(float)];
   
       BinaryWriter bw = new BinaryWriter(new MemoryStream(bytes));
       bw.Write(value.x); bw.Write(value.y);
       bw.Close();
   
       return bytes;
       }
   
   public static Vector2 DecodeVector2 (byte[] bytes)
   {
       BinaryReader br = new BinaryReader(new MemoryStream(bytes));
       var value = new Vector2(br.ReadSingle(), br.ReadSingle());
       br.Close();
   
       return value;
   }
   
   public static Vector2 InterpolateVector2Slerp (Timeline<Vector2>.Context context)
   {
       return Vector3.Slerp(context.Prev.Value, context.Next.Value,
             (float)(context.Time - context.Prev.Time) / (float)(context.Next.Time –
             context.Prev.Time));
   }
   	
   public static Vector2 ExtrapolateVector2Slerp (Timeline<Vector3>.Context context)
   {
       if (context.Prev.Prev != null && context.Prev.Prev.Time != context.Prev.Time)
       {
           return ExtrapolateVector3Slerp(context.Prev.Prev.Value,
                  context.Prev.Value,
                      (float)(context.Prev.Time - context.Prev.Prev.Time),
              (float)(context.Time - context.Prev.Prev.Time));
       }
       else return context.Prev.Value;
   }
   
   Timeline<Vector2>.TypeEncode = UnityTimelineUtils.EncodeVector2;
   Timeline<Vector2>.TypeDecode = UnityTimelineUtils.DecodeVector2;
   
   Timeline<Vector2>.TypeInterpolate =  
       TimelineUtils.BuildLinearInterpolator<Vector2>((x, y) => x+y, (x, y) => x*y);
   
   Timeline<Vector2>.TypeExtrapolate =
       TimelineUtils.BuildLinearExtrapolator<Vector2>((x, y) => x+y, (x, y) => x*y);

Send Filters

By default, Janus sends a message every time a value is set in a Timeline. In anything but the smallest examples, this will quickly overload the network. To reduce the number of messages, a variety of filters can be used to control which messages are sent. The simplest filters are based on time and will only send messages at a fixed rate. Other filters look at the value in the timeline and only send a new value if it has changed by a fixed amount from the previous value, and some filters use a combination of both options.

First the syntax for adding a filter is described followed by a description of each filter. All the parameters for the send filters are passed as functions. The sendRate and getThreshold parameters are merely floating point values however they must be passed as a function for example ()=>60 is a function that has no inputs and always returns the value 60. The getDelta parameter requires a function that has two inputs and calculates the difference between them. For example for a float timeline, the function might be (x,y) => Math,Abs(x-y).

Syntax
   Timeline<float> a = new Timeline<float>(“a”);
   a.AddSendFilter(TimelineUtils.BuildRateFilter<float>(()=>60)); 
   
   Timeline<Vector3> b = = new Timeline<Vector3>(“b”);
   b.AddSendFilter(TimelineUtils.BuildDeltaFilter<Vector3>((x,y) => (x-y).magnitude), () => 3);
   
   a.AddSendFilter(TimelineUtils.BuildExtrapolatedDeltaFilter<float>((x,y) => Mathf.Abs(x-y), ()=>0.1f)); 
   
   a.AddSendFilter(TimelineUtils.BuildDeltaRateFilter <float>((x,y) => Mathf.Abs(x-y), ()=>0.1f, ()=>1.0f));
Available Filters

BuildRateFilter<T>(Func<float> sendRate)

sendRate = the number of message to be sent per second. The filter will actually send less than sendRate messages as it checks if 1/sendRate seconds have elapsed between the timestamp on the currecnt message and on the previous message.


BuildDeltaFilter<T> (Func<T, T, float> getDelta, Func<float> getThreshold)

Compares current value to the last value sent and only sends if a given threshold is exceeded.


BuildExtrapolatedDeltaFilter<T>(Func<T, T, float> getDelta, Func<float> getThreshold)

Using extrapolation, calculates the value that the remote client would be able to display based on the previous values that were sent. A new value is only sent if the remote client would be unable to calculate the current value within a given threshold.


BuildDeltaRateFilter<T>(Func<T, T, float> getDelta, Func<float> getThreshold, Func<float> getRate)

Same as the Extrapolated Delta Filter, but also sends additional values if nothing has been sent in the time specified by getRate. I.e, if the rate is 2.0 and no value has been sent in the past 500ms, the next value will be sent even if the threshold is not exceeded.

Combining and Removing Filters

Send filters can be combined by adding more than one filter to a Timeline for example if the RateFilter and the DeltaFilter are both added to a Timeline, a value will only be sent if both the time limit and the threshold are exceeded.

To remove all send filters, use the ClearSendFilters method.

   a.ClearSendFilters();


Extrapolation and Interpolation

The default data types such as int, float and double all support stepwise, linear and quadratic interpolation.

For float and double the defaults are linear interpolation and stepwise extrapolation. For all other data types the defaults are stepwise.

The defaults can be changed for either an entire timeline type or for a single timeline as follows:

   Timeline<float>.TypeExtrapolate = TimelineUtils.BuildQuadraticExtrapolator<float>((x, y) => x + y, (x, y) => x * y);
   
   a.TypeInterpolate = TimelineUtils.BuildLinearInterpolator<float>((x, y) => x + y, (x, y) => x * y);
      
   b.TypeInterpolate = SteppingInterpolate<float>();  // b is a float timeline

Events

Timelines can also be used to pass commands type messages. In this case instead of accessing values in the Timeline, you most likely want to perform some action when the message arrives.

To facilitate this, Janus provides 4 events:

  • EntryInserted, triggered when a value from a local or remote peer is inserted into the local Timeline
  • EntryMet, triggered if an entry is inserted into a Timeline in the future and then the time of that entry occurs.
  • EntryPassed, triggered if an entry is inserted into a Timeline in the future and then the time of that entry passes.
  • RemoteEntryInserted, triggered when a value from a remote peer is inserted into the local Timeline.

The following examples show how to listen for the EntryPassed and RemoteEntryInserted events.

   a.EntryPassed += OnEntryPassed;
   
   static void OnEntryPassed (Timeline<float> timeline, TimelineEntry<float> entry)
   {
       Console.WriteLine("pass " + timeline.StringID + " " 
                          + entry.Time + " " + entry.Value);
   }
   
   
   a.RemoteEntryInserted += OnEntryInserted;
   
   static void OnEntryInserted (Timeline<float> timeline, TimelineEntry<float> entry)
   {
       Console.WriteLine("inserted " + timeline.StringID + " " 
                          + entry.Time + " " + entry.Value);
   }

Other Useful Things to Know

Cached Values

When a timeline value is set, the timeline synchronizer stores a copy of the most recent values it has received. When a client creates a Timeline, the timeline synchronizer will send the cached values (if it has any) to the client. By default, the number of cached values is 3. This can be changed by setting the CacheSize property of a timeline.

   var a = TimelineManager.Default.Get<int>("health1");
   a.CacheSize = 2;

Cached values will also trigger events, you can override this by setting the IgnoreCachedEvents property to true.

   a. IgnoreCachedEvents = true;
DeliveryMode

There are three delivery modes available for timeline messages

  • ReliableOrdered
  • ReliableUnordered
  • Unreliable

The default is unreliable. If cubeCommand is a timeline, we can change the delivery mode as follow:

   cubeCommand.DeliveryMode = DeliveryMode.ReliableOrdered;
Adding a Value to the Local Timeline Only

Sometimes you may want to add a value to a timeline, but not have it propagated over the network. This can be done using the Insert method. For example to set the avatar health timeline locally to 100 at time 0.

   avatarHealth(0, 100);
Accessing Specific Values

Sometimes you may want to access a specific value in a timeline. A variety of properties help with this:

  • FirstValue - Gets the value of the absolute first entry in the timeline
  • LastValue - Gets the value of the absolute last entry in the timeline.
  • NextValue - Gets the value of the immediate entry after the current time.
  • PreviousValue - Gets the value of the immediate entry before the current time.


  • FirstEntry - Gets the absolute first entry in the timeline.
  • LastEntry - Gets the absolute last entry in the timeline.
  • LastSentEntry - Gets the last entry sent to the remote clients
  • LastLastSentEntry - Gets the second last entry sent to the remote clients
  • NextEntry - Gets the immediate entry after the current time.
  • PreviousEntry - Gets the immediate entry before the current time

Note: a value is the object stored in the timeline, an entry also includes the time associated with that entry and pointers to the next and the previous entries.

Accessing Specific Times

You can also get information about the times stored in the timeline:

  • FirstTime - Gets the time of the absolute first entry in the timeline.
  • LastTime - Gets the time of the absolute last entry in the timeline.
  • LastSendTime - Time of the last message sent for this timeline
  • NextTime - Gets the time of the immediate entry after the current time.
  • PreviousTime - Gets the time of the immediate entry before the current time.
  • Now - Gets the current time as dictated by the manager.
Status
  • IsConnected - Indicates whether or not the timeline is connected to the network.
  • IsEmpty- Gets if the timeline is empty (has no entries).
  • MaxEntries - Sets or gets the maximum allowed number of entries.
  • NumEntries - Gets the current number of entries in the timeline.
  • TimestampMode
Identification
  • ID - Gets the unique ID of the timeline.
  • NumericID- The unique ID of this timeline as a numeral.
  • StringID - The unique ID of this timeline as a string.