internal override void HandlePacket(NetBuffer buffer, int bytesReceived, IPEndPoint senderEndpoint) { double now = NetTime.Now; NetConnection sender = FindConnection(senderEndpoint); if (sender != null) { sender.m_lastHeardFromRemote = now; if (sender.m_encryption.SymmetricEncryptionKeyBytes != null) { bool ok = sender.m_encryption.DecryptSymmetric(buffer); if (!ok) { Log.Warning("Failed to decrypt packet from client " + sender); return; } } } try { NetMessage response; int messagesReceived = 0; int usrMessagesReceived = 0; int ackMessagesReceived = 0; while (buffer.ReadBitsLeft > 7) { NetMessage msg = NetMessage.Decode(buffer); if (msg == null) { break; // done } messagesReceived++; msg.Sender = sender; switch (msg.m_type) { case NetMessageType.Acknowledge: case NetMessageType.AcknowledgeBitField: if (sender == null) { Log.Warning("Received Ack from not-connected source!"); } else { //Log.Debug("Received ack " + msg.SequenceChannel + "|" + msg.SequenceNumber); sender.ReceiveAcknowledge(msg); ackMessagesReceived++; } break; case NetMessageType.Handshake: NetHandshakeType tp = (NetHandshakeType)msg.ReadByte(); if (tp == NetHandshakeType.ConnectResponse) { Log.Warning("Received ConnectResponse?!"); } else if (tp == NetHandshakeType.Connect) { if (sender == null) { NetHandshake.HandleConnect(msg, this, senderEndpoint); } else { // resend response NetHandshake.SendConnectResponse(this, sender, senderEndpoint); Log.Verbose("Redundant Connect received; resending response"); } } else if (tp == NetHandshakeType.ConnectionEstablished) { if (sender == null) { Log.Warning("Received ConnectionEstablished, but no sender connection?!"); } else { float rt = (float)(now - sender.m_firstSentHandshake); sender.m_ping.Initialize(rt); ushort remoteValue = msg.ReadUInt16(); sender.RemoteClockOffset = NetTime.CalculateOffset(now, remoteValue, rt); Log.Verbose("Reinitializing remote clock offset to " + sender.RemoteClockOffset + " ms (roundtrip " + NetUtil.SecToMil(rt) + " ms)"); if (sender.Status == NetConnectionStatus.Connected) { Log.Verbose("Redundant ConnectionEstablished received"); } else { Log.Debug("Connection established"); sender.SetStatus(NetConnectionStatus.Connected, "Connected by established"); } } } else { // disconnected if (sender == null) { Log.Warning("Disconnected received from unconnected source: " + senderEndpoint); return; } string reason = msg.ReadString(); sender.Disconnected(reason); } break; case NetMessageType.Discovery: Log.Debug("Answering discovery response from " + senderEndpoint); response = NetDiscovery.EncodeResponse(this); SendSingleMessageAtOnce(response, null, senderEndpoint); break; case NetMessageType.PingPong: if (sender == null) { return; } bool isPong = msg.ReadBoolean(); bool isOptimizeInfo = msg.ReadBoolean(); if (isOptimizeInfo) { // DON'T handle optimizeinfo... only clients should adapt to server ping info } else if (isPong) { if (sender.Status == NetConnectionStatus.Connected) { sender.m_ping.HandlePong(now, msg); } } else { NetPing.ReplyPong(msg, sender); // send pong } break; case NetMessageType.User: case NetMessageType.UserFragmented: usrMessagesReceived++; if (sender == null) { Log.Warning("User message received from unconnected source: " + senderEndpoint); return; // don't handle user messages from unconnected sources } if (sender.Status == NetConnectionStatus.Connecting) { sender.SetStatus(NetConnectionStatus.Connected, "Connected by user message"); } sender.ReceiveMessage(now, msg); break; default: Log.Warning("Bad message type: " + msg); break; } } if (sender != null) { NetStatistics stats = sender.Statistics; stats.PacketsReceived++; stats.MessagesReceived += messagesReceived; stats.UserMessagesReceived += usrMessagesReceived; stats.AckMessagesReceived += ackMessagesReceived; stats.BytesReceived += bytesReceived; } } catch (Exception ex) { Log.Error("Failed to parse packet correctly; read/write mismatch? " + ex); } }
internal void OptimizeSettings(NetOptimization optimize, float roundtrip, bool silent) { m_currentOptimization = optimize; if (!silent) { m_forConnection.Parent.Log.Verbose("Optimizing network settings for " + NetUtil.SecToMil(roundtrip) + " ms roundtrip time (" + optimize.ToString() + ")"); } switch (optimize) { // // std = example of 100 ms roundtrip time // case NetOptimization.EmphasizeResponse: ResendFirstUnacknowledgedDelay = 0.1f + roundtrip * 1.5f; // std: ResendSubsequentUnacknowledgedDelay = 0.1f + (roundtrip * 3.0f); // std: NumResendsBeforeFailing = (int)(15.0f / ResendSubsequentUnacknowledgedDelay); // std: ForceExplicitAckDelay = roundtrip * 0.5f; // std: PingFrequency = 4.0f; break; case NetOptimization.EmphasizeBandwidth: ResendFirstUnacknowledgedDelay = 0.2f + roundtrip * 5.0f; // std: ResendSubsequentUnacknowledgedDelay = 0.25f + (roundtrip * 12.0f); // std: NumResendsBeforeFailing = (int)(30.0f / ResendSubsequentUnacknowledgedDelay); // std: ForceExplicitAckDelay = roundtrip * 2.0f; // std: PingFrequency = 12.0f; break; case NetOptimization.Neutral: default: ResendFirstUnacknowledgedDelay = 0.1f + roundtrip * 2.0f; // std: ResendSubsequentUnacknowledgedDelay = 0.25f + (roundtrip * 5.0f); // std: NumResendsBeforeFailing = (int)(20.0f / ResendSubsequentUnacknowledgedDelay); // std: ForceExplicitAckDelay = roundtrip * 1.0f; // std: PingFrequency = 6.0f; break; } m_forConnection.NotifyOptimized(); }
internal override void HandlePacket(NetBuffer buffer, int bytesReceived, IPEndPoint sender) { double now = NetTime.Now; try { NetMessage msg; if (m_serverConnection == null || m_serverConnection.Status == NetConnectionStatus.Disconnected) { // // unconnected packet // msg = NetMessage.Decode(buffer); if (msg == null) { Log.Warning("Malformed NetMessage?"); return; // malformed? } Log.Verbose("Received unconnected message: " + msg); // discovery response? if (msg.m_type == NetMessageType.Discovery) { NetServerInfo info = NetDiscovery.DecodeResponse(msg, sender); if (ServerDiscovered != null) { ServerDiscovered(this, new NetServerDiscoveredEventArgs(info)); } return; } if (m_serverConnection != null && sender.Equals(m_serverConnection.RemoteEndpoint)) { // we have m_serverConnection, but status is disconnected Log.Warning("Received " + buffer.LengthBytes + " from server; but we're disconnected!"); return; } Log.Warning("Received " + buffer.LengthBytes + " bytes from non-server source: " + sender + "(server is " + m_serverConnection.RemoteEndpoint + ")"); return; } // decrypt in place if (m_serverConnection.m_encryption.SymmetricEncryptionKeyBytes != null) { byte[] savedBuffer = null; if (m_serverConnection.Status == NetConnectionStatus.Connecting) { // special case; save buffer in case we get unencrypted response savedBuffer = new byte[buffer.LengthBytes]; Array.Copy(buffer.Data, 0, savedBuffer, 0, buffer.LengthBytes); } bool ok = m_serverConnection.m_encryption.DecryptSymmetric(buffer); if (!ok) { // failed to decrypt; drop this packet UNLESS we're in a connecting state, // in which case the server may want to tell us something if (m_serverConnection.Status == NetConnectionStatus.Connecting) { // ok let this one thru unencrypted Array.Copy(savedBuffer, buffer.Data, savedBuffer.Length); } else { Log.Warning("Failed to decrypt packet from server!"); return; } } } m_serverConnection.m_lastHeardFromRemote = now; int messagesReceived = 0; int usrMessagesReceived = 0; int ackMessagesReceived = 0; while (buffer.ReadBitsLeft > 7) { msg = NetMessage.Decode(buffer); if (msg == null) { break; // done } messagesReceived++; msg.Sender = m_serverConnection; switch (msg.m_type) { case NetMessageType.Handshake: NetHandshakeType tp = (NetHandshakeType)msg.ReadByte(); if (tp == NetHandshakeType.Connect || tp == NetHandshakeType.ConnectionEstablished) { Log.Warning("Client received " + tp + "?!"); } else if (tp == NetHandshakeType.ConnectResponse) { if (m_serverConnection.Status != NetConnectionStatus.Connecting) { Log.Verbose("Received redundant ConnectResponse!"); break; } // Log.Debug("ConnectResponse received"); m_serverConnection.SetStatus(NetConnectionStatus.Connected, "Connected"); Log.Info("Connected to " + m_serverConnection.RemoteEndpoint); // initialize ping to now - m_firstSentConnect float initRoundtrip = (float)(now - m_serverConnection.m_firstSentHandshake); m_serverConnection.m_ping.Initialize(initRoundtrip); ushort remoteValue = msg.ReadUInt16(); m_serverConnection.RemoteClockOffset = NetTime.CalculateOffset(now, remoteValue, initRoundtrip); Log.Verbose("Initializing remote clock offset to " + m_serverConnection.RemoteClockOffset + " ms (roundtrip " + NetUtil.SecToMil(initRoundtrip) + " ms)"); NetMessage established = new NetMessage(NetMessageType.Handshake, 3); established.Write((byte)NetHandshakeType.ConnectionEstablished); established.WriteSendStamp(); SendSingleMessageAtOnce(established, m_serverConnection, m_serverConnection.RemoteEndpoint); } else { // Disconnected string reason = msg.ReadString(); m_serverConnection.SetStatus(NetConnectionStatus.Disconnected, reason); } break; case NetMessageType.Acknowledge: //Log.Debug("Received ack " + msg.SequenceChannel + "|" + msg.SequenceNumber); m_serverConnection.ReceiveAcknowledge(msg); ackMessagesReceived++; break; case NetMessageType.PingPong: bool isPong = msg.ReadBoolean(); bool isOptimizeInfo = msg.ReadBoolean(); if (isOptimizeInfo) { m_serverConnection.m_ping.HandleOptimizeInfo(now, msg); } else if (isPong) { if (m_serverConnection.Status == NetConnectionStatus.Connected) { m_serverConnection.m_ping.HandlePong(now, msg); } } else { NetPing.ReplyPong(msg, m_serverConnection); } break; case NetMessageType.User: case NetMessageType.UserFragmented: //Log.Debug("User message received; " + msg.m_buffer.LengthBytes + " bytes"); m_serverConnection.ReceiveMessage(now, msg); usrMessagesReceived++; break; case NetMessageType.Discovery: NetServerInfo info = NetDiscovery.DecodeResponse(msg, sender); if (ServerDiscovered != null) { ServerDiscovered(this, new NetServerDiscoveredEventArgs(info)); } break; default: Log.Warning("Client received " + msg.m_type + "?!"); break; } } // add statistics NetStatistics stats = m_serverConnection.Statistics; stats.PacketsReceived++; stats.MessagesReceived += messagesReceived; stats.UserMessagesReceived += usrMessagesReceived; stats.AckMessagesReceived += ackMessagesReceived; stats.BytesReceived += bytesReceived; } catch (Exception ex) { Log.Error("Failed to parse packet correctly; read/write mismatch? " + ex); } }
internal void HandlePong(double now, NetMessage pongMessage) { int nr = pongMessage.ReadByte(6); if (nr != m_pingNrInProgress) { m_connection.Parent.Log.Warning("Pong received for wrong ping; received " + nr + " expecting " + m_pingNrInProgress); return; } double roundtrip = now - m_lastSentPing; // spike, or can we use this roundtrip? double avgRT = pongMessage.Sender.AverageRoundtripTime; if (roundtrip > avgRT * 2.0) { // must be spike m_connection.Parent.Log.Verbose("Not adjusting clock due lag spike... (rt " + NetUtil.SecToMil(roundtrip) + " avg " + NetUtil.SecToMil(avgRT) + ")"); } else { ushort val = pongMessage.ReadUInt16(); int curOffset = m_connection.RemoteClockOffset; int foundOffset = NetTime.CalculateOffset(now, val, roundtrip); int res = NetTime.MergeOffsets(curOffset, foundOffset); int rtMillis = (int)(roundtrip * 1000.0); if (res != curOffset) { //m_connection.Parent.Log.Verbose("Ping: " + rtMillis + " Local: " + NetTime.Encoded(now) + " CurOffset: " + curOffset + " Remote: " + val + " NewOffset: " + res + " (adjusted " + (res - curOffset) + ")"); m_connection.Parent.Log.Verbose("Roundtrip: " + rtMillis + " ms; Ajusted remote offset " + (res - curOffset) + " ms"); m_connection.RemoteClockOffset = res; } } AddRoundtrip((float)roundtrip); if (m_connection.Parent is NetServer) { // server is authorative on settings optimizing roundtrip numbers m_connection.Configuration.OptimizeSettings(m_averageRoundtrip); SendOptimizeInfo(m_averageRoundtrip); } return; }