/// <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"); }