// note: original HLAPI HandleBytes function handled >1 message in a while loop, but this wasn't necessary // anymore because NetworkServer/NetworkClient Update both use while loops to handle >1 data events per // frame already. // -> in other words, we always receive 1 message per Receive call, never two. // -> can be tested easily with a 1000ms send delay and then logging amount received in while loops here // and in NetworkServer/Client Update. HandleBytes already takes exactly one. /// <summary> /// This function allows custom network connection classes to process data from the network before it is passed to the application. /// </summary> /// <param name="buffer">The data received.</param> internal void TransportReceive(ArraySegment <byte> buffer, int channelId) { // unpack message using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(buffer)) { try { int msgType = MessagePacker.UnpackId(networkReader); if (msgType == MessagePacker.GetId <NotifyPacket>()) { // this is a notify message, send to the notify receive NotifyPacket notifyPacket = networkReader.ReadNotifyPacket(); ReceiveNotify(notifyPacket, networkReader, channelId); } else { // try to invoke the handler for that message InvokeHandler(msgType, networkReader, channelId); } } catch (InvalidDataException ex) { logger.Log(ex.ToString()); } catch (Exception ex) { logger.LogError("Closed connection: " + this + ". Invalid message " + ex); Connection?.Disconnect(); } } }
/// <summary> /// Sends a message, but notify when it is delivered or lost /// </summary> /// <typeparam name="T">type of message to send</typeparam> /// <param name="message">message to send</param> /// <param name="token">a arbitrary object that the sender will receive with their notification</param> public void SendNotify <T>(T message, object token, int channelId = Channel.Unreliable) { if (sendWindow.Count == WINDOW_SIZE) { NotifyLost?.Invoke(this, token); return; } using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { var notifyPacket = new NotifyPacket { Sequence = (ushort)sequencer.Next(), ReceiveSequence = receiveSequence, AckMask = receiveMask }; sendWindow.Enqueue(new PacketEnvelope { Sequence = notifyPacket.Sequence, Token = token }); MessagePacker.Pack(notifyPacket, writer); MessagePacker.Pack(message, writer); NetworkDiagnostics.OnSend(message, channelId, writer.Length, 1); Send(writer.ToArraySegment(), channelId); lastNotifySentTime = Time.unscaledTime; } }
internal void ReceiveNotify(NotifyPacket notifyPacket, NetworkReader networkReader, int channelId) { int sequenceDistance = (int)sequencer.Distance(notifyPacket.Sequence, receiveSequence); // sequence is so far out of bounds we can't save, just kick him (or her!) if (Math.Abs(sequenceDistance) > WINDOW_SIZE) { connection?.Disconnect(); return; } // this message is old, we already received // a newer or duplicate packet. Discard it if (sequenceDistance <= 0) { return; } receiveSequence = notifyPacket.Sequence; if (sequenceDistance >= ACK_MASK_BITS) { receiveMask = 1; } else { receiveMask = (receiveMask << sequenceDistance) | 1; } AckPackets(notifyPacket.ReceiveSequence, notifyPacket.AckMask); int msgType = MessagePacker.UnpackId(networkReader); InvokeHandler(msgType, networkReader, channelId); if (Time.unscaledTime - lastNotifySentTime > NOTIFY_ACK_TIMEOUT) { SendNotify(new NotifyAck(), null, channelId); } }
public static void WriteNotifyPacket(this NetworkWriter writer, NotifyPacket packet) { writer.WriteUInt16(packet.Sequence); writer.WriteUInt16(packet.ReceiveSequence); writer.WriteUInt64(packet.AckMask); }