/// <summary> /// Confirms that a reliable packet has been received by the client. /// Note that a higher rID than the lowest expected indicates that all /// previous reliable packets were received. /// </summary> public void confirmReliable(ushort rID, int streamID) { //Great! using (DdMonitor.Lock(_sync)) { //Get the relevant stream Client.StreamState stream = _streams[streamID]; //This satisfies all packets inbetween for (ushort i = stream.S2C_ReliableConfirmed; i <= rID; ++i) { //Get our associated info ReliableInfo ri; if (!stream.reliablePackets.TryGetValue(i, out ri)) { continue; } stream.reliablePackets.Remove(i); //Part of a data stream? if (ri.streamParent != null) { if (ri.streamParent.lastPacket == ri.packet) { ri.streamParent.onCompleted(); } } else { ri.onCompleted(); } } stream.S2C_ReliableConfirmed = (ushort)(rID + 1); } }
/// <summary> /// Allows the client to take care of anything it needs to /// </summary> public override void poll() { //Sync up! using (DdMonitor.Lock(_sync)) { //Only time out server-side clients int now = Environment.TickCount; if (connectionTimeout != -1 && !_bClientConn && now - base._lastPacketRecv > connectionTimeout) { //Farewell~ Log.write(TLog.Warning, "Client timeout: {0}", this); destroy(); return; } if (_tickDestroy > 0 && now - _tickDestroy > 15000) //15 seconds (gives enough time to send any queued messages) { //Farewell~ Log.write(TLog.Warning, "Client timeout: {0}", this); destroy(); return; } //Should we decay the bytes written? if (now - _tickLastDecay > 20) { _bytesWritten -= _decayRate; if (_bytesWritten < 0) { _bytesWritten = 0; } _tickLastDecay = now; } //Update connection stats? if (now - _tickLastBytesSample > 1000) { _stats.SendSpeed = _bytesSent; _stats.ReceiveSpeed = _bytesReceived; _bytesSent = 0; _bytesReceived = 0; _tickLastBytesSample = now; } //Handle each stream for (int i = 0; i < 4; ++i) { Client.StreamState stream = _streams[i]; //Queue reliable packets as necessary enqueueReliables(stream); //Make sure they all reach their destination ensureReliable(stream); } //Box packets as necessary and send them out sendQueuedPackets(); } }
/// <summary> /// Takes a note that the packet was received out of sync /// </summary> public void reportOutOfSync(PacketBase packet, ushort rID, int streamID) { //Get the relevant stream Client.StreamState stream = _streams[streamID]; //Keep the packet around for later stream.oosReliable[rID] = packet; //Is the last OOS packet still valid? if (stream.lastOOSPacket > stream.C2S_Reliable) { //Ignore the request until this OOS is honored return; } //Note it down as out of sync stream.lastOOSPacket = rID; stream.tickOOSPacket = Environment.TickCount; }
/// <summary> /// Sends a reliable packet to the client /// </summary> public void sendReliable(PacketBase packet, Action completionCallback, int streamID) { //Sync up! using (DdMonitor.Lock(_sync)) { //Get the relevant stream Client.StreamState stream = _streams[streamID]; //Make sure the packet is serialized packet.MakeSerialized(this, _handler); //Is the (packet and reliable header) too large to be sent as one? if (4 + packet._size + _CRCLength > _C2S_UDPSize) { //Add the stream packet to the reliable queue so we know //when to start streaming it. DataStream ds = new DataStream(); ReliableInfo ri = new ReliableInfo(); ds.amountSent = 0; ds.buffer = packet.Data; if (completionCallback != null) { ds.Completed += completionCallback; } ri.dataStream = ds; //Put it in the reliable queue stream.reliableQueue.Enqueue(ri); } else { //Jam it in the reliable queue to be parsed ReliableInfo ri = new ReliableInfo(); ri.packet = packet; ri.rid = -1; if (completionCallback != null) { ri.Completed += completionCallback; } //Put it in the reliable queue stream.reliableQueue.Enqueue(ri); } } }
/// <summary> /// Ensures that the client is receiving all reliable packets sent /// </summary> /// <remarks>Also is the only function that sends reliable packets.</remarks> private void ensureReliable(Client.StreamState stream) { //Compare times int currentTick = Environment.TickCount; //Do we need to send an out of sync notification? if (stream.lastOOSPacket > stream.C2S_Reliable && currentTick - stream.tickOOSPacket > 100) { OutOfSync oos = new OutOfSync(stream.streamID); oos.streamID = stream.streamID; oos.rNumber = stream.lastOOSPacket; send(oos); stream.tickOOSPacket = currentTick + 200; } //Do we have any bandwidth available? int bytesLeft = _rateThreshold - _bytesWritten; if (bytesLeft < 0) { return; } //We want to start with the first sent packet for (ushort n = stream.S2C_ReliableConfirmed; n < stream.S2C_Reliable; ++n) { //Does it exist? ReliableInfo ri; if (!stream.reliablePackets.TryGetValue(n, out ri)) { continue; } //Has it been delayed too long? if (currentTick - ri.timeSent < 1000) { continue; } //Resend it _packetQueue.Enqueue(ri.packet); //Was it a reattempt? if (ri.timeSent != 0) { ri.attempts++; //Log.write(TLog.Warning, "Reliable packet #" + ri.rid + " lost. (" + ri.attempts + ")"); } ri.timeSent = Environment.TickCount; //Don't go over the bandwidth limit or we'll just complicate things bytesLeft -= ri.packet._size; if (bytesLeft < 0) { break; } } }