corrade-vassal – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | vero | 1 | /* |
2 | * Copyright (c) 2006-2014, openmetaverse.org |
||
3 | * All rights reserved. |
||
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 | * |
||
8 | * - Redistributions of source code must retain the above copyright notice, this |
||
9 | * list of conditions and the following disclaimer. |
||
10 | * - Neither the name of the openmetaverse.org nor the names |
||
11 | * of its contributors may be used to endorse or promote products derived from |
||
12 | * this software without specific prior written permission. |
||
13 | * |
||
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||
15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||
18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||
19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||
20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||
21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||
22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||
23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||
24 | * POSSIBILITY OF SUCH DAMAGE. |
||
25 | */ |
||
26 | |||
27 | using System; |
||
28 | using System.Collections.Generic; |
||
29 | using System.Threading; |
||
30 | using System.Net; |
||
31 | using System.Net.Sockets; |
||
32 | using OpenMetaverse.Packets; |
||
33 | |||
34 | namespace OpenMetaverse |
||
35 | { |
||
36 | #region Enums |
||
37 | |||
38 | /// <summary> |
||
39 | /// Simulator (region) properties |
||
40 | /// </summary> |
||
41 | [Flags] |
||
42 | public enum RegionFlags : ulong |
||
43 | { |
||
44 | /// <summary>No flags set</summary> |
||
45 | None = 0, |
||
46 | /// <summary>Agents can take damage and be killed</summary> |
||
47 | AllowDamage = 1 << 0, |
||
48 | /// <summary>Landmarks can be created here</summary> |
||
49 | AllowLandmark = 1 << 1, |
||
50 | /// <summary>Home position can be set in this sim</summary> |
||
51 | AllowSetHome = 1 << 2, |
||
52 | /// <summary>Home position is reset when an agent teleports away</summary> |
||
53 | ResetHomeOnTeleport = 1 << 3, |
||
54 | /// <summary>Sun does not move</summary> |
||
55 | SunFixed = 1 << 4, |
||
56 | /// <summary>No object, land, etc. taxes</summary> |
||
57 | TaxFree = 1 << 5, |
||
58 | /// <summary>Disable heightmap alterations (agents can still plant |
||
59 | /// foliage)</summary> |
||
60 | BlockTerraform = 1 << 6, |
||
61 | /// <summary>Land cannot be released, sold, or purchased</summary> |
||
62 | BlockLandResell = 1 << 7, |
||
63 | /// <summary>All content is wiped nightly</summary> |
||
64 | Sandbox = 1 << 8, |
||
65 | /// <summary>Unknown: Related to the availability of an overview world map tile.(Think mainland images when zoomed out.)</summary> |
||
66 | NullLayer = 1 << 9, |
||
67 | /// <summary>Unknown: Related to region debug flags. Possibly to skip processing of agent interaction with world. </summary> |
||
68 | SkipAgentAction = 1 << 10, |
||
69 | /// <summary>Region does not update agent prim interest lists. Internal debugging option.</summary> |
||
70 | SkipUpdateInterestList = 1 << 11, |
||
71 | /// <summary>No collision detection for non-agent objects</summary> |
||
72 | SkipCollisions = 1 << 12, |
||
73 | /// <summary>No scripts are ran</summary> |
||
74 | SkipScripts = 1 << 13, |
||
75 | /// <summary>All physics processing is turned off</summary> |
||
76 | SkipPhysics = 1 << 14, |
||
77 | /// <summary>Region can be seen from other regions on world map. (Legacy world map option?) </summary> |
||
78 | ExternallyVisible = 1 << 15, |
||
79 | /// <summary>Region can be seen from mainland on world map. (Legacy world map option?) </summary> |
||
80 | MainlandVisible = 1 << 16, |
||
81 | /// <summary>Agents not explicitly on the access list can visit the region. </summary> |
||
82 | PublicAllowed = 1 << 17, |
||
83 | /// <summary>Traffic calculations are not run across entire region, overrides parcel settings. </summary> |
||
84 | BlockDwell = 1 << 18, |
||
85 | /// <summary>Flight is disabled (not currently enforced by the sim)</summary> |
||
86 | NoFly = 1 << 19, |
||
87 | /// <summary>Allow direct (p2p) teleporting</summary> |
||
88 | AllowDirectTeleport = 1 << 20, |
||
89 | /// <summary>Estate owner has temporarily disabled scripting</summary> |
||
90 | EstateSkipScripts = 1 << 21, |
||
91 | /// <summary>Restricts the usage of the LSL llPushObject function, applies to whole region.</summary> |
||
92 | RestrictPushObject = 1 << 22, |
||
93 | /// <summary>Deny agents with no payment info on file</summary> |
||
94 | DenyAnonymous = 1 << 23, |
||
95 | /// <summary>Deny agents with payment info on file</summary> |
||
96 | DenyIdentified = 1 << 24, |
||
97 | /// <summary>Deny agents who have made a monetary transaction</summary> |
||
98 | DenyTransacted = 1 << 25, |
||
99 | /// <summary>Parcels within the region may be joined or divided by anyone, not just estate owners/managers. </summary> |
||
100 | AllowParcelChanges = 1 << 26, |
||
101 | /// <summary>Abuse reports sent from within this region are sent to the estate owner defined email. </summary> |
||
102 | AbuseEmailToEstateOwner = 1 << 27, |
||
103 | /// <summary>Region is Voice Enabled</summary> |
||
104 | AllowVoice = 1 << 28, |
||
105 | /// <summary>Removes the ability from parcel owners to set their parcels to show in search.</summary> |
||
106 | BlockParcelSearch = 1 << 29, |
||
107 | /// <summary>Deny agents who have not been age verified from entering the region.</summary> |
||
108 | DenyAgeUnverified = 1 << 30 |
||
109 | |||
110 | } |
||
111 | |||
112 | /// <summary> |
||
113 | /// Region protocol flags |
||
114 | /// </summary> |
||
115 | [Flags] |
||
116 | public enum RegionProtocols : ulong |
||
117 | { |
||
118 | /// <summary>Nothing special</summary> |
||
119 | None = 0, |
||
120 | /// <summary>Region supports Server side Appearance</summary> |
||
121 | AgentAppearanceService = 1 << 0, |
||
122 | /// <summary>Viewer supports Server side Appearance</summary> |
||
123 | SelfAppearanceSupport = 1 << 2 |
||
124 | } |
||
125 | |||
126 | /// <summary> |
||
127 | /// Access level for a simulator |
||
128 | /// </summary> |
||
129 | public enum SimAccess : byte |
||
130 | { |
||
131 | /// <summary>Unknown or invalid access level</summary> |
||
132 | Unknown = 0, |
||
133 | /// <summary>Trial accounts allowed</summary> |
||
134 | Trial = 7, |
||
135 | /// <summary>PG rating</summary> |
||
136 | PG = 13, |
||
137 | /// <summary>Mature rating</summary> |
||
138 | Mature = 21, |
||
139 | /// <summary>Adult rating</summary> |
||
140 | Adult = 42, |
||
141 | /// <summary>Simulator is offline</summary> |
||
142 | Down = 254, |
||
143 | /// <summary>Simulator does not exist</summary> |
||
144 | NonExistent = 255 |
||
145 | } |
||
146 | |||
147 | #endregion Enums |
||
148 | |||
149 | /// <summary> |
||
150 | /// |
||
151 | /// </summary> |
||
152 | public class Simulator : UDPBase, IDisposable |
||
153 | { |
||
154 | #region Structs |
||
155 | /// <summary> |
||
156 | /// Simulator Statistics |
||
157 | /// </summary> |
||
158 | public struct SimStats |
||
159 | { |
||
160 | /// <summary>Total number of packets sent by this simulator to this agent</summary> |
||
161 | public long SentPackets; |
||
162 | /// <summary>Total number of packets received by this simulator to this agent</summary> |
||
163 | public long RecvPackets; |
||
164 | /// <summary>Total number of bytes sent by this simulator to this agent</summary> |
||
165 | public long SentBytes; |
||
166 | /// <summary>Total number of bytes received by this simulator to this agent</summary> |
||
167 | public long RecvBytes; |
||
168 | /// <summary>Time in seconds agent has been connected to simulator</summary> |
||
169 | public int ConnectTime; |
||
170 | /// <summary>Total number of packets that have been resent</summary> |
||
171 | public int ResentPackets; |
||
172 | /// <summary>Total number of resent packets recieved</summary> |
||
173 | public int ReceivedResends; |
||
174 | /// <summary>Total number of pings sent to this simulator by this agent</summary> |
||
175 | public int SentPings; |
||
176 | /// <summary>Total number of ping replies sent to this agent by this simulator</summary> |
||
177 | public int ReceivedPongs; |
||
178 | /// <summary> |
||
179 | /// Incoming bytes per second |
||
180 | /// </summary> |
||
181 | /// <remarks>It would be nice to have this claculated on the fly, but |
||
182 | /// this is far, far easier</remarks> |
||
183 | public int IncomingBPS; |
||
184 | /// <summary> |
||
185 | /// Outgoing bytes per second |
||
186 | /// </summary> |
||
187 | /// <remarks>It would be nice to have this claculated on the fly, but |
||
188 | /// this is far, far easier</remarks> |
||
189 | public int OutgoingBPS; |
||
190 | /// <summary>Time last ping was sent</summary> |
||
191 | public int LastPingSent; |
||
192 | /// <summary>ID of last Ping sent</summary> |
||
193 | public byte LastPingID; |
||
194 | /// <summary></summary> |
||
195 | public int LastLag; |
||
196 | /// <summary></summary> |
||
197 | public int MissedPings; |
||
198 | /// <summary>Current time dilation of this simulator</summary> |
||
199 | public float Dilation; |
||
200 | /// <summary>Current Frames per second of simulator</summary> |
||
201 | public int FPS; |
||
202 | /// <summary>Current Physics frames per second of simulator</summary> |
||
203 | public float PhysicsFPS; |
||
204 | /// <summary></summary> |
||
205 | public float AgentUpdates; |
||
206 | /// <summary></summary> |
||
207 | public float FrameTime; |
||
208 | /// <summary></summary> |
||
209 | public float NetTime; |
||
210 | /// <summary></summary> |
||
211 | public float PhysicsTime; |
||
212 | /// <summary></summary> |
||
213 | public float ImageTime; |
||
214 | /// <summary></summary> |
||
215 | public float ScriptTime; |
||
216 | /// <summary></summary> |
||
217 | public float AgentTime; |
||
218 | /// <summary></summary> |
||
219 | public float OtherTime; |
||
220 | /// <summary>Total number of objects Simulator is simulating</summary> |
||
221 | public int Objects; |
||
222 | /// <summary>Total number of Active (Scripted) objects running</summary> |
||
223 | public int ScriptedObjects; |
||
224 | /// <summary>Number of agents currently in this simulator</summary> |
||
225 | public int Agents; |
||
226 | /// <summary>Number of agents in neighbor simulators</summary> |
||
227 | public int ChildAgents; |
||
228 | /// <summary>Number of Active scripts running in this simulator</summary> |
||
229 | public int ActiveScripts; |
||
230 | /// <summary></summary> |
||
231 | public int LSLIPS; |
||
232 | /// <summary></summary> |
||
233 | public int INPPS; |
||
234 | /// <summary></summary> |
||
235 | public int OUTPPS; |
||
236 | /// <summary>Number of downloads pending</summary> |
||
237 | public int PendingDownloads; |
||
238 | /// <summary>Number of uploads pending</summary> |
||
239 | public int PendingUploads; |
||
240 | /// <summary></summary> |
||
241 | public int VirtualSize; |
||
242 | /// <summary></summary> |
||
243 | public int ResidentSize; |
||
244 | /// <summary>Number of local uploads pending</summary> |
||
245 | public int PendingLocalUploads; |
||
246 | /// <summary>Unacknowledged bytes in queue</summary> |
||
247 | public int UnackedBytes; |
||
248 | } |
||
249 | |||
250 | #endregion Structs |
||
251 | |||
252 | #region Public Members |
||
253 | /// <summary>A public reference to the client that this Simulator object |
||
254 | /// is attached to</summary> |
||
255 | public GridClient Client; |
||
256 | /// <summary>A Unique Cache identifier for this simulator</summary> |
||
257 | public UUID ID = UUID.Zero; |
||
258 | /// <summary>The capabilities for this simulator</summary> |
||
259 | public Caps Caps = null; |
||
260 | /// <summary></summary> |
||
261 | public ulong Handle; |
||
262 | /// <summary>The current version of software this simulator is running</summary> |
||
263 | public string SimVersion = String.Empty; |
||
264 | /// <summary></summary> |
||
265 | public string Name = String.Empty; |
||
266 | /// <summary>A 64x64 grid of parcel coloring values. The values stored |
||
267 | /// in this array are of the <seealso cref="ParcelArrayType"/> type</summary> |
||
268 | public byte[] ParcelOverlay = new byte[4096]; |
||
269 | /// <summary></summary> |
||
270 | public int ParcelOverlaysReceived; |
||
271 | /// <summary></summary> |
||
272 | public float TerrainHeightRange00; |
||
273 | /// <summary></summary> |
||
274 | public float TerrainHeightRange01; |
||
275 | /// <summary></summary> |
||
276 | public float TerrainHeightRange10; |
||
277 | /// <summary></summary> |
||
278 | public float TerrainHeightRange11; |
||
279 | /// <summary></summary> |
||
280 | public float TerrainStartHeight00; |
||
281 | /// <summary></summary> |
||
282 | public float TerrainStartHeight01; |
||
283 | /// <summary></summary> |
||
284 | public float TerrainStartHeight10; |
||
285 | /// <summary></summary> |
||
286 | public float TerrainStartHeight11; |
||
287 | /// <summary></summary> |
||
288 | public float WaterHeight; |
||
289 | /// <summary></summary> |
||
290 | public UUID SimOwner = UUID.Zero; |
||
291 | /// <summary></summary> |
||
292 | public UUID TerrainBase0 = UUID.Zero; |
||
293 | /// <summary></summary> |
||
294 | public UUID TerrainBase1 = UUID.Zero; |
||
295 | /// <summary></summary> |
||
296 | public UUID TerrainBase2 = UUID.Zero; |
||
297 | /// <summary></summary> |
||
298 | public UUID TerrainBase3 = UUID.Zero; |
||
299 | /// <summary></summary> |
||
300 | public UUID TerrainDetail0 = UUID.Zero; |
||
301 | /// <summary></summary> |
||
302 | public UUID TerrainDetail1 = UUID.Zero; |
||
303 | /// <summary></summary> |
||
304 | public UUID TerrainDetail2 = UUID.Zero; |
||
305 | /// <summary></summary> |
||
306 | public UUID TerrainDetail3 = UUID.Zero; |
||
307 | /// <summary>true if your agent has Estate Manager rights on this region</summary> |
||
308 | public bool IsEstateManager; |
||
309 | /// <summary></summary> |
||
310 | public RegionFlags Flags; |
||
311 | /// <summary></summary> |
||
312 | public SimAccess Access; |
||
313 | /// <summary></summary> |
||
314 | public float BillableFactor; |
||
315 | /// <summary>Statistics information for this simulator and the |
||
316 | /// connection to the simulator, calculated by the simulator itself |
||
317 | /// and the library</summary> |
||
318 | public SimStats Stats; |
||
319 | /// <summary>The regions Unique ID</summary> |
||
320 | public UUID RegionID = UUID.Zero; |
||
321 | /// <summary>The physical data center the simulator is located</summary> |
||
322 | /// <remarks>Known values are: |
||
323 | /// <list type="table"> |
||
324 | /// <item>Dallas</item> |
||
325 | /// <item>Chandler</item> |
||
326 | /// <item>SF</item> |
||
327 | /// </list> |
||
328 | /// </remarks> |
||
329 | public string ColoLocation; |
||
330 | /// <summary>The CPU Class of the simulator</summary> |
||
331 | /// <remarks>Most full mainland/estate sims appear to be 5, |
||
332 | /// Homesteads and Openspace appear to be 501</remarks> |
||
333 | public int CPUClass; |
||
334 | /// <summary>The number of regions sharing the same CPU as this one</summary> |
||
335 | /// <remarks>"Full Sims" appear to be 1, Homesteads appear to be 4</remarks> |
||
336 | public int CPURatio; |
||
337 | /// <summary>The billing product name</summary> |
||
338 | /// <remarks>Known values are: |
||
339 | /// <list type="table"> |
||
340 | /// <item>Mainland / Full Region (Sku: 023)</item> |
||
341 | /// <item>Estate / Full Region (Sku: 024)</item> |
||
342 | /// <item>Estate / Openspace (Sku: 027)</item> |
||
343 | /// <item>Estate / Homestead (Sku: 029)</item> |
||
344 | /// <item>Mainland / Homestead (Sku: 129) (Linden Owned)</item> |
||
345 | /// <item>Mainland / Linden Homes (Sku: 131)</item> |
||
346 | /// </list> |
||
347 | /// </remarks> |
||
348 | public string ProductName; |
||
349 | /// <summary>The billing product SKU</summary> |
||
350 | /// <remarks>Known values are: |
||
351 | /// <list type="table"> |
||
352 | /// <item>023 Mainland / Full Region</item> |
||
353 | /// <item>024 Estate / Full Region</item> |
||
354 | /// <item>027 Estate / Openspace</item> |
||
355 | /// <item>029 Estate / Homestead</item> |
||
356 | /// <item>129 Mainland / Homestead (Linden Owned)</item> |
||
357 | /// <item>131 Linden Homes / Full Region</item> |
||
358 | /// </list> |
||
359 | /// </remarks> |
||
360 | public string ProductSku; |
||
361 | |||
362 | /// <summary> |
||
363 | /// Flags indicating which protocols this region supports |
||
364 | /// </summary> |
||
365 | public RegionProtocols Protocols; |
||
366 | |||
367 | |||
368 | /// <summary>The current sequence number for packets sent to this |
||
369 | /// simulator. Must be Interlocked before modifying. Only |
||
370 | /// useful for applications manipulating sequence numbers</summary> |
||
371 | public int Sequence; |
||
372 | |||
373 | /// <summary> |
||
374 | /// A thread-safe dictionary containing avatars in a simulator |
||
375 | /// </summary> |
||
376 | public InternalDictionary<uint, Avatar> ObjectsAvatars = new InternalDictionary<uint, Avatar>(); |
||
377 | |||
378 | /// <summary> |
||
379 | /// A thread-safe dictionary containing primitives in a simulator |
||
380 | /// </summary> |
||
381 | public InternalDictionary<uint, Primitive> ObjectsPrimitives = new InternalDictionary<uint, Primitive>(); |
||
382 | |||
383 | public readonly TerrainPatch[] Terrain; |
||
384 | |||
385 | public readonly Vector2[] WindSpeeds; |
||
386 | |||
387 | /// <summary> |
||
388 | /// Provides access to an internal thread-safe dictionary containing parcel |
||
389 | /// information found in this simulator |
||
390 | /// </summary> |
||
391 | public InternalDictionary<int, Parcel> Parcels |
||
392 | { |
||
393 | get |
||
394 | { |
||
395 | if (Client.Settings.POOL_PARCEL_DATA) |
||
396 | { |
||
397 | return DataPool.Parcels; |
||
398 | } |
||
399 | if (_Parcels == null) _Parcels = new InternalDictionary<int, Parcel>(); |
||
400 | return _Parcels; |
||
401 | } |
||
402 | } |
||
403 | private InternalDictionary<int, Parcel> _Parcels; |
||
404 | |||
405 | /// <summary> |
||
406 | /// Provides access to an internal thread-safe multidimensional array containing a x,y grid mapped |
||
407 | /// to each 64x64 parcel's LocalID. |
||
408 | /// </summary> |
||
409 | public int[,] ParcelMap |
||
410 | { |
||
411 | get |
||
412 | { |
||
413 | lock (this) |
||
414 | { |
||
415 | if (Client.Settings.POOL_PARCEL_DATA) |
||
416 | { |
||
417 | return DataPool.ParcelMap; |
||
418 | } |
||
419 | if (_ParcelMap == null) _ParcelMap = new int[64, 64]; |
||
420 | return _ParcelMap; |
||
421 | } |
||
422 | } |
||
423 | } |
||
424 | |||
425 | /// <summary> |
||
426 | /// Checks simulator parcel map to make sure it has downloaded all data successfully |
||
427 | /// </summary> |
||
428 | /// <returns>true if map is full (contains no 0's)</returns> |
||
429 | public bool IsParcelMapFull() |
||
430 | { |
||
431 | for (int y = 0; y < 64; y++) |
||
432 | { |
||
433 | for (int x = 0; x < 64; x++) |
||
434 | { |
||
435 | if (this.ParcelMap[y, x] == 0) |
||
436 | return false; |
||
437 | } |
||
438 | } |
||
439 | return true; |
||
440 | } |
||
441 | |||
442 | /// <summary> |
||
443 | /// Is it safe to send agent updates to this sim |
||
444 | /// AgentMovementComplete message received |
||
445 | /// </summary> |
||
446 | public bool AgentMovementComplete; |
||
447 | |||
448 | #endregion Public Members |
||
449 | |||
450 | #region Properties |
||
451 | |||
452 | /// <summary>The IP address and port of the server</summary> |
||
453 | public IPEndPoint IPEndPoint { get { return remoteEndPoint; } } |
||
454 | /// <summary>Whether there is a working connection to the simulator or |
||
455 | /// not</summary> |
||
456 | public bool Connected { get { return connected; } } |
||
457 | /// <summary>Coarse locations of avatars in this simulator</summary> |
||
458 | public InternalDictionary<UUID, Vector3> AvatarPositions { get { return avatarPositions; } } |
||
459 | /// <summary>AvatarPositions key representing TrackAgent target</summary> |
||
460 | public UUID PreyID { get { return preyID; } } |
||
461 | /// <summary>Indicates if UDP connection to the sim is fully established</summary> |
||
462 | public bool HandshakeComplete { get { return handshakeComplete; } } |
||
463 | |||
464 | #endregion Properties |
||
465 | |||
466 | #region Internal/Private Members |
||
467 | /// <summary>Used internally to track sim disconnections</summary> |
||
468 | internal bool DisconnectCandidate = false; |
||
469 | /// <summary>Event that is triggered when the simulator successfully |
||
470 | /// establishes a connection</summary> |
||
471 | internal ManualResetEvent ConnectedEvent = new ManualResetEvent(false); |
||
472 | /// <summary>Whether this sim is currently connected or not. Hooked up |
||
473 | /// to the property Connected</summary> |
||
474 | internal bool connected; |
||
475 | /// <summary>Coarse locations of avatars in this simulator</summary> |
||
476 | internal InternalDictionary<UUID, Vector3> avatarPositions = new InternalDictionary<UUID, Vector3>(); |
||
477 | /// <summary>AvatarPositions key representing TrackAgent target</summary> |
||
478 | internal UUID preyID = UUID.Zero; |
||
479 | /// <summary>Sequence numbers of packets we've received |
||
480 | /// (for duplicate checking)</summary> |
||
481 | internal IncomingPacketIDCollection PacketArchive; |
||
482 | /// <summary>Packets we sent out that need ACKs from the simulator</summary> |
||
483 | internal SortedDictionary<uint, NetworkManager.OutgoingPacket> NeedAck = new SortedDictionary<uint, NetworkManager.OutgoingPacket>(); |
||
484 | /// <summary>Sequence number for pause/resume</summary> |
||
485 | internal int pauseSerial; |
||
486 | /// <summary>Indicates if UDP connection to the sim is fully established</summary> |
||
487 | internal bool handshakeComplete; |
||
488 | |||
489 | private NetworkManager Network; |
||
490 | private Queue<long> InBytes, OutBytes; |
||
491 | // ACKs that are queued up to be sent to the simulator |
||
492 | private LocklessQueue<uint> PendingAcks = new LocklessQueue<uint>(); |
||
493 | private Timer AckTimer; |
||
494 | private Timer PingTimer; |
||
495 | private Timer StatsTimer; |
||
496 | // simulator <> parcel LocalID Map |
||
497 | private int[,] _ParcelMap; |
||
498 | public readonly SimulatorDataPool DataPool; |
||
499 | internal bool DownloadingParcelMap |
||
500 | { |
||
501 | get |
||
502 | { |
||
503 | return Client.Settings.POOL_PARCEL_DATA ? DataPool.DownloadingParcelMap : _DownloadingParcelMap; |
||
504 | } |
||
505 | set |
||
506 | { |
||
507 | if (Client.Settings.POOL_PARCEL_DATA) DataPool.DownloadingParcelMap = value; |
||
508 | _DownloadingParcelMap = value; |
||
509 | } |
||
510 | } |
||
511 | |||
512 | internal bool _DownloadingParcelMap = false; |
||
513 | |||
514 | |||
515 | private ManualResetEvent GotUseCircuitCodeAck = new ManualResetEvent(false); |
||
516 | #endregion Internal/Private Members |
||
517 | |||
518 | /// <summary> |
||
519 | /// |
||
520 | /// </summary> |
||
521 | /// <param name="client">Reference to the GridClient object</param> |
||
522 | /// <param name="address">IPEndPoint of the simulator</param> |
||
523 | /// <param name="handle">handle of the simulator</param> |
||
524 | public Simulator(GridClient client, IPEndPoint address, ulong handle) |
||
525 | : base(address) |
||
526 | { |
||
527 | Client = client; |
||
528 | if (Client.Settings.POOL_PARCEL_DATA || Client.Settings.CACHE_PRIMITIVES) |
||
529 | { |
||
530 | SimulatorDataPool.SimulatorAdd(this); |
||
531 | DataPool = SimulatorDataPool.GetSimulatorData(Handle); |
||
532 | } |
||
533 | |||
534 | Handle = handle; |
||
535 | Network = Client.Network; |
||
536 | PacketArchive = new IncomingPacketIDCollection(Settings.PACKET_ARCHIVE_SIZE); |
||
537 | InBytes = new Queue<long>(Client.Settings.STATS_QUEUE_SIZE); |
||
538 | OutBytes = new Queue<long>(Client.Settings.STATS_QUEUE_SIZE); |
||
539 | |||
540 | if (client.Settings.STORE_LAND_PATCHES) |
||
541 | { |
||
542 | Terrain = new TerrainPatch[16 * 16]; |
||
543 | WindSpeeds = new Vector2[16 * 16]; |
||
544 | } |
||
545 | } |
||
546 | |||
547 | /// <summary> |
||
548 | /// Called when this Simulator object is being destroyed |
||
549 | /// </summary> |
||
550 | public void Dispose() |
||
551 | { |
||
552 | Dispose(true); |
||
553 | GC.SuppressFinalize(this); |
||
554 | } |
||
555 | |||
556 | public virtual void Dispose(bool disposing) |
||
557 | { |
||
558 | if (disposing) |
||
559 | { |
||
560 | if (AckTimer != null) |
||
561 | AckTimer.Dispose(); |
||
562 | if (PingTimer != null) |
||
563 | PingTimer.Dispose(); |
||
564 | if (StatsTimer != null) |
||
565 | StatsTimer.Dispose(); |
||
566 | if (ConnectedEvent != null) |
||
567 | ConnectedEvent.Close(); |
||
568 | |||
569 | // Force all the CAPS connections closed for this simulator |
||
570 | if (Caps != null) |
||
571 | Caps.Disconnect(true); |
||
572 | } |
||
573 | } |
||
574 | |||
575 | /// <summary> |
||
576 | /// Attempt to connect to this simulator |
||
577 | /// </summary> |
||
578 | /// <param name="moveToSim">Whether to move our agent in to this sim or not</param> |
||
579 | /// <returns>True if the connection succeeded or connection status is |
||
580 | /// unknown, false if there was a failure</returns> |
||
581 | public bool Connect(bool moveToSim) |
||
582 | { |
||
583 | handshakeComplete = false; |
||
584 | |||
585 | if (connected) |
||
586 | { |
||
587 | UseCircuitCode(true); |
||
588 | if (moveToSim) Client.Self.CompleteAgentMovement(this); |
||
589 | return true; |
||
590 | } |
||
591 | |||
592 | #region Start Timers |
||
593 | |||
594 | // Timer for sending out queued packet acknowledgements |
||
595 | if (AckTimer == null) |
||
596 | AckTimer = new Timer(AckTimer_Elapsed, null, Settings.NETWORK_TICK_INTERVAL, Timeout.Infinite); |
||
597 | |||
598 | // Timer for recording simulator connection statistics |
||
599 | if (StatsTimer == null) |
||
600 | StatsTimer = new Timer(StatsTimer_Elapsed, null, 1000, 1000); |
||
601 | |||
602 | // Timer for periodically pinging the simulator |
||
603 | if (PingTimer == null && Client.Settings.SEND_PINGS) |
||
604 | PingTimer = new Timer(PingTimer_Elapsed, null, Settings.PING_INTERVAL, Settings.PING_INTERVAL); |
||
605 | |||
606 | #endregion Start Timers |
||
607 | |||
608 | Logger.Log("Connecting to " + this.ToString(), Helpers.LogLevel.Info, Client); |
||
609 | |||
610 | try |
||
611 | { |
||
612 | // Create the UDP connection |
||
613 | Start(); |
||
614 | |||
615 | // Mark ourselves as connected before firing everything else up |
||
616 | connected = true; |
||
617 | |||
618 | // Initiate connection |
||
619 | UseCircuitCode(true); |
||
620 | |||
621 | Stats.ConnectTime = Environment.TickCount; |
||
622 | |||
623 | // Move our agent in to the sim to complete the connection |
||
624 | if (moveToSim) Client.Self.CompleteAgentMovement(this); |
||
625 | |||
626 | if (!ConnectedEvent.WaitOne(Client.Settings.LOGIN_TIMEOUT, false)) |
||
627 | { |
||
628 | Logger.Log("Giving up on waiting for RegionHandshake for " + this.ToString(), |
||
629 | Helpers.LogLevel.Warning, Client); |
||
630 | //Remove the simulator from the list, not useful if we haven't recieved the RegionHandshake |
||
631 | lock (Client.Network.Simulators) { |
||
632 | Client.Network.Simulators.Remove(this); |
||
633 | } |
||
634 | } |
||
635 | |||
636 | if (Client.Settings.SEND_AGENT_THROTTLE) |
||
637 | Client.Throttle.Set(this); |
||
638 | |||
639 | if (Client.Settings.SEND_AGENT_UPDATES) |
||
640 | Client.Self.Movement.SendUpdate(true, this); |
||
641 | |||
642 | return true; |
||
643 | } |
||
644 | catch (Exception e) |
||
645 | { |
||
646 | Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); |
||
647 | } |
||
648 | |||
649 | return false; |
||
650 | } |
||
651 | |||
652 | /// <summary> |
||
653 | /// Initiates connection to the simulator |
||
654 | /// </summary> |
||
655 | /// <param name="waitForAck">Should we block until ack for this packet is recieved</param> |
||
656 | public void UseCircuitCode(bool waitForAck) |
||
657 | { |
||
658 | // Send the UseCircuitCode packet to initiate the connection |
||
659 | UseCircuitCodePacket use = new UseCircuitCodePacket(); |
||
660 | use.CircuitCode.Code = Network.CircuitCode; |
||
661 | use.CircuitCode.ID = Client.Self.AgentID; |
||
662 | use.CircuitCode.SessionID = Client.Self.SessionID; |
||
663 | |||
664 | if (waitForAck) |
||
665 | { |
||
666 | GotUseCircuitCodeAck.Reset(); |
||
667 | } |
||
668 | |||
669 | // Send the initial packet out |
||
670 | SendPacket(use); |
||
671 | |||
672 | if (waitForAck) |
||
673 | { |
||
674 | if (!GotUseCircuitCodeAck.WaitOne(Client.Settings.LOGIN_TIMEOUT, false)) |
||
675 | { |
||
676 | Logger.Log("Failed to get ACK for UseCircuitCode packet", Helpers.LogLevel.Error, Client); |
||
677 | } |
||
678 | } |
||
679 | } |
||
680 | |||
681 | public void SetSeedCaps(string seedcaps) |
||
682 | { |
||
683 | if (Caps != null) |
||
684 | { |
||
685 | if (Caps._SeedCapsURI == seedcaps) return; |
||
686 | |||
687 | Logger.Log("Unexpected change of seed capability", Helpers.LogLevel.Warning, Client); |
||
688 | Caps.Disconnect(true); |
||
689 | Caps = null; |
||
690 | } |
||
691 | |||
692 | if (Client.Settings.ENABLE_CAPS) |
||
693 | { |
||
694 | // Connect to the new CAPS system |
||
695 | if (!String.IsNullOrEmpty(seedcaps)) |
||
696 | Caps = new Caps(this, seedcaps); |
||
697 | else |
||
698 | Logger.Log("Setting up a sim without a valid capabilities server!", Helpers.LogLevel.Error, Client); |
||
699 | } |
||
700 | |||
701 | } |
||
702 | |||
703 | /// <summary> |
||
704 | /// Disconnect from this simulator |
||
705 | /// </summary> |
||
706 | public void Disconnect(bool sendCloseCircuit) |
||
707 | { |
||
708 | if (connected) |
||
709 | { |
||
710 | connected = false; |
||
711 | |||
712 | // Destroy the timers |
||
713 | if (AckTimer != null) AckTimer.Dispose(); |
||
714 | if (StatsTimer != null) StatsTimer.Dispose(); |
||
715 | if (PingTimer != null) PingTimer.Dispose(); |
||
716 | |||
717 | AckTimer = null; |
||
718 | StatsTimer = null; |
||
719 | PingTimer = null; |
||
720 | |||
721 | // Kill the current CAPS system |
||
722 | if (Caps != null) |
||
723 | { |
||
724 | Caps.Disconnect(true); |
||
725 | Caps = null; |
||
726 | } |
||
727 | |||
728 | if (sendCloseCircuit) |
||
729 | { |
||
730 | // Try to send the CloseCircuit notice |
||
731 | CloseCircuitPacket close = new CloseCircuitPacket(); |
||
732 | UDPPacketBuffer buf = new UDPPacketBuffer(remoteEndPoint); |
||
733 | byte[] data = close.ToBytes(); |
||
734 | Buffer.BlockCopy(data, 0, buf.Data, 0, data.Length); |
||
735 | buf.DataLength = data.Length; |
||
736 | |||
737 | AsyncBeginSend(buf); |
||
738 | } |
||
739 | |||
740 | if (Client.Settings.POOL_PARCEL_DATA || Client.Settings.CACHE_PRIMITIVES) |
||
741 | { |
||
742 | SimulatorDataPool.SimulatorRelease(this); |
||
743 | } |
||
744 | |||
745 | // Shut the socket communication down |
||
746 | Stop(); |
||
747 | } |
||
748 | } |
||
749 | |||
750 | /// <summary> |
||
751 | /// Instructs the simulator to stop sending update (and possibly other) packets |
||
752 | /// </summary> |
||
753 | public void Pause() |
||
754 | { |
||
755 | AgentPausePacket pause = new AgentPausePacket(); |
||
756 | pause.AgentData.AgentID = Client.Self.AgentID; |
||
757 | pause.AgentData.SessionID = Client.Self.SessionID; |
||
758 | pause.AgentData.SerialNum = (uint)Interlocked.Exchange(ref pauseSerial, pauseSerial + 1); |
||
759 | |||
760 | Client.Network.SendPacket(pause, this); |
||
761 | } |
||
762 | |||
763 | /// <summary> |
||
764 | /// Instructs the simulator to resume sending update packets (unpause) |
||
765 | /// </summary> |
||
766 | public void Resume() |
||
767 | { |
||
768 | AgentResumePacket resume = new AgentResumePacket(); |
||
769 | resume.AgentData.AgentID = Client.Self.AgentID; |
||
770 | resume.AgentData.SessionID = Client.Self.SessionID; |
||
771 | resume.AgentData.SerialNum = (uint)Interlocked.Exchange(ref pauseSerial, pauseSerial + 1); |
||
772 | |||
773 | Client.Network.SendPacket(resume, this); |
||
774 | } |
||
775 | |||
776 | /// <summary> |
||
777 | /// Retrieve the terrain height at a given coordinate |
||
778 | /// </summary> |
||
779 | /// <param name="x">Sim X coordinate, valid range is from 0 to 255</param> |
||
780 | /// <param name="y">Sim Y coordinate, valid range is from 0 to 255</param> |
||
781 | /// <param name="height">The terrain height at the given point if the |
||
782 | /// lookup was successful, otherwise 0.0f</param> |
||
783 | /// <returns>True if the lookup was successful, otherwise false</returns> |
||
784 | public bool TerrainHeightAtPoint(int x, int y, out float height) |
||
785 | { |
||
786 | if (Terrain != null && x >= 0 && x < 256 && y >= 0 && y < 256) |
||
787 | { |
||
788 | int patchX = x / 16; |
||
789 | int patchY = y / 16; |
||
790 | x = x % 16; |
||
791 | y = y % 16; |
||
792 | |||
793 | TerrainPatch patch = Terrain[patchY * 16 + patchX]; |
||
794 | if (patch != null) |
||
795 | { |
||
796 | height = patch.Data[y * 16 + x]; |
||
797 | return true; |
||
798 | } |
||
799 | } |
||
800 | |||
801 | height = 0.0f; |
||
802 | return false; |
||
803 | } |
||
804 | |||
805 | #region Packet Sending |
||
806 | |||
807 | /// <summary> |
||
808 | /// Sends a packet |
||
809 | /// </summary> |
||
810 | /// <param name="packet">Packet to be sent</param> |
||
811 | public void SendPacket(Packet packet) |
||
812 | { |
||
813 | // DEBUG: This can go away after we are sure nothing in the library is trying to do this |
||
814 | if (packet.Header.AppendedAcks || (packet.Header.AckList != null && packet.Header.AckList.Length > 0)) |
||
815 | Logger.Log("Attempting to send packet " + packet.Type + " with ACKs appended before serialization", Helpers.LogLevel.Error); |
||
816 | |||
817 | if (packet.HasVariableBlocks) |
||
818 | { |
||
819 | byte[][] datas; |
||
820 | try { datas = packet.ToBytesMultiple(); } |
||
821 | catch (NullReferenceException) |
||
822 | { |
||
823 | Logger.Log("Failed to serialize " + packet.Type + " packet to one or more payloads due to a missing block or field. StackTrace: " + |
||
824 | Environment.StackTrace, Helpers.LogLevel.Error); |
||
825 | return; |
||
826 | } |
||
827 | int packetCount = datas.Length; |
||
828 | |||
829 | if (packetCount > 1) |
||
830 | Logger.DebugLog("Split " + packet.Type + " packet into " + packetCount + " packets"); |
||
831 | |||
832 | for (int i = 0; i < packetCount; i++) |
||
833 | { |
||
834 | byte[] data = datas[i]; |
||
835 | SendPacketData(data, data.Length, packet.Type, packet.Header.Zerocoded); |
||
836 | } |
||
837 | } |
||
838 | else |
||
839 | { |
||
840 | byte[] data = packet.ToBytes(); |
||
841 | SendPacketData(data, data.Length, packet.Type, packet.Header.Zerocoded); |
||
842 | } |
||
843 | } |
||
844 | |||
845 | public void SendPacketData(byte[] data, int dataLength, PacketType type, bool doZerocode) |
||
846 | { |
||
847 | UDPPacketBuffer buffer = new UDPPacketBuffer(remoteEndPoint, Packet.MTU); |
||
848 | |||
849 | // Zerocode if needed |
||
850 | if (doZerocode) |
||
851 | { |
||
852 | try { dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); } |
||
853 | catch (IndexOutOfRangeException) |
||
854 | { |
||
855 | // The packet grew larger than Packet.MTU bytes while zerocoding. |
||
856 | // Remove the MSG_ZEROCODED flag and send the unencoded data |
||
857 | // instead |
||
858 | data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); |
||
859 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); |
||
860 | } |
||
861 | } |
||
862 | else |
||
863 | { |
||
864 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); |
||
865 | } |
||
866 | buffer.DataLength = dataLength; |
||
867 | |||
868 | #region Queue or Send |
||
869 | |||
870 | NetworkManager.OutgoingPacket outgoingPacket = new NetworkManager.OutgoingPacket(this, buffer, type); |
||
871 | |||
872 | // Send ACK and logout packets directly, everything else goes through the queue |
||
873 | if (Client.Settings.THROTTLE_OUTGOING_PACKETS == false || |
||
874 | type == PacketType.PacketAck || |
||
875 | type == PacketType.LogoutRequest) |
||
876 | { |
||
877 | SendPacketFinal(outgoingPacket); |
||
878 | } |
||
879 | else |
||
880 | { |
||
881 | Network.PacketOutbox.Enqueue(outgoingPacket); |
||
882 | } |
||
883 | |||
884 | #endregion Queue or Send |
||
885 | |||
886 | #region Stats Tracking |
||
887 | if (Client.Settings.TRACK_UTILIZATION) |
||
888 | { |
||
889 | Client.Stats.Update(type.ToString(), OpenMetaverse.Stats.Type.Packet, dataLength, 0); |
||
890 | } |
||
891 | #endregion |
||
892 | } |
||
893 | |||
894 | internal void SendPacketFinal(NetworkManager.OutgoingPacket outgoingPacket) |
||
895 | { |
||
896 | UDPPacketBuffer buffer = outgoingPacket.Buffer; |
||
897 | byte flags = buffer.Data[0]; |
||
898 | bool isResend = (flags & Helpers.MSG_RESENT) != 0; |
||
899 | bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0; |
||
900 | |||
901 | // Keep track of when this packet was sent out (right now) |
||
902 | outgoingPacket.TickCount = Environment.TickCount; |
||
903 | |||
904 | #region ACK Appending |
||
905 | |||
906 | int dataLength = buffer.DataLength; |
||
907 | |||
908 | // Keep appending ACKs until there is no room left in the packet or there are |
||
909 | // no more ACKs to append |
||
910 | uint ackCount = 0; |
||
911 | uint ack; |
||
912 | while (dataLength + 5 < Packet.MTU && PendingAcks.TryDequeue(out ack)) |
||
913 | { |
||
914 | Utils.UIntToBytesBig(ack, buffer.Data, dataLength); |
||
915 | dataLength += 4; |
||
916 | ++ackCount; |
||
917 | } |
||
918 | |||
919 | if (ackCount > 0) |
||
920 | { |
||
921 | // Set the last byte of the packet equal to the number of appended ACKs |
||
922 | buffer.Data[dataLength++] = (byte)ackCount; |
||
923 | // Set the appended ACKs flag on this packet |
||
924 | buffer.Data[0] |= Helpers.MSG_APPENDED_ACKS; |
||
925 | } |
||
926 | |||
927 | buffer.DataLength = dataLength; |
||
928 | |||
929 | #endregion ACK Appending |
||
930 | |||
931 | if (!isResend) |
||
932 | { |
||
933 | // Not a resend, assign a new sequence number |
||
934 | uint sequenceNumber = (uint)Interlocked.Increment(ref Sequence); |
||
935 | Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); |
||
936 | outgoingPacket.SequenceNumber = sequenceNumber; |
||
937 | |||
938 | if (isReliable) |
||
939 | { |
||
940 | // Add this packet to the list of ACK responses we are waiting on from the server |
||
941 | lock (NeedAck) NeedAck[sequenceNumber] = outgoingPacket; |
||
942 | } |
||
943 | } |
||
944 | |||
945 | // Put the UDP payload on the wire |
||
946 | AsyncBeginSend(buffer); |
||
947 | } |
||
948 | |||
949 | /// <summary> |
||
950 | /// |
||
951 | /// </summary> |
||
952 | public void SendPing() |
||
953 | { |
||
954 | uint oldestUnacked = 0; |
||
955 | |||
956 | // Get the oldest NeedAck value, the first entry in the sorted dictionary |
||
957 | lock (NeedAck) |
||
958 | { |
||
959 | if (NeedAck.Count > 0) |
||
960 | { |
||
961 | SortedDictionary<uint, NetworkManager.OutgoingPacket>.KeyCollection.Enumerator en = NeedAck.Keys.GetEnumerator(); |
||
962 | en.MoveNext(); |
||
963 | oldestUnacked = en.Current; |
||
964 | } |
||
965 | } |
||
966 | |||
967 | //if (oldestUnacked != 0) |
||
968 | // Logger.DebugLog("Sending ping with oldestUnacked=" + oldestUnacked); |
||
969 | |||
970 | StartPingCheckPacket ping = new StartPingCheckPacket(); |
||
971 | ping.PingID.PingID = Stats.LastPingID++; |
||
972 | ping.PingID.OldestUnacked = oldestUnacked; |
||
973 | ping.Header.Reliable = false; |
||
974 | SendPacket(ping); |
||
975 | Stats.LastPingSent = Environment.TickCount; |
||
976 | } |
||
977 | |||
978 | #endregion Packet Sending |
||
979 | |||
980 | /// <summary> |
||
981 | /// Returns Simulator Name as a String |
||
982 | /// </summary> |
||
983 | /// <returns></returns> |
||
984 | public override string ToString() |
||
985 | { |
||
986 | if (!String.IsNullOrEmpty(Name)) |
||
987 | return String.Format("{0} ({1})", Name, remoteEndPoint); |
||
988 | else |
||
989 | return String.Format("({0})", remoteEndPoint); |
||
990 | } |
||
991 | |||
992 | /// <summary> |
||
993 | /// |
||
994 | /// </summary> |
||
995 | /// <returns></returns> |
||
996 | public override int GetHashCode() |
||
997 | { |
||
998 | return Handle.GetHashCode(); |
||
999 | } |
||
1000 | |||
1001 | /// <summary> |
||
1002 | /// |
||
1003 | /// </summary> |
||
1004 | /// <param name="obj"></param> |
||
1005 | /// <returns></returns> |
||
1006 | public override bool Equals(object obj) |
||
1007 | { |
||
1008 | Simulator sim = obj as Simulator; |
||
1009 | if (sim == null) |
||
1010 | return false; |
||
1011 | return (remoteEndPoint.Equals(sim.remoteEndPoint)); |
||
1012 | } |
||
1013 | |||
1014 | public static bool operator ==(Simulator lhs, Simulator rhs) |
||
1015 | { |
||
1016 | // If both are null, or both are same instance, return true |
||
1017 | if (System.Object.ReferenceEquals(lhs, rhs)) |
||
1018 | { |
||
1019 | return true; |
||
1020 | } |
||
1021 | |||
1022 | // If one is null, but not both, return false. |
||
1023 | if (((object)lhs == null) || ((object)rhs == null)) |
||
1024 | { |
||
1025 | return false; |
||
1026 | } |
||
1027 | |||
1028 | return lhs.remoteEndPoint.Equals(rhs.remoteEndPoint); |
||
1029 | } |
||
1030 | |||
1031 | public static bool operator !=(Simulator lhs, Simulator rhs) |
||
1032 | { |
||
1033 | return !(lhs == rhs); |
||
1034 | } |
||
1035 | |||
1036 | protected override void PacketReceived(UDPPacketBuffer buffer) |
||
1037 | { |
||
1038 | Packet packet = null; |
||
1039 | |||
1040 | // Check if this packet came from the server we expected it to come from |
||
1041 | if (!remoteEndPoint.Address.Equals(((IPEndPoint)buffer.RemoteEndPoint).Address)) |
||
1042 | { |
||
1043 | Logger.Log("Received " + buffer.DataLength + " bytes of data from unrecognized source " + |
||
1044 | ((IPEndPoint)buffer.RemoteEndPoint).ToString(), Helpers.LogLevel.Warning, Client); |
||
1045 | return; |
||
1046 | } |
||
1047 | |||
1048 | // Update the disconnect flag so this sim doesn't time out |
||
1049 | DisconnectCandidate = false; |
||
1050 | |||
1051 | #region Packet Decoding |
||
1052 | |||
1053 | int packetEnd = buffer.DataLength - 1; |
||
1054 | |||
1055 | try |
||
1056 | { |
||
1057 | packet = Packet.BuildPacket(buffer.Data, ref packetEnd, |
||
1058 | // Only allocate a buffer for zerodecoding if the packet is zerocoded |
||
1059 | ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[8192] : null); |
||
1060 | } |
||
1061 | catch (MalformedDataException) |
||
1062 | { |
||
1063 | Logger.Log(String.Format("Malformed data, cannot parse packet:\n{0}", |
||
1064 | Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)), Helpers.LogLevel.Error); |
||
1065 | } |
||
1066 | |||
1067 | // Fail-safe check |
||
1068 | if (packet == null) |
||
1069 | { |
||
1070 | Logger.Log("Couldn't build a message from the incoming data", Helpers.LogLevel.Warning, Client); |
||
1071 | return; |
||
1072 | } |
||
1073 | |||
1074 | Interlocked.Add(ref Stats.RecvBytes, buffer.DataLength); |
||
1075 | Interlocked.Increment(ref Stats.RecvPackets); |
||
1076 | |||
1077 | #endregion Packet Decoding |
||
1078 | |||
1079 | if (packet.Header.Resent) |
||
1080 | Interlocked.Increment(ref Stats.ReceivedResends); |
||
1081 | |||
1082 | #region ACK Receiving |
||
1083 | |||
1084 | // Handle appended ACKs |
||
1085 | if (packet.Header.AppendedAcks && packet.Header.AckList != null) |
||
1086 | { |
||
1087 | lock (NeedAck) |
||
1088 | { |
||
1089 | for (int i = 0; i < packet.Header.AckList.Length; i++) |
||
1090 | { |
||
1091 | if (NeedAck.ContainsKey(packet.Header.AckList[i]) && NeedAck[packet.Header.AckList[i]].Type == PacketType.UseCircuitCode) |
||
1092 | { |
||
1093 | GotUseCircuitCodeAck.Set(); |
||
1094 | } |
||
1095 | NeedAck.Remove(packet.Header.AckList[i]); |
||
1096 | } |
||
1097 | } |
||
1098 | } |
||
1099 | |||
1100 | // Handle PacketAck packets |
||
1101 | if (packet.Type == PacketType.PacketAck) |
||
1102 | { |
||
1103 | PacketAckPacket ackPacket = (PacketAckPacket)packet; |
||
1104 | |||
1105 | lock (NeedAck) |
||
1106 | { |
||
1107 | for (int i = 0; i < ackPacket.Packets.Length; i++) |
||
1108 | { |
||
1109 | if (NeedAck.ContainsKey(ackPacket.Packets[i].ID) && NeedAck[ackPacket.Packets[i].ID].Type == PacketType.UseCircuitCode) |
||
1110 | { |
||
1111 | GotUseCircuitCodeAck.Set(); |
||
1112 | } |
||
1113 | NeedAck.Remove(ackPacket.Packets[i].ID); |
||
1114 | } |
||
1115 | } |
||
1116 | } |
||
1117 | |||
1118 | #endregion ACK Receiving |
||
1119 | |||
1120 | if (packet.Header.Reliable) |
||
1121 | { |
||
1122 | #region ACK Sending |
||
1123 | |||
1124 | // Add this packet to the list of ACKs that need to be sent out |
||
1125 | uint sequence = (uint)packet.Header.Sequence; |
||
1126 | PendingAcks.Enqueue(sequence); |
||
1127 | |||
1128 | // Send out ACKs if we have a lot of them |
||
1129 | if (PendingAcks.Count >= Client.Settings.MAX_PENDING_ACKS) |
||
1130 | SendAcks(); |
||
1131 | |||
1132 | #endregion ACK Sending |
||
1133 | |||
1134 | // Check the archive of received packet IDs to see whether we already received this packet |
||
1135 | if (!PacketArchive.TryEnqueue(packet.Header.Sequence)) |
||
1136 | { |
||
1137 | if (packet.Header.Resent) |
||
1138 | Logger.DebugLog( |
||
1139 | string.Format( |
||
1140 | "Received a resend of already processed packet #{0}, type: {1} from {2}", |
||
1141 | packet.Header.Sequence, packet.Type, Name)); |
||
1142 | else |
||
1143 | Logger.Log( |
||
1144 | string.Format( |
||
1145 | "Received a duplicate (not marked as resend) of packet #{0}, type: {1} for {2} from {3}", |
||
1146 | packet.Header.Sequence, packet.Type, Client.Self.Name, Name), |
||
1147 | Helpers.LogLevel.Warning); |
||
1148 | |||
1149 | // Avoid firing a callback twice for the same packet |
||
1150 | return; |
||
1151 | } |
||
1152 | } |
||
1153 | |||
1154 | #region Inbox Insertion |
||
1155 | |||
1156 | NetworkManager.IncomingPacket incomingPacket; |
||
1157 | incomingPacket.Simulator = this; |
||
1158 | incomingPacket.Packet = packet; |
||
1159 | |||
1160 | Network.PacketInbox.Enqueue(incomingPacket); |
||
1161 | |||
1162 | #endregion Inbox Insertion |
||
1163 | |||
1164 | #region Stats Tracking |
||
1165 | if (Client.Settings.TRACK_UTILIZATION) |
||
1166 | { |
||
1167 | Client.Stats.Update(packet.Type.ToString(), OpenMetaverse.Stats.Type.Packet, 0, packet.Length); |
||
1168 | } |
||
1169 | #endregion |
||
1170 | } |
||
1171 | |||
1172 | protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent) |
||
1173 | { |
||
1174 | // Stats tracking |
||
1175 | Interlocked.Add(ref Stats.SentBytes, bytesSent); |
||
1176 | Interlocked.Increment(ref Stats.SentPackets); |
||
1177 | |||
1178 | Client.Network.RaisePacketSentEvent(buffer.Data, bytesSent, this); |
||
1179 | } |
||
1180 | |||
1181 | |||
1182 | /// <summary> |
||
1183 | /// Sends out pending acknowledgements |
||
1184 | /// </summary> |
||
1185 | /// <returns>Number of ACKs sent</returns> |
||
1186 | private int SendAcks() |
||
1187 | { |
||
1188 | uint ack; |
||
1189 | int ackCount = 0; |
||
1190 | |||
1191 | if (PendingAcks.TryDequeue(out ack)) |
||
1192 | { |
||
1193 | List<PacketAckPacket.PacketsBlock> blocks = new List<PacketAckPacket.PacketsBlock>(); |
||
1194 | PacketAckPacket.PacketsBlock block = new PacketAckPacket.PacketsBlock(); |
||
1195 | block.ID = ack; |
||
1196 | blocks.Add(block); |
||
1197 | |||
1198 | while (PendingAcks.TryDequeue(out ack)) |
||
1199 | { |
||
1200 | block = new PacketAckPacket.PacketsBlock(); |
||
1201 | block.ID = ack; |
||
1202 | blocks.Add(block); |
||
1203 | } |
||
1204 | |||
1205 | PacketAckPacket packet = new PacketAckPacket(); |
||
1206 | packet.Header.Reliable = false; |
||
1207 | packet.Packets = blocks.ToArray(); |
||
1208 | |||
1209 | ackCount = blocks.Count; |
||
1210 | SendPacket(packet); |
||
1211 | } |
||
1212 | |||
1213 | return ackCount; |
||
1214 | } |
||
1215 | |||
1216 | /// <summary> |
||
1217 | /// Resend unacknowledged packets |
||
1218 | /// </summary> |
||
1219 | private void ResendUnacked() |
||
1220 | { |
||
1221 | if (NeedAck.Count > 0) |
||
1222 | { |
||
1223 | NetworkManager.OutgoingPacket[] array; |
||
1224 | |||
1225 | lock (NeedAck) |
||
1226 | { |
||
1227 | // Create a temporary copy of the outgoing packets array to iterate over |
||
1228 | array = new NetworkManager.OutgoingPacket[NeedAck.Count]; |
||
1229 | NeedAck.Values.CopyTo(array, 0); |
||
1230 | } |
||
1231 | |||
1232 | int now = Environment.TickCount; |
||
1233 | |||
1234 | // Resend packets |
||
1235 | for (int i = 0; i < array.Length; i++) |
||
1236 | { |
||
1237 | NetworkManager.OutgoingPacket outgoing = array[i]; |
||
1238 | |||
1239 | if (outgoing.TickCount != 0 && now - outgoing.TickCount > Client.Settings.RESEND_TIMEOUT) |
||
1240 | { |
||
1241 | if (outgoing.ResendCount < Client.Settings.MAX_RESEND_COUNT) |
||
1242 | { |
||
1243 | if (Client.Settings.LOG_RESENDS) |
||
1244 | { |
||
1245 | Logger.DebugLog(String.Format("Resending {2} packet #{0}, {1}ms have passed", |
||
1246 | outgoing.SequenceNumber, now - outgoing.TickCount, outgoing.Type), Client); |
||
1247 | } |
||
1248 | |||
1249 | // The TickCount will be set to the current time when the packet |
||
1250 | // is actually sent out again |
||
1251 | outgoing.TickCount = 0; |
||
1252 | |||
1253 | // Set the resent flag |
||
1254 | outgoing.Buffer.Data[0] = (byte)(outgoing.Buffer.Data[0] | Helpers.MSG_RESENT); |
||
1255 | |||
1256 | // Stats tracking |
||
1257 | Interlocked.Increment(ref outgoing.ResendCount); |
||
1258 | Interlocked.Increment(ref Stats.ResentPackets); |
||
1259 | |||
1260 | SendPacketFinal(outgoing); |
||
1261 | } |
||
1262 | else |
||
1263 | { |
||
1264 | Logger.DebugLog(String.Format("Dropping packet #{0} after {1} failed attempts", |
||
1265 | outgoing.SequenceNumber, outgoing.ResendCount)); |
||
1266 | |||
1267 | lock (NeedAck) NeedAck.Remove(outgoing.SequenceNumber); |
||
1268 | } |
||
1269 | } |
||
1270 | } |
||
1271 | } |
||
1272 | } |
||
1273 | |||
1274 | private void AckTimer_Elapsed(object obj) |
||
1275 | { |
||
1276 | SendAcks(); |
||
1277 | ResendUnacked(); |
||
1278 | |||
1279 | // Start the ACK handling functions again after NETWORK_TICK_INTERVAL milliseconds |
||
1280 | if (null == AckTimer) return; |
||
1281 | try { AckTimer.Change(Settings.NETWORK_TICK_INTERVAL, Timeout.Infinite); } |
||
1282 | catch (Exception) { } |
||
1283 | } |
||
1284 | |||
1285 | private void StatsTimer_Elapsed(object obj) |
||
1286 | { |
||
1287 | long old_in = 0, old_out = 0; |
||
1288 | long recv = Stats.RecvBytes; |
||
1289 | long sent = Stats.SentBytes; |
||
1290 | |||
1291 | if (InBytes.Count >= Client.Settings.STATS_QUEUE_SIZE) |
||
1292 | old_in = InBytes.Dequeue(); |
||
1293 | if (OutBytes.Count >= Client.Settings.STATS_QUEUE_SIZE) |
||
1294 | old_out = OutBytes.Dequeue(); |
||
1295 | |||
1296 | InBytes.Enqueue(recv); |
||
1297 | OutBytes.Enqueue(sent); |
||
1298 | |||
1299 | if (old_in > 0 && old_out > 0) |
||
1300 | { |
||
1301 | Stats.IncomingBPS = (int)(recv - old_in) / Client.Settings.STATS_QUEUE_SIZE; |
||
1302 | Stats.OutgoingBPS = (int)(sent - old_out) / Client.Settings.STATS_QUEUE_SIZE; |
||
1303 | //Client.Log("Incoming: " + IncomingBPS + " Out: " + OutgoingBPS + |
||
1304 | // " Lag: " + LastLag + " Pings: " + ReceivedPongs + |
||
1305 | // "/" + SentPings, Helpers.LogLevel.Debug); |
||
1306 | } |
||
1307 | } |
||
1308 | |||
1309 | private void PingTimer_Elapsed(object obj) |
||
1310 | { |
||
1311 | SendPing(); |
||
1312 | Interlocked.Increment(ref Stats.SentPings); |
||
1313 | } |
||
1314 | } |
||
1315 | |||
1316 | public sealed class IncomingPacketIDCollection |
||
1317 | { |
||
1318 | readonly uint[] Items; |
||
1319 | HashSet<uint> hashSet; |
||
1320 | int first; |
||
1321 | int next; |
||
1322 | int capacity; |
||
1323 | |||
1324 | public IncomingPacketIDCollection(int capacity) |
||
1325 | { |
||
1326 | this.capacity = capacity; |
||
1327 | Items = new uint[capacity]; |
||
1328 | hashSet = new HashSet<uint>(); |
||
1329 | } |
||
1330 | |||
1331 | public bool TryEnqueue(uint ack) |
||
1332 | { |
||
1333 | lock (hashSet) |
||
1334 | { |
||
1335 | if (hashSet.Add(ack)) |
||
1336 | { |
||
1337 | Items[next] = ack; |
||
1338 | next = (next + 1) % capacity; |
||
1339 | if (next == first) |
||
1340 | { |
||
1341 | hashSet.Remove(Items[first]); |
||
1342 | first = (first + 1) % capacity; |
||
1343 | } |
||
1344 | |||
1345 | return true; |
||
1346 | } |
||
1347 | } |
||
1348 | |||
1349 | return false; |
||
1350 | } |
||
1351 | } |
||
1352 | |||
1353 | public class SimulatorDataPool |
||
1354 | { |
||
1355 | private static Timer InactiveSimReaper; |
||
1356 | |||
1357 | private static void RemoveOldSims(object state) |
||
1358 | { |
||
1359 | lock (SimulatorDataPools) |
||
1360 | { |
||
1361 | int SimTimeout = Settings.SIMULATOR_POOL_TIMEOUT; |
||
1362 | List<ulong> reap = new List<ulong>(); |
||
1363 | foreach (var pool in SimulatorDataPools.Values) |
||
1364 | { |
||
1365 | if (pool.InactiveSince != DateTime.MaxValue && pool.InactiveSince.AddMilliseconds(SimTimeout) < DateTime.Now) |
||
1366 | { |
||
1367 | reap.Add(pool.Handle); |
||
1368 | } |
||
1369 | } |
||
1370 | foreach (var hndl in reap) |
||
1371 | { |
||
1372 | SimulatorDataPools.Remove(hndl); |
||
1373 | } |
||
1374 | } |
||
1375 | } |
||
1376 | |||
1377 | public static void SimulatorAdd(Simulator sim) |
||
1378 | { |
||
1379 | lock (SimulatorDataPools) |
||
1380 | { |
||
1381 | if (InactiveSimReaper == null) |
||
1382 | { |
||
1383 | InactiveSimReaper = new Timer(RemoveOldSims, null, TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(3)); |
||
1384 | } |
||
1385 | SimulatorDataPool pool = GetSimulatorData(sim.Handle); |
||
1386 | if (pool.ActiveClients < 1) pool.ActiveClients = 1; else pool.ActiveClients++; |
||
1387 | pool.InactiveSince = DateTime.MaxValue; |
||
1388 | } |
||
1389 | } |
||
1390 | public static void SimulatorRelease(Simulator sim) |
||
1391 | { |
||
1392 | ulong hndl = sim.Handle; |
||
1393 | lock (SimulatorDataPools) |
||
1394 | { |
||
1395 | SimulatorDataPool dataPool = GetSimulatorData(hndl); |
||
1396 | dataPool.ActiveClients--; |
||
1397 | if (dataPool.ActiveClients <= 0) |
||
1398 | { |
||
1399 | dataPool.InactiveSince = DateTime.Now; |
||
1400 | } |
||
1401 | } |
||
1402 | } |
||
1403 | |||
1404 | static public Dictionary<ulong, SimulatorDataPool> SimulatorDataPools = new Dictionary<ulong, SimulatorDataPool>(); |
||
1405 | |||
1406 | /// <summary> |
||
1407 | /// Simulator handle |
||
1408 | /// </summary> |
||
1409 | readonly public ulong Handle; |
||
1410 | /// <summary> |
||
1411 | /// Number of GridClients using this datapool |
||
1412 | /// </summary> |
||
1413 | public int ActiveClients; |
||
1414 | /// <summary> |
||
1415 | /// Time that the last client disconnected from the simulator |
||
1416 | /// </summary> |
||
1417 | public DateTime InactiveSince = DateTime.MaxValue; |
||
1418 | |||
1419 | #region Pooled Items |
||
1420 | /// <summary> |
||
1421 | /// The cache of prims used and unused in this simulator |
||
1422 | /// </summary> |
||
1423 | public Dictionary<uint, Primitive> PrimCache = new Dictionary<uint, Primitive>(); |
||
1424 | |||
1425 | /// <summary> |
||
1426 | /// Shared parcel info only when POOL_PARCEL_DATA == true |
||
1427 | /// </summary> |
||
1428 | public InternalDictionary<int, Parcel> Parcels = new InternalDictionary<int, Parcel>(); |
||
1429 | public int[,] ParcelMap = new int[64, 64]; |
||
1430 | public bool DownloadingParcelMap = false; |
||
1431 | |||
1432 | #endregion Pooled Items |
||
1433 | |||
1434 | private SimulatorDataPool(ulong hndl) |
||
1435 | { |
||
1436 | this.Handle = hndl; |
||
1437 | } |
||
1438 | |||
1439 | public static SimulatorDataPool GetSimulatorData(ulong hndl) |
||
1440 | { |
||
1441 | SimulatorDataPool dict; |
||
1442 | lock (SimulatorDataPools) |
||
1443 | { |
||
1444 | if (!SimulatorDataPools.TryGetValue(hndl, out dict)) |
||
1445 | { |
||
1446 | dict = SimulatorDataPools[hndl] = new SimulatorDataPool(hndl); |
||
1447 | } |
||
1448 | } |
||
1449 | return dict; |
||
1450 | } |
||
1451 | #region Factories |
||
1452 | internal Primitive MakePrimitive(uint localID) |
||
1453 | { |
||
1454 | var dict = PrimCache; |
||
1455 | lock (dict) |
||
1456 | { |
||
1457 | Primitive prim; |
||
1458 | if (!dict.TryGetValue(localID, out prim)) |
||
1459 | { |
||
1460 | dict[localID] = prim = new Primitive { RegionHandle = Handle, LocalID = localID }; |
||
1461 | } |
||
1462 | return prim; |
||
1463 | } |
||
1464 | } |
||
1465 | |||
1466 | internal bool NeedsRequest(uint localID) |
||
1467 | { |
||
1468 | var dict = PrimCache; |
||
1469 | lock (dict) return !dict.ContainsKey(localID); |
||
1470 | } |
||
1471 | #endregion Factories |
||
1472 | |||
1473 | internal void ReleasePrims(List<uint> removePrims) |
||
1474 | { |
||
1475 | lock (PrimCache) |
||
1476 | { |
||
1477 | foreach (uint u in removePrims) |
||
1478 | { |
||
1479 | Primitive prim; |
||
1480 | if (PrimCache.TryGetValue(u, out prim)) prim.ActiveClients--; |
||
1481 | } |
||
1482 | } |
||
1483 | } |
||
1484 | } |
||
1485 | } |