/// <summary>
        /// Register all MmResponders that can receive messages.
        /// </summary>
        /// <param name="responder"></param>
        public void RegisterMmNetworkResponder(MmNetworkResponder responder)
        {
            MmRelayNode nodeToAdd;
            bool        presentInDictionary = MmRelayNodes.TryGetValue(responder.netId.Value, out nodeToAdd);

            if (!presentInDictionary)
            {
                MmRelayNodes.Add(responder.netId.Value, responder.MmRelayNode);
            }
        }
        /// <summary>
        /// Removes a particular relay node from the dictionary of supported nodes.
        /// </summary>
        public void RemoveMmNetworkResponder(MmNetworkResponder responder)
        {
            MmRelayNode nodeToRemove;
            bool        presentInDictionary = MmRelayNodes.TryGetValue(responder.netId.Value, out nodeToRemove);

            if (!presentInDictionary)
            {
                MmRelayNodes.Remove(responder.netId.Value);
            }
        }
        /// <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;
            //}
        }