/// <summary>
        /// Static loop method for receiving Steam P2P packets and enqueuing them in the receive queue
        /// </summary>
        public static async Task ReceiveLoop(MirrorpunchCommon a)
        {
            if (!a.Active)
            {
                return;   // Don't bother with receive loop if inactive
            }
            bool     warned     = false;
            DateTime timeWarned = DateTime.Now;

            try
            {
                while (a.Active)
                {
                    for (int i = 0; i < (int)P2PChannel.NUM_CHANNELS; i++)
                    {
                        if (!a.GetNextPacket(out P2Packet? packet, (P2PChannel)i))
                        {
                            continue;
                        }

                        if (packet == null)
                        {
                            continue;
                        }

                        int packetLen = packet.Value.Data.Length;

                        if (packetLen >= MP_PACKET_MAX)
                        {
                            Output.LogWarning($"{a.Name}: Received a packet that is too large ({packetLen})");
                            continue;
                        }

                        a.EnqueuePacket((P2Packet)packet);
                    }

                    if (!warned && a.ReceiveQueue.Length > MP_QUEUE_SIZE_WARNING)
                    {
                        Output.LogWarning($"{a.Name}: ReceiveQueue is backing up ({a.ReceiveQueue.Length} packets queued)");
                        timeWarned = DateTime.Now;
                    }

                    if (warned)
                    {
                        TimeSpan timeSinceWarning = DateTime.Now - timeWarned;
                        if (timeSinceWarning.TotalSeconds > MP_QUEUE_WARNING_TIMEOUT)
                        {
                            warned = false;
                        }
                    }

                    await Task.Delay(TimeSpan.FromMilliseconds(TickRate));
                }

                // Return if behavior is no longer active
                return;
            }
            catch (Exception e)
            {
                Output.LogError($"{a.Name}: Exception raised in ReceiveLoop ({e.Message})");
                return;   // Return on any exception
            }
        }
        /// <summary>
        /// Static method to process any messages in the receive queue
        /// </summary>
        public static bool ProcessMessages(MirrorpunchCommon a)
        {
            if (!a.Active)
            {
                return(false);   // Don't process messages if inactive
            }
            if (!a.ReceiveQueue.HasNodes)
            {
                return(true);    // Only return false if something goes wrong
            }
            DateTime timer = DateTime.Now;

            try
            {
                // Want to avoid a potential infinite loop, so only looping through a known number of nodes
                for (int i = 0; i < a.ReceiveQueue.Length; i++)
                {
                    P2Packet?packet = a.ReceiveQueue.Dequeue();

                    if (packet == null)
                    {
                        continue;
                    }

                    SteamId senderId = packet.Value.SteamId;

                    if (packet.Value.Data == null || packet.Value.Data.Length == 0)
                    {
                        continue;
                    }

                    if (!a.IsKnown(senderId))
                    {
                        byte byteType = packet.Value.Data[0];

                        if (byteType < 0 || byteType >= (byte)PacketType.NUM_TYPES)
                        {
                            throw new Exception("Packet with invalid PacketType received from unexpected SteamId; check sending code?");
                        }

                        PacketType packetType = (PacketType)byteType;

                        switch (packetType)
                        {
                        case PacketType.CONNECT:
                            Output.Log($"Connection requested by {senderId}");
                            a.OnSessionRequest(senderId);
                            break;

                        case PacketType.CONNECTION_ACCEPTED:
                            Output.Log($"Connection accepted by {senderId}");
                            break;

                        default:
                            throw new Exception($"Unexpected packet type received from {senderId} [{byteType}]");
                        }

                        // Packet from unknown sender handled without exception, continue
                        continue;
                    }

                    // Received a packet from a known sender, handle received data
                    byte[] data = packet.Value.Data;
                    a.OnReceivedData(senderId, data);
                }

                TimeSpan checkTimer = DateTime.Now - timer;

                if (checkTimer.TotalMilliseconds >= MP_PROCESS_WARNING)
                {
                    Output.LogWarning($"{a.Name}: ProcessMessages took longer than expected ({checkTimer.TotalMilliseconds} ms)");
                }

                // We made it here without an exception, report success
                return(true);
            }
            catch (Exception e)
            {
                Output.LogError($"{a.Name}: Exception in ProcessMessages ({e.Message})");
                return(false);
            }
        }
 /// <summary>
 /// Initialize common values
 /// </summary>
 private void InitCommon()
 {
     SteamNetworking.AllowP2PPacketRelay(_allowRelay);
     MirrorpunchCommon.SetTickRate(_tickRate);
 }