aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/Linden
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden')
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs53
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs102
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs13
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/FetchInventory2Module.cs3
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs12
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs4
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/MeshUploadFlagModule.cs4
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/NewFileAgentInventoryVariablePriceModule.cs93
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/ObjectAdd.cs14
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/UploadObjectAssetModule.cs16
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/SimulatorFeaturesModule.cs4
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/UploadBakedTextureModule.cs4
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs9
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs5
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs55
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs199
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs175
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs3
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs3
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/Tests/TestLLUDPServer.cs9
20 files changed, 478 insertions, 302 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs
index ef6dedb..d397893 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs
@@ -158,7 +158,9 @@ namespace OpenSim.Region.ClientStack.Linden
158 try 158 try
159 { 159 {
160 // the root of all evil 160 // the root of all evil
161 m_HostCapsObj.RegisterHandler("SEED", new RestStreamHandler("POST", capsBase + m_requestPath, SeedCapRequest)); 161 m_HostCapsObj.RegisterHandler(
162 "SEED", new RestStreamHandler("POST", capsBase + m_requestPath, SeedCapRequest, "SEED", null));
163
162 m_log.DebugFormat( 164 m_log.DebugFormat(
163 "[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID); 165 "[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID);
164 166
@@ -166,7 +168,10 @@ namespace OpenSim.Region.ClientStack.Linden
166 // new LLSDStreamhandler<OSDMapRequest, OSDMapLayerResponse>("POST", 168 // new LLSDStreamhandler<OSDMapRequest, OSDMapLayerResponse>("POST",
167 // capsBase + m_mapLayerPath, 169 // capsBase + m_mapLayerPath,
168 // GetMapLayer); 170 // GetMapLayer);
169 IRequestHandler req = new RestStreamHandler("POST", capsBase + m_notecardTaskUpdatePath, ScriptTaskInventory); 171 IRequestHandler req
172 = new RestStreamHandler(
173 "POST", capsBase + m_notecardTaskUpdatePath, ScriptTaskInventory, "UpdateScript", null);
174
170 m_HostCapsObj.RegisterHandler("UpdateScriptTaskInventory", req); 175 m_HostCapsObj.RegisterHandler("UpdateScriptTaskInventory", req);
171 m_HostCapsObj.RegisterHandler("UpdateScriptTask", req); 176 m_HostCapsObj.RegisterHandler("UpdateScriptTask", req);
172 } 177 }
@@ -181,14 +186,22 @@ namespace OpenSim.Region.ClientStack.Linden
181 try 186 try
182 { 187 {
183 // I don't think this one works... 188 // I don't think this one works...
184 m_HostCapsObj.RegisterHandler("NewFileAgentInventory", new LLSDStreamhandler<LLSDAssetUploadRequest, LLSDAssetUploadResponse>("POST", 189 m_HostCapsObj.RegisterHandler(
185 capsBase + m_newInventory, 190 "NewFileAgentInventory",
186 NewAgentInventoryRequest)); 191 new LLSDStreamhandler<LLSDAssetUploadRequest, LLSDAssetUploadResponse>(
187 IRequestHandler req = new RestStreamHandler("POST", capsBase + m_notecardUpdatePath, NoteCardAgentInventory); 192 "POST",
193 capsBase + m_newInventory,
194 NewAgentInventoryRequest,
195 "NewFileAgentInventory",
196 null));
197
198 IRequestHandler req
199 = new RestStreamHandler(
200 "POST", capsBase + m_notecardUpdatePath, NoteCardAgentInventory, "Update*", null);
201
188 m_HostCapsObj.RegisterHandler("UpdateNotecardAgentInventory", req); 202 m_HostCapsObj.RegisterHandler("UpdateNotecardAgentInventory", req);
189 m_HostCapsObj.RegisterHandler("UpdateScriptAgentInventory", req); 203 m_HostCapsObj.RegisterHandler("UpdateScriptAgentInventory", req);
190 m_HostCapsObj.RegisterHandler("UpdateScriptAgent", req); 204 m_HostCapsObj.RegisterHandler("UpdateScriptAgent", req);
191 m_HostCapsObj.RegisterHandler("CopyInventoryFromNotecard", new RestStreamHandler("POST", capsBase + m_copyFromNotecardPath, CopyInventoryFromNotecard));
192 IRequestHandler getObjectPhysicsDataHandler = new RestStreamHandler("POST", capsBase + m_getObjectPhysicsDataPath, GetObjectPhysicsData); 205 IRequestHandler getObjectPhysicsDataHandler = new RestStreamHandler("POST", capsBase + m_getObjectPhysicsDataPath, GetObjectPhysicsData);
193 m_HostCapsObj.RegisterHandler("GetObjectPhysicsData", getObjectPhysicsDataHandler); 206 m_HostCapsObj.RegisterHandler("GetObjectPhysicsData", getObjectPhysicsDataHandler);
194 IRequestHandler getObjectCostHandler = new RestStreamHandler("POST", capsBase + m_getObjectCostPath, GetObjectCost); 207 IRequestHandler getObjectCostHandler = new RestStreamHandler("POST", capsBase + m_getObjectCostPath, GetObjectCost);
@@ -197,6 +210,12 @@ namespace OpenSim.Region.ClientStack.Linden
197 m_HostCapsObj.RegisterHandler("ResourceCostSelected", ResourceCostSelectedHandler); 210 m_HostCapsObj.RegisterHandler("ResourceCostSelected", ResourceCostSelectedHandler);
198 211
199 212
213
214 m_HostCapsObj.RegisterHandler(
215 "CopyInventoryFromNotecard",
216 new RestStreamHandler(
217 "POST", capsBase + m_copyFromNotecardPath, CopyInventoryFromNotecard, "CopyInventoryFromNotecard", null));
218
200 // As of RC 1.22.9 of the Linden client this is 219 // As of RC 1.22.9 of the Linden client this is
201 // supported 220 // supported
202 221
@@ -245,7 +264,10 @@ namespace OpenSim.Region.ClientStack.Linden
245 264
246 if (!m_Scene.CheckClient(m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint)) 265 if (!m_Scene.CheckClient(m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint))
247 { 266 {
248 m_log.DebugFormat("[CAPS]: Unauthorized CAPS client"); 267 m_log.DebugFormat(
268 "[CAPS]: Unauthorized CAPS client {0} from {1}",
269 m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint);
270
249 return string.Empty; 271 return string.Empty;
250 } 272 }
251 273
@@ -296,7 +318,9 @@ namespace OpenSim.Region.ClientStack.Linden
296 m_dumpAssetsToFile); 318 m_dumpAssetsToFile);
297 uploader.OnUpLoad += TaskScriptUpdated; 319 uploader.OnUpLoad += TaskScriptUpdated;
298 320
299 m_HostCapsObj.HttpListener.AddStreamHandler(new BinaryStreamHandler("POST", capsBase + uploaderPath, uploader.uploaderCaps)); 321 m_HostCapsObj.HttpListener.AddStreamHandler(
322 new BinaryStreamHandler(
323 "POST", capsBase + uploaderPath, uploader.uploaderCaps, "TaskInventoryScriptUpdater", null));
300 324
301 string protocol = "http://"; 325 string protocol = "http://";
302 326
@@ -423,8 +447,14 @@ namespace OpenSim.Region.ClientStack.Linden
423 AssetUploader uploader = 447 AssetUploader uploader =
424 new AssetUploader(assetName, assetDes, newAsset, newInvItem, parentFolder, llsdRequest.inventory_type, 448 new AssetUploader(assetName, assetDes, newAsset, newInvItem, parentFolder, llsdRequest.inventory_type,
425 llsdRequest.asset_type, capsBase + uploaderPath, m_HostCapsObj.HttpListener, m_dumpAssetsToFile); 449 llsdRequest.asset_type, capsBase + uploaderPath, m_HostCapsObj.HttpListener, m_dumpAssetsToFile);
450
426 m_HostCapsObj.HttpListener.AddStreamHandler( 451 m_HostCapsObj.HttpListener.AddStreamHandler(
427 new BinaryStreamHandler("POST", capsBase + uploaderPath, uploader.uploaderCaps)); 452 new BinaryStreamHandler(
453 "POST",
454 capsBase + uploaderPath,
455 uploader.uploaderCaps,
456 "NewAgentInventoryRequest",
457 m_HostCapsObj.AgentID.ToString()));
428 458
429 string protocol = "http://"; 459 string protocol = "http://";
430 460
@@ -740,7 +770,8 @@ namespace OpenSim.Region.ClientStack.Linden
740 uploader.OnUpLoad += ItemUpdated; 770 uploader.OnUpLoad += ItemUpdated;
741 771
742 m_HostCapsObj.HttpListener.AddStreamHandler( 772 m_HostCapsObj.HttpListener.AddStreamHandler(
743 new BinaryStreamHandler("POST", capsBase + uploaderPath, uploader.uploaderCaps)); 773 new BinaryStreamHandler(
774 "POST", capsBase + uploaderPath, uploader.uploaderCaps, "NoteCardAgentInventory", null));
744 775
745 string protocol = "http://"; 776 string protocol = "http://";
746 777
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs
index 016ed97..ebfe687 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs
@@ -106,13 +106,14 @@ namespace OpenSim.Region.ClientStack.Linden
106 scene.EventManager.OnRegisterCaps += OnRegisterCaps; 106 scene.EventManager.OnRegisterCaps += OnRegisterCaps;
107 107
108 MainConsole.Instance.Commands.AddCommand( 108 MainConsole.Instance.Commands.AddCommand(
109 "Comms", 109 "Debug",
110 false, 110 false,
111 "debug eq", 111 "debug eq",
112 "debug eq [0|1]", 112 "debug eq [0|1|2]",
113 "Turn on event queue debugging", 113 "Turn on event queue debugging"
114 "debug eq 1 will turn on event queue debugging. This will log all outgoing event queue messages to clients.\n" 114 + "<= 0 - turns off all event queue logging"
115 + "debug eq 0 will turn off event queue debugging.", 115 + ">= 1 - turns on outgoing event logging"
116 + ">= 2 - turns on poll notification",
116 HandleDebugEq); 117 HandleDebugEq);
117 } 118 }
118 else 119 else
@@ -235,19 +236,19 @@ namespace OpenSim.Region.ClientStack.Linden
235// ClientClosed(client.AgentId); 236// ClientClosed(client.AgentId);
236// } 237// }
237 238
238 private void ClientClosed(UUID AgentID, Scene scene) 239 private void ClientClosed(UUID agentID, Scene scene)
239 { 240 {
240// m_log.DebugFormat("[EVENTQUEUE]: Closed client {0} in region {1}", AgentID, m_scene.RegionInfo.RegionName); 241// m_log.DebugFormat("[EVENTQUEUE]: Closed client {0} in region {1}", agentID, m_scene.RegionInfo.RegionName);
241 242
242 int count = 0; 243 int count = 0;
243 while (queues.ContainsKey(AgentID) && queues[AgentID].Count > 0 && count++ < 5) 244 while (queues.ContainsKey(agentID) && queues[agentID].Count > 0 && count++ < 5)
244 { 245 {
245 Thread.Sleep(1000); 246 Thread.Sleep(1000);
246 } 247 }
247 248
248 lock (queues) 249 lock (queues)
249 { 250 {
250 queues.Remove(AgentID); 251 queues.Remove(agentID);
251 } 252 }
252 253
253 List<UUID> removeitems = new List<UUID>(); 254 List<UUID> removeitems = new List<UUID>();
@@ -256,7 +257,7 @@ namespace OpenSim.Region.ClientStack.Linden
256 foreach (UUID ky in m_AvatarQueueUUIDMapping.Keys) 257 foreach (UUID ky in m_AvatarQueueUUIDMapping.Keys)
257 { 258 {
258// m_log.DebugFormat("[EVENTQUEUE]: Found key {0} in m_AvatarQueueUUIDMapping while looking for {1}", ky, AgentID); 259// m_log.DebugFormat("[EVENTQUEUE]: Found key {0} in m_AvatarQueueUUIDMapping while looking for {1}", ky, AgentID);
259 if (ky == AgentID) 260 if (ky == agentID)
260 { 261 {
261 removeitems.Add(ky); 262 removeitems.Add(ky);
262 } 263 }
@@ -267,7 +268,12 @@ namespace OpenSim.Region.ClientStack.Linden
267 UUID eventQueueGetUuid = m_AvatarQueueUUIDMapping[ky]; 268 UUID eventQueueGetUuid = m_AvatarQueueUUIDMapping[ky];
268 m_AvatarQueueUUIDMapping.Remove(ky); 269 m_AvatarQueueUUIDMapping.Remove(ky);
269 270
270 MainServer.Instance.RemovePollServiceHTTPHandler("","/CAPS/EQG/" + eventQueueGetUuid.ToString() + "/"); 271 string eqgPath = GenerateEqgCapPath(eventQueueGetUuid);
272 MainServer.Instance.RemovePollServiceHTTPHandler("", eqgPath);
273
274// m_log.DebugFormat(
275// "[EVENT QUEUE GET MODULE]: Removed EQG handler {0} for {1} in {2}",
276// eqgPath, agentID, m_scene.RegionInfo.RegionName);
271 } 277 }
272 } 278 }
273 279
@@ -281,7 +287,7 @@ namespace OpenSim.Region.ClientStack.Linden
281 { 287 {
282 searchval = m_QueueUUIDAvatarMapping[ky]; 288 searchval = m_QueueUUIDAvatarMapping[ky];
283 289
284 if (searchval == AgentID) 290 if (searchval == agentID)
285 { 291 {
286 removeitems.Add(ky); 292 removeitems.Add(ky);
287 } 293 }
@@ -305,6 +311,15 @@ namespace OpenSim.Region.ClientStack.Linden
305 //} 311 //}
306 } 312 }
307 313
314 /// <summary>
315 /// Generate an Event Queue Get handler path for the given eqg uuid.
316 /// </summary>
317 /// <param name='eqgUuid'></param>
318 private string GenerateEqgCapPath(UUID eqgUuid)
319 {
320 return string.Format("/CAPS/EQG/{0}/", eqgUuid);
321 }
322
308 public void OnRegisterCaps(UUID agentID, Caps caps) 323 public void OnRegisterCaps(UUID agentID, Caps caps)
309 { 324 {
310 // Register an event queue for the client 325 // Register an event queue for the client
@@ -316,8 +331,7 @@ namespace OpenSim.Region.ClientStack.Linden
316 // Let's instantiate a Queue for this agent right now 331 // Let's instantiate a Queue for this agent right now
317 TryGetQueue(agentID); 332 TryGetQueue(agentID);
318 333
319 string capsBase = "/CAPS/EQG/"; 334 UUID eventQueueGetUUID;
320 UUID EventQueueGetUUID = UUID.Zero;
321 335
322 lock (m_AvatarQueueUUIDMapping) 336 lock (m_AvatarQueueUUIDMapping)
323 { 337 {
@@ -325,43 +339,49 @@ namespace OpenSim.Region.ClientStack.Linden
325 if (m_AvatarQueueUUIDMapping.ContainsKey(agentID)) 339 if (m_AvatarQueueUUIDMapping.ContainsKey(agentID))
326 { 340 {
327 //m_log.DebugFormat("[EVENTQUEUE]: Found Existing UUID!"); 341 //m_log.DebugFormat("[EVENTQUEUE]: Found Existing UUID!");
328 EventQueueGetUUID = m_AvatarQueueUUIDMapping[agentID]; 342 eventQueueGetUUID = m_AvatarQueueUUIDMapping[agentID];
329 } 343 }
330 else 344 else
331 { 345 {
332 EventQueueGetUUID = UUID.Random(); 346 eventQueueGetUUID = UUID.Random();
333 //m_log.DebugFormat("[EVENTQUEUE]: Using random UUID!"); 347 //m_log.DebugFormat("[EVENTQUEUE]: Using random UUID!");
334 } 348 }
335 } 349 }
336 350
337 lock (m_QueueUUIDAvatarMapping) 351 lock (m_QueueUUIDAvatarMapping)
338 { 352 {
339 if (!m_QueueUUIDAvatarMapping.ContainsKey(EventQueueGetUUID)) 353 if (!m_QueueUUIDAvatarMapping.ContainsKey(eventQueueGetUUID))
340 m_QueueUUIDAvatarMapping.Add(EventQueueGetUUID, agentID); 354 m_QueueUUIDAvatarMapping.Add(eventQueueGetUUID, agentID);
341 } 355 }
342 356
343 lock (m_AvatarQueueUUIDMapping) 357 lock (m_AvatarQueueUUIDMapping)
344 { 358 {
345 if (!m_AvatarQueueUUIDMapping.ContainsKey(agentID)) 359 if (!m_AvatarQueueUUIDMapping.ContainsKey(agentID))
346 m_AvatarQueueUUIDMapping.Add(agentID, EventQueueGetUUID); 360 m_AvatarQueueUUIDMapping.Add(agentID, eventQueueGetUUID);
347 } 361 }
348 362
363 string eventQueueGetPath = GenerateEqgCapPath(eventQueueGetUUID);
364
349 // Register this as a caps handler 365 // Register this as a caps handler
350 // FIXME: Confusingly, we need to register separate as a capability so that the client is told about 366 // FIXME: Confusingly, we need to register separate as a capability so that the client is told about
351 // EventQueueGet when it receive capability information, but then we replace the rest handler immediately 367 // EventQueueGet when it receive capability information, but then we replace the rest handler immediately
352 // afterwards with the poll service. So for now, we'll pass a null instead to simplify code reading, but 368 // afterwards with the poll service. So for now, we'll pass a null instead to simplify code reading, but
353 // really it should be possible to directly register the poll handler as a capability. 369 // really it should be possible to directly register the poll handler as a capability.
354 caps.RegisterHandler("EventQueueGet", 370 caps.RegisterHandler("EventQueueGet", new RestHTTPHandler("POST", eventQueueGetPath, null));
355 new RestHTTPHandler("POST", capsBase + EventQueueGetUUID.ToString() + "/", null));
356// delegate(Hashtable m_dhttpMethod) 371// delegate(Hashtable m_dhttpMethod)
357// { 372// {
358// return ProcessQueue(m_dhttpMethod, agentID, caps); 373// return ProcessQueue(m_dhttpMethod, agentID, caps);
359// })); 374// }));
360 375
361 // This will persist this beyond the expiry of the caps handlers 376 // This will persist this beyond the expiry of the caps handlers
377 // TODO: Add EventQueueGet name/description for diagnostics
362 MainServer.Instance.AddPollServiceHTTPHandler( 378 MainServer.Instance.AddPollServiceHTTPHandler(
363 capsBase + EventQueueGetUUID.ToString() + "/", 379 eventQueueGetPath,
364 new PollServiceEventArgs(null, HasEvents, GetEvents, NoEvents, agentID,1000)); // 1 sec timeout 380 new PollServiceEventArgs(null, HasEvents, GetEvents, NoEvents, agentID, 1000));
381
382// m_log.DebugFormat(
383// "[EVENT QUEUE GET MODULE]: Registered EQG handler {0} for {1} in {2}",
384// eventQueueGetPath, agentID, m_scene.RegionInfo.RegionName);
365 385
366 Random rnd = new Random(Environment.TickCount); 386 Random rnd = new Random(Environment.TickCount);
367 lock (m_ids) 387 lock (m_ids)
@@ -384,9 +404,25 @@ namespace OpenSim.Region.ClientStack.Linden
384 return false; 404 return false;
385 } 405 }
386 406
407 /// <summary>
408 /// Logs a debug line for an outbound event queue message if appropriate.
409 /// </summary>
410 /// <param name='element'>Element containing message</param>
411 private void LogOutboundDebugMessage(OSD element, UUID agentId)
412 {
413 if (element is OSDMap)
414 {
415 OSDMap ev = (OSDMap)element;
416 m_log.DebugFormat(
417 "Eq OUT {0,-30} to {1,-20} {2,-20}",
418 ev["message"], m_scene.GetScenePresence(agentId).Name, m_scene.RegionInfo.RegionName);
419 }
420 }
421
387 public Hashtable GetEvents(UUID requestID, UUID pAgentId, string request) 422 public Hashtable GetEvents(UUID requestID, UUID pAgentId, string request)
388 { 423 {
389// m_log.DebugFormat("[EVENT QUEUE GET MODULE]: Invoked GetEvents() for {0}", pAgentId); 424 if (DebugLevel >= 2)
425 m_log.DebugFormat("POLLED FOR EQ MESSAGES BY {0} in {1}", pAgentId, m_scene.RegionInfo.RegionName);
390 426
391 Queue<OSD> queue = TryGetQueue(pAgentId); 427 Queue<OSD> queue = TryGetQueue(pAgentId);
392 OSD element; 428 OSD element;
@@ -410,13 +446,8 @@ namespace OpenSim.Region.ClientStack.Linden
410 } 446 }
411 else 447 else
412 { 448 {
413 if (DebugLevel > 0 && element is OSDMap) 449 if (DebugLevel > 0)
414 { 450 LogOutboundDebugMessage(element, pAgentId);
415 OSDMap ev = (OSDMap)element;
416 m_log.DebugFormat(
417 "[EVENT QUEUE GET MODULE]: Eq OUT {0} to {1}",
418 ev["message"], m_scene.GetScenePresence(pAgentId).Name);
419 }
420 451
421 array.Add(element); 452 array.Add(element);
422 453
@@ -426,13 +457,8 @@ namespace OpenSim.Region.ClientStack.Linden
426 { 457 {
427 element = queue.Dequeue(); 458 element = queue.Dequeue();
428 459
429 if (DebugLevel > 0 && element is OSDMap) 460 if (DebugLevel > 0)
430 { 461 LogOutboundDebugMessage(element, pAgentId);
431 OSDMap ev = (OSDMap)element;
432 m_log.DebugFormat(
433 "[EVENT QUEUE GET MODULE]: Eq OUT {0} to {1}",
434 ev["message"], m_scene.GetScenePresence(pAgentId).Name);
435 }
436 462
437 array.Add(element); 463 array.Add(element);
438 thisID++; 464 thisID++;
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs
index a5209b7..cd70410 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs
@@ -51,7 +51,16 @@ namespace OpenSim.Region.ClientStack.Linden.Tests
51 [SetUp] 51 [SetUp]
52 public void SetUp() 52 public void SetUp()
53 { 53 {
54 MainServer.Instance = new BaseHttpServer(9999, false, 9998, ""); 54 uint port = 9999;
55 uint sslPort = 9998;
56
57 // This is an unfortunate bit of clean up we have to do because MainServer manages things through static
58 // variables and the VM is not restarted between tests.
59 MainServer.RemoveHttpServer(port);
60
61 BaseHttpServer server = new BaseHttpServer(port, false, sslPort, "");
62 MainServer.AddHttpServer(server);
63 MainServer.Instance = server;
55 64
56 IConfigSource config = new IniConfigSource(); 65 IConfigSource config = new IniConfigSource();
57 config.AddConfig("Startup"); 66 config.AddConfig("Startup");
@@ -60,7 +69,7 @@ namespace OpenSim.Region.ClientStack.Linden.Tests
60 CapabilitiesModule capsModule = new CapabilitiesModule(); 69 CapabilitiesModule capsModule = new CapabilitiesModule();
61 EventQueueGetModule eqgModule = new EventQueueGetModule(); 70 EventQueueGetModule eqgModule = new EventQueueGetModule();
62 71
63 m_scene = SceneHelpers.SetupScene(); 72 m_scene = new SceneHelpers().SetupScene();
64 SceneHelpers.SetupSceneModules(m_scene, config, capsModule, eqgModule); 73 SceneHelpers.SetupSceneModules(m_scene, config, capsModule, eqgModule);
65 } 74 }
66 75
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/FetchInventory2Module.cs b/OpenSim/Region/ClientStack/Linden/Caps/FetchInventory2Module.cs
index 14501c7..cb5afcc 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/FetchInventory2Module.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/FetchInventory2Module.cs
@@ -132,7 +132,8 @@ namespace OpenSim.Region.ClientStack.Linden
132 capUrl = "/CAPS/" + UUID.Random(); 132 capUrl = "/CAPS/" + UUID.Random();
133 133
134 IRequestHandler reqHandler 134 IRequestHandler reqHandler
135 = new RestStreamHandler("POST", capUrl, m_fetchHandler.FetchInventoryRequest); 135 = new RestStreamHandler(
136 "POST", capUrl, m_fetchHandler.FetchInventoryRequest, capName, agentID.ToString());
136 137
137 caps.RegisterHandler(capName, reqHandler); 138 caps.RegisterHandler(capName, reqHandler);
138 } 139 }
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
index e7bd2e7..0d7b1fc 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
@@ -120,11 +120,13 @@ namespace OpenSim.Region.ClientStack.Linden
120 { 120 {
121// m_log.DebugFormat("[GETMESH]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); 121// m_log.DebugFormat("[GETMESH]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
122 GetMeshHandler gmeshHandler = new GetMeshHandler(m_AssetService); 122 GetMeshHandler gmeshHandler = new GetMeshHandler(m_AssetService);
123 IRequestHandler reqHandler = new RestHTTPHandler("GET", "/CAPS/" + UUID.Random(), 123 IRequestHandler reqHandler
124 delegate(Hashtable m_dhttpMethod) 124 = new RestHTTPHandler(
125 { 125 "GET",
126 return gmeshHandler.ProcessGetMesh(m_dhttpMethod, UUID.Zero, null); 126 "/CAPS/" + UUID.Random(),
127 }); 127 httpMethod => gmeshHandler.ProcessGetMesh(httpMethod, UUID.Zero, null),
128 "GetMesh",
129 agentID.ToString());
128 130
129 caps.RegisterHandler("GetMesh", reqHandler); 131 caps.RegisterHandler("GetMesh", reqHandler);
130 } 132 }
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs
index fffcee2..5ae9cc3 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs
@@ -130,7 +130,9 @@ namespace OpenSim.Region.ClientStack.Linden
130 if (m_URL == "localhost") 130 if (m_URL == "localhost")
131 { 131 {
132// m_log.DebugFormat("[GETTEXTURE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); 132// m_log.DebugFormat("[GETTEXTURE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
133 caps.RegisterHandler("GetTexture", new GetTextureHandler("/CAPS/" + capID + "/", m_assetService)); 133 caps.RegisterHandler(
134 "GetTexture",
135 new GetTextureHandler("/CAPS/" + capID + "/", m_assetService, "GetTexture", agentID.ToString()));
134 } 136 }
135 else 137 else
136 { 138 {
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/MeshUploadFlagModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/MeshUploadFlagModule.cs
index 18c7eae..44a6883 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/MeshUploadFlagModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/MeshUploadFlagModule.cs
@@ -117,7 +117,9 @@ namespace OpenSim.Region.ClientStack.Linden
117 117
118 public void RegisterCaps(UUID agentID, Caps caps) 118 public void RegisterCaps(UUID agentID, Caps caps)
119 { 119 {
120 IRequestHandler reqHandler = new RestHTTPHandler("GET", "/CAPS/" + UUID.Random(), MeshUploadFlag); 120 IRequestHandler reqHandler
121 = new RestHTTPHandler("GET", "/CAPS/" + UUID.Random(), MeshUploadFlag, "MeshUploadFlag", agentID.ToString());
122
121 caps.RegisterHandler("MeshUploadFlag", reqHandler); 123 caps.RegisterHandler("MeshUploadFlag", reqHandler);
122 m_agentID = agentID; 124 m_agentID = agentID;
123 } 125 }
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/NewFileAgentInventoryVariablePriceModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/NewFileAgentInventoryVariablePriceModule.cs
index 91872c5..52c4f44 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/NewFileAgentInventoryVariablePriceModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/NewFileAgentInventoryVariablePriceModule.cs
@@ -115,67 +115,66 @@ namespace OpenSim.Region.ClientStack.Linden
115 UUID capID = UUID.Random(); 115 UUID capID = UUID.Random();
116 116
117// m_log.Debug("[NEW FILE AGENT INVENTORY VARIABLE PRICE]: /CAPS/" + capID); 117// m_log.Debug("[NEW FILE AGENT INVENTORY VARIABLE PRICE]: /CAPS/" + capID);
118 caps.RegisterHandler("NewFileAgentInventoryVariablePrice", 118 caps.RegisterHandler(
119 119 "NewFileAgentInventoryVariablePrice",
120 new LLSDStreamhandler<LLSDAssetUploadRequest, LLSDNewFileAngentInventoryVariablePriceReplyResponse>("POST", 120 new LLSDStreamhandler<LLSDAssetUploadRequest, LLSDNewFileAngentInventoryVariablePriceReplyResponse>(
121 "/CAPS/" + capID.ToString(), 121 "POST",
122 delegate(LLSDAssetUploadRequest req) 122 "/CAPS/" + capID.ToString(),
123 { 123 req => NewAgentInventoryRequest(req, agentID),
124 return NewAgentInventoryRequest(req,agentID); 124 "NewFileAgentInventoryVariablePrice",
125 })); 125 agentID.ToString()));
126
127 } 126 }
128 127
129 #endregion 128 #endregion
130 129
131 public LLSDNewFileAngentInventoryVariablePriceReplyResponse NewAgentInventoryRequest(LLSDAssetUploadRequest llsdRequest, UUID agentID) 130 public LLSDNewFileAngentInventoryVariablePriceReplyResponse NewAgentInventoryRequest(LLSDAssetUploadRequest llsdRequest, UUID agentID)
132 { 131 {
133
134 //TODO: The Mesh uploader uploads many types of content. If you're going to implement a Money based limit 132 //TODO: The Mesh uploader uploads many types of content. If you're going to implement a Money based limit
135 // You need to be aware of this and 133 // you need to be aware of this
136
137 134
138 //if (llsdRequest.asset_type == "texture" || 135 //if (llsdRequest.asset_type == "texture" ||
139 // llsdRequest.asset_type == "animation" || 136 // llsdRequest.asset_type == "animation" ||
140 // llsdRequest.asset_type == "sound") 137 // llsdRequest.asset_type == "sound")
141 // { 138 // {
142 // check user level 139 // check user level
143 ScenePresence avatar = null;
144 IClientAPI client = null;
145 m_scene.TryGetScenePresence(agentID, out avatar);
146 140
147 if (avatar != null) 141 ScenePresence avatar = null;
142 IClientAPI client = null;
143 m_scene.TryGetScenePresence(agentID, out avatar);
144
145 if (avatar != null)
146 {
147 client = avatar.ControllingClient;
148
149 if (avatar.UserLevel < m_levelUpload)
148 { 150 {
149 client = avatar.ControllingClient; 151 if (client != null)
150 152 client.SendAgentAlertMessage("Unable to upload asset. Insufficient permissions.", false);
151 if (avatar.UserLevel < m_levelUpload) 153
152 { 154 LLSDNewFileAngentInventoryVariablePriceReplyResponse errorResponse = new LLSDNewFileAngentInventoryVariablePriceReplyResponse();
153 if (client != null) 155 errorResponse.rsvp = "";
154 client.SendAgentAlertMessage("Unable to upload asset. Insufficient permissions.", false); 156 errorResponse.state = "error";
155 157 return errorResponse;
156 LLSDNewFileAngentInventoryVariablePriceReplyResponse errorResponse = new LLSDNewFileAngentInventoryVariablePriceReplyResponse();
157 errorResponse.rsvp = "";
158 errorResponse.state = "error";
159 return errorResponse;
160 }
161 } 158 }
159 }
162 160
163 // check funds 161 // check funds
164 IMoneyModule mm = m_scene.RequestModuleInterface<IMoneyModule>(); 162 IMoneyModule mm = m_scene.RequestModuleInterface<IMoneyModule>();
165 163
166 if (mm != null) 164 if (mm != null)
165 {
166 if (!mm.UploadCovered(agentID, mm.UploadCharge))
167 { 167 {
168 if (!mm.UploadCovered(agentID, mm.UploadCharge)) 168 if (client != null)
169 { 169 client.SendAgentAlertMessage("Unable to upload asset. Insufficient funds.", false);
170 if (client != null) 170
171 client.SendAgentAlertMessage("Unable to upload asset. Insufficient funds.", false); 171 LLSDNewFileAngentInventoryVariablePriceReplyResponse errorResponse = new LLSDNewFileAngentInventoryVariablePriceReplyResponse();
172 172 errorResponse.rsvp = "";
173 LLSDNewFileAngentInventoryVariablePriceReplyResponse errorResponse = new LLSDNewFileAngentInventoryVariablePriceReplyResponse(); 173 errorResponse.state = "error";
174 errorResponse.rsvp = ""; 174 return errorResponse;
175 errorResponse.state = "error";
176 return errorResponse;
177 }
178 } 175 }
176 }
177
179 // } 178 // }
180 179
181 string assetName = llsdRequest.name; 180 string assetName = llsdRequest.name;
@@ -189,8 +188,14 @@ namespace OpenSim.Region.ClientStack.Linden
189 AssetUploader uploader = 188 AssetUploader uploader =
190 new AssetUploader(assetName, assetDes, newAsset, newInvItem, parentFolder, llsdRequest.inventory_type, 189 new AssetUploader(assetName, assetDes, newAsset, newInvItem, parentFolder, llsdRequest.inventory_type,
191 llsdRequest.asset_type, capsBase + uploaderPath, MainServer.Instance, m_dumpAssetsToFile); 190 llsdRequest.asset_type, capsBase + uploaderPath, MainServer.Instance, m_dumpAssetsToFile);
191
192 MainServer.Instance.AddStreamHandler( 192 MainServer.Instance.AddStreamHandler(
193 new BinaryStreamHandler("POST", capsBase + uploaderPath, uploader.uploaderCaps)); 193 new BinaryStreamHandler(
194 "POST",
195 capsBase + uploaderPath,
196 uploader.uploaderCaps,
197 "NewFileAgentInventoryVariablePrice",
198 agentID.ToString()));
194 199
195 string protocol = "http://"; 200 string protocol = "http://";
196 201
@@ -199,10 +204,9 @@ namespace OpenSim.Region.ClientStack.Linden
199 204
200 string uploaderURL = protocol + m_scene.RegionInfo.ExternalHostName + ":" + MainServer.Instance.Port.ToString() + capsBase + 205 string uploaderURL = protocol + m_scene.RegionInfo.ExternalHostName + ":" + MainServer.Instance.Port.ToString() + capsBase +
201 uploaderPath; 206 uploaderPath;
202 207
203 208
204 LLSDNewFileAngentInventoryVariablePriceReplyResponse uploadResponse = new LLSDNewFileAngentInventoryVariablePriceReplyResponse(); 209 LLSDNewFileAngentInventoryVariablePriceReplyResponse uploadResponse = new LLSDNewFileAngentInventoryVariablePriceReplyResponse();
205
206 210
207 uploadResponse.rsvp = uploaderURL; 211 uploadResponse.rsvp = uploaderURL;
208 uploadResponse.state = "upload"; 212 uploadResponse.state = "upload";
@@ -220,6 +224,7 @@ namespace OpenSim.Region.ClientStack.Linden
220 pinventoryItem, pparentFolder, pdata, pinventoryType, 224 pinventoryItem, pparentFolder, pdata, pinventoryType,
221 passetType,agentID); 225 passetType,agentID);
222 }; 226 };
227
223 return uploadResponse; 228 return uploadResponse;
224 } 229 }
225 230
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/ObjectAdd.cs b/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/ObjectAdd.cs
index 1c47f0e..4ccfc43 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/ObjectAdd.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/ObjectAdd.cs
@@ -66,12 +66,14 @@ namespace OpenSim.Region.ClientStack.Linden
66 66
67// m_log.InfoFormat("[OBJECTADD]: {0}", "/CAPS/OA/" + capuuid + "/"); 67// m_log.InfoFormat("[OBJECTADD]: {0}", "/CAPS/OA/" + capuuid + "/");
68 68
69 caps.RegisterHandler("ObjectAdd", 69 caps.RegisterHandler(
70 new RestHTTPHandler("POST", "/CAPS/OA/" + capuuid + "/", 70 "ObjectAdd",
71 delegate(Hashtable m_dhttpMethod) 71 new RestHTTPHandler(
72 { 72 "POST",
73 return ProcessAdd(m_dhttpMethod, agentID, caps); 73 "/CAPS/OA/" + capuuid + "/",
74 })); 74 httpMethod => ProcessAdd(httpMethod, agentID, caps),
75 "ObjectAdd",
76 agentID.ToString()));;
75 } 77 }
76 78
77 public Hashtable ProcessAdd(Hashtable request, UUID AgentId, Caps cap) 79 public Hashtable ProcessAdd(Hashtable request, UUID AgentId, Caps cap)
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/UploadObjectAssetModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/UploadObjectAssetModule.cs
index 7a3d97e..ba902b2 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/UploadObjectAssetModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/UploadObjectAssetModule.cs
@@ -106,12 +106,15 @@ namespace OpenSim.Region.ClientStack.Linden
106 UUID capID = UUID.Random(); 106 UUID capID = UUID.Random();
107 107
108// m_log.Debug("[UPLOAD OBJECT ASSET MODULE]: /CAPS/" + capID); 108// m_log.Debug("[UPLOAD OBJECT ASSET MODULE]: /CAPS/" + capID);
109 caps.RegisterHandler("UploadObjectAsset", 109 caps.RegisterHandler(
110 new RestHTTPHandler("POST", "/CAPS/OA/" + capID + "/", 110 "UploadObjectAsset",
111 delegate(Hashtable m_dhttpMethod) 111 new RestHTTPHandler(
112 { 112 "POST",
113 return ProcessAdd(m_dhttpMethod, agentID, caps); 113 "/CAPS/OA/" + capID + "/",
114 })); 114 httpMethod => ProcessAdd(httpMethod, agentID, caps),
115 "UploadObjectAsset",
116 agentID.ToString()));
117
115 /* 118 /*
116 caps.RegisterHandler("NewFileAgentInventoryVariablePrice", 119 caps.RegisterHandler("NewFileAgentInventoryVariablePrice",
117 120
@@ -330,7 +333,6 @@ namespace OpenSim.Region.ClientStack.Linden
330 grp.AbsolutePosition = obj.Position; 333 grp.AbsolutePosition = obj.Position;
331 prim.RotationOffset = obj.Rotation; 334 prim.RotationOffset = obj.Rotation;
332 335
333 grp.IsAttachment = false;
334 // Required for linking 336 // Required for linking
335 grp.RootPart.ClearUpdateSchedule(); 337 grp.RootPart.ClearUpdateSchedule();
336 338
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/SimulatorFeaturesModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/SimulatorFeaturesModule.cs
index 1dd8938..8ed0fb3 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/SimulatorFeaturesModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/SimulatorFeaturesModule.cs
@@ -154,7 +154,9 @@ namespace OpenSim.Region.ClientStack.Linden
154 public void RegisterCaps(UUID agentID, Caps caps) 154 public void RegisterCaps(UUID agentID, Caps caps)
155 { 155 {
156 IRequestHandler reqHandler 156 IRequestHandler reqHandler
157 = new RestHTTPHandler("GET", "/CAPS/" + UUID.Random(), HandleSimulatorFeaturesRequest); 157 = new RestHTTPHandler(
158 "GET", "/CAPS/" + UUID.Random(),
159 HandleSimulatorFeaturesRequest, "SimulatorFeatures", agentID.ToString());
158 160
159 caps.RegisterHandler("SimulatorFeatures", reqHandler); 161 caps.RegisterHandler("SimulatorFeatures", reqHandler);
160 } 162 }
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/UploadBakedTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/UploadBakedTextureModule.cs
index 45d6071..b3d61a8 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/UploadBakedTextureModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/UploadBakedTextureModule.cs
@@ -106,7 +106,9 @@ namespace OpenSim.Region.ClientStack.Linden
106 "POST", 106 "POST",
107 "/CAPS/" + caps.CapsObjectPath + m_uploadBakedTexturePath, 107 "/CAPS/" + caps.CapsObjectPath + m_uploadBakedTexturePath,
108 new UploadBakedTextureHandler( 108 new UploadBakedTextureHandler(
109 caps, m_scene.AssetService, m_persistBakedTextures).UploadBakedTexture)); 109 caps, m_scene.AssetService, m_persistBakedTextures).UploadBakedTexture,
110 "UploadBakedTexture",
111 agentID.ToString()));
110 } 112 }
111 } 113 }
112} \ No newline at end of file 114} \ No newline at end of file
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
index 10f43d1..2359bd6 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
@@ -144,7 +144,12 @@ namespace OpenSim.Region.ClientStack.Linden
144 capUrl = "/CAPS/" + UUID.Random(); 144 capUrl = "/CAPS/" + UUID.Random();
145 145
146 IRequestHandler reqHandler 146 IRequestHandler reqHandler
147 = new RestStreamHandler("POST", capUrl, m_webFetchHandler.FetchInventoryDescendentsRequest); 147 = new RestStreamHandler(
148 "POST",
149 capUrl,
150 m_webFetchHandler.FetchInventoryDescendentsRequest,
151 "FetchInventoryDescendents2",
152 agentID.ToString());
148 153
149 caps.RegisterHandler(capName, reqHandler); 154 caps.RegisterHandler(capName, reqHandler);
150 } 155 }
@@ -160,4 +165,4 @@ namespace OpenSim.Region.ClientStack.Linden
160// capName, capUrl, m_scene.RegionInfo.RegionName, agentID); 165// capName, capUrl, m_scene.RegionInfo.RegionName, agentID);
161 } 166 }
162 } 167 }
163} 168} \ No newline at end of file
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs b/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs
index 90b3ede..1b8535c 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs
@@ -39,7 +39,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
39 public sealed class IncomingPacket 39 public sealed class IncomingPacket
40 { 40 {
41 /// <summary>Client this packet came from</summary> 41 /// <summary>Client this packet came from</summary>
42 public LLUDPClient Client; 42 public LLClientView Client;
43
43 /// <summary>Packet data that has been received</summary> 44 /// <summary>Packet data that has been received</summary>
44 public Packet Packet; 45 public Packet Packet;
45 46
@@ -48,7 +49,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
48 /// </summary> 49 /// </summary>
49 /// <param name="client">Reference to the client this packet came from</param> 50 /// <param name="client">Reference to the client this packet came from</param>
50 /// <param name="packet">Packet data</param> 51 /// <param name="packet">Packet data</param>
51 public IncomingPacket(LLUDPClient client, Packet packet) 52 public IncomingPacket(LLClientView client, Packet packet)
52 { 53 {
53 Client = client; 54 Client = client;
54 Packet = packet; 55 Packet = packet;
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
index 9c96b52..e6289bd 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
@@ -59,7 +59,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
59 /// Handles new client connections 59 /// Handles new client connections
60 /// Constructor takes a single Packet and authenticates everything 60 /// Constructor takes a single Packet and authenticates everything
61 /// </summary> 61 /// </summary>
62 public class LLClientView : IClientAPI, IClientCore, IClientIM, IClientChat, IClientInventory, IClientIPEndpoint, IStatsCollector 62 public class LLClientView : IClientAPI, IClientCore, IClientIM, IClientChat, IClientInventory, IStatsCollector
63 { 63 {
64 /// <value> 64 /// <value>
65 /// Debug packet level. See OpenSim.RegisterConsoleCommands() for more details. 65 /// Debug packet level. See OpenSim.RegisterConsoleCommands() for more details.
@@ -365,7 +365,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
365 protected string m_lastName; 365 protected string m_lastName;
366 protected Thread m_clientThread; 366 protected Thread m_clientThread;
367 protected Vector3 m_startpos; 367 protected Vector3 m_startpos;
368 protected EndPoint m_userEndPoint;
369 protected UUID m_activeGroupID; 368 protected UUID m_activeGroupID;
370 protected string m_activeGroupName = String.Empty; 369 protected string m_activeGroupName = String.Empty;
371 protected ulong m_activeGroupPowers; 370 protected ulong m_activeGroupPowers;
@@ -458,7 +457,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
458 /// <summary> 457 /// <summary>
459 /// Constructor 458 /// Constructor
460 /// </summary> 459 /// </summary>
461 public LLClientView(EndPoint remoteEP, Scene scene, LLUDPServer udpServer, LLUDPClient udpClient, AuthenticateResponse sessionInfo, 460 public LLClientView(Scene scene, LLUDPServer udpServer, LLUDPClient udpClient, AuthenticateResponse sessionInfo,
462 UUID agentId, UUID sessionId, uint circuitCode) 461 UUID agentId, UUID sessionId, uint circuitCode)
463 { 462 {
464// DebugPacketLevel = 1; 463// DebugPacketLevel = 1;
@@ -466,7 +465,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
466 RegisterInterface<IClientIM>(this); 465 RegisterInterface<IClientIM>(this);
467 RegisterInterface<IClientInventory>(this); 466 RegisterInterface<IClientInventory>(this);
468 RegisterInterface<IClientChat>(this); 467 RegisterInterface<IClientChat>(this);
469 RegisterInterface<IClientIPEndpoint>(this);
470 468
471 m_scene = scene; 469 m_scene = scene;
472 m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); 470 m_entityUpdates = new PriorityQueue(m_scene.Entities.Count);
@@ -483,7 +481,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
483 m_sessionId = sessionId; 481 m_sessionId = sessionId;
484 m_secureSessionId = sessionInfo.LoginInfo.SecureSession; 482 m_secureSessionId = sessionInfo.LoginInfo.SecureSession;
485 m_circuitCode = circuitCode; 483 m_circuitCode = circuitCode;
486 m_userEndPoint = remoteEP;
487 m_firstName = sessionInfo.LoginInfo.First; 484 m_firstName = sessionInfo.LoginInfo.First;
488 m_lastName = sessionInfo.LoginInfo.Last; 485 m_lastName = sessionInfo.LoginInfo.Last;
489 m_startpos = sessionInfo.LoginInfo.StartPos; 486 m_startpos = sessionInfo.LoginInfo.StartPos;
@@ -515,6 +512,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
515 /// </summary> 512 /// </summary>
516 public void Close(bool sendStop) 513 public void Close(bool sendStop)
517 { 514 {
515 IsActive = false;
516
518 m_log.DebugFormat( 517 m_log.DebugFormat(
519 "[CLIENT]: Close has been called for {0} attached to scene {1}", 518 "[CLIENT]: Close has been called for {0} attached to scene {1}",
520 Name, m_scene.RegionInfo.RegionName); 519 Name, m_scene.RegionInfo.RegionName);
@@ -3902,7 +3901,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3902 canUseImproved = false; 3901 canUseImproved = false;
3903 } 3902 }
3904 } 3903 }
3905 3904
3906 #endregion UpdateFlags to packet type conversion 3905 #endregion UpdateFlags to packet type conversion
3907 3906
3908 #region Block Construction 3907 #region Block Construction
@@ -11866,7 +11865,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11866 if (DebugPacketLevel <= 100 && (packet.Type == PacketType.AvatarAnimation || packet.Type == PacketType.ViewerEffect)) 11865 if (DebugPacketLevel <= 100 && (packet.Type == PacketType.AvatarAnimation || packet.Type == PacketType.ViewerEffect))
11867 logPacket = false; 11866 logPacket = false;
11868 11867
11869 if (DebugPacketLevel <= 50 && packet.Type == PacketType.ImprovedTerseObjectUpdate) 11868 if (DebugPacketLevel <= 50
11869 & (packet.Type == PacketType.ImprovedTerseObjectUpdate || packet.Type == PacketType.ObjectUpdate))
11870 logPacket = false; 11870 logPacket = false;
11871 11871
11872 if (DebugPacketLevel <= 25 && packet.Type == PacketType.ObjectPropertiesFamily) 11872 if (DebugPacketLevel <= 25 && packet.Type == PacketType.ObjectPropertiesFamily)
@@ -11979,7 +11979,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11979 { 11979 {
11980 ClientInfo info = m_udpClient.GetClientInfo(); 11980 ClientInfo info = m_udpClient.GetClientInfo();
11981 11981
11982 info.userEP = m_userEndPoint;
11983 info.proxyEP = null; 11982 info.proxyEP = null;
11984 info.agentcircuit = RequestClientInfo(); 11983 info.agentcircuit = RequestClientInfo();
11985 11984
@@ -11991,11 +11990,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11991 m_udpClient.SetClientInfo(info); 11990 m_udpClient.SetClientInfo(info);
11992 } 11991 }
11993 11992
11994 public EndPoint GetClientEP()
11995 {
11996 return m_userEndPoint;
11997 }
11998
11999 #region Media Parcel Members 11993 #region Media Parcel Members
12000 11994
12001 public void SendParcelMediaCommand(uint flags, ParcelMediaCommandEnum command, float time) 11995 public void SendParcelMediaCommand(uint flags, ParcelMediaCommandEnum command, float time)
@@ -12076,10 +12070,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
12076 return string.Empty; 12070 return string.Empty;
12077 } 12071 }
12078 12072
12079 public void KillEndDone()
12080 {
12081 }
12082
12083 #region IClientCore 12073 #region IClientCore
12084 12074
12085 private readonly Dictionary<Type, object> m_clientInterfaces = new Dictionary<Type, object>(); 12075 private readonly Dictionary<Type, object> m_clientInterfaces = new Dictionary<Type, object>();
@@ -12167,21 +12157,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP
12167 protected void MakeAssetRequest(TransferRequestPacket transferRequest, UUID taskID) 12157 protected void MakeAssetRequest(TransferRequestPacket transferRequest, UUID taskID)
12168 { 12158 {
12169 UUID requestID = UUID.Zero; 12159 UUID requestID = UUID.Zero;
12170 if (transferRequest.TransferInfo.SourceType == (int)SourceType.Asset) 12160 int sourceType = transferRequest.TransferInfo.SourceType;
12161
12162 if (sourceType == (int)SourceType.Asset)
12171 { 12163 {
12172 requestID = new UUID(transferRequest.TransferInfo.Params, 0); 12164 requestID = new UUID(transferRequest.TransferInfo.Params, 0);
12173 } 12165 }
12174 else if (transferRequest.TransferInfo.SourceType == (int)SourceType.SimInventoryItem) 12166 else if (sourceType == (int)SourceType.SimInventoryItem)
12175 { 12167 {
12176 requestID = new UUID(transferRequest.TransferInfo.Params, 80); 12168 requestID = new UUID(transferRequest.TransferInfo.Params, 80);
12177 } 12169 }
12178 else if (transferRequest.TransferInfo.SourceType == (int)SourceType.SimEstate) 12170 else if (sourceType == (int)SourceType.SimEstate)
12179 { 12171 {
12180 requestID = taskID; 12172 requestID = taskID;
12181 } 12173 }
12182 12174
12183 12175// m_log.DebugFormat(
12184// m_log.DebugFormat("[CLIENT]: {0} requesting asset {1}", Name, requestID); 12176// "[LLCLIENTVIEW]: Received transfer request for {0} in {1} type {2} by {3}",
12177// requestID, taskID, (SourceType)sourceType, Name);
12185 12178
12186 12179
12187 //Note, the bool returned from the below function is useless since it is always false. 12180 //Note, the bool returned from the below function is useless since it is always false.
@@ -12270,24 +12263,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
12270 return numPackets; 12263 return numPackets;
12271 } 12264 }
12272 12265
12273 #region IClientIPEndpoint Members
12274
12275 public IPAddress EndPoint
12276 {
12277 get
12278 {
12279 if (m_userEndPoint is IPEndPoint)
12280 {
12281 IPEndPoint ep = (IPEndPoint)m_userEndPoint;
12282
12283 return ep.Address;
12284 }
12285 return null;
12286 }
12287 }
12288
12289 #endregion
12290
12291 public void SendRebakeAvatarTextures(UUID textureID) 12266 public void SendRebakeAvatarTextures(UUID textureID)
12292 { 12267 {
12293 RebakeAvatarTexturesPacket pack = 12268 RebakeAvatarTexturesPacket pack =
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
index 0831946..6e7a6a8 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
@@ -147,23 +147,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP
147 private int m_elapsed500MSOutgoingPacketHandler; 147 private int m_elapsed500MSOutgoingPacketHandler;
148 148
149 /// <summary>Flag to signal when clients should check for resends</summary> 149 /// <summary>Flag to signal when clients should check for resends</summary>
150 private bool m_resendUnacked; 150 protected bool m_resendUnacked;
151
151 /// <summary>Flag to signal when clients should send ACKs</summary> 152 /// <summary>Flag to signal when clients should send ACKs</summary>
152 private bool m_sendAcks; 153 protected bool m_sendAcks;
154
153 /// <summary>Flag to signal when clients should send pings</summary> 155 /// <summary>Flag to signal when clients should send pings</summary>
154 private bool m_sendPing; 156 protected bool m_sendPing;
155 157
156 private ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>> m_pendingCache = new ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>>(); 158 private ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>> m_pendingCache = new ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>>();
157 159
158 private int m_defaultRTO = 0; 160 private int m_defaultRTO = 0;
159 private int m_maxRTO = 0; 161 private int m_maxRTO = 0;
160 162 private int m_ackTimeout = 0;
163 private int m_pausedAckTimeout = 0;
161 private bool m_disableFacelights = false; 164 private bool m_disableFacelights = false;
162 165
163 public Socket Server { get { return null; } } 166 public Socket Server { get { return null; } }
164 167
165 private int m_malformedCount = 0; // Guard against a spamming attack 168 private int m_malformedCount = 0; // Guard against a spamming attack
166 169
170 /// <summary>
171 /// Record current outgoing client for monitoring purposes.
172 /// </summary>
173 private IClientAPI m_currentOutgoingClient;
174
175 /// <summary>
176 /// Recording current incoming client for monitoring purposes.
177 /// </summary>
178 private IClientAPI m_currentIncomingClient;
179
167 public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) 180 public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
168 : base(listenIP, (int)port) 181 : base(listenIP, (int)port)
169 { 182 {
@@ -200,11 +213,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
200 m_defaultRTO = config.GetInt("DefaultRTO", 0); 213 m_defaultRTO = config.GetInt("DefaultRTO", 0);
201 m_maxRTO = config.GetInt("MaxRTO", 0); 214 m_maxRTO = config.GetInt("MaxRTO", 0);
202 m_disableFacelights = config.GetBoolean("DisableFacelights", false); 215 m_disableFacelights = config.GetBoolean("DisableFacelights", false);
216 m_ackTimeout = 1000 * config.GetInt("AckTimeout", 60);
217 m_pausedAckTimeout = 1000 * config.GetInt("PausedAckTimeout", 300);
203 } 218 }
204 else 219 else
205 { 220 {
206 PrimUpdatesPerCallback = 100; 221 PrimUpdatesPerCallback = 100;
207 TextureSendLimit = 20; 222 TextureSendLimit = 20;
223 m_ackTimeout = 1000 * 60; // 1 minute
224 m_pausedAckTimeout = 1000 * 300; // 5 minutes
208 } 225 }
209 226
210 #region BinaryStats 227 #region BinaryStats
@@ -241,19 +258,56 @@ namespace OpenSim.Region.ClientStack.LindenUDP
241 if (m_scene == null) 258 if (m_scene == null)
242 throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); 259 throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference");
243 260
244 m_log.Info("[LLUDPSERVER]: Starting the LLUDP server in " + (m_asyncPacketHandling ? "asynchronous" : "synchronous") + " mode"); 261 m_log.InfoFormat(
262 "[LLUDPSERVER]: Starting the LLUDP server in {0} mode",
263 m_asyncPacketHandling ? "asynchronous" : "synchronous");
245 264
246 base.Start(m_recvBufferSize, m_asyncPacketHandling); 265 base.Start(m_recvBufferSize, m_asyncPacketHandling);
247 266
248 // Start the packet processing threads 267 // Start the packet processing threads
249 Watchdog.StartThread( 268 Watchdog.StartThread(
250 IncomingPacketHandler, "Incoming Packets (" + m_scene.RegionInfo.RegionName + ")", ThreadPriority.Normal, false, true); 269 IncomingPacketHandler,
270 string.Format("Incoming Packets ({0})", m_scene.RegionInfo.RegionName),
271 ThreadPriority.Normal,
272 false,
273 true,
274 GetWatchdogIncomingAlarmData,
275 Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
276
251 Watchdog.StartThread( 277 Watchdog.StartThread(
252 OutgoingPacketHandler, "Outgoing Packets (" + m_scene.RegionInfo.RegionName + ")", ThreadPriority.Normal, false, true); 278 OutgoingPacketHandler,
279 string.Format("Outgoing Packets ({0})", m_scene.RegionInfo.RegionName),
280 ThreadPriority.Normal,
281 false,
282 true,
283 GetWatchdogOutgoingAlarmData,
284 Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
253 285
254 m_elapsedMSSinceLastStatReport = Environment.TickCount; 286 m_elapsedMSSinceLastStatReport = Environment.TickCount;
255 } 287 }
256 288
289 /// <summary>
290 /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging.
291 /// </summary>
292 /// <returns></returns>
293 private string GetWatchdogIncomingAlarmData()
294 {
295 return string.Format(
296 "Client is {0}",
297 m_currentIncomingClient != null ? m_currentIncomingClient.Name : "none");
298 }
299
300 /// <summary>
301 /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging.
302 /// </summary>
303 /// <returns></returns>
304 private string GetWatchdogOutgoingAlarmData()
305 {
306 return string.Format(
307 "Client is {0}",
308 m_currentOutgoingClient != null ? m_currentOutgoingClient.Name : "none");
309 }
310
257 public new void Stop() 311 public new void Stop()
258 { 312 {
259 m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName); 313 m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
@@ -487,19 +541,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP
487 SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false, null); 541 SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false, null);
488 } 542 }
489 543
490 public void HandleUnacked(LLUDPClient udpClient) 544 public void HandleUnacked(LLClientView client)
491 { 545 {
546 LLUDPClient udpClient = client.UDPClient;
547
492 if (!udpClient.IsConnected) 548 if (!udpClient.IsConnected)
493 return; 549 return;
494 550
495 // Disconnect an agent if no packets are received for some time 551 // Disconnect an agent if no packets are received for some time
496 //FIXME: Make 60 an .ini setting 552 int timeoutTicks = m_ackTimeout;
497 if ((Environment.TickCount & Int32.MaxValue) - udpClient.TickLastPacketReceived > 1000 * 60) 553
554 // Allow more slack if the client is "paused" eg file upload dialogue is open
555 // Some sort of limit is needed in case the client crashes, loses its network connection
556 // or some other disaster prevents it from sendung the AgentResume
557 if (udpClient.IsPaused)
558 timeoutTicks = m_pausedAckTimeout;
559
560 if (client.IsActive &&
561 (Environment.TickCount & Int32.MaxValue) - udpClient.TickLastPacketReceived > timeoutTicks)
498 { 562 {
499 m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); 563 // We must set IsActive synchronously so that we can stop the packet loop reinvoking this method, even
500 StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); 564 // though it's set later on by LLClientView.Close()
565 client.IsActive = false;
566
567 // Fire this out on a different thread so that we don't hold up outgoing packet processing for
568 // everybody else if this is being called due to an ack timeout.
569 // This is the same as processing as the async process of a logout request.
570 Util.FireAndForget(o => DeactivateClientDueToTimeout(client));
501 571
502 RemoveClient(udpClient);
503 return; 572 return;
504 } 573 }
505 574
@@ -850,7 +919,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
850 #endregion Ping Check Handling 919 #endregion Ping Check Handling
851 920
852 // Inbox insertion 921 // Inbox insertion
853 packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); 922 packetInbox.Enqueue(new IncomingPacket((LLClientView)client, packet));
854 } 923 }
855 924
856 #region BinaryStats 925 #region BinaryStats
@@ -946,7 +1015,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
946 UDPPacketBuffer buffer = (UDPPacketBuffer)array[0]; 1015 UDPPacketBuffer buffer = (UDPPacketBuffer)array[0];
947 UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1]; 1016 UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1];
948 1017
949 m_log.DebugFormat("[LLUDPSERVER]: Handling UseCircuitCode request from {0}", buffer.RemoteEndPoint); 1018 m_log.DebugFormat(
1019 "[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}",
1020 uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, buffer.RemoteEndPoint);
950 1021
951 remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint; 1022 remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint;
952 1023
@@ -1001,8 +1072,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1001 { 1072 {
1002 // Don't create clients for unauthorized requesters. 1073 // Don't create clients for unauthorized requesters.
1003 m_log.WarnFormat( 1074 m_log.WarnFormat(
1004 "[LLUDPSERVER]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", 1075 "[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}",
1005 uccp.CircuitCode.ID, uccp.CircuitCode.Code, remoteEndPoint); 1076 uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, remoteEndPoint);
1006 lock (m_pendingCache) 1077 lock (m_pendingCache)
1007 m_pendingCache.Remove(remoteEndPoint); 1078 m_pendingCache.Remove(remoteEndPoint);
1008 } 1079 }
@@ -1090,7 +1161,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1090 { 1161 {
1091 LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO); 1162 LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO);
1092 1163
1093 client = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); 1164 client = new LLClientView(m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode);
1094 client.OnLogout += LogoutHandler; 1165 client.OnLogout += LogoutHandler;
1095 1166
1096 ((LLClientView)client).DisableFacelights = m_disableFacelights; 1167 ((LLClientView)client).DisableFacelights = m_disableFacelights;
@@ -1102,15 +1173,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1102 return client; 1173 return client;
1103 } 1174 }
1104 1175
1105 private void RemoveClient(LLUDPClient udpClient) 1176 /// <summary>
1177 /// Deactivates the client if we don't receive any packets within a certain amount of time (default 60 seconds).
1178 /// </summary>
1179 /// <remarks>
1180 /// If a connection is active then we will always receive packets even if nothing else is happening, due to
1181 /// regular client pings.
1182 /// </remarks>
1183 /// <param name='client'></param>
1184 private void DeactivateClientDueToTimeout(IClientAPI client)
1106 { 1185 {
1107 // Remove this client from the scene 1186 // We must set IsActive synchronously so that we can stop the packet loop reinvoking this method, even
1108 IClientAPI client; 1187 // though it's set later on by LLClientView.Close()
1109 if (m_scene.TryGetClient(udpClient.AgentID, out client)) 1188 client.IsActive = false;
1110 { 1189
1111 client.IsLoggingOut = true; 1190 m_log.WarnFormat(
1112 client.Close(false); 1191 "[LLUDPSERVER]: Ack timeout, disconnecting {0} agent for {1} in {2}",
1113 } 1192 client.SceneAgent.IsChildAgent ? "child" : "root", client.Name, m_scene.RegionInfo.RegionName);
1193
1194 StatsManager.SimExtraStats.AddAbnormalClientThreadTermination();
1195
1196 if (!client.SceneAgent.IsChildAgent)
1197 client.Kick("Simulator logged you out due to connection timeout");
1198
1199 Util.FireAndForget(o => client.Close());
1114 } 1200 }
1115 1201
1116 private void IncomingPacketHandler() 1202 private void IncomingPacketHandler()
@@ -1219,6 +1305,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1219 // client. m_packetSent will be set to true if a packet is sent 1305 // client. m_packetSent will be set to true if a packet is sent
1220 m_scene.ForEachClient(clientPacketHandler); 1306 m_scene.ForEachClient(clientPacketHandler);
1221 1307
1308 m_currentOutgoingClient = null;
1309
1222 // If nothing was sent, sleep for the minimum amount of time before a 1310 // If nothing was sent, sleep for the minimum amount of time before a
1223 // token bucket could get more tokens 1311 // token bucket could get more tokens
1224 if (!m_packetSent) 1312 if (!m_packetSent)
@@ -1235,18 +1323,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1235 Watchdog.RemoveThread(); 1323 Watchdog.RemoveThread();
1236 } 1324 }
1237 1325
1238 private void ClientOutgoingPacketHandler(IClientAPI client) 1326 protected void ClientOutgoingPacketHandler(IClientAPI client)
1239 { 1327 {
1328 m_currentOutgoingClient = client;
1329
1240 try 1330 try
1241 { 1331 {
1242 if (client is LLClientView) 1332 if (client is LLClientView)
1243 { 1333 {
1244 LLUDPClient udpClient = ((LLClientView)client).UDPClient; 1334 LLClientView llClient = (LLClientView)client;
1335 LLUDPClient udpClient = llClient.UDPClient;
1245 1336
1246 if (udpClient.IsConnected) 1337 if (udpClient.IsConnected)
1247 { 1338 {
1248 if (m_resendUnacked) 1339 if (m_resendUnacked)
1249 HandleUnacked(udpClient); 1340 HandleUnacked(llClient);
1250 1341
1251 if (m_sendAcks) 1342 if (m_sendAcks)
1252 SendAcks(udpClient); 1343 SendAcks(udpClient);
@@ -1262,8 +1353,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1262 } 1353 }
1263 catch (Exception ex) 1354 catch (Exception ex)
1264 { 1355 {
1265 m_log.Error("[LLUDPSERVER]: OutgoingPacketHandler iteration for " + client.Name + 1356 m_log.Error(
1266 " threw an exception: " + ex.Message, ex); 1357 string.Format("[LLUDPSERVER]: OutgoingPacketHandler iteration for {0} threw ", client.Name), ex);
1267 } 1358 }
1268 } 1359 }
1269 1360
@@ -1289,11 +1380,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1289 { 1380 {
1290 nticks++; 1381 nticks++;
1291 watch1.Start(); 1382 watch1.Start();
1383 m_currentOutgoingClient = client;
1384
1292 try 1385 try
1293 { 1386 {
1294 if (client is LLClientView) 1387 if (client is LLClientView)
1295 { 1388 {
1296 LLUDPClient udpClient = ((LLClientView)client).UDPClient; 1389 LLClientView llClient = (LLClientView)client;
1390 LLUDPClient udpClient = llClient.UDPClient;
1297 1391
1298 if (udpClient.IsConnected) 1392 if (udpClient.IsConnected)
1299 { 1393 {
@@ -1302,7 +1396,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1302 nticksUnack++; 1396 nticksUnack++;
1303 watch2.Start(); 1397 watch2.Start();
1304 1398
1305 HandleUnacked(udpClient); 1399 HandleUnacked(llClient);
1306 1400
1307 watch2.Stop(); 1401 watch2.Stop();
1308 avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack); 1402 avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack);
@@ -1373,23 +1467,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1373 1467
1374 #endregion 1468 #endregion
1375 1469
1376 private void ProcessInPacket(object state) 1470 private void ProcessInPacket(IncomingPacket incomingPacket)
1377 { 1471 {
1378 IncomingPacket incomingPacket = (IncomingPacket)state;
1379 Packet packet = incomingPacket.Packet; 1472 Packet packet = incomingPacket.Packet;
1380 LLUDPClient udpClient = incomingPacket.Client; 1473 LLClientView client = incomingPacket.Client;
1381 IClientAPI client;
1382 1474
1383 // Sanity check 1475 if (client.IsActive)
1384 if (packet == null || udpClient == null)
1385 { 1476 {
1386 m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", UDPClient=\"{1}\"", 1477 m_currentIncomingClient = client;
1387 packet, udpClient);
1388 }
1389 1478
1390 // Make sure this client is still alive
1391 if (m_scene.TryGetClient(udpClient.AgentID, out client))
1392 {
1393 try 1479 try
1394 { 1480 {
1395 // Process this packet 1481 // Process this packet
@@ -1404,21 +1490,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1404 catch (Exception e) 1490 catch (Exception e)
1405 { 1491 {
1406 // Don't let a failure in an individual client thread crash the whole sim. 1492 // Don't let a failure in an individual client thread crash the whole sim.
1407 m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", udpClient.AgentID, packet.Type); 1493 m_log.Error(
1408 m_log.Error(e.Message, e); 1494 string.Format(
1495 "[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw ",
1496 client.Name, packet.Type),
1497 e);
1498 }
1499 finally
1500 {
1501 m_currentIncomingClient = null;
1409 } 1502 }
1410 } 1503 }
1411 else 1504 else
1412 { 1505 {
1413 m_log.DebugFormat("[LLUDPSERVER]: Dropping incoming {0} packet for dead client {1}", packet.Type, udpClient.AgentID); 1506 m_log.DebugFormat(
1507 "[LLUDPSERVER]: Dropped incoming {0} for dead client {1} in {2}",
1508 packet.Type, client.Name, m_scene.RegionInfo.RegionName);
1414 } 1509 }
1415 } 1510 }
1416 1511
1417 protected void LogoutHandler(IClientAPI client) 1512 protected void LogoutHandler(IClientAPI client)
1418 { 1513 {
1419 client.SendLogoutPacket(); 1514 client.SendLogoutPacket();
1420 if (client.IsActive) 1515
1421 RemoveClient(((LLClientView)client).UDPClient); 1516 if (!client.IsLoggingOut)
1517 {
1518 client.IsLoggingOut = true;
1519 client.Close();
1520 }
1422 } 1521 }
1423 } 1522 }
1424} 1523}
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs
index a575e36..109a8e1 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs
@@ -45,6 +45,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
45 [TestFixture] 45 [TestFixture]
46 public class BasicCircuitTests 46 public class BasicCircuitTests
47 { 47 {
48 private Scene m_scene;
49 private TestLLUDPServer m_udpServer;
50
48 [TestFixtureSetUp] 51 [TestFixtureSetUp]
49 public void FixtureInit() 52 public void FixtureInit()
50 { 53 {
@@ -61,83 +64,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
61 Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod; 64 Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod;
62 } 65 }
63 66
64// /// <summary> 67 [SetUp]
65// /// Add a client for testing 68 public void SetUp()
66// /// </summary> 69 {
67// /// <param name="scene"></param> 70 m_scene = new SceneHelpers().SetupScene();
68// /// <param name="testLLUDPServer"></param> 71 }
69// /// <param name="testPacketServer"></param>
70// /// <param name="acm">Agent circuit manager used in setting up the stack</param>
71// protected void SetupStack(
72// IScene scene, out TestLLUDPServer testLLUDPServer, out TestLLPacketServer testPacketServer,
73// out AgentCircuitManager acm)
74// {
75// IConfigSource configSource = new IniConfigSource();
76// ClientStackUserSettings userSettings = new ClientStackUserSettings();
77// testLLUDPServer = new TestLLUDPServer();
78// acm = new AgentCircuitManager();
79//
80// uint port = 666;
81// testLLUDPServer.Initialise(null, ref port, 0, false, configSource, acm);
82// testPacketServer = new TestLLPacketServer(testLLUDPServer, userSettings);
83// testLLUDPServer.LocalScene = scene;
84// }
85
86// /// <summary>
87// /// Set up a client for tests which aren't concerned with this process itself and where only one client is being
88// /// tested
89// /// </summary>
90// /// <param name="circuitCode"></param>
91// /// <param name="epSender"></param>
92// /// <param name="testLLUDPServer"></param>
93// /// <param name="acm"></param>
94// protected void AddClient(
95// uint circuitCode, EndPoint epSender, TestLLUDPServer testLLUDPServer, AgentCircuitManager acm)
96// {
97// UUID myAgentUuid = UUID.Parse("00000000-0000-0000-0000-000000000001");
98// UUID mySessionUuid = UUID.Parse("00000000-0000-0000-0000-000000000002");
99//
100// AddClient(circuitCode, epSender, myAgentUuid, mySessionUuid, testLLUDPServer, acm);
101// }
102
103// /// <summary>
104// /// Set up a client for tests which aren't concerned with this process itself
105// /// </summary>
106// /// <param name="circuitCode"></param>
107// /// <param name="epSender"></param>
108// /// <param name="agentId"></param>
109// /// <param name="sessionId"></param>
110// /// <param name="testLLUDPServer"></param>
111// /// <param name="acm"></param>
112// protected void AddClient(
113// uint circuitCode, EndPoint epSender, UUID agentId, UUID sessionId,
114// TestLLUDPServer testLLUDPServer, AgentCircuitManager acm)
115// {
116// AgentCircuitData acd = new AgentCircuitData();
117// acd.AgentID = agentId;
118// acd.SessionID = sessionId;
119//
120// UseCircuitCodePacket uccp = new UseCircuitCodePacket();
121//
122// UseCircuitCodePacket.CircuitCodeBlock uccpCcBlock
123// = new UseCircuitCodePacket.CircuitCodeBlock();
124// uccpCcBlock.Code = circuitCode;
125// uccpCcBlock.ID = agentId;
126// uccpCcBlock.SessionID = sessionId;
127// uccp.CircuitCode = uccpCcBlock;
128//
129// acm.AddNewCircuit(circuitCode, acd);
130//
131// testLLUDPServer.LoadReceive(uccp, epSender);
132// testLLUDPServer.ReceiveData(null);
133// }
134 72
135 /// <summary> 73 /// <summary>
136 /// Build an object name packet for test purposes 74 /// Build an object name packet for test purposes
137 /// </summary> 75 /// </summary>
138 /// <param name="objectLocalId"></param> 76 /// <param name="objectLocalId"></param>
139 /// <param name="objectName"></param> 77 /// <param name="objectName"></param>
140 protected ObjectNamePacket BuildTestObjectNamePacket(uint objectLocalId, string objectName) 78 private ObjectNamePacket BuildTestObjectNamePacket(uint objectLocalId, string objectName)
141 { 79 {
142 ObjectNamePacket onp = new ObjectNamePacket(); 80 ObjectNamePacket onp = new ObjectNamePacket();
143 ObjectNamePacket.ObjectDataBlock odb = new ObjectNamePacket.ObjectDataBlock(); 81 ObjectNamePacket.ObjectDataBlock odb = new ObjectNamePacket.ObjectDataBlock();
@@ -148,6 +86,55 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
148 86
149 return onp; 87 return onp;
150 } 88 }
89
90 private void AddUdpServer()
91 {
92 AddUdpServer(new IniConfigSource());
93 }
94
95 private void AddUdpServer(IniConfigSource configSource)
96 {
97 uint port = 0;
98 AgentCircuitManager acm = m_scene.AuthenticateHandler;
99
100 m_udpServer = new TestLLUDPServer(IPAddress.Any, ref port, 0, false, configSource, acm);
101 m_udpServer.AddScene(m_scene);
102 }
103
104 /// <summary>
105 /// Used by tests that aren't testing this stage.
106 /// </summary>
107 private ScenePresence AddClient()
108 {
109 UUID myAgentUuid = TestHelpers.ParseTail(0x1);
110 UUID mySessionUuid = TestHelpers.ParseTail(0x2);
111 uint myCircuitCode = 123456;
112 IPEndPoint testEp = new IPEndPoint(IPAddress.Loopback, 999);
113
114 UseCircuitCodePacket uccp = new UseCircuitCodePacket();
115
116 UseCircuitCodePacket.CircuitCodeBlock uccpCcBlock
117 = new UseCircuitCodePacket.CircuitCodeBlock();
118 uccpCcBlock.Code = myCircuitCode;
119 uccpCcBlock.ID = myAgentUuid;
120 uccpCcBlock.SessionID = mySessionUuid;
121 uccp.CircuitCode = uccpCcBlock;
122
123 byte[] uccpBytes = uccp.ToBytes();
124 UDPPacketBuffer upb = new UDPPacketBuffer(testEp, uccpBytes.Length);
125 upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor.
126 Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length);
127
128 AgentCircuitData acd = new AgentCircuitData();
129 acd.AgentID = myAgentUuid;
130 acd.SessionID = mySessionUuid;
131
132 m_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd);
133
134 m_udpServer.PacketReceived(upb);
135
136 return m_scene.GetScenePresence(myAgentUuid);
137 }
151 138
152 /// <summary> 139 /// <summary>
153 /// Test adding a client to the stack 140 /// Test adding a client to the stack
@@ -158,19 +145,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
158 TestHelpers.InMethod(); 145 TestHelpers.InMethod();
159// XmlConfigurator.Configure(); 146// XmlConfigurator.Configure();
160 147
161 TestScene scene = SceneHelpers.SetupScene(); 148 AddUdpServer();
162 uint myCircuitCode = 123456; 149
163 UUID myAgentUuid = TestHelpers.ParseTail(0x1); 150 UUID myAgentUuid = TestHelpers.ParseTail(0x1);
164 UUID mySessionUuid = TestHelpers.ParseTail(0x2); 151 UUID mySessionUuid = TestHelpers.ParseTail(0x2);
152 uint myCircuitCode = 123456;
165 IPEndPoint testEp = new IPEndPoint(IPAddress.Loopback, 999); 153 IPEndPoint testEp = new IPEndPoint(IPAddress.Loopback, 999);
166 154
167 uint port = 0;
168 AgentCircuitManager acm = scene.AuthenticateHandler;
169
170 TestLLUDPServer llUdpServer
171 = new TestLLUDPServer(IPAddress.Any, ref port, 0, false, new IniConfigSource(), acm);
172 llUdpServer.AddScene(scene);
173
174 UseCircuitCodePacket uccp = new UseCircuitCodePacket(); 155 UseCircuitCodePacket uccp = new UseCircuitCodePacket();
175 156
176 UseCircuitCodePacket.CircuitCodeBlock uccpCcBlock 157 UseCircuitCodePacket.CircuitCodeBlock uccpCcBlock
@@ -185,26 +166,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
185 upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor. 166 upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor.
186 Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length); 167 Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length);
187 168
188 llUdpServer.PacketReceived(upb); 169 m_udpServer.PacketReceived(upb);
189 170
190 // Presence shouldn't exist since the circuit manager doesn't know about this circuit for authentication yet 171 // Presence shouldn't exist since the circuit manager doesn't know about this circuit for authentication yet
191 Assert.That(scene.GetScenePresence(myAgentUuid), Is.Null); 172 Assert.That(m_scene.GetScenePresence(myAgentUuid), Is.Null);
192 173
193 AgentCircuitData acd = new AgentCircuitData(); 174 AgentCircuitData acd = new AgentCircuitData();
194 acd.AgentID = myAgentUuid; 175 acd.AgentID = myAgentUuid;
195 acd.SessionID = mySessionUuid; 176 acd.SessionID = mySessionUuid;
196 177
197 acm.AddNewCircuit(myCircuitCode, acd); 178 m_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd);
198 179
199 llUdpServer.PacketReceived(upb); 180 m_udpServer.PacketReceived(upb);
200 181
201 // Should succeed now 182 // Should succeed now
202 ScenePresence sp = scene.GetScenePresence(myAgentUuid); 183 ScenePresence sp = m_scene.GetScenePresence(myAgentUuid);
203 Assert.That(sp.UUID, Is.EqualTo(myAgentUuid)); 184 Assert.That(sp.UUID, Is.EqualTo(myAgentUuid));
204 185
205 Assert.That(llUdpServer.PacketsSent.Count, Is.EqualTo(1)); 186 Assert.That(m_udpServer.PacketsSent.Count, Is.EqualTo(1));
206 187
207 Packet packet = llUdpServer.PacketsSent[0]; 188 Packet packet = m_udpServer.PacketsSent[0];
208 Assert.That(packet, Is.InstanceOf(typeof(PacketAckPacket))); 189 Assert.That(packet, Is.InstanceOf(typeof(PacketAckPacket)));
209 190
210 PacketAckPacket ackPacket = packet as PacketAckPacket; 191 PacketAckPacket ackPacket = packet as PacketAckPacket;
@@ -212,6 +193,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
212 Assert.That(ackPacket.Packets[0].ID, Is.EqualTo(0)); 193 Assert.That(ackPacket.Packets[0].ID, Is.EqualTo(0));
213 } 194 }
214 195
196 [Test]
197 public void TestLogoutClientDueToAck()
198 {
199 TestHelpers.InMethod();
200// TestHelpers.EnableLogging();
201
202 IniConfigSource ics = new IniConfigSource();
203 IConfig config = ics.AddConfig("ClientStack.LindenUDP");
204 config.Set("AckTimeout", -1);
205 AddUdpServer(ics);
206
207 ScenePresence sp = AddClient();
208 m_udpServer.ClientOutgoingPacketHandler(sp.ControllingClient, true, false, false);
209
210 ScenePresence spAfterAckTimeout = m_scene.GetScenePresence(sp.UUID);
211 Assert.That(spAfterAckTimeout, Is.Null);
212
213// TestHelpers.DisableLogging();
214 }
215
215// /// <summary> 216// /// <summary>
216// /// Test removing a client from the stack 217// /// Test removing a client from the stack
217// /// </summary> 218// /// </summary>
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs
index 1b68d68..5fcf376 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs
@@ -79,7 +79,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
79 79
80 J2KDecoderModule j2kdm = new J2KDecoderModule(); 80 J2KDecoderModule j2kdm = new J2KDecoderModule();
81 81
82 scene = SceneHelpers.SetupScene(); 82 SceneHelpers sceneHelpers = new SceneHelpers();
83 scene = sceneHelpers.SetupScene();
83 SceneHelpers.SetupSceneModules(scene, j2kdm); 84 SceneHelpers.SetupSceneModules(scene, j2kdm);
84 85
85 tc = new TestClient(SceneHelpers.GenerateAgentData(userId), scene); 86 tc = new TestClient(SceneHelpers.GenerateAgentData(userId), scene);
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs
index d76927b..119a677 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs
@@ -44,9 +44,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
44 } 44 }
45 protected int m_objectNameCallsReceived; 45 protected int m_objectNameCallsReceived;
46 46
47 public MockScene() 47 public MockScene() : base(new RegionInfo(1000, 1000, null, null))
48 { 48 {
49 m_regInfo = new RegionInfo(1000, 1000, null, null);
50 m_regStatus = RegionStatus.Up; 49 m_regStatus = RegionStatus.Up;
51 } 50 }
52 51
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/TestLLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/TestLLUDPServer.cs
index 0302385..27b9e5b 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/TestLLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/TestLLUDPServer.cs
@@ -59,6 +59,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
59 PacketsSent.Add(packet); 59 PacketsSent.Add(packet);
60 } 60 }
61 61
62 public void ClientOutgoingPacketHandler(IClientAPI client, bool resendUnacked, bool sendAcks, bool sendPing)
63 {
64 m_resendUnacked = resendUnacked;
65 m_sendAcks = sendAcks;
66 m_sendPing = sendPing;
67
68 ClientOutgoingPacketHandler(client);
69 }
70
62//// /// <summary> 71//// /// <summary>
63//// /// The chunks of data to pass to the LLUDPServer when it calls EndReceive 72//// /// The chunks of data to pass to the LLUDPServer when it calls EndReceive
64//// /// </summary> 73//// /// </summary>