示例#1
0
        protected override void HandleReceivedData(SteamId id, byte[] data)
        {
            try
            {
                if (!id.IsValid)
                {
                    throw new Exception("Received data from an invalid SteamId");
                }

                if (!HostId.IsValid)
                {
                    throw new Exception("Received data from a SteamId, but we don't have a host yet");
                }

                if (id != HostId)
                {
                    throw new Exception("Received data from a SteamId that isn't our current host");
                }

                if (data == null || data.Length == 0)
                {
                    throw new Exception("Data is null or zero-length");
                }

                // Raise OnDataReceived event
                OnDataReceived?.Invoke(data);
            }
            catch (Exception e)
            {
                Output.LogError($"{Name}: Exception in Client.HandleReceivedData ({e.Message})");
                return;
            }
        }
        ///--------------------------------------------------------------------
        /// MirrorpunchSteam methods
        ///--------------------------------------------------------------------

        private void OnEnable()
        {
            if (MaintainClient)
            {
                try
                {
                    if (AppId <= 0)
                    {
                        throw new Exception("Mirrorpunch: AppId is invalid");
                    }

                    SteamClient.Init(AppId);
                }
                catch (Exception e)
                {
                    string error = $"Mirrorpunch: Exception when initializing SteamClient ({e.Message}), disabling transport";

                    _enableTransport = false;
                    Output.LogError(error);
                }
            }

            if (EnableTransport)
            {
                InitCommon();
                InitServerClient();

                // Register events after server/client objects are initialized
                RegisterMirrorEvents();
            }
        }
        protected override void HandleReceivedData(SteamId id, byte[] data)
        {
            try
            {
                if (!id.IsValid)
                {
                    throw new Exception("Received data from an invalid SteamId");
                }

                if (data == null || data.Length == 0)
                {
                    throw new Exception("Data is null or zero-length");
                }

                int conn = PlayerRegistry.GetConnId(id);

                if (conn == -1)
                {
                    throw new Exception("Received data from a SteamId that isn't in the player registry");
                }

                // Raise OnDataReceived event
                OnDataReceived?.Invoke(conn, data);
            }
            catch (Exception e)
            {
                Output.LogError($"{Name}: Exception in Server.HandleReceivedData ({e.Message})");
                return;
            }
        }
        /// <summary>
        /// Attempts to send a packet to a connected client, if any; returns success
        /// </summary>
        public bool ServerSend(int conn, int channel, byte[] data)
        {
            try
            {
                if (!Active)
                {
                    throw new Exception("Server is not active, can't send packet");
                }

                if (channel < 0 || channel >= (int)P2PChannel.NUM_CHANNELS)
                {
                    throw new Exception("Invalid send channel specified");
                }

                SteamId id = PlayerRegistry.GetSteamId(conn);

                if (!id.IsValid)
                {
                    throw new Exception($"No valid SteamId found in player registry for connection id {conn}");
                }

                return(SendPacket(id, data, data.Length, (P2PChannel)channel));
            }
            catch (Exception e)
            {
                Output.LogError($"{Name}: Exception in ServerSend ({e.Message})");
                return(false);
            }
        }
        protected override void HandleSessionRequest(SteamId id)
        {
            try
            {
                // Ignore a session request from ourselves
                if (id == SteamClient.SteamId)
                {
                    throw new Exception("Received a session request from ourselves, somehow. There are better ways to handle loneliness, you just gotta reach out.");
                }

                // Ignore a session request if we already have this client in the registry
                if (PlayerRegistry.Contains(id))
                {
                    throw new Exception("Received a new session request from a peer that's already in the registry");
                }

                int conn = GetConnectionId();

                PlayerRegistry.Add(id, conn);
                AcceptSessionWithUser(id);

                // Raise OnConnected event
                OnConnected?.Invoke(conn);
            }
            catch (Exception e)
            {
                Output.LogError($"{Name}: Exception in Server.HandleSessionRequest ({e.Message})");
                return;
            }
        }
示例#6
0
        protected override void HandleSessionRequest(SteamId id)
        {
            try
            {
                // Ignore a session request from ourselves
                if (id == SteamClient.SteamId)
                {
                    throw new Exception("Received a session request from ourselves, somehow. There are better ways to handle loneliness, you just gotta reach out.");
                }

                // Ignore a session request if we already have a host
                if (HostId.IsValid)
                {
                    throw new Exception("Received a new session request when we already have a host");
                }

                _host = id;
                AcceptSessionWithUser(id);
            }
            catch (Exception e)
            {
                Output.LogError($"{Name}: Exception in Client.HandleSessionRequest ({e.Message})");
                return;
            }
        }
