/// <summary>
        /// Sends a packet via Steamworks; Returns whether or not the packed could be sent
        /// </summary>
        public bool SendPacket(SteamId id, byte[] data, int len = -1, P2PChannel channel = P2PChannel.RELIABLE)
        {
            if (len > MP_PACKET_MAX || data.Length > MP_PACKET_MAX)
            {
                Output.LogWarning($"{Name}: Attempted to send a packet which exceeded MP_MAX_SIZE!");
                return(false);
            }

            if (channel < 0 || channel >= P2PChannel.NUM_CHANNELS)
            {
                Output.LogWarning($"{Name}: Invalid send channel specified, defaulting to Reliable");
                return(SteamNetworking.SendP2PPacket(id, data, len, (int)P2PChannel.RELIABLE, P2PSend.Reliable));
            }

            // Cast channel to send type
            P2PSend send = (P2PSend)channel;

            return(SteamNetworking.SendP2PPacket(id, data, len, (int)channel, send));
        }
        protected override void HandleConnectionFailed(SteamId id, P2PSessionError error)
        {
            try
            {
                if (!id.IsValid)
                {
                    throw new Exception("An invalid SteamId is reporting connection failure");
                }

                if (!PlayerRegistry.Contains(id))
                {
                    throw new Exception("Received connection failure message from a peer that isn't in the player registry");
                }

                int conn = PlayerRegistry.GetConnId(id);

                // If no connection id could be found for that SteamId
                if (conn == -1)
                {
                    throw new Exception($"Invalid connection id for SteamId {id}, can't disconnect");
                }

                string info = P2PErrorToString(error);

                Exception ex = new Exception($"Connection failed with host {id} ({info})");
                Output.LogError(ex.Message);

                PlayerRegistry.Remove(id);
                CloseSessionWithUser(id);

                // Raise OnError event
                OnError?.Invoke(conn, ex);
            }
            catch (Exception e)
            {
                Output.LogWarning($"{Name}: Exception in Server.HandleConnectionFailed ({e.Message})");
                return;
            }
        }
Esempio n. 3
0
        protected override void HandleConnectionFailed(SteamId id, P2PSessionError error)
        {
            try
            {
                if (!id.IsValid)
                {
                    throw new Exception("An invalid SteamId is reporting connection failure");
                }

                if (!HostId.IsValid)
                {
                    throw new Exception($"Connection failed with {id}, but we didn't have a host anyway...");
                }

                if (id != HostId)
                {
                    throw new Exception($"Connection failed with {id}, but they weren't our host");
                }

                string info = P2PErrorToString(error);

                Exception ex = new Exception($"Connection failed with host {id} ({info})");
                Output.LogError(ex.Message);

                _host = new SteamId();
                CloseSessionWithUser(id);
                ResetStatus();

                // Raise OnError event
                OnError?.Invoke(ex);
            }
            catch (Exception e)
            {
                Output.LogWarning($"{Name}: Exception in Client.HandleConnectionFailed ({e.Message})");
                return;
            }
        }
        /// <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>
        /// 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
            }
        }
Esempio n. 6
0
        public async void ConnectToId(SteamId hostId)
        {
            // Refuse to attempt another connection if one is active or in progress
            if (Active || Status == PeerStatus.CONNECTING)
            {
                Output.LogWarning($"{Name}: Attempted to connect while a previous connection is active or in progress");
                return;
            }

            // Set timeout start time, set client active, and set client status to Connecting
            DateTime timeoutStart = DateTime.Now;

            // Set status appropriately
            _active = true;
            _status = PeerStatus.CONNECTING;

            try
            {
                // Send a request to connect
                byte[] sendData = new byte[] { (byte)PacketType.CONNECT };
                SendPacket(hostId, sendData, sendData.Length);

                while (Status == PeerStatus.CONNECTING)
                {
                    // Wait for a packet to arrive
                    while (!IsPacketAvailable())
                    {
                        TimeSpan timeoutChk = DateTime.Now - timeoutStart;

                        if (timeoutChk.TotalMilliseconds >= MP_TIMEOUT)
                        {
                            throw new Exception("Timed out attempting to connect");
                        }

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

                    GetNextPacket(out P2Packet? packet);

                    if (packet == null)
                    {
                        throw new Exception("Null packet received");
                    }

                    // If the packet is not null, get data and sender SteamId
                    byte[]  data   = packet.Value.Data;
                    SteamId sender = packet.Value.SteamId;

                    if (data.Length < 1 || data == null)
                    {
                        throw new Exception("Null or zero-length data array received");
                    }

                    // Ignore packets sent by someone who isn't expected
                    if (sender != hostId)
                    {
                        continue;
                    }

                    if (data[0] != (byte)PacketType.CONNECTION_ACCEPTED)
                    {
                        throw new Exception($"Unexpected response ({data[0]})");
                    }

                    // Now connected to a new server, set status and refresh receive queue
                    _status = PeerStatus.CONNECTED;
                    _receiveQueue.Clear();

                    // Raise OnConnected event
                    OnConnected?.Invoke();
                }

                _host = hostId;
            }
            catch (Exception e)
            {
                Output.LogError($"{Name}: Error connecting to host ({e.Message})");

                // If we catch an exception, reset active and status
                ResetStatus();
            }
        }