aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack')
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs923
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs671
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs12
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueHelper.cs29
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs228
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/MeshUploadFlagModule.cs16
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/NewFileAgentInventoryVariablePriceModule.cs296
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs7
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs200
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/J2KImage.cs3
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs1206
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs65
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs217
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs4
14 files changed, 2787 insertions, 1090 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs
index 185f9ce..c705f10 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs
@@ -26,6 +26,7 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.Timers;
29using System.Collections; 30using System.Collections;
30using System.Collections.Generic; 31using System.Collections.Generic;
31using System.IO; 32using System.IO;
@@ -53,14 +54,16 @@ using OSDMap = OpenMetaverse.StructuredData.OSDMap;
53namespace OpenSim.Region.ClientStack.Linden 54namespace OpenSim.Region.ClientStack.Linden
54{ 55{
55 public delegate void UpLoadedAsset( 56 public delegate void UpLoadedAsset(
56 string assetName, string description, UUID assetID, UUID inventoryItem, UUID parentFolder, 57 string assetName, string description, UUID assetID, UUID inventoryItem, UUID parentFolder,
57 byte[] data, string inventoryType, string assetType); 58 byte[] data, string inventoryType, string assetType,
59 int cost, UUID texturesFolder, int nreqtextures, int nreqmeshs, int nreqinstances,
60 bool IsAtestUpload, ref string error);
58 61
59 public delegate UUID UpdateItem(UUID itemID, byte[] data); 62 public delegate UUID UpdateItem(UUID itemID, byte[] data);
60 63
61 public delegate void UpdateTaskScript(UUID itemID, UUID primID, bool isScriptRunning, byte[] data, ref ArrayList errors); 64 public delegate void UpdateTaskScript(UUID itemID, UUID primID, bool isScriptRunning, byte[] data, ref ArrayList errors);
62 65
63 public delegate void NewInventoryItem(UUID userID, InventoryItemBase item); 66 public delegate void NewInventoryItem(UUID userID, InventoryItemBase item, uint cost);
64 67
65 public delegate void NewAsset(AssetBase asset); 68 public delegate void NewAsset(AssetBase asset);
66 69
@@ -86,6 +89,7 @@ namespace OpenSim.Region.ClientStack.Linden
86 89
87 private Scene m_Scene; 90 private Scene m_Scene;
88 private Caps m_HostCapsObj; 91 private Caps m_HostCapsObj;
92 private ModelCost m_ModelCost;
89 93
90 private static readonly string m_requestPath = "0000/"; 94 private static readonly string m_requestPath = "0000/";
91 // private static readonly string m_mapLayerPath = "0001/"; 95 // private static readonly string m_mapLayerPath = "0001/";
@@ -96,7 +100,10 @@ namespace OpenSim.Region.ClientStack.Linden
96 // private static readonly string m_fetchInventoryPath = "0006/"; 100 // private static readonly string m_fetchInventoryPath = "0006/";
97 private static readonly string m_copyFromNotecardPath = "0007/"; 101 private static readonly string m_copyFromNotecardPath = "0007/";
98 // private static readonly string m_remoteParcelRequestPath = "0009/";// This is in the LandManagementModule. 102 // private static readonly string m_remoteParcelRequestPath = "0009/";// This is in the LandManagementModule.
99 103 private static readonly string m_getObjectPhysicsDataPath = "0101/";
104 private static readonly string m_getObjectCostPath = "0102/";
105 private static readonly string m_ResourceCostSelectedPath = "0103/";
106
100 107
101 // These are callbacks which will be setup by the scene so that we can update scene data when we 108 // These are callbacks which will be setup by the scene so that we can update scene data when we
102 // receive capability calls 109 // receive capability calls
@@ -111,12 +118,50 @@ namespace OpenSim.Region.ClientStack.Linden
111 private IAssetService m_assetService; 118 private IAssetService m_assetService;
112 private bool m_dumpAssetsToFile = false; 119 private bool m_dumpAssetsToFile = false;
113 private string m_regionName; 120 private string m_regionName;
121
114 private int m_levelUpload = 0; 122 private int m_levelUpload = 0;
115 123
124 private bool m_enableFreeTestUpload = false; // allows "TEST-" prefix hack
125 private bool m_ForceFreeTestUpload = false; // forces all uploads to be test
126
127 private bool m_enableModelUploadTextureToInventory = false; // place uploaded textures also in inventory
128 // may not be visible till relog
129
130 private bool m_RestrictFreeTestUploadPerms = false; // reduces also the permitions. Needs a creator defined!!
131 private UUID m_testAssetsCreatorID = UUID.Zero;
132
133 private float m_PrimScaleMin = 0.001f;
134
135 private enum FileAgentInventoryState : int
136 {
137 idle = 0,
138 processRequest = 1,
139 waitUpload = 2,
140 processUpload = 3
141 }
142 private FileAgentInventoryState m_FileAgentInventoryState = FileAgentInventoryState.idle;
143
116 public BunchOfCaps(Scene scene, Caps caps) 144 public BunchOfCaps(Scene scene, Caps caps)
117 { 145 {
118 m_Scene = scene; 146 m_Scene = scene;
119 m_HostCapsObj = caps; 147 m_HostCapsObj = caps;
148
149 // create a model upload cost provider
150 m_ModelCost = new ModelCost();
151 // tell it about scene object limits
152 m_ModelCost.NonPhysicalPrimScaleMax = m_Scene.m_maxNonphys;
153 m_ModelCost.PhysicalPrimScaleMax = m_Scene.m_maxPhys;
154
155// m_ModelCost.ObjectLinkedPartsMax = ??
156// m_ModelCost.PrimScaleMin = ??
157
158 m_PrimScaleMin = m_ModelCost.PrimScaleMin;
159 float modelTextureUploadFactor = m_ModelCost.ModelTextureCostFactor;
160 float modelUploadFactor = m_ModelCost.ModelMeshCostFactor;
161 float modelMinUploadCostFactor = m_ModelCost.ModelMinCostFactor;
162 float modelPrimCreationCost = m_ModelCost.primCreationCost;
163 float modelMeshByteCost = m_ModelCost.bytecost;
164
120 IConfigSource config = m_Scene.Config; 165 IConfigSource config = m_Scene.Config;
121 if (config != null) 166 if (config != null)
122 { 167 {
@@ -131,6 +176,37 @@ namespace OpenSim.Region.ClientStack.Linden
131 { 176 {
132 m_persistBakedTextures = appearanceConfig.GetBoolean("PersistBakedTextures", m_persistBakedTextures); 177 m_persistBakedTextures = appearanceConfig.GetBoolean("PersistBakedTextures", m_persistBakedTextures);
133 } 178 }
179 // economy for model upload
180 IConfig EconomyConfig = config.Configs["Economy"];
181 if (EconomyConfig != null)
182 {
183 modelUploadFactor = EconomyConfig.GetFloat("MeshModelUploadCostFactor", modelUploadFactor);
184 modelTextureUploadFactor = EconomyConfig.GetFloat("MeshModelUploadTextureCostFactor", modelTextureUploadFactor);
185 modelMinUploadCostFactor = EconomyConfig.GetFloat("MeshModelMinCostFactor", modelMinUploadCostFactor);
186 // next 2 are normalized so final cost is afected by modelUploadFactor above and normal cost
187 modelPrimCreationCost = EconomyConfig.GetFloat("ModelPrimCreationCost", modelPrimCreationCost);
188 modelMeshByteCost = EconomyConfig.GetFloat("ModelMeshByteCost", modelMeshByteCost);
189
190 m_enableModelUploadTextureToInventory = EconomyConfig.GetBoolean("MeshModelAllowTextureToInventory", m_enableModelUploadTextureToInventory);
191
192 m_RestrictFreeTestUploadPerms = EconomyConfig.GetBoolean("m_RestrictFreeTestUploadPerms", m_RestrictFreeTestUploadPerms);
193 m_enableFreeTestUpload = EconomyConfig.GetBoolean("AllowFreeTestUpload", m_enableFreeTestUpload);
194 m_ForceFreeTestUpload = EconomyConfig.GetBoolean("ForceFreeTestUpload", m_ForceFreeTestUpload);
195 string testcreator = EconomyConfig.GetString("TestAssetsCreatorID", "");
196 if (testcreator != "")
197 {
198 UUID id;
199 UUID.TryParse(testcreator, out id);
200 if (id != null)
201 m_testAssetsCreatorID = id;
202 }
203
204 m_ModelCost.ModelMeshCostFactor = modelUploadFactor;
205 m_ModelCost.ModelTextureCostFactor = modelTextureUploadFactor;
206 m_ModelCost.ModelMinCostFactor = modelMinUploadCostFactor;
207 m_ModelCost.primCreationCost = modelPrimCreationCost;
208 m_ModelCost.bytecost = modelMeshByteCost;
209 }
134 } 210 }
135 211
136 m_assetService = m_Scene.AssetService; 212 m_assetService = m_Scene.AssetService;
@@ -142,6 +218,8 @@ namespace OpenSim.Region.ClientStack.Linden
142 ItemUpdatedCall = m_Scene.CapsUpdateInventoryItemAsset; 218 ItemUpdatedCall = m_Scene.CapsUpdateInventoryItemAsset;
143 TaskScriptUpdatedCall = m_Scene.CapsUpdateTaskInventoryScriptAsset; 219 TaskScriptUpdatedCall = m_Scene.CapsUpdateTaskInventoryScriptAsset;
144 GetClient = m_Scene.SceneGraph.GetControllingClient; 220 GetClient = m_Scene.SceneGraph.GetControllingClient;
221
222 m_FileAgentInventoryState = FileAgentInventoryState.idle;
145 } 223 }
146 224
147 /// <summary> 225 /// <summary>
@@ -187,7 +265,6 @@ namespace OpenSim.Region.ClientStack.Linden
187 { 265 {
188 try 266 try
189 { 267 {
190 // I don't think this one works...
191 m_HostCapsObj.RegisterHandler( 268 m_HostCapsObj.RegisterHandler(
192 "NewFileAgentInventory", 269 "NewFileAgentInventory",
193 new LLSDStreamhandler<LLSDAssetUploadRequest, LLSDAssetUploadResponse>( 270 new LLSDStreamhandler<LLSDAssetUploadRequest, LLSDAssetUploadResponse>(
@@ -204,6 +281,12 @@ namespace OpenSim.Region.ClientStack.Linden
204 m_HostCapsObj.RegisterHandler("UpdateNotecardAgentInventory", req); 281 m_HostCapsObj.RegisterHandler("UpdateNotecardAgentInventory", req);
205 m_HostCapsObj.RegisterHandler("UpdateScriptAgentInventory", req); 282 m_HostCapsObj.RegisterHandler("UpdateScriptAgentInventory", req);
206 m_HostCapsObj.RegisterHandler("UpdateScriptAgent", req); 283 m_HostCapsObj.RegisterHandler("UpdateScriptAgent", req);
284 IRequestHandler getObjectPhysicsDataHandler = new RestStreamHandler("POST", capsBase + m_getObjectPhysicsDataPath, GetObjectPhysicsData);
285 m_HostCapsObj.RegisterHandler("GetObjectPhysicsData", getObjectPhysicsDataHandler);
286 IRequestHandler getObjectCostHandler = new RestStreamHandler("POST", capsBase + m_getObjectCostPath, GetObjectCost);
287 m_HostCapsObj.RegisterHandler("GetObjectCost", getObjectCostHandler);
288 IRequestHandler ResourceCostSelectedHandler = new RestStreamHandler("POST", capsBase + m_ResourceCostSelectedPath, ResourceCostSelected);
289 m_HostCapsObj.RegisterHandler("ResourceCostSelected", ResourceCostSelectedHandler);
207 290
208 m_HostCapsObj.RegisterHandler( 291 m_HostCapsObj.RegisterHandler(
209 "CopyInventoryFromNotecard", 292 "CopyInventoryFromNotecard",
@@ -385,62 +468,176 @@ namespace OpenSim.Region.ClientStack.Linden
385 //m_log.Debug("[CAPS]: NewAgentInventoryRequest Request is: " + llsdRequest.ToString()); 468 //m_log.Debug("[CAPS]: NewAgentInventoryRequest Request is: " + llsdRequest.ToString());
386 //m_log.Debug("asset upload request via CAPS" + llsdRequest.inventory_type + " , " + llsdRequest.asset_type); 469 //m_log.Debug("asset upload request via CAPS" + llsdRequest.inventory_type + " , " + llsdRequest.asset_type);
387 470
471 // start by getting the client
472 IClientAPI client = null;
473 m_Scene.TryGetClient(m_HostCapsObj.AgentID, out client);
474
475 // check current state so we only have one service at a time
476 lock (m_ModelCost)
477 {
478 switch (m_FileAgentInventoryState)
479 {
480 case FileAgentInventoryState.processRequest:
481 case FileAgentInventoryState.processUpload:
482 LLSDAssetUploadError resperror = new LLSDAssetUploadError();
483 resperror.message = "Uploader busy processing previus request";
484 resperror.identifier = UUID.Zero;
485
486 LLSDAssetUploadResponse errorResponse = new LLSDAssetUploadResponse();
487 errorResponse.uploader = "";
488 errorResponse.state = "error";
489 errorResponse.error = resperror;
490 return errorResponse;
491 break;
492 case FileAgentInventoryState.waitUpload:
493 // todo stop current uploader server
494 break;
495 case FileAgentInventoryState.idle:
496 default:
497 break;
498 }
499
500 m_FileAgentInventoryState = FileAgentInventoryState.processRequest;
501 }
502
503 int cost = 0;
504 int nreqtextures = 0;
505 int nreqmeshs= 0;
506 int nreqinstances = 0;
507 bool IsAtestUpload = false;
508
509 string assetName = llsdRequest.name;
510
511 LLSDAssetUploadResponseData meshcostdata = new LLSDAssetUploadResponseData();
512
388 if (llsdRequest.asset_type == "texture" || 513 if (llsdRequest.asset_type == "texture" ||
389 llsdRequest.asset_type == "animation" || 514 llsdRequest.asset_type == "animation" ||
515 llsdRequest.asset_type == "mesh" ||
390 llsdRequest.asset_type == "sound") 516 llsdRequest.asset_type == "sound")
391 { 517 {
392 ScenePresence avatar = null; 518 ScenePresence avatar = null;
393 IClientAPI client = null;
394 m_Scene.TryGetScenePresence(m_HostCapsObj.AgentID, out avatar); 519 m_Scene.TryGetScenePresence(m_HostCapsObj.AgentID, out avatar);
395 520
396 // check user level 521 // check user level
397 if (avatar != null) 522 if (avatar != null)
398 { 523 {
399 client = avatar.ControllingClient;
400
401 if (avatar.UserLevel < m_levelUpload) 524 if (avatar.UserLevel < m_levelUpload)
402 { 525 {
403 if (client != null) 526 LLSDAssetUploadError resperror = new LLSDAssetUploadError();
404 client.SendAgentAlertMessage("Unable to upload asset. Insufficient permissions.", false); 527 resperror.message = "Insufficient permissions to upload";
528 resperror.identifier = UUID.Zero;
405 529
406 LLSDAssetUploadResponse errorResponse = new LLSDAssetUploadResponse(); 530 LLSDAssetUploadResponse errorResponse = new LLSDAssetUploadResponse();
407 errorResponse.uploader = ""; 531 errorResponse.uploader = "";
408 errorResponse.state = "error"; 532 errorResponse.state = "error";
533 errorResponse.error = resperror;
534 lock (m_ModelCost)
535 m_FileAgentInventoryState = FileAgentInventoryState.idle;
409 return errorResponse; 536 return errorResponse;
410 } 537 }
411 } 538 }
412 539
413 // check funds 540 // check test upload and funds
414 if (client != null) 541 if (client != null)
415 { 542 {
416 IMoneyModule mm = m_Scene.RequestModuleInterface<IMoneyModule>(); 543 IMoneyModule mm = m_Scene.RequestModuleInterface<IMoneyModule>();
417 544
545 int baseCost = 0;
418 if (mm != null) 546 if (mm != null)
547 baseCost = mm.UploadCharge;
548
549 string warning = String.Empty;
550
551 if (llsdRequest.asset_type == "mesh")
419 { 552 {
420 if (!mm.UploadCovered(client.AgentId, mm.UploadCharge)) 553 string error;
554 int modelcost;
555
556 if (!m_ModelCost.MeshModelCost(llsdRequest.asset_resources, baseCost, out modelcost,
557 meshcostdata, out error, ref warning))
421 { 558 {
422 client.SendAgentAlertMessage("Unable to upload asset. Insufficient funds.", false); 559 LLSDAssetUploadError resperror = new LLSDAssetUploadError();
560 resperror.message = error;
561 resperror.identifier = UUID.Zero;
423 562
424 LLSDAssetUploadResponse errorResponse = new LLSDAssetUploadResponse(); 563 LLSDAssetUploadResponse errorResponse = new LLSDAssetUploadResponse();
425 errorResponse.uploader = ""; 564 errorResponse.uploader = "";
426 errorResponse.state = "error"; 565 errorResponse.state = "error";
566 errorResponse.error = resperror;
567
568 lock (m_ModelCost)
569 m_FileAgentInventoryState = FileAgentInventoryState.idle;
427 return errorResponse; 570 return errorResponse;
428 } 571 }
572 cost = modelcost;
429 } 573 }
574 else
575 {
576 cost = baseCost;
577 }
578
579 if (cost > 0 && mm != null)
580 {
581 // check for test upload
582
583 if (m_ForceFreeTestUpload) // all are test
584 {
585 if (!(assetName.Length > 5 && assetName.StartsWith("TEST-"))) // has normal name lets change it
586 assetName = "TEST-" + assetName;
587
588 IsAtestUpload = true;
589 }
590
591 else if (m_enableFreeTestUpload) // only if prefixed with "TEST-"
592 {
593
594 IsAtestUpload = (assetName.Length > 5 && assetName.StartsWith("TEST-"));
595 }
596
597
598 if(IsAtestUpload) // let user know, still showing cost estimation
599 warning += "Upload will have no cost, for testing purposes only. Other uses are prohibited. Items will not work after 48 hours or on other regions";
600
601 // check funds
602 else
603 {
604 if (!mm.UploadCovered(client.AgentId, (int)cost))
605 {
606 LLSDAssetUploadError resperror = new LLSDAssetUploadError();
607 resperror.message = "Insuficient funds";
608 resperror.identifier = UUID.Zero;
609
610 LLSDAssetUploadResponse errorResponse = new LLSDAssetUploadResponse();
611 errorResponse.uploader = "";
612 errorResponse.state = "error";
613 errorResponse.error = resperror;
614 lock (m_ModelCost)
615 m_FileAgentInventoryState = FileAgentInventoryState.idle;
616 return errorResponse;
617 }
618 }
619 }
620
621 if (client != null && warning != String.Empty)
622 client.SendAgentAlertMessage(warning, true);
430 } 623 }
431 } 624 }
432 625
433 string assetName = llsdRequest.name;
434 string assetDes = llsdRequest.description; 626 string assetDes = llsdRequest.description;
435 string capsBase = "/CAPS/" + m_HostCapsObj.CapsObjectPath; 627 string capsBase = "/CAPS/" + m_HostCapsObj.CapsObjectPath;
436 UUID newAsset = UUID.Random(); 628 UUID newAsset = UUID.Random();
437 UUID newInvItem = UUID.Random(); 629 UUID newInvItem = UUID.Random();
438 UUID parentFolder = llsdRequest.folder_id; 630 UUID parentFolder = llsdRequest.folder_id;
439 string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000"); 631 string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000");
632 UUID texturesFolder = UUID.Zero;
633
634 if(!IsAtestUpload && m_enableModelUploadTextureToInventory)
635 texturesFolder = llsdRequest.texture_folder_id;
440 636
441 AssetUploader uploader = 637 AssetUploader uploader =
442 new AssetUploader(assetName, assetDes, newAsset, newInvItem, parentFolder, llsdRequest.inventory_type, 638 new AssetUploader(assetName, assetDes, newAsset, newInvItem, parentFolder, llsdRequest.inventory_type,
443 llsdRequest.asset_type, capsBase + uploaderPath, m_HostCapsObj.HttpListener, m_dumpAssetsToFile); 639 llsdRequest.asset_type, capsBase + uploaderPath, m_HostCapsObj.HttpListener, m_dumpAssetsToFile, cost,
640 texturesFolder, nreqtextures, nreqmeshs, nreqinstances, IsAtestUpload);
444 641
445 m_HostCapsObj.HttpListener.AddStreamHandler( 642 m_HostCapsObj.HttpListener.AddStreamHandler(
446 new BinaryStreamHandler( 643 new BinaryStreamHandler(
@@ -458,10 +655,22 @@ namespace OpenSim.Region.ClientStack.Linden
458 string uploaderURL = protocol + m_HostCapsObj.HostName + ":" + m_HostCapsObj.Port.ToString() + capsBase + 655 string uploaderURL = protocol + m_HostCapsObj.HostName + ":" + m_HostCapsObj.Port.ToString() + capsBase +
459 uploaderPath; 656 uploaderPath;
460 657
658
461 LLSDAssetUploadResponse uploadResponse = new LLSDAssetUploadResponse(); 659 LLSDAssetUploadResponse uploadResponse = new LLSDAssetUploadResponse();
462 uploadResponse.uploader = uploaderURL; 660 uploadResponse.uploader = uploaderURL;
463 uploadResponse.state = "upload"; 661 uploadResponse.state = "upload";
662 uploadResponse.upload_price = (int)cost;
663
664 if (llsdRequest.asset_type == "mesh")
665 {
666 uploadResponse.data = meshcostdata;
667 }
668
464 uploader.OnUpLoad += UploadCompleteHandler; 669 uploader.OnUpLoad += UploadCompleteHandler;
670
671 lock (m_ModelCost)
672 m_FileAgentInventoryState = FileAgentInventoryState.waitUpload;
673
465 return uploadResponse; 674 return uploadResponse;
466 } 675 }
467 676
@@ -473,8 +682,14 @@ namespace OpenSim.Region.ClientStack.Linden
473 /// <param name="data"></param> 682 /// <param name="data"></param>
474 public void UploadCompleteHandler(string assetName, string assetDescription, UUID assetID, 683 public void UploadCompleteHandler(string assetName, string assetDescription, UUID assetID,
475 UUID inventoryItem, UUID parentFolder, byte[] data, string inventoryType, 684 UUID inventoryItem, UUID parentFolder, byte[] data, string inventoryType,
476 string assetType) 685 string assetType, int cost,
686 UUID texturesFolder, int nreqtextures, int nreqmeshs, int nreqinstances,
687 bool IsAtestUpload, ref string error)
477 { 688 {
689
690 lock (m_ModelCost)
691 m_FileAgentInventoryState = FileAgentInventoryState.processUpload;
692
478 m_log.DebugFormat( 693 m_log.DebugFormat(
479 "[BUNCH OF CAPS]: Uploaded asset {0} for inventory item {1}, inv type {2}, asset type {3}", 694 "[BUNCH OF CAPS]: Uploaded asset {0} for inventory item {1}, inv type {2}, asset type {3}",
480 assetID, inventoryItem, inventoryType, assetType); 695 assetID, inventoryItem, inventoryType, assetType);
@@ -482,117 +697,247 @@ namespace OpenSim.Region.ClientStack.Linden
482 sbyte assType = 0; 697 sbyte assType = 0;
483 sbyte inType = 0; 698 sbyte inType = 0;
484 699
700 IClientAPI client = null;
701
702 UUID owner_id = m_HostCapsObj.AgentID;
703 UUID creatorID;
704
705 bool istest = IsAtestUpload && m_enableFreeTestUpload && (cost > 0);
706
707 bool restrictPerms = m_RestrictFreeTestUploadPerms && istest;
708
709 if (istest && m_testAssetsCreatorID != UUID.Zero)
710 creatorID = m_testAssetsCreatorID;
711 else
712 creatorID = owner_id;
713
714 string creatorIDstr = creatorID.ToString();
715
716 IMoneyModule mm = m_Scene.RequestModuleInterface<IMoneyModule>();
717 if (mm != null)
718 {
719 // make sure client still has enougth credit
720 if (!mm.UploadCovered(m_HostCapsObj.AgentID, (int)cost))
721 {
722 error = "Insufficient funds.";
723 return;
724 }
725 }
726
727 // strings to types
485 if (inventoryType == "sound") 728 if (inventoryType == "sound")
486 { 729 {
487 inType = 1; 730 inType = (sbyte)InventoryType.Sound;
488 assType = 1; 731 assType = (sbyte)AssetType.Sound;
489 } 732 }
490 else if (inventoryType == "animation") 733 else if (inventoryType == "animation")
491 { 734 {
492 inType = 19; 735 inType = (sbyte)InventoryType.Animation;
493 assType = 20; 736 assType = (sbyte)AssetType.Animation;
494 } 737 }
495 else if (inventoryType == "wearable") 738 else if (inventoryType == "wearable")
496 { 739 {
497 inType = 18; 740 inType = (sbyte)InventoryType.Wearable;
498 switch (assetType) 741 switch (assetType)
499 { 742 {
500 case "bodypart": 743 case "bodypart":
501 assType = 13; 744 assType = (sbyte)AssetType.Bodypart;
502 break; 745 break;
503 case "clothing": 746 case "clothing":
504 assType = 5; 747 assType = (sbyte)AssetType.Clothing;
505 break; 748 break;
506 } 749 }
507 } 750 }
508 else if (inventoryType == "object") 751 else if (inventoryType == "object")
509 { 752 {
510 inType = (sbyte)InventoryType.Object; 753 if (assetType == "mesh") // this code for now is for mesh models uploads only
511 assType = (sbyte)AssetType.Object;
512
513 List<Vector3> positions = new List<Vector3>();
514 List<Quaternion> rotations = new List<Quaternion>();
515 OSDMap request = (OSDMap)OSDParser.DeserializeLLSDXml(data);
516 OSDArray instance_list = (OSDArray)request["instance_list"];
517 OSDArray mesh_list = (OSDArray)request["mesh_list"];
518 OSDArray texture_list = (OSDArray)request["texture_list"];
519 SceneObjectGroup grp = null;
520
521 List<UUID> textures = new List<UUID>();
522 for (int i = 0; i < texture_list.Count; i++)
523 { 754 {
524 AssetBase textureAsset = new AssetBase(UUID.Random(), assetName, (sbyte)AssetType.Texture, ""); 755 inType = (sbyte)InventoryType.Object;
525 textureAsset.Data = texture_list[i].AsBinary(); 756 assType = (sbyte)AssetType.Object;
526 m_assetService.Store(textureAsset);
527 textures.Add(textureAsset.FullID);
528 }
529 757
530 for (int i = 0; i < mesh_list.Count; i++) 758 List<Vector3> positions = new List<Vector3>();
531 { 759 List<Quaternion> rotations = new List<Quaternion>();
532 PrimitiveBaseShape pbs = PrimitiveBaseShape.CreateBox(); 760 OSDMap request = (OSDMap)OSDParser.DeserializeLLSDXml(data);
761
762 // compare and get updated information
533 763
534 Primitive.TextureEntry textureEntry 764 bool mismatchError = true;
535 = new Primitive.TextureEntry(Primitive.TextureEntry.WHITE_TEXTURE);
536 OSDMap inner_instance_list = (OSDMap)instance_list[i];
537 765
538 OSDArray face_list = (OSDArray)inner_instance_list["face_list"]; 766 while (mismatchError)
539 for (uint face = 0; face < face_list.Count; face++)
540 { 767 {
541 OSDMap faceMap = (OSDMap)face_list[(int)face]; 768 mismatchError = false;
542 Primitive.TextureEntryFace f = pbs.Textures.CreateFace(face); 769 }
543 if(faceMap.ContainsKey("fullbright"))
544 f.Fullbright = faceMap["fullbright"].AsBoolean();
545 if (faceMap.ContainsKey ("diffuse_color"))
546 f.RGBA = faceMap["diffuse_color"].AsColor4();
547 770
548 int textureNum = faceMap["image"].AsInteger(); 771 if (mismatchError)
549 float imagerot = faceMap["imagerot"].AsInteger(); 772 {
550 float offsets = (float)faceMap["offsets"].AsReal(); 773 error = "Upload and fee estimation information don't match";
551 float offsett = (float)faceMap["offsett"].AsReal(); 774 lock (m_ModelCost)
552 float scales = (float)faceMap["scales"].AsReal(); 775 m_FileAgentInventoryState = FileAgentInventoryState.idle;
553 float scalet = (float)faceMap["scalet"].AsReal();
554 776
555 if(imagerot != 0) 777 return;
556 f.Rotation = imagerot; 778 }
557 779
558 if(offsets != 0) 780 OSDArray instance_list = (OSDArray)request["instance_list"];
559 f.OffsetU = offsets; 781 OSDArray mesh_list = (OSDArray)request["mesh_list"];
782 OSDArray texture_list = (OSDArray)request["texture_list"];
783 SceneObjectGroup grp = null;
560 784
561 if (offsett != 0) 785 // create and store texture assets
562 f.OffsetV = offsett; 786 bool doTextInv = (!istest && m_enableModelUploadTextureToInventory &&
787 texturesFolder != UUID.Zero);
563 788
564 if (scales != 0)
565 f.RepeatU = scales;
566 789
567 if (scalet != 0) 790 List<UUID> textures = new List<UUID>();
568 f.RepeatV = scalet;
569 791
570 if (textures.Count > textureNum) 792
571 f.TextureID = textures[textureNum]; 793 if (doTextInv)
572 else 794 m_Scene.TryGetClient(m_HostCapsObj.AgentID, out client);
573 f.TextureID = Primitive.TextureEntry.WHITE_TEXTURE;
574 795
575 textureEntry.FaceTextures[face] = f; 796 if(client == null) // don't put textures in inventory if there is no client
797 doTextInv = false;
798
799 for (int i = 0; i < texture_list.Count; i++)
800 {
801 AssetBase textureAsset = new AssetBase(UUID.Random(), assetName, (sbyte)AssetType.Texture, creatorIDstr);
802 textureAsset.Data = texture_list[i].AsBinary();
803 if (istest)
804 textureAsset.Local = true;
805 m_assetService.Store(textureAsset);
806 textures.Add(textureAsset.FullID);
807
808 if (doTextInv)
809 {
810 string name = assetName;
811 if (name.Length > 25)
812 name = name.Substring(0, 24);
813 name += "_Texture#" + i.ToString();
814 InventoryItemBase texitem = new InventoryItemBase();
815 texitem.Owner = m_HostCapsObj.AgentID;
816 texitem.CreatorId = creatorIDstr;
817 texitem.CreatorData = String.Empty;
818 texitem.ID = UUID.Random();
819 texitem.AssetID = textureAsset.FullID;
820 texitem.Description = "mesh model texture";
821 texitem.Name = name;
822 texitem.AssetType = (int)AssetType.Texture;
823 texitem.InvType = (int)InventoryType.Texture;
824 texitem.Folder = texturesFolder;
825
826 texitem.CurrentPermissions
827 = (uint)(PermissionMask.Move | PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer);
828
829 texitem.BasePermissions = (uint)PermissionMask.All;
830 texitem.EveryOnePermissions = 0;
831 texitem.NextPermissions = (uint)PermissionMask.All;
832 texitem.CreationDate = Util.UnixTimeSinceEpoch();
833
834 m_Scene.AddInventoryItem(client, texitem);
835 texitem = null;
836 }
837 }
838
839 // create and store meshs assets
840 List<UUID> meshAssets = new List<UUID>();
841 for (int i = 0; i < mesh_list.Count; i++)
842 {
843 AssetBase meshAsset = new AssetBase(UUID.Random(), assetName, (sbyte)AssetType.Mesh, creatorIDstr);
844 meshAsset.Data = mesh_list[i].AsBinary();
845 if (istest)
846 meshAsset.Local = true;
847 m_assetService.Store(meshAsset);
848 meshAssets.Add(meshAsset.FullID);
576 } 849 }
577 850
578 pbs.TextureEntry = textureEntry.GetBytes(); 851 int skipedMeshs = 0;
852 // build prims from instances
853 for (int i = 0; i < instance_list.Count; i++)
854 {
855 OSDMap inner_instance_list = (OSDMap)instance_list[i];
856
857 // skip prims that are 2 small
858 Vector3 scale = inner_instance_list["scale"].AsVector3();
859
860 if (scale.X < m_PrimScaleMin || scale.Y < m_PrimScaleMin || scale.Z < m_PrimScaleMin)
861 {
862 skipedMeshs++;
863 continue;
864 }
865
866 PrimitiveBaseShape pbs = PrimitiveBaseShape.CreateBox();
867
868 Primitive.TextureEntry textureEntry
869 = new Primitive.TextureEntry(Primitive.TextureEntry.WHITE_TEXTURE);
870
871
872 OSDArray face_list = (OSDArray)inner_instance_list["face_list"];
873 for (uint face = 0; face < face_list.Count; face++)
874 {
875 OSDMap faceMap = (OSDMap)face_list[(int)face];
876 Primitive.TextureEntryFace f = pbs.Textures.CreateFace(face);
877 if (faceMap.ContainsKey("fullbright"))
878 f.Fullbright = faceMap["fullbright"].AsBoolean();
879 if (faceMap.ContainsKey("diffuse_color"))
880 f.RGBA = faceMap["diffuse_color"].AsColor4();
881
882 int textureNum = faceMap["image"].AsInteger();
883 float imagerot = faceMap["imagerot"].AsInteger();
884 float offsets = (float)faceMap["offsets"].AsReal();
885 float offsett = (float)faceMap["offsett"].AsReal();
886 float scales = (float)faceMap["scales"].AsReal();
887 float scalet = (float)faceMap["scalet"].AsReal();
579 888
580 AssetBase meshAsset = new AssetBase(UUID.Random(), assetName, (sbyte)AssetType.Mesh, ""); 889 if (imagerot != 0)
581 meshAsset.Data = mesh_list[i].AsBinary(); 890 f.Rotation = imagerot;
582 m_assetService.Store(meshAsset);
583 891
584 pbs.SculptEntry = true; 892 if (offsets != 0)
585 pbs.SculptTexture = meshAsset.FullID; 893 f.OffsetU = offsets;
586 pbs.SculptType = (byte)SculptType.Mesh;
587 pbs.SculptData = meshAsset.Data;
588 894
589 Vector3 position = inner_instance_list["position"].AsVector3(); 895 if (offsett != 0)
590 Vector3 scale = inner_instance_list["scale"].AsVector3(); 896 f.OffsetV = offsett;
591 Quaternion rotation = inner_instance_list["rotation"].AsQuaternion(); 897
898 if (scales != 0)
899 f.RepeatU = scales;
900
901 if (scalet != 0)
902 f.RepeatV = scalet;
903
904 if (textures.Count > textureNum)
905 f.TextureID = textures[textureNum];
906 else
907 f.TextureID = Primitive.TextureEntry.WHITE_TEXTURE;
908
909 textureEntry.FaceTextures[face] = f;
910 }
911
912 pbs.TextureEntry = textureEntry.GetBytes();
913
914 bool hasmesh = false;
915 if (inner_instance_list.ContainsKey("mesh")) // seems to happen always but ...
916 {
917 int meshindx = inner_instance_list["mesh"].AsInteger();
918 if (meshAssets.Count > meshindx)
919 {
920 pbs.SculptEntry = true;
921 pbs.SculptType = (byte)SculptType.Mesh;
922 pbs.SculptTexture = meshAssets[meshindx]; // actual asset UUID after meshs suport introduction
923 // data will be requested from asset on rez (i hope)
924 hasmesh = true;
925 }
926 }
927
928 Vector3 position = inner_instance_list["position"].AsVector3();
929 Quaternion rotation = inner_instance_list["rotation"].AsQuaternion();
930
931 // for now viwers do send fixed defaults
932 // but this may change
933// int physicsShapeType = inner_instance_list["physics_shape_type"].AsInteger();
934 byte physicsShapeType = (byte)PhysShapeType.prim; // default for mesh is simple convex
935 if(hasmesh)
936 physicsShapeType = (byte) PhysShapeType.convex; // default for mesh is simple convex
937// int material = inner_instance_list["material"].AsInteger();
938 byte material = (byte)Material.Wood;
592 939
593// no longer used - begin ------------------------ 940// no longer used - begin ------------------------
594// int physicsShapeType = inner_instance_list["physics_shape_type"].AsInteger();
595// int material = inner_instance_list["material"].AsInteger();
596// int mesh = inner_instance_list["mesh"].AsInteger(); 941// int mesh = inner_instance_list["mesh"].AsInteger();
597 942
598// OSDMap permissions = (OSDMap)inner_instance_list["permissions"]; 943// OSDMap permissions = (OSDMap)inner_instance_list["permissions"];
@@ -607,24 +952,42 @@ namespace OpenSim.Region.ClientStack.Linden
607// UUID owner_id = permissions["owner_id"].AsUUID(); 952// UUID owner_id = permissions["owner_id"].AsUUID();
608// int owner_mask = permissions["owner_mask"].AsInteger(); 953// int owner_mask = permissions["owner_mask"].AsInteger();
609// no longer used - end ------------------------ 954// no longer used - end ------------------------
955
956
957 SceneObjectPart prim
958 = new SceneObjectPart(owner_id, pbs, position, Quaternion.Identity, Vector3.Zero);
959
960 prim.Scale = scale;
961 rotations.Add(rotation);
962 positions.Add(position);
963 prim.UUID = UUID.Random();
964 prim.CreatorID = creatorID;
965 prim.OwnerID = owner_id;
966 prim.GroupID = UUID.Zero;
967 prim.LastOwnerID = creatorID;
968 prim.CreationDate = Util.UnixTimeSinceEpoch();
969
970 if (grp == null)
971 prim.Name = assetName;
972 else
973 prim.Name = assetName + "#" + i.ToString();
610 974
611 UUID owner_id = m_HostCapsObj.AgentID; 975 if (restrictPerms)
976 {
977 prim.BaseMask = (uint)(PermissionMask.Move | PermissionMask.Modify);
978 prim.EveryoneMask = 0;
979 prim.GroupMask = 0;
980 prim.NextOwnerMask = 0;
981 prim.OwnerMask = (uint)(PermissionMask.Move | PermissionMask.Modify);
982 }
612 983
613 SceneObjectPart prim 984 if(istest)
614 = new SceneObjectPart(owner_id, pbs, position, Quaternion.Identity, Vector3.Zero); 985 prim.Description = "For testing only. Other uses are prohibited";
986 else
987 prim.Description = "";
615 988
616 prim.Scale = scale; 989 prim.Material = material;
617 prim.OffsetPosition = position; 990 prim.PhysicsShapeType = physicsShapeType;
618 rotations.Add(rotation);
619 positions.Add(position);
620 prim.UUID = UUID.Random();
621 prim.CreatorID = owner_id;
622 prim.OwnerID = owner_id;
623 prim.GroupID = UUID.Zero;
624 prim.LastOwnerID = prim.OwnerID;
625 prim.CreationDate = Util.UnixTimeSinceEpoch();
626 prim.Name = assetName;
627 prim.Description = "";
628 991
629// prim.BaseMask = (uint)base_mask; 992// prim.BaseMask = (uint)base_mask;
630// prim.EveryoneMask = (uint)everyone_mask; 993// prim.EveryoneMask = (uint)everyone_mask;
@@ -632,37 +995,64 @@ namespace OpenSim.Region.ClientStack.Linden
632// prim.NextOwnerMask = (uint)next_owner_mask; 995// prim.NextOwnerMask = (uint)next_owner_mask;
633// prim.OwnerMask = (uint)owner_mask; 996// prim.OwnerMask = (uint)owner_mask;
634 997
635 if (grp == null) 998 if (grp == null)
636 grp = new SceneObjectGroup(prim); 999 {
637 else 1000 grp = new SceneObjectGroup(prim);
638 grp.AddPart(prim); 1001 grp.LastOwnerID = creatorID;
639 } 1002 }
1003 else
1004 grp.AddPart(prim);
1005 }
640 1006
641 // Fix first link number 1007 Vector3 rootPos = positions[0];
642 if (grp.Parts.Length > 1)
643 grp.RootPart.LinkNum++;
644 1008
645 Vector3 rootPos = positions[0]; 1009 if (grp.Parts.Length > 1)
646 grp.AbsolutePosition = rootPos; 1010 {
647 for (int i = 0; i < positions.Count; i++) 1011 // Fix first link number
648 { 1012 grp.RootPart.LinkNum++;
649 Vector3 offset = positions[i] - rootPos; 1013
650 grp.Parts[i].OffsetPosition = offset; 1014 Quaternion rootRotConj = Quaternion.Conjugate(rotations[0]);
1015 Quaternion tmprot;
1016 Vector3 offset;
1017
1018 // fix children rotations and positions
1019 for (int i = 1; i < rotations.Count; i++)
1020 {
1021 tmprot = rotations[i];
1022 tmprot = rootRotConj * tmprot;
1023
1024 grp.Parts[i].RotationOffset = tmprot;
1025
1026 offset = positions[i] - rootPos;
1027
1028 offset *= rootRotConj;
1029 grp.Parts[i].OffsetPosition = offset;
1030 }
1031
1032 grp.AbsolutePosition = rootPos;
1033 grp.UpdateGroupRotationR(rotations[0]);
1034 }
1035 else
1036 {
1037 grp.AbsolutePosition = rootPos;
1038 grp.UpdateGroupRotationR(rotations[0]);
1039 }
1040
1041 data = ASCIIEncoding.ASCII.GetBytes(SceneObjectSerializer.ToOriginalXmlFormat(grp));
651 } 1042 }
652 1043
653 for (int i = 0; i < rotations.Count; i++) 1044 else // not a mesh model
654 { 1045 {
655 if (i != 0) 1046 m_log.ErrorFormat("[CAPS Asset Upload] got unsuported assetType for object upload");
656 grp.Parts[i].RotationOffset = rotations[i]; 1047 return;
657 } 1048 }
658
659 grp.UpdateGroupRotationR(rotations[0]);
660 data = ASCIIEncoding.ASCII.GetBytes(SceneObjectSerializer.ToOriginalXmlFormat(grp));
661 } 1049 }
662 1050
663 AssetBase asset; 1051 AssetBase asset;
664 asset = new AssetBase(assetID, assetName, assType, m_HostCapsObj.AgentID.ToString()); 1052 asset = new AssetBase(assetID, assetName, assType, creatorIDstr);
665 asset.Data = data; 1053 asset.Data = data;
1054 if (istest)
1055 asset.Local = true;
666 if (AddNewAsset != null) 1056 if (AddNewAsset != null)
667 AddNewAsset(asset); 1057 AddNewAsset(asset);
668 else if (m_assetService != null) 1058 else if (m_assetService != null)
@@ -670,11 +1060,17 @@ namespace OpenSim.Region.ClientStack.Linden
670 1060
671 InventoryItemBase item = new InventoryItemBase(); 1061 InventoryItemBase item = new InventoryItemBase();
672 item.Owner = m_HostCapsObj.AgentID; 1062 item.Owner = m_HostCapsObj.AgentID;
673 item.CreatorId = m_HostCapsObj.AgentID.ToString(); 1063 item.CreatorId = creatorIDstr;
674 item.CreatorData = String.Empty; 1064 item.CreatorData = String.Empty;
675 item.ID = inventoryItem; 1065 item.ID = inventoryItem;
676 item.AssetID = asset.FullID; 1066 item.AssetID = asset.FullID;
677 item.Description = assetDescription; 1067 if (istest)
1068 {
1069 item.Description = "For testing only. Other uses are prohibited";
1070 item.Flags = (uint) (InventoryItemFlags.SharedSingleReference);
1071 }
1072 else
1073 item.Description = assetDescription;
678 item.Name = assetName; 1074 item.Name = assetName;
679 item.AssetType = assType; 1075 item.AssetType = assType;
680 item.InvType = inType; 1076 item.InvType = inType;
@@ -682,18 +1078,60 @@ namespace OpenSim.Region.ClientStack.Linden
682 1078
683 // If we set PermissionMask.All then when we rez the item the next permissions will replace the current 1079 // If we set PermissionMask.All then when we rez the item the next permissions will replace the current
684 // (owner) permissions. This becomes a problem if next permissions are changed. 1080 // (owner) permissions. This becomes a problem if next permissions are changed.
685 item.CurrentPermissions
686 = (uint)(PermissionMask.Move | PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer);
687 1081
688 item.BasePermissions = (uint)PermissionMask.All; 1082 if (restrictPerms)
689 item.EveryOnePermissions = 0; 1083 {
690 item.NextPermissions = (uint)PermissionMask.All; 1084 item.CurrentPermissions
1085 = (uint)(PermissionMask.Move | PermissionMask.Modify);
1086
1087 item.BasePermissions = (uint)(PermissionMask.Move | PermissionMask.Modify);
1088 item.EveryOnePermissions = 0;
1089 item.NextPermissions = 0;
1090 }
1091 else
1092 {
1093 item.CurrentPermissions
1094 = (uint)(PermissionMask.Move | PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer);
1095
1096 item.BasePermissions = (uint)PermissionMask.All;
1097 item.EveryOnePermissions = 0;
1098 item.NextPermissions = (uint)PermissionMask.All;
1099 }
1100
691 item.CreationDate = Util.UnixTimeSinceEpoch(); 1101 item.CreationDate = Util.UnixTimeSinceEpoch();
692 1102
1103 m_Scene.TryGetClient(m_HostCapsObj.AgentID, out client);
1104
693 if (AddNewInventoryItem != null) 1105 if (AddNewInventoryItem != null)
694 { 1106 {
695 AddNewInventoryItem(m_HostCapsObj.AgentID, item); 1107 if (istest)
1108 {
1109 m_Scene.AddInventoryItem(client, item);
1110/*
1111 AddNewInventoryItem(m_HostCapsObj.AgentID, item, 0);
1112 if (client != null)
1113 client.SendAgentAlertMessage("Upload will have no cost, for personal test purposes only. Other uses are forbiden. Items may not work on a another region" , true);
1114 */
1115 }
1116 else
1117 {
1118 AddNewInventoryItem(m_HostCapsObj.AgentID, item, (uint)cost);
1119 if (client != null)
1120 {
1121 // let users see anything.. i don't so far
1122 string str;
1123 if (cost > 0)
1124 // dont remember where is money unit name to put here
1125 str = "Upload complete. charged " + cost.ToString() + "$";
1126 else
1127 str = "Upload complete";
1128 client.SendAgentAlertMessage(str, true);
1129 }
1130 }
696 } 1131 }
1132
1133 lock (m_ModelCost)
1134 m_FileAgentInventoryState = FileAgentInventoryState.idle;
697 } 1135 }
698 1136
699 /// <summary> 1137 /// <summary>
@@ -854,10 +1292,159 @@ namespace OpenSim.Region.ClientStack.Linden
854 response["int_response_code"] = 200; 1292 response["int_response_code"] = 200;
855 return LLSDHelpers.SerialiseLLSDReply(response); 1293 return LLSDHelpers.SerialiseLLSDReply(response);
856 } 1294 }
1295
1296 public string GetObjectPhysicsData(string request, string path,
1297 string param, IOSHttpRequest httpRequest,
1298 IOSHttpResponse httpResponse)
1299 {
1300 OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request);
1301 OSDMap resp = new OSDMap();
1302 OSDArray object_ids = (OSDArray)req["object_ids"];
1303
1304 for (int i = 0 ; i < object_ids.Count ; i++)
1305 {
1306 UUID uuid = object_ids[i].AsUUID();
1307
1308 SceneObjectPart obj = m_Scene.GetSceneObjectPart(uuid);
1309 if (obj != null)
1310 {
1311 OSDMap object_data = new OSDMap();
1312
1313 object_data["PhysicsShapeType"] = obj.PhysicsShapeType;
1314 object_data["Density"] = obj.Density;
1315 object_data["Friction"] = obj.Friction;
1316 object_data["Restitution"] = obj.Bounciness;
1317 object_data["GravityMultiplier"] = obj.GravityModifier;
1318
1319 resp[uuid.ToString()] = object_data;
1320 }
1321 }
1322
1323 string response = OSDParser.SerializeLLSDXmlString(resp);
1324 return response;
1325 }
1326
1327 public string GetObjectCost(string request, string path,
1328 string param, IOSHttpRequest httpRequest,
1329 IOSHttpResponse httpResponse)
1330 {
1331 OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request);
1332 OSDMap resp = new OSDMap();
1333
1334 OSDArray object_ids = (OSDArray)req["object_ids"];
1335
1336 for (int i = 0; i < object_ids.Count; i++)
1337 {
1338 UUID uuid = object_ids[i].AsUUID();
1339
1340 SceneObjectPart part = m_Scene.GetSceneObjectPart(uuid);
1341
1342 if (part != null)
1343 {
1344 SceneObjectGroup grp = part.ParentGroup;
1345 if (grp != null)
1346 {
1347 float linksetCost;
1348 float linksetPhysCost;
1349 float partCost;
1350 float partPhysCost;
1351
1352 grp.GetResourcesCosts(part, out linksetCost, out linksetPhysCost, out partCost, out partPhysCost);
1353
1354 OSDMap object_data = new OSDMap();
1355 object_data["linked_set_resource_cost"] = linksetCost;
1356 object_data["resource_cost"] = partCost;
1357 object_data["physics_cost"] = partPhysCost;
1358 object_data["linked_set_physics_cost"] = linksetPhysCost;
1359
1360 resp[uuid.ToString()] = object_data;
1361 }
1362 }
1363 }
1364
1365 string response = OSDParser.SerializeLLSDXmlString(resp);
1366 return response;
1367 }
1368
1369 public string ResourceCostSelected(string request, string path,
1370 string param, IOSHttpRequest httpRequest,
1371 IOSHttpResponse httpResponse)
1372 {
1373 OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request);
1374 OSDMap resp = new OSDMap();
1375
1376
1377 float phys=0;
1378 float stream=0;
1379 float simul=0;
1380
1381 if (req.ContainsKey("selected_roots"))
1382 {
1383 OSDArray object_ids = (OSDArray)req["selected_roots"];
1384
1385 // should go by SOG suming costs for all parts
1386 // ll v3 works ok with several objects select we get the list and adds ok
1387 // FS calls per object so results are wrong guess fs bug
1388 for (int i = 0; i < object_ids.Count; i++)
1389 {
1390 UUID uuid = object_ids[i].AsUUID();
1391 float Physc;
1392 float simulc;
1393 float streamc;
1394
1395 SceneObjectGroup grp = m_Scene.GetGroupByPrim(uuid);
1396 if (grp != null)
1397 {
1398 grp.GetSelectedCosts(out Physc, out streamc, out simulc);
1399 phys += Physc;
1400 stream += streamc;
1401 simul += simulc;
1402 }
1403 }
1404 }
1405 else if (req.ContainsKey("selected_prims"))
1406 {
1407 OSDArray object_ids = (OSDArray)req["selected_prims"];
1408
1409 // don't see in use in any of the 2 viewers
1410 // guess it should be for edit linked but... nothing
1411 // should go to SOP per part
1412 for (int i = 0; i < object_ids.Count; i++)
1413 {
1414 UUID uuid = object_ids[i].AsUUID();
1415
1416 SceneObjectPart part = m_Scene.GetSceneObjectPart(uuid);
1417 if (part != null)
1418 {
1419 phys += part.PhysicsCost;
1420 stream += part.StreamingCost;
1421 simul += part.SimulationCost;
1422 }
1423 }
1424 }
1425
1426 if (simul != 0)
1427 {
1428 OSDMap object_data = new OSDMap();
1429
1430 object_data["physics"] = phys;
1431 object_data["streaming"] = stream;
1432 object_data["simulation"] = simul;
1433
1434 resp["selected"] = object_data;
1435 }
1436
1437 string response = OSDParser.SerializeLLSDXmlString(resp);
1438 return response;
1439 }
857 } 1440 }
858 1441
859 public class AssetUploader 1442 public class AssetUploader
860 { 1443 {
1444 private static readonly ILog m_log =
1445 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
1446
1447
861 public event UpLoadedAsset OnUpLoad; 1448 public event UpLoadedAsset OnUpLoad;
862 private UpLoadedAsset handlerUpLoad = null; 1449 private UpLoadedAsset handlerUpLoad = null;
863 1450
@@ -872,10 +1459,21 @@ namespace OpenSim.Region.ClientStack.Linden
872 1459
873 private string m_invType = String.Empty; 1460 private string m_invType = String.Empty;
874 private string m_assetType = String.Empty; 1461 private string m_assetType = String.Empty;
1462 private int m_cost;
1463 private string m_error = String.Empty;
1464
1465 private Timer m_timeoutTimer = new Timer();
1466 private UUID m_texturesFolder;
1467 private int m_nreqtextures;
1468 private int m_nreqmeshs;
1469 private int m_nreqinstances;
1470 private bool m_IsAtestUpload;
875 1471
876 public AssetUploader(string assetName, string description, UUID assetID, UUID inventoryItem, 1472 public AssetUploader(string assetName, string description, UUID assetID, UUID inventoryItem,
877 UUID parentFolderID, string invType, string assetType, string path, 1473 UUID parentFolderID, string invType, string assetType, string path,
878 IHttpServer httpServer, bool dumpAssetsToFile) 1474 IHttpServer httpServer, bool dumpAssetsToFile,
1475 int totalCost, UUID texturesFolder, int nreqtextures, int nreqmeshs, int nreqinstances,
1476 bool IsAtestUpload)
879 { 1477 {
880 m_assetName = assetName; 1478 m_assetName = assetName;
881 m_assetDes = description; 1479 m_assetDes = description;
@@ -887,6 +1485,18 @@ namespace OpenSim.Region.ClientStack.Linden
887 m_assetType = assetType; 1485 m_assetType = assetType;
888 m_invType = invType; 1486 m_invType = invType;
889 m_dumpAssetsToFile = dumpAssetsToFile; 1487 m_dumpAssetsToFile = dumpAssetsToFile;
1488 m_cost = totalCost;
1489
1490 m_texturesFolder = texturesFolder;
1491 m_nreqtextures = nreqtextures;
1492 m_nreqmeshs = nreqmeshs;
1493 m_nreqinstances = nreqinstances;
1494 m_IsAtestUpload = IsAtestUpload;
1495
1496 m_timeoutTimer.Elapsed += TimedOut;
1497 m_timeoutTimer.Interval = 120000;
1498 m_timeoutTimer.AutoReset = false;
1499 m_timeoutTimer.Start();
890 } 1500 }
891 1501
892 /// <summary> 1502 /// <summary>
@@ -901,12 +1511,14 @@ namespace OpenSim.Region.ClientStack.Linden
901 UUID inv = inventoryItemID; 1511 UUID inv = inventoryItemID;
902 string res = String.Empty; 1512 string res = String.Empty;
903 LLSDAssetUploadComplete uploadComplete = new LLSDAssetUploadComplete(); 1513 LLSDAssetUploadComplete uploadComplete = new LLSDAssetUploadComplete();
1514/*
904 uploadComplete.new_asset = newAssetID.ToString(); 1515 uploadComplete.new_asset = newAssetID.ToString();
905 uploadComplete.new_inventory_item = inv; 1516 uploadComplete.new_inventory_item = inv;
906 uploadComplete.state = "complete"; 1517 uploadComplete.state = "complete";
907 1518
908 res = LLSDHelpers.SerialiseLLSDReply(uploadComplete); 1519 res = LLSDHelpers.SerialiseLLSDReply(uploadComplete);
909 1520*/
1521 m_timeoutTimer.Stop();
910 httpListener.RemoveStreamHandler("POST", uploaderPath); 1522 httpListener.RemoveStreamHandler("POST", uploaderPath);
911 1523
912 // TODO: probably make this a better set of extensions here 1524 // TODO: probably make this a better set of extensions here
@@ -923,12 +1535,49 @@ namespace OpenSim.Region.ClientStack.Linden
923 handlerUpLoad = OnUpLoad; 1535 handlerUpLoad = OnUpLoad;
924 if (handlerUpLoad != null) 1536 if (handlerUpLoad != null)
925 { 1537 {
926 handlerUpLoad(m_assetName, m_assetDes, newAssetID, inv, parentFolder, data, m_invType, m_assetType); 1538 handlerUpLoad(m_assetName, m_assetDes, newAssetID, inv, parentFolder, data, m_invType, m_assetType,
1539 m_cost, m_texturesFolder, m_nreqtextures, m_nreqmeshs, m_nreqinstances, m_IsAtestUpload, ref m_error);
1540 }
1541 if (m_IsAtestUpload)
1542 {
1543 LLSDAssetUploadError resperror = new LLSDAssetUploadError();
1544 resperror.message = "Upload SUCESSEFULL for testing purposes only. Other uses are prohibited. Item will not work after 48 hours or on other regions";
1545 resperror.identifier = inv;
1546
1547 uploadComplete.error = resperror;
1548 uploadComplete.state = "Upload4Testing";
927 } 1549 }
1550 else
1551 {
1552 if (m_error == String.Empty)
1553 {
1554 uploadComplete.new_asset = newAssetID.ToString();
1555 uploadComplete.new_inventory_item = inv;
1556 // if (m_texturesFolder != UUID.Zero)
1557 // uploadComplete.new_texture_folder_id = m_texturesFolder;
1558 uploadComplete.state = "complete";
1559 }
1560 else
1561 {
1562 LLSDAssetUploadError resperror = new LLSDAssetUploadError();
1563 resperror.message = m_error;
1564 resperror.identifier = inv;
928 1565
1566 uploadComplete.error = resperror;
1567 uploadComplete.state = "failed";
1568 }
1569 }
1570
1571 res = LLSDHelpers.SerialiseLLSDReply(uploadComplete);
929 return res; 1572 return res;
930 } 1573 }
931 1574
1575 private void TimedOut(object sender, ElapsedEventArgs args)
1576 {
1577 m_log.InfoFormat("[CAPS]: Removing URL and handler for timed out mesh upload");
1578 httpListener.RemoveStreamHandler("POST", uploaderPath);
1579 }
1580
932 ///Left this in and commented in case there are unforseen issues 1581 ///Left this in and commented in case there are unforseen issues
933 //private void SaveAssetToFile(string filename, byte[] data) 1582 //private void SaveAssetToFile(string filename, byte[] data)
934 //{ 1583 //{
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs
new file mode 100644
index 0000000..4a3fae6
--- /dev/null
+++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs
@@ -0,0 +1,671 @@
1// Proprietary code of Avination Virtual Limited
2// (c) 2012 Melanie Thielker, Leal Duarte
3//
4
5using System;
6using System.IO;
7using System.Collections;
8using System.Collections.Generic;
9using System.Text;
10
11using OpenMetaverse;
12using OpenMetaverse.StructuredData;
13
14using OpenSim.Framework;
15using OpenSim.Region.Framework;
16using OpenSim.Region.Framework.Scenes;
17using OpenSim.Framework.Capabilities;
18
19using ComponentAce.Compression.Libs.zlib;
20
21using OSDArray = OpenMetaverse.StructuredData.OSDArray;
22using OSDMap = OpenMetaverse.StructuredData.OSDMap;
23
24namespace OpenSim.Region.ClientStack.Linden
25{
26 public struct ModelPrimLimits
27 {
28
29 }
30
31 public class ModelCost
32 {
33
34 // upload fee defaults
35 // fees are normalized to 1.0
36 // this parameters scale them to basic cost ( so 1.0 translates to 10 )
37
38 public float ModelMeshCostFactor = 0.0f; // scale total cost relative to basic (excluding textures)
39 public float ModelTextureCostFactor = 1.0f; // scale textures fee to basic.
40 public float ModelMinCostFactor = 0.0f; // 0.5f; // minimum total model free excluding textures
41
42 // itens costs in normalized values
43 // ie will be multiplied by basicCost and factors above
44 public float primCreationCost = 0.002f; // extra cost for each prim creation overhead
45 // weigthed size to normalized cost
46 public float bytecost = 1e-5f;
47
48 // mesh upload fees based on compressed data sizes
49 // several data sections are counted more that once
50 // to promote user optimization
51 // following parameters control how many extra times they are added
52 // to global size.
53 // LOD meshs
54 const float medSizeWth = 1f; // 2x
55 const float lowSizeWth = 1.5f; // 2.5x
56 const float lowestSizeWth = 2f; // 3x
57 // favor potencially physical optimized meshs versus automatic decomposition
58 const float physMeshSizeWth = 6f; // counts 7x
59 const float physHullSizeWth = 8f; // counts 9x
60
61 // stream cost area factors
62 // more or less like SL
63 const float highLodFactor = 17.36f;
64 const float midLodFactor = 277.78f;
65 const float lowLodFactor = 1111.11f;
66
67 // physics cost is below, identical to SL, assuming shape type convex
68 // server cost is below identical to SL assuming non scripted non physical object
69
70 // internal
71 const int bytesPerCoord = 6; // 3 coords, 2 bytes per each
72
73 // control prims dimensions
74 public float PrimScaleMin = 0.001f;
75 public float NonPhysicalPrimScaleMax = 256f;
76 public float PhysicalPrimScaleMax = 10f;
77 public int ObjectLinkedPartsMax = 512;
78
79 // storage for a single mesh asset cost parameters
80 private class ameshCostParam
81 {
82 // LOD sizes for size dependent streaming cost
83 public int highLODSize;
84 public int medLODSize;
85 public int lowLODSize;
86 public int lowestLODSize;
87 // normalized fee based on compressed data sizes
88 public float costFee;
89 // physics cost
90 public float physicsCost;
91 }
92
93 // calculates a mesh model costs
94 // returns false on error, with a reason on parameter error
95 // resources input LLSD request
96 // basicCost input region assets upload cost
97 // totalcost returns model total upload fee
98 // meshcostdata returns detailed costs for viewer
99 public bool MeshModelCost(LLSDAssetResource resources, int basicCost, out int totalcost,
100 LLSDAssetUploadResponseData meshcostdata, out string error, ref string warning)
101 {
102 totalcost = 0;
103 error = string.Empty;
104
105 if (resources == null ||
106 resources.instance_list == null ||
107 resources.instance_list.Array.Count == 0)
108 {
109 error = "missing model information.";
110 return false;
111 }
112
113 int numberInstances = resources.instance_list.Array.Count;
114
115 if( numberInstances > ObjectLinkedPartsMax )
116 {
117 error = "Model whould have more than " + ObjectLinkedPartsMax.ToString() + " linked prims";
118 return false;
119 }
120
121 meshcostdata.model_streaming_cost = 0.0;
122 meshcostdata.simulation_cost = 0.0;
123 meshcostdata.physics_cost = 0.0;
124 meshcostdata.resource_cost = 0.0;
125
126 meshcostdata.upload_price_breakdown.mesh_instance = 0;
127 meshcostdata.upload_price_breakdown.mesh_physics = 0;
128 meshcostdata.upload_price_breakdown.mesh_streaming = 0;
129 meshcostdata.upload_price_breakdown.model = 0;
130
131 int itmp;
132
133 // textures cost
134 if (resources.texture_list != null && resources.texture_list.Array.Count > 0)
135 {
136 float textures_cost = (float)(resources.texture_list.Array.Count * basicCost);
137 textures_cost *= ModelTextureCostFactor;
138
139 itmp = (int)(textures_cost + 0.5f); // round
140 meshcostdata.upload_price_breakdown.texture = itmp;
141 totalcost += itmp;
142 }
143
144 // meshs assets cost
145 float meshsfee = 0;
146 int numberMeshs = 0;
147 bool haveMeshs = false;
148 List<ameshCostParam> meshsCosts = new List<ameshCostParam>();
149
150 if (resources.mesh_list != null && resources.mesh_list.Array.Count > 0)
151 {
152 numberMeshs = resources.mesh_list.Array.Count;
153
154 for (int i = 0; i < numberMeshs; i++)
155 {
156 ameshCostParam curCost = new ameshCostParam();
157 byte[] data = (byte[])resources.mesh_list.Array[i];
158
159 if (!MeshCost(data, curCost, out error))
160 {
161 return false;
162 }
163 meshsCosts.Add(curCost);
164 meshsfee += curCost.costFee;
165 }
166 haveMeshs = true;
167 }
168
169 // instances (prims) cost
170
171
172 int mesh;
173 int skipedSmall = 0;
174 for (int i = 0; i < numberInstances; i++)
175 {
176 Hashtable inst = (Hashtable)resources.instance_list.Array[i];
177
178 ArrayList ascale = (ArrayList)inst["scale"];
179 Vector3 scale;
180 double tmp;
181 tmp = (double)ascale[0];
182 scale.X = (float)tmp;
183 tmp = (double)ascale[1];
184 scale.Y = (float)tmp;
185 tmp = (double)ascale[2];
186 scale.Z = (float)tmp;
187
188 if (scale.X < PrimScaleMin || scale.Y < PrimScaleMin || scale.Z < PrimScaleMin)
189 {
190 skipedSmall++;
191 continue;
192 }
193
194 if (scale.X > NonPhysicalPrimScaleMax || scale.Y > NonPhysicalPrimScaleMax || scale.Z > NonPhysicalPrimScaleMax)
195 {
196 error = "Model contains parts with sides larger than " + NonPhysicalPrimScaleMax.ToString() + "m. Please ajust scale";
197 return false;
198 }
199
200 if (haveMeshs && inst.ContainsKey("mesh"))
201 {
202 mesh = (int)inst["mesh"];
203
204 if (mesh >= numberMeshs)
205 {
206 error = "Incoerent model information.";
207 return false;
208 }
209
210 // streamming cost
211
212 float sqdiam = scale.LengthSquared();
213
214 ameshCostParam curCost = meshsCosts[mesh];
215 float mesh_streaming = streamingCost(curCost, sqdiam);
216
217 meshcostdata.model_streaming_cost += mesh_streaming;
218 meshcostdata.physics_cost += curCost.physicsCost;
219 }
220 else // instance as no mesh ??
221 {
222 // to do later if needed
223 meshcostdata.model_streaming_cost += 0.5f;
224 meshcostdata.physics_cost += 1.0f;
225 }
226
227 // assume unscripted and static prim server cost
228 meshcostdata.simulation_cost += 0.5f;
229 // charge for prims creation
230 meshsfee += primCreationCost;
231 }
232
233 if (skipedSmall > 0)
234 {
235 if (skipedSmall > numberInstances / 2)
236 {
237 error = "Model contains too many prims smaller than " + PrimScaleMin.ToString() +
238 "m minimum allowed size. Please check scalling";
239 return false;
240 }
241 else
242 warning += skipedSmall.ToString() + " of the requested " +numberInstances.ToString() +
243 " model prims will not upload because they are smaller than " + PrimScaleMin.ToString() +
244 "m minimum allowed size. Please check scalling ";
245 }
246
247 if (meshcostdata.physics_cost <= meshcostdata.model_streaming_cost)
248 meshcostdata.resource_cost = meshcostdata.model_streaming_cost;
249 else
250 meshcostdata.resource_cost = meshcostdata.physics_cost;
251
252 if (meshcostdata.resource_cost < meshcostdata.simulation_cost)
253 meshcostdata.resource_cost = meshcostdata.simulation_cost;
254
255 // scale cost
256 // at this point a cost of 1.0 whould mean basic cost
257 meshsfee *= ModelMeshCostFactor;
258
259 if (meshsfee < ModelMinCostFactor)
260 meshsfee = ModelMinCostFactor;
261
262 // actually scale it to basic cost
263 meshsfee *= (float)basicCost;
264
265 meshsfee += 0.5f; // rounding
266
267 totalcost += (int)meshsfee;
268
269 // breakdown prices
270 // don't seem to be in use so removed code for now
271
272 return true;
273 }
274
275 // single mesh asset cost
276 private bool MeshCost(byte[] data, ameshCostParam cost, out string error)
277 {
278 cost.highLODSize = 0;
279 cost.medLODSize = 0;
280 cost.lowLODSize = 0;
281 cost.lowestLODSize = 0;
282 cost.physicsCost = 0.0f;
283 cost.costFee = 0.0f;
284
285 error = string.Empty;
286
287 if (data == null || data.Length == 0)
288 {
289 error = "Missing model information.";
290 return false;
291 }
292
293 OSD meshOsd = null;
294 int start = 0;
295
296 error = "Invalid model data";
297
298 using (MemoryStream ms = new MemoryStream(data))
299 {
300 try
301 {
302 OSD osd = OSDParser.DeserializeLLSDBinary(ms);
303 if (osd is OSDMap)
304 meshOsd = (OSDMap)osd;
305 else
306 return false;
307 }
308 catch (Exception e)
309 {
310 return false;
311 }
312 start = (int)ms.Position;
313 }
314
315 OSDMap map = (OSDMap)meshOsd;
316 OSDMap tmpmap;
317
318 int highlod_size = 0;
319 int medlod_size = 0;
320 int lowlod_size = 0;
321 int lowestlod_size = 0;
322 int skin_size = 0;
323
324 int hulls_size = 0;
325 int phys_nhulls;
326 int phys_hullsvertices = 0;
327
328 int physmesh_size = 0;
329 int phys_ntriangles = 0;
330
331 int submesh_offset = -1;
332
333 if (map.ContainsKey("physics_convex"))
334 {
335 tmpmap = (OSDMap)map["physics_convex"];
336 if (tmpmap.ContainsKey("offset"))
337 submesh_offset = tmpmap["offset"].AsInteger() + start;
338 if (tmpmap.ContainsKey("size"))
339 hulls_size = tmpmap["size"].AsInteger();
340 }
341
342 if (submesh_offset < 0 || hulls_size == 0)
343 {
344 error = "Missing physics_convex block";
345 return false;
346 }
347
348 if (!hulls(data, submesh_offset, hulls_size, out phys_hullsvertices, out phys_nhulls))
349 {
350 error = "Bad physics_convex block";
351 return false;
352 }
353
354 submesh_offset = -1;
355
356 // only look for LOD meshs sizes
357
358 if (map.ContainsKey("high_lod"))
359 {
360 tmpmap = (OSDMap)map["high_lod"];
361 // see at least if there is a offset for this one
362 if (tmpmap.ContainsKey("offset"))
363 submesh_offset = tmpmap["offset"].AsInteger() + start;
364 if (tmpmap.ContainsKey("size"))
365 highlod_size = tmpmap["size"].AsInteger();
366 }
367
368 if (submesh_offset < 0 || highlod_size <= 0)
369 {
370 error = "Missing high_lod block";
371 return false;
372 }
373
374 bool haveprev = true;
375
376 if (map.ContainsKey("medium_lod"))
377 {
378 tmpmap = (OSDMap)map["medium_lod"];
379 if (tmpmap.ContainsKey("size"))
380 medlod_size = tmpmap["size"].AsInteger();
381 else
382 haveprev = false;
383 }
384
385 if (haveprev && map.ContainsKey("low_lod"))
386 {
387 tmpmap = (OSDMap)map["low_lod"];
388 if (tmpmap.ContainsKey("size"))
389 lowlod_size = tmpmap["size"].AsInteger();
390 else
391 haveprev = false;
392 }
393
394 if (haveprev && map.ContainsKey("lowest_lod"))
395 {
396 tmpmap = (OSDMap)map["lowest_lod"];
397 if (tmpmap.ContainsKey("size"))
398 lowestlod_size = tmpmap["size"].AsInteger();
399 }
400
401 if (map.ContainsKey("skin"))
402 {
403 tmpmap = (OSDMap)map["skin"];
404 if (tmpmap.ContainsKey("size"))
405 skin_size = tmpmap["size"].AsInteger();
406 }
407
408 cost.highLODSize = highlod_size;
409 cost.medLODSize = medlod_size;
410 cost.lowLODSize = lowlod_size;
411 cost.lowestLODSize = lowestlod_size;
412
413 submesh_offset = -1;
414
415 tmpmap = null;
416 if(map.ContainsKey("physics_mesh"))
417 tmpmap = (OSDMap)map["physics_mesh"];
418 else if (map.ContainsKey("physics_shape")) // old naming
419 tmpmap = (OSDMap)map["physics_shape"];
420
421 if(tmpmap != null)
422 {
423 if (tmpmap.ContainsKey("offset"))
424 submesh_offset = tmpmap["offset"].AsInteger() + start;
425 if (tmpmap.ContainsKey("size"))
426 physmesh_size = tmpmap["size"].AsInteger();
427
428 if (submesh_offset >= 0 || physmesh_size > 0)
429 {
430
431 if (!submesh(data, submesh_offset, physmesh_size, out phys_ntriangles))
432 {
433 error = "Model data parsing error";
434 return false;
435 }
436 }
437 }
438
439 // upload is done in convex shape type so only one hull
440 phys_hullsvertices++;
441 cost.physicsCost = 0.04f * phys_hullsvertices;
442
443 float sfee;
444
445 sfee = data.Length; // start with total compressed data size
446
447 // penalize lod meshs that should be more builder optimized
448 sfee += medSizeWth * medlod_size;
449 sfee += lowSizeWth * lowlod_size;
450 sfee += lowestSizeWth * lowlod_size;
451
452 // physics
453 // favor potencial optimized meshs versus automatic decomposition
454 if (physmesh_size != 0)
455 sfee += physMeshSizeWth * (physmesh_size + hulls_size / 4); // reduce cost of mandatory convex hull
456 else
457 sfee += physHullSizeWth * hulls_size;
458
459 // bytes to money
460 sfee *= bytecost;
461
462 cost.costFee = sfee;
463 return true;
464 }
465
466 // parses a LOD or physics mesh component
467 private bool submesh(byte[] data, int offset, int size, out int ntriangles)
468 {
469 ntriangles = 0;
470
471 OSD decodedMeshOsd = new OSD();
472 byte[] meshBytes = new byte[size];
473 System.Buffer.BlockCopy(data, offset, meshBytes, 0, size);
474 try
475 {
476 using (MemoryStream inMs = new MemoryStream(meshBytes))
477 {
478 using (MemoryStream outMs = new MemoryStream())
479 {
480 using (ZOutputStream zOut = new ZOutputStream(outMs))
481 {
482 byte[] readBuffer = new byte[4096];
483 int readLen = 0;
484 while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0)
485 {
486 zOut.Write(readBuffer, 0, readLen);
487 }
488 zOut.Flush();
489 outMs.Seek(0, SeekOrigin.Begin);
490
491 byte[] decompressedBuf = outMs.GetBuffer();
492 decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
493 }
494 }
495 }
496 }
497 catch (Exception e)
498 {
499 return false;
500 }
501
502 OSDArray decodedMeshOsdArray = null;
503 if ((!decodedMeshOsd is OSDArray))
504 return false;
505
506 byte[] dummy;
507
508 decodedMeshOsdArray = (OSDArray)decodedMeshOsd;
509 foreach (OSD subMeshOsd in decodedMeshOsdArray)
510 {
511 if (subMeshOsd is OSDMap)
512 {
513 OSDMap subtmpmap = (OSDMap)subMeshOsd;
514 if (subtmpmap.ContainsKey("NoGeometry") && ((OSDBoolean)subtmpmap["NoGeometry"]))
515 continue;
516
517 if (!subtmpmap.ContainsKey("Position"))
518 return false;
519
520 if (subtmpmap.ContainsKey("TriangleList"))
521 {
522 dummy = subtmpmap["TriangleList"].AsBinary();
523 ntriangles += dummy.Length / bytesPerCoord;
524 }
525 else
526 return false;
527 }
528 }
529
530 return true;
531 }
532
533 // parses convex hulls component
534 private bool hulls(byte[] data, int offset, int size, out int nvertices, out int nhulls)
535 {
536 nvertices = 0;
537 nhulls = 1;
538
539 OSD decodedMeshOsd = new OSD();
540 byte[] meshBytes = new byte[size];
541 System.Buffer.BlockCopy(data, offset, meshBytes, 0, size);
542 try
543 {
544 using (MemoryStream inMs = new MemoryStream(meshBytes))
545 {
546 using (MemoryStream outMs = new MemoryStream())
547 {
548 using (ZOutputStream zOut = new ZOutputStream(outMs))
549 {
550 byte[] readBuffer = new byte[4096];
551 int readLen = 0;
552 while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0)
553 {
554 zOut.Write(readBuffer, 0, readLen);
555 }
556 zOut.Flush();
557 outMs.Seek(0, SeekOrigin.Begin);
558
559 byte[] decompressedBuf = outMs.GetBuffer();
560 decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
561 }
562 }
563 }
564 }
565 catch (Exception e)
566 {
567 return false;
568 }
569
570 OSDMap cmap = (OSDMap)decodedMeshOsd;
571 if (cmap == null)
572 return false;
573
574 byte[] dummy;
575
576 // must have one of this
577 if (cmap.ContainsKey("BoundingVerts"))
578 {
579 dummy = cmap["BoundingVerts"].AsBinary();
580 nvertices = dummy.Length / bytesPerCoord;
581 }
582 else
583 return false;
584
585/* upload is done with convex shape type
586 if (cmap.ContainsKey("HullList"))
587 {
588 dummy = cmap["HullList"].AsBinary();
589 nhulls += dummy.Length;
590 }
591
592
593 if (cmap.ContainsKey("Positions"))
594 {
595 dummy = cmap["Positions"].AsBinary();
596 nvertices = dummy.Length / bytesPerCoord;
597 }
598 */
599
600 return true;
601 }
602
603 // returns streaming cost from on mesh LODs sizes in curCost and square of prim size length
604 private float streamingCost(ameshCostParam curCost, float sqdiam)
605 {
606 // compute efective areas
607 float ma = 262144f;
608
609 float mh = sqdiam * highLodFactor;
610 if (mh > ma)
611 mh = ma;
612 float mm = sqdiam * midLodFactor;
613 if (mm > ma)
614 mm = ma;
615
616 float ml = sqdiam * lowLodFactor;
617 if (ml > ma)
618 ml = ma;
619
620 float mlst = ma;
621
622 mlst -= ml;
623 ml -= mm;
624 mm -= mh;
625
626 if (mlst < 1.0f)
627 mlst = 1.0f;
628 if (ml < 1.0f)
629 ml = 1.0f;
630 if (mm < 1.0f)
631 mm = 1.0f;
632 if (mh < 1.0f)
633 mh = 1.0f;
634
635 ma = mlst + ml + mm + mh;
636
637 // get LODs compressed sizes
638 // giving 384 bytes bonus
639 int lst = curCost.lowestLODSize - 384;
640 int l = curCost.lowLODSize - 384;
641 int m = curCost.medLODSize - 384;
642 int h = curCost.highLODSize - 384;
643
644 // use previus higher LOD size on missing ones
645 if (m <= 0)
646 m = h;
647 if (l <= 0)
648 l = m;
649 if (lst <= 0)
650 lst = l;
651
652 // force minumum sizes
653 if (lst < 16)
654 lst = 16;
655 if (l < 16)
656 l = 16;
657 if (m < 16)
658 m = 16;
659 if (h < 16)
660 h = 16;
661
662 // compute cost weighted by relative effective areas
663 float cost = (float)lst * mlst + (float)l * ml + (float)m * mm + (float)h * mh;
664 cost /= ma;
665
666 cost *= 0.004f; // overall tunning parameter
667
668 return cost;
669 }
670 }
671}
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs
index 0dd0904..132546b 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs
@@ -368,7 +368,7 @@ namespace OpenSim.Region.ClientStack.Linden
368 // TODO: Add EventQueueGet name/description for diagnostics 368 // TODO: Add EventQueueGet name/description for diagnostics
369 MainServer.Instance.AddPollServiceHTTPHandler( 369 MainServer.Instance.AddPollServiceHTTPHandler(
370 eventQueueGetPath, 370 eventQueueGetPath,
371 new PollServiceEventArgs(null, HasEvents, GetEvents, NoEvents, agentID)); 371 new PollServiceEventArgs(null, HasEvents, GetEvents, NoEvents, agentID, 1000));
372 372
373// m_log.DebugFormat( 373// m_log.DebugFormat(
374// "[EVENT QUEUE GET MODULE]: Registered EQG handler {0} for {1} in {2}", 374// "[EVENT QUEUE GET MODULE]: Registered EQG handler {0} for {1} in {2}",
@@ -410,7 +410,7 @@ namespace OpenSim.Region.ClientStack.Linden
410 } 410 }
411 } 411 }
412 412
413 public Hashtable GetEvents(UUID requestID, UUID pAgentId, string request) 413 public Hashtable GetEvents(UUID requestID, UUID pAgentId)
414 { 414 {
415 if (DebugLevel >= 2) 415 if (DebugLevel >= 2)
416 m_log.DebugFormat("POLLED FOR EQ MESSAGES BY {0} in {1}", pAgentId, m_scene.RegionInfo.RegionName); 416 m_log.DebugFormat("POLLED FOR EQ MESSAGES BY {0} in {1}", pAgentId, m_scene.RegionInfo.RegionName);
@@ -822,5 +822,13 @@ namespace OpenSim.Region.ClientStack.Linden
822 { 822 {
823 return EventQueueHelper.BuildEvent(eventName, eventBody); 823 return EventQueueHelper.BuildEvent(eventName, eventBody);
824 } 824 }
825
826 public void partPhysicsProperties(uint localID, byte physhapetype,
827 float density, float friction, float bounce, float gravmod,UUID avatarID)
828 {
829 OSD item = EventQueueHelper.partPhysicsProperties(localID, physhapetype,
830 density, friction, bounce, gravmod);
831 Enqueue(item, avatarID);
832 }
825 } 833 }
826} 834}
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueHelper.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueHelper.cs
index 3f49aba..7dcf137 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueHelper.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueHelper.cs
@@ -151,6 +151,12 @@ namespace OpenSim.Region.ClientStack.Linden
151 ulong regionHandle, byte simAccess, IPEndPoint regionExternalEndPoint, 151 ulong regionHandle, byte simAccess, IPEndPoint regionExternalEndPoint,
152 uint locationID, uint flags, string capsURL, UUID agentID) 152 uint locationID, uint flags, string capsURL, UUID agentID)
153 { 153 {
154 // not sure why flags get overwritten here
155 if ((flags & (uint)TeleportFlags.IsFlying) != 0)
156 flags = (uint)TeleportFlags.ViaLocation | (uint)TeleportFlags.IsFlying;
157 else
158 flags = (uint)TeleportFlags.ViaLocation;
159
154 OSDMap info = new OSDMap(); 160 OSDMap info = new OSDMap();
155 info.Add("AgentID", OSD.FromUUID(agentID)); 161 info.Add("AgentID", OSD.FromUUID(agentID));
156 info.Add("LocationID", OSD.FromInteger(4)); // TODO what is this? 162 info.Add("LocationID", OSD.FromInteger(4)); // TODO what is this?
@@ -159,7 +165,8 @@ namespace OpenSim.Region.ClientStack.Linden
159 info.Add("SimAccess", OSD.FromInteger(simAccess)); 165 info.Add("SimAccess", OSD.FromInteger(simAccess));
160 info.Add("SimIP", OSD.FromBinary(regionExternalEndPoint.Address.GetAddressBytes())); 166 info.Add("SimIP", OSD.FromBinary(regionExternalEndPoint.Address.GetAddressBytes()));
161 info.Add("SimPort", OSD.FromInteger(regionExternalEndPoint.Port)); 167 info.Add("SimPort", OSD.FromInteger(regionExternalEndPoint.Port));
162 info.Add("TeleportFlags", OSD.FromULong(1L << 4)); // AgentManager.TeleportFlags.ViaLocation 168// info.Add("TeleportFlags", OSD.FromULong(1L << 4)); // AgentManager.TeleportFlags.ViaLocation
169 info.Add("TeleportFlags", OSD.FromUInteger(flags));
163 170
164 OSDArray infoArr = new OSDArray(); 171 OSDArray infoArr = new OSDArray();
165 infoArr.Add(info); 172 infoArr.Add(info);
@@ -395,5 +402,25 @@ namespace OpenSim.Region.ClientStack.Linden
395 return message; 402 return message;
396 } 403 }
397 404
405 public static OSD partPhysicsProperties(uint localID, byte physhapetype,
406 float density, float friction, float bounce, float gravmod)
407 {
408
409 OSDMap physinfo = new OSDMap(6);
410 physinfo["LocalID"] = localID;
411 physinfo["Density"] = density;
412 physinfo["Friction"] = friction;
413 physinfo["GravityMultiplier"] = gravmod;
414 physinfo["Restitution"] = bounce;
415 physinfo["PhysicsShapeType"] = (int)physhapetype;
416
417 OSDArray array = new OSDArray(1);
418 array.Add(physinfo);
419
420 OSDMap llsdBody = new OSDMap(1);
421 llsdBody.Add("ObjectData", array);
422
423 return BuildEvent("ObjectPhysicsProperties", llsdBody);
424 }
398 } 425 }
399} 426}
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs
index 5ae9cc3..cc65981 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs
@@ -27,18 +27,13 @@
27 27
28using System; 28using System;
29using System.Collections; 29using System.Collections;
30using System.Collections.Specialized; 30using System.Collections.Generic;
31using System.Drawing;
32using System.Drawing.Imaging;
33using System.Reflection; 31using System.Reflection;
34using System.IO; 32using System.Threading;
35using System.Web;
36using log4net; 33using log4net;
37using Nini.Config; 34using Nini.Config;
38using Mono.Addins; 35using Mono.Addins;
39using OpenMetaverse; 36using OpenMetaverse;
40using OpenMetaverse.StructuredData;
41using OpenMetaverse.Imaging;
42using OpenSim.Framework; 37using OpenSim.Framework;
43using OpenSim.Framework.Servers; 38using OpenSim.Framework.Servers;
44using OpenSim.Framework.Servers.HttpServer; 39using OpenSim.Framework.Servers.HttpServer;
@@ -47,64 +42,81 @@ using OpenSim.Region.Framework.Scenes;
47using OpenSim.Services.Interfaces; 42using OpenSim.Services.Interfaces;
48using Caps = OpenSim.Framework.Capabilities.Caps; 43using Caps = OpenSim.Framework.Capabilities.Caps;
49using OpenSim.Capabilities.Handlers; 44using OpenSim.Capabilities.Handlers;
45using OpenSim.Framework.Monitoring;
50 46
51namespace OpenSim.Region.ClientStack.Linden 47namespace OpenSim.Region.ClientStack.Linden
52{ 48{
53 49
54 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] 50 /// <summary>
51 /// This module implements both WebFetchTextureDescendents and FetchTextureDescendents2 capabilities.
52 /// </summary>
53 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GetTextureModule")]
55 public class GetTextureModule : INonSharedRegionModule 54 public class GetTextureModule : INonSharedRegionModule
56 { 55 {
57// private static readonly ILog m_log = 56
58// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 57 struct aPollRequest
59 58 {
59 public PollServiceTextureEventArgs thepoll;
60 public UUID reqID;
61 public Hashtable request;
62 }
63
64 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
65
60 private Scene m_scene; 66 private Scene m_scene;
61 private IAssetService m_assetService;
62 67
63 private bool m_Enabled = false; 68 private static GetTextureHandler m_getTextureHandler;
69
70 private IAssetService m_assetService = null;
64 71
65 // TODO: Change this to a config option 72 private Dictionary<UUID, string> m_capsDict = new Dictionary<UUID, string>();
66 const string REDIRECT_URL = null; 73 private static Thread[] m_workerThreads = null;
67 74
68 private string m_URL; 75 private static OpenMetaverse.BlockingQueue<aPollRequest> m_queue =
76 new OpenMetaverse.BlockingQueue<aPollRequest>();
69 77
70 #region ISharedRegionModule Members 78 #region ISharedRegionModule Members
71 79
72 public void Initialise(IConfigSource source) 80 public void Initialise(IConfigSource source)
73 { 81 {
74 IConfig config = source.Configs["ClientStack.LindenCaps"];
75 if (config == null)
76 return;
77
78 m_URL = config.GetString("Cap_GetTexture", string.Empty);
79 // Cap doesn't exist
80 if (m_URL != string.Empty)
81 m_Enabled = true;
82 } 82 }
83 83
84 public void AddRegion(Scene s) 84 public void AddRegion(Scene s)
85 { 85 {
86 if (!m_Enabled)
87 return;
88
89 m_scene = s; 86 m_scene = s;
87 m_assetService = s.AssetService;
90 } 88 }
91 89
92 public void RemoveRegion(Scene s) 90 public void RemoveRegion(Scene s)
93 { 91 {
94 if (!m_Enabled)
95 return;
96
97 m_scene.EventManager.OnRegisterCaps -= RegisterCaps; 92 m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
93 m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps;
98 m_scene = null; 94 m_scene = null;
99 } 95 }
100 96
101 public void RegionLoaded(Scene s) 97 public void RegionLoaded(Scene s)
102 { 98 {
103 if (!m_Enabled) 99 // We'll reuse the same handler for all requests.
104 return; 100 m_getTextureHandler = new GetTextureHandler(m_assetService);
105 101
106 m_assetService = m_scene.RequestModuleInterface<IAssetService>();
107 m_scene.EventManager.OnRegisterCaps += RegisterCaps; 102 m_scene.EventManager.OnRegisterCaps += RegisterCaps;
103 m_scene.EventManager.OnDeregisterCaps += DeregisterCaps;
104
105 if (m_workerThreads == null)
106 {
107 m_workerThreads = new Thread[4];
108
109 for (uint i = 0; i < 4; i++)
110 {
111 m_workerThreads[i] = Watchdog.StartThread(DoTextureRequests,
112 String.Format("TextureWorkerThread{0}", i),
113 ThreadPriority.Normal,
114 false,
115 false,
116 null,
117 int.MaxValue);
118 }
119 }
108 } 120 }
109 121
110 public void PostInitialise() 122 public void PostInitialise()
@@ -122,24 +134,152 @@ namespace OpenSim.Region.ClientStack.Linden
122 134
123 #endregion 135 #endregion
124 136
125 public void RegisterCaps(UUID agentID, Caps caps) 137 ~GetTextureModule()
138 {
139 foreach (Thread t in m_workerThreads)
140 Watchdog.AbortThread(t.ManagedThreadId);
141
142 }
143
144 private class PollServiceTextureEventArgs : PollServiceEventArgs
145 {
146 private List<Hashtable> requests =
147 new List<Hashtable>();
148 private Dictionary<UUID, Hashtable> responses =
149 new Dictionary<UUID, Hashtable>();
150
151 private Scene m_scene;
152
153 public PollServiceTextureEventArgs(UUID pId, Scene scene) :
154 base(null, null, null, null, pId, int.MaxValue)
155 {
156 m_scene = scene;
157
158 HasEvents = (x, y) =>
159 {
160 lock (responses)
161 return responses.ContainsKey(x);
162 };
163 GetEvents = (x, y) =>
164 {
165 lock (responses)
166 {
167 try
168 {
169 return responses[x];
170 }
171 finally
172 {
173 responses.Remove(x);
174 }
175 }
176 };
177
178 Request = (x, y) =>
179 {
180 aPollRequest reqinfo = new aPollRequest();
181 reqinfo.thepoll = this;
182 reqinfo.reqID = x;
183 reqinfo.request = y;
184
185 m_queue.Enqueue(reqinfo);
186 };
187
188 // this should never happen except possible on shutdown
189 NoEvents = (x, y) =>
190 {
191/*
192 lock (requests)
193 {
194 Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
195 requests.Remove(request);
196 }
197*/
198 Hashtable response = new Hashtable();
199
200 response["int_response_code"] = 500;
201 response["str_response_string"] = "Script timeout";
202 response["content_type"] = "text/plain";
203 response["keepalive"] = false;
204 response["reusecontext"] = false;
205
206 return response;
207 };
208 }
209
210 public void Process(aPollRequest requestinfo)
211 {
212 Hashtable response;
213
214 UUID requestID = requestinfo.reqID;
215
216 // If the avatar is gone, don't bother to get the texture
217 if (m_scene.GetScenePresence(Id) == null)
218 {
219 response = new Hashtable();
220
221 response["int_response_code"] = 500;
222 response["str_response_string"] = "Script timeout";
223 response["content_type"] = "text/plain";
224 response["keepalive"] = false;
225 response["reusecontext"] = false;
226
227 lock (responses)
228 responses[requestID] = response;
229
230 return;
231 }
232
233 response = m_getTextureHandler.Handle(requestinfo.request);
234 lock (responses)
235 responses[requestID] = response;
236 }
237 }
238
239 private void RegisterCaps(UUID agentID, Caps caps)
126 { 240 {
127 UUID capID = UUID.Random(); 241 string capUrl = "/CAPS/" + UUID.Random() + "/";
242
243 // Register this as a poll service
244 PollServiceTextureEventArgs args = new PollServiceTextureEventArgs(agentID, m_scene);
245
246 args.Type = PollServiceEventArgs.EventType.Texture;
247 MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
128 248
129 //caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture)); 249 string hostName = m_scene.RegionInfo.ExternalHostName;
130 if (m_URL == "localhost") 250 uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
251 string protocol = "http";
252
253 if (MainServer.Instance.UseSSL)
131 { 254 {
132// m_log.DebugFormat("[GETTEXTURE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); 255 hostName = MainServer.Instance.SSLCommonName;
133 caps.RegisterHandler( 256 port = MainServer.Instance.SSLPort;
134 "GetTexture", 257 protocol = "https";
135 new GetTextureHandler("/CAPS/" + capID + "/", m_assetService, "GetTexture", agentID.ToString()));
136 } 258 }
137 else 259 caps.RegisterHandler("GetTexture", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
260
261 m_capsDict[agentID] = capUrl;
262 }
263
264 private void DeregisterCaps(UUID agentID, Caps caps)
265 {
266 string capUrl;
267
268 if (m_capsDict.TryGetValue(agentID, out capUrl))
138 { 269 {
139// m_log.DebugFormat("[GETTEXTURE]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName); 270 MainServer.Instance.RemoveHTTPHandler("", capUrl);
140 caps.RegisterHandler("GetTexture", m_URL); 271 m_capsDict.Remove(agentID);
141 } 272 }
142 } 273 }
143 274
275 private void DoTextureRequests()
276 {
277 while (true)
278 {
279 aPollRequest poolreq = m_queue.Dequeue();
280
281 poolreq.thepoll.Process(poolreq);
282 }
283 }
144 } 284 }
145} 285}
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/MeshUploadFlagModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/MeshUploadFlagModule.cs
index 44a6883..0251ac4 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/MeshUploadFlagModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/MeshUploadFlagModule.cs
@@ -129,15 +129,15 @@ namespace OpenSim.Region.ClientStack.Linden
129// m_log.DebugFormat("[MESH UPLOAD FLAG MODULE]: MeshUploadFlag request"); 129// m_log.DebugFormat("[MESH UPLOAD FLAG MODULE]: MeshUploadFlag request");
130 130
131 OSDMap data = new OSDMap(); 131 OSDMap data = new OSDMap();
132 ScenePresence sp = m_scene.GetScenePresence(m_agentID); 132// ScenePresence sp = m_scene.GetScenePresence(m_agentID);
133 data["username"] = sp.Firstname + "." + sp.Lastname; 133// data["username"] = sp.Firstname + "." + sp.Lastname;
134 data["display_name_next_update"] = new OSDDate(DateTime.Now); 134// data["display_name_next_update"] = new OSDDate(DateTime.Now);
135 data["legacy_first_name"] = sp.Firstname; 135// data["legacy_first_name"] = sp.Firstname;
136 data["mesh_upload_status"] = "valid"; 136 data["mesh_upload_status"] = "valid";
137 data["display_name"] = sp.Firstname + " " + sp.Lastname; 137// data["display_name"] = sp.Firstname + " " + sp.Lastname;
138 data["legacy_last_name"] = sp.Lastname; 138// data["legacy_last_name"] = sp.Lastname;
139 data["id"] = m_agentID; 139// data["id"] = m_agentID;
140 data["is_display_name_default"] = true; 140// data["is_display_name_default"] = true;
141 141
142 //Send back data 142 //Send back data
143 Hashtable responsedata = new Hashtable(); 143 Hashtable responsedata = new Hashtable();
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/NewFileAgentInventoryVariablePriceModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/NewFileAgentInventoryVariablePriceModule.cs
deleted file mode 100644
index 52c4f44..0000000
--- a/OpenSim/Region/ClientStack/Linden/Caps/NewFileAgentInventoryVariablePriceModule.cs
+++ /dev/null
@@ -1,296 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using System.Collections.Specialized;
31using System.Reflection;
32using System.IO;
33using System.Web;
34using Mono.Addins;
35using log4net;
36using Nini.Config;
37using OpenMetaverse;
38using OpenMetaverse.StructuredData;
39using OpenSim.Framework;
40using OpenSim.Framework.Servers;
41using OpenSim.Framework.Servers.HttpServer;
42using OpenSim.Region.Framework.Interfaces;
43using OpenSim.Region.Framework.Scenes;
44using OpenSim.Services.Interfaces;
45using Caps = OpenSim.Framework.Capabilities.Caps;
46using OpenSim.Framework.Capabilities;
47
48namespace OpenSim.Region.ClientStack.Linden
49{
50 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
51 public class NewFileAgentInventoryVariablePriceModule : INonSharedRegionModule
52 {
53// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
54
55 private Scene m_scene;
56// private IAssetService m_assetService;
57 private bool m_dumpAssetsToFile = false;
58 private bool m_enabled = true;
59 private int m_levelUpload = 0;
60
61 #region IRegionModuleBase Members
62
63
64 public Type ReplaceableInterface
65 {
66 get { return null; }
67 }
68
69 public void Initialise(IConfigSource source)
70 {
71 IConfig meshConfig = source.Configs["Mesh"];
72 if (meshConfig == null)
73 return;
74
75 m_enabled = meshConfig.GetBoolean("AllowMeshUpload", true);
76 m_levelUpload = meshConfig.GetInt("LevelUpload", 0);
77 }
78
79 public void AddRegion(Scene pScene)
80 {
81 m_scene = pScene;
82 }
83
84 public void RemoveRegion(Scene scene)
85 {
86
87 m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
88 m_scene = null;
89 }
90
91 public void RegionLoaded(Scene scene)
92 {
93
94// m_assetService = m_scene.RequestModuleInterface<IAssetService>();
95 m_scene.EventManager.OnRegisterCaps += RegisterCaps;
96 }
97
98 #endregion
99
100
101 #region IRegionModule Members
102
103
104
105 public void Close() { }
106
107 public string Name { get { return "NewFileAgentInventoryVariablePriceModule"; } }
108
109
110 public void RegisterCaps(UUID agentID, Caps caps)
111 {
112 if(!m_enabled)
113 return;
114
115 UUID capID = UUID.Random();
116
117// m_log.Debug("[NEW FILE AGENT INVENTORY VARIABLE PRICE]: /CAPS/" + capID);
118 caps.RegisterHandler(
119 "NewFileAgentInventoryVariablePrice",
120 new LLSDStreamhandler<LLSDAssetUploadRequest, LLSDNewFileAngentInventoryVariablePriceReplyResponse>(
121 "POST",
122 "/CAPS/" + capID.ToString(),
123 req => NewAgentInventoryRequest(req, agentID),
124 "NewFileAgentInventoryVariablePrice",
125 agentID.ToString()));
126 }
127
128 #endregion
129
130 public LLSDNewFileAngentInventoryVariablePriceReplyResponse NewAgentInventoryRequest(LLSDAssetUploadRequest llsdRequest, UUID agentID)
131 {
132 //TODO: The Mesh uploader uploads many types of content. If you're going to implement a Money based limit
133 // you need to be aware of this
134
135 //if (llsdRequest.asset_type == "texture" ||
136 // llsdRequest.asset_type == "animation" ||
137 // llsdRequest.asset_type == "sound")
138 // {
139 // check user level
140
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)
150 {
151 if (client != null)
152 client.SendAgentAlertMessage("Unable to upload asset. Insufficient permissions.", false);
153
154 LLSDNewFileAngentInventoryVariablePriceReplyResponse errorResponse = new LLSDNewFileAngentInventoryVariablePriceReplyResponse();
155 errorResponse.rsvp = "";
156 errorResponse.state = "error";
157 return errorResponse;
158 }
159 }
160
161 // check funds
162 IMoneyModule mm = m_scene.RequestModuleInterface<IMoneyModule>();
163
164 if (mm != null)
165 {
166 if (!mm.UploadCovered(agentID, mm.UploadCharge))
167 {
168 if (client != null)
169 client.SendAgentAlertMessage("Unable to upload asset. Insufficient funds.", false);
170
171 LLSDNewFileAngentInventoryVariablePriceReplyResponse errorResponse = new LLSDNewFileAngentInventoryVariablePriceReplyResponse();
172 errorResponse.rsvp = "";
173 errorResponse.state = "error";
174 return errorResponse;
175 }
176 }
177
178 // }
179
180 string assetName = llsdRequest.name;
181 string assetDes = llsdRequest.description;
182 string capsBase = "/CAPS/NewFileAgentInventoryVariablePrice/";
183 UUID newAsset = UUID.Random();
184 UUID newInvItem = UUID.Random();
185 UUID parentFolder = llsdRequest.folder_id;
186 string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000") + "/";
187
188 AssetUploader uploader =
189 new AssetUploader(assetName, assetDes, newAsset, newInvItem, parentFolder, llsdRequest.inventory_type,
190 llsdRequest.asset_type, capsBase + uploaderPath, MainServer.Instance, m_dumpAssetsToFile);
191
192 MainServer.Instance.AddStreamHandler(
193 new BinaryStreamHandler(
194 "POST",
195 capsBase + uploaderPath,
196 uploader.uploaderCaps,
197 "NewFileAgentInventoryVariablePrice",
198 agentID.ToString()));
199
200 string protocol = "http://";
201
202 if (MainServer.Instance.UseSSL)
203 protocol = "https://";
204
205 string uploaderURL = protocol + m_scene.RegionInfo.ExternalHostName + ":" + MainServer.Instance.Port.ToString() + capsBase +
206 uploaderPath;
207
208
209 LLSDNewFileAngentInventoryVariablePriceReplyResponse uploadResponse = new LLSDNewFileAngentInventoryVariablePriceReplyResponse();
210
211 uploadResponse.rsvp = uploaderURL;
212 uploadResponse.state = "upload";
213 uploadResponse.resource_cost = 0;
214 uploadResponse.upload_price = 0;
215
216 uploader.OnUpLoad += //UploadCompleteHandler;
217
218 delegate(
219 string passetName, string passetDescription, UUID passetID,
220 UUID pinventoryItem, UUID pparentFolder, byte[] pdata, string pinventoryType,
221 string passetType)
222 {
223 UploadCompleteHandler(passetName, passetDescription, passetID,
224 pinventoryItem, pparentFolder, pdata, pinventoryType,
225 passetType,agentID);
226 };
227
228 return uploadResponse;
229 }
230
231 public void UploadCompleteHandler(string assetName, string assetDescription, UUID assetID,
232 UUID inventoryItem, UUID parentFolder, byte[] data, string inventoryType,
233 string assetType,UUID AgentID)
234 {
235// m_log.DebugFormat(
236// "[NEW FILE AGENT INVENTORY VARIABLE PRICE MODULE]: Upload complete for {0}", inventoryItem);
237
238 sbyte assType = 0;
239 sbyte inType = 0;
240
241 if (inventoryType == "sound")
242 {
243 inType = 1;
244 assType = 1;
245 }
246 else if (inventoryType == "animation")
247 {
248 inType = 19;
249 assType = 20;
250 }
251 else if (inventoryType == "wearable")
252 {
253 inType = 18;
254 switch (assetType)
255 {
256 case "bodypart":
257 assType = 13;
258 break;
259 case "clothing":
260 assType = 5;
261 break;
262 }
263 }
264 else if (inventoryType == "mesh")
265 {
266 inType = (sbyte)InventoryType.Mesh;
267 assType = (sbyte)AssetType.Mesh;
268 }
269
270 AssetBase asset;
271 asset = new AssetBase(assetID, assetName, assType, AgentID.ToString());
272 asset.Data = data;
273
274 if (m_scene.AssetService != null)
275 m_scene.AssetService.Store(asset);
276
277 InventoryItemBase item = new InventoryItemBase();
278 item.Owner = AgentID;
279 item.CreatorId = AgentID.ToString();
280 item.ID = inventoryItem;
281 item.AssetID = asset.FullID;
282 item.Description = assetDescription;
283 item.Name = assetName;
284 item.AssetType = assType;
285 item.InvType = inType;
286 item.Folder = parentFolder;
287 item.CurrentPermissions
288 = (uint)(PermissionMask.Move | PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer);
289 item.BasePermissions = (uint)PermissionMask.All;
290 item.EveryOnePermissions = 0;
291 item.NextPermissions = (uint)PermissionMask.All;
292 item.CreationDate = Util.UnixTimeSinceEpoch();
293 m_scene.AddInventoryItem(item);
294 }
295 }
296}
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs
index 36af55f..413536d 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs
@@ -64,6 +64,8 @@ namespace OpenSim.Region.ClientStack.Linden
64 private Commands m_commands = new Commands(); 64 private Commands m_commands = new Commands();
65 public ICommands Commands { get { return m_commands; } } 65 public ICommands Commands { get { return m_commands; } }
66 66
67 public event ConsoleMessage OnConsoleMessage;
68
67 public void Initialise(IConfigSource source) 69 public void Initialise(IConfigSource source)
68 { 70 {
69 m_commands.AddCommand( "Help", false, "help", "help [<item>]", "Display help on a particular command or on a list of commands in a category", Help); 71 m_commands.AddCommand( "Help", false, "help", "help [<item>]", "Display help on a particular command or on a list of commands in a category", Help);
@@ -118,6 +120,11 @@ namespace OpenSim.Region.ClientStack.Linden
118 OSD osd = OSD.FromString(message); 120 OSD osd = OSD.FromString(message);
119 121
120 m_eventQueue.Enqueue(EventQueueHelper.BuildEvent("SimConsoleResponse", osd), agentID); 122 m_eventQueue.Enqueue(EventQueueHelper.BuildEvent("SimConsoleResponse", osd), agentID);
123
124 ConsoleMessage handlerConsoleMessage = OnConsoleMessage;
125
126 if (handlerConsoleMessage != null)
127 handlerConsoleMessage( agentID, message);
121 } 128 }
122 129
123 public bool RunCommand(string command, UUID invokerID) 130 public bool RunCommand(string command, UUID invokerID)
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
index 2359bd6..0caeddf 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
@@ -27,18 +27,22 @@
27 27
28using System; 28using System;
29using System.Collections; 29using System.Collections;
30using System.Collections.Generic;
30using System.Reflection; 31using System.Reflection;
32using System.Threading;
31using log4net; 33using log4net;
32using Nini.Config; 34using Nini.Config;
33using Mono.Addins; 35using Mono.Addins;
34using OpenMetaverse; 36using OpenMetaverse;
35using OpenSim.Framework; 37using OpenSim.Framework;
38using OpenSim.Framework.Servers;
36using OpenSim.Framework.Servers.HttpServer; 39using OpenSim.Framework.Servers.HttpServer;
37using OpenSim.Region.Framework.Interfaces; 40using OpenSim.Region.Framework.Interfaces;
38using OpenSim.Region.Framework.Scenes; 41using OpenSim.Region.Framework.Scenes;
39using OpenSim.Services.Interfaces; 42using OpenSim.Services.Interfaces;
40using Caps = OpenSim.Framework.Capabilities.Caps; 43using Caps = OpenSim.Framework.Capabilities.Caps;
41using OpenSim.Capabilities.Handlers; 44using OpenSim.Capabilities.Handlers;
45using OpenSim.Framework.Monitoring;
42 46
43namespace OpenSim.Region.ClientStack.Linden 47namespace OpenSim.Region.ClientStack.Linden
44{ 48{
@@ -48,67 +52,72 @@ namespace OpenSim.Region.ClientStack.Linden
48 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] 52 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
49 public class WebFetchInvDescModule : INonSharedRegionModule 53 public class WebFetchInvDescModule : INonSharedRegionModule
50 { 54 {
51// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 55 struct aPollRequest
56 {
57 public PollServiceInventoryEventArgs thepoll;
58 public UUID reqID;
59 public Hashtable request;
60 }
61
62 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
52 63
53 private Scene m_scene; 64 private Scene m_scene;
54 65
55 private IInventoryService m_InventoryService; 66 private IInventoryService m_InventoryService;
56 private ILibraryService m_LibraryService; 67 private ILibraryService m_LibraryService;
57 68
58 private bool m_Enabled; 69 private static WebFetchInvDescHandler m_webFetchHandler;
59 70
60 private string m_fetchInventoryDescendents2Url; 71 private Dictionary<UUID, string> m_capsDict = new Dictionary<UUID, string>();
61 private string m_webFetchInventoryDescendentsUrl; 72 private static Thread[] m_workerThreads = null;
62 73
63 private WebFetchInvDescHandler m_webFetchHandler; 74 private static OpenMetaverse.BlockingQueue<aPollRequest> m_queue =
75 new OpenMetaverse.BlockingQueue<aPollRequest>();
64 76
65 #region ISharedRegionModule Members 77 #region ISharedRegionModule Members
66 78
67 public void Initialise(IConfigSource source) 79 public void Initialise(IConfigSource source)
68 { 80 {
69 IConfig config = source.Configs["ClientStack.LindenCaps"];
70 if (config == null)
71 return;
72
73 m_fetchInventoryDescendents2Url = config.GetString("Cap_FetchInventoryDescendents2", string.Empty);
74 m_webFetchInventoryDescendentsUrl = config.GetString("Cap_WebFetchInventoryDescendents", string.Empty);
75
76 if (m_fetchInventoryDescendents2Url != string.Empty || m_webFetchInventoryDescendentsUrl != string.Empty)
77 {
78 m_Enabled = true;
79 }
80 } 81 }
81 82
82 public void AddRegion(Scene s) 83 public void AddRegion(Scene s)
83 { 84 {
84 if (!m_Enabled)
85 return;
86
87 m_scene = s; 85 m_scene = s;
88 } 86 }
89 87
90 public void RemoveRegion(Scene s) 88 public void RemoveRegion(Scene s)
91 { 89 {
92 if (!m_Enabled)
93 return;
94
95 m_scene.EventManager.OnRegisterCaps -= RegisterCaps; 90 m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
91 m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps;
96 m_scene = null; 92 m_scene = null;
97 } 93 }
98 94
99 public void RegionLoaded(Scene s) 95 public void RegionLoaded(Scene s)
100 { 96 {
101 if (!m_Enabled)
102 return;
103
104 m_InventoryService = m_scene.InventoryService; 97 m_InventoryService = m_scene.InventoryService;
105 m_LibraryService = m_scene.LibraryService; 98 m_LibraryService = m_scene.LibraryService;
106 99
107 // We'll reuse the same handler for all requests. 100 // We'll reuse the same handler for all requests.
108 if (m_fetchInventoryDescendents2Url == "localhost" || m_webFetchInventoryDescendentsUrl == "localhost") 101 m_webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService);
109 m_webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService);
110 102
111 m_scene.EventManager.OnRegisterCaps += RegisterCaps; 103 m_scene.EventManager.OnRegisterCaps += RegisterCaps;
104 m_scene.EventManager.OnDeregisterCaps += DeregisterCaps;
105
106 if (m_workerThreads == null)
107 {
108 m_workerThreads = new Thread[2];
109
110 for (uint i = 0; i < 2; i++)
111 {
112 m_workerThreads[i] = Watchdog.StartThread(DoInventoryRequests,
113 String.Format("InventoryWorkerThread{0}", i),
114 ThreadPriority.Normal,
115 false,
116 true,
117 null,
118 int.MaxValue);
119 }
120 }
112 } 121 }
113 122
114 public void PostInitialise() 123 public void PostInitialise()
@@ -126,43 +135,130 @@ namespace OpenSim.Region.ClientStack.Linden
126 135
127 #endregion 136 #endregion
128 137
129 private void RegisterCaps(UUID agentID, Caps caps) 138 ~WebFetchInvDescModule()
130 { 139 {
131 if (m_webFetchInventoryDescendentsUrl != "") 140 foreach (Thread t in m_workerThreads)
132 RegisterFetchCap(agentID, caps, "WebFetchInventoryDescendents", m_webFetchInventoryDescendentsUrl); 141 Watchdog.AbortThread(t.ManagedThreadId);
133
134 if (m_fetchInventoryDescendents2Url != "")
135 RegisterFetchCap(agentID, caps, "FetchInventoryDescendents2", m_fetchInventoryDescendents2Url);
136 } 142 }
137 143
138 private void RegisterFetchCap(UUID agentID, Caps caps, string capName, string url) 144 private class PollServiceInventoryEventArgs : PollServiceEventArgs
139 { 145 {
140 string capUrl; 146 private Dictionary<UUID, Hashtable> responses =
147 new Dictionary<UUID, Hashtable>();
148
149 public PollServiceInventoryEventArgs(UUID pId) :
150 base(null, null, null, null, pId, int.MaxValue)
151 {
152 HasEvents = (x, y) => { lock (responses) return responses.ContainsKey(x); };
153 GetEvents = (x, y) =>
154 {
155 lock (responses)
156 {
157 try
158 {
159 return responses[x];
160 }
161 finally
162 {
163 responses.Remove(x);
164 }
165 }
166 };
167
168 Request = (x, y) =>
169 {
170 aPollRequest reqinfo = new aPollRequest();
171 reqinfo.thepoll = this;
172 reqinfo.reqID = x;
173 reqinfo.request = y;
174
175 m_queue.Enqueue(reqinfo);
176 };
177
178 NoEvents = (x, y) =>
179 {
180/*
181 lock (requests)
182 {
183 Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
184 requests.Remove(request);
185 }
186*/
187 Hashtable response = new Hashtable();
188
189 response["int_response_code"] = 500;
190 response["str_response_string"] = "Script timeout";
191 response["content_type"] = "text/plain";
192 response["keepalive"] = false;
193 response["reusecontext"] = false;
194
195 return response;
196 };
197 }
141 198
142 if (url == "localhost") 199 public void Process(aPollRequest requestinfo)
143 { 200 {
144 capUrl = "/CAPS/" + UUID.Random(); 201 UUID requestID = requestinfo.reqID;
202
203 Hashtable response = new Hashtable();
204
205 response["int_response_code"] = 200;
206 response["content_type"] = "text/plain";
207 response["keepalive"] = false;
208 response["reusecontext"] = false;
145 209
146 IRequestHandler reqHandler 210 response["str_response_string"] = m_webFetchHandler.FetchInventoryDescendentsRequest(
147 = new RestStreamHandler( 211 requestinfo.request["body"].ToString(), String.Empty, String.Empty, null, null);
148 "POST",
149 capUrl,
150 m_webFetchHandler.FetchInventoryDescendentsRequest,
151 "FetchInventoryDescendents2",
152 agentID.ToString());
153 212
154 caps.RegisterHandler(capName, reqHandler); 213 lock (responses)
214 responses[requestID] = response;
155 } 215 }
156 else 216 }
217
218 private void RegisterCaps(UUID agentID, Caps caps)
219 {
220 string capUrl = "/CAPS/" + UUID.Random() + "/";
221
222 // Register this as a poll service
223 PollServiceInventoryEventArgs args = new PollServiceInventoryEventArgs(agentID);
224
225 args.Type = PollServiceEventArgs.EventType.Inventory;
226 MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
227
228 string hostName = m_scene.RegionInfo.ExternalHostName;
229 uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
230 string protocol = "http";
231
232 if (MainServer.Instance.UseSSL)
157 { 233 {
158 capUrl = url; 234 hostName = MainServer.Instance.SSLCommonName;
235 port = MainServer.Instance.SSLPort;
236 protocol = "https";
237 }
238 caps.RegisterHandler("FetchInventoryDescendents2", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
239
240 m_capsDict[agentID] = capUrl;
241 }
159 242
160 caps.RegisterHandler(capName, capUrl); 243 private void DeregisterCaps(UUID agentID, Caps caps)
244 {
245 string capUrl;
246
247 if (m_capsDict.TryGetValue(agentID, out capUrl))
248 {
249 MainServer.Instance.RemoveHTTPHandler("", capUrl);
250 m_capsDict.Remove(agentID);
161 } 251 }
252 }
162 253
163// m_log.DebugFormat( 254 private void DoInventoryRequests()
164// "[WEB FETCH INV DESC MODULE]: Registered capability {0} at {1} in region {2} for {3}", 255 {
165// capName, capUrl, m_scene.RegionInfo.RegionName, agentID); 256 while (true)
257 {
258 aPollRequest poolreq = m_queue.Dequeue();
259
260 poolreq.thepoll.Process(poolreq);
261 }
166 } 262 }
167 } 263 }
168} \ No newline at end of file 264}
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/J2KImage.cs b/OpenSim/Region/ClientStack/Linden/UDP/J2KImage.cs
index afbe56b..3995620 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/J2KImage.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/J2KImage.cs
@@ -234,6 +234,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
234 m_stopPacket = TexturePacketCount(); 234 m_stopPacket = TexturePacketCount();
235 } 235 }
236 236
237 //Give them at least two packets, to play nice with some broken viewers (SL also behaves this way)
238 if (m_stopPacket == 1 && m_layers[0].End > FIRST_PACKET_SIZE) m_stopPacket++;
239
237 m_currentPacket = StartPacket; 240 m_currentPacket = StartPacket;
238 } 241 }
239 } 242 }
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
index 2db8df2..d04bd96 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
@@ -99,6 +99,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
99 public event AvatarPickerRequest OnAvatarPickerRequest; 99 public event AvatarPickerRequest OnAvatarPickerRequest;
100 public event StartAnim OnStartAnim; 100 public event StartAnim OnStartAnim;
101 public event StopAnim OnStopAnim; 101 public event StopAnim OnStopAnim;
102 public event ChangeAnim OnChangeAnim;
102 public event Action<IClientAPI> OnRequestAvatarsData; 103 public event Action<IClientAPI> OnRequestAvatarsData;
103 public event LinkObjects OnLinkObjects; 104 public event LinkObjects OnLinkObjects;
104 public event DelinkObjects OnDelinkObjects; 105 public event DelinkObjects OnDelinkObjects;
@@ -126,6 +127,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
126 public event RequestObjectPropertiesFamily OnRequestObjectPropertiesFamily; 127 public event RequestObjectPropertiesFamily OnRequestObjectPropertiesFamily;
127 public event UpdatePrimFlags OnUpdatePrimFlags; 128 public event UpdatePrimFlags OnUpdatePrimFlags;
128 public event UpdatePrimTexture OnUpdatePrimTexture; 129 public event UpdatePrimTexture OnUpdatePrimTexture;
130 public event ClientChangeObject onClientChangeObject;
129 public event UpdateVector OnUpdatePrimGroupPosition; 131 public event UpdateVector OnUpdatePrimGroupPosition;
130 public event UpdateVector OnUpdatePrimSinglePosition; 132 public event UpdateVector OnUpdatePrimSinglePosition;
131 public event UpdatePrimRotation OnUpdatePrimGroupRotation; 133 public event UpdatePrimRotation OnUpdatePrimGroupRotation;
@@ -159,6 +161,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
159 public event RequestTaskInventory OnRequestTaskInventory; 161 public event RequestTaskInventory OnRequestTaskInventory;
160 public event UpdateInventoryItem OnUpdateInventoryItem; 162 public event UpdateInventoryItem OnUpdateInventoryItem;
161 public event CopyInventoryItem OnCopyInventoryItem; 163 public event CopyInventoryItem OnCopyInventoryItem;
164 public event MoveItemsAndLeaveCopy OnMoveItemsAndLeaveCopy;
162 public event MoveInventoryItem OnMoveInventoryItem; 165 public event MoveInventoryItem OnMoveInventoryItem;
163 public event RemoveInventoryItem OnRemoveInventoryItem; 166 public event RemoveInventoryItem OnRemoveInventoryItem;
164 public event RemoveInventoryFolder OnRemoveInventoryFolder; 167 public event RemoveInventoryFolder OnRemoveInventoryFolder;
@@ -257,7 +260,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
257 public event ClassifiedInfoRequest OnClassifiedInfoRequest; 260 public event ClassifiedInfoRequest OnClassifiedInfoRequest;
258 public event ClassifiedInfoUpdate OnClassifiedInfoUpdate; 261 public event ClassifiedInfoUpdate OnClassifiedInfoUpdate;
259 public event ClassifiedDelete OnClassifiedDelete; 262 public event ClassifiedDelete OnClassifiedDelete;
260 public event ClassifiedDelete OnClassifiedGodDelete; 263 public event ClassifiedGodDelete OnClassifiedGodDelete;
261 public event EventNotificationAddRequest OnEventNotificationAddRequest; 264 public event EventNotificationAddRequest OnEventNotificationAddRequest;
262 public event EventNotificationRemoveRequest OnEventNotificationRemoveRequest; 265 public event EventNotificationRemoveRequest OnEventNotificationRemoveRequest;
263 public event EventGodDelete OnEventGodDelete; 266 public event EventGodDelete OnEventGodDelete;
@@ -288,6 +291,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
288 public event GroupVoteHistoryRequest OnGroupVoteHistoryRequest; 291 public event GroupVoteHistoryRequest OnGroupVoteHistoryRequest;
289 public event SimWideDeletesDelegate OnSimWideDeletes; 292 public event SimWideDeletesDelegate OnSimWideDeletes;
290 public event SendPostcard OnSendPostcard; 293 public event SendPostcard OnSendPostcard;
294 public event ChangeInventoryItemFlags OnChangeInventoryItemFlags;
291 public event MuteListEntryUpdate OnUpdateMuteListEntry; 295 public event MuteListEntryUpdate OnUpdateMuteListEntry;
292 public event MuteListEntryRemove OnRemoveMuteListEntry; 296 public event MuteListEntryRemove OnRemoveMuteListEntry;
293 public event GodlikeMessage onGodlikeMessage; 297 public event GodlikeMessage onGodlikeMessage;
@@ -326,6 +330,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
326 private Prioritizer m_prioritizer; 330 private Prioritizer m_prioritizer;
327 private bool m_disableFacelights = false; 331 private bool m_disableFacelights = false;
328 332
333 private const uint MaxTransferBytesPerPacket = 600;
334
335
329 /// <value> 336 /// <value>
330 /// List used in construction of data blocks for an object update packet. This is to stop us having to 337 /// List used in construction of data blocks for an object update packet. This is to stop us having to
331 /// continually recreate it. 338 /// continually recreate it.
@@ -337,14 +344,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
337 /// thread servicing the m_primFullUpdates queue after a kill. If this happens the object persists as an 344 /// thread servicing the m_primFullUpdates queue after a kill. If this happens the object persists as an
338 /// ownerless phantom. 345 /// ownerless phantom.
339 /// 346 ///
340 /// All manipulation of this set has to occur under a lock 347 /// All manipulation of this set has to occur under an m_entityUpdates.SyncRoot lock
341 /// 348 ///
342 /// </value> 349 /// </value>
343 protected HashSet<uint> m_killRecord; 350// protected HashSet<uint> m_killRecord;
344 351
345// protected HashSet<uint> m_attachmentsSent; 352// protected HashSet<uint> m_attachmentsSent;
346 353
347 private int m_moneyBalance; 354 private int m_moneyBalance;
355 private bool m_deliverPackets = true;
348 private int m_animationSequenceNumber = 1; 356 private int m_animationSequenceNumber = 1;
349 private bool m_SendLogoutPacketWhenClosing = true; 357 private bool m_SendLogoutPacketWhenClosing = true;
350 private AgentUpdateArgs lastarg; 358 private AgentUpdateArgs lastarg;
@@ -381,6 +389,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
381 get { return m_startpos; } 389 get { return m_startpos; }
382 set { m_startpos = value; } 390 set { m_startpos = value; }
383 } 391 }
392 public bool DeliverPackets
393 {
394 get { return m_deliverPackets; }
395 set {
396 m_deliverPackets = value;
397 m_udpClient.m_deliverPackets = value;
398 }
399 }
384 public UUID AgentId { get { return m_agentId; } } 400 public UUID AgentId { get { return m_agentId; } }
385 public ISceneAgent SceneAgent { get; set; } 401 public ISceneAgent SceneAgent { get; set; }
386 public UUID ActiveGroupId { get { return m_activeGroupID; } } 402 public UUID ActiveGroupId { get { return m_activeGroupID; } }
@@ -458,7 +474,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
458 m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); 474 m_entityUpdates = new PriorityQueue(m_scene.Entities.Count);
459 m_entityProps = new PriorityQueue(m_scene.Entities.Count); 475 m_entityProps = new PriorityQueue(m_scene.Entities.Count);
460 m_fullUpdateDataBlocksBuilder = new List<ObjectUpdatePacket.ObjectDataBlock>(); 476 m_fullUpdateDataBlocksBuilder = new List<ObjectUpdatePacket.ObjectDataBlock>();
461 m_killRecord = new HashSet<uint>(); 477// m_killRecord = new HashSet<uint>();
462// m_attachmentsSent = new HashSet<uint>(); 478// m_attachmentsSent = new HashSet<uint>();
463 479
464 m_assetService = m_scene.RequestModuleInterface<IAssetService>(); 480 m_assetService = m_scene.RequestModuleInterface<IAssetService>();
@@ -488,12 +504,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
488 504
489 #region Client Methods 505 #region Client Methods
490 506
507
508 /// <summary>
509 /// Close down the client view
510 /// </summary>
491 public void Close() 511 public void Close()
492 { 512 {
493 Close(false); 513 Close(true, false);
494 } 514 }
495 515
496 public void Close(bool force) 516 public void Close(bool sendStop, bool force)
497 { 517 {
498 // We lock here to prevent race conditions between two threads calling close simultaneously (e.g. 518 // We lock here to prevent race conditions between two threads calling close simultaneously (e.g.
499 // a simultaneous relog just as a client is being closed out due to no packet ack from the old connection. 519 // a simultaneous relog just as a client is being closed out due to no packet ack from the old connection.
@@ -505,7 +525,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
505 return; 525 return;
506 526
507 IsActive = false; 527 IsActive = false;
508 CloseWithoutChecks(); 528 CloseWithoutChecks(sendStop);
509 } 529 }
510 } 530 }
511 531
@@ -518,12 +538,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
518 /// 538 ///
519 /// Callers must lock ClosingSyncLock before calling. 539 /// Callers must lock ClosingSyncLock before calling.
520 /// </remarks> 540 /// </remarks>
521 public void CloseWithoutChecks() 541 public void CloseWithoutChecks(bool sendStop)
522 { 542 {
523 m_log.DebugFormat( 543 m_log.DebugFormat(
524 "[CLIENT]: Close has been called for {0} attached to scene {1}", 544 "[CLIENT]: Close has been called for {0} attached to scene {1}",
525 Name, m_scene.RegionInfo.RegionName); 545 Name, m_scene.RegionInfo.RegionName);
526 546
547 if (sendStop)
548 {
549 // Send the STOP packet
550 DisableSimulatorPacket disable = (DisableSimulatorPacket)PacketPool.Instance.GetPacket(PacketType.DisableSimulator);
551 OutPacket(disable, ThrottleOutPacketType.Unknown);
552 }
553
527 // Shutdown the image manager 554 // Shutdown the image manager
528 ImageManager.Close(); 555 ImageManager.Close();
529 556
@@ -781,7 +808,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
781 handshake.RegionInfo3.ProductName = Util.StringToBytes256(regionInfo.RegionType); 808 handshake.RegionInfo3.ProductName = Util.StringToBytes256(regionInfo.RegionType);
782 handshake.RegionInfo3.ProductSKU = Utils.EmptyBytes; 809 handshake.RegionInfo3.ProductSKU = Utils.EmptyBytes;
783 810
784 OutPacket(handshake, ThrottleOutPacketType.Task); 811// OutPacket(handshake, ThrottleOutPacketType.Task);
812 // use same as MoveAgentIntoRegion (both should be task )
813 OutPacket(handshake, ThrottleOutPacketType.Unknown);
785 } 814 }
786 815
787 public void MoveAgentIntoRegion(RegionInfo regInfo, Vector3 pos, Vector3 look) 816 public void MoveAgentIntoRegion(RegionInfo regInfo, Vector3 pos, Vector3 look)
@@ -820,7 +849,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
820 reply.ChatData.OwnerID = fromAgentID; 849 reply.ChatData.OwnerID = fromAgentID;
821 reply.ChatData.SourceID = fromAgentID; 850 reply.ChatData.SourceID = fromAgentID;
822 851
823 OutPacket(reply, ThrottleOutPacketType.Task); 852 OutPacket(reply, ThrottleOutPacketType.Unknown);
824 } 853 }
825 854
826 /// <summary> 855 /// <summary>
@@ -1106,6 +1135,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1106 public virtual void SendLayerData(float[] map) 1135 public virtual void SendLayerData(float[] map)
1107 { 1136 {
1108 Util.FireAndForget(DoSendLayerData, map); 1137 Util.FireAndForget(DoSendLayerData, map);
1138
1139 // Send it sync, and async. It's not that much data
1140 // and it improves user experience just so much!
1141 DoSendLayerData(map);
1109 } 1142 }
1110 1143
1111 /// <summary> 1144 /// <summary>
@@ -1118,16 +1151,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1118 1151
1119 try 1152 try
1120 { 1153 {
1121 //for (int y = 0; y < 16; y++) 1154 for (int y = 0; y < 16; y++)
1122 //{ 1155 {
1123 // for (int x = 0; x < 16; x++) 1156 for (int x = 0; x < 16; x+=4)
1124 // { 1157 {
1125 // SendLayerData(x, y, map); 1158 SendLayerPacket(x, y, map);
1126 // } 1159 }
1127 //} 1160 }
1128
1129 // Send LayerData in a spiral pattern. Fun!
1130 SendLayerTopRight(map, 0, 0, 15, 15);
1131 } 1161 }
1132 catch (Exception e) 1162 catch (Exception e)
1133 { 1163 {
@@ -1135,51 +1165,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1135 } 1165 }
1136 } 1166 }
1137 1167
1138 private void SendLayerTopRight(float[] map, int x1, int y1, int x2, int y2)
1139 {
1140 // Row
1141 for (int i = x1; i <= x2; i++)
1142 SendLayerData(i, y1, map);
1143
1144 // Column
1145 for (int j = y1 + 1; j <= y2; j++)
1146 SendLayerData(x2, j, map);
1147
1148 if (x2 - x1 > 0)
1149 SendLayerBottomLeft(map, x1, y1 + 1, x2 - 1, y2);
1150 }
1151
1152 void SendLayerBottomLeft(float[] map, int x1, int y1, int x2, int y2)
1153 {
1154 // Row in reverse
1155 for (int i = x2; i >= x1; i--)
1156 SendLayerData(i, y2, map);
1157
1158 // Column in reverse
1159 for (int j = y2 - 1; j >= y1; j--)
1160 SendLayerData(x1, j, map);
1161
1162 if (x2 - x1 > 0)
1163 SendLayerTopRight(map, x1 + 1, y1, x2, y2 - 1);
1164 }
1165
1166 /// <summary> 1168 /// <summary>
1167 /// Sends a set of four patches (x, x+1, ..., x+3) to the client 1169 /// Sends a set of four patches (x, x+1, ..., x+3) to the client
1168 /// </summary> 1170 /// </summary>
1169 /// <param name="map">heightmap</param> 1171 /// <param name="map">heightmap</param>
1170 /// <param name="px">X coordinate for patches 0..12</param> 1172 /// <param name="px">X coordinate for patches 0..12</param>
1171 /// <param name="py">Y coordinate for patches 0..15</param> 1173 /// <param name="py">Y coordinate for patches 0..15</param>
1172 // private void SendLayerPacket(float[] map, int y, int x) 1174 private void SendLayerPacket(int x, int y, float[] map)
1173 // { 1175 {
1174 // int[] patches = new int[4]; 1176 int[] patches = new int[4];
1175 // patches[0] = x + 0 + y * 16; 1177 patches[0] = x + 0 + y * 16;
1176 // patches[1] = x + 1 + y * 16; 1178 patches[1] = x + 1 + y * 16;
1177 // patches[2] = x + 2 + y * 16; 1179 patches[2] = x + 2 + y * 16;
1178 // patches[3] = x + 3 + y * 16; 1180 patches[3] = x + 3 + y * 16;
1179 1181
1180 // Packet layerpack = LLClientView.TerrainManager.CreateLandPacket(map, patches); 1182 float[] heightmap = (map.Length == 65536) ?
1181 // OutPacket(layerpack, ThrottleOutPacketType.Land); 1183 map :
1182 // } 1184 LLHeightFieldMoronize(map);
1185
1186 try
1187 {
1188 Packet layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches);
1189 OutPacket(layerpack, ThrottleOutPacketType.Land);
1190 }
1191 catch
1192 {
1193 for (int px = x ; px < x + 4 ; px++)
1194 SendLayerData(px, y, map);
1195 }
1196 }
1183 1197
1184 /// <summary> 1198 /// <summary>
1185 /// Sends a specified patch to a client 1199 /// Sends a specified patch to a client
@@ -1199,7 +1213,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1199 LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches); 1213 LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches);
1200 layerpack.Header.Reliable = true; 1214 layerpack.Header.Reliable = true;
1201 1215
1202 OutPacket(layerpack, ThrottleOutPacketType.Land); 1216 OutPacket(layerpack, ThrottleOutPacketType.Task);
1203 } 1217 }
1204 catch (Exception e) 1218 catch (Exception e)
1205 { 1219 {
@@ -1562,7 +1576,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1562 1576
1563 public void SendKillObject(ulong regionHandle, List<uint> localIDs) 1577 public void SendKillObject(ulong regionHandle, List<uint> localIDs)
1564 { 1578 {
1565// m_log.DebugFormat("[CLIENT]: Sending KillObjectPacket to {0} for {1} in {2}", Name, localID, regionHandle); 1579// foreach (uint id in localIDs)
1580// m_log.DebugFormat("[CLIENT]: Sending KillObjectPacket to {0} for {1} in {2}", Name, id, regionHandle);
1566 1581
1567 KillObjectPacket kill = (KillObjectPacket)PacketPool.Instance.GetPacket(PacketType.KillObject); 1582 KillObjectPacket kill = (KillObjectPacket)PacketPool.Instance.GetPacket(PacketType.KillObject);
1568 // TODO: don't create new blocks if recycling an old packet 1583 // TODO: don't create new blocks if recycling an old packet
@@ -1584,17 +1599,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1584 // We MUST lock for both manipulating the kill record and sending the packet, in order to avoid a race 1599 // We MUST lock for both manipulating the kill record and sending the packet, in order to avoid a race
1585 // condition where a kill can be processed before an out-of-date update for the same object. 1600 // condition where a kill can be processed before an out-of-date update for the same object.
1586 // ProcessEntityUpdates() also takes the m_killRecord lock. 1601 // ProcessEntityUpdates() also takes the m_killRecord lock.
1587 lock (m_killRecord) 1602// lock (m_killRecord)
1588 { 1603// {
1589 foreach (uint localID in localIDs) 1604// foreach (uint localID in localIDs)
1590 m_killRecord.Add(localID); 1605// m_killRecord.Add(localID);
1591 1606
1592 // The throttle queue used here must match that being used for updates. Otherwise, there is a 1607 // The throttle queue used here must match that being used for updates. Otherwise, there is a
1593 // chance that a kill packet put on a separate queue will be sent to the client before an existing 1608 // chance that a kill packet put on a separate queue will be sent to the client before an existing
1594 // update packet on another queue. Receiving updates after kills results in unowned and undeletable 1609 // update packet on another queue. Receiving updates after kills results in unowned and undeletable
1595 // scene objects in a viewer until that viewer is relogged in. 1610 // scene objects in a viewer until that viewer is relogged in.
1596 OutPacket(kill, ThrottleOutPacketType.Task); 1611 OutPacket(kill, ThrottleOutPacketType.Task);
1597 } 1612// }
1598 } 1613 }
1599 } 1614 }
1600 1615
@@ -2052,9 +2067,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2052 OutPacket(bulkUpdate, ThrottleOutPacketType.Asset); 2067 OutPacket(bulkUpdate, ThrottleOutPacketType.Asset);
2053 } 2068 }
2054 2069
2055 /// <see>IClientAPI.SendInventoryItemCreateUpdate(InventoryItemBase)</see>
2056 public void SendInventoryItemCreateUpdate(InventoryItemBase Item, uint callbackId) 2070 public void SendInventoryItemCreateUpdate(InventoryItemBase Item, uint callbackId)
2057 { 2071 {
2072 SendInventoryItemCreateUpdate(Item, UUID.Zero, callbackId);
2073 }
2074
2075 /// <see>IClientAPI.SendInventoryItemCreateUpdate(InventoryItemBase)</see>
2076 public void SendInventoryItemCreateUpdate(InventoryItemBase Item, UUID transactionID, uint callbackId)
2077 {
2058 const uint FULL_MASK_PERMISSIONS = (uint)PermissionMask.All; 2078 const uint FULL_MASK_PERMISSIONS = (uint)PermissionMask.All;
2059 2079
2060 UpdateCreateInventoryItemPacket InventoryReply 2080 UpdateCreateInventoryItemPacket InventoryReply
@@ -2064,6 +2084,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2064 // TODO: don't create new blocks if recycling an old packet 2084 // TODO: don't create new blocks if recycling an old packet
2065 InventoryReply.AgentData.AgentID = AgentId; 2085 InventoryReply.AgentData.AgentID = AgentId;
2066 InventoryReply.AgentData.SimApproved = true; 2086 InventoryReply.AgentData.SimApproved = true;
2087 InventoryReply.AgentData.TransactionID = transactionID;
2067 InventoryReply.InventoryData = new UpdateCreateInventoryItemPacket.InventoryDataBlock[1]; 2088 InventoryReply.InventoryData = new UpdateCreateInventoryItemPacket.InventoryDataBlock[1];
2068 InventoryReply.InventoryData[0] = new UpdateCreateInventoryItemPacket.InventoryDataBlock(); 2089 InventoryReply.InventoryData[0] = new UpdateCreateInventoryItemPacket.InventoryDataBlock();
2069 InventoryReply.InventoryData[0].ItemID = Item.ID; 2090 InventoryReply.InventoryData[0].ItemID = Item.ID;
@@ -2133,16 +2154,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2133 replytask.InventoryData.TaskID = taskID; 2154 replytask.InventoryData.TaskID = taskID;
2134 replytask.InventoryData.Serial = serial; 2155 replytask.InventoryData.Serial = serial;
2135 replytask.InventoryData.Filename = fileName; 2156 replytask.InventoryData.Filename = fileName;
2136 OutPacket(replytask, ThrottleOutPacketType.Asset); 2157 OutPacket(replytask, ThrottleOutPacketType.Task);
2137 } 2158 }
2138 2159
2139 public void SendXferPacket(ulong xferID, uint packet, byte[] data) 2160 public void SendXferPacket(ulong xferID, uint packet, byte[] data, bool isTaskInventory)
2140 { 2161 {
2162 ThrottleOutPacketType type = ThrottleOutPacketType.Asset;
2163 if (isTaskInventory)
2164 type = ThrottleOutPacketType.Task;
2165
2141 SendXferPacketPacket sendXfer = (SendXferPacketPacket)PacketPool.Instance.GetPacket(PacketType.SendXferPacket); 2166 SendXferPacketPacket sendXfer = (SendXferPacketPacket)PacketPool.Instance.GetPacket(PacketType.SendXferPacket);
2142 sendXfer.XferID.ID = xferID; 2167 sendXfer.XferID.ID = xferID;
2143 sendXfer.XferID.Packet = packet; 2168 sendXfer.XferID.Packet = packet;
2144 sendXfer.DataPacket.Data = data; 2169 sendXfer.DataPacket.Data = data;
2145 OutPacket(sendXfer, ThrottleOutPacketType.Asset); 2170 OutPacket(sendXfer, type);
2146 } 2171 }
2147 2172
2148 public void SendAbortXferPacket(ulong xferID) 2173 public void SendAbortXferPacket(ulong xferID)
@@ -2324,6 +2349,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2324 OutPacket(sound, ThrottleOutPacketType.Task); 2349 OutPacket(sound, ThrottleOutPacketType.Task);
2325 } 2350 }
2326 2351
2352 public void SendTransferAbort(TransferRequestPacket transferRequest)
2353 {
2354 TransferAbortPacket abort = (TransferAbortPacket)PacketPool.Instance.GetPacket(PacketType.TransferAbort);
2355 abort.TransferInfo.TransferID = transferRequest.TransferInfo.TransferID;
2356 abort.TransferInfo.ChannelType = transferRequest.TransferInfo.ChannelType;
2357 m_log.Debug("[Assets] Aborting transfer; asset request failed");
2358 OutPacket(abort, ThrottleOutPacketType.Task);
2359 }
2360
2327 public void SendTriggeredSound(UUID soundID, UUID ownerID, UUID objectID, UUID parentID, ulong handle, Vector3 position, float gain) 2361 public void SendTriggeredSound(UUID soundID, UUID ownerID, UUID objectID, UUID parentID, ulong handle, Vector3 position, float gain)
2328 { 2362 {
2329 SoundTriggerPacket sound = (SoundTriggerPacket)PacketPool.Instance.GetPacket(PacketType.SoundTrigger); 2363 SoundTriggerPacket sound = (SoundTriggerPacket)PacketPool.Instance.GetPacket(PacketType.SoundTrigger);
@@ -2616,6 +2650,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2616 } 2650 }
2617 } 2651 }
2618 2652
2653 public void SendPartPhysicsProprieties(ISceneEntity entity)
2654 {
2655 SceneObjectPart part = (SceneObjectPart)entity;
2656 if (part != null && AgentId != UUID.Zero)
2657 {
2658 try
2659 {
2660 IEventQueue eq = Scene.RequestModuleInterface<IEventQueue>();
2661 if (eq != null)
2662 {
2663 uint localid = part.LocalId;
2664 byte physshapetype = part.PhysicsShapeType;
2665 float density = part.Density;
2666 float friction = part.Friction;
2667 float bounce = part.Bounciness;
2668 float gravmod = part.GravityModifier;
2669
2670 eq.partPhysicsProperties(localid, physshapetype, density, friction, bounce, gravmod,AgentId);
2671 }
2672 }
2673 catch (Exception ex)
2674 {
2675 m_log.Error("Unable to send part Physics Proprieties - exception: " + ex.ToString());
2676 }
2677 part.UpdatePhysRequired = false;
2678 }
2679 }
2680
2681
2619 2682
2620 public void SendGroupNameReply(UUID groupLLUID, string GroupName) 2683 public void SendGroupNameReply(UUID groupLLUID, string GroupName)
2621 { 2684 {
@@ -2713,7 +2776,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2713 else 2776 else
2714 { 2777 {
2715 int processedLength = 0; 2778 int processedLength = 0;
2716 int maxChunkSize = Settings.MAX_PACKET_SIZE - 100; 2779// int maxChunkSize = Settings.MAX_PACKET_SIZE - 100;
2780
2781 int maxChunkSize = (int) MaxTransferBytesPerPacket;
2717 int packetNumber = 0; 2782 int packetNumber = 0;
2718 2783
2719 while (processedLength < req.AssetInf.Data.Length) 2784 while (processedLength < req.AssetInf.Data.Length)
@@ -2784,7 +2849,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2784 reply.Data.ParcelID = parcelID; 2849 reply.Data.ParcelID = parcelID;
2785 reply.Data.OwnerID = land.OwnerID; 2850 reply.Data.OwnerID = land.OwnerID;
2786 reply.Data.Name = Utils.StringToBytes(land.Name); 2851 reply.Data.Name = Utils.StringToBytes(land.Name);
2787 reply.Data.Desc = Utils.StringToBytes(land.Description); 2852 if (land != null && land.Description != null && land.Description != String.Empty)
2853 reply.Data.Desc = Utils.StringToBytes(land.Description.Substring(0, land.Description.Length > 254 ? 254: land.Description.Length));
2854 else
2855 reply.Data.Desc = new Byte[0];
2788 reply.Data.ActualArea = land.Area; 2856 reply.Data.ActualArea = land.Area;
2789 reply.Data.BillableArea = land.Area; // TODO: what is this? 2857 reply.Data.BillableArea = land.Area; // TODO: what is this?
2790 2858
@@ -3519,7 +3587,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3519 3587
3520 AvatarAppearancePacket avp = (AvatarAppearancePacket)PacketPool.Instance.GetPacket(PacketType.AvatarAppearance); 3588 AvatarAppearancePacket avp = (AvatarAppearancePacket)PacketPool.Instance.GetPacket(PacketType.AvatarAppearance);
3521 // TODO: don't create new blocks if recycling an old packet 3589 // TODO: don't create new blocks if recycling an old packet
3522 avp.VisualParam = new AvatarAppearancePacket.VisualParamBlock[218]; 3590 avp.VisualParam = new AvatarAppearancePacket.VisualParamBlock[visualParams.Length];
3523 avp.ObjectData.TextureEntry = textureEntry; 3591 avp.ObjectData.TextureEntry = textureEntry;
3524 3592
3525 AvatarAppearancePacket.VisualParamBlock avblock = null; 3593 AvatarAppearancePacket.VisualParamBlock avblock = null;
@@ -3649,7 +3717,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3649 /// </summary> 3717 /// </summary>
3650 public void SendEntityUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) 3718 public void SendEntityUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags)
3651 { 3719 {
3652 //double priority = m_prioritizer.GetUpdatePriority(this, entity); 3720 if (entity is SceneObjectPart)
3721 {
3722 SceneObjectPart e = (SceneObjectPart)entity;
3723 SceneObjectGroup g = e.ParentGroup;
3724 if (g.RootPart.Shape.State > 30) // HUD
3725 if (g.OwnerID != AgentId)
3726 return; // Don't send updates for other people's HUDs
3727 }
3728
3653 uint priority = m_prioritizer.GetUpdatePriority(this, entity); 3729 uint priority = m_prioritizer.GetUpdatePriority(this, entity);
3654 3730
3655 lock (m_entityUpdates.SyncRoot) 3731 lock (m_entityUpdates.SyncRoot)
@@ -3716,27 +3792,74 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3716 3792
3717 // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race 3793 // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race
3718 // condition where a kill can be processed before an out-of-date update for the same object. 3794 // condition where a kill can be processed before an out-of-date update for the same object.
3719 lock (m_killRecord) 3795 float avgTimeDilation = 1.0f;
3796 IEntityUpdate iupdate;
3797 Int32 timeinqueue; // this is just debugging code & can be dropped later
3798
3799 while (updatesThisCall < maxUpdates)
3720 { 3800 {
3721 float avgTimeDilation = 1.0f; 3801 lock (m_entityUpdates.SyncRoot)
3722 IEntityUpdate iupdate; 3802 if (!m_entityUpdates.TryDequeue(out iupdate, out timeinqueue))
3723 Int32 timeinqueue; // this is just debugging code & can be dropped later 3803 break;
3804
3805 EntityUpdate update = (EntityUpdate)iupdate;
3806
3807 avgTimeDilation += update.TimeDilation;
3808 avgTimeDilation *= 0.5f;
3724 3809
3725 while (updatesThisCall < maxUpdates) 3810 if (update.Entity is SceneObjectPart)
3726 { 3811 {
3727 lock (m_entityUpdates.SyncRoot) 3812 SceneObjectPart part = (SceneObjectPart)update.Entity;
3728 if (!m_entityUpdates.TryDequeue(out iupdate, out timeinqueue))
3729 break;
3730 3813
3731 EntityUpdate update = (EntityUpdate)iupdate; 3814 if (part.ParentGroup.IsDeleted)
3732 3815 continue;
3733 avgTimeDilation += update.TimeDilation;
3734 avgTimeDilation *= 0.5f;
3735 3816
3736 if (update.Entity is SceneObjectPart) 3817 if (part.ParentGroup.IsAttachment)
3818 { // Someone else's HUD, why are we getting these?
3819 if (part.ParentGroup.OwnerID != AgentId &&
3820 part.ParentGroup.RootPart.Shape.State > 30)
3821 continue;
3822 ScenePresence sp;
3823 // Owner is not in the sim, don't update it to
3824 // anyone
3825 if (!m_scene.TryGetScenePresence(part.OwnerID, out sp))
3826 continue;
3827
3828 List<SceneObjectGroup> atts = sp.GetAttachments();
3829 bool found = false;
3830 foreach (SceneObjectGroup att in atts)
3831 {
3832 if (att == part.ParentGroup)
3833 {
3834 found = true;
3835 break;
3836 }
3837 }
3838
3839 // It's an attachment of a valid avatar, but
3840 // doesn't seem to be attached, skip
3841 if (!found)
3842 continue;
3843
3844 // On vehicle crossing, the attachments are received
3845 // while the avatar is still a child. Don't send
3846 // updates here because the LocalId has not yet
3847 // been updated and the viewer will derender the
3848 // attachments until the avatar becomes root.
3849 if (sp.IsChildAgent)
3850 continue;
3851
3852 // If the object is an attachment we don't want it to be in the kill
3853 // record. Else attaching from inworld and subsequently dropping
3854 // it will no longer work.
3855// lock (m_killRecord)
3856// {
3857// m_killRecord.Remove(part.LocalId);
3858// m_killRecord.Remove(part.ParentGroup.RootPart.LocalId);
3859// }
3860 }
3861 else
3737 { 3862 {
3738 SceneObjectPart part = (SceneObjectPart)update.Entity;
3739
3740 // Please do not remove this unless you can demonstrate on the OpenSim mailing list that a client 3863 // Please do not remove this unless you can demonstrate on the OpenSim mailing list that a client
3741 // will never receive an update after a prim kill. Even then, keeping the kill record may be a good 3864 // will never receive an update after a prim kill. Even then, keeping the kill record may be a good
3742 // safety measure. 3865 // safety measure.
@@ -3747,241 +3870,177 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3747 // 3870 //
3748 // This doesn't appear to apply to child prims - a client will happily ignore these updates 3871 // This doesn't appear to apply to child prims - a client will happily ignore these updates
3749 // after the root prim has been deleted. 3872 // after the root prim has been deleted.
3750 if (m_killRecord.Contains(part.LocalId)) 3873 //
3751 { 3874 // We ignore this for attachments because attaching something from inworld breaks unless we do.
3752 // m_log.WarnFormat( 3875// lock (m_killRecord)
3753 // "[CLIENT]: Preventing update for prim with local id {0} after client for user {1} told it was deleted", 3876// {
3754 // part.LocalId, Name); 3877// if (m_killRecord.Contains(part.LocalId))
3755 continue; 3878// continue;
3756 } 3879// if (m_killRecord.Contains(part.ParentGroup.RootPart.LocalId))
3757 3880// continue;
3758 if (part.ParentGroup.IsAttachment && m_disableFacelights) 3881// }
3882 }
3883
3884 if (part.ParentGroup.IsAttachment && m_disableFacelights)
3885 {
3886 if (part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.LeftHand &&
3887 part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.RightHand)
3759 { 3888 {
3760 if (part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.LeftHand && 3889 part.Shape.LightEntry = false;
3761 part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.RightHand)
3762 {
3763 part.Shape.LightEntry = false;
3764 }
3765 } 3890 }
3766 } 3891 }
3767 3892 }
3768 #region UpdateFlags to packet type conversion 3893
3769 3894 ++updatesThisCall;
3770 PrimUpdateFlags updateFlags = (PrimUpdateFlags)update.Flags; 3895
3771 3896 #region UpdateFlags to packet type conversion
3772 bool canUseCompressed = true; 3897
3773 bool canUseImproved = true; 3898 PrimUpdateFlags updateFlags = (PrimUpdateFlags)update.Flags;
3774 3899
3775 // Compressed object updates only make sense for LL primitives 3900 bool canUseCompressed = true;
3776 if (!(update.Entity is SceneObjectPart)) 3901 bool canUseImproved = true;
3902
3903 // Compressed object updates only make sense for LL primitives
3904 if (!(update.Entity is SceneObjectPart))
3905 {
3906 canUseCompressed = false;
3907 }
3908
3909 if (updateFlags.HasFlag(PrimUpdateFlags.FullUpdate))
3910 {
3911 canUseCompressed = false;
3912 canUseImproved = false;
3913 }
3914 else
3915 {
3916 if (updateFlags.HasFlag(PrimUpdateFlags.Velocity) ||
3917 updateFlags.HasFlag(PrimUpdateFlags.Acceleration) ||
3918 updateFlags.HasFlag(PrimUpdateFlags.CollisionPlane) ||
3919 updateFlags.HasFlag(PrimUpdateFlags.Joint))
3777 { 3920 {
3778 canUseCompressed = false; 3921 canUseCompressed = false;
3779 } 3922 }
3780 3923
3781 if (updateFlags.HasFlag(PrimUpdateFlags.FullUpdate)) 3924 if (updateFlags.HasFlag(PrimUpdateFlags.PrimFlags) ||
3925 updateFlags.HasFlag(PrimUpdateFlags.ParentID) ||
3926 updateFlags.HasFlag(PrimUpdateFlags.Scale) ||
3927 updateFlags.HasFlag(PrimUpdateFlags.PrimData) ||
3928 updateFlags.HasFlag(PrimUpdateFlags.Text) ||
3929 updateFlags.HasFlag(PrimUpdateFlags.NameValue) ||
3930 updateFlags.HasFlag(PrimUpdateFlags.ExtraData) ||
3931 updateFlags.HasFlag(PrimUpdateFlags.TextureAnim) ||
3932 updateFlags.HasFlag(PrimUpdateFlags.Sound) ||
3933 updateFlags.HasFlag(PrimUpdateFlags.Particles) ||
3934 updateFlags.HasFlag(PrimUpdateFlags.Material) ||
3935 updateFlags.HasFlag(PrimUpdateFlags.ClickAction) ||
3936 updateFlags.HasFlag(PrimUpdateFlags.MediaURL) ||
3937 updateFlags.HasFlag(PrimUpdateFlags.Joint))
3782 { 3938 {
3783 canUseCompressed = false;
3784 canUseImproved = false; 3939 canUseImproved = false;
3785 } 3940 }
3786 else 3941 }
3787 {
3788 if (updateFlags.HasFlag(PrimUpdateFlags.Velocity) ||
3789 updateFlags.HasFlag(PrimUpdateFlags.Acceleration) ||
3790 updateFlags.HasFlag(PrimUpdateFlags.CollisionPlane) ||
3791 updateFlags.HasFlag(PrimUpdateFlags.Joint))
3792 {
3793 canUseCompressed = false;
3794 }
3795
3796 if (updateFlags.HasFlag(PrimUpdateFlags.PrimFlags) ||
3797 updateFlags.HasFlag(PrimUpdateFlags.ParentID) ||
3798 updateFlags.HasFlag(PrimUpdateFlags.Scale) ||
3799 updateFlags.HasFlag(PrimUpdateFlags.PrimData) ||
3800 updateFlags.HasFlag(PrimUpdateFlags.Text) ||
3801 updateFlags.HasFlag(PrimUpdateFlags.NameValue) ||
3802 updateFlags.HasFlag(PrimUpdateFlags.ExtraData) ||
3803 updateFlags.HasFlag(PrimUpdateFlags.TextureAnim) ||
3804 updateFlags.HasFlag(PrimUpdateFlags.Sound) ||
3805 updateFlags.HasFlag(PrimUpdateFlags.Particles) ||
3806 updateFlags.HasFlag(PrimUpdateFlags.Material) ||
3807 updateFlags.HasFlag(PrimUpdateFlags.ClickAction) ||
3808 updateFlags.HasFlag(PrimUpdateFlags.MediaURL) ||
3809 updateFlags.HasFlag(PrimUpdateFlags.Joint))
3810 {
3811 canUseImproved = false;
3812 }
3813 }
3814
3815 #endregion UpdateFlags to packet type conversion
3816
3817 #region Block Construction
3818
3819 // TODO: Remove this once we can build compressed updates
3820 canUseCompressed = false;
3821 3942
3822 if (!canUseImproved && !canUseCompressed) 3943 #endregion UpdateFlags to packet type conversion
3823 {
3824 ObjectUpdatePacket.ObjectDataBlock updateBlock;
3825 3944
3826 if (update.Entity is ScenePresence) 3945 #region Block Construction
3827 {
3828 updateBlock = CreateAvatarUpdateBlock((ScenePresence)update.Entity);
3829 }
3830 else
3831 {
3832 SceneObjectPart part = (SceneObjectPart)update.Entity;
3833 updateBlock = CreatePrimUpdateBlock(part, AgentId);
3834
3835 // If the part has become a private hud since the update was scheduled then we do not
3836 // want to send it to other avatars.
3837 if (part.ParentGroup.IsAttachment
3838 && part.ParentGroup.HasPrivateAttachmentPoint
3839 && part.ParentGroup.AttachedAvatar != AgentId)
3840 continue;
3841
3842 // If the part has since been deleted, then drop the update. In the case of attachments,
3843 // this is to avoid spurious updates to other viewers since post-processing of attachments
3844 // has to change the IsAttachment flag for various reasons (which will end up in a pass
3845 // of the test above).
3846 //
3847 // Actual deletions (kills) happen in another method.
3848 if (part.ParentGroup.IsDeleted)
3849 continue;
3850 }
3851 3946
3852 objectUpdateBlocks.Value.Add(updateBlock); 3947 // TODO: Remove this once we can build compressed updates
3853 objectUpdates.Value.Add(update); 3948 canUseCompressed = false;
3854 }
3855 else if (!canUseImproved)
3856 {
3857 SceneObjectPart part = (SceneObjectPart)update.Entity;
3858 ObjectUpdateCompressedPacket.ObjectDataBlock compressedBlock
3859 = CreateCompressedUpdateBlock(part, updateFlags);
3860
3861 // If the part has since been deleted, then drop the update. In the case of attachments,
3862 // this is to avoid spurious updates to other viewers since post-processing of attachments
3863 // has to change the IsAttachment flag for various reasons (which will end up in a pass
3864 // of the test above).
3865 //
3866 // Actual deletions (kills) happen in another method.
3867 if (part.ParentGroup.IsDeleted)
3868 continue;
3869 3949
3870 compressedUpdateBlocks.Value.Add(compressedBlock); 3950 if (!canUseImproved && !canUseCompressed)
3871 compressedUpdates.Value.Add(update); 3951 {
3952 if (update.Entity is ScenePresence)
3953 {
3954 objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity));
3872 } 3955 }
3873 else 3956 else
3874 { 3957 {
3875 if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId) 3958 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId));
3876 {
3877 // Self updates go into a special list
3878 terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3879 terseAgentUpdates.Value.Add(update);
3880 }
3881 else
3882 {
3883 ImprovedTerseObjectUpdatePacket.ObjectDataBlock terseUpdateBlock
3884 = CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures));
3885
3886 // Everything else goes here
3887 if (update.Entity is SceneObjectPart)
3888 {
3889 SceneObjectPart part = (SceneObjectPart)update.Entity;
3890
3891 // If the part has become a private hud since the update was scheduled then we do not
3892 // want to send it to other avatars.
3893 if (part.ParentGroup.IsAttachment
3894 && part.ParentGroup.HasPrivateAttachmentPoint
3895 && part.ParentGroup.AttachedAvatar != AgentId)
3896 continue;
3897
3898 // If the part has since been deleted, then drop the update. In the case of attachments,
3899 // this is to avoid spurious updates to other viewers since post-processing of attachments
3900 // has to change the IsAttachment flag for various reasons (which will end up in a pass
3901 // of the test above).
3902 //
3903 // Actual deletions (kills) happen in another method.
3904 if (part.ParentGroup.IsDeleted)
3905 continue;
3906 }
3907
3908 terseUpdateBlocks.Value.Add(terseUpdateBlock);
3909 terseUpdates.Value.Add(update);
3910 }
3911 } 3959 }
3912
3913 ++updatesThisCall;
3914
3915 #endregion Block Construction
3916 } 3960 }
3917 3961 else if (!canUseImproved)
3918 #region Packet Sending 3962 {
3919 ushort timeDilation = Utils.FloatToUInt16(avgTimeDilation, 0.0f, 1.0f); 3963 compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags));
3920 3964 }
3921 if (terseAgentUpdateBlocks.IsValueCreated) 3965 else
3922 { 3966 {
3923 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value; 3967 if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId)
3968 // Self updates go into a special list
3969 terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3970 else
3971 // Everything else goes here
3972 terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3973 }
3924 3974
3925 ImprovedTerseObjectUpdatePacket packet 3975 #endregion Block Construction
3926 = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); 3976 }
3927 3977
3928 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; 3978 #region Packet Sending
3929 packet.RegionData.TimeDilation = timeDilation; 3979
3930 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; 3980 const float TIME_DILATION = 1.0f;
3981 ushort timeDilation = Utils.FloatToUInt16(avgTimeDilation, 0.0f, 1.0f);
3982
3983 if (terseAgentUpdateBlocks.IsValueCreated)
3984 {
3985 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value;
3931 3986
3932 for (int i = 0; i < blocks.Count; i++) 3987 ImprovedTerseObjectUpdatePacket packet
3933 packet.ObjectData[i] = blocks[i]; 3988 = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate);
3934 // If any of the packets created from this call go unacknowledged, all of the updates will be resent 3989 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3935 OutPacket(packet, ThrottleOutPacketType.Unknown, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseAgentUpdates.Value, oPacket); }); 3990 packet.RegionData.TimeDilation = timeDilation;
3936 } 3991 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
3937 3992
3938 if (objectUpdateBlocks.IsValueCreated) 3993 for (int i = 0; i < blocks.Count; i++)
3939 { 3994 packet.ObjectData[i] = blocks[i];
3940 List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value;
3941
3942 ObjectUpdatePacket packet = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate);
3943 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3944 packet.RegionData.TimeDilation = timeDilation;
3945 packet.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[blocks.Count];
3946
3947 for (int i = 0; i < blocks.Count; i++)
3948 packet.ObjectData[i] = blocks[i];
3949 // If any of the packets created from this call go unacknowledged, all of the updates will be resent
3950 OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(objectUpdates.Value, oPacket); });
3951 }
3952
3953 if (compressedUpdateBlocks.IsValueCreated)
3954 {
3955 List<ObjectUpdateCompressedPacket.ObjectDataBlock> blocks = compressedUpdateBlocks.Value;
3956
3957 ObjectUpdateCompressedPacket packet = (ObjectUpdateCompressedPacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdateCompressed);
3958 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3959 packet.RegionData.TimeDilation = timeDilation;
3960 packet.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[blocks.Count];
3961
3962 for (int i = 0; i < blocks.Count; i++)
3963 packet.ObjectData[i] = blocks[i];
3964 // If any of the packets created from this call go unacknowledged, all of the updates will be resent
3965 OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(compressedUpdates.Value, oPacket); });
3966 }
3967 3995
3968 if (terseUpdateBlocks.IsValueCreated) 3996 OutPacket(packet, ThrottleOutPacketType.Unknown, true);
3969 { 3997 }
3970 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value;
3971
3972 ImprovedTerseObjectUpdatePacket packet
3973 = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(
3974 PacketType.ImprovedTerseObjectUpdate);
3975 3998
3976 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; 3999 if (objectUpdateBlocks.IsValueCreated)
3977 packet.RegionData.TimeDilation = timeDilation; 4000 {
3978 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; 4001 List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value;
3979 4002
3980 for (int i = 0; i < blocks.Count; i++) 4003 ObjectUpdatePacket packet = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate);
3981 packet.ObjectData[i] = blocks[i]; 4004 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3982 // If any of the packets created from this call go unacknowledged, all of the updates will be resent 4005 packet.RegionData.TimeDilation = timeDilation;
3983 OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseUpdates.Value, oPacket); }); 4006 packet.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[blocks.Count];
3984 } 4007
4008 for (int i = 0; i < blocks.Count; i++)
4009 packet.ObjectData[i] = blocks[i];
4010
4011 OutPacket(packet, ThrottleOutPacketType.Task, true);
4012 }
4013
4014 if (compressedUpdateBlocks.IsValueCreated)
4015 {
4016 List<ObjectUpdateCompressedPacket.ObjectDataBlock> blocks = compressedUpdateBlocks.Value;
4017
4018 ObjectUpdateCompressedPacket packet = (ObjectUpdateCompressedPacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdateCompressed);
4019 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
4020 packet.RegionData.TimeDilation = timeDilation;
4021 packet.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[blocks.Count];
4022
4023 for (int i = 0; i < blocks.Count; i++)
4024 packet.ObjectData[i] = blocks[i];
4025
4026 OutPacket(packet, ThrottleOutPacketType.Task, true);
4027 }
4028
4029 if (terseUpdateBlocks.IsValueCreated)
4030 {
4031 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value;
4032
4033 ImprovedTerseObjectUpdatePacket packet
4034 = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(
4035 PacketType.ImprovedTerseObjectUpdate);
4036 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
4037 packet.RegionData.TimeDilation = timeDilation;
4038 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
4039
4040 for (int i = 0; i < blocks.Count; i++)
4041 packet.ObjectData[i] = blocks[i];
4042
4043 OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseUpdates.Value, oPacket); });
3985 } 4044 }
3986 4045
3987 #endregion Packet Sending 4046 #endregion Packet Sending
@@ -4274,11 +4333,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4274 4333
4275 // Pass in the delegate so that if this packet needs to be resent, we send the current properties 4334 // Pass in the delegate so that if this packet needs to be resent, we send the current properties
4276 // of the object rather than the properties when the packet was created 4335 // of the object rather than the properties when the packet was created
4277 OutPacket(packet, ThrottleOutPacketType.Task, true, 4336 // HACK : Remove intelligent resending until it's fixed in core
4278 delegate(OutgoingPacket oPacket) 4337 //OutPacket(packet, ThrottleOutPacketType.Task, true,
4279 { 4338 // delegate(OutgoingPacket oPacket)
4280 ResendPropertyUpdates(updates, oPacket); 4339 // {
4281 }); 4340 // ResendPropertyUpdates(updates, oPacket);
4341 // });
4342 OutPacket(packet, ThrottleOutPacketType.Task, true);
4282 4343
4283 // pbcnt += blocks.Count; 4344 // pbcnt += blocks.Count;
4284 // ppcnt++; 4345 // ppcnt++;
@@ -4304,11 +4365,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4304 // of the object rather than the properties when the packet was created 4365 // of the object rather than the properties when the packet was created
4305 List<ObjectPropertyUpdate> updates = new List<ObjectPropertyUpdate>(); 4366 List<ObjectPropertyUpdate> updates = new List<ObjectPropertyUpdate>();
4306 updates.Add(familyUpdates.Value[i]); 4367 updates.Add(familyUpdates.Value[i]);
4307 OutPacket(packet, ThrottleOutPacketType.Task, true, 4368 // HACK : Remove intelligent resending until it's fixed in core
4308 delegate(OutgoingPacket oPacket) 4369 //OutPacket(packet, ThrottleOutPacketType.Task, true,
4309 { 4370 // delegate(OutgoingPacket oPacket)
4310 ResendPropertyUpdates(updates, oPacket); 4371 // {
4311 }); 4372 // ResendPropertyUpdates(updates, oPacket);
4373 // });
4374 OutPacket(packet, ThrottleOutPacketType.Task, true);
4312 4375
4313 // fpcnt++; 4376 // fpcnt++;
4314 // fbcnt++; 4377 // fbcnt++;
@@ -4680,7 +4743,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4680 4743
4681 if (landData.SimwideArea > 0) 4744 if (landData.SimwideArea > 0)
4682 { 4745 {
4683 int simulatorCapacity = (int)(((float)landData.SimwideArea / 65536.0f) * (float)m_scene.RegionInfo.ObjectCapacity * (float)m_scene.RegionInfo.RegionSettings.ObjectBonus); 4746 int simulatorCapacity = (int)((long)landData.SimwideArea * (long)m_scene.RegionInfo.ObjectCapacity * (long)m_scene.RegionInfo.RegionSettings.ObjectBonus / 65536L);
4747 // Never report more than sim total capacity
4748 if (simulatorCapacity > m_scene.RegionInfo.ObjectCapacity)
4749 simulatorCapacity = m_scene.RegionInfo.ObjectCapacity;
4684 updateMessage.SimWideMaxPrims = simulatorCapacity; 4750 updateMessage.SimWideMaxPrims = simulatorCapacity;
4685 } 4751 }
4686 else 4752 else
@@ -4809,14 +4875,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4809 4875
4810 if (notifyCount > 0) 4876 if (notifyCount > 0)
4811 { 4877 {
4812 if (notifyCount > 32) 4878// if (notifyCount > 32)
4813 { 4879// {
4814 m_log.InfoFormat( 4880// m_log.InfoFormat(
4815 "[LAND]: More than {0} avatars own prims on this parcel. Only sending back details of first {0}" 4881// "[LAND]: More than {0} avatars own prims on this parcel. Only sending back details of first {0}"
4816 + " - a developer might want to investigate whether this is a hard limit", 32); 4882// + " - a developer might want to investigate whether this is a hard limit", 32);
4817 4883//
4818 notifyCount = 32; 4884// notifyCount = 32;
4819 } 4885// }
4820 4886
4821 ParcelObjectOwnersReplyPacket.DataBlock[] dataBlock 4887 ParcelObjectOwnersReplyPacket.DataBlock[] dataBlock
4822 = new ParcelObjectOwnersReplyPacket.DataBlock[notifyCount]; 4888 = new ParcelObjectOwnersReplyPacket.DataBlock[notifyCount];
@@ -4871,9 +4937,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4871 { 4937 {
4872 ScenePresence presence = (ScenePresence)entity; 4938 ScenePresence presence = (ScenePresence)entity;
4873 4939
4940 position = presence.OffsetPosition;
4941 rotation = presence.Rotation;
4942
4943 if (presence.ParentID != 0)
4944 {
4945 SceneObjectPart part = m_scene.GetSceneObjectPart(presence.ParentID);
4946 if (part != null && part != part.ParentGroup.RootPart)
4947 {
4948 position = part.OffsetPosition + presence.OffsetPosition * part.RotationOffset;
4949 rotation = part.RotationOffset * presence.Rotation;
4950 }
4951 }
4952
4874 attachPoint = 0; 4953 attachPoint = 0;
4875 collisionPlane = presence.CollisionPlane; 4954 collisionPlane = presence.CollisionPlane;
4876 position = presence.OffsetPosition;
4877 velocity = presence.Velocity; 4955 velocity = presence.Velocity;
4878 acceleration = Vector3.Zero; 4956 acceleration = Vector3.Zero;
4879 4957
@@ -4883,7 +4961,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4883// acceleration = new Vector3(1, 0, 0); 4961// acceleration = new Vector3(1, 0, 0);
4884 4962
4885 angularVelocity = Vector3.Zero; 4963 angularVelocity = Vector3.Zero;
4886 rotation = presence.Rotation;
4887 4964
4888 if (sendTexture) 4965 if (sendTexture)
4889 textureEntry = presence.Appearance.Texture.GetBytes(); 4966 textureEntry = presence.Appearance.Texture.GetBytes();
@@ -4990,13 +5067,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4990 5067
4991 protected ObjectUpdatePacket.ObjectDataBlock CreateAvatarUpdateBlock(ScenePresence data) 5068 protected ObjectUpdatePacket.ObjectDataBlock CreateAvatarUpdateBlock(ScenePresence data)
4992 { 5069 {
5070 Vector3 offsetPosition = data.OffsetPosition;
5071 Quaternion rotation = data.Rotation;
5072 uint parentID = data.ParentID;
5073
5074 if (parentID != 0)
5075 {
5076 SceneObjectPart part = m_scene.GetSceneObjectPart(parentID);
5077 if (part != null && part != part.ParentGroup.RootPart)
5078 {
5079 offsetPosition = part.OffsetPosition + data.OffsetPosition * part.RotationOffset;
5080 rotation = part.RotationOffset * data.Rotation;
5081 parentID = part.ParentGroup.RootPart.LocalId;
5082 }
5083 }
5084
4993 byte[] objectData = new byte[76]; 5085 byte[] objectData = new byte[76];
4994 5086
4995 data.CollisionPlane.ToBytes(objectData, 0); 5087 data.CollisionPlane.ToBytes(objectData, 0);
4996 data.OffsetPosition.ToBytes(objectData, 16); 5088 offsetPosition.ToBytes(objectData, 16);
4997// data.Velocity.ToBytes(objectData, 28); 5089// data.Velocity.ToBytes(objectData, 28);
4998// data.Acceleration.ToBytes(objectData, 40); 5090// data.Acceleration.ToBytes(objectData, 40);
4999 data.Rotation.ToBytes(objectData, 52); 5091 rotation.ToBytes(objectData, 52);
5000 //data.AngularVelocity.ToBytes(objectData, 64); 5092 //data.AngularVelocity.ToBytes(objectData, 64);
5001 5093
5002 ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); 5094 ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock();
@@ -5010,7 +5102,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5010 update.NameValue = Utils.StringToBytes("FirstName STRING RW SV " + data.Firstname + "\nLastName STRING RW SV " + 5102 update.NameValue = Utils.StringToBytes("FirstName STRING RW SV " + data.Firstname + "\nLastName STRING RW SV " +
5011 data.Lastname + "\nTitle STRING RW SV " + data.Grouptitle); 5103 data.Lastname + "\nTitle STRING RW SV " + data.Grouptitle);
5012 update.ObjectData = objectData; 5104 update.ObjectData = objectData;
5013 update.ParentID = data.ParentID; 5105 update.ParentID = parentID;
5014 update.PathCurve = 16; 5106 update.PathCurve = 16;
5015 update.PathScaleX = 100; 5107 update.PathScaleX = 100;
5016 update.PathScaleY = 100; 5108 update.PathScaleY = 100;
@@ -5028,10 +5120,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5028 update.TextureEntry = Utils.EmptyBytes; 5120 update.TextureEntry = Utils.EmptyBytes;
5029// update.TextureEntry = (data.Appearance.Texture != null) ? data.Appearance.Texture.GetBytes() : Utils.EmptyBytes; 5121// update.TextureEntry = (data.Appearance.Texture != null) ? data.Appearance.Texture.GetBytes() : Utils.EmptyBytes;
5030 5122
5123/* all this flags seem related to prims and not avatars. This allow for wrong viewer side move of a avatar in prim edition mode (anv mantis 854)
5031 update.UpdateFlags = (uint)( 5124 update.UpdateFlags = (uint)(
5032 PrimFlags.Physics | PrimFlags.ObjectModify | PrimFlags.ObjectCopy | PrimFlags.ObjectAnyOwner | 5125 PrimFlags.Physics | PrimFlags.ObjectModify | PrimFlags.ObjectCopy | PrimFlags.ObjectAnyOwner |
5033 PrimFlags.ObjectYouOwner | PrimFlags.ObjectMove | PrimFlags.InventoryEmpty | PrimFlags.ObjectTransfer | 5126 PrimFlags.ObjectYouOwner | PrimFlags.ObjectMove | PrimFlags.InventoryEmpty | PrimFlags.ObjectTransfer |
5034 PrimFlags.ObjectOwnerModify); 5127 PrimFlags.ObjectOwnerModify);
5128*/
5129 update.UpdateFlags = 0;
5035 5130
5036 return update; 5131 return update;
5037 } 5132 }
@@ -5351,6 +5446,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5351 AddLocalPacketHandler(PacketType.TransferAbort, HandleTransferAbort, false); 5446 AddLocalPacketHandler(PacketType.TransferAbort, HandleTransferAbort, false);
5352 AddLocalPacketHandler(PacketType.MuteListRequest, HandleMuteListRequest, false); 5447 AddLocalPacketHandler(PacketType.MuteListRequest, HandleMuteListRequest, false);
5353 AddLocalPacketHandler(PacketType.UseCircuitCode, HandleUseCircuitCode); 5448 AddLocalPacketHandler(PacketType.UseCircuitCode, HandleUseCircuitCode);
5449 AddLocalPacketHandler(PacketType.CreateNewOutfitAttachments, HandleCreateNewOutfitAttachments);
5354 AddLocalPacketHandler(PacketType.AgentHeightWidth, HandleAgentHeightWidth, false); 5450 AddLocalPacketHandler(PacketType.AgentHeightWidth, HandleAgentHeightWidth, false);
5355 AddLocalPacketHandler(PacketType.InventoryDescendents, HandleInventoryDescendents); 5451 AddLocalPacketHandler(PacketType.InventoryDescendents, HandleInventoryDescendents);
5356 AddLocalPacketHandler(PacketType.DirPlacesQuery, HandleDirPlacesQuery); 5452 AddLocalPacketHandler(PacketType.DirPlacesQuery, HandleDirPlacesQuery);
@@ -5417,6 +5513,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5417 AddLocalPacketHandler(PacketType.GroupVoteHistoryRequest, HandleGroupVoteHistoryRequest); 5513 AddLocalPacketHandler(PacketType.GroupVoteHistoryRequest, HandleGroupVoteHistoryRequest);
5418 AddLocalPacketHandler(PacketType.SimWideDeletes, HandleSimWideDeletes); 5514 AddLocalPacketHandler(PacketType.SimWideDeletes, HandleSimWideDeletes);
5419 AddLocalPacketHandler(PacketType.SendPostcard, HandleSendPostcard); 5515 AddLocalPacketHandler(PacketType.SendPostcard, HandleSendPostcard);
5516 AddLocalPacketHandler(PacketType.ChangeInventoryItemFlags, HandleChangeInventoryItemFlags);
5420 5517
5421 AddGenericPacketHandler("autopilot", HandleAutopilot); 5518 AddGenericPacketHandler("autopilot", HandleAutopilot);
5422 } 5519 }
@@ -5452,6 +5549,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5452 (x.CameraLeftAxis != lastarg.CameraLeftAxis) || 5549 (x.CameraLeftAxis != lastarg.CameraLeftAxis) ||
5453 (x.CameraUpAxis != lastarg.CameraUpAxis) || 5550 (x.CameraUpAxis != lastarg.CameraUpAxis) ||
5454 (x.ControlFlags != lastarg.ControlFlags) || 5551 (x.ControlFlags != lastarg.ControlFlags) ||
5552 (x.ControlFlags != 0) ||
5455 (x.Far != lastarg.Far) || 5553 (x.Far != lastarg.Far) ||
5456 (x.Flags != lastarg.Flags) || 5554 (x.Flags != lastarg.Flags) ||
5457 (x.State != lastarg.State) || 5555 (x.State != lastarg.State) ||
@@ -6350,6 +6448,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
6350 { 6448 {
6351 handlerCompleteMovementToRegion(sender, true); 6449 handlerCompleteMovementToRegion(sender, true);
6352 } 6450 }
6451 else
6452 m_log.Debug("HandleCompleteAgentMovement NULL handler");
6453
6353 handlerCompleteMovementToRegion = null; 6454 handlerCompleteMovementToRegion = null;
6354 6455
6355 return true; 6456 return true;
@@ -6367,7 +6468,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
6367 return true; 6468 return true;
6368 } 6469 }
6369 #endregion 6470 #endregion
6370 6471/*
6371 StartAnim handlerStartAnim = null; 6472 StartAnim handlerStartAnim = null;
6372 StopAnim handlerStopAnim = null; 6473 StopAnim handlerStopAnim = null;
6373 6474
@@ -6391,6 +6492,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP
6391 } 6492 }
6392 } 6493 }
6393 return true; 6494 return true;
6495*/
6496 ChangeAnim handlerChangeAnim = null;
6497
6498 for (int i = 0; i < AgentAni.AnimationList.Length; i++)
6499 {
6500 handlerChangeAnim = OnChangeAnim;
6501 if (handlerChangeAnim != null)
6502 {
6503 handlerChangeAnim(AgentAni.AnimationList[i].AnimID, AgentAni.AnimationList[i].StartAnim, false);
6504 }
6505 }
6506
6507 handlerChangeAnim = OnChangeAnim;
6508 if (handlerChangeAnim != null)
6509 {
6510 handlerChangeAnim(UUID.Zero, false, true);
6511 }
6512
6513 return true;
6394 } 6514 }
6395 6515
6396 private bool HandleAgentRequestSit(IClientAPI sender, Packet Pack) 6516 private bool HandleAgentRequestSit(IClientAPI sender, Packet Pack)
@@ -7016,10 +7136,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
7016 // 46,47,48 are special positions within the packet 7136 // 46,47,48 are special positions within the packet
7017 // This may change so perhaps we need a better way 7137 // This may change so perhaps we need a better way
7018 // of storing this (OMV.FlagUpdatePacket.UsePhysics,etc?) 7138 // of storing this (OMV.FlagUpdatePacket.UsePhysics,etc?)
7019 bool UsePhysics = (data[46] != 0) ? true : false; 7139 /*
7020 bool IsTemporary = (data[47] != 0) ? true : false; 7140 bool UsePhysics = (data[46] != 0) ? true : false;
7021 bool IsPhantom = (data[48] != 0) ? true : false; 7141 bool IsTemporary = (data[47] != 0) ? true : false;
7022 handlerUpdatePrimFlags(flags.AgentData.ObjectLocalID, UsePhysics, IsTemporary, IsPhantom, this); 7142 bool IsPhantom = (data[48] != 0) ? true : false;
7143 handlerUpdatePrimFlags(flags.AgentData.ObjectLocalID, UsePhysics, IsTemporary, IsPhantom, this);
7144 */
7145 bool UsePhysics = flags.AgentData.UsePhysics;
7146 bool IsPhantom = flags.AgentData.IsPhantom;
7147 bool IsTemporary = flags.AgentData.IsTemporary;
7148 ObjectFlagUpdatePacket.ExtraPhysicsBlock[] blocks = flags.ExtraPhysics;
7149 ExtraPhysicsData physdata = new ExtraPhysicsData();
7150
7151 if (blocks == null || blocks.Length == 0)
7152 {
7153 physdata.PhysShapeType = PhysShapeType.invalid;
7154 }
7155 else
7156 {
7157 ObjectFlagUpdatePacket.ExtraPhysicsBlock phsblock = blocks[0];
7158 physdata.PhysShapeType = (PhysShapeType)phsblock.PhysicsShapeType;
7159 physdata.Bounce = phsblock.Restitution;
7160 physdata.Density = phsblock.Density;
7161 physdata.Friction = phsblock.Friction;
7162 physdata.GravitationModifier = phsblock.GravityMultiplier;
7163 }
7164
7165 handlerUpdatePrimFlags(flags.AgentData.ObjectLocalID, UsePhysics, IsTemporary, IsPhantom, physdata, this);
7023 } 7166 }
7024 return true; 7167 return true;
7025 } 7168 }
@@ -8620,16 +8763,61 @@ namespace OpenSim.Region.ClientStack.LindenUDP
8620 8763
8621 #region Parcel related packets 8764 #region Parcel related packets
8622 8765
8766 // acumulate several HandleRegionHandleRequest consecutive overlaping requests
8767 // to be done with minimal resources as possible
8768 // variables temporary here while in test
8769
8770 Queue<UUID> RegionHandleRequests = new Queue<UUID>();
8771 bool RegionHandleRequestsInService = false;
8772
8623 private bool HandleRegionHandleRequest(IClientAPI sender, Packet Pack) 8773 private bool HandleRegionHandleRequest(IClientAPI sender, Packet Pack)
8624 { 8774 {
8625 RegionHandleRequestPacket rhrPack = (RegionHandleRequestPacket)Pack; 8775 UUID currentUUID;
8626 8776
8627 RegionHandleRequest handlerRegionHandleRequest = OnRegionHandleRequest; 8777 RegionHandleRequest handlerRegionHandleRequest = OnRegionHandleRequest;
8628 if (handlerRegionHandleRequest != null) 8778
8779 if (handlerRegionHandleRequest == null)
8780 return true;
8781
8782 RegionHandleRequestPacket rhrPack = (RegionHandleRequestPacket)Pack;
8783
8784 lock (RegionHandleRequests)
8629 { 8785 {
8630 handlerRegionHandleRequest(this, rhrPack.RequestBlock.RegionID); 8786 if (RegionHandleRequestsInService)
8787 {
8788 // we are already busy doing a previus request
8789 // so enqueue it
8790 RegionHandleRequests.Enqueue(rhrPack.RequestBlock.RegionID);
8791 return true;
8792 }
8793
8794 // else do it
8795 currentUUID = rhrPack.RequestBlock.RegionID;
8796 RegionHandleRequestsInService = true;
8631 } 8797 }
8632 return true; 8798
8799 while (true)
8800 {
8801 handlerRegionHandleRequest(this, currentUUID);
8802
8803 lock (RegionHandleRequests)
8804 {
8805 // exit condition, nothing to do or closed
8806 // current code seems to assume we may loose the handler at anytime,
8807 // so keep checking it
8808 handlerRegionHandleRequest = OnRegionHandleRequest;
8809
8810 if (RegionHandleRequests.Count == 0 || !IsActive || handlerRegionHandleRequest == null)
8811 {
8812 RegionHandleRequests.Clear();
8813 RegionHandleRequestsInService = false;
8814 return true;
8815 }
8816 currentUUID = RegionHandleRequests.Dequeue();
8817 }
8818 }
8819
8820 return true; // actually unreached
8633 } 8821 }
8634 8822
8635 private bool HandleParcelInfoRequest(IClientAPI sender, Packet Pack) 8823 private bool HandleParcelInfoRequest(IClientAPI sender, Packet Pack)
@@ -9873,7 +10061,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
9873 handlerUpdateMuteListEntry(this, UpdateMuteListEntry.MuteData.MuteID, 10061 handlerUpdateMuteListEntry(this, UpdateMuteListEntry.MuteData.MuteID,
9874 Utils.BytesToString(UpdateMuteListEntry.MuteData.MuteName), 10062 Utils.BytesToString(UpdateMuteListEntry.MuteData.MuteName),
9875 UpdateMuteListEntry.MuteData.MuteType, 10063 UpdateMuteListEntry.MuteData.MuteType,
9876 UpdateMuteListEntry.AgentData.AgentID); 10064 UpdateMuteListEntry.MuteData.MuteFlags);
9877 return true; 10065 return true;
9878 } 10066 }
9879 return false; 10067 return false;
@@ -9888,8 +10076,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
9888 { 10076 {
9889 handlerRemoveMuteListEntry(this, 10077 handlerRemoveMuteListEntry(this,
9890 RemoveMuteListEntry.MuteData.MuteID, 10078 RemoveMuteListEntry.MuteData.MuteID,
9891 Utils.BytesToString(RemoveMuteListEntry.MuteData.MuteName), 10079 Utils.BytesToString(RemoveMuteListEntry.MuteData.MuteName));
9892 RemoveMuteListEntry.AgentData.AgentID);
9893 return true; 10080 return true;
9894 } 10081 }
9895 return false; 10082 return false;
@@ -9933,10 +10120,55 @@ namespace OpenSim.Region.ClientStack.LindenUDP
9933 return false; 10120 return false;
9934 } 10121 }
9935 10122
10123 private bool HandleChangeInventoryItemFlags(IClientAPI client, Packet packet)
10124 {
10125 ChangeInventoryItemFlagsPacket ChangeInventoryItemFlags =
10126 (ChangeInventoryItemFlagsPacket)packet;
10127 ChangeInventoryItemFlags handlerChangeInventoryItemFlags = OnChangeInventoryItemFlags;
10128 if (handlerChangeInventoryItemFlags != null)
10129 {
10130 foreach(ChangeInventoryItemFlagsPacket.InventoryDataBlock b in ChangeInventoryItemFlags.InventoryData)
10131 handlerChangeInventoryItemFlags(this, b.ItemID, b.Flags);
10132 return true;
10133 }
10134 return false;
10135 }
10136
9936 private bool HandleUseCircuitCode(IClientAPI sender, Packet Pack) 10137 private bool HandleUseCircuitCode(IClientAPI sender, Packet Pack)
9937 { 10138 {
9938 return true; 10139 return true;
9939 } 10140 }
10141
10142 private bool HandleCreateNewOutfitAttachments(IClientAPI sender, Packet Pack)
10143 {
10144 CreateNewOutfitAttachmentsPacket packet = (CreateNewOutfitAttachmentsPacket)Pack;
10145
10146 #region Packet Session and User Check
10147 if (m_checkPackets)
10148 {
10149 if (packet.AgentData.SessionID != SessionId ||
10150 packet.AgentData.AgentID != AgentId)
10151 return true;
10152 }
10153 #endregion
10154 MoveItemsAndLeaveCopy handlerMoveItemsAndLeaveCopy = null;
10155 List<InventoryItemBase> items = new List<InventoryItemBase>();
10156 foreach (CreateNewOutfitAttachmentsPacket.ObjectDataBlock n in packet.ObjectData)
10157 {
10158 InventoryItemBase b = new InventoryItemBase();
10159 b.ID = n.OldItemID;
10160 b.Folder = n.OldFolderID;
10161 items.Add(b);
10162 }
10163
10164 handlerMoveItemsAndLeaveCopy = OnMoveItemsAndLeaveCopy;
10165 if (handlerMoveItemsAndLeaveCopy != null)
10166 {
10167 handlerMoveItemsAndLeaveCopy(this, items, packet.HeaderData.NewFolderID);
10168 }
10169
10170 return true;
10171 }
9940 10172
9941 private bool HandleAgentHeightWidth(IClientAPI sender, Packet Pack) 10173 private bool HandleAgentHeightWidth(IClientAPI sender, Packet Pack)
9942 { 10174 {
@@ -10363,6 +10595,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
10363 groupProfileReply.GroupData.MaturePublish = d.MaturePublish; 10595 groupProfileReply.GroupData.MaturePublish = d.MaturePublish;
10364 groupProfileReply.GroupData.OwnerRole = d.OwnerRole; 10596 groupProfileReply.GroupData.OwnerRole = d.OwnerRole;
10365 10597
10598 Scene scene = (Scene)m_scene;
10599 if (scene.Permissions.IsGod(sender.AgentId) && (!sender.IsGroupMember(groupProfileRequest.GroupData.GroupID)))
10600 {
10601 ScenePresence p;
10602 if (scene.TryGetScenePresence(sender.AgentId, out p))
10603 {
10604 if (p.GodLevel >= 200)
10605 {
10606 groupProfileReply.GroupData.OpenEnrollment = true;
10607 groupProfileReply.GroupData.MembershipFee = 0;
10608 }
10609 }
10610 }
10611
10366 OutPacket(groupProfileReply, ThrottleOutPacketType.Task); 10612 OutPacket(groupProfileReply, ThrottleOutPacketType.Task);
10367 } 10613 }
10368 return true; 10614 return true;
@@ -10936,11 +11182,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
10936 11182
10937 StartLure handlerStartLure = OnStartLure; 11183 StartLure handlerStartLure = OnStartLure;
10938 if (handlerStartLure != null) 11184 if (handlerStartLure != null)
10939 handlerStartLure(startLureRequest.Info.LureType, 11185 {
10940 Utils.BytesToString( 11186 for (int i = 0 ; i < startLureRequest.TargetData.Length ; i++)
10941 startLureRequest.Info.Message), 11187 {
10942 startLureRequest.TargetData[0].TargetID, 11188 handlerStartLure(startLureRequest.Info.LureType,
10943 this); 11189 Utils.BytesToString(
11190 startLureRequest.Info.Message),
11191 startLureRequest.TargetData[i].TargetID,
11192 this);
11193 }
11194 }
10944 return true; 11195 return true;
10945 } 11196 }
10946 private bool HandleTeleportLureRequest(IClientAPI sender, Packet Pack) 11197 private bool HandleTeleportLureRequest(IClientAPI sender, Packet Pack)
@@ -11054,10 +11305,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11054 } 11305 }
11055 #endregion 11306 #endregion
11056 11307
11057 ClassifiedDelete handlerClassifiedGodDelete = OnClassifiedGodDelete; 11308 ClassifiedGodDelete handlerClassifiedGodDelete = OnClassifiedGodDelete;
11058 if (handlerClassifiedGodDelete != null) 11309 if (handlerClassifiedGodDelete != null)
11059 handlerClassifiedGodDelete( 11310 handlerClassifiedGodDelete(
11060 classifiedGodDelete.Data.ClassifiedID, 11311 classifiedGodDelete.Data.ClassifiedID,
11312 classifiedGodDelete.Data.QueryID,
11061 this); 11313 this);
11062 return true; 11314 return true;
11063 } 11315 }
@@ -11423,209 +11675,147 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11423 } 11675 }
11424 else 11676 else
11425 { 11677 {
11426// m_log.DebugFormat( 11678 ClientChangeObject updatehandler = onClientChangeObject;
11427// "[CLIENT]: Processing block {0} type {1} for {2} {3}",
11428// i, block.Type, part.Name, part.LocalId);
11429 11679
11430// // Do this once since fetch parts creates a new array. 11680 if (updatehandler != null)
11431// SceneObjectPart[] parts = part.ParentGroup.Parts; 11681 {
11432// for (int j = 0; j < parts.Length; j++) 11682 ObjectChangeData udata = new ObjectChangeData();
11433// {
11434// part.StoreUndoState();
11435// parts[j].IgnoreUndoUpdate = true;
11436// }
11437 11683
11438 UpdatePrimGroupRotation handlerUpdatePrimGroupRotation; 11684 /*ubit from ll JIRA:
11685 * 0x01 position
11686 * 0x02 rotation
11687 * 0x04 scale
11688
11689 * 0x08 LINK_SET
11690 * 0x10 UNIFORM for scale
11691 */
11439 11692
11440 switch (block.Type) 11693 // translate to internal changes
11441 { 11694 // not all cases .. just the ones older code did
11442 case 1:
11443 Vector3 pos1 = new Vector3(block.Data, 0);
11444 11695
11445 UpdateVector handlerUpdatePrimSinglePosition = OnUpdatePrimSinglePosition; 11696 switch (block.Type)
11446 if (handlerUpdatePrimSinglePosition != null) 11697 {
11447 { 11698 case 1: //change position sp
11448 // m_log.Debug("new movement position is " + pos.X + " , " + pos.Y + " , " + pos.Z); 11699 udata.position = new Vector3(block.Data, 0);
11449 handlerUpdatePrimSinglePosition(localId, pos1, this);
11450 }
11451 break;
11452 11700
11453 case 2: 11701 udata.change = ObjectChangeType.primP;
11454 Quaternion rot1 = new Quaternion(block.Data, 0, true); 11702 updatehandler(localId, udata, this);
11703 break;
11455 11704
11456 UpdatePrimSingleRotation handlerUpdatePrimSingleRotation = OnUpdatePrimSingleRotation; 11705 case 2: // rotation sp
11457 if (handlerUpdatePrimSingleRotation != null) 11706 udata.rotation = new Quaternion(block.Data, 0, true);
11458 {
11459 // m_log.Info("new tab rotation is " + rot1.X + " , " + rot1.Y + " , " + rot1.Z + " , " + rot1.W);
11460 handlerUpdatePrimSingleRotation(localId, rot1, this);
11461 }
11462 break;
11463 11707
11464 case 3: 11708 udata.change = ObjectChangeType.primR;
11465 Vector3 rotPos = new Vector3(block.Data, 0); 11709 updatehandler(localId, udata, this);
11466 Quaternion rot2 = new Quaternion(block.Data, 12, true); 11710 break;
11467 11711
11468 UpdatePrimSingleRotationPosition handlerUpdatePrimSingleRotationPosition = OnUpdatePrimSingleRotationPosition; 11712 case 3: // position plus rotation
11469 if (handlerUpdatePrimSingleRotationPosition != null) 11713 udata.position = new Vector3(block.Data, 0);
11470 { 11714 udata.rotation = new Quaternion(block.Data, 12, true);
11471 // m_log.Debug("new mouse rotation position is " + rotPos.X + " , " + rotPos.Y + " , " + rotPos.Z);
11472 // m_log.Info("new mouse rotation is " + rot2.X + " , " + rot2.Y + " , " + rot2.Z + " , " + rot2.W);
11473 handlerUpdatePrimSingleRotationPosition(localId, rot2, rotPos, this);
11474 }
11475 break;
11476 11715
11477 case 4: 11716 udata.change = ObjectChangeType.primPR;
11478 case 20: 11717 updatehandler(localId, udata, this);
11479 Vector3 scale4 = new Vector3(block.Data, 0); 11718 break;
11480 11719
11481 UpdateVector handlerUpdatePrimScale = OnUpdatePrimScale; 11720 case 4: // scale sp
11482 if (handlerUpdatePrimScale != null) 11721 udata.scale = new Vector3(block.Data, 0);
11483 { 11722 udata.change = ObjectChangeType.primS;
11484 // m_log.Debug("new scale is " + scale4.X + " , " + scale4.Y + " , " + scale4.Z);
11485 handlerUpdatePrimScale(localId, scale4, this);
11486 }
11487 break;
11488 11723
11489 case 5: 11724 updatehandler(localId, udata, this);
11490 Vector3 scale1 = new Vector3(block.Data, 12); 11725 break;
11491 Vector3 pos11 = new Vector3(block.Data, 0);
11492 11726
11493 handlerUpdatePrimScale = OnUpdatePrimScale; 11727 case 0x14: // uniform scale sp
11494 if (handlerUpdatePrimScale != null) 11728 udata.scale = new Vector3(block.Data, 0);
11495 {
11496 // m_log.Debug("new scale is " + scale.X + " , " + scale.Y + " , " + scale.Z);
11497 handlerUpdatePrimScale(localId, scale1, this);
11498 11729
11499 handlerUpdatePrimSinglePosition = OnUpdatePrimSinglePosition; 11730 udata.change = ObjectChangeType.primUS;
11500 if (handlerUpdatePrimSinglePosition != null) 11731 updatehandler(localId, udata, this);
11501 { 11732 break;
11502 handlerUpdatePrimSinglePosition(localId, pos11, this);
11503 }
11504 }
11505 break;
11506 11733
11507 case 9: 11734 case 5: // scale and position sp
11508 Vector3 pos2 = new Vector3(block.Data, 0); 11735 udata.position = new Vector3(block.Data, 0);
11736 udata.scale = new Vector3(block.Data, 12);
11509 11737
11510 UpdateVector handlerUpdateVector = OnUpdatePrimGroupPosition; 11738 udata.change = ObjectChangeType.primPS;
11739 updatehandler(localId, udata, this);
11740 break;
11511 11741
11512 if (handlerUpdateVector != null) 11742 case 0x15: //uniform scale and position
11513 { 11743 udata.position = new Vector3(block.Data, 0);
11514 handlerUpdateVector(localId, pos2, this); 11744 udata.scale = new Vector3(block.Data, 12);
11515 }
11516 break;
11517 11745
11518 case 10: 11746 udata.change = ObjectChangeType.primPUS;
11519 Quaternion rot3 = new Quaternion(block.Data, 0, true); 11747 updatehandler(localId, udata, this);
11748 break;
11520 11749
11521 UpdatePrimRotation handlerUpdatePrimRotation = OnUpdatePrimGroupRotation; 11750 // now group related (bit 4)
11522 if (handlerUpdatePrimRotation != null) 11751 case 9: //( 8 + 1 )group position
11523 { 11752 udata.position = new Vector3(block.Data, 0);
11524 // Console.WriteLine("new rotation is " + rot3.X + " , " + rot3.Y + " , " + rot3.Z + " , " + rot3.W);
11525 handlerUpdatePrimRotation(localId, rot3, this);
11526 }
11527 break;
11528 11753
11529 case 11: 11754 udata.change = ObjectChangeType.groupP;
11530 Vector3 pos3 = new Vector3(block.Data, 0); 11755 updatehandler(localId, udata, this);
11531 Quaternion rot4 = new Quaternion(block.Data, 12, true); 11756 break;
11532 11757
11533 handlerUpdatePrimGroupRotation = OnUpdatePrimGroupMouseRotation; 11758 case 0x0A: // (8 + 2) group rotation
11534 if (handlerUpdatePrimGroupRotation != null) 11759 udata.rotation = new Quaternion(block.Data, 0, true);
11535 {
11536 // m_log.Debug("new rotation position is " + pos.X + " , " + pos.Y + " , " + pos.Z);
11537 // m_log.Debug("new group mouse rotation is " + rot4.X + " , " + rot4.Y + " , " + rot4.Z + " , " + rot4.W);
11538 handlerUpdatePrimGroupRotation(localId, pos3, rot4, this);
11539 }
11540 break;
11541 case 12:
11542 case 28:
11543 Vector3 scale7 = new Vector3(block.Data, 0);
11544 11760
11545 UpdateVector handlerUpdatePrimGroupScale = OnUpdatePrimGroupScale; 11761 udata.change = ObjectChangeType.groupR;
11546 if (handlerUpdatePrimGroupScale != null) 11762 updatehandler(localId, udata, this);
11547 { 11763 break;
11548 // m_log.Debug("new scale is " + scale7.X + " , " + scale7.Y + " , " + scale7.Z);
11549 handlerUpdatePrimGroupScale(localId, scale7, this);
11550 }
11551 break;
11552 11764
11553 case 13: 11765 case 0x0B: //( 8 + 2 + 1) group rotation and position
11554 Vector3 scale2 = new Vector3(block.Data, 12); 11766 udata.position = new Vector3(block.Data, 0);
11555 Vector3 pos4 = new Vector3(block.Data, 0); 11767 udata.rotation = new Quaternion(block.Data, 12, true);
11556 11768
11557 handlerUpdatePrimScale = OnUpdatePrimScale; 11769 udata.change = ObjectChangeType.groupPR;
11558 if (handlerUpdatePrimScale != null) 11770 updatehandler(localId, udata, this);
11559 { 11771 break;
11560 //m_log.Debug("new scale is " + scale.X + " , " + scale.Y + " , " + scale.Z);
11561 handlerUpdatePrimScale(localId, scale2, this);
11562 11772
11563 // Change the position based on scale (for bug number 246) 11773 case 0x0C: // (8 + 4) group scale
11564 handlerUpdatePrimSinglePosition = OnUpdatePrimSinglePosition; 11774 // only afects root prim and only sent by viewer editor object tab scaling
11565 // m_log.Debug("new movement position is " + pos.X + " , " + pos.Y + " , " + pos.Z); 11775 // mouse edition only allows uniform scaling
11566 if (handlerUpdatePrimSinglePosition != null) 11776 // SL MAY CHANGE THIS in viewers
11567 {
11568 handlerUpdatePrimSinglePosition(localId, pos4, this);
11569 }
11570 }
11571 break;
11572 11777
11573 case 29: 11778 udata.scale = new Vector3(block.Data, 0);
11574 Vector3 scale5 = new Vector3(block.Data, 12);
11575 Vector3 pos5 = new Vector3(block.Data, 0);
11576 11779
11577 handlerUpdatePrimGroupScale = OnUpdatePrimGroupScale; 11780 udata.change = ObjectChangeType.groupS;
11578 if (handlerUpdatePrimGroupScale != null) 11781 updatehandler(localId, udata, this);
11579 {
11580 // m_log.Debug("new scale is " + scale.X + " , " + scale.Y + " , " + scale.Z);
11581 part.StoreUndoState(true);
11582 part.IgnoreUndoUpdate = true;
11583 handlerUpdatePrimGroupScale(localId, scale5, this);
11584 handlerUpdateVector = OnUpdatePrimGroupPosition;
11585 11782
11586 if (handlerUpdateVector != null) 11783 break;
11587 {
11588 handlerUpdateVector(localId, pos5, this);
11589 }
11590 11784
11591 part.IgnoreUndoUpdate = false; 11785 case 0x0D: //(8 + 4 + 1) group scale and position
11592 } 11786 // exception as above
11593 11787
11594 break; 11788 udata.position = new Vector3(block.Data, 0);
11789 udata.scale = new Vector3(block.Data, 12);
11595 11790
11596 case 21: 11791 udata.change = ObjectChangeType.groupPS;
11597 Vector3 scale6 = new Vector3(block.Data, 12); 11792 updatehandler(localId, udata, this);
11598 Vector3 pos6 = new Vector3(block.Data, 0); 11793 break;
11599 11794
11600 handlerUpdatePrimScale = OnUpdatePrimScale; 11795 case 0x1C: // (0x10 + 8 + 4 ) group scale UNIFORM
11601 if (handlerUpdatePrimScale != null) 11796 udata.scale = new Vector3(block.Data, 0);
11602 {
11603 part.StoreUndoState(false);
11604 part.IgnoreUndoUpdate = true;
11605 11797
11606 // m_log.Debug("new scale is " + scale.X + " , " + scale.Y + " , " + scale.Z); 11798 udata.change = ObjectChangeType.groupUS;
11607 handlerUpdatePrimScale(localId, scale6, this); 11799 updatehandler(localId, udata, this);
11608 handlerUpdatePrimSinglePosition = OnUpdatePrimSinglePosition; 11800 break;
11609 if (handlerUpdatePrimSinglePosition != null)
11610 {
11611 handlerUpdatePrimSinglePosition(localId, pos6, this);
11612 }
11613 11801
11614 part.IgnoreUndoUpdate = false; 11802 case 0x1D: // (UNIFORM + GROUP + SCALE + POS)
11615 } 11803 udata.position = new Vector3(block.Data, 0);
11616 break; 11804 udata.scale = new Vector3(block.Data, 12);
11617 11805
11618 default: 11806 udata.change = ObjectChangeType.groupPUS;
11619 m_log.Debug("[CLIENT]: MultipleObjUpdate recieved an unknown packet type: " + (block.Type)); 11807 updatehandler(localId, udata, this);
11620 break; 11808 break;
11809
11810 default:
11811 m_log.Debug("[CLIENT]: MultipleObjUpdate recieved an unknown packet type: " + (block.Type));
11812 break;
11813 }
11621 } 11814 }
11622 11815
11623// for (int j = 0; j < parts.Length; j++)
11624// parts[j].IgnoreUndoUpdate = false;
11625 } 11816 }
11626 } 11817 }
11627 } 11818 }
11628
11629 return true; 11819 return true;
11630 } 11820 }
11631 11821
@@ -12075,7 +12265,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
12075// "[LLCLIENTVIEW]: Received transfer request for {0} in {1} type {2} by {3}", 12265// "[LLCLIENTVIEW]: Received transfer request for {0} in {1} type {2} by {3}",
12076// requestID, taskID, (SourceType)sourceType, Name); 12266// requestID, taskID, (SourceType)sourceType, Name);
12077 12267
12268
12269 //Note, the bool returned from the below function is useless since it is always false.
12078 m_assetService.Get(requestID.ToString(), transferRequest, AssetReceived); 12270 m_assetService.Get(requestID.ToString(), transferRequest, AssetReceived);
12271
12079 } 12272 }
12080 12273
12081 /// <summary> 12274 /// <summary>
@@ -12141,7 +12334,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
12141 /// <returns></returns> 12334 /// <returns></returns>
12142 private static int CalculateNumPackets(byte[] data) 12335 private static int CalculateNumPackets(byte[] data)
12143 { 12336 {
12144 const uint m_maxPacketSize = 600; 12337// const uint m_maxPacketSize = 600;
12338 uint m_maxPacketSize = MaxTransferBytesPerPacket;
12145 int numPackets = 1; 12339 int numPackets = 1;
12146 12340
12147 if (data == null) 12341 if (data == null)
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs
index 8963756..c472176 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs
@@ -158,6 +158,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
158 158
159 private int m_defaultRTO = 1000; // 1sec is the recommendation in the RFC 159 private int m_defaultRTO = 1000; // 1sec is the recommendation in the RFC
160 private int m_maxRTO = 60000; 160 private int m_maxRTO = 60000;
161 public bool m_deliverPackets = true;
161 162
162 /// <summary> 163 /// <summary>
163 /// Default constructor 164 /// Default constructor
@@ -440,6 +441,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
440 if (category >= 0 && category < m_packetOutboxes.Length) 441 if (category >= 0 && category < m_packetOutboxes.Length)
441 { 442 {
442 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category]; 443 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category];
444
445 if (m_deliverPackets == false)
446 {
447 queue.Enqueue(packet);
448 return true;
449 }
450
443 TokenBucket bucket = m_throttleCategories[category]; 451 TokenBucket bucket = m_throttleCategories[category];
444 452
445 // Don't send this packet if there is already a packet waiting in the queue 453 // Don't send this packet if there is already a packet waiting in the queue
@@ -489,7 +497,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
489 /// <returns>True if any packets were sent, otherwise false</returns> 497 /// <returns>True if any packets were sent, otherwise false</returns>
490 public bool DequeueOutgoing() 498 public bool DequeueOutgoing()
491 { 499 {
492 OutgoingPacket packet; 500 if (m_deliverPackets == false) return false;
501
502 OutgoingPacket packet = null;
493 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue; 503 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue;
494 TokenBucket bucket; 504 TokenBucket bucket;
495 bool packetSent = false; 505 bool packetSent = false;
@@ -521,32 +531,49 @@ namespace OpenSim.Region.ClientStack.LindenUDP
521 // No dequeued packet waiting to be sent, try to pull one off 531 // No dequeued packet waiting to be sent, try to pull one off
522 // this queue 532 // this queue
523 queue = m_packetOutboxes[i]; 533 queue = m_packetOutboxes[i];
524 if (queue.Dequeue(out packet)) 534 if (queue != null)
525 { 535 {
526 // A packet was pulled off the queue. See if we have 536 bool success = false;
527 // enough tokens in the bucket to send it out 537 try
528 if (bucket.RemoveTokens(packet.Buffer.DataLength))
529 { 538 {
530 // Send the packet 539 success = queue.Dequeue(out packet);
531 m_udpServer.SendPacketFinal(packet);
532 packetSent = true;
533 } 540 }
534 else 541 catch
535 { 542 {
536 // Save the dequeued packet for the next iteration 543 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
537 m_nextPackets[i] = packet;
538 } 544 }
539 545 if (success)
540 // If the queue is empty after this dequeue, fire the queue 546 {
541 // empty callback now so it has a chance to fill before we 547 // A packet was pulled off the queue. See if we have
542 // get back here 548 // enough tokens in the bucket to send it out
543 if (queue.Count == 0) 549 if (bucket.RemoveTokens(packet.Buffer.DataLength))
550 {
551 // Send the packet
552 m_udpServer.SendPacketFinal(packet);
553 packetSent = true;
554 }
555 else
556 {
557 // Save the dequeued packet for the next iteration
558 m_nextPackets[i] = packet;
559 }
560
561 // If the queue is empty after this dequeue, fire the queue
562 // empty callback now so it has a chance to fill before we
563 // get back here
564 if (queue.Count == 0)
565 emptyCategories |= CategoryToFlag(i);
566 }
567 else
568 {
569 // No packets in this queue. Fire the queue empty callback
570 // if it has not been called recently
544 emptyCategories |= CategoryToFlag(i); 571 emptyCategories |= CategoryToFlag(i);
572 }
545 } 573 }
546 else 574 else
547 { 575 {
548 // No packets in this queue. Fire the queue empty callback 576 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
549 // if it has not been called recently
550 emptyCategories |= CategoryToFlag(i); 577 emptyCategories |= CategoryToFlag(i);
551 } 578 }
552 } 579 }
@@ -704,4 +731,4 @@ namespace OpenSim.Region.ClientStack.LindenUDP
704 } 731 }
705 } 732 }
706 } 733 }
707} \ No newline at end of file 734}
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
index e3f4679..97b79ce 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
@@ -112,7 +112,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
112 /// <summary>Handlers for incoming packets</summary> 112 /// <summary>Handlers for incoming packets</summary>
113 //PacketEventDictionary packetEvents = new PacketEventDictionary(); 113 //PacketEventDictionary packetEvents = new PacketEventDictionary();
114 /// <summary>Incoming packets that are awaiting handling</summary> 114 /// <summary>Incoming packets that are awaiting handling</summary>
115 private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>(); 115 //private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>();
116
117 private DoubleQueue<IncomingPacket> packetInbox = new DoubleQueue<IncomingPacket>();
116 118
117 /// <summary></summary> 119 /// <summary></summary>
118 //private UDPClientCollection m_clients = new UDPClientCollection(); 120 //private UDPClientCollection m_clients = new UDPClientCollection();
@@ -167,6 +169,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
167 /// <summary>Flag to signal when clients should send pings</summary> 169 /// <summary>Flag to signal when clients should send pings</summary>
168 protected bool m_sendPing; 170 protected bool m_sendPing;
169 171
172 private ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>> m_pendingCache = new ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>>();
173
170 private int m_defaultRTO = 0; 174 private int m_defaultRTO = 0;
171 private int m_maxRTO = 0; 175 private int m_maxRTO = 0;
172 private int m_ackTimeout = 0; 176 private int m_ackTimeout = 0;
@@ -793,19 +797,44 @@ namespace OpenSim.Region.ClientStack.LindenUDP
793 797
794 #region Packet to Client Mapping 798 #region Packet to Client Mapping
795 799
796 // UseCircuitCode handling 800 // If there is already a client for this endpoint, don't process UseCircuitCode
797 if (packet.Type == PacketType.UseCircuitCode) 801 IClientAPI client = null;
802 if (!m_scene.TryGetClient(address, out client))
798 { 803 {
799 object[] array = new object[] { buffer, packet }; 804 // UseCircuitCode handling
805 if (packet.Type == PacketType.UseCircuitCode)
806 {
807 // And if there is a UseCircuitCode pending, also drop it
808 lock (m_pendingCache)
809 {
810 if (m_pendingCache.Contains(address))
811 return;
812
813 m_pendingCache.AddOrUpdate(address, new Queue<UDPPacketBuffer>(), 60);
814 }
800 815
801 Util.FireAndForget(HandleUseCircuitCode, array); 816 object[] array = new object[] { buffer, packet };
802 817
803 return; 818 Util.FireAndForget(HandleUseCircuitCode, array);
819
820 return;
821 }
822 }
823
824 // If this is a pending connection, enqueue, don't process yet
825 lock (m_pendingCache)
826 {
827 Queue<UDPPacketBuffer> queue;
828 if (m_pendingCache.TryGetValue(address, out queue))
829 {
830 //m_log.DebugFormat("[LLUDPSERVER]: Enqueued a {0} packet into the pending queue", packet.Type);
831 queue.Enqueue(buffer);
832 return;
833 }
804 } 834 }
805 835
806 // Determine which agent this packet came from 836 // Determine which agent this packet came from
807 IClientAPI client; 837 if (client == null || !(client is LLClientView))
808 if (!m_scene.TryGetClient(address, out client) || !(client is LLClientView))
809 { 838 {
810 //m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName); 839 //m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName);
811 return; 840 return;
@@ -814,7 +843,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
814 udpClient = ((LLClientView)client).UDPClient; 843 udpClient = ((LLClientView)client).UDPClient;
815 844
816 if (!udpClient.IsConnected) 845 if (!udpClient.IsConnected)
846 {
847// m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet for a unConnected client in " + m_scene.RegionInfo.RegionName);
817 return; 848 return;
849 }
818 850
819 #endregion Packet to Client Mapping 851 #endregion Packet to Client Mapping
820 852
@@ -917,7 +949,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
917 #endregion Ping Check Handling 949 #endregion Ping Check Handling
918 950
919 // Inbox insertion 951 // Inbox insertion
920 packetInbox.Enqueue(new IncomingPacket((LLClientView)client, packet)); 952 if (packet.Type == PacketType.AgentUpdate ||
953 packet.Type == PacketType.ChatFromViewer)
954 packetInbox.EnqueueHigh(new IncomingPacket((LLClientView)client, packet));
955 else
956 packetInbox.EnqueueLow(new IncomingPacket((LLClientView)client, packet));
957// packetInbox.Enqueue(new IncomingPacket((LLClientView)client, packet));
921 } 958 }
922 959
923 #region BinaryStats 960 #region BinaryStats
@@ -1039,6 +1076,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1039 // We only want to send initial data to new clients, not ones which are being converted from child to root. 1076 // We only want to send initial data to new clients, not ones which are being converted from child to root.
1040 if (client != null) 1077 if (client != null)
1041 client.SceneAgent.SendInitialDataToMe(); 1078 client.SceneAgent.SendInitialDataToMe();
1079
1080 // Now we know we can handle more data
1081 Thread.Sleep(200);
1082
1083 // Obtain the queue and remove it from the cache
1084 Queue<UDPPacketBuffer> queue = null;
1085
1086 lock (m_pendingCache)
1087 {
1088 if (!m_pendingCache.TryGetValue(remoteEndPoint, out queue))
1089 {
1090 m_log.DebugFormat("[LLUDPSERVER]: Client created but no pending queue present");
1091 return;
1092 }
1093 m_pendingCache.Remove(remoteEndPoint);
1094 }
1095
1096 m_log.DebugFormat("[LLUDPSERVER]: Client created, processing pending queue, {0} entries", queue.Count);
1097
1098 // Reinject queued packets
1099 while(queue.Count > 0)
1100 {
1101 UDPPacketBuffer buf = queue.Dequeue();
1102 PacketReceived(buf);
1103 }
1104 queue = null;
1042 } 1105 }
1043 else 1106 else
1044 { 1107 {
@@ -1046,6 +1109,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1046 m_log.WarnFormat( 1109 m_log.WarnFormat(
1047 "[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}", 1110 "[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}",
1048 uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, remoteEndPoint); 1111 uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, remoteEndPoint);
1112 lock (m_pendingCache)
1113 m_pendingCache.Remove(remoteEndPoint);
1049 } 1114 }
1050 1115
1051 // m_log.DebugFormat( 1116 // m_log.DebugFormat(
@@ -1164,7 +1229,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1164 if (!client.SceneAgent.IsChildAgent) 1229 if (!client.SceneAgent.IsChildAgent)
1165 client.Kick("Simulator logged you out due to connection timeout"); 1230 client.Kick("Simulator logged you out due to connection timeout");
1166 1231
1167 client.CloseWithoutChecks(); 1232 client.CloseWithoutChecks(true);
1168 } 1233 }
1169 } 1234 }
1170 1235
@@ -1176,6 +1241,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1176 1241
1177 while (base.IsRunning) 1242 while (base.IsRunning)
1178 { 1243 {
1244 m_scene.ThreadAlive(1);
1179 try 1245 try
1180 { 1246 {
1181 IncomingPacket incomingPacket = null; 1247 IncomingPacket incomingPacket = null;
@@ -1218,6 +1284,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1218 1284
1219 while (base.IsRunning) 1285 while (base.IsRunning)
1220 { 1286 {
1287 m_scene.ThreadAlive(2);
1221 try 1288 try
1222 { 1289 {
1223 m_packetSent = false; 1290 m_packetSent = false;
@@ -1439,8 +1506,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1439 Packet packet = incomingPacket.Packet; 1506 Packet packet = incomingPacket.Packet;
1440 LLClientView client = incomingPacket.Client; 1507 LLClientView client = incomingPacket.Client;
1441 1508
1442 if (client.IsActive) 1509// if (client.IsActive)
1443 { 1510// {
1444 m_currentIncomingClient = client; 1511 m_currentIncomingClient = client;
1445 1512
1446 try 1513 try
@@ -1467,13 +1534,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1467 { 1534 {
1468 m_currentIncomingClient = null; 1535 m_currentIncomingClient = null;
1469 } 1536 }
1470 } 1537// }
1471 else 1538// else
1472 { 1539// {
1473 m_log.DebugFormat( 1540// m_log.DebugFormat(
1474 "[LLUDPSERVER]: Dropped incoming {0} for dead client {1} in {2}", 1541// "[LLUDPSERVER]: Dropped incoming {0} for dead client {1} in {2}",
1475 packet.Type, client.Name, m_scene.RegionInfo.RegionName); 1542// packet.Type, client.Name, m_scene.RegionInfo.RegionName);
1476 } 1543// }
1477 } 1544 }
1478 1545
1479 protected void LogoutHandler(IClientAPI client) 1546 protected void LogoutHandler(IClientAPI client)
@@ -1483,8 +1550,116 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1483 if (!client.IsLoggingOut) 1550 if (!client.IsLoggingOut)
1484 { 1551 {
1485 client.IsLoggingOut = true; 1552 client.IsLoggingOut = true;
1486 client.Close(); 1553 client.Close(false, false);
1554 }
1555 }
1556 }
1557
1558 internal class DoubleQueue<T> where T:class
1559 {
1560 private Queue<T> m_lowQueue = new Queue<T>();
1561 private Queue<T> m_highQueue = new Queue<T>();
1562
1563 private object m_syncRoot = new object();
1564 private Semaphore m_s = new Semaphore(0, 1);
1565
1566 public DoubleQueue()
1567 {
1568 }
1569
1570 public virtual int Count
1571 {
1572 get { return m_highQueue.Count + m_lowQueue.Count; }
1573 }
1574
1575 public virtual void Enqueue(T data)
1576 {
1577 Enqueue(m_lowQueue, data);
1578 }
1579
1580 public virtual void EnqueueLow(T data)
1581 {
1582 Enqueue(m_lowQueue, data);
1583 }
1584
1585 public virtual void EnqueueHigh(T data)
1586 {
1587 Enqueue(m_highQueue, data);
1588 }
1589
1590 private void Enqueue(Queue<T> q, T data)
1591 {
1592 lock (m_syncRoot)
1593 {
1594 m_lowQueue.Enqueue(data);
1595 m_s.WaitOne(0);
1596 m_s.Release();
1597 }
1598 }
1599
1600 public virtual T Dequeue()
1601 {
1602 return Dequeue(Timeout.Infinite);
1603 }
1604
1605 public virtual T Dequeue(int tmo)
1606 {
1607 return Dequeue(TimeSpan.FromMilliseconds(tmo));
1608 }
1609
1610 public virtual T Dequeue(TimeSpan wait)
1611 {
1612 T res = null;
1613
1614 if (!Dequeue(wait, ref res))
1615 return null;
1616
1617 return res;
1618 }
1619
1620 public bool Dequeue(int timeout, ref T res)
1621 {
1622 return Dequeue(TimeSpan.FromMilliseconds(timeout), ref res);
1623 }
1624
1625 public bool Dequeue(TimeSpan wait, ref T res)
1626 {
1627 if (!m_s.WaitOne(wait))
1628 return false;
1629
1630 lock (m_syncRoot)
1631 {
1632 if (m_highQueue.Count > 0)
1633 res = m_highQueue.Dequeue();
1634 else
1635 res = m_lowQueue.Dequeue();
1636
1637 if (m_highQueue.Count == 0 && m_lowQueue.Count == 0)
1638 return true;
1639
1640 try
1641 {
1642 m_s.Release();
1643 }
1644 catch
1645 {
1646 }
1647
1648 return true;
1649 }
1650 }
1651
1652 public virtual void Clear()
1653 {
1654
1655 lock (m_syncRoot)
1656 {
1657 // Make sure sem count is 0
1658 m_s.WaitOne(0);
1659
1660 m_lowQueue.Clear();
1661 m_highQueue.Clear();
1487 } 1662 }
1488 } 1663 }
1489 } 1664 }
1490} \ No newline at end of file 1665}
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
index 039379d..cfe7c9d 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
@@ -100,10 +100,6 @@ namespace OpenMetaverse
100 const int SIO_UDP_CONNRESET = -1744830452; 100 const int SIO_UDP_CONNRESET = -1744830452;
101 101
102 IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); 102 IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort);
103
104 m_log.DebugFormat(
105 "[UDPBASE]: Binding UDP listener using internal IP address config {0}:{1}",
106 ipep.Address, ipep.Port);
107 103
108 m_udpSocket = new Socket( 104 m_udpSocket = new Socket(
109 AddressFamily.InterNetwork, 105 AddressFamily.InterNetwork,