private static unsafe void Transmit()
        {
            // Transmit anything in buffers
            while (HasSendDataQueued)
            {
                MessageStream *sendStream = MessageStreamManager.PlayerConnectionMt_DequeSendStream();
                if (sendStream == null)  // rare return value in multithreading situations when the queue is not empty
                {
                    continue;
                }
                MessageStream.MessageStreamBuffer *bufferRead = sendStream->BufferRead;

                int offset = 0;

                while (bufferRead != null)
                {
                    if (bufferRead->Size > 0)
                    {
#if UNITY_WEBGL
                        uint actualRead = WebSocketSend(bufferRead->Buffer + offset, bufferRead->Size - offset);
                        if (actualRead == 0xffffffff)
#else
                        uint actualRead = Baselib_Socket_TCP_Send(hSocket, bufferRead->Buffer + offset, (uint)(bufferRead->Size - offset), (Baselib_ErrorState *)UnsafeUtility.AddressOf(ref errState));
                        if (errState.code != Baselib_ErrorCode.Success)
#endif
                        {
                            // Something bad happened; lost connection maybe?
                            // After cleaning up, next time we will try to re-initialize
                            Disconnect();
                            initRetryCounter = kInitRetryCounter;
                            return;
                        }

                        if (actualRead == 0)
                        {
                            // Move the data to be sent to the front of this buffer for next time
                            sendStream->RecyclePartialStream(bufferRead, offset);
                            MessageStreamManager.PlayerConnectionMt_QueueSendStream((IntPtr)sendStream);
                            return;
                        }

                        offset += (int)actualRead;
                    }

                    if (offset == bufferRead->Size)
                    {
                        bufferRead = bufferRead->Next;
                        offset     = 0;
                    }
                }

                MessageStreamManager.RecycleStream(sendStream);
            }
        }
        internal unsafe void BufferTestRecycleInternal(string[] textData, int recycleSize)
        {
            MessageStream buf = new MessageStream(16);

            MessageStream.MessageStreamBuffer *recycleUntil = buf.BufferRead;
            int    recycleOffset = recycleSize;
            string totalText     = "";

            int sizeBeforeWriteBuffer = 0;
            int totalSize             = 0;

            foreach (string text in textData)
            {
                totalText += text;
                bool incRecycleUntil = false;
                if (text.Length * 2 + buf.BufferWrite->Size > buf.BufferWrite->Capacity)
                {
                    sizeBeforeWriteBuffer += buf.BufferWrite->Size;
                    if (sizeBeforeWriteBuffer < recycleSize)
                    {
                        incRecycleUntil = true;
                        recycleOffset  -= buf.BufferWrite->Size;
                    }
                }

                BufferTestWriteInternal(ref buf, text);

                if (incRecycleUntil)
                {
                    recycleUntil = recycleUntil->Next;
                }

                totalSize += text.Length * 2;
            }

            int preRecycleUntilSize     = recycleUntil->Size;
            int preRecycleUntilNextSize = recycleUntil->Next != null ? recycleUntil->Next->Size : -1;
            int preRecycleWriteSize     = buf.BufferWrite->Size;

            buf.RecyclePartialStream(recycleUntil, recycleOffset);

            // After recycling----

            // - Find first buffer with data remaining after recycling
            MessageStream.MessageStreamBuffer *firstBuffer = buf.BufferRead;
            while (firstBuffer->Size == 0 && firstBuffer->Next != null)
            {
                firstBuffer = firstBuffer->Next;
            }

            // - if recycleOffset == size of the last buffer, next buffer should be "First buffer remaining" and be full
            // - otherwise, ensure size in first buffer with data is as expected
            if (preRecycleUntilSize == recycleOffset)
            {
                Assert.AreEqual(preRecycleUntilNextSize, firstBuffer->Size);
            }
            else
            {
                Assert.AreEqual(preRecycleUntilSize - recycleOffset, firstBuffer->Size);
            }
            unsafe
            {
                Assert.AreEqual(totalText.Substring(recycleSize / 2, firstBuffer->Size / 2), new string((char *)firstBuffer->Buffer, 0, firstBuffer->Size / 2));
            }

            // - Check write buffer - if different than first buffer remaining it should be same as before
            //                      - otherwise should be pre-recycle write buffer size minus recycleOffset (which should work out the same as previous check)
            if (firstBuffer->Buffer == buf.BufferWrite->Buffer)
            {
                Assert.AreEqual(preRecycleWriteSize - recycleOffset, buf.BufferWrite->Size);
            }
            else
            {
                Assert.AreEqual(preRecycleWriteSize, buf.BufferWrite->Size);
            }
            unsafe
            {
                if (firstBuffer->Buffer == buf.BufferWrite->Buffer)
                {
                    Assert.AreEqual(totalText.Substring(recycleSize / 2, (totalSize - recycleSize) / 2), new string((char *)buf.BufferWrite->Buffer, 0, buf.BufferWrite->Size / 2));
                }
                else
                {
                    Assert.AreEqual(totalText.Substring(sizeBeforeWriteBuffer / 2, buf.BufferWrite->Size / 2), new string((char *)buf.BufferWrite->Buffer, 0, buf.BufferWrite->Size / 2));
                }
            }

            // - Check read buffer - if smaller than first data size it should be 0
            //                     - otherwise if recycled more than its size it should be 0
            //                     - otherwise it should be the first buffer with data remaining
            //                     - If recycled including full offset, read should be 0 and not have a next

            if (buf.BufferRead->Capacity < textData[0].Length * 2)
            {
                Assert.AreEqual(0, buf.BufferRead->Size);
            }
            else if (buf.BufferRead->Size <= recycleSize)
            {
                Assert.AreEqual(0, buf.BufferRead->Size);
            }
            else
            {
                Assert.AreEqual(buf.BufferRead->Buffer, firstBuffer->Buffer);
            }

            if (preRecycleUntilSize == recycleOffset)
            {
                Assert.AreEqual(0, buf.BufferRead->Size);
                Assert.AreEqual(IntPtr.Zero, (IntPtr)buf.BufferRead->Next);
            }

            unsafe
            {
                Assert.AreEqual(totalText.Substring(recycleSize / 2, buf.BufferRead->Size / 2), new string((char *)buf.BufferRead->Buffer, 0, buf.BufferRead->Size / 2));
            }

            // - Check other details
            Assert.AreEqual(totalSize - recycleSize, buf.TotalBytes);
        }
        private static unsafe void Receive()
        {
            // Receive anything sent to us
            // Similar setup for sending data
            MessageHeader *header = (MessageHeader *)receiveStream.BufferRead->Buffer;

            int bytesNeeded = 0;

            if (receiveStream.TotalBytes < sizeof(MessageHeader))
            {
                bytesNeeded = sizeof(MessageHeader) - receiveStream.TotalBytes;
            }
            else
            {
                bytesNeeded = sizeof(MessageHeader) + header->bytes - receiveStream.TotalBytes;
            }

            while (bytesNeeded > 0)
            {
                MessageStream.MessageStreamBuffer *bufferWrite = receiveStream.BufferWrite;

                int bytesAvail = bufferWrite->Capacity - bufferWrite->Size;
#if UNITY_WEBGL
                uint actualWritten = WebSocketReceive(bufferWrite->Buffer + bufferWrite->Size,
                                                      bytesNeeded <= bytesAvail ? bytesNeeded : bytesAvail);
                if (actualWritten == 0xffffffff)
#else
                uint actualWritten = Baselib_Socket_TCP_Recv(hSocket, bufferWrite->Buffer + bufferWrite->Size,
                                                             (uint)(bytesNeeded <= bytesAvail ? bytesNeeded : bytesAvail), (Baselib_ErrorState *)UnsafeUtility.AddressOf(ref errState));
                if (errState.code != Baselib_ErrorCode.Success)
#endif
                {
                    // Something bad happened; lost connection maybe?
                    // After cleaning up, next time we will try to re-initialize
                    Disconnect();
                    initRetryCounter = kInitRetryCounter;
                    return;
                }

                if (bytesNeeded > 0 && actualWritten == 0)
                {
                    // Don't do anything with data until we've received everything
                    return;
                }

                receiveStream.UpdateSize((int)actualWritten);
                bytesNeeded -= (int)actualWritten;
                if (bytesNeeded == 0)
                {
                    // Finished receiving header
                    if (receiveStream.TotalBytes == sizeof(MessageHeader))
                    {
                        // De-synced somewhere... reset connection
                        if (header->magicId != EditorMessageIds.kMagicNumber)
                        {
                            Disconnect();
                            initRetryCounter = kInitRetryCounter;
                            return;
                        }
                        bytesNeeded = header->bytes;
                        receiveStream.Allocate(bytesNeeded);
                    }

                    // Finished receiving message
                    if (bytesNeeded == 0)
                    {
                        // Otherwise bytesNeeded becomes 0 after message is finished, which can be immediately in the
                        // case of PlayerMessageId.kApplicationQuit (message size 0)
                        foreach (var e in m_EventMessageList)
                        {
                            if (e.messageId == header->messageId)
                            {
                                // This could be anything from a 4-byte "bool" to an asset sent from the editor
                                byte[] messageData = receiveStream.ToByteArray(sizeof(MessageHeader), receiveStream.TotalBytes);
                                e.Invoke(0, messageData);
                            }
                        }

                        if (header->messageId == EditorMessageIds.kApplicationQuit)
                        {
                            UnityEngine.Debug.Log("Unity editor has been closed");
                            Disconnect();
                        }

                        if (header->messageId == EditorMessageIds.kNoFurtherConnections)
                        {
                            UnityEngine.Debug.Log("Unity editor can not accept any more connections");
                            Disconnect();
                        }

                        // Poll for next message
                        receiveStream.RecycleStreamAndFreeExtra();
                        bytesNeeded = sizeof(MessageHeader);
                    }
                }
            }

            // This code should not be executable
            throw new InvalidOperationException("Internal error receiving network data");
        }