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; } }
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; } }
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; } }
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; } }
/// <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); } }
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; } }
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 } }