diff options
Diffstat (limited to 'OpenSim/Region/ClientStack')
29 files changed, 2385 insertions, 1425 deletions
diff --git a/OpenSim/Region/ClientStack/IClientNetworkServer.cs b/OpenSim/Region/ClientStack/IClientNetworkServer.cs index 54a441b..bb7e6d0 100644 --- a/OpenSim/Region/ClientStack/IClientNetworkServer.cs +++ b/OpenSim/Region/ClientStack/IClientNetworkServer.cs | |||
@@ -38,11 +38,22 @@ namespace OpenSim.Region.ClientStack | |||
38 | IPAddress _listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, | 38 | IPAddress _listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, |
39 | AgentCircuitManager authenticateClass); | 39 | AgentCircuitManager authenticateClass); |
40 | 40 | ||
41 | void NetworkStop(); | ||
42 | bool HandlesRegion(Location x); | 41 | bool HandlesRegion(Location x); |
43 | void AddScene(IScene x); | ||
44 | 42 | ||
43 | /// <summary> | ||
44 | /// Add the given scene to be handled by this IClientNetworkServer. | ||
45 | /// </summary> | ||
46 | /// <param name='scene'></param> | ||
47 | void AddScene(IScene scene); | ||
48 | |||
49 | /// <summary> | ||
50 | /// Start sending and receiving data. | ||
51 | /// </summary> | ||
45 | void Start(); | 52 | void Start(); |
53 | |||
54 | /// <summary> | ||
55 | /// Stop sending and receiving data. | ||
56 | /// </summary> | ||
46 | void Stop(); | 57 | void Stop(); |
47 | } | 58 | } |
48 | } | 59 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/AvatarPickerSearchModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/AvatarPickerSearchModule.cs new file mode 100644 index 0000000..bbadc55 --- /dev/null +++ b/OpenSim/Region/ClientStack/Linden/Caps/AvatarPickerSearchModule.cs | |||
@@ -0,0 +1,136 @@ | |||
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 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Specialized; | ||
31 | using System.Drawing; | ||
32 | using System.Drawing.Imaging; | ||
33 | using System.Reflection; | ||
34 | using System.IO; | ||
35 | using System.Web; | ||
36 | using log4net; | ||
37 | using Nini.Config; | ||
38 | using Mono.Addins; | ||
39 | using OpenMetaverse; | ||
40 | using OpenMetaverse.StructuredData; | ||
41 | using OpenSim.Framework; | ||
42 | using OpenSim.Framework.Servers; | ||
43 | using OpenSim.Framework.Servers.HttpServer; | ||
44 | using OpenSim.Region.Framework.Interfaces; | ||
45 | using OpenSim.Region.Framework.Scenes; | ||
46 | using OpenSim.Services.Interfaces; | ||
47 | using Caps = OpenSim.Framework.Capabilities.Caps; | ||
48 | using OpenSim.Capabilities.Handlers; | ||
49 | |||
50 | namespace OpenSim.Region.ClientStack.Linden | ||
51 | { | ||
52 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AvatarPickerSearchModule")] | ||
53 | public class AvatarPickerSearchModule : INonSharedRegionModule | ||
54 | { | ||
55 | // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
56 | |||
57 | private Scene m_scene; | ||
58 | private IPeople m_People; | ||
59 | private bool m_Enabled = false; | ||
60 | |||
61 | private string m_URL; | ||
62 | |||
63 | #region ISharedRegionModule Members | ||
64 | |||
65 | public void Initialise(IConfigSource source) | ||
66 | { | ||
67 | IConfig config = source.Configs["ClientStack.LindenCaps"]; | ||
68 | if (config == null) | ||
69 | return; | ||
70 | |||
71 | m_URL = config.GetString("Cap_AvatarPickerSearch", string.Empty); | ||
72 | // Cap doesn't exist | ||
73 | if (m_URL != string.Empty) | ||
74 | m_Enabled = true; | ||
75 | } | ||
76 | |||
77 | public void AddRegion(Scene s) | ||
78 | { | ||
79 | if (!m_Enabled) | ||
80 | return; | ||
81 | |||
82 | m_scene = s; | ||
83 | } | ||
84 | |||
85 | public void RemoveRegion(Scene s) | ||
86 | { | ||
87 | if (!m_Enabled) | ||
88 | return; | ||
89 | |||
90 | m_scene.EventManager.OnRegisterCaps -= RegisterCaps; | ||
91 | m_scene = null; | ||
92 | } | ||
93 | |||
94 | public void RegionLoaded(Scene s) | ||
95 | { | ||
96 | if (!m_Enabled) | ||
97 | return; | ||
98 | |||
99 | m_People = m_scene.RequestModuleInterface<IPeople>(); | ||
100 | m_scene.EventManager.OnRegisterCaps += RegisterCaps; | ||
101 | } | ||
102 | |||
103 | public void PostInitialise() | ||
104 | { | ||
105 | } | ||
106 | |||
107 | public void Close() { } | ||
108 | |||
109 | public string Name { get { return "AvatarPickerSearchModule"; } } | ||
110 | |||
111 | public Type ReplaceableInterface | ||
112 | { | ||
113 | get { return null; } | ||
114 | } | ||
115 | |||
116 | #endregion | ||
117 | |||
118 | public void RegisterCaps(UUID agentID, Caps caps) | ||
119 | { | ||
120 | UUID capID = UUID.Random(); | ||
121 | |||
122 | if (m_URL == "localhost") | ||
123 | { | ||
124 | // m_log.DebugFormat("[AVATAR PICKER SEARCH]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); | ||
125 | caps.RegisterHandler( | ||
126 | "AvatarPickerSearch", | ||
127 | new AvatarPickerSearchHandler("/CAPS/" + capID + "/", m_People, "AvatarPickerSearch", "Search for avatars by name")); | ||
128 | } | ||
129 | else | ||
130 | { | ||
131 | // m_log.DebugFormat("[AVATAR PICKER SEARCH]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName); | ||
132 | caps.RegisterHandler("AvatarPickerSearch", m_URL); | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs index 8241e07..ab8f0c9 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs | |||
@@ -105,6 +105,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
105 | private static readonly string m_getObjectCostPath = "0102/"; | 105 | private static readonly string m_getObjectCostPath = "0102/"; |
106 | private static readonly string m_ResourceCostSelectedPath = "0103/"; | 106 | private static readonly string m_ResourceCostSelectedPath = "0103/"; |
107 | private static readonly string m_UpdateAgentInformationPath = "0500/"; | 107 | private static readonly string m_UpdateAgentInformationPath = "0500/"; |
108 | private static readonly string m_animSetTaskUpdatePath = "0260/"; | ||
108 | 109 | ||
109 | // These are callbacks which will be setup by the scene so that we can update scene data when we | 110 | // These are callbacks which will be setup by the scene so that we can update scene data when we |
110 | // receive capability calls | 111 | // receive capability calls |
@@ -248,13 +249,31 @@ namespace OpenSim.Region.ClientStack.Linden | |||
248 | //m_capsHandlers["MapLayer"] = | 249 | //m_capsHandlers["MapLayer"] = |
249 | // new LLSDStreamhandler<OSDMapRequest, OSDMapLayerResponse>("POST", | 250 | // new LLSDStreamhandler<OSDMapRequest, OSDMapLayerResponse>("POST", |
250 | // capsBase + m_mapLayerPath, | 251 | // capsBase + m_mapLayerPath, |
251 | // GetMapLayer); | 252 | // GetMapLayer); |
253 | |||
254 | IRequestHandler getObjectPhysicsDataHandler | ||
255 | = new RestStreamHandler( | ||
256 | "POST", capsBase + m_getObjectPhysicsDataPath, GetObjectPhysicsData, "GetObjectPhysicsData", null); | ||
257 | m_HostCapsObj.RegisterHandler("GetObjectPhysicsData", getObjectPhysicsDataHandler); | ||
258 | |||
259 | IRequestHandler getObjectCostHandler = new RestStreamHandler("POST", capsBase + m_getObjectCostPath, GetObjectCost); | ||
260 | m_HostCapsObj.RegisterHandler("GetObjectCost", getObjectCostHandler); | ||
261 | IRequestHandler ResourceCostSelectedHandler = new RestStreamHandler("POST", capsBase + m_ResourceCostSelectedPath, ResourceCostSelected); | ||
262 | m_HostCapsObj.RegisterHandler("ResourceCostSelected", ResourceCostSelectedHandler); | ||
263 | |||
264 | |||
252 | IRequestHandler req | 265 | IRequestHandler req |
253 | = new RestStreamHandler( | 266 | = new RestStreamHandler( |
254 | "POST", capsBase + m_notecardTaskUpdatePath, ScriptTaskInventory, "UpdateScript", null); | 267 | "POST", capsBase + m_notecardTaskUpdatePath, ScriptTaskInventory, "UpdateScript", null); |
255 | 268 | ||
256 | m_HostCapsObj.RegisterHandler("UpdateScriptTaskInventory", req); | 269 | m_HostCapsObj.RegisterHandler("UpdateScriptTaskInventory", req); |
257 | m_HostCapsObj.RegisterHandler("UpdateScriptTask", req); | 270 | m_HostCapsObj.RegisterHandler("UpdateScriptTask", req); |
271 | |||
272 | // IRequestHandler animSetRequestHandler | ||
273 | // = new RestStreamHandler( | ||
274 | // "POST", capsBase + m_animSetTaskUpdatePath, AnimSetTaskInventory, "UpdateScript", null); | ||
275 | |||
276 | // m_HostCapsObj.RegisterHandler("UpdateAnimSetTaskInventory", animSetRequestHandler); | ||
258 | } | 277 | } |
259 | catch (Exception e) | 278 | catch (Exception e) |
260 | { | 279 | { |
@@ -280,15 +299,15 @@ namespace OpenSim.Region.ClientStack.Linden | |||
280 | "POST", capsBase + m_notecardUpdatePath, NoteCardAgentInventory, "Update*", null); | 299 | "POST", capsBase + m_notecardUpdatePath, NoteCardAgentInventory, "Update*", null); |
281 | 300 | ||
282 | m_HostCapsObj.RegisterHandler("UpdateNotecardAgentInventory", req); | 301 | m_HostCapsObj.RegisterHandler("UpdateNotecardAgentInventory", req); |
302 | m_HostCapsObj.RegisterHandler("UpdateAnimSetAgentInventory", req); | ||
283 | m_HostCapsObj.RegisterHandler("UpdateScriptAgentInventory", req); | 303 | m_HostCapsObj.RegisterHandler("UpdateScriptAgentInventory", req); |
284 | m_HostCapsObj.RegisterHandler("UpdateScriptAgent", req); | 304 | m_HostCapsObj.RegisterHandler("UpdateScriptAgent", req); |
285 | IRequestHandler getObjectPhysicsDataHandler = new RestStreamHandler("POST", capsBase + m_getObjectPhysicsDataPath, GetObjectPhysicsData); | 305 | |
286 | m_HostCapsObj.RegisterHandler("GetObjectPhysicsData", getObjectPhysicsDataHandler); | 306 | |
287 | IRequestHandler getObjectCostHandler = new RestStreamHandler("POST", capsBase + m_getObjectCostPath, GetObjectCost); | 307 | |
288 | m_HostCapsObj.RegisterHandler("GetObjectCost", getObjectCostHandler); | 308 | IRequestHandler UpdateAgentInformationHandler |
289 | IRequestHandler ResourceCostSelectedHandler = new RestStreamHandler("POST", capsBase + m_ResourceCostSelectedPath, ResourceCostSelected); | 309 | = new RestStreamHandler( |
290 | m_HostCapsObj.RegisterHandler("ResourceCostSelected", ResourceCostSelectedHandler); | 310 | "POST", capsBase + m_UpdateAgentInformationPath, UpdateAgentInformation, "UpdateAgentInformation", null); |
291 | IRequestHandler UpdateAgentInformationHandler = new RestStreamHandler("POST", capsBase + m_UpdateAgentInformationPath, UpdateAgentInformation); | ||
292 | m_HostCapsObj.RegisterHandler("UpdateAgentInformation", UpdateAgentInformationHandler); | 311 | m_HostCapsObj.RegisterHandler("UpdateAgentInformation", UpdateAgentInformationHandler); |
293 | 312 | ||
294 | m_HostCapsObj.RegisterHandler( | 313 | m_HostCapsObj.RegisterHandler( |
@@ -361,18 +380,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
361 | foreach (OSD c in capsRequested) | 380 | foreach (OSD c in capsRequested) |
362 | validCaps.Add(c.AsString()); | 381 | validCaps.Add(c.AsString()); |
363 | 382 | ||
364 | Hashtable caps = m_HostCapsObj.CapsHandlers.GetCapsDetails(true, validCaps); | 383 | string result = LLSDHelpers.SerialiseLLSDReply(m_HostCapsObj.GetCapsDetails(true, validCaps)); |
365 | |||
366 | // Add the external too | ||
367 | foreach (KeyValuePair<string, string> kvp in m_HostCapsObj.ExternalCapsHandlers) | ||
368 | { | ||
369 | if (!validCaps.Contains(kvp.Key)) | ||
370 | continue; | ||
371 | |||
372 | caps[kvp.Key] = kvp.Value; | ||
373 | } | ||
374 | |||
375 | string result = LLSDHelpers.SerialiseLLSDReply(caps); | ||
376 | 384 | ||
377 | //m_log.DebugFormat("[CAPS] CapsRequest {0}", result); | 385 | //m_log.DebugFormat("[CAPS] CapsRequest {0}", result); |
378 | 386 | ||
@@ -530,6 +538,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
530 | 538 | ||
531 | if (llsdRequest.asset_type == "texture" || | 539 | if (llsdRequest.asset_type == "texture" || |
532 | llsdRequest.asset_type == "animation" || | 540 | llsdRequest.asset_type == "animation" || |
541 | llsdRequest.asset_type == "animatn" || // this is the asset name actually used by viewers | ||
533 | llsdRequest.asset_type == "mesh" || | 542 | llsdRequest.asset_type == "mesh" || |
534 | llsdRequest.asset_type == "sound") | 543 | llsdRequest.asset_type == "sound") |
535 | { | 544 | { |
@@ -571,6 +580,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
571 | string error; | 580 | string error; |
572 | int modelcost; | 581 | int modelcost; |
573 | 582 | ||
583 | |||
574 | if (!m_ModelCost.MeshModelCost(llsdRequest.asset_resources, baseCost, out modelcost, | 584 | if (!m_ModelCost.MeshModelCost(llsdRequest.asset_resources, baseCost, out modelcost, |
575 | meshcostdata, out error, ref warning)) | 585 | meshcostdata, out error, ref warning)) |
576 | { | 586 | { |
@@ -748,11 +758,21 @@ namespace OpenSim.Region.ClientStack.Linden | |||
748 | inType = (sbyte)InventoryType.Sound; | 758 | inType = (sbyte)InventoryType.Sound; |
749 | assType = (sbyte)AssetType.Sound; | 759 | assType = (sbyte)AssetType.Sound; |
750 | } | 760 | } |
761 | else if (inventoryType == "snapshot") | ||
762 | { | ||
763 | inType = (sbyte)InventoryType.Snapshot; | ||
764 | } | ||
751 | else if (inventoryType == "animation") | 765 | else if (inventoryType == "animation") |
752 | { | 766 | { |
753 | inType = (sbyte)InventoryType.Animation; | 767 | inType = (sbyte)InventoryType.Animation; |
754 | assType = (sbyte)AssetType.Animation; | 768 | assType = (sbyte)AssetType.Animation; |
755 | } | 769 | } |
770 | else if (inventoryType == "animset") | ||
771 | { | ||
772 | inType = (sbyte)CustomInventoryType.AnimationSet; | ||
773 | assType = (sbyte)CustomAssetType.AnimationSet; | ||
774 | m_log.Debug("got animset upload request"); | ||
775 | } | ||
756 | else if (inventoryType == "wearable") | 776 | else if (inventoryType == "wearable") |
757 | { | 777 | { |
758 | inType = (sbyte)InventoryType.Wearable; | 778 | inType = (sbyte)InventoryType.Wearable; |
@@ -778,7 +798,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
778 | OSDMap request = (OSDMap)OSDParser.DeserializeLLSDXml(data); | 798 | OSDMap request = (OSDMap)OSDParser.DeserializeLLSDXml(data); |
779 | 799 | ||
780 | // compare and get updated information | 800 | // compare and get updated information |
781 | 801 | /* does nothing still we do need something to avoid special viewer to upload something diferent from the cost estimation | |
782 | bool mismatchError = true; | 802 | bool mismatchError = true; |
783 | 803 | ||
784 | while (mismatchError) | 804 | while (mismatchError) |
@@ -794,7 +814,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
794 | 814 | ||
795 | return; | 815 | return; |
796 | } | 816 | } |
797 | 817 | */ | |
798 | OSDArray instance_list = (OSDArray)request["instance_list"]; | 818 | OSDArray instance_list = (OSDArray)request["instance_list"]; |
799 | OSDArray mesh_list = (OSDArray)request["mesh_list"]; | 819 | OSDArray mesh_list = (OSDArray)request["mesh_list"]; |
800 | OSDArray texture_list = (OSDArray)request["texture_list"]; | 820 | OSDArray texture_list = (OSDArray)request["texture_list"]; |
@@ -808,7 +828,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
808 | List<UUID> textures = new List<UUID>(); | 828 | List<UUID> textures = new List<UUID>(); |
809 | 829 | ||
810 | 830 | ||
811 | if (doTextInv) | 831 | // if (doTextInv) |
812 | m_Scene.TryGetClient(m_HostCapsObj.AgentID, out client); | 832 | m_Scene.TryGetClient(m_HostCapsObj.AgentID, out client); |
813 | 833 | ||
814 | if(client == null) // don't put textures in inventory if there is no client | 834 | if(client == null) // don't put textures in inventory if there is no client |
@@ -856,14 +876,73 @@ namespace OpenSim.Region.ClientStack.Linden | |||
856 | 876 | ||
857 | // create and store meshs assets | 877 | // create and store meshs assets |
858 | List<UUID> meshAssets = new List<UUID>(); | 878 | List<UUID> meshAssets = new List<UUID>(); |
879 | List<bool> meshAvatarSkeletons = new List<bool>(); | ||
880 | List<bool> meshAvatarColliders = new List<bool>(); | ||
881 | |||
882 | bool curAvSkeleton; | ||
883 | bool curAvCollider; | ||
859 | for (int i = 0; i < mesh_list.Count; i++) | 884 | for (int i = 0; i < mesh_list.Count; i++) |
860 | { | 885 | { |
886 | curAvSkeleton = false; | ||
887 | curAvCollider = false; | ||
888 | |||
889 | // we do need to parse the mesh now | ||
890 | OSD osd = OSDParser.DeserializeLLSDBinary(mesh_list[i]); | ||
891 | if (osd is OSDMap) | ||
892 | { | ||
893 | OSDMap mosd = (OSDMap)osd; | ||
894 | if (mosd.ContainsKey("skeleton")) | ||
895 | { | ||
896 | OSDMap skeleton = (OSDMap)mosd["skeleton"]; | ||
897 | int sksize = skeleton["size"].AsInteger(); | ||
898 | if (sksize > 0) | ||
899 | curAvSkeleton = true; | ||
900 | } | ||
901 | } | ||
902 | |||
861 | AssetBase meshAsset = new AssetBase(UUID.Random(), assetName, (sbyte)AssetType.Mesh, creatorIDstr); | 903 | AssetBase meshAsset = new AssetBase(UUID.Random(), assetName, (sbyte)AssetType.Mesh, creatorIDstr); |
862 | meshAsset.Data = mesh_list[i].AsBinary(); | 904 | meshAsset.Data = mesh_list[i].AsBinary(); |
863 | if (istest) | 905 | if (istest) |
864 | meshAsset.Local = true; | 906 | meshAsset.Local = true; |
865 | m_assetService.Store(meshAsset); | 907 | m_assetService.Store(meshAsset); |
866 | meshAssets.Add(meshAsset.FullID); | 908 | meshAssets.Add(meshAsset.FullID); |
909 | meshAvatarSkeletons.Add(curAvSkeleton); | ||
910 | meshAvatarColliders.Add(curAvCollider); | ||
911 | |||
912 | // test code | ||
913 | if (curAvSkeleton && client != null) | ||
914 | { | ||
915 | string name = assetName; | ||
916 | if (name.Length > 25) | ||
917 | name = name.Substring(0, 24); | ||
918 | name += "_Mesh#" + i.ToString(); | ||
919 | InventoryItemBase meshitem = new InventoryItemBase(); | ||
920 | meshitem.Owner = m_HostCapsObj.AgentID; | ||
921 | meshitem.CreatorId = creatorIDstr; | ||
922 | meshitem.CreatorData = String.Empty; | ||
923 | meshitem.ID = UUID.Random(); | ||
924 | meshitem.AssetID = meshAsset.FullID; | ||
925 | meshitem.Description = "mesh "; | ||
926 | meshitem.Name = name; | ||
927 | meshitem.AssetType = (int)AssetType.Mesh; | ||
928 | meshitem.InvType = (int)InventoryType.Mesh; | ||
929 | // meshitem.Folder = UUID.Zero; // send to default | ||
930 | |||
931 | meshitem.Folder = parentFolder; // dont let it go to folder Meshes that viewers dont show | ||
932 | |||
933 | // If we set PermissionMask.All then when we rez the item the next permissions will replace the current | ||
934 | // (owner) permissions. This becomes a problem if next permissions are changed. | ||
935 | meshitem.CurrentPermissions | ||
936 | = (uint)(PermissionMask.Move | PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer); | ||
937 | |||
938 | meshitem.BasePermissions = (uint)PermissionMask.All; | ||
939 | meshitem.EveryOnePermissions = 0; | ||
940 | meshitem.NextPermissions = (uint)PermissionMask.All; | ||
941 | meshitem.CreationDate = Util.UnixTimeSinceEpoch(); | ||
942 | |||
943 | m_Scene.AddInventoryItem(client, meshitem); | ||
944 | meshitem = null; | ||
945 | } | ||
867 | } | 946 | } |
868 | 947 | ||
869 | int skipedMeshs = 0; | 948 | int skipedMeshs = 0; |
@@ -1104,7 +1183,12 @@ namespace OpenSim.Region.ClientStack.Linden | |||
1104 | // If we set PermissionMask.All then when we rez the item the next permissions will replace the current | 1183 | // If we set PermissionMask.All then when we rez the item the next permissions will replace the current |
1105 | // (owner) permissions. This becomes a problem if next permissions are changed. | 1184 | // (owner) permissions. This becomes a problem if next permissions are changed. |
1106 | 1185 | ||
1107 | if (restrictPerms) | 1186 | if (inType == (sbyte)CustomInventoryType.AnimationSet) |
1187 | { | ||
1188 | AnimationSet.setCreateItemPermitions(item); | ||
1189 | } | ||
1190 | |||
1191 | else if (restrictPerms) | ||
1108 | { | 1192 | { |
1109 | item.BasePermissions = (uint)(PermissionMask.Move | PermissionMask.Modify); | 1193 | item.BasePermissions = (uint)(PermissionMask.Move | PermissionMask.Modify); |
1110 | item.CurrentPermissions = (uint)(PermissionMask.Move | PermissionMask.Modify); | 1194 | item.CurrentPermissions = (uint)(PermissionMask.Move | PermissionMask.Modify); |
@@ -1380,6 +1464,17 @@ namespace OpenSim.Region.ClientStack.Linden | |||
1380 | 1464 | ||
1381 | resp[uuid.ToString()] = object_data; | 1465 | resp[uuid.ToString()] = object_data; |
1382 | } | 1466 | } |
1467 | else | ||
1468 | { | ||
1469 | OSDMap object_data = new OSDMap(); | ||
1470 | object_data["linked_set_resource_cost"] = 0; | ||
1471 | object_data["resource_cost"] = 0; | ||
1472 | object_data["physics_cost"] = 0; | ||
1473 | object_data["linked_set_physics_cost"] = 0; | ||
1474 | |||
1475 | resp[uuid.ToString()] = object_data; | ||
1476 | } | ||
1477 | |||
1383 | } | 1478 | } |
1384 | } | 1479 | } |
1385 | 1480 | ||
@@ -1444,7 +1539,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
1444 | } | 1539 | } |
1445 | } | 1540 | } |
1446 | 1541 | ||
1447 | if (simul != 0) | 1542 | // if (simul != 0) |
1448 | { | 1543 | { |
1449 | OSDMap object_data = new OSDMap(); | 1544 | OSDMap object_data = new OSDMap(); |
1450 | 1545 | ||
@@ -1505,7 +1600,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
1505 | private int m_nreqmeshs; | 1600 | private int m_nreqmeshs; |
1506 | private int m_nreqinstances; | 1601 | private int m_nreqinstances; |
1507 | private bool m_IsAtestUpload; | 1602 | private bool m_IsAtestUpload; |
1508 | 1603 | ||
1509 | public AssetUploader(string assetName, string description, UUID assetID, UUID inventoryItem, | 1604 | public AssetUploader(string assetName, string description, UUID assetID, UUID inventoryItem, |
1510 | UUID parentFolderID, string invType, string assetType, string path, | 1605 | UUID parentFolderID, string invType, string assetType, string path, |
1511 | IHttpServer httpServer, bool dumpAssetsToFile, | 1606 | IHttpServer httpServer, bool dumpAssetsToFile, |
@@ -1573,7 +1668,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
1573 | if (handlerUpLoad != null) | 1668 | if (handlerUpLoad != null) |
1574 | { | 1669 | { |
1575 | handlerUpLoad(m_assetName, m_assetDes, newAssetID, inv, parentFolder, data, m_invType, m_assetType, | 1670 | handlerUpLoad(m_assetName, m_assetDes, newAssetID, inv, parentFolder, data, m_invType, m_assetType, |
1576 | m_cost, m_texturesFolder, m_nreqtextures, m_nreqmeshs, m_nreqinstances, m_IsAtestUpload, ref m_error); | 1671 | m_cost, m_texturesFolder, m_nreqtextures, m_nreqmeshs, m_nreqinstances, m_IsAtestUpload, |
1672 | ref m_error); | ||
1577 | } | 1673 | } |
1578 | if (m_IsAtestUpload) | 1674 | if (m_IsAtestUpload) |
1579 | { | 1675 | { |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs index 4a3fae6..546bcd9 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs | |||
@@ -96,11 +96,15 @@ namespace OpenSim.Region.ClientStack.Linden | |||
96 | // basicCost input region assets upload cost | 96 | // basicCost input region assets upload cost |
97 | // totalcost returns model total upload fee | 97 | // totalcost returns model total upload fee |
98 | // meshcostdata returns detailed costs for viewer | 98 | // meshcostdata returns detailed costs for viewer |
99 | public bool MeshModelCost(LLSDAssetResource resources, int basicCost, out int totalcost, | 99 | // avatarSkeleton if mesh includes a avatar skeleton |
100 | // useAvatarCollider if we should use physics mesh for avatar | ||
101 | public bool MeshModelCost(LLSDAssetResource resources, int basicCost, out int totalcost, | ||
100 | LLSDAssetUploadResponseData meshcostdata, out string error, ref string warning) | 102 | LLSDAssetUploadResponseData meshcostdata, out string error, ref string warning) |
101 | { | 103 | { |
102 | totalcost = 0; | 104 | totalcost = 0; |
103 | error = string.Empty; | 105 | error = string.Empty; |
106 | |||
107 | bool avatarSkeleton = false; | ||
104 | 108 | ||
105 | if (resources == null || | 109 | if (resources == null || |
106 | resources.instance_list == null || | 110 | resources.instance_list == null || |
@@ -145,6 +149,10 @@ namespace OpenSim.Region.ClientStack.Linden | |||
145 | float meshsfee = 0; | 149 | float meshsfee = 0; |
146 | int numberMeshs = 0; | 150 | int numberMeshs = 0; |
147 | bool haveMeshs = false; | 151 | bool haveMeshs = false; |
152 | |||
153 | bool curskeleton; | ||
154 | bool curAvatarPhys; | ||
155 | |||
148 | List<ameshCostParam> meshsCosts = new List<ameshCostParam>(); | 156 | List<ameshCostParam> meshsCosts = new List<ameshCostParam>(); |
149 | 157 | ||
150 | if (resources.mesh_list != null && resources.mesh_list.Array.Count > 0) | 158 | if (resources.mesh_list != null && resources.mesh_list.Array.Count > 0) |
@@ -156,10 +164,20 @@ namespace OpenSim.Region.ClientStack.Linden | |||
156 | ameshCostParam curCost = new ameshCostParam(); | 164 | ameshCostParam curCost = new ameshCostParam(); |
157 | byte[] data = (byte[])resources.mesh_list.Array[i]; | 165 | byte[] data = (byte[])resources.mesh_list.Array[i]; |
158 | 166 | ||
159 | if (!MeshCost(data, curCost, out error)) | 167 | if (!MeshCost(data, curCost,out curskeleton, out curAvatarPhys, out error)) |
160 | { | 168 | { |
161 | return false; | 169 | return false; |
162 | } | 170 | } |
171 | |||
172 | if (curskeleton) | ||
173 | { | ||
174 | if (avatarSkeleton) | ||
175 | { | ||
176 | error = "model can only contain a avatar skeleton"; | ||
177 | return false; | ||
178 | } | ||
179 | avatarSkeleton = true; | ||
180 | } | ||
163 | meshsCosts.Add(curCost); | 181 | meshsCosts.Add(curCost); |
164 | meshsfee += curCost.costFee; | 182 | meshsfee += curCost.costFee; |
165 | } | 183 | } |
@@ -273,7 +291,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
273 | } | 291 | } |
274 | 292 | ||
275 | // single mesh asset cost | 293 | // single mesh asset cost |
276 | private bool MeshCost(byte[] data, ameshCostParam cost, out string error) | 294 | private bool MeshCost(byte[] data, ameshCostParam cost,out bool skeleton, out bool avatarPhys, out string error) |
277 | { | 295 | { |
278 | cost.highLODSize = 0; | 296 | cost.highLODSize = 0; |
279 | cost.medLODSize = 0; | 297 | cost.medLODSize = 0; |
@@ -284,6 +302,9 @@ namespace OpenSim.Region.ClientStack.Linden | |||
284 | 302 | ||
285 | error = string.Empty; | 303 | error = string.Empty; |
286 | 304 | ||
305 | skeleton = false; | ||
306 | avatarPhys = false; | ||
307 | |||
287 | if (data == null || data.Length == 0) | 308 | if (data == null || data.Length == 0) |
288 | { | 309 | { |
289 | error = "Missing model information."; | 310 | error = "Missing model information."; |
@@ -330,6 +351,17 @@ namespace OpenSim.Region.ClientStack.Linden | |||
330 | 351 | ||
331 | int submesh_offset = -1; | 352 | int submesh_offset = -1; |
332 | 353 | ||
354 | if (map.ContainsKey("skeleton")) | ||
355 | { | ||
356 | tmpmap = (OSDMap)map["skeleton"]; | ||
357 | if (tmpmap.ContainsKey("offset") && tmpmap.ContainsKey("size")) | ||
358 | { | ||
359 | int sksize = tmpmap["size"].AsInteger(); | ||
360 | if(sksize > 0) | ||
361 | skeleton = true; | ||
362 | } | ||
363 | } | ||
364 | |||
333 | if (map.ContainsKey("physics_convex")) | 365 | if (map.ContainsKey("physics_convex")) |
334 | { | 366 | { |
335 | tmpmap = (OSDMap)map["physics_convex"]; | 367 | tmpmap = (OSDMap)map["physics_convex"]; |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs index eb40eb1..ca6c3ca 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs | |||
@@ -65,12 +65,18 @@ namespace OpenSim.Region.ClientStack.Linden | |||
65 | /// </value> | 65 | /// </value> |
66 | public int DebugLevel { get; set; } | 66 | public int DebugLevel { get; set; } |
67 | 67 | ||
68 | // Viewer post requests timeout in 60 secs | ||
69 | // https://bitbucket.org/lindenlab/viewer-release/src/421c20423df93d650cc305dc115922bb30040999/indra/llmessage/llhttpclient.cpp?at=default#cl-44 | ||
70 | // | ||
71 | private const int VIEWER_TIMEOUT = 60 * 1000; | ||
72 | // Just to be safe, we work on a 10 sec shorter cycle | ||
73 | private const int SERVER_EQ_TIME_NO_EVENTS = VIEWER_TIMEOUT - (10 * 1000); | ||
74 | |||
68 | protected Scene m_scene; | 75 | protected Scene m_scene; |
69 | 76 | ||
70 | private Dictionary<UUID, int> m_ids = new Dictionary<UUID, int>(); | 77 | private Dictionary<UUID, int> m_ids = new Dictionary<UUID, int>(); |
71 | 78 | ||
72 | private Dictionary<UUID, Queue<OSD>> queues = new Dictionary<UUID, Queue<OSD>>(); | 79 | private Dictionary<UUID, Queue<OSD>> queues = new Dictionary<UUID, Queue<OSD>>(); |
73 | private Dictionary<UUID, UUID> m_QueueUUIDAvatarMapping = new Dictionary<UUID, UUID>(); | ||
74 | private Dictionary<UUID, UUID> m_AvatarQueueUUIDMapping = new Dictionary<UUID, UUID>(); | 80 | private Dictionary<UUID, UUID> m_AvatarQueueUUIDMapping = new Dictionary<UUID, UUID>(); |
75 | 81 | ||
76 | #region INonSharedRegionModule methods | 82 | #region INonSharedRegionModule methods |
@@ -84,7 +90,6 @@ namespace OpenSim.Region.ClientStack.Linden | |||
84 | scene.RegisterModuleInterface<IEventQueue>(this); | 90 | scene.RegisterModuleInterface<IEventQueue>(this); |
85 | 91 | ||
86 | scene.EventManager.OnClientClosed += ClientClosed; | 92 | scene.EventManager.OnClientClosed += ClientClosed; |
87 | scene.EventManager.OnMakeChildAgent += MakeChildAgent; | ||
88 | scene.EventManager.OnRegisterCaps += OnRegisterCaps; | 93 | scene.EventManager.OnRegisterCaps += OnRegisterCaps; |
89 | 94 | ||
90 | MainConsole.Instance.Commands.AddCommand( | 95 | MainConsole.Instance.Commands.AddCommand( |
@@ -113,7 +118,6 @@ namespace OpenSim.Region.ClientStack.Linden | |||
113 | return; | 118 | return; |
114 | 119 | ||
115 | scene.EventManager.OnClientClosed -= ClientClosed; | 120 | scene.EventManager.OnClientClosed -= ClientClosed; |
116 | scene.EventManager.OnMakeChildAgent -= MakeChildAgent; | ||
117 | scene.EventManager.OnRegisterCaps -= OnRegisterCaps; | 121 | scene.EventManager.OnRegisterCaps -= OnRegisterCaps; |
118 | 122 | ||
119 | scene.UnregisterModuleInterface<IEventQueue>(this); | 123 | scene.UnregisterModuleInterface<IEventQueue>(this); |
@@ -172,29 +176,6 @@ namespace OpenSim.Region.ClientStack.Linden | |||
172 | } | 176 | } |
173 | 177 | ||
174 | /// <summary> | 178 | /// <summary> |
175 | /// Always returns a valid queue | ||
176 | /// </summary> | ||
177 | /// <param name="agentId"></param> | ||
178 | /// <returns></returns> | ||
179 | private Queue<OSD> TryGetQueue(UUID agentId) | ||
180 | { | ||
181 | lock (queues) | ||
182 | { | ||
183 | if (!queues.ContainsKey(agentId)) | ||
184 | { | ||
185 | /* | ||
186 | m_log.DebugFormat( | ||
187 | "[EVENTQUEUE]: Adding new queue for agent {0} in region {1}", | ||
188 | agentId, m_scene.RegionInfo.RegionName); | ||
189 | */ | ||
190 | queues[agentId] = new Queue<OSD>(); | ||
191 | } | ||
192 | |||
193 | return queues[agentId]; | ||
194 | } | ||
195 | } | ||
196 | |||
197 | /// <summary> | ||
198 | /// May return a null queue | 179 | /// May return a null queue |
199 | /// </summary> | 180 | /// </summary> |
200 | /// <param name="agentId"></param> | 181 | /// <param name="agentId"></param> |
@@ -221,8 +202,17 @@ namespace OpenSim.Region.ClientStack.Linden | |||
221 | { | 202 | { |
222 | Queue<OSD> queue = GetQueue(avatarID); | 203 | Queue<OSD> queue = GetQueue(avatarID); |
223 | if (queue != null) | 204 | if (queue != null) |
205 | { | ||
224 | lock (queue) | 206 | lock (queue) |
225 | queue.Enqueue(ev); | 207 | queue.Enqueue(ev); |
208 | } | ||
209 | else | ||
210 | { | ||
211 | OSDMap evMap = (OSDMap)ev; | ||
212 | m_log.WarnFormat( | ||
213 | "[EVENTQUEUE]: (Enqueue) No queue found for agent {0} when placing message {1} in region {2}", | ||
214 | avatarID, evMap["message"], m_scene.Name); | ||
215 | } | ||
226 | } | 216 | } |
227 | catch (NullReferenceException e) | 217 | catch (NullReferenceException e) |
228 | { | 218 | { |
@@ -237,77 +227,22 @@ namespace OpenSim.Region.ClientStack.Linden | |||
237 | 227 | ||
238 | private void ClientClosed(UUID agentID, Scene scene) | 228 | private void ClientClosed(UUID agentID, Scene scene) |
239 | { | 229 | { |
240 | // m_log.DebugFormat("[EVENTQUEUE]: Closed client {0} in region {1}", agentID, m_scene.RegionInfo.RegionName); | 230 | //m_log.DebugFormat("[EVENTQUEUE]: Closed client {0} in region {1}", agentID, m_scene.RegionInfo.RegionName); |
241 | |||
242 | int count = 0; | ||
243 | while (queues.ContainsKey(agentID) && queues[agentID].Count > 0 && count++ < 5) | ||
244 | { | ||
245 | Thread.Sleep(1000); | ||
246 | } | ||
247 | 231 | ||
248 | lock (queues) | 232 | lock (queues) |
249 | { | ||
250 | queues.Remove(agentID); | 233 | queues.Remove(agentID); |
251 | } | ||
252 | 234 | ||
253 | List<UUID> removeitems = new List<UUID>(); | ||
254 | lock (m_AvatarQueueUUIDMapping) | 235 | lock (m_AvatarQueueUUIDMapping) |
255 | { | 236 | m_AvatarQueueUUIDMapping.Remove(agentID); |
256 | foreach (UUID ky in m_AvatarQueueUUIDMapping.Keys) | ||
257 | { | ||
258 | // m_log.DebugFormat("[EVENTQUEUE]: Found key {0} in m_AvatarQueueUUIDMapping while looking for {1}", ky, AgentID); | ||
259 | if (ky == agentID) | ||
260 | { | ||
261 | removeitems.Add(ky); | ||
262 | } | ||
263 | } | ||
264 | |||
265 | foreach (UUID ky in removeitems) | ||
266 | { | ||
267 | UUID eventQueueGetUuid = m_AvatarQueueUUIDMapping[ky]; | ||
268 | m_AvatarQueueUUIDMapping.Remove(ky); | ||
269 | 237 | ||
270 | string eqgPath = GenerateEqgCapPath(eventQueueGetUuid); | 238 | lock (m_ids) |
271 | MainServer.Instance.RemovePollServiceHTTPHandler("", eqgPath); | ||
272 | |||
273 | // m_log.DebugFormat( | ||
274 | // "[EVENT QUEUE GET MODULE]: Removed EQG handler {0} for {1} in {2}", | ||
275 | // eqgPath, agentID, m_scene.RegionInfo.RegionName); | ||
276 | } | ||
277 | } | ||
278 | |||
279 | UUID searchval = UUID.Zero; | ||
280 | |||
281 | removeitems.Clear(); | ||
282 | |||
283 | lock (m_QueueUUIDAvatarMapping) | ||
284 | { | 239 | { |
285 | foreach (UUID ky in m_QueueUUIDAvatarMapping.Keys) | 240 | if (!m_ids.ContainsKey(agentID)) |
286 | { | 241 | m_ids.Remove(agentID); |
287 | searchval = m_QueueUUIDAvatarMapping[ky]; | ||
288 | |||
289 | if (searchval == agentID) | ||
290 | { | ||
291 | removeitems.Add(ky); | ||
292 | } | ||
293 | } | ||
294 | |||
295 | foreach (UUID ky in removeitems) | ||
296 | m_QueueUUIDAvatarMapping.Remove(ky); | ||
297 | } | 242 | } |
298 | } | ||
299 | 243 | ||
300 | private void MakeChildAgent(ScenePresence avatar) | 244 | // m_log.DebugFormat("[EVENTQUEUE]: Deleted queues for {0} in region {1}", agentID, m_scene.RegionInfo.RegionName); |
301 | { | 245 | |
302 | //m_log.DebugFormat("[EVENTQUEUE]: Make Child agent {0} in region {1}.", avatar.UUID, m_scene.RegionInfo.RegionName); | ||
303 | //lock (m_ids) | ||
304 | // { | ||
305 | //if (m_ids.ContainsKey(avatar.UUID)) | ||
306 | //{ | ||
307 | // close the event queue. | ||
308 | //m_ids[avatar.UUID] = -1; | ||
309 | //} | ||
310 | //} | ||
311 | } | 246 | } |
312 | 247 | ||
313 | /// <summary> | 248 | /// <summary> |
@@ -322,85 +257,109 @@ namespace OpenSim.Region.ClientStack.Linden | |||
322 | public void OnRegisterCaps(UUID agentID, Caps caps) | 257 | public void OnRegisterCaps(UUID agentID, Caps caps) |
323 | { | 258 | { |
324 | // Register an event queue for the client | 259 | // Register an event queue for the client |
325 | 260 | m_log.DebugFormat( | |
326 | //m_log.DebugFormat( | 261 | "[EVENTQUEUE]: OnRegisterCaps: agentID {0} caps {1} region {2}", |
327 | // "[EVENTQUEUE]: OnRegisterCaps: agentID {0} caps {1} region {2}", | 262 | agentID, caps, m_scene.RegionInfo.RegionName); |
328 | // agentID, caps, m_scene.RegionInfo.RegionName); | ||
329 | |||
330 | // Let's instantiate a Queue for this agent right now | ||
331 | TryGetQueue(agentID); | ||
332 | 263 | ||
333 | UUID eventQueueGetUUID; | 264 | UUID eventQueueGetUUID; |
265 | Queue<OSD> queue; | ||
266 | Random rnd = new Random(Environment.TickCount); | ||
267 | int nrnd = rnd.Next(30000000); | ||
268 | if (nrnd < 0) | ||
269 | nrnd = -nrnd; | ||
334 | 270 | ||
335 | lock (m_AvatarQueueUUIDMapping) | 271 | lock (queues) |
336 | { | 272 | { |
337 | // Reuse open queues. The client does! | 273 | if (queues.ContainsKey(agentID)) |
338 | if (m_AvatarQueueUUIDMapping.ContainsKey(agentID)) | 274 | queue = queues[agentID]; |
275 | else | ||
276 | queue = null; | ||
277 | |||
278 | if (queue == null) | ||
339 | { | 279 | { |
340 | //m_log.DebugFormat("[EVENTQUEUE]: Found Existing UUID!"); | 280 | queue = new Queue<OSD>(); |
341 | eventQueueGetUUID = m_AvatarQueueUUIDMapping[agentID]; | 281 | queues[agentID] = queue; |
282 | |||
283 | // push markers to handle old responses still waiting | ||
284 | // this will cost at most viewer getting two forced noevents | ||
285 | // even being a new queue better be safe | ||
286 | queue.Enqueue(null); | ||
287 | queue.Enqueue(null); // one should be enough | ||
288 | |||
289 | lock (m_AvatarQueueUUIDMapping) | ||
290 | { | ||
291 | eventQueueGetUUID = UUID.Random(); | ||
292 | if (m_AvatarQueueUUIDMapping.ContainsKey(agentID)) | ||
293 | { | ||
294 | // oops this should not happen ? | ||
295 | m_log.DebugFormat("[EVENTQUEUE]: Found Existing UUID without a queue"); | ||
296 | eventQueueGetUUID = m_AvatarQueueUUIDMapping[agentID]; | ||
297 | } | ||
298 | m_AvatarQueueUUIDMapping.Add(agentID, eventQueueGetUUID); | ||
299 | } | ||
300 | lock (m_ids) | ||
301 | { | ||
302 | if (!m_ids.ContainsKey(agentID)) | ||
303 | m_ids.Add(agentID, nrnd); | ||
304 | else | ||
305 | m_ids[agentID] = nrnd; | ||
306 | } | ||
342 | } | 307 | } |
343 | else | 308 | else |
344 | { | 309 | { |
345 | eventQueueGetUUID = UUID.Random(); | 310 | // push markers to handle old responses still waiting |
346 | //m_log.DebugFormat("[EVENTQUEUE]: Using random UUID!"); | 311 | // this will cost at most viewer getting two forced noevents |
312 | // even being a new queue better be safe | ||
313 | queue.Enqueue(null); | ||
314 | queue.Enqueue(null); // one should be enough | ||
315 | |||
316 | // reuse or not to reuse TODO FIX | ||
317 | lock (m_AvatarQueueUUIDMapping) | ||
318 | { | ||
319 | // Reuse open queues. The client does! | ||
320 | // Its reuse caps path not queues those are been reused already | ||
321 | if (m_AvatarQueueUUIDMapping.ContainsKey(agentID)) | ||
322 | { | ||
323 | m_log.DebugFormat("[EVENTQUEUE]: Found Existing UUID!"); | ||
324 | eventQueueGetUUID = m_AvatarQueueUUIDMapping[agentID]; | ||
325 | } | ||
326 | else | ||
327 | { | ||
328 | eventQueueGetUUID = UUID.Random(); | ||
329 | m_AvatarQueueUUIDMapping.Add(agentID, eventQueueGetUUID); | ||
330 | m_log.DebugFormat("[EVENTQUEUE]: Using random UUID!"); | ||
331 | } | ||
332 | } | ||
333 | lock (m_ids) | ||
334 | { | ||
335 | // change to negative numbers so they are changed at end of sending first marker | ||
336 | // old data on a queue may be sent on a response for a new caps | ||
337 | // but at least will be sent with coerent IDs | ||
338 | if (!m_ids.ContainsKey(agentID)) | ||
339 | m_ids.Add(agentID, -nrnd); // should not happen | ||
340 | else | ||
341 | m_ids[agentID] = -m_ids[agentID]; | ||
342 | } | ||
347 | } | 343 | } |
348 | } | 344 | } |
349 | 345 | ||
350 | lock (m_QueueUUIDAvatarMapping) | 346 | caps.RegisterPollHandler( |
351 | { | 347 | "EventQueueGet", |
352 | if (!m_QueueUUIDAvatarMapping.ContainsKey(eventQueueGetUUID)) | 348 | new PollServiceEventArgs(null, GenerateEqgCapPath(eventQueueGetUUID), HasEvents, GetEvents, NoEvents, agentID, SERVER_EQ_TIME_NO_EVENTS)); |
353 | m_QueueUUIDAvatarMapping.Add(eventQueueGetUUID, agentID); | ||
354 | } | ||
355 | |||
356 | lock (m_AvatarQueueUUIDMapping) | ||
357 | { | ||
358 | if (!m_AvatarQueueUUIDMapping.ContainsKey(agentID)) | ||
359 | m_AvatarQueueUUIDMapping.Add(agentID, eventQueueGetUUID); | ||
360 | } | ||
361 | |||
362 | string eventQueueGetPath = GenerateEqgCapPath(eventQueueGetUUID); | ||
363 | |||
364 | // Register this as a caps handler | ||
365 | // FIXME: Confusingly, we need to register separate as a capability so that the client is told about | ||
366 | // EventQueueGet when it receive capability information, but then we replace the rest handler immediately | ||
367 | // afterwards with the poll service. So for now, we'll pass a null instead to simplify code reading, but | ||
368 | // really it should be possible to directly register the poll handler as a capability. | ||
369 | caps.RegisterHandler("EventQueueGet", new RestHTTPHandler("POST", eventQueueGetPath, null)); | ||
370 | // delegate(Hashtable m_dhttpMethod) | ||
371 | // { | ||
372 | // return ProcessQueue(m_dhttpMethod, agentID, caps); | ||
373 | // })); | ||
374 | |||
375 | // This will persist this beyond the expiry of the caps handlers | ||
376 | // TODO: Add EventQueueGet name/description for diagnostics | ||
377 | MainServer.Instance.AddPollServiceHTTPHandler( | ||
378 | eventQueueGetPath, | ||
379 | new PollServiceEventArgs(null, HasEvents, GetEvents, NoEvents, agentID, 40000)); | ||
380 | |||
381 | // m_log.DebugFormat( | ||
382 | // "[EVENT QUEUE GET MODULE]: Registered EQG handler {0} for {1} in {2}", | ||
383 | // eventQueueGetPath, agentID, m_scene.RegionInfo.RegionName); | ||
384 | |||
385 | Random rnd = new Random(Environment.TickCount); | ||
386 | lock (m_ids) | ||
387 | { | ||
388 | if (!m_ids.ContainsKey(agentID)) | ||
389 | m_ids.Add(agentID, rnd.Next(30000000)); | ||
390 | } | ||
391 | } | 349 | } |
392 | 350 | ||
393 | public bool HasEvents(UUID requestID, UUID agentID) | 351 | public bool HasEvents(UUID requestID, UUID agentID) |
394 | { | 352 | { |
395 | // Don't use this, because of race conditions at agent closing time | ||
396 | //Queue<OSD> queue = TryGetQueue(agentID); | ||
397 | |||
398 | Queue<OSD> queue = GetQueue(agentID); | 353 | Queue<OSD> queue = GetQueue(agentID); |
399 | if (queue != null) | 354 | if (queue != null) |
400 | lock (queue) | 355 | lock (queue) |
356 | { | ||
357 | //m_log.WarnFormat("POLLED FOR EVENTS BY {0} in {1} -- {2}", agentID, m_scene.RegionInfo.RegionName, queue.Count); | ||
401 | return queue.Count > 0; | 358 | return queue.Count > 0; |
359 | } | ||
402 | 360 | ||
403 | return false; | 361 | //m_log.WarnFormat("POLLED FOR EVENTS BY {0} unknown agent", agentID); |
362 | return true; | ||
404 | } | 363 | } |
405 | 364 | ||
406 | /// <summary> | 365 | /// <summary> |
@@ -414,65 +373,80 @@ namespace OpenSim.Region.ClientStack.Linden | |||
414 | OSDMap ev = (OSDMap)element; | 373 | OSDMap ev = (OSDMap)element; |
415 | m_log.DebugFormat( | 374 | m_log.DebugFormat( |
416 | "Eq OUT {0,-30} to {1,-20} {2,-20}", | 375 | "Eq OUT {0,-30} to {1,-20} {2,-20}", |
417 | ev["message"], m_scene.GetScenePresence(agentId).Name, m_scene.RegionInfo.RegionName); | 376 | ev["message"], m_scene.GetScenePresence(agentId).Name, m_scene.Name); |
418 | } | 377 | } |
419 | } | 378 | } |
420 | 379 | ||
421 | public Hashtable GetEvents(UUID requestID, UUID pAgentId) | 380 | public Hashtable GetEvents(UUID requestID, UUID pAgentId) |
422 | { | 381 | { |
423 | if (DebugLevel >= 2) | 382 | if (DebugLevel >= 2) |
424 | m_log.DebugFormat("POLLED FOR EQ MESSAGES BY {0} in {1}", pAgentId, m_scene.RegionInfo.RegionName); | 383 | m_log.WarnFormat("POLLED FOR EQ MESSAGES BY {0} in {1}", pAgentId, m_scene.Name); |
425 | 384 | ||
426 | Queue<OSD> queue = TryGetQueue(pAgentId); | 385 | Queue<OSD> queue = GetQueue(pAgentId); |
427 | OSD element; | 386 | if (queue == null) |
428 | lock (queue) | ||
429 | { | 387 | { |
430 | if (queue.Count == 0) | 388 | return NoEvents(requestID, pAgentId); |
431 | return NoEvents(requestID, pAgentId); | ||
432 | element = queue.Dequeue(); // 15s timeout | ||
433 | } | 389 | } |
434 | 390 | ||
391 | OSD element = null;; | ||
392 | OSDArray array = new OSDArray(); | ||
435 | int thisID = 0; | 393 | int thisID = 0; |
436 | lock (m_ids) | 394 | bool negativeID = false; |
437 | thisID = m_ids[pAgentId]; | ||
438 | 395 | ||
439 | OSDArray array = new OSDArray(); | 396 | lock (queue) |
440 | if (element == null) // didn't have an event in 15s | ||
441 | { | ||
442 | // Send it a fake event to keep the client polling! It doesn't like 502s like the proxys say! | ||
443 | array.Add(EventQueueHelper.KeepAliveEvent()); | ||
444 | //m_log.DebugFormat("[EVENTQUEUE]: adding fake event for {0} in region {1}", pAgentId, m_scene.RegionInfo.RegionName); | ||
445 | } | ||
446 | else | ||
447 | { | 397 | { |
448 | if (DebugLevel > 0) | 398 | if (queue.Count == 0) |
449 | LogOutboundDebugMessage(element, pAgentId); | 399 | return NoEvents(requestID, pAgentId); |
450 | 400 | ||
451 | array.Add(element); | 401 | lock (m_ids) |
402 | thisID = m_ids[pAgentId]; | ||
452 | 403 | ||
453 | lock (queue) | 404 | if (thisID < 0) |
454 | { | 405 | { |
455 | while (queue.Count > 0) | 406 | negativeID = true; |
456 | { | 407 | thisID = -thisID; |
457 | element = queue.Dequeue(); | 408 | } |
409 | |||
410 | while (queue.Count > 0) | ||
411 | { | ||
412 | element = queue.Dequeue(); | ||
413 | // add elements until a marker is found | ||
414 | // so they get into a response | ||
415 | if (element == null) | ||
416 | break; | ||
417 | if (DebugLevel > 0) | ||
418 | LogOutboundDebugMessage(element, pAgentId); | ||
419 | array.Add(element); | ||
420 | thisID++; | ||
421 | } | ||
422 | } | ||
458 | 423 | ||
459 | if (DebugLevel > 0) | 424 | OSDMap events = null; |
460 | LogOutboundDebugMessage(element, pAgentId); | ||
461 | 425 | ||
462 | array.Add(element); | 426 | if (array.Count > 0) |
463 | thisID++; | 427 | { |
464 | } | 428 | events = new OSDMap(); |
465 | } | 429 | events.Add("events", array); |
430 | events.Add("id", new OSDInteger(thisID)); | ||
466 | } | 431 | } |
467 | 432 | ||
468 | OSDMap events = new OSDMap(); | 433 | if (negativeID && element == null) |
469 | events.Add("events", array); | 434 | { |
435 | Random rnd = new Random(Environment.TickCount); | ||
436 | thisID = rnd.Next(30000000); | ||
437 | if (thisID < 0) | ||
438 | thisID = -thisID; | ||
439 | } | ||
470 | 440 | ||
471 | events.Add("id", new OSDInteger(thisID)); | ||
472 | lock (m_ids) | 441 | lock (m_ids) |
473 | { | 442 | { |
474 | m_ids[pAgentId] = thisID + 1; | 443 | m_ids[pAgentId] = thisID + 1; |
475 | } | 444 | } |
445 | |||
446 | // if there where no elements before a marker send a NoEvents | ||
447 | if (array.Count == 0) | ||
448 | return NoEvents(requestID, pAgentId); | ||
449 | |||
476 | Hashtable responsedata = new Hashtable(); | 450 | Hashtable responsedata = new Hashtable(); |
477 | responsedata["int_response_code"] = 200; | 451 | responsedata["int_response_code"] = 200; |
478 | responsedata["content_type"] = "application/xml"; | 452 | responsedata["content_type"] = "application/xml"; |
@@ -495,289 +469,53 @@ namespace OpenSim.Region.ClientStack.Linden | |||
495 | responsedata["http_protocol_version"] = "HTTP/1.0"; | 469 | responsedata["http_protocol_version"] = "HTTP/1.0"; |
496 | return responsedata; | 470 | return responsedata; |
497 | } | 471 | } |
498 | 472 | ||
499 | // public Hashtable ProcessQueue(Hashtable request, UUID agentID, Caps caps) | ||
500 | // { | ||
501 | // // TODO: this has to be redone to not busy-wait (and block the thread), | ||
502 | // // TODO: as soon as we have a non-blocking way to handle HTTP-requests. | ||
503 | // | ||
504 | //// if (m_log.IsDebugEnabled) | ||
505 | //// { | ||
506 | //// String debug = "[EVENTQUEUE]: Got request for agent {0} in region {1} from thread {2}: [ "; | ||
507 | //// foreach (object key in request.Keys) | ||
508 | //// { | ||
509 | //// debug += key.ToString() + "=" + request[key].ToString() + " "; | ||
510 | //// } | ||
511 | //// m_log.DebugFormat(debug + " ]", agentID, m_scene.RegionInfo.RegionName, System.Threading.Thread.CurrentThread.Name); | ||
512 | //// } | ||
513 | // | ||
514 | // Queue<OSD> queue = TryGetQueue(agentID); | ||
515 | // OSD element; | ||
516 | // | ||
517 | // lock (queue) | ||
518 | // element = queue.Dequeue(); // 15s timeout | ||
519 | // | ||
520 | // Hashtable responsedata = new Hashtable(); | ||
521 | // | ||
522 | // int thisID = 0; | ||
523 | // lock (m_ids) | ||
524 | // thisID = m_ids[agentID]; | ||
525 | // | ||
526 | // if (element == null) | ||
527 | // { | ||
528 | // //m_log.ErrorFormat("[EVENTQUEUE]: Nothing to process in " + m_scene.RegionInfo.RegionName); | ||
529 | // if (thisID == -1) // close-request | ||
530 | // { | ||
531 | // m_log.ErrorFormat("[EVENTQUEUE]: 404 in " + m_scene.RegionInfo.RegionName); | ||
532 | // responsedata["int_response_code"] = 404; //501; //410; //404; | ||
533 | // responsedata["content_type"] = "text/plain"; | ||
534 | // responsedata["keepalive"] = false; | ||
535 | // responsedata["str_response_string"] = "Closed EQG"; | ||
536 | // return responsedata; | ||
537 | // } | ||
538 | // responsedata["int_response_code"] = 502; | ||
539 | // responsedata["content_type"] = "text/plain"; | ||
540 | // responsedata["keepalive"] = false; | ||
541 | // responsedata["str_response_string"] = "Upstream error: "; | ||
542 | // responsedata["error_status_text"] = "Upstream error:"; | ||
543 | // responsedata["http_protocol_version"] = "HTTP/1.0"; | ||
544 | // return responsedata; | ||
545 | // } | ||
546 | // | ||
547 | // OSDArray array = new OSDArray(); | ||
548 | // if (element == null) // didn't have an event in 15s | ||
549 | // { | ||
550 | // // Send it a fake event to keep the client polling! It doesn't like 502s like the proxys say! | ||
551 | // array.Add(EventQueueHelper.KeepAliveEvent()); | ||
552 | // //m_log.DebugFormat("[EVENTQUEUE]: adding fake event for {0} in region {1}", agentID, m_scene.RegionInfo.RegionName); | ||
553 | // } | ||
554 | // else | ||
555 | // { | ||
556 | // array.Add(element); | ||
557 | // | ||
558 | // if (element is OSDMap) | ||
559 | // { | ||
560 | // OSDMap ev = (OSDMap)element; | ||
561 | // m_log.DebugFormat( | ||
562 | // "[EVENT QUEUE GET MODULE]: Eq OUT {0} to {1}", | ||
563 | // ev["message"], m_scene.GetScenePresence(agentID).Name); | ||
564 | // } | ||
565 | // | ||
566 | // lock (queue) | ||
567 | // { | ||
568 | // while (queue.Count > 0) | ||
569 | // { | ||
570 | // element = queue.Dequeue(); | ||
571 | // | ||
572 | // if (element is OSDMap) | ||
573 | // { | ||
574 | // OSDMap ev = (OSDMap)element; | ||
575 | // m_log.DebugFormat( | ||
576 | // "[EVENT QUEUE GET MODULE]: Eq OUT {0} to {1}", | ||
577 | // ev["message"], m_scene.GetScenePresence(agentID).Name); | ||
578 | // } | ||
579 | // | ||
580 | // array.Add(element); | ||
581 | // thisID++; | ||
582 | // } | ||
583 | // } | ||
584 | // } | ||
585 | // | ||
586 | // OSDMap events = new OSDMap(); | ||
587 | // events.Add("events", array); | ||
588 | // | ||
589 | // events.Add("id", new OSDInteger(thisID)); | ||
590 | // lock (m_ids) | ||
591 | // { | ||
592 | // m_ids[agentID] = thisID + 1; | ||
593 | // } | ||
594 | // | ||
595 | // responsedata["int_response_code"] = 200; | ||
596 | // responsedata["content_type"] = "application/xml"; | ||
597 | // responsedata["keepalive"] = false; | ||
598 | // responsedata["str_response_string"] = OSDParser.SerializeLLSDXmlString(events); | ||
599 | // | ||
600 | // m_log.DebugFormat("[EVENTQUEUE]: sending response for {0} in region {1}: {2}", agentID, m_scene.RegionInfo.RegionName, responsedata["str_response_string"]); | ||
601 | // | ||
602 | // return responsedata; | ||
603 | // } | ||
604 | |||
605 | // public Hashtable EventQueuePath2(Hashtable request) | ||
606 | // { | ||
607 | // string capuuid = (string)request["uri"]; //path.Replace("/CAPS/EQG/",""); | ||
608 | // // pull off the last "/" in the path. | ||
609 | // Hashtable responsedata = new Hashtable(); | ||
610 | // capuuid = capuuid.Substring(0, capuuid.Length - 1); | ||
611 | // capuuid = capuuid.Replace("/CAPS/EQG/", ""); | ||
612 | // UUID AvatarID = UUID.Zero; | ||
613 | // UUID capUUID = UUID.Zero; | ||
614 | // | ||
615 | // // parse the path and search for the avatar with it registered | ||
616 | // if (UUID.TryParse(capuuid, out capUUID)) | ||
617 | // { | ||
618 | // lock (m_QueueUUIDAvatarMapping) | ||
619 | // { | ||
620 | // if (m_QueueUUIDAvatarMapping.ContainsKey(capUUID)) | ||
621 | // { | ||
622 | // AvatarID = m_QueueUUIDAvatarMapping[capUUID]; | ||
623 | // } | ||
624 | // } | ||
625 | // | ||
626 | // if (AvatarID != UUID.Zero) | ||
627 | // { | ||
628 | // return ProcessQueue(request, AvatarID, m_scene.CapsModule.GetCapsForUser(AvatarID)); | ||
629 | // } | ||
630 | // else | ||
631 | // { | ||
632 | // responsedata["int_response_code"] = 404; | ||
633 | // responsedata["content_type"] = "text/plain"; | ||
634 | // responsedata["keepalive"] = false; | ||
635 | // responsedata["str_response_string"] = "Not Found"; | ||
636 | // responsedata["error_status_text"] = "Not Found"; | ||
637 | // responsedata["http_protocol_version"] = "HTTP/1.0"; | ||
638 | // return responsedata; | ||
639 | // // return 404 | ||
640 | // } | ||
641 | // } | ||
642 | // else | ||
643 | // { | ||
644 | // responsedata["int_response_code"] = 404; | ||
645 | // responsedata["content_type"] = "text/plain"; | ||
646 | // responsedata["keepalive"] = false; | ||
647 | // responsedata["str_response_string"] = "Not Found"; | ||
648 | // responsedata["error_status_text"] = "Not Found"; | ||
649 | // responsedata["http_protocol_version"] = "HTTP/1.0"; | ||
650 | // return responsedata; | ||
651 | // // return 404 | ||
652 | // } | ||
653 | // } | ||
654 | |||
655 | public OSD EventQueueFallBack(string path, OSD request, string endpoint) | ||
656 | { | ||
657 | // This is a fallback element to keep the client from loosing EventQueueGet | ||
658 | // Why does CAPS fail sometimes!? | ||
659 | m_log.Warn("[EVENTQUEUE]: In the Fallback handler! We lost the Queue in the rest handler!"); | ||
660 | string capuuid = path.Replace("/CAPS/EQG/",""); | ||
661 | capuuid = capuuid.Substring(0, capuuid.Length - 1); | ||
662 | |||
663 | // UUID AvatarID = UUID.Zero; | ||
664 | UUID capUUID = UUID.Zero; | ||
665 | if (UUID.TryParse(capuuid, out capUUID)) | ||
666 | { | ||
667 | /* Don't remove this yet code cleaners! | ||
668 | * Still testing this! | ||
669 | * | ||
670 | lock (m_QueueUUIDAvatarMapping) | ||
671 | { | ||
672 | if (m_QueueUUIDAvatarMapping.ContainsKey(capUUID)) | ||
673 | { | ||
674 | AvatarID = m_QueueUUIDAvatarMapping[capUUID]; | ||
675 | } | ||
676 | } | ||
677 | |||
678 | |||
679 | if (AvatarID != UUID.Zero) | ||
680 | { | ||
681 | // Repair the CAP! | ||
682 | //OpenSim.Framework.Capabilities.Caps caps = m_scene.GetCapsHandlerForUser(AvatarID); | ||
683 | //string capsBase = "/CAPS/EQG/"; | ||
684 | //caps.RegisterHandler("EventQueueGet", | ||
685 | //new RestHTTPHandler("POST", capsBase + capUUID.ToString() + "/", | ||
686 | //delegate(Hashtable m_dhttpMethod) | ||
687 | //{ | ||
688 | // return ProcessQueue(m_dhttpMethod, AvatarID, caps); | ||
689 | //})); | ||
690 | // start new ID sequence. | ||
691 | Random rnd = new Random(System.Environment.TickCount); | ||
692 | lock (m_ids) | ||
693 | { | ||
694 | if (!m_ids.ContainsKey(AvatarID)) | ||
695 | m_ids.Add(AvatarID, rnd.Next(30000000)); | ||
696 | } | ||
697 | |||
698 | |||
699 | int thisID = 0; | ||
700 | lock (m_ids) | ||
701 | thisID = m_ids[AvatarID]; | ||
702 | |||
703 | BlockingLLSDQueue queue = GetQueue(AvatarID); | ||
704 | OSDArray array = new OSDArray(); | ||
705 | LLSD element = queue.Dequeue(15000); // 15s timeout | ||
706 | if (element == null) | ||
707 | { | ||
708 | |||
709 | array.Add(EventQueueHelper.KeepAliveEvent()); | ||
710 | } | ||
711 | else | ||
712 | { | ||
713 | array.Add(element); | ||
714 | while (queue.Count() > 0) | ||
715 | { | ||
716 | array.Add(queue.Dequeue(1)); | ||
717 | thisID++; | ||
718 | } | ||
719 | } | ||
720 | OSDMap events = new OSDMap(); | ||
721 | events.Add("events", array); | ||
722 | |||
723 | events.Add("id", new LLSDInteger(thisID)); | ||
724 | |||
725 | lock (m_ids) | ||
726 | { | ||
727 | m_ids[AvatarID] = thisID + 1; | ||
728 | } | ||
729 | |||
730 | return events; | ||
731 | } | ||
732 | else | ||
733 | { | ||
734 | return new LLSD(); | ||
735 | } | ||
736 | * | ||
737 | */ | ||
738 | } | ||
739 | else | ||
740 | { | ||
741 | //return new LLSD(); | ||
742 | } | ||
743 | |||
744 | return new OSDString("shutdown404!"); | ||
745 | } | ||
746 | |||
747 | public void DisableSimulator(ulong handle, UUID avatarID) | 473 | public void DisableSimulator(ulong handle, UUID avatarID) |
748 | { | 474 | { |
749 | OSD item = EventQueueHelper.DisableSimulator(handle); | 475 | OSD item = EventQueueHelper.DisableSimulator(handle); |
750 | Enqueue(item, avatarID); | 476 | Enqueue(item, avatarID); |
751 | } | 477 | } |
752 | 478 | ||
753 | public virtual void EnableSimulator(ulong handle, IPEndPoint endPoint, UUID avatarID) | 479 | public virtual void EnableSimulator(ulong handle, IPEndPoint endPoint, UUID avatarID, int regionSizeX, int regionSizeY) |
754 | { | 480 | { |
755 | OSD item = EventQueueHelper.EnableSimulator(handle, endPoint); | 481 | m_log.DebugFormat("{0} EnableSimulator. handle={1}, avatarID={2}, regionSize={3},{4}>", |
482 | "[EVENT QUEUE GET MODULE]", handle, avatarID, regionSizeX, regionSizeY); | ||
483 | |||
484 | OSD item = EventQueueHelper.EnableSimulator(handle, endPoint, regionSizeX, regionSizeY); | ||
756 | Enqueue(item, avatarID); | 485 | Enqueue(item, avatarID); |
757 | } | 486 | } |
758 | 487 | ||
759 | public virtual void EstablishAgentCommunication(UUID avatarID, IPEndPoint endPoint, string capsPath) | 488 | public virtual void EstablishAgentCommunication(UUID avatarID, IPEndPoint endPoint, string capsPath, |
489 | ulong regionHandle, int regionSizeX, int regionSizeY) | ||
760 | { | 490 | { |
761 | OSD item = EventQueueHelper.EstablishAgentCommunication(avatarID, endPoint.ToString(), capsPath); | 491 | m_log.DebugFormat("{0} EstablishAgentCommunication. handle={1}, avatarID={2}, regionSize={3},{4}>", |
492 | "[EVENT QUEUE GET MODULE]", regionHandle, avatarID, regionSizeX, regionSizeY); | ||
493 | OSD item = EventQueueHelper.EstablishAgentCommunication(avatarID, endPoint.ToString(), capsPath, regionHandle, regionSizeX, regionSizeY); | ||
762 | Enqueue(item, avatarID); | 494 | Enqueue(item, avatarID); |
763 | } | 495 | } |
764 | 496 | ||
765 | public virtual void TeleportFinishEvent(ulong regionHandle, byte simAccess, | 497 | public virtual void TeleportFinishEvent(ulong regionHandle, byte simAccess, |
766 | IPEndPoint regionExternalEndPoint, | 498 | IPEndPoint regionExternalEndPoint, |
767 | uint locationID, uint flags, string capsURL, | 499 | uint locationID, uint flags, string capsURL, |
768 | UUID avatarID) | 500 | UUID avatarID, int regionSizeX, int regionSizeY) |
769 | { | 501 | { |
502 | m_log.DebugFormat("{0} TeleportFinishEvent. handle={1}, avatarID={2}, regionSize={3},{4}>", | ||
503 | "[EVENT QUEUE GET MODULE]", regionHandle, avatarID, regionSizeX, regionSizeY); | ||
504 | |||
770 | OSD item = EventQueueHelper.TeleportFinishEvent(regionHandle, simAccess, regionExternalEndPoint, | 505 | OSD item = EventQueueHelper.TeleportFinishEvent(regionHandle, simAccess, regionExternalEndPoint, |
771 | locationID, flags, capsURL, avatarID); | 506 | locationID, flags, capsURL, avatarID, regionSizeX, regionSizeY); |
772 | Enqueue(item, avatarID); | 507 | Enqueue(item, avatarID); |
773 | } | 508 | } |
774 | 509 | ||
775 | public virtual void CrossRegion(ulong handle, Vector3 pos, Vector3 lookAt, | 510 | public virtual void CrossRegion(ulong handle, Vector3 pos, Vector3 lookAt, |
776 | IPEndPoint newRegionExternalEndPoint, | 511 | IPEndPoint newRegionExternalEndPoint, |
777 | string capsURL, UUID avatarID, UUID sessionID) | 512 | string capsURL, UUID avatarID, UUID sessionID, int regionSizeX, int regionSizeY) |
778 | { | 513 | { |
514 | m_log.DebugFormat("{0} CrossRegion. handle={1}, avatarID={2}, regionSize={3},{4}>", | ||
515 | "[EVENT QUEUE GET MODULE]", handle, avatarID, regionSizeX, regionSizeY); | ||
516 | |||
779 | OSD item = EventQueueHelper.CrossRegion(handle, pos, lookAt, newRegionExternalEndPoint, | 517 | OSD item = EventQueueHelper.CrossRegion(handle, pos, lookAt, newRegionExternalEndPoint, |
780 | capsURL, avatarID, sessionID); | 518 | capsURL, avatarID, sessionID, regionSizeX, regionSizeY); |
781 | Enqueue(item, avatarID); | 519 | Enqueue(item, avatarID); |
782 | } | 520 | } |
783 | 521 | ||
@@ -794,12 +532,12 @@ namespace OpenSim.Region.ClientStack.Linden | |||
794 | 532 | ||
795 | } | 533 | } |
796 | 534 | ||
797 | public void ChatterBoxSessionAgentListUpdates(UUID sessionID, UUID fromAgent, UUID toAgent, bool canVoiceChat, | 535 | public void ChatterBoxSessionAgentListUpdates(UUID sessionID, UUID fromAgent, UUID anotherAgent, bool canVoiceChat, |
798 | bool isModerator, bool textMute) | 536 | bool isModerator, bool textMute) |
799 | { | 537 | { |
800 | OSD item = EventQueueHelper.ChatterBoxSessionAgentListUpdates(sessionID, fromAgent, canVoiceChat, | 538 | OSD item = EventQueueHelper.ChatterBoxSessionAgentListUpdates(sessionID, fromAgent, canVoiceChat, |
801 | isModerator, textMute); | 539 | isModerator, textMute); |
802 | Enqueue(item, toAgent); | 540 | Enqueue(item, fromAgent); |
803 | //m_log.InfoFormat("########### eq ChatterBoxSessionAgentListUpdates #############\n{0}", item); | 541 | //m_log.InfoFormat("########### eq ChatterBoxSessionAgentListUpdates #############\n{0}", item); |
804 | } | 542 | } |
805 | 543 | ||
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueHelper.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueHelper.cs index 7dcf137..3fb7de2 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueHelper.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueHelper.cs | |||
@@ -70,13 +70,15 @@ namespace OpenSim.Region.ClientStack.Linden | |||
70 | return llsdEvent; | 70 | return llsdEvent; |
71 | } | 71 | } |
72 | 72 | ||
73 | public static OSD EnableSimulator(ulong handle, IPEndPoint endPoint) | 73 | public static OSD EnableSimulator(ulong handle, IPEndPoint endPoint, int regionSizeX, int regionSizeY) |
74 | { | 74 | { |
75 | OSDMap llsdSimInfo = new OSDMap(3); | 75 | OSDMap llsdSimInfo = new OSDMap(5); |
76 | 76 | ||
77 | llsdSimInfo.Add("Handle", new OSDBinary(ulongToByteArray(handle))); | 77 | llsdSimInfo.Add("Handle", new OSDBinary(ulongToByteArray(handle))); |
78 | llsdSimInfo.Add("IP", new OSDBinary(endPoint.Address.GetAddressBytes())); | 78 | llsdSimInfo.Add("IP", new OSDBinary(endPoint.Address.GetAddressBytes())); |
79 | llsdSimInfo.Add("Port", new OSDInteger(endPoint.Port)); | 79 | llsdSimInfo.Add("Port", new OSDInteger(endPoint.Port)); |
80 | llsdSimInfo.Add("RegionSizeX", new OSDInteger(regionSizeX)); | ||
81 | llsdSimInfo.Add("RegionSizeY", new OSDInteger(regionSizeY)); | ||
80 | 82 | ||
81 | OSDArray arr = new OSDArray(1); | 83 | OSDArray arr = new OSDArray(1); |
82 | arr.Add(llsdSimInfo); | 84 | arr.Add(llsdSimInfo); |
@@ -104,7 +106,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
104 | 106 | ||
105 | public static OSD CrossRegion(ulong handle, Vector3 pos, Vector3 lookAt, | 107 | public static OSD CrossRegion(ulong handle, Vector3 pos, Vector3 lookAt, |
106 | IPEndPoint newRegionExternalEndPoint, | 108 | IPEndPoint newRegionExternalEndPoint, |
107 | string capsURL, UUID agentID, UUID sessionID) | 109 | string capsURL, UUID agentID, UUID sessionID, |
110 | int regionSizeX, int regionSizeY) | ||
108 | { | 111 | { |
109 | OSDArray lookAtArr = new OSDArray(3); | 112 | OSDArray lookAtArr = new OSDArray(3); |
110 | lookAtArr.Add(OSD.FromReal(lookAt.X)); | 113 | lookAtArr.Add(OSD.FromReal(lookAt.X)); |
@@ -130,11 +133,13 @@ namespace OpenSim.Region.ClientStack.Linden | |||
130 | OSDArray agentDataArr = new OSDArray(1); | 133 | OSDArray agentDataArr = new OSDArray(1); |
131 | agentDataArr.Add(agentDataMap); | 134 | agentDataArr.Add(agentDataMap); |
132 | 135 | ||
133 | OSDMap regionDataMap = new OSDMap(4); | 136 | OSDMap regionDataMap = new OSDMap(6); |
134 | regionDataMap.Add("RegionHandle", OSD.FromBinary(ulongToByteArray(handle))); | 137 | regionDataMap.Add("RegionHandle", OSD.FromBinary(ulongToByteArray(handle))); |
135 | regionDataMap.Add("SeedCapability", OSD.FromString(capsURL)); | 138 | regionDataMap.Add("SeedCapability", OSD.FromString(capsURL)); |
136 | regionDataMap.Add("SimIP", OSD.FromBinary(newRegionExternalEndPoint.Address.GetAddressBytes())); | 139 | regionDataMap.Add("SimIP", OSD.FromBinary(newRegionExternalEndPoint.Address.GetAddressBytes())); |
137 | regionDataMap.Add("SimPort", OSD.FromInteger(newRegionExternalEndPoint.Port)); | 140 | regionDataMap.Add("SimPort", OSD.FromInteger(newRegionExternalEndPoint.Port)); |
141 | regionDataMap.Add("RegionSizeX", new OSDInteger(regionSizeX)); | ||
142 | regionDataMap.Add("RegionSizeY", new OSDInteger(regionSizeY)); | ||
138 | 143 | ||
139 | OSDArray regionDataArr = new OSDArray(1); | 144 | OSDArray regionDataArr = new OSDArray(1); |
140 | regionDataArr.Add(regionDataMap); | 145 | regionDataArr.Add(regionDataMap); |
@@ -148,8 +153,9 @@ namespace OpenSim.Region.ClientStack.Linden | |||
148 | } | 153 | } |
149 | 154 | ||
150 | public static OSD TeleportFinishEvent( | 155 | public static OSD TeleportFinishEvent( |
151 | ulong regionHandle, byte simAccess, IPEndPoint regionExternalEndPoint, | 156 | ulong regionHandle, byte simAccess, IPEndPoint regionExternalEndPoint, |
152 | uint locationID, uint flags, string capsURL, UUID agentID) | 157 | uint locationID, uint flags, string capsURL, UUID agentID, |
158 | int regionSizeX, int regionSizeY) | ||
153 | { | 159 | { |
154 | // not sure why flags get overwritten here | 160 | // not sure why flags get overwritten here |
155 | if ((flags & (uint)TeleportFlags.IsFlying) != 0) | 161 | if ((flags & (uint)TeleportFlags.IsFlying) != 0) |
@@ -167,6 +173,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
167 | info.Add("SimPort", OSD.FromInteger(regionExternalEndPoint.Port)); | 173 | info.Add("SimPort", OSD.FromInteger(regionExternalEndPoint.Port)); |
168 | // info.Add("TeleportFlags", OSD.FromULong(1L << 4)); // AgentManager.TeleportFlags.ViaLocation | 174 | // info.Add("TeleportFlags", OSD.FromULong(1L << 4)); // AgentManager.TeleportFlags.ViaLocation |
169 | info.Add("TeleportFlags", OSD.FromUInteger(flags)); | 175 | info.Add("TeleportFlags", OSD.FromUInteger(flags)); |
176 | info.Add("RegionSizeX", new OSDInteger(regionSizeX)); | ||
177 | info.Add("RegionSizeY", new OSDInteger(regionSizeY)); | ||
170 | 178 | ||
171 | OSDArray infoArr = new OSDArray(); | 179 | OSDArray infoArr = new OSDArray(); |
172 | infoArr.Add(info); | 180 | infoArr.Add(info); |
@@ -194,12 +202,18 @@ namespace OpenSim.Region.ClientStack.Linden | |||
194 | return BuildEvent("ScriptRunningReply", body); | 202 | return BuildEvent("ScriptRunningReply", body); |
195 | } | 203 | } |
196 | 204 | ||
197 | public static OSD EstablishAgentCommunication(UUID agentID, string simIpAndPort, string seedcap) | 205 | public static OSD EstablishAgentCommunication(UUID agentID, string simIpAndPort, string seedcap, |
206 | ulong regionHandle, int regionSizeX, int regionSizeY) | ||
198 | { | 207 | { |
199 | OSDMap body = new OSDMap(3); | 208 | OSDMap body = new OSDMap(6) |
200 | body.Add("agent-id", new OSDUUID(agentID)); | 209 | { |
201 | body.Add("sim-ip-and-port", new OSDString(simIpAndPort)); | 210 | {"agent-id", new OSDUUID(agentID)}, |
202 | body.Add("seed-capability", new OSDString(seedcap)); | 211 | {"sim-ip-and-port", new OSDString(simIpAndPort)}, |
212 | {"seed-capability", new OSDString(seedcap)}, | ||
213 | {"region-handle", OSD.FromULong(regionHandle)}, | ||
214 | {"region-size-x", OSD.FromInteger(regionSizeX)}, | ||
215 | {"region-size-y", OSD.FromInteger(regionSizeY)} | ||
216 | }; | ||
203 | 217 | ||
204 | return BuildEvent("EstablishAgentCommunication", body); | 218 | return BuildEvent("EstablishAgentCommunication", body); |
205 | } | 219 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs index 141af8a..9e24bce 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs | |||
@@ -76,7 +76,7 @@ namespace OpenSim.Region.ClientStack.Linden.Tests | |||
76 | } | 76 | } |
77 | 77 | ||
78 | [Test] | 78 | [Test] |
79 | public void AddForClient() | 79 | public void TestAddForClient() |
80 | { | 80 | { |
81 | TestHelpers.InMethod(); | 81 | TestHelpers.InMethod(); |
82 | // log4net.Config.XmlConfigurator.Configure(); | 82 | // log4net.Config.XmlConfigurator.Configure(); |
@@ -88,15 +88,15 @@ namespace OpenSim.Region.ClientStack.Linden.Tests | |||
88 | } | 88 | } |
89 | 89 | ||
90 | [Test] | 90 | [Test] |
91 | public void RemoveForClient() | 91 | public void TestRemoveForClient() |
92 | { | 92 | { |
93 | TestHelpers.InMethod(); | 93 | TestHelpers.InMethod(); |
94 | // log4net.Config.XmlConfigurator.Configure(); | 94 | // TestHelpers.EnableLogging(); |
95 | 95 | ||
96 | UUID spId = TestHelpers.ParseTail(0x1); | 96 | UUID spId = TestHelpers.ParseTail(0x1); |
97 | 97 | ||
98 | SceneHelpers.AddScenePresence(m_scene, spId); | 98 | SceneHelpers.AddScenePresence(m_scene, spId); |
99 | m_scene.IncomingCloseAgent(spId, false); | 99 | m_scene.CloseAgent(spId, false); |
100 | 100 | ||
101 | // TODO: Add more assertions for the other aspects of event queues | 101 | // TODO: Add more assertions for the other aspects of event queues |
102 | Assert.That(MainServer.Instance.GetPollServiceHandlerKeys().Count, Is.EqualTo(0)); | 102 | Assert.That(MainServer.Instance.GetPollServiceHandlerKeys().Count, Is.EqualTo(0)); |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs index 6ec1115..a381a1b 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs | |||
@@ -189,9 +189,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
189 | // Now we know when the throttle is changed by the client in the case of a root agent or by a neighbor region in the case of a child agent. | 189 | // Now we know when the throttle is changed by the client in the case of a root agent or by a neighbor region in the case of a child agent. |
190 | public void ThrottleUpdate(ScenePresence p) | 190 | public void ThrottleUpdate(ScenePresence p) |
191 | { | 191 | { |
192 | byte[] throttles = p.ControllingClient.GetThrottlesPacked(1); | ||
193 | UUID user = p.UUID; | 192 | UUID user = p.UUID; |
194 | int imagethrottle = ExtractTaskThrottle(throttles); | 193 | int imagethrottle = p.ControllingClient.GetAgentThrottleSilent((int)ThrottleOutPacketType.Asset); |
195 | PollServiceMeshEventArgs args; | 194 | PollServiceMeshEventArgs args; |
196 | if (m_pollservices.TryGetValue(user, out args)) | 195 | if (m_pollservices.TryGetValue(user, out args)) |
197 | { | 196 | { |
@@ -199,44 +198,6 @@ namespace OpenSim.Region.ClientStack.Linden | |||
199 | } | 198 | } |
200 | } | 199 | } |
201 | 200 | ||
202 | private int ExtractTaskThrottle(byte[] pthrottles) | ||
203 | { | ||
204 | |||
205 | byte[] adjData; | ||
206 | int pos = 0; | ||
207 | |||
208 | if (!BitConverter.IsLittleEndian) | ||
209 | { | ||
210 | byte[] newData = new byte[7 * 4]; | ||
211 | Buffer.BlockCopy(pthrottles, 0, newData, 0, 7 * 4); | ||
212 | |||
213 | for (int i = 0; i < 7; i++) | ||
214 | Array.Reverse(newData, i * 4, 4); | ||
215 | |||
216 | adjData = newData; | ||
217 | } | ||
218 | else | ||
219 | { | ||
220 | adjData = pthrottles; | ||
221 | } | ||
222 | |||
223 | // 0.125f converts from bits to bytes | ||
224 | //int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | ||
225 | //pos += 4; | ||
226 | // int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | ||
227 | //pos += 4; | ||
228 | // int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | ||
229 | // pos += 4; | ||
230 | // int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | ||
231 | // pos += 4; | ||
232 | pos += 16; | ||
233 | int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | ||
234 | // pos += 4; | ||
235 | //int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); //pos += 4; | ||
236 | //int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | ||
237 | return task; | ||
238 | } | ||
239 | |||
240 | private class PollServiceMeshEventArgs : PollServiceEventArgs | 201 | private class PollServiceMeshEventArgs : PollServiceEventArgs |
241 | { | 202 | { |
242 | private List<Hashtable> requests = | 203 | private List<Hashtable> requests = |
@@ -246,8 +207,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
246 | 207 | ||
247 | private Scene m_scene; | 208 | private Scene m_scene; |
248 | private MeshCapsDataThrottler m_throttler; | 209 | private MeshCapsDataThrottler m_throttler; |
249 | public PollServiceMeshEventArgs(UUID pId, Scene scene) : | 210 | public PollServiceMeshEventArgs(string uri, UUID pId, Scene scene) : |
250 | base(null, null, null, null, pId, int.MaxValue) | 211 | base(null, uri, null, null, null, pId, int.MaxValue) |
251 | { | 212 | { |
252 | m_scene = scene; | 213 | m_scene = scene; |
253 | m_throttler = new MeshCapsDataThrottler(100000, 1400000, 10000, scene, pId); | 214 | m_throttler = new MeshCapsDataThrottler(100000, 1400000, 10000, scene, pId); |
@@ -361,7 +322,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
361 | string capUrl = "/CAPS/" + UUID.Random() + "/"; | 322 | string capUrl = "/CAPS/" + UUID.Random() + "/"; |
362 | 323 | ||
363 | // Register this as a poll service | 324 | // Register this as a poll service |
364 | PollServiceMeshEventArgs args = new PollServiceMeshEventArgs(agentID, m_scene); | 325 | PollServiceMeshEventArgs args = new PollServiceMeshEventArgs(capUrl, agentID, m_scene); |
365 | 326 | ||
366 | args.Type = PollServiceEventArgs.EventType.Mesh; | 327 | args.Type = PollServiceEventArgs.EventType.Mesh; |
367 | MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args); | 328 | MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args); |
@@ -425,7 +386,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
425 | { | 386 | { |
426 | ThrottleBytes = pBytes; | 387 | ThrottleBytes = pBytes; |
427 | lastTimeElapsed = Util.EnvironmentTickCount(); | 388 | lastTimeElapsed = Util.EnvironmentTickCount(); |
428 | Throttle = ThrottleOutPacketType.Task; | 389 | Throttle = ThrottleOutPacketType.Asset; |
429 | m_scene = pScene; | 390 | m_scene = pScene; |
430 | User = puser; | 391 | User = puser; |
431 | } | 392 | } |
@@ -550,8 +511,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
550 | // UDPSetThrottle = (int) (pimagethrottle*(100 - CapThrottleDistributon)); | 511 | // UDPSetThrottle = (int) (pimagethrottle*(100 - CapThrottleDistributon)); |
551 | 512 | ||
552 | float udp = 1.0f - CapThrottleDistributon; | 513 | float udp = 1.0f - CapThrottleDistributon; |
553 | if(udp < 0.5f) | 514 | if(udp < 0.7f) |
554 | udp = 0.5f; | 515 | udp = 0.7f; |
555 | UDPSetThrottle = (int) ((float)pimagethrottle * udp); | 516 | UDPSetThrottle = (int) ((float)pimagethrottle * udp); |
556 | if (CapSetThrottle < 4068) | 517 | if (CapSetThrottle < 4068) |
557 | CapSetThrottle = 4068; // at least two discovery mesh | 518 | CapSetThrottle = 4068; // at least two discovery mesh |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs index 0570144..e053054 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs | |||
@@ -84,6 +84,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
84 | 84 | ||
85 | private Dictionary<UUID,PollServiceTextureEventArgs> m_pollservices = new Dictionary<UUID,PollServiceTextureEventArgs>(); | 85 | private Dictionary<UUID,PollServiceTextureEventArgs> m_pollservices = new Dictionary<UUID,PollServiceTextureEventArgs>(); |
86 | 86 | ||
87 | private string m_URL; | ||
88 | |||
87 | #region ISharedRegionModule Members | 89 | #region ISharedRegionModule Members |
88 | 90 | ||
89 | public void Initialise(IConfigSource source) | 91 | public void Initialise(IConfigSource source) |
@@ -215,7 +217,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
215 | private Scene m_scene; | 217 | private Scene m_scene; |
216 | private CapsDataThrottler m_throttler = new CapsDataThrottler(100000, 1400000,10000); | 218 | private CapsDataThrottler m_throttler = new CapsDataThrottler(100000, 1400000,10000); |
217 | public PollServiceTextureEventArgs(UUID pId, Scene scene) : | 219 | public PollServiceTextureEventArgs(UUID pId, Scene scene) : |
218 | base(null, null, null, null, pId, int.MaxValue) | 220 | base(null, "", null, null, null, pId, int.MaxValue) |
219 | { | 221 | { |
220 | m_scene = scene; | 222 | m_scene = scene; |
221 | // x is request id, y is userid | 223 | // x is request id, y is userid |
@@ -368,7 +370,11 @@ namespace OpenSim.Region.ClientStack.Linden | |||
368 | port = MainServer.Instance.SSLPort; | 370 | port = MainServer.Instance.SSLPort; |
369 | protocol = "https"; | 371 | protocol = "https"; |
370 | } | 372 | } |
371 | caps.RegisterHandler("GetTexture", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl)); | 373 | IExternalCapsModule handler = m_scene.RequestModuleInterface<IExternalCapsModule>(); |
374 | if (handler != null) | ||
375 | handler.RegisterExternalUserCapsHandler(agentID, caps, "GetTexture", capUrl); | ||
376 | else | ||
377 | caps.RegisterHandler("GetTexture", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl)); | ||
372 | m_pollservices[agentID] = args; | 378 | m_pollservices[agentID] = args; |
373 | m_capsDict[agentID] = capUrl; | 379 | m_capsDict[agentID] = capUrl; |
374 | } | 380 | } |
@@ -380,13 +386,11 @@ namespace OpenSim.Region.ClientStack.Linden | |||
380 | 386 | ||
381 | private void DeregisterCaps(UUID agentID, Caps caps) | 387 | private void DeregisterCaps(UUID agentID, Caps caps) |
382 | { | 388 | { |
383 | string capUrl; | ||
384 | PollServiceTextureEventArgs args; | 389 | PollServiceTextureEventArgs args; |
385 | if (m_capsDict.TryGetValue(agentID, out capUrl)) | 390 | |
386 | { | 391 | MainServer.Instance.RemoveHTTPHandler("", m_URL); |
387 | MainServer.Instance.RemoveHTTPHandler("", capUrl); | 392 | m_capsDict.Remove(agentID); |
388 | m_capsDict.Remove(agentID); | 393 | |
389 | } | ||
390 | if (m_pollservices.TryGetValue(agentID, out args)) | 394 | if (m_pollservices.TryGetValue(agentID, out args)) |
391 | { | 395 | { |
392 | m_pollservices.Remove(agentID); | 396 | m_pollservices.Remove(agentID); |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/ObjectAdd.cs b/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/ObjectAdd.cs index 92805e2..94f8bc1 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/ObjectAdd.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/ObjectAdd.cs | |||
@@ -155,6 +155,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
155 | Quaternion rotation = Quaternion.Identity; | 155 | Quaternion rotation = Quaternion.Identity; |
156 | Vector3 scale = Vector3.Zero; | 156 | Vector3 scale = Vector3.Zero; |
157 | int state = 0; | 157 | int state = 0; |
158 | int lastattach = 0; | ||
158 | 159 | ||
159 | if (r.Type != OSDType.Map) // not a proper req | 160 | if (r.Type != OSDType.Map) // not a proper req |
160 | return responsedata; | 161 | return responsedata; |
@@ -224,6 +225,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
224 | 225 | ||
225 | ray_target_id = ObjMap["RayTargetId"].AsUUID(); | 226 | ray_target_id = ObjMap["RayTargetId"].AsUUID(); |
226 | state = ObjMap["State"].AsInteger(); | 227 | state = ObjMap["State"].AsInteger(); |
228 | lastattach = ObjMap["LastAttachPoint"].AsInteger(); | ||
227 | try | 229 | try |
228 | { | 230 | { |
229 | ray_end = ((OSDArray)ObjMap["RayEnd"]).AsVector3(); | 231 | ray_end = ((OSDArray)ObjMap["RayEnd"]).AsVector3(); |
@@ -290,6 +292,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
290 | 292 | ||
291 | //session_id = rm["session_id"].AsUUID(); | 293 | //session_id = rm["session_id"].AsUUID(); |
292 | state = rm["state"].AsInteger(); | 294 | state = rm["state"].AsInteger(); |
295 | lastattach = rm["last_attach_point"].AsInteger(); | ||
293 | try | 296 | try |
294 | { | 297 | { |
295 | ray_end = ((OSDArray)rm["ray_end"]).AsVector3(); | 298 | ray_end = ((OSDArray)rm["ray_end"]).AsVector3(); |
@@ -331,6 +334,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
331 | pbs.ProfileEnd = (ushort)profile_end; | 334 | pbs.ProfileEnd = (ushort)profile_end; |
332 | pbs.Scale = scale; | 335 | pbs.Scale = scale; |
333 | pbs.State = (byte)state; | 336 | pbs.State = (byte)state; |
337 | pbs.LastAttachPoint = (byte)lastattach; | ||
334 | 338 | ||
335 | SceneObjectGroup obj = null; ; | 339 | SceneObjectGroup obj = null; ; |
336 | 340 | ||
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/UploadObjectAssetModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/UploadObjectAssetModule.cs index 55a503e..769fe28 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/UploadObjectAssetModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/ObjectCaps/UploadObjectAssetModule.cs | |||
@@ -277,6 +277,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
277 | pbs.ProfileEnd = (ushort) obj.ProfileEnd; | 277 | pbs.ProfileEnd = (ushort) obj.ProfileEnd; |
278 | pbs.Scale = obj.Scale; | 278 | pbs.Scale = obj.Scale; |
279 | pbs.State = (byte) 0; | 279 | pbs.State = (byte) 0; |
280 | pbs.LastAttachPoint = (byte) 0; | ||
280 | SceneObjectPart prim = new SceneObjectPart(); | 281 | SceneObjectPart prim = new SceneObjectPart(); |
281 | prim.UUID = UUID.Random(); | 282 | prim.UUID = UUID.Random(); |
282 | prim.CreatorID = AgentId; | 283 | prim.CreatorID = AgentId; |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/Properties/AssemblyInfo.cs b/OpenSim/Region/ClientStack/Linden/Caps/Properties/AssemblyInfo.cs index 595d01a..112608b 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/Properties/AssemblyInfo.cs | |||
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices; | |||
29 | // Build Number | 29 | // Build Number |
30 | // Revision | 30 | // Revision |
31 | // | 31 | // |
32 | [assembly: AssemblyVersion("0.7.6.*")] | 32 | [assembly: AssemblyVersion("0.8.0.*")] |
33 | 33 | ||
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs index 79d56c4..5196368 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs | |||
@@ -183,7 +183,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
183 | m_isGod = m_scene.Permissions.IsGod(agentID); | 183 | m_isGod = m_scene.Permissions.IsGod(agentID); |
184 | } | 184 | } |
185 | 185 | ||
186 | public override byte[] Handle(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) | 186 | protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) |
187 | { | 187 | { |
188 | StreamReader reader = new StreamReader(request); | 188 | StreamReader reader = new StreamReader(request); |
189 | string message = reader.ReadToEnd(); | 189 | string message = reader.ReadToEnd(); |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/SimulatorFeaturesModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/SimulatorFeaturesModule.cs index 7d9f935..bedec80 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/SimulatorFeaturesModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/SimulatorFeaturesModule.cs | |||
@@ -68,7 +68,6 @@ namespace OpenSim.Region.ClientStack.Linden | |||
68 | /// </summary> | 68 | /// </summary> |
69 | private OSDMap m_features = new OSDMap(); | 69 | private OSDMap m_features = new OSDMap(); |
70 | 70 | ||
71 | private string m_MapImageServerURL = string.Empty; | ||
72 | private string m_SearchURL = string.Empty; | 71 | private string m_SearchURL = string.Empty; |
73 | private bool m_ExportSupported = false; | 72 | private bool m_ExportSupported = false; |
74 | 73 | ||
@@ -78,15 +77,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
78 | { | 77 | { |
79 | IConfig config = source.Configs["SimulatorFeatures"]; | 78 | IConfig config = source.Configs["SimulatorFeatures"]; |
80 | if (config != null) | 79 | if (config != null) |
81 | { | 80 | { |
82 | m_MapImageServerURL = config.GetString("MapImageServerURI", string.Empty); | ||
83 | if (m_MapImageServerURL != string.Empty) | ||
84 | { | ||
85 | m_MapImageServerURL = m_MapImageServerURL.Trim(); | ||
86 | if (!m_MapImageServerURL.EndsWith("/")) | ||
87 | m_MapImageServerURL = m_MapImageServerURL + "/"; | ||
88 | } | ||
89 | |||
90 | m_SearchURL = config.GetString("SearchServerURI", string.Empty); | 81 | m_SearchURL = config.GetString("SearchServerURI", string.Empty); |
91 | 82 | ||
92 | m_ExportSupported = config.GetBoolean("ExportSupported", m_ExportSupported); | 83 | m_ExportSupported = config.GetBoolean("ExportSupported", m_ExportSupported); |
@@ -140,8 +131,9 @@ namespace OpenSim.Region.ClientStack.Linden | |||
140 | m_features["MeshRezEnabled"] = true; | 131 | m_features["MeshRezEnabled"] = true; |
141 | m_features["MeshUploadEnabled"] = true; | 132 | m_features["MeshUploadEnabled"] = true; |
142 | m_features["MeshXferEnabled"] = true; | 133 | m_features["MeshXferEnabled"] = true; |
134 | |||
143 | m_features["PhysicsMaterialsEnabled"] = true; | 135 | m_features["PhysicsMaterialsEnabled"] = true; |
144 | 136 | ||
145 | OSDMap typesMap = new OSDMap(); | 137 | OSDMap typesMap = new OSDMap(); |
146 | typesMap["convex"] = true; | 138 | typesMap["convex"] = true; |
147 | typesMap["none"] = true; | 139 | typesMap["none"] = true; |
@@ -149,15 +141,20 @@ namespace OpenSim.Region.ClientStack.Linden | |||
149 | m_features["PhysicsShapeTypes"] = typesMap; | 141 | m_features["PhysicsShapeTypes"] = typesMap; |
150 | 142 | ||
151 | // Extra information for viewers that want to use it | 143 | // Extra information for viewers that want to use it |
152 | OSDMap gridServicesMap = new OSDMap(); | 144 | |
153 | if (m_MapImageServerURL != string.Empty) | 145 | OSDMap extrasMap = new OSDMap(); |
154 | gridServicesMap["map-server-url"] = m_MapImageServerURL; | ||
155 | if (m_SearchURL != string.Empty) | ||
156 | gridServicesMap["search"] = m_SearchURL; | ||
157 | m_features["GridServices"] = gridServicesMap; | ||
158 | 146 | ||
147 | extrasMap["AvatarSkeleton"] = true; | ||
148 | extrasMap["AnimationSet"] = true; | ||
149 | |||
150 | // TODO: Take these out of here into their respective modules, like map-server-url | ||
151 | if (m_SearchURL != string.Empty) | ||
152 | extrasMap["search-server-url"] = m_SearchURL; | ||
159 | if (m_ExportSupported) | 153 | if (m_ExportSupported) |
160 | m_features["ExportSupported"] = true; | 154 | extrasMap["ExportSupported"] = true; |
155 | |||
156 | m_features["OpenSimExtras"] = extrasMap; | ||
157 | |||
161 | } | 158 | } |
162 | } | 159 | } |
163 | 160 | ||
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/UploadBakedTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/UploadBakedTextureModule.cs index eca576d..50e9275 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/UploadBakedTextureModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/UploadBakedTextureModule.cs | |||
@@ -64,11 +64,18 @@ namespace OpenSim.Region.ClientStack.Linden | |||
64 | 64 | ||
65 | private Scene m_scene; | 65 | private Scene m_scene; |
66 | private bool m_persistBakedTextures; | 66 | private bool m_persistBakedTextures; |
67 | private string m_URL; | ||
67 | 68 | ||
68 | private IBakedTextureModule m_BakedTextureModule; | 69 | private IBakedTextureModule m_BakedTextureModule; |
69 | 70 | ||
70 | public void Initialise(IConfigSource source) | 71 | public void Initialise(IConfigSource source) |
71 | { | 72 | { |
73 | IConfig config = source.Configs["ClientStack.LindenCaps"]; | ||
74 | if (config == null) | ||
75 | return; | ||
76 | |||
77 | m_URL = config.GetString("Cap_UploadBakedTexture", string.Empty); | ||
78 | |||
72 | IConfig appearanceConfig = source.Configs["Appearance"]; | 79 | IConfig appearanceConfig = source.Configs["Appearance"]; |
73 | if (appearanceConfig != null) | 80 | if (appearanceConfig != null) |
74 | m_persistBakedTextures = appearanceConfig.GetBoolean("PersistBakedTextures", m_persistBakedTextures); | 81 | m_persistBakedTextures = appearanceConfig.GetBoolean("PersistBakedTextures", m_persistBakedTextures); |
@@ -89,9 +96,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
89 | s.EventManager.OnRemovePresence -= DeRegisterPresence; | 96 | s.EventManager.OnRemovePresence -= DeRegisterPresence; |
90 | m_BakedTextureModule = null; | 97 | m_BakedTextureModule = null; |
91 | m_scene = null; | 98 | m_scene = null; |
92 | } | 99 | } |
93 | |||
94 | |||
95 | 100 | ||
96 | public void RegionLoaded(Scene s) | 101 | public void RegionLoaded(Scene s) |
97 | { | 102 | { |
@@ -103,44 +108,58 @@ namespace OpenSim.Region.ClientStack.Linden | |||
103 | 108 | ||
104 | private void DeRegisterPresence(UUID agentId) | 109 | private void DeRegisterPresence(UUID agentId) |
105 | { | 110 | { |
106 | ScenePresence presence = null; | 111 | // ScenePresence presence = null; |
107 | if (m_scene.TryGetScenePresence(agentId, out presence)) | 112 | // if (m_scene.TryGetScenePresence(agentId, out presence)) |
108 | { | 113 | { |
109 | presence.ControllingClient.OnSetAppearance -= CaptureAppearanceSettings; | 114 | // presence.ControllingClient.OnSetAppearance -= CaptureAppearanceSettings; |
110 | } | 115 | } |
111 | 116 | ||
112 | } | 117 | } |
113 | 118 | ||
114 | private void RegisterNewPresence(ScenePresence presence) | 119 | private void RegisterNewPresence(ScenePresence presence) |
115 | { | 120 | { |
116 | presence.ControllingClient.OnSetAppearance += CaptureAppearanceSettings; | 121 | // presence.ControllingClient.OnSetAppearance += CaptureAppearanceSettings; |
117 | |||
118 | } | 122 | } |
119 | 123 | ||
120 | private void CaptureAppearanceSettings(IClientAPI remoteClient, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 avSize, WearableCacheItem[] cacheItems) | 124 | /* not in use. work done in AvatarFactoryModule ValidateBakedTextureCache() and UpdateBakedTextureCache() |
121 | { | 125 | private void CaptureAppearanceSettings(IClientAPI remoteClient, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 avSize, WearableCacheItem[] cacheItems) |
122 | int maxCacheitemsLoop = cacheItems.Length; | ||
123 | if (maxCacheitemsLoop > AvatarWearable.MAX_WEARABLES) | ||
124 | { | ||
125 | maxCacheitemsLoop = AvatarWearable.MAX_WEARABLES; | ||
126 | m_log.WarnFormat("[CACHEDBAKES]: Too Many Cache items Provided {0}, the max is {1}. Truncating!", cacheItems.Length, AvatarWearable.MAX_WEARABLES); | ||
127 | } | ||
128 | |||
129 | m_BakedTextureModule = m_scene.RequestModuleInterface<IBakedTextureModule>(); | ||
130 | if (cacheItems.Length > 0) | ||
131 | { | ||
132 | m_log.Debug("[Cacheitems]: " + cacheItems.Length); | ||
133 | for (int iter = 0; iter < maxCacheitemsLoop; iter++) | ||
134 | { | ||
135 | m_log.Debug("[Cacheitems] {" + iter + "/" + cacheItems[iter].TextureIndex + "}: c-" + cacheItems[iter].CacheId + ", t-" + | ||
136 | cacheItems[iter].TextureID); | ||
137 | } | ||
138 | |||
139 | ScenePresence p = null; | ||
140 | if (m_scene.TryGetScenePresence(remoteClient.AgentId, out p)) | ||
141 | { | 126 | { |
127 | // if cacheItems.Length > 0 viewer is giving us current textures information. | ||
128 | // baked ones should had been uploaded and in assets cache as local itens | ||
129 | |||
130 | |||
131 | if (cacheItems.Length == 0) | ||
132 | return; // no textures information, nothing to do | ||
133 | |||
134 | ScenePresence p = null; | ||
135 | if (!m_scene.TryGetScenePresence(remoteClient.AgentId, out p)) | ||
136 | return; // what are we doing if there is no presence to cache for? | ||
137 | |||
138 | if (p.IsDeleted) | ||
139 | return; // does this really work? | ||
140 | |||
141 | int maxCacheitemsLoop = cacheItems.Length; | ||
142 | if (maxCacheitemsLoop > 20) | ||
143 | { | ||
144 | maxCacheitemsLoop = AvatarWearable.MAX_WEARABLES; | ||
145 | m_log.WarnFormat("[CACHEDBAKES]: Too Many Cache items Provided {0}, the max is {1}. Truncating!", cacheItems.Length, AvatarWearable.MAX_WEARABLES); | ||
146 | } | ||
147 | |||
148 | m_BakedTextureModule = m_scene.RequestModuleInterface<IBakedTextureModule>(); | ||
149 | |||
150 | |||
151 | // some nice debug | ||
152 | m_log.Debug("[Cacheitems]: " + cacheItems.Length); | ||
153 | for (int iter = 0; iter < maxCacheitemsLoop; iter++) | ||
154 | { | ||
155 | m_log.Debug("[Cacheitems] {" + iter + "/" + cacheItems[iter].TextureIndex + "}: c-" + cacheItems[iter].CacheId + ", t-" + | ||
156 | cacheItems[iter].TextureID); | ||
157 | } | ||
158 | |||
159 | // p.Appearance.WearableCacheItems is in memory primary cashID to textures mapper | ||
142 | 160 | ||
143 | WearableCacheItem[] existingitems = p.Appearance.WearableCacheItems; | 161 | WearableCacheItem[] existingitems = p.Appearance.WearableCacheItems; |
162 | |||
144 | if (existingitems == null) | 163 | if (existingitems == null) |
145 | { | 164 | { |
146 | if (m_BakedTextureModule != null) | 165 | if (m_BakedTextureModule != null) |
@@ -154,38 +173,22 @@ namespace OpenSim.Region.ClientStack.Linden | |||
154 | p.Appearance.WearableCacheItems = savedcache; | 173 | p.Appearance.WearableCacheItems = savedcache; |
155 | p.Appearance.WearableCacheItemsDirty = false; | 174 | p.Appearance.WearableCacheItemsDirty = false; |
156 | } | 175 | } |
157 | |||
158 | } | ||
159 | /* | ||
160 | * The following Catch types DO NOT WORK with m_BakedTextureModule.Get | ||
161 | * it jumps to the General Packet Exception Handler if you don't catch Exception! | ||
162 | * | ||
163 | catch (System.Net.Sockets.SocketException) | ||
164 | { | ||
165 | cacheItems = null; | ||
166 | } | ||
167 | catch (WebException) | ||
168 | { | ||
169 | cacheItems = null; | ||
170 | } | 176 | } |
171 | catch (InvalidOperationException) | 177 | |
172 | { | ||
173 | cacheItems = null; | ||
174 | } */ | ||
175 | catch (Exception) | 178 | catch (Exception) |
176 | { | 179 | { |
177 | // The service logs a sufficient error message. | 180 | // The service logs a sufficient error message. |
178 | } | 181 | } |
179 | 182 | ||
180 | 183 | ||
181 | if (savedcache != null) | 184 | if (savedcache != null) |
182 | existingitems = savedcache; | 185 | existingitems = savedcache; |
183 | } | 186 | } |
184 | } | 187 | } |
188 | |||
185 | // Existing items null means it's a fully new appearance | 189 | // Existing items null means it's a fully new appearance |
186 | if (existingitems == null) | 190 | if (existingitems == null) |
187 | { | 191 | { |
188 | |||
189 | for (int i = 0; i < maxCacheitemsLoop; i++) | 192 | for (int i = 0; i < maxCacheitemsLoop; i++) |
190 | { | 193 | { |
191 | if (textureEntry.FaceTextures.Length > cacheItems[i].TextureIndex) | 194 | if (textureEntry.FaceTextures.Length > cacheItems[i].TextureIndex) |
@@ -198,7 +201,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
198 | AppearanceManager.DEFAULT_AVATAR_TEXTURE; | 201 | AppearanceManager.DEFAULT_AVATAR_TEXTURE; |
199 | continue; | 202 | continue; |
200 | } | 203 | } |
201 | cacheItems[i].TextureID =face.TextureID; | 204 | cacheItems[i].TextureID = face.TextureID; |
202 | if (m_scene.AssetService != null) | 205 | if (m_scene.AssetService != null) |
203 | cacheItems[i].TextureAsset = | 206 | cacheItems[i].TextureAsset = |
204 | m_scene.AssetService.GetCached(cacheItems[i].TextureID.ToString()); | 207 | m_scene.AssetService.GetCached(cacheItems[i].TextureID.ToString()); |
@@ -207,15 +210,10 @@ namespace OpenSim.Region.ClientStack.Linden | |||
207 | { | 210 | { |
208 | m_log.WarnFormat("[CACHEDBAKES]: Invalid Texture Index Provided, Texture doesn't exist or hasn't been uploaded yet {0}, the max is {1}. Skipping!", cacheItems[i].TextureIndex, textureEntry.FaceTextures.Length); | 211 | m_log.WarnFormat("[CACHEDBAKES]: Invalid Texture Index Provided, Texture doesn't exist or hasn't been uploaded yet {0}, the max is {1}. Skipping!", cacheItems[i].TextureIndex, textureEntry.FaceTextures.Length); |
209 | } | 212 | } |
210 | |||
211 | |||
212 | } | 213 | } |
213 | } | 214 | } |
214 | else | 215 | else |
215 | 216 | { | |
216 | |||
217 | { | ||
218 | // for each uploaded baked texture | ||
219 | for (int i = 0; i < maxCacheitemsLoop; i++) | 217 | for (int i = 0; i < maxCacheitemsLoop; i++) |
220 | { | 218 | { |
221 | if (textureEntry.FaceTextures.Length > cacheItems[i].TextureIndex) | 219 | if (textureEntry.FaceTextures.Length > cacheItems[i].TextureIndex) |
@@ -246,23 +244,24 @@ namespace OpenSim.Region.ClientStack.Linden | |||
246 | } | 244 | } |
247 | } | 245 | } |
248 | } | 246 | } |
249 | |||
250 | |||
251 | |||
252 | p.Appearance.WearableCacheItems = cacheItems; | 247 | p.Appearance.WearableCacheItems = cacheItems; |
253 | |||
254 | |||
255 | 248 | ||
256 | if (m_BakedTextureModule != null) | 249 | if (m_BakedTextureModule != null) |
257 | { | 250 | { |
258 | m_BakedTextureModule.Store(remoteClient.AgentId, cacheItems); | 251 | m_BakedTextureModule.Store(remoteClient.AgentId, cacheItems); |
259 | p.Appearance.WearableCacheItemsDirty = true; | 252 | p.Appearance.WearableCacheItemsDirty = true; |
260 | 253 | ||
261 | } | 254 | } |
262 | } | 255 | else |
263 | } | 256 | p.Appearance.WearableCacheItemsDirty = false; |
264 | } | ||
265 | 257 | ||
258 | for (int iter = 0; iter < maxCacheitemsLoop; iter++) | ||
259 | { | ||
260 | m_log.Debug("[CacheitemsLeaving] {" + iter + "/" + cacheItems[iter].TextureIndex + "}: c-" + cacheItems[iter].CacheId + ", t-" + | ||
261 | cacheItems[iter].TextureID); | ||
262 | } | ||
263 | } | ||
264 | */ | ||
266 | public void PostInitialise() | 265 | public void PostInitialise() |
267 | { | 266 | { |
268 | } | 267 | } |
@@ -280,23 +279,28 @@ namespace OpenSim.Region.ClientStack.Linden | |||
280 | 279 | ||
281 | public void RegisterCaps(UUID agentID, Caps caps) | 280 | public void RegisterCaps(UUID agentID, Caps caps) |
282 | { | 281 | { |
283 | UploadBakedTextureHandler avatarhandler = new UploadBakedTextureHandler( | 282 | // UUID capID = UUID.Random(); |
284 | caps, m_scene.AssetService, m_persistBakedTextures); | ||
285 | |||
286 | |||
287 | |||
288 | caps.RegisterHandler( | ||
289 | "UploadBakedTexture", | ||
290 | new RestStreamHandler( | ||
291 | "POST", | ||
292 | "/CAPS/" + caps.CapsObjectPath + m_uploadBakedTexturePath, | ||
293 | avatarhandler.UploadBakedTexture, | ||
294 | "UploadBakedTexture", | ||
295 | agentID.ToString())); | ||
296 | 283 | ||
297 | 284 | //caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture)); | |
298 | 285 | if (m_URL == "localhost") | |
286 | { | ||
287 | UploadBakedTextureHandler avatarhandler = new UploadBakedTextureHandler( | ||
288 | caps, m_scene.AssetService, m_persistBakedTextures); | ||
299 | 289 | ||
290 | caps.RegisterHandler( | ||
291 | "UploadBakedTexture", | ||
292 | new RestStreamHandler( | ||
293 | "POST", | ||
294 | "/CAPS/" + caps.CapsObjectPath + m_uploadBakedTexturePath, | ||
295 | avatarhandler.UploadBakedTexture, | ||
296 | "UploadBakedTexture", | ||
297 | agentID.ToString())); | ||
298 | |||
299 | } | ||
300 | else | ||
301 | { | ||
302 | caps.RegisterHandler("UploadBakedTexture", m_URL); | ||
303 | } | ||
300 | } | 304 | } |
301 | } | 305 | } |
302 | } \ No newline at end of file | 306 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs index 707cc93..6fc35cd 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs | |||
@@ -71,9 +71,13 @@ namespace OpenSim.Region.ClientStack.Linden | |||
71 | private IInventoryService m_InventoryService; | 71 | private IInventoryService m_InventoryService; |
72 | private ILibraryService m_LibraryService; | 72 | private ILibraryService m_LibraryService; |
73 | 73 | ||
74 | private bool m_Enabled; | ||
75 | |||
76 | private string m_fetchInventoryDescendents2Url; | ||
77 | private string m_webFetchInventoryDescendentsUrl; | ||
78 | |||
74 | private static WebFetchInvDescHandler m_webFetchHandler; | 79 | private static WebFetchInvDescHandler m_webFetchHandler; |
75 | 80 | ||
76 | private Dictionary<UUID, string> m_capsDict = new Dictionary<UUID, string>(); | ||
77 | private static Thread[] m_workerThreads = null; | 81 | private static Thread[] m_workerThreads = null; |
78 | 82 | ||
79 | private static DoubleQueue<aPollRequest> m_queue = | 83 | private static DoubleQueue<aPollRequest> m_queue = |
@@ -83,22 +87,45 @@ namespace OpenSim.Region.ClientStack.Linden | |||
83 | 87 | ||
84 | public void Initialise(IConfigSource source) | 88 | public void Initialise(IConfigSource source) |
85 | { | 89 | { |
90 | IConfig config = source.Configs["ClientStack.LindenCaps"]; | ||
91 | if (config == null) | ||
92 | return; | ||
93 | |||
94 | m_fetchInventoryDescendents2Url = config.GetString("Cap_FetchInventoryDescendents2", string.Empty); | ||
95 | m_webFetchInventoryDescendentsUrl = config.GetString("Cap_WebFetchInventoryDescendents", string.Empty); | ||
96 | |||
97 | if (m_fetchInventoryDescendents2Url != string.Empty || m_webFetchInventoryDescendentsUrl != string.Empty) | ||
98 | { | ||
99 | m_Enabled = true; | ||
100 | } | ||
86 | } | 101 | } |
87 | 102 | ||
88 | public void AddRegion(Scene s) | 103 | public void AddRegion(Scene s) |
89 | { | 104 | { |
105 | if (!m_Enabled) | ||
106 | return; | ||
107 | |||
90 | m_scene = s; | 108 | m_scene = s; |
91 | } | 109 | } |
92 | 110 | ||
93 | public void RemoveRegion(Scene s) | 111 | public void RemoveRegion(Scene s) |
94 | { | 112 | { |
113 | if (!m_Enabled) | ||
114 | return; | ||
115 | |||
95 | m_scene.EventManager.OnRegisterCaps -= RegisterCaps; | 116 | m_scene.EventManager.OnRegisterCaps -= RegisterCaps; |
96 | m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps; | 117 | |
118 | foreach (Thread t in m_workerThreads) | ||
119 | Watchdog.AbortThread(t.ManagedThreadId); | ||
120 | |||
97 | m_scene = null; | 121 | m_scene = null; |
98 | } | 122 | } |
99 | 123 | ||
100 | public void RegionLoaded(Scene s) | 124 | public void RegionLoaded(Scene s) |
101 | { | 125 | { |
126 | if (!m_Enabled) | ||
127 | return; | ||
128 | |||
102 | m_InventoryService = m_scene.InventoryService; | 129 | m_InventoryService = m_scene.InventoryService; |
103 | m_LibraryService = m_scene.LibraryService; | 130 | m_LibraryService = m_scene.LibraryService; |
104 | 131 | ||
@@ -106,7 +133,6 @@ namespace OpenSim.Region.ClientStack.Linden | |||
106 | m_webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService); | 133 | m_webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService); |
107 | 134 | ||
108 | m_scene.EventManager.OnRegisterCaps += RegisterCaps; | 135 | m_scene.EventManager.OnRegisterCaps += RegisterCaps; |
109 | m_scene.EventManager.OnDeregisterCaps += DeregisterCaps; | ||
110 | 136 | ||
111 | if (m_workerThreads == null) | 137 | if (m_workerThreads == null) |
112 | { | 138 | { |
@@ -140,12 +166,6 @@ namespace OpenSim.Region.ClientStack.Linden | |||
140 | 166 | ||
141 | #endregion | 167 | #endregion |
142 | 168 | ||
143 | ~WebFetchInvDescModule() | ||
144 | { | ||
145 | foreach (Thread t in m_workerThreads) | ||
146 | Watchdog.AbortThread(t.ManagedThreadId); | ||
147 | } | ||
148 | |||
149 | private class PollServiceInventoryEventArgs : PollServiceEventArgs | 169 | private class PollServiceInventoryEventArgs : PollServiceEventArgs |
150 | { | 170 | { |
151 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 171 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
@@ -155,8 +175,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
155 | 175 | ||
156 | private Scene m_scene; | 176 | private Scene m_scene; |
157 | 177 | ||
158 | public PollServiceInventoryEventArgs(Scene scene, UUID pId) : | 178 | public PollServiceInventoryEventArgs(Scene scene, string url, UUID pId) : |
159 | base(null, null, null, null, pId, int.MaxValue) | 179 | base(null, url, null, null, null, pId, int.MaxValue) |
160 | { | 180 | { |
161 | m_scene = scene; | 181 | m_scene = scene; |
162 | 182 | ||
@@ -278,53 +298,72 @@ namespace OpenSim.Region.ClientStack.Linden | |||
278 | requestinfo.request["body"].ToString(), String.Empty, String.Empty, null, null); | 298 | requestinfo.request["body"].ToString(), String.Empty, String.Empty, null, null); |
279 | 299 | ||
280 | lock (responses) | 300 | lock (responses) |
281 | responses[requestID] = response; | 301 | responses[requestID] = response; |
282 | } | 302 | } |
283 | } | 303 | } |
284 | 304 | ||
285 | private void RegisterCaps(UUID agentID, Caps caps) | 305 | private void RegisterCaps(UUID agentID, Caps caps) |
286 | { | 306 | { |
287 | string capUrl = "/CAPS/" + UUID.Random() + "/"; | 307 | RegisterFetchDescendentsCap(agentID, caps, "FetchInventoryDescendents2", m_fetchInventoryDescendents2Url); |
288 | |||
289 | // Register this as a poll service | ||
290 | PollServiceInventoryEventArgs args = new PollServiceInventoryEventArgs(m_scene, agentID); | ||
291 | |||
292 | args.Type = PollServiceEventArgs.EventType.Inventory; | ||
293 | MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args); | ||
294 | |||
295 | string hostName = m_scene.RegionInfo.ExternalHostName; | ||
296 | uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port; | ||
297 | string protocol = "http"; | ||
298 | |||
299 | if (MainServer.Instance.UseSSL) | ||
300 | { | ||
301 | hostName = MainServer.Instance.SSLCommonName; | ||
302 | port = MainServer.Instance.SSLPort; | ||
303 | protocol = "https"; | ||
304 | } | ||
305 | caps.RegisterHandler("FetchInventoryDescendents2", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl)); | ||
306 | |||
307 | m_capsDict[agentID] = capUrl; | ||
308 | } | 308 | } |
309 | 309 | ||
310 | private void DeregisterCaps(UUID agentID, Caps caps) | 310 | private void RegisterFetchDescendentsCap(UUID agentID, Caps caps, string capName, string url) |
311 | { | 311 | { |
312 | string capUrl; | 312 | string capUrl; |
313 | 313 | ||
314 | if (m_capsDict.TryGetValue(agentID, out capUrl)) | 314 | // disable the cap clause |
315 | if (url == "") | ||
316 | { | ||
317 | return; | ||
318 | } | ||
319 | // handled by the simulator | ||
320 | else if (url == "localhost") | ||
321 | { | ||
322 | capUrl = "/CAPS/" + UUID.Random() + "/"; | ||
323 | |||
324 | // Register this as a poll service | ||
325 | PollServiceInventoryEventArgs args = new PollServiceInventoryEventArgs(m_scene, capUrl, agentID); | ||
326 | args.Type = PollServiceEventArgs.EventType.Inventory; | ||
327 | |||
328 | caps.RegisterPollHandler(capName, args); | ||
329 | } | ||
330 | // external handler | ||
331 | else | ||
315 | { | 332 | { |
316 | MainServer.Instance.RemoveHTTPHandler("", capUrl); | 333 | capUrl = url; |
317 | m_capsDict.Remove(agentID); | 334 | IExternalCapsModule handler = m_scene.RequestModuleInterface<IExternalCapsModule>(); |
335 | if (handler != null) | ||
336 | handler.RegisterExternalUserCapsHandler(agentID,caps,capName,capUrl); | ||
337 | else | ||
338 | caps.RegisterHandler(capName, capUrl); | ||
318 | } | 339 | } |
340 | |||
341 | // m_log.DebugFormat( | ||
342 | // "[FETCH INVENTORY DESCENDENTS2 MODULE]: Registered capability {0} at {1} in region {2} for {3}", | ||
343 | // capName, capUrl, m_scene.RegionInfo.RegionName, agentID); | ||
319 | } | 344 | } |
320 | 345 | ||
346 | // private void DeregisterCaps(UUID agentID, Caps caps) | ||
347 | // { | ||
348 | // string capUrl; | ||
349 | // | ||
350 | // if (m_capsDict.TryGetValue(agentID, out capUrl)) | ||
351 | // { | ||
352 | // MainServer.Instance.RemoveHTTPHandler("", capUrl); | ||
353 | // m_capsDict.Remove(agentID); | ||
354 | // } | ||
355 | // } | ||
356 | |||
321 | private void DoInventoryRequests() | 357 | private void DoInventoryRequests() |
322 | { | 358 | { |
323 | while (true) | 359 | while (true) |
324 | { | 360 | { |
361 | Watchdog.UpdateThread(); | ||
362 | |||
325 | aPollRequest poolreq = m_queue.Dequeue(); | 363 | aPollRequest poolreq = m_queue.Dequeue(); |
326 | 364 | ||
327 | poolreq.thepoll.Process(poolreq); | 365 | if (poolreq != null && poolreq.thepoll != null) |
366 | poolreq.thepoll.Process(poolreq); | ||
328 | } | 367 | } |
329 | } | 368 | } |
330 | } | 369 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/J2KImage.cs b/OpenSim/Region/ClientStack/Linden/UDP/J2KImage.cs index 3995620..15d6f7f 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/J2KImage.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/J2KImage.cs | |||
@@ -424,12 +424,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
424 | // foreign user is visiting, we need to try again after the first fail to the local | 424 | // foreign user is visiting, we need to try again after the first fail to the local |
425 | // asset service. | 425 | // asset service. |
426 | string assetServerURL = string.Empty; | 426 | string assetServerURL = string.Empty; |
427 | if (InventoryAccessModule.IsForeignUser(AgentID, out assetServerURL)) | 427 | if (InventoryAccessModule.IsForeignUser(AgentID, out assetServerURL) && !string.IsNullOrEmpty(assetServerURL)) |
428 | { | 428 | { |
429 | if (!assetServerURL.EndsWith("/") && !assetServerURL.EndsWith("=")) | 429 | if (!assetServerURL.EndsWith("/") && !assetServerURL.EndsWith("=")) |
430 | assetServerURL = assetServerURL + "/"; | 430 | assetServerURL = assetServerURL + "/"; |
431 | 431 | ||
432 | m_log.DebugFormat("[J2KIMAGE]: texture {0} not found in local asset storage. Trying user's storage.", assetServerURL + id); | 432 | // m_log.DebugFormat("[J2KIMAGE]: texture {0} not found in local asset storage. Trying user's storage.", assetServerURL + id); |
433 | AssetService.Get(assetServerURL + id, InventoryAccessModule, AssetReceived); | 433 | AssetService.Get(assetServerURL + id, InventoryAccessModule, AssetReceived); |
434 | return; | 434 | return; |
435 | } | 435 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 5ee1596..b7a4b93 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs | |||
@@ -84,6 +84,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
84 | public event ModifyTerrain OnModifyTerrain; | 84 | public event ModifyTerrain OnModifyTerrain; |
85 | public event Action<IClientAPI> OnRegionHandShakeReply; | 85 | public event Action<IClientAPI> OnRegionHandShakeReply; |
86 | public event GenericCall1 OnRequestWearables; | 86 | public event GenericCall1 OnRequestWearables; |
87 | public event CachedTextureRequest OnCachedTextureRequest; | ||
87 | public event SetAppearance OnSetAppearance; | 88 | public event SetAppearance OnSetAppearance; |
88 | public event AvatarNowWearing OnAvatarNowWearing; | 89 | public event AvatarNowWearing OnAvatarNowWearing; |
89 | public event RezSingleAttachmentFromInv OnRezSingleAttachmentFromInv; | 90 | public event RezSingleAttachmentFromInv OnRezSingleAttachmentFromInv; |
@@ -95,6 +96,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
95 | public event Action<IClientAPI, bool> OnCompleteMovementToRegion; | 96 | public event Action<IClientAPI, bool> OnCompleteMovementToRegion; |
96 | public event UpdateAgent OnPreAgentUpdate; | 97 | public event UpdateAgent OnPreAgentUpdate; |
97 | public event UpdateAgent OnAgentUpdate; | 98 | public event UpdateAgent OnAgentUpdate; |
99 | public event UpdateAgent OnAgentCameraUpdate; | ||
98 | public event AgentRequestSit OnAgentRequestSit; | 100 | public event AgentRequestSit OnAgentRequestSit; |
99 | public event AgentSit OnAgentSit; | 101 | public event AgentSit OnAgentSit; |
100 | public event AvatarPickerRequest OnAvatarPickerRequest; | 102 | public event AvatarPickerRequest OnAvatarPickerRequest; |
@@ -335,6 +337,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
335 | private bool m_VelocityInterpolate = false; | 337 | private bool m_VelocityInterpolate = false; |
336 | private const uint MaxTransferBytesPerPacket = 600; | 338 | private const uint MaxTransferBytesPerPacket = 600; |
337 | 339 | ||
340 | private volatile bool m_justEditedTerrain = false; | ||
338 | 341 | ||
339 | /// <value> | 342 | /// <value> |
340 | /// List used in construction of data blocks for an object update packet. This is to stop us having to | 343 | /// List used in construction of data blocks for an object update packet. This is to stop us having to |
@@ -355,7 +358,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
355 | // protected HashSet<uint> m_attachmentsSent; | 358 | // protected HashSet<uint> m_attachmentsSent; |
356 | 359 | ||
357 | private bool m_deliverPackets = true; | 360 | private bool m_deliverPackets = true; |
358 | private int m_animationSequenceNumber = 1; | 361 | |
359 | private bool m_SendLogoutPacketWhenClosing = true; | 362 | private bool m_SendLogoutPacketWhenClosing = true; |
360 | 363 | ||
361 | /// <summary> | 364 | /// <summary> |
@@ -367,7 +370,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
367 | /// This does mean that agent updates must be processed synchronously, at least for each client, and called methods | 370 | /// This does mean that agent updates must be processed synchronously, at least for each client, and called methods |
368 | /// cannot retain a reference to it outside of that method. | 371 | /// cannot retain a reference to it outside of that method. |
369 | /// </remarks> | 372 | /// </remarks> |
370 | private AgentUpdateArgs m_lastAgentUpdateArgs; | 373 | private AgentUpdateArgs m_thisAgentUpdateArgs = new AgentUpdateArgs(); |
371 | 374 | ||
372 | protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>(); | 375 | protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>(); |
373 | protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers | 376 | protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers |
@@ -416,6 +419,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
416 | public ulong ActiveGroupPowers { get { return m_activeGroupPowers; } } | 419 | public ulong ActiveGroupPowers { get { return m_activeGroupPowers; } } |
417 | public bool IsGroupMember(UUID groupID) { return m_groupPowers.ContainsKey(groupID); } | 420 | public bool IsGroupMember(UUID groupID) { return m_groupPowers.ContainsKey(groupID); } |
418 | 421 | ||
422 | public int PingTimeMS | ||
423 | { | ||
424 | get | ||
425 | { | ||
426 | if (UDPClient != null) | ||
427 | return UDPClient.PingTimeMS; | ||
428 | return 0; | ||
429 | } | ||
430 | } | ||
431 | |||
419 | /// <summary> | 432 | /// <summary> |
420 | /// Entity update queues | 433 | /// Entity update queues |
421 | /// </summary> | 434 | /// </summary> |
@@ -437,7 +450,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
437 | public string Name { get { return FirstName + " " + LastName; } } | 450 | public string Name { get { return FirstName + " " + LastName; } } |
438 | 451 | ||
439 | public uint CircuitCode { get { return m_circuitCode; } } | 452 | public uint CircuitCode { get { return m_circuitCode; } } |
440 | public int NextAnimationSequenceNumber { get { return m_animationSequenceNumber++; } } | 453 | public int NextAnimationSequenceNumber |
454 | { | ||
455 | get { return m_udpServer.NextAnimationSequenceNumber; } | ||
456 | } | ||
441 | 457 | ||
442 | /// <summary> | 458 | /// <summary> |
443 | /// As well as it's function in IClientAPI, in LLClientView we are locking on this property in order to | 459 | /// As well as it's function in IClientAPI, in LLClientView we are locking on this property in order to |
@@ -458,6 +474,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
458 | set { m_disableFacelights = value; } | 474 | set { m_disableFacelights = value; } |
459 | } | 475 | } |
460 | 476 | ||
477 | |||
461 | public bool SendLogoutPacketWhenClosing { set { m_SendLogoutPacketWhenClosing = value; } } | 478 | public bool SendLogoutPacketWhenClosing { set { m_SendLogoutPacketWhenClosing = value; } } |
462 | 479 | ||
463 | 480 | ||
@@ -504,6 +521,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
504 | m_udpServer = udpServer; | 521 | m_udpServer = udpServer; |
505 | m_udpClient = udpClient; | 522 | m_udpClient = udpClient; |
506 | m_udpClient.OnQueueEmpty += HandleQueueEmpty; | 523 | m_udpClient.OnQueueEmpty += HandleQueueEmpty; |
524 | m_udpClient.HasUpdates += HandleHasUpdates; | ||
507 | m_udpClient.OnPacketStats += PopulateStats; | 525 | m_udpClient.OnPacketStats += PopulateStats; |
508 | 526 | ||
509 | m_prioritizer = new Prioritizer(m_scene); | 527 | m_prioritizer = new Prioritizer(m_scene); |
@@ -533,7 +551,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
533 | // We still perform a force close inside the sync lock since this is intended to attempt close where | 551 | // We still perform a force close inside the sync lock since this is intended to attempt close where |
534 | // there is some unidentified connection problem, not where we have issues due to deadlock | 552 | // there is some unidentified connection problem, not where we have issues due to deadlock |
535 | if (!IsActive && !force) | 553 | if (!IsActive && !force) |
554 | { | ||
555 | m_log.DebugFormat( | ||
556 | "[CLIENT]: Not attempting to close inactive client {0} in {1} since force flag is not set", | ||
557 | Name, m_scene.Name); | ||
558 | |||
536 | return; | 559 | return; |
560 | } | ||
537 | 561 | ||
538 | IsActive = false; | 562 | IsActive = false; |
539 | CloseWithoutChecks(sendStop); | 563 | CloseWithoutChecks(sendStop); |
@@ -583,7 +607,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
583 | 607 | ||
584 | // Disable UDP handling for this client | 608 | // Disable UDP handling for this client |
585 | m_udpClient.Shutdown(); | 609 | m_udpClient.Shutdown(); |
586 | 610 | ||
587 | 611 | ||
588 | //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false)); | 612 | //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false)); |
589 | //GC.Collect(); | 613 | //GC.Collect(); |
@@ -709,12 +733,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
709 | //there is a local handler for this packet type | 733 | //there is a local handler for this packet type |
710 | if (pprocessor.Async) | 734 | if (pprocessor.Async) |
711 | { | 735 | { |
736 | ClientInfo cinfo = UDPClient.GetClientInfo(); | ||
737 | if (!cinfo.AsyncRequests.ContainsKey(packet.Type.ToString())) | ||
738 | cinfo.AsyncRequests[packet.Type.ToString()] = 0; | ||
739 | cinfo.AsyncRequests[packet.Type.ToString()]++; | ||
740 | |||
712 | object obj = new AsyncPacketProcess(this, pprocessor.method, packet); | 741 | object obj = new AsyncPacketProcess(this, pprocessor.method, packet); |
713 | Util.FireAndForget(ProcessSpecificPacketAsync, obj); | 742 | Util.FireAndForget(ProcessSpecificPacketAsync, obj); |
714 | result = true; | 743 | result = true; |
715 | } | 744 | } |
716 | else | 745 | else |
717 | { | 746 | { |
747 | ClientInfo cinfo = UDPClient.GetClientInfo(); | ||
748 | if (!cinfo.SyncRequests.ContainsKey(packet.Type.ToString())) | ||
749 | cinfo.SyncRequests[packet.Type.ToString()] = 0; | ||
750 | cinfo.SyncRequests[packet.Type.ToString()]++; | ||
751 | |||
718 | result = pprocessor.method(this, packet); | 752 | result = pprocessor.method(this, packet); |
719 | } | 753 | } |
720 | } | 754 | } |
@@ -729,6 +763,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
729 | } | 763 | } |
730 | if (found) | 764 | if (found) |
731 | { | 765 | { |
766 | ClientInfo cinfo = UDPClient.GetClientInfo(); | ||
767 | if (!cinfo.GenericRequests.ContainsKey(packet.Type.ToString())) | ||
768 | cinfo.GenericRequests[packet.Type.ToString()] = 0; | ||
769 | cinfo.GenericRequests[packet.Type.ToString()]++; | ||
770 | |||
732 | result = method(this, packet); | 771 | result = method(this, packet); |
733 | } | 772 | } |
734 | } | 773 | } |
@@ -758,7 +797,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
758 | 797 | ||
759 | public virtual void Start() | 798 | public virtual void Start() |
760 | { | 799 | { |
761 | m_scene.AddNewClient(this, PresenceType.User); | 800 | m_scene.AddNewAgent(this, PresenceType.User); |
762 | 801 | ||
763 | RefreshGroupMembership(); | 802 | RefreshGroupMembership(); |
764 | } | 803 | } |
@@ -820,14 +859,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
820 | handshake.RegionInfo3.ProductName = Util.StringToBytes256(regionInfo.RegionType); | 859 | handshake.RegionInfo3.ProductName = Util.StringToBytes256(regionInfo.RegionType); |
821 | handshake.RegionInfo3.ProductSKU = Utils.EmptyBytes; | 860 | handshake.RegionInfo3.ProductSKU = Utils.EmptyBytes; |
822 | 861 | ||
823 | handshake.RegionInfo4 = new RegionHandshakePacket.RegionInfo4Block[0]; | 862 | handshake.RegionInfo4 = new RegionHandshakePacket.RegionInfo4Block[1]; |
824 | // OutPacket(handshake, ThrottleOutPacketType.Task); | 863 | handshake.RegionInfo4[0] = new RegionHandshakePacket.RegionInfo4Block(); |
825 | // use same as MoveAgentIntoRegion (both should be task ) | 864 | handshake.RegionInfo4[0].RegionFlagsExtended = args.regionFlags; |
865 | handshake.RegionInfo4[0].RegionProtocols = 0; // 1 here would indicate that SSB is supported | ||
866 | |||
826 | OutPacket(handshake, ThrottleOutPacketType.Unknown); | 867 | OutPacket(handshake, ThrottleOutPacketType.Unknown); |
827 | } | 868 | } |
828 | 869 | ||
870 | |||
829 | public void MoveAgentIntoRegion(RegionInfo regInfo, Vector3 pos, Vector3 look) | 871 | public void MoveAgentIntoRegion(RegionInfo regInfo, Vector3 pos, Vector3 look) |
830 | { | 872 | { |
873 | m_thisAgentUpdateArgs.CameraAtAxis.X = float.MinValue; | ||
874 | m_thisAgentUpdateArgs.ControlFlags = uint.MaxValue; | ||
875 | |||
831 | AgentMovementCompletePacket mov = (AgentMovementCompletePacket)PacketPool.Instance.GetPacket(PacketType.AgentMovementComplete); | 876 | AgentMovementCompletePacket mov = (AgentMovementCompletePacket)PacketPool.Instance.GetPacket(PacketType.AgentMovementComplete); |
832 | mov.SimData.ChannelVersion = m_channelVersion; | 877 | mov.SimData.ChannelVersion = m_channelVersion; |
833 | mov.AgentData.SessionID = m_sessionId; | 878 | mov.AgentData.SessionID = m_sessionId; |
@@ -1210,9 +1255,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1210 | LLHeightFieldMoronize(map); | 1255 | LLHeightFieldMoronize(map); |
1211 | 1256 | ||
1212 | LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches); | 1257 | LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches); |
1213 | layerpack.Header.Reliable = true; | 1258 | |
1259 | // When a user edits the terrain, so much data is sent, the data queues up fast and presents a sub optimal editing experience. | ||
1260 | // To alleviate this issue, when the user edits the terrain, we start skipping the queues until they're done editing the terrain. | ||
1261 | // We also make them unreliable because it's extremely likely that multiple packets will be sent for a terrain patch area | ||
1262 | // invalidating previous packets for that area. | ||
1263 | |||
1264 | // It's possible for an editing user to flood themselves with edited packets but the majority of use cases are such that only a | ||
1265 | // tiny percentage of users will be editing the terrain. Other, non-editing users will see the edits much slower. | ||
1266 | |||
1267 | // One last note on this topic, by the time users are going to be editing the terrain, it's extremely likely that the sim will | ||
1268 | // have rezzed already and therefore this is not likely going to cause any additional issues with lost packets, objects or terrain | ||
1269 | // patches. | ||
1214 | 1270 | ||
1215 | OutPacket(layerpack, ThrottleOutPacketType.Task); | 1271 | // m_justEditedTerrain is volatile, so test once and duplicate two affected statements so we only have one cache miss. |
1272 | if (m_justEditedTerrain) | ||
1273 | { | ||
1274 | layerpack.Header.Reliable = false; | ||
1275 | OutPacket(layerpack, | ||
1276 | ThrottleOutPacketType.Unknown ); | ||
1277 | } | ||
1278 | else | ||
1279 | { | ||
1280 | layerpack.Header.Reliable = true; | ||
1281 | OutPacket(layerpack, | ||
1282 | ThrottleOutPacketType.Task); | ||
1283 | } | ||
1216 | } | 1284 | } |
1217 | catch (Exception e) | 1285 | catch (Exception e) |
1218 | { | 1286 | { |
@@ -1405,6 +1473,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1405 | 1473 | ||
1406 | mapReply.AgentData.AgentID = AgentId; | 1474 | mapReply.AgentData.AgentID = AgentId; |
1407 | mapReply.Data = new MapBlockReplyPacket.DataBlock[mapBlocks2.Length]; | 1475 | mapReply.Data = new MapBlockReplyPacket.DataBlock[mapBlocks2.Length]; |
1476 | mapReply.Size = new MapBlockReplyPacket.SizeBlock[mapBlocks2.Length]; | ||
1408 | mapReply.AgentData.Flags = flag; | 1477 | mapReply.AgentData.Flags = flag; |
1409 | 1478 | ||
1410 | for (int i = 0; i < mapBlocks2.Length; i++) | 1479 | for (int i = 0; i < mapBlocks2.Length; i++) |
@@ -1419,6 +1488,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1419 | mapReply.Data[i].RegionFlags = mapBlocks2[i].RegionFlags; | 1488 | mapReply.Data[i].RegionFlags = mapBlocks2[i].RegionFlags; |
1420 | mapReply.Data[i].Access = mapBlocks2[i].Access; | 1489 | mapReply.Data[i].Access = mapBlocks2[i].Access; |
1421 | mapReply.Data[i].Agents = mapBlocks2[i].Agents; | 1490 | mapReply.Data[i].Agents = mapBlocks2[i].Agents; |
1491 | |||
1492 | // TODO: hookup varregion sim size here | ||
1493 | mapReply.Size[i] = new MapBlockReplyPacket.SizeBlock(); | ||
1494 | mapReply.Size[i].SizeX = 256; | ||
1495 | mapReply.Size[i].SizeY = 256; | ||
1422 | } | 1496 | } |
1423 | OutPacket(mapReply, ThrottleOutPacketType.Land); | 1497 | OutPacket(mapReply, ThrottleOutPacketType.Land); |
1424 | } | 1498 | } |
@@ -1578,13 +1652,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1578 | pc.PingID.OldestUnacked = 0; | 1652 | pc.PingID.OldestUnacked = 0; |
1579 | 1653 | ||
1580 | OutPacket(pc, ThrottleOutPacketType.Unknown); | 1654 | OutPacket(pc, ThrottleOutPacketType.Unknown); |
1655 | UDPClient.m_lastStartpingTimeMS = Util.EnvironmentTickCount(); | ||
1581 | } | 1656 | } |
1582 | 1657 | ||
1583 | public void SendKillObject(ulong regionHandle, List<uint> localIDs) | 1658 | public void SendKillObject(List<uint> localIDs) |
1584 | { | 1659 | { |
1585 | // foreach (uint id in localIDs) | 1660 | // foreach (uint id in localIDs) |
1586 | // m_log.DebugFormat("[CLIENT]: Sending KillObjectPacket to {0} for {1} in {2}", Name, id, regionHandle); | 1661 | // m_log.DebugFormat("[CLIENT]: Sending KillObjectPacket to {0} for {1} in {2}", Name, id, regionHandle); |
1587 | 1662 | ||
1663 | // remove pending entities | ||
1664 | lock (m_entityProps.SyncRoot) | ||
1665 | m_entityProps.Remove(localIDs); | ||
1666 | lock (m_entityUpdates.SyncRoot) | ||
1667 | m_entityUpdates.Remove(localIDs); | ||
1668 | |||
1588 | KillObjectPacket kill = (KillObjectPacket)PacketPool.Instance.GetPacket(PacketType.KillObject); | 1669 | KillObjectPacket kill = (KillObjectPacket)PacketPool.Instance.GetPacket(PacketType.KillObject); |
1589 | // TODO: don't create new blocks if recycling an old packet | 1670 | // TODO: don't create new blocks if recycling an old packet |
1590 | kill.ObjectData = new KillObjectPacket.ObjectDataBlock[localIDs.Count]; | 1671 | kill.ObjectData = new KillObjectPacket.ObjectDataBlock[localIDs.Count]; |
@@ -1596,28 +1677,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1596 | kill.Header.Reliable = true; | 1677 | kill.Header.Reliable = true; |
1597 | kill.Header.Zerocoded = true; | 1678 | kill.Header.Zerocoded = true; |
1598 | 1679 | ||
1599 | if (localIDs.Count == 1 && m_scene.GetScenePresence(localIDs[0]) != null) | 1680 | OutPacket(kill, ThrottleOutPacketType.Task); |
1600 | { | 1681 | } |
1601 | OutPacket(kill, ThrottleOutPacketType.Task); | ||
1602 | } | ||
1603 | else | ||
1604 | { | ||
1605 | // We MUST lock for both manipulating the kill record and sending the packet, in order to avoid a race | ||
1606 | // condition where a kill can be processed before an out-of-date update for the same object. | ||
1607 | // ProcessEntityUpdates() also takes the m_killRecord lock. | ||
1608 | // lock (m_killRecord) | ||
1609 | // { | ||
1610 | // foreach (uint localID in localIDs) | ||
1611 | // m_killRecord.Add(localID); | ||
1612 | |||
1613 | // The throttle queue used here must match that being used for updates. Otherwise, there is a | ||
1614 | // chance that a kill packet put on a separate queue will be sent to the client before an existing | ||
1615 | // update packet on another queue. Receiving updates after kills results in unowned and undeletable | ||
1616 | // scene objects in a viewer until that viewer is relogged in. | ||
1617 | OutPacket(kill, ThrottleOutPacketType.Task); | ||
1618 | // } | ||
1619 | } | ||
1620 | } | ||
1621 | 1682 | ||
1622 | /// <summary> | 1683 | /// <summary> |
1623 | /// Send information about the items contained in a folder to the client. | 1684 | /// Send information about the items contained in a folder to the client. |
@@ -2594,11 +2655,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2594 | { | 2655 | { |
2595 | AvatarSitResponsePacket avatarSitResponse = new AvatarSitResponsePacket(); | 2656 | AvatarSitResponsePacket avatarSitResponse = new AvatarSitResponsePacket(); |
2596 | avatarSitResponse.SitObject.ID = TargetID; | 2657 | avatarSitResponse.SitObject.ID = TargetID; |
2597 | if (CameraAtOffset != Vector3.Zero) | 2658 | avatarSitResponse.SitTransform.CameraAtOffset = CameraAtOffset; |
2598 | { | 2659 | avatarSitResponse.SitTransform.CameraEyeOffset = CameraEyeOffset; |
2599 | avatarSitResponse.SitTransform.CameraAtOffset = CameraAtOffset; | ||
2600 | avatarSitResponse.SitTransform.CameraEyeOffset = CameraEyeOffset; | ||
2601 | } | ||
2602 | avatarSitResponse.SitTransform.ForceMouselook = ForceMouseLook; | 2660 | avatarSitResponse.SitTransform.ForceMouselook = ForceMouseLook; |
2603 | avatarSitResponse.SitTransform.AutoPilot = autopilot; | 2661 | avatarSitResponse.SitTransform.AutoPilot = autopilot; |
2604 | avatarSitResponse.SitTransform.SitPosition = OffsetPos; | 2662 | avatarSitResponse.SitTransform.SitPosition = OffsetPos; |
@@ -3630,8 +3688,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3630 | avp.Sender.IsTrial = false; | 3688 | avp.Sender.IsTrial = false; |
3631 | avp.Sender.ID = agentID; | 3689 | avp.Sender.ID = agentID; |
3632 | avp.AppearanceData = new AvatarAppearancePacket.AppearanceDataBlock[0]; | 3690 | avp.AppearanceData = new AvatarAppearancePacket.AppearanceDataBlock[0]; |
3691 | |||
3692 | // this need be use in future | ||
3693 | // avp.AppearanceData[0].AppearanceVersion = 0; | ||
3694 | // avp.AppearanceData[0].CofVersion = 0; | ||
3695 | |||
3633 | //m_log.DebugFormat("[CLIENT]: Sending appearance for {0} to {1}", agentID.ToString(), AgentId.ToString()); | 3696 | //m_log.DebugFormat("[CLIENT]: Sending appearance for {0} to {1}", agentID.ToString(), AgentId.ToString()); |
3634 | OutPacket(avp, ThrottleOutPacketType.Task); | 3697 | OutPacket(avp, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority); |
3635 | } | 3698 | } |
3636 | 3699 | ||
3637 | public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) | 3700 | public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) |
@@ -3659,7 +3722,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3659 | ani.AnimationSourceList[i].ObjectID = objectIDs[i]; | 3722 | ani.AnimationSourceList[i].ObjectID = objectIDs[i]; |
3660 | } | 3723 | } |
3661 | ani.Header.Reliable = false; | 3724 | ani.Header.Reliable = false; |
3662 | OutPacket(ani, ThrottleOutPacketType.Task); | 3725 | OutPacket(ani, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority); |
3663 | } | 3726 | } |
3664 | 3727 | ||
3665 | #endregion | 3728 | #endregion |
@@ -3688,7 +3751,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3688 | objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; | 3751 | objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; |
3689 | objupdate.ObjectData[0] = CreateAvatarUpdateBlock(presence); | 3752 | objupdate.ObjectData[0] = CreateAvatarUpdateBlock(presence); |
3690 | 3753 | ||
3691 | OutPacket(objupdate, ThrottleOutPacketType.Task); | 3754 | OutPacket(objupdate, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority); |
3692 | 3755 | ||
3693 | // We need to record the avatar local id since the root prim of an attachment points to this. | 3756 | // We need to record the avatar local id since the root prim of an attachment points to this. |
3694 | // m_attachmentsSent.Add(avatar.LocalId); | 3757 | // m_attachmentsSent.Add(avatar.LocalId); |
@@ -3751,8 +3814,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3751 | { | 3814 | { |
3752 | SceneObjectPart e = (SceneObjectPart)entity; | 3815 | SceneObjectPart e = (SceneObjectPart)entity; |
3753 | SceneObjectGroup g = e.ParentGroup; | 3816 | SceneObjectGroup g = e.ParentGroup; |
3754 | if (g.RootPart.Shape.State > 30) // HUD | 3817 | if (g.HasPrivateAttachmentPoint && g.OwnerID != AgentId) |
3755 | if (g.OwnerID != AgentId) | ||
3756 | return; // Don't send updates for other people's HUDs | 3818 | return; // Don't send updates for other people's HUDs |
3757 | } | 3819 | } |
3758 | 3820 | ||
@@ -3762,6 +3824,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3762 | m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); | 3824 | m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); |
3763 | } | 3825 | } |
3764 | 3826 | ||
3827 | |||
3765 | /// <summary> | 3828 | /// <summary> |
3766 | /// Requeue an EntityUpdate when it was not acknowledged by the client. | 3829 | /// Requeue an EntityUpdate when it was not acknowledged by the client. |
3767 | /// We will update the priority and put it in the correct queue, merging update flags | 3830 | /// We will update the priority and put it in the correct queue, merging update flags |
@@ -3769,8 +3832,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3769 | /// The original update time is used for the merged update. | 3832 | /// The original update time is used for the merged update. |
3770 | /// </summary> | 3833 | /// </summary> |
3771 | private void ResendPrimUpdate(EntityUpdate update) | 3834 | private void ResendPrimUpdate(EntityUpdate update) |
3772 | { | 3835 | { |
3773 | // If the update exists in priority queue, it will be updated. | 3836 | // If the update exists in priority queue, it will be updated. |
3774 | // If it does not exist then it will be added with the current (rather than its original) priority | 3837 | // If it does not exist then it will be added with the current (rather than its original) priority |
3775 | uint priority = m_prioritizer.GetUpdatePriority(this, update.Entity); | 3838 | uint priority = m_prioritizer.GetUpdatePriority(this, update.Entity); |
3776 | 3839 | ||
@@ -3796,10 +3859,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3796 | // Count this as a resent packet since we are going to requeue all of the updates contained in it | 3859 | // Count this as a resent packet since we are going to requeue all of the updates contained in it |
3797 | Interlocked.Increment(ref m_udpClient.PacketsResent); | 3860 | Interlocked.Increment(ref m_udpClient.PacketsResent); |
3798 | 3861 | ||
3862 | // We're not going to worry about interlock yet since its not currently critical that this total count | ||
3863 | // is 100% correct | ||
3864 | m_udpServer.PacketsResentCount++; | ||
3865 | |||
3799 | foreach (EntityUpdate update in updates) | 3866 | foreach (EntityUpdate update in updates) |
3800 | ResendPrimUpdate(update); | 3867 | ResendPrimUpdate(update); |
3801 | } | 3868 | } |
3802 | 3869 | ||
3803 | private void ProcessEntityUpdates(int maxUpdates) | 3870 | private void ProcessEntityUpdates(int maxUpdates) |
3804 | { | 3871 | { |
3805 | OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); | 3872 | OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); |
@@ -3841,13 +3908,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3841 | { | 3908 | { |
3842 | SceneObjectPart part = (SceneObjectPart)update.Entity; | 3909 | SceneObjectPart part = (SceneObjectPart)update.Entity; |
3843 | 3910 | ||
3844 | if (part.ParentGroup.IsDeleted) | 3911 | if (part.ParentGroup.IsDeleted || part.ParentGroup.inTransit) |
3845 | continue; | 3912 | continue; |
3846 | 3913 | ||
3847 | if (part.ParentGroup.IsAttachment) | 3914 | if (part.ParentGroup.IsAttachment) |
3848 | { // Someone else's HUD, why are we getting these? | 3915 | { // Someone else's HUD, why are we getting these? |
3849 | if (part.ParentGroup.OwnerID != AgentId && | 3916 | if (part.ParentGroup.OwnerID != AgentId && part.ParentGroup.HasPrivateAttachmentPoint) |
3850 | part.ParentGroup.RootPart.Shape.State > 30) | ||
3851 | continue; | 3917 | continue; |
3852 | ScenePresence sp; | 3918 | ScenePresence sp; |
3853 | // Owner is not in the sim, don't update it to | 3919 | // Owner is not in the sim, don't update it to |
@@ -3879,36 +3945,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3879 | if (sp.IsChildAgent) | 3945 | if (sp.IsChildAgent) |
3880 | continue; | 3946 | continue; |
3881 | 3947 | ||
3882 | // If the object is an attachment we don't want it to be in the kill | ||
3883 | // record. Else attaching from inworld and subsequently dropping | ||
3884 | // it will no longer work. | ||
3885 | // lock (m_killRecord) | ||
3886 | // { | ||
3887 | // m_killRecord.Remove(part.LocalId); | ||
3888 | // m_killRecord.Remove(part.ParentGroup.RootPart.LocalId); | ||
3889 | // } | ||
3890 | } | ||
3891 | else | ||
3892 | { | ||
3893 | // Please do not remove this unless you can demonstrate on the OpenSim mailing list that a client | ||
3894 | // will never receive an update after a prim kill. Even then, keeping the kill record may be a good | ||
3895 | // safety measure. | ||
3896 | // | ||
3897 | // If a Linden Lab 1.23.5 client (and possibly later and earlier) receives an object update | ||
3898 | // after a kill, it will keep displaying the deleted object until relog. OpenSim currently performs | ||
3899 | // updates and kills on different threads with different scheduling strategies, hence this protection. | ||
3900 | // | ||
3901 | // This doesn't appear to apply to child prims - a client will happily ignore these updates | ||
3902 | // after the root prim has been deleted. | ||
3903 | // | ||
3904 | // We ignore this for attachments because attaching something from inworld breaks unless we do. | ||
3905 | // lock (m_killRecord) | ||
3906 | // { | ||
3907 | // if (m_killRecord.Contains(part.LocalId)) | ||
3908 | // continue; | ||
3909 | // if (m_killRecord.Contains(part.ParentGroup.RootPart.LocalId)) | ||
3910 | // continue; | ||
3911 | // } | ||
3912 | } | 3948 | } |
3913 | 3949 | ||
3914 | if (part.ParentGroup.IsAttachment && m_disableFacelights) | 3950 | if (part.ParentGroup.IsAttachment && m_disableFacelights) |
@@ -3939,7 +3975,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3939 | else if (update.Entity is ScenePresence) | 3975 | else if (update.Entity is ScenePresence) |
3940 | { | 3976 | { |
3941 | ScenePresence presence = (ScenePresence)update.Entity; | 3977 | ScenePresence presence = (ScenePresence)update.Entity; |
3942 | 3978 | if (presence.IsDeleted) | |
3979 | continue; | ||
3943 | // If ParentUUID is not UUID.Zero and ParentID is 0, this | 3980 | // If ParentUUID is not UUID.Zero and ParentID is 0, this |
3944 | // avatar is in the process of crossing regions while | 3981 | // avatar is in the process of crossing regions while |
3945 | // sat on an object. In this state, we don't want any | 3982 | // sat on an object. In this state, we don't want any |
@@ -4022,8 +4059,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4022 | } | 4059 | } |
4023 | else | 4060 | else |
4024 | { | 4061 | { |
4025 | if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId) | 4062 | if (update.Entity is ScenePresence) |
4026 | // Self updates go into a special list | 4063 | // ALL presence updates go into a special list |
4027 | terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); | 4064 | terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); |
4028 | else | 4065 | else |
4029 | // Everything else goes here | 4066 | // Everything else goes here |
@@ -4035,7 +4072,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4035 | 4072 | ||
4036 | #region Packet Sending | 4073 | #region Packet Sending |
4037 | 4074 | ||
4038 | const float TIME_DILATION = 1.0f; | 4075 | // const float TIME_DILATION = 1.0f; |
4039 | ushort timeDilation = Utils.FloatToUInt16(avgTimeDilation, 0.0f, 1.0f); | 4076 | ushort timeDilation = Utils.FloatToUInt16(avgTimeDilation, 0.0f, 1.0f); |
4040 | 4077 | ||
4041 | if (terseAgentUpdateBlocks.IsValueCreated) | 4078 | if (terseAgentUpdateBlocks.IsValueCreated) |
@@ -4051,7 +4088,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4051 | for (int i = 0; i < blocks.Count; i++) | 4088 | for (int i = 0; i < blocks.Count; i++) |
4052 | packet.ObjectData[i] = blocks[i]; | 4089 | packet.ObjectData[i] = blocks[i]; |
4053 | 4090 | ||
4054 | OutPacket(packet, ThrottleOutPacketType.Unknown, true); | 4091 | OutPacket(packet, ThrottleOutPacketType.Unknown, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseUpdates.Value, oPacket); }); |
4055 | } | 4092 | } |
4056 | 4093 | ||
4057 | if (objectUpdateBlocks.IsValueCreated) | 4094 | if (objectUpdateBlocks.IsValueCreated) |
@@ -4097,13 +4134,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4097 | 4134 | ||
4098 | for (int i = 0; i < blocks.Count; i++) | 4135 | for (int i = 0; i < blocks.Count; i++) |
4099 | packet.ObjectData[i] = blocks[i]; | 4136 | packet.ObjectData[i] = blocks[i]; |
4100 | 4137 | ||
4101 | OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseUpdates.Value, oPacket); }); | 4138 | OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseUpdates.Value, oPacket); }); |
4102 | } | 4139 | } |
4103 | 4140 | ||
4104 | #endregion Packet Sending | 4141 | #endregion Packet Sending |
4105 | } | 4142 | } |
4106 | 4143 | ||
4144 | // hack.. dont use | ||
4145 | public void SendPartFullUpdate(ISceneEntity ent, uint? parentID) | ||
4146 | { | ||
4147 | if (ent is SceneObjectPart) | ||
4148 | { | ||
4149 | SceneObjectPart part = (SceneObjectPart)ent; | ||
4150 | ObjectUpdatePacket packet = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); | ||
4151 | packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; | ||
4152 | packet.RegionData.TimeDilation = 1; | ||
4153 | packet.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; | ||
4154 | |||
4155 | ObjectUpdatePacket.ObjectDataBlock blk = CreatePrimUpdateBlock(part, this.m_agentId); | ||
4156 | if (parentID.HasValue) | ||
4157 | { | ||
4158 | blk.ParentID = parentID.Value; | ||
4159 | } | ||
4160 | |||
4161 | packet.ObjectData[0] = blk; | ||
4162 | |||
4163 | OutPacket(packet, ThrottleOutPacketType.Task, true); | ||
4164 | } | ||
4165 | } | ||
4166 | |||
4107 | public void ReprioritizeUpdates() | 4167 | public void ReprioritizeUpdates() |
4108 | { | 4168 | { |
4109 | lock (m_entityUpdates.SyncRoot) | 4169 | lock (m_entityUpdates.SyncRoot) |
@@ -4140,8 +4200,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4140 | 4200 | ||
4141 | void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) | 4201 | void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) |
4142 | { | 4202 | { |
4203 | // if (!m_udpServer.IsRunningOutbound) | ||
4204 | // return; | ||
4205 | |||
4143 | if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) | 4206 | if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) |
4144 | { | 4207 | { |
4208 | // if (!m_udpServer.IsRunningOutbound) | ||
4209 | // return; | ||
4210 | |||
4145 | if (m_maxUpdates == 0 || m_LastQueueFill == 0) | 4211 | if (m_maxUpdates == 0 || m_LastQueueFill == 0) |
4146 | { | 4212 | { |
4147 | m_maxUpdates = m_udpServer.PrimUpdatesPerCallback; | 4213 | m_maxUpdates = m_udpServer.PrimUpdatesPerCallback; |
@@ -4167,6 +4233,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4167 | ImageManager.ProcessImageQueue(m_udpServer.TextureSendLimit); | 4233 | ImageManager.ProcessImageQueue(m_udpServer.TextureSendLimit); |
4168 | } | 4234 | } |
4169 | 4235 | ||
4236 | internal bool HandleHasUpdates(ThrottleOutPacketTypeFlags categories) | ||
4237 | { | ||
4238 | bool hasUpdates = false; | ||
4239 | |||
4240 | if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) | ||
4241 | { | ||
4242 | if (m_entityUpdates.Count > 0) | ||
4243 | hasUpdates = true; | ||
4244 | else if (m_entityProps.Count > 0) | ||
4245 | hasUpdates = true; | ||
4246 | } | ||
4247 | |||
4248 | if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) | ||
4249 | { | ||
4250 | if (ImageManager.HasUpdates()) | ||
4251 | hasUpdates = true; | ||
4252 | } | ||
4253 | |||
4254 | return hasUpdates; | ||
4255 | } | ||
4256 | |||
4170 | public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID) | 4257 | public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID) |
4171 | { | 4258 | { |
4172 | AssetUploadCompletePacket newPack = new AssetUploadCompletePacket(); | 4259 | AssetUploadCompletePacket newPack = new AssetUploadCompletePacket(); |
@@ -4312,6 +4399,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4312 | // Count this as a resent packet since we are going to requeue all of the updates contained in it | 4399 | // Count this as a resent packet since we are going to requeue all of the updates contained in it |
4313 | Interlocked.Increment(ref m_udpClient.PacketsResent); | 4400 | Interlocked.Increment(ref m_udpClient.PacketsResent); |
4314 | 4401 | ||
4402 | // We're not going to worry about interlock yet since its not currently critical that this total count | ||
4403 | // is 100% correct | ||
4404 | m_udpServer.PacketsResentCount++; | ||
4405 | |||
4315 | foreach (ObjectPropertyUpdate update in updates) | 4406 | foreach (ObjectPropertyUpdate update in updates) |
4316 | ResendPropertyUpdate(update); | 4407 | ResendPropertyUpdate(update); |
4317 | } | 4408 | } |
@@ -4499,6 +4590,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4499 | SceneObjectPart root = sop.ParentGroup.RootPart; | 4590 | SceneObjectPart root = sop.ParentGroup.RootPart; |
4500 | 4591 | ||
4501 | block.TouchName = Util.StringToBytes256(root.TouchName); | 4592 | block.TouchName = Util.StringToBytes256(root.TouchName); |
4593 | |||
4594 | // SL 3.3.4, at least, appears to read this information as a concatenated byte[] stream of UUIDs but | ||
4595 | // it's not yet clear whether this is actually used. If this is done in the future then a pre-cached | ||
4596 | // copy is really needed since it's less efficient to be constantly recreating this byte array. | ||
4597 | // using (MemoryStream memStream = new MemoryStream()) | ||
4598 | // { | ||
4599 | // using (BinaryWriter binWriter = new BinaryWriter(memStream)) | ||
4600 | // { | ||
4601 | // for (int i = 0; i < sop.GetNumberOfSides(); i++) | ||
4602 | // { | ||
4603 | // Primitive.TextureEntryFace teFace = sop.Shape.Textures.FaceTextures[i]; | ||
4604 | // | ||
4605 | // UUID textureID; | ||
4606 | // | ||
4607 | // if (teFace != null) | ||
4608 | // textureID = teFace.TextureID; | ||
4609 | // else | ||
4610 | // textureID = sop.Shape.Textures.DefaultTexture.TextureID; | ||
4611 | // | ||
4612 | // binWriter.Write(textureID.GetBytes()); | ||
4613 | // } | ||
4614 | // | ||
4615 | // block.TextureID = memStream.ToArray(); | ||
4616 | // } | ||
4617 | // } | ||
4618 | |||
4502 | block.TextureID = new byte[0]; // TextureID ??? | 4619 | block.TextureID = new byte[0]; // TextureID ??? |
4503 | block.SitName = Util.StringToBytes256(root.SitName); | 4620 | block.SitName = Util.StringToBytes256(root.SitName); |
4504 | block.OwnerMask = root.OwnerMask; | 4621 | block.OwnerMask = root.OwnerMask; |
@@ -4839,7 +4956,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4839 | IEventQueue eq = Scene.RequestModuleInterface<IEventQueue>(); | 4956 | IEventQueue eq = Scene.RequestModuleInterface<IEventQueue>(); |
4840 | if (eq != null) | 4957 | if (eq != null) |
4841 | { | 4958 | { |
4842 | eq.ParcelProperties(updateMessage, this.AgentId); | 4959 | |
4960 | OSD message_body = updateMessage.Serialize(); | ||
4961 | // Add new fields here until OMV has them | ||
4962 | OSDMap bodyMap = (OSDMap)message_body; | ||
4963 | OSDArray parcelDataArray = (OSDArray)bodyMap["ParcelData"]; | ||
4964 | OSDMap parcelData = (OSDMap)parcelDataArray[0]; | ||
4965 | parcelData["SeeAVs"] = OSD.FromBoolean(landData.SeeAVs); | ||
4966 | parcelData["AnyAVSounds"] = OSD.FromBoolean(landData.AnyAVSounds); | ||
4967 | parcelData["GroupAVSounds"] = OSD.FromBoolean(landData.GroupAVSounds); | ||
4968 | OSDMap message = new OSDMap(); | ||
4969 | message.Add("message", OSD.FromString("ParcelProperties")); | ||
4970 | message.Add("body", message_body); | ||
4971 | |||
4972 | eq.Enqueue (message, this.AgentId); | ||
4973 | |||
4974 | // eq.ParcelProperties(updateMessage, this.AgentId); | ||
4843 | } | 4975 | } |
4844 | else | 4976 | else |
4845 | { | 4977 | { |
@@ -4877,7 +5009,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4877 | 5009 | ||
4878 | public void SendForceClientSelectObjects(List<uint> ObjectIDs) | 5010 | public void SendForceClientSelectObjects(List<uint> ObjectIDs) |
4879 | { | 5011 | { |
4880 | m_log.WarnFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count); | 5012 | // m_log.DebugFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count); |
4881 | 5013 | ||
4882 | bool firstCall = true; | 5014 | bool firstCall = true; |
4883 | const int MAX_OBJECTS_PER_PACKET = 251; | 5015 | const int MAX_OBJECTS_PER_PACKET = 251; |
@@ -4997,35 +5129,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4997 | 5129 | ||
4998 | position = presence.OffsetPosition; | 5130 | position = presence.OffsetPosition; |
4999 | rotation = presence.Rotation; | 5131 | rotation = presence.Rotation; |
5000 | 5132 | angularVelocity = presence.AngularVelocity; | |
5001 | if (presence.ParentID != 0) | 5133 | rotation = presence.Rotation; |
5002 | { | ||
5003 | SceneObjectPart part = m_scene.GetSceneObjectPart(presence.ParentID); | ||
5004 | if (part != null && part != part.ParentGroup.RootPart) | ||
5005 | { | ||
5006 | position = part.OffsetPosition + presence.OffsetPosition * part.RotationOffset; | ||
5007 | rotation = part.RotationOffset * presence.Rotation; | ||
5008 | } | ||
5009 | angularVelocity = Vector3.Zero; | ||
5010 | } | ||
5011 | else | ||
5012 | { | ||
5013 | angularVelocity = presence.AngularVelocity; | ||
5014 | rotation = presence.Rotation; | ||
5015 | } | ||
5016 | 5134 | ||
5017 | attachPoint = 0; | 5135 | attachPoint = 0; |
5136 | // m_log.DebugFormat( | ||
5137 | // "[LLCLIENTVIEW]: Sending terse update to {0} with position {1} in {2}", Name, presence.OffsetPosition, m_scene.Name); | ||
5138 | |||
5139 | // attachPoint = presence.State; // Core: commented | ||
5018 | collisionPlane = presence.CollisionPlane; | 5140 | collisionPlane = presence.CollisionPlane; |
5019 | velocity = presence.Velocity; | 5141 | velocity = presence.Velocity; |
5020 | acceleration = Vector3.Zero; | 5142 | acceleration = Vector3.Zero; |
5021 | 5143 | ||
5022 | // Interestingly, sending this to non-zero will cause the client's avatar to start moving & accelerating | ||
5023 | // in that direction, even though we don't model this on the server. Implementing this in the future | ||
5024 | // may improve movement smoothness. | ||
5025 | // acceleration = new Vector3(1, 0, 0); | ||
5026 | |||
5027 | if (sendTexture) | 5144 | if (sendTexture) |
5028 | textureEntry = presence.Appearance.Texture.GetBytes(); | 5145 | { |
5146 | if (presence.Invisible) | ||
5147 | textureEntry = AvatarAppearance.Invisible.GetBytes(); | ||
5148 | else | ||
5149 | textureEntry = presence.Appearance.Texture.GetBytes(); | ||
5150 | } | ||
5029 | else | 5151 | else |
5030 | textureEntry = null; | 5152 | textureEntry = null; |
5031 | } | 5153 | } |
@@ -5034,7 +5156,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5034 | SceneObjectPart part = (SceneObjectPart)entity; | 5156 | SceneObjectPart part = (SceneObjectPart)entity; |
5035 | 5157 | ||
5036 | attachPoint = part.ParentGroup.AttachmentPoint; | 5158 | attachPoint = part.ParentGroup.AttachmentPoint; |
5037 | 5159 | attachPoint = ((attachPoint % 16) * 16 + (attachPoint / 16)); | |
5038 | // m_log.DebugFormat( | 5160 | // m_log.DebugFormat( |
5039 | // "[LLCLIENTVIEW]: Sending attachPoint {0} for {1} {2} to {3}", | 5161 | // "[LLCLIENTVIEW]: Sending attachPoint {0} for {1} {2} to {3}", |
5040 | // attachPoint, part.Name, part.LocalId, Name); | 5162 | // attachPoint, part.Name, part.LocalId, Name); |
@@ -5062,7 +5184,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5062 | pos += 4; | 5184 | pos += 4; |
5063 | 5185 | ||
5064 | // Avatar/CollisionPlane | 5186 | // Avatar/CollisionPlane |
5065 | data[pos++] = (byte)((attachPoint % 16) * 16 + (attachPoint / 16)); ; | 5187 | data[pos++] = (byte) attachPoint; |
5066 | if (avatar) | 5188 | if (avatar) |
5067 | { | 5189 | { |
5068 | data[pos++] = 1; | 5190 | data[pos++] = 1; |
@@ -5132,30 +5254,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5132 | Vector3 offsetPosition = data.OffsetPosition; | 5254 | Vector3 offsetPosition = data.OffsetPosition; |
5133 | Quaternion rotation = data.Rotation; | 5255 | Quaternion rotation = data.Rotation; |
5134 | uint parentID = data.ParentID; | 5256 | uint parentID = data.ParentID; |
5135 | 5257 | ||
5136 | if (parentID != 0) | 5258 | // m_log.DebugFormat( |
5137 | { | 5259 | // "[LLCLIENTVIEW]: Sending full update to {0} with position {1} in {2}", Name, data.OffsetPosition, m_scene.Name); |
5138 | SceneObjectPart part = m_scene.GetSceneObjectPart(parentID); | ||
5139 | if (part != null && part != part.ParentGroup.RootPart) | ||
5140 | { | ||
5141 | offsetPosition = part.OffsetPosition + data.OffsetPosition * part.RotationOffset; | ||
5142 | rotation = part.RotationOffset * data.Rotation; | ||
5143 | parentID = part.ParentGroup.RootPart.LocalId; | ||
5144 | } | ||
5145 | } | ||
5146 | 5260 | ||
5147 | byte[] objectData = new byte[76]; | 5261 | byte[] objectData = new byte[76]; |
5148 | 5262 | ||
5149 | data.CollisionPlane.ToBytes(objectData, 0); | ||
5150 | offsetPosition.ToBytes(objectData, 16); | ||
5151 | Vector3 velocity = new Vector3(0, 0, 0); | 5263 | Vector3 velocity = new Vector3(0, 0, 0); |
5152 | Vector3 acceleration = new Vector3(0, 0, 0); | 5264 | Vector3 acceleration = new Vector3(0, 0, 0); |
5265 | rotation.Normalize(); | ||
5266 | |||
5267 | data.CollisionPlane.ToBytes(objectData, 0); | ||
5268 | offsetPosition.ToBytes(objectData, 16); | ||
5153 | velocity.ToBytes(objectData, 28); | 5269 | velocity.ToBytes(objectData, 28); |
5154 | acceleration.ToBytes(objectData, 40); | 5270 | acceleration.ToBytes(objectData, 40); |
5155 | // data.Velocity.ToBytes(objectData, 28); | ||
5156 | // data.Acceleration.ToBytes(objectData, 40); | ||
5157 | rotation.ToBytes(objectData, 52); | 5271 | rotation.ToBytes(objectData, 52); |
5158 | //data.AngularVelocity.ToBytes(objectData, 64); | 5272 | data.AngularVelocity.ToBytes(objectData, 64); |
5159 | 5273 | ||
5160 | ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); | 5274 | ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); |
5161 | 5275 | ||
@@ -5168,7 +5282,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5168 | update.NameValue = Utils.StringToBytes("FirstName STRING RW SV " + data.Firstname + "\nLastName STRING RW SV " + | 5282 | update.NameValue = Utils.StringToBytes("FirstName STRING RW SV " + data.Firstname + "\nLastName STRING RW SV " + |
5169 | data.Lastname + "\nTitle STRING RW SV " + data.Grouptitle); | 5283 | data.Lastname + "\nTitle STRING RW SV " + data.Grouptitle); |
5170 | update.ObjectData = objectData; | 5284 | update.ObjectData = objectData; |
5171 | update.ParentID = parentID; | 5285 | |
5286 | SceneObjectPart parentPart = data.ParentPart; | ||
5287 | if (parentPart != null) | ||
5288 | update.ParentID = parentPart.ParentGroup.LocalId; | ||
5289 | else | ||
5290 | update.ParentID = 0; | ||
5291 | |||
5172 | update.PathCurve = 16; | 5292 | update.PathCurve = 16; |
5173 | update.PathScaleX = 100; | 5293 | update.PathScaleX = 100; |
5174 | update.PathScaleY = 100; | 5294 | update.PathScaleY = 100; |
@@ -5205,15 +5325,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5205 | data.RelativePosition.ToBytes(objectData, 0); | 5325 | data.RelativePosition.ToBytes(objectData, 0); |
5206 | data.Velocity.ToBytes(objectData, 12); | 5326 | data.Velocity.ToBytes(objectData, 12); |
5207 | data.Acceleration.ToBytes(objectData, 24); | 5327 | data.Acceleration.ToBytes(objectData, 24); |
5208 | try | 5328 | |
5209 | { | 5329 | Quaternion rotation = data.RotationOffset; |
5210 | data.RotationOffset.ToBytes(objectData, 36); | 5330 | rotation.Normalize(); |
5211 | } | 5331 | rotation.ToBytes(objectData, 36); |
5212 | catch (Exception e) | ||
5213 | { | ||
5214 | m_log.Warn("[LLClientView]: exception converting quaternion to bytes, using Quaternion.Identity. Exception: " + e.ToString()); | ||
5215 | OpenMetaverse.Quaternion.Identity.ToBytes(objectData, 36); | ||
5216 | } | ||
5217 | data.AngularVelocity.ToBytes(objectData, 48); | 5332 | data.AngularVelocity.ToBytes(objectData, 48); |
5218 | 5333 | ||
5219 | ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); | 5334 | ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); |
@@ -5227,6 +5342,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5227 | //update.JointType = 0; | 5342 | //update.JointType = 0; |
5228 | update.Material = data.Material; | 5343 | update.Material = data.Material; |
5229 | update.MediaURL = Utils.EmptyBytes; // FIXME: Support this in OpenSim | 5344 | update.MediaURL = Utils.EmptyBytes; // FIXME: Support this in OpenSim |
5345 | /* | ||
5230 | if (data.ParentGroup.IsAttachment) | 5346 | if (data.ParentGroup.IsAttachment) |
5231 | { | 5347 | { |
5232 | update.NameValue = Util.StringToBytes256("AttachItemID STRING RW SV " + data.ParentGroup.FromItemID); | 5348 | update.NameValue = Util.StringToBytes256("AttachItemID STRING RW SV " + data.ParentGroup.FromItemID); |
@@ -5240,6 +5356,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5240 | // case for attachments may contain conflicting values that can end up crashing the viewer. | 5356 | // case for attachments may contain conflicting values that can end up crashing the viewer. |
5241 | update.State = data.ParentGroup.RootPart.Shape.State; | 5357 | update.State = data.ParentGroup.RootPart.Shape.State; |
5242 | } | 5358 | } |
5359 | */ | ||
5360 | |||
5361 | if (data.ParentGroup.IsAttachment) | ||
5362 | { | ||
5363 | if (data.IsRoot) | ||
5364 | { | ||
5365 | update.NameValue = Util.StringToBytes256("AttachItemID STRING RW SV " + data.ParentGroup.FromItemID); | ||
5366 | } | ||
5367 | else | ||
5368 | update.NameValue = Utils.EmptyBytes; | ||
5369 | |||
5370 | int st = (int)data.ParentGroup.AttachmentPoint; | ||
5371 | update.State = (byte)(((st & 0xf0) >> 4) + ((st & 0x0f) << 4)); ; | ||
5372 | } | ||
5373 | else | ||
5374 | { | ||
5375 | update.NameValue = Utils.EmptyBytes; | ||
5376 | update.State = data.Shape.State; // not sure about this | ||
5377 | } | ||
5378 | |||
5243 | 5379 | ||
5244 | // m_log.DebugFormat( | 5380 | // m_log.DebugFormat( |
5245 | // "[LLCLIENTVIEW]: Sending state {0} for {1} {2} to {3}", | 5381 | // "[LLCLIENTVIEW]: Sending state {0} for {1} {2} to {3}", |
@@ -5387,7 +5523,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5387 | AddLocalPacketHandler(PacketType.RezObject, HandlerRezObject); | 5523 | AddLocalPacketHandler(PacketType.RezObject, HandlerRezObject); |
5388 | AddLocalPacketHandler(PacketType.DeRezObject, HandlerDeRezObject); | 5524 | AddLocalPacketHandler(PacketType.DeRezObject, HandlerDeRezObject); |
5389 | AddLocalPacketHandler(PacketType.ModifyLand, HandlerModifyLand); | 5525 | AddLocalPacketHandler(PacketType.ModifyLand, HandlerModifyLand); |
5390 | AddLocalPacketHandler(PacketType.RegionHandshakeReply, HandlerRegionHandshakeReply); | 5526 | AddLocalPacketHandler(PacketType.RegionHandshakeReply, HandlerRegionHandshakeReply, false); |
5391 | AddLocalPacketHandler(PacketType.AgentWearablesRequest, HandlerAgentWearablesRequest); | 5527 | AddLocalPacketHandler(PacketType.AgentWearablesRequest, HandlerAgentWearablesRequest); |
5392 | AddLocalPacketHandler(PacketType.AgentSetAppearance, HandlerAgentSetAppearance); | 5528 | AddLocalPacketHandler(PacketType.AgentSetAppearance, HandlerAgentSetAppearance); |
5393 | AddLocalPacketHandler(PacketType.AgentIsNowWearing, HandlerAgentIsNowWearing); | 5529 | AddLocalPacketHandler(PacketType.AgentIsNowWearing, HandlerAgentIsNowWearing); |
@@ -5448,8 +5584,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5448 | AddLocalPacketHandler(PacketType.ScriptAnswerYes, HandleScriptAnswerYes, false); | 5584 | AddLocalPacketHandler(PacketType.ScriptAnswerYes, HandleScriptAnswerYes, false); |
5449 | AddLocalPacketHandler(PacketType.ObjectClickAction, HandleObjectClickAction, false); | 5585 | AddLocalPacketHandler(PacketType.ObjectClickAction, HandleObjectClickAction, false); |
5450 | AddLocalPacketHandler(PacketType.ObjectMaterial, HandleObjectMaterial, false); | 5586 | AddLocalPacketHandler(PacketType.ObjectMaterial, HandleObjectMaterial, false); |
5451 | AddLocalPacketHandler(PacketType.RequestImage, HandleRequestImage); | 5587 | AddLocalPacketHandler(PacketType.RequestImage, HandleRequestImage, false); |
5452 | AddLocalPacketHandler(PacketType.TransferRequest, HandleTransferRequest); | 5588 | AddLocalPacketHandler(PacketType.TransferRequest, HandleTransferRequest, false); |
5453 | AddLocalPacketHandler(PacketType.AssetUploadRequest, HandleAssetUploadRequest); | 5589 | AddLocalPacketHandler(PacketType.AssetUploadRequest, HandleAssetUploadRequest); |
5454 | AddLocalPacketHandler(PacketType.RequestXfer, HandleRequestXfer); | 5590 | AddLocalPacketHandler(PacketType.RequestXfer, HandleRequestXfer); |
5455 | AddLocalPacketHandler(PacketType.SendXferPacket, HandleSendXferPacket); | 5591 | AddLocalPacketHandler(PacketType.SendXferPacket, HandleSendXferPacket); |
@@ -5481,7 +5617,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5481 | AddLocalPacketHandler(PacketType.TeleportCancel, HandleTeleportCancel); | 5617 | AddLocalPacketHandler(PacketType.TeleportCancel, HandleTeleportCancel); |
5482 | AddLocalPacketHandler(PacketType.TeleportLocationRequest, HandleTeleportLocationRequest); | 5618 | AddLocalPacketHandler(PacketType.TeleportLocationRequest, HandleTeleportLocationRequest); |
5483 | AddLocalPacketHandler(PacketType.UUIDNameRequest, HandleUUIDNameRequest, false); | 5619 | AddLocalPacketHandler(PacketType.UUIDNameRequest, HandleUUIDNameRequest, false); |
5484 | AddLocalPacketHandler(PacketType.RegionHandleRequest, HandleRegionHandleRequest); | 5620 | AddLocalPacketHandler(PacketType.RegionHandleRequest, HandleRegionHandleRequest, false); |
5485 | AddLocalPacketHandler(PacketType.ParcelInfoRequest, HandleParcelInfoRequest); | 5621 | AddLocalPacketHandler(PacketType.ParcelInfoRequest, HandleParcelInfoRequest); |
5486 | AddLocalPacketHandler(PacketType.ParcelAccessListRequest, HandleParcelAccessListRequest, false); | 5622 | AddLocalPacketHandler(PacketType.ParcelAccessListRequest, HandleParcelAccessListRequest, false); |
5487 | AddLocalPacketHandler(PacketType.ParcelAccessListUpdate, HandleParcelAccessListUpdate, false); | 5623 | AddLocalPacketHandler(PacketType.ParcelAccessListUpdate, HandleParcelAccessListUpdate, false); |
@@ -5594,82 +5730,146 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5594 | 5730 | ||
5595 | #region Packet Handlers | 5731 | #region Packet Handlers |
5596 | 5732 | ||
5733 | public int TotalAgentUpdates { get; set; } | ||
5734 | |||
5597 | #region Scene/Avatar | 5735 | #region Scene/Avatar |
5598 | 5736 | ||
5599 | private bool HandleAgentUpdate(IClientAPI sener, Packet packet) | 5737 | // Threshold for body rotation to be a significant agent update |
5738 | // use the abs of cos | ||
5739 | private const float QDELTABody = 1.0f - 0.0001f; | ||
5740 | private const float QDELTAHead = 1.0f - 0.0001f; | ||
5741 | // Threshold for camera rotation to be a significant agent update | ||
5742 | private const float VDELTA = 0.01f; | ||
5743 | |||
5744 | /// <summary> | ||
5745 | /// This checks the update significance against the last update made. | ||
5746 | /// </summary> | ||
5747 | /// <remarks>Can only be called by one thread at a time</remarks> | ||
5748 | /// <returns></returns> | ||
5749 | /// <param name='x'></param> | ||
5750 | public bool CheckAgentUpdateSignificance(AgentUpdatePacket.AgentDataBlock x) | ||
5600 | { | 5751 | { |
5601 | if (OnAgentUpdate != null) | 5752 | return CheckAgentMovementUpdateSignificance(x) || CheckAgentCameraUpdateSignificance(x); |
5753 | } | ||
5754 | |||
5755 | /// <summary> | ||
5756 | /// This checks the movement/state update significance against the last update made. | ||
5757 | /// </summary> | ||
5758 | /// <remarks>Can only be called by one thread at a time</remarks> | ||
5759 | /// <returns></returns> | ||
5760 | /// <param name='x'></param> | ||
5761 | private bool CheckAgentMovementUpdateSignificance(AgentUpdatePacket.AgentDataBlock x) | ||
5762 | { | ||
5763 | float qdelta1 = Math.Abs(Quaternion.Dot(x.BodyRotation, m_thisAgentUpdateArgs.BodyRotation)); | ||
5764 | //qdelta2 = Math.Abs(Quaternion.Dot(x.HeadRotation, m_thisAgentUpdateArgs.HeadRotation)); | ||
5765 | |||
5766 | bool movementSignificant = | ||
5767 | (x.ControlFlags != m_thisAgentUpdateArgs.ControlFlags) // significant if control flags changed | ||
5768 | || (x.ControlFlags != (byte)AgentManager.ControlFlags.NONE) // significant if user supplying any movement update commands | ||
5769 | || (x.Flags != m_thisAgentUpdateArgs.Flags) // significant if Flags changed | ||
5770 | || (x.State != m_thisAgentUpdateArgs.State) // significant if Stats changed | ||
5771 | || (qdelta1 < QDELTABody) // significant if body rotation above(below cos) threshold | ||
5772 | // Ignoring head rotation altogether, because it's not being used for anything interesting up the stack | ||
5773 | // || (qdelta2 < QDELTAHead) // significant if head rotation above(below cos) threshold | ||
5774 | || (Math.Abs(x.Far - m_thisAgentUpdateArgs.Far) >= 32) // significant if far distance changed | ||
5775 | ; | ||
5776 | //if (movementSignificant) | ||
5777 | //{ | ||
5778 | //m_log.DebugFormat("[LLCLIENTVIEW]: Bod {0} {1}", | ||
5779 | // qdelta1, qdelta2); | ||
5780 | //m_log.DebugFormat("[LLCLIENTVIEW]: St {0} {1} {2} {3}", | ||
5781 | // x.ControlFlags, x.Flags, x.Far, x.State); | ||
5782 | //} | ||
5783 | return movementSignificant; | ||
5784 | } | ||
5785 | |||
5786 | /// <summary> | ||
5787 | /// This checks the camera update significance against the last update made. | ||
5788 | /// </summary> | ||
5789 | /// <remarks>Can only be called by one thread at a time</remarks> | ||
5790 | /// <returns></returns> | ||
5791 | /// <param name='x'></param> | ||
5792 | private bool CheckAgentCameraUpdateSignificance(AgentUpdatePacket.AgentDataBlock x) | ||
5793 | { | ||
5794 | float vdelta1 = Vector3.Distance(x.CameraAtAxis, m_thisAgentUpdateArgs.CameraAtAxis); | ||
5795 | float vdelta2 = Vector3.Distance(x.CameraCenter, m_thisAgentUpdateArgs.CameraCenter); | ||
5796 | float vdelta3 = Vector3.Distance(x.CameraLeftAxis, m_thisAgentUpdateArgs.CameraLeftAxis); | ||
5797 | float vdelta4 = Vector3.Distance(x.CameraUpAxis, m_thisAgentUpdateArgs.CameraUpAxis); | ||
5798 | |||
5799 | bool cameraSignificant = | ||
5800 | (vdelta1 > VDELTA) || | ||
5801 | (vdelta2 > VDELTA) || | ||
5802 | (vdelta3 > VDELTA) || | ||
5803 | (vdelta4 > VDELTA) | ||
5804 | ; | ||
5805 | |||
5806 | //if (cameraSignificant) | ||
5807 | //{ | ||
5808 | //m_log.DebugFormat("[LLCLIENTVIEW]: Cam1 {0} {1}", | ||
5809 | // x.CameraAtAxis, x.CameraCenter); | ||
5810 | //m_log.DebugFormat("[LLCLIENTVIEW]: Cam2 {0} {1}", | ||
5811 | // x.CameraLeftAxis, x.CameraUpAxis); | ||
5812 | //} | ||
5813 | |||
5814 | return cameraSignificant; | ||
5815 | } | ||
5816 | |||
5817 | private bool HandleAgentUpdate(IClientAPI sender, Packet packet) | ||
5818 | { | ||
5819 | // We got here, which means that something in agent update was significant | ||
5820 | |||
5821 | AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet; | ||
5822 | AgentUpdatePacket.AgentDataBlock x = agentUpdate.AgentData; | ||
5823 | |||
5824 | if (x.AgentID != AgentId || x.SessionID != SessionId) | ||
5602 | { | 5825 | { |
5603 | AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet; | 5826 | PacketPool.Instance.ReturnPacket(packet); |
5827 | return false; | ||
5828 | } | ||
5604 | 5829 | ||
5605 | #region Packet Session and User Check | 5830 | TotalAgentUpdates++; |
5606 | if (agentUpdate.AgentData.SessionID != SessionId || agentUpdate.AgentData.AgentID != AgentId) | ||
5607 | { | ||
5608 | PacketPool.Instance.ReturnPacket(packet); | ||
5609 | return false; | ||
5610 | } | ||
5611 | #endregion | ||
5612 | 5831 | ||
5613 | bool update = false; | 5832 | bool movement = CheckAgentMovementUpdateSignificance(x); |
5614 | AgentUpdatePacket.AgentDataBlock x = agentUpdate.AgentData; | 5833 | bool camera = CheckAgentCameraUpdateSignificance(x); |
5615 | |||
5616 | if (m_lastAgentUpdateArgs != null) | ||
5617 | { | ||
5618 | // These should be ordered from most-likely to | ||
5619 | // least likely to change. I've made an initial | ||
5620 | // guess at that. | ||
5621 | update = | ||
5622 | ( | ||
5623 | (x.BodyRotation != m_lastAgentUpdateArgs.BodyRotation) || | ||
5624 | (x.CameraAtAxis != m_lastAgentUpdateArgs.CameraAtAxis) || | ||
5625 | (x.CameraCenter != m_lastAgentUpdateArgs.CameraCenter) || | ||
5626 | (x.CameraLeftAxis != m_lastAgentUpdateArgs.CameraLeftAxis) || | ||
5627 | (x.CameraUpAxis != m_lastAgentUpdateArgs.CameraUpAxis) || | ||
5628 | (x.ControlFlags != m_lastAgentUpdateArgs.ControlFlags) || | ||
5629 | (x.ControlFlags != 0) || | ||
5630 | (x.Far != m_lastAgentUpdateArgs.Far) || | ||
5631 | (x.Flags != m_lastAgentUpdateArgs.Flags) || | ||
5632 | (x.State != m_lastAgentUpdateArgs.State) || | ||
5633 | (x.HeadRotation != m_lastAgentUpdateArgs.HeadRotation) || | ||
5634 | (x.SessionID != m_lastAgentUpdateArgs.SessionID) || | ||
5635 | (x.AgentID != m_lastAgentUpdateArgs.AgentID) | ||
5636 | ); | ||
5637 | } | ||
5638 | else | ||
5639 | { | ||
5640 | m_lastAgentUpdateArgs = new AgentUpdateArgs(); | ||
5641 | update = true; | ||
5642 | } | ||
5643 | 5834 | ||
5644 | if (update) | 5835 | // Was there a significant movement/state change? |
5645 | { | 5836 | if (movement) |
5646 | // m_log.DebugFormat("[LLCLIENTVIEW]: Triggered AgentUpdate for {0}", sener.Name); | 5837 | { |
5838 | m_thisAgentUpdateArgs.BodyRotation = x.BodyRotation; | ||
5839 | m_thisAgentUpdateArgs.ControlFlags = x.ControlFlags; | ||
5840 | m_thisAgentUpdateArgs.Far = x.Far; | ||
5841 | m_thisAgentUpdateArgs.Flags = x.Flags; | ||
5842 | m_thisAgentUpdateArgs.HeadRotation = x.HeadRotation; | ||
5843 | // m_thisAgentUpdateArgs.SessionID = x.SessionID; | ||
5844 | m_thisAgentUpdateArgs.State = x.State; | ||
5845 | |||
5846 | UpdateAgent handlerAgentUpdate = OnAgentUpdate; | ||
5847 | UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate; | ||
5647 | 5848 | ||
5648 | m_lastAgentUpdateArgs.AgentID = x.AgentID; | 5849 | if (handlerPreAgentUpdate != null) |
5649 | m_lastAgentUpdateArgs.BodyRotation = x.BodyRotation; | 5850 | OnPreAgentUpdate(this, m_thisAgentUpdateArgs); |
5650 | m_lastAgentUpdateArgs.CameraAtAxis = x.CameraAtAxis; | ||
5651 | m_lastAgentUpdateArgs.CameraCenter = x.CameraCenter; | ||
5652 | m_lastAgentUpdateArgs.CameraLeftAxis = x.CameraLeftAxis; | ||
5653 | m_lastAgentUpdateArgs.CameraUpAxis = x.CameraUpAxis; | ||
5654 | m_lastAgentUpdateArgs.ControlFlags = x.ControlFlags; | ||
5655 | m_lastAgentUpdateArgs.Far = x.Far; | ||
5656 | m_lastAgentUpdateArgs.Flags = x.Flags; | ||
5657 | m_lastAgentUpdateArgs.HeadRotation = x.HeadRotation; | ||
5658 | m_lastAgentUpdateArgs.SessionID = x.SessionID; | ||
5659 | m_lastAgentUpdateArgs.State = x.State; | ||
5660 | 5851 | ||
5661 | UpdateAgent handlerAgentUpdate = OnAgentUpdate; | 5852 | if (handlerAgentUpdate != null) |
5662 | UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate; | 5853 | OnAgentUpdate(this, m_thisAgentUpdateArgs); |
5663 | 5854 | ||
5664 | if (handlerPreAgentUpdate != null) | 5855 | handlerAgentUpdate = null; |
5665 | OnPreAgentUpdate(this, m_lastAgentUpdateArgs); | 5856 | handlerPreAgentUpdate = null; |
5857 | } | ||
5666 | 5858 | ||
5667 | if (handlerAgentUpdate != null) | 5859 | // Was there a significant camera(s) change? |
5668 | OnAgentUpdate(this, m_lastAgentUpdateArgs); | 5860 | if (camera) |
5861 | { | ||
5862 | m_thisAgentUpdateArgs.CameraAtAxis = x.CameraAtAxis; | ||
5863 | m_thisAgentUpdateArgs.CameraCenter = x.CameraCenter; | ||
5864 | m_thisAgentUpdateArgs.CameraLeftAxis = x.CameraLeftAxis; | ||
5865 | m_thisAgentUpdateArgs.CameraUpAxis = x.CameraUpAxis; | ||
5669 | 5866 | ||
5670 | handlerAgentUpdate = null; | 5867 | UpdateAgent handlerAgentCameraUpdate = OnAgentCameraUpdate; |
5671 | handlerPreAgentUpdate = null; | 5868 | |
5672 | } | 5869 | if (handlerAgentCameraUpdate != null) |
5870 | handlerAgentCameraUpdate(this, m_thisAgentUpdateArgs); | ||
5871 | |||
5872 | handlerAgentCameraUpdate = null; | ||
5673 | } | 5873 | } |
5674 | 5874 | ||
5675 | PacketPool.Instance.ReturnPacket(packet); | 5875 | PacketPool.Instance.ReturnPacket(packet); |
@@ -6260,6 +6460,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6260 | //m_log.Info("[LAND]: LAND:" + modify.ToString()); | 6460 | //m_log.Info("[LAND]: LAND:" + modify.ToString()); |
6261 | if (modify.ParcelData.Length > 0) | 6461 | if (modify.ParcelData.Length > 0) |
6262 | { | 6462 | { |
6463 | // Note: the ModifyTerrain event handler sends out updated packets before the end of this event. Therefore, | ||
6464 | // a simple boolean value should work and perhaps queue up just a few terrain patch packets at the end of the edit. | ||
6465 | m_justEditedTerrain = true; // Prevent terrain packet (Land layer) from being queued, make it unreliable | ||
6263 | if (OnModifyTerrain != null) | 6466 | if (OnModifyTerrain != null) |
6264 | { | 6467 | { |
6265 | for (int i = 0; i < modify.ParcelData.Length; i++) | 6468 | for (int i = 0; i < modify.ParcelData.Length; i++) |
@@ -6275,6 +6478,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6275 | } | 6478 | } |
6276 | } | 6479 | } |
6277 | } | 6480 | } |
6481 | m_justEditedTerrain = false; // Queue terrain packet (Land layer) if necessary, make it reliable again | ||
6278 | } | 6482 | } |
6279 | 6483 | ||
6280 | return true; | 6484 | return true; |
@@ -6343,7 +6547,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6343 | 6547 | ||
6344 | WearableCacheItem[] cacheitems = new WearableCacheItem[appear.WearableData.Length]; | 6548 | WearableCacheItem[] cacheitems = new WearableCacheItem[appear.WearableData.Length]; |
6345 | for (int i=0; i<appear.WearableData.Length;i++) | 6549 | for (int i=0; i<appear.WearableData.Length;i++) |
6346 | cacheitems[i] = new WearableCacheItem(){CacheId = appear.WearableData[i].CacheID,TextureIndex=Convert.ToUInt32(appear.WearableData[i].TextureIndex)}; | 6550 | cacheitems[i] = new WearableCacheItem(){ |
6551 | CacheId = appear.WearableData[i].CacheID, | ||
6552 | TextureIndex=Convert.ToUInt32(appear.WearableData[i].TextureIndex) | ||
6553 | }; | ||
6347 | 6554 | ||
6348 | 6555 | ||
6349 | 6556 | ||
@@ -6549,8 +6756,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6549 | return true; | 6756 | return true; |
6550 | } | 6757 | } |
6551 | 6758 | ||
6552 | private bool HandleCompleteAgentMovement(IClientAPI sender, Packet Pack) | 6759 | private bool HandleCompleteAgentMovement(IClientAPI sender, Packet Pack) |
6553 | { | 6760 | { |
6761 | m_log.DebugFormat("[LLClientView] HandleCompleteAgentMovement"); | ||
6762 | |||
6554 | Action<IClientAPI, bool> handlerCompleteMovementToRegion = OnCompleteMovementToRegion; | 6763 | Action<IClientAPI, bool> handlerCompleteMovementToRegion = OnCompleteMovementToRegion; |
6555 | if (handlerCompleteMovementToRegion != null) | 6764 | if (handlerCompleteMovementToRegion != null) |
6556 | { | 6765 | { |
@@ -6636,6 +6845,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6636 | } | 6845 | } |
6637 | #endregion | 6846 | #endregion |
6638 | 6847 | ||
6848 | if (SceneAgent.IsChildAgent) | ||
6849 | { | ||
6850 | SendCantSitBecauseChildAgentResponse(); | ||
6851 | return true; | ||
6852 | } | ||
6853 | |||
6639 | AgentRequestSit handlerAgentRequestSit = OnAgentRequestSit; | 6854 | AgentRequestSit handlerAgentRequestSit = OnAgentRequestSit; |
6640 | 6855 | ||
6641 | if (handlerAgentRequestSit != null) | 6856 | if (handlerAgentRequestSit != null) |
@@ -6660,6 +6875,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6660 | } | 6875 | } |
6661 | #endregion | 6876 | #endregion |
6662 | 6877 | ||
6878 | if (SceneAgent.IsChildAgent) | ||
6879 | { | ||
6880 | SendCantSitBecauseChildAgentResponse(); | ||
6881 | return true; | ||
6882 | } | ||
6883 | |||
6663 | AgentSit handlerAgentSit = OnAgentSit; | 6884 | AgentSit handlerAgentSit = OnAgentSit; |
6664 | if (handlerAgentSit != null) | 6885 | if (handlerAgentSit != null) |
6665 | { | 6886 | { |
@@ -6669,6 +6890,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6669 | return true; | 6890 | return true; |
6670 | } | 6891 | } |
6671 | 6892 | ||
6893 | /// <summary> | ||
6894 | /// Used when a child agent gets a sit response which should not be fulfilled. | ||
6895 | /// </summary> | ||
6896 | private void SendCantSitBecauseChildAgentResponse() | ||
6897 | { | ||
6898 | SendAlertMessage("Try moving closer. Can't sit on object because it is not in the same region as you."); | ||
6899 | } | ||
6900 | |||
6672 | private bool HandleSoundTrigger(IClientAPI sender, Packet Pack) | 6901 | private bool HandleSoundTrigger(IClientAPI sender, Packet Pack) |
6673 | { | 6902 | { |
6674 | SoundTriggerPacket soundTriggerPacket = (SoundTriggerPacket)Pack; | 6903 | SoundTriggerPacket soundTriggerPacket = (SoundTriggerPacket)Pack; |
@@ -7879,129 +8108,145 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
7879 | //m_log.Debug("ClientView.ProcessPackets.cs:ProcessInPacket() - Got transfer request"); | 8108 | //m_log.Debug("ClientView.ProcessPackets.cs:ProcessInPacket() - Got transfer request"); |
7880 | 8109 | ||
7881 | TransferRequestPacket transfer = (TransferRequestPacket)Pack; | 8110 | TransferRequestPacket transfer = (TransferRequestPacket)Pack; |
7882 | //m_log.Debug("Transfer Request: " + transfer.ToString()); | ||
7883 | // Validate inventory transfers | ||
7884 | // Has to be done here, because AssetCache can't do it | ||
7885 | // | ||
7886 | UUID taskID = UUID.Zero; | 8111 | UUID taskID = UUID.Zero; |
7887 | if (transfer.TransferInfo.SourceType == (int)SourceType.SimInventoryItem) | 8112 | if (transfer.TransferInfo.SourceType == (int)SourceType.SimInventoryItem) |
7888 | { | 8113 | { |
7889 | taskID = new UUID(transfer.TransferInfo.Params, 48); | ||
7890 | UUID itemID = new UUID(transfer.TransferInfo.Params, 64); | ||
7891 | UUID requestID = new UUID(transfer.TransferInfo.Params, 80); | ||
7892 | |||
7893 | // m_log.DebugFormat( | ||
7894 | // "[CLIENT]: Got request for asset {0} from item {1} in prim {2} by {3}", | ||
7895 | // requestID, itemID, taskID, Name); | ||
7896 | |||
7897 | if (!(((Scene)m_scene).Permissions.BypassPermissions())) | 8114 | if (!(((Scene)m_scene).Permissions.BypassPermissions())) |
7898 | { | 8115 | { |
7899 | if (taskID != UUID.Zero) // Prim | 8116 | // We're spawning a thread because the permissions check can block this thread |
8117 | Util.FireAndForget(delegate | ||
7900 | { | 8118 | { |
7901 | SceneObjectPart part = ((Scene)m_scene).GetSceneObjectPart(taskID); | 8119 | // This requests the asset if needed |
8120 | HandleSimInventoryTransferRequestWithPermsCheck(sender, transfer); | ||
8121 | }); | ||
8122 | return true; | ||
8123 | } | ||
8124 | } | ||
8125 | else if (transfer.TransferInfo.SourceType == (int)SourceType.SimEstate) | ||
8126 | { | ||
8127 | //TransferRequestPacket does not include covenant uuid? | ||
8128 | //get scene covenant uuid | ||
8129 | taskID = m_scene.RegionInfo.RegionSettings.Covenant; | ||
8130 | } | ||
7902 | 8131 | ||
7903 | if (part == null) | 8132 | // This is non-blocking |
7904 | { | 8133 | MakeAssetRequest(transfer, taskID); |
7905 | m_log.WarnFormat( | ||
7906 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but prim does not exist", | ||
7907 | Name, requestID, itemID, taskID); | ||
7908 | return true; | ||
7909 | } | ||
7910 | 8134 | ||
7911 | TaskInventoryItem tii = part.Inventory.GetInventoryItem(itemID); | 8135 | return true; |
7912 | if (tii == null) | 8136 | } |
7913 | { | ||
7914 | m_log.WarnFormat( | ||
7915 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but item does not exist", | ||
7916 | Name, requestID, itemID, taskID); | ||
7917 | return true; | ||
7918 | } | ||
7919 | 8137 | ||
7920 | if (tii.Type == (int)AssetType.LSLText) | 8138 | private void HandleSimInventoryTransferRequestWithPermsCheck(IClientAPI sender, TransferRequestPacket transfer) |
7921 | { | 8139 | { |
7922 | if (!((Scene)m_scene).Permissions.CanEditScript(itemID, taskID, AgentId)) | 8140 | UUID taskID = new UUID(transfer.TransferInfo.Params, 48); |
7923 | return true; | 8141 | UUID itemID = new UUID(transfer.TransferInfo.Params, 64); |
7924 | } | 8142 | UUID requestID = new UUID(transfer.TransferInfo.Params, 80); |
7925 | else if (tii.Type == (int)AssetType.Notecard) | ||
7926 | { | ||
7927 | if (!((Scene)m_scene).Permissions.CanEditNotecard(itemID, taskID, AgentId)) | ||
7928 | return true; | ||
7929 | } | ||
7930 | else | ||
7931 | { | ||
7932 | // TODO: Change this code to allow items other than notecards and scripts to be successfully | ||
7933 | // shared with group. In fact, this whole block of permissions checking should move to an IPermissionsModule | ||
7934 | if (part.OwnerID != AgentId) | ||
7935 | { | ||
7936 | m_log.WarnFormat( | ||
7937 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but the prim is owned by {4}", | ||
7938 | Name, requestID, itemID, taskID, part.OwnerID); | ||
7939 | return true; | ||
7940 | } | ||
7941 | 8143 | ||
7942 | if ((part.OwnerMask & (uint)PermissionMask.Modify) == 0) | 8144 | //m_log.DebugFormat( |
7943 | { | 8145 | // "[CLIENT]: Got request for asset {0} from item {1} in prim {2} by {3}", |
7944 | m_log.WarnFormat( | 8146 | // requestID, itemID, taskID, Name); |
7945 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but modify permissions are not set", | ||
7946 | Name, requestID, itemID, taskID); | ||
7947 | return true; | ||
7948 | } | ||
7949 | 8147 | ||
7950 | if (tii.OwnerID != AgentId) | 8148 | //m_log.Debug("Transfer Request: " + transfer.ToString()); |
7951 | { | 8149 | // Validate inventory transfers |
7952 | m_log.WarnFormat( | 8150 | // Has to be done here, because AssetCache can't do it |
7953 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but the item is owned by {4}", | 8151 | // |
7954 | Name, requestID, itemID, taskID, tii.OwnerID); | 8152 | if (taskID != UUID.Zero) // Prim |
7955 | return true; | 8153 | { |
7956 | } | 8154 | SceneObjectPart part = ((Scene)m_scene).GetSceneObjectPart(taskID); |
7957 | 8155 | ||
7958 | if (( | 8156 | if (part == null) |
7959 | tii.CurrentPermissions & ((uint)PermissionMask.Modify | (uint)PermissionMask.Copy | (uint)PermissionMask.Transfer)) | 8157 | { |
7960 | != ((uint)PermissionMask.Modify | (uint)PermissionMask.Copy | (uint)PermissionMask.Transfer)) | 8158 | m_log.WarnFormat( |
7961 | { | 8159 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but prim does not exist", |
7962 | m_log.WarnFormat( | 8160 | Name, requestID, itemID, taskID); |
7963 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but item permissions are not modify/copy/transfer", | 8161 | return; |
7964 | Name, requestID, itemID, taskID); | 8162 | } |
7965 | return true; | ||
7966 | } | ||
7967 | 8163 | ||
7968 | if (tii.AssetID != requestID) | 8164 | TaskInventoryItem tii = part.Inventory.GetInventoryItem(itemID); |
7969 | { | 8165 | if (tii == null) |
7970 | m_log.WarnFormat( | 8166 | { |
7971 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but this does not match item's asset {4}", | 8167 | m_log.WarnFormat( |
7972 | Name, requestID, itemID, taskID, tii.AssetID); | 8168 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but item does not exist", |
7973 | return true; | 8169 | Name, requestID, itemID, taskID); |
7974 | } | 8170 | return; |
7975 | } | 8171 | } |
8172 | |||
8173 | if (tii.Type == (int)AssetType.LSLText) | ||
8174 | { | ||
8175 | if (!((Scene)m_scene).Permissions.CanEditScript(itemID, taskID, AgentId)) | ||
8176 | return; | ||
8177 | } | ||
8178 | else if (tii.Type == (int)AssetType.Notecard) | ||
8179 | { | ||
8180 | if (!((Scene)m_scene).Permissions.CanEditNotecard(itemID, taskID, AgentId)) | ||
8181 | return; | ||
8182 | } | ||
8183 | else | ||
8184 | { | ||
8185 | // TODO: Change this code to allow items other than notecards and scripts to be successfully | ||
8186 | // shared with group. In fact, this whole block of permissions checking should move to an IPermissionsModule | ||
8187 | if (part.OwnerID != AgentId) | ||
8188 | { | ||
8189 | m_log.WarnFormat( | ||
8190 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but the prim is owned by {4}", | ||
8191 | Name, requestID, itemID, taskID, part.OwnerID); | ||
8192 | return; | ||
7976 | } | 8193 | } |
7977 | else // Agent | 8194 | |
8195 | if ((part.OwnerMask & (uint)PermissionMask.Modify) == 0) | ||
7978 | { | 8196 | { |
7979 | IInventoryAccessModule invAccess = m_scene.RequestModuleInterface<IInventoryAccessModule>(); | 8197 | m_log.WarnFormat( |
7980 | if (invAccess != null) | 8198 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but modify permissions are not set", |
7981 | { | 8199 | Name, requestID, itemID, taskID); |
7982 | if (!invAccess.CanGetAgentInventoryItem(this, itemID, requestID)) | 8200 | return; |
7983 | return false; | 8201 | } |
7984 | } | 8202 | |
7985 | else | 8203 | if (tii.OwnerID != AgentId) |
7986 | { | 8204 | { |
7987 | return false; | 8205 | m_log.WarnFormat( |
7988 | } | 8206 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but the item is owned by {4}", |
8207 | Name, requestID, itemID, taskID, tii.OwnerID); | ||
8208 | return; | ||
8209 | } | ||
8210 | |||
8211 | if (( | ||
8212 | tii.CurrentPermissions & ((uint)PermissionMask.Modify | (uint)PermissionMask.Copy | (uint)PermissionMask.Transfer)) | ||
8213 | != ((uint)PermissionMask.Modify | (uint)PermissionMask.Copy | (uint)PermissionMask.Transfer)) | ||
8214 | { | ||
8215 | m_log.WarnFormat( | ||
8216 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but item permissions are not modify/copy/transfer", | ||
8217 | Name, requestID, itemID, taskID); | ||
8218 | return; | ||
8219 | } | ||
8220 | |||
8221 | if (tii.AssetID != requestID) | ||
8222 | { | ||
8223 | m_log.WarnFormat( | ||
8224 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but this does not match item's asset {4}", | ||
8225 | Name, requestID, itemID, taskID, tii.AssetID); | ||
8226 | return; | ||
7989 | } | 8227 | } |
7990 | } | 8228 | } |
7991 | } | 8229 | } |
7992 | else | 8230 | else // Agent |
7993 | if (transfer.TransferInfo.SourceType == (int)SourceType.SimEstate) | 8231 | { |
8232 | IInventoryAccessModule invAccess = m_scene.RequestModuleInterface<IInventoryAccessModule>(); | ||
8233 | if (invAccess != null) | ||
8234 | { | ||
8235 | if (!invAccess.CanGetAgentInventoryItem(this, itemID, requestID)) | ||
8236 | return; | ||
8237 | } | ||
8238 | else | ||
7994 | { | 8239 | { |
7995 | //TransferRequestPacket does not include covenant uuid? | 8240 | return; |
7996 | //get scene covenant uuid | ||
7997 | taskID = m_scene.RegionInfo.RegionSettings.Covenant; | ||
7998 | } | 8241 | } |
8242 | } | ||
7999 | 8243 | ||
8244 | // Permissions out of the way, let's request the asset | ||
8000 | MakeAssetRequest(transfer, taskID); | 8245 | MakeAssetRequest(transfer, taskID); |
8001 | 8246 | ||
8002 | return true; | ||
8003 | } | 8247 | } |
8004 | 8248 | ||
8249 | |||
8005 | private bool HandleAssetUploadRequest(IClientAPI sender, Packet Pack) | 8250 | private bool HandleAssetUploadRequest(IClientAPI sender, Packet Pack) |
8006 | { | 8251 | { |
8007 | AssetUploadRequestPacket request = (AssetUploadRequestPacket)Pack; | 8252 | AssetUploadRequestPacket request = (AssetUploadRequestPacket)Pack; |
@@ -11732,8 +11977,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11732 | } | 11977 | } |
11733 | 11978 | ||
11734 | /// <summary> | 11979 | /// <summary> |
11735 | /// Send a response back to a client when it asks the asset server (via the region server) if it has | ||
11736 | /// its appearance texture cached. | ||
11737 | /// </summary> | 11980 | /// </summary> |
11738 | /// <remarks> | 11981 | /// <remarks> |
11739 | /// At the moment, we always reply that there is no cached texture. | 11982 | /// At the moment, we always reply that there is no cached texture. |
@@ -11741,6 +11984,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11741 | /// <param name="simclient"></param> | 11984 | /// <param name="simclient"></param> |
11742 | /// <param name="packet"></param> | 11985 | /// <param name="packet"></param> |
11743 | /// <returns></returns> | 11986 | /// <returns></returns> |
11987 | // TODO: Convert old handler to use new method | ||
11988 | /*protected bool HandleAgentTextureCached(IClientAPI simclient, Packet packet) | ||
11989 | { | ||
11990 | AgentCachedTexturePacket cachedtex = (AgentCachedTexturePacket)packet; | ||
11991 | |||
11992 | if (cachedtex.AgentData.SessionID != SessionId) | ||
11993 | return false; | ||
11994 | |||
11995 | |||
11996 | List<CachedTextureRequestArg> requestArgs = new List<CachedTextureRequestArg>(); | ||
11997 | |||
11998 | for (int i = 0; i < cachedtex.WearableData.Length; i++) | ||
11999 | { | ||
12000 | CachedTextureRequestArg arg = new CachedTextureRequestArg(); | ||
12001 | arg.BakedTextureIndex = cachedtex.WearableData[i].TextureIndex; | ||
12002 | arg.WearableHashID = cachedtex.WearableData[i].ID; | ||
12003 | |||
12004 | requestArgs.Add(arg); | ||
12005 | } | ||
12006 | |||
12007 | CachedTextureRequest handlerCachedTextureRequest = OnCachedTextureRequest; | ||
12008 | if (handlerCachedTextureRequest != null) | ||
12009 | { | ||
12010 | handlerCachedTextureRequest(simclient,cachedtex.AgentData.SerialNum,requestArgs); | ||
12011 | } | ||
12012 | |||
12013 | return true; | ||
12014 | }*/ | ||
12015 | |||
11744 | protected bool HandleAgentTextureCached(IClientAPI simclient, Packet packet) | 12016 | protected bool HandleAgentTextureCached(IClientAPI simclient, Packet packet) |
11745 | { | 12017 | { |
11746 | //m_log.Debug("texture cached: " + packet.ToString()); | 12018 | //m_log.Debug("texture cached: " + packet.ToString()); |
@@ -11749,156 +12021,105 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11749 | 12021 | ||
11750 | if (cachedtex.AgentData.SessionID != SessionId) | 12022 | if (cachedtex.AgentData.SessionID != SessionId) |
11751 | return false; | 12023 | return false; |
11752 | |||
11753 | 12024 | ||
11754 | // TODO: don't create new blocks if recycling an old packet | 12025 | // TODO: don't create new blocks if recycling an old packet |
11755 | cachedresp.AgentData.AgentID = AgentId; | 12026 | cachedresp.AgentData.AgentID = AgentId; |
11756 | cachedresp.AgentData.SessionID = m_sessionId; | 12027 | cachedresp.AgentData.SessionID = m_sessionId; |
11757 | cachedresp.AgentData.SerialNum = m_cachedTextureSerial; | 12028 | cachedresp.AgentData.SerialNum = cachedtex.AgentData.SerialNum; |
11758 | m_cachedTextureSerial++; | ||
11759 | cachedresp.WearableData = | 12029 | cachedresp.WearableData = |
11760 | new AgentCachedTextureResponsePacket.WearableDataBlock[cachedtex.WearableData.Length]; | 12030 | new AgentCachedTextureResponsePacket.WearableDataBlock[cachedtex.WearableData.Length]; |
11761 | 12031 | ||
11762 | //IAvatarFactoryModule fac = m_scene.RequestModuleInterface<IAvatarFactoryModule>(); | ||
11763 | // var item = fac.GetBakedTextureFaces(AgentId); | ||
11764 | //WearableCacheItem[] items = fac.GetCachedItems(AgentId); | ||
11765 | |||
11766 | IAssetService cache = m_scene.AssetService; | ||
11767 | IBakedTextureModule bakedTextureModule = m_scene.RequestModuleInterface<IBakedTextureModule>(); | ||
11768 | //bakedTextureModule = null; | ||
11769 | int maxWearablesLoop = cachedtex.WearableData.Length; | 12032 | int maxWearablesLoop = cachedtex.WearableData.Length; |
11770 | if (maxWearablesLoop > AvatarWearable.MAX_WEARABLES) | 12033 | if (maxWearablesLoop > AvatarWearable.MAX_WEARABLES) |
11771 | maxWearablesLoop = AvatarWearable.MAX_WEARABLES; | 12034 | maxWearablesLoop = AvatarWearable.MAX_WEARABLES; |
11772 | 12035 | ||
11773 | if (bakedTextureModule != null && cache != null) | 12036 | int cacheHits = 0; |
11774 | { | ||
11775 | // We need to make sure the asset stored in the bake is available on this server also by it's assetid before we map it to a Cacheid | ||
11776 | 12037 | ||
11777 | WearableCacheItem[] cacheItems = null; | 12038 | // We need to make sure the asset stored in the bake is available on this server also by it's assetid before we map it to a Cacheid |
11778 | ScenePresence p = m_scene.GetScenePresence(AgentId); | ||
11779 | if (p.Appearance != null) | ||
11780 | if (p.Appearance.WearableCacheItems == null || p.Appearance.WearableCacheItemsDirty) | ||
11781 | { | ||
11782 | try | ||
11783 | { | ||
11784 | cacheItems = bakedTextureModule.Get(AgentId); | ||
11785 | p.Appearance.WearableCacheItems = cacheItems; | ||
11786 | p.Appearance.WearableCacheItemsDirty = false; | ||
11787 | } | ||
11788 | 12039 | ||
11789 | /* | 12040 | WearableCacheItem[] cacheItems = null; |
11790 | * The following Catch types DO NOT WORK, it jumps to the General Packet Exception Handler if you don't catch Exception! | ||
11791 | * | ||
11792 | catch (System.Net.Sockets.SocketException) | ||
11793 | { | ||
11794 | cacheItems = null; | ||
11795 | } | ||
11796 | catch (WebException) | ||
11797 | { | ||
11798 | cacheItems = null; | ||
11799 | } | ||
11800 | catch (InvalidOperationException) | ||
11801 | { | ||
11802 | cacheItems = null; | ||
11803 | } */ | ||
11804 | catch (Exception) | ||
11805 | { | ||
11806 | cacheItems = null; | ||
11807 | } | ||
11808 | |||
11809 | } | ||
11810 | else if (p.Appearance.WearableCacheItems != null) | ||
11811 | { | ||
11812 | cacheItems = p.Appearance.WearableCacheItems; | ||
11813 | } | ||
11814 | 12041 | ||
11815 | if (cache != null && cacheItems != null) | 12042 | ScenePresence p = m_scene.GetScenePresence(AgentId); |
11816 | { | ||
11817 | foreach (WearableCacheItem item in cacheItems) | ||
11818 | { | ||
11819 | |||
11820 | if (cache.GetCached(item.TextureID.ToString()) == null) | ||
11821 | { | ||
11822 | item.TextureAsset.Temporary = true; | ||
11823 | cache.Store(item.TextureAsset); | ||
11824 | } | ||
11825 | 12043 | ||
12044 | if (p != null && p.Appearance != null) | ||
12045 | { | ||
12046 | cacheItems = p.Appearance.WearableCacheItems; | ||
12047 | } | ||
11826 | 12048 | ||
11827 | } | 12049 | if (cacheItems != null) |
11828 | } | 12050 | { |
11829 | 12051 | for (int i = 0; i < maxWearablesLoop; i++) | |
11830 | if (cacheItems != null) | ||
11831 | { | 12052 | { |
11832 | 12053 | int idx = cachedtex.WearableData[i].TextureIndex; | |
11833 | for (int i = 0; i < maxWearablesLoop; i++) | ||
11834 | { | ||
11835 | WearableCacheItem item = | ||
11836 | WearableCacheItem.SearchTextureIndex(cachedtex.WearableData[i].TextureIndex,cacheItems); | ||
11837 | 12054 | ||
11838 | cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock(); | 12055 | cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock(); |
11839 | cachedresp.WearableData[i].TextureIndex= cachedtex.WearableData[i].TextureIndex; | 12056 | cachedresp.WearableData[i].TextureIndex = cachedtex.WearableData[i].TextureIndex; |
11840 | cachedresp.WearableData[i].HostName = new byte[0]; | 12057 | cachedresp.WearableData[i].HostName = new byte[0]; |
11841 | if (item != null && cachedtex.WearableData[i].ID == item.CacheId) | 12058 | if (cachedtex.WearableData[i].ID == cacheItems[idx].CacheId) |
11842 | { | 12059 | { |
11843 | 12060 | cachedresp.WearableData[i].TextureID = cacheItems[idx].TextureID; | |
11844 | cachedresp.WearableData[i].TextureID = item.TextureID; | 12061 | cacheHits++; |
11845 | } | ||
11846 | else | ||
11847 | { | ||
11848 | cachedresp.WearableData[i].TextureID = UUID.Zero; | ||
11849 | } | ||
11850 | } | 12062 | } |
11851 | } | 12063 | else |
11852 | else | ||
11853 | { | ||
11854 | for (int i = 0; i < maxWearablesLoop; i++) | ||
11855 | { | 12064 | { |
11856 | cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock(); | ||
11857 | cachedresp.WearableData[i].TextureIndex = cachedtex.WearableData[i].TextureIndex; | ||
11858 | cachedresp.WearableData[i].TextureID = UUID.Zero; | 12065 | cachedresp.WearableData[i].TextureID = UUID.Zero; |
11859 | //UUID.Parse("8334fb6e-c2f5-46ee-807d-a435f61a8d46"); | ||
11860 | cachedresp.WearableData[i].HostName = new byte[0]; | ||
11861 | } | 12066 | } |
11862 | } | 12067 | } |
11863 | } | 12068 | } |
11864 | else | 12069 | else |
11865 | { | 12070 | { |
11866 | if (cache == null) | 12071 | for (int i = 0; i < maxWearablesLoop; i++) |
11867 | { | 12072 | { |
11868 | for (int i = 0; i < maxWearablesLoop; i++) | 12073 | cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock(); |
11869 | { | 12074 | cachedresp.WearableData[i].TextureIndex = cachedtex.WearableData[i].TextureIndex; |
11870 | cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock(); | 12075 | cachedresp.WearableData[i].TextureID = UUID.Zero; |
11871 | cachedresp.WearableData[i].TextureIndex = cachedtex.WearableData[i].TextureIndex; | 12076 | //UUID.Parse("8334fb6e-c2f5-46ee-807d-a435f61a8d46"); |
11872 | cachedresp.WearableData[i].TextureID = UUID.Zero; | 12077 | cachedresp.WearableData[i].HostName = new byte[0]; |
11873 | //UUID.Parse("8334fb6e-c2f5-46ee-807d-a435f61a8d46"); | ||
11874 | cachedresp.WearableData[i].HostName = new byte[0]; | ||
11875 | } | ||
11876 | } | 12078 | } |
11877 | else | 12079 | } |
11878 | { | ||
11879 | for (int i = 0; i < maxWearablesLoop; i++) | ||
11880 | { | ||
11881 | cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock(); | ||
11882 | cachedresp.WearableData[i].TextureIndex = cachedtex.WearableData[i].TextureIndex; | ||
11883 | |||
11884 | 12080 | ||
12081 | m_log.DebugFormat("texture cached: hits {0}", cacheHits); | ||
11885 | 12082 | ||
11886 | if (cache.GetCached(cachedresp.WearableData[i].TextureID.ToString()) == null) | ||
11887 | cachedresp.WearableData[i].TextureID = UUID.Zero; | ||
11888 | //UUID.Parse("8334fb6e-c2f5-46ee-807d-a435f61a8d46"); | ||
11889 | else | ||
11890 | cachedresp.WearableData[i].TextureID = UUID.Zero; | ||
11891 | // UUID.Parse("8334fb6e-c2f5-46ee-807d-a435f61a8d46"); | ||
11892 | cachedresp.WearableData[i].HostName = new byte[0]; | ||
11893 | } | ||
11894 | } | ||
11895 | } | ||
11896 | cachedresp.Header.Zerocoded = true; | 12083 | cachedresp.Header.Zerocoded = true; |
11897 | OutPacket(cachedresp, ThrottleOutPacketType.Task); | 12084 | OutPacket(cachedresp, ThrottleOutPacketType.Task); |
11898 | 12085 | ||
11899 | return true; | 12086 | return true; |
11900 | } | 12087 | } |
11901 | 12088 | ||
12089 | /// <summary> | ||
12090 | /// Send a response back to a client when it asks the asset server (via the region server) if it has | ||
12091 | /// its appearance texture cached. | ||
12092 | /// </summary> | ||
12093 | /// <param name="avatar"></param> | ||
12094 | /// <param name="serial"></param> | ||
12095 | /// <param name="cachedTextures"></param> | ||
12096 | /// <returns></returns> | ||
12097 | public void SendCachedTextureResponse(ISceneEntity avatar, int serial, List<CachedTextureResponseArg> cachedTextures) | ||
12098 | { | ||
12099 | ScenePresence presence = avatar as ScenePresence; | ||
12100 | if (presence == null) | ||
12101 | return; | ||
12102 | |||
12103 | AgentCachedTextureResponsePacket cachedresp = (AgentCachedTextureResponsePacket)PacketPool.Instance.GetPacket(PacketType.AgentCachedTextureResponse); | ||
12104 | |||
12105 | // TODO: don't create new blocks if recycling an old packet | ||
12106 | cachedresp.AgentData.AgentID = m_agentId; | ||
12107 | cachedresp.AgentData.SessionID = m_sessionId; | ||
12108 | cachedresp.AgentData.SerialNum = serial; | ||
12109 | cachedresp.WearableData = new AgentCachedTextureResponsePacket.WearableDataBlock[cachedTextures.Count]; | ||
12110 | |||
12111 | for (int i = 0; i < cachedTextures.Count; i++) | ||
12112 | { | ||
12113 | cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock(); | ||
12114 | cachedresp.WearableData[i].TextureIndex = (byte)cachedTextures[i].BakedTextureIndex; | ||
12115 | cachedresp.WearableData[i].TextureID = cachedTextures[i].BakedTextureID; | ||
12116 | cachedresp.WearableData[i].HostName = new byte[0]; | ||
12117 | } | ||
12118 | |||
12119 | cachedresp.Header.Zerocoded = true; | ||
12120 | OutPacket(cachedresp, ThrottleOutPacketType.Task); | ||
12121 | } | ||
12122 | |||
11902 | protected bool HandleMultipleObjUpdate(IClientAPI simClient, Packet packet) | 12123 | protected bool HandleMultipleObjUpdate(IClientAPI simClient, Packet packet) |
11903 | { | 12124 | { |
11904 | MultipleObjectUpdatePacket multipleupdate = (MultipleObjectUpdatePacket)packet; | 12125 | MultipleObjectUpdatePacket multipleupdate = (MultipleObjectUpdatePacket)packet; |
@@ -11924,8 +12145,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11924 | if (part == null) | 12145 | if (part == null) |
11925 | { | 12146 | { |
11926 | // It's a ghost! tell the client to delete it from view. | 12147 | // It's a ghost! tell the client to delete it from view. |
11927 | simClient.SendKillObject(Scene.RegionInfo.RegionHandle, | 12148 | simClient.SendKillObject(new List<uint> { localId }); |
11928 | new List<uint> { localId }); | ||
11929 | } | 12149 | } |
11930 | else | 12150 | else |
11931 | { | 12151 | { |
@@ -12129,7 +12349,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12129 | /// <param name="throttles"></param> | 12349 | /// <param name="throttles"></param> |
12130 | public void SetChildAgentThrottle(byte[] throttles) | 12350 | public void SetChildAgentThrottle(byte[] throttles) |
12131 | { | 12351 | { |
12132 | m_udpClient.SetThrottles(throttles); | 12352 | SetChildAgentThrottle(throttles, 1.0f); |
12353 | } | ||
12354 | |||
12355 | public void SetChildAgentThrottle(byte[] throttles,float factor) | ||
12356 | { | ||
12357 | m_udpClient.SetThrottles(throttles, factor); | ||
12133 | GenericCall2 handler = OnUpdateThrottles; | 12358 | GenericCall2 handler = OnUpdateThrottles; |
12134 | if (handler != null) | 12359 | if (handler != null) |
12135 | { | 12360 | { |
@@ -12138,16 +12363,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12138 | } | 12363 | } |
12139 | 12364 | ||
12140 | /// <summary> | 12365 | /// <summary> |
12141 | /// Sets the throttles from values supplied by the client | 12366 | /// Sets the throttles from values supplied caller |
12142 | /// </summary> | 12367 | /// </summary> |
12143 | /// <param name="throttles"></param> | 12368 | /// <param name="throttles"></param> |
12144 | public void SetAgentThrottleSilent(int throttle, int setting) | 12369 | public void SetAgentThrottleSilent(int throttle, int setting) |
12145 | { | 12370 | { |
12146 | m_udpClient.ForceThrottleSetting(throttle,setting); | 12371 | m_udpClient.ForceThrottleSetting(throttle,setting); |
12147 | //m_udpClient.SetThrottles(throttles); | ||
12148 | |||
12149 | } | 12372 | } |
12150 | 12373 | ||
12374 | public int GetAgentThrottleSilent(int throttle) | ||
12375 | { | ||
12376 | return m_udpClient.GetThrottleSetting(throttle); | ||
12377 | } | ||
12151 | 12378 | ||
12152 | /// <summary> | 12379 | /// <summary> |
12153 | /// Get the current throttles for this client as a packed byte array | 12380 | /// Get the current throttles for this client as a packed byte array |
@@ -12250,8 +12477,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12250 | uint regionY = 0; | 12477 | uint regionY = 0; |
12251 | 12478 | ||
12252 | Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out regionX, out regionY); | 12479 | Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out regionX, out regionY); |
12253 | locx = Convert.ToSingle(args[0]) - (float)regionX; | 12480 | locx = (float)(Convert.ToDouble(args[0]) - (double)regionX); |
12254 | locy = Convert.ToSingle(args[1]) - (float)regionY; | 12481 | locy = (float)(Convert.ToDouble(args[1]) - (double)regionY); |
12255 | locz = Convert.ToSingle(args[2]); | 12482 | locz = Convert.ToSingle(args[2]); |
12256 | 12483 | ||
12257 | Action<Vector3, bool, bool> handlerAutoPilotGo = OnAutoPilotGo; | 12484 | Action<Vector3, bool, bool> handlerAutoPilotGo = OnAutoPilotGo; |
@@ -12299,6 +12526,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12299 | 12526 | ||
12300 | shape.PCode = addPacket.ObjectData.PCode; | 12527 | shape.PCode = addPacket.ObjectData.PCode; |
12301 | shape.State = addPacket.ObjectData.State; | 12528 | shape.State = addPacket.ObjectData.State; |
12529 | shape.LastAttachPoint = addPacket.ObjectData.State; | ||
12302 | shape.PathBegin = addPacket.ObjectData.PathBegin; | 12530 | shape.PathBegin = addPacket.ObjectData.PathBegin; |
12303 | shape.PathEnd = addPacket.ObjectData.PathEnd; | 12531 | shape.PathEnd = addPacket.ObjectData.PathEnd; |
12304 | shape.PathScaleX = addPacket.ObjectData.PathScaleX; | 12532 | shape.PathScaleX = addPacket.ObjectData.PathScaleX; |
@@ -12329,7 +12557,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12329 | ClientInfo info = m_udpClient.GetClientInfo(); | 12557 | ClientInfo info = m_udpClient.GetClientInfo(); |
12330 | 12558 | ||
12331 | info.proxyEP = null; | 12559 | info.proxyEP = null; |
12332 | info.agentcircuit = RequestClientInfo(); | 12560 | if (info.agentcircuit == null) |
12561 | info.agentcircuit = RequestClientInfo(); | ||
12333 | 12562 | ||
12334 | return info; | 12563 | return info; |
12335 | } | 12564 | } |
@@ -12712,11 +12941,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12712 | OutPacket(dialog, ThrottleOutPacketType.Task); | 12941 | OutPacket(dialog, ThrottleOutPacketType.Task); |
12713 | } | 12942 | } |
12714 | 12943 | ||
12715 | public void StopFlying(ISceneEntity p) | 12944 | public void SendAgentTerseUpdate(ISceneEntity p) |
12716 | { | 12945 | { |
12717 | if (p is ScenePresence) | 12946 | if (p is ScenePresence) |
12718 | { | 12947 | { |
12719 | ScenePresence presence = p as ScenePresence; | 12948 | // m_log.DebugFormat( |
12949 | // "[LLCLIENTVIEW]: Immediately sending terse agent update for {0} to {1} in {2}", | ||
12950 | // p.Name, Name, Scene.Name); | ||
12951 | |||
12720 | // It turns out to get the agent to stop flying, you have to feed it stop flying velocities | 12952 | // It turns out to get the agent to stop flying, you have to feed it stop flying velocities |
12721 | // There's no explicit message to send the client to tell it to stop flying.. it relies on the | 12953 | // There's no explicit message to send the client to tell it to stop flying.. it relies on the |
12722 | // velocity, collision plane and avatar height | 12954 | // velocity, collision plane and avatar height |
@@ -12724,34 +12956,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12724 | // Add 1/6 the avatar's height to it's position so it doesn't shoot into the air | 12956 | // Add 1/6 the avatar's height to it's position so it doesn't shoot into the air |
12725 | // when the avatar stands up | 12957 | // when the avatar stands up |
12726 | 12958 | ||
12727 | Vector3 pos = presence.AbsolutePosition; | ||
12728 | |||
12729 | if (presence.Appearance.AvatarHeight != 127.0f) | ||
12730 | pos += new Vector3(0f, 0f, (presence.Appearance.AvatarHeight/6f)); | ||
12731 | else | ||
12732 | pos += new Vector3(0f, 0f, (1.56f/6f)); | ||
12733 | |||
12734 | presence.AbsolutePosition = pos; | ||
12735 | |||
12736 | // attach a suitable collision plane regardless of the actual situation to force the LLClient to land. | ||
12737 | // Collision plane below the avatar's position a 6th of the avatar's height is suitable. | ||
12738 | // Mind you, that this method doesn't get called if the avatar's velocity magnitude is greater then a | ||
12739 | // certain amount.. because the LLClient wouldn't land in that situation anyway. | ||
12740 | |||
12741 | // why are we still testing for this really old height value default??? | ||
12742 | if (presence.Appearance.AvatarHeight != 127.0f) | ||
12743 | presence.CollisionPlane = new Vector4(0, 0, 0, pos.Z - presence.Appearance.AvatarHeight/6f); | ||
12744 | else | ||
12745 | presence.CollisionPlane = new Vector4(0, 0, 0, pos.Z - (1.56f/6f)); | ||
12746 | |||
12747 | |||
12748 | ImprovedTerseObjectUpdatePacket.ObjectDataBlock block = | 12959 | ImprovedTerseObjectUpdatePacket.ObjectDataBlock block = |
12749 | CreateImprovedTerseBlock(p, false); | 12960 | CreateImprovedTerseBlock(p, false); |
12750 | 12961 | ||
12751 | const float TIME_DILATION = 1.0f; | 12962 | const float TIME_DILATION = 1.0f; |
12752 | ushort timeDilation = Utils.FloatToUInt16(TIME_DILATION, 0.0f, 1.0f); | 12963 | ushort timeDilation = Utils.FloatToUInt16(TIME_DILATION, 0.0f, 1.0f); |
12753 | 12964 | ||
12754 | |||
12755 | ImprovedTerseObjectUpdatePacket packet | 12965 | ImprovedTerseObjectUpdatePacket packet |
12756 | = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket( | 12966 | = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket( |
12757 | PacketType.ImprovedTerseObjectUpdate); | 12967 | PacketType.ImprovedTerseObjectUpdate); |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLImageManager.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLImageManager.cs index 073c357..41dd4d1 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLImageManager.cs | |||
@@ -206,6 +206,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
206 | } | 206 | } |
207 | } | 207 | } |
208 | 208 | ||
209 | public bool HasUpdates() | ||
210 | { | ||
211 | J2KImage image = GetHighestPriorityImage(); | ||
212 | |||
213 | return image != null && image.IsDecoded; | ||
214 | } | ||
215 | |||
209 | public bool ProcessImageQueue(int packetsToSend) | 216 | public bool ProcessImageQueue(int packetsToSend) |
210 | { | 217 | { |
211 | int packetsSent = 0; | 218 | int packetsSent = 0; |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs index e52ac37..0ae7617 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs | |||
@@ -31,6 +31,7 @@ using System.Net; | |||
31 | using System.Threading; | 31 | using System.Threading; |
32 | using log4net; | 32 | using log4net; |
33 | using OpenSim.Framework; | 33 | using OpenSim.Framework; |
34 | using OpenSim.Framework.Monitoring; | ||
34 | using OpenMetaverse; | 35 | using OpenMetaverse; |
35 | using OpenMetaverse.Packets; | 36 | using OpenMetaverse.Packets; |
36 | 37 | ||
@@ -81,6 +82,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
81 | /// hooked to put more data on the empty queue</summary> | 82 | /// hooked to put more data on the empty queue</summary> |
82 | public event QueueEmpty OnQueueEmpty; | 83 | public event QueueEmpty OnQueueEmpty; |
83 | 84 | ||
85 | public event Func<ThrottleOutPacketTypeFlags, bool> HasUpdates; | ||
86 | |||
84 | /// <summary>AgentID for this client</summary> | 87 | /// <summary>AgentID for this client</summary> |
85 | public readonly UUID AgentID; | 88 | public readonly UUID AgentID; |
86 | /// <summary>The remote address of the connected client</summary> | 89 | /// <summary>The remote address of the connected client</summary> |
@@ -141,8 +144,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
141 | get { return m_throttleClient; } | 144 | get { return m_throttleClient; } |
142 | } | 145 | } |
143 | 146 | ||
144 | /// <summary>Throttle bucket for this agent's connection</summary> | ||
145 | private readonly TokenBucket m_throttleCategory; | ||
146 | /// <summary>Throttle buckets for each packet category</summary> | 147 | /// <summary>Throttle buckets for each packet category</summary> |
147 | private readonly TokenBucket[] m_throttleCategories; | 148 | private readonly TokenBucket[] m_throttleCategories; |
148 | /// <summary>Outgoing queues for throttled packets</summary> | 149 | /// <summary>Outgoing queues for throttled packets</summary> |
@@ -160,6 +161,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
160 | private int m_maxRTO = 60000; | 161 | private int m_maxRTO = 60000; |
161 | public bool m_deliverPackets = true; | 162 | public bool m_deliverPackets = true; |
162 | 163 | ||
164 | private float m_burstTime; | ||
165 | |||
166 | public int m_lastStartpingTimeMS; | ||
167 | public int m_pingMS; | ||
168 | |||
169 | public int PingTimeMS | ||
170 | { | ||
171 | get | ||
172 | { | ||
173 | if (m_pingMS < 10) | ||
174 | return 10; | ||
175 | if(m_pingMS > 2000) | ||
176 | return 2000; | ||
177 | return m_pingMS; | ||
178 | } | ||
179 | } | ||
180 | |||
181 | /// <summary> | ||
182 | /// This is the percentage of the udp texture queue to add to the task queue since | ||
183 | /// textures are now generally handled through http. | ||
184 | /// </summary> | ||
185 | private double m_cannibalrate = 0.0; | ||
186 | |||
187 | private ClientInfo m_info = new ClientInfo(); | ||
188 | |||
163 | /// <summary> | 189 | /// <summary> |
164 | /// Default constructor | 190 | /// Default constructor |
165 | /// </summary> | 191 | /// </summary> |
@@ -190,13 +216,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
190 | if (maxRTO != 0) | 216 | if (maxRTO != 0) |
191 | m_maxRTO = maxRTO; | 217 | m_maxRTO = maxRTO; |
192 | 218 | ||
219 | m_burstTime = rates.BrustTime; | ||
220 | float m_burst = rates.ClientMaxRate * m_burstTime; | ||
221 | |||
193 | // Create a token bucket throttle for this client that has the scene token bucket as a parent | 222 | // Create a token bucket throttle for this client that has the scene token bucket as a parent |
194 | m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.Total, rates.AdaptiveThrottlesEnabled); | 223 | m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.ClientMaxRate, m_burst, rates.AdaptiveThrottlesEnabled); |
195 | // Create a token bucket throttle for the total categary with the client bucket as a throttle | ||
196 | m_throttleCategory = new TokenBucket(m_throttleClient, 0); | ||
197 | // Create an array of token buckets for this clients different throttle categories | 224 | // Create an array of token buckets for this clients different throttle categories |
198 | m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; | 225 | m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; |
199 | 226 | ||
227 | m_cannibalrate = rates.CannibalizeTextureRate; | ||
228 | |||
229 | m_burst = rates.Total * rates.BrustTime; | ||
230 | |||
200 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) | 231 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) |
201 | { | 232 | { |
202 | ThrottleOutPacketType type = (ThrottleOutPacketType)i; | 233 | ThrottleOutPacketType type = (ThrottleOutPacketType)i; |
@@ -204,7 +235,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
204 | // Initialize the packet outboxes, where packets sit while they are waiting for tokens | 235 | // Initialize the packet outboxes, where packets sit while they are waiting for tokens |
205 | m_packetOutboxes[i] = new DoubleLocklessQueue<OutgoingPacket>(); | 236 | m_packetOutboxes[i] = new DoubleLocklessQueue<OutgoingPacket>(); |
206 | // Initialize the token buckets that control the throttling for each category | 237 | // Initialize the token buckets that control the throttling for each category |
207 | m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetRate(type)); | 238 | m_throttleCategories[i] = new TokenBucket(m_throttleClient, rates.GetRate(type), m_burst); |
208 | } | 239 | } |
209 | 240 | ||
210 | // Default the retransmission timeout to one second | 241 | // Default the retransmission timeout to one second |
@@ -212,6 +243,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
212 | 243 | ||
213 | // Initialize this to a sane value to prevent early disconnects | 244 | // Initialize this to a sane value to prevent early disconnects |
214 | TickLastPacketReceived = Environment.TickCount & Int32.MaxValue; | 245 | TickLastPacketReceived = Environment.TickCount & Int32.MaxValue; |
246 | m_pingMS = (int)(3.0 * server.TickCountResolution); // so filter doesnt start at 0; | ||
215 | } | 247 | } |
216 | 248 | ||
217 | /// <summary> | 249 | /// <summary> |
@@ -241,20 +273,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
241 | // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists | 273 | // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists |
242 | // of pending and needed ACKs for every client every time some method wants information about | 274 | // of pending and needed ACKs for every client every time some method wants information about |
243 | // this connection is a recipe for poor performance | 275 | // this connection is a recipe for poor performance |
244 | ClientInfo info = new ClientInfo(); | 276 | |
245 | info.pendingAcks = new Dictionary<uint, uint>(); | 277 | m_info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; |
246 | info.needAck = new Dictionary<uint, byte[]>(); | 278 | m_info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; |
247 | 279 | m_info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; | |
248 | info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; | 280 | m_info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; |
249 | info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; | 281 | m_info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; |
250 | info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; | 282 | m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; |
251 | info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; | 283 | m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; |
252 | info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; | 284 | m_info.totalThrottle = (int)m_throttleClient.DripRate; |
253 | info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; | 285 | |
254 | info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; | 286 | return m_info; |
255 | info.totalThrottle = (int)m_throttleCategory.DripRate; | ||
256 | |||
257 | return info; | ||
258 | } | 287 | } |
259 | 288 | ||
260 | /// <summary> | 289 | /// <summary> |
@@ -311,6 +340,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
311 | 340 | ||
312 | public void SetThrottles(byte[] throttleData) | 341 | public void SetThrottles(byte[] throttleData) |
313 | { | 342 | { |
343 | SetThrottles(throttleData, 1.0f); | ||
344 | } | ||
345 | |||
346 | public void SetThrottles(byte[] throttleData, float factor) | ||
347 | { | ||
314 | byte[] adjData; | 348 | byte[] adjData; |
315 | int pos = 0; | 349 | int pos = 0; |
316 | 350 | ||
@@ -330,16 +364,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
330 | } | 364 | } |
331 | 365 | ||
332 | // 0.125f converts from bits to bytes | 366 | // 0.125f converts from bits to bytes |
333 | int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; | 367 | float scale = 0.125f * factor; |
334 | int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; | 368 | int resend = (int)(BitConverter.ToSingle(adjData, pos) * scale); pos += 4; |
335 | int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; | 369 | int land = (int)(BitConverter.ToSingle(adjData, pos) * scale); pos += 4; |
336 | int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; | 370 | int wind = (int)(BitConverter.ToSingle(adjData, pos) * scale); pos += 4; |
337 | int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; | 371 | int cloud = (int)(BitConverter.ToSingle(adjData, pos) * scale); pos += 4; |
338 | int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; | 372 | int task = (int)(BitConverter.ToSingle(adjData, pos) * scale); pos += 4; |
339 | int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | 373 | int texture = (int)(BitConverter.ToSingle(adjData, pos) * scale); pos += 4; |
374 | int asset = (int)(BitConverter.ToSingle(adjData, pos) * scale); | ||
340 | 375 | ||
341 | // Make sure none of the throttles are set below our packet MTU, | 376 | // Make sure none of the throttles are set below our packet MTU, |
342 | // otherwise a throttle could become permanently clogged | 377 | // otherwise a throttle could become permanently clogged |
378 | |||
379 | /* not using floats | ||
343 | resend = Math.Max(resend, LLUDPServer.MTU); | 380 | resend = Math.Max(resend, LLUDPServer.MTU); |
344 | land = Math.Max(land, LLUDPServer.MTU); | 381 | land = Math.Max(land, LLUDPServer.MTU); |
345 | wind = Math.Max(wind, LLUDPServer.MTU); | 382 | wind = Math.Max(wind, LLUDPServer.MTU); |
@@ -347,8 +384,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
347 | task = Math.Max(task, LLUDPServer.MTU); | 384 | task = Math.Max(task, LLUDPServer.MTU); |
348 | texture = Math.Max(texture, LLUDPServer.MTU); | 385 | texture = Math.Max(texture, LLUDPServer.MTU); |
349 | asset = Math.Max(asset, LLUDPServer.MTU); | 386 | asset = Math.Max(asset, LLUDPServer.MTU); |
387 | */ | ||
388 | |||
389 | // Since most textures are now delivered through http, make it possible | ||
390 | // to cannibalize some of the bw from the texture throttle to use for | ||
391 | // the task queue (e.g. object updates) | ||
392 | task = task + (int)(m_cannibalrate * texture); | ||
393 | texture = (int)((1 - m_cannibalrate) * texture); | ||
394 | |||
395 | int total = resend + land + wind + cloud + task + texture + asset; | ||
396 | |||
397 | float m_burst = total * m_burstTime; | ||
350 | 398 | ||
351 | //int total = resend + land + wind + cloud + task + texture + asset; | ||
352 | //m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, Total={8}", | 399 | //m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, Total={8}", |
353 | // AgentID, resend, land, wind, cloud, task, texture, asset, total); | 400 | // AgentID, resend, land, wind, cloud, task, texture, asset, total); |
354 | 401 | ||
@@ -357,24 +404,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
357 | 404 | ||
358 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; | 405 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; |
359 | bucket.RequestedDripRate = resend; | 406 | bucket.RequestedDripRate = resend; |
407 | bucket.RequestedBurst = m_burst; | ||
360 | 408 | ||
361 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; | 409 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; |
362 | bucket.RequestedDripRate = land; | 410 | bucket.RequestedDripRate = land; |
411 | bucket.RequestedBurst = m_burst; | ||
363 | 412 | ||
364 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; | 413 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; |
365 | bucket.RequestedDripRate = wind; | 414 | bucket.RequestedDripRate = wind; |
415 | bucket.RequestedBurst = m_burst; | ||
366 | 416 | ||
367 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; | 417 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; |
368 | bucket.RequestedDripRate = cloud; | 418 | bucket.RequestedDripRate = cloud; |
419 | bucket.RequestedBurst = m_burst; | ||
369 | 420 | ||
370 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; | 421 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; |
371 | bucket.RequestedDripRate = asset; | 422 | bucket.RequestedDripRate = asset; |
423 | bucket.RequestedBurst = m_burst; | ||
372 | 424 | ||
373 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; | 425 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; |
374 | bucket.RequestedDripRate = task; | 426 | bucket.RequestedDripRate = task; |
427 | bucket.RequestedBurst = m_burst; | ||
375 | 428 | ||
376 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; | 429 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; |
377 | bucket.RequestedDripRate = texture; | 430 | bucket.RequestedDripRate = texture; |
431 | bucket.RequestedBurst = m_burst; | ||
378 | 432 | ||
379 | // Reset the packed throttles cached data | 433 | // Reset the packed throttles cached data |
380 | m_packedThrottles = null; | 434 | m_packedThrottles = null; |
@@ -392,25 +446,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
392 | int i = 0; | 446 | int i = 0; |
393 | 447 | ||
394 | // multiply by 8 to convert bytes back to bits | 448 | // multiply by 8 to convert bytes back to bits |
395 | rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate * 8 * multiplier; | 449 | multiplier *= 8; |
450 | |||
451 | rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate * multiplier; | ||
396 | Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; | 452 | Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; |
397 | 453 | ||
398 | rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate * 8 * multiplier; | 454 | rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate * multiplier; |
399 | Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; | 455 | Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; |
400 | 456 | ||
401 | rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate * 8 * multiplier; | 457 | rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate * multiplier; |
402 | Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; | 458 | Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; |
403 | 459 | ||
404 | rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate * 8 * multiplier; | 460 | rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate * multiplier; |
405 | Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; | 461 | Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; |
406 | 462 | ||
407 | rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate * 8 * multiplier; | 463 | rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate * multiplier; |
408 | Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; | 464 | Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; |
409 | 465 | ||
410 | rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate * 8 * multiplier; | 466 | rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate * multiplier; |
411 | Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; | 467 | Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; |
412 | 468 | ||
413 | rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate * 8 * multiplier; | 469 | rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate * multiplier; |
414 | Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; | 470 | Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; |
415 | 471 | ||
416 | m_packedThrottles = data; | 472 | m_packedThrottles = data; |
@@ -418,6 +474,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
418 | 474 | ||
419 | return data; | 475 | return data; |
420 | } | 476 | } |
477 | |||
478 | public int GetCatBytesCanSend(ThrottleOutPacketType cat, int timeMS) | ||
479 | { | ||
480 | int icat = (int)cat; | ||
481 | if (icat > 0 && icat < THROTTLE_CATEGORY_COUNT) | ||
482 | { | ||
483 | TokenBucket bucket = m_throttleCategories[icat]; | ||
484 | return bucket.GetCatBytesCanSend(timeMS); | ||
485 | } | ||
486 | else | ||
487 | return 0; | ||
488 | } | ||
421 | 489 | ||
422 | /// <summary> | 490 | /// <summary> |
423 | /// Queue an outgoing packet if appropriate. | 491 | /// Queue an outgoing packet if appropriate. |
@@ -449,19 +517,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
449 | 517 | ||
450 | TokenBucket bucket = m_throttleCategories[category]; | 518 | TokenBucket bucket = m_throttleCategories[category]; |
451 | 519 | ||
452 | // Don't send this packet if there is already a packet waiting in the queue | 520 | // Don't send this packet if queue is not empty |
453 | // even if we have the tokens to send it, tokens should go to the already | 521 | if (queue.Count > 0 || m_nextPackets[category] != null) |
454 | // queued packets | ||
455 | if (queue.Count > 0) | ||
456 | { | 522 | { |
457 | queue.Enqueue(packet, highPriority); | 523 | queue.Enqueue(packet, highPriority); |
458 | return true; | 524 | return true; |
459 | } | 525 | } |
460 | 526 | ||
461 | |||
462 | if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength)) | 527 | if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength)) |
463 | { | 528 | { |
464 | // Enough tokens were removed from the bucket, the packet will not be queued | 529 | // enough tokens so it can be sent imediatly by caller |
465 | return false; | 530 | return false; |
466 | } | 531 | } |
467 | else | 532 | else |
@@ -496,7 +561,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
496 | /// <returns>True if any packets were sent, otherwise false</returns> | 561 | /// <returns>True if any packets were sent, otherwise false</returns> |
497 | public bool DequeueOutgoing() | 562 | public bool DequeueOutgoing() |
498 | { | 563 | { |
499 | if (m_deliverPackets == false) return false; | 564 | // if (m_deliverPackets == false) return false; |
500 | 565 | ||
501 | OutgoingPacket packet = null; | 566 | OutgoingPacket packet = null; |
502 | DoubleLocklessQueue<OutgoingPacket> queue; | 567 | DoubleLocklessQueue<OutgoingPacket> queue; |
@@ -523,6 +588,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
523 | m_udpServer.SendPacketFinal(nextPacket); | 588 | m_udpServer.SendPacketFinal(nextPacket); |
524 | m_nextPackets[i] = null; | 589 | m_nextPackets[i] = null; |
525 | packetSent = true; | 590 | packetSent = true; |
591 | |||
592 | if (m_packetOutboxes[i].Count < 5) | ||
593 | emptyCategories |= CategoryToFlag(i); | ||
526 | } | 594 | } |
527 | } | 595 | } |
528 | else | 596 | else |
@@ -550,6 +618,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
550 | // Send the packet | 618 | // Send the packet |
551 | m_udpServer.SendPacketFinal(packet); | 619 | m_udpServer.SendPacketFinal(packet); |
552 | packetSent = true; | 620 | packetSent = true; |
621 | |||
622 | if (queue.Count < 5) | ||
623 | emptyCategories |= CategoryToFlag(i); | ||
553 | } | 624 | } |
554 | else | 625 | else |
555 | { | 626 | { |
@@ -557,11 +628,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
557 | m_nextPackets[i] = packet; | 628 | m_nextPackets[i] = packet; |
558 | } | 629 | } |
559 | 630 | ||
560 | // If the queue is empty after this dequeue, fire the queue | ||
561 | // empty callback now so it has a chance to fill before we | ||
562 | // get back here | ||
563 | if (queue.Count == 0) | ||
564 | emptyCategories |= CategoryToFlag(i); | ||
565 | } | 631 | } |
566 | else | 632 | else |
567 | { | 633 | { |
@@ -639,6 +705,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
639 | RTO = Math.Min(RTO * 2, m_maxRTO); | 705 | RTO = Math.Min(RTO * 2, m_maxRTO); |
640 | } | 706 | } |
641 | 707 | ||
708 | |||
709 | const int MIN_CALLBACK_MS = 10; | ||
710 | |||
642 | /// <summary> | 711 | /// <summary> |
643 | /// Does an early check to see if this queue empty callback is already | 712 | /// Does an early check to see if this queue empty callback is already |
644 | /// running, then asynchronously firing the event | 713 | /// running, then asynchronously firing the event |
@@ -646,15 +715,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
646 | /// <param name="categories">Throttle categories to fire the callback for</param> | 715 | /// <param name="categories">Throttle categories to fire the callback for</param> |
647 | private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) | 716 | private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) |
648 | { | 717 | { |
649 | if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) | 718 | if (!m_isQueueEmptyRunning) |
650 | { | 719 | { |
651 | // Use a value of 0 to signal that FireQueueEmpty is running | 720 | int start = Environment.TickCount & Int32.MaxValue; |
652 | m_nextOnQueueEmpty = 0; | 721 | |
653 | // Asynchronously run the callback | 722 | if (start < m_nextOnQueueEmpty) |
654 | Util.FireAndForget(FireQueueEmpty, categories); | 723 | return; |
724 | |||
725 | m_isQueueEmptyRunning = true; | ||
726 | |||
727 | m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; | ||
728 | if (m_nextOnQueueEmpty == 0) | ||
729 | m_nextOnQueueEmpty = 1; | ||
730 | |||
731 | if (HasUpdates(categories)) | ||
732 | { | ||
733 | // Asynchronously run the callback | ||
734 | Util.FireAndForget(FireQueueEmpty, categories); | ||
735 | } | ||
736 | else | ||
737 | { | ||
738 | m_isQueueEmptyRunning = false; | ||
739 | } | ||
655 | } | 740 | } |
656 | } | 741 | } |
657 | 742 | ||
743 | private bool m_isQueueEmptyRunning; | ||
744 | |||
745 | |||
658 | /// <summary> | 746 | /// <summary> |
659 | /// Fires the OnQueueEmpty callback and sets the minimum time that it | 747 | /// Fires the OnQueueEmpty callback and sets the minimum time that it |
660 | /// can be called again | 748 | /// can be called again |
@@ -664,26 +752,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
664 | /// signature</param> | 752 | /// signature</param> |
665 | private void FireQueueEmpty(object o) | 753 | private void FireQueueEmpty(object o) |
666 | { | 754 | { |
667 | const int MIN_CALLBACK_MS = 30; | ||
668 | |||
669 | ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o; | 755 | ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o; |
670 | QueueEmpty callback = OnQueueEmpty; | 756 | QueueEmpty callback = OnQueueEmpty; |
671 | |||
672 | int start = Environment.TickCount & Int32.MaxValue; | ||
673 | 757 | ||
674 | if (callback != null) | 758 | if (callback != null) |
675 | { | 759 | { |
760 | // if (m_udpServer.IsRunningOutbound) | ||
761 | // { | ||
676 | try { callback(categories); } | 762 | try { callback(categories); } |
677 | catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); } | 763 | catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); } |
764 | // } | ||
678 | } | 765 | } |
679 | 766 | ||
680 | m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; | 767 | m_isQueueEmptyRunning = false; |
681 | if (m_nextOnQueueEmpty == 0) | ||
682 | m_nextOnQueueEmpty = 1; | ||
683 | } | 768 | } |
769 | |||
684 | internal void ForceThrottleSetting(int throttle, int setting) | 770 | internal void ForceThrottleSetting(int throttle, int setting) |
685 | { | 771 | { |
686 | m_throttleCategories[throttle].RequestedDripRate = Math.Max(setting, LLUDPServer.MTU); ; | 772 | if (throttle > 0 && throttle < THROTTLE_CATEGORY_COUNT) |
773 | m_throttleCategories[throttle].RequestedDripRate = Math.Max(setting, LLUDPServer.MTU); | ||
774 | } | ||
775 | |||
776 | internal int GetThrottleSetting(int throttle) | ||
777 | { | ||
778 | if (throttle > 0 && throttle < THROTTLE_CATEGORY_COUNT) | ||
779 | return (int)m_throttleCategories[throttle].RequestedDripRate; | ||
780 | else | ||
781 | return 0; | ||
687 | } | 782 | } |
688 | 783 | ||
689 | /// <summary> | 784 | /// <summary> |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 4154ef2..f66534d 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | |||
@@ -34,6 +34,7 @@ using System.Net.Sockets; | |||
34 | using System.Reflection; | 34 | using System.Reflection; |
35 | using System.Threading; | 35 | using System.Threading; |
36 | using log4net; | 36 | using log4net; |
37 | using NDesk.Options; | ||
37 | using Nini.Config; | 38 | using Nini.Config; |
38 | using OpenMetaverse.Packets; | 39 | using OpenMetaverse.Packets; |
39 | using OpenSim.Framework; | 40 | using OpenSim.Framework; |
@@ -62,20 +63,41 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
62 | m_udpServer = new LLUDPServer(listenIP, ref port, proxyPortOffsetParm, allow_alternate_port, configSource, circuitManager); | 63 | m_udpServer = new LLUDPServer(listenIP, ref port, proxyPortOffsetParm, allow_alternate_port, configSource, circuitManager); |
63 | } | 64 | } |
64 | 65 | ||
65 | public void NetworkStop() | ||
66 | { | ||
67 | m_udpServer.Stop(); | ||
68 | } | ||
69 | |||
70 | public void AddScene(IScene scene) | 66 | public void AddScene(IScene scene) |
71 | { | 67 | { |
72 | m_udpServer.AddScene(scene); | 68 | m_udpServer.AddScene(scene); |
73 | 69 | ||
74 | StatsManager.RegisterStat( | 70 | StatsManager.RegisterStat( |
75 | new Stat( | 71 | new Stat( |
72 | "ClientLogoutsDueToNoReceives", | ||
73 | "Number of times a client has been logged out because no packets were received before the timeout.", | ||
74 | "", | ||
75 | "", | ||
76 | "clientstack", | ||
77 | scene.Name, | ||
78 | StatType.Pull, | ||
79 | MeasuresOfInterest.None, | ||
80 | stat => stat.Value = m_udpServer.ClientLogoutsDueToNoReceives, | ||
81 | StatVerbosity.Debug)); | ||
82 | |||
83 | StatsManager.RegisterStat( | ||
84 | new Stat( | ||
85 | "IncomingUDPReceivesCount", | ||
86 | "Number of UDP receives performed", | ||
87 | "", | ||
88 | "", | ||
89 | "clientstack", | ||
90 | scene.Name, | ||
91 | StatType.Pull, | ||
92 | MeasuresOfInterest.AverageChangeOverTime, | ||
93 | stat => stat.Value = m_udpServer.UdpReceives, | ||
94 | StatVerbosity.Debug)); | ||
95 | |||
96 | StatsManager.RegisterStat( | ||
97 | new Stat( | ||
76 | "IncomingPacketsProcessedCount", | 98 | "IncomingPacketsProcessedCount", |
77 | "Number of inbound UDP packets processed", | 99 | "Number of inbound LL protocol packets processed", |
78 | "Number of inbound UDP packets processed", | 100 | "", |
79 | "", | 101 | "", |
80 | "clientstack", | 102 | "clientstack", |
81 | scene.Name, | 103 | scene.Name, |
@@ -83,6 +105,86 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
83 | MeasuresOfInterest.AverageChangeOverTime, | 105 | MeasuresOfInterest.AverageChangeOverTime, |
84 | stat => stat.Value = m_udpServer.IncomingPacketsProcessed, | 106 | stat => stat.Value = m_udpServer.IncomingPacketsProcessed, |
85 | StatVerbosity.Debug)); | 107 | StatVerbosity.Debug)); |
108 | |||
109 | StatsManager.RegisterStat( | ||
110 | new Stat( | ||
111 | "IncomingPacketsMalformedCount", | ||
112 | "Number of inbound UDP packets that could not be recognized as LL protocol packets.", | ||
113 | "", | ||
114 | "", | ||
115 | "clientstack", | ||
116 | scene.Name, | ||
117 | StatType.Pull, | ||
118 | MeasuresOfInterest.AverageChangeOverTime, | ||
119 | stat => stat.Value = m_udpServer.IncomingMalformedPacketCount, | ||
120 | StatVerbosity.Info)); | ||
121 | |||
122 | StatsManager.RegisterStat( | ||
123 | new Stat( | ||
124 | "IncomingPacketsOrphanedCount", | ||
125 | "Number of inbound packets that were not initial connections packets and could not be associated with a viewer.", | ||
126 | "", | ||
127 | "", | ||
128 | "clientstack", | ||
129 | scene.Name, | ||
130 | StatType.Pull, | ||
131 | MeasuresOfInterest.AverageChangeOverTime, | ||
132 | stat => stat.Value = m_udpServer.IncomingOrphanedPacketCount, | ||
133 | StatVerbosity.Info)); | ||
134 | |||
135 | StatsManager.RegisterStat( | ||
136 | new Stat( | ||
137 | "IncomingPacketsResentCount", | ||
138 | "Number of inbound packets that clients indicate are resends.", | ||
139 | "", | ||
140 | "", | ||
141 | "clientstack", | ||
142 | scene.Name, | ||
143 | StatType.Pull, | ||
144 | MeasuresOfInterest.AverageChangeOverTime, | ||
145 | stat => stat.Value = m_udpServer.IncomingPacketsResentCount, | ||
146 | StatVerbosity.Debug)); | ||
147 | |||
148 | StatsManager.RegisterStat( | ||
149 | new Stat( | ||
150 | "OutgoingUDPSendsCount", | ||
151 | "Number of UDP sends performed", | ||
152 | "", | ||
153 | "", | ||
154 | "clientstack", | ||
155 | scene.Name, | ||
156 | StatType.Pull, | ||
157 | MeasuresOfInterest.AverageChangeOverTime, | ||
158 | stat => stat.Value = m_udpServer.UdpSends, | ||
159 | StatVerbosity.Debug)); | ||
160 | |||
161 | StatsManager.RegisterStat( | ||
162 | new Stat( | ||
163 | "OutgoingPacketsResentCount", | ||
164 | "Number of packets resent because a client did not acknowledge receipt", | ||
165 | "", | ||
166 | "", | ||
167 | "clientstack", | ||
168 | scene.Name, | ||
169 | StatType.Pull, | ||
170 | MeasuresOfInterest.AverageChangeOverTime, | ||
171 | stat => stat.Value = m_udpServer.PacketsResentCount, | ||
172 | StatVerbosity.Debug)); | ||
173 | |||
174 | StatsManager.RegisterStat( | ||
175 | new Stat( | ||
176 | "AverageUDPProcessTime", | ||
177 | "Average number of milliseconds taken to process each incoming UDP packet in a sample.", | ||
178 | "This is for initial receive processing which is separate from the later client LL packet processing stage.", | ||
179 | "ms", | ||
180 | "clientstack", | ||
181 | scene.Name, | ||
182 | StatType.Pull, | ||
183 | MeasuresOfInterest.None, | ||
184 | stat => stat.Value = m_udpServer.AverageReceiveTicksForLastSamplePeriod / TimeSpan.TicksPerMillisecond, | ||
185 | // stat => | ||
186 | // stat.Value = Math.Round(m_udpServer.AverageReceiveTicksForLastSamplePeriod / TimeSpan.TicksPerMillisecond, 7), | ||
187 | StatVerbosity.Debug)); | ||
86 | } | 188 | } |
87 | 189 | ||
88 | public bool HandlesRegion(Location x) | 190 | public bool HandlesRegion(Location x) |
@@ -107,10 +209,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
107 | /// </summary> | 209 | /// </summary> |
108 | public class LLUDPServer : OpenSimUDPBase | 210 | public class LLUDPServer : OpenSimUDPBase |
109 | { | 211 | { |
212 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
213 | |||
110 | /// <summary>Maximum transmission unit, or UDP packet size, for the LLUDP protocol</summary> | 214 | /// <summary>Maximum transmission unit, or UDP packet size, for the LLUDP protocol</summary> |
111 | public const int MTU = 1400; | 215 | public const int MTU = 1400; |
112 | 216 | ||
113 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 217 | /// <summary>Number of forced client logouts due to no receipt of packets before timeout.</summary> |
218 | public int ClientLogoutsDueToNoReceives { get; private set; } | ||
219 | |||
220 | /// <summary> | ||
221 | /// Default packet debug level given to new clients | ||
222 | /// </summary> | ||
223 | public int DefaultClientPacketDebugLevel { get; set; } | ||
114 | 224 | ||
115 | /// <summary>The measured resolution of Environment.TickCount</summary> | 225 | /// <summary>The measured resolution of Environment.TickCount</summary> |
116 | public readonly float TickCountResolution; | 226 | public readonly float TickCountResolution; |
@@ -183,7 +293,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
183 | /// <summary>Flag to signal when clients should send pings</summary> | 293 | /// <summary>Flag to signal when clients should send pings</summary> |
184 | protected bool m_sendPing; | 294 | protected bool m_sendPing; |
185 | 295 | ||
296 | private int m_animationSequenceNumber; | ||
297 | |||
298 | public int NextAnimationSequenceNumber | ||
299 | { | ||
300 | get | ||
301 | { | ||
302 | m_animationSequenceNumber++; | ||
303 | if (m_animationSequenceNumber > 2147482624) | ||
304 | m_animationSequenceNumber = 1; | ||
305 | return m_animationSequenceNumber; | ||
306 | } | ||
307 | } | ||
308 | |||
309 | |||
310 | |||
186 | private ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>> m_pendingCache = new ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>>(); | 311 | private ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>> m_pendingCache = new ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>>(); |
312 | |||
313 | /// <summary> | ||
314 | /// Event used to signal when queued packets are available for sending. | ||
315 | /// </summary> | ||
316 | /// <remarks> | ||
317 | /// This allows the outbound loop to only operate when there is data to send rather than continuously polling. | ||
318 | /// Some data is sent immediately and not queued. That data would not trigger this event. | ||
319 | /// WRONG use. May be usefull in future revision | ||
320 | /// </remarks> | ||
321 | // private AutoResetEvent m_dataPresentEvent = new AutoResetEvent(false); | ||
322 | |||
187 | private Pool<IncomingPacket> m_incomingPacketPool; | 323 | private Pool<IncomingPacket> m_incomingPacketPool; |
188 | 324 | ||
189 | /// <summary> | 325 | /// <summary> |
@@ -204,7 +340,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
204 | 340 | ||
205 | public Socket Server { get { return null; } } | 341 | public Socket Server { get { return null; } } |
206 | 342 | ||
207 | private int m_malformedCount = 0; // Guard against a spamming attack | 343 | /// <summary> |
344 | /// Record how many packets have been resent | ||
345 | /// </summary> | ||
346 | internal int PacketsResentCount { get; set; } | ||
347 | |||
348 | /// <summary> | ||
349 | /// Record how many packets have been sent | ||
350 | /// </summary> | ||
351 | internal int PacketsSentCount { get; set; } | ||
352 | |||
353 | /// <summary> | ||
354 | /// Record how many incoming packets are indicated as resends by clients. | ||
355 | /// </summary> | ||
356 | internal int IncomingPacketsResentCount { get; set; } | ||
357 | |||
358 | /// <summary> | ||
359 | /// Record how many inbound packets could not be recognized as LLUDP packets. | ||
360 | /// </summary> | ||
361 | public int IncomingMalformedPacketCount { get; private set; } | ||
362 | |||
363 | /// <summary> | ||
364 | /// Record how many inbound packets could not be associated with a simulator circuit. | ||
365 | /// </summary> | ||
366 | public int IncomingOrphanedPacketCount { get; private set; } | ||
208 | 367 | ||
209 | /// <summary> | 368 | /// <summary> |
210 | /// Record current outgoing client for monitoring purposes. | 369 | /// Record current outgoing client for monitoring purposes. |
@@ -225,16 +384,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
225 | 384 | ||
226 | // Measure the resolution of Environment.TickCount | 385 | // Measure the resolution of Environment.TickCount |
227 | TickCountResolution = 0f; | 386 | TickCountResolution = 0f; |
228 | for (int i = 0; i < 5; i++) | 387 | for (int i = 0; i < 10; i++) |
229 | { | 388 | { |
230 | int start = Environment.TickCount; | 389 | int start = Environment.TickCount; |
231 | int now = start; | 390 | int now = start; |
232 | while (now == start) | 391 | while (now == start) |
233 | now = Environment.TickCount; | 392 | now = Environment.TickCount; |
234 | TickCountResolution += (float)(now - start) * 0.2f; | 393 | TickCountResolution += (float)(now - start) * 0.1f; |
235 | } | 394 | } |
236 | m_log.Info("[LLUDPSERVER]: Average Environment.TickCount resolution: " + TickCountResolution + "ms"); | ||
237 | TickCountResolution = (float)Math.Ceiling(TickCountResolution); | 395 | TickCountResolution = (float)Math.Ceiling(TickCountResolution); |
396 | m_log.Info("[LLUDPSERVER]: Average Environment.TickCount resolution: " + TickCountResolution + "ms"); | ||
238 | 397 | ||
239 | #endregion Environment.TickCount Measurement | 398 | #endregion Environment.TickCount Measurement |
240 | 399 | ||
@@ -242,6 +401,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
242 | int sceneThrottleBps = 0; | 401 | int sceneThrottleBps = 0; |
243 | bool usePools = false; | 402 | bool usePools = false; |
244 | 403 | ||
404 | |||
405 | |||
245 | IConfig config = configSource.Configs["ClientStack.LindenUDP"]; | 406 | IConfig config = configSource.Configs["ClientStack.LindenUDP"]; |
246 | if (config != null) | 407 | if (config != null) |
247 | { | 408 | { |
@@ -288,9 +449,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
288 | } | 449 | } |
289 | #endregion BinaryStats | 450 | #endregion BinaryStats |
290 | 451 | ||
291 | m_throttle = new TokenBucket(null, sceneThrottleBps); | 452 | m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps * 10e-3f); |
292 | ThrottleRates = new ThrottleRates(configSource); | 453 | ThrottleRates = new ThrottleRates(configSource); |
293 | 454 | ||
455 | Random rnd = new Random(Util.EnvironmentTickCount()); | ||
456 | m_animationSequenceNumber = rnd.Next(11474826); | ||
457 | |||
294 | if (usePools) | 458 | if (usePools) |
295 | EnablePools(); | 459 | EnablePools(); |
296 | } | 460 | } |
@@ -461,6 +625,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
461 | m_scene = (Scene)scene; | 625 | m_scene = (Scene)scene; |
462 | m_location = new Location(m_scene.RegionInfo.RegionHandle); | 626 | m_location = new Location(m_scene.RegionInfo.RegionHandle); |
463 | 627 | ||
628 | StatsManager.RegisterStat( | ||
629 | new Stat( | ||
630 | "InboxPacketsCount", | ||
631 | "Number of LL protocol packets waiting for the second stage of processing after initial receive.", | ||
632 | "Number of LL protocol packets waiting for the second stage of processing after initial receive.", | ||
633 | "", | ||
634 | "clientstack", | ||
635 | scene.Name, | ||
636 | StatType.Pull, | ||
637 | MeasuresOfInterest.AverageChangeOverTime, | ||
638 | stat => stat.Value = packetInbox.Count, | ||
639 | StatVerbosity.Debug)); | ||
640 | |||
464 | // XXX: These stats are also pool stats but we register them separately since they are currently not | 641 | // XXX: These stats are also pool stats but we register them separately since they are currently not |
465 | // turned on and off by EnablePools()/DisablePools() | 642 | // turned on and off by EnablePools()/DisablePools() |
466 | StatsManager.RegisterStat( | 643 | StatsManager.RegisterStat( |
@@ -521,6 +698,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
521 | EnablePoolStats(); | 698 | EnablePoolStats(); |
522 | 699 | ||
523 | MainConsole.Instance.Commands.AddCommand( | 700 | MainConsole.Instance.Commands.AddCommand( |
701 | "Debug", false, "debug lludp packet", | ||
702 | "debug lludp packet [--default] <level> [<avatar-first-name> <avatar-last-name>]", | ||
703 | "Turn on packet debugging", | ||
704 | "If level > 255 then all incoming and outgoing packets are logged.\n" | ||
705 | + "If level <= 255 then incoming AgentUpdate and outgoing SimStats and SimulatorViewerTimeMessage packets are not logged.\n" | ||
706 | + "If level <= 200 then incoming RequestImage and outgoing ImagePacket, ImageData, LayerData and CoarseLocationUpdate packets are not logged.\n" | ||
707 | + "If level <= 100 then incoming ViewerEffect and AgentAnimation and outgoing ViewerEffect and AvatarAnimation packets are not logged.\n" | ||
708 | + "If level <= 50 then outgoing ImprovedTerseObjectUpdate packets are not logged.\n" | ||
709 | + "If level <= 0 then no packets are logged.\n" | ||
710 | + "If --default is specified then the level becomes the default logging level for all subsequent agents.\n" | ||
711 | + "In this case, you cannot also specify an avatar name.\n" | ||
712 | + "If an avatar name is given then only packets from that avatar are logged.", | ||
713 | HandlePacketCommand); | ||
714 | |||
715 | MainConsole.Instance.Commands.AddCommand( | ||
524 | "Debug", | 716 | "Debug", |
525 | false, | 717 | false, |
526 | "debug lludp start", | 718 | "debug lludp start", |
@@ -559,10 +751,79 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
559 | "debug lludp status", | 751 | "debug lludp status", |
560 | "Return status of LLUDP packet processing.", | 752 | "Return status of LLUDP packet processing.", |
561 | HandleStatusCommand); | 753 | HandleStatusCommand); |
754 | /* disabled | ||
755 | MainConsole.Instance.Commands.AddCommand( | ||
756 | "Debug", | ||
757 | false, | ||
758 | "debug lludp toggle agentupdate", | ||
759 | "debug lludp toggle agentupdate", | ||
760 | "Toggle whether agentupdate packets are processed or simply discarded.", | ||
761 | HandleAgentUpdateCommand); | ||
762 | */ | ||
763 | } | ||
764 | |||
765 | private void HandlePacketCommand(string module, string[] args) | ||
766 | { | ||
767 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene) | ||
768 | return; | ||
769 | |||
770 | bool setAsDefaultLevel = false; | ||
771 | OptionSet optionSet = new OptionSet().Add("default", o => setAsDefaultLevel = o != null); | ||
772 | List<string> filteredArgs = optionSet.Parse(args); | ||
773 | |||
774 | string name = null; | ||
775 | |||
776 | if (filteredArgs.Count == 6) | ||
777 | { | ||
778 | if (!setAsDefaultLevel) | ||
779 | { | ||
780 | name = string.Format("{0} {1}", filteredArgs[4], filteredArgs[5]); | ||
781 | } | ||
782 | else | ||
783 | { | ||
784 | MainConsole.Instance.OutputFormat("ERROR: Cannot specify a user name when setting default logging level"); | ||
785 | return; | ||
786 | } | ||
787 | } | ||
788 | |||
789 | if (filteredArgs.Count > 3) | ||
790 | { | ||
791 | int newDebug; | ||
792 | if (int.TryParse(filteredArgs[3], out newDebug)) | ||
793 | { | ||
794 | if (setAsDefaultLevel) | ||
795 | { | ||
796 | DefaultClientPacketDebugLevel = newDebug; | ||
797 | MainConsole.Instance.OutputFormat( | ||
798 | "Debug packet debug for new clients set to {0} in {1}", DefaultClientPacketDebugLevel, m_scene.Name); | ||
799 | } | ||
800 | else | ||
801 | { | ||
802 | m_scene.ForEachScenePresence(sp => | ||
803 | { | ||
804 | if (name == null || sp.Name == name) | ||
805 | { | ||
806 | MainConsole.Instance.OutputFormat( | ||
807 | "Packet debug for {0} ({1}) set to {2} in {3}", | ||
808 | sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, m_scene.Name); | ||
809 | |||
810 | sp.ControllingClient.DebugPacketLevel = newDebug; | ||
811 | } | ||
812 | }); | ||
813 | } | ||
814 | } | ||
815 | else | ||
816 | { | ||
817 | MainConsole.Instance.Output("Usage: debug lludp packet [--default] 0..255 [<first-name> <last-name>]"); | ||
818 | } | ||
819 | } | ||
562 | } | 820 | } |
563 | 821 | ||
564 | private void HandleStartCommand(string module, string[] args) | 822 | private void HandleStartCommand(string module, string[] args) |
565 | { | 823 | { |
824 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene) | ||
825 | return; | ||
826 | |||
566 | if (args.Length != 4) | 827 | if (args.Length != 4) |
567 | { | 828 | { |
568 | MainConsole.Instance.Output("Usage: debug lludp start <in|out|all>"); | 829 | MainConsole.Instance.Output("Usage: debug lludp start <in|out|all>"); |
@@ -580,6 +841,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
580 | 841 | ||
581 | private void HandleStopCommand(string module, string[] args) | 842 | private void HandleStopCommand(string module, string[] args) |
582 | { | 843 | { |
844 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene) | ||
845 | return; | ||
846 | |||
583 | if (args.Length != 4) | 847 | if (args.Length != 4) |
584 | { | 848 | { |
585 | MainConsole.Instance.Output("Usage: debug lludp stop <in|out|all>"); | 849 | MainConsole.Instance.Output("Usage: debug lludp stop <in|out|all>"); |
@@ -597,6 +861,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
597 | 861 | ||
598 | private void HandlePoolCommand(string module, string[] args) | 862 | private void HandlePoolCommand(string module, string[] args) |
599 | { | 863 | { |
864 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene) | ||
865 | return; | ||
866 | |||
600 | if (args.Length != 4) | 867 | if (args.Length != 4) |
601 | { | 868 | { |
602 | MainConsole.Instance.Output("Usage: debug lludp pool <on|off>"); | 869 | MainConsole.Instance.Output("Usage: debug lludp pool <on|off>"); |
@@ -627,8 +894,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
627 | } | 894 | } |
628 | } | 895 | } |
629 | 896 | ||
897 | bool m_discardAgentUpdates; | ||
898 | |||
899 | private void HandleAgentUpdateCommand(string module, string[] args) | ||
900 | { | ||
901 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene) | ||
902 | return; | ||
903 | |||
904 | m_discardAgentUpdates = !m_discardAgentUpdates; | ||
905 | |||
906 | MainConsole.Instance.OutputFormat( | ||
907 | "Discard AgentUpdates now {0} for {1}", m_discardAgentUpdates, m_scene.Name); | ||
908 | } | ||
909 | |||
630 | private void HandleStatusCommand(string module, string[] args) | 910 | private void HandleStatusCommand(string module, string[] args) |
631 | { | 911 | { |
912 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene) | ||
913 | return; | ||
914 | |||
632 | MainConsole.Instance.OutputFormat( | 915 | MainConsole.Instance.OutputFormat( |
633 | "IN LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningInbound ? "enabled" : "disabled"); | 916 | "IN LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningInbound ? "enabled" : "disabled"); |
634 | 917 | ||
@@ -636,6 +919,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
636 | "OUT LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningOutbound ? "enabled" : "disabled"); | 919 | "OUT LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningOutbound ? "enabled" : "disabled"); |
637 | 920 | ||
638 | MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", m_scene.Name, UsePools ? "on" : "off"); | 921 | MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", m_scene.Name, UsePools ? "on" : "off"); |
922 | |||
923 | MainConsole.Instance.OutputFormat( | ||
924 | "Packet debug level for new clients is {0}", DefaultClientPacketDebugLevel); | ||
639 | } | 925 | } |
640 | 926 | ||
641 | public bool HandlesRegion(Location x) | 927 | public bool HandlesRegion(Location x) |
@@ -643,44 +929,44 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
643 | return x == m_location; | 929 | return x == m_location; |
644 | } | 930 | } |
645 | 931 | ||
646 | public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) | 932 | // public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) |
647 | { | 933 | // { |
648 | // CoarseLocationUpdate and AvatarGroupsReply packets cannot be split in an automated way | 934 | // // CoarseLocationUpdate and AvatarGroupsReply packets cannot be split in an automated way |
649 | if ((packet.Type == PacketType.CoarseLocationUpdate || packet.Type == PacketType.AvatarGroupsReply) && allowSplitting) | 935 | // if ((packet.Type == PacketType.CoarseLocationUpdate || packet.Type == PacketType.AvatarGroupsReply) && allowSplitting) |
650 | allowSplitting = false; | 936 | // allowSplitting = false; |
651 | 937 | // | |
652 | if (allowSplitting && packet.HasVariableBlocks) | 938 | // if (allowSplitting && packet.HasVariableBlocks) |
653 | { | 939 | // { |
654 | byte[][] datas = packet.ToBytesMultiple(); | 940 | // byte[][] datas = packet.ToBytesMultiple(); |
655 | int packetCount = datas.Length; | 941 | // int packetCount = datas.Length; |
656 | 942 | // | |
657 | if (packetCount < 1) | 943 | // if (packetCount < 1) |
658 | m_log.Error("[LLUDPSERVER]: Failed to split " + packet.Type + " with estimated length " + packet.Length); | 944 | // m_log.Error("[LLUDPSERVER]: Failed to split " + packet.Type + " with estimated length " + packet.Length); |
659 | 945 | // | |
660 | for (int i = 0; i < packetCount; i++) | 946 | // for (int i = 0; i < packetCount; i++) |
661 | { | 947 | // { |
662 | byte[] data = datas[i]; | 948 | // byte[] data = datas[i]; |
663 | m_scene.ForEachClient( | 949 | // m_scene.ForEachClient( |
664 | delegate(IClientAPI client) | 950 | // delegate(IClientAPI client) |
665 | { | 951 | // { |
666 | if (client is LLClientView) | 952 | // if (client is LLClientView) |
667 | SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null); | 953 | // SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null); |
668 | } | 954 | // } |
669 | ); | 955 | // ); |
670 | } | 956 | // } |
671 | } | 957 | // } |
672 | else | 958 | // else |
673 | { | 959 | // { |
674 | byte[] data = packet.ToBytes(); | 960 | // byte[] data = packet.ToBytes(); |
675 | m_scene.ForEachClient( | 961 | // m_scene.ForEachClient( |
676 | delegate(IClientAPI client) | 962 | // delegate(IClientAPI client) |
677 | { | 963 | // { |
678 | if (client is LLClientView) | 964 | // if (client is LLClientView) |
679 | SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null); | 965 | // SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null); |
680 | } | 966 | // } |
681 | ); | 967 | // ); |
682 | } | 968 | // } |
683 | } | 969 | // } |
684 | 970 | ||
685 | /// <summary> | 971 | /// <summary> |
686 | /// Start the process of sending a packet to the client. | 972 | /// Start the process of sending a packet to the client. |
@@ -700,6 +986,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
700 | if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting) | 986 | if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting) |
701 | allowSplitting = false; | 987 | allowSplitting = false; |
702 | 988 | ||
989 | bool packetQueued = false; | ||
990 | |||
703 | if (allowSplitting && packet.HasVariableBlocks) | 991 | if (allowSplitting && packet.HasVariableBlocks) |
704 | { | 992 | { |
705 | byte[][] datas = packet.ToBytesMultiple(); | 993 | byte[][] datas = packet.ToBytesMultiple(); |
@@ -711,16 +999,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
711 | for (int i = 0; i < packetCount; i++) | 999 | for (int i = 0; i < packetCount; i++) |
712 | { | 1000 | { |
713 | byte[] data = datas[i]; | 1001 | byte[] data = datas[i]; |
714 | SendPacketData(udpClient, data, packet.Type, category, method); | 1002 | |
1003 | if (!SendPacketData(udpClient, data, packet.Type, category, method)) | ||
1004 | packetQueued = true; | ||
715 | } | 1005 | } |
716 | } | 1006 | } |
717 | else | 1007 | else |
718 | { | 1008 | { |
719 | byte[] data = packet.ToBytes(); | 1009 | byte[] data = packet.ToBytes(); |
720 | SendPacketData(udpClient, data, packet.Type, category, method); | 1010 | packetQueued = SendPacketData(udpClient, data, packet.Type, category, method); |
721 | } | 1011 | } |
722 | 1012 | ||
723 | PacketPool.Instance.ReturnPacket(packet); | 1013 | PacketPool.Instance.ReturnPacket(packet); |
1014 | |||
1015 | /// WRONG use. May be usefull in future revision | ||
1016 | // if (packetQueued) | ||
1017 | // m_dataPresentEvent.Set(); | ||
724 | } | 1018 | } |
725 | 1019 | ||
726 | /// <summary> | 1020 | /// <summary> |
@@ -734,7 +1028,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
734 | /// The method to call if the packet is not acked by the client. If null, then a standard | 1028 | /// The method to call if the packet is not acked by the client. If null, then a standard |
735 | /// resend of the packet is done. | 1029 | /// resend of the packet is done. |
736 | /// </param> | 1030 | /// </param> |
737 | public void SendPacketData( | 1031 | /// <returns>true if the data was sent immediately, false if it was queued for sending</returns> |
1032 | public bool SendPacketData( | ||
738 | LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category, UnackedPacketMethod method) | 1033 | LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category, UnackedPacketMethod method) |
739 | { | 1034 | { |
740 | int dataLength = data.Length; | 1035 | int dataLength = data.Length; |
@@ -807,7 +1102,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
807 | // packet so that it isn't sent before a queued update packet. | 1102 | // packet so that it isn't sent before a queued update packet. |
808 | bool requestQueue = type == PacketType.KillObject; | 1103 | bool requestQueue = type == PacketType.KillObject; |
809 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, requestQueue, highPriority)) | 1104 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, requestQueue, highPriority)) |
1105 | { | ||
810 | SendPacketFinal(outgoingPacket); | 1106 | SendPacketFinal(outgoingPacket); |
1107 | return true; | ||
1108 | } | ||
1109 | |||
1110 | return false; | ||
811 | 1111 | ||
812 | #endregion Queue or Send | 1112 | #endregion Queue or Send |
813 | } | 1113 | } |
@@ -848,6 +1148,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
848 | pc.PingID.OldestUnacked = 0; | 1148 | pc.PingID.OldestUnacked = 0; |
849 | 1149 | ||
850 | SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false, null); | 1150 | SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false, null); |
1151 | udpClient.m_lastStartpingTimeMS = Util.EnvironmentTickCount(); | ||
851 | } | 1152 | } |
852 | 1153 | ||
853 | public void CompletePing(LLUDPClient udpClient, byte pingID) | 1154 | public void CompletePing(LLUDPClient udpClient, byte pingID) |
@@ -883,7 +1184,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
883 | // Fire this out on a different thread so that we don't hold up outgoing packet processing for | 1184 | // Fire this out on a different thread so that we don't hold up outgoing packet processing for |
884 | // everybody else if this is being called due to an ack timeout. | 1185 | // everybody else if this is being called due to an ack timeout. |
885 | // This is the same as processing as the async process of a logout request. | 1186 | // This is the same as processing as the async process of a logout request. |
886 | Util.FireAndForget(o => DeactivateClientDueToTimeout(client)); | 1187 | Util.FireAndForget(o => DeactivateClientDueToTimeout(client, timeoutTicks)); |
887 | 1188 | ||
888 | return; | 1189 | return; |
889 | } | 1190 | } |
@@ -944,7 +1245,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
944 | int dataLength = buffer.DataLength; | 1245 | int dataLength = buffer.DataLength; |
945 | 1246 | ||
946 | // NOTE: I'm seeing problems with some viewers when ACKs are appended to zerocoded packets so I've disabled that here | 1247 | // NOTE: I'm seeing problems with some viewers when ACKs are appended to zerocoded packets so I've disabled that here |
947 | if (!isZerocoded) | 1248 | if (!isZerocoded && !isResend && outgoingPacket.UnackedMethod == null) |
948 | { | 1249 | { |
949 | // Keep appending ACKs until there is no room left in the buffer or there are | 1250 | // Keep appending ACKs until there is no room left in the buffer or there are |
950 | // no more ACKs to append | 1251 | // no more ACKs to append |
@@ -988,6 +1289,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
988 | else | 1289 | else |
989 | { | 1290 | { |
990 | Interlocked.Increment(ref udpClient.PacketsResent); | 1291 | Interlocked.Increment(ref udpClient.PacketsResent); |
1292 | |||
1293 | // We're not going to worry about interlock yet since its not currently critical that this total count | ||
1294 | // is 100% correct | ||
1295 | PacketsResentCount++; | ||
991 | } | 1296 | } |
992 | 1297 | ||
993 | #endregion Sequence Number Assignment | 1298 | #endregion Sequence Number Assignment |
@@ -995,6 +1300,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
995 | // Stats tracking | 1300 | // Stats tracking |
996 | Interlocked.Increment(ref udpClient.PacketsSent); | 1301 | Interlocked.Increment(ref udpClient.PacketsSent); |
997 | 1302 | ||
1303 | // We're not going to worry about interlock yet since its not currently critical that this total count | ||
1304 | // is 100% correct | ||
1305 | PacketsSentCount++; | ||
1306 | |||
998 | // Put the UDP payload on the wire | 1307 | // Put the UDP payload on the wire |
999 | AsyncBeginSend(buffer); | 1308 | AsyncBeginSend(buffer); |
1000 | 1309 | ||
@@ -1002,6 +1311,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1002 | outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue; | 1311 | outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue; |
1003 | } | 1312 | } |
1004 | 1313 | ||
1314 | private void RecordMalformedInboundPacket(IPEndPoint endPoint) | ||
1315 | { | ||
1316 | // if (m_malformedCount < 100) | ||
1317 | // m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString()); | ||
1318 | |||
1319 | IncomingMalformedPacketCount++; | ||
1320 | |||
1321 | if ((IncomingMalformedPacketCount % 10000) == 0) | ||
1322 | m_log.WarnFormat( | ||
1323 | "[LLUDPSERVER]: Received {0} malformed packets so far, probable network attack. Last was from {1}", | ||
1324 | IncomingMalformedPacketCount, endPoint); | ||
1325 | } | ||
1326 | |||
1005 | public override void PacketReceived(UDPPacketBuffer buffer) | 1327 | public override void PacketReceived(UDPPacketBuffer buffer) |
1006 | { | 1328 | { |
1007 | // Debugging/Profiling | 1329 | // Debugging/Profiling |
@@ -1023,6 +1345,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1023 | // "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}", | 1345 | // "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}", |
1024 | // buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); | 1346 | // buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); |
1025 | 1347 | ||
1348 | RecordMalformedInboundPacket(endPoint); | ||
1349 | |||
1026 | return; // Drop undersized packet | 1350 | return; // Drop undersized packet |
1027 | } | 1351 | } |
1028 | 1352 | ||
@@ -1041,6 +1365,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1041 | // "[LLUDPSERVER]: Dropping packet with malformed header received from {0} in {1}", | 1365 | // "[LLUDPSERVER]: Dropping packet with malformed header received from {0} in {1}", |
1042 | // buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); | 1366 | // buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); |
1043 | 1367 | ||
1368 | RecordMalformedInboundPacket(endPoint); | ||
1369 | |||
1044 | return; // Malformed header | 1370 | return; // Malformed header |
1045 | } | 1371 | } |
1046 | 1372 | ||
@@ -1056,34 +1382,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1056 | // Only allocate a buffer for zerodecoding if the packet is zerocoded | 1382 | // Only allocate a buffer for zerodecoding if the packet is zerocoded |
1057 | ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); | 1383 | ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); |
1058 | } | 1384 | } |
1059 | catch (MalformedDataException) | ||
1060 | { | ||
1061 | } | ||
1062 | catch (IndexOutOfRangeException) | ||
1063 | { | ||
1064 | // m_log.WarnFormat( | ||
1065 | // "[LLUDPSERVER]: Dropping short packet received from {0} in {1}", | ||
1066 | // buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); | ||
1067 | |||
1068 | return; // Drop short packet | ||
1069 | } | ||
1070 | catch (Exception e) | 1385 | catch (Exception e) |
1071 | { | 1386 | { |
1072 | if (m_malformedCount < 100) | 1387 | if (IncomingMalformedPacketCount < 100) |
1073 | m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString()); | 1388 | m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString()); |
1074 | |||
1075 | m_malformedCount++; | ||
1076 | |||
1077 | if ((m_malformedCount % 100000) == 0) | ||
1078 | m_log.DebugFormat("[LLUDPSERVER]: Received {0} malformed packets so far, probable network attack.", m_malformedCount); | ||
1079 | } | 1389 | } |
1080 | 1390 | ||
1081 | // Fail-safe check | 1391 | // Fail-safe check |
1082 | if (packet == null) | 1392 | if (packet == null) |
1083 | { | 1393 | { |
1084 | m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse {0} byte packet from {1}:", | 1394 | if (IncomingMalformedPacketCount < 100) |
1085 | buffer.DataLength, buffer.RemoteEndPoint); | 1395 | { |
1086 | m_log.Error(Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); | 1396 | m_log.WarnFormat("[LLUDPSERVER]: Malformed data, cannot parse {0} byte packet from {1}, data {2}:", |
1397 | buffer.DataLength, buffer.RemoteEndPoint, Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); | ||
1398 | } | ||
1399 | |||
1400 | RecordMalformedInboundPacket(endPoint); | ||
1401 | |||
1087 | return; | 1402 | return; |
1088 | } | 1403 | } |
1089 | 1404 | ||
@@ -1127,12 +1442,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1127 | queue.Enqueue(buffer); | 1442 | queue.Enqueue(buffer); |
1128 | return; | 1443 | return; |
1129 | } | 1444 | } |
1445 | |||
1446 | /* | ||
1447 | else if (packet.Type == PacketType.CompleteAgentMovement) | ||
1448 | { | ||
1449 | // Send ack straight away to let the viewer know that we got it. | ||
1450 | SendAckImmediate(endPoint, packet.Header.Sequence); | ||
1451 | |||
1452 | // We need to copy the endpoint so that it doesn't get changed when another thread reuses the | ||
1453 | // buffer. | ||
1454 | object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; | ||
1455 | |||
1456 | Util.FireAndForget(HandleCompleteMovementIntoRegion, array); | ||
1457 | |||
1458 | return; | ||
1459 | } | ||
1460 | */ | ||
1130 | } | 1461 | } |
1131 | 1462 | ||
1132 | // Determine which agent this packet came from | 1463 | // Determine which agent this packet came from |
1133 | if (client == null || !(client is LLClientView)) | 1464 | if (client == null || !(client is LLClientView)) |
1134 | { | 1465 | { |
1135 | //m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName); | 1466 | //m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName); |
1467 | |||
1468 | IncomingOrphanedPacketCount++; | ||
1469 | |||
1470 | if ((IncomingOrphanedPacketCount % 10000) == 0) | ||
1471 | m_log.WarnFormat( | ||
1472 | "[LLUDPSERVER]: Received {0} orphaned packets so far. Last was from {1}", | ||
1473 | IncomingOrphanedPacketCount, endPoint); | ||
1474 | |||
1136 | return; | 1475 | return; |
1137 | } | 1476 | } |
1138 | 1477 | ||
@@ -1211,6 +1550,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1211 | 1550 | ||
1212 | #region Incoming Packet Accounting | 1551 | #region Incoming Packet Accounting |
1213 | 1552 | ||
1553 | // We're not going to worry about interlock yet since its not currently critical that this total count | ||
1554 | // is 100% correct | ||
1555 | if (packet.Header.Resent) | ||
1556 | IncomingPacketsResentCount++; | ||
1557 | |||
1214 | // Check the archive of received reliable packet IDs to see whether we already received this packet | 1558 | // Check the archive of received reliable packet IDs to see whether we already received this packet |
1215 | if (packet.Header.Reliable && !udpClient.PacketArchive.TryEnqueue(packet.Header.Sequence)) | 1559 | if (packet.Header.Reliable && !udpClient.PacketArchive.TryEnqueue(packet.Header.Sequence)) |
1216 | { | 1560 | { |
@@ -1233,6 +1577,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1233 | LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length); | 1577 | LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length); |
1234 | #endregion BinaryStats | 1578 | #endregion BinaryStats |
1235 | 1579 | ||
1580 | // AgentUpdate mess removed from here | ||
1581 | |||
1236 | #region Ping Check Handling | 1582 | #region Ping Check Handling |
1237 | 1583 | ||
1238 | if (packet.Type == PacketType.StartPingCheck) | 1584 | if (packet.Type == PacketType.StartPingCheck) |
@@ -1242,7 +1588,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1242 | // We don't need to do anything else with ping checks | 1588 | // We don't need to do anything else with ping checks |
1243 | StartPingCheckPacket startPing = (StartPingCheckPacket)packet; | 1589 | StartPingCheckPacket startPing = (StartPingCheckPacket)packet; |
1244 | CompletePing(udpClient, startPing.PingID.PingID); | 1590 | CompletePing(udpClient, startPing.PingID.PingID); |
1245 | 1591 | ||
1246 | if ((Environment.TickCount - m_elapsedMSSinceLastStatReport) >= 3000) | 1592 | if ((Environment.TickCount - m_elapsedMSSinceLastStatReport) >= 3000) |
1247 | { | 1593 | { |
1248 | udpClient.SendPacketStats(); | 1594 | udpClient.SendPacketStats(); |
@@ -1252,7 +1598,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1252 | } | 1598 | } |
1253 | else if (packet.Type == PacketType.CompletePingCheck) | 1599 | else if (packet.Type == PacketType.CompletePingCheck) |
1254 | { | 1600 | { |
1255 | // We don't currently track client ping times | 1601 | int t = Util.EnvironmentTickCountSubtract(udpClient.m_lastStartpingTimeMS); |
1602 | int c = udpClient.m_pingMS; | ||
1603 | c = 800 * c + 200 * t; | ||
1604 | c /= 1000; | ||
1605 | udpClient.m_pingMS = c; | ||
1256 | return; | 1606 | return; |
1257 | } | 1607 | } |
1258 | 1608 | ||
@@ -1272,11 +1622,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1272 | incomingPacket = new IncomingPacket((LLClientView)client, packet); | 1622 | incomingPacket = new IncomingPacket((LLClientView)client, packet); |
1273 | } | 1623 | } |
1274 | 1624 | ||
1275 | if (incomingPacket.Packet.Type == PacketType.AgentUpdate || | 1625 | // if (incomingPacket.Packet.Type == PacketType.AgentUpdate || |
1276 | incomingPacket.Packet.Type == PacketType.ChatFromViewer) | 1626 | // incomingPacket.Packet.Type == PacketType.ChatFromViewer) |
1627 | if (incomingPacket.Packet.Type == PacketType.ChatFromViewer) | ||
1277 | packetInbox.EnqueueHigh(incomingPacket); | 1628 | packetInbox.EnqueueHigh(incomingPacket); |
1278 | else | 1629 | else |
1279 | packetInbox.EnqueueLow(incomingPacket); | 1630 | packetInbox.EnqueueLow(incomingPacket); |
1631 | |||
1280 | } | 1632 | } |
1281 | 1633 | ||
1282 | #region BinaryStats | 1634 | #region BinaryStats |
@@ -1393,7 +1745,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1393 | 1745 | ||
1394 | try | 1746 | try |
1395 | { | 1747 | { |
1396 | // DateTime startTime = DateTime.Now; | 1748 | // DateTime startTime = DateTime.Now; |
1397 | object[] array = (object[])o; | 1749 | object[] array = (object[])o; |
1398 | endPoint = (IPEndPoint)array[0]; | 1750 | endPoint = (IPEndPoint)array[0]; |
1399 | UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1]; | 1751 | UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1]; |
@@ -1413,20 +1765,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1413 | uccp.CircuitCode.SessionID, | 1765 | uccp.CircuitCode.SessionID, |
1414 | endPoint, | 1766 | endPoint, |
1415 | sessionInfo); | 1767 | sessionInfo); |
1416 | |||
1417 | // Send ack straight away to let the viewer know that the connection is active. | ||
1418 | // The client will be null if it already exists (e.g. if on a region crossing the client sends a use | ||
1419 | // circuit code to the existing child agent. This is not particularly obvious. | ||
1420 | SendAckImmediate(endPoint, uccp.Header.Sequence); | ||
1421 | |||
1422 | // We only want to send initial data to new clients, not ones which are being converted from child to root. | ||
1423 | if (client != null) | ||
1424 | client.SceneAgent.SendInitialDataToMe(); | ||
1425 | 1768 | ||
1426 | // Now we know we can handle more data | 1769 | // Now we know we can handle more data |
1427 | Thread.Sleep(200); | 1770 | Thread.Sleep(200); |
1428 | 1771 | ||
1429 | // Obtain the queue and remove it from the cache | 1772 | // Obtain the pending queue and remove it from the cache |
1430 | Queue<UDPPacketBuffer> queue = null; | 1773 | Queue<UDPPacketBuffer> queue = null; |
1431 | 1774 | ||
1432 | lock (m_pendingCache) | 1775 | lock (m_pendingCache) |
@@ -1435,6 +1778,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1435 | { | 1778 | { |
1436 | m_log.DebugFormat("[LLUDPSERVER]: Client created but no pending queue present"); | 1779 | m_log.DebugFormat("[LLUDPSERVER]: Client created but no pending queue present"); |
1437 | return; | 1780 | return; |
1781 | |||
1438 | } | 1782 | } |
1439 | m_pendingCache.Remove(endPoint); | 1783 | m_pendingCache.Remove(endPoint); |
1440 | } | 1784 | } |
@@ -1442,12 +1786,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1442 | m_log.DebugFormat("[LLUDPSERVER]: Client created, processing pending queue, {0} entries", queue.Count); | 1786 | m_log.DebugFormat("[LLUDPSERVER]: Client created, processing pending queue, {0} entries", queue.Count); |
1443 | 1787 | ||
1444 | // Reinject queued packets | 1788 | // Reinject queued packets |
1445 | while(queue.Count > 0) | 1789 | while (queue.Count > 0) |
1446 | { | 1790 | { |
1447 | UDPPacketBuffer buf = queue.Dequeue(); | 1791 | UDPPacketBuffer buf = queue.Dequeue(); |
1448 | PacketReceived(buf); | 1792 | PacketReceived(buf); |
1449 | } | 1793 | } |
1794 | |||
1450 | queue = null; | 1795 | queue = null; |
1796 | |||
1797 | // Send ack straight away to let the viewer know that the connection is active. | ||
1798 | // The client will be null if it already exists (e.g. if on a region crossing the client sends a use | ||
1799 | // circuit code to the existing child agent. This is not particularly obvious. | ||
1800 | SendAckImmediate(endPoint, uccp.Header.Sequence); | ||
1801 | |||
1802 | // We only want to send initial data to new clients, not ones which are being converted from child to root. | ||
1803 | if (client != null) | ||
1804 | { | ||
1805 | AgentCircuitData aCircuit = m_scene.AuthenticateHandler.GetAgentCircuitData(uccp.CircuitCode.Code); | ||
1806 | bool tp = (aCircuit.teleportFlags > 0); | ||
1807 | // Let's delay this for TP agents, otherwise the viewer doesn't know where to get resources from | ||
1808 | if (!tp) | ||
1809 | client.SceneAgent.SendInitialDataToMe(); | ||
1810 | } | ||
1451 | } | 1811 | } |
1452 | else | 1812 | else |
1453 | { | 1813 | { |
@@ -1455,10 +1815,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1455 | m_log.WarnFormat( | 1815 | m_log.WarnFormat( |
1456 | "[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}", | 1816 | "[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}", |
1457 | uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, endPoint); | 1817 | uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, endPoint); |
1818 | |||
1458 | lock (m_pendingCache) | 1819 | lock (m_pendingCache) |
1459 | m_pendingCache.Remove(endPoint); | 1820 | m_pendingCache.Remove(endPoint); |
1460 | } | 1821 | } |
1461 | |||
1462 | // m_log.DebugFormat( | 1822 | // m_log.DebugFormat( |
1463 | // "[LLUDPSERVER]: Handling UseCircuitCode request from {0} took {1}ms", | 1823 | // "[LLUDPSERVER]: Handling UseCircuitCode request from {0} took {1}ms", |
1464 | // buffer.RemoteEndPoint, (DateTime.Now - startTime).Milliseconds); | 1824 | // buffer.RemoteEndPoint, (DateTime.Now - startTime).Milliseconds); |
@@ -1475,6 +1835,117 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1475 | e.StackTrace); | 1835 | e.StackTrace); |
1476 | } | 1836 | } |
1477 | } | 1837 | } |
1838 | /* | ||
1839 | private void HandleCompleteMovementIntoRegion(object o) | ||
1840 | { | ||
1841 | IPEndPoint endPoint = null; | ||
1842 | IClientAPI client = null; | ||
1843 | |||
1844 | try | ||
1845 | { | ||
1846 | object[] array = (object[])o; | ||
1847 | endPoint = (IPEndPoint)array[0]; | ||
1848 | CompleteAgentMovementPacket packet = (CompleteAgentMovementPacket)array[1]; | ||
1849 | |||
1850 | m_log.DebugFormat( | ||
1851 | "[LLUDPSERVER]: Handling CompleteAgentMovement request from {0} in {1}", endPoint, m_scene.Name); | ||
1852 | |||
1853 | // Determine which agent this packet came from | ||
1854 | // We need to wait here because in when using the OpenSimulator V2 teleport protocol to travel to a destination | ||
1855 | // simulator with no existing child presence, the viewer (at least LL 3.3.4) will send UseCircuitCode | ||
1856 | // and then CompleteAgentMovement immediately without waiting for an ack. As we are now handling these | ||
1857 | // packets asynchronously, we need to account for this thread proceeding more quickly than the | ||
1858 | // UseCircuitCode thread. | ||
1859 | int count = 40; | ||
1860 | while (count-- > 0) | ||
1861 | { | ||
1862 | if (m_scene.TryGetClient(endPoint, out client)) | ||
1863 | { | ||
1864 | if (!client.IsActive) | ||
1865 | { | ||
1866 | // This check exists to catch a condition where the client has been closed by another thread | ||
1867 | // but has not yet been removed from the client manager (and possibly a new connection has | ||
1868 | // not yet been established). | ||
1869 | m_log.DebugFormat( | ||
1870 | "[LLUDPSERVER]: Received a CompleteAgentMovement from {0} for {1} in {2} but client is not active yet. Waiting.", | ||
1871 | endPoint, client.Name, m_scene.Name); | ||
1872 | } | ||
1873 | else if (client.SceneAgent == null) | ||
1874 | { | ||
1875 | // This check exists to catch a condition where the new client has been added to the client | ||
1876 | // manager but the SceneAgent has not yet been set in Scene.AddNewAgent(). If we are too | ||
1877 | // eager, then the new ScenePresence may not have registered a listener for this messsage | ||
1878 | // before we try to process it. | ||
1879 | // XXX: A better long term fix may be to add the SceneAgent before the client is added to | ||
1880 | // the client manager | ||
1881 | m_log.DebugFormat( | ||
1882 | "[LLUDPSERVER]: Received a CompleteAgentMovement from {0} for {1} in {2} but client SceneAgent not set yet. Waiting.", | ||
1883 | endPoint, client.Name, m_scene.Name); | ||
1884 | } | ||
1885 | else | ||
1886 | { | ||
1887 | break; | ||
1888 | } | ||
1889 | } | ||
1890 | else | ||
1891 | { | ||
1892 | m_log.DebugFormat( | ||
1893 | "[LLUDPSERVER]: Received a CompleteAgentMovement from {0} in {1} but no client exists yet. Waiting.", | ||
1894 | endPoint, m_scene.Name); | ||
1895 | } | ||
1896 | |||
1897 | Thread.Sleep(200); | ||
1898 | } | ||
1899 | |||
1900 | if (client == null) | ||
1901 | { | ||
1902 | m_log.DebugFormat( | ||
1903 | "[LLUDPSERVER]: No client found for CompleteAgentMovement from {0} in {1} after wait. Dropping.", | ||
1904 | endPoint, m_scene.Name); | ||
1905 | |||
1906 | return; | ||
1907 | } | ||
1908 | else if (!client.IsActive || client.SceneAgent == null) | ||
1909 | { | ||
1910 | // This check exists to catch a condition where the client has been closed by another thread | ||
1911 | // but has not yet been removed from the client manager. | ||
1912 | // The packet could be simply ignored but it is useful to know if this condition occurred for other debugging | ||
1913 | // purposes. | ||
1914 | m_log.DebugFormat( | ||
1915 | "[LLUDPSERVER]: Received a CompleteAgentMovement from {0} for {1} in {2} but client is not active after wait. Dropping.", | ||
1916 | endPoint, client.Name, m_scene.Name); | ||
1917 | |||
1918 | return; | ||
1919 | } | ||
1920 | |||
1921 | IncomingPacket incomingPacket1; | ||
1922 | |||
1923 | // Inbox insertion | ||
1924 | if (UsePools) | ||
1925 | { | ||
1926 | incomingPacket1 = m_incomingPacketPool.GetObject(); | ||
1927 | incomingPacket1.Client = (LLClientView)client; | ||
1928 | incomingPacket1.Packet = packet; | ||
1929 | } | ||
1930 | else | ||
1931 | { | ||
1932 | incomingPacket1 = new IncomingPacket((LLClientView)client, packet); | ||
1933 | } | ||
1934 | |||
1935 | packetInbox.Enqueue(incomingPacket1); | ||
1936 | } | ||
1937 | catch (Exception e) | ||
1938 | { | ||
1939 | m_log.ErrorFormat( | ||
1940 | "[LLUDPSERVER]: CompleteAgentMovement handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}", | ||
1941 | endPoint != null ? endPoint.ToString() : "n/a", | ||
1942 | client != null ? client.Name : "unknown", | ||
1943 | client != null ? client.AgentId.ToString() : "unknown", | ||
1944 | e.Message, | ||
1945 | e.StackTrace); | ||
1946 | } | ||
1947 | } | ||
1948 | */ | ||
1478 | 1949 | ||
1479 | /// <summary> | 1950 | /// <summary> |
1480 | /// Send an ack immediately to the given endpoint. | 1951 | /// Send an ack immediately to the given endpoint. |
@@ -1532,6 +2003,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1532 | uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo) | 2003 | uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo) |
1533 | { | 2004 | { |
1534 | IClientAPI client = null; | 2005 | IClientAPI client = null; |
2006 | bool createNew = false; | ||
1535 | 2007 | ||
1536 | // We currently synchronize this code across the whole scene to avoid issues such as | 2008 | // We currently synchronize this code across the whole scene to avoid issues such as |
1537 | // http://opensimulator.org/mantis/view.php?id=5365 However, once locking per agent circuit can be done | 2009 | // http://opensimulator.org/mantis/view.php?id=5365 However, once locking per agent circuit can be done |
@@ -1540,10 +2012,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1540 | { | 2012 | { |
1541 | if (!m_scene.TryGetClient(agentID, out client)) | 2013 | if (!m_scene.TryGetClient(agentID, out client)) |
1542 | { | 2014 | { |
2015 | createNew = true; | ||
2016 | } | ||
2017 | else | ||
2018 | { | ||
2019 | if (client.SceneAgent == null) | ||
2020 | { | ||
2021 | m_scene.CloseAgent(agentID, true); | ||
2022 | createNew = true; | ||
2023 | } | ||
2024 | } | ||
2025 | |||
2026 | if (createNew) | ||
2027 | { | ||
1543 | LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO); | 2028 | LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO); |
1544 | 2029 | ||
1545 | client = new LLClientView(m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); | 2030 | client = new LLClientView(m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); |
1546 | client.OnLogout += LogoutHandler; | 2031 | client.OnLogout += LogoutHandler; |
2032 | client.DebugPacketLevel = DefaultClientPacketDebugLevel; | ||
1547 | 2033 | ||
1548 | ((LLClientView)client).DisableFacelights = m_disableFacelights; | 2034 | ((LLClientView)client).DisableFacelights = m_disableFacelights; |
1549 | 2035 | ||
@@ -1562,21 +2048,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1562 | /// regular client pings. | 2048 | /// regular client pings. |
1563 | /// </remarks> | 2049 | /// </remarks> |
1564 | /// <param name='client'></param> | 2050 | /// <param name='client'></param> |
1565 | private void DeactivateClientDueToTimeout(LLClientView client) | 2051 | /// <param name='timeoutTicks'></param> |
2052 | private void DeactivateClientDueToTimeout(LLClientView client, int timeoutTicks) | ||
1566 | { | 2053 | { |
1567 | lock (client.CloseSyncLock) | 2054 | lock (client.CloseSyncLock) |
1568 | { | 2055 | { |
1569 | m_log.WarnFormat( | 2056 | ClientLogoutsDueToNoReceives++; |
1570 | "[LLUDPSERVER]: Ack timeout, disconnecting {0} agent for {1} in {2}", | 2057 | |
1571 | client.SceneAgent.IsChildAgent ? "child" : "root", client.Name, m_scene.RegionInfo.RegionName); | 2058 | if (client.SceneAgent != null) |
1572 | 2059 | { | |
1573 | StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); | 2060 | m_log.WarnFormat( |
1574 | 2061 | "[LLUDPSERVER]: No packets received from {0} agent of {1} for {2}ms in {3}. Disconnecting.", | |
1575 | if (!client.SceneAgent.IsChildAgent) | 2062 | client.SceneAgent.IsChildAgent ? "child" : "root", client.Name, timeoutTicks, m_scene.Name); |
1576 | client.Kick("Simulator logged you out due to connection timeout"); | ||
1577 | 2063 | ||
1578 | client.CloseWithoutChecks(true); | 2064 | if (!client.SceneAgent.IsChildAgent) |
2065 | client.Kick("Simulator logged you out due to connection timeout."); | ||
2066 | } | ||
1579 | } | 2067 | } |
2068 | |||
2069 | if (!m_scene.CloseAgent(client.AgentId, true)) | ||
2070 | client.Close(true,true); | ||
1580 | } | 2071 | } |
1581 | 2072 | ||
1582 | private void IncomingPacketHandler() | 2073 | private void IncomingPacketHandler() |
@@ -1592,6 +2083,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1592 | { | 2083 | { |
1593 | IncomingPacket incomingPacket = null; | 2084 | IncomingPacket incomingPacket = null; |
1594 | 2085 | ||
2086 | /* | ||
1595 | // HACK: This is a test to try and rate limit packet handling on Mono. | 2087 | // HACK: This is a test to try and rate limit packet handling on Mono. |
1596 | // If it works, a more elegant solution can be devised | 2088 | // If it works, a more elegant solution can be devised |
1597 | if (Util.FireAndForgetCount() < 2) | 2089 | if (Util.FireAndForgetCount() < 2) |
@@ -1599,6 +2091,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1599 | //m_log.Debug("[LLUDPSERVER]: Incoming packet handler is sleeping"); | 2091 | //m_log.Debug("[LLUDPSERVER]: Incoming packet handler is sleeping"); |
1600 | Thread.Sleep(30); | 2092 | Thread.Sleep(30); |
1601 | } | 2093 | } |
2094 | */ | ||
1602 | 2095 | ||
1603 | if (packetInbox.Dequeue(100, ref incomingPacket)) | 2096 | if (packetInbox.Dequeue(100, ref incomingPacket)) |
1604 | { | 2097 | { |
@@ -1608,7 +2101,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1608 | m_incomingPacketPool.ReturnObject(incomingPacket); | 2101 | m_incomingPacketPool.ReturnObject(incomingPacket); |
1609 | } | 2102 | } |
1610 | } | 2103 | } |
1611 | catch (Exception ex) | 2104 | catch(Exception ex) |
1612 | { | 2105 | { |
1613 | m_log.Error("[LLUDPSERVER]: Error in the incoming packet handler loop: " + ex.Message, ex); | 2106 | m_log.Error("[LLUDPSERVER]: Error in the incoming packet handler loop: " + ex.Message, ex); |
1614 | } | 2107 | } |
@@ -1694,9 +2187,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1694 | 2187 | ||
1695 | // If nothing was sent, sleep for the minimum amount of time before a | 2188 | // If nothing was sent, sleep for the minimum amount of time before a |
1696 | // token bucket could get more tokens | 2189 | // token bucket could get more tokens |
2190 | |||
1697 | if (!m_packetSent) | 2191 | if (!m_packetSent) |
1698 | Thread.Sleep((int)TickCountResolution); | 2192 | Thread.Sleep((int)TickCountResolution); |
1699 | 2193 | ||
2194 | // .... wrong core code removed | ||
2195 | |||
2196 | |||
1700 | Watchdog.UpdateThread(); | 2197 | Watchdog.UpdateThread(); |
1701 | } | 2198 | } |
1702 | catch (Exception ex) | 2199 | catch (Exception ex) |
@@ -1912,7 +2409,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1912 | if (!client.IsLoggingOut) | 2409 | if (!client.IsLoggingOut) |
1913 | { | 2410 | { |
1914 | client.IsLoggingOut = true; | 2411 | client.IsLoggingOut = true; |
1915 | client.Close(false, false); | 2412 | m_scene.CloseAgent(client.AgentId, false); |
1916 | } | 2413 | } |
1917 | } | 2414 | } |
1918 | } | 2415 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs index 7035e38..0030dee 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs | |||
@@ -78,6 +78,36 @@ namespace OpenMetaverse | |||
78 | public bool IsRunningOutbound { get; private set; } | 78 | public bool IsRunningOutbound { get; private set; } |
79 | 79 | ||
80 | /// <summary> | 80 | /// <summary> |
81 | /// Number of UDP receives. | ||
82 | /// </summary> | ||
83 | public int UdpReceives { get; private set; } | ||
84 | |||
85 | /// <summary> | ||
86 | /// Number of UDP sends | ||
87 | /// </summary> | ||
88 | public int UdpSends { get; private set; } | ||
89 | |||
90 | /// <summary> | ||
91 | /// Number of receives over which to establish a receive time average. | ||
92 | /// </summary> | ||
93 | private readonly static int s_receiveTimeSamples = 500; | ||
94 | |||
95 | /// <summary> | ||
96 | /// Current number of samples taken to establish a receive time average. | ||
97 | /// </summary> | ||
98 | private int m_currentReceiveTimeSamples; | ||
99 | |||
100 | /// <summary> | ||
101 | /// Cumulative receive time for the sample so far. | ||
102 | /// </summary> | ||
103 | private int m_receiveTicksInCurrentSamplePeriod; | ||
104 | |||
105 | /// <summary> | ||
106 | /// The average time taken for each require receive in the last sample. | ||
107 | /// </summary> | ||
108 | public float AverageReceiveTicksForLastSamplePeriod { get; private set; } | ||
109 | |||
110 | /// <summary> | ||
81 | /// Default constructor | 111 | /// Default constructor |
82 | /// </summary> | 112 | /// </summary> |
83 | /// <param name="bindAddress">Local IP address to bind the server to</param> | 113 | /// <param name="bindAddress">Local IP address to bind the server to</param> |
@@ -111,6 +141,8 @@ namespace OpenMetaverse | |||
111 | 141 | ||
112 | if (!IsRunningInbound) | 142 | if (!IsRunningInbound) |
113 | { | 143 | { |
144 | m_log.DebugFormat("[UDPBASE]: Starting inbound UDP loop"); | ||
145 | |||
114 | const int SIO_UDP_CONNRESET = -1744830452; | 146 | const int SIO_UDP_CONNRESET = -1744830452; |
115 | 147 | ||
116 | IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); | 148 | IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); |
@@ -120,6 +152,10 @@ namespace OpenMetaverse | |||
120 | SocketType.Dgram, | 152 | SocketType.Dgram, |
121 | ProtocolType.Udp); | 153 | ProtocolType.Udp); |
122 | 154 | ||
155 | // OpenSim may need this but in AVN, this messes up automated | ||
156 | // sim restarts badly | ||
157 | //m_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false); | ||
158 | |||
123 | try | 159 | try |
124 | { | 160 | { |
125 | // This udp socket flag is not supported under mono, | 161 | // This udp socket flag is not supported under mono, |
@@ -151,6 +187,8 @@ namespace OpenMetaverse | |||
151 | /// </summary> | 187 | /// </summary> |
152 | public void StartOutbound() | 188 | public void StartOutbound() |
153 | { | 189 | { |
190 | m_log.DebugFormat("[UDPBASE]: Starting outbound UDP loop"); | ||
191 | |||
154 | IsRunningOutbound = true; | 192 | IsRunningOutbound = true; |
155 | } | 193 | } |
156 | 194 | ||
@@ -158,10 +196,8 @@ namespace OpenMetaverse | |||
158 | { | 196 | { |
159 | if (IsRunningInbound) | 197 | if (IsRunningInbound) |
160 | { | 198 | { |
161 | // wait indefinitely for a writer lock. Once this is called, the .NET runtime | 199 | m_log.DebugFormat("[UDPBASE]: Stopping inbound UDP loop"); |
162 | // will deny any more reader locks, in effect blocking all other send/receive | 200 | |
163 | // threads. Once we have the lock, we set IsRunningInbound = false to inform the other | ||
164 | // threads that the socket is closed. | ||
165 | IsRunningInbound = false; | 201 | IsRunningInbound = false; |
166 | m_udpSocket.Close(); | 202 | m_udpSocket.Close(); |
167 | } | 203 | } |
@@ -169,6 +205,8 @@ namespace OpenMetaverse | |||
169 | 205 | ||
170 | public void StopOutbound() | 206 | public void StopOutbound() |
171 | { | 207 | { |
208 | m_log.DebugFormat("[UDPBASE]: Stopping outbound UDP loop"); | ||
209 | |||
172 | IsRunningOutbound = false; | 210 | IsRunningOutbound = false; |
173 | } | 211 | } |
174 | 212 | ||
@@ -257,7 +295,16 @@ namespace OpenMetaverse | |||
257 | m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); | 295 | m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); |
258 | } | 296 | } |
259 | } | 297 | } |
260 | catch (ObjectDisposedException) { } | 298 | catch (ObjectDisposedException e) |
299 | { | ||
300 | m_log.Error( | ||
301 | string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); | ||
302 | } | ||
303 | catch (Exception e) | ||
304 | { | ||
305 | m_log.Error( | ||
306 | string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); | ||
307 | } | ||
261 | } | 308 | } |
262 | } | 309 | } |
263 | 310 | ||
@@ -267,17 +314,21 @@ namespace OpenMetaverse | |||
267 | // to AsyncBeginReceive | 314 | // to AsyncBeginReceive |
268 | if (IsRunningInbound) | 315 | if (IsRunningInbound) |
269 | { | 316 | { |
317 | UdpReceives++; | ||
318 | |||
270 | // Asynchronous mode will start another receive before the | 319 | // Asynchronous mode will start another receive before the |
271 | // callback for this packet is even fired. Very parallel :-) | 320 | // callback for this packet is even fired. Very parallel :-) |
272 | if (m_asyncPacketHandling) | 321 | if (m_asyncPacketHandling) |
273 | AsyncBeginReceive(); | 322 | AsyncBeginReceive(); |
274 | 323 | ||
275 | // get the buffer that was created in AsyncBeginReceive | ||
276 | // this is the received data | ||
277 | UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; | ||
278 | |||
279 | try | 324 | try |
280 | { | 325 | { |
326 | // get the buffer that was created in AsyncBeginReceive | ||
327 | // this is the received data | ||
328 | UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; | ||
329 | |||
330 | int startTick = Util.EnvironmentTickCount(); | ||
331 | |||
281 | // get the length of data actually read from the socket, store it with the | 332 | // get the length of data actually read from the socket, store it with the |
282 | // buffer | 333 | // buffer |
283 | buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); | 334 | buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); |
@@ -285,9 +336,42 @@ namespace OpenMetaverse | |||
285 | // call the abstract method PacketReceived(), passing the buffer that | 336 | // call the abstract method PacketReceived(), passing the buffer that |
286 | // has just been filled from the socket read. | 337 | // has just been filled from the socket read. |
287 | PacketReceived(buffer); | 338 | PacketReceived(buffer); |
339 | |||
340 | // If more than one thread can be calling AsyncEndReceive() at once (e.g. if m_asyncPacketHandler) | ||
341 | // then a particular stat may be inaccurate due to a race condition. We won't worry about this | ||
342 | // since this should be rare and won't cause a runtime problem. | ||
343 | if (m_currentReceiveTimeSamples >= s_receiveTimeSamples) | ||
344 | { | ||
345 | AverageReceiveTicksForLastSamplePeriod | ||
346 | = (float)m_receiveTicksInCurrentSamplePeriod / s_receiveTimeSamples; | ||
347 | |||
348 | m_receiveTicksInCurrentSamplePeriod = 0; | ||
349 | m_currentReceiveTimeSamples = 0; | ||
350 | } | ||
351 | else | ||
352 | { | ||
353 | m_receiveTicksInCurrentSamplePeriod += Util.EnvironmentTickCountSubtract(startTick); | ||
354 | m_currentReceiveTimeSamples++; | ||
355 | } | ||
356 | } | ||
357 | catch (SocketException se) | ||
358 | { | ||
359 | m_log.Error( | ||
360 | string.Format( | ||
361 | "[UDPBASE]: Error processing UDP end receive {0}, socket error code {1}. Exception ", | ||
362 | UdpReceives, se.ErrorCode), | ||
363 | se); | ||
364 | } | ||
365 | catch (ObjectDisposedException e) | ||
366 | { | ||
367 | m_log.Error( | ||
368 | string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e); | ||
369 | } | ||
370 | catch (Exception e) | ||
371 | { | ||
372 | m_log.Error( | ||
373 | string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e); | ||
288 | } | 374 | } |
289 | catch (SocketException) { } | ||
290 | catch (ObjectDisposedException) { } | ||
291 | finally | 375 | finally |
292 | { | 376 | { |
293 | // if (UsePools) | 377 | // if (UsePools) |
@@ -298,14 +382,13 @@ namespace OpenMetaverse | |||
298 | if (!m_asyncPacketHandling) | 382 | if (!m_asyncPacketHandling) |
299 | AsyncBeginReceive(); | 383 | AsyncBeginReceive(); |
300 | } | 384 | } |
301 | |||
302 | } | 385 | } |
303 | } | 386 | } |
304 | 387 | ||
305 | public void AsyncBeginSend(UDPPacketBuffer buf) | 388 | public void AsyncBeginSend(UDPPacketBuffer buf) |
306 | { | 389 | { |
307 | if (IsRunningOutbound) | 390 | // if (IsRunningOutbound) |
308 | { | 391 | // { |
309 | try | 392 | try |
310 | { | 393 | { |
311 | m_udpSocket.BeginSendTo( | 394 | m_udpSocket.BeginSendTo( |
@@ -319,7 +402,7 @@ namespace OpenMetaverse | |||
319 | } | 402 | } |
320 | catch (SocketException) { } | 403 | catch (SocketException) { } |
321 | catch (ObjectDisposedException) { } | 404 | catch (ObjectDisposedException) { } |
322 | } | 405 | // } |
323 | } | 406 | } |
324 | 407 | ||
325 | void AsyncEndSend(IAsyncResult result) | 408 | void AsyncEndSend(IAsyncResult result) |
@@ -328,9 +411,11 @@ namespace OpenMetaverse | |||
328 | { | 411 | { |
329 | // UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; | 412 | // UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; |
330 | m_udpSocket.EndSendTo(result); | 413 | m_udpSocket.EndSendTo(result); |
414 | |||
415 | UdpSends++; | ||
331 | } | 416 | } |
332 | catch (SocketException) { } | 417 | catch (SocketException) { } |
333 | catch (ObjectDisposedException) { } | 418 | catch (ObjectDisposedException) { } |
334 | } | 419 | } |
335 | } | 420 | } |
336 | } \ No newline at end of file | 421 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs b/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs index 1fdc410..5a2bcee 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs | |||
@@ -145,39 +145,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
145 | return packet; | 145 | return packet; |
146 | } | 146 | } |
147 | 147 | ||
148 | // private byte[] decoded_header = new byte[10]; | ||
149 | private static PacketType GetType(byte[] bytes) | 148 | private static PacketType GetType(byte[] bytes) |
150 | { | 149 | { |
151 | byte[] decoded_header = new byte[10 + 8]; | ||
152 | ushort id; | 150 | ushort id; |
153 | PacketFrequency freq; | 151 | PacketFrequency freq; |
152 | bool isZeroCoded = (bytes[0] & Helpers.MSG_ZEROCODED) != 0; | ||
154 | 153 | ||
155 | if ((bytes[0] & Helpers.MSG_ZEROCODED) != 0) | 154 | if (bytes[6] == 0xFF) |
156 | { | 155 | { |
157 | Helpers.ZeroDecode(bytes, 16, decoded_header); | 156 | if (bytes[7] == 0xFF) |
158 | } | ||
159 | else | ||
160 | { | ||
161 | Buffer.BlockCopy(bytes, 0, decoded_header, 0, 10); | ||
162 | } | ||
163 | |||
164 | if (decoded_header[6] == 0xFF) | ||
165 | { | ||
166 | if (decoded_header[7] == 0xFF) | ||
167 | { | 157 | { |
168 | id = (ushort) ((decoded_header[8] << 8) + decoded_header[9]); | ||
169 | freq = PacketFrequency.Low; | 158 | freq = PacketFrequency.Low; |
159 | if (isZeroCoded && bytes[8] == 0) | ||
160 | id = bytes[10]; | ||
161 | else | ||
162 | id = (ushort)((bytes[8] << 8) + bytes[9]); | ||
170 | } | 163 | } |
171 | else | 164 | else |
172 | { | 165 | { |
173 | id = decoded_header[7]; | ||
174 | freq = PacketFrequency.Medium; | 166 | freq = PacketFrequency.Medium; |
167 | id = bytes[7]; | ||
175 | } | 168 | } |
176 | } | 169 | } |
177 | else | 170 | else |
178 | { | 171 | { |
179 | id = decoded_header[6]; | ||
180 | freq = PacketFrequency.High; | 172 | freq = PacketFrequency.High; |
173 | id = bytes[6]; | ||
181 | } | 174 | } |
182 | 175 | ||
183 | return Packet.GetType(id, freq); | 176 | return Packet.GetType(id, freq); |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Properties/AssemblyInfo.cs b/OpenSim/Region/ClientStack/Linden/UDP/Properties/AssemblyInfo.cs index 98ef72f..f8d0c02 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Properties/AssemblyInfo.cs | |||
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices; | |||
29 | // Build Number | 29 | // Build Number |
30 | // Revision | 30 | // Revision |
31 | // | 31 | // |
32 | [assembly: AssemblyVersion("0.7.6.*")] | 32 | [assembly: AssemblyVersion("0.8.0.*")] |
33 | 33 | ||
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs index 556df30..9700224 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs | |||
@@ -33,6 +33,7 @@ using NUnit.Framework; | |||
33 | using OpenMetaverse; | 33 | using OpenMetaverse; |
34 | using OpenMetaverse.Packets; | 34 | using OpenMetaverse.Packets; |
35 | using OpenSim.Framework; | 35 | using OpenSim.Framework; |
36 | using OpenSim.Framework.Monitoring; | ||
36 | using OpenSim.Region.Framework.Scenes; | 37 | using OpenSim.Region.Framework.Scenes; |
37 | using OpenSim.Tests.Common; | 38 | using OpenSim.Tests.Common; |
38 | using OpenSim.Tests.Common.Mock; | 39 | using OpenSim.Tests.Common.Mock; |
@@ -69,6 +70,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
69 | { | 70 | { |
70 | base.SetUp(); | 71 | base.SetUp(); |
71 | m_scene = new SceneHelpers().SetupScene(); | 72 | m_scene = new SceneHelpers().SetupScene(); |
73 | StatsManager.SimExtraStats = new SimExtraStatsCollector(); | ||
72 | } | 74 | } |
73 | 75 | ||
74 | /// <summary> | 76 | /// <summary> |
@@ -198,7 +200,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
198 | public void TestLogoutClientDueToAck() | 200 | public void TestLogoutClientDueToAck() |
199 | { | 201 | { |
200 | TestHelpers.InMethod(); | 202 | TestHelpers.InMethod(); |
201 | // TestHelpers.EnableLogging(); | 203 | TestHelpers.EnableLogging(); |
202 | 204 | ||
203 | IniConfigSource ics = new IniConfigSource(); | 205 | IniConfigSource ics = new IniConfigSource(); |
204 | IConfig config = ics.AddConfig("ClientStack.LindenUDP"); | 206 | IConfig config = ics.AddConfig("ClientStack.LindenUDP"); |
@@ -210,8 +212,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
210 | 212 | ||
211 | ScenePresence spAfterAckTimeout = m_scene.GetScenePresence(sp.UUID); | 213 | ScenePresence spAfterAckTimeout = m_scene.GetScenePresence(sp.UUID); |
212 | Assert.That(spAfterAckTimeout, Is.Null); | 214 | Assert.That(spAfterAckTimeout, Is.Null); |
213 | |||
214 | // TestHelpers.DisableLogging(); | ||
215 | } | 215 | } |
216 | 216 | ||
217 | // /// <summary> | 217 | // /// <summary> |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs index 7d9f581..575e54c 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs | |||
@@ -29,6 +29,7 @@ using System; | |||
29 | using System.IO; | 29 | using System.IO; |
30 | using System.Net; | 30 | using System.Net; |
31 | using System.Reflection; | 31 | using System.Reflection; |
32 | using System.Threading; | ||
32 | using log4net.Config; | 33 | using log4net.Config; |
33 | using Nini.Config; | 34 | using Nini.Config; |
34 | using NUnit.Framework; | 35 | using NUnit.Framework; |
@@ -53,6 +54,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
53 | [TestFixtureSetUp] | 54 | [TestFixtureSetUp] |
54 | public void FixtureInit() | 55 | public void FixtureInit() |
55 | { | 56 | { |
57 | // Don't allow tests to be bamboozled by asynchronous events. Execute everything on the same thread. | ||
58 | Util.FireAndForgetMethod = FireAndForgetMethod.None; | ||
59 | |||
56 | using ( | 60 | using ( |
57 | Stream resource | 61 | Stream resource |
58 | = GetType().Assembly.GetManifestResourceStream( | 62 | = GetType().Assembly.GetManifestResourceStream( |
@@ -72,9 +76,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
72 | } | 76 | } |
73 | } | 77 | } |
74 | 78 | ||
79 | [TestFixtureTearDown] | ||
80 | public void TearDown() | ||
81 | { | ||
82 | // We must set this back afterwards, otherwise later tests will fail since they're expecting multiple | ||
83 | // threads. Possibly, later tests should be rewritten not to worry about such things. | ||
84 | Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod; | ||
85 | } | ||
86 | |||
75 | [SetUp] | 87 | [SetUp] |
76 | public void SetUp() | 88 | public override void SetUp() |
77 | { | 89 | { |
90 | base.SetUp(); | ||
91 | |||
78 | UUID userId = TestHelpers.ParseTail(0x3); | 92 | UUID userId = TestHelpers.ParseTail(0x3); |
79 | 93 | ||
80 | J2KDecoderModule j2kdm = new J2KDecoderModule(); | 94 | J2KDecoderModule j2kdm = new J2KDecoderModule(); |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs index 119a677..e2178e5 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs | |||
@@ -52,17 +52,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
52 | public override void Update(int frames) {} | 52 | public override void Update(int frames) {} |
53 | public override void LoadWorldMap() {} | 53 | public override void LoadWorldMap() {} |
54 | 54 | ||
55 | public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type) | 55 | public override ISceneAgent AddNewAgent(IClientAPI client, PresenceType type) |
56 | { | 56 | { |
57 | client.OnObjectName += RecordObjectNameCall; | 57 | client.OnObjectName += RecordObjectNameCall; |
58 | 58 | ||
59 | // FIXME | 59 | // FIXME |
60 | return null; | 60 | return null; |
61 | } | 61 | } |
62 | 62 | ||
63 | public override void RemoveClient(UUID agentID, bool someReason) {} | 63 | public override bool CloseAgent(UUID agentID, bool force) { return true; } |
64 | // public override void CloseAllAgents(uint circuitcode) {} | 64 | |
65 | public override bool CheckClient(UUID clientId, IPEndPoint endPoint) { return true; } | 65 | public override bool CheckClient(UUID clientId, IPEndPoint endPoint) { return true; } |
66 | |||
66 | public override void OtherRegionUp(GridRegion otherRegion) { } | 67 | public override void OtherRegionUp(GridRegion otherRegion) { } |
67 | 68 | ||
68 | public override bool TryGetScenePresence(UUID uuid, out ScenePresence sp) { sp = null; return false; } | 69 | public override bool TryGetScenePresence(UUID uuid, out ScenePresence sp) { sp = null; return false; } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs index c9aac0b..451dee5 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs | |||
@@ -59,6 +59,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
59 | /// <summary>Flag used to enable adaptive throttles</summary> | 59 | /// <summary>Flag used to enable adaptive throttles</summary> |
60 | public bool AdaptiveThrottlesEnabled; | 60 | public bool AdaptiveThrottlesEnabled; |
61 | 61 | ||
62 | /// <summary>Amount of the texture throttle to steal for the task throttle</summary> | ||
63 | public double CannibalizeTextureRate; | ||
64 | |||
65 | public int ClientMaxRate; | ||
66 | public float BrustTime; | ||
67 | |||
62 | /// <summary> | 68 | /// <summary> |
63 | /// Default constructor | 69 | /// Default constructor |
64 | /// </summary> | 70 | /// </summary> |
@@ -77,9 +83,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
77 | Texture = throttleConfig.GetInt("texture_default", 18500); | 83 | Texture = throttleConfig.GetInt("texture_default", 18500); |
78 | Asset = throttleConfig.GetInt("asset_default", 10500); | 84 | Asset = throttleConfig.GetInt("asset_default", 10500); |
79 | 85 | ||
80 | Total = throttleConfig.GetInt("client_throttle_max_bps", 0); | 86 | Total = Resend + Land + Wind + Cloud + Task + Texture + Asset; |
87 | // 3000000 bps default max | ||
88 | ClientMaxRate = throttleConfig.GetInt("client_throttle_max_bps", 375000); | ||
89 | if (ClientMaxRate > 1000000) | ||
90 | ClientMaxRate = 1000000; // no more than 8Mbps | ||
91 | |||
92 | BrustTime = (float)throttleConfig.GetInt("client_throttle_burtsTimeMS", 10); | ||
93 | BrustTime *= 1e-3f; | ||
81 | 94 | ||
82 | AdaptiveThrottlesEnabled = throttleConfig.GetBoolean("enable_adaptive_throttles", false); | 95 | AdaptiveThrottlesEnabled = throttleConfig.GetBoolean("enable_adaptive_throttles", false); |
96 | |||
97 | CannibalizeTextureRate = (double)throttleConfig.GetFloat("CannibalizeTextureRate", 0.0f); | ||
98 | CannibalizeTextureRate = Util.Clamp<double>(CannibalizeTextureRate,0.0, 0.9); | ||
83 | } | 99 | } |
84 | catch (Exception) { } | 100 | catch (Exception) { } |
85 | } | 101 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs index 4c33db5..1877cf0 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs | |||
@@ -44,23 +44,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
44 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 44 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
45 | private static Int32 m_counter = 0; | 45 | private static Int32 m_counter = 0; |
46 | 46 | ||
47 | // private Int32 m_identifier; | 47 | // private Int32 m_identifier; |
48 | 48 | ||
49 | /// <summary> | 49 | protected const float m_timeScale = 1e-3f; |
50 | /// Number of ticks (ms) per quantum, drip rate and max burst | ||
51 | /// are defined over this interval. | ||
52 | /// </summary> | ||
53 | protected const Int32 m_ticksPerQuantum = 1000; | ||
54 | 50 | ||
55 | /// <summary> | 51 | /// <summary> |
56 | /// This is the number of quantums worth of packets that can | 52 | /// This is the number of m_minimumDripRate bytes |
57 | /// be accommodated during a burst | 53 | /// allowed in a burst |
54 | /// roughtly, with this settings, the maximum time system will take | ||
55 | /// to recheck a bucket in ms | ||
56 | /// | ||
58 | /// </summary> | 57 | /// </summary> |
59 | protected const Double m_quantumsPerBurst = 1.5; | 58 | protected const float m_quantumsPerBurst = 5; |
60 | 59 | ||
61 | /// <summary> | 60 | /// <summary> |
62 | /// </summary> | 61 | /// </summary> |
63 | protected const Int32 m_minimumDripRate = 1400; | 62 | protected const float m_minimumDripRate = 1400; |
64 | 63 | ||
65 | /// <summary>Time of the last drip, in system ticks</summary> | 64 | /// <summary>Time of the last drip, in system ticks</summary> |
66 | protected Int32 m_lastDrip; | 65 | protected Int32 m_lastDrip; |
@@ -69,12 +68,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
69 | /// The number of bytes that can be sent at this moment. This is the | 68 | /// The number of bytes that can be sent at this moment. This is the |
70 | /// current number of tokens in the bucket | 69 | /// current number of tokens in the bucket |
71 | /// </summary> | 70 | /// </summary> |
72 | protected Int64 m_tokenCount; | 71 | protected float m_tokenCount; |
73 | 72 | ||
74 | /// <summary> | 73 | /// <summary> |
75 | /// Map of children buckets and their requested maximum burst rate | 74 | /// Map of children buckets and their requested maximum burst rate |
76 | /// </summary> | 75 | /// </summary> |
77 | protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>(); | 76 | protected Dictionary<TokenBucket, float> m_children = new Dictionary<TokenBucket, float>(); |
78 | 77 | ||
79 | #region Properties | 78 | #region Properties |
80 | 79 | ||
@@ -91,25 +90,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
91 | } | 90 | } |
92 | 91 | ||
93 | /// <summary> | 92 | /// <summary> |
94 | /// Maximum burst rate in bytes per second. This is the maximum number | 93 | /// This is the maximum number |
95 | /// of tokens that can accumulate in the bucket at any one time. This | 94 | /// of tokens that can accumulate in the bucket at any one time. This |
96 | /// also sets the total request for leaf nodes | 95 | /// also sets the total request for leaf nodes |
97 | /// </summary> | 96 | /// </summary> |
98 | protected Int64 m_burstRate; | 97 | protected float m_burst; |
99 | public Int64 RequestedBurstRate | 98 | public float RequestedBurst |
100 | { | 99 | { |
101 | get { return m_burstRate; } | 100 | get { return m_burst; } |
102 | set { m_burstRate = (value < 0 ? 0 : value); } | 101 | set { |
102 | float rate = (value < 0 ? 0 : value); | ||
103 | if (rate < m_minimumDripRate) | ||
104 | rate = m_minimumDripRate; | ||
105 | else if (rate > m_minimumDripRate * m_quantumsPerBurst) | ||
106 | rate = m_minimumDripRate * m_quantumsPerBurst; | ||
107 | |||
108 | m_burst = rate; | ||
109 | } | ||
103 | } | 110 | } |
104 | 111 | ||
105 | public Int64 BurstRate | 112 | public float Burst |
106 | { | 113 | { |
107 | get { | 114 | get { |
108 | double rate = RequestedBurstRate * BurstRateModifier(); | 115 | float rate = RequestedBurst * BurstModifier(); |
109 | if (rate < m_minimumDripRate * m_quantumsPerBurst) | 116 | if (rate < m_minimumDripRate) |
110 | rate = m_minimumDripRate * m_quantumsPerBurst; | 117 | rate = m_minimumDripRate; |
111 | 118 | return (float)rate; | |
112 | return (Int64) rate; | ||
113 | } | 119 | } |
114 | } | 120 | } |
115 | 121 | ||
@@ -120,30 +126,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
120 | /// <remarks>Tokens are added to the bucket any time | 126 | /// <remarks>Tokens are added to the bucket any time |
121 | /// <seealso cref="RemoveTokens"/> is called, at the granularity of | 127 | /// <seealso cref="RemoveTokens"/> is called, at the granularity of |
122 | /// the system tick interval (typically around 15-22ms)</remarks> | 128 | /// the system tick interval (typically around 15-22ms)</remarks> |
123 | protected Int64 m_dripRate; | 129 | protected float m_dripRate; |
124 | public virtual Int64 RequestedDripRate | 130 | public virtual float RequestedDripRate |
125 | { | 131 | { |
126 | get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } | 132 | get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } |
127 | set { | 133 | set { |
128 | m_dripRate = (value < 0 ? 0 : value); | 134 | m_dripRate = (value < 0 ? 0 : value); |
129 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); | ||
130 | m_totalDripRequest = m_dripRate; | 135 | m_totalDripRequest = m_dripRate; |
136 | |||
131 | if (m_parent != null) | 137 | if (m_parent != null) |
132 | m_parent.RegisterRequest(this,m_dripRate); | 138 | m_parent.RegisterRequest(this,m_dripRate); |
133 | } | 139 | } |
134 | } | 140 | } |
135 | 141 | ||
136 | public virtual Int64 DripRate | 142 | public virtual float DripRate |
137 | { | 143 | { |
138 | get { | 144 | get { |
145 | float rate = Math.Min(RequestedDripRate,TotalDripRequest); | ||
139 | if (m_parent == null) | 146 | if (m_parent == null) |
140 | return Math.Min(RequestedDripRate,TotalDripRequest); | 147 | return rate; |
141 | 148 | ||
142 | double rate = (double)RequestedDripRate * m_parent.DripRateModifier(); | 149 | rate *= m_parent.DripRateModifier(); |
143 | if (rate < m_minimumDripRate) | 150 | if (rate < m_minimumDripRate) |
144 | rate = m_minimumDripRate; | 151 | rate = m_minimumDripRate; |
145 | 152 | ||
146 | return (Int64)rate; | 153 | return (float)rate; |
147 | } | 154 | } |
148 | } | 155 | } |
149 | 156 | ||
@@ -151,8 +158,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
151 | /// The current total of the requested maximum burst rates of | 158 | /// The current total of the requested maximum burst rates of |
152 | /// this bucket's children buckets. | 159 | /// this bucket's children buckets. |
153 | /// </summary> | 160 | /// </summary> |
154 | protected Int64 m_totalDripRequest; | 161 | protected float m_totalDripRequest; |
155 | public Int64 TotalDripRequest | 162 | public float TotalDripRequest |
156 | { | 163 | { |
157 | get { return m_totalDripRequest; } | 164 | get { return m_totalDripRequest; } |
158 | set { m_totalDripRequest = value; } | 165 | set { m_totalDripRequest = value; } |
@@ -171,16 +178,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
171 | /// zero if this bucket has no maximum capacity</param> | 178 | /// zero if this bucket has no maximum capacity</param> |
172 | /// <param name="dripRate">Rate that the bucket fills, in bytes per | 179 | /// <param name="dripRate">Rate that the bucket fills, in bytes per |
173 | /// second. If zero, the bucket always remains full</param> | 180 | /// second. If zero, the bucket always remains full</param> |
174 | public TokenBucket(TokenBucket parent, Int64 dripRate) | 181 | public TokenBucket(TokenBucket parent, float dripRate, float MaxBurst) |
175 | { | 182 | { |
176 | // m_identifier = m_counter++; | 183 | // m_identifier = m_counter++; |
177 | m_counter++; | 184 | m_counter++; |
178 | 185 | ||
179 | Parent = parent; | 186 | Parent = parent; |
180 | RequestedDripRate = dripRate; | 187 | RequestedDripRate = dripRate; |
188 | RequestedBurst = MaxBurst; | ||
181 | // TotalDripRequest = dripRate; // this will be overwritten when a child node registers | 189 | // TotalDripRequest = dripRate; // this will be overwritten when a child node registers |
182 | // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); | 190 | // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); |
183 | m_lastDrip = Util.EnvironmentTickCount(); | 191 | m_lastDrip = Util.EnvironmentTickCount() + 100000; |
184 | } | 192 | } |
185 | 193 | ||
186 | #endregion Constructor | 194 | #endregion Constructor |
@@ -192,15 +200,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
192 | /// hierarchy. However, if any of the parents is over-booked, then | 200 | /// hierarchy. However, if any of the parents is over-booked, then |
193 | /// the modifier will be less than 1. | 201 | /// the modifier will be less than 1. |
194 | /// </summary> | 202 | /// </summary> |
195 | protected double DripRateModifier() | 203 | protected float DripRateModifier() |
196 | { | 204 | { |
197 | Int64 driprate = DripRate; | 205 | float driprate = DripRate; |
198 | return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; | 206 | return driprate >= TotalDripRequest ? 1.0f : driprate / TotalDripRequest; |
199 | } | 207 | } |
200 | 208 | ||
201 | /// <summary> | 209 | /// <summary> |
202 | /// </summary> | 210 | /// </summary> |
203 | protected double BurstRateModifier() | 211 | protected float BurstModifier() |
204 | { | 212 | { |
205 | // for now... burst rate is always m_quantumsPerBurst (constant) | 213 | // for now... burst rate is always m_quantumsPerBurst (constant) |
206 | // larger than drip rate so the ratio of burst requests is the | 214 | // larger than drip rate so the ratio of burst requests is the |
@@ -212,7 +220,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
212 | /// Register drip rate requested by a child of this throttle. Pass the | 220 | /// Register drip rate requested by a child of this throttle. Pass the |
213 | /// changes up the hierarchy. | 221 | /// changes up the hierarchy. |
214 | /// </summary> | 222 | /// </summary> |
215 | public void RegisterRequest(TokenBucket child, Int64 request) | 223 | public void RegisterRequest(TokenBucket child, float request) |
216 | { | 224 | { |
217 | lock (m_children) | 225 | lock (m_children) |
218 | { | 226 | { |
@@ -220,7 +228,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
220 | // m_totalDripRequest = m_children.Values.Sum(); | 228 | // m_totalDripRequest = m_children.Values.Sum(); |
221 | 229 | ||
222 | m_totalDripRequest = 0; | 230 | m_totalDripRequest = 0; |
223 | foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) | 231 | foreach (KeyValuePair<TokenBucket, float> cref in m_children) |
224 | m_totalDripRequest += cref.Value; | 232 | m_totalDripRequest += cref.Value; |
225 | } | 233 | } |
226 | 234 | ||
@@ -241,7 +249,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
241 | // m_totalDripRequest = m_children.Values.Sum(); | 249 | // m_totalDripRequest = m_children.Values.Sum(); |
242 | 250 | ||
243 | m_totalDripRequest = 0; | 251 | m_totalDripRequest = 0; |
244 | foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) | 252 | foreach (KeyValuePair<TokenBucket, float> cref in m_children) |
245 | m_totalDripRequest += cref.Value; | 253 | m_totalDripRequest += cref.Value; |
246 | } | 254 | } |
247 | 255 | ||
@@ -257,7 +265,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
257 | /// <param name="amount">Number of tokens to remove from the bucket</param> | 265 | /// <param name="amount">Number of tokens to remove from the bucket</param> |
258 | /// <returns>True if the requested number of tokens were removed from | 266 | /// <returns>True if the requested number of tokens were removed from |
259 | /// the bucket, otherwise false</returns> | 267 | /// the bucket, otherwise false</returns> |
260 | public bool RemoveTokens(Int64 amount) | 268 | public bool RemoveTokens(int amount) |
261 | { | 269 | { |
262 | // Deposit tokens for this interval | 270 | // Deposit tokens for this interval |
263 | Drip(); | 271 | Drip(); |
@@ -274,19 +282,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
274 | return false; | 282 | return false; |
275 | } | 283 | } |
276 | 284 | ||
277 | /// <summary> | 285 | public int GetCatBytesCanSend(int timeMS) |
278 | /// Deposit tokens into the bucket from a child bucket that did | ||
279 | /// not use all of its available tokens | ||
280 | /// </summary> | ||
281 | protected void Deposit(Int64 count) | ||
282 | { | 286 | { |
283 | m_tokenCount += count; | 287 | // return (int)(m_tokenCount + timeMS * m_dripRate * 1e-3); |
284 | 288 | return (int)(timeMS * m_dripRate * 1e-3); | |
285 | // Deposit the overflow in the parent bucket, this is how we share | ||
286 | // unused bandwidth | ||
287 | Int64 burstrate = BurstRate; | ||
288 | if (m_tokenCount > burstrate) | ||
289 | m_tokenCount = burstrate; | ||
290 | } | 289 | } |
291 | 290 | ||
292 | /// <summary> | 291 | /// <summary> |
@@ -305,17 +304,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
305 | return; | 304 | return; |
306 | } | 305 | } |
307 | 306 | ||
308 | // Determine the interval over which we are adding tokens, never add | 307 | Int32 now = Util.EnvironmentTickCount(); |
309 | // more than a single quantum of tokens | 308 | Int32 deltaMS = now - m_lastDrip; |
310 | Int32 deltaMS = Math.Min(Util.EnvironmentTickCountSubtract(m_lastDrip), m_ticksPerQuantum); | 309 | m_lastDrip = now; |
311 | m_lastDrip = Util.EnvironmentTickCount(); | ||
312 | 310 | ||
313 | // This can be 0 in the very unusual case that the timer wrapped | ||
314 | // It can be 0 if we try add tokens at a sub-tick rate | ||
315 | if (deltaMS <= 0) | 311 | if (deltaMS <= 0) |
316 | return; | 312 | return; |
317 | 313 | ||
318 | Deposit(deltaMS * DripRate / m_ticksPerQuantum); | 314 | m_tokenCount += deltaMS * DripRate * m_timeScale; |
315 | |||
316 | float burst = Burst; | ||
317 | if (m_tokenCount > burst) | ||
318 | m_tokenCount = burst; | ||
319 | } | 319 | } |
320 | } | 320 | } |
321 | 321 | ||
@@ -325,20 +325,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
325 | 325 | ||
326 | /// <summary> | 326 | /// <summary> |
327 | /// The minimum rate for flow control. Minimum drip rate is one | 327 | /// The minimum rate for flow control. Minimum drip rate is one |
328 | /// packet per second. Open the throttle to 15 packets per second | 328 | /// packet per second. |
329 | /// or about 160kbps. | ||
330 | /// </summary> | 329 | /// </summary> |
331 | protected const Int64 m_minimumFlow = m_minimumDripRate * 15; | 330 | |
331 | protected const float m_minimumFlow = 50000; | ||
332 | 332 | ||
333 | // <summary> | 333 | // <summary> |
334 | // The maximum rate for flow control. Drip rate can never be | 334 | // The maximum rate for flow control. Drip rate can never be |
335 | // greater than this. | 335 | // greater than this. |
336 | // </summary> | 336 | // </summary> |
337 | protected Int64 m_maxDripRate = 0; | 337 | |
338 | protected Int64 MaxDripRate | 338 | protected float m_maxDripRate = 0; |
339 | public float MaxDripRate | ||
339 | { | 340 | { |
340 | get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); } | 341 | get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); } |
341 | set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); } | 342 | set |
343 | { | ||
344 | m_maxDripRate = (value == 0 ? m_totalDripRequest : Math.Max(value, m_minimumFlow)); | ||
345 | } | ||
342 | } | 346 | } |
343 | 347 | ||
344 | private bool m_enabled = false; | 348 | private bool m_enabled = false; |
@@ -346,12 +350,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
346 | // <summary> | 350 | // <summary> |
347 | // | 351 | // |
348 | // </summary> | 352 | // </summary> |
349 | public virtual Int64 AdjustedDripRate | 353 | public virtual float AdjustedDripRate |
350 | { | 354 | { |
351 | get { return m_dripRate; } | 355 | get { return m_dripRate; } |
352 | set { | 356 | set { |
353 | m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value,m_minimumFlow,MaxDripRate); | 357 | m_dripRate = OpenSim.Framework.Util.Clamp<float>(value,m_minimumFlow,MaxDripRate); |
354 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); | 358 | |
355 | if (m_parent != null) | 359 | if (m_parent != null) |
356 | m_parent.RegisterRequest(this,m_dripRate); | 360 | m_parent.RegisterRequest(this,m_dripRate); |
357 | } | 361 | } |
@@ -360,16 +364,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
360 | // <summary> | 364 | // <summary> |
361 | // | 365 | // |
362 | // </summary> | 366 | // </summary> |
363 | public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate, bool enabled) : base(parent,maxDripRate) | 367 | public AdaptiveTokenBucket(TokenBucket parent, float maxDripRate,float maxBurst, bool enabled) |
368 | : base(parent, maxDripRate,maxBurst) | ||
364 | { | 369 | { |
365 | m_enabled = enabled; | 370 | m_enabled = enabled; |
371 | |||
372 | MaxDripRate = maxDripRate; | ||
366 | 373 | ||
367 | if (m_enabled) | 374 | if (enabled) |
368 | { | 375 | AdjustedDripRate = m_maxDripRate * .5f; |
369 | // m_log.DebugFormat("[TOKENBUCKET] Adaptive throttle enabled"); | 376 | else |
370 | MaxDripRate = maxDripRate; | 377 | AdjustedDripRate = m_maxDripRate; |
371 | AdjustedDripRate = m_minimumFlow; | ||
372 | } | ||
373 | } | 378 | } |
374 | 379 | ||
375 | // <summary> | 380 | // <summary> |