diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/ClientView.PacketQueue.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/ClientView.PacketQueue.cs | 373 |
1 files changed, 0 insertions, 373 deletions
diff --git a/OpenSim/Region/ClientStack/ClientView.PacketQueue.cs b/OpenSim/Region/ClientStack/ClientView.PacketQueue.cs deleted file mode 100644 index d7b86e8..0000000 --- a/OpenSim/Region/ClientStack/ClientView.PacketQueue.cs +++ /dev/null | |||
@@ -1,373 +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 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.Generic; | ||
30 | using System.Net; | ||
31 | using System.Net.Sockets; | ||
32 | using System.Timers; | ||
33 | using libsecondlife; | ||
34 | using libsecondlife.Packets; | ||
35 | using OpenSim.Framework; | ||
36 | using OpenSim.Framework.Console; | ||
37 | |||
38 | namespace OpenSim.Region.ClientStack | ||
39 | { | ||
40 | public partial class ClientView | ||
41 | { | ||
42 | protected BlockingQueue<QueItem> PacketQueue; | ||
43 | |||
44 | protected Queue<QueItem> IncomingPacketQueue; | ||
45 | protected Queue<QueItem> OutgoingPacketQueue; | ||
46 | protected Queue<QueItem> ResendOutgoingPacketQueue; | ||
47 | protected Queue<QueItem> LandOutgoingPacketQueue; | ||
48 | protected Queue<QueItem> WindOutgoingPacketQueue; | ||
49 | protected Queue<QueItem> CloudOutgoingPacketQueue; | ||
50 | protected Queue<QueItem> TaskOutgoingPacketQueue; | ||
51 | protected Queue<QueItem> TextureOutgoingPacketQueue; | ||
52 | protected Queue<QueItem> AssetOutgoingPacketQueue; | ||
53 | |||
54 | protected Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>(); | ||
55 | protected Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>(); | ||
56 | |||
57 | protected Timer AckTimer; | ||
58 | protected uint Sequence = 0; | ||
59 | protected object SequenceLock = new object(); | ||
60 | protected const int MAX_APPENDED_ACKS = 10; | ||
61 | protected const int RESEND_TIMEOUT = 4000; | ||
62 | protected const int MAX_SEQUENCE = 0xFFFFFF; | ||
63 | |||
64 | private uint m_circuitCode; | ||
65 | public EndPoint userEP; | ||
66 | |||
67 | protected PacketServer m_networkServer; | ||
68 | |||
69 | public uint CircuitCode | ||
70 | { | ||
71 | get { return m_circuitCode; } | ||
72 | set { m_circuitCode = value; } | ||
73 | } | ||
74 | |||
75 | protected virtual void ProcessOutPacket(Packet Pack) | ||
76 | { | ||
77 | // Keep track of when this packet was sent out | ||
78 | Pack.TickCount = System.Environment.TickCount; | ||
79 | |||
80 | if (!Pack.Header.Resent) | ||
81 | { | ||
82 | // Set the sequence number | ||
83 | lock (SequenceLock) | ||
84 | { | ||
85 | if (Sequence >= MAX_SEQUENCE) | ||
86 | { | ||
87 | Sequence = 1; | ||
88 | } | ||
89 | else | ||
90 | { | ||
91 | Sequence++; | ||
92 | } | ||
93 | |||
94 | Pack.Header.Sequence = Sequence; | ||
95 | } | ||
96 | |||
97 | if (Pack.Header.Reliable) //DIRTY HACK | ||
98 | { | ||
99 | lock (NeedAck) | ||
100 | { | ||
101 | if (!NeedAck.ContainsKey(Pack.Header.Sequence)) | ||
102 | { | ||
103 | try | ||
104 | { | ||
105 | NeedAck.Add(Pack.Header.Sequence, Pack); | ||
106 | } | ||
107 | catch (Exception e) // HACKY | ||
108 | { | ||
109 | e.ToString(); | ||
110 | // Ignore | ||
111 | // Seems to throw a exception here occasionally | ||
112 | // of 'duplicate key' despite being locked. | ||
113 | // !?!?!? | ||
114 | } | ||
115 | } | ||
116 | else | ||
117 | { | ||
118 | // Client.Log("Attempted to add a duplicate sequence number (" + | ||
119 | // packet.Header.Sequence + ") to the NeedAck dictionary for packet type " + | ||
120 | // packet.Type.ToString(), Helpers.LogLevel.Warning); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | // Don't append ACKs to resent packets, in case that's what was causing the | ||
125 | // delivery to fail | ||
126 | if (!Pack.Header.Resent) | ||
127 | { | ||
128 | // Append any ACKs that need to be sent out to this packet | ||
129 | lock (PendingAcks) | ||
130 | { | ||
131 | if (PendingAcks.Count > 0 && PendingAcks.Count < MAX_APPENDED_ACKS && | ||
132 | Pack.Type != PacketType.PacketAck && | ||
133 | Pack.Type != PacketType.LogoutRequest) | ||
134 | { | ||
135 | Pack.Header.AckList = new uint[PendingAcks.Count]; | ||
136 | int i = 0; | ||
137 | |||
138 | foreach (uint ack in PendingAcks.Values) | ||
139 | { | ||
140 | Pack.Header.AckList[i] = ack; | ||
141 | i++; | ||
142 | } | ||
143 | |||
144 | PendingAcks.Clear(); | ||
145 | Pack.Header.AppendedAcks = true; | ||
146 | } | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | |||
152 | byte[] ZeroOutBuffer = new byte[4096]; | ||
153 | byte[] sendbuffer; | ||
154 | sendbuffer = Pack.ToBytes(); | ||
155 | |||
156 | try | ||
157 | { | ||
158 | if (Pack.Header.Zerocoded) | ||
159 | { | ||
160 | int packetsize = Helpers.ZeroEncode(sendbuffer, sendbuffer.Length, ZeroOutBuffer); | ||
161 | m_networkServer.SendPacketTo(ZeroOutBuffer, packetsize, SocketFlags.None, m_circuitCode); //userEP); | ||
162 | } | ||
163 | else | ||
164 | { | ||
165 | m_networkServer.SendPacketTo(sendbuffer, sendbuffer.Length, SocketFlags.None, m_circuitCode); | ||
166 | //userEP); | ||
167 | } | ||
168 | } | ||
169 | catch (Exception e) | ||
170 | { | ||
171 | MainLog.Instance.Warn("client", | ||
172 | "ClientView.PacketQueue.cs:ProcessOutPacket() - WARNING: Socket exception occurred on connection " + | ||
173 | userEP.ToString() + " - killing thread"); | ||
174 | MainLog.Instance.Error(e.ToString()); | ||
175 | KillThread(); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | public virtual void InPacket(Packet NewPack) | ||
180 | { | ||
181 | // Handle appended ACKs | ||
182 | if (NewPack.Header.AppendedAcks) | ||
183 | { | ||
184 | lock (NeedAck) | ||
185 | { | ||
186 | foreach (uint ack in NewPack.Header.AckList) | ||
187 | { | ||
188 | NeedAck.Remove(ack); | ||
189 | } | ||
190 | } | ||
191 | } | ||
192 | |||
193 | // Handle PacketAck packets | ||
194 | if (NewPack.Type == PacketType.PacketAck) | ||
195 | { | ||
196 | PacketAckPacket ackPacket = (PacketAckPacket) NewPack; | ||
197 | |||
198 | lock (NeedAck) | ||
199 | { | ||
200 | foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets) | ||
201 | { | ||
202 | NeedAck.Remove(block.ID); | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | else if ((NewPack.Type == PacketType.StartPingCheck)) | ||
207 | { | ||
208 | //reply to pingcheck | ||
209 | StartPingCheckPacket startPing = (StartPingCheckPacket) NewPack; | ||
210 | CompletePingCheckPacket endPing = new CompletePingCheckPacket(); | ||
211 | endPing.PingID.PingID = startPing.PingID.PingID; | ||
212 | OutPacket(endPing, ThrottleOutPacketType.Task); | ||
213 | } | ||
214 | else | ||
215 | { | ||
216 | QueItem item = new QueItem(); | ||
217 | item.Packet = NewPack; | ||
218 | item.Incoming = true; | ||
219 | PacketQueue.Enqueue(item); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | private void ThrottleCheck(ref int TypeBytesSent, int Throttle, Queue<QueItem> q, QueItem item) | ||
224 | { | ||
225 | // The idea.. is if the packet throttle queues are empty | ||
226 | // and the client is under throttle for the type. Queue | ||
227 | // it up directly. This basically short cuts having to | ||
228 | // wait for the timer to fire to put things into the | ||
229 | // output queue | ||
230 | |||
231 | if(q.Count == 0 && TypeBytesSent <= ((int)(Throttle / throttleTimeDivisor))) | ||
232 | { | ||
233 | bytesSent += item.Packet.ToBytes().Length; | ||
234 | TypeBytesSent += item.Packet.ToBytes().Length; | ||
235 | PacketQueue.Enqueue(item); | ||
236 | } | ||
237 | else | ||
238 | { | ||
239 | q.Enqueue(item); | ||
240 | } | ||
241 | } | ||
242 | |||
243 | public virtual void OutPacket(Packet NewPack, ThrottleOutPacketType throttlePacketType) | ||
244 | { | ||
245 | QueItem item = new QueItem(); | ||
246 | item.Packet = NewPack; | ||
247 | item.Incoming = false; | ||
248 | item.throttleType = throttlePacketType; // Packet throttle type | ||
249 | |||
250 | // The idea.. is if the packet throttle queues are empty and the client is under throttle for the type. | ||
251 | // Queue it up directly. | ||
252 | switch (throttlePacketType) | ||
253 | { | ||
254 | case ThrottleOutPacketType.Resend: | ||
255 | ThrottleCheck(ref ResendBytesSent, ResendthrottleOutbound, ResendOutgoingPacketQueue, item); | ||
256 | break; | ||
257 | case ThrottleOutPacketType.Texture: | ||
258 | ThrottleCheck(ref TextureBytesSent, TexturethrottleOutbound, TextureOutgoingPacketQueue, item); | ||
259 | break; | ||
260 | case ThrottleOutPacketType.Task: | ||
261 | ThrottleCheck(ref TaskBytesSent, TaskthrottleOutbound, TaskOutgoingPacketQueue, item); | ||
262 | break; | ||
263 | case ThrottleOutPacketType.Land: | ||
264 | ThrottleCheck(ref LandBytesSent, LandthrottleOutbound, LandOutgoingPacketQueue, item); | ||
265 | break; | ||
266 | case ThrottleOutPacketType.Asset: | ||
267 | ThrottleCheck(ref AssetBytesSent, AssetthrottleOutbound, AssetOutgoingPacketQueue, item); | ||
268 | break; | ||
269 | case ThrottleOutPacketType.Cloud: | ||
270 | ThrottleCheck(ref CloudBytesSent, CloudthrottleOutbound, CloudOutgoingPacketQueue, item); | ||
271 | break; | ||
272 | case ThrottleOutPacketType.Wind: | ||
273 | ThrottleCheck(ref WindBytesSent, WindthrottleOutbound, WindOutgoingPacketQueue, item); | ||
274 | break; | ||
275 | |||
276 | default: | ||
277 | // Acknowledgements and other such stuff should go directly to the blocking Queue | ||
278 | // Throttling them may and likely 'will' be problematic | ||
279 | PacketQueue.Enqueue(item); | ||
280 | break; | ||
281 | } | ||
282 | //OutgoingPacketQueue.Enqueue(item); | ||
283 | } | ||
284 | |||
285 | # region Low Level Packet Methods | ||
286 | |||
287 | protected void ack_pack(Packet Pack) | ||
288 | { | ||
289 | if (Pack.Header.Reliable) | ||
290 | { | ||
291 | PacketAckPacket ack_it = new PacketAckPacket(); | ||
292 | ack_it.Packets = new PacketAckPacket.PacketsBlock[1]; | ||
293 | ack_it.Packets[0] = new PacketAckPacket.PacketsBlock(); | ||
294 | ack_it.Packets[0].ID = Pack.Header.Sequence; | ||
295 | ack_it.Header.Reliable = false; | ||
296 | |||
297 | OutPacket(ack_it, ThrottleOutPacketType.Unknown); | ||
298 | } | ||
299 | /* | ||
300 | if (Pack.Header.Reliable) | ||
301 | { | ||
302 | lock (PendingAcks) | ||
303 | { | ||
304 | uint sequence = (uint)Pack.Header.Sequence; | ||
305 | if (!PendingAcks.ContainsKey(sequence)) { PendingAcks[sequence] = sequence; } | ||
306 | } | ||
307 | }*/ | ||
308 | } | ||
309 | |||
310 | protected void ResendUnacked() | ||
311 | { | ||
312 | int now = System.Environment.TickCount; | ||
313 | |||
314 | lock (NeedAck) | ||
315 | { | ||
316 | foreach (Packet packet in NeedAck.Values) | ||
317 | { | ||
318 | if ((now - packet.TickCount > RESEND_TIMEOUT) && (!packet.Header.Resent)) | ||
319 | { | ||
320 | MainLog.Instance.Verbose("Resending " + packet.Type.ToString() + " packet, " + | ||
321 | (now - packet.TickCount) + "ms have passed"); | ||
322 | |||
323 | packet.Header.Resent = true; | ||
324 | OutPacket(packet, ThrottleOutPacketType.Resend); | ||
325 | } | ||
326 | } | ||
327 | } | ||
328 | } | ||
329 | |||
330 | protected void SendAcks() | ||
331 | { | ||
332 | lock (PendingAcks) | ||
333 | { | ||
334 | if (PendingAcks.Count > 0) | ||
335 | { | ||
336 | if (PendingAcks.Count > 250) | ||
337 | { | ||
338 | // FIXME: Handle the odd case where we have too many pending ACKs queued up | ||
339 | MainLog.Instance.Verbose("Too many ACKs queued up!"); | ||
340 | return; | ||
341 | } | ||
342 | |||
343 | //OpenSim.Framework.Console.MainLog.Instance.WriteLine("Sending PacketAck"); | ||
344 | |||
345 | |||
346 | int i = 0; | ||
347 | PacketAckPacket acks = new PacketAckPacket(); | ||
348 | acks.Packets = new PacketAckPacket.PacketsBlock[PendingAcks.Count]; | ||
349 | |||
350 | foreach (uint ack in PendingAcks.Values) | ||
351 | { | ||
352 | acks.Packets[i] = new PacketAckPacket.PacketsBlock(); | ||
353 | acks.Packets[i].ID = ack; | ||
354 | i++; | ||
355 | } | ||
356 | |||
357 | acks.Header.Reliable = false; | ||
358 | OutPacket(acks, ThrottleOutPacketType.Unknown); | ||
359 | |||
360 | PendingAcks.Clear(); | ||
361 | } | ||
362 | } | ||
363 | } | ||
364 | |||
365 | protected void AckTimer_Elapsed(object sender, ElapsedEventArgs ea) | ||
366 | { | ||
367 | SendAcks(); | ||
368 | ResendUnacked(); | ||
369 | } | ||
370 | |||
371 | #endregion | ||
372 | } | ||
373 | } \ No newline at end of file | ||