/// <summary> /// Sends a message, but notify when it is delivered or lost /// </summary> /// <typeparam name="T">type of message to send</typeparam> /// <param name="msg">message to send</param> /// <param name="token">a arbitrary object that the sender will receive with their notification</param> public void SendNotify <T>(T msg, object token, byte channelId = (byte)Channels.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(msg, writer); NetworkDiagnostics.OnSend(msg, channelId, writer.Length, 1); SendAsync(writer.ToArraySegment(), channelId).Forget(); } }
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) { 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); }
// 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); Disconnect(); } } }