diff options
author | Melanie Thielker | 2008-07-22 17:58:42 +0000 |
---|---|---|
committer | Melanie Thielker | 2008-07-22 17:58:42 +0000 |
commit | f112cebde2c1bc06108839acac82bc8addd7c506 (patch) | |
tree | 7f1e7fabf2fec74171d5982f09d847b47e20d7ca /OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs | |
parent | * refactor: move new inventory service call by user server to OGS1 with all t... (diff) | |
download | opensim-SC_OLD-f112cebde2c1bc06108839acac82bc8addd7c506.zip opensim-SC_OLD-f112cebde2c1bc06108839acac82bc8addd7c506.tar.gz opensim-SC_OLD-f112cebde2c1bc06108839acac82bc8addd7c506.tar.bz2 opensim-SC_OLD-f112cebde2c1bc06108839acac82bc8addd7c506.tar.xz |
Refactor the packet scheduling out of ClientView. Add intelligent
resending, timeouts, packet discarding. Add notification event for
packet discarding. Add priority scheduling for packet queues.
Add outgoing duplicate detection facility. Correct packet sequencing.
Make provisions for automatic server side throttle adjustments (comes
in next installment)
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs | 692 |
1 files changed, 692 insertions, 0 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs new file mode 100644 index 0000000..1ec375f --- /dev/null +++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs | |||
@@ -0,0 +1,692 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Net; | ||
32 | using System.Net.Sockets; | ||
33 | using System.Timers; | ||
34 | using libsecondlife; | ||
35 | using libsecondlife.Packets; | ||
36 | using Timer = System.Timers.Timer; | ||
37 | using OpenSim.Framework; | ||
38 | |||
39 | namespace OpenSim.Region.ClientStack.LindenUDP | ||
40 | { | ||
41 | public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes); | ||
42 | public delegate void PacketDrop(Packet pack, Object id); | ||
43 | public delegate bool SynchronizeClientHandler(IScene scene, Packet packet, LLUUID agentID, ThrottleOutPacketType throttlePacketType); | ||
44 | |||
45 | public interface IPacketHandler | ||
46 | { | ||
47 | event PacketStats OnPacketStats; | ||
48 | event PacketDrop OnPacketDrop; | ||
49 | SynchronizeClientHandler SynchronizeClient { set; } | ||
50 | |||
51 | int PacketsReceived { get; } | ||
52 | int PacketsReceivedReported { get; } | ||
53 | uint SilenceLimit { get; set; } | ||
54 | uint DiscardTimeout { get; set; } | ||
55 | uint ResendTimeout { get; set; } | ||
56 | |||
57 | void InPacket(Packet packet); | ||
58 | void ProcessInPacket(Packet packet); | ||
59 | void OutPacket(Packet NewPack, | ||
60 | ThrottleOutPacketType throttlePacketType); | ||
61 | void OutPacket(Packet NewPack, | ||
62 | ThrottleOutPacketType throttlePacketType, Object id); | ||
63 | LLPacketQueue PacketQueue { get; } | ||
64 | void Stop(); | ||
65 | void Flush(); | ||
66 | void Clear(); | ||
67 | ClientInfo GetClientInfo(); | ||
68 | void SetClientInfo(ClientInfo info); | ||
69 | void AddImportantPacket(PacketType type); | ||
70 | void RemoveImportantPacket(PacketType type); | ||
71 | } | ||
72 | |||
73 | public class LLPacketHandler : IPacketHandler | ||
74 | { | ||
75 | // Packet queues | ||
76 | // | ||
77 | LLPacketQueue m_PacketQueue; | ||
78 | |||
79 | public LLPacketQueue PacketQueue | ||
80 | { | ||
81 | get { return m_PacketQueue; } | ||
82 | } | ||
83 | |||
84 | // Timer to run stats and acks on | ||
85 | // | ||
86 | private Timer m_AckTimer = new Timer(250); | ||
87 | |||
88 | // A list of the packets we haven't acked yet | ||
89 | // | ||
90 | private Dictionary<uint,uint> m_PendingAcks = new Dictionary<uint,uint>(); | ||
91 | // Dictionary of the packets that need acks from the client. | ||
92 | // | ||
93 | private class AckData | ||
94 | { | ||
95 | public AckData(Packet packet, Object identifier) | ||
96 | { | ||
97 | Packet = packet; | ||
98 | Identifier = identifier; | ||
99 | } | ||
100 | |||
101 | public Packet Packet; | ||
102 | public Object Identifier; | ||
103 | } | ||
104 | private Dictionary<uint, AckData> m_NeedAck = | ||
105 | new Dictionary<uint, AckData>(); | ||
106 | |||
107 | private uint m_ResendTimeout = 1000; | ||
108 | |||
109 | public uint ResendTimeout | ||
110 | { | ||
111 | get { return m_ResendTimeout; } | ||
112 | set { m_ResendTimeout = value; } | ||
113 | } | ||
114 | |||
115 | private uint m_DiscardTimeout = 8000; | ||
116 | |||
117 | public uint DiscardTimeout | ||
118 | { | ||
119 | get { return m_DiscardTimeout; } | ||
120 | set { m_DiscardTimeout = value; } | ||
121 | } | ||
122 | |||
123 | private uint m_SilenceLimit = 250; | ||
124 | |||
125 | public uint SilenceLimit | ||
126 | { | ||
127 | get { return m_SilenceLimit; } | ||
128 | set { m_SilenceLimit = value; } | ||
129 | } | ||
130 | |||
131 | private int m_LastAck = 0; | ||
132 | |||
133 | // Track duplicated packets. This uses a Dictionary. Both insertion | ||
134 | // and lookup are common operations and need to take advantage of | ||
135 | // the hashing. Expiration is less common and can be allowed the | ||
136 | // time for a linear scan. | ||
137 | // | ||
138 | private Dictionary<uint, int> m_DupeTracker = | ||
139 | new Dictionary<uint, int>(); | ||
140 | private uint m_DupeTrackerWindow = 30; | ||
141 | |||
142 | // Values for the SimStatsReporter | ||
143 | // | ||
144 | private int m_PacketsReceived = 0; | ||
145 | private int m_PacketsReceivedReported = 0; | ||
146 | private int m_PacketsSent = 0; | ||
147 | private int m_PacketsSentReported = 0; | ||
148 | private int m_UnackedBytes = 0; | ||
149 | |||
150 | public int PacketsReceived | ||
151 | { | ||
152 | get { return m_PacketsReceived; } | ||
153 | } | ||
154 | |||
155 | public int PacketsReceivedReported | ||
156 | { | ||
157 | get { return m_PacketsReceivedReported; } | ||
158 | } | ||
159 | |||
160 | // The client we are working for | ||
161 | // | ||
162 | private IClientAPI m_Client; | ||
163 | |||
164 | // Some events | ||
165 | // | ||
166 | public event PacketStats OnPacketStats; | ||
167 | public event PacketDrop OnPacketDrop; | ||
168 | |||
169 | private SynchronizeClientHandler m_SynchronizeClient = null; | ||
170 | |||
171 | public SynchronizeClientHandler SynchronizeClient | ||
172 | { | ||
173 | set { m_SynchronizeClient = value; } | ||
174 | } | ||
175 | |||
176 | // Packet sequencing | ||
177 | // | ||
178 | private uint m_Sequence = 0; | ||
179 | private object m_SequenceLock = new object(); | ||
180 | private const int MAX_SEQUENCE = 0xFFFFFF; | ||
181 | |||
182 | List<PacketType> m_ImportantPackets = new List<PacketType>(); | ||
183 | |||
184 | //////////////////////////////////////////////////////////////////// | ||
185 | |||
186 | // Constructors | ||
187 | // | ||
188 | public LLPacketHandler(IClientAPI client) | ||
189 | { | ||
190 | m_Client = client; | ||
191 | |||
192 | m_PacketQueue = new LLPacketQueue(client.AgentId); | ||
193 | |||
194 | m_AckTimer.Elapsed += AckTimerElapsed; | ||
195 | m_AckTimer.Start(); | ||
196 | } | ||
197 | |||
198 | public void Stop() | ||
199 | { | ||
200 | m_AckTimer.Stop(); | ||
201 | |||
202 | m_PacketQueue.Enqueue(null); | ||
203 | } | ||
204 | |||
205 | // Send one packet. This actually doesn't send anything, it queues | ||
206 | // it. Designed to be fire-and-forget, but there is an optional | ||
207 | // notifier. | ||
208 | // | ||
209 | public void OutPacket( | ||
210 | Packet packet, ThrottleOutPacketType throttlePacketType) | ||
211 | { | ||
212 | OutPacket(packet, throttlePacketType, null); | ||
213 | } | ||
214 | |||
215 | public void OutPacket( | ||
216 | Packet packet, ThrottleOutPacketType throttlePacketType, | ||
217 | Object id) | ||
218 | { | ||
219 | // Call the load balancer's hook. If this is not active here | ||
220 | // we defer to the sim server this client is actually connected | ||
221 | // to. Packet drop notifies will not be triggered in this | ||
222 | // configuration! | ||
223 | // | ||
224 | if ((m_SynchronizeClient != null) && (!m_Client.IsActive)) | ||
225 | { | ||
226 | if (m_SynchronizeClient(m_Client.Scene, packet, | ||
227 | m_Client.AgentId, throttlePacketType)) | ||
228 | return; | ||
229 | } | ||
230 | |||
231 | packet.Header.Sequence = NextPacketSequenceNumber(); | ||
232 | |||
233 | lock(m_NeedAck) | ||
234 | { | ||
235 | DropResend(id); | ||
236 | |||
237 | QueuePacket(packet, throttlePacketType, id); | ||
238 | |||
239 | // We want to see that packet arrive if it's reliable | ||
240 | if(packet.Header.Reliable) | ||
241 | { | ||
242 | m_UnackedBytes += packet.ToBytes().Length; | ||
243 | m_NeedAck[packet.Header.Sequence] = new AckData(packet, id); | ||
244 | } | ||
245 | } | ||
246 | } | ||
247 | |||
248 | private void QueuePacket( | ||
249 | Packet packet, ThrottleOutPacketType throttlePacketType, | ||
250 | Object id) | ||
251 | { | ||
252 | // Add acks to outgoing packets | ||
253 | // | ||
254 | lock(m_PendingAcks) | ||
255 | { | ||
256 | if(m_PendingAcks.Count > 0) | ||
257 | { | ||
258 | int count = m_PendingAcks.Count; | ||
259 | if(count > 10) | ||
260 | count = 10; | ||
261 | packet.Header.AckList = new uint[count]; | ||
262 | |||
263 | int i = 0; | ||
264 | |||
265 | foreach (uint ack in new List<uint>(m_PendingAcks.Keys)) | ||
266 | { | ||
267 | packet.Header.AckList[i] = ack; | ||
268 | i++; | ||
269 | m_PendingAcks.Remove(ack); | ||
270 | if (i >= 10) // That is how much space there is | ||
271 | break; | ||
272 | } | ||
273 | } | ||
274 | } | ||
275 | |||
276 | packet.TickCount = System.Environment.TickCount; | ||
277 | |||
278 | LLQueItem item = new LLQueItem(); | ||
279 | item.Packet = packet; | ||
280 | item.Incoming = false; | ||
281 | item.throttleType = throttlePacketType; | ||
282 | item.Identifier = id; | ||
283 | |||
284 | m_PacketQueue.Enqueue(item); | ||
285 | m_PacketsSent++; | ||
286 | } | ||
287 | |||
288 | private void ResendUnacked() | ||
289 | { | ||
290 | int now = System.Environment.TickCount; | ||
291 | int lastAck = m_LastAck; | ||
292 | |||
293 | // Unless we have received at least one ack, don't bother resending | ||
294 | // anything. There may not be a client there, don't clog up the | ||
295 | // pipes. | ||
296 | // | ||
297 | if(lastAck == 0) | ||
298 | return; | ||
299 | |||
300 | lock (m_NeedAck) | ||
301 | { | ||
302 | // Nothing to do | ||
303 | // | ||
304 | if(m_NeedAck.Count == 0) | ||
305 | return; | ||
306 | |||
307 | // If we have seen no acks in <SilenceLimit> s but are | ||
308 | // waiting for acks, then there may be no one listening. | ||
309 | // No need to resend anything. Keep it until it gets stale, | ||
310 | // then it will be dropped. | ||
311 | // | ||
312 | if (((now - lastAck) > m_SilenceLimit) && | ||
313 | m_NeedAck.Count > 0) | ||
314 | { | ||
315 | return; | ||
316 | } | ||
317 | |||
318 | foreach (AckData data in new List<AckData>(m_NeedAck.Values)) | ||
319 | { | ||
320 | Packet packet = data.Packet; | ||
321 | |||
322 | // Packets this old get resent | ||
323 | // | ||
324 | if ((now - packet.TickCount) > m_ResendTimeout) | ||
325 | { | ||
326 | // Resend the packet. Set the packet's tick count to | ||
327 | // now, and keep it marked as resent. | ||
328 | // | ||
329 | packet.Header.Resent = true; | ||
330 | QueuePacket(packet, ThrottleOutPacketType.Resend, | ||
331 | data.Identifier); | ||
332 | } | ||
333 | |||
334 | // The discard logic | ||
335 | // If the packet is in the queue for <DiscardTimeout> s | ||
336 | // without having been processed, then we have clogged | ||
337 | // pipes. Most likely, the client is gone | ||
338 | // Drop the packets | ||
339 | // | ||
340 | if ((now - packet.TickCount) > m_DiscardTimeout) | ||
341 | { | ||
342 | if(!m_ImportantPackets.Contains(packet.Type)) | ||
343 | m_NeedAck.Remove(packet.Header.Sequence); | ||
344 | |||
345 | TriggerOnPacketDrop(packet, data.Identifier); | ||
346 | |||
347 | continue; | ||
348 | } | ||
349 | } | ||
350 | } | ||
351 | } | ||
352 | |||
353 | // Send the pending packet acks to the client | ||
354 | // Will send blocks of acks for up to 250 packets | ||
355 | // | ||
356 | private void SendAcks() | ||
357 | { | ||
358 | lock (m_PendingAcks) | ||
359 | { | ||
360 | if (m_PendingAcks.Count == 0) | ||
361 | return; | ||
362 | |||
363 | PacketAckPacket acks = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck); | ||
364 | |||
365 | // The case of equality is more common than one might think, | ||
366 | // because this function will be called unconditionally when | ||
367 | // the counter reaches 250. So there is a good chance another | ||
368 | // packet with 250 blocks exists. | ||
369 | // | ||
370 | if(acks.Packets == null || | ||
371 | acks.Packets.Length != m_PendingAcks.Count) | ||
372 | acks.Packets = new PacketAckPacket.PacketsBlock[m_PendingAcks.Count]; | ||
373 | int i = 0; | ||
374 | foreach (uint ack in new List<uint>(m_PendingAcks.Keys)) | ||
375 | { | ||
376 | acks.Packets[i] = new PacketAckPacket.PacketsBlock(); | ||
377 | acks.Packets[i].ID = ack; | ||
378 | |||
379 | m_PendingAcks.Remove(ack); | ||
380 | i++; | ||
381 | } | ||
382 | |||
383 | acks.Header.Reliable = false; | ||
384 | OutPacket(acks, ThrottleOutPacketType.Unknown); | ||
385 | } | ||
386 | } | ||
387 | |||
388 | // Queue a packet ack. It will be sent either after 250 acks are | ||
389 | // queued, or when the timer fires. | ||
390 | // | ||
391 | private void AckPacket(Packet packet) | ||
392 | { | ||
393 | lock (m_PendingAcks) | ||
394 | { | ||
395 | if(m_PendingAcks.Count < 250) | ||
396 | { | ||
397 | if(!m_PendingAcks.ContainsKey(packet.Header.Sequence)) | ||
398 | m_PendingAcks.Add(packet.Header.Sequence, | ||
399 | packet.Header.Sequence); | ||
400 | return; | ||
401 | } | ||
402 | } | ||
403 | |||
404 | SendAcks(); | ||
405 | |||
406 | lock (m_PendingAcks) | ||
407 | { | ||
408 | // If this is still full we have a truly exceptional | ||
409 | // condition (means, can't happen) | ||
410 | // | ||
411 | if(m_PendingAcks.Count < 250) | ||
412 | { | ||
413 | if(!m_PendingAcks.ContainsKey(packet.Header.Sequence)) | ||
414 | m_PendingAcks.Add(packet.Header.Sequence, | ||
415 | packet.Header.Sequence); | ||
416 | return; | ||
417 | } | ||
418 | } | ||
419 | } | ||
420 | |||
421 | // When the timer elapses, send the pending acks, trigger resends | ||
422 | // and report all the stats. | ||
423 | // | ||
424 | private void AckTimerElapsed(object sender, ElapsedEventArgs ea) | ||
425 | { | ||
426 | SendAcks(); | ||
427 | ResendUnacked(); | ||
428 | SendPacketStats(); | ||
429 | } | ||
430 | |||
431 | // Push out pachet counts for the sim status reporter | ||
432 | // | ||
433 | private void SendPacketStats() | ||
434 | { | ||
435 | PacketStats handlerPacketStats = OnPacketStats; | ||
436 | if (handlerPacketStats != null) | ||
437 | { | ||
438 | handlerPacketStats( | ||
439 | m_PacketsReceived - m_PacketsReceivedReported, | ||
440 | m_PacketsSent - m_PacketsSentReported, | ||
441 | m_UnackedBytes); | ||
442 | |||
443 | m_PacketsReceivedReported = m_PacketsReceived; | ||
444 | m_PacketsSentReported = m_PacketsSent; | ||
445 | } | ||
446 | } | ||
447 | |||
448 | // We can't keep an unlimited record of dupes. This will prune the | ||
449 | // dictionary by age. | ||
450 | // | ||
451 | private void PruneDupeTracker() | ||
452 | { | ||
453 | lock (m_DupeTracker) | ||
454 | { | ||
455 | Dictionary<uint, int> packs = | ||
456 | new Dictionary<uint, int>(m_DupeTracker); | ||
457 | |||
458 | foreach (uint pack in packs.Keys) | ||
459 | { | ||
460 | if(Util.UnixTimeSinceEpoch() - m_DupeTracker[pack] > | ||
461 | m_DupeTrackerWindow) | ||
462 | m_DupeTracker.Remove(pack); | ||
463 | } | ||
464 | } | ||
465 | } | ||
466 | |||
467 | public void InPacket(Packet packet) | ||
468 | { | ||
469 | if(packet == null) | ||
470 | return; | ||
471 | |||
472 | // If this client is on another partial instance, no need | ||
473 | // to handle packets | ||
474 | // | ||
475 | if(!m_Client.IsActive && packet.Type != PacketType.LogoutRequest) | ||
476 | { | ||
477 | PacketPool.Instance.ReturnPacket(packet); | ||
478 | return; | ||
479 | } | ||
480 | |||
481 | // Any packet can have some packet acks in the header. | ||
482 | // Process them here | ||
483 | // | ||
484 | if(packet.Header.AppendedAcks) | ||
485 | { | ||
486 | foreach(uint id in packet.Header.AckList) | ||
487 | { | ||
488 | ProcessAck(id); | ||
489 | } | ||
490 | } | ||
491 | |||
492 | // When too many acks are needed to be sent, the client sends | ||
493 | // a packet consisting of acks only | ||
494 | // | ||
495 | if(packet.Type == PacketType.PacketAck) | ||
496 | { | ||
497 | PacketAckPacket ackPacket = (PacketAckPacket)packet; | ||
498 | |||
499 | foreach (PacketAckPacket.PacketsBlock block in | ||
500 | ackPacket.Packets) | ||
501 | { | ||
502 | ProcessAck(block.ID); | ||
503 | } | ||
504 | |||
505 | PacketPool.Instance.ReturnPacket(packet); | ||
506 | return; | ||
507 | } | ||
508 | else if(packet.Type == PacketType.StartPingCheck) | ||
509 | { | ||
510 | StartPingCheckPacket startPing = (StartPingCheckPacket)packet; | ||
511 | CompletePingCheckPacket endPing = (CompletePingCheckPacket)PacketPool.Instance.GetPacket(PacketType.CompletePingCheck); | ||
512 | |||
513 | endPing.PingID.PingID = startPing.PingID.PingID; | ||
514 | OutPacket(endPing, ThrottleOutPacketType.Task); | ||
515 | } | ||
516 | else | ||
517 | { | ||
518 | LLQueItem item = new LLQueItem(); | ||
519 | item.Packet = packet; | ||
520 | item.Incoming = true; | ||
521 | m_PacketQueue.Enqueue(item); | ||
522 | } | ||
523 | } | ||
524 | |||
525 | public void ProcessInPacket(Packet packet) | ||
526 | { | ||
527 | // Always ack the packet! | ||
528 | // | ||
529 | AckPacket(packet); | ||
530 | |||
531 | if (packet.Type != PacketType.AgentUpdate) | ||
532 | m_PacketsReceived++; | ||
533 | |||
534 | // Check for duplicate packets.. packets that the client is | ||
535 | // resending because it didn't receive our ack | ||
536 | // | ||
537 | lock (m_DupeTracker) | ||
538 | { | ||
539 | if (m_DupeTracker.ContainsKey(packet.Header.Sequence)) | ||
540 | return; | ||
541 | |||
542 | m_DupeTracker.Add(packet.Header.Sequence, | ||
543 | Util.UnixTimeSinceEpoch()); | ||
544 | } | ||
545 | |||
546 | m_Client.ProcessInPacket(packet); | ||
547 | } | ||
548 | |||
549 | public void Flush() | ||
550 | { | ||
551 | m_PacketQueue.Flush(); | ||
552 | } | ||
553 | |||
554 | public void Clear() | ||
555 | { | ||
556 | m_NeedAck.Clear(); | ||
557 | m_PendingAcks.Clear(); | ||
558 | m_Sequence += 1000000; | ||
559 | } | ||
560 | |||
561 | private void ProcessAck(uint id) | ||
562 | { | ||
563 | AckData data; | ||
564 | Packet packet; | ||
565 | |||
566 | lock(m_NeedAck) | ||
567 | { | ||
568 | if(!m_NeedAck.TryGetValue(id, out data)) | ||
569 | return; | ||
570 | |||
571 | packet = data.Packet; | ||
572 | |||
573 | m_NeedAck.Remove(id); | ||
574 | m_UnackedBytes -= packet.ToBytes().Length; | ||
575 | |||
576 | m_LastAck = System.Environment.TickCount; | ||
577 | } | ||
578 | } | ||
579 | |||
580 | // Allocate packet sequence numbers in a threadsave manner | ||
581 | // | ||
582 | protected uint NextPacketSequenceNumber() | ||
583 | { | ||
584 | // Set the sequence number | ||
585 | uint seq = 1; | ||
586 | lock (m_SequenceLock) | ||
587 | { | ||
588 | if (m_Sequence >= MAX_SEQUENCE) | ||
589 | { | ||
590 | m_Sequence = 1; | ||
591 | } | ||
592 | else | ||
593 | { | ||
594 | m_Sequence++; | ||
595 | } | ||
596 | seq = m_Sequence; | ||
597 | } | ||
598 | return seq; | ||
599 | } | ||
600 | |||
601 | public ClientInfo GetClientInfo() | ||
602 | { | ||
603 | ClientInfo info = new ClientInfo(); | ||
604 | info.pendingAcks = m_PendingAcks; | ||
605 | info.needAck = new Dictionary<uint, byte[]>(); | ||
606 | |||
607 | lock (m_NeedAck) | ||
608 | { | ||
609 | foreach (uint key in m_NeedAck.Keys) | ||
610 | info.needAck.Add(key, m_NeedAck[key].Packet.ToBytes()); | ||
611 | } | ||
612 | |||
613 | LLQueItem[] queitems = m_PacketQueue.GetQueueArray(); | ||
614 | |||
615 | for (int i = 0; i < queitems.Length; i++) | ||
616 | { | ||
617 | if (queitems[i].Incoming == false) | ||
618 | info.out_packets.Add(queitems[i].Packet.ToBytes()); | ||
619 | } | ||
620 | |||
621 | info.sequence = m_Sequence; | ||
622 | |||
623 | return info; | ||
624 | } | ||
625 | |||
626 | public void SetClientInfo(ClientInfo info) | ||
627 | { | ||
628 | m_PendingAcks = info.pendingAcks; | ||
629 | m_NeedAck = new Dictionary<uint, AckData>(); | ||
630 | |||
631 | Packet packet = null; | ||
632 | int packetEnd = 0; | ||
633 | byte[] zero = new byte[3000]; | ||
634 | |||
635 | foreach (uint key in info.needAck.Keys) | ||
636 | { | ||
637 | byte[] buff = info.needAck[key]; | ||
638 | packetEnd = buff.Length - 1; | ||
639 | |||
640 | try | ||
641 | { | ||
642 | packet = PacketPool.Instance.GetPacket(buff, ref packetEnd, zero); | ||
643 | } | ||
644 | catch (Exception) | ||
645 | { | ||
646 | } | ||
647 | |||
648 | m_NeedAck.Add(key, new AckData(packet, null)); | ||
649 | } | ||
650 | |||
651 | m_Sequence = info.sequence; | ||
652 | } | ||
653 | |||
654 | public void AddImportantPacket(PacketType type) | ||
655 | { | ||
656 | if(m_ImportantPackets.Contains(type)) | ||
657 | return; | ||
658 | |||
659 | m_ImportantPackets.Add(type); | ||
660 | } | ||
661 | |||
662 | public void RemoveImportantPacket(PacketType type) | ||
663 | { | ||
664 | if(!m_ImportantPackets.Contains(type)) | ||
665 | return; | ||
666 | |||
667 | m_ImportantPackets.Remove(type); | ||
668 | } | ||
669 | |||
670 | private void DropResend(Object id) | ||
671 | { | ||
672 | foreach (AckData data in new List<AckData>(m_NeedAck.Values)) | ||
673 | { | ||
674 | if(data.Identifier != null && data.Identifier == id) | ||
675 | { | ||
676 | m_NeedAck.Remove(data.Packet.Header.Sequence); | ||
677 | return; | ||
678 | } | ||
679 | } | ||
680 | } | ||
681 | |||
682 | private void TriggerOnPacketDrop(Packet packet, Object id) | ||
683 | { | ||
684 | PacketDrop handlerPacketDrop = OnPacketDrop; | ||
685 | |||
686 | if(handlerPacketDrop == null) | ||
687 | return; | ||
688 | |||
689 | handlerPacketDrop(packet, id); | ||
690 | } | ||
691 | } | ||
692 | } | ||