/// <summary> /// Enqueues necessary packets for a data stream /// </summary> private bool enqueueDataStream(DataStream ds, StreamState stream) { //Write packets into the stream while we still have bandwidth available int bytesLeft = _rateThreshold - _bytesWritten; while (bytesLeft > 0 && ds.amountSent < ds.buffer.Length) { //Prepare a data packet DataPacket dp = new DataPacket(stream.streamID); dp._bFirstPacket = (ds.amountSent == 0); dp.data = ds.buffer; dp.offset = ds.amountSent; dp.rNumber = stream.S2C_Reliable++; dp.MakeSerialized(this, _handler); ds.lastPacket = dp; //For completion notification ds.amountSent += dp._dataRead; bytesLeft -= dp._dataRead; //Add it to our list of reliable packets ReliableInfo ri = new ReliableInfo(); ri.streamParent = ds; ri.packet = dp; ri.rid = dp.rNumber; ri.timeSent = 0; stream.reliablePackets[dp.rNumber] = ri; } //Have we completed? return(!(ds.amountSent >= ds.buffer.Length)); }
/// <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> /// Adds any reliable packets waiting to be sent to the packet queue /// </summary> private void enqueueReliables(StreamState stream) { //Check for data streams if (stream.reliableQueue.Count == 0) { return; } ReliableInfo ri = stream.reliableQueue.Peek(); while (ri.dataStream != null) { //If it's still streaming, then don't send any more reliables yet if (enqueueDataStream(ri.dataStream, stream)) { return; } else { stream.reliableQueue.Dequeue(); } //Check the next if (stream.reliableQueue.Count == 0) { return; } ri = stream.reliableQueue.Peek(); } //Take care of reliable packet waiting to be streamed ICollection <ReliableInfo> reliableBoxed = boxReliablePackets(stream.reliableQueue, stream, stream.streamID); //Insert them all into our reliable table foreach (ReliableInfo info in reliableBoxed) { //Make sure the rid is correct if (info.rid == -1) { Log.write(TLog.Error, "Reliable info was queued for sending with an unassigned reliable number."); continue; } stream.reliablePackets[(ushort)info.rid] = info; } }
/// <summary> /// Groups packets together into box packets as necessary /// </summary> public ICollection <ReliableInfo> boxReliablePackets(Queue <ReliableInfo> packetQueue, StreamState stream, int streamID) { //Empty? if (packetQueue.Count == 0) { return(null); } //If it's just one packet, there's no need else if (packetQueue.Count == 1) { //We need to put it in a reliable case Reliable reli = new Reliable(); ReliableInfo rinfo = packetQueue.Dequeue(); reli.streamID = streamID; reli.packet = rinfo.packet; rinfo.rid = reli.rNumber = stream.S2C_Reliable++; rinfo.packet = reli; List <ReliableInfo> list = new List <ReliableInfo>(); list.Add(rinfo); return(list); } //Go through the list, creating boxed packets as we go List <ReliableInfo> reliables = new List <ReliableInfo>(); ReliableInfo info; ReliableBox box = new ReliableBox(streamID); int packetStartSize = 4 /*reliable*/ + 2 /*reliable box*/ + _CRCLength; int currentSize = packetStartSize; //Header+footer size of a boxed reliable packet //Group our normal packets while (packetQueue.Count > 0 && packetQueue.Peek().dataStream == null) { ReliableInfo pInfo = packetQueue.Dequeue(); //If the packet exceeds the max limit, send it on it's own if (2 + 1 + pInfo.packet.Length > byte.MaxValue) { //We need to send the previous boxing packets first, as they //should be in order of reliable id if (box.reliables.Count > 0) { info = new ReliableInfo(); info.rid = stream.S2C_Reliable++; //Don't add a lonely box if (box.reliables.Count == 1) { info.packet = new Reliable(box.reliables[0].packet, info.rid, streamID); } else { //Make sure the box is serialized box.MakeSerialized(this, _handler); info.packet = new Reliable(box, info.rid, streamID); } info.consolidateEvents(box.reliables); reliables.Add(info); } box = new ReliableBox(streamID); currentSize = packetStartSize; //Add the packet on it's own Reliable reli = new Reliable(); reli.streamID = streamID; reli.packet = pInfo.packet; pInfo.rid = reli.rNumber = stream.S2C_Reliable++; pInfo.packet = reli; reliables.Add(pInfo); continue; } //Do we have space to add this packet? if (currentSize + pInfo.packet.Length + 1 > udpMaxSize) { //There's not enough room, box up our current packet info = new ReliableInfo(); info.rid = stream.S2C_Reliable++; //Don't add a lonely box if (box.reliables.Count == 1) { info.packet = new Reliable(box.reliables[0].packet, info.rid, streamID); } else { //Make sure the box is serialized box.MakeSerialized(this, _handler); info.packet = new Reliable(box, info.rid, streamID); } info.consolidateEvents(box.reliables); reliables.Add(info); box = new ReliableBox(streamID); currentSize = packetStartSize; } //Add the packet to the box list box.reliables.Add(pInfo); currentSize += pInfo.packet.Length + 1; } //If the last box has more than one packet, keep it if (box.reliables.Count > 1) { info = new ReliableInfo(); info.rid = stream.S2C_Reliable++; //Make sure the box is serialized box.MakeSerialized(this, _handler); info.packet = new Reliable(box, info.rid, streamID); info.consolidateEvents(box.reliables); reliables.Add(info); } else if (box.reliables.Count == 1) { //If it's only one packet, we don't need the box info = new ReliableInfo(); info.rid = stream.S2C_Reliable++; info.packet = new Reliable(box.reliables[0].packet, info.rid, streamID); info.consolidateEvents(box.reliables[0]); reliables.Add(info); } //Boxed them all return(reliables); }
/// <summary> /// Merges all events from a reliable infos into the class /// </summary> public void consolidateEvents(ReliableInfo reliable) { Completed += reliable.Completed; }