/// <summary> /// Deserialize the MmMetadataBlock /// </summary> /// <param name="reader">UNET based deserializer object</param> public virtual void Deserialize(NetworkReader reader) { LevelFilter = (MmLevelFilter)reader.ReadInt16(); ActiveFilter = (MmActiveFilter)reader.ReadInt16(); SelectedFilter = (MmSelectedFilter)reader.ReadInt16(); NetworkFilter = (MmNetworkFilter)reader.ReadInt16(); Tag = (MmTag)reader.ReadInt16(); }
/// <summary> /// Copy Constructor for MmMetadataBlock /// </summary> /// <param name="original">MmMetadataBlock to be copied.</param> public MmMetadataBlock(MmMetadataBlock original) { LevelFilter = original.LevelFilter; ActiveFilter = original.ActiveFilter; SelectedFilter = original.SelectedFilter; NetworkFilter = original.NetworkFilter; Tag = original.Tag; }
/// <summary> /// Deserialize the MmMetadataBlock /// </summary> /// <param name="data">Object array representation of a MmMetadataBlock</param> /// <param name="index">The index of the next element to be read from data</param> /// <returns>The index of the next element to be read from data</returns> public virtual int Deserialize(object[] data, int index) { LevelFilter = (MercuryMessaging.MmLevelFilter)((short)data[index++]); ActiveFilter = (MercuryMessaging.MmActiveFilter)((short)data[index++]); SelectedFilter = (MercuryMessaging.MmSelectedFilter)((short)data[index++]); NetworkFilter = (MercuryMessaging.MmNetworkFilter)((short)data[index++]); Tag = (MercuryMessaging.MmTag)((short)data[index++]); return(index); }
/// <summary> /// Create an MmMetadataBlock /// </summary> /// <param name="tag"></param> /// <param name="levelFilter"><see cref="MmLevelFilter"/></param> /// <param name="activeFilter"><see cref="MmActiveFilter"/></param> /// <param name="selectedFilter"><see cref="MmSelectedFilter"/></param> /// <param name="networkFilter"><see cref="MmNetworkFilter"/></param> public MmMetadataBlock(MmTag tag, MmLevelFilter levelFilter = MmLevelFilterHelper.Default, MmActiveFilter activeFilter = default(MmActiveFilter), MmSelectedFilter selectedFilter = default(MmSelectedFilter), MmNetworkFilter networkFilter = default(MmNetworkFilter)) { LevelFilter = levelFilter; ActiveFilter = activeFilter; SelectedFilter = selectedFilter; NetworkFilter = networkFilter; Tag = tag; }
/// <summary> /// Create an MmMetadataBlock /// </summary> /// <param name="levelFilter"><see cref="MmLevelFilter"/></param> /// <param name="activeFilter"><see cref="MmActiveFilter"/></param> /// <param name="selectedFilter"><see cref="MmSelectedFilter"/></param> /// <param name="networkFilter"><see cref="MmNetworkFilter"/></param> public MmMetadataBlock( MmLevelFilter levelFilter = MmLevelFilterHelper.Default, MmActiveFilter activeFilter = MmActiveFilter.Active, MmSelectedFilter selectedFilter = MmSelectedFilter.All, MmNetworkFilter networkFilter = MmNetworkFilter.All) { LevelFilter = levelFilter; ActiveFilter = activeFilter; SelectedFilter = selectedFilter; NetworkFilter = networkFilter; Tag = MmTagHelper.Everything; }
/// <summary> /// This method determines if a particular MmResponder should /// receive a message via MmInvoke. /// This performs 4 checks: Tag Check, Level Check, Active Check, & Selected Check. /// </summary> /// <param name="levelFilter">Extracted message level filter - before adjust.</param> /// <param name="activeFilter">Extracted message active filter - before adjust.</param> /// <param name="selectedFilter">Extracted message selected filter - before adjust.</param> /// <param name="networkFilter">Extracted message network filter - before adjust.</param> /// <param name="mmRoutingTableItem">RoutingTableItem of currently observed MmResponder</param> /// <param name="message">MmMessage to be checked.</param> /// <returns>Returns whether responder has passed all checks.</returns> protected virtual bool ResponderCheck(MmLevelFilter levelFilter, MmActiveFilter activeFilter, MmSelectedFilter selectedFilter, MmNetworkFilter networkFilter, MmRoutingTableItem mmRoutingTableItem, MmMessage message) { if (!TagCheck(mmRoutingTableItem, message)) { return(false); // Failed TagCheck } return(LevelCheck(levelFilter, mmRoutingTableItem.Responder, mmRoutingTableItem.Level) && ActiveCheck(activeFilter, mmRoutingTableItem.Responder) && SelectedCheck(selectedFilter, mmRoutingTableItem.Responder) && NetworkCheck(mmRoutingTableItem, message)); }
/// <summary> /// Create an MmMessage, with filters defined directly /// </summary> /// <param name="mmMethod">Identifier of target MmMethod</param> /// <param name="levelFilter">Determines direction of messages</param> /// <param name="activeFilter">Determines whether message sent to active and/or inactive objects</param> /// <param name="selectedFilter">Determines whether message sent to objects "selected" as defined by MmRelayNode implementation</param> /// <param name="networkFilter">Determines whether message will remain local or can be sent over the network</param> public MmMessage(MmMethod mmMethod, MmLevelFilter levelFilter, MmActiveFilter activeFilter, MmSelectedFilter selectedFilter, MmNetworkFilter networkFilter) { MmMethod = mmMethod; MetadataBlock = new MmMetadataBlock(); MetadataBlock.LevelFilter = levelFilter; MetadataBlock.ActiveFilter = activeFilter; MetadataBlock.SelectedFilter = selectedFilter; MetadataBlock.NetworkFilter = networkFilter; }
/// <summary> /// Override the basic functionality of MmRelayNode to allow for faster processing by skipping checks. /// </summary> /// <param name="msgType">Type of message. This specifies /// the type of the payload. This is important in /// networked scenarios, when proper deseriaization into /// the correct type requires knowing what was /// used to serialize the object originally.</param> /// <param name="message">The message to send. /// This class builds on UNET's MessageBase so it is /// Auto [de]serialized by UNET.</param> public override void MmInvoke(MmMessage message) { if (AllowStandardMmInvoke) { base.MmInvoke(message); } else { //If the MmRelayNode has not been initialized, initialize it here, // and refresh the parents - to ensure proper routing can occur. InitializeNode(); MmNetworkFilter networkFilter = message.MetadataBlock.NetworkFilter; if (MmNetworkResponder != null && message.MetadataBlock.NetworkFilter != MmNetworkFilter.Local && !message.IsDeserialized) { MmNetworkFilter originalNetworkFilter = NetworkFilterAdjust(ref message); MmNetworkResponder.MmInvoke(message); message.MetadataBlock.NetworkFilter = originalNetworkFilter; } if (!AllowNetworkPropagationLocally && !message.IsDeserialized && message.MetadataBlock.NetworkFilter == MmNetworkFilter.Network) { return; } foreach (var routingTableItem in RoutingTable) { var responder = routingTableItem.Responder; //bool isLocalResponder = responder.MmGameObject == this.gameObject; MmLevelFilter responderLevel = routingTableItem.Level; responder.MmInvoke(message); } } }
/// <summary> /// Checks the provided MmRoutingTableItem to see /// whether it passes the list filter requirements. /// </summary> /// <param name="item">Observed MmRoutingTableItem.</param> /// <param name="listFilter">ListFilter <see cref="ListFilter"/></param> /// <param name="levelFilter">LevelFilter <see cref="MmLevelFilter"/></param> /// <returns>Whether MmRoutingTableItem passes filter check.</returns> public bool CheckFilter(MmRoutingTableItem item, ListFilter listFilter, MmLevelFilter levelFilter) { //Level Check if ((levelFilter & item.Level) == 0) { return(false); } //List Filter check if (listFilter == ListFilter.RelayNodeOnly && !(item.Responder is MmRelayNode)) { return(false); } if (listFilter == ListFilter.ResponderOnly && item.Responder is MmRelayNode) { return(false); } //All conditions passed, return true return(true); }
/// <summary> /// Get a list of the names all MmRoutingTableItems that /// match the provided filters. /// </summary> /// <param name="filter">ListFilter <see cref="ListFilter"/></param> /// <param name="levelFilter">LevelFilter <see cref="MmLevelFilter"/></param> /// <returns>List of names of MmRoutingTableItems that pass filter checks.</returns> public List <string> GetMmNames(ListFilter filter = default(ListFilter), MmLevelFilter levelFilter = MmLevelFilterHelper.Default) { return(GetMmRoutingTableItems(filter, levelFilter). Select(x => x.Name).ToList()); }
/// <summary> /// Get a list of all MmRoutingTableItems that /// match the provided filters. /// </summary> /// <param name="filter">ListFilter <see cref="ListFilter"/></param> /// <param name="levelFilter">LevelFilter <see cref="MmLevelFilter"/></param> /// <returns>List of MmRoutingTableItems that pass filter checks.</returns> public List <MmRoutingTableItem> GetMmRoutingTableItems( ListFilter filter = default(ListFilter), MmLevelFilter levelFilter = MmLevelFilterHelper.Default) { return(this.Where(x => CheckFilter(x, filter, levelFilter)).ToList()); }
/// <summary> /// Draw method for MmBehaviorListItemDrawer. /// </summary> /// <param name="position"></param> /// <param name="property"></param> /// <param name="label"></param> public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { int oldIndentLevel = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; label = EditorGUI.BeginProperty(position, label, property); Rect contentPosition = EditorGUI.PrefixLabel(position, label); /* * if (position.height > 16f) { * position.height = 16f; * EditorGUI.indentLevel += 1; * contentPosition = EditorGUI.IndentedRect(position); * contentPosition.y += 18f; * } */ var originalWidth = contentPosition.width; var remainingWidth = originalWidth; /*---------------------------*/ contentPosition.width = (originalWidth - (FieldWidthClone + FieldWidthLevel + FieldWidthTags)) / 2f; SerializedProperty behaviorProperty = property.FindPropertyRelative("Responder"); var level = property.FindPropertyRelative("Level"); var listItemName = property.FindPropertyRelative("Name"); EditorGUI.BeginChangeCheck(); EditorGUI.PropertyField( contentPosition, behaviorProperty, GUIContent.none); if (EditorGUI.EndChangeCheck()) { MmResponder responderObj = behaviorProperty.objectReferenceValue as System.Object as MmResponder; if (responderObj != null) { MonoBehaviour containerObj = property.serializedObject.targetObject as MonoBehaviour; //Get Name & Tag from the assigned object and apply it as the name of the object. listItemName.stringValue = responderObj.gameObject.name; //Get the reference of the list owner's WFNode for comparison. //Then, assign the level based on whether this is the same object, or a different one. MmLevelFilter levelFilterValue = MmLevelFilter.Self; if (responderObj.gameObject == containerObj.gameObject) { levelFilterValue = MmLevelFilter.Self; } else { levelFilterValue = MmLevelFilter.Child; } level.enumValueIndex = Array.IndexOf(Enum.GetValues(typeof(MmLevelFilter)), levelFilterValue); } } remainingWidth -= contentPosition.width; contentPosition.x += contentPosition.width; /*---------------------------*/ contentPosition.width = (originalWidth - (FieldWidthClone + FieldWidthLevel + FieldWidthTags)) / 2f; // (Same as above) EditorGUI.PropertyField( contentPosition, listItemName, GUIContent.none); remainingWidth -= contentPosition.width; contentPosition.x += contentPosition.width; /*---------------------------*/ contentPosition.width = FieldWidthLevel; //Level EditorGUI.LabelField( contentPosition, new GUIContent(level.enumValueIndex != -1 ? level.enumNames[level.enumValueIndex] : "None"), EditorStyles.label); remainingWidth -= contentPosition.width; contentPosition.x += contentPosition.width; /*---------------------------*/ contentPosition.width = FieldWidthClone; EditorGUI.PropertyField( contentPosition, property.FindPropertyRelative("Clone"), GUIContent.none); remainingWidth -= contentPosition.width; contentPosition.x += contentPosition.width; /*---------------------------*/ contentPosition.width = FieldWidthTags; EditorGUI.PropertyField( contentPosition, property.FindPropertyRelative("Tags"), GUIContent.none); remainingWidth -= contentPosition.width; contentPosition.x += contentPosition.width; EditorGUI.EndProperty(); EditorGUI.indentLevel = oldIndentLevel; }
/// <summary> /// Determine if MmResponder passes MmRelayNode level filter check using value embedded in MmMessage. /// </summary> /// <param name="levelFilter">Level filter value extracted from MmMessage.</param> /// <param name="responder">Observed MmResponder.</param> /// <param name="responderLevel">Observed MmResponder Level.</param> /// <returns>Returns whether observed MmResponder has passed level filter check.</returns> protected virtual bool LevelCheck(MmLevelFilter levelFilter, IMmResponder responder, MmLevelFilter responderLevel) { return((levelFilter & responderLevel) > 0); }
/// <summary> /// If the level filter is designated 'Child', then it is recorded locally, /// but converted to a 'Child+Self' for use by the RoutingTable /// (which need to pass the message on to all children, but still need to be able /// to execute the message on their own responders, otherwise, it just goes /// to the terminal points of the graph without ever executing). /// </summary> /// <param name="message">MmMessage to be adjusted.</param> /// <param name="direction">Intended direction of message</param> /// <returns>Base implementation returns messages's level filter.</returns> protected virtual MmLevelFilter LevelFilterAdjust(ref MmMessage message, MmLevelFilter direction) { return(message.MetadataBlock.LevelFilter); }
/// <summary> /// Invoke an MmMethod. /// </summary> /// <param name="msgType">Type of message. This specifies /// the type of the payload. This is important in /// networked scenarios, when proper deseriaization into /// the correct type requires knowing what was /// used to serialize the object originally. /// </param> /// <param name="message">The message to send. /// This class builds on UNET's MessageBase so it is /// Auto [de]serialized by UNET.</param> public override void MmInvoke(MmMessageType msgType, MmMessage message) { //If the MmRelayNode has not been initialized, initialize it here, // and refresh the parents - to ensure proper routing can occur. InitializeNode(); //TODO: Switch to using mutex for threaded applications doNotModifyRoutingTable = true; MmNetworkFilter networkFilter = message.MetadataBlock.NetworkFilter; //Experimental: Allow forced serial execution (ordered) of messages. //if (serialExecution) //{ // if (!_executing) // { // _executing = true; // } // else // { // MmLogger.LogFramework("<<<<<>>>>>Queueing<<<<<>>>>>"); // KeyValuePair<MmMessageType, MmMessage> newMessage = // new KeyValuePair<MmMessageType, MmMessage>(msgType, message); // SerialExecutionQueue.Enqueue(newMessage); // return; // } //} //MmLogger.LogFramework (gameObject.name + ": MmRelayNode received MmMethod call: " + param.MmMethod.ToString ()); // If an MmNetworkResponder is attached to this object, and the MmMessage has not already been deserialized // then call the MmNetworkResponder's network message invocation function. if (MmNetworkResponder != null && message.MetadataBlock.NetworkFilter != MmNetworkFilter.Local && !message.IsDeserialized) { //if (!dirty) //{ // dirty = true; // message.TimeStamp = DateTime.UtcNow.ToShortTimeString(); // _prevMessageTime = message.TimeStamp; //} //This will ensure that beyond the point at which a message is determined to be sendable, // it will not be treated as networ networkFilter = NetworkFilterAdjust(ref message); MmNetworkResponder.MmInvoke(msgType, message); } //Todo: it's possible to get this to happen only once per graph. Switch Invoke code to support. var upwardMessage = message.Copy(); upwardMessage.MetadataBlock.LevelFilter = MmLevelFilterHelper.SelfAndParents; var downwardMessage = message.Copy(); downwardMessage.MetadataBlock.LevelFilter = MmLevelFilterHelper.SelfAndChildren; MmLevelFilter levelFilter = message.MetadataBlock.LevelFilter; MmActiveFilter activeFilter = ActiveFilterAdjust(ref message); MmSelectedFilter selectedFilter = SelectedFilterAdjust(ref message); //If this message was a network-only message and // this node does not allow for propagation of network messages, // then return. if (!AllowNetworkPropagationLocally && !message.IsDeserialized && message.MetadataBlock.NetworkFilter == MmNetworkFilter.Network) { return; } foreach (var routingTableItem in RoutingTable) { var responder = routingTableItem.Responder; //bool isLocalResponder = responder.MmGameObject == this.gameObject; MmLevelFilter responderLevel = routingTableItem.Level; //Check individual responder level and then call the right param. MmMessage responderSpecificMessage; if ((responderLevel & MmLevelFilter.Parent) > 0) { responderSpecificMessage = upwardMessage; } else if ((responderLevel & MmLevelFilter.Child) > 0) { responderSpecificMessage = downwardMessage; } else { responderSpecificMessage = message; } //MmLogger.LogFramework (gameObject.name + "observing " + responder.MmGameObject.name); if (ResponderCheck(levelFilter, activeFilter, selectedFilter, networkFilter, routingTableItem, responderSpecificMessage)) { responder.MmInvoke(msgType, responderSpecificMessage); } } //if (dirty && _prevMessageTime == message.TimeStamp) //{ // dirty = false; //} doNotModifyRoutingTable = false; while (MmRespondersToAdd.Any()) { var routingTableItem = MmRespondersToAdd.Dequeue(); MmAddToRoutingTable(routingTableItem.Responder, routingTableItem.Level); if (ResponderCheck(levelFilter, activeFilter, selectedFilter, networkFilter, routingTableItem, message)) { routingTableItem.Responder.MmInvoke(msgType, message); } } //if (serialExecution) //{ // if (SerialExecutionQueue.Count != 0) // { // MmLogger.LogFramework("%%%%%%%%%%%Dequeueing%%%%%%%%%"); // KeyValuePair<MmMessageType, MmMessage> DequeuedMessage = SerialExecutionQueue.Dequeue(); // MmInvoke(DequeuedMessage.Key, DequeuedMessage.Value); // } // _executing = false; //} }
/// <summary> /// Add an MmResponder to the MmRoutingTable, with level designation. /// </summary> /// <param name="mmResponder">MmResponder to be added.</param> /// <param name="level">Level designation of responder.</param> /// <returns>Reference to new MmRoutingTable item.</returns> public virtual MmRoutingTableItem MmAddToRoutingTable(MmResponder mmResponder, MmLevelFilter level) { var routingTableItem = new MmRoutingTableItem(mmResponder.name, mmResponder) { Level = level }; if (RoutingTable.Contains(mmResponder)) { return(null); // Already in list } //If there is an MmInvoke executing, add it to the // MmRespondersToAdd queue. if (doNotModifyRoutingTable) { MmRespondersToAdd.Enqueue(routingTableItem); } else { RoutingTable.Add(routingTableItem); } return(routingTableItem); }