diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs | 870 |
1 files changed, 0 insertions, 870 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs deleted file mode 100644 index e98a360..0000000 --- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs +++ /dev/null | |||
@@ -1,870 +0,0 @@ | |||
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 OpenSimulator 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.Reflection; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Net.Sockets; | ||
32 | using System.Threading; | ||
33 | using System.Timers; | ||
34 | using OpenMetaverse; | ||
35 | using OpenMetaverse.Packets; | ||
36 | using log4net; | ||
37 | using OpenSim.Framework; | ||
38 | using Timer=System.Timers.Timer; | ||
39 | |||
40 | namespace OpenSim.Region.ClientStack.LindenUDP | ||
41 | { | ||
42 | public class LLPacketHandler : ILLPacketHandler | ||
43 | { | ||
44 | private static readonly ILog m_log | ||
45 | = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
46 | |||
47 | //private int m_resentCount; | ||
48 | |||
49 | // Packet queues | ||
50 | // | ||
51 | LLPacketQueue m_PacketQueue; | ||
52 | |||
53 | public LLPacketQueue PacketQueue | ||
54 | { | ||
55 | get { return m_PacketQueue; } | ||
56 | } | ||
57 | |||
58 | // Timer to run stats and acks on | ||
59 | // | ||
60 | private Timer m_AckTimer = new Timer(250); | ||
61 | |||
62 | // A list of the packets we haven't acked yet | ||
63 | // | ||
64 | private List<uint> m_PendingAcks = new List<uint>(); | ||
65 | private Dictionary<uint, uint> m_PendingAcksMap = new Dictionary<uint, uint>(); | ||
66 | |||
67 | private Dictionary<uint, LLQueItem> m_NeedAck = | ||
68 | new Dictionary<uint, LLQueItem>(); | ||
69 | |||
70 | /// <summary> | ||
71 | /// The number of milliseconds that can pass before a packet that needs an ack is resent. | ||
72 | /// </param> | ||
73 | private uint m_ResendTimeout = 4000; | ||
74 | |||
75 | public uint ResendTimeout | ||
76 | { | ||
77 | get { return m_ResendTimeout; } | ||
78 | set { m_ResendTimeout = value; } | ||
79 | } | ||
80 | |||
81 | private int m_MaxReliableResends = 3; | ||
82 | |||
83 | public int MaxReliableResends | ||
84 | { | ||
85 | get { return m_MaxReliableResends; } | ||
86 | set { m_MaxReliableResends = value; } | ||
87 | } | ||
88 | |||
89 | // Track duplicated packets. This uses a Dictionary. Both insertion | ||
90 | // and lookup are common operations and need to take advantage of | ||
91 | // the hashing. Expiration is less common and can be allowed the | ||
92 | // time for a linear scan. | ||
93 | // | ||
94 | private List<uint> m_alreadySeenList = new List<uint>(); | ||
95 | private Dictionary<uint, int>m_alreadySeenTracker = new Dictionary<uint, int>(); | ||
96 | private int m_alreadySeenWindow = 30000; | ||
97 | private int m_lastAlreadySeenCheck = Environment.TickCount & Int32.MaxValue; | ||
98 | |||
99 | // private Dictionary<uint, int> m_DupeTracker = | ||
100 | // new Dictionary<uint, int>(); | ||
101 | // private uint m_DupeTrackerWindow = 30; | ||
102 | // private int m_DupeTrackerLastCheck = Environment.TickCount; | ||
103 | |||
104 | // Values for the SimStatsReporter | ||
105 | // | ||
106 | private int m_PacketsReceived = 0; | ||
107 | private int m_PacketsReceivedReported = 0; | ||
108 | private int m_PacketsSent = 0; | ||
109 | private int m_PacketsSentReported = 0; | ||
110 | private int m_UnackedBytes = 0; | ||
111 | |||
112 | private int m_LastResend = 0; | ||
113 | |||
114 | public int PacketsReceived | ||
115 | { | ||
116 | get { return m_PacketsReceived; } | ||
117 | } | ||
118 | |||
119 | public int PacketsReceivedReported | ||
120 | { | ||
121 | get { return m_PacketsReceivedReported; } | ||
122 | } | ||
123 | |||
124 | // The client we are working for | ||
125 | // | ||
126 | private IClientAPI m_Client; | ||
127 | |||
128 | // Some events | ||
129 | // | ||
130 | public event PacketStats OnPacketStats; | ||
131 | public event PacketDrop OnPacketDrop; | ||
132 | public event QueueEmpty OnQueueEmpty; | ||
133 | |||
134 | |||
135 | //private SynchronizeClientHandler m_SynchronizeClient = null; | ||
136 | |||
137 | public SynchronizeClientHandler SynchronizeClient | ||
138 | { | ||
139 | set { /* m_SynchronizeClient = value; */ } | ||
140 | } | ||
141 | |||
142 | // Packet sequencing | ||
143 | // | ||
144 | private uint m_Sequence = 0; | ||
145 | private object m_SequenceLock = new object(); | ||
146 | private const int MAX_SEQUENCE = 0xFFFFFF; | ||
147 | |||
148 | // Packet dropping | ||
149 | // | ||
150 | List<PacketType> m_ImportantPackets = new List<PacketType>(); | ||
151 | private bool m_ReliableIsImportant = false; | ||
152 | |||
153 | public bool ReliableIsImportant | ||
154 | { | ||
155 | get { return m_ReliableIsImportant; } | ||
156 | set { m_ReliableIsImportant = value; } | ||
157 | } | ||
158 | |||
159 | private int m_DropSafeTimeout; | ||
160 | |||
161 | LLPacketServer m_PacketServer; | ||
162 | private byte[] m_ZeroOutBuffer = new byte[4096]; | ||
163 | |||
164 | //////////////////////////////////////////////////////////////////// | ||
165 | |||
166 | // Constructors | ||
167 | // | ||
168 | public LLPacketHandler(IClientAPI client, LLPacketServer server, ClientStackUserSettings userSettings) | ||
169 | { | ||
170 | m_Client = client; | ||
171 | m_PacketServer = server; | ||
172 | m_DropSafeTimeout = Environment.TickCount + 15000; | ||
173 | |||
174 | m_PacketQueue = new LLPacketQueue(client.AgentId, userSettings); | ||
175 | |||
176 | m_PacketQueue.OnQueueEmpty += TriggerOnQueueEmpty; | ||
177 | |||
178 | m_AckTimer.Elapsed += AckTimerElapsed; | ||
179 | m_AckTimer.Start(); | ||
180 | } | ||
181 | |||
182 | public void Dispose() | ||
183 | { | ||
184 | m_AckTimer.Stop(); | ||
185 | m_AckTimer.Close(); | ||
186 | |||
187 | m_PacketQueue.Enqueue(null); | ||
188 | m_PacketQueue.Close(); | ||
189 | m_Client = null; | ||
190 | } | ||
191 | |||
192 | // Send one packet. This actually doesn't send anything, it queues | ||
193 | // it. Designed to be fire-and-forget, but there is an optional | ||
194 | // notifier. | ||
195 | // | ||
196 | public void OutPacket( | ||
197 | Packet packet, ThrottleOutPacketType throttlePacketType) | ||
198 | { | ||
199 | OutPacket(packet, throttlePacketType, null); | ||
200 | } | ||
201 | |||
202 | public void OutPacket( | ||
203 | Packet packet, ThrottleOutPacketType throttlePacketType, | ||
204 | Object id) | ||
205 | { | ||
206 | // Call the load balancer's hook. If this is not active here | ||
207 | // we defer to the sim server this client is actually connected | ||
208 | // to. Packet drop notifies will not be triggered in this | ||
209 | // configuration! | ||
210 | // | ||
211 | |||
212 | packet.Header.Sequence = 0; | ||
213 | |||
214 | lock (m_NeedAck) | ||
215 | { | ||
216 | DropResend(id); | ||
217 | |||
218 | AddAcks(ref packet); | ||
219 | QueuePacket(packet, throttlePacketType, id); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | private void AddAcks(ref Packet packet) | ||
224 | { | ||
225 | // These packet types have shown to have issues with | ||
226 | // acks being appended to the payload, just don't send | ||
227 | // any with them until libsl is fixed. | ||
228 | // | ||
229 | if (packet is ViewerEffectPacket) | ||
230 | return; | ||
231 | if (packet is SimStatsPacket) | ||
232 | return; | ||
233 | |||
234 | // Add acks to outgoing packets | ||
235 | // | ||
236 | if (m_PendingAcks.Count > 0) | ||
237 | { | ||
238 | int count = m_PendingAcks.Count; | ||
239 | if (count > 10) | ||
240 | count = 10; | ||
241 | packet.Header.AckList = new uint[count]; | ||
242 | packet.Header.AppendedAcks = true; | ||
243 | |||
244 | for (int i = 0; i < count; i++) | ||
245 | { | ||
246 | packet.Header.AckList[i] = m_PendingAcks[i]; | ||
247 | m_PendingAcksMap.Remove(m_PendingAcks[i]); | ||
248 | } | ||
249 | m_PendingAcks.RemoveRange(0, count); | ||
250 | } | ||
251 | } | ||
252 | |||
253 | private void QueuePacket( | ||
254 | Packet packet, ThrottleOutPacketType throttlePacketType, | ||
255 | Object id) | ||
256 | { | ||
257 | LLQueItem item = new LLQueItem(); | ||
258 | item.Packet = packet; | ||
259 | item.Incoming = false; | ||
260 | item.throttleType = throttlePacketType; | ||
261 | item.TickCount = Environment.TickCount; | ||
262 | item.Identifier = id; | ||
263 | item.Resends = 0; | ||
264 | item.Length = packet.Length; | ||
265 | item.Sequence = packet.Header.Sequence; | ||
266 | |||
267 | m_PacketQueue.Enqueue(item); | ||
268 | m_PacketsSent++; | ||
269 | } | ||
270 | |||
271 | private void ResendUnacked() | ||
272 | { | ||
273 | int now = Environment.TickCount; | ||
274 | |||
275 | int intervalMs = 250; | ||
276 | |||
277 | if (m_LastResend != 0) | ||
278 | intervalMs = now - m_LastResend; | ||
279 | |||
280 | lock (m_NeedAck) | ||
281 | { | ||
282 | if (m_DropSafeTimeout > now || | ||
283 | intervalMs > 500) // We were frozen! | ||
284 | { | ||
285 | foreach (LLQueItem data in m_NeedAck.Values) | ||
286 | { | ||
287 | if (m_DropSafeTimeout > now) | ||
288 | { | ||
289 | m_NeedAck[data.Packet.Header.Sequence].TickCount = now; | ||
290 | } | ||
291 | else | ||
292 | { | ||
293 | m_NeedAck[data.Packet.Header.Sequence].TickCount += intervalMs; | ||
294 | } | ||
295 | } | ||
296 | } | ||
297 | |||
298 | m_LastResend = now; | ||
299 | |||
300 | // Unless we have received at least one ack, don't bother resending | ||
301 | // anything. There may not be a client there, don't clog up the | ||
302 | // pipes. | ||
303 | |||
304 | |||
305 | // Nothing to do | ||
306 | // | ||
307 | if (m_NeedAck.Count == 0) | ||
308 | return; | ||
309 | |||
310 | int resent = 0; | ||
311 | long dueDate = now - m_ResendTimeout; | ||
312 | |||
313 | List<LLQueItem> dropped = new List<LLQueItem>(); | ||
314 | foreach (LLQueItem data in m_NeedAck.Values) | ||
315 | { | ||
316 | Packet packet = data.Packet; | ||
317 | |||
318 | // Packets this old get resent | ||
319 | // | ||
320 | if (data.TickCount < dueDate && data.Sequence != 0 && !m_PacketQueue.Contains(data.Sequence)) | ||
321 | { | ||
322 | if (resent < 20) // Was 20 (= Max 117kbit/sec resends) | ||
323 | { | ||
324 | m_NeedAck[packet.Header.Sequence].Resends++; | ||
325 | |||
326 | // The client needs to be told that a packet is being resent, otherwise it appears to believe | ||
327 | // that it should reset its sequence to that packet number. | ||
328 | packet.Header.Resent = true; | ||
329 | |||
330 | if ((m_NeedAck[packet.Header.Sequence].Resends >= m_MaxReliableResends) && | ||
331 | (!m_ReliableIsImportant)) | ||
332 | { | ||
333 | dropped.Add(data); | ||
334 | continue; | ||
335 | } | ||
336 | |||
337 | m_NeedAck[packet.Header.Sequence].TickCount = Environment.TickCount; | ||
338 | QueuePacket(packet, ThrottleOutPacketType.Resend, data.Identifier); | ||
339 | resent++; | ||
340 | } | ||
341 | else | ||
342 | { | ||
343 | m_NeedAck[packet.Header.Sequence].TickCount += intervalMs; | ||
344 | } | ||
345 | } | ||
346 | } | ||
347 | |||
348 | foreach (LLQueItem data in dropped) | ||
349 | { | ||
350 | m_NeedAck.Remove(data.Packet.Header.Sequence); | ||
351 | TriggerOnPacketDrop(data.Packet, data.Identifier); | ||
352 | m_PacketQueue.Cancel(data.Packet.Header.Sequence); | ||
353 | PacketPool.Instance.ReturnPacket(data.Packet); | ||
354 | } | ||
355 | } | ||
356 | } | ||
357 | |||
358 | // Send the pending packet acks to the client | ||
359 | // Will send blocks of acks for up to 250 packets | ||
360 | // | ||
361 | private void SendAcks() | ||
362 | { | ||
363 | lock (m_NeedAck) | ||
364 | { | ||
365 | if (m_PendingAcks.Count == 0) | ||
366 | return; | ||
367 | |||
368 | PacketAckPacket acks = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck); | ||
369 | |||
370 | // The case of equality is more common than one might think, | ||
371 | // because this function will be called unconditionally when | ||
372 | // the counter reaches 250. So there is a good chance another | ||
373 | // packet with 250 blocks exists. | ||
374 | // | ||
375 | if (acks.Packets == null || | ||
376 | acks.Packets.Length != m_PendingAcks.Count) | ||
377 | acks.Packets = new PacketAckPacket.PacketsBlock[m_PendingAcks.Count]; | ||
378 | |||
379 | for (int i = 0; i < m_PendingAcks.Count; i++) | ||
380 | { | ||
381 | acks.Packets[i] = new PacketAckPacket.PacketsBlock(); | ||
382 | acks.Packets[i].ID = m_PendingAcks[i]; | ||
383 | |||
384 | } | ||
385 | m_PendingAcks.Clear(); | ||
386 | m_PendingAcksMap.Clear(); | ||
387 | |||
388 | acks.Header.Reliable = false; | ||
389 | OutPacket(acks, ThrottleOutPacketType.Unknown); | ||
390 | } | ||
391 | } | ||
392 | |||
393 | // Queue a packet ack. It will be sent either after 250 acks are | ||
394 | // queued, or when the timer fires. | ||
395 | // | ||
396 | private void AckPacket(Packet packet) | ||
397 | { | ||
398 | lock (m_NeedAck) | ||
399 | { | ||
400 | if (m_PendingAcks.Count < 250) | ||
401 | { | ||
402 | if (!m_PendingAcksMap.ContainsKey(packet.Header.Sequence)) | ||
403 | { | ||
404 | m_PendingAcks.Add(packet.Header.Sequence); | ||
405 | m_PendingAcksMap.Add(packet.Header.Sequence, | ||
406 | packet.Header.Sequence); | ||
407 | } | ||
408 | return; | ||
409 | } | ||
410 | } | ||
411 | |||
412 | SendAcks(); | ||
413 | |||
414 | lock (m_NeedAck) | ||
415 | { | ||
416 | // If this is still full we have a truly exceptional | ||
417 | // condition (means, can't happen) | ||
418 | // | ||
419 | if (m_PendingAcks.Count < 250) | ||
420 | { | ||
421 | if (!m_PendingAcksMap.ContainsKey(packet.Header.Sequence)) | ||
422 | { | ||
423 | m_PendingAcks.Add(packet.Header.Sequence); | ||
424 | m_PendingAcksMap.Add(packet.Header.Sequence, | ||
425 | packet.Header.Sequence); | ||
426 | } | ||
427 | return; | ||
428 | } | ||
429 | } | ||
430 | } | ||
431 | |||
432 | // When the timer elapses, send the pending acks, trigger resends | ||
433 | // and report all the stats. | ||
434 | // | ||
435 | private void AckTimerElapsed(object sender, ElapsedEventArgs ea) | ||
436 | { | ||
437 | SendAcks(); | ||
438 | ResendUnacked(); | ||
439 | SendPacketStats(); | ||
440 | } | ||
441 | |||
442 | // Push out pachet counts for the sim status reporter | ||
443 | // | ||
444 | private void SendPacketStats() | ||
445 | { | ||
446 | PacketStats handlerPacketStats = OnPacketStats; | ||
447 | if (handlerPacketStats != null) | ||
448 | { | ||
449 | handlerPacketStats( | ||
450 | m_PacketsReceived - m_PacketsReceivedReported, | ||
451 | m_PacketsSent - m_PacketsSentReported, | ||
452 | m_UnackedBytes); | ||
453 | |||
454 | m_PacketsReceivedReported = m_PacketsReceived; | ||
455 | m_PacketsSentReported = m_PacketsSent; | ||
456 | } | ||
457 | } | ||
458 | |||
459 | // We can't keep an unlimited record of dupes. This will prune the | ||
460 | // dictionary by age. | ||
461 | // | ||
462 | // NOTE: this needs to be called from within lock | ||
463 | // (m_alreadySeenTracker) context! | ||
464 | private void ExpireSeenPackets() | ||
465 | { | ||
466 | if (m_alreadySeenList.Count < 1024) | ||
467 | return; | ||
468 | |||
469 | int ticks = 0; | ||
470 | int tc = Environment.TickCount & Int32.MaxValue; | ||
471 | if (tc >= m_lastAlreadySeenCheck) | ||
472 | ticks = tc - m_lastAlreadySeenCheck; | ||
473 | else | ||
474 | ticks = Int32.MaxValue - m_lastAlreadySeenCheck + tc; | ||
475 | |||
476 | if (ticks < 2000) return; | ||
477 | m_lastAlreadySeenCheck = tc; | ||
478 | |||
479 | // we calculate the drop dead tick count here instead of | ||
480 | // in the loop: any packet with a timestamp before | ||
481 | // dropDeadTC can be expired | ||
482 | int dropDeadTC = tc - m_alreadySeenWindow; | ||
483 | int i = 0; | ||
484 | while (i < m_alreadySeenList.Count && m_alreadySeenTracker[m_alreadySeenList[i]] < dropDeadTC) | ||
485 | { | ||
486 | m_alreadySeenTracker.Remove(m_alreadySeenList[i]); | ||
487 | i++; | ||
488 | } | ||
489 | // if we dropped packet from m_alreadySeenTracker we need | ||
490 | // to drop them from m_alreadySeenList as well, let's do | ||
491 | // that in one go: the list is ordered after all. | ||
492 | if (i > 0) | ||
493 | { | ||
494 | m_alreadySeenList.RemoveRange(0, i); | ||
495 | // m_log.DebugFormat("[CLIENT]: expired {0} packets, {1}:{2} left", i, m_alreadySeenList.Count, m_alreadySeenTracker.Count); | ||
496 | } | ||
497 | } | ||
498 | |||
499 | public void InPacket(Packet packet) | ||
500 | { | ||
501 | if (packet == null) | ||
502 | return; | ||
503 | |||
504 | // When too many acks are needed to be sent, the client sends | ||
505 | // a packet consisting of acks only | ||
506 | // | ||
507 | if (packet.Type == PacketType.PacketAck) | ||
508 | { | ||
509 | PacketAckPacket ackPacket = (PacketAckPacket)packet; | ||
510 | |||
511 | foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets) | ||
512 | { | ||
513 | ProcessAck(block.ID); | ||
514 | } | ||
515 | |||
516 | PacketPool.Instance.ReturnPacket(packet); | ||
517 | return; | ||
518 | } | ||
519 | |||
520 | // Any packet can have some packet acks in the header. | ||
521 | // Process them here | ||
522 | // | ||
523 | if (packet.Header.AppendedAcks) | ||
524 | { | ||
525 | foreach (uint id in packet.Header.AckList) | ||
526 | { | ||
527 | ProcessAck(id); | ||
528 | } | ||
529 | } | ||
530 | |||
531 | // If this client is on another partial instance, no need | ||
532 | // to handle packets | ||
533 | // | ||
534 | if (!m_Client.IsActive && packet.Type != PacketType.LogoutRequest) | ||
535 | { | ||
536 | PacketPool.Instance.ReturnPacket(packet); | ||
537 | return; | ||
538 | } | ||
539 | |||
540 | if (packet.Type == PacketType.StartPingCheck) | ||
541 | { | ||
542 | StartPingCheckPacket startPing = (StartPingCheckPacket)packet; | ||
543 | CompletePingCheckPacket endPing | ||
544 | = (CompletePingCheckPacket)PacketPool.Instance.GetPacket(PacketType.CompletePingCheck); | ||
545 | |||
546 | endPing.PingID.PingID = startPing.PingID.PingID; | ||
547 | OutPacket(endPing, ThrottleOutPacketType.Task); | ||
548 | } | ||
549 | else | ||
550 | { | ||
551 | LLQueItem item = new LLQueItem(); | ||
552 | item.Packet = packet; | ||
553 | item.Incoming = true; | ||
554 | m_PacketQueue.Enqueue(item); | ||
555 | } | ||
556 | } | ||
557 | |||
558 | public void ProcessInPacket(LLQueItem item) | ||
559 | { | ||
560 | Packet packet = item.Packet; | ||
561 | |||
562 | // Always ack the packet! | ||
563 | // | ||
564 | if (packet.Header.Reliable) | ||
565 | AckPacket(packet); | ||
566 | |||
567 | if (packet.Type != PacketType.AgentUpdate) | ||
568 | m_PacketsReceived++; | ||
569 | |||
570 | // Check for duplicate packets.. packets that the client is | ||
571 | // resending because it didn't receive our ack | ||
572 | // | ||
573 | lock (m_alreadySeenTracker) | ||
574 | { | ||
575 | ExpireSeenPackets(); | ||
576 | |||
577 | if (m_alreadySeenTracker.ContainsKey(packet.Header.Sequence)) | ||
578 | return; | ||
579 | |||
580 | m_alreadySeenTracker.Add(packet.Header.Sequence, Environment.TickCount & Int32.MaxValue); | ||
581 | m_alreadySeenList.Add(packet.Header.Sequence); | ||
582 | } | ||
583 | |||
584 | m_Client.ProcessInPacket(packet); | ||
585 | } | ||
586 | |||
587 | public void Flush() | ||
588 | { | ||
589 | m_PacketQueue.Flush(); | ||
590 | m_UnackedBytes = (-1 * m_UnackedBytes); | ||
591 | SendPacketStats(); | ||
592 | } | ||
593 | |||
594 | public void Clear() | ||
595 | { | ||
596 | m_UnackedBytes = (-1 * m_UnackedBytes); | ||
597 | SendPacketStats(); | ||
598 | lock (m_NeedAck) | ||
599 | { | ||
600 | m_NeedAck.Clear(); | ||
601 | m_PendingAcks.Clear(); | ||
602 | m_PendingAcksMap.Clear(); | ||
603 | } | ||
604 | m_Sequence += 1000000; | ||
605 | } | ||
606 | |||
607 | private void ProcessAck(uint id) | ||
608 | { | ||
609 | LLQueItem data; | ||
610 | |||
611 | lock (m_NeedAck) | ||
612 | { | ||
613 | //m_log.DebugFormat("[CLIENT]: In {0} received ack for packet {1}", m_Client.Scene.RegionInfo.ExternalEndPoint.Port, id); | ||
614 | |||
615 | if (!m_NeedAck.TryGetValue(id, out data)) | ||
616 | return; | ||
617 | |||
618 | m_NeedAck.Remove(id); | ||
619 | m_PacketQueue.Cancel(data.Sequence); | ||
620 | PacketPool.Instance.ReturnPacket(data.Packet); | ||
621 | m_UnackedBytes -= data.Length; | ||
622 | } | ||
623 | } | ||
624 | |||
625 | // Allocate packet sequence numbers in a threadsave manner | ||
626 | // | ||
627 | protected uint NextPacketSequenceNumber() | ||
628 | { | ||
629 | // Set the sequence number | ||
630 | uint seq = 1; | ||
631 | lock (m_SequenceLock) | ||
632 | { | ||
633 | if (m_Sequence >= MAX_SEQUENCE) | ||
634 | { | ||
635 | m_Sequence = 1; | ||
636 | } | ||
637 | else | ||
638 | { | ||
639 | m_Sequence++; | ||
640 | } | ||
641 | seq = m_Sequence; | ||
642 | } | ||
643 | return seq; | ||
644 | } | ||
645 | |||
646 | public ClientInfo GetClientInfo() | ||
647 | { | ||
648 | ClientInfo info = new ClientInfo(); | ||
649 | |||
650 | info.pendingAcks = m_PendingAcksMap; | ||
651 | info.needAck = new Dictionary<uint, byte[]>(); | ||
652 | |||
653 | lock (m_NeedAck) | ||
654 | { | ||
655 | foreach (uint key in m_NeedAck.Keys) | ||
656 | info.needAck.Add(key, m_NeedAck[key].Packet.ToBytes()); | ||
657 | } | ||
658 | |||
659 | LLQueItem[] queitems = m_PacketQueue.GetQueueArray(); | ||
660 | |||
661 | for (int i = 0; i < queitems.Length; i++) | ||
662 | { | ||
663 | if (queitems[i].Incoming == false) | ||
664 | info.out_packets.Add(queitems[i].Packet.ToBytes()); | ||
665 | } | ||
666 | |||
667 | info.sequence = m_Sequence; | ||
668 | |||
669 | float multiplier = m_PacketQueue.ThrottleMultiplier; | ||
670 | info.resendThrottle = (int) (m_PacketQueue.ResendThrottle.Throttle / multiplier); | ||
671 | info.landThrottle = (int) (m_PacketQueue.LandThrottle.Throttle / multiplier); | ||
672 | info.windThrottle = (int) (m_PacketQueue.WindThrottle.Throttle / multiplier); | ||
673 | info.cloudThrottle = (int) (m_PacketQueue.CloudThrottle.Throttle / multiplier); | ||
674 | info.taskThrottle = (int) (m_PacketQueue.TaskThrottle.Throttle / multiplier); | ||
675 | info.assetThrottle = (int) (m_PacketQueue.AssetThrottle.Throttle / multiplier); | ||
676 | info.textureThrottle = (int) (m_PacketQueue.TextureThrottle.Throttle / multiplier); | ||
677 | info.totalThrottle = (int) (m_PacketQueue.TotalThrottle.Throttle / multiplier); | ||
678 | |||
679 | return info; | ||
680 | } | ||
681 | |||
682 | public void SetClientInfo(ClientInfo info) | ||
683 | { | ||
684 | m_PendingAcksMap = info.pendingAcks; | ||
685 | m_PendingAcks = new List<uint>(m_PendingAcksMap.Keys); | ||
686 | m_NeedAck = new Dictionary<uint, LLQueItem>(); | ||
687 | |||
688 | Packet packet = null; | ||
689 | int packetEnd = 0; | ||
690 | byte[] zero = new byte[3000]; | ||
691 | |||
692 | foreach (uint key in info.needAck.Keys) | ||
693 | { | ||
694 | byte[] buff = info.needAck[key]; | ||
695 | packetEnd = buff.Length - 1; | ||
696 | |||
697 | try | ||
698 | { | ||
699 | packet = PacketPool.Instance.GetPacket(buff, ref packetEnd, zero); | ||
700 | } | ||
701 | catch (Exception) | ||
702 | { | ||
703 | } | ||
704 | |||
705 | LLQueItem item = new LLQueItem(); | ||
706 | item.Packet = packet; | ||
707 | item.Incoming = false; | ||
708 | item.throttleType = 0; | ||
709 | item.TickCount = Environment.TickCount; | ||
710 | item.Identifier = 0; | ||
711 | item.Resends = 0; | ||
712 | item.Length = packet.Length; | ||
713 | item.Sequence = packet.Header.Sequence; | ||
714 | m_NeedAck.Add(key, item); | ||
715 | } | ||
716 | |||
717 | m_Sequence = info.sequence; | ||
718 | |||
719 | m_PacketQueue.ResendThrottle.Throttle = info.resendThrottle; | ||
720 | m_PacketQueue.LandThrottle.Throttle = info.landThrottle; | ||
721 | m_PacketQueue.WindThrottle.Throttle = info.windThrottle; | ||
722 | m_PacketQueue.CloudThrottle.Throttle = info.cloudThrottle; | ||
723 | m_PacketQueue.TaskThrottle.Throttle = info.taskThrottle; | ||
724 | m_PacketQueue.AssetThrottle.Throttle = info.assetThrottle; | ||
725 | m_PacketQueue.TextureThrottle.Throttle = info.textureThrottle; | ||
726 | m_PacketQueue.TotalThrottle.Throttle = info.totalThrottle; | ||
727 | } | ||
728 | |||
729 | public void AddImportantPacket(PacketType type) | ||
730 | { | ||
731 | if (m_ImportantPackets.Contains(type)) | ||
732 | return; | ||
733 | |||
734 | m_ImportantPackets.Add(type); | ||
735 | } | ||
736 | |||
737 | public void RemoveImportantPacket(PacketType type) | ||
738 | { | ||
739 | if (!m_ImportantPackets.Contains(type)) | ||
740 | return; | ||
741 | |||
742 | m_ImportantPackets.Remove(type); | ||
743 | } | ||
744 | |||
745 | private void DropResend(Object id) | ||
746 | { | ||
747 | LLQueItem d = null; | ||
748 | |||
749 | foreach (LLQueItem data in m_NeedAck.Values) | ||
750 | { | ||
751 | if (data.Identifier != null && data.Identifier == id) | ||
752 | { | ||
753 | d = data; | ||
754 | break; | ||
755 | } | ||
756 | } | ||
757 | |||
758 | if (null == d) return; | ||
759 | |||
760 | m_NeedAck.Remove(d.Packet.Header.Sequence); | ||
761 | m_PacketQueue.Cancel(d.Sequence); | ||
762 | PacketPool.Instance.ReturnPacket(d.Packet); | ||
763 | } | ||
764 | |||
765 | private void TriggerOnPacketDrop(Packet packet, Object id) | ||
766 | { | ||
767 | PacketDrop handlerPacketDrop = OnPacketDrop; | ||
768 | |||
769 | if (handlerPacketDrop == null) | ||
770 | return; | ||
771 | |||
772 | handlerPacketDrop(packet, id); | ||
773 | } | ||
774 | |||
775 | private void TriggerOnQueueEmpty(ThrottleOutPacketType queue) | ||
776 | { | ||
777 | QueueEmpty handlerQueueEmpty = OnQueueEmpty; | ||
778 | |||
779 | if (handlerQueueEmpty != null) | ||
780 | handlerQueueEmpty(queue); | ||
781 | } | ||
782 | |||
783 | // Convert the packet to bytes and stuff it onto the send queue | ||
784 | // | ||
785 | public void ProcessOutPacket(LLQueItem item) | ||
786 | { | ||
787 | Packet packet = item.Packet; | ||
788 | |||
789 | // Assign sequence number here to prevent out of order packets | ||
790 | if (packet.Header.Sequence == 0) | ||
791 | { | ||
792 | lock (m_NeedAck) | ||
793 | { | ||
794 | packet.Header.Sequence = NextPacketSequenceNumber(); | ||
795 | item.Sequence = packet.Header.Sequence; | ||
796 | item.TickCount = Environment.TickCount; | ||
797 | |||
798 | // We want to see that packet arrive if it's reliable | ||
799 | if (packet.Header.Reliable) | ||
800 | { | ||
801 | m_UnackedBytes += item.Length; | ||
802 | |||
803 | // Keep track of when this packet was sent out | ||
804 | item.TickCount = Environment.TickCount; | ||
805 | |||
806 | m_NeedAck[packet.Header.Sequence] = item; | ||
807 | } | ||
808 | } | ||
809 | } | ||
810 | |||
811 | // If we sent a killpacket | ||
812 | if (packet is KillPacket) | ||
813 | Abort(); | ||
814 | |||
815 | try | ||
816 | { | ||
817 | // If this packet has been reused/returned, the ToBytes | ||
818 | // will blow up in our face. | ||
819 | // Fail gracefully. | ||
820 | // | ||
821 | |||
822 | // Actually make the byte array and send it | ||
823 | byte[] sendbuffer = item.Packet.ToBytes(); | ||
824 | |||
825 | if (packet.Header.Zerocoded) | ||
826 | { | ||
827 | int packetsize = Helpers.ZeroEncode(sendbuffer, | ||
828 | sendbuffer.Length, m_ZeroOutBuffer); | ||
829 | m_PacketServer.SendPacketTo(m_ZeroOutBuffer, packetsize, | ||
830 | SocketFlags.None, m_Client.CircuitCode); | ||
831 | } | ||
832 | else | ||
833 | { | ||
834 | // Need some extra space in case we need to add proxy | ||
835 | // information to the message later | ||
836 | Buffer.BlockCopy(sendbuffer, 0, m_ZeroOutBuffer, 0, | ||
837 | sendbuffer.Length); | ||
838 | m_PacketServer.SendPacketTo(m_ZeroOutBuffer, | ||
839 | sendbuffer.Length, SocketFlags.None, m_Client.CircuitCode); | ||
840 | } | ||
841 | } | ||
842 | catch (NullReferenceException) | ||
843 | { | ||
844 | m_log.Error("[PACKET]: Detected reuse of a returned packet"); | ||
845 | m_PacketQueue.Cancel(item.Sequence); | ||
846 | return; | ||
847 | } | ||
848 | |||
849 | // If this is a reliable packet, we are still holding a ref | ||
850 | // Dont't return in that case | ||
851 | // | ||
852 | if (!packet.Header.Reliable) | ||
853 | { | ||
854 | m_PacketQueue.Cancel(item.Sequence); | ||
855 | PacketPool.Instance.ReturnPacket(packet); | ||
856 | } | ||
857 | } | ||
858 | |||
859 | private void Abort() | ||
860 | { | ||
861 | m_PacketQueue.Close(); | ||
862 | Thread.CurrentThread.Abort(); | ||
863 | } | ||
864 | |||
865 | public int GetQueueCount(ThrottleOutPacketType queue) | ||
866 | { | ||
867 | return m_PacketQueue.GetQueueCount(queue); | ||
868 | } | ||
869 | } | ||
870 | } | ||