示例#7
0
        public void ClientDisconnect()
        {
            try
            {
                if (Status != PeerStatus.CONNECTED || !HostId.IsValid)
                {
                    throw new Exception("Attempted to disconnect while not connected or HostId is invalid");
                }

                if (!SteamNetworking.CloseP2PSessionWithUser(HostId))
                {
                    throw new Exception($"Unable to disconnect from {HostId}");
                }
            }
            catch (Exception e)
            {
                Output.LogError($"{Name}: Exception in ClientDisconnect ({e.Message})");
            }
            finally
            {
                // Reset status and host id
                _status = PeerStatus.OFFLINE;
                _host   = new SteamId();

                // Raise OnDisconnected event
                OnDisconnected?.Invoke();
            }
        }
 private void Start()
 {
     if (!SteamClient.IsValid)
     {
         _enableTransport = false;
         Output.LogError("Mirrorpunch: SteamClient is invalid in Start, disabling transport");
         return;
     }
 }
        /// <summary>
        /// Attempts to remove a player entry from the registry by connection id
        /// </summary>
        public bool Remove(int conn)
        {
            if (!_connDict.TryGetValue(conn, out SteamId steam))
            {
                Output.LogError($"Error fetching connection id from SteamId dictionary");
                return(false);
            }

            return(Remove(steam, conn));
        }
        public override void ClientConnect(string id)
        {
            if (!ulong.TryParse(id, out ulong hostId))
            {
                Output.LogError($"Mirrorpunch: Could not parse string ({id}) into ulong, can't connect");
                return;
            }

            Client.ConnectToId(hostId);
        }
        ///--------------------------------------------------------------------
        /// MirrorpunchCommon methods
        ///--------------------------------------------------------------------

        /// <summary>
        /// Initialize unique functionality and variables
        /// </summary>
        public virtual void Init()
        {
            Output.Log($"Initializing {Name}");

            if (MpSteam == null)
            {
                Output.LogError($"{Name}: Reference to MirrorpunchSteam is null in Init, can't proceed");
                ResetStatus();
                return;
            }

            RegisterCallbacks();
        }
        public string ServerGetClientAddress(int conn)
        {
            try
            {
                SteamId id = PlayerRegistry.GetSteamId(conn);

                if (!id.IsValid)
                {
                    throw new Exception($"No valid SteamId found in player registry for connection id {conn}");
                }

                return(id.ToString());
            }
            catch (Exception e)
            {
                Output.LogError($"{Name}: Exception in ServerGetClientAddress ({e.Message})");
                return(null);
            }
        }
        protected override void HandleDisconnectRequest(SteamId id)
        {
            try
            {
                // Ignore a disconnect request from an invalid SteamId
                if (!id.IsValid)
                {
                    throw new Exception("Received a disconnect request from an invalid SteamId");
                }

                // Ignore a disconnect request from ourselves
                if (id == SteamClient.SteamId)
                {
                    throw new Exception("Received a disconnect request from ourselves, somehow. That's rough, buddy.");
                }

                // Ignore a disconnect request if we already have this client in the registry
                if (!PlayerRegistry.Contains(id))
                {
                    throw new Exception("Received a disconnect request from a peer that's not in the 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");
                }

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

                // Raise OnDisconnected event
                OnDisconnected?.Invoke(conn);
            }
            catch (Exception e)
            {
                Output.LogError($"{Name}: Exception in Server.HandleDisconnectRequest ({e.Message})");
                return;
            }
        }
示例#14
0
        protected override void HandleDisconnectRequest(SteamId id)
        {
            try
            {
                // Ignore a disconnect request from an invalid SteamId
                if (!id.IsValid)
                {
                    throw new Exception("Received a disconnect request from an invalid SteamId");
                }

                // Ignore a disconnect request from ourselves
                if (id == SteamClient.SteamId)
                {
                    throw new Exception("Received a disconnect request from ourselves, somehow. That's rough, buddy.");
                }

                // Ignore a disconnect request if we don't have a host
                if (!HostId.IsValid)
                {
                    throw new Exception("Received a disconnect request but we don't have a host");
                }

                // Ignore a disconnect request from someone who isn't our host
                if (id != HostId)
                {
                    throw new Exception("Received a disconnect request from a peer that isn't our current host");
                }

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

                // Raise OnDisconnected event
                OnDisconnected?.Invoke();
            }
            catch (Exception e)
            {
                Output.LogError($"{Name}: Exception in Client.HandleDisconnectRequest ({e.Message})");
                return;
            }
        }
        public bool ServerDisconnect(int conn)
        {
            try
            {
                SteamId id = PlayerRegistry.GetSteamId(conn);

                if (!id.IsValid)
                {
                    throw new Exception($"No valid SteamId found in player registry for connection id {conn}");
                }

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

                return(true);
            }
            catch (Exception e)
            {
                Output.LogError($"{Name}: Exception in ServerDisconnect ({e.Message})");
                return(false);
            }
        }
        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;
            }
        }
示例#17
0
        /// <summary>
        /// Attempts to send a packet to a connected host, if any; returns success
        /// </summary>
        public bool ClientSend(int channel, byte[] data)
        {
            try
            {
                if (Status != PeerStatus.CONNECTED || !HostId.IsValid)
                {
                    throw new Exception("Not connected to a host, or HostId is invalid; can't send");
                }

                if (channel < 0 || channel >= (int)P2PChannel.NUM_CHANNELS)
                {
                    throw new Exception("Invalid send channel specified");
                }

                return(SendPacket(HostId, data, data.Length, (P2PChannel)channel));
            }
            catch (Exception e)
            {
                Output.LogError($"{Name}: Exception in ClientSend ({e.Message})");
                return(false);
            }
        }
示例#18
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;
            }
        }
示例#19
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();
            }
        }
        /// <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
            }
        }