/// <summary>
        /// </summary>
        /// <param name="arg"></param>
        private async void SendOutgoingUdpMessages(object arg)
        {
            object[] args = (object[])arg;

            AsyncUdpSocketSender             sender = (AsyncUdpSocketSender)args[0];
            EventPoller <OutgoingUdpMessage> outgoingMessagePoller = (EventPoller <OutgoingUdpMessage>)args[1];
            CancellationToken cancellation = (CancellationToken)args[2];

            int eventsThisIteration = 0;

            while (!cancellation.IsCancellationRequested)
            {
                // poll for send events
                outgoingMessagePoller.Poll((m, s, eob) =>
                {
                    // Send udp message
                    if (m.SendType == UdpSendType.SendTo)
                    {
                        sender.BeginSendTo(m.Buffer, 0, m.Size, m.Endpoint);
                    }

                    eventsThisIteration++;
                    return(eventsThisIteration < MaxUdpMessageSendBeforeSleep);
                });

                await Task.Delay(1, cancellation);
            }
        }
        // Start is called before the first frame update
        private void Start()
        {
            // UDP Socket Listener/Sender initialization
            _socket         = new UdpSocket();
            _socketListener = new AsyncUdpSocketListener(_socket);
            _socketSender   = new AsyncUdpSocketSender(_socket);

            // Add to RingBuffer from listener async callbacks which happen off the main thread
            _socketListener.ReceivedUdpMessageEvent += AddUdpMessageToReceivedBuffer;

            // Init ring buffers
            _receivedMessageBuffer = RingBuffer <UdpMessage> .Create(
                ProducerType.Single,
                UdpMessage.DefaultFactory,
                256,
                new BusySpinWaitStrategy());

            _receivedMessagePoller = _receivedMessageBuffer.NewPoller();
            _receivedMessageBuffer.AddGatingSequences(_receivedMessagePoller.Sequence);

            _outgoingMessageBuffer = RingBuffer <OutgoingUdpMessage> .Create(
                ProducerType.Single,
                OutgoingUdpMessage.DefaultFactory,
                256,
                new BusySpinWaitStrategy());

            _outgoingMessagePoller = _outgoingMessageBuffer.NewPoller();
            _outgoingMessageBuffer.AddGatingSequences(_outgoingMessagePoller.Sequence);

            // This makes a deep copy of the sent message before publishing it to the outgoing message buffer
            // It makes a deep copy because the buffers containing the data in the main thread could change
            // before the data has a chance to be copied into the ring buffer. Prob want to think of a way around this.. maybe more ring buffers
            _outgoingUdpMessageSender = new RingBufferOutgoingUdpMessageSender(_outgoingMessageBuffer);

            // The NetManager reactor. TODO: Benchmark and see if I have actually made any improvement by implementing disruptor
            // Though I do like the idea of clearing up the locks and doing all the logic in the main thread esp since
            // the game server will need to routinely access connected client info
            RNetManager = new NetManager(null, _outgoingUdpMessageSender)
            {
                DisconnectTimeout = 600
            };
            RGameReactor.RNetManager = RNetManager;
            _updateEvent             = new GameEvent {
                EventId = GameEvent.Event.Update
            };
            _tempEvent = new GameEvent(); // reusable event for the update loop

            //if (!ShouldBind)
            //{
            //RNetManager.SimulateLatency = true;
            RNetManager.SimulatePacketLoss = true;
            //    R_NetManager.SimulationMaxLatency = 10;
            //    R_NetManager.SimulationMinLatency = 0;
            //}

            _cancellationSource = new CancellationTokenSource();
            _processOutgoing    = new Thread(SendOutgoingUdpMessages)
            {
                IsBackground = true,
                Name         = "UdpServer"
            };

            // Every frame the previous frames events get replaced with new output events created by the NetManager reactor for that frame
            _batchedEvents = new NetManagerEvent[MaxUdpMessagesPerFrame];
            for (int i = 0; i < MaxUdpMessagesPerFrame; i++)
            {
                _batchedEvents[i] = new NetManagerEvent {
                    EventId = NetManagerEvent.Event.UdpMessage
                }
            }
            ;

            // BIND
            if (ShouldBind)
            {
                _socket.BindLocalIpv4(BindAddress, BindPort);
            }

            // CONNECT - TEMPORARY - NEEDS TO BE SOME SORT OF STATE MACHINE
            if (ShouldConnect)
            {
                RNetManager.Connect(ConnectAddress, ConnectPort, "somekey");
            }

            // No point actually awaiting this call.. it kicks off a
            // recurring execution where the finish method always calls the start method again
#pragma warning disable 4014
            _socketListener.StartAsyncReceive();
#pragma warning restore 4014


            // Start thread that polls for outgoing udp messages and sends them on the socket
            _processOutgoing.Start(new object[] { _socketSender, _outgoingMessagePoller, _cancellationSource.Token });

            // Start coroutine that will send the check timeout event
            StartCoroutine("SendCheckTimeoutEvent");
        }