/// <summary> /// Send a message to this exact same netpeer (loopback). /// </summary> public void SendUnconnectedToSelf(NetOutgoingMessage message) { if (message == null) { throw new ArgumentNullException(nameof(message)); } message.AssertNotSent(nameof(message)); if (Socket == null) { throw new InvalidOperationException("No socket bound."); } message._messageType = NetMessageType.Unconnected; message._isSent = true; if (!Configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData)) { return; // dropping unconnected message since it's not enabled for receiving } var om = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData); om.Write(message); om.IsFragment = false; om.ReceiveTime = NetTime.Now; om.SenderConnection = null; om.SenderEndPoint = Socket.LocalEndPoint as IPEndPoint; LidgrenException.Assert(om.BitLength == message.BitLength); ReleaseMessage(om); }
// public double LastSendRespondedTo { get { return m_connection.m_lastSendRespondedTo; } } internal void PacketSent(int numBytes, int numMessages) { LidgrenException.Assert(numBytes > 0 && numMessages > 0); _sentPackets++; _sentBytes += numBytes; _sentMessages += numMessages; }
internal void PacketReceived(int numBytes, int numMessages, int numFragments) { LidgrenException.Assert(numBytes > 0 && numMessages > 0); _receivedPackets++; _receivedBytes += numBytes; _receivedMessages += numMessages; _receivedFragments += numFragments; }
public override int GetAllowedSends() { int retval = _windowSize - NetUtility.PowOf2Mod( _sendStart + NetConstants.SequenceNumbers - _windowStart, NetConstants.SequenceNumbers); LidgrenException.Assert(retval >= 0 && retval <= _windowSize); return(retval); }
// send message immediately internal void SendLibraryMessage(NetOutgoingMessage message, IPEndPoint recipient) { AssertIsOnLibraryThread(); LidgrenException.Assert(!message._isSent); int length = 0; message.Encode(_sendBuffer, ref length, 0); SendPacket(length, recipient, 1); }
/// <summary> /// Compress (lossy) a <see cref="float"/> in the range 0..1 using the specified amount of bits. /// </summary> public static void WriteUnit(this IBitBuffer buffer, float value, int bitCount) { LidgrenException.Assert( (value >= 0.0) && (value <= 1.0), " WriteUnitSingle() must be passed a float in the range 0 to 1; val is " + value); int maxValue = (1 << bitCount) - 1; uint writeVal = (uint)(value * maxValue); buffer.Write(writeVal, bitCount); }
/// <summary> /// Compress a <see cref="float"/> within a specified range using the specified amount of bits. /// </summary> public static void WriteRanged(this IBitBuffer buffer, float value, float min, float max, int bitCount) { LidgrenException.Assert( (value >= min) && (value <= max), " WriteRangedSingle() must be passed a float in the range MIN to MAX; val is " + value); float range = max - min; float unit = (value - min) / range; int maxVal = (1 << bitCount) - 1; buffer.Write((uint)(maxVal * unit), bitCount); }
/// <summary> /// Compress (lossy) a <see cref="float"/> in the range -1..1 using the specified amount of bits. /// </summary> public static void WriteSigned(this IBitBuffer buffer, float value, int bitCount) { LidgrenException.Assert( (value >= -1.0) && (value <= 1.0), " WriteSignedSingle() must be passed a float in the range -1 to 1; val is " + value); float unit = (value + 1f) * 0.5f; int maxVal = (1 << bitCount) - 1; uint writeVal = (uint)(unit * maxVal); buffer.Write(writeVal, bitCount); }
/// <summary> /// Writes an <see cref="int"/> with the least amount of bits needed for the specified range. /// Returns the number of bits written. /// </summary> public static int WriteRanged(this IBitBuffer buffer, int min, int max, int value) { LidgrenException.Assert(value >= min && value <= max, "Value not within min/max range!"); uint range = (uint)(max - min); int numBits = NetBitWriter.BitsForValue(range); uint rvalue = (uint)(value - min); buffer.Write(rvalue, numBits); return(numBits); }
public override int GetAllowedSends() { if (!_doFlowControl) { return(int.MaxValue); } int value = _windowSize - NetUtility.PowOf2Mod( _sendStart + NetConstants.SequenceNumbers - _windowStart, _windowSize); LidgrenException.Assert(value >= 0 && value <= _windowSize); return(value); }
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly // seqNr is the actual nr received public override NetSocketResult ReceiveAcknowledge(TimeSpan now, int seqNr) { if (!_doFlowControl) { // we have no use for acks on this channel since we don't respect the window anyway _connection.Peer.LogWarning(new NetLogMessage(NetLogCode.SuppressedUnreliableAck, endPoint: _connection)); return(new NetSocketResult(true, false)); } // late (dupe), on time or early ack? int relate = NetUtility.RelativeSequenceNumber(seqNr, _windowStart); if (relate < 0) { //m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr); return(new NetSocketResult(true, false)); // late/duplicate ack } if (relate == 0) { //m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr); // ack arrived right on time LidgrenException.Assert(seqNr == _windowStart); _receivedAcks[_windowStart] = false; _windowStart = NetUtility.PowOf2Mod(_windowStart + 1, NetConstants.SequenceNumbers); return(new NetSocketResult(true, false)); } // Advance window to this position _receivedAcks[seqNr] = true; while (_windowStart != seqNr) { _receivedAcks[_windowStart] = false; _windowStart = NetUtility.PowOf2Mod(_windowStart + 1, NetConstants.SequenceNumbers); } return(new NetSocketResult(true, false)); }
// on user thread // the message must not be sent already private NetSendResult SendFragmentedMessage( NetOutgoingMessage message, IEnumerable <NetConnection?> recipients, NetDeliveryMethod method, int sequenceChannel) { // determine minimum mtu for all recipients int mtu = GetMTU(recipients, out int recipientCount); if (recipientCount == 0) { Recycle(message); return(NetSendResult.NoRecipients); } int group = GetNextFragmentGroup(); // do not send msg; but set fragmentgroup in case user tries to recycle it immediately message._fragmentGroup = group << 2 | 1; // create fragmentation specifics int totalBytes = message.ByteLength; int bytesPerChunk = NetFragmentationHelper.GetBestChunkSize(group, totalBytes, mtu); int numChunks = totalBytes / bytesPerChunk; if (numChunks * bytesPerChunk < totalBytes) { numChunks++; } var retval = NetSendResult.Sent; int bitsPerChunk = bytesPerChunk * 8; int bitsLeft = message.BitLength; byte[] buffer = message.GetBuffer(); for (int i = 0; i < numChunks; i++) { NetOutgoingMessage chunk = CreateMessage(); chunk.SetBuffer(buffer, false); chunk.BitLength = Math.Min(bitsLeft, bitsPerChunk); chunk._fragmentGroup = group << 2 | 1; chunk._fragmentGroupTotalBits = totalBytes * 8; chunk._fragmentChunkByteSize = bytesPerChunk; chunk._fragmentChunkNumber = i; LidgrenException.Assert(chunk.BitLength != 0); LidgrenException.Assert(chunk.GetEncodedSize() <= mtu); Interlocked.Add(ref chunk._recyclingCount, recipientCount); foreach (NetConnection?recipient in recipients.AsListEnumerator()) { if (recipient == null) { continue; } NetSendResult result = recipient.EnqueueMessage(chunk, method, sequenceChannel).Result; if (result > retval) { retval = result; // return "worst" result } } bitsLeft -= bitsPerChunk; } return(retval); }
// on user thread private async ValueTask <NetSendResult> SendFragmentedMessageAsync( PipeReader reader, NetConnection recipient, int sequenceChannel, CancellationToken cancellationToken) { int group = GetNextFragmentGroup(); (NetSendResult Result, NetSenderChannel?) SendChunk(NetOutgoingMessage chunk) { return(recipient.EnqueueMessage(chunk, NetDeliveryMethod.ReliableOrdered, sequenceChannel)); } NetSendResult finalResult; Exception? exception = null; try { ReadResult readResult; do { readResult = await reader.ReadAsync(cancellationToken).ConfigureAwait(false); if (readResult.IsCanceled) { NetOutgoingMessage cancelChunk = CreateStreamChunk(0, group, NetStreamFragmentType.Cancelled); finalResult = SendChunk(cancelChunk).Result; break; } ReadOnlySequence <byte> buffer = readResult.Buffer; int mtu = recipient.CurrentMTU; int bytesPerChunk = NetFragmentationHelper.GetBestChunkSize(group, (int)buffer.Length, mtu); while (buffer.Length > 0) { long chunkLength = Math.Min(buffer.Length, bytesPerChunk); NetOutgoingMessage chunk = CreateStreamChunk((int)chunkLength, group, NetStreamFragmentType.Data); foreach (ReadOnlyMemory <byte> memory in buffer.Slice(0, chunkLength)) { chunk.Write(memory.Span); } buffer = buffer.Slice(chunkLength); LidgrenException.Assert(chunk.GetEncodedSize() <= mtu); Interlocked.Add(ref chunk._recyclingCount, 1); var(result, channel) = SendChunk(chunk); if (result == NetSendResult.Queued) { await channel !.WaitForIdleAsync(millisecondsTimeout: 10000, cancellationToken); } else if (result != NetSendResult.Sent) { NetOutgoingMessage cancelChunk = CreateStreamChunk(0, group, NetStreamFragmentType.Cancelled); finalResult = SendChunk(cancelChunk).Result; reader.CancelPendingRead(); break; } } reader.AdvanceTo(readResult.Buffer.End); }while (!readResult.IsCompleted); NetOutgoingMessage endChunk = CreateStreamChunk(0, group, NetStreamFragmentType.EndOfStream); finalResult = SendChunk(endChunk).Result; } catch (Exception ex) { exception = ex; NetOutgoingMessage errorChunk = CreateStreamChunk(0, group, NetStreamFragmentType.ServerError); finalResult = SendChunk(errorChunk).Result; } await reader.CompleteAsync(exception).ConfigureAwait(false); return(finalResult); }