private void TransmitThread(object param) { Thread.CurrentThread.IsBackground = true; int lastAckTick = Environment.TickCount; int lastPingTick = Environment.TickCount; int lastSimStatsTick = Environment.TickCount; byte pingID = 0; Thread.CurrentThread.Name = string.Format("LLUDP:Transmitter for CircuitCode {0} / IP {1}", CircuitCode, RemoteEndPoint.ToString()); var QueueList = new Queue <Message> [(int)Message.QueueOutType.NumQueues]; Message.QueueOutType qroutidx; var QueueCount = new int[(int)Message.QueueOutType.NumQueues]; for (uint qidx = 0; qidx < (uint)Message.QueueOutType.NumQueues; ++qidx) { QueueList[qidx] = new Queue <Message>(); } var HighPriorityQueue = QueueList[(uint)Message.QueueOutType.High]; var MediumPriorityQueue = QueueList[(uint)Message.QueueOutType.Medium]; var LowPriorityQueue = QueueList[(uint)Message.QueueOutType.Low]; int qcount; int timeout = 10; Message m; CancelTxThread cancelmsg; try { while (m_TxRunning) { foreach (var q in QueueList) { if (q.Count > 0) { timeout = 0; } } qcount = m_TxQueue.Count + 1; m = null; cancelmsg = null; while (qcount > 0) { try { m = m_TxQueue.Dequeue(timeout); } catch { break; } timeout = 0; --qcount; cancelmsg = m as CancelTxThread; if (cancelmsg != null) { return; } if (m is AcksReceived) { /* nothing additional to do here with AcksReceived */ } else if (m_QueueOutTable.TryGetValue(m.Number, out qroutidx)) { m.OutQueue = qroutidx; QueueList[(uint)m.OutQueue].Enqueue(m); } else if (m.Number == MessageType.LayerData) { var ld = (LayerData)m; switch (ld.LayerType) { case LayerData.LayerDataType.Land: case LayerData.LayerDataType.LandExtended: m.OutQueue = Message.QueueOutType.LandLayerData; QueueList[(uint)m.OutQueue].Enqueue(m); break; case LayerData.LayerDataType.Wind: case LayerData.LayerDataType.WindExtended: m.OutQueue = Message.QueueOutType.WindLayerData; QueueList[(uint)m.OutQueue].Enqueue(m); break; default: m.OutQueue = Message.QueueOutType.GenericLayerData; QueueList[(uint)m.OutQueue].Enqueue(m); break; } } else if (m.Number < MessageType.Medium) { m.OutQueue = Message.QueueOutType.High; HighPriorityQueue.Enqueue(m); } else if (m.Number < MessageType.Low) { m.OutQueue = Message.QueueOutType.Medium; MediumPriorityQueue.Enqueue(m); } else { m.OutQueue = Message.QueueOutType.Low; LowPriorityQueue.Enqueue(m); } } for (uint qidx = 0; qidx < (uint)Message.QueueOutType.NumQueues; ++qidx) { QueueCount[qidx] = QueueList[qidx].Count; } do { /* make high packets pass low priority packets */ for (int queueidx = 0; queueidx < QueueList.Length; ++queueidx) { var q = QueueList[queueidx]; if (q.Count == 0) { continue; } if (m_AckThrottlingCount[queueidx] > 100) { QueueCount[queueidx] = 0; continue; } try { m = q.Dequeue(); if (QueueCount[queueidx] > 0) { --QueueCount[queueidx]; } } catch { QueueCount[queueidx] = 0; continue; } if (m != null) { try { var p = new UDPPacket { OutQueue = m.OutQueue, IsZeroEncoded = m.ZeroFlag || m.ForceZeroFlag }; p.WriteMessageNumber(m.Number); m.Serialize(p); p.Flush(); p.IsReliable = m.IsReliable; p.AckMessage = p.IsReliable ? m : null; p.FinishZLE(); int savedDataLength = p.DataLength; if (p.IsReliable) { if (MAX_DATA_MTU > 1 + (uint)savedDataLength) { uint appendableAcks = (MAX_DATA_MTU - 1 - (uint)savedDataLength) / 4; var curacks = (uint)m_AckList.Count; if (appendableAcks != 0 && curacks != 0) { p.HasAckFlag = true; uint cnt = 0; while (cnt < appendableAcks && cnt < curacks && cnt < 255) { p.WriteUInt32BE_NoZLE(m_AckList.Dequeue()); ++cnt; } p.WriteUInt8((byte)cnt); } } Interlocked.Increment(ref m_AckThrottlingCount[queueidx]); } SendCircuitPacket(p); Interlocked.Increment(ref m_PacketsSent); p.EnqueuedAtTime = Environment.TickCount; p.TransferredAtTime = Environment.TickCount; if (m.IsReliable) { lock (m_UnackedPacketsHash) { m_UnackedPacketsHash.Add(p.SequenceNumber, p); } lock (m_UnackedBytesLock) { m_UnackedBytes += p.DataLength; } } if (m.Number == MessageType.LogoutReply) { lock (m_LogoutReplyLock) { m_LogoutReplySeqNo = p.SequenceNumber; m_LogoutReplySentAtTime = Environment.TickCount; m_LogoutReplySent = true; } } if (m.Number == MessageType.KickUser) { m_KickUserSentAtTime = Environment.TickCount; m_KickUserSent = true; } } catch (Exception e) { m_Log.DebugFormat("Failed to serialize message of type {0}: {1}\n{2}", m.TypeDescription, e.Message, e.StackTrace); } } } qcount = 0; for (int queueidx = 0; queueidx < QueueCount.Length; ++queueidx) { qcount += QueueCount[queueidx]; } } while (qcount != 0); if (Environment.TickCount - m_LastReceivedPacketAtTime >= (IsCircuitInPause ? 300000 : 60000)) { LogMsgOnTimeout(); /* do not do sync termination here */ ThreadPool.QueueUserWorkItem(TerminateCircuitAsyncRun); return; } if ((Environment.TickCount - m_LogoutReplySentAtTime >= 10000 && m_LogoutReplySent) || (Environment.TickCount - m_KickUserSentAtTime >= 10000 && m_KickUserSent)) { LogMsgLogoutReply(); /* do not do sync termination here */ ThreadPool.QueueUserWorkItem(TerminateCircuitAsyncRun); return; } if (Environment.TickCount - lastSimStatsTick >= 1000) { int deltatime = Environment.TickCount - lastSimStatsTick; lastSimStatsTick = Environment.TickCount; SendSimStats(deltatime); } if (Environment.TickCount - lastPingTick >= 5000) { long lasttimesent; if (!m_PingSendTicks.TryGetValue(pingID, out lasttimesent)) { lastPingTick = Environment.TickCount; var p = new UDPPacket(); p.WriteMessageNumber(MessageType.StartPingCheck); p.WriteUInt8(pingID); m_PingSendTicks[pingID] = PingTimeSource.TickCount; pingID++; p.WriteUInt32(0); try { SendCircuitPacket(p); Interlocked.Increment(ref m_PacketsSent); } catch (ObjectDisposedException) { return; } } else if (PingTimeSource.TicksElapsed(PingTimeSource.TickCount, lasttimesent) >= PingTimeSource.SecsToTicks(5)) { m_PingSendTicks.Remove(pingID); #if DEBUG if (!IsCircuitInPause) { m_Log.DebugFormat("Missing response to ping id {0}", pingID); } #endif } } if (Environment.TickCount - lastAckTick >= 1000) { lastAckTick = Environment.TickCount; /* check for acks to be send */ int c = m_AckList.Count; while (c > 0) { var p = new UDPPacket(); p.WriteMessageNumber(MessageType.PacketAck); if (c > 100) { p.WriteUInt8(100); for (int i = 0; i < 100; ++i) { p.WriteUInt32(m_AckList.Dequeue()); } c -= 100; } else { p.WriteUInt8((byte)c); for (int i = 0; i < c; ++i) { p.WriteUInt32(m_AckList.Dequeue()); } c = 0; } SendCircuitPacket(p); Interlocked.Increment(ref m_PacketsSent); } } } } finally { m_TxRunning = false; } }