// tick: processes up to 'limit' messages for each connection // => limit parameter to avoid deadlocks / too long freezes if server or // client is too slow to process network load // => Mirror & DOTSNET need to have a process limit anyway. // might as well do it here and make life easier. // => returns amount of remaining messages to process, so the caller // can call tick again as many times as needed (or up to a limit) // // Tick() may process multiple messages, but Mirror needs a way to stop // processing immediately if a scene change messages arrives. Mirror // can't process any other messages during a scene change. // (could be useful for others too) // => make sure to allocate the lambda only once in transports public int Tick(int processLimit, Func <bool> checkEnabled = null) { // only if pipe was created yet (after start()) if (receivePipe == null) { return(0); } // process up to 'processLimit' messages for this connection for (int i = 0; i < processLimit; ++i) { // check enabled in case a Mirror scene message arrived if (checkEnabled != null && !checkEnabled()) { break; } // peek first. allows us to process the first queued entry while // still keeping the pooled byte[] alive by not removing anything. if (receivePipe.TryPeek(out int connectionId, out EventType eventType, out ArraySegment <byte> message)) { switch (eventType) { case EventType.Connected: OnConnected?.Invoke(connectionId); break; case EventType.Data: OnData?.Invoke(connectionId, message); break; case EventType.Disconnected: OnDisconnected?.Invoke(connectionId); // remove disconnected connection now that the final // disconnected message was processed. clients.TryRemove(connectionId, out ConnectionState _); break; } // IMPORTANT: now dequeue and return it to pool AFTER we are // done processing the event. receivePipe.TryDequeue(); }
public int Tick(int processLimit, Func <bool> checkEnabled = null) { int remaining = 0; // for each connection // checks enabled in case a Mirror scene message arrived foreach (KeyValuePair <int, ConnectionState> kvp in clients) { MagnificentReceivePipe receivePipe = kvp.Value.receivePipe; // need a processLimit copy just for this connection so that // we can count the Connected message as a processed one. // => otherwise decreasing the limit in Connected event would // decrease the limit for everyone! int connectionProcessLimit = processLimit; // always process connect FIRST before anything else if (connectionProcessLimit > 0) { if (receivePipe.CheckConnected()) { OnConnected?.Invoke(kvp.Key); // it counts as a processed message --connectionProcessLimit; } } // process up to 'processLimit' messages for this connection // checks enabled in case a Mirror scene message arrived for (int i = 0; i < connectionProcessLimit; ++i) { // check enabled in case a Mirror scene message arrived if (checkEnabled != null && !checkEnabled()) { break; } // peek first. allows us to process the first queued entry while // still keeping the pooled byte[] alive by not removing anything. if (receivePipe.TryPeek(out ArraySegment <byte> message)) { OnData?.Invoke(kvp.Key, message); // IMPORTANT: now dequeue and return it to pool AFTER we are // done processing the event. receivePipe.TryDequeue(); } // AFTER PROCESSING, add remaining ones to our counter remaining += receivePipe.Count; } // always process disconnect AFTER anything else // (should never process data messages after disconnect message) if (connectionProcessLimit > 0) { if (receivePipe.CheckDisconnected()) { OnDisconnected?.Invoke(kvp.Key); connectionsToRemove.Add(kvp.Key); } } } // remove all disconnected connections now that we processed the // final disconnect message. foreach (int connectionId in connectionsToRemove) { clients.TryRemove(connectionId, out ConnectionState _); } connectionsToRemove.Clear(); return(remaining); }