diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 219 |
1 files changed, 148 insertions, 71 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 4eee6b6..a9bc7d2 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | |||
@@ -33,6 +33,8 @@ using OpenSim.Framework; | |||
33 | using OpenMetaverse; | 33 | using OpenMetaverse; |
34 | using OpenMetaverse.Packets; | 34 | using OpenMetaverse.Packets; |
35 | 35 | ||
36 | using TokenBucket = OpenSim.Region.ClientStack.LindenUDP.TokenBucket; | ||
37 | |||
36 | namespace OpenSim.Region.ClientStack.LindenUDP | 38 | namespace OpenSim.Region.ClientStack.LindenUDP |
37 | { | 39 | { |
38 | #region Delegates | 40 | #region Delegates |
@@ -48,11 +50,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
48 | /// are waiting on ACKs for</param> | 50 | /// are waiting on ACKs for</param> |
49 | public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes); | 51 | public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes); |
50 | /// <summary> | 52 | /// <summary> |
51 | /// Fired when the queue for a packet category is empty. This event can be | 53 | /// Fired when the queue for one or more packet categories is empty. This |
52 | /// hooked to put more data on the empty queue | 54 | /// event can be hooked to put more data on the empty queues |
53 | /// </summary> | 55 | /// </summary> |
54 | /// <param name="category">Category of the packet queue that is empty</param> | 56 | /// <param name="category">Categories of the packet queues that are empty</param> |
55 | public delegate void QueueEmpty(ThrottleOutPacketType category); | 57 | public delegate void QueueEmpty(ThrottleOutPacketTypeFlags categories); |
56 | 58 | ||
57 | #endregion Delegates | 59 | #endregion Delegates |
58 | 60 | ||
@@ -98,12 +100,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
98 | /// <summary>True when this connection is alive, otherwise false</summary> | 100 | /// <summary>True when this connection is alive, otherwise false</summary> |
99 | public bool IsConnected = true; | 101 | public bool IsConnected = true; |
100 | /// <summary>True when this connection is paused, otherwise false</summary> | 102 | /// <summary>True when this connection is paused, otherwise false</summary> |
101 | public bool IsPaused = true; | 103 | public bool IsPaused; |
102 | /// <summary>Environment.TickCount when the last packet was received for this client</summary> | 104 | /// <summary>Environment.TickCount when the last packet was received for this client</summary> |
103 | public int TickLastPacketReceived; | 105 | public int TickLastPacketReceived; |
104 | 106 | ||
105 | /// <summary>Timer granularity. This is set to the measured resolution of Environment.TickCount</summary> | ||
106 | public readonly float G; | ||
107 | /// <summary>Smoothed round-trip time. A smoothed average of the round-trip time for sending a | 107 | /// <summary>Smoothed round-trip time. A smoothed average of the round-trip time for sending a |
108 | /// reliable packet to the client and receiving an ACK</summary> | 108 | /// reliable packet to the client and receiving an ACK</summary> |
109 | public float SRTT; | 109 | public float SRTT; |
@@ -128,6 +128,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
128 | private int m_packetsReceivedReported; | 128 | private int m_packetsReceivedReported; |
129 | /// <summary>Total number of sent packets that we have reported to the OnPacketStats event(s)</summary> | 129 | /// <summary>Total number of sent packets that we have reported to the OnPacketStats event(s)</summary> |
130 | private int m_packetsSentReported; | 130 | private int m_packetsSentReported; |
131 | /// <summary>Holds the Environment.TickCount value of when the next OnQueueEmpty can be fired</summary> | ||
132 | private int m_nextOnQueueEmpty = 1; | ||
131 | 133 | ||
132 | /// <summary>Throttle bucket for this agent's connection</summary> | 134 | /// <summary>Throttle bucket for this agent's connection</summary> |
133 | private readonly TokenBucket m_throttle; | 135 | private readonly TokenBucket m_throttle; |
@@ -140,9 +142,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
140 | /// <summary>A container that can hold one packet for each outbox, used to store | 142 | /// <summary>A container that can hold one packet for each outbox, used to store |
141 | /// dequeued packets that are being held for throttling</summary> | 143 | /// dequeued packets that are being held for throttling</summary> |
142 | private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; | 144 | private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; |
143 | /// <summary>Flags to prevent queue empty callbacks from stacking up on | ||
144 | /// top of each other</summary> | ||
145 | private readonly bool[] m_onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT]; | ||
146 | /// <summary>A reference to the LLUDPServer that is managing this client</summary> | 145 | /// <summary>A reference to the LLUDPServer that is managing this client</summary> |
147 | private readonly LLUDPServer m_udpServer; | 146 | private readonly LLUDPServer m_udpServer; |
148 | 147 | ||
@@ -163,26 +162,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
163 | CircuitCode = circuitCode; | 162 | CircuitCode = circuitCode; |
164 | m_udpServer = server; | 163 | m_udpServer = server; |
165 | m_defaultThrottleRates = rates; | 164 | m_defaultThrottleRates = rates; |
165 | // Create a token bucket throttle for this client that has the scene token bucket as a parent | ||
166 | m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); | 166 | m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); |
167 | // Create an array of token buckets for this clients different throttle categories | ||
167 | m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; | 168 | m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; |
168 | 169 | ||
169 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) | 170 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) |
170 | { | 171 | { |
171 | ThrottleOutPacketType type = (ThrottleOutPacketType)i; | 172 | ThrottleOutPacketType type = (ThrottleOutPacketType)i; |
172 | 173 | ||
174 | // Initialize the packet outboxes, where packets sit while they are waiting for tokens | ||
173 | m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); | 175 | m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); |
176 | // Initialize the token buckets that control the throttling for each category | ||
174 | m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); | 177 | m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); |
175 | } | 178 | } |
176 | 179 | ||
177 | // Set the granularity variable used for retransmission calculations to | ||
178 | // the measured resolution of Environment.TickCount | ||
179 | G = server.TickCountResolution; | ||
180 | |||
181 | // Default the retransmission timeout to three seconds | 180 | // Default the retransmission timeout to three seconds |
182 | RTO = 3000; | 181 | RTO = 3000; |
183 | 182 | ||
184 | // Initialize this to a sane value to prevent early disconnects | 183 | // Initialize this to a sane value to prevent early disconnects |
185 | TickLastPacketReceived = Environment.TickCount; | 184 | TickLastPacketReceived = Environment.TickCount & Int32.MaxValue; |
186 | } | 185 | } |
187 | 186 | ||
188 | /// <summary> | 187 | /// <summary> |
@@ -191,7 +190,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
191 | public void Shutdown() | 190 | public void Shutdown() |
192 | { | 191 | { |
193 | IsConnected = false; | 192 | IsConnected = false; |
194 | NeedAcks.Clear(); | ||
195 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) | 193 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) |
196 | { | 194 | { |
197 | m_packetOutboxes[i].Clear(); | 195 | m_packetOutboxes[i].Clear(); |
@@ -293,36 +291,59 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
293 | int state = (int)((float)task * STATE_TASK_PERCENTAGE); | 291 | int state = (int)((float)task * STATE_TASK_PERCENTAGE); |
294 | task -= state; | 292 | task -= state; |
295 | 293 | ||
296 | int ceiling = Int32.MaxValue; | 294 | // Make sure none of the throttles are set below our packet MTU, |
297 | if (m_defaultThrottleRates.Total != 0) | 295 | // otherwise a throttle could become permanently clogged |
298 | { | 296 | resend = Math.Max(resend, LLUDPServer.MTU); |
299 | ceiling = m_defaultThrottleRates.Total; | 297 | land = Math.Max(land, LLUDPServer.MTU); |
300 | if (ceiling < Packet.MTU) ceiling = Packet.MTU; | 298 | wind = Math.Max(wind, LLUDPServer.MTU); |
301 | } | 299 | cloud = Math.Max(cloud, LLUDPServer.MTU); |
302 | 300 | task = Math.Max(task, LLUDPServer.MTU); | |
303 | resend = Utils.Clamp(resend, Packet.MTU, ceiling); | 301 | texture = Math.Max(texture, LLUDPServer.MTU); |
304 | land = Utils.Clamp(land, Packet.MTU, ceiling); | 302 | asset = Math.Max(asset, LLUDPServer.MTU); |
305 | wind = Utils.Clamp(wind, Packet.MTU, ceiling); | 303 | state = Math.Max(state, LLUDPServer.MTU); |
306 | cloud = Utils.Clamp(cloud, Packet.MTU, ceiling); | ||
307 | task = Utils.Clamp(task, Packet.MTU, ceiling); | ||
308 | texture = Utils.Clamp(texture, Packet.MTU, ceiling); | ||
309 | asset = Utils.Clamp(asset, Packet.MTU, ceiling); | ||
310 | state = Utils.Clamp(state, Packet.MTU, ceiling); | ||
311 | 304 | ||
312 | int total = resend + land + wind + cloud + task + texture + asset + state; | 305 | int total = resend + land + wind + cloud + task + texture + asset + state; |
313 | int taskTotal = task + state; | ||
314 | 306 | ||
315 | m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, State={8}, Total={9}", | 307 | m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, State={8}, Total={9}", |
316 | AgentID, resend, land, wind, cloud, task, texture, asset, state, total); | 308 | AgentID, resend, land, wind, cloud, task, texture, asset, state, total); |
317 | 309 | ||
318 | SetThrottle(ThrottleOutPacketType.Resend, resend, resend); | 310 | // Update the token buckets with new throttle values |
319 | SetThrottle(ThrottleOutPacketType.Land, land, land); | 311 | TokenBucket bucket; |
320 | SetThrottle(ThrottleOutPacketType.Wind, wind, wind); | 312 | |
321 | SetThrottle(ThrottleOutPacketType.Cloud, cloud, cloud); | 313 | bucket = m_throttle; |
322 | SetThrottle(ThrottleOutPacketType.Task, task, taskTotal); | 314 | bucket.MaxBurst = total; |
323 | SetThrottle(ThrottleOutPacketType.Texture, texture, texture); | 315 | |
324 | SetThrottle(ThrottleOutPacketType.Asset, asset, asset); | 316 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; |
325 | SetThrottle(ThrottleOutPacketType.State, state, taskTotal); | 317 | bucket.DripRate = resend; |
318 | bucket.MaxBurst = resend; | ||
319 | |||
320 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; | ||
321 | bucket.DripRate = land; | ||
322 | bucket.MaxBurst = land; | ||
323 | |||
324 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; | ||
325 | bucket.DripRate = wind; | ||
326 | bucket.MaxBurst = wind; | ||
327 | |||
328 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; | ||
329 | bucket.DripRate = cloud; | ||
330 | bucket.MaxBurst = cloud; | ||
331 | |||
332 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; | ||
333 | bucket.DripRate = asset; | ||
334 | bucket.MaxBurst = asset; | ||
335 | |||
336 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; | ||
337 | bucket.DripRate = task + state; | ||
338 | bucket.MaxBurst = task + state; | ||
339 | |||
340 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; | ||
341 | bucket.DripRate = state; | ||
342 | bucket.MaxBurst = state; | ||
343 | |||
344 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; | ||
345 | bucket.DripRate = texture; | ||
346 | bucket.MaxBurst = texture; | ||
326 | } | 347 | } |
327 | 348 | ||
328 | public byte[] GetThrottlesPacked() | 349 | public byte[] GetThrottlesPacked() |
@@ -342,17 +363,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
342 | return data; | 363 | return data; |
343 | } | 364 | } |
344 | 365 | ||
345 | public void SetThrottle(ThrottleOutPacketType category, int rate, int maxBurst) | ||
346 | { | ||
347 | int i = (int)category; | ||
348 | if (i >= 0 && i < m_throttleCategories.Length) | ||
349 | { | ||
350 | TokenBucket bucket = m_throttleCategories[(int)category]; | ||
351 | bucket.DripRate = rate; | ||
352 | bucket.MaxBurst = maxBurst; | ||
353 | } | ||
354 | } | ||
355 | |||
356 | public bool EnqueueOutgoing(OutgoingPacket packet) | 366 | public bool EnqueueOutgoing(OutgoingPacket packet) |
357 | { | 367 | { |
358 | int category = (int)packet.Category; | 368 | int category = (int)packet.Category; |
@@ -394,10 +404,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
394 | OpenSim.Framework.LocklessQueue<OutgoingPacket> queue; | 404 | OpenSim.Framework.LocklessQueue<OutgoingPacket> queue; |
395 | TokenBucket bucket; | 405 | TokenBucket bucket; |
396 | bool packetSent = false; | 406 | bool packetSent = false; |
407 | ThrottleOutPacketTypeFlags emptyCategories = 0; | ||
408 | |||
409 | //string queueDebugOutput = String.Empty; // Serious debug business | ||
397 | 410 | ||
398 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) | 411 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) |
399 | { | 412 | { |
400 | bucket = m_throttleCategories[i]; | 413 | bucket = m_throttleCategories[i]; |
414 | //queueDebugOutput += m_packetOutboxes[i].Count + " "; // Serious debug business | ||
401 | 415 | ||
402 | if (m_nextPackets[i] != null) | 416 | if (m_nextPackets[i] != null) |
403 | { | 417 | { |
@@ -438,17 +452,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
438 | // empty callback now so it has a chance to fill before we | 452 | // empty callback now so it has a chance to fill before we |
439 | // get back here | 453 | // get back here |
440 | if (queue.Count == 0) | 454 | if (queue.Count == 0) |
441 | BeginFireQueueEmpty(i); | 455 | emptyCategories |= CategoryToFlag(i); |
442 | } | 456 | } |
443 | else | 457 | else |
444 | { | 458 | { |
445 | // No packets in this queue. Fire the queue empty callback | 459 | // No packets in this queue. Fire the queue empty callback |
446 | // if it has not been called recently | 460 | // if it has not been called recently |
447 | BeginFireQueueEmpty(i); | 461 | emptyCategories |= CategoryToFlag(i); |
448 | } | 462 | } |
449 | } | 463 | } |
450 | } | 464 | } |
451 | 465 | ||
466 | if (emptyCategories != 0) | ||
467 | BeginFireQueueEmpty(emptyCategories); | ||
468 | |||
469 | //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business | ||
452 | return packetSent; | 470 | return packetSent; |
453 | } | 471 | } |
454 | 472 | ||
@@ -479,8 +497,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
479 | SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r; | 497 | SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r; |
480 | } | 498 | } |
481 | 499 | ||
482 | // Always round retransmission timeout up to two seconds | 500 | RTO = (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR)); |
483 | RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR))); | 501 | |
502 | // Clamp the retransmission timeout to manageable values | ||
503 | RTO = Utils.Clamp(RTO, 3000, 10000); | ||
504 | |||
484 | //m_log.Debug("[LLUDPCLIENT]: Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " + | 505 | //m_log.Debug("[LLUDPCLIENT]: Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " + |
485 | // RTTVAR + " based on new RTT of " + r + "ms"); | 506 | // RTTVAR + " based on new RTT of " + r + "ms"); |
486 | } | 507 | } |
@@ -491,33 +512,89 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
491 | /// </summary> | 512 | /// </summary> |
492 | /// <param name="throttleIndex">Throttle category to fire the callback | 513 | /// <param name="throttleIndex">Throttle category to fire the callback |
493 | /// for</param> | 514 | /// for</param> |
494 | private void BeginFireQueueEmpty(int throttleIndex) | 515 | private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) |
495 | { | 516 | { |
496 | if (!m_onQueueEmptyRunning[throttleIndex]) | 517 | if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) |
497 | Util.FireAndForget(FireQueueEmpty, throttleIndex); | 518 | { |
519 | // Use a value of 0 to signal that FireQueueEmpty is running | ||
520 | m_nextOnQueueEmpty = 0; | ||
521 | // Asynchronously run the callback | ||
522 | Util.FireAndForget(FireQueueEmpty, categories); | ||
523 | } | ||
498 | } | 524 | } |
499 | 525 | ||
500 | /// <summary> | 526 | /// <summary> |
501 | /// Checks to see if this queue empty callback is already running, | 527 | /// Fires the OnQueueEmpty callback and sets the minimum time that it |
502 | /// then firing the event | 528 | /// can be called again |
503 | /// </summary> | 529 | /// </summary> |
504 | /// <param name="o">Throttle category to fire the callback for, stored | 530 | /// <param name="o">Throttle categories to fire the callback for, |
505 | /// as an object to match the WaitCallback delegate signature</param> | 531 | /// stored as an object to match the WaitCallback delegate |
532 | /// signature</param> | ||
506 | private void FireQueueEmpty(object o) | 533 | private void FireQueueEmpty(object o) |
507 | { | 534 | { |
508 | int i = (int)o; | 535 | const int MIN_CALLBACK_MS = 30; |
509 | ThrottleOutPacketType type = (ThrottleOutPacketType)i; | 536 | |
537 | ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o; | ||
510 | QueueEmpty callback = OnQueueEmpty; | 538 | QueueEmpty callback = OnQueueEmpty; |
539 | |||
540 | int start = Environment.TickCount & Int32.MaxValue; | ||
511 | 541 | ||
512 | if (callback != null) | 542 | if (callback != null) |
513 | { | 543 | { |
514 | if (!m_onQueueEmptyRunning[i]) | 544 | try { callback(categories); } |
515 | { | 545 | catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); } |
516 | m_onQueueEmptyRunning[i] = true; | 546 | } |
517 | try { callback(type); } | 547 | |
518 | catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); } | 548 | m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; |
519 | m_onQueueEmptyRunning[i] = false; | 549 | if (m_nextOnQueueEmpty == 0) |
520 | } | 550 | m_nextOnQueueEmpty = 1; |
551 | } | ||
552 | |||
553 | /// <summary> | ||
554 | /// Converts a <seealso cref="ThrottleOutPacketType"/> integer to a | ||
555 | /// flag value | ||
556 | /// </summary> | ||
557 | /// <param name="i">Throttle category to convert</param> | ||
558 | /// <returns>Flag representation of the throttle category</returns> | ||
559 | private static ThrottleOutPacketTypeFlags CategoryToFlag(int i) | ||
560 | { | ||
561 | ThrottleOutPacketType category = (ThrottleOutPacketType)i; | ||
562 | |||
563 | /* | ||
564 | * Land = 1, | ||
565 | /// <summary>Wind data</summary> | ||
566 | Wind = 2, | ||
567 | /// <summary>Cloud data</summary> | ||
568 | Cloud = 3, | ||
569 | /// <summary>Any packets that do not fit into the other throttles</summary> | ||
570 | Task = 4, | ||
571 | /// <summary>Texture assets</summary> | ||
572 | Texture = 5, | ||
573 | /// <summary>Non-texture assets</summary> | ||
574 | Asset = 6, | ||
575 | /// <summary>Avatar and primitive data</summary> | ||
576 | /// <remarks>This is a sub-category of Task</remarks> | ||
577 | State = 7, | ||
578 | */ | ||
579 | |||
580 | switch (category) | ||
581 | { | ||
582 | case ThrottleOutPacketType.Land: | ||
583 | return ThrottleOutPacketTypeFlags.Land; | ||
584 | case ThrottleOutPacketType.Wind: | ||
585 | return ThrottleOutPacketTypeFlags.Wind; | ||
586 | case ThrottleOutPacketType.Cloud: | ||
587 | return ThrottleOutPacketTypeFlags.Cloud; | ||
588 | case ThrottleOutPacketType.Task: | ||
589 | return ThrottleOutPacketTypeFlags.Task; | ||
590 | case ThrottleOutPacketType.Texture: | ||
591 | return ThrottleOutPacketTypeFlags.Texture; | ||
592 | case ThrottleOutPacketType.Asset: | ||
593 | return ThrottleOutPacketTypeFlags.Asset; | ||
594 | case ThrottleOutPacketType.State: | ||
595 | return ThrottleOutPacketTypeFlags.State; | ||
596 | default: | ||
597 | return 0; | ||
521 | } | 598 | } |
522 | } | 599 | } |
523 | } | 600 | } |