public bool Run()
        {
            m_Log.Info("Putting test data into ObjectProperties");
            ObjectProperties propMsg = new ObjectProperties();

            propMsg.ObjectData.Add(testData);
            UDPPacket p = new UDPPacket(4096, true);

            propMsg.Serialize(p);
            p.Flush();
            m_Log.InfoFormat("Serialized length={0}", p.DataLength);
            return(p.DataLength == 118);
        }
        public void SendNetTest(IPAddress address)
        {
            var p = new UDPPacket
            {
                OutQueue       = Message.QueueOutType.High,
                IsZeroEncoded  = false,
                SequenceNumber = (uint)Interlocked.Increment(ref m_NetTestSeqNumber)
            };

            p.WriteMessageNumber(MessageType.NetTest);
            new NetTest {
                Port = (ushort)LocalPort
            }.Serialize(p);
            p.Flush();
            SendPacketTo(p, new IPEndPoint(address, LocalPort));
        }
        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;
            }
        }