diff options
author | David Walter Seikel | 2016-11-03 21:44:39 +1000 |
---|---|---|
committer | David Walter Seikel | 2016-11-03 21:44:39 +1000 |
commit | 134f86e8d5c414409631b25b8c6f0ee45fbd8631 (patch) | |
tree | 216b89d3fb89acfb81be1e440c25c41ab09fa96d /OpenSim/Region/ClientStack | |
parent | More changing to production grid. Double oops. (diff) | |
download | opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.zip opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.gz opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.bz2 opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.xz |
Initial update to OpenSim 0.8.2.1 source code.
Diffstat (limited to '')
43 files changed, 5537 insertions, 1406 deletions
diff --git a/OpenSim/Region/ClientStack/RegionApplicationBase.cs b/OpenSim/Region/Application/RegionApplicationBase.cs index 853b72d..08c8579 100644 --- a/OpenSim/Region/ClientStack/RegionApplicationBase.cs +++ b/OpenSim/Region/Application/RegionApplicationBase.cs | |||
@@ -32,15 +32,15 @@ using log4net; | |||
32 | using Nini.Config; | 32 | using Nini.Config; |
33 | using OpenMetaverse; | 33 | using OpenMetaverse; |
34 | using OpenSim.Framework; | 34 | using OpenSim.Framework; |
35 | using OpenSim.Framework.Communications; | ||
36 | using OpenSim.Framework.Servers; | 35 | using OpenSim.Framework.Servers; |
37 | using OpenSim.Framework.Servers.HttpServer; | 36 | using OpenSim.Framework.Servers.HttpServer; |
38 | using OpenSim.Region.Framework; | 37 | using OpenSim.Region.Framework; |
39 | using OpenSim.Region.Framework.Interfaces; | 38 | using OpenSim.Region.Framework.Interfaces; |
40 | using OpenSim.Region.Framework.Scenes; | 39 | using OpenSim.Region.Framework.Scenes; |
41 | using OpenSim.Region.Physics.Manager; | 40 | using OpenSim.Region.PhysicsModules.SharedBase; |
41 | using OpenSim.Services.Interfaces; | ||
42 | 42 | ||
43 | namespace OpenSim.Region.ClientStack | 43 | namespace OpenSim |
44 | { | 44 | { |
45 | public abstract class RegionApplicationBase : BaseOpenSimServer | 45 | public abstract class RegionApplicationBase : BaseOpenSimServer |
46 | { | 46 | { |
@@ -52,7 +52,6 @@ namespace OpenSim.Region.ClientStack | |||
52 | protected uint m_httpServerPort; | 52 | protected uint m_httpServerPort; |
53 | protected ISimulationDataService m_simulationDataService; | 53 | protected ISimulationDataService m_simulationDataService; |
54 | protected IEstateDataService m_estateDataService; | 54 | protected IEstateDataService m_estateDataService; |
55 | protected ClientStackManager m_clientStackManager; | ||
56 | 55 | ||
57 | public SceneManager SceneManager { get; protected set; } | 56 | public SceneManager SceneManager { get; protected set; } |
58 | public NetworkServersInfo NetServersInfo { get { return m_networkServersInfo; } } | 57 | public NetworkServersInfo NetServersInfo { get { return m_networkServersInfo; } } |
@@ -61,23 +60,11 @@ namespace OpenSim.Region.ClientStack | |||
61 | 60 | ||
62 | protected abstract void Initialize(); | 61 | protected abstract void Initialize(); |
63 | 62 | ||
64 | /// <summary> | ||
65 | /// Get a new physics scene. | ||
66 | /// </summary> | ||
67 | /// | ||
68 | /// <param name="osSceneIdentifier"> | ||
69 | /// The name of the OpenSim scene this physics scene is serving. This will be used in log messages. | ||
70 | /// </param> | ||
71 | /// <returns></returns> | ||
72 | protected abstract PhysicsScene GetPhysicsScene(string osSceneIdentifier); | ||
73 | |||
74 | protected abstract ClientStackManager CreateClientStackManager(); | ||
75 | protected abstract Scene CreateScene(RegionInfo regionInfo, ISimulationDataService simDataService, IEstateDataService estateDataService, AgentCircuitManager circuitManager); | 63 | protected abstract Scene CreateScene(RegionInfo regionInfo, ISimulationDataService simDataService, IEstateDataService estateDataService, AgentCircuitManager circuitManager); |
76 | 64 | ||
77 | protected override void StartupSpecific() | 65 | protected override void StartupSpecific() |
78 | { | 66 | { |
79 | SceneManager = SceneManager.Instance; | 67 | SceneManager = SceneManager.Instance; |
80 | m_clientStackManager = CreateClientStackManager(); | ||
81 | 68 | ||
82 | Initialize(); | 69 | Initialize(); |
83 | 70 | ||
@@ -112,24 +99,5 @@ namespace OpenSim.Region.ClientStack | |||
112 | base.StartupSpecific(); | 99 | base.StartupSpecific(); |
113 | } | 100 | } |
114 | 101 | ||
115 | /// <summary> | ||
116 | /// Get a new physics scene. | ||
117 | /// </summary> | ||
118 | /// <param name="engine">The name of the physics engine to use</param> | ||
119 | /// <param name="meshEngine">The name of the mesh engine to use</param> | ||
120 | /// <param name="config">The configuration data to pass to the physics and mesh engines</param> | ||
121 | /// <param name="osSceneIdentifier"> | ||
122 | /// The name of the OpenSim scene this physics scene is serving. This will be used in log messages. | ||
123 | /// </param> | ||
124 | /// <returns></returns> | ||
125 | protected PhysicsScene GetPhysicsScene( | ||
126 | string engine, string meshEngine, IConfigSource config, string osSceneIdentifier) | ||
127 | { | ||
128 | PhysicsPluginManager physicsPluginManager; | ||
129 | physicsPluginManager = new PhysicsPluginManager(); | ||
130 | physicsPluginManager.LoadPluginsFromAssemblies("Physics"); | ||
131 | |||
132 | return physicsPluginManager.GetPhysicsScene(engine, meshEngine, config, osSceneIdentifier); | ||
133 | } | ||
134 | } | 102 | } |
135 | } \ No newline at end of file | 103 | } \ No newline at end of file |
diff --git a/OpenSim/Region/ClientStack/ClientStackManager.cs b/OpenSim/Region/ClientStack/ClientStackManager.cs deleted file mode 100644 index 84ea0b3..0000000 --- a/OpenSim/Region/ClientStack/ClientStackManager.cs +++ /dev/null | |||
@@ -1,128 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Net; | ||
30 | using System.Reflection; | ||
31 | using log4net; | ||
32 | using Nini.Config; | ||
33 | using OpenSim.Framework; | ||
34 | |||
35 | namespace OpenSim.Region.ClientStack | ||
36 | { | ||
37 | public class ClientStackManager | ||
38 | { | ||
39 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
40 | |||
41 | private Type plugin; | ||
42 | private Assembly pluginAssembly; | ||
43 | |||
44 | public ClientStackManager(string dllName) | ||
45 | { | ||
46 | m_log.Info("[CLIENTSTACK]: Attempting to load " + dllName); | ||
47 | |||
48 | try | ||
49 | { | ||
50 | plugin = null; | ||
51 | pluginAssembly = Assembly.LoadFrom(dllName); | ||
52 | |||
53 | foreach (Type pluginType in pluginAssembly.GetTypes()) | ||
54 | { | ||
55 | if (pluginType.IsPublic) | ||
56 | { | ||
57 | Type typeInterface = pluginType.GetInterface("IClientNetworkServer", true); | ||
58 | |||
59 | if (typeInterface != null) | ||
60 | { | ||
61 | m_log.Info("[CLIENTSTACK]: Added IClientNetworkServer Interface"); | ||
62 | plugin = pluginType; | ||
63 | return; | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | } catch (ReflectionTypeLoadException e) | ||
68 | { | ||
69 | foreach (Exception e2 in e.LoaderExceptions) | ||
70 | { | ||
71 | m_log.Error(e2.ToString()); | ||
72 | } | ||
73 | throw e; | ||
74 | } | ||
75 | } | ||
76 | |||
77 | /// <summary> | ||
78 | /// Create a server that can set up sessions for virtual world client <-> server communications | ||
79 | /// </summary> | ||
80 | /// <param name="_listenIP"></param> | ||
81 | /// <param name="port"></param> | ||
82 | /// <param name="proxyPortOffset"></param> | ||
83 | /// <param name="allow_alternate_port"></param> | ||
84 | /// <param name="assetCache"></param> | ||
85 | /// <param name="authenticateClass"></param> | ||
86 | /// <returns></returns> | ||
87 | public IClientNetworkServer CreateServer( | ||
88 | IPAddress _listenIP, ref uint port, int proxyPortOffset, bool allow_alternate_port, | ||
89 | AgentCircuitManager authenticateClass) | ||
90 | { | ||
91 | return CreateServer( | ||
92 | _listenIP, ref port, proxyPortOffset, allow_alternate_port, null, authenticateClass); | ||
93 | } | ||
94 | |||
95 | /// <summary> | ||
96 | /// Create a server that can set up sessions for virtual world client <-> server communications | ||
97 | /// </summary> | ||
98 | /// <param name="_listenIP"></param> | ||
99 | /// <param name="port"></param> | ||
100 | /// <param name="proxyPortOffset"></param> | ||
101 | /// <param name="allow_alternate_port"></param> | ||
102 | /// <param name="configSource"> | ||
103 | /// Can be null, in which case default values are used | ||
104 | /// </param> | ||
105 | /// <param name="assetCache"></param> | ||
106 | /// <param name="authenticateClass"></param> | ||
107 | /// <returns></returns> | ||
108 | public IClientNetworkServer CreateServer( | ||
109 | IPAddress _listenIP, ref uint port, int proxyPortOffset, bool allow_alternate_port, IConfigSource configSource, | ||
110 | AgentCircuitManager authenticateClass) | ||
111 | { | ||
112 | if (plugin != null) | ||
113 | { | ||
114 | IClientNetworkServer server = | ||
115 | (IClientNetworkServer)Activator.CreateInstance(pluginAssembly.GetType(plugin.ToString())); | ||
116 | |||
117 | server.Initialise( | ||
118 | _listenIP, ref port, proxyPortOffset, allow_alternate_port, | ||
119 | configSource, authenticateClass); | ||
120 | |||
121 | return server; | ||
122 | } | ||
123 | |||
124 | m_log.Error("[CLIENTSTACK]: Couldn't initialize a new server"); | ||
125 | return null; | ||
126 | } | ||
127 | } | ||
128 | } | ||
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/AgentPreferencesModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/AgentPreferencesModule.cs new file mode 100644 index 0000000..aabdb51 --- /dev/null +++ b/OpenSim/Region/ClientStack/Linden/Caps/AgentPreferencesModule.cs | |||
@@ -0,0 +1,182 @@ | |||
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.Generic; | ||
30 | using System.Reflection; | ||
31 | using System.IO; | ||
32 | using log4net; | ||
33 | using Mono.Addins; | ||
34 | using Nini.Config; | ||
35 | using OpenMetaverse; | ||
36 | using OpenMetaverse.StructuredData; | ||
37 | using OpenSim.Framework.Console; | ||
38 | using OpenSim.Framework.Servers; | ||
39 | using OpenSim.Framework.Servers.HttpServer; | ||
40 | using OpenSim.Region.Framework.Interfaces; | ||
41 | using OpenSim.Region.Framework.Scenes; | ||
42 | using OpenSim.Services.Interfaces; | ||
43 | using Caps = OpenSim.Framework.Capabilities.Caps; | ||
44 | using OpenSim.Capabilities.Handlers; | ||
45 | |||
46 | namespace OpenSim.Region.ClientStack.LindenCaps | ||
47 | { | ||
48 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AgentPreferencesModule")] | ||
49 | public class AgentPreferencesModule : ISharedRegionModule | ||
50 | { | ||
51 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
52 | |||
53 | private List<Scene> m_scenes = new List<Scene>(); | ||
54 | |||
55 | public void Initialise(IConfigSource source) | ||
56 | { | ||
57 | |||
58 | } | ||
59 | |||
60 | #region Region module | ||
61 | |||
62 | public void AddRegion(Scene scene) | ||
63 | { | ||
64 | lock (m_scenes) m_scenes.Add(scene); | ||
65 | } | ||
66 | |||
67 | public void RemoveRegion(Scene scene) | ||
68 | { | ||
69 | lock (m_scenes) m_scenes.Remove(scene); | ||
70 | scene.EventManager.OnRegisterCaps -= RegisterCaps; | ||
71 | scene = null; | ||
72 | } | ||
73 | |||
74 | public void RegionLoaded(Scene scene) | ||
75 | { | ||
76 | scene.EventManager.OnRegisterCaps += delegate(UUID agentID, OpenSim.Framework.Capabilities.Caps caps) | ||
77 | { | ||
78 | RegisterCaps(agentID, caps); | ||
79 | }; | ||
80 | } | ||
81 | |||
82 | public void PostInitialise() {} | ||
83 | |||
84 | public void Close() {} | ||
85 | |||
86 | public string Name { get { return "AgentPreferencesModule"; } } | ||
87 | |||
88 | public Type ReplaceableInterface | ||
89 | { | ||
90 | get { return null; } | ||
91 | } | ||
92 | |||
93 | public void RegisterCaps(UUID agent, Caps caps) | ||
94 | { | ||
95 | UUID capId = UUID.Random(); | ||
96 | caps.RegisterHandler("AgentPreferences", | ||
97 | new RestStreamHandler("POST", "/CAPS/" + capId, | ||
98 | delegate(string request, string path, string param, | ||
99 | IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) | ||
100 | { | ||
101 | return UpdateAgentPreferences(request, path, param, agent); | ||
102 | })); | ||
103 | caps.RegisterHandler("UpdateAgentLanguage", | ||
104 | new RestStreamHandler("POST", "/CAPS/" + capId, | ||
105 | delegate(string request, string path, string param, | ||
106 | IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) | ||
107 | { | ||
108 | return UpdateAgentPreferences(request, path, param, agent); | ||
109 | })); | ||
110 | caps.RegisterHandler("UpdateAgentInformation", | ||
111 | new RestStreamHandler("POST", "/CAPS/" + capId, | ||
112 | delegate(string request, string path, string param, | ||
113 | IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) | ||
114 | { | ||
115 | return UpdateAgentPreferences(request, path, param, agent); | ||
116 | })); | ||
117 | } | ||
118 | |||
119 | public string UpdateAgentPreferences(string request, string path, string param, UUID agent) | ||
120 | { | ||
121 | OSDMap resp = new OSDMap(); | ||
122 | // The viewer doesn't do much with the return value, so for now, if there is no preference service, | ||
123 | // we'll return a null llsd block for debugging purposes. This may change if someone knows what the | ||
124 | // correct server response would be here. | ||
125 | if (m_scenes[0].AgentPreferencesService == null) | ||
126 | { | ||
127 | return OSDParser.SerializeLLSDXmlString(resp); | ||
128 | } | ||
129 | m_log.DebugFormat("[AgentPrefs]: UpdateAgentPreferences for {0}", agent.ToString()); | ||
130 | OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); | ||
131 | AgentPrefs data = m_scenes[0].AgentPreferencesService.GetAgentPreferences(agent); | ||
132 | if (data == null) | ||
133 | { | ||
134 | data = new AgentPrefs(agent); | ||
135 | } | ||
136 | |||
137 | if (req.ContainsKey("access_prefs")) | ||
138 | { | ||
139 | OSDMap accessPrefs = (OSDMap)req["access_prefs"]; // We could check with ContainsKey... | ||
140 | data.AccessPrefs = accessPrefs["max"].AsString(); | ||
141 | } | ||
142 | if (req.ContainsKey("default_object_perm_masks")) | ||
143 | { | ||
144 | OSDMap permsMap = (OSDMap)req["default_object_perm_masks"]; | ||
145 | data.PermEveryone = permsMap["Everyone"].AsInteger(); | ||
146 | data.PermGroup = permsMap["Group"].AsInteger(); | ||
147 | data.PermNextOwner = permsMap["NextOwner"].AsInteger(); | ||
148 | } | ||
149 | if (req.ContainsKey("hover_height")) | ||
150 | { | ||
151 | data.HoverHeight = req["hover_height"].AsReal(); | ||
152 | } | ||
153 | if (req.ContainsKey("language")) | ||
154 | { | ||
155 | data.Language = req["language"].AsString(); | ||
156 | } | ||
157 | if (req.ContainsKey("language_is_public")) | ||
158 | { | ||
159 | data.LanguageIsPublic = req["language_is_public"].AsBoolean(); | ||
160 | } | ||
161 | m_scenes[0].AgentPreferencesService.StoreAgentPreferences(data); | ||
162 | OSDMap respAccessPrefs = new OSDMap(); | ||
163 | respAccessPrefs["max"] = data.AccessPrefs; | ||
164 | resp["access_prefs"] = respAccessPrefs; | ||
165 | OSDMap respDefaultPerms = new OSDMap(); | ||
166 | respDefaultPerms["Everyone"] = data.PermEveryone; | ||
167 | respDefaultPerms["Group"] = data.PermGroup; | ||
168 | respDefaultPerms["NextOwner"] = data.PermNextOwner; | ||
169 | resp["default_object_perm_masks"] = respDefaultPerms; | ||
170 | resp["god_level"] = 0; // *TODO: Add this | ||
171 | resp["hover_height"] = data.HoverHeight; | ||
172 | resp["language"] = data.Language; | ||
173 | resp["language_is_public"] = data.LanguageIsPublic; | ||
174 | |||
175 | string response = OSDParser.SerializeLLSDXmlString(resp); | ||
176 | return response; | ||
177 | } | ||
178 | |||
179 | #endregion Region module | ||
180 | } | ||
181 | } | ||
182 | |||
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 568e216..774202e 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs | |||
@@ -44,11 +44,13 @@ using OpenSim.Region.Framework.Scenes; | |||
44 | using OpenSim.Region.Framework.Scenes.Serialization; | 44 | using OpenSim.Region.Framework.Scenes.Serialization; |
45 | using OpenSim.Framework.Servers; | 45 | using OpenSim.Framework.Servers; |
46 | using OpenSim.Framework.Servers.HttpServer; | 46 | using OpenSim.Framework.Servers.HttpServer; |
47 | using OpenSim.Framework.Client; | ||
47 | using OpenSim.Services.Interfaces; | 48 | using OpenSim.Services.Interfaces; |
48 | 49 | ||
49 | using Caps = OpenSim.Framework.Capabilities.Caps; | 50 | using Caps = OpenSim.Framework.Capabilities.Caps; |
50 | using OSDArray = OpenMetaverse.StructuredData.OSDArray; | 51 | using OSDArray = OpenMetaverse.StructuredData.OSDArray; |
51 | using OSDMap = OpenMetaverse.StructuredData.OSDMap; | 52 | using OSDMap = OpenMetaverse.StructuredData.OSDMap; |
53 | using PermissionMask = OpenSim.Framework.PermissionMask; | ||
52 | 54 | ||
53 | namespace OpenSim.Region.ClientStack.Linden | 55 | namespace OpenSim.Region.ClientStack.Linden |
54 | { | 56 | { |
@@ -96,6 +98,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
96 | // private static readonly string m_fetchInventoryPath = "0006/"; | 98 | // private static readonly string m_fetchInventoryPath = "0006/"; |
97 | private static readonly string m_copyFromNotecardPath = "0007/"; | 99 | private static readonly string m_copyFromNotecardPath = "0007/"; |
98 | // private static readonly string m_remoteParcelRequestPath = "0009/";// This is in the LandManagementModule. | 100 | // private static readonly string m_remoteParcelRequestPath = "0009/";// This is in the LandManagementModule. |
101 | private static readonly string m_getObjectPhysicsDataPath = "0101/"; | ||
102 | /* 0102 - 0103 RESERVED */ | ||
99 | private static readonly string m_UpdateAgentInformationPath = "0500/"; | 103 | private static readonly string m_UpdateAgentInformationPath = "0500/"; |
100 | 104 | ||
101 | // These are callbacks which will be setup by the scene so that we can update scene data when we | 105 | // These are callbacks which will be setup by the scene so that we can update scene data when we |
@@ -204,7 +208,15 @@ namespace OpenSim.Region.ClientStack.Linden | |||
204 | m_HostCapsObj.RegisterHandler("UpdateNotecardAgentInventory", req); | 208 | m_HostCapsObj.RegisterHandler("UpdateNotecardAgentInventory", req); |
205 | m_HostCapsObj.RegisterHandler("UpdateScriptAgentInventory", req); | 209 | m_HostCapsObj.RegisterHandler("UpdateScriptAgentInventory", req); |
206 | m_HostCapsObj.RegisterHandler("UpdateScriptAgent", req); | 210 | m_HostCapsObj.RegisterHandler("UpdateScriptAgent", req); |
207 | IRequestHandler UpdateAgentInformationHandler = new RestStreamHandler("POST", capsBase + m_UpdateAgentInformationPath, UpdateAgentInformation); | 211 | |
212 | IRequestHandler getObjectPhysicsDataHandler | ||
213 | = new RestStreamHandler( | ||
214 | "POST", capsBase + m_getObjectPhysicsDataPath, GetObjectPhysicsData, "GetObjectPhysicsData", null); | ||
215 | m_HostCapsObj.RegisterHandler("GetObjectPhysicsData", getObjectPhysicsDataHandler); | ||
216 | |||
217 | IRequestHandler UpdateAgentInformationHandler | ||
218 | = new RestStreamHandler( | ||
219 | "POST", capsBase + m_UpdateAgentInformationPath, UpdateAgentInformation, "UpdateAgentInformation", null); | ||
208 | m_HostCapsObj.RegisterHandler("UpdateAgentInformation", UpdateAgentInformationHandler); | 220 | m_HostCapsObj.RegisterHandler("UpdateAgentInformation", UpdateAgentInformationHandler); |
209 | 221 | ||
210 | m_HostCapsObj.RegisterHandler( | 222 | m_HostCapsObj.RegisterHandler( |
@@ -256,8 +268,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
256 | public string SeedCapRequest(string request, string path, string param, | 268 | public string SeedCapRequest(string request, string path, string param, |
257 | IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) | 269 | IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) |
258 | { | 270 | { |
259 | m_log.DebugFormat( | 271 | // m_log.DebugFormat( |
260 | "[CAPS]: Received SEED caps request in {0} for agent {1}", m_regionName, m_HostCapsObj.AgentID); | 272 | // "[CAPS]: Received SEED caps request in {0} for agent {1}", m_regionName, m_HostCapsObj.AgentID); |
261 | 273 | ||
262 | if (!m_Scene.CheckClient(m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint)) | 274 | if (!m_Scene.CheckClient(m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint)) |
263 | { | 275 | { |
@@ -268,13 +280,13 @@ namespace OpenSim.Region.ClientStack.Linden | |||
268 | return string.Empty; | 280 | return string.Empty; |
269 | } | 281 | } |
270 | 282 | ||
271 | Hashtable caps = m_HostCapsObj.CapsHandlers.GetCapsDetails(true); | 283 | OSDArray capsRequested = (OSDArray)OSDParser.DeserializeLLSDXml(request); |
284 | List<string> validCaps = new List<string>(); | ||
272 | 285 | ||
273 | // Add the external too | 286 | foreach (OSD c in capsRequested) |
274 | foreach (KeyValuePair<string, string> kvp in m_HostCapsObj.ExternalCapsHandlers) | 287 | validCaps.Add(c.AsString()); |
275 | caps[kvp.Key] = kvp.Value; | ||
276 | 288 | ||
277 | string result = LLSDHelpers.SerialiseLLSDReply(caps); | 289 | string result = LLSDHelpers.SerialiseLLSDReply(m_HostCapsObj.GetCapsDetails(true, validCaps)); |
278 | 290 | ||
279 | //m_log.DebugFormat("[CAPS] CapsRequest {0}", result); | 291 | //m_log.DebugFormat("[CAPS] CapsRequest {0}", result); |
280 | 292 | ||
@@ -487,24 +499,28 @@ namespace OpenSim.Region.ClientStack.Linden | |||
487 | 499 | ||
488 | if (inventoryType == "sound") | 500 | if (inventoryType == "sound") |
489 | { | 501 | { |
490 | inType = 1; | 502 | inType = (sbyte)InventoryType.Sound; |
491 | assType = 1; | 503 | assType = (sbyte)AssetType.Sound; |
504 | } | ||
505 | else if (inventoryType == "snapshot") | ||
506 | { | ||
507 | inType = (sbyte)InventoryType.Snapshot; | ||
492 | } | 508 | } |
493 | else if (inventoryType == "animation") | 509 | else if (inventoryType == "animation") |
494 | { | 510 | { |
495 | inType = 19; | 511 | inType = (sbyte)InventoryType.Animation; |
496 | assType = 20; | 512 | assType = (sbyte)AssetType.Animation; |
497 | } | 513 | } |
498 | else if (inventoryType == "wearable") | 514 | else if (inventoryType == "wearable") |
499 | { | 515 | { |
500 | inType = 18; | 516 | inType = (sbyte)InventoryType.Wearable; |
501 | switch (assetType) | 517 | switch (assetType) |
502 | { | 518 | { |
503 | case "bodypart": | 519 | case "bodypart": |
504 | assType = 13; | 520 | assType = (sbyte)AssetType.Bodypart; |
505 | break; | 521 | break; |
506 | case "clothing": | 522 | case "clothing": |
507 | assType = 5; | 523 | assType = (sbyte)AssetType.Clothing; |
508 | break; | 524 | break; |
509 | } | 525 | } |
510 | } | 526 | } |
@@ -521,6 +537,41 @@ namespace OpenSim.Region.ClientStack.Linden | |||
521 | OSDArray texture_list = (OSDArray)request["texture_list"]; | 537 | OSDArray texture_list = (OSDArray)request["texture_list"]; |
522 | SceneObjectGroup grp = null; | 538 | SceneObjectGroup grp = null; |
523 | 539 | ||
540 | InventoryFolderBase textureUploadFolder = null; | ||
541 | |||
542 | List<InventoryFolderBase> foldersToUpdate = new List<InventoryFolderBase>(); | ||
543 | List<InventoryItemBase> itemsToUpdate = new List<InventoryItemBase>(); | ||
544 | IClientInventory clientInv = null; | ||
545 | |||
546 | if (texture_list.Count > 0) | ||
547 | { | ||
548 | ScenePresence avatar = null; | ||
549 | m_Scene.TryGetScenePresence(m_HostCapsObj.AgentID, out avatar); | ||
550 | |||
551 | if (avatar != null) | ||
552 | { | ||
553 | IClientCore core = (IClientCore)avatar.ControllingClient; | ||
554 | |||
555 | if (core.TryGet<IClientInventory>(out clientInv)) | ||
556 | { | ||
557 | var systemTextureFolder = m_Scene.InventoryService.GetFolderForType(m_HostCapsObj.AgentID, FolderType.Texture); | ||
558 | textureUploadFolder = new InventoryFolderBase(UUID.Random(), assetName, m_HostCapsObj.AgentID, (short)FolderType.None, systemTextureFolder.ID, 1); | ||
559 | if (m_Scene.InventoryService.AddFolder(textureUploadFolder)) | ||
560 | { | ||
561 | foldersToUpdate.Add(textureUploadFolder); | ||
562 | |||
563 | m_log.DebugFormat( | ||
564 | "[BUNCH OF CAPS]: Created new folder '{0}' ({1}) for textures uploaded with mesh object {2}", | ||
565 | textureUploadFolder.Name, textureUploadFolder.ID, assetName); | ||
566 | } | ||
567 | else | ||
568 | { | ||
569 | textureUploadFolder = null; | ||
570 | } | ||
571 | } | ||
572 | } | ||
573 | } | ||
574 | |||
524 | List<UUID> textures = new List<UUID>(); | 575 | List<UUID> textures = new List<UUID>(); |
525 | for (int i = 0; i < texture_list.Count; i++) | 576 | for (int i = 0; i < texture_list.Count; i++) |
526 | { | 577 | { |
@@ -528,6 +579,38 @@ namespace OpenSim.Region.ClientStack.Linden | |||
528 | textureAsset.Data = texture_list[i].AsBinary(); | 579 | textureAsset.Data = texture_list[i].AsBinary(); |
529 | m_assetService.Store(textureAsset); | 580 | m_assetService.Store(textureAsset); |
530 | textures.Add(textureAsset.FullID); | 581 | textures.Add(textureAsset.FullID); |
582 | |||
583 | if (textureUploadFolder != null) | ||
584 | { | ||
585 | InventoryItemBase textureItem = new InventoryItemBase(); | ||
586 | textureItem.Owner = m_HostCapsObj.AgentID; | ||
587 | textureItem.CreatorId = m_HostCapsObj.AgentID.ToString(); | ||
588 | textureItem.CreatorData = String.Empty; | ||
589 | textureItem.ID = UUID.Random(); | ||
590 | textureItem.AssetID = textureAsset.FullID; | ||
591 | textureItem.Description = assetDescription; | ||
592 | textureItem.Name = assetName + " - Texture " + (i + 1).ToString(); | ||
593 | textureItem.AssetType = (int)AssetType.Texture; | ||
594 | textureItem.InvType = (int)InventoryType.Texture; | ||
595 | textureItem.Folder = textureUploadFolder.ID; | ||
596 | textureItem.CurrentPermissions | ||
597 | = (uint)(PermissionMask.Move | PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer | PermissionMask.Export); | ||
598 | textureItem.BasePermissions = (uint)PermissionMask.All | (uint)PermissionMask.Export; | ||
599 | textureItem.EveryOnePermissions = 0; | ||
600 | textureItem.NextPermissions = (uint)PermissionMask.All; | ||
601 | textureItem.CreationDate = Util.UnixTimeSinceEpoch(); | ||
602 | m_Scene.InventoryService.AddItem(textureItem); | ||
603 | itemsToUpdate.Add(textureItem); | ||
604 | |||
605 | m_log.DebugFormat( | ||
606 | "[BUNCH OF CAPS]: Created new inventory item '{0}' ({1}) for texture uploaded with mesh object {2}", | ||
607 | textureItem.Name, textureItem.ID, assetName); | ||
608 | } | ||
609 | } | ||
610 | |||
611 | if (clientInv != null && (foldersToUpdate.Count > 0 || itemsToUpdate.Count > 0)) | ||
612 | { | ||
613 | clientInv.SendBulkUpdateInventory(foldersToUpdate.ToArray(), itemsToUpdate.ToArray()); | ||
531 | } | 614 | } |
532 | 615 | ||
533 | for (int i = 0; i < mesh_list.Count; i++) | 616 | for (int i = 0; i < mesh_list.Count; i++) |
@@ -701,9 +784,9 @@ namespace OpenSim.Region.ClientStack.Linden | |||
701 | // If we set PermissionMask.All then when we rez the item the next permissions will replace the current | 784 | // If we set PermissionMask.All then when we rez the item the next permissions will replace the current |
702 | // (owner) permissions. This becomes a problem if next permissions are changed. | 785 | // (owner) permissions. This becomes a problem if next permissions are changed. |
703 | item.CurrentPermissions | 786 | item.CurrentPermissions |
704 | = (uint)(PermissionMask.Move | PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer); | 787 | = (uint)(PermissionMask.Move | PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer | PermissionMask.Export); |
705 | 788 | ||
706 | item.BasePermissions = (uint)PermissionMask.All; | 789 | item.BasePermissions = (uint)PermissionMask.All | (uint)PermissionMask.Export; |
707 | item.EveryOnePermissions = 0; | 790 | item.EveryOnePermissions = 0; |
708 | item.NextPermissions = (uint)PermissionMask.All; | 791 | item.NextPermissions = (uint)PermissionMask.All; |
709 | item.CreationDate = Util.UnixTimeSinceEpoch(); | 792 | item.CreationDate = Util.UnixTimeSinceEpoch(); |
@@ -850,18 +933,26 @@ namespace OpenSim.Region.ClientStack.Linden | |||
850 | item = m_Scene.InventoryService.GetItem(new InventoryItemBase(itemID)); | 933 | item = m_Scene.InventoryService.GetItem(new InventoryItemBase(itemID)); |
851 | if (item != null) | 934 | if (item != null) |
852 | { | 935 | { |
853 | copyItem = m_Scene.GiveInventoryItem(m_HostCapsObj.AgentID, item.Owner, itemID, folderID); | 936 | string message; |
854 | if (copyItem != null && client != null) | 937 | copyItem = m_Scene.GiveInventoryItem(m_HostCapsObj.AgentID, item.Owner, itemID, folderID, out message); |
938 | if (client != null) | ||
855 | { | 939 | { |
856 | m_log.InfoFormat("[CAPS]: CopyInventoryFromNotecard, ItemID:{0}, FolderID:{1}", copyItem.ID, copyItem.Folder); | 940 | if (copyItem != null) |
857 | client.SendBulkUpdateInventory(copyItem); | 941 | { |
942 | m_log.InfoFormat("[CAPS]: CopyInventoryFromNotecard, ItemID:{0}, FolderID:{1}", copyItem.ID, copyItem.Folder); | ||
943 | client.SendBulkUpdateInventory(copyItem); | ||
944 | } | ||
945 | else | ||
946 | { | ||
947 | client.SendAgentAlertMessage(message, false); | ||
948 | } | ||
858 | } | 949 | } |
859 | } | 950 | } |
860 | else | 951 | else |
861 | { | 952 | { |
862 | m_log.ErrorFormat("[CAPS]: CopyInventoryFromNotecard - Failed to retrieve item {0} from notecard {1}", itemID, notecardID); | 953 | m_log.ErrorFormat("[CAPS]: CopyInventoryFromNotecard - Failed to retrieve item {0} from notecard {1}", itemID, notecardID); |
863 | if (client != null) | 954 | if (client != null) |
864 | client.SendAlertMessage("Failed to retrieve item"); | 955 | client.SendAgentAlertMessage("Failed to retrieve item", false); |
865 | } | 956 | } |
866 | } | 957 | } |
867 | catch (Exception e) | 958 | catch (Exception e) |
@@ -873,17 +964,49 @@ namespace OpenSim.Region.ClientStack.Linden | |||
873 | return LLSDHelpers.SerialiseLLSDReply(response); | 964 | return LLSDHelpers.SerialiseLLSDReply(response); |
874 | } | 965 | } |
875 | 966 | ||
876 | public string UpdateAgentInformation(string request, string path, | 967 | public string GetObjectPhysicsData(string request, string path, |
877 | string param, IOSHttpRequest httpRequest, | 968 | string param, IOSHttpRequest httpRequest, |
878 | IOSHttpResponse httpResponse) | 969 | IOSHttpResponse httpResponse) |
879 | { | 970 | { |
880 | OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); | 971 | OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); |
881 | OSDMap resp = new OSDMap(); | 972 | OSDMap resp = new OSDMap(); |
973 | OSDArray object_ids = (OSDArray)req["object_ids"]; | ||
882 | 974 | ||
883 | OSDMap accessPrefs = new OSDMap(); | 975 | for (int i = 0 ; i < object_ids.Count ; i++) |
884 | accessPrefs["max"] = "A"; | 976 | { |
977 | UUID uuid = object_ids[i].AsUUID(); | ||
978 | |||
979 | SceneObjectPart obj = m_Scene.GetSceneObjectPart(uuid); | ||
980 | if (obj != null) | ||
981 | { | ||
982 | OSDMap object_data = new OSDMap(); | ||
885 | 983 | ||
886 | resp["access_prefs"] = accessPrefs; | 984 | object_data["PhysicsShapeType"] = obj.PhysicsShapeType; |
985 | object_data["Density"] = obj.Density; | ||
986 | object_data["Friction"] = obj.Friction; | ||
987 | object_data["Restitution"] = obj.Restitution; | ||
988 | object_data["GravityMultiplier"] = obj.GravityModifier; | ||
989 | |||
990 | resp[uuid.ToString()] = object_data; | ||
991 | } | ||
992 | } | ||
993 | |||
994 | string response = OSDParser.SerializeLLSDXmlString(resp); | ||
995 | return response; | ||
996 | } | ||
997 | |||
998 | public string UpdateAgentInformation(string request, string path, | ||
999 | string param, IOSHttpRequest httpRequest, | ||
1000 | IOSHttpResponse httpResponse) | ||
1001 | { | ||
1002 | OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); | ||
1003 | OSDMap accessPrefs = (OSDMap)req["access_prefs"]; | ||
1004 | string desiredMaturity = accessPrefs["max"]; | ||
1005 | |||
1006 | OSDMap resp = new OSDMap(); | ||
1007 | OSDMap respAccessPrefs = new OSDMap(); | ||
1008 | respAccessPrefs["max"] = desiredMaturity; // echoing the maturity back means success | ||
1009 | resp["access_prefs"] = respAccessPrefs; | ||
887 | 1010 | ||
888 | string response = OSDParser.SerializeLLSDXmlString(resp); | 1011 | string response = OSDParser.SerializeLLSDXmlString(resp); |
889 | return response; | 1012 | return response; |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCapsModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCapsModule.cs index b735dfa..c241075 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCapsModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCapsModule.cs | |||
@@ -40,8 +40,8 @@ using OpenSim.Region.Framework.Interfaces; | |||
40 | using OpenSim.Region.Framework.Scenes; | 40 | using OpenSim.Region.Framework.Scenes; |
41 | using Caps = OpenSim.Framework.Capabilities.Caps; | 41 | using Caps = OpenSim.Framework.Capabilities.Caps; |
42 | 42 | ||
43 | [assembly: Addin("LindenCaps", "0.1")] | 43 | [assembly: Addin("LindenCaps", OpenSim.VersionInfo.VersionNumber)] |
44 | [assembly: AddinDependency("OpenSim", "0.5")] | 44 | [assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)] |
45 | namespace OpenSim.Region.ClientStack.Linden | 45 | namespace OpenSim.Region.ClientStack.Linden |
46 | { | 46 | { |
47 | 47 | ||
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs index 4d2c0f2..9b9f6a7 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs | |||
@@ -59,12 +59,20 @@ namespace OpenSim.Region.ClientStack.Linden | |||
59 | public class EventQueueGetModule : IEventQueue, INonSharedRegionModule | 59 | public class EventQueueGetModule : IEventQueue, INonSharedRegionModule |
60 | { | 60 | { |
61 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 61 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
62 | private static string LogHeader = "[EVENT QUEUE GET MODULE]"; | ||
62 | 63 | ||
63 | /// <value> | 64 | /// <value> |
64 | /// Debug level. | 65 | /// Debug level. |
65 | /// </value> | 66 | /// </value> |
66 | public int DebugLevel { get; set; } | 67 | public int DebugLevel { get; set; } |
67 | 68 | ||
69 | // Viewer post requests timeout in 60 secs | ||
70 | // https://bitbucket.org/lindenlab/viewer-release/src/421c20423df93d650cc305dc115922bb30040999/indra/llmessage/llhttpclient.cpp?at=default#cl-44 | ||
71 | // | ||
72 | private const int VIEWER_TIMEOUT = 60 * 1000; | ||
73 | // Just to be safe, we work on a 10 sec shorter cycle | ||
74 | private const int SERVER_EQ_TIME_NO_EVENTS = VIEWER_TIMEOUT - (10 * 1000); | ||
75 | |||
68 | protected Scene m_scene; | 76 | protected Scene m_scene; |
69 | 77 | ||
70 | private Dictionary<UUID, int> m_ids = new Dictionary<UUID, int>(); | 78 | private Dictionary<UUID, int> m_ids = new Dictionary<UUID, int>(); |
@@ -84,7 +92,6 @@ namespace OpenSim.Region.ClientStack.Linden | |||
84 | scene.RegisterModuleInterface<IEventQueue>(this); | 92 | scene.RegisterModuleInterface<IEventQueue>(this); |
85 | 93 | ||
86 | scene.EventManager.OnClientClosed += ClientClosed; | 94 | scene.EventManager.OnClientClosed += ClientClosed; |
87 | scene.EventManager.OnMakeChildAgent += MakeChildAgent; | ||
88 | scene.EventManager.OnRegisterCaps += OnRegisterCaps; | 95 | scene.EventManager.OnRegisterCaps += OnRegisterCaps; |
89 | 96 | ||
90 | MainConsole.Instance.Commands.AddCommand( | 97 | MainConsole.Instance.Commands.AddCommand( |
@@ -94,9 +101,17 @@ namespace OpenSim.Region.ClientStack.Linden | |||
94 | "debug eq [0|1|2]", | 101 | "debug eq [0|1|2]", |
95 | "Turn on event queue debugging\n" | 102 | "Turn on event queue debugging\n" |
96 | + " <= 0 - turns off all event queue logging\n" | 103 | + " <= 0 - turns off all event queue logging\n" |
97 | + " >= 1 - turns on outgoing event logging\n" | 104 | + " >= 1 - turns on event queue setup and outgoing event logging\n" |
98 | + " >= 2 - turns on poll notification", | 105 | + " >= 2 - turns on poll notification", |
99 | HandleDebugEq); | 106 | HandleDebugEq); |
107 | |||
108 | MainConsole.Instance.Commands.AddCommand( | ||
109 | "Debug", | ||
110 | false, | ||
111 | "show eq", | ||
112 | "show eq", | ||
113 | "Show contents of event queues for logged in avatars. Used for debugging.", | ||
114 | HandleShowEq); | ||
100 | } | 115 | } |
101 | 116 | ||
102 | public void RemoveRegion(Scene scene) | 117 | public void RemoveRegion(Scene scene) |
@@ -105,7 +120,6 @@ namespace OpenSim.Region.ClientStack.Linden | |||
105 | return; | 120 | return; |
106 | 121 | ||
107 | scene.EventManager.OnClientClosed -= ClientClosed; | 122 | scene.EventManager.OnClientClosed -= ClientClosed; |
108 | scene.EventManager.OnMakeChildAgent -= MakeChildAgent; | ||
109 | scene.EventManager.OnRegisterCaps -= OnRegisterCaps; | 123 | scene.EventManager.OnRegisterCaps -= OnRegisterCaps; |
110 | 124 | ||
111 | scene.UnregisterModuleInterface<IEventQueue>(this); | 125 | scene.UnregisterModuleInterface<IEventQueue>(this); |
@@ -138,7 +152,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
138 | 152 | ||
139 | if (!(args.Length == 3 && int.TryParse(args[2], out debugLevel))) | 153 | if (!(args.Length == 3 && int.TryParse(args[2], out debugLevel))) |
140 | { | 154 | { |
141 | MainConsole.Instance.OutputFormat("Usage: debug eq [0|1]"); | 155 | MainConsole.Instance.OutputFormat("Usage: debug eq [0|1|2]"); |
142 | } | 156 | } |
143 | else | 157 | else |
144 | { | 158 | { |
@@ -148,6 +162,21 @@ namespace OpenSim.Region.ClientStack.Linden | |||
148 | } | 162 | } |
149 | } | 163 | } |
150 | 164 | ||
165 | protected void HandleShowEq(string module, string[] args) | ||
166 | { | ||
167 | MainConsole.Instance.OutputFormat("For scene {0}", m_scene.Name); | ||
168 | |||
169 | lock (queues) | ||
170 | { | ||
171 | foreach (KeyValuePair<UUID, Queue<OSD>> kvp in queues) | ||
172 | { | ||
173 | MainConsole.Instance.OutputFormat( | ||
174 | "For agent {0} there are {1} messages queued for send.", | ||
175 | kvp.Key, kvp.Value.Count); | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | |||
151 | /// <summary> | 180 | /// <summary> |
152 | /// Always returns a valid queue | 181 | /// Always returns a valid queue |
153 | /// </summary> | 182 | /// </summary> |
@@ -159,14 +188,14 @@ namespace OpenSim.Region.ClientStack.Linden | |||
159 | { | 188 | { |
160 | if (!queues.ContainsKey(agentId)) | 189 | if (!queues.ContainsKey(agentId)) |
161 | { | 190 | { |
162 | /* | 191 | if (DebugLevel > 0) |
163 | m_log.DebugFormat( | 192 | m_log.DebugFormat( |
164 | "[EVENTQUEUE]: Adding new queue for agent {0} in region {1}", | 193 | "[EVENTQUEUE]: Adding new queue for agent {0} in region {1}", |
165 | agentId, m_scene.RegionInfo.RegionName); | 194 | agentId, m_scene.RegionInfo.RegionName); |
166 | */ | 195 | |
167 | queues[agentId] = new Queue<OSD>(); | 196 | queues[agentId] = new Queue<OSD>(); |
168 | } | 197 | } |
169 | 198 | ||
170 | return queues[agentId]; | 199 | return queues[agentId]; |
171 | } | 200 | } |
172 | } | 201 | } |
@@ -198,8 +227,23 @@ namespace OpenSim.Region.ClientStack.Linden | |||
198 | { | 227 | { |
199 | Queue<OSD> queue = GetQueue(avatarID); | 228 | Queue<OSD> queue = GetQueue(avatarID); |
200 | if (queue != null) | 229 | if (queue != null) |
230 | { | ||
201 | lock (queue) | 231 | lock (queue) |
202 | queue.Enqueue(ev); | 232 | queue.Enqueue(ev); |
233 | } | ||
234 | else if (DebugLevel > 0) | ||
235 | { | ||
236 | ScenePresence sp = m_scene.GetScenePresence(avatarID); | ||
237 | |||
238 | // This assumes that an NPC should never have a queue. | ||
239 | if (sp != null && sp.PresenceType != PresenceType.Npc) | ||
240 | { | ||
241 | OSDMap evMap = (OSDMap)ev; | ||
242 | m_log.WarnFormat( | ||
243 | "[EVENTQUEUE]: (Enqueue) No queue found for agent {0} {1} when placing message {2} in region {3}", | ||
244 | sp.Name, sp.UUID, evMap["message"], m_scene.Name); | ||
245 | } | ||
246 | } | ||
203 | } | 247 | } |
204 | catch (NullReferenceException e) | 248 | catch (NullReferenceException e) |
205 | { | 249 | { |
@@ -214,44 +258,14 @@ namespace OpenSim.Region.ClientStack.Linden | |||
214 | 258 | ||
215 | private void ClientClosed(UUID agentID, Scene scene) | 259 | private void ClientClosed(UUID agentID, Scene scene) |
216 | { | 260 | { |
217 | // m_log.DebugFormat("[EVENTQUEUE]: Closed client {0} in region {1}", agentID, m_scene.RegionInfo.RegionName); | 261 | //m_log.DebugFormat("[EVENTQUEUE]: Closed client {0} in region {1}", agentID, m_scene.RegionInfo.RegionName); |
218 | |||
219 | int count = 0; | ||
220 | while (queues.ContainsKey(agentID) && queues[agentID].Count > 0 && count++ < 5) | ||
221 | { | ||
222 | Thread.Sleep(1000); | ||
223 | } | ||
224 | 262 | ||
225 | lock (queues) | 263 | lock (queues) |
226 | { | ||
227 | queues.Remove(agentID); | 264 | queues.Remove(agentID); |
228 | } | ||
229 | 265 | ||
230 | List<UUID> removeitems = new List<UUID>(); | 266 | List<UUID> removeitems = new List<UUID>(); |
231 | lock (m_AvatarQueueUUIDMapping) | 267 | lock (m_AvatarQueueUUIDMapping) |
232 | { | 268 | m_AvatarQueueUUIDMapping.Remove(agentID); |
233 | foreach (UUID ky in m_AvatarQueueUUIDMapping.Keys) | ||
234 | { | ||
235 | // m_log.DebugFormat("[EVENTQUEUE]: Found key {0} in m_AvatarQueueUUIDMapping while looking for {1}", ky, AgentID); | ||
236 | if (ky == agentID) | ||
237 | { | ||
238 | removeitems.Add(ky); | ||
239 | } | ||
240 | } | ||
241 | |||
242 | foreach (UUID ky in removeitems) | ||
243 | { | ||
244 | UUID eventQueueGetUuid = m_AvatarQueueUUIDMapping[ky]; | ||
245 | m_AvatarQueueUUIDMapping.Remove(ky); | ||
246 | |||
247 | string eqgPath = GenerateEqgCapPath(eventQueueGetUuid); | ||
248 | MainServer.Instance.RemovePollServiceHTTPHandler("", eqgPath); | ||
249 | |||
250 | // m_log.DebugFormat( | ||
251 | // "[EVENT QUEUE GET MODULE]: Removed EQG handler {0} for {1} in {2}", | ||
252 | // eqgPath, agentID, m_scene.RegionInfo.RegionName); | ||
253 | } | ||
254 | } | ||
255 | 269 | ||
256 | UUID searchval = UUID.Zero; | 270 | UUID searchval = UUID.Zero; |
257 | 271 | ||
@@ -272,19 +286,9 @@ namespace OpenSim.Region.ClientStack.Linden | |||
272 | foreach (UUID ky in removeitems) | 286 | foreach (UUID ky in removeitems) |
273 | m_QueueUUIDAvatarMapping.Remove(ky); | 287 | m_QueueUUIDAvatarMapping.Remove(ky); |
274 | } | 288 | } |
275 | } | ||
276 | 289 | ||
277 | private void MakeChildAgent(ScenePresence avatar) | 290 | // m_log.DebugFormat("[EVENTQUEUE]: Deleted queues for {0} in region {1}", agentID, m_scene.RegionInfo.RegionName); |
278 | { | 291 | |
279 | //m_log.DebugFormat("[EVENTQUEUE]: Make Child agent {0} in region {1}.", avatar.UUID, m_scene.RegionInfo.RegionName); | ||
280 | //lock (m_ids) | ||
281 | // { | ||
282 | //if (m_ids.ContainsKey(avatar.UUID)) | ||
283 | //{ | ||
284 | // close the event queue. | ||
285 | //m_ids[avatar.UUID] = -1; | ||
286 | //} | ||
287 | //} | ||
288 | } | 292 | } |
289 | 293 | ||
290 | /// <summary> | 294 | /// <summary> |
@@ -300,9 +304,10 @@ namespace OpenSim.Region.ClientStack.Linden | |||
300 | { | 304 | { |
301 | // Register an event queue for the client | 305 | // Register an event queue for the client |
302 | 306 | ||
303 | //m_log.DebugFormat( | 307 | if (DebugLevel > 0) |
304 | // "[EVENTQUEUE]: OnRegisterCaps: agentID {0} caps {1} region {2}", | 308 | m_log.DebugFormat( |
305 | // agentID, caps, m_scene.RegionInfo.RegionName); | 309 | "[EVENTQUEUE]: OnRegisterCaps: agentID {0} caps {1} region {2}", |
310 | agentID, caps, m_scene.RegionInfo.RegionName); | ||
306 | 311 | ||
307 | // Let's instantiate a Queue for this agent right now | 312 | // Let's instantiate a Queue for this agent right now |
308 | TryGetQueue(agentID); | 313 | TryGetQueue(agentID); |
@@ -336,28 +341,9 @@ namespace OpenSim.Region.ClientStack.Linden | |||
336 | m_AvatarQueueUUIDMapping.Add(agentID, eventQueueGetUUID); | 341 | m_AvatarQueueUUIDMapping.Add(agentID, eventQueueGetUUID); |
337 | } | 342 | } |
338 | 343 | ||
339 | string eventQueueGetPath = GenerateEqgCapPath(eventQueueGetUUID); | 344 | caps.RegisterPollHandler( |
340 | 345 | "EventQueueGet", | |
341 | // Register this as a caps handler | 346 | new PollServiceEventArgs(null, GenerateEqgCapPath(eventQueueGetUUID), HasEvents, GetEvents, NoEvents, agentID, SERVER_EQ_TIME_NO_EVENTS)); |
342 | // FIXME: Confusingly, we need to register separate as a capability so that the client is told about | ||
343 | // EventQueueGet when it receive capability information, but then we replace the rest handler immediately | ||
344 | // afterwards with the poll service. So for now, we'll pass a null instead to simplify code reading, but | ||
345 | // really it should be possible to directly register the poll handler as a capability. | ||
346 | caps.RegisterHandler("EventQueueGet", new RestHTTPHandler("POST", eventQueueGetPath, null)); | ||
347 | // delegate(Hashtable m_dhttpMethod) | ||
348 | // { | ||
349 | // return ProcessQueue(m_dhttpMethod, agentID, caps); | ||
350 | // })); | ||
351 | |||
352 | // This will persist this beyond the expiry of the caps handlers | ||
353 | // TODO: Add EventQueueGet name/description for diagnostics | ||
354 | MainServer.Instance.AddPollServiceHTTPHandler( | ||
355 | eventQueueGetPath, | ||
356 | new PollServiceEventArgs(null, HasEvents, GetEvents, NoEvents, agentID)); | ||
357 | |||
358 | // m_log.DebugFormat( | ||
359 | // "[EVENT QUEUE GET MODULE]: Registered EQG handler {0} for {1} in {2}", | ||
360 | // eventQueueGetPath, agentID, m_scene.RegionInfo.RegionName); | ||
361 | 347 | ||
362 | Random rnd = new Random(Environment.TickCount); | 348 | Random rnd = new Random(Environment.TickCount); |
363 | lock (m_ids) | 349 | lock (m_ids) |
@@ -375,7 +361,10 @@ namespace OpenSim.Region.ClientStack.Linden | |||
375 | Queue<OSD> queue = GetQueue(agentID); | 361 | Queue<OSD> queue = GetQueue(agentID); |
376 | if (queue != null) | 362 | if (queue != null) |
377 | lock (queue) | 363 | lock (queue) |
364 | { | ||
365 | //m_log.WarnFormat("POLLED FOR EVENTS BY {0} in {1} -- {2}", agentID, m_scene.RegionInfo.RegionName, queue.Count); | ||
378 | return queue.Count > 0; | 366 | return queue.Count > 0; |
367 | } | ||
379 | 368 | ||
380 | return false; | 369 | return false; |
381 | } | 370 | } |
@@ -391,16 +380,21 @@ namespace OpenSim.Region.ClientStack.Linden | |||
391 | OSDMap ev = (OSDMap)element; | 380 | OSDMap ev = (OSDMap)element; |
392 | m_log.DebugFormat( | 381 | m_log.DebugFormat( |
393 | "Eq OUT {0,-30} to {1,-20} {2,-20}", | 382 | "Eq OUT {0,-30} to {1,-20} {2,-20}", |
394 | ev["message"], m_scene.GetScenePresence(agentId).Name, m_scene.RegionInfo.RegionName); | 383 | ev["message"], m_scene.GetScenePresence(agentId).Name, m_scene.Name); |
395 | } | 384 | } |
396 | } | 385 | } |
397 | 386 | ||
398 | public Hashtable GetEvents(UUID requestID, UUID pAgentId, string request) | 387 | public Hashtable GetEvents(UUID requestID, UUID pAgentId) |
399 | { | 388 | { |
400 | if (DebugLevel >= 2) | 389 | if (DebugLevel >= 2) |
401 | m_log.DebugFormat("POLLED FOR EQ MESSAGES BY {0} in {1}", pAgentId, m_scene.RegionInfo.RegionName); | 390 | m_log.WarnFormat("POLLED FOR EQ MESSAGES BY {0} in {1}", pAgentId, m_scene.Name); |
391 | |||
392 | Queue<OSD> queue = GetQueue(pAgentId); | ||
393 | if (queue == null) | ||
394 | { | ||
395 | return NoEvents(requestID, pAgentId); | ||
396 | } | ||
402 | 397 | ||
403 | Queue<OSD> queue = TryGetQueue(pAgentId); | ||
404 | OSD element; | 398 | OSD element; |
405 | lock (queue) | 399 | lock (queue) |
406 | { | 400 | { |
@@ -727,34 +721,51 @@ namespace OpenSim.Region.ClientStack.Linden | |||
727 | Enqueue(item, avatarID); | 721 | Enqueue(item, avatarID); |
728 | } | 722 | } |
729 | 723 | ||
730 | public virtual void EnableSimulator(ulong handle, IPEndPoint endPoint, UUID avatarID) | 724 | public virtual void EnableSimulator(ulong handle, IPEndPoint endPoint, UUID avatarID, int regionSizeX, int regionSizeY) |
731 | { | 725 | { |
732 | OSD item = EventQueueHelper.EnableSimulator(handle, endPoint); | 726 | if (DebugLevel > 0) |
727 | m_log.DebugFormat("{0} EnableSimulator. handle={1}, endPoint={2}, avatarID={3}", | ||
728 | LogHeader, handle, endPoint, avatarID, regionSizeX, regionSizeY); | ||
729 | |||
730 | OSD item = EventQueueHelper.EnableSimulator(handle, endPoint, regionSizeX, regionSizeY); | ||
733 | Enqueue(item, avatarID); | 731 | Enqueue(item, avatarID); |
734 | } | 732 | } |
735 | 733 | ||
736 | public virtual void EstablishAgentCommunication(UUID avatarID, IPEndPoint endPoint, string capsPath) | 734 | public virtual void EstablishAgentCommunication(UUID avatarID, IPEndPoint endPoint, string capsPath, |
735 | ulong regionHandle, int regionSizeX, int regionSizeY) | ||
737 | { | 736 | { |
738 | OSD item = EventQueueHelper.EstablishAgentCommunication(avatarID, endPoint.ToString(), capsPath); | 737 | if (DebugLevel > 0) |
738 | m_log.DebugFormat("{0} EstablishAgentCommunication. handle={1}, endPoint={2}, avatarID={3}", | ||
739 | LogHeader, regionHandle, endPoint, avatarID, regionSizeX, regionSizeY); | ||
740 | |||
741 | OSD item = EventQueueHelper.EstablishAgentCommunication(avatarID, endPoint.ToString(), capsPath, regionHandle, regionSizeX, regionSizeY); | ||
739 | Enqueue(item, avatarID); | 742 | Enqueue(item, avatarID); |
740 | } | 743 | } |
741 | 744 | ||
742 | public virtual void TeleportFinishEvent(ulong regionHandle, byte simAccess, | 745 | public virtual void TeleportFinishEvent(ulong regionHandle, byte simAccess, |
743 | IPEndPoint regionExternalEndPoint, | 746 | IPEndPoint regionExternalEndPoint, |
744 | uint locationID, uint flags, string capsURL, | 747 | uint locationID, uint flags, string capsURL, |
745 | UUID avatarID) | 748 | UUID avatarID, int regionSizeX, int regionSizeY) |
746 | { | 749 | { |
750 | if (DebugLevel > 0) | ||
751 | m_log.DebugFormat("{0} TeleportFinishEvent. handle={1}, endPoint={2}, avatarID={3}", | ||
752 | LogHeader, regionHandle, regionExternalEndPoint, avatarID, regionSizeX, regionSizeY); | ||
753 | |||
747 | OSD item = EventQueueHelper.TeleportFinishEvent(regionHandle, simAccess, regionExternalEndPoint, | 754 | OSD item = EventQueueHelper.TeleportFinishEvent(regionHandle, simAccess, regionExternalEndPoint, |
748 | locationID, flags, capsURL, avatarID); | 755 | locationID, flags, capsURL, avatarID, regionSizeX, regionSizeY); |
749 | Enqueue(item, avatarID); | 756 | Enqueue(item, avatarID); |
750 | } | 757 | } |
751 | 758 | ||
752 | public virtual void CrossRegion(ulong handle, Vector3 pos, Vector3 lookAt, | 759 | public virtual void CrossRegion(ulong handle, Vector3 pos, Vector3 lookAt, |
753 | IPEndPoint newRegionExternalEndPoint, | 760 | IPEndPoint newRegionExternalEndPoint, |
754 | string capsURL, UUID avatarID, UUID sessionID) | 761 | string capsURL, UUID avatarID, UUID sessionID, int regionSizeX, int regionSizeY) |
755 | { | 762 | { |
763 | if (DebugLevel > 0) | ||
764 | m_log.DebugFormat("{0} CrossRegion. handle={1}, avatarID={2}, regionSize={3},{4}>", | ||
765 | LogHeader, handle, avatarID, regionSizeX, regionSizeY); | ||
766 | |||
756 | OSD item = EventQueueHelper.CrossRegion(handle, pos, lookAt, newRegionExternalEndPoint, | 767 | OSD item = EventQueueHelper.CrossRegion(handle, pos, lookAt, newRegionExternalEndPoint, |
757 | capsURL, avatarID, sessionID); | 768 | capsURL, avatarID, sessionID, regionSizeX, regionSizeY); |
758 | Enqueue(item, avatarID); | 769 | Enqueue(item, avatarID); |
759 | } | 770 | } |
760 | 771 | ||
@@ -771,12 +782,12 @@ namespace OpenSim.Region.ClientStack.Linden | |||
771 | 782 | ||
772 | } | 783 | } |
773 | 784 | ||
774 | public void ChatterBoxSessionAgentListUpdates(UUID sessionID, UUID fromAgent, UUID toAgent, bool canVoiceChat, | 785 | public void ChatterBoxSessionAgentListUpdates(UUID sessionID, UUID fromAgent, UUID anotherAgent, bool canVoiceChat, |
775 | bool isModerator, bool textMute) | 786 | bool isModerator, bool textMute) |
776 | { | 787 | { |
777 | OSD item = EventQueueHelper.ChatterBoxSessionAgentListUpdates(sessionID, fromAgent, canVoiceChat, | 788 | OSD item = EventQueueHelper.ChatterBoxSessionAgentListUpdates(sessionID, fromAgent, canVoiceChat, |
778 | isModerator, textMute); | 789 | isModerator, textMute); |
779 | Enqueue(item, toAgent); | 790 | Enqueue(item, fromAgent); |
780 | //m_log.InfoFormat("########### eq ChatterBoxSessionAgentListUpdates #############\n{0}", item); | 791 | //m_log.InfoFormat("########### eq ChatterBoxSessionAgentListUpdates #############\n{0}", item); |
781 | } | 792 | } |
782 | 793 | ||
@@ -807,5 +818,13 @@ namespace OpenSim.Region.ClientStack.Linden | |||
807 | { | 818 | { |
808 | return EventQueueHelper.BuildEvent(eventName, eventBody); | 819 | return EventQueueHelper.BuildEvent(eventName, eventBody); |
809 | } | 820 | } |
821 | |||
822 | public void partPhysicsProperties(uint localID, byte physhapetype, | ||
823 | float density, float friction, float bounce, float gravmod,UUID avatarID) | ||
824 | { | ||
825 | OSD item = EventQueueHelper.partPhysicsProperties(localID, physhapetype, | ||
826 | density, friction, bounce, gravmod); | ||
827 | Enqueue(item, avatarID); | ||
828 | } | ||
810 | } | 829 | } |
811 | } | 830 | } \ No newline at end of file |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueHelper.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueHelper.cs index 3f49aba..384af74 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", OSD.FromUInteger((uint) regionSizeX)); | ||
81 | llsdSimInfo.Add("RegionSizeY", OSD.FromUInteger((uint) 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", OSD.FromUInteger((uint)regionSizeX)); | ||
142 | regionDataMap.Add("RegionSizeY", OSD.FromUInteger((uint)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 | OSDMap info = new OSDMap(); | 160 | OSDMap info = new OSDMap(); |
155 | info.Add("AgentID", OSD.FromUUID(agentID)); | 161 | info.Add("AgentID", OSD.FromUUID(agentID)); |
@@ -160,6 +166,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
160 | info.Add("SimIP", OSD.FromBinary(regionExternalEndPoint.Address.GetAddressBytes())); | 166 | info.Add("SimIP", OSD.FromBinary(regionExternalEndPoint.Address.GetAddressBytes())); |
161 | info.Add("SimPort", OSD.FromInteger(regionExternalEndPoint.Port)); | 167 | info.Add("SimPort", OSD.FromInteger(regionExternalEndPoint.Port)); |
162 | info.Add("TeleportFlags", OSD.FromULong(1L << 4)); // AgentManager.TeleportFlags.ViaLocation | 168 | info.Add("TeleportFlags", OSD.FromULong(1L << 4)); // AgentManager.TeleportFlags.ViaLocation |
169 | info.Add("RegionSizeX", OSD.FromUInteger((uint)regionSizeX)); | ||
170 | info.Add("RegionSizeY", OSD.FromUInteger((uint)regionSizeY)); | ||
163 | 171 | ||
164 | OSDArray infoArr = new OSDArray(); | 172 | OSDArray infoArr = new OSDArray(); |
165 | infoArr.Add(info); | 173 | infoArr.Add(info); |
@@ -187,12 +195,18 @@ namespace OpenSim.Region.ClientStack.Linden | |||
187 | return BuildEvent("ScriptRunningReply", body); | 195 | return BuildEvent("ScriptRunningReply", body); |
188 | } | 196 | } |
189 | 197 | ||
190 | public static OSD EstablishAgentCommunication(UUID agentID, string simIpAndPort, string seedcap) | 198 | public static OSD EstablishAgentCommunication(UUID agentID, string simIpAndPort, string seedcap, |
199 | ulong regionHandle, int regionSizeX, int regionSizeY) | ||
191 | { | 200 | { |
192 | OSDMap body = new OSDMap(3); | 201 | OSDMap body = new OSDMap(6) |
193 | body.Add("agent-id", new OSDUUID(agentID)); | 202 | { |
194 | body.Add("sim-ip-and-port", new OSDString(simIpAndPort)); | 203 | {"agent-id", new OSDUUID(agentID)}, |
195 | body.Add("seed-capability", new OSDString(seedcap)); | 204 | {"sim-ip-and-port", new OSDString(simIpAndPort)}, |
205 | {"seed-capability", new OSDString(seedcap)}, | ||
206 | {"region-handle", OSD.FromULong(regionHandle)}, | ||
207 | {"region-size-x", OSD.FromInteger(regionSizeX)}, | ||
208 | {"region-size-y", OSD.FromInteger(regionSizeY)} | ||
209 | }; | ||
196 | 210 | ||
197 | return BuildEvent("EstablishAgentCommunication", body); | 211 | return BuildEvent("EstablishAgentCommunication", body); |
198 | } | 212 | } |
@@ -395,5 +409,25 @@ namespace OpenSim.Region.ClientStack.Linden | |||
395 | return message; | 409 | return message; |
396 | } | 410 | } |
397 | 411 | ||
412 | public static OSD partPhysicsProperties(uint localID, byte physhapetype, | ||
413 | float density, float friction, float bounce, float gravmod) | ||
414 | { | ||
415 | |||
416 | OSDMap physinfo = new OSDMap(6); | ||
417 | physinfo["LocalID"] = localID; | ||
418 | physinfo["Density"] = density; | ||
419 | physinfo["Friction"] = friction; | ||
420 | physinfo["GravityMultiplier"] = gravmod; | ||
421 | physinfo["Restitution"] = bounce; | ||
422 | physinfo["PhysicsShapeType"] = (int)physhapetype; | ||
423 | |||
424 | OSDArray array = new OSDArray(1); | ||
425 | array.Add(physinfo); | ||
426 | |||
427 | OSDMap llsdBody = new OSDMap(1); | ||
428 | llsdBody.Add("ObjectData", array); | ||
429 | |||
430 | return BuildEvent("ObjectPhysicsProperties", llsdBody); | ||
431 | } | ||
398 | } | 432 | } |
399 | } | 433 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs index ed8ec16..16a902d 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs | |||
@@ -26,6 +26,7 @@ | |||
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections; | ||
29 | using System.Collections.Generic; | 30 | using System.Collections.Generic; |
30 | using System.Net; | 31 | using System.Net; |
31 | using log4net.Config; | 32 | using log4net.Config; |
@@ -33,13 +34,15 @@ using Nini.Config; | |||
33 | using NUnit.Framework; | 34 | using NUnit.Framework; |
34 | using OpenMetaverse; | 35 | using OpenMetaverse; |
35 | using OpenMetaverse.Packets; | 36 | using OpenMetaverse.Packets; |
37 | using OpenMetaverse.StructuredData; | ||
36 | using OpenSim.Framework; | 38 | using OpenSim.Framework; |
37 | using OpenSim.Framework.Servers; | 39 | using OpenSim.Framework.Servers; |
38 | using OpenSim.Framework.Servers.HttpServer; | 40 | using OpenSim.Framework.Servers.HttpServer; |
39 | using OpenSim.Region.ClientStack.Linden; | 41 | using OpenSim.Region.ClientStack.Linden; |
40 | using OpenSim.Region.CoreModules.Framework; | 42 | using OpenSim.Region.CoreModules.Framework; |
43 | using OpenSim.Region.Framework.Scenes; | ||
44 | using OpenSim.Region.OptionalModules.World.NPC; | ||
41 | using OpenSim.Tests.Common; | 45 | using OpenSim.Tests.Common; |
42 | using OpenSim.Tests.Common.Mock; | ||
43 | 46 | ||
44 | namespace OpenSim.Region.ClientStack.Linden.Tests | 47 | namespace OpenSim.Region.ClientStack.Linden.Tests |
45 | { | 48 | { |
@@ -47,10 +50,14 @@ namespace OpenSim.Region.ClientStack.Linden.Tests | |||
47 | public class EventQueueTests : OpenSimTestCase | 50 | public class EventQueueTests : OpenSimTestCase |
48 | { | 51 | { |
49 | private TestScene m_scene; | 52 | private TestScene m_scene; |
53 | private EventQueueGetModule m_eqgMod; | ||
54 | private NPCModule m_npcMod; | ||
50 | 55 | ||
51 | [SetUp] | 56 | [SetUp] |
52 | public void SetUp() | 57 | public override void SetUp() |
53 | { | 58 | { |
59 | base.SetUp(); | ||
60 | |||
54 | uint port = 9999; | 61 | uint port = 9999; |
55 | uint sslPort = 9998; | 62 | uint sslPort = 9998; |
56 | 63 | ||
@@ -67,14 +74,19 @@ namespace OpenSim.Region.ClientStack.Linden.Tests | |||
67 | config.Configs["Startup"].Set("EventQueue", "true"); | 74 | config.Configs["Startup"].Set("EventQueue", "true"); |
68 | 75 | ||
69 | CapabilitiesModule capsModule = new CapabilitiesModule(); | 76 | CapabilitiesModule capsModule = new CapabilitiesModule(); |
70 | EventQueueGetModule eqgModule = new EventQueueGetModule(); | 77 | m_eqgMod = new EventQueueGetModule(); |
78 | |||
79 | // For NPC test support | ||
80 | config.AddConfig("NPC"); | ||
81 | config.Configs["NPC"].Set("Enabled", "true"); | ||
82 | m_npcMod = new NPCModule(); | ||
71 | 83 | ||
72 | m_scene = new SceneHelpers().SetupScene(); | 84 | m_scene = new SceneHelpers().SetupScene(); |
73 | SceneHelpers.SetupSceneModules(m_scene, config, capsModule, eqgModule); | 85 | SceneHelpers.SetupSceneModules(m_scene, config, capsModule, m_eqgMod, m_npcMod); |
74 | } | 86 | } |
75 | 87 | ||
76 | [Test] | 88 | [Test] |
77 | public void AddForClient() | 89 | public void TestAddForClient() |
78 | { | 90 | { |
79 | TestHelpers.InMethod(); | 91 | TestHelpers.InMethod(); |
80 | // log4net.Config.XmlConfigurator.Configure(); | 92 | // log4net.Config.XmlConfigurator.Configure(); |
@@ -86,18 +98,93 @@ namespace OpenSim.Region.ClientStack.Linden.Tests | |||
86 | } | 98 | } |
87 | 99 | ||
88 | [Test] | 100 | [Test] |
89 | public void RemoveForClient() | 101 | public void TestRemoveForClient() |
90 | { | 102 | { |
91 | TestHelpers.InMethod(); | 103 | TestHelpers.InMethod(); |
92 | // log4net.Config.XmlConfigurator.Configure(); | 104 | // TestHelpers.EnableLogging(); |
93 | 105 | ||
94 | UUID spId = TestHelpers.ParseTail(0x1); | 106 | UUID spId = TestHelpers.ParseTail(0x1); |
95 | 107 | ||
96 | SceneHelpers.AddScenePresence(m_scene, spId); | 108 | SceneHelpers.AddScenePresence(m_scene, spId); |
97 | m_scene.IncomingCloseAgent(spId, false); | 109 | m_scene.CloseAgent(spId, false); |
98 | 110 | ||
99 | // TODO: Add more assertions for the other aspects of event queues | 111 | // TODO: Add more assertions for the other aspects of event queues |
100 | Assert.That(MainServer.Instance.GetPollServiceHandlerKeys().Count, Is.EqualTo(0)); | 112 | Assert.That(MainServer.Instance.GetPollServiceHandlerKeys().Count, Is.EqualTo(0)); |
101 | } | 113 | } |
114 | |||
115 | [Test] | ||
116 | public void TestEnqueueMessage() | ||
117 | { | ||
118 | TestHelpers.InMethod(); | ||
119 | // log4net.Config.XmlConfigurator.Configure(); | ||
120 | |||
121 | ScenePresence sp = SceneHelpers.AddScenePresence(m_scene, TestHelpers.ParseTail(0x1)); | ||
122 | |||
123 | string messageName = "TestMessage"; | ||
124 | |||
125 | m_eqgMod.Enqueue(m_eqgMod.BuildEvent(messageName, new OSDMap()), sp.UUID); | ||
126 | |||
127 | Hashtable eventsResponse = m_eqgMod.GetEvents(UUID.Zero, sp.UUID); | ||
128 | |||
129 | Assert.That((int)eventsResponse["int_response_code"], Is.EqualTo((int)HttpStatusCode.OK)); | ||
130 | |||
131 | // Console.WriteLine("Response [{0}]", (string)eventsResponse["str_response_string"]); | ||
132 | |||
133 | OSDMap rawOsd = (OSDMap)OSDParser.DeserializeLLSDXml((string)eventsResponse["str_response_string"]); | ||
134 | OSDArray eventsOsd = (OSDArray)rawOsd["events"]; | ||
135 | |||
136 | bool foundUpdate = false; | ||
137 | foreach (OSD osd in eventsOsd) | ||
138 | { | ||
139 | OSDMap eventOsd = (OSDMap)osd; | ||
140 | |||
141 | if (eventOsd["message"] == messageName) | ||
142 | foundUpdate = true; | ||
143 | } | ||
144 | |||
145 | Assert.That(foundUpdate, Is.True, string.Format("Did not find {0} in response", messageName)); | ||
146 | } | ||
147 | |||
148 | /// <summary> | ||
149 | /// Test an attempt to put a message on the queue of a user that is not in the region. | ||
150 | /// </summary> | ||
151 | [Test] | ||
152 | public void TestEnqueueMessageNoUser() | ||
153 | { | ||
154 | TestHelpers.InMethod(); | ||
155 | TestHelpers.EnableLogging(); | ||
156 | |||
157 | string messageName = "TestMessage"; | ||
158 | |||
159 | m_eqgMod.Enqueue(m_eqgMod.BuildEvent(messageName, new OSDMap()), TestHelpers.ParseTail(0x1)); | ||
160 | |||
161 | Hashtable eventsResponse = m_eqgMod.GetEvents(UUID.Zero, TestHelpers.ParseTail(0x1)); | ||
162 | |||
163 | Assert.That((int)eventsResponse["int_response_code"], Is.EqualTo((int)HttpStatusCode.BadGateway)); | ||
164 | } | ||
165 | |||
166 | /// <summary> | ||
167 | /// NPCs do not currently have an event queue but a caller may try to send a message anyway, so check behaviour. | ||
168 | /// </summary> | ||
169 | [Test] | ||
170 | public void TestEnqueueMessageToNpc() | ||
171 | { | ||
172 | TestHelpers.InMethod(); | ||
173 | // TestHelpers.EnableLogging(); | ||
174 | |||
175 | UUID npcId | ||
176 | = m_npcMod.CreateNPC( | ||
177 | "John", "Smith", new Vector3(128, 128, 30), UUID.Zero, true, m_scene, new AvatarAppearance()); | ||
178 | |||
179 | ScenePresence npc = m_scene.GetScenePresence(npcId); | ||
180 | |||
181 | string messageName = "TestMessage"; | ||
182 | |||
183 | m_eqgMod.Enqueue(m_eqgMod.BuildEvent(messageName, new OSDMap()), npc.UUID); | ||
184 | |||
185 | Hashtable eventsResponse = m_eqgMod.GetEvents(UUID.Zero, npc.UUID); | ||
186 | |||
187 | Assert.That((int)eventsResponse["int_response_code"], Is.EqualTo((int)HttpStatusCode.BadGateway)); | ||
188 | } | ||
102 | } | 189 | } |
103 | } \ No newline at end of file | 190 | } \ No newline at end of file |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/FetchInventory2Module.cs b/OpenSim/Region/ClientStack/Linden/Caps/FetchInventory2Module.cs index 87d3d1c..e0a11cc 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/FetchInventory2Module.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/FetchInventory2Module.cs | |||
@@ -25,20 +25,16 @@ | |||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Reflection; | ||
31 | using log4net; | ||
32 | using Nini.Config; | ||
33 | using Mono.Addins; | 28 | using Mono.Addins; |
29 | using Nini.Config; | ||
34 | using OpenMetaverse; | 30 | using OpenMetaverse; |
35 | using OpenSim.Framework; | 31 | using OpenSim.Capabilities.Handlers; |
36 | using OpenSim.Framework.Servers.HttpServer; | 32 | using OpenSim.Framework.Servers.HttpServer; |
37 | using OpenSim.Region.Framework.Interfaces; | 33 | using OpenSim.Region.Framework.Interfaces; |
38 | using OpenSim.Region.Framework.Scenes; | 34 | using OpenSim.Region.Framework.Scenes; |
39 | using OpenSim.Services.Interfaces; | 35 | using OpenSim.Services.Interfaces; |
36 | using System; | ||
40 | using Caps = OpenSim.Framework.Capabilities.Caps; | 37 | using Caps = OpenSim.Framework.Capabilities.Caps; |
41 | using OpenSim.Capabilities.Handlers; | ||
42 | 38 | ||
43 | namespace OpenSim.Region.ClientStack.Linden | 39 | namespace OpenSim.Region.ClientStack.Linden |
44 | { | 40 | { |
@@ -58,8 +54,6 @@ namespace OpenSim.Region.ClientStack.Linden | |||
58 | 54 | ||
59 | private string m_fetchInventory2Url; | 55 | private string m_fetchInventory2Url; |
60 | 56 | ||
61 | private FetchInventory2Handler m_fetchHandler; | ||
62 | |||
63 | #region ISharedRegionModule Members | 57 | #region ISharedRegionModule Members |
64 | 58 | ||
65 | public void Initialise(IConfigSource source) | 59 | public void Initialise(IConfigSource source) |
@@ -98,10 +92,6 @@ namespace OpenSim.Region.ClientStack.Linden | |||
98 | 92 | ||
99 | m_inventoryService = m_scene.InventoryService; | 93 | m_inventoryService = m_scene.InventoryService; |
100 | 94 | ||
101 | // We'll reuse the same handler for all requests. | ||
102 | if (m_fetchInventory2Url == "localhost") | ||
103 | m_fetchHandler = new FetchInventory2Handler(m_inventoryService); | ||
104 | |||
105 | m_scene.EventManager.OnRegisterCaps += RegisterCaps; | 95 | m_scene.EventManager.OnRegisterCaps += RegisterCaps; |
106 | } | 96 | } |
107 | 97 | ||
@@ -131,9 +121,11 @@ namespace OpenSim.Region.ClientStack.Linden | |||
131 | { | 121 | { |
132 | capUrl = "/CAPS/" + UUID.Random(); | 122 | capUrl = "/CAPS/" + UUID.Random(); |
133 | 123 | ||
124 | FetchInventory2Handler fetchHandler = new FetchInventory2Handler(m_inventoryService, agentID); | ||
125 | |||
134 | IRequestHandler reqHandler | 126 | IRequestHandler reqHandler |
135 | = new RestStreamHandler( | 127 | = new RestStreamHandler( |
136 | "POST", capUrl, m_fetchHandler.FetchInventoryRequest, capName, agentID.ToString()); | 128 | "POST", capUrl, fetchHandler.FetchInventoryRequest, capName, agentID.ToString()); |
137 | 129 | ||
138 | caps.RegisterHandler(capName, reqHandler); | 130 | caps.RegisterHandler(capName, reqHandler); |
139 | } | 131 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetDisplayNamesModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetDisplayNamesModule.cs new file mode 100644 index 0000000..6617bbc --- /dev/null +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetDisplayNamesModule.cs | |||
@@ -0,0 +1,144 @@ | |||
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 OpenMetaverse.Imaging; | ||
42 | using OpenSim.Framework; | ||
43 | using OpenSim.Framework.Servers; | ||
44 | using OpenSim.Framework.Servers.HttpServer; | ||
45 | using OpenSim.Region.Framework.Interfaces; | ||
46 | using OpenSim.Region.Framework.Scenes; | ||
47 | using OpenSim.Services.Interfaces; | ||
48 | using Caps = OpenSim.Framework.Capabilities.Caps; | ||
49 | using OpenSim.Capabilities.Handlers; | ||
50 | |||
51 | namespace OpenSim.Region.ClientStack.Linden | ||
52 | { | ||
53 | |||
54 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GetDisplayNamesModule")] | ||
55 | public class GetDisplayNamesModule : INonSharedRegionModule | ||
56 | { | ||
57 | private static readonly ILog m_log = | ||
58 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
59 | |||
60 | private Scene m_scene; | ||
61 | private IUserManagement m_UserManager; | ||
62 | |||
63 | private bool m_Enabled = false; | ||
64 | |||
65 | private string m_URL; | ||
66 | |||
67 | #region ISharedRegionModule Members | ||
68 | |||
69 | public void Initialise(IConfigSource source) | ||
70 | { | ||
71 | IConfig config = source.Configs["ClientStack.LindenCaps"]; | ||
72 | if (config == null) | ||
73 | return; | ||
74 | |||
75 | m_URL = config.GetString("Cap_GetDisplayNames", string.Empty); | ||
76 | if (m_URL != string.Empty) | ||
77 | m_Enabled = true; | ||
78 | } | ||
79 | |||
80 | public void AddRegion(Scene s) | ||
81 | { | ||
82 | if (!m_Enabled) | ||
83 | return; | ||
84 | |||
85 | m_scene = s; | ||
86 | } | ||
87 | |||
88 | public void RemoveRegion(Scene s) | ||
89 | { | ||
90 | if (!m_Enabled) | ||
91 | return; | ||
92 | |||
93 | m_scene.EventManager.OnRegisterCaps -= RegisterCaps; | ||
94 | m_scene = null; | ||
95 | } | ||
96 | |||
97 | public void RegionLoaded(Scene s) | ||
98 | { | ||
99 | if (!m_Enabled) | ||
100 | return; | ||
101 | |||
102 | m_UserManager = m_scene.RequestModuleInterface<IUserManagement>(); | ||
103 | m_scene.EventManager.OnRegisterCaps += RegisterCaps; | ||
104 | } | ||
105 | |||
106 | public void PostInitialise() | ||
107 | { | ||
108 | } | ||
109 | |||
110 | public void Close() { } | ||
111 | |||
112 | public string Name { get { return "GetDisplayNamesModule"; } } | ||
113 | |||
114 | public Type ReplaceableInterface | ||
115 | { | ||
116 | get { return null; } | ||
117 | } | ||
118 | |||
119 | #endregion | ||
120 | |||
121 | public void RegisterCaps(UUID agentID, Caps caps) | ||
122 | { | ||
123 | UUID capID = UUID.Random(); | ||
124 | |||
125 | if (m_URL == "localhost") | ||
126 | { | ||
127 | m_log.DebugFormat("[GET_DISPLAY_NAMES]: /CAPS/agents/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); | ||
128 | caps.RegisterHandler( | ||
129 | "GetDisplayNames", | ||
130 | new GetDisplayNamesHandler("/CAPS/agents" + capID + "/", m_UserManager, "GetDisplayNames", agentID.ToString())); | ||
131 | } | ||
132 | else | ||
133 | { | ||
134 | // m_log.DebugFormat("[GETTEXTURE]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName); | ||
135 | IExternalCapsModule handler = m_scene.RequestModuleInterface<IExternalCapsModule>(); | ||
136 | if (handler != null) | ||
137 | handler.RegisterExternalUserCapsHandler(agentID,caps,"GetDisplayNames", m_URL); | ||
138 | else | ||
139 | caps.RegisterHandler("GetDisplayNames", m_URL); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | } | ||
144 | } | ||
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs index 8e1f63a..f57d857 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs | |||
@@ -57,6 +57,9 @@ namespace OpenSim.Region.ClientStack.Linden | |||
57 | private IAssetService m_AssetService; | 57 | private IAssetService m_AssetService; |
58 | private bool m_Enabled = true; | 58 | private bool m_Enabled = true; |
59 | private string m_URL; | 59 | private string m_URL; |
60 | private string m_URL2; | ||
61 | private string m_RedirectURL = null; | ||
62 | private string m_RedirectURL2 = null; | ||
60 | 63 | ||
61 | #region Region Module interfaceBase Members | 64 | #region Region Module interfaceBase Members |
62 | 65 | ||
@@ -74,7 +77,18 @@ namespace OpenSim.Region.ClientStack.Linden | |||
74 | m_URL = config.GetString("Cap_GetMesh", string.Empty); | 77 | m_URL = config.GetString("Cap_GetMesh", string.Empty); |
75 | // Cap doesn't exist | 78 | // Cap doesn't exist |
76 | if (m_URL != string.Empty) | 79 | if (m_URL != string.Empty) |
80 | { | ||
81 | m_Enabled = true; | ||
82 | m_RedirectURL = config.GetString("GetMeshRedirectURL"); | ||
83 | } | ||
84 | |||
85 | m_URL2 = config.GetString("Cap_GetMesh2", string.Empty); | ||
86 | // Cap doesn't exist | ||
87 | if (m_URL2 != string.Empty) | ||
88 | { | ||
77 | m_Enabled = true; | 89 | m_Enabled = true; |
90 | m_RedirectURL2 = config.GetString("GetMesh2RedirectURL"); | ||
91 | } | ||
78 | } | 92 | } |
79 | 93 | ||
80 | public void AddRegion(Scene pScene) | 94 | public void AddRegion(Scene pScene) |
@@ -113,28 +127,42 @@ namespace OpenSim.Region.ClientStack.Linden | |||
113 | 127 | ||
114 | public void RegisterCaps(UUID agentID, Caps caps) | 128 | public void RegisterCaps(UUID agentID, Caps caps) |
115 | { | 129 | { |
116 | // UUID capID = UUID.Random(); | 130 | UUID capID = UUID.Random(); |
131 | bool getMeshRegistered = false; | ||
117 | 132 | ||
118 | //caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture)); | 133 | if (m_URL == string.Empty) |
119 | if (m_URL == "localhost") | 134 | { |
135 | |||
136 | } | ||
137 | else if (m_URL == "localhost") | ||
120 | { | 138 | { |
121 | // m_log.DebugFormat("[GETMESH]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); | 139 | getMeshRegistered = true; |
122 | GetMeshHandler gmeshHandler = new GetMeshHandler(m_AssetService); | 140 | caps.RegisterHandler( |
123 | IRequestHandler reqHandler | 141 | "GetMesh", |
124 | = new RestHTTPHandler( | 142 | new GetMeshHandler("/CAPS/" + capID + "/", m_AssetService, "GetMesh", agentID.ToString(), m_RedirectURL)); |
125 | "GET", | ||
126 | "/CAPS/" + UUID.Random(), | ||
127 | httpMethod => gmeshHandler.ProcessGetMesh(httpMethod, UUID.Zero, null), | ||
128 | "GetMesh", | ||
129 | agentID.ToString()); | ||
130 | |||
131 | caps.RegisterHandler("GetMesh", reqHandler); | ||
132 | } | 143 | } |
133 | else | 144 | else |
134 | { | 145 | { |
135 | // m_log.DebugFormat("[GETMESH]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName); | ||
136 | caps.RegisterHandler("GetMesh", m_URL); | 146 | caps.RegisterHandler("GetMesh", m_URL); |
137 | } | 147 | } |
148 | |||
149 | if(m_URL2 == string.Empty) | ||
150 | { | ||
151 | |||
152 | } | ||
153 | else if (m_URL2 == "localhost") | ||
154 | { | ||
155 | if (!getMeshRegistered) | ||
156 | { | ||
157 | caps.RegisterHandler( | ||
158 | "GetMesh2", | ||
159 | new GetMeshHandler("/CAPS/" + capID + "/", m_AssetService, "GetMesh2", agentID.ToString(), m_RedirectURL2)); | ||
160 | } | ||
161 | } | ||
162 | else | ||
163 | { | ||
164 | caps.RegisterHandler("GetMesh2", m_URL2); | ||
165 | } | ||
138 | } | 166 | } |
139 | 167 | ||
140 | } | 168 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs index 13415f8..bb932f2 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs | |||
@@ -63,7 +63,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
63 | private bool m_Enabled = false; | 63 | private bool m_Enabled = false; |
64 | 64 | ||
65 | // TODO: Change this to a config option | 65 | // TODO: Change this to a config option |
66 | const string REDIRECT_URL = null; | 66 | private string m_RedirectURL = null; |
67 | 67 | ||
68 | private string m_URL; | 68 | private string m_URL; |
69 | 69 | ||
@@ -78,7 +78,10 @@ namespace OpenSim.Region.ClientStack.Linden | |||
78 | m_URL = config.GetString("Cap_GetTexture", string.Empty); | 78 | m_URL = config.GetString("Cap_GetTexture", string.Empty); |
79 | // Cap doesn't exist | 79 | // Cap doesn't exist |
80 | if (m_URL != string.Empty) | 80 | if (m_URL != string.Empty) |
81 | { | ||
81 | m_Enabled = true; | 82 | m_Enabled = true; |
83 | m_RedirectURL = config.GetString("GetTextureRedirectURL"); | ||
84 | } | ||
82 | } | 85 | } |
83 | 86 | ||
84 | public void AddRegion(Scene s) | 87 | public void AddRegion(Scene s) |
@@ -132,12 +135,16 @@ namespace OpenSim.Region.ClientStack.Linden | |||
132 | // m_log.DebugFormat("[GETTEXTURE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); | 135 | // m_log.DebugFormat("[GETTEXTURE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); |
133 | caps.RegisterHandler( | 136 | caps.RegisterHandler( |
134 | "GetTexture", | 137 | "GetTexture", |
135 | new GetTextureHandler("/CAPS/" + capID + "/", m_assetService, "GetTexture", agentID.ToString())); | 138 | new GetTextureHandler("/CAPS/" + capID + "/", m_assetService, "GetTexture", agentID.ToString(), m_RedirectURL)); |
136 | } | 139 | } |
137 | else | 140 | else |
138 | { | 141 | { |
139 | // m_log.DebugFormat("[GETTEXTURE]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName); | 142 | // m_log.DebugFormat("[GETTEXTURE]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName); |
140 | caps.RegisterHandler("GetTexture", m_URL); | 143 | IExternalCapsModule handler = m_scene.RequestModuleInterface<IExternalCapsModule>(); |
144 | if (handler != null) | ||
145 | handler.RegisterExternalUserCapsHandler(agentID,caps,"GetTexture", m_URL); | ||
146 | else | ||
147 | caps.RegisterHandler("GetTexture", m_URL); | ||
141 | } | 148 | } |
142 | } | 149 | } |
143 | 150 | ||
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/MeshUploadFlagModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/MeshUploadFlagModule.cs index 33b1f77..45d33cd 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/MeshUploadFlagModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/MeshUploadFlagModule.cs | |||
@@ -57,7 +57,6 @@ namespace OpenSim.Region.ClientStack.Linden | |||
57 | public bool Enabled { get; private set; } | 57 | public bool Enabled { get; private set; } |
58 | 58 | ||
59 | private Scene m_scene; | 59 | private Scene m_scene; |
60 | private UUID m_agentID; | ||
61 | 60 | ||
62 | #region ISharedRegionModule Members | 61 | #region ISharedRegionModule Members |
63 | 62 | ||
@@ -118,25 +117,26 @@ namespace OpenSim.Region.ClientStack.Linden | |||
118 | public void RegisterCaps(UUID agentID, Caps caps) | 117 | public void RegisterCaps(UUID agentID, Caps caps) |
119 | { | 118 | { |
120 | IRequestHandler reqHandler | 119 | IRequestHandler reqHandler |
121 | = new RestHTTPHandler("GET", "/CAPS/" + UUID.Random(), MeshUploadFlag, "MeshUploadFlag", agentID.ToString()); | 120 | = new RestHTTPHandler( |
121 | "GET", "/CAPS/" + UUID.Random(), ht => MeshUploadFlag(ht, agentID), "MeshUploadFlag", agentID.ToString()); | ||
122 | 122 | ||
123 | caps.RegisterHandler("MeshUploadFlag", reqHandler); | 123 | caps.RegisterHandler("MeshUploadFlag", reqHandler); |
124 | m_agentID = agentID; | 124 | |
125 | } | 125 | } |
126 | 126 | ||
127 | private Hashtable MeshUploadFlag(Hashtable mDhttpMethod) | 127 | private Hashtable MeshUploadFlag(Hashtable mDhttpMethod, UUID agentID) |
128 | { | 128 | { |
129 | // m_log.DebugFormat("[MESH UPLOAD FLAG MODULE]: MeshUploadFlag request"); | 129 | // m_log.DebugFormat("[MESH UPLOAD FLAG MODULE]: MeshUploadFlag request"); |
130 | 130 | ||
131 | OSDMap data = new OSDMap(); | 131 | OSDMap data = new OSDMap(); |
132 | ScenePresence sp = m_scene.GetScenePresence(m_agentID); | 132 | ScenePresence sp = m_scene.GetScenePresence(agentID); |
133 | data["username"] = sp.Firstname + "." + sp.Lastname; | 133 | data["username"] = sp.Firstname + "." + sp.Lastname; |
134 | data["display_name_next_update"] = new OSDDate(DateTime.Now); | 134 | data["display_name_next_update"] = new OSDDate(DateTime.Now); |
135 | data["legacy_first_name"] = sp.Firstname; | 135 | data["legacy_first_name"] = sp.Firstname; |
136 | data["mesh_upload_status"] = "valid"; | 136 | data["mesh_upload_status"] = "valid"; |
137 | data["display_name"] = sp.Firstname + " " + sp.Lastname; | 137 | data["display_name"] = sp.Firstname + " " + sp.Lastname; |
138 | data["legacy_last_name"] = sp.Lastname; | 138 | data["legacy_last_name"] = sp.Lastname; |
139 | data["id"] = m_agentID; | 139 | data["id"] = agentID; |
140 | data["is_display_name_default"] = true; | 140 | data["is_display_name_default"] = true; |
141 | 141 | ||
142 | //Send back data | 142 | //Send back data |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/NewFileAgentInventoryVariablePriceModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/NewFileAgentInventoryVariablePriceModule.cs index 5529550..f69a0bb 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/NewFileAgentInventoryVariablePriceModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/NewFileAgentInventoryVariablePriceModule.cs | |||
@@ -44,6 +44,7 @@ using OpenSim.Region.Framework.Scenes; | |||
44 | using OpenSim.Services.Interfaces; | 44 | using OpenSim.Services.Interfaces; |
45 | using Caps = OpenSim.Framework.Capabilities.Caps; | 45 | using Caps = OpenSim.Framework.Capabilities.Caps; |
46 | using OpenSim.Framework.Capabilities; | 46 | using OpenSim.Framework.Capabilities; |
47 | using PermissionMask = OpenSim.Framework.PermissionMask; | ||
47 | 48 | ||
48 | namespace OpenSim.Region.ClientStack.Linden | 49 | namespace OpenSim.Region.ClientStack.Linden |
49 | { | 50 | { |
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 060a61c..0adfa1a 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.5.*")] | 32 | [assembly: AssemblyVersion("0.8.3.*")] |
33 | [assembly: AssemblyFileVersion("1.0.0.0")] | 33 | |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs index 17c7270..a133a69 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs | |||
@@ -56,8 +56,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
56 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "RegionConsoleModule")] | 56 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "RegionConsoleModule")] |
57 | public class RegionConsoleModule : INonSharedRegionModule, IRegionConsole | 57 | public class RegionConsoleModule : INonSharedRegionModule, IRegionConsole |
58 | { | 58 | { |
59 | private static readonly ILog m_log = | 59 | // private static readonly ILog m_log = |
60 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 60 | // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
61 | 61 | ||
62 | private Scene m_scene; | 62 | private Scene m_scene; |
63 | private IEventQueue m_eventQueue; | 63 | private IEventQueue m_eventQueue; |
@@ -157,8 +157,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
157 | 157 | ||
158 | public class ConsoleHandler : BaseStreamHandler | 158 | public class ConsoleHandler : BaseStreamHandler |
159 | { | 159 | { |
160 | private static readonly ILog m_log = | 160 | // private static readonly ILog m_log = |
161 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 161 | // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
162 | 162 | ||
163 | private RegionConsoleModule m_consoleModule; | 163 | private RegionConsoleModule m_consoleModule; |
164 | private UUID m_agentID; | 164 | private UUID m_agentID; |
@@ -176,7 +176,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
176 | m_isGod = m_scene.Permissions.IsGod(agentID); | 176 | m_isGod = m_scene.Permissions.IsGod(agentID); |
177 | } | 177 | } |
178 | 178 | ||
179 | public override byte[] Handle(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) | 179 | protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) |
180 | { | 180 | { |
181 | StreamReader reader = new StreamReader(request); | 181 | StreamReader reader = new StreamReader(request); |
182 | string message = reader.ReadToEnd(); | 182 | string message = reader.ReadToEnd(); |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/SimulatorFeaturesModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/SimulatorFeaturesModule.cs index 191bccf..e258bcb 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/SimulatorFeaturesModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/SimulatorFeaturesModule.cs | |||
@@ -27,6 +27,7 @@ | |||
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections; | 29 | using System.Collections; |
30 | using System.Collections.Generic; | ||
30 | using System.Reflection; | 31 | using System.Reflection; |
31 | using log4net; | 32 | using log4net; |
32 | using Nini.Config; | 33 | using Nini.Config; |
@@ -37,7 +38,7 @@ using OpenSim.Framework; | |||
37 | using OpenSim.Framework.Servers.HttpServer; | 38 | using OpenSim.Framework.Servers.HttpServer; |
38 | using OpenSim.Region.Framework.Interfaces; | 39 | using OpenSim.Region.Framework.Interfaces; |
39 | using OpenSim.Region.Framework.Scenes; | 40 | using OpenSim.Region.Framework.Scenes; |
40 | using OpenSim.Services.Interfaces; | 41 | // using OpenSim.Services.Interfaces; |
41 | using Caps = OpenSim.Framework.Capabilities.Caps; | 42 | using Caps = OpenSim.Framework.Capabilities.Caps; |
42 | 43 | ||
43 | namespace OpenSim.Region.ClientStack.Linden | 44 | namespace OpenSim.Region.ClientStack.Linden |
@@ -56,8 +57,10 @@ namespace OpenSim.Region.ClientStack.Linden | |||
56 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimulatorFeaturesModule")] | 57 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimulatorFeaturesModule")] |
57 | public class SimulatorFeaturesModule : ISharedRegionModule, ISimulatorFeaturesModule | 58 | public class SimulatorFeaturesModule : ISharedRegionModule, ISimulatorFeaturesModule |
58 | { | 59 | { |
59 | // private static readonly ILog m_log = | 60 | private static readonly ILog m_log = |
60 | // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 61 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
62 | |||
63 | public event SimulatorFeaturesRequestDelegate OnSimulatorFeaturesRequest; | ||
61 | 64 | ||
62 | private Scene m_scene; | 65 | private Scene m_scene; |
63 | 66 | ||
@@ -66,25 +69,40 @@ namespace OpenSim.Region.ClientStack.Linden | |||
66 | /// </summary> | 69 | /// </summary> |
67 | private OSDMap m_features = new OSDMap(); | 70 | private OSDMap m_features = new OSDMap(); |
68 | 71 | ||
69 | private string m_MapImageServerURL = string.Empty; | ||
70 | private string m_SearchURL = string.Empty; | 72 | private string m_SearchURL = string.Empty; |
73 | private string m_DestinationGuideURL = string.Empty; | ||
74 | private bool m_ExportSupported = false; | ||
75 | private string m_GridName = string.Empty; | ||
76 | private string m_GridURL = string.Empty; | ||
71 | 77 | ||
72 | #region ISharedRegionModule Members | 78 | #region ISharedRegionModule Members |
73 | 79 | ||
74 | public void Initialise(IConfigSource source) | 80 | public void Initialise(IConfigSource source) |
75 | { | 81 | { |
76 | IConfig config = source.Configs["SimulatorFeatures"]; | 82 | IConfig config = source.Configs["SimulatorFeatures"]; |
83 | |||
77 | if (config != null) | 84 | if (config != null) |
78 | { | 85 | { |
79 | m_MapImageServerURL = config.GetString("MapImageServerURI", string.Empty); | 86 | // |
80 | if (m_MapImageServerURL != string.Empty) | 87 | // All this is obsolete since getting these features from the grid service!! |
81 | { | 88 | // Will be removed after the next release |
82 | m_MapImageServerURL = m_MapImageServerURL.Trim(); | 89 | // |
83 | if (!m_MapImageServerURL.EndsWith("/")) | 90 | m_SearchURL = config.GetString("SearchServerURI", m_SearchURL); |
84 | m_MapImageServerURL = m_MapImageServerURL + "/"; | 91 | |
85 | } | 92 | m_DestinationGuideURL = config.GetString ("DestinationGuideURI", m_DestinationGuideURL); |
86 | 93 | ||
87 | m_SearchURL = config.GetString("SearchServerURI", string.Empty); | 94 | if (m_DestinationGuideURL == string.Empty) // Make this consistent with the variable in the LoginService config |
95 | m_DestinationGuideURL = config.GetString("DestinationGuide", m_DestinationGuideURL); | ||
96 | |||
97 | m_ExportSupported = config.GetBoolean("ExportSupported", m_ExportSupported); | ||
98 | |||
99 | m_GridURL = Util.GetConfigVarFromSections<string>( | ||
100 | source, "GatekeeperURI", new string[] { "Startup", "Hypergrid", "SimulatorFeatures" }, String.Empty); | ||
101 | |||
102 | m_GridName = config.GetString("GridName", string.Empty); | ||
103 | if (m_GridName == string.Empty) | ||
104 | m_GridName = Util.GetConfigVarFromSections<string>( | ||
105 | source, "gridname", new string[] { "GridInfo", "SimulatorFeatures" }, String.Empty); | ||
88 | } | 106 | } |
89 | 107 | ||
90 | AddDefaultFeatures(); | 108 | AddDefaultFeatures(); |
@@ -94,6 +112,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
94 | { | 112 | { |
95 | m_scene = s; | 113 | m_scene = s; |
96 | m_scene.EventManager.OnRegisterCaps += RegisterCaps; | 114 | m_scene.EventManager.OnRegisterCaps += RegisterCaps; |
115 | |||
116 | m_scene.RegisterModuleInterface<ISimulatorFeaturesModule>(this); | ||
97 | } | 117 | } |
98 | 118 | ||
99 | public void RemoveRegion(Scene s) | 119 | public void RemoveRegion(Scene s) |
@@ -103,6 +123,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
103 | 123 | ||
104 | public void RegionLoaded(Scene s) | 124 | public void RegionLoaded(Scene s) |
105 | { | 125 | { |
126 | GetGridExtraFeatures(s); | ||
106 | } | 127 | } |
107 | 128 | ||
108 | public void PostInitialise() | 129 | public void PostInitialise() |
@@ -128,26 +149,43 @@ namespace OpenSim.Region.ClientStack.Linden | |||
128 | /// </remarks> | 149 | /// </remarks> |
129 | private void AddDefaultFeatures() | 150 | private void AddDefaultFeatures() |
130 | { | 151 | { |
152 | |||
131 | lock (m_features) | 153 | lock (m_features) |
132 | { | 154 | { |
133 | m_features["MeshRezEnabled"] = true; | 155 | m_features["MeshRezEnabled"] = true; |
134 | m_features["MeshUploadEnabled"] = true; | 156 | m_features["MeshUploadEnabled"] = true; |
135 | m_features["MeshXferEnabled"] = true; | 157 | m_features["MeshXferEnabled"] = true; |
136 | m_features["PhysicsMaterialsEnabled"] = true; | 158 | m_features["PhysicsMaterialsEnabled"] = true; |
137 | 159 | ||
138 | OSDMap typesMap = new OSDMap(); | 160 | OSDMap typesMap = new OSDMap(); |
139 | typesMap["convex"] = true; | 161 | typesMap["convex"] = true; |
140 | typesMap["none"] = true; | 162 | typesMap["none"] = true; |
141 | typesMap["prim"] = true; | 163 | typesMap["prim"] = true; |
142 | m_features["PhysicsShapeTypes"] = typesMap; | 164 | m_features["PhysicsShapeTypes"] = typesMap; |
143 | 165 | ||
144 | // Extra information for viewers that want to use it | 166 | // Extra information for viewers that want to use it |
145 | OSDMap gridServicesMap = new OSDMap(); | 167 | // TODO: Take these out of here into their respective modules, like map-server-url |
146 | if (m_MapImageServerURL != string.Empty) | 168 | OSDMap extrasMap; |
147 | gridServicesMap["map-server-url"] = m_MapImageServerURL; | 169 | if(m_features.ContainsKey("OpenSimExtras")) |
170 | { | ||
171 | extrasMap = (OSDMap)m_features["OpenSimExtras"]; | ||
172 | } | ||
173 | else | ||
174 | extrasMap = new OSDMap(); | ||
175 | |||
148 | if (m_SearchURL != string.Empty) | 176 | if (m_SearchURL != string.Empty) |
149 | gridServicesMap["search"] = m_SearchURL; | 177 | extrasMap["search-server-url"] = m_SearchURL; |
150 | m_features["GridServices"] = gridServicesMap; | 178 | if (!string.IsNullOrEmpty(m_DestinationGuideURL)) |
179 | extrasMap["destination-guide-url"] = m_DestinationGuideURL; | ||
180 | if (m_ExportSupported) | ||
181 | extrasMap["ExportSupported"] = true; | ||
182 | if (m_GridURL != string.Empty) | ||
183 | extrasMap["GridURL"] = m_GridURL; | ||
184 | if (m_GridName != string.Empty) | ||
185 | extrasMap["GridName"] = m_GridName; | ||
186 | |||
187 | if (extrasMap.Count > 0) | ||
188 | m_features["OpenSimExtras"] = extrasMap; | ||
151 | } | 189 | } |
152 | } | 190 | } |
153 | 191 | ||
@@ -156,7 +194,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
156 | IRequestHandler reqHandler | 194 | IRequestHandler reqHandler |
157 | = new RestHTTPHandler( | 195 | = new RestHTTPHandler( |
158 | "GET", "/CAPS/" + UUID.Random(), | 196 | "GET", "/CAPS/" + UUID.Random(), |
159 | HandleSimulatorFeaturesRequest, "SimulatorFeatures", agentID.ToString()); | 197 | x => { return HandleSimulatorFeaturesRequest(x, agentID); }, "SimulatorFeatures", agentID.ToString()); |
160 | 198 | ||
161 | caps.RegisterHandler("SimulatorFeatures", reqHandler); | 199 | caps.RegisterHandler("SimulatorFeatures", reqHandler); |
162 | } | 200 | } |
@@ -185,20 +223,81 @@ namespace OpenSim.Region.ClientStack.Linden | |||
185 | return new OSDMap(m_features); | 223 | return new OSDMap(m_features); |
186 | } | 224 | } |
187 | 225 | ||
188 | private Hashtable HandleSimulatorFeaturesRequest(Hashtable mDhttpMethod) | 226 | private OSDMap DeepCopy() |
227 | { | ||
228 | // This isn't the cheapest way of doing this but the rate | ||
229 | // of occurrence is low (on sim entry only) and it's a sure | ||
230 | // way to get a true deep copy. | ||
231 | OSD copy = OSDParser.DeserializeLLSDXml(OSDParser.SerializeLLSDXmlString(m_features)); | ||
232 | |||
233 | return (OSDMap)copy; | ||
234 | } | ||
235 | |||
236 | private Hashtable HandleSimulatorFeaturesRequest(Hashtable mDhttpMethod, UUID agentID) | ||
189 | { | 237 | { |
190 | // m_log.DebugFormat("[SIMULATOR FEATURES MODULE]: SimulatorFeatures request"); | 238 | // m_log.DebugFormat("[SIMULATOR FEATURES MODULE]: SimulatorFeatures request"); |
191 | 239 | ||
240 | OSDMap copy = DeepCopy(); | ||
241 | |||
242 | // Let's add the agentID to the destination guide, if it is expecting that. | ||
243 | if (copy.ContainsKey("OpenSimExtras") && ((OSDMap)(copy["OpenSimExtras"])).ContainsKey("destination-guide-url")) | ||
244 | ((OSDMap)copy["OpenSimExtras"])["destination-guide-url"] = Replace(((OSDMap)copy["OpenSimExtras"])["destination-guide-url"], "[USERID]", agentID.ToString()); | ||
245 | |||
246 | SimulatorFeaturesRequestDelegate handlerOnSimulatorFeaturesRequest = OnSimulatorFeaturesRequest; | ||
247 | |||
248 | if (handlerOnSimulatorFeaturesRequest != null) | ||
249 | handlerOnSimulatorFeaturesRequest(agentID, ref copy); | ||
250 | |||
192 | //Send back data | 251 | //Send back data |
193 | Hashtable responsedata = new Hashtable(); | 252 | Hashtable responsedata = new Hashtable(); |
194 | responsedata["int_response_code"] = 200; | 253 | responsedata["int_response_code"] = 200; |
195 | responsedata["content_type"] = "text/plain"; | 254 | responsedata["content_type"] = "text/plain"; |
196 | responsedata["keepalive"] = false; | 255 | responsedata["keepalive"] = false; |
197 | 256 | ||
198 | lock (m_features) | 257 | responsedata["str_response_string"] = OSDParser.SerializeLLSDXmlString(copy); |
199 | responsedata["str_response_string"] = OSDParser.SerializeLLSDXmlString(m_features); | ||
200 | 258 | ||
201 | return responsedata; | 259 | return responsedata; |
202 | } | 260 | } |
261 | |||
262 | /// <summary> | ||
263 | /// Gets the grid extra features. | ||
264 | /// </summary> | ||
265 | /// <param name='featuresURI'> | ||
266 | /// The URI Robust uses to handle the get_extra_features request | ||
267 | /// </param> | ||
268 | private void GetGridExtraFeatures(Scene scene) | ||
269 | { | ||
270 | Dictionary<string, object> extraFeatures = scene.GridService.GetExtraFeatures(); | ||
271 | if (extraFeatures.ContainsKey("Result") && extraFeatures["Result"] != null && extraFeatures["Result"].ToString() == "Failure") | ||
272 | { | ||
273 | m_log.WarnFormat("[SIMULATOR FEATURES MODULE]: Unable to retrieve grid-wide features"); | ||
274 | return; | ||
275 | } | ||
276 | |||
277 | lock (m_features) | ||
278 | { | ||
279 | OSDMap extrasMap = new OSDMap(); | ||
280 | |||
281 | foreach(string key in extraFeatures.Keys) | ||
282 | { | ||
283 | extrasMap[key] = (string)extraFeatures[key]; | ||
284 | |||
285 | if (key == "ExportSupported") | ||
286 | { | ||
287 | bool.TryParse(extraFeatures[key].ToString(), out m_ExportSupported); | ||
288 | } | ||
289 | } | ||
290 | m_features["OpenSimExtras"] = extrasMap; | ||
291 | |||
292 | } | ||
293 | } | ||
294 | |||
295 | private string Replace(string url, string substring, string replacement) | ||
296 | { | ||
297 | if (!String.IsNullOrEmpty(url) && url.Contains(substring)) | ||
298 | return url.Replace(substring, replacement); | ||
299 | |||
300 | return url; | ||
301 | } | ||
203 | } | 302 | } |
204 | } | 303 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/Tests/WebFetchInvDescModuleTests.cs b/OpenSim/Region/ClientStack/Linden/Caps/Tests/WebFetchInvDescModuleTests.cs new file mode 100644 index 0000000..dd4a691 --- /dev/null +++ b/OpenSim/Region/ClientStack/Linden/Caps/Tests/WebFetchInvDescModuleTests.cs | |||
@@ -0,0 +1,159 @@ | |||
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.Generic; | ||
31 | using System.IO; | ||
32 | using System.Net; | ||
33 | using System.Text; | ||
34 | using HttpServer; | ||
35 | using log4net.Config; | ||
36 | using Nini.Config; | ||
37 | using NUnit.Framework; | ||
38 | using OpenMetaverse; | ||
39 | using OpenMetaverse.Packets; | ||
40 | using OpenMetaverse.StructuredData; | ||
41 | using OpenSim.Framework; | ||
42 | using OpenSim.Framework.Capabilities; | ||
43 | using OpenSim.Framework.Servers; | ||
44 | using OpenSim.Framework.Servers.HttpServer; | ||
45 | using OpenSim.Region.ClientStack.Linden; | ||
46 | using OpenSim.Region.CoreModules.Framework; | ||
47 | using OpenSim.Region.Framework.Scenes; | ||
48 | using OpenSim.Services.Interfaces; | ||
49 | using OpenSim.Tests.Common; | ||
50 | using OSDArray = OpenMetaverse.StructuredData.OSDArray; | ||
51 | using OSDMap = OpenMetaverse.StructuredData.OSDMap; | ||
52 | |||
53 | namespace OpenSim.Region.ClientStack.Linden.Caps.Tests | ||
54 | { | ||
55 | [TestFixture] | ||
56 | public class WebFetchInvDescModuleTests : OpenSimTestCase | ||
57 | { | ||
58 | [TestFixtureSetUp] | ||
59 | public void TestFixtureSetUp() | ||
60 | { | ||
61 | // Don't allow tests to be bamboozled by asynchronous events. Execute everything on the same thread. | ||
62 | Util.FireAndForgetMethod = FireAndForgetMethod.RegressionTest; | ||
63 | } | ||
64 | |||
65 | [TestFixtureTearDown] | ||
66 | public void TestFixureTearDown() | ||
67 | { | ||
68 | // We must set this back afterwards, otherwise later tests will fail since they're expecting multiple | ||
69 | // threads. Possibly, later tests should be rewritten so none of them require async stuff (which regression | ||
70 | // tests really shouldn't). | ||
71 | Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod; | ||
72 | } | ||
73 | |||
74 | [SetUp] | ||
75 | public override void SetUp() | ||
76 | { | ||
77 | base.SetUp(); | ||
78 | |||
79 | // This is an unfortunate bit of clean up we have to do because MainServer manages things through static | ||
80 | // variables and the VM is not restarted between tests. | ||
81 | uint port = 9999; | ||
82 | MainServer.RemoveHttpServer(port); | ||
83 | |||
84 | BaseHttpServer server = new BaseHttpServer(port, false, 0, ""); | ||
85 | MainServer.AddHttpServer(server); | ||
86 | MainServer.Instance = server; | ||
87 | |||
88 | server.Start(false); | ||
89 | } | ||
90 | |||
91 | [Test] | ||
92 | public void TestInventoryDescendentsFetch() | ||
93 | { | ||
94 | TestHelpers.InMethod(); | ||
95 | TestHelpers.EnableLogging(); | ||
96 | |||
97 | BaseHttpServer httpServer = MainServer.Instance; | ||
98 | Scene scene = new SceneHelpers().SetupScene(); | ||
99 | |||
100 | CapabilitiesModule capsModule = new CapabilitiesModule(); | ||
101 | WebFetchInvDescModule wfidModule = new WebFetchInvDescModule(false); | ||
102 | |||
103 | IConfigSource config = new IniConfigSource(); | ||
104 | config.AddConfig("ClientStack.LindenCaps"); | ||
105 | config.Configs["ClientStack.LindenCaps"].Set("Cap_FetchInventoryDescendents2", "localhost"); | ||
106 | |||
107 | SceneHelpers.SetupSceneModules(scene, config, capsModule, wfidModule); | ||
108 | |||
109 | UserAccount ua = UserAccountHelpers.CreateUserWithInventory(scene, TestHelpers.ParseTail(0x1)); | ||
110 | |||
111 | // We need a user present to have any capabilities set up | ||
112 | SceneHelpers.AddScenePresence(scene, ua.PrincipalID); | ||
113 | |||
114 | TestHttpRequest req = new TestHttpRequest(); | ||
115 | OpenSim.Framework.Capabilities.Caps userCaps = capsModule.GetCapsForUser(ua.PrincipalID); | ||
116 | PollServiceEventArgs pseArgs; | ||
117 | userCaps.TryGetPollHandler("FetchInventoryDescendents2", out pseArgs); | ||
118 | req.UriPath = pseArgs.Url; | ||
119 | req.Uri = new Uri("file://" + req.UriPath); | ||
120 | |||
121 | // Retrieve root folder details directly so that we can request | ||
122 | InventoryFolderBase folder = scene.InventoryService.GetRootFolder(ua.PrincipalID); | ||
123 | |||
124 | OSDMap osdFolder = new OSDMap(); | ||
125 | osdFolder["folder_id"] = folder.ID; | ||
126 | osdFolder["owner_id"] = ua.PrincipalID; | ||
127 | osdFolder["fetch_folders"] = true; | ||
128 | osdFolder["fetch_items"] = true; | ||
129 | osdFolder["sort_order"] = 0; | ||
130 | |||
131 | OSDArray osdFoldersArray = new OSDArray(); | ||
132 | osdFoldersArray.Add(osdFolder); | ||
133 | |||
134 | OSDMap osdReqMap = new OSDMap(); | ||
135 | osdReqMap["folders"] = osdFoldersArray; | ||
136 | |||
137 | req.Body = new MemoryStream(OSDParser.SerializeLLSDXmlBytes(osdReqMap)); | ||
138 | |||
139 | TestHttpClientContext context = new TestHttpClientContext(false); | ||
140 | |||
141 | // WARNING: This results in a caught exception, because queryString is null | ||
142 | MainServer.Instance.OnRequest(context, new RequestEventArgs(req)); | ||
143 | |||
144 | // Drive processing of the queued inventory request synchronously. | ||
145 | wfidModule.WaitProcessQueuedInventoryRequest(); | ||
146 | MainServer.Instance.PollServiceRequestManager.WaitPerformResponse(); | ||
147 | |||
148 | // System.Threading.Thread.Sleep(10000); | ||
149 | |||
150 | OSDMap responseOsd = (OSDMap)OSDParser.DeserializeLLSDXml(context.ResponseBody); | ||
151 | OSDArray foldersOsd = (OSDArray)responseOsd["folders"]; | ||
152 | OSDMap folderOsd = (OSDMap)foldersOsd[0]; | ||
153 | |||
154 | // A sanity check that the response has the expected number of descendents for a default inventory | ||
155 | // TODO: Need a more thorough check. | ||
156 | Assert.That((int)folderOsd["descendents"], Is.EqualTo(16)); | ||
157 | } | ||
158 | } | ||
159 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/UploadBakedTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/UploadBakedTextureModule.cs index 3b0ccd7..8cdebcd 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/UploadBakedTextureModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/UploadBakedTextureModule.cs | |||
@@ -27,6 +27,7 @@ | |||
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections; | 29 | using System.Collections; |
30 | using System.Collections.Generic; | ||
30 | using System.Collections.Specialized; | 31 | using System.Collections.Specialized; |
31 | using System.Drawing; | 32 | using System.Drawing; |
32 | using System.Drawing.Imaging; | 33 | using System.Drawing.Imaging; |
@@ -53,8 +54,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
53 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "UploadBakedTextureModule")] | 54 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "UploadBakedTextureModule")] |
54 | public class UploadBakedTextureModule : INonSharedRegionModule | 55 | public class UploadBakedTextureModule : INonSharedRegionModule |
55 | { | 56 | { |
56 | // private static readonly ILog m_log = | 57 | private static readonly ILog m_log = |
57 | // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 58 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
58 | 59 | ||
59 | /// <summary> | 60 | /// <summary> |
60 | /// For historical reasons this is fixed, but there | 61 | /// For historical reasons this is fixed, but there |
@@ -64,31 +65,210 @@ namespace OpenSim.Region.ClientStack.Linden | |||
64 | private Scene m_scene; | 65 | private Scene m_scene; |
65 | private bool m_persistBakedTextures; | 66 | private bool m_persistBakedTextures; |
66 | 67 | ||
68 | private IBakedTextureModule m_BakedTextureModule; | ||
69 | |||
67 | public void Initialise(IConfigSource source) | 70 | public void Initialise(IConfigSource source) |
68 | { | 71 | { |
69 | IConfig appearanceConfig = source.Configs["Appearance"]; | 72 | IConfig appearanceConfig = source.Configs["Appearance"]; |
70 | if (appearanceConfig != null) | 73 | if (appearanceConfig != null) |
71 | m_persistBakedTextures = appearanceConfig.GetBoolean("PersistBakedTextures", m_persistBakedTextures); | 74 | m_persistBakedTextures = appearanceConfig.GetBoolean("PersistBakedTextures", m_persistBakedTextures); |
75 | |||
76 | |||
72 | } | 77 | } |
73 | 78 | ||
74 | public void AddRegion(Scene s) | 79 | public void AddRegion(Scene s) |
75 | { | 80 | { |
76 | m_scene = s; | 81 | m_scene = s; |
82 | |||
77 | } | 83 | } |
78 | 84 | ||
79 | public void RemoveRegion(Scene s) | 85 | public void RemoveRegion(Scene s) |
80 | { | 86 | { |
87 | s.EventManager.OnRegisterCaps -= RegisterCaps; | ||
88 | s.EventManager.OnNewPresence -= RegisterNewPresence; | ||
89 | s.EventManager.OnRemovePresence -= DeRegisterPresence; | ||
90 | m_BakedTextureModule = null; | ||
91 | m_scene = null; | ||
81 | } | 92 | } |
82 | 93 | ||
94 | |||
95 | |||
83 | public void RegionLoaded(Scene s) | 96 | public void RegionLoaded(Scene s) |
84 | { | 97 | { |
85 | m_scene.EventManager.OnRegisterCaps += RegisterCaps; | 98 | m_scene.EventManager.OnRegisterCaps += RegisterCaps; |
99 | m_scene.EventManager.OnNewPresence += RegisterNewPresence; | ||
100 | m_scene.EventManager.OnRemovePresence += DeRegisterPresence; | ||
101 | |||
102 | } | ||
103 | |||
104 | private void DeRegisterPresence(UUID agentId) | ||
105 | { | ||
106 | ScenePresence presence = null; | ||
107 | if (m_scene.TryGetScenePresence(agentId, out presence)) | ||
108 | { | ||
109 | presence.ControllingClient.OnSetAppearance -= CaptureAppearanceSettings; | ||
110 | } | ||
111 | |||
112 | } | ||
113 | |||
114 | private void RegisterNewPresence(ScenePresence presence) | ||
115 | { | ||
116 | presence.ControllingClient.OnSetAppearance += CaptureAppearanceSettings; | ||
117 | |||
118 | } | ||
119 | |||
120 | private void CaptureAppearanceSettings(IClientAPI remoteClient, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 avSize, WearableCacheItem[] cacheItems) | ||
121 | { | ||
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 | { | ||
142 | |||
143 | WearableCacheItem[] existingitems = p.Appearance.WearableCacheItems; | ||
144 | if (existingitems == null) | ||
145 | { | ||
146 | if (m_BakedTextureModule != null) | ||
147 | { | ||
148 | WearableCacheItem[] savedcache = null; | ||
149 | try | ||
150 | { | ||
151 | if (p.Appearance.WearableCacheItemsDirty) | ||
152 | { | ||
153 | savedcache = m_BakedTextureModule.Get(p.UUID); | ||
154 | p.Appearance.WearableCacheItems = savedcache; | ||
155 | p.Appearance.WearableCacheItemsDirty = false; | ||
156 | } | ||
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 | } | ||
171 | catch (InvalidOperationException) | ||
172 | { | ||
173 | cacheItems = null; | ||
174 | } */ | ||
175 | catch (Exception) | ||
176 | { | ||
177 | // The service logs a sufficient error message. | ||
178 | } | ||
179 | |||
180 | |||
181 | if (savedcache != null) | ||
182 | existingitems = savedcache; | ||
183 | } | ||
184 | } | ||
185 | // Existing items null means it's a fully new appearance | ||
186 | if (existingitems == null) | ||
187 | { | ||
188 | |||
189 | for (int i = 0; i < maxCacheitemsLoop; i++) | ||
190 | { | ||
191 | if (textureEntry.FaceTextures.Length > cacheItems[i].TextureIndex) | ||
192 | { | ||
193 | Primitive.TextureEntryFace face = textureEntry.FaceTextures[cacheItems[i].TextureIndex]; | ||
194 | if (face == null) | ||
195 | { | ||
196 | textureEntry.CreateFace(cacheItems[i].TextureIndex); | ||
197 | textureEntry.FaceTextures[cacheItems[i].TextureIndex].TextureID = | ||
198 | AppearanceManager.DEFAULT_AVATAR_TEXTURE; | ||
199 | continue; | ||
200 | } | ||
201 | cacheItems[i].TextureID =face.TextureID; | ||
202 | if (m_scene.AssetService != null) | ||
203 | cacheItems[i].TextureAsset = | ||
204 | m_scene.AssetService.GetCached(cacheItems[i].TextureID.ToString()); | ||
205 | } | ||
206 | else | ||
207 | { | ||
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); | ||
209 | } | ||
210 | |||
211 | |||
212 | } | ||
213 | } | ||
214 | else | ||
215 | |||
216 | |||
217 | { | ||
218 | // for each uploaded baked texture | ||
219 | for (int i = 0; i < maxCacheitemsLoop; i++) | ||
220 | { | ||
221 | if (textureEntry.FaceTextures.Length > cacheItems[i].TextureIndex) | ||
222 | { | ||
223 | Primitive.TextureEntryFace face = textureEntry.FaceTextures[cacheItems[i].TextureIndex]; | ||
224 | if (face == null) | ||
225 | { | ||
226 | textureEntry.CreateFace(cacheItems[i].TextureIndex); | ||
227 | textureEntry.FaceTextures[cacheItems[i].TextureIndex].TextureID = | ||
228 | AppearanceManager.DEFAULT_AVATAR_TEXTURE; | ||
229 | continue; | ||
230 | } | ||
231 | cacheItems[i].TextureID = | ||
232 | face.TextureID; | ||
233 | } | ||
234 | else | ||
235 | { | ||
236 | 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); | ||
237 | } | ||
238 | } | ||
239 | |||
240 | for (int i = 0; i < maxCacheitemsLoop; i++) | ||
241 | { | ||
242 | if (cacheItems[i].TextureAsset == null) | ||
243 | { | ||
244 | cacheItems[i].TextureAsset = | ||
245 | m_scene.AssetService.GetCached(cacheItems[i].TextureID.ToString()); | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | |||
250 | |||
251 | |||
252 | p.Appearance.WearableCacheItems = cacheItems; | ||
253 | |||
254 | |||
255 | |||
256 | if (m_BakedTextureModule != null) | ||
257 | { | ||
258 | m_BakedTextureModule.Store(remoteClient.AgentId, cacheItems); | ||
259 | p.Appearance.WearableCacheItemsDirty = true; | ||
260 | |||
261 | } | ||
262 | } | ||
263 | } | ||
86 | } | 264 | } |
87 | 265 | ||
88 | public void PostInitialise() | 266 | public void PostInitialise() |
89 | { | 267 | { |
90 | } | 268 | } |
91 | 269 | ||
270 | |||
271 | |||
92 | public void Close() { } | 272 | public void Close() { } |
93 | 273 | ||
94 | public string Name { get { return "UploadBakedTextureModule"; } } | 274 | public string Name { get { return "UploadBakedTextureModule"; } } |
@@ -100,15 +280,23 @@ namespace OpenSim.Region.ClientStack.Linden | |||
100 | 280 | ||
101 | public void RegisterCaps(UUID agentID, Caps caps) | 281 | public void RegisterCaps(UUID agentID, Caps caps) |
102 | { | 282 | { |
283 | UploadBakedTextureHandler avatarhandler = new UploadBakedTextureHandler( | ||
284 | caps, m_scene.AssetService, m_persistBakedTextures); | ||
285 | |||
286 | |||
287 | |||
103 | caps.RegisterHandler( | 288 | caps.RegisterHandler( |
104 | "UploadBakedTexture", | 289 | "UploadBakedTexture", |
105 | new RestStreamHandler( | 290 | new RestStreamHandler( |
106 | "POST", | 291 | "POST", |
107 | "/CAPS/" + caps.CapsObjectPath + m_uploadBakedTexturePath, | 292 | "/CAPS/" + caps.CapsObjectPath + m_uploadBakedTexturePath, |
108 | new UploadBakedTextureHandler( | 293 | avatarhandler.UploadBakedTexture, |
109 | caps, m_scene.AssetService, m_persistBakedTextures).UploadBakedTexture, | ||
110 | "UploadBakedTexture", | 294 | "UploadBakedTexture", |
111 | agentID.ToString())); | 295 | agentID.ToString())); |
296 | |||
297 | |||
298 | |||
299 | |||
112 | } | 300 | } |
113 | } | 301 | } |
114 | } \ No newline at end of file | 302 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs index 6890f4a..025ffea 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs | |||
@@ -27,15 +27,21 @@ | |||
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections; | 29 | using System.Collections; |
30 | using System.Collections.Generic; | ||
30 | using System.Reflection; | 31 | using System.Reflection; |
32 | using System.Threading; | ||
31 | using log4net; | 33 | using log4net; |
32 | using Nini.Config; | 34 | using Nini.Config; |
33 | using Mono.Addins; | 35 | using Mono.Addins; |
34 | using OpenMetaverse; | 36 | using OpenMetaverse; |
37 | using OpenMetaverse.StructuredData; | ||
35 | using OpenSim.Framework; | 38 | using OpenSim.Framework; |
39 | using OpenSim.Framework.Monitoring; | ||
40 | using OpenSim.Framework.Servers; | ||
36 | using OpenSim.Framework.Servers.HttpServer; | 41 | using OpenSim.Framework.Servers.HttpServer; |
37 | using OpenSim.Region.Framework.Interfaces; | 42 | using OpenSim.Region.Framework.Interfaces; |
38 | using OpenSim.Region.Framework.Scenes; | 43 | using OpenSim.Region.Framework.Scenes; |
44 | using OpenSim.Framework.Capabilities; | ||
39 | using OpenSim.Services.Interfaces; | 45 | using OpenSim.Services.Interfaces; |
40 | using Caps = OpenSim.Framework.Capabilities.Caps; | 46 | using Caps = OpenSim.Framework.Capabilities.Caps; |
41 | using OpenSim.Capabilities.Handlers; | 47 | using OpenSim.Capabilities.Handlers; |
@@ -48,9 +54,37 @@ namespace OpenSim.Region.ClientStack.Linden | |||
48 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WebFetchInvDescModule")] | 54 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WebFetchInvDescModule")] |
49 | public class WebFetchInvDescModule : INonSharedRegionModule | 55 | public class WebFetchInvDescModule : INonSharedRegionModule |
50 | { | 56 | { |
51 | // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 57 | class aPollRequest |
58 | { | ||
59 | public PollServiceInventoryEventArgs thepoll; | ||
60 | public UUID reqID; | ||
61 | public Hashtable request; | ||
62 | public ScenePresence presence; | ||
63 | public List<UUID> folders; | ||
64 | } | ||
65 | |||
66 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
67 | |||
68 | /// <summary> | ||
69 | /// Control whether requests will be processed asynchronously. | ||
70 | /// </summary> | ||
71 | /// <remarks> | ||
72 | /// Defaults to true. Can currently not be changed once a region has been added to the module. | ||
73 | /// </remarks> | ||
74 | public bool ProcessQueuedRequestsAsync { get; private set; } | ||
52 | 75 | ||
53 | private Scene m_scene; | 76 | /// <summary> |
77 | /// Number of inventory requests processed by this module. | ||
78 | /// </summary> | ||
79 | /// <remarks> | ||
80 | /// It's the PollServiceRequestManager that actually sends completed requests back to the requester. | ||
81 | /// </remarks> | ||
82 | public static int ProcessedRequestsCount { get; set; } | ||
83 | |||
84 | private static Stat s_queuedRequestsStat; | ||
85 | private static Stat s_processedRequestsStat; | ||
86 | |||
87 | public Scene Scene { get; private set; } | ||
54 | 88 | ||
55 | private IInventoryService m_InventoryService; | 89 | private IInventoryService m_InventoryService; |
56 | private ILibraryService m_LibraryService; | 90 | private ILibraryService m_LibraryService; |
@@ -60,10 +94,22 @@ namespace OpenSim.Region.ClientStack.Linden | |||
60 | private string m_fetchInventoryDescendents2Url; | 94 | private string m_fetchInventoryDescendents2Url; |
61 | private string m_webFetchInventoryDescendentsUrl; | 95 | private string m_webFetchInventoryDescendentsUrl; |
62 | 96 | ||
63 | private WebFetchInvDescHandler m_webFetchHandler; | 97 | private static FetchInvDescHandler m_webFetchHandler; |
98 | |||
99 | private static Thread[] m_workerThreads = null; | ||
100 | |||
101 | private static DoubleQueue<aPollRequest> m_queue = | ||
102 | new DoubleQueue<aPollRequest>(); | ||
64 | 103 | ||
65 | #region ISharedRegionModule Members | 104 | #region ISharedRegionModule Members |
66 | 105 | ||
106 | public WebFetchInvDescModule() : this(true) {} | ||
107 | |||
108 | public WebFetchInvDescModule(bool processQueuedResultsAsync) | ||
109 | { | ||
110 | ProcessQueuedRequestsAsync = processQueuedResultsAsync; | ||
111 | } | ||
112 | |||
67 | public void Initialise(IConfigSource source) | 113 | public void Initialise(IConfigSource source) |
68 | { | 114 | { |
69 | IConfig config = source.Configs["ClientStack.LindenCaps"]; | 115 | IConfig config = source.Configs["ClientStack.LindenCaps"]; |
@@ -84,7 +130,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
84 | if (!m_Enabled) | 130 | if (!m_Enabled) |
85 | return; | 131 | return; |
86 | 132 | ||
87 | m_scene = s; | 133 | Scene = s; |
88 | } | 134 | } |
89 | 135 | ||
90 | public void RemoveRegion(Scene s) | 136 | public void RemoveRegion(Scene s) |
@@ -92,8 +138,23 @@ namespace OpenSim.Region.ClientStack.Linden | |||
92 | if (!m_Enabled) | 138 | if (!m_Enabled) |
93 | return; | 139 | return; |
94 | 140 | ||
95 | m_scene.EventManager.OnRegisterCaps -= RegisterCaps; | 141 | Scene.EventManager.OnRegisterCaps -= RegisterCaps; |
96 | m_scene = null; | 142 | |
143 | StatsManager.DeregisterStat(s_processedRequestsStat); | ||
144 | StatsManager.DeregisterStat(s_queuedRequestsStat); | ||
145 | |||
146 | if (ProcessQueuedRequestsAsync) | ||
147 | { | ||
148 | if (m_workerThreads != null) | ||
149 | { | ||
150 | foreach (Thread t in m_workerThreads) | ||
151 | Watchdog.AbortThread(t.ManagedThreadId); | ||
152 | |||
153 | m_workerThreads = null; | ||
154 | } | ||
155 | } | ||
156 | |||
157 | Scene = null; | ||
97 | } | 158 | } |
98 | 159 | ||
99 | public void RegionLoaded(Scene s) | 160 | public void RegionLoaded(Scene s) |
@@ -101,14 +162,61 @@ namespace OpenSim.Region.ClientStack.Linden | |||
101 | if (!m_Enabled) | 162 | if (!m_Enabled) |
102 | return; | 163 | return; |
103 | 164 | ||
104 | m_InventoryService = m_scene.InventoryService; | 165 | if (s_processedRequestsStat == null) |
105 | m_LibraryService = m_scene.LibraryService; | 166 | s_processedRequestsStat = |
167 | new Stat( | ||
168 | "ProcessedFetchInventoryRequests", | ||
169 | "Number of processed fetch inventory requests", | ||
170 | "These have not necessarily yet been dispatched back to the requester.", | ||
171 | "", | ||
172 | "inventory", | ||
173 | "httpfetch", | ||
174 | StatType.Pull, | ||
175 | MeasuresOfInterest.AverageChangeOverTime, | ||
176 | stat => { stat.Value = ProcessedRequestsCount; }, | ||
177 | StatVerbosity.Debug); | ||
178 | |||
179 | if (s_queuedRequestsStat == null) | ||
180 | s_queuedRequestsStat = | ||
181 | new Stat( | ||
182 | "QueuedFetchInventoryRequests", | ||
183 | "Number of fetch inventory requests queued for processing", | ||
184 | "", | ||
185 | "", | ||
186 | "inventory", | ||
187 | "httpfetch", | ||
188 | StatType.Pull, | ||
189 | MeasuresOfInterest.AverageChangeOverTime, | ||
190 | stat => { stat.Value = m_queue.Count; }, | ||
191 | StatVerbosity.Debug); | ||
192 | |||
193 | StatsManager.RegisterStat(s_processedRequestsStat); | ||
194 | StatsManager.RegisterStat(s_queuedRequestsStat); | ||
195 | |||
196 | m_InventoryService = Scene.InventoryService; | ||
197 | m_LibraryService = Scene.LibraryService; | ||
106 | 198 | ||
107 | // We'll reuse the same handler for all requests. | 199 | // We'll reuse the same handler for all requests. |
108 | if (m_fetchInventoryDescendents2Url == "localhost" || m_webFetchInventoryDescendentsUrl == "localhost") | 200 | m_webFetchHandler = new FetchInvDescHandler(m_InventoryService, m_LibraryService, Scene); |
109 | m_webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService); | ||
110 | 201 | ||
111 | m_scene.EventManager.OnRegisterCaps += RegisterCaps; | 202 | Scene.EventManager.OnRegisterCaps += RegisterCaps; |
203 | |||
204 | int nworkers = 2; // was 2 | ||
205 | if (ProcessQueuedRequestsAsync && m_workerThreads == null) | ||
206 | { | ||
207 | m_workerThreads = new Thread[nworkers]; | ||
208 | |||
209 | for (uint i = 0; i < nworkers; i++) | ||
210 | { | ||
211 | m_workerThreads[i] = WorkManager.StartThread(DoInventoryRequests, | ||
212 | String.Format("InventoryWorkerThread{0}", i), | ||
213 | ThreadPriority.Normal, | ||
214 | false, | ||
215 | true, | ||
216 | null, | ||
217 | int.MaxValue); | ||
218 | } | ||
219 | } | ||
112 | } | 220 | } |
113 | 221 | ||
114 | public void PostInitialise() | 222 | public void PostInitialise() |
@@ -126,43 +234,221 @@ namespace OpenSim.Region.ClientStack.Linden | |||
126 | 234 | ||
127 | #endregion | 235 | #endregion |
128 | 236 | ||
129 | private void RegisterCaps(UUID agentID, Caps caps) | 237 | private class PollServiceInventoryEventArgs : PollServiceEventArgs |
130 | { | 238 | { |
131 | if (m_webFetchInventoryDescendentsUrl != "") | 239 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
132 | RegisterFetchCap(agentID, caps, "WebFetchInventoryDescendents", m_webFetchInventoryDescendentsUrl); | 240 | |
241 | private Dictionary<UUID, Hashtable> responses = | ||
242 | new Dictionary<UUID, Hashtable>(); | ||
243 | |||
244 | private WebFetchInvDescModule m_module; | ||
133 | 245 | ||
134 | if (m_fetchInventoryDescendents2Url != "") | 246 | public PollServiceInventoryEventArgs(WebFetchInvDescModule module, string url, UUID pId) : |
135 | RegisterFetchCap(agentID, caps, "FetchInventoryDescendents2", m_fetchInventoryDescendents2Url); | 247 | base(null, url, null, null, null, pId, int.MaxValue) |
248 | { | ||
249 | m_module = module; | ||
250 | |||
251 | HasEvents = (x, y) => { lock (responses) return responses.ContainsKey(x); }; | ||
252 | GetEvents = (x, y) => | ||
253 | { | ||
254 | lock (responses) | ||
255 | { | ||
256 | try | ||
257 | { | ||
258 | return responses[x]; | ||
259 | } | ||
260 | finally | ||
261 | { | ||
262 | responses.Remove(x); | ||
263 | } | ||
264 | } | ||
265 | }; | ||
266 | |||
267 | Request = (x, y) => | ||
268 | { | ||
269 | ScenePresence sp = m_module.Scene.GetScenePresence(Id); | ||
270 | |||
271 | aPollRequest reqinfo = new aPollRequest(); | ||
272 | reqinfo.thepoll = this; | ||
273 | reqinfo.reqID = x; | ||
274 | reqinfo.request = y; | ||
275 | reqinfo.presence = sp; | ||
276 | reqinfo.folders = new List<UUID>(); | ||
277 | |||
278 | // Decode the request here | ||
279 | string request = y["body"].ToString(); | ||
280 | |||
281 | request = request.Replace("<string>00000000-0000-0000-0000-000000000000</string>", "<uuid>00000000-0000-0000-0000-000000000000</uuid>"); | ||
282 | |||
283 | request = request.Replace("<key>fetch_folders</key><integer>0</integer>", "<key>fetch_folders</key><boolean>0</boolean>"); | ||
284 | request = request.Replace("<key>fetch_folders</key><integer>1</integer>", "<key>fetch_folders</key><boolean>1</boolean>"); | ||
285 | |||
286 | Hashtable hash = new Hashtable(); | ||
287 | try | ||
288 | { | ||
289 | hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request)); | ||
290 | } | ||
291 | catch (LLSD.LLSDParseException e) | ||
292 | { | ||
293 | m_log.ErrorFormat("[INVENTORY]: Fetch error: {0}{1}" + e.Message, e.StackTrace); | ||
294 | m_log.Error("Request: " + request); | ||
295 | return; | ||
296 | } | ||
297 | catch (System.Xml.XmlException) | ||
298 | { | ||
299 | m_log.ErrorFormat("[INVENTORY]: XML Format error"); | ||
300 | } | ||
301 | |||
302 | ArrayList foldersrequested = (ArrayList)hash["folders"]; | ||
303 | |||
304 | bool highPriority = false; | ||
305 | |||
306 | for (int i = 0; i < foldersrequested.Count; i++) | ||
307 | { | ||
308 | Hashtable inventoryhash = (Hashtable)foldersrequested[i]; | ||
309 | string folder = inventoryhash["folder_id"].ToString(); | ||
310 | UUID folderID; | ||
311 | if (UUID.TryParse(folder, out folderID)) | ||
312 | { | ||
313 | if (!reqinfo.folders.Contains(folderID)) | ||
314 | { | ||
315 | //TODO: Port COF handling from Avination | ||
316 | reqinfo.folders.Add(folderID); | ||
317 | } | ||
318 | } | ||
319 | } | ||
320 | |||
321 | if (highPriority) | ||
322 | m_queue.EnqueueHigh(reqinfo); | ||
323 | else | ||
324 | m_queue.EnqueueLow(reqinfo); | ||
325 | }; | ||
326 | |||
327 | NoEvents = (x, y) => | ||
328 | { | ||
329 | /* | ||
330 | lock (requests) | ||
331 | { | ||
332 | Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString()); | ||
333 | requests.Remove(request); | ||
334 | } | ||
335 | */ | ||
336 | Hashtable response = new Hashtable(); | ||
337 | |||
338 | response["int_response_code"] = 500; | ||
339 | response["str_response_string"] = "Script timeout"; | ||
340 | response["content_type"] = "text/plain"; | ||
341 | response["keepalive"] = false; | ||
342 | response["reusecontext"] = false; | ||
343 | |||
344 | return response; | ||
345 | }; | ||
346 | } | ||
347 | |||
348 | public void Process(aPollRequest requestinfo) | ||
349 | { | ||
350 | UUID requestID = requestinfo.reqID; | ||
351 | |||
352 | Hashtable response = new Hashtable(); | ||
353 | |||
354 | response["int_response_code"] = 200; | ||
355 | response["content_type"] = "text/plain"; | ||
356 | response["keepalive"] = false; | ||
357 | response["reusecontext"] = false; | ||
358 | |||
359 | response["str_response_string"] = m_webFetchHandler.FetchInventoryDescendentsRequest( | ||
360 | requestinfo.request["body"].ToString(), String.Empty, String.Empty, null, null); | ||
361 | |||
362 | lock (responses) | ||
363 | { | ||
364 | if (responses.ContainsKey(requestID)) | ||
365 | m_log.WarnFormat("[FETCH INVENTORY DESCENDENTS2 MODULE]: Caught in the act of loosing responses! Please report this on mantis #7054"); | ||
366 | responses[requestID] = response; | ||
367 | } | ||
368 | |||
369 | WebFetchInvDescModule.ProcessedRequestsCount++; | ||
370 | } | ||
371 | } | ||
372 | |||
373 | private void RegisterCaps(UUID agentID, Caps caps) | ||
374 | { | ||
375 | RegisterFetchDescendentsCap(agentID, caps, "FetchInventoryDescendents2", m_fetchInventoryDescendents2Url); | ||
136 | } | 376 | } |
137 | 377 | ||
138 | private void RegisterFetchCap(UUID agentID, Caps caps, string capName, string url) | 378 | private void RegisterFetchDescendentsCap(UUID agentID, Caps caps, string capName, string url) |
139 | { | 379 | { |
140 | string capUrl; | 380 | string capUrl; |
141 | 381 | ||
142 | if (url == "localhost") | 382 | // disable the cap clause |
383 | if (url == "") | ||
384 | { | ||
385 | return; | ||
386 | } | ||
387 | // handled by the simulator | ||
388 | else if (url == "localhost") | ||
143 | { | 389 | { |
144 | capUrl = "/CAPS/" + UUID.Random(); | 390 | capUrl = "/CAPS/" + UUID.Random() + "/"; |
145 | 391 | ||
146 | IRequestHandler reqHandler | 392 | // Register this as a poll service |
147 | = new RestStreamHandler( | 393 | PollServiceInventoryEventArgs args = new PollServiceInventoryEventArgs(this, capUrl, agentID); |
148 | "POST", | 394 | args.Type = PollServiceEventArgs.EventType.Inventory; |
149 | capUrl, | ||
150 | m_webFetchHandler.FetchInventoryDescendentsRequest, | ||
151 | "FetchInventoryDescendents2", | ||
152 | agentID.ToString()); | ||
153 | 395 | ||
154 | caps.RegisterHandler(capName, reqHandler); | 396 | caps.RegisterPollHandler(capName, args); |
155 | } | 397 | } |
398 | // external handler | ||
156 | else | 399 | else |
157 | { | 400 | { |
158 | capUrl = url; | 401 | capUrl = url; |
402 | IExternalCapsModule handler = Scene.RequestModuleInterface<IExternalCapsModule>(); | ||
403 | if (handler != null) | ||
404 | handler.RegisterExternalUserCapsHandler(agentID,caps,capName,capUrl); | ||
405 | else | ||
406 | caps.RegisterHandler(capName, capUrl); | ||
407 | } | ||
159 | 408 | ||
160 | caps.RegisterHandler(capName, capUrl); | 409 | // m_log.DebugFormat( |
410 | // "[FETCH INVENTORY DESCENDENTS2 MODULE]: Registered capability {0} at {1} in region {2} for {3}", | ||
411 | // capName, capUrl, m_scene.RegionInfo.RegionName, agentID); | ||
412 | } | ||
413 | |||
414 | // private void DeregisterCaps(UUID agentID, Caps caps) | ||
415 | // { | ||
416 | // string capUrl; | ||
417 | // | ||
418 | // if (m_capsDict.TryGetValue(agentID, out capUrl)) | ||
419 | // { | ||
420 | // MainServer.Instance.RemoveHTTPHandler("", capUrl); | ||
421 | // m_capsDict.Remove(agentID); | ||
422 | // } | ||
423 | // } | ||
424 | |||
425 | private void DoInventoryRequests() | ||
426 | { | ||
427 | while (true) | ||
428 | { | ||
429 | Watchdog.UpdateThread(); | ||
430 | |||
431 | WaitProcessQueuedInventoryRequest(); | ||
161 | } | 432 | } |
433 | } | ||
434 | |||
435 | public void WaitProcessQueuedInventoryRequest() | ||
436 | { | ||
437 | aPollRequest poolreq = m_queue.Dequeue(); | ||
162 | 438 | ||
163 | // m_log.DebugFormat( | 439 | if (poolreq != null && poolreq.thepoll != null) |
164 | // "[WEB FETCH INV DESC MODULE]: Registered capability {0} at {1} in region {2} for {3}", | 440 | { |
165 | // capName, capUrl, m_scene.RegionInfo.RegionName, agentID); | 441 | try |
442 | { | ||
443 | poolreq.thepoll.Process(poolreq); | ||
444 | } | ||
445 | catch (Exception e) | ||
446 | { | ||
447 | m_log.ErrorFormat( | ||
448 | "[INVENTORY]: Failed to process queued inventory request {0} for {1} in {2}. Exception {3}", | ||
449 | poolreq.reqID, poolreq.presence != null ? poolreq.presence.Name : "unknown", Scene.Name, e); | ||
450 | } | ||
451 | } | ||
166 | } | 452 | } |
167 | } | 453 | } |
168 | } \ No newline at end of file | 454 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/J2KImage.cs b/OpenSim/Region/ClientStack/Linden/UDP/J2KImage.cs index afbe56b..4d0568d 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/J2KImage.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/J2KImage.cs | |||
@@ -421,12 +421,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
421 | // foreign user is visiting, we need to try again after the first fail to the local | 421 | // foreign user is visiting, we need to try again after the first fail to the local |
422 | // asset service. | 422 | // asset service. |
423 | string assetServerURL = string.Empty; | 423 | string assetServerURL = string.Empty; |
424 | if (InventoryAccessModule.IsForeignUser(AgentID, out assetServerURL)) | 424 | if (InventoryAccessModule.IsForeignUser(AgentID, out assetServerURL) && !string.IsNullOrEmpty(assetServerURL)) |
425 | { | 425 | { |
426 | if (!assetServerURL.EndsWith("/") && !assetServerURL.EndsWith("=")) | 426 | if (!assetServerURL.EndsWith("/") && !assetServerURL.EndsWith("=")) |
427 | assetServerURL = assetServerURL + "/"; | 427 | assetServerURL = assetServerURL + "/"; |
428 | 428 | ||
429 | m_log.DebugFormat("[J2KIMAGE]: texture {0} not found in local asset storage. Trying user's storage.", assetServerURL + id); | 429 | // m_log.DebugFormat("[J2KIMAGE]: texture {0} not found in local asset storage. Trying user's storage.", assetServerURL + id); |
430 | AssetService.Get(assetServerURL + id, InventoryAccessModule, AssetReceived); | 430 | AssetService.Get(assetServerURL + id, InventoryAccessModule, AssetReceived); |
431 | return; | 431 | return; |
432 | } | 432 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 967fa44..b17b822 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs | |||
@@ -34,11 +34,13 @@ using System.Text; | |||
34 | using System.Threading; | 34 | using System.Threading; |
35 | using System.Timers; | 35 | using System.Timers; |
36 | using System.Xml; | 36 | using System.Xml; |
37 | |||
37 | using log4net; | 38 | using log4net; |
38 | using OpenMetaverse; | 39 | using OpenMetaverse; |
39 | using OpenMetaverse.Packets; | 40 | using OpenMetaverse.Packets; |
40 | using OpenMetaverse.Messages.Linden; | 41 | using OpenMetaverse.Messages.Linden; |
41 | using OpenMetaverse.StructuredData; | 42 | using OpenMetaverse.StructuredData; |
43 | |||
42 | using OpenSim.Framework; | 44 | using OpenSim.Framework; |
43 | using OpenSim.Framework.Client; | 45 | using OpenSim.Framework.Client; |
44 | using OpenSim.Framework.Monitoring; | 46 | using OpenSim.Framework.Monitoring; |
@@ -48,9 +50,9 @@ using OpenSim.Services.Interfaces; | |||
48 | using Timer = System.Timers.Timer; | 50 | using Timer = System.Timers.Timer; |
49 | using AssetLandmark = OpenSim.Framework.AssetLandmark; | 51 | using AssetLandmark = OpenSim.Framework.AssetLandmark; |
50 | using RegionFlags = OpenMetaverse.RegionFlags; | 52 | using RegionFlags = OpenMetaverse.RegionFlags; |
51 | using Nini.Config; | ||
52 | 53 | ||
53 | using System.IO; | 54 | using System.IO; |
55 | using PermissionMask = OpenSim.Framework.PermissionMask; | ||
54 | 56 | ||
55 | namespace OpenSim.Region.ClientStack.LindenUDP | 57 | namespace OpenSim.Region.ClientStack.LindenUDP |
56 | { | 58 | { |
@@ -69,7 +71,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
69 | 71 | ||
70 | #region Events | 72 | #region Events |
71 | 73 | ||
72 | public event GenericMessage OnGenericMessage; | ||
73 | public event BinaryGenericMessage OnBinaryGenericMessage; | 74 | public event BinaryGenericMessage OnBinaryGenericMessage; |
74 | public event Action<IClientAPI> OnLogout; | 75 | public event Action<IClientAPI> OnLogout; |
75 | public event ObjectPermissions OnObjectPermissions; | 76 | public event ObjectPermissions OnObjectPermissions; |
@@ -77,7 +78,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
77 | public event ViewerEffectEventHandler OnViewerEffect; | 78 | public event ViewerEffectEventHandler OnViewerEffect; |
78 | public event ImprovedInstantMessage OnInstantMessage; | 79 | public event ImprovedInstantMessage OnInstantMessage; |
79 | public event ChatMessage OnChatFromClient; | 80 | public event ChatMessage OnChatFromClient; |
80 | public event TextureRequest OnRequestTexture; | ||
81 | public event RezObject OnRezObject; | 81 | public event RezObject OnRezObject; |
82 | public event DeRezObject OnDeRezObject; | 82 | public event DeRezObject OnDeRezObject; |
83 | public event ModifyTerrain OnModifyTerrain; | 83 | public event ModifyTerrain OnModifyTerrain; |
@@ -94,6 +94,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
94 | public event Action<IClientAPI, bool> OnCompleteMovementToRegion; | 94 | public event Action<IClientAPI, bool> OnCompleteMovementToRegion; |
95 | public event UpdateAgent OnPreAgentUpdate; | 95 | public event UpdateAgent OnPreAgentUpdate; |
96 | public event UpdateAgent OnAgentUpdate; | 96 | public event UpdateAgent OnAgentUpdate; |
97 | public event UpdateAgent OnAgentCameraUpdate; | ||
97 | public event AgentRequestSit OnAgentRequestSit; | 98 | public event AgentRequestSit OnAgentRequestSit; |
98 | public event AgentSit OnAgentSit; | 99 | public event AgentSit OnAgentSit; |
99 | public event AvatarPickerRequest OnAvatarPickerRequest; | 100 | public event AvatarPickerRequest OnAvatarPickerRequest; |
@@ -134,15 +135,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
134 | public event UpdatePrimGroupRotation OnUpdatePrimGroupMouseRotation; | 135 | public event UpdatePrimGroupRotation OnUpdatePrimGroupMouseRotation; |
135 | public event UpdateVector OnUpdatePrimScale; | 136 | public event UpdateVector OnUpdatePrimScale; |
136 | public event UpdateVector OnUpdatePrimGroupScale; | 137 | public event UpdateVector OnUpdatePrimGroupScale; |
137 | public event StatusChange OnChildAgentStatus; | ||
138 | public event GenericCall2 OnStopMovement; | ||
139 | public event Action<UUID> OnRemoveAvatar; | ||
140 | public event RequestMapBlocks OnRequestMapBlocks; | 138 | public event RequestMapBlocks OnRequestMapBlocks; |
141 | public event RequestMapName OnMapNameRequest; | 139 | public event RequestMapName OnMapNameRequest; |
142 | public event TeleportLocationRequest OnTeleportLocationRequest; | 140 | public event TeleportLocationRequest OnTeleportLocationRequest; |
143 | public event TeleportLandmarkRequest OnTeleportLandmarkRequest; | 141 | public event TeleportLandmarkRequest OnTeleportLandmarkRequest; |
144 | public event TeleportCancel OnTeleportCancel; | 142 | public event TeleportCancel OnTeleportCancel; |
145 | public event DisconnectUser OnDisconnectUser; | ||
146 | public event RequestAvatarProperties OnRequestAvatarProperties; | 143 | public event RequestAvatarProperties OnRequestAvatarProperties; |
147 | public event SetAlwaysRun OnSetAlwaysRun; | 144 | public event SetAlwaysRun OnSetAlwaysRun; |
148 | public event FetchInventory OnAgentDataUpdateRequest; | 145 | public event FetchInventory OnAgentDataUpdateRequest; |
@@ -172,7 +169,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
172 | public event UpdateTaskInventory OnUpdateTaskInventory; | 169 | public event UpdateTaskInventory OnUpdateTaskInventory; |
173 | public event MoveTaskInventory OnMoveTaskItem; | 170 | public event MoveTaskInventory OnMoveTaskItem; |
174 | public event RemoveTaskInventory OnRemoveTaskItem; | 171 | public event RemoveTaskInventory OnRemoveTaskItem; |
175 | public event RequestAsset OnRequestAsset; | ||
176 | public event UUIDNameRequest OnNameFromUUIDRequest; | 172 | public event UUIDNameRequest OnNameFromUUIDRequest; |
177 | public event ParcelAccessListRequest OnParcelAccessListRequest; | 173 | public event ParcelAccessListRequest OnParcelAccessListRequest; |
178 | public event ParcelAccessListUpdateRequest OnParcelAccessListUpdateRequest; | 174 | public event ParcelAccessListUpdateRequest OnParcelAccessListUpdateRequest; |
@@ -203,7 +199,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
203 | public event RequestPayPrice OnRequestPayPrice; | 199 | public event RequestPayPrice OnRequestPayPrice; |
204 | public event ObjectSaleInfo OnObjectSaleInfo; | 200 | public event ObjectSaleInfo OnObjectSaleInfo; |
205 | public event ObjectBuy OnObjectBuy; | 201 | public event ObjectBuy OnObjectBuy; |
206 | public event BuyObjectInventory OnBuyObjectInventory; | ||
207 | public event AgentSit OnUndo; | 202 | public event AgentSit OnUndo; |
208 | public event AgentSit OnRedo; | 203 | public event AgentSit OnRedo; |
209 | public event LandUndo OnLandUndo; | 204 | public event LandUndo OnLandUndo; |
@@ -212,7 +207,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
212 | public event RequestObjectPropertiesFamily OnObjectGroupRequest; | 207 | public event RequestObjectPropertiesFamily OnObjectGroupRequest; |
213 | public event DetailedEstateDataRequest OnDetailedEstateDataRequest; | 208 | public event DetailedEstateDataRequest OnDetailedEstateDataRequest; |
214 | public event SetEstateFlagsRequest OnSetEstateFlagsRequest; | 209 | public event SetEstateFlagsRequest OnSetEstateFlagsRequest; |
215 | public event SetEstateTerrainBaseTexture OnSetEstateTerrainBaseTexture; | ||
216 | public event SetEstateTerrainDetailTexture OnSetEstateTerrainDetailTexture; | 210 | public event SetEstateTerrainDetailTexture OnSetEstateTerrainDetailTexture; |
217 | public event SetEstateTerrainTextureHeights OnSetEstateTerrainTextureHeights; | 211 | public event SetEstateTerrainTextureHeights OnSetEstateTerrainTextureHeights; |
218 | public event CommitEstateTerrainTextureRequest OnCommitEstateTerrainTextureRequest; | 212 | public event CommitEstateTerrainTextureRequest OnCommitEstateTerrainTextureRequest; |
@@ -235,7 +229,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
235 | public event GetScriptRunning OnGetScriptRunning; | 229 | public event GetScriptRunning OnGetScriptRunning; |
236 | public event SetScriptRunning OnSetScriptRunning; | 230 | public event SetScriptRunning OnSetScriptRunning; |
237 | public event Action<Vector3, bool, bool> OnAutoPilotGo; | 231 | public event Action<Vector3, bool, bool> OnAutoPilotGo; |
238 | public event TerrainUnacked OnUnackedTerrain; | ||
239 | public event ActivateGesture OnActivateGesture; | 232 | public event ActivateGesture OnActivateGesture; |
240 | public event DeactivateGesture OnDeactivateGesture; | 233 | public event DeactivateGesture OnDeactivateGesture; |
241 | public event ObjectOwner OnObjectOwner; | 234 | public event ObjectOwner OnObjectOwner; |
@@ -293,6 +286,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
293 | public event GodlikeMessage onGodlikeMessage; | 286 | public event GodlikeMessage onGodlikeMessage; |
294 | public event GodUpdateRegionInfoUpdate OnGodUpdateRegionInfoUpdate; | 287 | public event GodUpdateRegionInfoUpdate OnGodUpdateRegionInfoUpdate; |
295 | 288 | ||
289 | #pragma warning disable 0067 | ||
290 | public event GenericMessage OnGenericMessage; | ||
291 | public event TextureRequest OnRequestTexture; | ||
292 | public event StatusChange OnChildAgentStatus; | ||
293 | public event GenericCall2 OnStopMovement; | ||
294 | public event Action<UUID> OnRemoveAvatar; | ||
295 | public event DisconnectUser OnDisconnectUser; | ||
296 | public event RequestAsset OnRequestAsset; | ||
297 | public event BuyObjectInventory OnBuyObjectInventory; | ||
298 | public event SetEstateTerrainBaseTexture OnSetEstateTerrainBaseTexture; | ||
299 | public event TerrainUnacked OnUnackedTerrain; | ||
300 | public event CachedTextureRequest OnCachedTextureRequest; | ||
301 | #pragma warning restore 0067 | ||
302 | |||
296 | #endregion Events | 303 | #endregion Events |
297 | 304 | ||
298 | #region Class Members | 305 | #region Class Members |
@@ -304,6 +311,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
304 | private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; | 311 | private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; |
305 | 312 | ||
306 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 313 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
314 | private static string LogHeader = "[LLCLIENTVIEW]"; | ||
307 | protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients | 315 | protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients |
308 | 316 | ||
309 | /// <summary> | 317 | /// <summary> |
@@ -325,7 +333,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
325 | private PriorityQueue m_entityProps; | 333 | private PriorityQueue m_entityProps; |
326 | private Prioritizer m_prioritizer; | 334 | private Prioritizer m_prioritizer; |
327 | private bool m_disableFacelights = false; | 335 | private bool m_disableFacelights = false; |
328 | 336 | private volatile bool m_justEditedTerrain = false; | |
329 | /// <value> | 337 | /// <value> |
330 | /// List used in construction of data blocks for an object update packet. This is to stop us having to | 338 | /// List used in construction of data blocks for an object update packet. This is to stop us having to |
331 | /// continually recreate it. | 339 | /// continually recreate it. |
@@ -344,7 +352,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
344 | 352 | ||
345 | // protected HashSet<uint> m_attachmentsSent; | 353 | // protected HashSet<uint> m_attachmentsSent; |
346 | 354 | ||
347 | private int m_moneyBalance; | ||
348 | private int m_animationSequenceNumber = 1; | 355 | private int m_animationSequenceNumber = 1; |
349 | private bool m_SendLogoutPacketWhenClosing = true; | 356 | private bool m_SendLogoutPacketWhenClosing = true; |
350 | 357 | ||
@@ -357,7 +364,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
357 | /// This does mean that agent updates must be processed synchronously, at least for each client, and called methods | 364 | /// This does mean that agent updates must be processed synchronously, at least for each client, and called methods |
358 | /// cannot retain a reference to it outside of that method. | 365 | /// cannot retain a reference to it outside of that method. |
359 | /// </remarks> | 366 | /// </remarks> |
360 | private AgentUpdateArgs m_lastAgentUpdateArgs; | 367 | private AgentUpdateArgs m_thisAgentUpdateArgs = new AgentUpdateArgs(); |
361 | 368 | ||
362 | protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>(); | 369 | protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>(); |
363 | protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers | 370 | protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers |
@@ -393,9 +400,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
393 | } | 400 | } |
394 | public UUID AgentId { get { return m_agentId; } } | 401 | public UUID AgentId { get { return m_agentId; } } |
395 | public ISceneAgent SceneAgent { get; set; } | 402 | public ISceneAgent SceneAgent { get; set; } |
396 | public UUID ActiveGroupId { get { return m_activeGroupID; } } | 403 | public UUID ActiveGroupId { get { return m_activeGroupID; } private set { m_activeGroupID = value; } } |
397 | public string ActiveGroupName { get { return m_activeGroupName; } } | 404 | public string ActiveGroupName { get { return m_activeGroupName; } private set { m_activeGroupName = value; } } |
398 | public ulong ActiveGroupPowers { get { return m_activeGroupPowers; } } | 405 | public ulong ActiveGroupPowers { get { return m_activeGroupPowers; } private set { m_activeGroupPowers = value; } } |
399 | public bool IsGroupMember(UUID groupID) { return m_groupPowers.ContainsKey(groupID); } | 406 | public bool IsGroupMember(UUID groupID) { return m_groupPowers.ContainsKey(groupID); } |
400 | 407 | ||
401 | /// <summary> | 408 | /// <summary> |
@@ -419,7 +426,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
419 | public string Name { get { return FirstName + " " + LastName; } } | 426 | public string Name { get { return FirstName + " " + LastName; } } |
420 | 427 | ||
421 | public uint CircuitCode { get { return m_circuitCode; } } | 428 | public uint CircuitCode { get { return m_circuitCode; } } |
422 | public int MoneyBalance { get { return m_moneyBalance; } } | ||
423 | public int NextAnimationSequenceNumber { get { return m_animationSequenceNumber++; } } | 429 | public int NextAnimationSequenceNumber { get { return m_animationSequenceNumber++; } } |
424 | 430 | ||
425 | /// <summary> | 431 | /// <summary> |
@@ -447,7 +453,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
447 | 453 | ||
448 | // ~LLClientView() | 454 | // ~LLClientView() |
449 | // { | 455 | // { |
450 | // m_log.DebugFormat("[LLCLIENTVIEW]: Destructor called for {0}, circuit code {1}", Name, CircuitCode); | 456 | // m_log.DebugFormat("{0} Destructor called for {1}, circuit code {2}", LogHeader, Name, CircuitCode); |
451 | // } | 457 | // } |
452 | 458 | ||
453 | /// <summary> | 459 | /// <summary> |
@@ -482,11 +488,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
482 | m_firstName = sessionInfo.LoginInfo.First; | 488 | m_firstName = sessionInfo.LoginInfo.First; |
483 | m_lastName = sessionInfo.LoginInfo.Last; | 489 | m_lastName = sessionInfo.LoginInfo.Last; |
484 | m_startpos = sessionInfo.LoginInfo.StartPos; | 490 | m_startpos = sessionInfo.LoginInfo.StartPos; |
485 | m_moneyBalance = 1000; | ||
486 | 491 | ||
487 | m_udpServer = udpServer; | 492 | m_udpServer = udpServer; |
488 | m_udpClient = udpClient; | 493 | m_udpClient = udpClient; |
489 | m_udpClient.OnQueueEmpty += HandleQueueEmpty; | 494 | m_udpClient.OnQueueEmpty += HandleQueueEmpty; |
495 | m_udpClient.HasUpdates += HandleHasUpdates; | ||
490 | m_udpClient.OnPacketStats += PopulateStats; | 496 | m_udpClient.OnPacketStats += PopulateStats; |
491 | 497 | ||
492 | m_prioritizer = new Prioritizer(m_scene); | 498 | m_prioritizer = new Prioritizer(m_scene); |
@@ -512,7 +518,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
512 | // We still perform a force close inside the sync lock since this is intended to attempt close where | 518 | // We still perform a force close inside the sync lock since this is intended to attempt close where |
513 | // there is some unidentified connection problem, not where we have issues due to deadlock | 519 | // there is some unidentified connection problem, not where we have issues due to deadlock |
514 | if (!IsActive && !force) | 520 | if (!IsActive && !force) |
521 | { | ||
522 | m_log.DebugFormat( "{0} Not attempting to close inactive client {1} in {2} since force flag is not set", | ||
523 | LogHeader, Name, m_scene.Name); | ||
524 | |||
515 | return; | 525 | return; |
526 | } | ||
516 | 527 | ||
517 | IsActive = false; | 528 | IsActive = false; |
518 | CloseWithoutChecks(); | 529 | CloseWithoutChecks(); |
@@ -637,12 +648,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
637 | /// <returns>true if the handler was added. This is currently always the case.</returns> | 648 | /// <returns>true if the handler was added. This is currently always the case.</returns> |
638 | public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync) | 649 | public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync) |
639 | { | 650 | { |
651 | return AddLocalPacketHandler(packetType, handler, doAsync, false); | ||
652 | } | ||
653 | |||
654 | /// <summary> | ||
655 | /// Add a handler for the given packet type. | ||
656 | /// </summary> | ||
657 | /// <param name="packetType"></param> | ||
658 | /// <param name="handler"></param> | ||
659 | /// <param name="doAsync"> | ||
660 | /// If true, when the packet is received handle it on a different thread. Whether this is given direct to | ||
661 | /// a threadpool thread or placed in a queue depends on the inEngine parameter. | ||
662 | /// </param> | ||
663 | /// <param name="inEngine"> | ||
664 | /// If async is false then this parameter is ignored. | ||
665 | /// If async is true and inEngine is false, then the packet is sent directly to a | ||
666 | /// threadpool thread. | ||
667 | /// If async is true and inEngine is true, then the packet is sent to the IncomingPacketAsyncHandlingEngine. | ||
668 | /// This may result in slower handling but reduces the risk of overloading the simulator when there are many | ||
669 | /// simultaneous async requests. | ||
670 | /// </param> | ||
671 | /// <returns>true if the handler was added. This is currently always the case.</returns> | ||
672 | public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync, bool inEngine) | ||
673 | { | ||
640 | bool result = false; | 674 | bool result = false; |
641 | lock (m_packetHandlers) | 675 | lock (m_packetHandlers) |
642 | { | 676 | { |
643 | if (!m_packetHandlers.ContainsKey(packetType)) | 677 | if (!m_packetHandlers.ContainsKey(packetType)) |
644 | { | 678 | { |
645 | m_packetHandlers.Add(packetType, new PacketProcessor() { method = handler, Async = doAsync }); | 679 | m_packetHandlers.Add( |
680 | packetType, new PacketProcessor() { method = handler, Async = doAsync, InEngine = inEngine }); | ||
646 | result = true; | 681 | result = true; |
647 | } | 682 | } |
648 | } | 683 | } |
@@ -677,15 +712,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
677 | PacketProcessor pprocessor; | 712 | PacketProcessor pprocessor; |
678 | if (m_packetHandlers.TryGetValue(packet.Type, out pprocessor)) | 713 | if (m_packetHandlers.TryGetValue(packet.Type, out pprocessor)) |
679 | { | 714 | { |
715 | ClientInfo cinfo = UDPClient.GetClientInfo(); | ||
716 | |||
680 | //there is a local handler for this packet type | 717 | //there is a local handler for this packet type |
681 | if (pprocessor.Async) | 718 | if (pprocessor.Async) |
682 | { | 719 | { |
720 | if (!cinfo.AsyncRequests.ContainsKey(packet.Type.ToString())) | ||
721 | cinfo.AsyncRequests[packet.Type.ToString()] = 0; | ||
722 | cinfo.AsyncRequests[packet.Type.ToString()]++; | ||
723 | |||
683 | object obj = new AsyncPacketProcess(this, pprocessor.method, packet); | 724 | object obj = new AsyncPacketProcess(this, pprocessor.method, packet); |
684 | Util.FireAndForget(ProcessSpecificPacketAsync, obj); | 725 | |
726 | if (pprocessor.InEngine) | ||
727 | m_udpServer.IpahEngine.QueueJob(packet.Type.ToString(), () => ProcessSpecificPacketAsync(obj)); | ||
728 | else | ||
729 | Util.FireAndForget(ProcessSpecificPacketAsync, obj, packet.Type.ToString()); | ||
730 | |||
685 | result = true; | 731 | result = true; |
686 | } | 732 | } |
687 | else | 733 | else |
688 | { | 734 | { |
735 | if (!cinfo.SyncRequests.ContainsKey(packet.Type.ToString())) | ||
736 | cinfo.SyncRequests[packet.Type.ToString()] = 0; | ||
737 | cinfo.SyncRequests[packet.Type.ToString()]++; | ||
738 | |||
689 | result = pprocessor.method(this, packet); | 739 | result = pprocessor.method(this, packet); |
690 | } | 740 | } |
691 | } | 741 | } |
@@ -700,6 +750,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
700 | } | 750 | } |
701 | if (found) | 751 | if (found) |
702 | { | 752 | { |
753 | ClientInfo cinfo = UDPClient.GetClientInfo(); | ||
754 | if (!cinfo.GenericRequests.ContainsKey(packet.Type.ToString())) | ||
755 | cinfo.GenericRequests[packet.Type.ToString()] = 0; | ||
756 | cinfo.GenericRequests[packet.Type.ToString()]++; | ||
757 | |||
703 | result = method(this, packet); | 758 | result = method(this, packet); |
704 | } | 759 | } |
705 | } | 760 | } |
@@ -717,9 +772,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
717 | catch (Exception e) | 772 | catch (Exception e) |
718 | { | 773 | { |
719 | // Make sure that we see any exception caused by the asynchronous operation. | 774 | // Make sure that we see any exception caused by the asynchronous operation. |
720 | m_log.ErrorFormat( | 775 | m_log.Error( |
721 | "[LLCLIENTVIEW]: Caught exception while processing {0} for {1}, {2} {3}", | 776 | string.Format( |
722 | packetObject.Pack, Name, e.Message, e.StackTrace); | 777 | "[LLCLIENTVIEW]: Caught exception while processing {0} for {1} ", packetObject.Pack, Name), |
778 | e); | ||
723 | } | 779 | } |
724 | } | 780 | } |
725 | 781 | ||
@@ -729,7 +785,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
729 | 785 | ||
730 | public virtual void Start() | 786 | public virtual void Start() |
731 | { | 787 | { |
732 | m_scene.AddNewClient(this, PresenceType.User); | 788 | m_scene.AddNewAgent(this, PresenceType.User); |
733 | 789 | ||
734 | RefreshGroupMembership(); | 790 | RefreshGroupMembership(); |
735 | } | 791 | } |
@@ -791,9 +847,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
791 | handshake.RegionInfo3.ProductName = Util.StringToBytes256(regionInfo.RegionType); | 847 | handshake.RegionInfo3.ProductName = Util.StringToBytes256(regionInfo.RegionType); |
792 | handshake.RegionInfo3.ProductSKU = Utils.EmptyBytes; | 848 | handshake.RegionInfo3.ProductSKU = Utils.EmptyBytes; |
793 | 849 | ||
794 | OutPacket(handshake, ThrottleOutPacketType.Task); | 850 | handshake.RegionInfo4 = new RegionHandshakePacket.RegionInfo4Block[1]; |
851 | handshake.RegionInfo4[0] = new RegionHandshakePacket.RegionInfo4Block(); | ||
852 | handshake.RegionInfo4[0].RegionFlagsExtended = args.regionFlags; | ||
853 | handshake.RegionInfo4[0].RegionProtocols = 0; // 1 here would indicate that SSB is supported | ||
854 | |||
855 | OutPacket(handshake, ThrottleOutPacketType.Unknown); | ||
795 | } | 856 | } |
796 | 857 | ||
858 | |||
797 | public void MoveAgentIntoRegion(RegionInfo regInfo, Vector3 pos, Vector3 look) | 859 | public void MoveAgentIntoRegion(RegionInfo regInfo, Vector3 pos, Vector3 look) |
798 | { | 860 | { |
799 | AgentMovementCompletePacket mov = (AgentMovementCompletePacket)PacketPool.Instance.GetPacket(PacketType.AgentMovementComplete); | 861 | AgentMovementCompletePacket mov = (AgentMovementCompletePacket)PacketPool.Instance.GetPacket(PacketType.AgentMovementComplete); |
@@ -893,9 +955,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
893 | } | 955 | } |
894 | } | 956 | } |
895 | 957 | ||
896 | public void SendGenericMessage(string method, List<string> message) | 958 | public void SendGenericMessage(string method, UUID invoice, List<string> message) |
897 | { | 959 | { |
898 | GenericMessagePacket gmp = new GenericMessagePacket(); | 960 | GenericMessagePacket gmp = new GenericMessagePacket(); |
961 | |||
962 | gmp.AgentData.AgentID = AgentId; | ||
963 | gmp.AgentData.SessionID = m_sessionId; | ||
964 | gmp.AgentData.TransactionID = invoice; | ||
965 | |||
899 | gmp.MethodData.Method = Util.StringToBytes256(method); | 966 | gmp.MethodData.Method = Util.StringToBytes256(method); |
900 | gmp.ParamList = new GenericMessagePacket.ParamListBlock[message.Count]; | 967 | gmp.ParamList = new GenericMessagePacket.ParamListBlock[message.Count]; |
901 | int i = 0; | 968 | int i = 0; |
@@ -908,9 +975,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
908 | OutPacket(gmp, ThrottleOutPacketType.Task); | 975 | OutPacket(gmp, ThrottleOutPacketType.Task); |
909 | } | 976 | } |
910 | 977 | ||
911 | public void SendGenericMessage(string method, List<byte[]> message) | 978 | public void SendGenericMessage(string method, UUID invoice, List<byte[]> message) |
912 | { | 979 | { |
913 | GenericMessagePacket gmp = new GenericMessagePacket(); | 980 | GenericMessagePacket gmp = new GenericMessagePacket(); |
981 | |||
982 | gmp.AgentData.AgentID = AgentId; | ||
983 | gmp.AgentData.SessionID = m_sessionId; | ||
984 | gmp.AgentData.TransactionID = invoice; | ||
985 | |||
914 | gmp.MethodData.Method = Util.StringToBytes256(method); | 986 | gmp.MethodData.Method = Util.StringToBytes256(method); |
915 | gmp.ParamList = new GenericMessagePacket.ParamListBlock[message.Count]; | 987 | gmp.ParamList = new GenericMessagePacket.ParamListBlock[message.Count]; |
916 | int i = 0; | 988 | int i = 0; |
@@ -1112,11 +1184,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1112 | 1184 | ||
1113 | /// <summary> | 1185 | /// <summary> |
1114 | /// Send the region heightmap to the client | 1186 | /// Send the region heightmap to the client |
1187 | /// This method is only called when not doing intellegent terrain patch sending and | ||
1188 | /// is only called when the scene presence is initially created and sends all of the | ||
1189 | /// region's patches to the client. | ||
1115 | /// </summary> | 1190 | /// </summary> |
1116 | /// <param name="map">heightmap</param> | 1191 | /// <param name="map">heightmap</param> |
1117 | public virtual void SendLayerData(float[] map) | 1192 | public virtual void SendLayerData(float[] map) |
1118 | { | 1193 | { |
1119 | Util.FireAndForget(DoSendLayerData, map); | 1194 | Util.FireAndForget(DoSendLayerData, m_scene.Heightmap.GetTerrainData(), "LLClientView.DoSendLayerData"); |
1120 | } | 1195 | } |
1121 | 1196 | ||
1122 | /// <summary> | 1197 | /// <summary> |
@@ -1125,10 +1200,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1125 | /// <param name="o"></param> | 1200 | /// <param name="o"></param> |
1126 | private void DoSendLayerData(object o) | 1201 | private void DoSendLayerData(object o) |
1127 | { | 1202 | { |
1128 | float[] map = LLHeightFieldMoronize((float[])o); | 1203 | TerrainData map = (TerrainData)o; |
1129 | 1204 | ||
1130 | try | 1205 | try |
1131 | { | 1206 | { |
1207 | // Send LayerData in typerwriter pattern | ||
1132 | //for (int y = 0; y < 16; y++) | 1208 | //for (int y = 0; y < 16; y++) |
1133 | //{ | 1209 | //{ |
1134 | // for (int x = 0; x < 16; x++) | 1210 | // for (int x = 0; x < 16; x++) |
@@ -1138,7 +1214,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1138 | //} | 1214 | //} |
1139 | 1215 | ||
1140 | // Send LayerData in a spiral pattern. Fun! | 1216 | // Send LayerData in a spiral pattern. Fun! |
1141 | SendLayerTopRight(map, 0, 0, 15, 15); | 1217 | SendLayerTopRight(map, 0, 0, map.SizeX/Constants.TerrainPatchSize-1, map.SizeY/Constants.TerrainPatchSize-1); |
1142 | } | 1218 | } |
1143 | catch (Exception e) | 1219 | catch (Exception e) |
1144 | { | 1220 | { |
@@ -1146,7 +1222,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1146 | } | 1222 | } |
1147 | } | 1223 | } |
1148 | 1224 | ||
1149 | private void SendLayerTopRight(float[] map, int x1, int y1, int x2, int y2) | 1225 | private void SendLayerTopRight(TerrainData map, int x1, int y1, int x2, int y2) |
1150 | { | 1226 | { |
1151 | // Row | 1227 | // Row |
1152 | for (int i = x1; i <= x2; i++) | 1228 | for (int i = x1; i <= x2; i++) |
@@ -1156,11 +1232,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1156 | for (int j = y1 + 1; j <= y2; j++) | 1232 | for (int j = y1 + 1; j <= y2; j++) |
1157 | SendLayerData(x2, j, map); | 1233 | SendLayerData(x2, j, map); |
1158 | 1234 | ||
1159 | if (x2 - x1 > 0) | 1235 | if (x2 - x1 > 0 && y2 - y1 > 0) |
1160 | SendLayerBottomLeft(map, x1, y1 + 1, x2 - 1, y2); | 1236 | SendLayerBottomLeft(map, x1, y1 + 1, x2 - 1, y2); |
1161 | } | 1237 | } |
1162 | 1238 | ||
1163 | void SendLayerBottomLeft(float[] map, int x1, int y1, int x2, int y2) | 1239 | void SendLayerBottomLeft(TerrainData map, int x1, int y1, int x2, int y2) |
1164 | { | 1240 | { |
1165 | // Row in reverse | 1241 | // Row in reverse |
1166 | for (int i = x2; i >= x1; i--) | 1242 | for (int i = x2; i >= x1; i--) |
@@ -1170,7 +1246,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1170 | for (int j = y2 - 1; j >= y1; j--) | 1246 | for (int j = y2 - 1; j >= y1; j--) |
1171 | SendLayerData(x1, j, map); | 1247 | SendLayerData(x1, j, map); |
1172 | 1248 | ||
1173 | if (x2 - x1 > 0) | 1249 | if (x2 - x1 > 0 && y2 - y1 > 0) |
1174 | SendLayerTopRight(map, x1 + 1, y1, x2, y2 - 1); | 1250 | SendLayerTopRight(map, x1 + 1, y1, x2, y2 - 1); |
1175 | } | 1251 | } |
1176 | 1252 | ||
@@ -1192,25 +1268,98 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1192 | // OutPacket(layerpack, ThrottleOutPacketType.Land); | 1268 | // OutPacket(layerpack, ThrottleOutPacketType.Land); |
1193 | // } | 1269 | // } |
1194 | 1270 | ||
1271 | // Legacy form of invocation that passes around a bare data array. | ||
1272 | // Just ignore what was passed and use the real terrain info that is part of the scene. | ||
1273 | // As a HORRIBLE kludge in an attempt to not change the definition of IClientAPI, | ||
1274 | // there is a special form for specifying multiple terrain patches to send. | ||
1275 | // The form is to pass 'px' as negative the number of patches to send and to | ||
1276 | // pass the float array as pairs of patch X and Y coordinates. So, passing 'px' | ||
1277 | // as -2 and map= [3, 5, 8, 4] would mean to send two terrain heightmap patches | ||
1278 | // and the patches to send are <3,5> and <8,4>. | ||
1279 | public void SendLayerData(int px, int py, float[] map) | ||
1280 | { | ||
1281 | if (px >= 0) | ||
1282 | { | ||
1283 | SendLayerData(px, py, m_scene.Heightmap.GetTerrainData()); | ||
1284 | } | ||
1285 | else | ||
1286 | { | ||
1287 | int numPatches = -px; | ||
1288 | int[] xPatches = new int[numPatches]; | ||
1289 | int[] yPatches = new int[numPatches]; | ||
1290 | for (int pp = 0; pp < numPatches; pp++) | ||
1291 | { | ||
1292 | xPatches[pp] = (int)map[pp * 2]; | ||
1293 | yPatches[pp] = (int)map[pp * 2 + 1]; | ||
1294 | } | ||
1295 | |||
1296 | // DebugSendingPatches("SendLayerData", xPatches, yPatches); | ||
1297 | |||
1298 | SendLayerData(xPatches, yPatches, m_scene.Heightmap.GetTerrainData()); | ||
1299 | } | ||
1300 | } | ||
1301 | |||
1302 | private void DebugSendingPatches(string pWho, int[] pX, int[] pY) | ||
1303 | { | ||
1304 | if (m_log.IsDebugEnabled) | ||
1305 | { | ||
1306 | int numPatches = pX.Length; | ||
1307 | string Xs = ""; | ||
1308 | string Ys = ""; | ||
1309 | for (int pp = 0; pp < numPatches; pp++) | ||
1310 | { | ||
1311 | Xs += String.Format("{0}", (int)pX[pp]) + ","; | ||
1312 | Ys += String.Format("{0}", (int)pY[pp]) + ","; | ||
1313 | } | ||
1314 | m_log.DebugFormat("{0} {1}: numPatches={2}, X={3}, Y={4}", LogHeader, pWho, numPatches, Xs, Ys); | ||
1315 | } | ||
1316 | } | ||
1317 | |||
1195 | /// <summary> | 1318 | /// <summary> |
1196 | /// Sends a specified patch to a client | 1319 | /// Sends a terrain packet for the point specified. |
1320 | /// This is a legacy call that has refarbed the terrain into a flat map of floats. | ||
1321 | /// We just use the terrain from the region we know about. | ||
1197 | /// </summary> | 1322 | /// </summary> |
1198 | /// <param name="px">Patch coordinate (x) 0..15</param> | 1323 | /// <param name="px">Patch coordinate (x) 0..15</param> |
1199 | /// <param name="py">Patch coordinate (y) 0..15</param> | 1324 | /// <param name="py">Patch coordinate (y) 0..15</param> |
1200 | /// <param name="map">heightmap</param> | 1325 | /// <param name="map">heightmap</param> |
1201 | public void SendLayerData(int px, int py, float[] map) | 1326 | public void SendLayerData(int px, int py, TerrainData terrData) |
1327 | { | ||
1328 | int[] xPatches = new[] { px }; | ||
1329 | int[] yPatches = new[] { py }; | ||
1330 | SendLayerData(xPatches, yPatches, terrData); | ||
1331 | } | ||
1332 | |||
1333 | private void SendLayerData(int[] px, int[] py, TerrainData terrData) | ||
1202 | { | 1334 | { |
1203 | try | 1335 | try |
1204 | { | 1336 | { |
1205 | int[] patches = new int[] { py * 16 + px }; | 1337 | /* test code using the terrain compressor in libOpenMetaverse |
1206 | float[] heightmap = (map.Length == 65536) ? | 1338 | int[] patchInd = new int[1]; |
1207 | map : | 1339 | patchInd[0] = px + (py * Constants.TerrainPatchSize); |
1208 | LLHeightFieldMoronize(map); | 1340 | LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(terrData.GetFloatsSerialized(), patchInd); |
1341 | */ | ||
1342 | // Many, many patches could have been passed to us. Since the patches will be compressed | ||
1343 | // into variable sized blocks, we cannot pre-compute how many will fit into one | ||
1344 | // packet. While some fancy packing algorithm is possible, 4 seems to always fit. | ||
1345 | int PatchesAssumedToFit = 4; | ||
1346 | for (int pcnt = 0; pcnt < px.Length; pcnt += PatchesAssumedToFit) | ||
1347 | { | ||
1348 | int remaining = Math.Min(px.Length - pcnt, PatchesAssumedToFit); | ||
1349 | int[] xPatches = new int[remaining]; | ||
1350 | int[] yPatches = new int[remaining]; | ||
1351 | for (int ii = 0; ii < remaining; ii++) | ||
1352 | { | ||
1353 | xPatches[ii] = px[pcnt + ii]; | ||
1354 | yPatches[ii] = py[pcnt + ii]; | ||
1355 | } | ||
1356 | LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLandPacket(terrData, xPatches, yPatches); | ||
1357 | // DebugSendingPatches("SendLayerDataInternal", xPatches, yPatches); | ||
1209 | 1358 | ||
1210 | LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches); | 1359 | SendTheLayerPacket(layerpack); |
1211 | layerpack.Header.Reliable = true; | 1360 | } |
1361 | // LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLandPacket(terrData, px, py); | ||
1212 | 1362 | ||
1213 | OutPacket(layerpack, ThrottleOutPacketType.Land); | ||
1214 | } | 1363 | } |
1215 | catch (Exception e) | 1364 | catch (Exception e) |
1216 | { | 1365 | { |
@@ -1218,36 +1367,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1218 | } | 1367 | } |
1219 | } | 1368 | } |
1220 | 1369 | ||
1221 | /// <summary> | 1370 | // When a user edits the terrain, so much data is sent, the data queues up fast and presents a |
1222 | /// Munges heightfield into the LLUDP backed in restricted heightfield. | 1371 | // sub optimal editing experience. To alleviate this issue, when the user edits the terrain, we |
1223 | /// </summary> | 1372 | // start skipping the queues until they're done editing the terrain. We also make them |
1224 | /// <param name="map">float array in the base; Constants.RegionSize</param> | 1373 | // unreliable because it's extremely likely that multiple packets will be sent for a terrain patch |
1225 | /// <returns>float array in the base 256</returns> | 1374 | // area invalidating previous packets for that area. |
1226 | internal float[] LLHeightFieldMoronize(float[] map) | 1375 | |
1376 | // It's possible for an editing user to flood themselves with edited packets but the majority | ||
1377 | // of use cases are such that only a tiny percentage of users will be editing the terrain. | ||
1378 | // Other, non-editing users will see the edits much slower. | ||
1379 | |||
1380 | // One last note on this topic, by the time users are going to be editing the terrain, it's | ||
1381 | // extremely likely that the sim will have rezzed already and therefore this is not likely going | ||
1382 | // to cause any additional issues with lost packets, objects or terrain patches. | ||
1383 | |||
1384 | // m_justEditedTerrain is volatile, so test once and duplicate two affected statements so we | ||
1385 | // only have one cache miss. | ||
1386 | private void SendTheLayerPacket(LayerDataPacket layerpack) | ||
1227 | { | 1387 | { |
1228 | if (map.Length == 65536) | 1388 | if (m_justEditedTerrain) |
1229 | return map; | 1389 | { |
1390 | layerpack.Header.Reliable = false; | ||
1391 | OutPacket(layerpack, ThrottleOutPacketType.Unknown ); | ||
1392 | } | ||
1230 | else | 1393 | else |
1231 | { | 1394 | { |
1232 | float[] returnmap = new float[65536]; | 1395 | layerpack.Header.Reliable = true; |
1233 | 1396 | OutPacket(layerpack, ThrottleOutPacketType.Land); | |
1234 | if (map.Length < 65535) | ||
1235 | { | ||
1236 | // rebase the vector stride to 256 | ||
1237 | for (int i = 0; i < Constants.RegionSize; i++) | ||
1238 | Array.Copy(map, i * (int)Constants.RegionSize, returnmap, i * 256, (int)Constants.RegionSize); | ||
1239 | } | ||
1240 | else | ||
1241 | { | ||
1242 | for (int i = 0; i < 256; i++) | ||
1243 | Array.Copy(map, i * (int)Constants.RegionSize, returnmap, i * 256, 256); | ||
1244 | } | ||
1245 | |||
1246 | //Array.Copy(map,0,returnmap,0,(map.Length < 65536)? map.Length : 65536); | ||
1247 | |||
1248 | return returnmap; | ||
1249 | } | 1397 | } |
1250 | |||
1251 | } | 1398 | } |
1252 | 1399 | ||
1253 | /// <summary> | 1400 | /// <summary> |
@@ -1256,7 +1403,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1256 | /// <param name="windSpeeds">16x16 array of wind speeds</param> | 1403 | /// <param name="windSpeeds">16x16 array of wind speeds</param> |
1257 | public virtual void SendWindData(Vector2[] windSpeeds) | 1404 | public virtual void SendWindData(Vector2[] windSpeeds) |
1258 | { | 1405 | { |
1259 | Util.FireAndForget(DoSendWindData, windSpeeds); | 1406 | Util.FireAndForget(DoSendWindData, windSpeeds, "LLClientView.SendWindData"); |
1260 | } | 1407 | } |
1261 | 1408 | ||
1262 | /// <summary> | 1409 | /// <summary> |
@@ -1265,7 +1412,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1265 | /// <param name="windSpeeds">16x16 array of cloud densities</param> | 1412 | /// <param name="windSpeeds">16x16 array of cloud densities</param> |
1266 | public virtual void SendCloudData(float[] cloudDensity) | 1413 | public virtual void SendCloudData(float[] cloudDensity) |
1267 | { | 1414 | { |
1268 | Util.FireAndForget(DoSendCloudData, cloudDensity); | 1415 | Util.FireAndForget(DoSendCloudData, cloudDensity, "LLClientView.SendCloudData"); |
1269 | } | 1416 | } |
1270 | 1417 | ||
1271 | /// <summary> | 1418 | /// <summary> |
@@ -1276,21 +1423,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1276 | { | 1423 | { |
1277 | Vector2[] windSpeeds = (Vector2[])o; | 1424 | Vector2[] windSpeeds = (Vector2[])o; |
1278 | TerrainPatch[] patches = new TerrainPatch[2]; | 1425 | TerrainPatch[] patches = new TerrainPatch[2]; |
1279 | patches[0] = new TerrainPatch(); | 1426 | patches[0] = new TerrainPatch { Data = new float[16 * 16] }; |
1280 | patches[0].Data = new float[16 * 16]; | 1427 | patches[1] = new TerrainPatch { Data = new float[16 * 16] }; |
1281 | patches[1] = new TerrainPatch(); | ||
1282 | patches[1].Data = new float[16 * 16]; | ||
1283 | 1428 | ||
1284 | for (int y = 0; y < 16; y++) | 1429 | for (int x = 0; x < 16 * 16; x++) |
1285 | { | 1430 | { |
1286 | for (int x = 0; x < 16; x++) | 1431 | patches[0].Data[x] = windSpeeds[x].X; |
1287 | { | 1432 | patches[1].Data[x] = windSpeeds[x].Y; |
1288 | patches[0].Data[y * 16 + x] = windSpeeds[y * 16 + x].X; | ||
1289 | patches[1].Data[y * 16 + x] = windSpeeds[y * 16 + x].Y; | ||
1290 | } | ||
1291 | } | 1433 | } |
1292 | 1434 | ||
1293 | LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, TerrainPatch.LayerType.Wind); | 1435 | byte layerType = (byte)TerrainPatch.LayerType.Wind; |
1436 | if (m_scene.RegionInfo.RegionSizeX > Constants.RegionSize || m_scene.RegionInfo.RegionSizeY > Constants.RegionSize) | ||
1437 | layerType = (byte)TerrainPatch.LayerType.WindExtended; | ||
1438 | |||
1439 | // LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, (TerrainPatch.LayerType)layerType); | ||
1440 | LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLayerDataPacket(patches, layerType, | ||
1441 | (int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY); | ||
1294 | layerpack.Header.Zerocoded = true; | 1442 | layerpack.Header.Zerocoded = true; |
1295 | OutPacket(layerpack, ThrottleOutPacketType.Wind); | 1443 | OutPacket(layerpack, ThrottleOutPacketType.Wind); |
1296 | } | 1444 | } |
@@ -1314,7 +1462,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1314 | } | 1462 | } |
1315 | } | 1463 | } |
1316 | 1464 | ||
1317 | LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, TerrainPatch.LayerType.Cloud); | 1465 | byte layerType = (byte)TerrainPatch.LayerType.Cloud; |
1466 | if (m_scene.RegionInfo.RegionSizeX > Constants.RegionSize || m_scene.RegionInfo.RegionSizeY > Constants.RegionSize) | ||
1467 | layerType = (byte)TerrainPatch.LayerType.CloudExtended; | ||
1468 | |||
1469 | // LayerDataPacket layerpack = TerrainCompressor.CreateLayerDataPacket(patches, (TerrainPatch.LayerType)layerType); | ||
1470 | LayerDataPacket layerpack = OpenSimTerrainCompressor.CreateLayerDataPacket(patches, layerType, | ||
1471 | (int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY); | ||
1318 | layerpack.Header.Zerocoded = true; | 1472 | layerpack.Header.Zerocoded = true; |
1319 | OutPacket(layerpack, ThrottleOutPacketType.Cloud); | 1473 | OutPacket(layerpack, ThrottleOutPacketType.Cloud); |
1320 | } | 1474 | } |
@@ -1403,6 +1557,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1403 | 1557 | ||
1404 | mapReply.AgentData.AgentID = AgentId; | 1558 | mapReply.AgentData.AgentID = AgentId; |
1405 | mapReply.Data = new MapBlockReplyPacket.DataBlock[mapBlocks2.Length]; | 1559 | mapReply.Data = new MapBlockReplyPacket.DataBlock[mapBlocks2.Length]; |
1560 | mapReply.Size = new MapBlockReplyPacket.SizeBlock[mapBlocks2.Length]; | ||
1406 | mapReply.AgentData.Flags = flag; | 1561 | mapReply.AgentData.Flags = flag; |
1407 | 1562 | ||
1408 | for (int i = 0; i < mapBlocks2.Length; i++) | 1563 | for (int i = 0; i < mapBlocks2.Length; i++) |
@@ -1417,6 +1572,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1417 | mapReply.Data[i].RegionFlags = mapBlocks2[i].RegionFlags; | 1572 | mapReply.Data[i].RegionFlags = mapBlocks2[i].RegionFlags; |
1418 | mapReply.Data[i].Access = mapBlocks2[i].Access; | 1573 | mapReply.Data[i].Access = mapBlocks2[i].Access; |
1419 | mapReply.Data[i].Agents = mapBlocks2[i].Agents; | 1574 | mapReply.Data[i].Agents = mapBlocks2[i].Agents; |
1575 | |||
1576 | mapReply.Size[i] = new MapBlockReplyPacket.SizeBlock(); | ||
1577 | mapReply.Size[i].SizeX = mapBlocks2[i].SizeX; | ||
1578 | mapReply.Size[i].SizeY = mapBlocks2[i].SizeY; | ||
1420 | } | 1579 | } |
1421 | OutPacket(mapReply, ThrottleOutPacketType.Land); | 1580 | OutPacket(mapReply, ThrottleOutPacketType.Land); |
1422 | } | 1581 | } |
@@ -1521,7 +1680,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1521 | OutPacket(tpProgress, ThrottleOutPacketType.Unknown); | 1680 | OutPacket(tpProgress, ThrottleOutPacketType.Unknown); |
1522 | } | 1681 | } |
1523 | 1682 | ||
1524 | public void SendMoneyBalance(UUID transaction, bool success, byte[] description, int balance) | 1683 | public void SendMoneyBalance(UUID transaction, bool success, byte[] description, int balance, int transactionType, UUID sourceID, bool sourceIsGroup, UUID destID, bool destIsGroup, int amount, string item) |
1525 | { | 1684 | { |
1526 | MoneyBalanceReplyPacket money = (MoneyBalanceReplyPacket)PacketPool.Instance.GetPacket(PacketType.MoneyBalanceReply); | 1685 | MoneyBalanceReplyPacket money = (MoneyBalanceReplyPacket)PacketPool.Instance.GetPacket(PacketType.MoneyBalanceReply); |
1527 | money.MoneyData.AgentID = AgentId; | 1686 | money.MoneyData.AgentID = AgentId; |
@@ -1529,7 +1688,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1529 | money.MoneyData.TransactionSuccess = success; | 1688 | money.MoneyData.TransactionSuccess = success; |
1530 | money.MoneyData.Description = description; | 1689 | money.MoneyData.Description = description; |
1531 | money.MoneyData.MoneyBalance = balance; | 1690 | money.MoneyData.MoneyBalance = balance; |
1532 | money.TransactionInfo.ItemDescription = Util.StringToBytes256("NONE"); | 1691 | money.TransactionInfo.TransactionType = transactionType; |
1692 | money.TransactionInfo.SourceID = sourceID; | ||
1693 | money.TransactionInfo.IsSourceGroup = sourceIsGroup; | ||
1694 | money.TransactionInfo.DestID = destID; | ||
1695 | money.TransactionInfo.IsDestGroup = destIsGroup; | ||
1696 | money.TransactionInfo.Amount = amount; | ||
1697 | money.TransactionInfo.ItemDescription = Util.StringToBytes256(item); | ||
1698 | |||
1533 | OutPacket(money, ThrottleOutPacketType.Task); | 1699 | OutPacket(money, ThrottleOutPacketType.Task); |
1534 | } | 1700 | } |
1535 | 1701 | ||
@@ -1571,7 +1737,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1571 | OutPacket(pc, ThrottleOutPacketType.Unknown); | 1737 | OutPacket(pc, ThrottleOutPacketType.Unknown); |
1572 | } | 1738 | } |
1573 | 1739 | ||
1574 | public void SendKillObject(ulong regionHandle, List<uint> localIDs) | 1740 | public void SendKillObject(List<uint> localIDs) |
1575 | { | 1741 | { |
1576 | // m_log.DebugFormat("[CLIENT]: Sending KillObjectPacket to {0} for {1} in {2}", Name, localID, regionHandle); | 1742 | // m_log.DebugFormat("[CLIENT]: Sending KillObjectPacket to {0} for {1} in {2}", Name, localID, regionHandle); |
1577 | 1743 | ||
@@ -1588,7 +1754,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1588 | 1754 | ||
1589 | if (localIDs.Count == 1 && m_scene.GetScenePresence(localIDs[0]) != null) | 1755 | if (localIDs.Count == 1 && m_scene.GetScenePresence(localIDs[0]) != null) |
1590 | { | 1756 | { |
1591 | OutPacket(kill, ThrottleOutPacketType.State); | 1757 | OutPacket(kill, ThrottleOutPacketType.Task); |
1592 | } | 1758 | } |
1593 | else | 1759 | else |
1594 | { | 1760 | { |
@@ -1700,6 +1866,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1700 | newBlock.Name = Util.StringToBytes256(folder.Name); | 1866 | newBlock.Name = Util.StringToBytes256(folder.Name); |
1701 | newBlock.ParentID = folder.ParentID; | 1867 | newBlock.ParentID = folder.ParentID; |
1702 | newBlock.Type = (sbyte)folder.Type; | 1868 | newBlock.Type = (sbyte)folder.Type; |
1869 | //if (newBlock.Type == InventoryItemBase.SUITCASE_FOLDER_TYPE) | ||
1870 | // newBlock.Type = InventoryItemBase.SUITCASE_FOLDER_FAKE_TYPE; | ||
1703 | 1871 | ||
1704 | return newBlock; | 1872 | return newBlock; |
1705 | } | 1873 | } |
@@ -1807,7 +1975,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1807 | 1975 | ||
1808 | public void SendInventoryItemDetails(UUID ownerID, InventoryItemBase item) | 1976 | public void SendInventoryItemDetails(UUID ownerID, InventoryItemBase item) |
1809 | { | 1977 | { |
1810 | const uint FULL_MASK_PERMISSIONS = (uint)PermissionMask.All; | 1978 | // Fudge this value. It's only needed to make the CRC anyway |
1979 | const uint FULL_MASK_PERMISSIONS = (uint)0x7fffffff; | ||
1811 | 1980 | ||
1812 | FetchInventoryReplyPacket inventoryReply = (FetchInventoryReplyPacket)PacketPool.Instance.GetPacket(PacketType.FetchInventoryReply); | 1981 | FetchInventoryReplyPacket inventoryReply = (FetchInventoryReplyPacket)PacketPool.Instance.GetPacket(PacketType.FetchInventoryReply); |
1813 | // TODO: don't create new blocks if recycling an old packet | 1982 | // TODO: don't create new blocks if recycling an old packet |
@@ -1948,8 +2117,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1948 | 2117 | ||
1949 | folderBlock.FolderID = folder.ID; | 2118 | folderBlock.FolderID = folder.ID; |
1950 | folderBlock.ParentID = folder.ParentID; | 2119 | folderBlock.ParentID = folder.ParentID; |
1951 | //folderBlock.Type = -1; | ||
1952 | folderBlock.Type = (sbyte)folder.Type; | 2120 | folderBlock.Type = (sbyte)folder.Type; |
2121 | // Leaving this here for now, just in case we need to do this for a while | ||
2122 | //if (folderBlock.Type == InventoryItemBase.SUITCASE_FOLDER_TYPE) | ||
2123 | // folderBlock.Type = InventoryItemBase.SUITCASE_FOLDER_FAKE_TYPE; | ||
1953 | folderBlock.Name = Util.StringToBytes256(folder.Name); | 2124 | folderBlock.Name = Util.StringToBytes256(folder.Name); |
1954 | 2125 | ||
1955 | return folderBlock; | 2126 | return folderBlock; |
@@ -2012,7 +2183,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2012 | 2183 | ||
2013 | protected void SendBulkUpdateInventoryItem(InventoryItemBase item) | 2184 | protected void SendBulkUpdateInventoryItem(InventoryItemBase item) |
2014 | { | 2185 | { |
2015 | const uint FULL_MASK_PERMISSIONS = (uint)PermissionMask.All; | 2186 | const uint FULL_MASK_PERMISSIONS = (uint)0x7ffffff; |
2016 | 2187 | ||
2017 | BulkUpdateInventoryPacket bulkUpdate | 2188 | BulkUpdateInventoryPacket bulkUpdate |
2018 | = (BulkUpdateInventoryPacket)PacketPool.Instance.GetPacket(PacketType.BulkUpdateInventory); | 2189 | = (BulkUpdateInventoryPacket)PacketPool.Instance.GetPacket(PacketType.BulkUpdateInventory); |
@@ -2066,7 +2237,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2066 | /// <see>IClientAPI.SendInventoryItemCreateUpdate(InventoryItemBase)</see> | 2237 | /// <see>IClientAPI.SendInventoryItemCreateUpdate(InventoryItemBase)</see> |
2067 | public void SendInventoryItemCreateUpdate(InventoryItemBase Item, uint callbackId) | 2238 | public void SendInventoryItemCreateUpdate(InventoryItemBase Item, uint callbackId) |
2068 | { | 2239 | { |
2069 | const uint FULL_MASK_PERMISSIONS = (uint)PermissionMask.All; | 2240 | const uint FULL_MASK_PERMISSIONS = (uint)0x7fffffff; |
2070 | 2241 | ||
2071 | UpdateCreateInventoryItemPacket InventoryReply | 2242 | UpdateCreateInventoryItemPacket InventoryReply |
2072 | = (UpdateCreateInventoryItemPacket)PacketPool.Instance.GetPacket( | 2243 | = (UpdateCreateInventoryItemPacket)PacketPool.Instance.GetPacket( |
@@ -2212,9 +2383,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2212 | 2383 | ||
2213 | public void SendAgentDataUpdate(UUID agentid, UUID activegroupid, string firstname, string lastname, ulong grouppowers, string groupname, string grouptitle) | 2384 | public void SendAgentDataUpdate(UUID agentid, UUID activegroupid, string firstname, string lastname, ulong grouppowers, string groupname, string grouptitle) |
2214 | { | 2385 | { |
2215 | m_activeGroupID = activegroupid; | 2386 | if (agentid == AgentId) |
2216 | m_activeGroupName = groupname; | 2387 | { |
2217 | m_activeGroupPowers = grouppowers; | 2388 | ActiveGroupId = activegroupid; |
2389 | ActiveGroupName = groupname; | ||
2390 | ActiveGroupPowers = grouppowers; | ||
2391 | } | ||
2218 | 2392 | ||
2219 | AgentDataUpdatePacket sendAgentDataUpdate = (AgentDataUpdatePacket)PacketPool.Instance.GetPacket(PacketType.AgentDataUpdate); | 2393 | AgentDataUpdatePacket sendAgentDataUpdate = (AgentDataUpdatePacket)PacketPool.Instance.GetPacket(PacketType.AgentDataUpdate); |
2220 | sendAgentDataUpdate.AgentData.ActiveGroupID = activegroupid; | 2394 | sendAgentDataUpdate.AgentData.ActiveGroupID = activegroupid; |
@@ -2261,6 +2435,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2261 | /// <returns></returns> | 2435 | /// <returns></returns> |
2262 | public AgentAlertMessagePacket BuildAgentAlertPacket(string message, bool modal) | 2436 | public AgentAlertMessagePacket BuildAgentAlertPacket(string message, bool modal) |
2263 | { | 2437 | { |
2438 | // Prepend a slash to make the message come up in the top right | ||
2439 | // again. | ||
2440 | // Allow special formats to be sent from aware modules. | ||
2441 | if (!modal && !message.StartsWith("ALERT: ") && !message.StartsWith("NOTIFY: ") && message != "Home position set." && message != "You died and have been teleported to your home location") | ||
2442 | message = "/" + message; | ||
2264 | AgentAlertMessagePacket alertPack = (AgentAlertMessagePacket)PacketPool.Instance.GetPacket(PacketType.AgentAlertMessage); | 2443 | AgentAlertMessagePacket alertPack = (AgentAlertMessagePacket)PacketPool.Instance.GetPacket(PacketType.AgentAlertMessage); |
2265 | alertPack.AgentData.AgentID = AgentId; | 2444 | alertPack.AgentData.AgentID = AgentId; |
2266 | alertPack.AlertData.Message = Util.StringToBytes256(message); | 2445 | alertPack.AlertData.Message = Util.StringToBytes256(message); |
@@ -2556,11 +2735,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2556 | { | 2735 | { |
2557 | AvatarSitResponsePacket avatarSitResponse = new AvatarSitResponsePacket(); | 2736 | AvatarSitResponsePacket avatarSitResponse = new AvatarSitResponsePacket(); |
2558 | avatarSitResponse.SitObject.ID = TargetID; | 2737 | avatarSitResponse.SitObject.ID = TargetID; |
2559 | if (CameraAtOffset != Vector3.Zero) | 2738 | avatarSitResponse.SitTransform.CameraAtOffset = CameraAtOffset; |
2560 | { | 2739 | avatarSitResponse.SitTransform.CameraEyeOffset = CameraEyeOffset; |
2561 | avatarSitResponse.SitTransform.CameraAtOffset = CameraAtOffset; | ||
2562 | avatarSitResponse.SitTransform.CameraEyeOffset = CameraEyeOffset; | ||
2563 | } | ||
2564 | avatarSitResponse.SitTransform.ForceMouselook = ForceMouseLook; | 2740 | avatarSitResponse.SitTransform.ForceMouselook = ForceMouseLook; |
2565 | avatarSitResponse.SitTransform.AutoPilot = autopilot; | 2741 | avatarSitResponse.SitTransform.AutoPilot = autopilot; |
2566 | avatarSitResponse.SitTransform.SitPosition = OffsetPos; | 2742 | avatarSitResponse.SitTransform.SitPosition = OffsetPos; |
@@ -2627,6 +2803,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2627 | } | 2803 | } |
2628 | } | 2804 | } |
2629 | 2805 | ||
2806 | public void SendPartPhysicsProprieties(ISceneEntity entity) | ||
2807 | { | ||
2808 | SceneObjectPart part = (SceneObjectPart)entity; | ||
2809 | if (part != null && AgentId != UUID.Zero) | ||
2810 | { | ||
2811 | try | ||
2812 | { | ||
2813 | IEventQueue eq = Scene.RequestModuleInterface<IEventQueue>(); | ||
2814 | if (eq != null) | ||
2815 | { | ||
2816 | uint localid = part.LocalId; | ||
2817 | byte physshapetype = part.PhysicsShapeType; | ||
2818 | float density = part.Density; | ||
2819 | float friction = part.Friction; | ||
2820 | float bounce = part.Restitution; | ||
2821 | float gravmod = part.GravityModifier; | ||
2822 | eq.partPhysicsProperties(localid, physshapetype, density, friction, bounce, gravmod,AgentId); | ||
2823 | } | ||
2824 | } | ||
2825 | catch (Exception ex) | ||
2826 | { | ||
2827 | m_log.Error("Unable to send part Physics Proprieties - exception: " + ex.ToString()); | ||
2828 | } | ||
2829 | part.UpdatePhysRequired = false; | ||
2830 | } | ||
2831 | } | ||
2832 | |||
2833 | |||
2630 | 2834 | ||
2631 | public void SendGroupNameReply(UUID groupLLUID, string GroupName) | 2835 | public void SendGroupNameReply(UUID groupLLUID, string GroupName) |
2632 | { | 2836 | { |
@@ -2681,8 +2885,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2681 | { | 2885 | { |
2682 | if (req.AssetInf.Data == null) | 2886 | if (req.AssetInf.Data == null) |
2683 | { | 2887 | { |
2684 | m_log.ErrorFormat("Cannot send asset {0} ({1}), asset data is null", | 2888 | m_log.ErrorFormat("{0} Cannot send asset {1} ({2}), asset data is null", |
2685 | req.AssetInf.ID, req.AssetInf.Metadata.ContentType); | 2889 | LogHeader, req.AssetInf.ID, req.AssetInf.Metadata.ContentType); |
2686 | return; | 2890 | return; |
2687 | } | 2891 | } |
2688 | 2892 | ||
@@ -3530,7 +3734,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3530 | 3734 | ||
3531 | AvatarAppearancePacket avp = (AvatarAppearancePacket)PacketPool.Instance.GetPacket(PacketType.AvatarAppearance); | 3735 | AvatarAppearancePacket avp = (AvatarAppearancePacket)PacketPool.Instance.GetPacket(PacketType.AvatarAppearance); |
3532 | // TODO: don't create new blocks if recycling an old packet | 3736 | // TODO: don't create new blocks if recycling an old packet |
3533 | avp.VisualParam = new AvatarAppearancePacket.VisualParamBlock[218]; | 3737 | avp.VisualParam = new AvatarAppearancePacket.VisualParamBlock[visualParams.Length]; |
3534 | avp.ObjectData.TextureEntry = textureEntry; | 3738 | avp.ObjectData.TextureEntry = textureEntry; |
3535 | 3739 | ||
3536 | AvatarAppearancePacket.VisualParamBlock avblock = null; | 3740 | AvatarAppearancePacket.VisualParamBlock avblock = null; |
@@ -3543,6 +3747,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3543 | 3747 | ||
3544 | avp.Sender.IsTrial = false; | 3748 | avp.Sender.IsTrial = false; |
3545 | avp.Sender.ID = agentID; | 3749 | avp.Sender.ID = agentID; |
3750 | avp.AppearanceData = new AvatarAppearancePacket.AppearanceDataBlock[0]; | ||
3751 | avp.AppearanceHover = new AvatarAppearancePacket.AppearanceHoverBlock[0]; | ||
3546 | //m_log.DebugFormat("[CLIENT]: Sending appearance for {0} to {1}", agentID.ToString(), AgentId.ToString()); | 3752 | //m_log.DebugFormat("[CLIENT]: Sending appearance for {0} to {1}", agentID.ToString(), AgentId.ToString()); |
3547 | OutPacket(avp, ThrottleOutPacketType.Task); | 3753 | OutPacket(avp, ThrottleOutPacketType.Task); |
3548 | } | 3754 | } |
@@ -3660,11 +3866,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3660 | /// </summary> | 3866 | /// </summary> |
3661 | public void SendEntityUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) | 3867 | public void SendEntityUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) |
3662 | { | 3868 | { |
3663 | //double priority = m_prioritizer.GetUpdatePriority(this, entity); | 3869 | if (entity.UUID == m_agentId && !updateFlags.HasFlag(PrimUpdateFlags.FullUpdate)) |
3664 | uint priority = m_prioritizer.GetUpdatePriority(this, entity); | 3870 | { |
3871 | ImprovedTerseObjectUpdatePacket packet | ||
3872 | = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); | ||
3665 | 3873 | ||
3666 | lock (m_entityUpdates.SyncRoot) | 3874 | packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; |
3667 | m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); | 3875 | packet.RegionData.TimeDilation = Utils.FloatToUInt16(1, 0.0f, 1.0f); |
3876 | packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1]; | ||
3877 | packet.ObjectData[0] = CreateImprovedTerseBlock(entity, false); | ||
3878 | OutPacket(packet, ThrottleOutPacketType.Unknown, true); | ||
3879 | } | ||
3880 | else | ||
3881 | { | ||
3882 | //double priority = m_prioritizer.GetUpdatePriority(this, entity); | ||
3883 | uint priority = m_prioritizer.GetUpdatePriority(this, entity); | ||
3884 | |||
3885 | lock (m_entityUpdates.SyncRoot) | ||
3886 | m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); | ||
3887 | } | ||
3668 | } | 3888 | } |
3669 | 3889 | ||
3670 | /// <summary> | 3890 | /// <summary> |
@@ -3699,12 +3919,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3699 | m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber); | 3919 | m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber); |
3700 | 3920 | ||
3701 | // Count this as a resent packet since we are going to requeue all of the updates contained in it | 3921 | // Count this as a resent packet since we are going to requeue all of the updates contained in it |
3702 | Interlocked.Increment(ref m_udpClient.PacketsResent); | 3922 | Interlocked.Increment(ref m_udpClient.PacketsResent); |
3923 | |||
3924 | // We're not going to worry about interlock yet since its not currently critical that this total count | ||
3925 | // is 100% correct | ||
3926 | m_udpServer.PacketsResentCount++; | ||
3703 | 3927 | ||
3704 | foreach (EntityUpdate update in updates) | 3928 | foreach (EntityUpdate update in updates) |
3705 | ResendPrimUpdate(update); | 3929 | ResendPrimUpdate(update); |
3706 | } | 3930 | } |
3707 | 3931 | ||
3932 | // OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); | ||
3933 | // OpenSim.Framework.Lazy<List<ObjectUpdateCompressedPacket.ObjectDataBlock>> compressedUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdateCompressedPacket.ObjectDataBlock>>(); | ||
3934 | // OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); | ||
3935 | // OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); | ||
3936 | // | ||
3937 | // OpenSim.Framework.Lazy<List<EntityUpdate>> objectUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>(); | ||
3938 | // OpenSim.Framework.Lazy<List<EntityUpdate>> compressedUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>(); | ||
3939 | // OpenSim.Framework.Lazy<List<EntityUpdate>> terseUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>(); | ||
3940 | // OpenSim.Framework.Lazy<List<EntityUpdate>> terseAgentUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>(); | ||
3941 | |||
3942 | |||
3708 | private void ProcessEntityUpdates(int maxUpdates) | 3943 | private void ProcessEntityUpdates(int maxUpdates) |
3709 | { | 3944 | { |
3710 | OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); | 3945 | OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); |
@@ -3717,6 +3952,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3717 | OpenSim.Framework.Lazy<List<EntityUpdate>> terseUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>(); | 3952 | OpenSim.Framework.Lazy<List<EntityUpdate>> terseUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>(); |
3718 | OpenSim.Framework.Lazy<List<EntityUpdate>> terseAgentUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>(); | 3953 | OpenSim.Framework.Lazy<List<EntityUpdate>> terseAgentUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>(); |
3719 | 3954 | ||
3955 | // objectUpdateBlocks.Value.Clear(); | ||
3956 | // compressedUpdateBlocks.Value.Clear(); | ||
3957 | // terseUpdateBlocks.Value.Clear(); | ||
3958 | // terseAgentUpdateBlocks.Value.Clear(); | ||
3959 | // objectUpdates.Value.Clear(); | ||
3960 | // compressedUpdates.Value.Clear(); | ||
3961 | // terseUpdates.Value.Clear(); | ||
3962 | // terseAgentUpdates.Value.Clear(); | ||
3963 | |||
3720 | // Check to see if this is a flush | 3964 | // Check to see if this is a flush |
3721 | if (maxUpdates <= 0) | 3965 | if (maxUpdates <= 0) |
3722 | { | 3966 | { |
@@ -3774,6 +4018,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3774 | part.Shape.LightEntry = false; | 4018 | part.Shape.LightEntry = false; |
3775 | } | 4019 | } |
3776 | } | 4020 | } |
4021 | |||
4022 | if (part.Shape != null && (part.Shape.SculptType == (byte)SculptType.Mesh)) | ||
4023 | { | ||
4024 | // Ensure that mesh has at least 8 valid faces | ||
4025 | part.Shape.ProfileBegin = 12500; | ||
4026 | part.Shape.ProfileEnd = 0; | ||
4027 | part.Shape.ProfileHollow = 27500; | ||
4028 | } | ||
3777 | } | 4029 | } |
3778 | 4030 | ||
3779 | #region UpdateFlags to packet type conversion | 4031 | #region UpdateFlags to packet type conversion |
@@ -3995,6 +4247,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3995 | } | 4247 | } |
3996 | } | 4248 | } |
3997 | 4249 | ||
4250 | // m_log.DebugFormat( | ||
4251 | // "[LLCLIENTVIEW]: Sent {0} updates in ProcessEntityUpdates() for {1} {2} in {3}", | ||
4252 | // updatesThisCall, Name, SceneAgent.IsChildAgent ? "child" : "root", Scene.Name); | ||
4253 | // | ||
3998 | #endregion Packet Sending | 4254 | #endregion Packet Sending |
3999 | } | 4255 | } |
4000 | 4256 | ||
@@ -4034,8 +4290,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4034 | 4290 | ||
4035 | void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) | 4291 | void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) |
4036 | { | 4292 | { |
4293 | // if (!m_udpServer.IsRunningOutbound) | ||
4294 | // return; | ||
4295 | |||
4037 | if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) | 4296 | if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) |
4038 | { | 4297 | { |
4298 | // if (!m_udpServer.IsRunningOutbound) | ||
4299 | // return; | ||
4300 | |||
4039 | if (m_maxUpdates == 0 || m_LastQueueFill == 0) | 4301 | if (m_maxUpdates == 0 || m_LastQueueFill == 0) |
4040 | { | 4302 | { |
4041 | m_maxUpdates = m_udpServer.PrimUpdatesPerCallback; | 4303 | m_maxUpdates = m_udpServer.PrimUpdatesPerCallback; |
@@ -4061,6 +4323,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4061 | ImageManager.ProcessImageQueue(m_udpServer.TextureSendLimit); | 4323 | ImageManager.ProcessImageQueue(m_udpServer.TextureSendLimit); |
4062 | } | 4324 | } |
4063 | 4325 | ||
4326 | internal bool HandleHasUpdates(ThrottleOutPacketTypeFlags categories) | ||
4327 | { | ||
4328 | bool hasUpdates = false; | ||
4329 | |||
4330 | if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) | ||
4331 | { | ||
4332 | if (m_entityUpdates.Count > 0) | ||
4333 | hasUpdates = true; | ||
4334 | else if (m_entityProps.Count > 0) | ||
4335 | hasUpdates = true; | ||
4336 | } | ||
4337 | |||
4338 | if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) | ||
4339 | { | ||
4340 | if (ImageManager.HasUpdates()) | ||
4341 | hasUpdates = true; | ||
4342 | } | ||
4343 | |||
4344 | return hasUpdates; | ||
4345 | } | ||
4346 | |||
4064 | public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID) | 4347 | public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID) |
4065 | { | 4348 | { |
4066 | AssetUploadCompletePacket newPack = new AssetUploadCompletePacket(); | 4349 | AssetUploadCompletePacket newPack = new AssetUploadCompletePacket(); |
@@ -4156,7 +4439,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4156 | pack.Stat = stats.StatsBlock; | 4439 | pack.Stat = stats.StatsBlock; |
4157 | 4440 | ||
4158 | pack.Header.Reliable = false; | 4441 | pack.Header.Reliable = false; |
4159 | 4442 | pack.RegionInfo = new SimStatsPacket.RegionInfoBlock[0]; | |
4160 | OutPacket(pack, ThrottleOutPacketType.Task); | 4443 | OutPacket(pack, ThrottleOutPacketType.Task); |
4161 | } | 4444 | } |
4162 | 4445 | ||
@@ -4206,6 +4489,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4206 | // Count this as a resent packet since we are going to requeue all of the updates contained in it | 4489 | // Count this as a resent packet since we are going to requeue all of the updates contained in it |
4207 | Interlocked.Increment(ref m_udpClient.PacketsResent); | 4490 | Interlocked.Increment(ref m_udpClient.PacketsResent); |
4208 | 4491 | ||
4492 | // We're not going to worry about interlock yet since its not currently critical that this total count | ||
4493 | // is 100% correct | ||
4494 | m_udpServer.PacketsResentCount++; | ||
4495 | |||
4209 | foreach (ObjectPropertyUpdate update in updates) | 4496 | foreach (ObjectPropertyUpdate update in updates) |
4210 | ResendPropertyUpdate(update); | 4497 | ResendPropertyUpdate(update); |
4211 | } | 4498 | } |
@@ -4389,6 +4676,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4389 | SceneObjectPart root = sop.ParentGroup.RootPart; | 4676 | SceneObjectPart root = sop.ParentGroup.RootPart; |
4390 | 4677 | ||
4391 | block.TouchName = Util.StringToBytes256(root.TouchName); | 4678 | block.TouchName = Util.StringToBytes256(root.TouchName); |
4679 | |||
4680 | // SL 3.3.4, at least, appears to read this information as a concatenated byte[] stream of UUIDs but | ||
4681 | // it's not yet clear whether this is actually used. If this is done in the future then a pre-cached | ||
4682 | // copy is really needed since it's less efficient to be constantly recreating this byte array. | ||
4683 | // using (MemoryStream memStream = new MemoryStream()) | ||
4684 | // { | ||
4685 | // using (BinaryWriter binWriter = new BinaryWriter(memStream)) | ||
4686 | // { | ||
4687 | // for (int i = 0; i < sop.GetNumberOfSides(); i++) | ||
4688 | // { | ||
4689 | // Primitive.TextureEntryFace teFace = sop.Shape.Textures.FaceTextures[i]; | ||
4690 | // | ||
4691 | // UUID textureID; | ||
4692 | // | ||
4693 | // if (teFace != null) | ||
4694 | // textureID = teFace.TextureID; | ||
4695 | // else | ||
4696 | // textureID = sop.Shape.Textures.DefaultTexture.TextureID; | ||
4697 | // | ||
4698 | // binWriter.Write(textureID.GetBytes()); | ||
4699 | // } | ||
4700 | // | ||
4701 | // block.TextureID = memStream.ToArray(); | ||
4702 | // } | ||
4703 | // } | ||
4704 | |||
4392 | block.TextureID = new byte[0]; // TextureID ??? | 4705 | block.TextureID = new byte[0]; // TextureID ??? |
4393 | block.SitName = Util.StringToBytes256(root.SitName); | 4706 | block.SitName = Util.StringToBytes256(root.SitName); |
4394 | block.OwnerMask = root.OwnerMask; | 4707 | block.OwnerMask = root.OwnerMask; |
@@ -4543,7 +4856,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4543 | rinfopack.AgentData = new RegionInfoPacket.AgentDataBlock(); | 4856 | rinfopack.AgentData = new RegionInfoPacket.AgentDataBlock(); |
4544 | rinfopack.AgentData.AgentID = AgentId; | 4857 | rinfopack.AgentData.AgentID = AgentId; |
4545 | rinfopack.AgentData.SessionID = SessionId; | 4858 | rinfopack.AgentData.SessionID = SessionId; |
4546 | 4859 | rinfopack.RegionInfo3 = new RegionInfoPacket.RegionInfo3Block[0]; | |
4547 | 4860 | ||
4548 | OutPacket(rinfopack, ThrottleOutPacketType.Task); | 4861 | OutPacket(rinfopack, ThrottleOutPacketType.Task); |
4549 | } | 4862 | } |
@@ -4764,7 +5077,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4764 | 5077 | ||
4765 | public void SendForceClientSelectObjects(List<uint> ObjectIDs) | 5078 | public void SendForceClientSelectObjects(List<uint> ObjectIDs) |
4766 | { | 5079 | { |
4767 | m_log.WarnFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count); | 5080 | // m_log.DebugFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count); |
4768 | 5081 | ||
4769 | bool firstCall = true; | 5082 | bool firstCall = true; |
4770 | const int MAX_OBJECTS_PER_PACKET = 251; | 5083 | const int MAX_OBJECTS_PER_PACKET = 251; |
@@ -4882,7 +5195,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4882 | { | 5195 | { |
4883 | ScenePresence presence = (ScenePresence)entity; | 5196 | ScenePresence presence = (ScenePresence)entity; |
4884 | 5197 | ||
4885 | attachPoint = 0; | 5198 | // m_log.DebugFormat( |
5199 | // "[LLCLIENTVIEW]: Sending terse update to {0} with pos {1}, vel {2} in {3}", | ||
5200 | // Name, presence.OffsetPosition, presence.Velocity, m_scene.Name); | ||
5201 | |||
5202 | attachPoint = presence.State; | ||
4886 | collisionPlane = presence.CollisionPlane; | 5203 | collisionPlane = presence.CollisionPlane; |
4887 | position = presence.OffsetPosition; | 5204 | position = presence.OffsetPosition; |
4888 | velocity = presence.Velocity; | 5205 | velocity = presence.Velocity; |
@@ -4893,9 +5210,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4893 | // may improve movement smoothness. | 5210 | // may improve movement smoothness. |
4894 | // acceleration = new Vector3(1, 0, 0); | 5211 | // acceleration = new Vector3(1, 0, 0); |
4895 | 5212 | ||
4896 | angularVelocity = Vector3.Zero; | 5213 | angularVelocity = presence.AngularVelocity; |
5214 | |||
5215 | // Whilst not in mouselook, an avatar will transmit only the Z rotation as this is the only axis | ||
5216 | // it rotates around. | ||
5217 | // In mouselook, X and Y co-ordinate will also be sent but when used in Rotation, these cause unwanted | ||
5218 | // excessive up and down movements of the camera when looking up and down. | ||
5219 | // See http://opensimulator.org/mantis/view.php?id=3274 | ||
5220 | // This does not affect head movement, since this is controlled entirely by camera movement rather than | ||
5221 | // body rotation. We still need to transmit X and Y for sitting avatars but mouselook does not change | ||
5222 | // the rotation in this case. | ||
4897 | rotation = presence.Rotation; | 5223 | rotation = presence.Rotation; |
4898 | 5224 | ||
5225 | if (!presence.IsSatOnObject) | ||
5226 | { | ||
5227 | rotation.X = 0; | ||
5228 | rotation.Y = 0; | ||
5229 | } | ||
5230 | |||
4899 | if (sendTexture) | 5231 | if (sendTexture) |
4900 | textureEntry = presence.Appearance.Texture.GetBytes(); | 5232 | textureEntry = presence.Appearance.Texture.GetBytes(); |
4901 | else | 5233 | else |
@@ -4906,7 +5238,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4906 | SceneObjectPart part = (SceneObjectPart)entity; | 5238 | SceneObjectPart part = (SceneObjectPart)entity; |
4907 | 5239 | ||
4908 | attachPoint = part.ParentGroup.AttachmentPoint; | 5240 | attachPoint = part.ParentGroup.AttachmentPoint; |
4909 | 5241 | attachPoint = ((attachPoint % 16) * 16 + (attachPoint / 16)); | |
4910 | // m_log.DebugFormat( | 5242 | // m_log.DebugFormat( |
4911 | // "[LLCLIENTVIEW]: Sending attachPoint {0} for {1} {2} to {3}", | 5243 | // "[LLCLIENTVIEW]: Sending attachPoint {0} for {1} {2} to {3}", |
4912 | // attachPoint, part.Name, part.LocalId, Name); | 5244 | // attachPoint, part.Name, part.LocalId, Name); |
@@ -4934,7 +5266,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4934 | pos += 4; | 5266 | pos += 4; |
4935 | 5267 | ||
4936 | // Avatar/CollisionPlane | 5268 | // Avatar/CollisionPlane |
4937 | data[pos++] = (byte)((attachPoint % 16) * 16 + (attachPoint / 16)); ; | 5269 | data[pos++] = (byte) attachPoint; |
4938 | if (avatar) | 5270 | if (avatar) |
4939 | { | 5271 | { |
4940 | data[pos++] = 1; | 5272 | data[pos++] = 1; |
@@ -5001,13 +5333,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5001 | 5333 | ||
5002 | protected ObjectUpdatePacket.ObjectDataBlock CreateAvatarUpdateBlock(ScenePresence data) | 5334 | protected ObjectUpdatePacket.ObjectDataBlock CreateAvatarUpdateBlock(ScenePresence data) |
5003 | { | 5335 | { |
5336 | // m_log.DebugFormat( | ||
5337 | // "[LLCLIENTVIEW]: Sending full update to {0} with pos {1}, vel {2} in {3}", Name, data.OffsetPosition, data.Velocity, m_scene.Name); | ||
5338 | |||
5004 | byte[] objectData = new byte[76]; | 5339 | byte[] objectData = new byte[76]; |
5005 | 5340 | ||
5006 | data.CollisionPlane.ToBytes(objectData, 0); | 5341 | data.CollisionPlane.ToBytes(objectData, 0); |
5007 | data.OffsetPosition.ToBytes(objectData, 16); | 5342 | data.OffsetPosition.ToBytes(objectData, 16); |
5008 | // data.Velocity.ToBytes(objectData, 28); | 5343 | data.Velocity.ToBytes(objectData, 28); |
5009 | // data.Acceleration.ToBytes(objectData, 40); | 5344 | // data.Acceleration.ToBytes(objectData, 40); |
5010 | data.Rotation.ToBytes(objectData, 52); | 5345 | |
5346 | // Whilst not in mouselook, an avatar will transmit only the Z rotation as this is the only axis | ||
5347 | // it rotates around. | ||
5348 | // In mouselook, X and Y co-ordinate will also be sent but when used in Rotation, these cause unwanted | ||
5349 | // excessive up and down movements of the camera when looking up and down. | ||
5350 | // See http://opensimulator.org/mantis/view.php?id=3274 | ||
5351 | // This does not affect head movement, since this is controlled entirely by camera movement rather than | ||
5352 | // body rotation. We still need to transmit X and Y for sitting avatars but mouselook does not change | ||
5353 | // the rotation in this case. | ||
5354 | Quaternion rot = data.Rotation; | ||
5355 | |||
5356 | if (!data.IsSatOnObject) | ||
5357 | { | ||
5358 | rot.X = 0; | ||
5359 | rot.Y = 0; | ||
5360 | } | ||
5361 | |||
5362 | rot.ToBytes(objectData, 52); | ||
5011 | //data.AngularVelocity.ToBytes(objectData, 64); | 5363 | //data.AngularVelocity.ToBytes(objectData, 64); |
5012 | 5364 | ||
5013 | ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); | 5365 | ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); |
@@ -5021,7 +5373,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5021 | update.NameValue = Utils.StringToBytes("FirstName STRING RW SV " + data.Firstname + "\nLastName STRING RW SV " + | 5373 | update.NameValue = Utils.StringToBytes("FirstName STRING RW SV " + data.Firstname + "\nLastName STRING RW SV " + |
5022 | data.Lastname + "\nTitle STRING RW SV " + data.Grouptitle); | 5374 | data.Lastname + "\nTitle STRING RW SV " + data.Grouptitle); |
5023 | update.ObjectData = objectData; | 5375 | update.ObjectData = objectData; |
5024 | update.ParentID = data.ParentID; | 5376 | |
5377 | SceneObjectPart parentPart = data.ParentPart; | ||
5378 | if (parentPart != null) | ||
5379 | update.ParentID = parentPart.ParentGroup.LocalId; | ||
5380 | else | ||
5381 | update.ParentID = 0; | ||
5382 | |||
5025 | update.PathCurve = 16; | 5383 | update.PathCurve = 16; |
5026 | update.PathScaleX = 100; | 5384 | update.PathScaleX = 100; |
5027 | update.PathScaleY = 100; | 5385 | update.PathScaleY = 100; |
@@ -5075,10 +5433,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5075 | //update.JointType = 0; | 5433 | //update.JointType = 0; |
5076 | update.Material = data.Material; | 5434 | update.Material = data.Material; |
5077 | update.MediaURL = Utils.EmptyBytes; // FIXME: Support this in OpenSim | 5435 | update.MediaURL = Utils.EmptyBytes; // FIXME: Support this in OpenSim |
5436 | |||
5078 | if (data.ParentGroup.IsAttachment) | 5437 | if (data.ParentGroup.IsAttachment) |
5079 | { | 5438 | { |
5080 | update.NameValue = Util.StringToBytes256("AttachItemID STRING RW SV " + data.ParentGroup.FromItemID); | 5439 | update.NameValue |
5440 | = Util.StringToBytes256( | ||
5441 | string.Format("AttachItemID STRING RW SV {0}", data.ParentGroup.FromItemID)); | ||
5442 | |||
5081 | update.State = (byte)((data.ParentGroup.AttachmentPoint % 16) * 16 + (data.ParentGroup.AttachmentPoint / 16)); | 5443 | update.State = (byte)((data.ParentGroup.AttachmentPoint % 16) * 16 + (data.ParentGroup.AttachmentPoint / 16)); |
5444 | |||
5445 | // m_log.DebugFormat( | ||
5446 | // "[LLCLIENTVIEW]: Sending NameValue {0} for {1} {2} to {3}", | ||
5447 | // Util.UTF8.GetString(update.NameValue), data.Name, data.LocalId, Name); | ||
5448 | // | ||
5449 | // m_log.DebugFormat( | ||
5450 | // "[LLCLIENTVIEW]: Sending state {0} for {1} {2} to {3}", | ||
5451 | // update.State, data.Name, data.LocalId, Name); | ||
5082 | } | 5452 | } |
5083 | else | 5453 | else |
5084 | { | 5454 | { |
@@ -5089,10 +5459,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5089 | update.State = data.ParentGroup.RootPart.Shape.State; | 5459 | update.State = data.ParentGroup.RootPart.Shape.State; |
5090 | } | 5460 | } |
5091 | 5461 | ||
5092 | // m_log.DebugFormat( | ||
5093 | // "[LLCLIENTVIEW]: Sending state {0} for {1} {2} to {3}", | ||
5094 | // update.State, data.Name, data.LocalId, Name); | ||
5095 | |||
5096 | update.ObjectData = objectData; | 5462 | update.ObjectData = objectData; |
5097 | update.ParentID = data.ParentID; | 5463 | update.ParentID = data.ParentID; |
5098 | update.PathBegin = data.Shape.PathBegin; | 5464 | update.PathBegin = data.Shape.PathBegin; |
@@ -5192,8 +5558,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5192 | 5558 | ||
5193 | public ulong GetGroupPowers(UUID groupID) | 5559 | public ulong GetGroupPowers(UUID groupID) |
5194 | { | 5560 | { |
5195 | if (groupID == m_activeGroupID) | 5561 | if (groupID == ActiveGroupId) |
5196 | return m_activeGroupPowers; | 5562 | return ActiveGroupPowers; |
5197 | 5563 | ||
5198 | if (m_groupPowers.ContainsKey(groupID)) | 5564 | if (m_groupPowers.ContainsKey(groupID)) |
5199 | return m_groupPowers[groupID]; | 5565 | return m_groupPowers[groupID]; |
@@ -5221,10 +5587,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5221 | AddLocalPacketHandler(PacketType.ParcelBuy, HandleParcelBuyRequest, false); | 5587 | AddLocalPacketHandler(PacketType.ParcelBuy, HandleParcelBuyRequest, false); |
5222 | AddLocalPacketHandler(PacketType.UUIDGroupNameRequest, HandleUUIDGroupNameRequest); | 5588 | AddLocalPacketHandler(PacketType.UUIDGroupNameRequest, HandleUUIDGroupNameRequest); |
5223 | AddLocalPacketHandler(PacketType.ObjectGroup, HandleObjectGroupRequest); | 5589 | AddLocalPacketHandler(PacketType.ObjectGroup, HandleObjectGroupRequest); |
5224 | AddLocalPacketHandler(PacketType.GenericMessage, HandleGenericMessage); | 5590 | AddLocalPacketHandler(PacketType.GenericMessage, HandleGenericMessage, true, true); |
5225 | AddLocalPacketHandler(PacketType.AvatarPropertiesRequest, HandleAvatarPropertiesRequest); | 5591 | AddLocalPacketHandler(PacketType.AvatarPropertiesRequest, HandleAvatarPropertiesRequest, true, true); |
5226 | AddLocalPacketHandler(PacketType.ChatFromViewer, HandleChatFromViewer); | 5592 | AddLocalPacketHandler(PacketType.ChatFromViewer, HandleChatFromViewer); |
5227 | AddLocalPacketHandler(PacketType.AvatarPropertiesUpdate, HandlerAvatarPropertiesUpdate); | 5593 | AddLocalPacketHandler(PacketType.AvatarPropertiesUpdate, HandlerAvatarPropertiesUpdate, true, true); |
5228 | AddLocalPacketHandler(PacketType.ScriptDialogReply, HandlerScriptDialogReply); | 5594 | AddLocalPacketHandler(PacketType.ScriptDialogReply, HandlerScriptDialogReply); |
5229 | AddLocalPacketHandler(PacketType.ImprovedInstantMessage, HandlerImprovedInstantMessage); | 5595 | AddLocalPacketHandler(PacketType.ImprovedInstantMessage, HandlerImprovedInstantMessage); |
5230 | AddLocalPacketHandler(PacketType.AcceptFriendship, HandlerAcceptFriendship); | 5596 | AddLocalPacketHandler(PacketType.AcceptFriendship, HandlerAcceptFriendship); |
@@ -5233,7 +5599,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5233 | AddLocalPacketHandler(PacketType.RezObject, HandlerRezObject); | 5599 | AddLocalPacketHandler(PacketType.RezObject, HandlerRezObject); |
5234 | AddLocalPacketHandler(PacketType.DeRezObject, HandlerDeRezObject); | 5600 | AddLocalPacketHandler(PacketType.DeRezObject, HandlerDeRezObject); |
5235 | AddLocalPacketHandler(PacketType.ModifyLand, HandlerModifyLand); | 5601 | AddLocalPacketHandler(PacketType.ModifyLand, HandlerModifyLand); |
5236 | AddLocalPacketHandler(PacketType.RegionHandshakeReply, HandlerRegionHandshakeReply); | 5602 | AddLocalPacketHandler(PacketType.RegionHandshakeReply, HandlerRegionHandshakeReply, false); |
5237 | AddLocalPacketHandler(PacketType.AgentWearablesRequest, HandlerAgentWearablesRequest); | 5603 | AddLocalPacketHandler(PacketType.AgentWearablesRequest, HandlerAgentWearablesRequest); |
5238 | AddLocalPacketHandler(PacketType.AgentSetAppearance, HandlerAgentSetAppearance); | 5604 | AddLocalPacketHandler(PacketType.AgentSetAppearance, HandlerAgentSetAppearance); |
5239 | AddLocalPacketHandler(PacketType.AgentIsNowWearing, HandlerAgentIsNowWearing); | 5605 | AddLocalPacketHandler(PacketType.AgentIsNowWearing, HandlerAgentIsNowWearing); |
@@ -5294,8 +5660,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5294 | AddLocalPacketHandler(PacketType.ScriptAnswerYes, HandleScriptAnswerYes, false); | 5660 | AddLocalPacketHandler(PacketType.ScriptAnswerYes, HandleScriptAnswerYes, false); |
5295 | AddLocalPacketHandler(PacketType.ObjectClickAction, HandleObjectClickAction, false); | 5661 | AddLocalPacketHandler(PacketType.ObjectClickAction, HandleObjectClickAction, false); |
5296 | AddLocalPacketHandler(PacketType.ObjectMaterial, HandleObjectMaterial, false); | 5662 | AddLocalPacketHandler(PacketType.ObjectMaterial, HandleObjectMaterial, false); |
5297 | AddLocalPacketHandler(PacketType.RequestImage, HandleRequestImage); | 5663 | AddLocalPacketHandler(PacketType.RequestImage, HandleRequestImage, false); |
5298 | AddLocalPacketHandler(PacketType.TransferRequest, HandleTransferRequest); | 5664 | AddLocalPacketHandler(PacketType.TransferRequest, HandleTransferRequest, false); |
5299 | AddLocalPacketHandler(PacketType.AssetUploadRequest, HandleAssetUploadRequest); | 5665 | AddLocalPacketHandler(PacketType.AssetUploadRequest, HandleAssetUploadRequest); |
5300 | AddLocalPacketHandler(PacketType.RequestXfer, HandleRequestXfer); | 5666 | AddLocalPacketHandler(PacketType.RequestXfer, HandleRequestXfer); |
5301 | AddLocalPacketHandler(PacketType.SendXferPacket, HandleSendXferPacket); | 5667 | AddLocalPacketHandler(PacketType.SendXferPacket, HandleSendXferPacket); |
@@ -5327,7 +5693,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5327 | AddLocalPacketHandler(PacketType.TeleportCancel, HandleTeleportCancel); | 5693 | AddLocalPacketHandler(PacketType.TeleportCancel, HandleTeleportCancel); |
5328 | AddLocalPacketHandler(PacketType.TeleportLocationRequest, HandleTeleportLocationRequest); | 5694 | AddLocalPacketHandler(PacketType.TeleportLocationRequest, HandleTeleportLocationRequest); |
5329 | AddLocalPacketHandler(PacketType.UUIDNameRequest, HandleUUIDNameRequest, false); | 5695 | AddLocalPacketHandler(PacketType.UUIDNameRequest, HandleUUIDNameRequest, false); |
5330 | AddLocalPacketHandler(PacketType.RegionHandleRequest, HandleRegionHandleRequest); | 5696 | AddLocalPacketHandler(PacketType.RegionHandleRequest, HandleRegionHandleRequest, false); |
5331 | AddLocalPacketHandler(PacketType.ParcelInfoRequest, HandleParcelInfoRequest); | 5697 | AddLocalPacketHandler(PacketType.ParcelInfoRequest, HandleParcelInfoRequest); |
5332 | AddLocalPacketHandler(PacketType.ParcelAccessListRequest, HandleParcelAccessListRequest, false); | 5698 | AddLocalPacketHandler(PacketType.ParcelAccessListRequest, HandleParcelAccessListRequest, false); |
5333 | AddLocalPacketHandler(PacketType.ParcelAccessListUpdate, HandleParcelAccessListUpdate, false); | 5699 | AddLocalPacketHandler(PacketType.ParcelAccessListUpdate, HandleParcelAccessListUpdate, false); |
@@ -5409,8 +5775,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5409 | AddLocalPacketHandler(PacketType.PickDelete, HandlePickDelete); | 5775 | AddLocalPacketHandler(PacketType.PickDelete, HandlePickDelete); |
5410 | AddLocalPacketHandler(PacketType.PickGodDelete, HandlePickGodDelete); | 5776 | AddLocalPacketHandler(PacketType.PickGodDelete, HandlePickGodDelete); |
5411 | AddLocalPacketHandler(PacketType.PickInfoUpdate, HandlePickInfoUpdate); | 5777 | AddLocalPacketHandler(PacketType.PickInfoUpdate, HandlePickInfoUpdate); |
5412 | AddLocalPacketHandler(PacketType.AvatarNotesUpdate, HandleAvatarNotesUpdate); | 5778 | AddLocalPacketHandler(PacketType.AvatarNotesUpdate, HandleAvatarNotesUpdate, true, true); |
5413 | AddLocalPacketHandler(PacketType.AvatarInterestsUpdate, HandleAvatarInterestsUpdate); | 5779 | AddLocalPacketHandler(PacketType.AvatarInterestsUpdate, HandleAvatarInterestsUpdate, true, true); |
5414 | AddLocalPacketHandler(PacketType.GrantUserRights, HandleGrantUserRights); | 5780 | AddLocalPacketHandler(PacketType.GrantUserRights, HandleGrantUserRights); |
5415 | AddLocalPacketHandler(PacketType.PlacesQuery, HandlePlacesQuery); | 5781 | AddLocalPacketHandler(PacketType.PlacesQuery, HandlePlacesQuery); |
5416 | AddLocalPacketHandler(PacketType.UpdateMuteListEntry, HandleUpdateMuteListEntry); | 5782 | AddLocalPacketHandler(PacketType.UpdateMuteListEntry, HandleUpdateMuteListEntry); |
@@ -5438,82 +5804,137 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5438 | 5804 | ||
5439 | #region Packet Handlers | 5805 | #region Packet Handlers |
5440 | 5806 | ||
5807 | public int TotalAgentUpdates { get; set; } | ||
5808 | |||
5441 | #region Scene/Avatar | 5809 | #region Scene/Avatar |
5442 | 5810 | ||
5443 | private bool HandleAgentUpdate(IClientAPI sener, Packet packet) | 5811 | // Threshold for body rotation to be a significant agent update |
5812 | private const float QDELTA = 0.000001f; | ||
5813 | // Threshold for camera rotation to be a significant agent update | ||
5814 | private const float VDELTA = 0.01f; | ||
5815 | |||
5816 | /// <summary> | ||
5817 | /// This checks the update significance against the last update made. | ||
5818 | /// </summary> | ||
5819 | /// <remarks>Can only be called by one thread at a time</remarks> | ||
5820 | /// <returns></returns> | ||
5821 | /// <param name='x'></param> | ||
5822 | public bool CheckAgentUpdateSignificance(AgentUpdatePacket.AgentDataBlock x) | ||
5444 | { | 5823 | { |
5445 | if (OnAgentUpdate != null) | 5824 | return CheckAgentMovementUpdateSignificance(x) || CheckAgentCameraUpdateSignificance(x); |
5446 | { | 5825 | } |
5447 | AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet; | ||
5448 | 5826 | ||
5449 | #region Packet Session and User Check | 5827 | /// <summary> |
5450 | if (agentUpdate.AgentData.SessionID != SessionId || agentUpdate.AgentData.AgentID != AgentId) | 5828 | /// This checks the movement/state update significance against the last update made. |
5451 | { | 5829 | /// </summary> |
5452 | PacketPool.Instance.ReturnPacket(packet); | 5830 | /// <remarks>Can only be called by one thread at a time</remarks> |
5453 | return false; | 5831 | /// <returns></returns> |
5454 | } | 5832 | /// <param name='x'></param> |
5455 | #endregion | 5833 | private bool CheckAgentMovementUpdateSignificance(AgentUpdatePacket.AgentDataBlock x) |
5834 | { | ||
5835 | float qdelta1 = 1 - (float)Math.Pow(Quaternion.Dot(x.BodyRotation, m_thisAgentUpdateArgs.BodyRotation), 2); | ||
5836 | //qdelta2 = 1 - (float)Math.Pow(Quaternion.Dot(x.HeadRotation, m_thisAgentUpdateArgs.HeadRotation), 2); | ||
5837 | |||
5838 | bool movementSignificant = | ||
5839 | (qdelta1 > QDELTA) // significant if body rotation above threshold | ||
5840 | // Ignoring head rotation altogether, because it's not being used for anything interesting up the stack | ||
5841 | // || (qdelta2 > QDELTA * 10) // significant if head rotation above threshold | ||
5842 | || (x.ControlFlags != m_thisAgentUpdateArgs.ControlFlags) // significant if control flags changed | ||
5843 | || (x.ControlFlags != (byte)AgentManager.ControlFlags.NONE) // significant if user supplying any movement update commands | ||
5844 | || (x.Far != m_thisAgentUpdateArgs.Far) // significant if far distance changed | ||
5845 | || (x.Flags != m_thisAgentUpdateArgs.Flags) // significant if Flags changed | ||
5846 | || (x.State != m_thisAgentUpdateArgs.State) // significant if Stats changed | ||
5847 | ; | ||
5848 | //if (movementSignificant) | ||
5849 | //{ | ||
5850 | //m_log.DebugFormat("[LLCLIENTVIEW]: Bod {0} {1}", | ||
5851 | // qdelta1, qdelta2); | ||
5852 | //m_log.DebugFormat("[LLCLIENTVIEW]: St {0} {1} {2} {3}", | ||
5853 | // x.ControlFlags, x.Flags, x.Far, x.State); | ||
5854 | //} | ||
5855 | return movementSignificant; | ||
5856 | } | ||
5456 | 5857 | ||
5457 | bool update = false; | 5858 | /// <summary> |
5458 | AgentUpdatePacket.AgentDataBlock x = agentUpdate.AgentData; | 5859 | /// This checks the camera update significance against the last update made. |
5459 | 5860 | /// </summary> | |
5460 | if (m_lastAgentUpdateArgs != null) | 5861 | /// <remarks>Can only be called by one thread at a time</remarks> |
5461 | { | 5862 | /// <returns></returns> |
5462 | // These should be ordered from most-likely to | 5863 | /// <param name='x'></param> |
5463 | // least likely to change. I've made an initial | 5864 | private bool CheckAgentCameraUpdateSignificance(AgentUpdatePacket.AgentDataBlock x) |
5464 | // guess at that. | 5865 | { |
5465 | update = | 5866 | float vdelta1 = Vector3.Distance(x.CameraAtAxis, m_thisAgentUpdateArgs.CameraAtAxis); |
5466 | ( | 5867 | float vdelta2 = Vector3.Distance(x.CameraCenter, m_thisAgentUpdateArgs.CameraCenter); |
5467 | (x.BodyRotation != m_lastAgentUpdateArgs.BodyRotation) || | 5868 | float vdelta3 = Vector3.Distance(x.CameraLeftAxis, m_thisAgentUpdateArgs.CameraLeftAxis); |
5468 | (x.CameraAtAxis != m_lastAgentUpdateArgs.CameraAtAxis) || | 5869 | float vdelta4 = Vector3.Distance(x.CameraUpAxis, m_thisAgentUpdateArgs.CameraUpAxis); |
5469 | (x.CameraCenter != m_lastAgentUpdateArgs.CameraCenter) || | ||
5470 | (x.CameraLeftAxis != m_lastAgentUpdateArgs.CameraLeftAxis) || | ||
5471 | (x.CameraUpAxis != m_lastAgentUpdateArgs.CameraUpAxis) || | ||
5472 | (x.ControlFlags != m_lastAgentUpdateArgs.ControlFlags) || | ||
5473 | (x.Far != m_lastAgentUpdateArgs.Far) || | ||
5474 | (x.Flags != m_lastAgentUpdateArgs.Flags) || | ||
5475 | (x.State != m_lastAgentUpdateArgs.State) || | ||
5476 | (x.HeadRotation != m_lastAgentUpdateArgs.HeadRotation) || | ||
5477 | (x.SessionID != m_lastAgentUpdateArgs.SessionID) || | ||
5478 | (x.AgentID != m_lastAgentUpdateArgs.AgentID) | ||
5479 | ); | ||
5480 | } | ||
5481 | else | ||
5482 | { | ||
5483 | m_lastAgentUpdateArgs = new AgentUpdateArgs(); | ||
5484 | update = true; | ||
5485 | } | ||
5486 | 5870 | ||
5487 | if (update) | 5871 | bool cameraSignificant = |
5488 | { | 5872 | (vdelta1 > VDELTA) || |
5489 | // m_log.DebugFormat("[LLCLIENTVIEW]: Triggered AgentUpdate for {0}", sener.Name); | 5873 | (vdelta2 > VDELTA) || |
5874 | (vdelta3 > VDELTA) || | ||
5875 | (vdelta4 > VDELTA) | ||
5876 | ; | ||
5490 | 5877 | ||
5491 | m_lastAgentUpdateArgs.AgentID = x.AgentID; | 5878 | //if (cameraSignificant) |
5492 | m_lastAgentUpdateArgs.BodyRotation = x.BodyRotation; | 5879 | //{ |
5493 | m_lastAgentUpdateArgs.CameraAtAxis = x.CameraAtAxis; | 5880 | //m_log.DebugFormat("[LLCLIENTVIEW]: Cam1 {0} {1}", |
5494 | m_lastAgentUpdateArgs.CameraCenter = x.CameraCenter; | 5881 | // x.CameraAtAxis, x.CameraCenter); |
5495 | m_lastAgentUpdateArgs.CameraLeftAxis = x.CameraLeftAxis; | 5882 | //m_log.DebugFormat("[LLCLIENTVIEW]: Cam2 {0} {1}", |
5496 | m_lastAgentUpdateArgs.CameraUpAxis = x.CameraUpAxis; | 5883 | // x.CameraLeftAxis, x.CameraUpAxis); |
5497 | m_lastAgentUpdateArgs.ControlFlags = x.ControlFlags; | 5884 | //} |
5498 | m_lastAgentUpdateArgs.Far = x.Far; | ||
5499 | m_lastAgentUpdateArgs.Flags = x.Flags; | ||
5500 | m_lastAgentUpdateArgs.HeadRotation = x.HeadRotation; | ||
5501 | m_lastAgentUpdateArgs.SessionID = x.SessionID; | ||
5502 | m_lastAgentUpdateArgs.State = x.State; | ||
5503 | 5885 | ||
5504 | UpdateAgent handlerAgentUpdate = OnAgentUpdate; | 5886 | return cameraSignificant; |
5505 | UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate; | 5887 | } |
5506 | 5888 | ||
5507 | if (handlerPreAgentUpdate != null) | 5889 | private bool HandleAgentUpdate(IClientAPI sener, Packet packet) |
5508 | OnPreAgentUpdate(this, m_lastAgentUpdateArgs); | 5890 | { |
5891 | // We got here, which means that something in agent update was significant | ||
5509 | 5892 | ||
5510 | if (handlerAgentUpdate != null) | 5893 | AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet; |
5511 | OnAgentUpdate(this, m_lastAgentUpdateArgs); | 5894 | AgentUpdatePacket.AgentDataBlock x = agentUpdate.AgentData; |
5512 | 5895 | ||
5513 | handlerAgentUpdate = null; | 5896 | if (x.AgentID != AgentId || x.SessionID != SessionId) |
5514 | handlerPreAgentUpdate = null; | 5897 | return false; |
5515 | } | 5898 | |
5516 | } | 5899 | // Before we update the current m_thisAgentUpdateArgs, let's check this again |
5900 | // to see what exactly changed | ||
5901 | bool movement = CheckAgentMovementUpdateSignificance(x); | ||
5902 | bool camera = CheckAgentCameraUpdateSignificance(x); | ||
5903 | |||
5904 | m_thisAgentUpdateArgs.AgentID = x.AgentID; | ||
5905 | m_thisAgentUpdateArgs.BodyRotation = x.BodyRotation; | ||
5906 | m_thisAgentUpdateArgs.CameraAtAxis = x.CameraAtAxis; | ||
5907 | m_thisAgentUpdateArgs.CameraCenter = x.CameraCenter; | ||
5908 | m_thisAgentUpdateArgs.CameraLeftAxis = x.CameraLeftAxis; | ||
5909 | m_thisAgentUpdateArgs.CameraUpAxis = x.CameraUpAxis; | ||
5910 | m_thisAgentUpdateArgs.ControlFlags = x.ControlFlags; | ||
5911 | m_thisAgentUpdateArgs.Far = x.Far; | ||
5912 | m_thisAgentUpdateArgs.Flags = x.Flags; | ||
5913 | m_thisAgentUpdateArgs.HeadRotation = x.HeadRotation; | ||
5914 | m_thisAgentUpdateArgs.SessionID = x.SessionID; | ||
5915 | m_thisAgentUpdateArgs.State = x.State; | ||
5916 | |||
5917 | UpdateAgent handlerAgentUpdate = OnAgentUpdate; | ||
5918 | UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate; | ||
5919 | UpdateAgent handlerAgentCameraUpdate = OnAgentCameraUpdate; | ||
5920 | |||
5921 | // Was there a significant movement/state change? | ||
5922 | if (movement) | ||
5923 | { | ||
5924 | if (handlerPreAgentUpdate != null) | ||
5925 | OnPreAgentUpdate(this, m_thisAgentUpdateArgs); | ||
5926 | |||
5927 | if (handlerAgentUpdate != null) | ||
5928 | OnAgentUpdate(this, m_thisAgentUpdateArgs); | ||
5929 | } | ||
5930 | // Was there a significant camera(s) change? | ||
5931 | if (camera) | ||
5932 | if (handlerAgentCameraUpdate != null) | ||
5933 | handlerAgentCameraUpdate(this, m_thisAgentUpdateArgs); | ||
5934 | |||
5935 | handlerAgentUpdate = null; | ||
5936 | handlerPreAgentUpdate = null; | ||
5937 | handlerAgentCameraUpdate = null; | ||
5517 | 5938 | ||
5518 | PacketPool.Instance.ReturnPacket(packet); | 5939 | PacketPool.Instance.ReturnPacket(packet); |
5519 | 5940 | ||
@@ -6080,6 +6501,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6080 | //m_log.Info("[LAND]: LAND:" + modify.ToString()); | 6501 | //m_log.Info("[LAND]: LAND:" + modify.ToString()); |
6081 | if (modify.ParcelData.Length > 0) | 6502 | if (modify.ParcelData.Length > 0) |
6082 | { | 6503 | { |
6504 | // Note: the ModifyTerrain event handler sends out updated packets before the end of this event. Therefore, | ||
6505 | // a simple boolean value should work and perhaps queue up just a few terrain patch packets at the end of the edit. | ||
6506 | m_justEditedTerrain = true; // Prevent terrain packet (Land layer) from being queued, make it unreliable | ||
6083 | if (OnModifyTerrain != null) | 6507 | if (OnModifyTerrain != null) |
6084 | { | 6508 | { |
6085 | for (int i = 0; i < modify.ParcelData.Length; i++) | 6509 | for (int i = 0; i < modify.ParcelData.Length; i++) |
@@ -6095,6 +6519,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6095 | } | 6519 | } |
6096 | } | 6520 | } |
6097 | } | 6521 | } |
6522 | m_justEditedTerrain = false; // Queue terrain packet (Land layer) if necessary, make it reliable again | ||
6098 | } | 6523 | } |
6099 | 6524 | ||
6100 | return true; | 6525 | return true; |
@@ -6149,17 +6574,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6149 | // Temporarily protect ourselves from the mantis #951 failure. | 6574 | // Temporarily protect ourselves from the mantis #951 failure. |
6150 | // However, we could do this for several other handlers where a failure isn't terminal | 6575 | // However, we could do this for several other handlers where a failure isn't terminal |
6151 | // for the client session anyway, in order to protect ourselves against bad code in plugins | 6576 | // for the client session anyway, in order to protect ourselves against bad code in plugins |
6577 | Vector3 avSize = appear.AgentData.Size; | ||
6152 | try | 6578 | try |
6153 | { | 6579 | { |
6154 | byte[] visualparams = new byte[appear.VisualParam.Length]; | 6580 | byte[] visualparams = new byte[appear.VisualParam.Length]; |
6155 | for (int i = 0; i < appear.VisualParam.Length; i++) | 6581 | for (int i = 0; i < appear.VisualParam.Length; i++) |
6156 | visualparams[i] = appear.VisualParam[i].ParamValue; | 6582 | visualparams[i] = appear.VisualParam[i].ParamValue; |
6583 | //var b = appear.WearableData[0]; | ||
6157 | 6584 | ||
6158 | Primitive.TextureEntry te = null; | 6585 | Primitive.TextureEntry te = null; |
6159 | if (appear.ObjectData.TextureEntry.Length > 1) | 6586 | if (appear.ObjectData.TextureEntry.Length > 1) |
6160 | te = new Primitive.TextureEntry(appear.ObjectData.TextureEntry, 0, appear.ObjectData.TextureEntry.Length); | 6587 | te = new Primitive.TextureEntry(appear.ObjectData.TextureEntry, 0, appear.ObjectData.TextureEntry.Length); |
6161 | 6588 | ||
6162 | handlerSetAppearance(sender, te, visualparams); | 6589 | WearableCacheItem[] cacheitems = new WearableCacheItem[appear.WearableData.Length]; |
6590 | for (int i=0; i<appear.WearableData.Length;i++) | ||
6591 | cacheitems[i] = new WearableCacheItem(){CacheId = appear.WearableData[i].CacheID,TextureIndex=Convert.ToUInt32(appear.WearableData[i].TextureIndex)}; | ||
6592 | |||
6593 | |||
6594 | |||
6595 | handlerSetAppearance(sender, te, visualparams,avSize, cacheitems); | ||
6163 | } | 6596 | } |
6164 | catch (Exception e) | 6597 | catch (Exception e) |
6165 | { | 6598 | { |
@@ -6426,6 +6859,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6426 | } | 6859 | } |
6427 | #endregion | 6860 | #endregion |
6428 | 6861 | ||
6862 | if (SceneAgent.IsChildAgent) | ||
6863 | { | ||
6864 | SendCantSitBecauseChildAgentResponse(); | ||
6865 | return true; | ||
6866 | } | ||
6867 | |||
6429 | AgentRequestSit handlerAgentRequestSit = OnAgentRequestSit; | 6868 | AgentRequestSit handlerAgentRequestSit = OnAgentRequestSit; |
6430 | 6869 | ||
6431 | if (handlerAgentRequestSit != null) | 6870 | if (handlerAgentRequestSit != null) |
@@ -6450,6 +6889,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6450 | } | 6889 | } |
6451 | #endregion | 6890 | #endregion |
6452 | 6891 | ||
6892 | if (SceneAgent.IsChildAgent) | ||
6893 | { | ||
6894 | SendCantSitBecauseChildAgentResponse(); | ||
6895 | return true; | ||
6896 | } | ||
6897 | |||
6453 | AgentSit handlerAgentSit = OnAgentSit; | 6898 | AgentSit handlerAgentSit = OnAgentSit; |
6454 | if (handlerAgentSit != null) | 6899 | if (handlerAgentSit != null) |
6455 | { | 6900 | { |
@@ -6459,6 +6904,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6459 | return true; | 6904 | return true; |
6460 | } | 6905 | } |
6461 | 6906 | ||
6907 | /// <summary> | ||
6908 | /// Used when a child agent gets a sit response which should not be fulfilled. | ||
6909 | /// </summary> | ||
6910 | private void SendCantSitBecauseChildAgentResponse() | ||
6911 | { | ||
6912 | SendAlertMessage("Try moving closer. Can't sit on object because it is not in the same region as you."); | ||
6913 | } | ||
6914 | |||
6462 | private bool HandleSoundTrigger(IClientAPI sender, Packet Pack) | 6915 | private bool HandleSoundTrigger(IClientAPI sender, Packet Pack) |
6463 | { | 6916 | { |
6464 | SoundTriggerPacket soundTriggerPacket = (SoundTriggerPacket)Pack; | 6917 | SoundTriggerPacket soundTriggerPacket = (SoundTriggerPacket)Pack; |
@@ -6856,7 +7309,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6856 | { | 7309 | { |
6857 | handlerObjectDuplicate(dupe.ObjectData[i].ObjectLocalID, dupe.SharedData.Offset, | 7310 | handlerObjectDuplicate(dupe.ObjectData[i].ObjectLocalID, dupe.SharedData.Offset, |
6858 | dupe.SharedData.DuplicateFlags, AgentId, | 7311 | dupe.SharedData.DuplicateFlags, AgentId, |
6859 | m_activeGroupID); | 7312 | ActiveGroupId); |
6860 | } | 7313 | } |
6861 | } | 7314 | } |
6862 | 7315 | ||
@@ -7031,14 +7484,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
7031 | 7484 | ||
7032 | if (handlerUpdatePrimFlags != null) | 7485 | if (handlerUpdatePrimFlags != null) |
7033 | { | 7486 | { |
7034 | byte[] data = Pack.ToBytes(); | 7487 | // byte[] data = Pack.ToBytes(); |
7035 | // 46,47,48 are special positions within the packet | 7488 | // 46,47,48 are special positions within the packet |
7036 | // This may change so perhaps we need a better way | 7489 | // This may change so perhaps we need a better way |
7037 | // of storing this (OMV.FlagUpdatePacket.UsePhysics,etc?) | 7490 | // of storing this (OMV.FlagUpdatePacket.UsePhysics,etc?) |
7038 | bool UsePhysics = (data[46] != 0) ? true : false; | 7491 | /* |
7039 | bool IsTemporary = (data[47] != 0) ? true : false; | 7492 | bool UsePhysics = (data[46] != 0) ? true : false; |
7040 | bool IsPhantom = (data[48] != 0) ? true : false; | 7493 | bool IsTemporary = (data[47] != 0) ? true : false; |
7041 | handlerUpdatePrimFlags(flags.AgentData.ObjectLocalID, UsePhysics, IsTemporary, IsPhantom, this); | 7494 | bool IsPhantom = (data[48] != 0) ? true : false; |
7495 | handlerUpdatePrimFlags(flags.AgentData.ObjectLocalID, UsePhysics, IsTemporary, IsPhantom, this); | ||
7496 | */ | ||
7497 | bool UsePhysics = flags.AgentData.UsePhysics; | ||
7498 | bool IsPhantom = flags.AgentData.IsPhantom; | ||
7499 | bool IsTemporary = flags.AgentData.IsTemporary; | ||
7500 | ObjectFlagUpdatePacket.ExtraPhysicsBlock[] blocks = flags.ExtraPhysics; | ||
7501 | ExtraPhysicsData physdata = new ExtraPhysicsData(); | ||
7502 | |||
7503 | if (blocks == null || blocks.Length == 0) | ||
7504 | { | ||
7505 | physdata.PhysShapeType = PhysShapeType.invalid; | ||
7506 | } | ||
7507 | else | ||
7508 | { | ||
7509 | ObjectFlagUpdatePacket.ExtraPhysicsBlock phsblock = blocks[0]; | ||
7510 | physdata.PhysShapeType = (PhysShapeType)phsblock.PhysicsShapeType; | ||
7511 | physdata.Bounce = phsblock.Restitution; | ||
7512 | physdata.Density = phsblock.Density; | ||
7513 | physdata.Friction = phsblock.Friction; | ||
7514 | physdata.GravitationModifier = phsblock.GravityMultiplier; | ||
7515 | } | ||
7516 | |||
7517 | handlerUpdatePrimFlags(flags.AgentData.ObjectLocalID, UsePhysics, IsTemporary, IsPhantom, physdata, this); | ||
7042 | } | 7518 | } |
7043 | return true; | 7519 | return true; |
7044 | } | 7520 | } |
@@ -7446,7 +7922,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
7446 | if (handlerObjectDuplicateOnRay != null) | 7922 | if (handlerObjectDuplicateOnRay != null) |
7447 | { | 7923 | { |
7448 | handlerObjectDuplicateOnRay(dupeOnRay.ObjectData[i].ObjectLocalID, dupeOnRay.AgentData.DuplicateFlags, | 7924 | handlerObjectDuplicateOnRay(dupeOnRay.ObjectData[i].ObjectLocalID, dupeOnRay.AgentData.DuplicateFlags, |
7449 | AgentId, m_activeGroupID, dupeOnRay.AgentData.RayTargetID, dupeOnRay.AgentData.RayEnd, | 7925 | AgentId, ActiveGroupId, dupeOnRay.AgentData.RayTargetID, dupeOnRay.AgentData.RayEnd, |
7450 | dupeOnRay.AgentData.RayStart, dupeOnRay.AgentData.BypassRaycast, dupeOnRay.AgentData.RayEndIsIntersection, | 7926 | dupeOnRay.AgentData.RayStart, dupeOnRay.AgentData.BypassRaycast, dupeOnRay.AgentData.RayEndIsIntersection, |
7451 | dupeOnRay.AgentData.CopyCenters, dupeOnRay.AgentData.CopyRotates); | 7927 | dupeOnRay.AgentData.CopyCenters, dupeOnRay.AgentData.CopyRotates); |
7452 | } | 7928 | } |
@@ -7640,129 +8116,146 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
7640 | //m_log.Debug("ClientView.ProcessPackets.cs:ProcessInPacket() - Got transfer request"); | 8116 | //m_log.Debug("ClientView.ProcessPackets.cs:ProcessInPacket() - Got transfer request"); |
7641 | 8117 | ||
7642 | TransferRequestPacket transfer = (TransferRequestPacket)Pack; | 8118 | TransferRequestPacket transfer = (TransferRequestPacket)Pack; |
7643 | //m_log.Debug("Transfer Request: " + transfer.ToString()); | ||
7644 | // Validate inventory transfers | ||
7645 | // Has to be done here, because AssetCache can't do it | ||
7646 | // | ||
7647 | UUID taskID = UUID.Zero; | 8119 | UUID taskID = UUID.Zero; |
7648 | if (transfer.TransferInfo.SourceType == (int)SourceType.SimInventoryItem) | 8120 | if (transfer.TransferInfo.SourceType == (int)SourceType.SimInventoryItem) |
7649 | { | 8121 | { |
7650 | taskID = new UUID(transfer.TransferInfo.Params, 48); | ||
7651 | UUID itemID = new UUID(transfer.TransferInfo.Params, 64); | ||
7652 | UUID requestID = new UUID(transfer.TransferInfo.Params, 80); | ||
7653 | |||
7654 | // m_log.DebugFormat( | ||
7655 | // "[CLIENT]: Got request for asset {0} from item {1} in prim {2} by {3}", | ||
7656 | // requestID, itemID, taskID, Name); | ||
7657 | |||
7658 | if (!(((Scene)m_scene).Permissions.BypassPermissions())) | 8122 | if (!(((Scene)m_scene).Permissions.BypassPermissions())) |
7659 | { | 8123 | { |
7660 | if (taskID != UUID.Zero) // Prim | 8124 | // We're spawning a thread because the permissions check can block this thread |
8125 | Util.FireAndForget(delegate | ||
7661 | { | 8126 | { |
7662 | SceneObjectPart part = ((Scene)m_scene).GetSceneObjectPart(taskID); | 8127 | // This requests the asset if needed |
8128 | HandleSimInventoryTransferRequestWithPermsCheck(sender, transfer); | ||
8129 | }, null, "LLClientView.HandleTransferRequest"); | ||
7663 | 8130 | ||
7664 | if (part == null) | 8131 | return true; |
7665 | { | 8132 | } |
7666 | m_log.WarnFormat( | 8133 | } |
7667 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but prim does not exist", | 8134 | else if (transfer.TransferInfo.SourceType == (int)SourceType.SimEstate) |
7668 | Name, requestID, itemID, taskID); | 8135 | { |
7669 | return true; | 8136 | //TransferRequestPacket does not include covenant uuid? |
7670 | } | 8137 | //get scene covenant uuid |
8138 | taskID = m_scene.RegionInfo.RegionSettings.Covenant; | ||
8139 | } | ||
7671 | 8140 | ||
7672 | TaskInventoryItem tii = part.Inventory.GetInventoryItem(itemID); | 8141 | // This is non-blocking |
7673 | if (tii == null) | 8142 | MakeAssetRequest(transfer, taskID); |
7674 | { | ||
7675 | m_log.WarnFormat( | ||
7676 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but item does not exist", | ||
7677 | Name, requestID, itemID, taskID); | ||
7678 | return true; | ||
7679 | } | ||
7680 | 8143 | ||
7681 | if (tii.Type == (int)AssetType.LSLText) | 8144 | return true; |
7682 | { | 8145 | } |
7683 | if (!((Scene)m_scene).Permissions.CanEditScript(itemID, taskID, AgentId)) | ||
7684 | return true; | ||
7685 | } | ||
7686 | else if (tii.Type == (int)AssetType.Notecard) | ||
7687 | { | ||
7688 | if (!((Scene)m_scene).Permissions.CanEditNotecard(itemID, taskID, AgentId)) | ||
7689 | return true; | ||
7690 | } | ||
7691 | else | ||
7692 | { | ||
7693 | // TODO: Change this code to allow items other than notecards and scripts to be successfully | ||
7694 | // shared with group. In fact, this whole block of permissions checking should move to an IPermissionsModule | ||
7695 | if (part.OwnerID != AgentId) | ||
7696 | { | ||
7697 | m_log.WarnFormat( | ||
7698 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but the prim is owned by {4}", | ||
7699 | Name, requestID, itemID, taskID, part.OwnerID); | ||
7700 | return true; | ||
7701 | } | ||
7702 | 8146 | ||
7703 | if ((part.OwnerMask & (uint)PermissionMask.Modify) == 0) | 8147 | private void HandleSimInventoryTransferRequestWithPermsCheck(IClientAPI sender, TransferRequestPacket transfer) |
7704 | { | 8148 | { |
7705 | m_log.WarnFormat( | 8149 | UUID taskID = new UUID(transfer.TransferInfo.Params, 48); |
7706 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but modify permissions are not set", | 8150 | UUID itemID = new UUID(transfer.TransferInfo.Params, 64); |
7707 | Name, requestID, itemID, taskID); | 8151 | UUID requestID = new UUID(transfer.TransferInfo.Params, 80); |
7708 | return true; | ||
7709 | } | ||
7710 | 8152 | ||
7711 | if (tii.OwnerID != AgentId) | 8153 | //m_log.DebugFormat( |
7712 | { | 8154 | // "[CLIENT]: Got request for asset {0} from item {1} in prim {2} by {3}", |
7713 | m_log.WarnFormat( | 8155 | // requestID, itemID, taskID, Name); |
7714 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but the item is owned by {4}", | ||
7715 | Name, requestID, itemID, taskID, tii.OwnerID); | ||
7716 | return true; | ||
7717 | } | ||
7718 | 8156 | ||
7719 | if (( | 8157 | //m_log.Debug("Transfer Request: " + transfer.ToString()); |
7720 | tii.CurrentPermissions & ((uint)PermissionMask.Modify | (uint)PermissionMask.Copy | (uint)PermissionMask.Transfer)) | 8158 | // Validate inventory transfers |
7721 | != ((uint)PermissionMask.Modify | (uint)PermissionMask.Copy | (uint)PermissionMask.Transfer)) | 8159 | // Has to be done here, because AssetCache can't do it |
7722 | { | 8160 | // |
7723 | m_log.WarnFormat( | 8161 | if (taskID != UUID.Zero) // Prim |
7724 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but item permissions are not modify/copy/transfer", | 8162 | { |
7725 | Name, requestID, itemID, taskID); | 8163 | SceneObjectPart part = ((Scene)m_scene).GetSceneObjectPart(taskID); |
7726 | return true; | ||
7727 | } | ||
7728 | 8164 | ||
7729 | if (tii.AssetID != requestID) | 8165 | if (part == null) |
7730 | { | 8166 | { |
7731 | m_log.WarnFormat( | 8167 | m_log.WarnFormat( |
7732 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but this does not match item's asset {4}", | 8168 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but prim does not exist", |
7733 | Name, requestID, itemID, taskID, tii.AssetID); | 8169 | Name, requestID, itemID, taskID); |
7734 | return true; | 8170 | return; |
7735 | } | 8171 | } |
7736 | } | 8172 | |
8173 | TaskInventoryItem tii = part.Inventory.GetInventoryItem(itemID); | ||
8174 | if (tii == null) | ||
8175 | { | ||
8176 | m_log.WarnFormat( | ||
8177 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but item does not exist", | ||
8178 | Name, requestID, itemID, taskID); | ||
8179 | return; | ||
8180 | } | ||
8181 | |||
8182 | if (tii.Type == (int)AssetType.LSLText) | ||
8183 | { | ||
8184 | if (!((Scene)m_scene).Permissions.CanEditScript(itemID, taskID, AgentId)) | ||
8185 | return; | ||
8186 | } | ||
8187 | else if (tii.Type == (int)AssetType.Notecard) | ||
8188 | { | ||
8189 | if (!((Scene)m_scene).Permissions.CanEditNotecard(itemID, taskID, AgentId)) | ||
8190 | return; | ||
8191 | } | ||
8192 | else | ||
8193 | { | ||
8194 | // TODO: Change this code to allow items other than notecards and scripts to be successfully | ||
8195 | // shared with group. In fact, this whole block of permissions checking should move to an IPermissionsModule | ||
8196 | if (part.OwnerID != AgentId) | ||
8197 | { | ||
8198 | m_log.WarnFormat( | ||
8199 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but the prim is owned by {4}", | ||
8200 | Name, requestID, itemID, taskID, part.OwnerID); | ||
8201 | return; | ||
7737 | } | 8202 | } |
7738 | else // Agent | 8203 | |
8204 | if ((part.OwnerMask & (uint)PermissionMask.Modify) == 0) | ||
7739 | { | 8205 | { |
7740 | IInventoryAccessModule invAccess = m_scene.RequestModuleInterface<IInventoryAccessModule>(); | 8206 | m_log.WarnFormat( |
7741 | if (invAccess != null) | 8207 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but modify permissions are not set", |
7742 | { | 8208 | Name, requestID, itemID, taskID); |
7743 | if (!invAccess.CanGetAgentInventoryItem(this, itemID, requestID)) | 8209 | return; |
7744 | return false; | 8210 | } |
7745 | } | 8211 | |
7746 | else | 8212 | if (tii.OwnerID != AgentId) |
7747 | { | 8213 | { |
7748 | return false; | 8214 | m_log.WarnFormat( |
7749 | } | 8215 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but the item is owned by {4}", |
8216 | Name, requestID, itemID, taskID, tii.OwnerID); | ||
8217 | return; | ||
8218 | } | ||
8219 | |||
8220 | if (( | ||
8221 | tii.CurrentPermissions & ((uint)PermissionMask.Modify | (uint)PermissionMask.Copy | (uint)PermissionMask.Transfer)) | ||
8222 | != ((uint)PermissionMask.Modify | (uint)PermissionMask.Copy | (uint)PermissionMask.Transfer)) | ||
8223 | { | ||
8224 | m_log.WarnFormat( | ||
8225 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but item permissions are not modify/copy/transfer", | ||
8226 | Name, requestID, itemID, taskID); | ||
8227 | return; | ||
8228 | } | ||
8229 | |||
8230 | if (tii.AssetID != requestID) | ||
8231 | { | ||
8232 | m_log.WarnFormat( | ||
8233 | "[CLIENT]: {0} requested asset {1} from item {2} in prim {3} but this does not match item's asset {4}", | ||
8234 | Name, requestID, itemID, taskID, tii.AssetID); | ||
8235 | return; | ||
7750 | } | 8236 | } |
7751 | } | 8237 | } |
7752 | } | 8238 | } |
7753 | else | 8239 | else // Agent |
7754 | if (transfer.TransferInfo.SourceType == (int)SourceType.SimEstate) | 8240 | { |
8241 | IInventoryAccessModule invAccess = m_scene.RequestModuleInterface<IInventoryAccessModule>(); | ||
8242 | if (invAccess != null) | ||
7755 | { | 8243 | { |
7756 | //TransferRequestPacket does not include covenant uuid? | 8244 | if (!invAccess.CanGetAgentInventoryItem(this, itemID, requestID)) |
7757 | //get scene covenant uuid | 8245 | return; |
7758 | taskID = m_scene.RegionInfo.RegionSettings.Covenant; | ||
7759 | } | 8246 | } |
8247 | else | ||
8248 | { | ||
8249 | return; | ||
8250 | } | ||
8251 | } | ||
7760 | 8252 | ||
8253 | // Permissions out of the way, let's request the asset | ||
7761 | MakeAssetRequest(transfer, taskID); | 8254 | MakeAssetRequest(transfer, taskID); |
7762 | 8255 | ||
7763 | return true; | ||
7764 | } | 8256 | } |
7765 | 8257 | ||
8258 | |||
7766 | private bool HandleAssetUploadRequest(IClientAPI sender, Packet Pack) | 8259 | private bool HandleAssetUploadRequest(IClientAPI sender, Packet Pack) |
7767 | { | 8260 | { |
7768 | AssetUploadRequestPacket request = (AssetUploadRequestPacket)Pack; | 8261 | AssetUploadRequestPacket request = (AssetUploadRequestPacket)Pack; |
@@ -8477,8 +8970,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
8477 | return true; | 8970 | return true; |
8478 | } | 8971 | } |
8479 | #endregion | 8972 | #endregion |
8480 | string mapName = Util.UTF8.GetString(map.NameData.Name, 0, | 8973 | string mapName = (map.NameData.Name.Length == 0) ? m_scene.RegionInfo.RegionName : |
8481 | map.NameData.Name.Length - 1); | 8974 | Util.UTF8.GetString(map.NameData.Name, 0, map.NameData.Name.Length - 1); |
8482 | RequestMapName handlerMapNameRequest = OnMapNameRequest; | 8975 | RequestMapName handlerMapNameRequest = OnMapNameRequest; |
8483 | if (handlerMapNameRequest != null) | 8976 | if (handlerMapNameRequest != null) |
8484 | { | 8977 | { |
@@ -8583,7 +9076,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
8583 | if (aCircuit != null && aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("AssetServerURI")) | 9076 | if (aCircuit != null && aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("AssetServerURI")) |
8584 | { | 9077 | { |
8585 | string assetServer = aCircuit.ServiceURLs["AssetServerURI"].ToString(); | 9078 | string assetServer = aCircuit.ServiceURLs["AssetServerURI"].ToString(); |
8586 | return ((Scene)Scene).AssetService.Get(assetServer + "/" + id); | 9079 | if (!string.IsNullOrEmpty(assetServer)) |
9080 | return ((Scene)Scene).AssetService.Get(assetServer + "/" + id); | ||
8587 | } | 9081 | } |
8588 | 9082 | ||
8589 | return null; | 9083 | return null; |
@@ -8606,6 +9100,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
8606 | TeleportLocationRequest handlerTeleportLocationRequest = OnTeleportLocationRequest; | 9100 | TeleportLocationRequest handlerTeleportLocationRequest = OnTeleportLocationRequest; |
8607 | if (handlerTeleportLocationRequest != null) | 9101 | if (handlerTeleportLocationRequest != null) |
8608 | { | 9102 | { |
9103 | // Adjust teleport location to base of a larger region if requested to teleport to a sub-region | ||
9104 | uint locX, locY; | ||
9105 | Util.RegionHandleToWorldLoc(tpLocReq.Info.RegionHandle, out locX, out locY); | ||
9106 | if ((locX >= m_scene.RegionInfo.WorldLocX) | ||
9107 | && (locX < (m_scene.RegionInfo.WorldLocX + m_scene.RegionInfo.RegionSizeX)) | ||
9108 | && (locY >= m_scene.RegionInfo.WorldLocY) | ||
9109 | && (locY < (m_scene.RegionInfo.WorldLocY + m_scene.RegionInfo.RegionSizeY)) ) | ||
9110 | { | ||
9111 | tpLocReq.Info.RegionHandle = m_scene.RegionInfo.RegionHandle; | ||
9112 | tpLocReq.Info.Position.X += locX - m_scene.RegionInfo.WorldLocX; | ||
9113 | tpLocReq.Info.Position.Y += locY - m_scene.RegionInfo.WorldLocY; | ||
9114 | } | ||
9115 | |||
8609 | handlerTeleportLocationRequest(this, tpLocReq.Info.RegionHandle, tpLocReq.Info.Position, | 9116 | handlerTeleportLocationRequest(this, tpLocReq.Info.RegionHandle, tpLocReq.Info.Position, |
8610 | tpLocReq.Info.LookAt, 16); | 9117 | tpLocReq.Info.LookAt, 16); |
8611 | } | 9118 | } |
@@ -9399,6 +9906,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
9399 | } | 9906 | } |
9400 | return true; | 9907 | return true; |
9401 | 9908 | ||
9909 | case "kickestate": | ||
9910 | |||
9911 | if(((Scene)m_scene).Permissions.CanIssueEstateCommand(AgentId, false)) | ||
9912 | { | ||
9913 | UUID invoice = messagePacket.MethodData.Invoice; | ||
9914 | UUID SenderID = messagePacket.AgentData.AgentID; | ||
9915 | UUID Prey; | ||
9916 | |||
9917 | UUID.TryParse(Utils.BytesToString(messagePacket.ParamList[0].Parameter), out Prey); | ||
9918 | |||
9919 | OnEstateTeleportOneUserHomeRequest(this, invoice, SenderID, Prey); | ||
9920 | } | ||
9921 | return true; | ||
9922 | |||
9402 | default: | 9923 | default: |
9403 | m_log.WarnFormat( | 9924 | m_log.WarnFormat( |
9404 | "[LLCLIENTVIEW]: EstateOwnerMessage: Unknown method {0} requested for {1} in {2}", | 9925 | "[LLCLIENTVIEW]: EstateOwnerMessage: Unknown method {0} requested for {1} in {2}", |
@@ -9604,7 +10125,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
9604 | EconomyDataRequest handlerEconomoyDataRequest = OnEconomyDataRequest; | 10125 | EconomyDataRequest handlerEconomoyDataRequest = OnEconomyDataRequest; |
9605 | if (handlerEconomoyDataRequest != null) | 10126 | if (handlerEconomoyDataRequest != null) |
9606 | { | 10127 | { |
9607 | handlerEconomoyDataRequest(AgentId); | 10128 | handlerEconomoyDataRequest(this); |
9608 | } | 10129 | } |
9609 | return true; | 10130 | return true; |
9610 | } | 10131 | } |
@@ -10032,7 +10553,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
10032 | handlerDirFindQuery(this, | 10553 | handlerDirFindQuery(this, |
10033 | dirFindQueryPacket.QueryData.QueryID, | 10554 | dirFindQueryPacket.QueryData.QueryID, |
10034 | Utils.BytesToString( | 10555 | Utils.BytesToString( |
10035 | dirFindQueryPacket.QueryData.QueryText), | 10556 | dirFindQueryPacket.QueryData.QueryText).Trim(), |
10036 | dirFindQueryPacket.QueryData.QueryFlags, | 10557 | dirFindQueryPacket.QueryData.QueryFlags, |
10037 | dirFindQueryPacket.QueryData.QueryStart); | 10558 | dirFindQueryPacket.QueryData.QueryStart); |
10038 | } | 10559 | } |
@@ -11384,8 +11905,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11384 | } | 11905 | } |
11385 | 11906 | ||
11386 | /// <summary> | 11907 | /// <summary> |
11387 | /// Send a response back to a client when it asks the asset server (via the region server) if it has | ||
11388 | /// its appearance texture cached. | ||
11389 | /// </summary> | 11908 | /// </summary> |
11390 | /// <remarks> | 11909 | /// <remarks> |
11391 | /// At the moment, we always reply that there is no cached texture. | 11910 | /// At the moment, we always reply that there is no cached texture. |
@@ -11395,13 +11914,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11395 | /// <returns></returns> | 11914 | /// <returns></returns> |
11396 | protected bool HandleAgentTextureCached(IClientAPI simclient, Packet packet) | 11915 | protected bool HandleAgentTextureCached(IClientAPI simclient, Packet packet) |
11397 | { | 11916 | { |
11398 | //m_log.Debug("texture cached: " + packet.ToString()); | ||
11399 | AgentCachedTexturePacket cachedtex = (AgentCachedTexturePacket)packet; | 11917 | AgentCachedTexturePacket cachedtex = (AgentCachedTexturePacket)packet; |
11400 | AgentCachedTextureResponsePacket cachedresp = (AgentCachedTextureResponsePacket)PacketPool.Instance.GetPacket(PacketType.AgentCachedTextureResponse); | 11918 | AgentCachedTextureResponsePacket cachedresp = (AgentCachedTextureResponsePacket)PacketPool.Instance.GetPacket(PacketType.AgentCachedTextureResponse); |
11401 | 11919 | ||
11402 | if (cachedtex.AgentData.SessionID != SessionId) | 11920 | if (cachedtex.AgentData.SessionID != SessionId) |
11403 | return false; | 11921 | return false; |
11404 | 11922 | ||
11923 | |||
11405 | // TODO: don't create new blocks if recycling an old packet | 11924 | // TODO: don't create new blocks if recycling an old packet |
11406 | cachedresp.AgentData.AgentID = AgentId; | 11925 | cachedresp.AgentData.AgentID = AgentId; |
11407 | cachedresp.AgentData.SessionID = m_sessionId; | 11926 | cachedresp.AgentData.SessionID = m_sessionId; |
@@ -11410,19 +11929,127 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11410 | cachedresp.WearableData = | 11929 | cachedresp.WearableData = |
11411 | new AgentCachedTextureResponsePacket.WearableDataBlock[cachedtex.WearableData.Length]; | 11930 | new AgentCachedTextureResponsePacket.WearableDataBlock[cachedtex.WearableData.Length]; |
11412 | 11931 | ||
11413 | for (int i = 0; i < cachedtex.WearableData.Length; i++) | 11932 | int maxWearablesLoop = cachedtex.WearableData.Length; |
11933 | if (maxWearablesLoop > AvatarWearable.MAX_WEARABLES) | ||
11934 | maxWearablesLoop = AvatarWearable.MAX_WEARABLES; | ||
11935 | |||
11936 | // Find the cached baked textures for this user, if they're available | ||
11937 | |||
11938 | IAssetService cache = m_scene.AssetService; | ||
11939 | IBakedTextureModule bakedTextureModule = m_scene.RequestModuleInterface<IBakedTextureModule>(); | ||
11940 | |||
11941 | WearableCacheItem[] cacheItems = null; | ||
11942 | |||
11943 | if (bakedTextureModule != null && cache != null) | ||
11414 | { | 11944 | { |
11415 | cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock(); | 11945 | ScenePresence p = m_scene.GetScenePresence(AgentId); |
11416 | cachedresp.WearableData[i].TextureIndex = cachedtex.WearableData[i].TextureIndex; | 11946 | if (p.Appearance != null) |
11417 | cachedresp.WearableData[i].TextureID = UUID.Zero; | 11947 | { |
11418 | cachedresp.WearableData[i].HostName = new byte[0]; | 11948 | if (p.Appearance.WearableCacheItems == null || p.Appearance.WearableCacheItemsDirty) |
11949 | { | ||
11950 | try | ||
11951 | { | ||
11952 | cacheItems = bakedTextureModule.Get(AgentId); | ||
11953 | p.Appearance.WearableCacheItems = cacheItems; | ||
11954 | p.Appearance.WearableCacheItemsDirty = false; | ||
11955 | } | ||
11956 | catch (Exception) | ||
11957 | { | ||
11958 | cacheItems = null; | ||
11959 | } | ||
11960 | |||
11961 | } | ||
11962 | else if (p.Appearance.WearableCacheItems != null) | ||
11963 | { | ||
11964 | cacheItems = p.Appearance.WearableCacheItems; | ||
11965 | } | ||
11966 | } | ||
11419 | } | 11967 | } |
11420 | 11968 | ||
11969 | if (cacheItems != null) | ||
11970 | { | ||
11971 | // We need to make sure the asset stored in the bake is available on this server also by its assetid before we map it to a Cacheid. | ||
11972 | // Copy the baked textures to the sim's assets cache (local only). | ||
11973 | foreach (WearableCacheItem item in cacheItems) | ||
11974 | { | ||
11975 | if (cache.GetCached(item.TextureID.ToString()) == null) | ||
11976 | { | ||
11977 | item.TextureAsset.Temporary = true; | ||
11978 | item.TextureAsset.Local = true; | ||
11979 | cache.Store(item.TextureAsset); | ||
11980 | } | ||
11981 | } | ||
11982 | |||
11983 | // Return the cached textures | ||
11984 | for (int i = 0; i < maxWearablesLoop; i++) | ||
11985 | { | ||
11986 | WearableCacheItem item = | ||
11987 | WearableCacheItem.SearchTextureIndex(cachedtex.WearableData[i].TextureIndex, cacheItems); | ||
11988 | |||
11989 | cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock(); | ||
11990 | cachedresp.WearableData[i].TextureIndex = cachedtex.WearableData[i].TextureIndex; | ||
11991 | cachedresp.WearableData[i].HostName = new byte[0]; | ||
11992 | if (item != null && cachedtex.WearableData[i].ID == item.CacheId) | ||
11993 | { | ||
11994 | cachedresp.WearableData[i].TextureID = item.TextureID; | ||
11995 | } | ||
11996 | else | ||
11997 | { | ||
11998 | cachedresp.WearableData[i].TextureID = UUID.Zero; | ||
11999 | } | ||
12000 | } | ||
12001 | } | ||
12002 | else | ||
12003 | { | ||
12004 | // Cached textures not available | ||
12005 | for (int i = 0; i < maxWearablesLoop; i++) | ||
12006 | { | ||
12007 | cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock(); | ||
12008 | cachedresp.WearableData[i].TextureIndex = cachedtex.WearableData[i].TextureIndex; | ||
12009 | cachedresp.WearableData[i].TextureID = UUID.Zero; | ||
12010 | cachedresp.WearableData[i].HostName = new byte[0]; | ||
12011 | } | ||
12012 | } | ||
12013 | |||
11421 | cachedresp.Header.Zerocoded = true; | 12014 | cachedresp.Header.Zerocoded = true; |
11422 | OutPacket(cachedresp, ThrottleOutPacketType.Task); | 12015 | OutPacket(cachedresp, ThrottleOutPacketType.Task); |
11423 | 12016 | ||
11424 | return true; | 12017 | return true; |
11425 | } | 12018 | } |
12019 | |||
12020 | /// <summary> | ||
12021 | /// Send a response back to a client when it asks the asset server (via the region server) if it has | ||
12022 | /// its appearance texture cached. | ||
12023 | /// </summary> | ||
12024 | /// <param name="avatar"></param> | ||
12025 | /// <param name="serial"></param> | ||
12026 | /// <param name="cachedTextures"></param> | ||
12027 | /// <returns></returns> | ||
12028 | public void SendCachedTextureResponse(ISceneEntity avatar, int serial, List<CachedTextureResponseArg> cachedTextures) | ||
12029 | { | ||
12030 | ScenePresence presence = avatar as ScenePresence; | ||
12031 | if (presence == null) | ||
12032 | return; | ||
12033 | |||
12034 | AgentCachedTextureResponsePacket cachedresp = (AgentCachedTextureResponsePacket)PacketPool.Instance.GetPacket(PacketType.AgentCachedTextureResponse); | ||
12035 | |||
12036 | // TODO: don't create new blocks if recycling an old packet | ||
12037 | cachedresp.AgentData.AgentID = m_agentId; | ||
12038 | cachedresp.AgentData.SessionID = m_sessionId; | ||
12039 | cachedresp.AgentData.SerialNum = serial; | ||
12040 | cachedresp.WearableData = new AgentCachedTextureResponsePacket.WearableDataBlock[cachedTextures.Count]; | ||
12041 | |||
12042 | for (int i = 0; i < cachedTextures.Count; i++) | ||
12043 | { | ||
12044 | cachedresp.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock(); | ||
12045 | cachedresp.WearableData[i].TextureIndex = (byte)cachedTextures[i].BakedTextureIndex; | ||
12046 | cachedresp.WearableData[i].TextureID = cachedTextures[i].BakedTextureID; | ||
12047 | cachedresp.WearableData[i].HostName = new byte[0]; | ||
12048 | } | ||
12049 | |||
12050 | cachedresp.Header.Zerocoded = true; | ||
12051 | OutPacket(cachedresp, ThrottleOutPacketType.Task); | ||
12052 | } | ||
11426 | 12053 | ||
11427 | protected bool HandleMultipleObjUpdate(IClientAPI simClient, Packet packet) | 12054 | protected bool HandleMultipleObjUpdate(IClientAPI simClient, Packet packet) |
11428 | { | 12055 | { |
@@ -11449,8 +12076,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11449 | if (part == null) | 12076 | if (part == null) |
11450 | { | 12077 | { |
11451 | // It's a ghost! tell the client to delete it from view. | 12078 | // It's a ghost! tell the client to delete it from view. |
11452 | simClient.SendKillObject(Scene.RegionInfo.RegionHandle, | 12079 | simClient.SendKillObject(new List<uint> { localId }); |
11453 | new List<uint> { localId }); | ||
11454 | } | 12080 | } |
11455 | else | 12081 | else |
11456 | { | 12082 | { |
@@ -11777,6 +12403,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11777 | /// provide your own method.</param> | 12403 | /// provide your own method.</param> |
11778 | protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting, UnackedPacketMethod method) | 12404 | protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting, UnackedPacketMethod method) |
11779 | { | 12405 | { |
12406 | if (m_outPacketsToDrop != null) | ||
12407 | if (m_outPacketsToDrop.Contains(packet.Type.ToString())) | ||
12408 | return; | ||
12409 | |||
11780 | if (DebugPacketLevel > 0) | 12410 | if (DebugPacketLevel > 0) |
11781 | { | 12411 | { |
11782 | bool logPacket = true; | 12412 | bool logPacket = true; |
@@ -11811,17 +12441,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11811 | m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting, method); | 12441 | m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting, method); |
11812 | } | 12442 | } |
11813 | 12443 | ||
11814 | public bool AddMoney(int debit) | ||
11815 | { | ||
11816 | if (m_moneyBalance + debit >= 0) | ||
11817 | { | ||
11818 | m_moneyBalance += debit; | ||
11819 | SendMoneyBalance(UUID.Zero, true, Util.StringToBytes256("Poof Poof!"), m_moneyBalance); | ||
11820 | return true; | ||
11821 | } | ||
11822 | return false; | ||
11823 | } | ||
11824 | |||
11825 | protected void HandleAutopilot(Object sender, string method, List<String> args) | 12444 | protected void HandleAutopilot(Object sender, string method, List<String> args) |
11826 | { | 12445 | { |
11827 | float locx = 0; | 12446 | float locx = 0; |
@@ -11846,6 +12465,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11846 | /// <param name="Pack">OpenMetaverse.packet</param> | 12465 | /// <param name="Pack">OpenMetaverse.packet</param> |
11847 | public void ProcessInPacket(Packet packet) | 12466 | public void ProcessInPacket(Packet packet) |
11848 | { | 12467 | { |
12468 | if (m_inPacketsToDrop != null) | ||
12469 | if (m_inPacketsToDrop.Contains(packet.Type.ToString())) | ||
12470 | return; | ||
12471 | |||
11849 | if (DebugPacketLevel > 0) | 12472 | if (DebugPacketLevel > 0) |
11850 | { | 12473 | { |
11851 | bool logPacket = true; | 12474 | bool logPacket = true; |
@@ -11880,6 +12503,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11880 | 12503 | ||
11881 | shape.PCode = addPacket.ObjectData.PCode; | 12504 | shape.PCode = addPacket.ObjectData.PCode; |
11882 | shape.State = addPacket.ObjectData.State; | 12505 | shape.State = addPacket.ObjectData.State; |
12506 | shape.LastAttachPoint = addPacket.ObjectData.State; | ||
11883 | shape.PathBegin = addPacket.ObjectData.PathBegin; | 12507 | shape.PathBegin = addPacket.ObjectData.PathBegin; |
11884 | shape.PathEnd = addPacket.ObjectData.PathEnd; | 12508 | shape.PathEnd = addPacket.ObjectData.PathEnd; |
11885 | shape.PathScaleX = addPacket.ObjectData.PathScaleX; | 12509 | shape.PathScaleX = addPacket.ObjectData.PathScaleX; |
@@ -11910,7 +12534,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11910 | ClientInfo info = m_udpClient.GetClientInfo(); | 12534 | ClientInfo info = m_udpClient.GetClientInfo(); |
11911 | 12535 | ||
11912 | info.proxyEP = null; | 12536 | info.proxyEP = null; |
11913 | info.agentcircuit = RequestClientInfo(); | 12537 | if (info.agentcircuit == null) |
12538 | info.agentcircuit = RequestClientInfo(); | ||
11914 | 12539 | ||
11915 | return info; | 12540 | return info; |
11916 | } | 12541 | } |
@@ -12079,6 +12704,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12079 | return String.Empty; | 12704 | return String.Empty; |
12080 | } | 12705 | } |
12081 | 12706 | ||
12707 | public OSDMap OReport(string uptime, string version) | ||
12708 | { | ||
12709 | return new OSDMap(); | ||
12710 | } | ||
12711 | |||
12082 | /// <summary> | 12712 | /// <summary> |
12083 | /// Make an asset request to the asset service in response to a client request. | 12713 | /// Make an asset request to the asset service in response to a client request. |
12084 | /// </summary> | 12714 | /// </summary> |
@@ -12126,16 +12756,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12126 | 12756 | ||
12127 | if (asset == null) | 12757 | if (asset == null) |
12128 | { | 12758 | { |
12129 | req.AssetInf = null; | 12759 | // Try the user's asset server |
12130 | req.AssetRequestSource = source; | 12760 | IInventoryAccessModule inventoryAccessModule = Scene.RequestModuleInterface<IInventoryAccessModule>(); |
12131 | req.IsTextureRequest = false; | 12761 | |
12132 | req.NumPackets = 0; | 12762 | string assetServerURL = string.Empty; |
12133 | req.Params = transferRequest.TransferInfo.Params; | 12763 | if (inventoryAccessModule.IsForeignUser(AgentId, out assetServerURL) && !string.IsNullOrEmpty(assetServerURL)) |
12134 | req.RequestAssetID = requestID; | 12764 | { |
12135 | req.TransferRequestID = transferRequest.TransferInfo.TransferID; | 12765 | if (!assetServerURL.EndsWith("/") && !assetServerURL.EndsWith("=")) |
12766 | assetServerURL = assetServerURL + "/"; | ||
12767 | |||
12768 | //m_log.DebugFormat("[LLCLIENTVIEW]: asset {0} not found in local storage. Trying user's storage.", assetServerURL + id); | ||
12769 | asset = m_scene.AssetService.Get(assetServerURL + id); | ||
12770 | } | ||
12771 | |||
12772 | if (asset == null) | ||
12773 | { | ||
12774 | req.AssetInf = null; | ||
12775 | req.AssetRequestSource = source; | ||
12776 | req.IsTextureRequest = false; | ||
12777 | req.NumPackets = 0; | ||
12778 | req.Params = transferRequest.TransferInfo.Params; | ||
12779 | req.RequestAssetID = requestID; | ||
12780 | req.TransferRequestID = transferRequest.TransferInfo.TransferID; | ||
12781 | |||
12782 | SendAssetNotFound(req); | ||
12783 | return; | ||
12784 | } | ||
12136 | 12785 | ||
12137 | SendAssetNotFound(req); | ||
12138 | return; | ||
12139 | } | 12786 | } |
12140 | 12787 | ||
12141 | if (transferRequest.TransferInfo.SourceType == (int)SourceType.Asset) | 12788 | if (transferRequest.TransferInfo.SourceType == (int)SourceType.Asset) |
@@ -12201,8 +12848,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12201 | 12848 | ||
12202 | public struct PacketProcessor | 12849 | public struct PacketProcessor |
12203 | { | 12850 | { |
12204 | public PacketMethod method; | 12851 | /// <summary> |
12205 | public bool Async; | 12852 | /// Packet handling method. |
12853 | /// </summary> | ||
12854 | public PacketMethod method { get; set; } | ||
12855 | |||
12856 | /// <summary> | ||
12857 | /// Should this packet be handled asynchronously? | ||
12858 | /// </summary> | ||
12859 | public bool Async { get; set; } | ||
12860 | |||
12861 | /// <summary> | ||
12862 | /// If async is true, should this packet be handled in the async engine or given directly to a threadpool | ||
12863 | /// thread? | ||
12864 | /// </summary> | ||
12865 | public bool InEngine { get; set; } | ||
12206 | } | 12866 | } |
12207 | 12867 | ||
12208 | public class AsyncPacketProcess | 12868 | public class AsyncPacketProcess |
@@ -12284,11 +12944,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12284 | OutPacket(dialog, ThrottleOutPacketType.Task); | 12944 | OutPacket(dialog, ThrottleOutPacketType.Task); |
12285 | } | 12945 | } |
12286 | 12946 | ||
12287 | public void StopFlying(ISceneEntity p) | 12947 | public void SendAgentTerseUpdate(ISceneEntity p) |
12288 | { | 12948 | { |
12289 | if (p is ScenePresence) | 12949 | if (p is ScenePresence) |
12290 | { | 12950 | { |
12291 | ScenePresence presence = p as ScenePresence; | 12951 | // m_log.DebugFormat( |
12952 | // "[LLCLIENTVIEW]: Immediately sending terse agent update for {0} to {1} in {2}", | ||
12953 | // p.Name, Name, Scene.Name); | ||
12954 | |||
12292 | // It turns out to get the agent to stop flying, you have to feed it stop flying velocities | 12955 | // It turns out to get the agent to stop flying, you have to feed it stop flying velocities |
12293 | // There's no explicit message to send the client to tell it to stop flying.. it relies on the | 12956 | // There's no explicit message to send the client to tell it to stop flying.. it relies on the |
12294 | // velocity, collision plane and avatar height | 12957 | // velocity, collision plane and avatar height |
@@ -12296,34 +12959,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12296 | // Add 1/6 the avatar's height to it's position so it doesn't shoot into the air | 12959 | // Add 1/6 the avatar's height to it's position so it doesn't shoot into the air |
12297 | // when the avatar stands up | 12960 | // when the avatar stands up |
12298 | 12961 | ||
12299 | Vector3 pos = presence.AbsolutePosition; | ||
12300 | |||
12301 | if (presence.Appearance.AvatarHeight != 127.0f) | ||
12302 | pos += new Vector3(0f, 0f, (presence.Appearance.AvatarHeight/6f)); | ||
12303 | else | ||
12304 | pos += new Vector3(0f, 0f, (1.56f/6f)); | ||
12305 | |||
12306 | presence.AbsolutePosition = pos; | ||
12307 | |||
12308 | // attach a suitable collision plane regardless of the actual situation to force the LLClient to land. | ||
12309 | // Collision plane below the avatar's position a 6th of the avatar's height is suitable. | ||
12310 | // Mind you, that this method doesn't get called if the avatar's velocity magnitude is greater then a | ||
12311 | // certain amount.. because the LLClient wouldn't land in that situation anyway. | ||
12312 | |||
12313 | // why are we still testing for this really old height value default??? | ||
12314 | if (presence.Appearance.AvatarHeight != 127.0f) | ||
12315 | presence.CollisionPlane = new Vector4(0, 0, 0, pos.Z - presence.Appearance.AvatarHeight/6f); | ||
12316 | else | ||
12317 | presence.CollisionPlane = new Vector4(0, 0, 0, pos.Z - (1.56f/6f)); | ||
12318 | |||
12319 | |||
12320 | ImprovedTerseObjectUpdatePacket.ObjectDataBlock block = | 12962 | ImprovedTerseObjectUpdatePacket.ObjectDataBlock block = |
12321 | CreateImprovedTerseBlock(p, false); | 12963 | CreateImprovedTerseBlock(p, false); |
12322 | 12964 | ||
12323 | const float TIME_DILATION = 1.0f; | 12965 | const float TIME_DILATION = 1.0f; |
12324 | ushort timeDilation = Utils.FloatToUInt16(TIME_DILATION, 0.0f, 1.0f); | 12966 | ushort timeDilation = Utils.FloatToUInt16(TIME_DILATION, 0.0f, 1.0f); |
12325 | 12967 | ||
12326 | |||
12327 | ImprovedTerseObjectUpdatePacket packet | 12968 | ImprovedTerseObjectUpdatePacket packet |
12328 | = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket( | 12969 | = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket( |
12329 | PacketType.ImprovedTerseObjectUpdate); | 12970 | PacketType.ImprovedTerseObjectUpdate); |
@@ -12565,5 +13206,51 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12565 | eq.Enqueue(BuildEvent("BulkUpdateInventory", | 13206 | eq.Enqueue(BuildEvent("BulkUpdateInventory", |
12566 | llsd), AgentId); | 13207 | llsd), AgentId); |
12567 | } | 13208 | } |
13209 | |||
13210 | private HashSet<string> m_outPacketsToDrop; | ||
13211 | |||
13212 | public bool AddOutPacketToDropSet(string packetName) | ||
13213 | { | ||
13214 | if (m_outPacketsToDrop == null) | ||
13215 | m_outPacketsToDrop = new HashSet<string>(); | ||
13216 | |||
13217 | return m_outPacketsToDrop.Add(packetName); | ||
13218 | } | ||
13219 | |||
13220 | public bool RemoveOutPacketFromDropSet(string packetName) | ||
13221 | { | ||
13222 | if (m_outPacketsToDrop == null) | ||
13223 | return false; | ||
13224 | |||
13225 | return m_outPacketsToDrop.Remove(packetName); | ||
13226 | } | ||
13227 | |||
13228 | public HashSet<string> GetOutPacketDropSet() | ||
13229 | { | ||
13230 | return new HashSet<string>(m_outPacketsToDrop); | ||
13231 | } | ||
13232 | |||
13233 | private HashSet<string> m_inPacketsToDrop; | ||
13234 | |||
13235 | public bool AddInPacketToDropSet(string packetName) | ||
13236 | { | ||
13237 | if (m_inPacketsToDrop == null) | ||
13238 | m_inPacketsToDrop = new HashSet<string>(); | ||
13239 | |||
13240 | return m_inPacketsToDrop.Add(packetName); | ||
13241 | } | ||
13242 | |||
13243 | public bool RemoveInPacketFromDropSet(string packetName) | ||
13244 | { | ||
13245 | if (m_inPacketsToDrop == null) | ||
13246 | return false; | ||
13247 | |||
13248 | return m_inPacketsToDrop.Remove(packetName); | ||
13249 | } | ||
13250 | |||
13251 | public HashSet<string> GetInPacketDropSet() | ||
13252 | { | ||
13253 | return new HashSet<string>(m_inPacketsToDrop); | ||
13254 | } | ||
12568 | } | 13255 | } |
12569 | } | 13256 | } |
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 8963756..0394e54 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 | ||
@@ -75,12 +76,41 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
75 | /// or removed, this number must also change</summary> | 76 | /// or removed, this number must also change</summary> |
76 | const int THROTTLE_CATEGORY_COUNT = 8; | 77 | const int THROTTLE_CATEGORY_COUNT = 8; |
77 | 78 | ||
79 | /// <summary> | ||
80 | /// Controls whether information is logged about each outbound packet immediately before it is sent. For debug purposes. | ||
81 | /// </summary> | ||
82 | /// <remarks>Any level above 0 will turn on logging.</remarks> | ||
83 | public int DebugDataOutLevel { get; set; } | ||
84 | |||
85 | /// <summary> | ||
86 | /// Controls whether information is logged about each outbound packet immediately before it is sent. For debug purposes. | ||
87 | /// </summary> | ||
88 | /// <remarks>Any level above 0 will turn on logging.</remarks> | ||
89 | public int ThrottleDebugLevel | ||
90 | { | ||
91 | get | ||
92 | { | ||
93 | return m_throttleDebugLevel; | ||
94 | } | ||
95 | |||
96 | set | ||
97 | { | ||
98 | m_throttleDebugLevel = value; | ||
99 | m_throttleClient.DebugLevel = m_throttleDebugLevel; | ||
100 | foreach (TokenBucket tb in m_throttleCategories) | ||
101 | tb.DebugLevel = m_throttleDebugLevel; | ||
102 | } | ||
103 | } | ||
104 | private int m_throttleDebugLevel; | ||
105 | |||
78 | /// <summary>Fired when updated networking stats are produced for this client</summary> | 106 | /// <summary>Fired when updated networking stats are produced for this client</summary> |
79 | public event PacketStats OnPacketStats; | 107 | public event PacketStats OnPacketStats; |
80 | /// <summary>Fired when the queue for a packet category is empty. This event can be | 108 | /// <summary>Fired when the queue for a packet category is empty. This event can be |
81 | /// hooked to put more data on the empty queue</summary> | 109 | /// hooked to put more data on the empty queue</summary> |
82 | public event QueueEmpty OnQueueEmpty; | 110 | public event QueueEmpty OnQueueEmpty; |
83 | 111 | ||
112 | public event Func<ThrottleOutPacketTypeFlags, bool> HasUpdates; | ||
113 | |||
84 | /// <summary>AgentID for this client</summary> | 114 | /// <summary>AgentID for this client</summary> |
85 | public readonly UUID AgentID; | 115 | public readonly UUID AgentID; |
86 | /// <summary>The remote address of the connected client</summary> | 116 | /// <summary>The remote address of the connected client</summary> |
@@ -89,8 +119,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
89 | public readonly uint CircuitCode; | 119 | public readonly uint CircuitCode; |
90 | /// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary> | 120 | /// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary> |
91 | public readonly IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200); | 121 | public readonly IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200); |
122 | |||
123 | /// <summary> | ||
124 | /// If true then we take action in response to unacked reliably sent packets such as resending the packet. | ||
125 | /// </summary> | ||
126 | public bool ProcessUnackedSends { get; set; } | ||
127 | |||
92 | /// <summary>Packets we have sent that need to be ACKed by the client</summary> | 128 | /// <summary>Packets we have sent that need to be ACKed by the client</summary> |
93 | public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); | 129 | public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); |
130 | |||
94 | /// <summary>ACKs that are queued up, waiting to be sent to the client</summary> | 131 | /// <summary>ACKs that are queued up, waiting to be sent to the client</summary> |
95 | public readonly OpenSim.Framework.LocklessQueue<uint> PendingAcks = new OpenSim.Framework.LocklessQueue<uint>(); | 132 | public readonly OpenSim.Framework.LocklessQueue<uint> PendingAcks = new OpenSim.Framework.LocklessQueue<uint>(); |
96 | 133 | ||
@@ -141,8 +178,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
141 | get { return m_throttleClient; } | 178 | get { return m_throttleClient; } |
142 | } | 179 | } |
143 | 180 | ||
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> | 181 | /// <summary>Throttle buckets for each packet category</summary> |
147 | private readonly TokenBucket[] m_throttleCategories; | 182 | private readonly TokenBucket[] m_throttleCategories; |
148 | /// <summary>Outgoing queues for throttled packets</summary> | 183 | /// <summary>Outgoing queues for throttled packets</summary> |
@@ -160,6 +195,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
160 | private int m_maxRTO = 60000; | 195 | private int m_maxRTO = 60000; |
161 | 196 | ||
162 | /// <summary> | 197 | /// <summary> |
198 | /// This is the percentage of the udp texture queue to add to the task queue since | ||
199 | /// textures are now generally handled through http. | ||
200 | /// </summary> | ||
201 | private double m_cannibalrate = 0.0; | ||
202 | |||
203 | private ClientInfo m_info = new ClientInfo(); | ||
204 | |||
205 | /// <summary> | ||
163 | /// Default constructor | 206 | /// Default constructor |
164 | /// </summary> | 207 | /// </summary> |
165 | /// <param name="server">Reference to the UDP server this client is connected to</param> | 208 | /// <param name="server">Reference to the UDP server this client is connected to</param> |
@@ -189,21 +232,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
189 | if (maxRTO != 0) | 232 | if (maxRTO != 0) |
190 | m_maxRTO = maxRTO; | 233 | m_maxRTO = maxRTO; |
191 | 234 | ||
235 | ProcessUnackedSends = true; | ||
236 | |||
192 | // Create a token bucket throttle for this client that has the scene token bucket as a parent | 237 | // Create a token bucket throttle for this client that has the scene token bucket as a parent |
193 | m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.Total, rates.AdaptiveThrottlesEnabled); | 238 | m_throttleClient |
194 | // Create a token bucket throttle for the total categary with the client bucket as a throttle | 239 | = new AdaptiveTokenBucket( |
195 | m_throttleCategory = new TokenBucket(m_throttleClient, 0); | 240 | string.Format("adaptive throttle for {0} in {1}", AgentID, server.Scene.Name), |
241 | parentThrottle, 0, rates.Total, rates.MinimumAdaptiveThrottleRate, rates.AdaptiveThrottlesEnabled); | ||
242 | |||
196 | // Create an array of token buckets for this clients different throttle categories | 243 | // Create an array of token buckets for this clients different throttle categories |
197 | m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; | 244 | m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; |
198 | 245 | ||
246 | m_cannibalrate = rates.CannibalizeTextureRate; | ||
247 | |||
199 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) | 248 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) |
200 | { | 249 | { |
201 | ThrottleOutPacketType type = (ThrottleOutPacketType)i; | 250 | ThrottleOutPacketType type = (ThrottleOutPacketType)i; |
202 | 251 | ||
203 | // Initialize the packet outboxes, where packets sit while they are waiting for tokens | 252 | // Initialize the packet outboxes, where packets sit while they are waiting for tokens |
204 | m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); | 253 | m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); |
254 | |||
205 | // Initialize the token buckets that control the throttling for each category | 255 | // Initialize the token buckets that control the throttling for each category |
206 | m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetRate(type)); | 256 | m_throttleCategories[i] |
257 | = new TokenBucket( | ||
258 | string.Format("{0} throttle for {1} in {2}", type, AgentID, server.Scene.Name), | ||
259 | m_throttleClient, rates.GetRate(type), 0); | ||
207 | } | 260 | } |
208 | 261 | ||
209 | // Default the retransmission timeout to one second | 262 | // Default the retransmission timeout to one second |
@@ -240,20 +293,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
240 | // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists | 293 | // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists |
241 | // of pending and needed ACKs for every client every time some method wants information about | 294 | // of pending and needed ACKs for every client every time some method wants information about |
242 | // this connection is a recipe for poor performance | 295 | // this connection is a recipe for poor performance |
243 | ClientInfo info = new ClientInfo(); | 296 | |
244 | info.pendingAcks = new Dictionary<uint, uint>(); | 297 | m_info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; |
245 | info.needAck = new Dictionary<uint, byte[]>(); | 298 | m_info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; |
246 | 299 | m_info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; | |
247 | info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; | 300 | m_info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; |
248 | info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; | 301 | m_info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; |
249 | info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; | 302 | m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; |
250 | info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; | 303 | m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; |
251 | info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; | 304 | m_info.totalThrottle = (int)m_throttleClient.DripRate; |
252 | info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; | 305 | m_info.targetThrottle = (int)m_throttleClient.TargetDripRate; |
253 | info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; | 306 | m_info.maxThrottle = (int)m_throttleClient.MaxDripRate; |
254 | info.totalThrottle = (int)m_throttleCategory.DripRate; | 307 | |
255 | 308 | return m_info; | |
256 | return info; | ||
257 | } | 309 | } |
258 | 310 | ||
259 | /// <summary> | 311 | /// <summary> |
@@ -269,6 +321,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
269 | } | 321 | } |
270 | 322 | ||
271 | /// <summary> | 323 | /// <summary> |
324 | /// Get the total number of pakcets queued for this client. | ||
325 | /// </summary> | ||
326 | /// <returns></returns> | ||
327 | public int GetTotalPacketsQueuedCount() | ||
328 | { | ||
329 | int total = 0; | ||
330 | |||
331 | for (int i = 0; i <= (int)ThrottleOutPacketType.Asset; i++) | ||
332 | total += m_packetOutboxes[i].Count; | ||
333 | |||
334 | return total; | ||
335 | } | ||
336 | |||
337 | /// <summary> | ||
338 | /// Get the number of packets queued for the given throttle type. | ||
339 | /// </summary> | ||
340 | /// <returns></returns> | ||
341 | /// <param name="throttleType"></param> | ||
342 | public int GetPacketsQueuedCount(ThrottleOutPacketType throttleType) | ||
343 | { | ||
344 | if ((int)throttleType > 0) | ||
345 | return m_packetOutboxes[(int)throttleType].Count; | ||
346 | else | ||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | /// <summary> | ||
272 | /// Return statistics information about client packet queues. | 351 | /// Return statistics information about client packet queues. |
273 | /// </summary> | 352 | /// </summary> |
274 | /// <remarks> | 353 | /// <remarks> |
@@ -278,7 +357,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
278 | public string GetStats() | 357 | public string GetStats() |
279 | { | 358 | { |
280 | return string.Format( | 359 | return string.Format( |
281 | "{0,7} {1,7} {2,7} {3,9} {4,7} {5,7} {6,7} {7,7} {8,7} {9,8} {10,7} {11,7} {12,7}", | 360 | "{0,7} {1,7} {2,7} {3,9} {4,7} {5,7} {6,7} {7,7} {8,7} {9,8} {10,7} {11,7}", |
282 | Util.EnvironmentTickCountSubtract(TickLastPacketReceived), | 361 | Util.EnvironmentTickCountSubtract(TickLastPacketReceived), |
283 | PacketsReceived, | 362 | PacketsReceived, |
284 | PacketsSent, | 363 | PacketsSent, |
@@ -290,8 +369,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
290 | m_packetOutboxes[(int)ThrottleOutPacketType.Cloud].Count, | 369 | m_packetOutboxes[(int)ThrottleOutPacketType.Cloud].Count, |
291 | m_packetOutboxes[(int)ThrottleOutPacketType.Task].Count, | 370 | m_packetOutboxes[(int)ThrottleOutPacketType.Task].Count, |
292 | m_packetOutboxes[(int)ThrottleOutPacketType.Texture].Count, | 371 | m_packetOutboxes[(int)ThrottleOutPacketType.Texture].Count, |
293 | m_packetOutboxes[(int)ThrottleOutPacketType.Asset].Count, | 372 | m_packetOutboxes[(int)ThrottleOutPacketType.Asset].Count); |
294 | m_packetOutboxes[(int)ThrottleOutPacketType.State].Count); | ||
295 | } | 373 | } |
296 | 374 | ||
297 | public void SendPacketStats() | 375 | public void SendPacketStats() |
@@ -337,8 +415,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
337 | int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; | 415 | int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; |
338 | int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; | 416 | int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; |
339 | int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | 417 | int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); |
340 | // State is a subcategory of task that we allocate a percentage to | 418 | |
341 | int state = 0; | 419 | if (ThrottleDebugLevel > 0) |
420 | { | ||
421 | long total = resend + land + wind + cloud + task + texture + asset; | ||
422 | m_log.DebugFormat( | ||
423 | "[LLUDPCLIENT]: {0} is setting throttles in {1} to Resend={2}, Land={3}, Wind={4}, Cloud={5}, Task={6}, Texture={7}, Asset={8}, TOTAL = {9}", | ||
424 | AgentID, m_udpServer.Scene.Name, resend, land, wind, cloud, task, texture, asset, total); | ||
425 | } | ||
342 | 426 | ||
343 | // Make sure none of the throttles are set below our packet MTU, | 427 | // Make sure none of the throttles are set below our packet MTU, |
344 | // otherwise a throttle could become permanently clogged | 428 | // otherwise a throttle could become permanently clogged |
@@ -350,11 +434,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
350 | texture = Math.Max(texture, LLUDPServer.MTU); | 434 | texture = Math.Max(texture, LLUDPServer.MTU); |
351 | asset = Math.Max(asset, LLUDPServer.MTU); | 435 | asset = Math.Max(asset, LLUDPServer.MTU); |
352 | 436 | ||
437 | // Since most textures are now delivered through http, make it possible | ||
438 | // to cannibalize some of the bw from the texture throttle to use for | ||
439 | // the task queue (e.g. object updates) | ||
440 | task = task + (int)(m_cannibalrate * texture); | ||
441 | texture = (int)((1 - m_cannibalrate) * texture); | ||
442 | |||
353 | //int total = resend + land + wind + cloud + task + texture + asset; | 443 | //int total = resend + land + wind + cloud + task + texture + asset; |
354 | //m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, Total={8}", | 444 | |
355 | // AgentID, resend, land, wind, cloud, task, texture, asset, total); | 445 | if (ThrottleDebugLevel > 0) |
446 | { | ||
447 | long total = resend + land + wind + cloud + task + texture + asset; | ||
448 | m_log.DebugFormat( | ||
449 | "[LLUDPCLIENT]: {0} is setting throttles in {1} to Resend={2}, Land={3}, Wind={4}, Cloud={5}, Task={6}, Texture={7}, Asset={8}, TOTAL = {9}", | ||
450 | AgentID, m_udpServer.Scene.Name, resend, land, wind, cloud, task, texture, asset, total); | ||
451 | } | ||
356 | 452 | ||
357 | // Update the token buckets with new throttle values | 453 | // Update the token buckets with new throttle values |
454 | if (m_throttleClient.AdaptiveEnabled) | ||
455 | { | ||
456 | long total = resend + land + wind + cloud + task + texture + asset; | ||
457 | m_throttleClient.TargetDripRate = total; | ||
458 | } | ||
459 | |||
358 | TokenBucket bucket; | 460 | TokenBucket bucket; |
359 | 461 | ||
360 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; | 462 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; |
@@ -375,9 +477,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
375 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; | 477 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; |
376 | bucket.RequestedDripRate = task; | 478 | bucket.RequestedDripRate = task; |
377 | 479 | ||
378 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; | ||
379 | bucket.RequestedDripRate = state; | ||
380 | |||
381 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; | 480 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; |
382 | bucket.RequestedDripRate = texture; | 481 | bucket.RequestedDripRate = texture; |
383 | 482 | ||
@@ -620,15 +719,45 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
620 | /// <param name="categories">Throttle categories to fire the callback for</param> | 719 | /// <param name="categories">Throttle categories to fire the callback for</param> |
621 | private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) | 720 | private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) |
622 | { | 721 | { |
623 | if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) | 722 | // if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) |
723 | if (!m_isQueueEmptyRunning && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) | ||
624 | { | 724 | { |
725 | m_isQueueEmptyRunning = true; | ||
726 | |||
727 | int start = Environment.TickCount & Int32.MaxValue; | ||
728 | const int MIN_CALLBACK_MS = 30; | ||
729 | |||
730 | m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; | ||
731 | if (m_nextOnQueueEmpty == 0) | ||
732 | m_nextOnQueueEmpty = 1; | ||
733 | |||
625 | // Use a value of 0 to signal that FireQueueEmpty is running | 734 | // Use a value of 0 to signal that FireQueueEmpty is running |
626 | m_nextOnQueueEmpty = 0; | 735 | // m_nextOnQueueEmpty = 0; |
627 | // Asynchronously run the callback | 736 | |
628 | Util.FireAndForget(FireQueueEmpty, categories); | 737 | m_categories = categories; |
738 | |||
739 | if (HasUpdates(m_categories)) | ||
740 | { | ||
741 | if (!m_udpServer.OqrEngine.IsRunning) | ||
742 | { | ||
743 | // Asynchronously run the callback | ||
744 | Util.FireAndForget(FireQueueEmpty, categories, "LLUDPClient.BeginFireQueueEmpty"); | ||
745 | } | ||
746 | else | ||
747 | { | ||
748 | m_udpServer.OqrEngine.QueueJob(AgentID.ToString(), () => FireQueueEmpty(categories)); | ||
749 | } | ||
750 | } | ||
751 | else | ||
752 | { | ||
753 | m_isQueueEmptyRunning = false; | ||
754 | } | ||
629 | } | 755 | } |
630 | } | 756 | } |
631 | 757 | ||
758 | private bool m_isQueueEmptyRunning; | ||
759 | private ThrottleOutPacketTypeFlags m_categories = 0; | ||
760 | |||
632 | /// <summary> | 761 | /// <summary> |
633 | /// Fires the OnQueueEmpty callback and sets the minimum time that it | 762 | /// Fires the OnQueueEmpty callback and sets the minimum time that it |
634 | /// can be called again | 763 | /// can be called again |
@@ -636,24 +765,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
636 | /// <param name="o">Throttle categories to fire the callback for, | 765 | /// <param name="o">Throttle categories to fire the callback for, |
637 | /// stored as an object to match the WaitCallback delegate | 766 | /// stored as an object to match the WaitCallback delegate |
638 | /// signature</param> | 767 | /// signature</param> |
639 | private void FireQueueEmpty(object o) | 768 | public void FireQueueEmpty(object o) |
640 | { | 769 | { |
641 | const int MIN_CALLBACK_MS = 30; | 770 | // m_log.DebugFormat("[LLUDPCLIENT]: FireQueueEmpty for {0} in {1}", AgentID, m_udpServer.Scene.Name); |
642 | 771 | ||
643 | ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o; | 772 | // int start = Environment.TickCount & Int32.MaxValue; |
644 | QueueEmpty callback = OnQueueEmpty; | 773 | // const int MIN_CALLBACK_MS = 30; |
645 | |||
646 | int start = Environment.TickCount & Int32.MaxValue; | ||
647 | 774 | ||
648 | if (callback != null) | 775 | // if (m_udpServer.IsRunningOutbound) |
649 | { | 776 | // { |
650 | try { callback(categories); } | 777 | ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o; |
651 | catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); } | 778 | QueueEmpty callback = OnQueueEmpty; |
652 | } | 779 | |
780 | if (callback != null) | ||
781 | { | ||
782 | // if (m_udpServer.IsRunningOutbound) | ||
783 | // { | ||
784 | try { callback(categories); } | ||
785 | catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); } | ||
786 | // } | ||
787 | } | ||
788 | // } | ||
789 | |||
790 | // m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; | ||
791 | // if (m_nextOnQueueEmpty == 0) | ||
792 | // m_nextOnQueueEmpty = 1; | ||
793 | |||
794 | // } | ||
653 | 795 | ||
654 | m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; | 796 | m_isQueueEmptyRunning = false; |
655 | if (m_nextOnQueueEmpty == 0) | ||
656 | m_nextOnQueueEmpty = 1; | ||
657 | } | 797 | } |
658 | 798 | ||
659 | /// <summary> | 799 | /// <summary> |
@@ -678,9 +818,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
678 | Texture = 5, | 818 | Texture = 5, |
679 | /// <summary>Non-texture assets</summary> | 819 | /// <summary>Non-texture assets</summary> |
680 | Asset = 6, | 820 | Asset = 6, |
681 | /// <summary>Avatar and primitive data</summary> | ||
682 | /// <remarks>This is a sub-category of Task</remarks> | ||
683 | State = 7, | ||
684 | */ | 821 | */ |
685 | 822 | ||
686 | switch (category) | 823 | switch (category) |
@@ -697,11 +834,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
697 | return ThrottleOutPacketTypeFlags.Texture; | 834 | return ThrottleOutPacketTypeFlags.Texture; |
698 | case ThrottleOutPacketType.Asset: | 835 | case ThrottleOutPacketType.Asset: |
699 | return ThrottleOutPacketTypeFlags.Asset; | 836 | return ThrottleOutPacketTypeFlags.Asset; |
700 | case ThrottleOutPacketType.State: | ||
701 | return ThrottleOutPacketTypeFlags.State; | ||
702 | default: | 837 | default: |
703 | return 0; | 838 | return 0; |
704 | } | 839 | } |
705 | } | 840 | } |
706 | } | 841 | } |
707 | } \ No newline at end of file | 842 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index a7628d2..4528714 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | |||
@@ -40,8 +40,9 @@ using OpenSim.Framework; | |||
40 | using OpenSim.Framework.Console; | 40 | using OpenSim.Framework.Console; |
41 | using OpenSim.Framework.Monitoring; | 41 | using OpenSim.Framework.Monitoring; |
42 | using OpenSim.Region.Framework.Scenes; | 42 | using OpenSim.Region.Framework.Scenes; |
43 | using OpenSim.Region.Framework.Interfaces; | ||
43 | using OpenMetaverse; | 44 | using OpenMetaverse; |
44 | 45 | using Mono.Addins; | |
45 | using TokenBucket = OpenSim.Region.ClientStack.LindenUDP.TokenBucket; | 46 | using TokenBucket = OpenSim.Region.ClientStack.LindenUDP.TokenBucket; |
46 | 47 | ||
47 | namespace OpenSim.Region.ClientStack.LindenUDP | 48 | namespace OpenSim.Region.ClientStack.LindenUDP |
@@ -49,22 +50,58 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
49 | /// <summary> | 50 | /// <summary> |
50 | /// A shim around LLUDPServer that implements the IClientNetworkServer interface | 51 | /// A shim around LLUDPServer that implements the IClientNetworkServer interface |
51 | /// </summary> | 52 | /// </summary> |
52 | public sealed class LLUDPServerShim : IClientNetworkServer | 53 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "LLUDPServerShim")] |
54 | public sealed class LLUDPServerShim : INonSharedRegionModule | ||
53 | { | 55 | { |
56 | private bool m_Enabled = true; | ||
57 | private IConfigSource m_Config; | ||
54 | LLUDPServer m_udpServer; | 58 | LLUDPServer m_udpServer; |
55 | 59 | ||
56 | public LLUDPServerShim() | 60 | #region INonSharedRegionModule |
61 | public string Name | ||
57 | { | 62 | { |
63 | get { return "LLUDPServerShim"; } | ||
58 | } | 64 | } |
59 | 65 | ||
60 | public void Initialise(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) | 66 | public Type ReplaceableInterface |
61 | { | 67 | { |
62 | m_udpServer = new LLUDPServer(listenIP, ref port, proxyPortOffsetParm, allow_alternate_port, configSource, circuitManager); | 68 | get { return null; } |
63 | } | 69 | } |
64 | 70 | ||
65 | public void NetworkStop() | 71 | public void Initialise(IConfigSource source) |
66 | { | 72 | { |
67 | m_udpServer.Stop(); | 73 | m_Config = source; |
74 | } | ||
75 | |||
76 | public void Close() | ||
77 | { | ||
78 | } | ||
79 | |||
80 | public void AddRegion(Scene scene) | ||
81 | { | ||
82 | uint port = (uint)scene.RegionInfo.InternalEndPoint.Port; | ||
83 | |||
84 | IPAddress listenIP = scene.RegionInfo.InternalEndPoint.Address; | ||
85 | Initialise(listenIP, ref port, scene.RegionInfo.ProxyOffset, scene.RegionInfo.m_allow_alternate_ports, m_Config, scene.AuthenticateHandler); | ||
86 | scene.RegionInfo.InternalEndPoint.Port = (int)port; | ||
87 | |||
88 | AddScene(scene); | ||
89 | } | ||
90 | |||
91 | public void RemoveRegion(Scene scene) | ||
92 | { | ||
93 | Stop(); | ||
94 | } | ||
95 | |||
96 | public void RegionLoaded(Scene scene) | ||
97 | { | ||
98 | Start(); | ||
99 | } | ||
100 | #endregion | ||
101 | |||
102 | public void Initialise(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) | ||
103 | { | ||
104 | m_udpServer = new LLUDPServer(listenIP, ref port, proxyPortOffsetParm, allow_alternate_port, configSource, circuitManager); | ||
68 | } | 105 | } |
69 | 106 | ||
70 | public void AddScene(IScene scene) | 107 | public void AddScene(IScene scene) |
@@ -73,9 +110,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
73 | 110 | ||
74 | StatsManager.RegisterStat( | 111 | StatsManager.RegisterStat( |
75 | new Stat( | 112 | new Stat( |
113 | "ClientLogoutsDueToNoReceives", | ||
114 | "Number of times a client has been logged out because no packets were received before the timeout.", | ||
115 | "", | ||
116 | "", | ||
117 | "clientstack", | ||
118 | scene.Name, | ||
119 | StatType.Pull, | ||
120 | MeasuresOfInterest.None, | ||
121 | stat => stat.Value = m_udpServer.ClientLogoutsDueToNoReceives, | ||
122 | StatVerbosity.Debug)); | ||
123 | |||
124 | StatsManager.RegisterStat( | ||
125 | new Stat( | ||
126 | "IncomingUDPReceivesCount", | ||
127 | "Number of UDP receives performed", | ||
128 | "", | ||
129 | "", | ||
130 | "clientstack", | ||
131 | scene.Name, | ||
132 | StatType.Pull, | ||
133 | MeasuresOfInterest.AverageChangeOverTime, | ||
134 | stat => stat.Value = m_udpServer.UdpReceives, | ||
135 | StatVerbosity.Debug)); | ||
136 | |||
137 | StatsManager.RegisterStat( | ||
138 | new Stat( | ||
76 | "IncomingPacketsProcessedCount", | 139 | "IncomingPacketsProcessedCount", |
77 | "Number of inbound UDP packets processed", | 140 | "Number of inbound LL protocol packets processed", |
78 | "Number of inbound UDP packets processed", | 141 | "", |
79 | "", | 142 | "", |
80 | "clientstack", | 143 | "clientstack", |
81 | scene.Name, | 144 | scene.Name, |
@@ -83,6 +146,86 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
83 | MeasuresOfInterest.AverageChangeOverTime, | 146 | MeasuresOfInterest.AverageChangeOverTime, |
84 | stat => stat.Value = m_udpServer.IncomingPacketsProcessed, | 147 | stat => stat.Value = m_udpServer.IncomingPacketsProcessed, |
85 | StatVerbosity.Debug)); | 148 | StatVerbosity.Debug)); |
149 | |||
150 | StatsManager.RegisterStat( | ||
151 | new Stat( | ||
152 | "IncomingPacketsMalformedCount", | ||
153 | "Number of inbound UDP packets that could not be recognized as LL protocol packets.", | ||
154 | "", | ||
155 | "", | ||
156 | "clientstack", | ||
157 | scene.Name, | ||
158 | StatType.Pull, | ||
159 | MeasuresOfInterest.AverageChangeOverTime, | ||
160 | stat => stat.Value = m_udpServer.IncomingMalformedPacketCount, | ||
161 | StatVerbosity.Info)); | ||
162 | |||
163 | StatsManager.RegisterStat( | ||
164 | new Stat( | ||
165 | "IncomingPacketsOrphanedCount", | ||
166 | "Number of inbound packets that were not initial connections packets and could not be associated with a viewer.", | ||
167 | "", | ||
168 | "", | ||
169 | "clientstack", | ||
170 | scene.Name, | ||
171 | StatType.Pull, | ||
172 | MeasuresOfInterest.AverageChangeOverTime, | ||
173 | stat => stat.Value = m_udpServer.IncomingOrphanedPacketCount, | ||
174 | StatVerbosity.Info)); | ||
175 | |||
176 | StatsManager.RegisterStat( | ||
177 | new Stat( | ||
178 | "IncomingPacketsResentCount", | ||
179 | "Number of inbound packets that clients indicate are resends.", | ||
180 | "", | ||
181 | "", | ||
182 | "clientstack", | ||
183 | scene.Name, | ||
184 | StatType.Pull, | ||
185 | MeasuresOfInterest.AverageChangeOverTime, | ||
186 | stat => stat.Value = m_udpServer.IncomingPacketsResentCount, | ||
187 | StatVerbosity.Debug)); | ||
188 | |||
189 | StatsManager.RegisterStat( | ||
190 | new Stat( | ||
191 | "OutgoingUDPSendsCount", | ||
192 | "Number of UDP sends performed", | ||
193 | "", | ||
194 | "", | ||
195 | "clientstack", | ||
196 | scene.Name, | ||
197 | StatType.Pull, | ||
198 | MeasuresOfInterest.AverageChangeOverTime, | ||
199 | stat => stat.Value = m_udpServer.UdpSends, | ||
200 | StatVerbosity.Debug)); | ||
201 | |||
202 | StatsManager.RegisterStat( | ||
203 | new Stat( | ||
204 | "OutgoingPacketsResentCount", | ||
205 | "Number of packets resent because a client did not acknowledge receipt", | ||
206 | "", | ||
207 | "", | ||
208 | "clientstack", | ||
209 | scene.Name, | ||
210 | StatType.Pull, | ||
211 | MeasuresOfInterest.AverageChangeOverTime, | ||
212 | stat => stat.Value = m_udpServer.PacketsResentCount, | ||
213 | StatVerbosity.Debug)); | ||
214 | |||
215 | StatsManager.RegisterStat( | ||
216 | new Stat( | ||
217 | "AverageUDPProcessTime", | ||
218 | "Average number of milliseconds taken to process each incoming UDP packet in a sample.", | ||
219 | "This is for initial receive processing which is separate from the later client LL packet processing stage.", | ||
220 | "ms", | ||
221 | "clientstack", | ||
222 | scene.Name, | ||
223 | StatType.Pull, | ||
224 | MeasuresOfInterest.None, | ||
225 | stat => stat.Value = m_udpServer.AverageReceiveTicksForLastSamplePeriod, | ||
226 | // stat => | ||
227 | // stat.Value = Math.Round(m_udpServer.AverageReceiveTicksForLastSamplePeriod, 7), | ||
228 | StatVerbosity.Debug)); | ||
86 | } | 229 | } |
87 | 230 | ||
88 | public bool HandlesRegion(Location x) | 231 | public bool HandlesRegion(Location x) |
@@ -99,6 +242,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
99 | { | 242 | { |
100 | m_udpServer.Stop(); | 243 | m_udpServer.Stop(); |
101 | } | 244 | } |
245 | |||
102 | } | 246 | } |
103 | 247 | ||
104 | /// <summary> | 248 | /// <summary> |
@@ -107,10 +251,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
107 | /// </summary> | 251 | /// </summary> |
108 | public class LLUDPServer : OpenSimUDPBase | 252 | public class LLUDPServer : OpenSimUDPBase |
109 | { | 253 | { |
254 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
255 | |||
110 | /// <summary>Maximum transmission unit, or UDP packet size, for the LLUDP protocol</summary> | 256 | /// <summary>Maximum transmission unit, or UDP packet size, for the LLUDP protocol</summary> |
111 | public const int MTU = 1400; | 257 | public const int MTU = 1400; |
112 | 258 | ||
113 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 259 | /// <summary>Number of forced client logouts due to no receipt of packets before timeout.</summary> |
260 | public int ClientLogoutsDueToNoReceives { get; private set; } | ||
261 | |||
262 | /// <summary> | ||
263 | /// Default packet debug level given to new clients | ||
264 | /// </summary> | ||
265 | public int DefaultClientPacketDebugLevel { get; set; } | ||
266 | |||
267 | /// <summary> | ||
268 | /// If set then all inbound agent updates are discarded. For debugging purposes. | ||
269 | /// discard agent update. | ||
270 | /// </summary> | ||
271 | public bool DiscardInboundAgentUpdates { get; set; } | ||
114 | 272 | ||
115 | /// <summary>The measured resolution of Environment.TickCount</summary> | 273 | /// <summary>The measured resolution of Environment.TickCount</summary> |
116 | public readonly float TickCountResolution; | 274 | public readonly float TickCountResolution; |
@@ -128,19 +286,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
128 | /// <summary>Incoming packets that are awaiting handling</summary> | 286 | /// <summary>Incoming packets that are awaiting handling</summary> |
129 | private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>(); | 287 | private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>(); |
130 | 288 | ||
131 | /// <summary></summary> | ||
132 | //private UDPClientCollection m_clients = new UDPClientCollection(); | ||
133 | /// <summary>Bandwidth throttle for this UDP server</summary> | 289 | /// <summary>Bandwidth throttle for this UDP server</summary> |
134 | protected TokenBucket m_throttle; | 290 | public TokenBucket Throttle { get; private set; } |
135 | 291 | ||
136 | /// <summary>Bandwidth throttle rates for this UDP server</summary> | 292 | /// <summary>Per client throttle rates enforced by this server</summary> |
293 | /// <remarks> | ||
294 | /// If the total rate is non-zero, then this is the maximum total throttle setting that any client can ever have. | ||
295 | /// The other rates (resend, asset, etc.) are the defaults for a new client and can be changed (and usually | ||
296 | /// do get changed immediately). They do not need to sum to the total. | ||
297 | /// </remarks> | ||
137 | public ThrottleRates ThrottleRates { get; private set; } | 298 | public ThrottleRates ThrottleRates { get; private set; } |
138 | 299 | ||
139 | /// <summary>Manages authentication for agent circuits</summary> | 300 | /// <summary>Manages authentication for agent circuits</summary> |
140 | private AgentCircuitManager m_circuitManager; | 301 | private AgentCircuitManager m_circuitManager; |
141 | 302 | ||
142 | /// <summary>Reference to the scene this UDP server is attached to</summary> | 303 | /// <summary>Reference to the scene this UDP server is attached to</summary> |
143 | protected Scene m_scene; | 304 | public Scene Scene { get; private set; } |
144 | 305 | ||
145 | /// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary> | 306 | /// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary> |
146 | private Location m_location; | 307 | private Location m_location; |
@@ -181,6 +342,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
181 | /// <summary>Flag to signal when clients should send pings</summary> | 342 | /// <summary>Flag to signal when clients should send pings</summary> |
182 | protected bool m_sendPing; | 343 | protected bool m_sendPing; |
183 | 344 | ||
345 | /// <summary> | ||
346 | /// Event used to signal when queued packets are available for sending. | ||
347 | /// </summary> | ||
348 | /// <remarks> | ||
349 | /// This allows the outbound loop to only operate when there is data to send rather than continuously polling. | ||
350 | /// Some data is sent immediately and not queued. That data would not trigger this event. | ||
351 | /// </remarks> | ||
352 | private AutoResetEvent m_dataPresentEvent = new AutoResetEvent(false); | ||
353 | |||
184 | private Pool<IncomingPacket> m_incomingPacketPool; | 354 | private Pool<IncomingPacket> m_incomingPacketPool; |
185 | 355 | ||
186 | /// <summary> | 356 | /// <summary> |
@@ -201,7 +371,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
201 | 371 | ||
202 | public Socket Server { get { return null; } } | 372 | public Socket Server { get { return null; } } |
203 | 373 | ||
204 | private int m_malformedCount = 0; // Guard against a spamming attack | 374 | /// <summary> |
375 | /// Record how many packets have been resent | ||
376 | /// </summary> | ||
377 | internal int PacketsResentCount { get; set; } | ||
378 | |||
379 | /// <summary> | ||
380 | /// Record how many packets have been sent | ||
381 | /// </summary> | ||
382 | internal int PacketsSentCount { get; set; } | ||
383 | |||
384 | /// <summary> | ||
385 | /// Record how many incoming packets are indicated as resends by clients. | ||
386 | /// </summary> | ||
387 | internal int IncomingPacketsResentCount { get; set; } | ||
388 | |||
389 | /// <summary> | ||
390 | /// Record how many inbound packets could not be recognized as LLUDP packets. | ||
391 | /// </summary> | ||
392 | public int IncomingMalformedPacketCount { get; private set; } | ||
393 | |||
394 | /// <summary> | ||
395 | /// Record how many inbound packets could not be associated with a simulator circuit. | ||
396 | /// </summary> | ||
397 | public int IncomingOrphanedPacketCount { get; private set; } | ||
205 | 398 | ||
206 | /// <summary> | 399 | /// <summary> |
207 | /// Record current outgoing client for monitoring purposes. | 400 | /// Record current outgoing client for monitoring purposes. |
@@ -213,6 +406,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
213 | /// </summary> | 406 | /// </summary> |
214 | private IClientAPI m_currentIncomingClient; | 407 | private IClientAPI m_currentIncomingClient; |
215 | 408 | ||
409 | /// <summary> | ||
410 | /// Queue some low priority but potentially high volume async requests so that they don't overwhelm available | ||
411 | /// threadpool threads. | ||
412 | /// </summary> | ||
413 | public JobEngine IpahEngine { get; private set; } | ||
414 | |||
415 | /// <summary> | ||
416 | /// Run queue empty processing within a single persistent thread. | ||
417 | /// </summary> | ||
418 | /// <remarks> | ||
419 | /// This is the alternative to having every | ||
420 | /// connection schedule its own job in the threadpool which causes performance problems when there are many | ||
421 | /// connections. | ||
422 | /// </remarks> | ||
423 | public JobEngine OqrEngine { get; private set; } | ||
424 | |||
216 | public LLUDPServer( | 425 | public LLUDPServer( |
217 | IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, | 426 | IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, |
218 | IConfigSource configSource, AgentCircuitManager circuitManager) | 427 | IConfigSource configSource, AgentCircuitManager circuitManager) |
@@ -278,27 +487,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
278 | m_shouldCollectStats = false; | 487 | m_shouldCollectStats = false; |
279 | if (config != null) | 488 | if (config != null) |
280 | { | 489 | { |
281 | if (config.Contains("enabled") && config.GetBoolean("enabled")) | 490 | m_shouldCollectStats = config.GetBoolean("Enabled", false); |
282 | { | 491 | binStatsMaxFilesize = TimeSpan.FromSeconds(config.GetInt("packet_headers_period_seconds", 300)); |
283 | if (config.Contains("collect_packet_headers")) | 492 | binStatsDir = config.GetString("stats_dir", "."); |
284 | m_shouldCollectStats = config.GetBoolean("collect_packet_headers"); | 493 | m_aggregatedBWStats = config.GetBoolean("aggregatedBWStats", false); |
285 | if (config.Contains("packet_headers_period_seconds")) | 494 | } |
286 | { | 495 | #endregion BinaryStats |
287 | binStatsMaxFilesize = TimeSpan.FromSeconds(config.GetInt("region_stats_period_seconds")); | 496 | |
288 | } | 497 | // FIXME: Can't add info here because don't know scene yet. |
289 | if (config.Contains("stats_dir")) | 498 | // m_throttle |
290 | { | 499 | // = new TokenBucket( |
291 | binStatsDir = config.GetString("stats_dir"); | 500 | // string.Format("server throttle bucket for {0}", Scene.Name), null, sceneThrottleBps); |
292 | } | 501 | |
293 | } | 502 | Throttle = new TokenBucket("server throttle bucket", null, 0, sceneThrottleBps); |
294 | else | 503 | |
295 | { | ||
296 | m_shouldCollectStats = false; | ||
297 | } | ||
298 | } | ||
299 | #endregion BinaryStats | ||
300 | |||
301 | m_throttle = new TokenBucket(null, sceneThrottleBps); | ||
302 | ThrottleRates = new ThrottleRates(configSource); | 504 | ThrottleRates = new ThrottleRates(configSource); |
303 | 505 | ||
304 | if (usePools) | 506 | if (usePools) |
@@ -309,11 +511,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
309 | { | 511 | { |
310 | StartInbound(); | 512 | StartInbound(); |
311 | StartOutbound(); | 513 | StartOutbound(); |
514 | IpahEngine.Start(); | ||
515 | OqrEngine.Start(); | ||
312 | 516 | ||
313 | m_elapsedMSSinceLastStatReport = Environment.TickCount; | 517 | m_elapsedMSSinceLastStatReport = Environment.TickCount; |
314 | } | 518 | } |
315 | 519 | ||
316 | private void StartInbound() | 520 | public void StartInbound() |
317 | { | 521 | { |
318 | m_log.InfoFormat( | 522 | m_log.InfoFormat( |
319 | "[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}", | 523 | "[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}", |
@@ -322,9 +526,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
322 | base.StartInbound(m_recvBufferSize, m_asyncPacketHandling); | 526 | base.StartInbound(m_recvBufferSize, m_asyncPacketHandling); |
323 | 527 | ||
324 | // This thread will process the packets received that are placed on the packetInbox | 528 | // This thread will process the packets received that are placed on the packetInbox |
325 | Watchdog.StartThread( | 529 | WorkManager.StartThread( |
326 | IncomingPacketHandler, | 530 | IncomingPacketHandler, |
327 | string.Format("Incoming Packets ({0})", m_scene.RegionInfo.RegionName), | 531 | string.Format("Incoming Packets ({0})", Scene.Name), |
328 | ThreadPriority.Normal, | 532 | ThreadPriority.Normal, |
329 | false, | 533 | false, |
330 | true, | 534 | true, |
@@ -332,15 +536,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
332 | Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS); | 536 | Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS); |
333 | } | 537 | } |
334 | 538 | ||
335 | private new void StartOutbound() | 539 | public override void StartOutbound() |
336 | { | 540 | { |
337 | m_log.Info("[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server"); | 541 | m_log.Info("[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server"); |
338 | 542 | ||
339 | base.StartOutbound(); | 543 | base.StartOutbound(); |
340 | 544 | ||
341 | Watchdog.StartThread( | 545 | WorkManager.StartThread( |
342 | OutgoingPacketHandler, | 546 | OutgoingPacketHandler, |
343 | string.Format("Outgoing Packets ({0})", m_scene.RegionInfo.RegionName), | 547 | string.Format("Outgoing Packets ({0})", Scene.Name), |
344 | ThreadPriority.Normal, | 548 | ThreadPriority.Normal, |
345 | false, | 549 | false, |
346 | true, | 550 | true, |
@@ -350,12 +554,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
350 | 554 | ||
351 | public void Stop() | 555 | public void Stop() |
352 | { | 556 | { |
353 | m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName); | 557 | m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + Scene.Name); |
354 | base.StopOutbound(); | 558 | base.StopOutbound(); |
355 | base.StopInbound(); | 559 | base.StopInbound(); |
560 | IpahEngine.Stop(); | ||
561 | OqrEngine.Stop(); | ||
356 | } | 562 | } |
357 | 563 | ||
358 | protected override bool EnablePools() | 564 | public override bool EnablePools() |
359 | { | 565 | { |
360 | if (!UsePools) | 566 | if (!UsePools) |
361 | { | 567 | { |
@@ -369,7 +575,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
369 | return false; | 575 | return false; |
370 | } | 576 | } |
371 | 577 | ||
372 | protected override bool DisablePools() | 578 | public override bool DisablePools() |
373 | { | 579 | { |
374 | if (UsePools) | 580 | if (UsePools) |
375 | { | 581 | { |
@@ -389,7 +595,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
389 | /// This is a seperate method so that it can be called once we have an m_scene to distinguish different scene | 595 | /// This is a seperate method so that it can be called once we have an m_scene to distinguish different scene |
390 | /// stats. | 596 | /// stats. |
391 | /// </summary> | 597 | /// </summary> |
392 | private void EnablePoolStats() | 598 | protected internal void EnablePoolStats() |
393 | { | 599 | { |
394 | m_poolCountStat | 600 | m_poolCountStat |
395 | = new Stat( | 601 | = new Stat( |
@@ -398,7 +604,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
398 | "The number of objects currently stored within the UDPPacketBuffer pool", | 604 | "The number of objects currently stored within the UDPPacketBuffer pool", |
399 | "", | 605 | "", |
400 | "clientstack", | 606 | "clientstack", |
401 | m_scene.Name, | 607 | Scene.Name, |
402 | StatType.Pull, | 608 | StatType.Pull, |
403 | stat => stat.Value = Pool.Count, | 609 | stat => stat.Value = Pool.Count, |
404 | StatVerbosity.Debug); | 610 | StatVerbosity.Debug); |
@@ -412,7 +618,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
412 | "The number of objects currently stored within the incoming packet pool", | 618 | "The number of objects currently stored within the incoming packet pool", |
413 | "", | 619 | "", |
414 | "clientstack", | 620 | "clientstack", |
415 | m_scene.Name, | 621 | Scene.Name, |
416 | StatType.Pull, | 622 | StatType.Pull, |
417 | stat => stat.Value = m_incomingPacketPool.Count, | 623 | stat => stat.Value = m_incomingPacketPool.Count, |
418 | StatVerbosity.Debug); | 624 | StatVerbosity.Debug); |
@@ -423,7 +629,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
423 | /// <summary> | 629 | /// <summary> |
424 | /// Disables pool stats. | 630 | /// Disables pool stats. |
425 | /// </summary> | 631 | /// </summary> |
426 | private void DisablePoolStats() | 632 | protected internal void DisablePoolStats() |
427 | { | 633 | { |
428 | StatsManager.DeregisterStat(m_poolCountStat); | 634 | StatsManager.DeregisterStat(m_poolCountStat); |
429 | m_poolCountStat = null; | 635 | m_poolCountStat = null; |
@@ -456,7 +662,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
456 | 662 | ||
457 | public void AddScene(IScene scene) | 663 | public void AddScene(IScene scene) |
458 | { | 664 | { |
459 | if (m_scene != null) | 665 | if (Scene != null) |
460 | { | 666 | { |
461 | m_log.Error("[LLUDPSERVER]: AddScene() called on an LLUDPServer that already has a scene"); | 667 | m_log.Error("[LLUDPSERVER]: AddScene() called on an LLUDPServer that already has a scene"); |
462 | return; | 668 | return; |
@@ -468,8 +674,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
468 | return; | 674 | return; |
469 | } | 675 | } |
470 | 676 | ||
471 | m_scene = (Scene)scene; | 677 | Scene = (Scene)scene; |
472 | m_location = new Location(m_scene.RegionInfo.RegionHandle); | 678 | m_location = new Location(Scene.RegionInfo.RegionHandle); |
679 | |||
680 | IpahEngine | ||
681 | = new JobEngine( | ||
682 | string.Format("Incoming Packet Async Handling Engine ({0})", Scene.Name), | ||
683 | "INCOMING PACKET ASYNC HANDLING ENGINE"); | ||
684 | |||
685 | OqrEngine | ||
686 | = new JobEngine( | ||
687 | string.Format("Outgoing Queue Refill Engine ({0})", Scene.Name), | ||
688 | "OUTGOING QUEUE REFILL ENGINE"); | ||
689 | |||
690 | StatsManager.RegisterStat( | ||
691 | new Stat( | ||
692 | "InboxPacketsCount", | ||
693 | "Number of LL protocol packets waiting for the second stage of processing after initial receive.", | ||
694 | "Number of LL protocol packets waiting for the second stage of processing after initial receive.", | ||
695 | "", | ||
696 | "clientstack", | ||
697 | scene.Name, | ||
698 | StatType.Pull, | ||
699 | MeasuresOfInterest.AverageChangeOverTime, | ||
700 | stat => stat.Value = packetInbox.Count, | ||
701 | StatVerbosity.Debug)); | ||
473 | 702 | ||
474 | // XXX: These stats are also pool stats but we register them separately since they are currently not | 703 | // XXX: These stats are also pool stats but we register them separately since they are currently not |
475 | // turned on and off by EnablePools()/DisablePools() | 704 | // turned on and off by EnablePools()/DisablePools() |
@@ -479,7 +708,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
479 | "Packets reused", | 708 | "Packets reused", |
480 | "Number of packets reused out of all requests to the packet pool", | 709 | "Number of packets reused out of all requests to the packet pool", |
481 | "clientstack", | 710 | "clientstack", |
482 | m_scene.Name, | 711 | Scene.Name, |
483 | StatType.Pull, | 712 | StatType.Pull, |
484 | stat => | 713 | stat => |
485 | { PercentageStat pstat = (PercentageStat)stat; | 714 | { PercentageStat pstat = (PercentageStat)stat; |
@@ -493,7 +722,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
493 | "Packet data blocks reused", | 722 | "Packet data blocks reused", |
494 | "Number of data blocks reused out of all requests to the packet pool", | 723 | "Number of data blocks reused out of all requests to the packet pool", |
495 | "clientstack", | 724 | "clientstack", |
496 | m_scene.Name, | 725 | Scene.Name, |
497 | StatType.Pull, | 726 | StatType.Pull, |
498 | stat => | 727 | stat => |
499 | { PercentageStat pstat = (PercentageStat)stat; | 728 | { PercentageStat pstat = (PercentageStat)stat; |
@@ -508,7 +737,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
508 | "The number of objects currently stored within the packet pool", | 737 | "The number of objects currently stored within the packet pool", |
509 | "", | 738 | "", |
510 | "clientstack", | 739 | "clientstack", |
511 | m_scene.Name, | 740 | Scene.Name, |
512 | StatType.Pull, | 741 | StatType.Pull, |
513 | stat => stat.Value = PacketPool.Instance.PacketsPooled, | 742 | stat => stat.Value = PacketPool.Instance.PacketsPooled, |
514 | StatVerbosity.Debug)); | 743 | StatVerbosity.Debug)); |
@@ -520,132 +749,57 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
520 | "The number of objects currently stored within the packet data block pool", | 749 | "The number of objects currently stored within the packet data block pool", |
521 | "", | 750 | "", |
522 | "clientstack", | 751 | "clientstack", |
523 | m_scene.Name, | 752 | Scene.Name, |
524 | StatType.Pull, | 753 | StatType.Pull, |
525 | stat => stat.Value = PacketPool.Instance.BlocksPooled, | 754 | stat => stat.Value = PacketPool.Instance.BlocksPooled, |
526 | StatVerbosity.Debug)); | 755 | StatVerbosity.Debug)); |
756 | |||
757 | StatsManager.RegisterStat( | ||
758 | new Stat( | ||
759 | "OutgoingPacketsQueuedCount", | ||
760 | "Packets queued for outgoing send", | ||
761 | "Number of queued outgoing packets across all connections", | ||
762 | "", | ||
763 | "clientstack", | ||
764 | Scene.Name, | ||
765 | StatType.Pull, | ||
766 | MeasuresOfInterest.AverageChangeOverTime, | ||
767 | stat => stat.Value = GetTotalQueuedOutgoingPackets(), | ||
768 | StatVerbosity.Info)); | ||
769 | |||
770 | StatsManager.RegisterStat( | ||
771 | new Stat( | ||
772 | "IncomingPacketAsyncRequestsWaiting", | ||
773 | "Number of incoming packets waiting for async processing in engine.", | ||
774 | "", | ||
775 | "", | ||
776 | "clientstack", | ||
777 | Scene.Name, | ||
778 | StatType.Pull, | ||
779 | MeasuresOfInterest.None, | ||
780 | stat => stat.Value = IpahEngine.JobsWaiting, | ||
781 | StatVerbosity.Debug)); | ||
782 | |||
783 | StatsManager.RegisterStat( | ||
784 | new Stat( | ||
785 | "OQRERequestsWaiting", | ||
786 | "Number of outgong queue refill requests waiting for processing.", | ||
787 | "", | ||
788 | "", | ||
789 | "clientstack", | ||
790 | Scene.Name, | ||
791 | StatType.Pull, | ||
792 | MeasuresOfInterest.None, | ||
793 | stat => stat.Value = OqrEngine.JobsWaiting, | ||
794 | StatVerbosity.Debug)); | ||
527 | 795 | ||
528 | // We delay enabling pool stats to AddScene() instead of Initialize() so that we can distinguish pool stats by | 796 | // We delay enabling pool stats to AddScene() instead of Initialize() so that we can distinguish pool stats by |
529 | // scene name | 797 | // scene name |
530 | if (UsePools) | 798 | if (UsePools) |
531 | EnablePoolStats(); | 799 | EnablePoolStats(); |
532 | 800 | ||
533 | MainConsole.Instance.Commands.AddCommand( | 801 | LLUDPServerCommands commands = new LLUDPServerCommands(MainConsole.Instance, this); |
534 | "Debug", | 802 | commands.Register(); |
535 | false, | ||
536 | "debug lludp start", | ||
537 | "debug lludp start <in|out|all>", | ||
538 | "Control LLUDP packet processing.", | ||
539 | "No effect if packet processing has already started.\n" | ||
540 | + "in - start inbound processing.\n" | ||
541 | + "out - start outbound processing.\n" | ||
542 | + "all - start in and outbound processing.\n", | ||
543 | HandleStartCommand); | ||
544 | |||
545 | MainConsole.Instance.Commands.AddCommand( | ||
546 | "Debug", | ||
547 | false, | ||
548 | "debug lludp stop", | ||
549 | "debug lludp stop <in|out|all>", | ||
550 | "Stop LLUDP packet processing.", | ||
551 | "No effect if packet processing has already stopped.\n" | ||
552 | + "in - stop inbound processing.\n" | ||
553 | + "out - stop outbound processing.\n" | ||
554 | + "all - stop in and outbound processing.\n", | ||
555 | HandleStopCommand); | ||
556 | |||
557 | MainConsole.Instance.Commands.AddCommand( | ||
558 | "Debug", | ||
559 | false, | ||
560 | "debug lludp pool", | ||
561 | "debug lludp pool <on|off>", | ||
562 | "Turn object pooling within the lludp component on or off.", | ||
563 | HandlePoolCommand); | ||
564 | |||
565 | MainConsole.Instance.Commands.AddCommand( | ||
566 | "Debug", | ||
567 | false, | ||
568 | "debug lludp status", | ||
569 | "debug lludp status", | ||
570 | "Return status of LLUDP packet processing.", | ||
571 | HandleStatusCommand); | ||
572 | } | ||
573 | |||
574 | private void HandleStartCommand(string module, string[] args) | ||
575 | { | ||
576 | if (args.Length != 4) | ||
577 | { | ||
578 | MainConsole.Instance.Output("Usage: debug lludp start <in|out|all>"); | ||
579 | return; | ||
580 | } | ||
581 | |||
582 | string subCommand = args[3]; | ||
583 | |||
584 | if (subCommand == "in" || subCommand == "all") | ||
585 | StartInbound(); | ||
586 | |||
587 | if (subCommand == "out" || subCommand == "all") | ||
588 | StartOutbound(); | ||
589 | } | ||
590 | |||
591 | private void HandleStopCommand(string module, string[] args) | ||
592 | { | ||
593 | if (args.Length != 4) | ||
594 | { | ||
595 | MainConsole.Instance.Output("Usage: debug lludp stop <in|out|all>"); | ||
596 | return; | ||
597 | } | ||
598 | |||
599 | string subCommand = args[3]; | ||
600 | |||
601 | if (subCommand == "in" || subCommand == "all") | ||
602 | StopInbound(); | ||
603 | |||
604 | if (subCommand == "out" || subCommand == "all") | ||
605 | StopOutbound(); | ||
606 | } | ||
607 | |||
608 | private void HandlePoolCommand(string module, string[] args) | ||
609 | { | ||
610 | if (args.Length != 4) | ||
611 | { | ||
612 | MainConsole.Instance.Output("Usage: debug lludp pool <on|off>"); | ||
613 | return; | ||
614 | } | ||
615 | |||
616 | string enabled = args[3]; | ||
617 | |||
618 | if (enabled == "on") | ||
619 | { | ||
620 | if (EnablePools()) | ||
621 | { | ||
622 | EnablePoolStats(); | ||
623 | MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", m_scene.Name); | ||
624 | } | ||
625 | } | ||
626 | else if (enabled == "off") | ||
627 | { | ||
628 | if (DisablePools()) | ||
629 | { | ||
630 | DisablePoolStats(); | ||
631 | MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", m_scene.Name); | ||
632 | } | ||
633 | } | ||
634 | else | ||
635 | { | ||
636 | MainConsole.Instance.Output("Usage: debug lludp pool <on|off>"); | ||
637 | } | ||
638 | } | ||
639 | |||
640 | private void HandleStatusCommand(string module, string[] args) | ||
641 | { | ||
642 | MainConsole.Instance.OutputFormat( | ||
643 | "IN LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningInbound ? "enabled" : "disabled"); | ||
644 | |||
645 | MainConsole.Instance.OutputFormat( | ||
646 | "OUT LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningOutbound ? "enabled" : "disabled"); | ||
647 | |||
648 | MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", m_scene.Name, UsePools ? "on" : "off"); | ||
649 | } | 803 | } |
650 | 804 | ||
651 | public bool HandlesRegion(Location x) | 805 | public bool HandlesRegion(Location x) |
@@ -653,45 +807,62 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
653 | return x == m_location; | 807 | return x == m_location; |
654 | } | 808 | } |
655 | 809 | ||
656 | public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) | 810 | public int GetTotalQueuedOutgoingPackets() |
657 | { | 811 | { |
658 | // CoarseLocationUpdate and AvatarGroupsReply packets cannot be split in an automated way | 812 | int total = 0; |
659 | if ((packet.Type == PacketType.CoarseLocationUpdate || packet.Type == PacketType.AvatarGroupsReply) && allowSplitting) | ||
660 | allowSplitting = false; | ||
661 | 813 | ||
662 | if (allowSplitting && packet.HasVariableBlocks) | 814 | foreach (ScenePresence sp in Scene.GetScenePresences()) |
663 | { | 815 | { |
664 | byte[][] datas = packet.ToBytesMultiple(); | 816 | // XXX: Need a better way to determine which IClientAPIs have UDPClients (NPCs do not, for instance). |
665 | int packetCount = datas.Length; | 817 | if (sp.ControllingClient is LLClientView) |
666 | |||
667 | if (packetCount < 1) | ||
668 | m_log.Error("[LLUDPSERVER]: Failed to split " + packet.Type + " with estimated length " + packet.Length); | ||
669 | |||
670 | for (int i = 0; i < packetCount; i++) | ||
671 | { | 818 | { |
672 | byte[] data = datas[i]; | 819 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; |
673 | m_scene.ForEachClient( | 820 | total += udpClient.GetTotalPacketsQueuedCount(); |
674 | delegate(IClientAPI client) | ||
675 | { | ||
676 | if (client is LLClientView) | ||
677 | SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null); | ||
678 | } | ||
679 | ); | ||
680 | } | 821 | } |
681 | } | 822 | } |
682 | else | 823 | |
683 | { | 824 | return total; |
684 | byte[] data = packet.ToBytes(); | ||
685 | m_scene.ForEachClient( | ||
686 | delegate(IClientAPI client) | ||
687 | { | ||
688 | if (client is LLClientView) | ||
689 | SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null); | ||
690 | } | ||
691 | ); | ||
692 | } | ||
693 | } | 825 | } |
694 | 826 | ||
827 | // public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) | ||
828 | // { | ||
829 | // // CoarseLocationUpdate and AvatarGroupsReply packets cannot be split in an automated way | ||
830 | // if ((packet.Type == PacketType.CoarseLocationUpdate || packet.Type == PacketType.AvatarGroupsReply) && allowSplitting) | ||
831 | // allowSplitting = false; | ||
832 | // | ||
833 | // if (allowSplitting && packet.HasVariableBlocks) | ||
834 | // { | ||
835 | // byte[][] datas = packet.ToBytesMultiple(); | ||
836 | // int packetCount = datas.Length; | ||
837 | // | ||
838 | // if (packetCount < 1) | ||
839 | // m_log.Error("[LLUDPSERVER]: Failed to split " + packet.Type + " with estimated length " + packet.Length); | ||
840 | // | ||
841 | // for (int i = 0; i < packetCount; i++) | ||
842 | // { | ||
843 | // byte[] data = datas[i]; | ||
844 | // m_scene.ForEachClient( | ||
845 | // delegate(IClientAPI client) | ||
846 | // { | ||
847 | // if (client is LLClientView) | ||
848 | // SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null); | ||
849 | // } | ||
850 | // ); | ||
851 | // } | ||
852 | // } | ||
853 | // else | ||
854 | // { | ||
855 | // byte[] data = packet.ToBytes(); | ||
856 | // m_scene.ForEachClient( | ||
857 | // delegate(IClientAPI client) | ||
858 | // { | ||
859 | // if (client is LLClientView) | ||
860 | // SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null); | ||
861 | // } | ||
862 | // ); | ||
863 | // } | ||
864 | // } | ||
865 | |||
695 | /// <summary> | 866 | /// <summary> |
696 | /// Start the process of sending a packet to the client. | 867 | /// Start the process of sending a packet to the client. |
697 | /// </summary> | 868 | /// </summary> |
@@ -710,6 +881,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
710 | if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting) | 881 | if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting) |
711 | allowSplitting = false; | 882 | allowSplitting = false; |
712 | 883 | ||
884 | bool packetQueued = false; | ||
885 | |||
713 | if (allowSplitting && packet.HasVariableBlocks) | 886 | if (allowSplitting && packet.HasVariableBlocks) |
714 | { | 887 | { |
715 | byte[][] datas = packet.ToBytesMultiple(); | 888 | byte[][] datas = packet.ToBytesMultiple(); |
@@ -721,16 +894,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
721 | for (int i = 0; i < packetCount; i++) | 894 | for (int i = 0; i < packetCount; i++) |
722 | { | 895 | { |
723 | byte[] data = datas[i]; | 896 | byte[] data = datas[i]; |
724 | SendPacketData(udpClient, data, packet.Type, category, method); | 897 | if (!SendPacketData(udpClient, data, packet.Type, category, method)) |
898 | packetQueued = true; | ||
725 | } | 899 | } |
726 | } | 900 | } |
727 | else | 901 | else |
728 | { | 902 | { |
729 | byte[] data = packet.ToBytes(); | 903 | byte[] data = packet.ToBytes(); |
730 | SendPacketData(udpClient, data, packet.Type, category, method); | 904 | if (!SendPacketData(udpClient, data, packet.Type, category, method)) |
905 | packetQueued = true; | ||
731 | } | 906 | } |
732 | 907 | ||
733 | PacketPool.Instance.ReturnPacket(packet); | 908 | PacketPool.Instance.ReturnPacket(packet); |
909 | |||
910 | if (packetQueued) | ||
911 | m_dataPresentEvent.Set(); | ||
734 | } | 912 | } |
735 | 913 | ||
736 | /// <summary> | 914 | /// <summary> |
@@ -744,7 +922,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
744 | /// The method to call if the packet is not acked by the client. If null, then a standard | 922 | /// The method to call if the packet is not acked by the client. If null, then a standard |
745 | /// resend of the packet is done. | 923 | /// resend of the packet is done. |
746 | /// </param> | 924 | /// </param> |
747 | public void SendPacketData( | 925 | /// <returns>true if the data was sent immediately, false if it was queued for sending</returns> |
926 | public bool SendPacketData( | ||
748 | LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category, UnackedPacketMethod method) | 927 | LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category, UnackedPacketMethod method) |
749 | { | 928 | { |
750 | int dataLength = data.Length; | 929 | int dataLength = data.Length; |
@@ -801,15 +980,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
801 | #region Queue or Send | 980 | #region Queue or Send |
802 | 981 | ||
803 | OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, null); | 982 | OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, null); |
983 | |||
804 | // If we were not provided a method for handling unacked, use the UDPServer default method | 984 | // If we were not provided a method for handling unacked, use the UDPServer default method |
805 | outgoingPacket.UnackedMethod = ((method == null) ? delegate(OutgoingPacket oPacket) { ResendUnacked(oPacket); } : method); | 985 | if ((outgoingPacket.Buffer.Data[0] & Helpers.MSG_RELIABLE) != 0) |
986 | outgoingPacket.UnackedMethod = ((method == null) ? delegate(OutgoingPacket oPacket) { ResendUnacked(oPacket); } : method); | ||
806 | 987 | ||
807 | // If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will | 988 | // If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will |
808 | // continue to display the deleted object until relog. Therefore, we need to always queue a kill object | 989 | // continue to display the deleted object until relog. Therefore, we need to always queue a kill object |
809 | // packet so that it isn't sent before a queued update packet. | 990 | // packet so that it isn't sent before a queued update packet. |
810 | bool requestQueue = type == PacketType.KillObject; | 991 | bool forceQueue = (type == PacketType.KillObject); |
811 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, requestQueue)) | 992 | |
993 | // if (type == PacketType.ImprovedTerseObjectUpdate) | ||
994 | // { | ||
995 | // m_log.DebugFormat("Direct send ITOU to {0} in {1}", udpClient.AgentID, Scene.Name); | ||
996 | // SendPacketFinal(outgoingPacket); | ||
997 | // return false; | ||
998 | // } | ||
999 | // else | ||
1000 | // { | ||
1001 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, forceQueue)) | ||
1002 | { | ||
812 | SendPacketFinal(outgoingPacket); | 1003 | SendPacketFinal(outgoingPacket); |
1004 | return true; | ||
1005 | } | ||
1006 | else | ||
1007 | { | ||
1008 | return false; | ||
1009 | } | ||
1010 | // } | ||
813 | 1011 | ||
814 | #endregion Queue or Send | 1012 | #endregion Queue or Send |
815 | } | 1013 | } |
@@ -885,7 +1083,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
885 | // Fire this out on a different thread so that we don't hold up outgoing packet processing for | 1083 | // Fire this out on a different thread so that we don't hold up outgoing packet processing for |
886 | // everybody else if this is being called due to an ack timeout. | 1084 | // everybody else if this is being called due to an ack timeout. |
887 | // This is the same as processing as the async process of a logout request. | 1085 | // This is the same as processing as the async process of a logout request. |
888 | Util.FireAndForget(o => DeactivateClientDueToTimeout(client)); | 1086 | Util.FireAndForget( |
1087 | o => DeactivateClientDueToTimeout(client, timeoutTicks), null, "LLUDPServer.DeactivateClientDueToTimeout"); | ||
889 | 1088 | ||
890 | return; | 1089 | return; |
891 | } | 1090 | } |
@@ -981,7 +1180,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
981 | Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); | 1180 | Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); |
982 | outgoingPacket.SequenceNumber = sequenceNumber; | 1181 | outgoingPacket.SequenceNumber = sequenceNumber; |
983 | 1182 | ||
984 | if (isReliable) | 1183 | if (udpClient.ProcessUnackedSends && isReliable) |
985 | { | 1184 | { |
986 | // Add this packet to the list of ACK responses we are waiting on from the server | 1185 | // Add this packet to the list of ACK responses we are waiting on from the server |
987 | udpClient.NeedAcks.Add(outgoingPacket); | 1186 | udpClient.NeedAcks.Add(outgoingPacket); |
@@ -990,6 +1189,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
990 | else | 1189 | else |
991 | { | 1190 | { |
992 | Interlocked.Increment(ref udpClient.PacketsResent); | 1191 | Interlocked.Increment(ref udpClient.PacketsResent); |
1192 | |||
1193 | // We're not going to worry about interlock yet since its not currently critical that this total count | ||
1194 | // is 100% correct | ||
1195 | PacketsResentCount++; | ||
993 | } | 1196 | } |
994 | 1197 | ||
995 | #endregion Sequence Number Assignment | 1198 | #endregion Sequence Number Assignment |
@@ -997,6 +1200,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
997 | // Stats tracking | 1200 | // Stats tracking |
998 | Interlocked.Increment(ref udpClient.PacketsSent); | 1201 | Interlocked.Increment(ref udpClient.PacketsSent); |
999 | 1202 | ||
1203 | // We're not going to worry about interlock yet since its not currently critical that this total count | ||
1204 | // is 100% correct | ||
1205 | PacketsSentCount++; | ||
1206 | |||
1207 | if (udpClient.DebugDataOutLevel > 0) | ||
1208 | m_log.DebugFormat( | ||
1209 | "[LLUDPSERVER]: Sending packet #{0} (rel: {1}, res: {2}) to {3} from {4}", | ||
1210 | outgoingPacket.SequenceNumber, isReliable, isResend, udpClient.AgentID, Scene.Name); | ||
1211 | |||
1000 | // Put the UDP payload on the wire | 1212 | // Put the UDP payload on the wire |
1001 | AsyncBeginSend(buffer); | 1213 | AsyncBeginSend(buffer); |
1002 | 1214 | ||
@@ -1004,6 +1216,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1004 | outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue; | 1216 | outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue; |
1005 | } | 1217 | } |
1006 | 1218 | ||
1219 | private void RecordMalformedInboundPacket(IPEndPoint endPoint) | ||
1220 | { | ||
1221 | // if (m_malformedCount < 100) | ||
1222 | // m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString()); | ||
1223 | |||
1224 | IncomingMalformedPacketCount++; | ||
1225 | |||
1226 | if ((IncomingMalformedPacketCount % 10000) == 0) | ||
1227 | m_log.WarnFormat( | ||
1228 | "[LLUDPSERVER]: Received {0} malformed packets so far, probable network attack. Last was from {1}", | ||
1229 | IncomingMalformedPacketCount, endPoint); | ||
1230 | } | ||
1231 | |||
1007 | public override void PacketReceived(UDPPacketBuffer buffer) | 1232 | public override void PacketReceived(UDPPacketBuffer buffer) |
1008 | { | 1233 | { |
1009 | // Debugging/Profiling | 1234 | // Debugging/Profiling |
@@ -1025,6 +1250,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1025 | // "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}", | 1250 | // "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}", |
1026 | // buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); | 1251 | // buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); |
1027 | 1252 | ||
1253 | RecordMalformedInboundPacket(endPoint); | ||
1254 | |||
1028 | return; // Drop undersized packet | 1255 | return; // Drop undersized packet |
1029 | } | 1256 | } |
1030 | 1257 | ||
@@ -1043,6 +1270,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1043 | // "[LLUDPSERVER]: Dropping packet with malformed header received from {0} in {1}", | 1270 | // "[LLUDPSERVER]: Dropping packet with malformed header received from {0} in {1}", |
1044 | // buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); | 1271 | // buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); |
1045 | 1272 | ||
1273 | RecordMalformedInboundPacket(endPoint); | ||
1274 | |||
1046 | return; // Malformed header | 1275 | return; // Malformed header |
1047 | } | 1276 | } |
1048 | 1277 | ||
@@ -1058,34 +1287,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1058 | // Only allocate a buffer for zerodecoding if the packet is zerocoded | 1287 | // Only allocate a buffer for zerodecoding if the packet is zerocoded |
1059 | ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); | 1288 | ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); |
1060 | } | 1289 | } |
1061 | catch (MalformedDataException) | ||
1062 | { | ||
1063 | } | ||
1064 | catch (IndexOutOfRangeException) | ||
1065 | { | ||
1066 | // m_log.WarnFormat( | ||
1067 | // "[LLUDPSERVER]: Dropping short packet received from {0} in {1}", | ||
1068 | // buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); | ||
1069 | |||
1070 | return; // Drop short packet | ||
1071 | } | ||
1072 | catch (Exception e) | 1290 | catch (Exception e) |
1073 | { | 1291 | { |
1074 | if (m_malformedCount < 100) | 1292 | if (IncomingMalformedPacketCount < 100) |
1075 | m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString()); | 1293 | m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString()); |
1076 | |||
1077 | m_malformedCount++; | ||
1078 | |||
1079 | if ((m_malformedCount % 100000) == 0) | ||
1080 | m_log.DebugFormat("[LLUDPSERVER]: Received {0} malformed packets so far, probable network attack.", m_malformedCount); | ||
1081 | } | 1294 | } |
1082 | 1295 | ||
1083 | // Fail-safe check | 1296 | // Fail-safe check |
1084 | if (packet == null) | 1297 | if (packet == null) |
1085 | { | 1298 | { |
1086 | m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse {0} byte packet from {1}:", | 1299 | if (IncomingMalformedPacketCount < 100) |
1087 | buffer.DataLength, buffer.RemoteEndPoint); | 1300 | { |
1088 | m_log.Error(Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); | 1301 | m_log.WarnFormat("[LLUDPSERVER]: Malformed data, cannot parse {0} byte packet from {1}, data {2}:", |
1302 | buffer.DataLength, buffer.RemoteEndPoint, Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); | ||
1303 | } | ||
1304 | |||
1305 | RecordMalformedInboundPacket(endPoint); | ||
1306 | |||
1089 | return; | 1307 | return; |
1090 | } | 1308 | } |
1091 | 1309 | ||
@@ -1100,16 +1318,38 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1100 | // buffer. | 1318 | // buffer. |
1101 | object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; | 1319 | object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; |
1102 | 1320 | ||
1103 | Util.FireAndForget(HandleUseCircuitCode, array); | 1321 | Util.FireAndForget(HandleUseCircuitCode, array, "LLUDPServer.HandleUseCircuitCode"); |
1322 | |||
1323 | return; | ||
1324 | } | ||
1325 | else if (packet.Type == PacketType.CompleteAgentMovement) | ||
1326 | { | ||
1327 | // Send ack straight away to let the viewer know that we got it. | ||
1328 | SendAckImmediate(endPoint, packet.Header.Sequence); | ||
1329 | |||
1330 | // We need to copy the endpoint so that it doesn't get changed when another thread reuses the | ||
1331 | // buffer. | ||
1332 | object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; | ||
1333 | |||
1334 | Util.FireAndForget( | ||
1335 | HandleCompleteMovementIntoRegion, array, "LLUDPServer.HandleCompleteMovementIntoRegion"); | ||
1104 | 1336 | ||
1105 | return; | 1337 | return; |
1106 | } | 1338 | } |
1107 | 1339 | ||
1108 | // Determine which agent this packet came from | 1340 | // Determine which agent this packet came from |
1109 | IClientAPI client; | 1341 | IClientAPI client; |
1110 | if (!m_scene.TryGetClient(endPoint, out client) || !(client is LLClientView)) | 1342 | if (!Scene.TryGetClient(endPoint, out client) || !(client is LLClientView)) |
1111 | { | 1343 | { |
1112 | //m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName); | 1344 | //m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName); |
1345 | |||
1346 | IncomingOrphanedPacketCount++; | ||
1347 | |||
1348 | if ((IncomingOrphanedPacketCount % 10000) == 0) | ||
1349 | m_log.WarnFormat( | ||
1350 | "[LLUDPSERVER]: Received {0} orphaned packets so far. Last was from {1}", | ||
1351 | IncomingOrphanedPacketCount, endPoint); | ||
1352 | |||
1113 | return; | 1353 | return; |
1114 | } | 1354 | } |
1115 | 1355 | ||
@@ -1128,30 +1368,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1128 | 1368 | ||
1129 | #region ACK Receiving | 1369 | #region ACK Receiving |
1130 | 1370 | ||
1131 | // Handle appended ACKs | 1371 | if (udpClient.ProcessUnackedSends) |
1132 | if (packet.Header.AppendedAcks && packet.Header.AckList != null) | ||
1133 | { | 1372 | { |
1134 | // m_log.DebugFormat( | 1373 | // Handle appended ACKs |
1135 | // "[LLUDPSERVER]: Handling {0} appended acks from {1} in {2}", | 1374 | if (packet.Header.AppendedAcks && packet.Header.AckList != null) |
1136 | // packet.Header.AckList.Length, client.Name, m_scene.Name); | 1375 | { |
1376 | // m_log.DebugFormat( | ||
1377 | // "[LLUDPSERVER]: Handling {0} appended acks from {1} in {2}", | ||
1378 | // packet.Header.AckList.Length, client.Name, m_scene.Name); | ||
1137 | 1379 | ||
1138 | for (int i = 0; i < packet.Header.AckList.Length; i++) | 1380 | for (int i = 0; i < packet.Header.AckList.Length; i++) |
1139 | udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent); | 1381 | udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent); |
1140 | } | 1382 | } |
1141 | 1383 | ||
1142 | // Handle PacketAck packets | 1384 | // Handle PacketAck packets |
1143 | if (packet.Type == PacketType.PacketAck) | 1385 | if (packet.Type == PacketType.PacketAck) |
1144 | { | 1386 | { |
1145 | PacketAckPacket ackPacket = (PacketAckPacket)packet; | 1387 | PacketAckPacket ackPacket = (PacketAckPacket)packet; |
1146 | 1388 | ||
1147 | // m_log.DebugFormat( | 1389 | // m_log.DebugFormat( |
1148 | // "[LLUDPSERVER]: Handling {0} packet acks for {1} in {2}", | 1390 | // "[LLUDPSERVER]: Handling {0} packet acks for {1} in {2}", |
1149 | // ackPacket.Packets.Length, client.Name, m_scene.Name); | 1391 | // ackPacket.Packets.Length, client.Name, m_scene.Name); |
1150 | 1392 | ||
1151 | for (int i = 0; i < ackPacket.Packets.Length; i++) | 1393 | for (int i = 0; i < ackPacket.Packets.Length; i++) |
1152 | udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent); | 1394 | udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent); |
1153 | 1395 | ||
1154 | // We don't need to do anything else with PacketAck packets | 1396 | // We don't need to do anything else with PacketAck packets |
1397 | return; | ||
1398 | } | ||
1399 | } | ||
1400 | else if (packet.Type == PacketType.PacketAck) | ||
1401 | { | ||
1155 | return; | 1402 | return; |
1156 | } | 1403 | } |
1157 | 1404 | ||
@@ -1185,6 +1432,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1185 | 1432 | ||
1186 | #region Incoming Packet Accounting | 1433 | #region Incoming Packet Accounting |
1187 | 1434 | ||
1435 | // We're not going to worry about interlock yet since its not currently critical that this total count | ||
1436 | // is 100% correct | ||
1437 | if (packet.Header.Resent) | ||
1438 | IncomingPacketsResentCount++; | ||
1439 | |||
1188 | // Check the archive of received reliable packet IDs to see whether we already received this packet | 1440 | // Check the archive of received reliable packet IDs to see whether we already received this packet |
1189 | if (packet.Header.Reliable && !udpClient.PacketArchive.TryEnqueue(packet.Header.Sequence)) | 1441 | if (packet.Header.Reliable && !udpClient.PacketArchive.TryEnqueue(packet.Header.Sequence)) |
1190 | { | 1442 | { |
@@ -1207,6 +1459,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1207 | LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length); | 1459 | LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length); |
1208 | #endregion BinaryStats | 1460 | #endregion BinaryStats |
1209 | 1461 | ||
1462 | if (packet.Type == PacketType.AgentUpdate) | ||
1463 | { | ||
1464 | if (DiscardInboundAgentUpdates) | ||
1465 | return; | ||
1466 | |||
1467 | ((LLClientView)client).TotalAgentUpdates++; | ||
1468 | |||
1469 | AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet; | ||
1470 | |||
1471 | LLClientView llClient = client as LLClientView; | ||
1472 | if (agentUpdate.AgentData.SessionID != client.SessionId | ||
1473 | || agentUpdate.AgentData.AgentID != client.AgentId | ||
1474 | || !(llClient == null || llClient.CheckAgentUpdateSignificance(agentUpdate.AgentData)) ) | ||
1475 | { | ||
1476 | PacketPool.Instance.ReturnPacket(packet); | ||
1477 | return; | ||
1478 | } | ||
1479 | } | ||
1480 | |||
1210 | #region Ping Check Handling | 1481 | #region Ping Check Handling |
1211 | 1482 | ||
1212 | if (packet.Type == PacketType.StartPingCheck) | 1483 | if (packet.Type == PacketType.StartPingCheck) |
@@ -1266,8 +1537,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1266 | static object binStatsLogLock = new object(); | 1537 | static object binStatsLogLock = new object(); |
1267 | static string binStatsDir = ""; | 1538 | static string binStatsDir = ""; |
1268 | 1539 | ||
1540 | //for Aggregated In/Out BW logging | ||
1541 | static bool m_aggregatedBWStats = false; | ||
1542 | static long m_aggregatedBytesIn = 0; | ||
1543 | static long m_aggregatedByestOut = 0; | ||
1544 | static object aggBWStatsLock = new object(); | ||
1545 | |||
1546 | public static long AggregatedLLUDPBytesIn | ||
1547 | { | ||
1548 | get { return m_aggregatedBytesIn; } | ||
1549 | } | ||
1550 | public static long AggregatedLLUDPBytesOut | ||
1551 | { | ||
1552 | get {return m_aggregatedByestOut;} | ||
1553 | } | ||
1554 | |||
1269 | public static void LogPacketHeader(bool incoming, uint circuit, byte flags, PacketType packetType, ushort size) | 1555 | public static void LogPacketHeader(bool incoming, uint circuit, byte flags, PacketType packetType, ushort size) |
1270 | { | 1556 | { |
1557 | if (m_aggregatedBWStats) | ||
1558 | { | ||
1559 | lock (aggBWStatsLock) | ||
1560 | { | ||
1561 | if (incoming) | ||
1562 | m_aggregatedBytesIn += size; | ||
1563 | else | ||
1564 | m_aggregatedByestOut += size; | ||
1565 | } | ||
1566 | } | ||
1567 | |||
1271 | if (!m_shouldCollectStats) return; | 1568 | if (!m_shouldCollectStats) return; |
1272 | 1569 | ||
1273 | // Binary logging format is TTTTTTTTCCCCFPPPSS, T=Time, C=Circuit, F=Flags, P=PacketType, S=size | 1570 | // Binary logging format is TTTTTTTTCCCCFPPPSS, T=Time, C=Circuit, F=Flags, P=PacketType, S=size |
@@ -1344,7 +1641,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1344 | 1641 | ||
1345 | m_log.DebugFormat( | 1642 | m_log.DebugFormat( |
1346 | "[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}", | 1643 | "[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}", |
1347 | uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, endPoint); | 1644 | uccp.CircuitCode.Code, Scene.RegionInfo.RegionName, endPoint); |
1348 | 1645 | ||
1349 | AuthenticateResponse sessionInfo; | 1646 | AuthenticateResponse sessionInfo; |
1350 | if (IsClientAuthorized(uccp, out sessionInfo)) | 1647 | if (IsClientAuthorized(uccp, out sessionInfo)) |
@@ -1365,14 +1662,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1365 | 1662 | ||
1366 | // We only want to send initial data to new clients, not ones which are being converted from child to root. | 1663 | // We only want to send initial data to new clients, not ones which are being converted from child to root. |
1367 | if (client != null) | 1664 | if (client != null) |
1368 | client.SceneAgent.SendInitialDataToMe(); | 1665 | { |
1666 | AgentCircuitData aCircuit = Scene.AuthenticateHandler.GetAgentCircuitData(uccp.CircuitCode.Code); | ||
1667 | bool tp = (aCircuit.teleportFlags > 0); | ||
1668 | // Let's delay this for TP agents, otherwise the viewer doesn't know where to get resources from | ||
1669 | if (!tp && !client.SceneAgent.SentInitialDataToClient) | ||
1670 | client.SceneAgent.SendInitialDataToClient(); | ||
1671 | } | ||
1369 | } | 1672 | } |
1370 | else | 1673 | else |
1371 | { | 1674 | { |
1372 | // Don't create clients for unauthorized requesters. | 1675 | // Don't create clients for unauthorized requesters. |
1373 | m_log.WarnFormat( | 1676 | m_log.WarnFormat( |
1374 | "[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}", | 1677 | "[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}", |
1375 | uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, endPoint); | 1678 | uccp.CircuitCode.ID, Scene.RegionInfo.RegionName, uccp.CircuitCode.Code, endPoint); |
1376 | } | 1679 | } |
1377 | 1680 | ||
1378 | // m_log.DebugFormat( | 1681 | // m_log.DebugFormat( |
@@ -1392,6 +1695,116 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1392 | } | 1695 | } |
1393 | } | 1696 | } |
1394 | 1697 | ||
1698 | private void HandleCompleteMovementIntoRegion(object o) | ||
1699 | { | ||
1700 | IPEndPoint endPoint = null; | ||
1701 | IClientAPI client = null; | ||
1702 | |||
1703 | try | ||
1704 | { | ||
1705 | object[] array = (object[])o; | ||
1706 | endPoint = (IPEndPoint)array[0]; | ||
1707 | CompleteAgentMovementPacket packet = (CompleteAgentMovementPacket)array[1]; | ||
1708 | |||
1709 | m_log.DebugFormat( | ||
1710 | "[LLUDPSERVER]: Handling CompleteAgentMovement request from {0} in {1}", endPoint, Scene.Name); | ||
1711 | |||
1712 | // Determine which agent this packet came from | ||
1713 | // We need to wait here because in when using the OpenSimulator V2 teleport protocol to travel to a destination | ||
1714 | // simulator with no existing child presence, the viewer (at least LL 3.3.4) will send UseCircuitCode | ||
1715 | // and then CompleteAgentMovement immediately without waiting for an ack. As we are now handling these | ||
1716 | // packets asynchronously, we need to account for this thread proceeding more quickly than the | ||
1717 | // UseCircuitCode thread. | ||
1718 | int count = 40; | ||
1719 | while (count-- > 0) | ||
1720 | { | ||
1721 | if (Scene.TryGetClient(endPoint, out client)) | ||
1722 | { | ||
1723 | if (!client.IsActive) | ||
1724 | { | ||
1725 | // This check exists to catch a condition where the client has been closed by another thread | ||
1726 | // but has not yet been removed from the client manager (and possibly a new connection has | ||
1727 | // not yet been established). | ||
1728 | m_log.DebugFormat( | ||
1729 | "[LLUDPSERVER]: Received a CompleteAgentMovement from {0} for {1} in {2} but client is not active yet. Waiting.", | ||
1730 | endPoint, client.Name, Scene.Name); | ||
1731 | } | ||
1732 | else if (client.SceneAgent == null) | ||
1733 | { | ||
1734 | // This check exists to catch a condition where the new client has been added to the client | ||
1735 | // manager but the SceneAgent has not yet been set in Scene.AddNewAgent(). If we are too | ||
1736 | // eager, then the new ScenePresence may not have registered a listener for this messsage | ||
1737 | // before we try to process it. | ||
1738 | // XXX: A better long term fix may be to add the SceneAgent before the client is added to | ||
1739 | // the client manager | ||
1740 | m_log.DebugFormat( | ||
1741 | "[LLUDPSERVER]: Received a CompleteAgentMovement from {0} for {1} in {2} but client SceneAgent not set yet. Waiting.", | ||
1742 | endPoint, client.Name, Scene.Name); | ||
1743 | } | ||
1744 | else | ||
1745 | { | ||
1746 | break; | ||
1747 | } | ||
1748 | } | ||
1749 | else | ||
1750 | { | ||
1751 | m_log.DebugFormat( | ||
1752 | "[LLUDPSERVER]: Received a CompleteAgentMovement from {0} in {1} but no client exists yet. Waiting.", | ||
1753 | endPoint, Scene.Name); | ||
1754 | } | ||
1755 | |||
1756 | Thread.Sleep(200); | ||
1757 | } | ||
1758 | |||
1759 | if (client == null) | ||
1760 | { | ||
1761 | m_log.DebugFormat( | ||
1762 | "[LLUDPSERVER]: No client found for CompleteAgentMovement from {0} in {1} after wait. Dropping.", | ||
1763 | endPoint, Scene.Name); | ||
1764 | |||
1765 | return; | ||
1766 | } | ||
1767 | else if (!client.IsActive || client.SceneAgent == null) | ||
1768 | { | ||
1769 | // This check exists to catch a condition where the client has been closed by another thread | ||
1770 | // but has not yet been removed from the client manager. | ||
1771 | // The packet could be simply ignored but it is useful to know if this condition occurred for other debugging | ||
1772 | // purposes. | ||
1773 | m_log.DebugFormat( | ||
1774 | "[LLUDPSERVER]: Received a CompleteAgentMovement from {0} for {1} in {2} but client is not active after wait. Dropping.", | ||
1775 | endPoint, client.Name, Scene.Name); | ||
1776 | |||
1777 | return; | ||
1778 | } | ||
1779 | |||
1780 | IncomingPacket incomingPacket1; | ||
1781 | |||
1782 | // Inbox insertion | ||
1783 | if (UsePools) | ||
1784 | { | ||
1785 | incomingPacket1 = m_incomingPacketPool.GetObject(); | ||
1786 | incomingPacket1.Client = (LLClientView)client; | ||
1787 | incomingPacket1.Packet = packet; | ||
1788 | } | ||
1789 | else | ||
1790 | { | ||
1791 | incomingPacket1 = new IncomingPacket((LLClientView)client, packet); | ||
1792 | } | ||
1793 | |||
1794 | packetInbox.Enqueue(incomingPacket1); | ||
1795 | } | ||
1796 | catch (Exception e) | ||
1797 | { | ||
1798 | m_log.ErrorFormat( | ||
1799 | "[LLUDPSERVER]: CompleteAgentMovement handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}", | ||
1800 | endPoint != null ? endPoint.ToString() : "n/a", | ||
1801 | client != null ? client.Name : "unknown", | ||
1802 | client != null ? client.AgentId.ToString() : "unknown", | ||
1803 | e.Message, | ||
1804 | e.StackTrace); | ||
1805 | } | ||
1806 | } | ||
1807 | |||
1395 | /// <summary> | 1808 | /// <summary> |
1396 | /// Send an ack immediately to the given endpoint. | 1809 | /// Send an ack immediately to the given endpoint. |
1397 | /// </summary> | 1810 | /// </summary> |
@@ -1454,12 +1867,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1454 | // consistently, this lock could probably be removed. | 1867 | // consistently, this lock could probably be removed. |
1455 | lock (this) | 1868 | lock (this) |
1456 | { | 1869 | { |
1457 | if (!m_scene.TryGetClient(agentID, out client)) | 1870 | if (!Scene.TryGetClient(agentID, out client)) |
1458 | { | 1871 | { |
1459 | LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO); | 1872 | LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, Throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO); |
1460 | 1873 | ||
1461 | client = new LLClientView(m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); | 1874 | client = new LLClientView(Scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); |
1462 | client.OnLogout += LogoutHandler; | 1875 | client.OnLogout += LogoutHandler; |
1876 | client.DebugPacketLevel = DefaultClientPacketDebugLevel; | ||
1463 | 1877 | ||
1464 | ((LLClientView)client).DisableFacelights = m_disableFacelights; | 1878 | ((LLClientView)client).DisableFacelights = m_disableFacelights; |
1465 | 1879 | ||
@@ -1478,25 +1892,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1478 | /// regular client pings. | 1892 | /// regular client pings. |
1479 | /// </remarks> | 1893 | /// </remarks> |
1480 | /// <param name='client'></param> | 1894 | /// <param name='client'></param> |
1481 | private void DeactivateClientDueToTimeout(LLClientView client) | 1895 | /// <param name='timeoutTicks'></param> |
1896 | private void DeactivateClientDueToTimeout(LLClientView client, int timeoutTicks) | ||
1482 | { | 1897 | { |
1483 | lock (client.CloseSyncLock) | 1898 | lock (client.CloseSyncLock) |
1484 | { | 1899 | { |
1900 | ClientLogoutsDueToNoReceives++; | ||
1901 | |||
1485 | m_log.WarnFormat( | 1902 | m_log.WarnFormat( |
1486 | "[LLUDPSERVER]: Ack timeout, disconnecting {0} agent for {1} in {2}", | 1903 | "[LLUDPSERVER]: No packets received from {0} agent of {1} for {2}ms in {3}. Disconnecting.", |
1487 | client.SceneAgent.IsChildAgent ? "child" : "root", client.Name, m_scene.RegionInfo.RegionName); | 1904 | client.SceneAgent.IsChildAgent ? "child" : "root", client.Name, timeoutTicks, Scene.Name); |
1488 | |||
1489 | StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); | ||
1490 | 1905 | ||
1491 | if (!client.SceneAgent.IsChildAgent) | 1906 | if (!client.SceneAgent.IsChildAgent) |
1492 | client.Kick("Simulator logged you out due to connection timeout"); | 1907 | client.Kick("Simulator logged you out due to connection timeout."); |
1493 | |||
1494 | client.CloseWithoutChecks(); | ||
1495 | } | 1908 | } |
1909 | |||
1910 | Scene.CloseAgent(client.AgentId, true); | ||
1496 | } | 1911 | } |
1497 | 1912 | ||
1498 | private void IncomingPacketHandler() | 1913 | private void IncomingPacketHandler() |
1499 | { | 1914 | { |
1915 | Thread.CurrentThread.Priority = ThreadPriority.Highest; | ||
1916 | |||
1500 | // Set this culture for the thread that incoming packets are received | 1917 | // Set this culture for the thread that incoming packets are received |
1501 | // on to en-US to avoid number parsing issues | 1918 | // on to en-US to avoid number parsing issues |
1502 | Culture.SetCurrentCulture(); | 1919 | Culture.SetCurrentCulture(); |
@@ -1507,6 +1924,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1507 | { | 1924 | { |
1508 | IncomingPacket incomingPacket = null; | 1925 | IncomingPacket incomingPacket = null; |
1509 | 1926 | ||
1927 | /* | ||
1510 | // HACK: This is a test to try and rate limit packet handling on Mono. | 1928 | // HACK: This is a test to try and rate limit packet handling on Mono. |
1511 | // If it works, a more elegant solution can be devised | 1929 | // If it works, a more elegant solution can be devised |
1512 | if (Util.FireAndForgetCount() < 2) | 1930 | if (Util.FireAndForgetCount() < 2) |
@@ -1514,6 +1932,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1514 | //m_log.Debug("[LLUDPSERVER]: Incoming packet handler is sleeping"); | 1932 | //m_log.Debug("[LLUDPSERVER]: Incoming packet handler is sleeping"); |
1515 | Thread.Sleep(30); | 1933 | Thread.Sleep(30); |
1516 | } | 1934 | } |
1935 | */ | ||
1517 | 1936 | ||
1518 | if (packetInbox.Dequeue(100, ref incomingPacket)) | 1937 | if (packetInbox.Dequeue(100, ref incomingPacket)) |
1519 | { | 1938 | { |
@@ -1540,6 +1959,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1540 | 1959 | ||
1541 | private void OutgoingPacketHandler() | 1960 | private void OutgoingPacketHandler() |
1542 | { | 1961 | { |
1962 | Thread.CurrentThread.Priority = ThreadPriority.Highest; | ||
1963 | |||
1543 | // Set this culture for the thread that outgoing packets are sent | 1964 | // Set this culture for the thread that outgoing packets are sent |
1544 | // on to en-US to avoid number parsing issues | 1965 | // on to en-US to avoid number parsing issues |
1545 | Culture.SetCurrentCulture(); | 1966 | Culture.SetCurrentCulture(); |
@@ -1602,14 +2023,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1602 | 2023 | ||
1603 | // Handle outgoing packets, resends, acknowledgements, and pings for each | 2024 | // Handle outgoing packets, resends, acknowledgements, and pings for each |
1604 | // client. m_packetSent will be set to true if a packet is sent | 2025 | // client. m_packetSent will be set to true if a packet is sent |
1605 | m_scene.ForEachClient(clientPacketHandler); | 2026 | Scene.ForEachClient(clientPacketHandler); |
1606 | 2027 | ||
1607 | m_currentOutgoingClient = null; | 2028 | m_currentOutgoingClient = null; |
1608 | 2029 | ||
1609 | // If nothing was sent, sleep for the minimum amount of time before a | 2030 | // If nothing was sent, sleep for the minimum amount of time before a |
1610 | // token bucket could get more tokens | 2031 | // token bucket could get more tokens |
2032 | //if (!m_packetSent) | ||
2033 | // Thread.Sleep((int)TickCountResolution); | ||
2034 | // | ||
2035 | // Instead, now wait for data present to be explicitly signalled. Evidence so far is that with | ||
2036 | // modern mono it reduces CPU base load since there is no more continuous polling. | ||
1611 | if (!m_packetSent) | 2037 | if (!m_packetSent) |
1612 | Thread.Sleep((int)TickCountResolution); | 2038 | m_dataPresentEvent.WaitOne(100); |
1613 | 2039 | ||
1614 | Watchdog.UpdateThread(); | 2040 | Watchdog.UpdateThread(); |
1615 | } | 2041 | } |
@@ -1635,7 +2061,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1635 | 2061 | ||
1636 | if (udpClient.IsConnected) | 2062 | if (udpClient.IsConnected) |
1637 | { | 2063 | { |
1638 | if (m_resendUnacked) | 2064 | if (udpClient.ProcessUnackedSends && m_resendUnacked) |
1639 | HandleUnacked(llClient); | 2065 | HandleUnacked(llClient); |
1640 | 2066 | ||
1641 | if (m_sendAcks) | 2067 | if (m_sendAcks) |
@@ -1764,7 +2190,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1764 | watch1.Reset(); | 2190 | watch1.Reset(); |
1765 | 2191 | ||
1766 | // reuse this -- it's every ~100ms | 2192 | // reuse this -- it's every ~100ms |
1767 | if (m_scene.EmergencyMonitoring && nticks % 100 == 0) | 2193 | if (Scene.EmergencyMonitoring && nticks % 100 == 0) |
1768 | { | 2194 | { |
1769 | m_log.InfoFormat("[LLUDPSERVER]: avg processing ticks: {0} avg unacked: {1} avg acks: {2} avg ping: {3} avg dequeue: {4} (TickCountRes: {5} sent: {6} notsent: {7})", | 2195 | m_log.InfoFormat("[LLUDPSERVER]: avg processing ticks: {0} avg unacked: {1} avg acks: {2} avg ping: {3} avg dequeue: {4} (TickCountRes: {5} sent: {6} notsent: {7})", |
1770 | avgProcessingTicks, avgResendUnackedTicks, avgSendAcksTicks, avgSendPingTicks, avgDequeueTicks, TickCountResolution, npacksSent, npackNotSent); | 2196 | avgProcessingTicks, avgResendUnackedTicks, avgSendAcksTicks, avgSendPingTicks, avgDequeueTicks, TickCountResolution, npacksSent, npackNotSent); |
@@ -1813,7 +2239,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1813 | { | 2239 | { |
1814 | m_log.DebugFormat( | 2240 | m_log.DebugFormat( |
1815 | "[LLUDPSERVER]: Dropped incoming {0} for dead client {1} in {2}", | 2241 | "[LLUDPSERVER]: Dropped incoming {0} for dead client {1} in {2}", |
1816 | packet.Type, client.Name, m_scene.RegionInfo.RegionName); | 2242 | packet.Type, client.Name, Scene.RegionInfo.RegionName); |
1817 | } | 2243 | } |
1818 | 2244 | ||
1819 | IncomingPacketsProcessed++; | 2245 | IncomingPacketsProcessed++; |
@@ -1826,8 +2252,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1826 | if (!client.IsLoggingOut) | 2252 | if (!client.IsLoggingOut) |
1827 | { | 2253 | { |
1828 | client.IsLoggingOut = true; | 2254 | client.IsLoggingOut = true; |
1829 | client.Close(); | 2255 | Scene.CloseAgent(client.AgentId, false); |
1830 | } | 2256 | } |
1831 | } | 2257 | } |
1832 | } | 2258 | } |
1833 | } \ No newline at end of file | 2259 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs new file mode 100644 index 0000000..ac6c0b4 --- /dev/null +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs | |||
@@ -0,0 +1,901 @@ | |||
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.Generic; | ||
30 | using System.Text; | ||
31 | using NDesk.Options; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Framework.Console; | ||
34 | using OpenSim.Region.Framework.Scenes; | ||
35 | |||
36 | namespace OpenSim.Region.ClientStack.LindenUDP | ||
37 | { | ||
38 | public class LLUDPServerCommands | ||
39 | { | ||
40 | private ICommandConsole m_console; | ||
41 | private LLUDPServer m_udpServer; | ||
42 | |||
43 | public LLUDPServerCommands(ICommandConsole console, LLUDPServer udpServer) | ||
44 | { | ||
45 | m_console = console; | ||
46 | m_udpServer = udpServer; | ||
47 | } | ||
48 | |||
49 | public void Register() | ||
50 | { | ||
51 | m_console.Commands.AddCommand( | ||
52 | "Comms", false, "show server throttles", | ||
53 | "show server throttles", | ||
54 | "Show information about server throttles", | ||
55 | HandleShowServerThrottlesCommand); | ||
56 | |||
57 | m_console.Commands.AddCommand( | ||
58 | "Debug", false, "debug lludp packet", | ||
59 | "debug lludp packet [--default | --all] <level> [<avatar-first-name> <avatar-last-name>]", | ||
60 | "Turn on packet debugging. This logs information when the client stack hands a processed packet off to downstream code or when upstream code first requests that a certain packet be sent.", | ||
61 | "If level > 255 then all incoming and outgoing packets are logged.\n" | ||
62 | + "If level <= 255 then incoming AgentUpdate and outgoing SimStats and SimulatorViewerTimeMessage packets are not logged.\n" | ||
63 | + "If level <= 200 then incoming RequestImage and outgoing ImagePacket, ImageData, LayerData and CoarseLocationUpdate packets are not logged.\n" | ||
64 | + "If level <= 100 then incoming ViewerEffect and AgentAnimation and outgoing ViewerEffect and AvatarAnimation packets are not logged.\n" | ||
65 | + "If level <= 50 then outgoing ImprovedTerseObjectUpdate packets are not logged.\n" | ||
66 | + "If level <= 0 then no packets are logged.\n" | ||
67 | + "If --default is specified then the level becomes the default logging level for all subsequent agents.\n" | ||
68 | + "If --all is specified then the level becomes the default logging level for all current and subsequent agents.\n" | ||
69 | + "In these cases, you cannot also specify an avatar name.\n" | ||
70 | + "If an avatar name is given then only packets from that avatar are logged.", | ||
71 | HandlePacketCommand); | ||
72 | |||
73 | m_console.Commands.AddCommand( | ||
74 | "Debug", false, "debug lludp data out", | ||
75 | "debug lludp data out <level> <avatar-first-name> <avatar-last-name>\"", | ||
76 | "Turn on debugging for final outgoing data to the given user's client.", | ||
77 | "This operates at a much lower level than the packet command and prints out available details when the data is actually sent.\n" | ||
78 | + "If level > 0 then information about all outgoing UDP data for this avatar is logged.\n" | ||
79 | + "If level <= 0 then no information about outgoing UDP data for this avatar is logged.", | ||
80 | HandleDataCommand); | ||
81 | |||
82 | m_console.Commands.AddCommand( | ||
83 | "Debug", false, "debug lludp drop", | ||
84 | "debug lludp drop <in|out> <add|remove> <packet-name>", | ||
85 | "Drop all in or outbound packets that match the given name", | ||
86 | "For test purposes.", | ||
87 | HandleDropCommand); | ||
88 | |||
89 | m_console.Commands.AddCommand( | ||
90 | "Debug", | ||
91 | false, | ||
92 | "debug lludp start", | ||
93 | "debug lludp start <in|out|all>", | ||
94 | "Control LLUDP packet processing.", | ||
95 | "No effect if packet processing has already started.\n" | ||
96 | + "in - start inbound processing.\n" | ||
97 | + "out - start outbound processing.\n" | ||
98 | + "all - start in and outbound processing.\n", | ||
99 | HandleStartCommand); | ||
100 | |||
101 | m_console.Commands.AddCommand( | ||
102 | "Debug", | ||
103 | false, | ||
104 | "debug lludp stop", | ||
105 | "debug lludp stop <in|out|all>", | ||
106 | "Stop LLUDP packet processing.", | ||
107 | "No effect if packet processing has already stopped.\n" | ||
108 | + "in - stop inbound processing.\n" | ||
109 | + "out - stop outbound processing.\n" | ||
110 | + "all - stop in and outbound processing.\n", | ||
111 | HandleStopCommand); | ||
112 | |||
113 | m_console.Commands.AddCommand( | ||
114 | "Debug", | ||
115 | false, | ||
116 | "debug lludp pool", | ||
117 | "debug lludp pool <on|off>", | ||
118 | "Turn object pooling within the lludp component on or off.", | ||
119 | HandlePoolCommand); | ||
120 | |||
121 | m_console.Commands.AddCommand( | ||
122 | "Debug", | ||
123 | false, | ||
124 | "debug lludp status", | ||
125 | "debug lludp status", | ||
126 | "Return status of LLUDP packet processing.", | ||
127 | HandleStatusCommand); | ||
128 | |||
129 | m_console.Commands.AddCommand( | ||
130 | "Debug", | ||
131 | false, | ||
132 | "debug lludp throttles log", | ||
133 | "debug lludp throttles log <level> [<avatar-first-name> <avatar-last-name>]", | ||
134 | "Change debug logging level for throttles.", | ||
135 | "If level >= 0 then throttle debug logging is performed.\n" | ||
136 | + "If level <= 0 then no throttle debug logging is performed.", | ||
137 | HandleThrottleCommand); | ||
138 | |||
139 | m_console.Commands.AddCommand( | ||
140 | "Debug", | ||
141 | false, | ||
142 | "debug lludp throttles get", | ||
143 | "debug lludp throttles get [<avatar-first-name> <avatar-last-name>]", | ||
144 | "Return debug settings for throttles.", | ||
145 | "adaptive - true/false, controls adaptive throttle setting.\n" | ||
146 | + "request - request drip rate in kbps.\n" | ||
147 | + "max - the max kbps throttle allowed for the specified existing clients. Use 'debug lludp get new-client-throttle-max' to see the setting for new clients.\n", | ||
148 | HandleThrottleGetCommand); | ||
149 | |||
150 | m_console.Commands.AddCommand( | ||
151 | "Debug", | ||
152 | false, | ||
153 | "debug lludp throttles set", | ||
154 | "debug lludp throttles set <param> <value> [<avatar-first-name> <avatar-last-name>]", | ||
155 | "Set a throttle parameter for the given client.", | ||
156 | "adaptive - true/false, controls adaptive throttle setting.\n" | ||
157 | + "current - current drip rate in kbps.\n" | ||
158 | + "request - requested drip rate in kbps.\n" | ||
159 | + "max - the max kbps throttle allowed for the specified existing clients. Use 'debug lludp set new-client-throttle-max' to change the settings for new clients.\n", | ||
160 | HandleThrottleSetCommand); | ||
161 | |||
162 | m_console.Commands.AddCommand( | ||
163 | "Debug", | ||
164 | false, | ||
165 | "debug lludp get", | ||
166 | "debug lludp get", | ||
167 | "Get debug parameters for the server.", | ||
168 | "max-scene-throttle - the current max cumulative kbps provided for this scene to clients.\n" | ||
169 | + "max-new-client-throttle - the max kbps throttle allowed to new clients. Use 'debug lludp throttles get max' to see the settings for existing clients.", | ||
170 | HandleGetCommand); | ||
171 | |||
172 | m_console.Commands.AddCommand( | ||
173 | "Debug", | ||
174 | false, | ||
175 | "debug lludp set", | ||
176 | "debug lludp set <param> <value>", | ||
177 | "Set a parameter for the server.", | ||
178 | "max-scene-throttle - the current max cumulative kbps provided for this scene to clients.\n" | ||
179 | + "max-new-client-throttle - the max kbps throttle allowed to each new client. Use 'debug lludp throttles set max' to set for existing clients.", | ||
180 | HandleSetCommand); | ||
181 | |||
182 | m_console.Commands.AddCommand( | ||
183 | "Debug", | ||
184 | false, | ||
185 | "debug lludp toggle agentupdate", | ||
186 | "debug lludp toggle agentupdate", | ||
187 | "Toggle whether agentupdate packets are processed or simply discarded.", | ||
188 | HandleAgentUpdateCommand); | ||
189 | |||
190 | MainConsole.Instance.Commands.AddCommand( | ||
191 | "Debug", | ||
192 | false, | ||
193 | "debug lludp oqre", | ||
194 | "debug lludp oqre <start|stop|status>", | ||
195 | "Start, stop or get status of OutgoingQueueRefillEngine.", | ||
196 | "If stopped then refill requests are processed directly via the threadpool.", | ||
197 | HandleOqreCommand); | ||
198 | |||
199 | m_console.Commands.AddCommand( | ||
200 | "Debug", | ||
201 | false, | ||
202 | "debug lludp client get", | ||
203 | "debug lludp client get [<avatar-first-name> <avatar-last-name>]", | ||
204 | "Get debug parameters for the client. If no name is given then all client information is returned.", | ||
205 | "process-unacked-sends - Do we take action if a sent reliable packet has not been acked.", | ||
206 | HandleClientGetCommand); | ||
207 | |||
208 | m_console.Commands.AddCommand( | ||
209 | "Debug", | ||
210 | false, | ||
211 | "debug lludp client set", | ||
212 | "debug lludp client set <param> <value> [<avatar-first-name> <avatar-last-name>]", | ||
213 | "Set a debug parameter for a particular client. If no name is given then the value is set on all clients.", | ||
214 | "process-unacked-sends - Do we take action if a sent reliable packet has not been acked.", | ||
215 | HandleClientSetCommand); | ||
216 | } | ||
217 | |||
218 | private void HandleShowServerThrottlesCommand(string module, string[] args) | ||
219 | { | ||
220 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
221 | return; | ||
222 | |||
223 | m_console.OutputFormat("Throttles for {0}", m_udpServer.Scene.Name); | ||
224 | ConsoleDisplayList cdl = new ConsoleDisplayList(); | ||
225 | cdl.AddRow("Adaptive throttles", m_udpServer.ThrottleRates.AdaptiveThrottlesEnabled); | ||
226 | |||
227 | long maxSceneDripRate = m_udpServer.Throttle.MaxDripRate; | ||
228 | cdl.AddRow( | ||
229 | "Max scene throttle", | ||
230 | maxSceneDripRate != 0 ? string.Format("{0} kbps", maxSceneDripRate * 8 / 1000) : "unset"); | ||
231 | |||
232 | int maxClientDripRate = m_udpServer.ThrottleRates.Total; | ||
233 | cdl.AddRow( | ||
234 | "Max new client throttle", | ||
235 | maxClientDripRate != 0 ? string.Format("{0} kbps", maxClientDripRate * 8 / 1000) : "unset"); | ||
236 | |||
237 | m_console.Output(cdl.ToString()); | ||
238 | |||
239 | m_console.OutputFormat("{0}\n", GetServerThrottlesReport(m_udpServer)); | ||
240 | } | ||
241 | |||
242 | private string GetServerThrottlesReport(LLUDPServer udpServer) | ||
243 | { | ||
244 | StringBuilder report = new StringBuilder(); | ||
245 | |||
246 | report.AppendFormat( | ||
247 | "{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}\n", | ||
248 | "Total", | ||
249 | "Resend", | ||
250 | "Land", | ||
251 | "Wind", | ||
252 | "Cloud", | ||
253 | "Task", | ||
254 | "Texture", | ||
255 | "Asset"); | ||
256 | |||
257 | report.AppendFormat( | ||
258 | "{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}\n", | ||
259 | "kb/s", | ||
260 | "kb/s", | ||
261 | "kb/s", | ||
262 | "kb/s", | ||
263 | "kb/s", | ||
264 | "kb/s", | ||
265 | "kb/s", | ||
266 | "kb/s"); | ||
267 | |||
268 | ThrottleRates throttleRates = udpServer.ThrottleRates; | ||
269 | report.AppendFormat( | ||
270 | "{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}", | ||
271 | (throttleRates.Total * 8) / 1000, | ||
272 | (throttleRates.Resend * 8) / 1000, | ||
273 | (throttleRates.Land * 8) / 1000, | ||
274 | (throttleRates.Wind * 8) / 1000, | ||
275 | (throttleRates.Cloud * 8) / 1000, | ||
276 | (throttleRates.Task * 8) / 1000, | ||
277 | (throttleRates.Texture * 8) / 1000, | ||
278 | (throttleRates.Asset * 8) / 1000); | ||
279 | |||
280 | return report.ToString(); | ||
281 | } | ||
282 | |||
283 | protected string GetColumnEntry(string entry, int maxLength, int columnPadding) | ||
284 | { | ||
285 | return string.Format( | ||
286 | "{0,-" + maxLength + "}{1,-" + columnPadding + "}", | ||
287 | entry.Length > maxLength ? entry.Substring(0, maxLength) : entry, | ||
288 | ""); | ||
289 | } | ||
290 | |||
291 | private void HandleDataCommand(string module, string[] args) | ||
292 | { | ||
293 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
294 | return; | ||
295 | |||
296 | if (args.Length != 7) | ||
297 | { | ||
298 | MainConsole.Instance.OutputFormat("Usage: debug lludp data out <true|false> <avatar-first-name> <avatar-last-name>"); | ||
299 | return; | ||
300 | } | ||
301 | |||
302 | int level; | ||
303 | if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out level)) | ||
304 | return; | ||
305 | |||
306 | string firstName = args[5]; | ||
307 | string lastName = args[6]; | ||
308 | |||
309 | m_udpServer.Scene.ForEachScenePresence(sp => | ||
310 | { | ||
311 | if (sp.Firstname == firstName && sp.Lastname == lastName) | ||
312 | { | ||
313 | MainConsole.Instance.OutputFormat( | ||
314 | "Data debug for {0} ({1}) set to {2} in {3}", | ||
315 | sp.Name, sp.IsChildAgent ? "child" : "root", level, m_udpServer.Scene.Name); | ||
316 | |||
317 | ((LLClientView)sp.ControllingClient).UDPClient.DebugDataOutLevel = level; | ||
318 | } | ||
319 | }); | ||
320 | } | ||
321 | |||
322 | private void HandleThrottleCommand(string module, string[] args) | ||
323 | { | ||
324 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
325 | return; | ||
326 | |||
327 | bool all = args.Length == 5; | ||
328 | bool one = args.Length == 7; | ||
329 | |||
330 | if (!all && !one) | ||
331 | { | ||
332 | MainConsole.Instance.OutputFormat( | ||
333 | "Usage: debug lludp throttles log <level> [<avatar-first-name> <avatar-last-name>]"); | ||
334 | return; | ||
335 | } | ||
336 | |||
337 | int level; | ||
338 | if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out level)) | ||
339 | return; | ||
340 | |||
341 | string firstName = null; | ||
342 | string lastName = null; | ||
343 | |||
344 | if (one) | ||
345 | { | ||
346 | firstName = args[5]; | ||
347 | lastName = args[6]; | ||
348 | } | ||
349 | |||
350 | m_udpServer.Scene.ForEachScenePresence(sp => | ||
351 | { | ||
352 | if (all || (sp.Firstname == firstName && sp.Lastname == lastName)) | ||
353 | { | ||
354 | MainConsole.Instance.OutputFormat( | ||
355 | "Throttle log level for {0} ({1}) set to {2} in {3}", | ||
356 | sp.Name, sp.IsChildAgent ? "child" : "root", level, m_udpServer.Scene.Name); | ||
357 | |||
358 | ((LLClientView)sp.ControllingClient).UDPClient.ThrottleDebugLevel = level; | ||
359 | } | ||
360 | }); | ||
361 | } | ||
362 | |||
363 | private void HandleThrottleSetCommand(string module, string[] args) | ||
364 | { | ||
365 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
366 | return; | ||
367 | |||
368 | bool all = args.Length == 6; | ||
369 | bool one = args.Length == 8; | ||
370 | |||
371 | if (!all && !one) | ||
372 | { | ||
373 | MainConsole.Instance.OutputFormat( | ||
374 | "Usage: debug lludp throttles set <param> <value> [<avatar-first-name> <avatar-last-name>]"); | ||
375 | return; | ||
376 | } | ||
377 | |||
378 | string param = args[4]; | ||
379 | string rawValue = args[5]; | ||
380 | |||
381 | string firstName = null; | ||
382 | string lastName = null; | ||
383 | |||
384 | if (one) | ||
385 | { | ||
386 | firstName = args[6]; | ||
387 | lastName = args[7]; | ||
388 | } | ||
389 | |||
390 | if (param == "adaptive") | ||
391 | { | ||
392 | bool newValue; | ||
393 | if (!ConsoleUtil.TryParseConsoleBool(MainConsole.Instance, rawValue, out newValue)) | ||
394 | return; | ||
395 | |||
396 | m_udpServer.Scene.ForEachScenePresence(sp => | ||
397 | { | ||
398 | if (all || (sp.Firstname == firstName && sp.Lastname == lastName)) | ||
399 | { | ||
400 | MainConsole.Instance.OutputFormat( | ||
401 | "Setting param {0} to {1} for {2} ({3}) in {4}", | ||
402 | param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name); | ||
403 | |||
404 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; | ||
405 | udpClient.FlowThrottle.AdaptiveEnabled = newValue; | ||
406 | // udpClient.FlowThrottle.MaxDripRate = 0; | ||
407 | // udpClient.FlowThrottle.AdjustedDripRate = 0; | ||
408 | } | ||
409 | }); | ||
410 | } | ||
411 | else if (param == "request") | ||
412 | { | ||
413 | int newValue; | ||
414 | if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue)) | ||
415 | return; | ||
416 | |||
417 | int newCurrentThrottleKbps = newValue * 1000 / 8; | ||
418 | |||
419 | m_udpServer.Scene.ForEachScenePresence(sp => | ||
420 | { | ||
421 | if (all || (sp.Firstname == firstName && sp.Lastname == lastName)) | ||
422 | { | ||
423 | MainConsole.Instance.OutputFormat( | ||
424 | "Setting param {0} to {1} for {2} ({3}) in {4}", | ||
425 | param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name); | ||
426 | |||
427 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; | ||
428 | udpClient.FlowThrottle.RequestedDripRate = newCurrentThrottleKbps; | ||
429 | } | ||
430 | }); | ||
431 | } | ||
432 | else if (param == "max") | ||
433 | { | ||
434 | int newValue; | ||
435 | if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue)) | ||
436 | return; | ||
437 | |||
438 | int newThrottleMaxKbps = newValue * 1000 / 8; | ||
439 | |||
440 | m_udpServer.Scene.ForEachScenePresence(sp => | ||
441 | { | ||
442 | if (all || (sp.Firstname == firstName && sp.Lastname == lastName)) | ||
443 | { | ||
444 | MainConsole.Instance.OutputFormat( | ||
445 | "Setting param {0} to {1} for {2} ({3}) in {4}", | ||
446 | param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name); | ||
447 | |||
448 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; | ||
449 | udpClient.FlowThrottle.MaxDripRate = newThrottleMaxKbps; | ||
450 | } | ||
451 | }); | ||
452 | } | ||
453 | } | ||
454 | |||
455 | private void HandleThrottleGetCommand(string module, string[] args) | ||
456 | { | ||
457 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
458 | return; | ||
459 | |||
460 | bool all = args.Length == 4; | ||
461 | bool one = args.Length == 6; | ||
462 | |||
463 | if (!all && !one) | ||
464 | { | ||
465 | MainConsole.Instance.OutputFormat( | ||
466 | "Usage: debug lludp throttles get [<avatar-first-name> <avatar-last-name>]"); | ||
467 | return; | ||
468 | } | ||
469 | |||
470 | string firstName = null; | ||
471 | string lastName = null; | ||
472 | |||
473 | if (one) | ||
474 | { | ||
475 | firstName = args[4]; | ||
476 | lastName = args[5]; | ||
477 | } | ||
478 | |||
479 | m_udpServer.Scene.ForEachScenePresence(sp => | ||
480 | { | ||
481 | if (all || (sp.Firstname == firstName && sp.Lastname == lastName)) | ||
482 | { | ||
483 | m_console.OutputFormat( | ||
484 | "Status for {0} ({1}) in {2}", | ||
485 | sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name); | ||
486 | |||
487 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; | ||
488 | |||
489 | ConsoleDisplayList cdl = new ConsoleDisplayList(); | ||
490 | cdl.AddRow("adaptive", udpClient.FlowThrottle.AdaptiveEnabled); | ||
491 | cdl.AddRow("current", string.Format("{0} kbps", udpClient.FlowThrottle.DripRate * 8 / 1000)); | ||
492 | cdl.AddRow("request", string.Format("{0} kbps", udpClient.FlowThrottle.RequestedDripRate * 8 / 1000)); | ||
493 | cdl.AddRow("max", string.Format("{0} kbps", udpClient.FlowThrottle.MaxDripRate * 8 / 1000)); | ||
494 | |||
495 | m_console.Output(cdl.ToString()); | ||
496 | } | ||
497 | }); | ||
498 | } | ||
499 | |||
500 | private void HandleGetCommand(string module, string[] args) | ||
501 | { | ||
502 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
503 | return; | ||
504 | |||
505 | m_console.OutputFormat("Debug settings for {0}", m_udpServer.Scene.Name); | ||
506 | ConsoleDisplayList cdl = new ConsoleDisplayList(); | ||
507 | |||
508 | long maxSceneDripRate = m_udpServer.Throttle.MaxDripRate; | ||
509 | cdl.AddRow( | ||
510 | "max-scene-throttle", | ||
511 | maxSceneDripRate != 0 ? string.Format("{0} kbps", maxSceneDripRate * 8 / 1000) : "unset"); | ||
512 | |||
513 | int maxClientDripRate = m_udpServer.ThrottleRates.Total; | ||
514 | cdl.AddRow( | ||
515 | "max-new-client-throttle", | ||
516 | maxClientDripRate != 0 ? string.Format("{0} kbps", maxClientDripRate * 8 / 1000) : "unset"); | ||
517 | |||
518 | m_console.Output(cdl.ToString()); | ||
519 | } | ||
520 | |||
521 | private void HandleSetCommand(string module, string[] args) | ||
522 | { | ||
523 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
524 | return; | ||
525 | |||
526 | if (args.Length != 5) | ||
527 | { | ||
528 | MainConsole.Instance.OutputFormat("Usage: debug lludp set <param> <value>"); | ||
529 | return; | ||
530 | } | ||
531 | |||
532 | string param = args[3]; | ||
533 | string rawValue = args[4]; | ||
534 | |||
535 | int newValue; | ||
536 | |||
537 | if (param == "max-scene-throttle") | ||
538 | { | ||
539 | if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue)) | ||
540 | return; | ||
541 | |||
542 | m_udpServer.Throttle.MaxDripRate = newValue * 1000 / 8; | ||
543 | } | ||
544 | else if (param == "max-new-client-throttle") | ||
545 | { | ||
546 | if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue)) | ||
547 | return; | ||
548 | |||
549 | m_udpServer.ThrottleRates.Total = newValue * 1000 / 8; | ||
550 | } | ||
551 | else | ||
552 | { | ||
553 | return; | ||
554 | } | ||
555 | |||
556 | m_console.OutputFormat("{0} set to {1} in {2}", param, rawValue, m_udpServer.Scene.Name); | ||
557 | } | ||
558 | |||
559 | private void HandleClientGetCommand(string module, string[] args) | ||
560 | { | ||
561 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
562 | return; | ||
563 | |||
564 | if (args.Length != 4 && args.Length != 6) | ||
565 | { | ||
566 | MainConsole.Instance.OutputFormat("Usage: debug lludp client get [<avatar-first-name> <avatar-last-name>]"); | ||
567 | return; | ||
568 | } | ||
569 | |||
570 | string name = null; | ||
571 | |||
572 | if (args.Length == 6) | ||
573 | name = string.Format("{0} {1}", args[4], args[5]); | ||
574 | |||
575 | m_udpServer.Scene.ForEachScenePresence( | ||
576 | sp => | ||
577 | { | ||
578 | if ((name == null || sp.Name == name) && sp.ControllingClient is LLClientView) | ||
579 | { | ||
580 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; | ||
581 | |||
582 | m_console.OutputFormat( | ||
583 | "Client debug parameters for {0} ({1}) in {2}", | ||
584 | sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name); | ||
585 | |||
586 | ConsoleDisplayList cdl = new ConsoleDisplayList(); | ||
587 | cdl.AddRow("process-unacked-sends", udpClient.ProcessUnackedSends); | ||
588 | |||
589 | m_console.Output(cdl.ToString()); | ||
590 | } | ||
591 | }); | ||
592 | } | ||
593 | |||
594 | private void HandleClientSetCommand(string module, string[] args) | ||
595 | { | ||
596 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
597 | return; | ||
598 | |||
599 | if (args.Length != 6 && args.Length != 8) | ||
600 | { | ||
601 | MainConsole.Instance.OutputFormat("Usage: debug lludp client set <param> <value> [<avatar-first-name> <avatar-last-name>]"); | ||
602 | return; | ||
603 | } | ||
604 | |||
605 | string param = args[4]; | ||
606 | string rawValue = args[5]; | ||
607 | |||
608 | string name = null; | ||
609 | |||
610 | if (args.Length == 8) | ||
611 | name = string.Format("{0} {1}", args[6], args[7]); | ||
612 | |||
613 | if (param == "process-unacked-sends") | ||
614 | { | ||
615 | bool newValue; | ||
616 | |||
617 | if (!ConsoleUtil.TryParseConsoleBool(MainConsole.Instance, rawValue, out newValue)) | ||
618 | return; | ||
619 | |||
620 | m_udpServer.Scene.ForEachScenePresence( | ||
621 | sp => | ||
622 | { | ||
623 | if ((name == null || sp.Name == name) && sp.ControllingClient is LLClientView) | ||
624 | { | ||
625 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; | ||
626 | udpClient.ProcessUnackedSends = newValue; | ||
627 | |||
628 | m_console.OutputFormat("{0} set to {1} for {2} in {3}", param, newValue, sp.Name, m_udpServer.Scene.Name); | ||
629 | } | ||
630 | }); | ||
631 | } | ||
632 | } | ||
633 | |||
634 | private void HandlePacketCommand(string module, string[] args) | ||
635 | { | ||
636 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
637 | return; | ||
638 | |||
639 | bool setAsDefaultLevel = false; | ||
640 | bool setAll = false; | ||
641 | OptionSet optionSet = new OptionSet() | ||
642 | .Add("default", o => setAsDefaultLevel = (o != null)) | ||
643 | .Add("all", o => setAll = (o != null)); | ||
644 | List<string> filteredArgs = optionSet.Parse(args); | ||
645 | |||
646 | string name = null; | ||
647 | |||
648 | if (filteredArgs.Count == 6) | ||
649 | { | ||
650 | if (!(setAsDefaultLevel || setAll)) | ||
651 | { | ||
652 | name = string.Format("{0} {1}", filteredArgs[4], filteredArgs[5]); | ||
653 | } | ||
654 | else | ||
655 | { | ||
656 | MainConsole.Instance.OutputFormat("ERROR: Cannot specify a user name when setting default/all logging level"); | ||
657 | return; | ||
658 | } | ||
659 | } | ||
660 | |||
661 | if (filteredArgs.Count > 3) | ||
662 | { | ||
663 | int newDebug; | ||
664 | if (int.TryParse(filteredArgs[3], out newDebug)) | ||
665 | { | ||
666 | if (setAsDefaultLevel || setAll) | ||
667 | { | ||
668 | m_udpServer.DefaultClientPacketDebugLevel = newDebug; | ||
669 | |||
670 | MainConsole.Instance.OutputFormat( | ||
671 | "Packet debug for {0} clients set to {1} in {2}", | ||
672 | (setAll ? "all" : "future"), m_udpServer.DefaultClientPacketDebugLevel, m_udpServer.Scene.Name); | ||
673 | |||
674 | if (setAll) | ||
675 | { | ||
676 | m_udpServer.Scene.ForEachScenePresence(sp => | ||
677 | { | ||
678 | MainConsole.Instance.OutputFormat( | ||
679 | "Packet debug for {0} ({1}) set to {2} in {3}", | ||
680 | sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, m_udpServer.Scene.Name); | ||
681 | |||
682 | sp.ControllingClient.DebugPacketLevel = newDebug; | ||
683 | }); | ||
684 | } | ||
685 | } | ||
686 | else | ||
687 | { | ||
688 | m_udpServer.Scene.ForEachScenePresence(sp => | ||
689 | { | ||
690 | if (name == null || sp.Name == name) | ||
691 | { | ||
692 | MainConsole.Instance.OutputFormat( | ||
693 | "Packet debug for {0} ({1}) set to {2} in {3}", | ||
694 | sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, m_udpServer.Scene.Name); | ||
695 | |||
696 | sp.ControllingClient.DebugPacketLevel = newDebug; | ||
697 | } | ||
698 | }); | ||
699 | } | ||
700 | } | ||
701 | else | ||
702 | { | ||
703 | MainConsole.Instance.Output("Usage: debug lludp packet [--default | --all] 0..255 [<first-name> <last-name>]"); | ||
704 | } | ||
705 | } | ||
706 | } | ||
707 | |||
708 | private void HandleDropCommand(string module, string[] args) | ||
709 | { | ||
710 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
711 | return; | ||
712 | |||
713 | if (args.Length != 6) | ||
714 | { | ||
715 | MainConsole.Instance.Output("Usage: debug lludp drop <in|out> <add|remove> <packet-name>"); | ||
716 | return; | ||
717 | } | ||
718 | |||
719 | string direction = args[3]; | ||
720 | string subCommand = args[4]; | ||
721 | string packetName = args[5]; | ||
722 | |||
723 | if (subCommand == "add") | ||
724 | { | ||
725 | MainConsole.Instance.OutputFormat( | ||
726 | "Adding packet {0} to {1} drop list for all connections in {2}", | ||
727 | direction, packetName, m_udpServer.Scene.Name); | ||
728 | |||
729 | m_udpServer.Scene.ForEachScenePresence( | ||
730 | sp => | ||
731 | { | ||
732 | LLClientView llcv = (LLClientView)sp.ControllingClient; | ||
733 | |||
734 | if (direction == "in") | ||
735 | llcv.AddInPacketToDropSet(packetName); | ||
736 | else if (direction == "out") | ||
737 | llcv.AddOutPacketToDropSet(packetName); | ||
738 | } | ||
739 | ); | ||
740 | } | ||
741 | else if (subCommand == "remove") | ||
742 | { | ||
743 | MainConsole.Instance.OutputFormat( | ||
744 | "Removing packet {0} from {1} drop list for all connections in {2}", | ||
745 | direction, packetName, m_udpServer.Scene.Name); | ||
746 | |||
747 | m_udpServer.Scene.ForEachScenePresence( | ||
748 | sp => | ||
749 | { | ||
750 | LLClientView llcv = (LLClientView)sp.ControllingClient; | ||
751 | |||
752 | if (direction == "in") | ||
753 | llcv.RemoveInPacketFromDropSet(packetName); | ||
754 | else if (direction == "out") | ||
755 | llcv.RemoveOutPacketFromDropSet(packetName); | ||
756 | } | ||
757 | ); | ||
758 | } | ||
759 | } | ||
760 | |||
761 | private void HandleStartCommand(string module, string[] args) | ||
762 | { | ||
763 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
764 | return; | ||
765 | |||
766 | if (args.Length != 4) | ||
767 | { | ||
768 | MainConsole.Instance.Output("Usage: debug lludp start <in|out|all>"); | ||
769 | return; | ||
770 | } | ||
771 | |||
772 | string subCommand = args[3]; | ||
773 | |||
774 | if (subCommand == "in" || subCommand == "all") | ||
775 | m_udpServer.StartInbound(); | ||
776 | |||
777 | if (subCommand == "out" || subCommand == "all") | ||
778 | m_udpServer.StartOutbound(); | ||
779 | } | ||
780 | |||
781 | private void HandleStopCommand(string module, string[] args) | ||
782 | { | ||
783 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
784 | return; | ||
785 | |||
786 | if (args.Length != 4) | ||
787 | { | ||
788 | MainConsole.Instance.Output("Usage: debug lludp stop <in|out|all>"); | ||
789 | return; | ||
790 | } | ||
791 | |||
792 | string subCommand = args[3]; | ||
793 | |||
794 | if (subCommand == "in" || subCommand == "all") | ||
795 | m_udpServer.StopInbound(); | ||
796 | |||
797 | if (subCommand == "out" || subCommand == "all") | ||
798 | m_udpServer.StopOutbound(); | ||
799 | } | ||
800 | |||
801 | private void HandlePoolCommand(string module, string[] args) | ||
802 | { | ||
803 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
804 | return; | ||
805 | |||
806 | if (args.Length != 4) | ||
807 | { | ||
808 | MainConsole.Instance.Output("Usage: debug lludp pool <on|off>"); | ||
809 | return; | ||
810 | } | ||
811 | |||
812 | string enabled = args[3]; | ||
813 | |||
814 | if (enabled == "on") | ||
815 | { | ||
816 | if (m_udpServer.EnablePools()) | ||
817 | { | ||
818 | m_udpServer.EnablePoolStats(); | ||
819 | MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", m_udpServer.Scene.Name); | ||
820 | } | ||
821 | } | ||
822 | else if (enabled == "off") | ||
823 | { | ||
824 | if (m_udpServer.DisablePools()) | ||
825 | { | ||
826 | m_udpServer.DisablePoolStats(); | ||
827 | MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", m_udpServer.Scene.Name); | ||
828 | } | ||
829 | } | ||
830 | else | ||
831 | { | ||
832 | MainConsole.Instance.Output("Usage: debug lludp pool <on|off>"); | ||
833 | } | ||
834 | } | ||
835 | |||
836 | private void HandleAgentUpdateCommand(string module, string[] args) | ||
837 | { | ||
838 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
839 | return; | ||
840 | |||
841 | m_udpServer.DiscardInboundAgentUpdates = !m_udpServer.DiscardInboundAgentUpdates; | ||
842 | |||
843 | MainConsole.Instance.OutputFormat( | ||
844 | "Discard AgentUpdates now {0} for {1}", m_udpServer.DiscardInboundAgentUpdates, m_udpServer.Scene.Name); | ||
845 | } | ||
846 | |||
847 | private void HandleStatusCommand(string module, string[] args) | ||
848 | { | ||
849 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
850 | return; | ||
851 | |||
852 | MainConsole.Instance.OutputFormat( | ||
853 | "IN LLUDP packet processing for {0} is {1}", m_udpServer.Scene.Name, m_udpServer.IsRunningInbound ? "enabled" : "disabled"); | ||
854 | |||
855 | MainConsole.Instance.OutputFormat( | ||
856 | "OUT LLUDP packet processing for {0} is {1}", m_udpServer.Scene.Name, m_udpServer.IsRunningOutbound ? "enabled" : "disabled"); | ||
857 | |||
858 | MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", m_udpServer.Scene.Name, m_udpServer.UsePools ? "on" : "off"); | ||
859 | |||
860 | MainConsole.Instance.OutputFormat( | ||
861 | "Packet debug level for new clients is {0}", m_udpServer.DefaultClientPacketDebugLevel); | ||
862 | } | ||
863 | |||
864 | private void HandleOqreCommand(string module, string[] args) | ||
865 | { | ||
866 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
867 | return; | ||
868 | |||
869 | if (args.Length != 4) | ||
870 | { | ||
871 | MainConsole.Instance.Output("Usage: debug lludp oqre <stop|start|status>"); | ||
872 | return; | ||
873 | } | ||
874 | |||
875 | string subCommand = args[3]; | ||
876 | |||
877 | if (subCommand == "stop") | ||
878 | { | ||
879 | m_udpServer.OqrEngine.Stop(); | ||
880 | MainConsole.Instance.OutputFormat("Stopped OQRE for {0}", m_udpServer.Scene.Name); | ||
881 | } | ||
882 | else if (subCommand == "start") | ||
883 | { | ||
884 | m_udpServer.OqrEngine.Start(); | ||
885 | MainConsole.Instance.OutputFormat("Started OQRE for {0}", m_udpServer.Scene.Name); | ||
886 | } | ||
887 | else if (subCommand == "status") | ||
888 | { | ||
889 | MainConsole.Instance.OutputFormat("OQRE in {0}", m_udpServer.Scene.Name); | ||
890 | MainConsole.Instance.OutputFormat("Running: {0}", m_udpServer.OqrEngine.IsRunning); | ||
891 | MainConsole.Instance.OutputFormat( | ||
892 | "Requests waiting: {0}", | ||
893 | m_udpServer.OqrEngine.IsRunning ? m_udpServer.OqrEngine.JobsWaiting.ToString() : "n/a"); | ||
894 | } | ||
895 | else | ||
896 | { | ||
897 | MainConsole.Instance.OutputFormat("Unrecognized OQRE subcommand {0}", subCommand); | ||
898 | } | ||
899 | } | ||
900 | } | ||
901 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs index f143c32..f62dc15 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs | |||
@@ -78,6 +78,92 @@ 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 | #region PacketDropDebugging | ||
111 | /// <summary> | ||
112 | /// For debugging purposes only... random number generator for dropping | ||
113 | /// outbound packets. | ||
114 | /// </summary> | ||
115 | private Random m_dropRandomGenerator = new Random(); | ||
116 | |||
117 | /// <summary> | ||
118 | /// For debugging purposes only... parameters for a simplified | ||
119 | /// model of packet loss with bursts, overall drop rate should | ||
120 | /// be roughly 1 - m_dropLengthProbability / (m_dropProbabiliy + m_dropLengthProbability) | ||
121 | /// which is about 1% for parameters 0.0015 and 0.15 | ||
122 | /// </summary> | ||
123 | private double m_dropProbability = 0.0030; | ||
124 | private double m_dropLengthProbability = 0.15; | ||
125 | private bool m_dropState = false; | ||
126 | |||
127 | /// <summary> | ||
128 | /// For debugging purposes only... parameters to control the time | ||
129 | /// duration over which packet loss bursts can occur, if no packets | ||
130 | /// have been sent for m_dropResetTicks milliseconds, then reset the | ||
131 | /// state of the packet dropper to its default. | ||
132 | /// </summary> | ||
133 | private int m_dropLastTick = 0; | ||
134 | private int m_dropResetTicks = 500; | ||
135 | |||
136 | /// <summary> | ||
137 | /// Debugging code used to simulate dropped packets with bursts | ||
138 | /// </summary> | ||
139 | private bool DropOutgoingPacket() | ||
140 | { | ||
141 | double rnum = m_dropRandomGenerator.NextDouble(); | ||
142 | |||
143 | // if the connection has been idle for awhile (more than m_dropResetTicks) then | ||
144 | // reset the state to the default state, don't continue a burst | ||
145 | int curtick = Util.EnvironmentTickCount(); | ||
146 | if (Util.EnvironmentTickCountSubtract(curtick, m_dropLastTick) > m_dropResetTicks) | ||
147 | m_dropState = false; | ||
148 | |||
149 | m_dropLastTick = curtick; | ||
150 | |||
151 | // if we are dropping packets, then the probability of dropping | ||
152 | // this packet is the probability that we stay in the burst | ||
153 | if (m_dropState) | ||
154 | { | ||
155 | m_dropState = (rnum < (1.0 - m_dropLengthProbability)) ? true : false; | ||
156 | } | ||
157 | else | ||
158 | { | ||
159 | m_dropState = (rnum < m_dropProbability) ? true : false; | ||
160 | } | ||
161 | |||
162 | return m_dropState; | ||
163 | } | ||
164 | #endregion PacketDropDebugging | ||
165 | |||
166 | /// <summary> | ||
81 | /// Default constructor | 167 | /// Default constructor |
82 | /// </summary> | 168 | /// </summary> |
83 | /// <param name="bindAddress">Local IP address to bind the server to</param> | 169 | /// <param name="bindAddress">Local IP address to bind the server to</param> |
@@ -87,6 +173,10 @@ namespace OpenMetaverse | |||
87 | { | 173 | { |
88 | m_localBindAddress = bindAddress; | 174 | m_localBindAddress = bindAddress; |
89 | m_udpPort = port; | 175 | m_udpPort = port; |
176 | |||
177 | // for debugging purposes only, initializes the random number generator | ||
178 | // used for simulating packet loss | ||
179 | // m_dropRandomGenerator = new Random(); | ||
90 | } | 180 | } |
91 | 181 | ||
92 | /// <summary> | 182 | /// <summary> |
@@ -105,12 +195,14 @@ namespace OpenMetaverse | |||
105 | /// manner (not throwing an exception when the remote side resets the | 195 | /// manner (not throwing an exception when the remote side resets the |
106 | /// connection). This call is ignored on Mono where the flag is not | 196 | /// connection). This call is ignored on Mono where the flag is not |
107 | /// necessary</remarks> | 197 | /// necessary</remarks> |
108 | public void StartInbound(int recvBufferSize, bool asyncPacketHandling) | 198 | public virtual void StartInbound(int recvBufferSize, bool asyncPacketHandling) |
109 | { | 199 | { |
110 | m_asyncPacketHandling = asyncPacketHandling; | 200 | m_asyncPacketHandling = asyncPacketHandling; |
111 | 201 | ||
112 | if (!IsRunningInbound) | 202 | if (!IsRunningInbound) |
113 | { | 203 | { |
204 | m_log.DebugFormat("[UDPBASE]: Starting inbound UDP loop"); | ||
205 | |||
114 | const int SIO_UDP_CONNRESET = -1744830452; | 206 | const int SIO_UDP_CONNRESET = -1744830452; |
115 | 207 | ||
116 | IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); | 208 | IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); |
@@ -126,6 +218,17 @@ namespace OpenMetaverse | |||
126 | 218 | ||
127 | try | 219 | try |
128 | { | 220 | { |
221 | if (m_udpSocket.Ttl < 128) | ||
222 | { | ||
223 | m_udpSocket.Ttl = 128; | ||
224 | } | ||
225 | } | ||
226 | catch (SocketException) | ||
227 | { | ||
228 | m_log.Debug("[UDPBASE]: Failed to increase default TTL"); | ||
229 | } | ||
230 | try | ||
231 | { | ||
129 | // This udp socket flag is not supported under mono, | 232 | // This udp socket flag is not supported under mono, |
130 | // so we'll catch the exception and continue | 233 | // so we'll catch the exception and continue |
131 | m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); | 234 | m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); |
@@ -136,6 +239,12 @@ namespace OpenMetaverse | |||
136 | m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring"); | 239 | m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring"); |
137 | } | 240 | } |
138 | 241 | ||
242 | // On at least Mono 3.2.8, multiple UDP sockets can bind to the same port by default. At the moment | ||
243 | // we never want two regions to listen on the same port as they cannot demultiplex each other's messages, | ||
244 | // leading to a confusing bug. | ||
245 | // By default, Windows does not allow two sockets to bind to the same port. | ||
246 | m_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false); | ||
247 | |||
139 | if (recvBufferSize != 0) | 248 | if (recvBufferSize != 0) |
140 | m_udpSocket.ReceiveBufferSize = recvBufferSize; | 249 | m_udpSocket.ReceiveBufferSize = recvBufferSize; |
141 | 250 | ||
@@ -153,30 +262,32 @@ namespace OpenMetaverse | |||
153 | /// <summary> | 262 | /// <summary> |
154 | /// Start outbound UDP packet handling. | 263 | /// Start outbound UDP packet handling. |
155 | /// </summary> | 264 | /// </summary> |
156 | public void StartOutbound() | 265 | public virtual void StartOutbound() |
157 | { | 266 | { |
267 | m_log.DebugFormat("[UDPBASE]: Starting outbound UDP loop"); | ||
268 | |||
158 | IsRunningOutbound = true; | 269 | IsRunningOutbound = true; |
159 | } | 270 | } |
160 | 271 | ||
161 | public void StopInbound() | 272 | public virtual void StopInbound() |
162 | { | 273 | { |
163 | if (IsRunningInbound) | 274 | if (IsRunningInbound) |
164 | { | 275 | { |
165 | // wait indefinitely for a writer lock. Once this is called, the .NET runtime | 276 | m_log.DebugFormat("[UDPBASE]: Stopping inbound UDP loop"); |
166 | // will deny any more reader locks, in effect blocking all other send/receive | 277 | |
167 | // threads. Once we have the lock, we set IsRunningInbound = false to inform the other | ||
168 | // threads that the socket is closed. | ||
169 | IsRunningInbound = false; | 278 | IsRunningInbound = false; |
170 | m_udpSocket.Close(); | 279 | m_udpSocket.Close(); |
171 | } | 280 | } |
172 | } | 281 | } |
173 | 282 | ||
174 | public void StopOutbound() | 283 | public virtual void StopOutbound() |
175 | { | 284 | { |
285 | m_log.DebugFormat("[UDPBASE]: Stopping outbound UDP loop"); | ||
286 | |||
176 | IsRunningOutbound = false; | 287 | IsRunningOutbound = false; |
177 | } | 288 | } |
178 | 289 | ||
179 | protected virtual bool EnablePools() | 290 | public virtual bool EnablePools() |
180 | { | 291 | { |
181 | if (!UsePools) | 292 | if (!UsePools) |
182 | { | 293 | { |
@@ -190,7 +301,7 @@ namespace OpenMetaverse | |||
190 | return false; | 301 | return false; |
191 | } | 302 | } |
192 | 303 | ||
193 | protected virtual bool DisablePools() | 304 | public virtual bool DisablePools() |
194 | { | 305 | { |
195 | if (UsePools) | 306 | if (UsePools) |
196 | { | 307 | { |
@@ -261,7 +372,16 @@ namespace OpenMetaverse | |||
261 | m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); | 372 | m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); |
262 | } | 373 | } |
263 | } | 374 | } |
264 | catch (ObjectDisposedException) { } | 375 | catch (ObjectDisposedException e) |
376 | { | ||
377 | m_log.Error( | ||
378 | string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); | ||
379 | } | ||
380 | catch (Exception e) | ||
381 | { | ||
382 | m_log.Error( | ||
383 | string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); | ||
384 | } | ||
265 | } | 385 | } |
266 | } | 386 | } |
267 | 387 | ||
@@ -271,17 +391,21 @@ namespace OpenMetaverse | |||
271 | // to AsyncBeginReceive | 391 | // to AsyncBeginReceive |
272 | if (IsRunningInbound) | 392 | if (IsRunningInbound) |
273 | { | 393 | { |
394 | UdpReceives++; | ||
395 | |||
274 | // Asynchronous mode will start another receive before the | 396 | // Asynchronous mode will start another receive before the |
275 | // callback for this packet is even fired. Very parallel :-) | 397 | // callback for this packet is even fired. Very parallel :-) |
276 | if (m_asyncPacketHandling) | 398 | if (m_asyncPacketHandling) |
277 | AsyncBeginReceive(); | 399 | AsyncBeginReceive(); |
278 | 400 | ||
279 | // get the buffer that was created in AsyncBeginReceive | ||
280 | // this is the received data | ||
281 | UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; | ||
282 | |||
283 | try | 401 | try |
284 | { | 402 | { |
403 | // get the buffer that was created in AsyncBeginReceive | ||
404 | // this is the received data | ||
405 | UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; | ||
406 | |||
407 | int startTick = Util.EnvironmentTickCount(); | ||
408 | |||
285 | // get the length of data actually read from the socket, store it with the | 409 | // get the length of data actually read from the socket, store it with the |
286 | // buffer | 410 | // buffer |
287 | buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); | 411 | buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); |
@@ -289,9 +413,42 @@ namespace OpenMetaverse | |||
289 | // call the abstract method PacketReceived(), passing the buffer that | 413 | // call the abstract method PacketReceived(), passing the buffer that |
290 | // has just been filled from the socket read. | 414 | // has just been filled from the socket read. |
291 | PacketReceived(buffer); | 415 | PacketReceived(buffer); |
416 | |||
417 | // If more than one thread can be calling AsyncEndReceive() at once (e.g. if m_asyncPacketHandler) | ||
418 | // then a particular stat may be inaccurate due to a race condition. We won't worry about this | ||
419 | // since this should be rare and won't cause a runtime problem. | ||
420 | if (m_currentReceiveTimeSamples >= s_receiveTimeSamples) | ||
421 | { | ||
422 | AverageReceiveTicksForLastSamplePeriod | ||
423 | = (float)m_receiveTicksInCurrentSamplePeriod / s_receiveTimeSamples; | ||
424 | |||
425 | m_receiveTicksInCurrentSamplePeriod = 0; | ||
426 | m_currentReceiveTimeSamples = 0; | ||
427 | } | ||
428 | else | ||
429 | { | ||
430 | m_receiveTicksInCurrentSamplePeriod += Util.EnvironmentTickCountSubtract(startTick); | ||
431 | m_currentReceiveTimeSamples++; | ||
432 | } | ||
433 | } | ||
434 | catch (SocketException se) | ||
435 | { | ||
436 | m_log.Error( | ||
437 | string.Format( | ||
438 | "[UDPBASE]: Error processing UDP end receive {0}, socket error code {1}. Exception ", | ||
439 | UdpReceives, se.ErrorCode), | ||
440 | se); | ||
441 | } | ||
442 | catch (ObjectDisposedException e) | ||
443 | { | ||
444 | m_log.Error( | ||
445 | string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e); | ||
446 | } | ||
447 | catch (Exception e) | ||
448 | { | ||
449 | m_log.Error( | ||
450 | string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e); | ||
292 | } | 451 | } |
293 | catch (SocketException) { } | ||
294 | catch (ObjectDisposedException) { } | ||
295 | finally | 452 | finally |
296 | { | 453 | { |
297 | // if (UsePools) | 454 | // if (UsePools) |
@@ -302,14 +459,19 @@ namespace OpenMetaverse | |||
302 | if (!m_asyncPacketHandling) | 459 | if (!m_asyncPacketHandling) |
303 | AsyncBeginReceive(); | 460 | AsyncBeginReceive(); |
304 | } | 461 | } |
305 | |||
306 | } | 462 | } |
307 | } | 463 | } |
308 | 464 | ||
309 | public void AsyncBeginSend(UDPPacketBuffer buf) | 465 | public void AsyncBeginSend(UDPPacketBuffer buf) |
310 | { | 466 | { |
311 | if (IsRunningOutbound) | 467 | // if (IsRunningOutbound) |
312 | { | 468 | // { |
469 | |||
470 | // This is strictly for debugging purposes to simulate dropped | ||
471 | // packets when testing throttles & retransmission code | ||
472 | // if (DropOutgoingPacket()) | ||
473 | // return; | ||
474 | |||
313 | try | 475 | try |
314 | { | 476 | { |
315 | m_udpSocket.BeginSendTo( | 477 | m_udpSocket.BeginSendTo( |
@@ -323,7 +485,7 @@ namespace OpenMetaverse | |||
323 | } | 485 | } |
324 | catch (SocketException) { } | 486 | catch (SocketException) { } |
325 | catch (ObjectDisposedException) { } | 487 | catch (ObjectDisposedException) { } |
326 | } | 488 | // } |
327 | } | 489 | } |
328 | 490 | ||
329 | void AsyncEndSend(IAsyncResult result) | 491 | void AsyncEndSend(IAsyncResult result) |
@@ -332,6 +494,8 @@ namespace OpenMetaverse | |||
332 | { | 494 | { |
333 | // UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; | 495 | // UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; |
334 | m_udpSocket.EndSendTo(result); | 496 | m_udpSocket.EndSendTo(result); |
497 | |||
498 | UdpSends++; | ||
335 | } | 499 | } |
336 | catch (SocketException) { } | 500 | catch (SocketException) { } |
337 | catch (ObjectDisposedException) { } | 501 | catch (ObjectDisposedException) { } |
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 af2f6f8..bf505b4 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Properties/AssemblyInfo.cs | |||
@@ -1,6 +1,7 @@ | |||
1 | using System.Reflection; | 1 | using System.Reflection; |
2 | using System.Runtime.CompilerServices; | 2 | using System.Runtime.CompilerServices; |
3 | using System.Runtime.InteropServices; | 3 | using System.Runtime.InteropServices; |
4 | using Mono.Addins; | ||
4 | 5 | ||
5 | // General Information about an assembly is controlled through the following | 6 | // General Information about an assembly is controlled through the following |
6 | // set of attributes. Change these attribute values to modify the information | 7 | // set of attributes. Change these attribute values to modify the information |
@@ -29,5 +30,7 @@ using System.Runtime.InteropServices; | |||
29 | // Build Number | 30 | // Build Number |
30 | // Revision | 31 | // Revision |
31 | // | 32 | // |
32 | [assembly: AssemblyVersion("0.7.5.*")] | 33 | [assembly: AssemblyVersion("0.8.3.*")] |
33 | [assembly: AssemblyFileVersion("1.0.0.0")] | 34 | |
35 | [assembly: Addin("LindenUDP", OpenSim.VersionInfo.VersionNumber)] | ||
36 | [assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)] | ||
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs index 556df30..a935dd2 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs | |||
@@ -33,9 +33,9 @@ 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 | 39 | ||
40 | namespace OpenSim.Region.ClientStack.LindenUDP.Tests | 40 | namespace OpenSim.Region.ClientStack.LindenUDP.Tests |
41 | { | 41 | { |
@@ -46,7 +46,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
46 | public class BasicCircuitTests : OpenSimTestCase | 46 | public class BasicCircuitTests : OpenSimTestCase |
47 | { | 47 | { |
48 | private Scene m_scene; | 48 | private Scene m_scene; |
49 | private TestLLUDPServer m_udpServer; | ||
50 | 49 | ||
51 | [TestFixtureSetUp] | 50 | [TestFixtureSetUp] |
52 | public void FixtureInit() | 51 | public void FixtureInit() |
@@ -69,74 +68,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
69 | { | 68 | { |
70 | base.SetUp(); | 69 | base.SetUp(); |
71 | m_scene = new SceneHelpers().SetupScene(); | 70 | m_scene = new SceneHelpers().SetupScene(); |
71 | StatsManager.SimExtraStats = new SimExtraStatsCollector(); | ||
72 | } | 72 | } |
73 | 73 | ||
74 | /// <summary> | 74 | // /// <summary> |
75 | /// Build an object name packet for test purposes | 75 | // /// Build an object name packet for test purposes |
76 | /// </summary> | 76 | // /// </summary> |
77 | /// <param name="objectLocalId"></param> | 77 | // /// <param name="objectLocalId"></param> |
78 | /// <param name="objectName"></param> | 78 | // /// <param name="objectName"></param> |
79 | private ObjectNamePacket BuildTestObjectNamePacket(uint objectLocalId, string objectName) | 79 | // private ObjectNamePacket BuildTestObjectNamePacket(uint objectLocalId, string objectName) |
80 | { | 80 | // { |
81 | ObjectNamePacket onp = new ObjectNamePacket(); | 81 | // ObjectNamePacket onp = new ObjectNamePacket(); |
82 | ObjectNamePacket.ObjectDataBlock odb = new ObjectNamePacket.ObjectDataBlock(); | 82 | // ObjectNamePacket.ObjectDataBlock odb = new ObjectNamePacket.ObjectDataBlock(); |
83 | odb.LocalID = objectLocalId; | 83 | // odb.LocalID = objectLocalId; |
84 | odb.Name = Utils.StringToBytes(objectName); | 84 | // odb.Name = Utils.StringToBytes(objectName); |
85 | onp.ObjectData = new ObjectNamePacket.ObjectDataBlock[] { odb }; | 85 | // onp.ObjectData = new ObjectNamePacket.ObjectDataBlock[] { odb }; |
86 | onp.Header.Zerocoded = false; | 86 | // onp.Header.Zerocoded = false; |
87 | 87 | // | |
88 | return onp; | 88 | // return onp; |
89 | } | 89 | // } |
90 | 90 | // | |
91 | private void AddUdpServer() | ||
92 | { | ||
93 | AddUdpServer(new IniConfigSource()); | ||
94 | } | ||
95 | |||
96 | private void AddUdpServer(IniConfigSource configSource) | ||
97 | { | ||
98 | uint port = 0; | ||
99 | AgentCircuitManager acm = m_scene.AuthenticateHandler; | ||
100 | |||
101 | m_udpServer = new TestLLUDPServer(IPAddress.Any, ref port, 0, false, configSource, acm); | ||
102 | m_udpServer.AddScene(m_scene); | ||
103 | } | ||
104 | |||
105 | /// <summary> | ||
106 | /// Used by tests that aren't testing this stage. | ||
107 | /// </summary> | ||
108 | private ScenePresence AddClient() | ||
109 | { | ||
110 | UUID myAgentUuid = TestHelpers.ParseTail(0x1); | ||
111 | UUID mySessionUuid = TestHelpers.ParseTail(0x2); | ||
112 | uint myCircuitCode = 123456; | ||
113 | IPEndPoint testEp = new IPEndPoint(IPAddress.Loopback, 999); | ||
114 | |||
115 | UseCircuitCodePacket uccp = new UseCircuitCodePacket(); | ||
116 | |||
117 | UseCircuitCodePacket.CircuitCodeBlock uccpCcBlock | ||
118 | = new UseCircuitCodePacket.CircuitCodeBlock(); | ||
119 | uccpCcBlock.Code = myCircuitCode; | ||
120 | uccpCcBlock.ID = myAgentUuid; | ||
121 | uccpCcBlock.SessionID = mySessionUuid; | ||
122 | uccp.CircuitCode = uccpCcBlock; | ||
123 | |||
124 | byte[] uccpBytes = uccp.ToBytes(); | ||
125 | UDPPacketBuffer upb = new UDPPacketBuffer(testEp, uccpBytes.Length); | ||
126 | upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor. | ||
127 | Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length); | ||
128 | |||
129 | AgentCircuitData acd = new AgentCircuitData(); | ||
130 | acd.AgentID = myAgentUuid; | ||
131 | acd.SessionID = mySessionUuid; | ||
132 | |||
133 | m_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd); | ||
134 | |||
135 | m_udpServer.PacketReceived(upb); | ||
136 | |||
137 | return m_scene.GetScenePresence(myAgentUuid); | ||
138 | } | ||
139 | |||
140 | /// <summary> | 91 | /// <summary> |
141 | /// Test adding a client to the stack | 92 | /// Test adding a client to the stack |
142 | /// </summary> | 93 | /// </summary> |
@@ -146,7 +97,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
146 | TestHelpers.InMethod(); | 97 | TestHelpers.InMethod(); |
147 | // TestHelpers.EnableLogging(); | 98 | // TestHelpers.EnableLogging(); |
148 | 99 | ||
149 | AddUdpServer(); | 100 | TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(m_scene); |
150 | 101 | ||
151 | UUID myAgentUuid = TestHelpers.ParseTail(0x1); | 102 | UUID myAgentUuid = TestHelpers.ParseTail(0x1); |
152 | UUID mySessionUuid = TestHelpers.ParseTail(0x2); | 103 | UUID mySessionUuid = TestHelpers.ParseTail(0x2); |
@@ -167,7 +118,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
167 | upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor. | 118 | upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor. |
168 | Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length); | 119 | Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length); |
169 | 120 | ||
170 | m_udpServer.PacketReceived(upb); | 121 | udpServer.PacketReceived(upb); |
171 | 122 | ||
172 | // Presence shouldn't exist since the circuit manager doesn't know about this circuit for authentication yet | 123 | // Presence shouldn't exist since the circuit manager doesn't know about this circuit for authentication yet |
173 | Assert.That(m_scene.GetScenePresence(myAgentUuid), Is.Null); | 124 | Assert.That(m_scene.GetScenePresence(myAgentUuid), Is.Null); |
@@ -178,15 +129,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
178 | 129 | ||
179 | m_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd); | 130 | m_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd); |
180 | 131 | ||
181 | m_udpServer.PacketReceived(upb); | 132 | udpServer.PacketReceived(upb); |
182 | 133 | ||
183 | // Should succeed now | 134 | // Should succeed now |
184 | ScenePresence sp = m_scene.GetScenePresence(myAgentUuid); | 135 | ScenePresence sp = m_scene.GetScenePresence(myAgentUuid); |
185 | Assert.That(sp.UUID, Is.EqualTo(myAgentUuid)); | 136 | Assert.That(sp.UUID, Is.EqualTo(myAgentUuid)); |
186 | 137 | ||
187 | Assert.That(m_udpServer.PacketsSent.Count, Is.EqualTo(1)); | 138 | Assert.That(udpServer.PacketsSent.Count, Is.EqualTo(1)); |
188 | 139 | ||
189 | Packet packet = m_udpServer.PacketsSent[0]; | 140 | Packet packet = udpServer.PacketsSent[0]; |
190 | Assert.That(packet, Is.InstanceOf(typeof(PacketAckPacket))); | 141 | Assert.That(packet, Is.InstanceOf(typeof(PacketAckPacket))); |
191 | 142 | ||
192 | PacketAckPacket ackPacket = packet as PacketAckPacket; | 143 | PacketAckPacket ackPacket = packet as PacketAckPacket; |
@@ -203,15 +154,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
203 | IniConfigSource ics = new IniConfigSource(); | 154 | IniConfigSource ics = new IniConfigSource(); |
204 | IConfig config = ics.AddConfig("ClientStack.LindenUDP"); | 155 | IConfig config = ics.AddConfig("ClientStack.LindenUDP"); |
205 | config.Set("AckTimeout", -1); | 156 | config.Set("AckTimeout", -1); |
206 | AddUdpServer(ics); | 157 | TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(m_scene, ics); |
158 | |||
159 | ScenePresence sp | ||
160 | = ClientStackHelpers.AddChildClient( | ||
161 | m_scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456); | ||
207 | 162 | ||
208 | ScenePresence sp = AddClient(); | 163 | udpServer.ClientOutgoingPacketHandler(sp.ControllingClient, true, false, false); |
209 | m_udpServer.ClientOutgoingPacketHandler(sp.ControllingClient, true, false, false); | ||
210 | 164 | ||
211 | ScenePresence spAfterAckTimeout = m_scene.GetScenePresence(sp.UUID); | 165 | ScenePresence spAfterAckTimeout = m_scene.GetScenePresence(sp.UUID); |
212 | Assert.That(spAfterAckTimeout, Is.Null); | 166 | Assert.That(spAfterAckTimeout, Is.Null); |
213 | |||
214 | // TestHelpers.DisableLogging(); | ||
215 | } | 167 | } |
216 | 168 | ||
217 | // /// <summary> | 169 | // /// <summary> |
@@ -233,7 +185,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
233 | // testLLUDPServer.RemoveClientCircuit(myCircuitCode); | 185 | // testLLUDPServer.RemoveClientCircuit(myCircuitCode); |
234 | // Assert.IsFalse(testLLUDPServer.HasCircuit(myCircuitCode)); | 186 | // Assert.IsFalse(testLLUDPServer.HasCircuit(myCircuitCode)); |
235 | // | 187 | // |
236 | // // Check that removing a non-existant circuit doesn't have any bad effects | 188 | // // Check that removing a non-existent circuit doesn't have any bad effects |
237 | // testLLUDPServer.RemoveClientCircuit(101); | 189 | // testLLUDPServer.RemoveClientCircuit(101); |
238 | // Assert.IsFalse(testLLUDPServer.HasCircuit(101)); | 190 | // Assert.IsFalse(testLLUDPServer.HasCircuit(101)); |
239 | // } | 191 | // } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs index 7d9f581..6c57e6d 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; |
@@ -38,7 +39,6 @@ using OpenSim.Framework; | |||
38 | using OpenSim.Region.CoreModules.Agent.TextureSender; | 39 | using OpenSim.Region.CoreModules.Agent.TextureSender; |
39 | using OpenSim.Region.Framework.Scenes; | 40 | using OpenSim.Region.Framework.Scenes; |
40 | using OpenSim.Tests.Common; | 41 | using OpenSim.Tests.Common; |
41 | using OpenSim.Tests.Common.Mock; | ||
42 | 42 | ||
43 | namespace OpenSim.Region.ClientStack.LindenUDP.Tests | 43 | namespace OpenSim.Region.ClientStack.LindenUDP.Tests |
44 | { | 44 | { |
@@ -53,6 +53,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
53 | [TestFixtureSetUp] | 53 | [TestFixtureSetUp] |
54 | public void FixtureInit() | 54 | public void FixtureInit() |
55 | { | 55 | { |
56 | // Don't allow tests to be bamboozled by asynchronous events. Execute everything on the same thread. | ||
57 | Util.FireAndForgetMethod = FireAndForgetMethod.None; | ||
58 | |||
56 | using ( | 59 | using ( |
57 | Stream resource | 60 | Stream resource |
58 | = GetType().Assembly.GetManifestResourceStream( | 61 | = GetType().Assembly.GetManifestResourceStream( |
@@ -72,9 +75,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
72 | } | 75 | } |
73 | } | 76 | } |
74 | 77 | ||
78 | [TestFixtureTearDown] | ||
79 | public void TearDown() | ||
80 | { | ||
81 | // We must set this back afterwards, otherwise later tests will fail since they're expecting multiple | ||
82 | // threads. Possibly, later tests should be rewritten not to worry about such things. | ||
83 | Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod; | ||
84 | } | ||
85 | |||
75 | [SetUp] | 86 | [SetUp] |
76 | public void SetUp() | 87 | public override void SetUp() |
77 | { | 88 | { |
89 | base.SetUp(); | ||
90 | |||
78 | UUID userId = TestHelpers.ParseTail(0x3); | 91 | UUID userId = TestHelpers.ParseTail(0x3); |
79 | 92 | ||
80 | J2KDecoderModule j2kdm = new J2KDecoderModule(); | 93 | J2KDecoderModule j2kdm = new J2KDecoderModule(); |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/PacketHandlerTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/PacketHandlerTests.cs index 5f73a94..92f1fc3 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/PacketHandlerTests.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/PacketHandlerTests.cs | |||
@@ -30,7 +30,6 @@ using NUnit.Framework; | |||
30 | using OpenMetaverse; | 30 | using OpenMetaverse; |
31 | using OpenMetaverse.Packets; | 31 | using OpenMetaverse.Packets; |
32 | using OpenSim.Framework; | 32 | using OpenSim.Framework; |
33 | using OpenSim.Tests.Common.Mock; | ||
34 | using OpenSim.Tests.Common; | 33 | using OpenSim.Tests.Common; |
35 | 34 | ||
36 | namespace OpenSim.Region.ClientStack.LindenUDP.Tests | 35 | namespace OpenSim.Region.ClientStack.LindenUDP.Tests |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/ThrottleTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/ThrottleTests.cs new file mode 100644 index 0000000..3c82a78 --- /dev/null +++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/ThrottleTests.cs | |||
@@ -0,0 +1,427 @@ | |||
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 Nini.Config; | ||
30 | using NUnit.Framework; | ||
31 | using OpenMetaverse.Packets; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Region.Framework.Scenes; | ||
34 | using OpenSim.Tests.Common; | ||
35 | |||
36 | namespace OpenSim.Region.ClientStack.LindenUDP.Tests | ||
37 | { | ||
38 | [TestFixture] | ||
39 | public class ThrottleTests : OpenSimTestCase | ||
40 | { | ||
41 | [TestFixtureSetUp] | ||
42 | public void FixtureInit() | ||
43 | { | ||
44 | // Don't allow tests to be bamboozled by asynchronous events. Execute everything on the same thread. | ||
45 | Util.FireAndForgetMethod = FireAndForgetMethod.RegressionTest; | ||
46 | } | ||
47 | |||
48 | [TestFixtureTearDown] | ||
49 | public void TearDown() | ||
50 | { | ||
51 | // We must set this back afterwards, otherwise later tests will fail since they're expecting multiple | ||
52 | // threads. Possibly, later tests should be rewritten so none of them require async stuff (which regression | ||
53 | // tests really shouldn't). | ||
54 | Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod; | ||
55 | } | ||
56 | |||
57 | [Test] | ||
58 | public void TestSetRequestDripRate() | ||
59 | { | ||
60 | TestHelpers.InMethod(); | ||
61 | |||
62 | TokenBucket tb = new TokenBucket("tb", null, 5000, 0); | ||
63 | AssertRates(tb, 5000, 0, 5000, 0); | ||
64 | |||
65 | tb.RequestedDripRate = 4000; | ||
66 | AssertRates(tb, 4000, 0, 4000, 0); | ||
67 | |||
68 | tb.RequestedDripRate = 6000; | ||
69 | AssertRates(tb, 6000, 0, 6000, 0); | ||
70 | } | ||
71 | |||
72 | [Test] | ||
73 | public void TestSetRequestDripRateWithMax() | ||
74 | { | ||
75 | TestHelpers.InMethod(); | ||
76 | |||
77 | TokenBucket tb = new TokenBucket("tb", null, 5000, 10000); | ||
78 | AssertRates(tb, 5000, 0, 5000, 10000); | ||
79 | |||
80 | tb.RequestedDripRate = 4000; | ||
81 | AssertRates(tb, 4000, 0, 4000, 10000); | ||
82 | |||
83 | tb.RequestedDripRate = 6000; | ||
84 | AssertRates(tb, 6000, 0, 6000, 10000); | ||
85 | |||
86 | tb.RequestedDripRate = 12000; | ||
87 | AssertRates(tb, 10000, 0, 10000, 10000); | ||
88 | } | ||
89 | |||
90 | [Test] | ||
91 | public void TestSetRequestDripRateWithChildren() | ||
92 | { | ||
93 | TestHelpers.InMethod(); | ||
94 | |||
95 | TokenBucket tbParent = new TokenBucket("tbParent", null, 0, 0); | ||
96 | TokenBucket tbChild1 = new TokenBucket("tbChild1", tbParent, 3000, 0); | ||
97 | TokenBucket tbChild2 = new TokenBucket("tbChild2", tbParent, 5000, 0); | ||
98 | |||
99 | AssertRates(tbParent, 8000, 8000, 8000, 0); | ||
100 | AssertRates(tbChild1, 3000, 0, 3000, 0); | ||
101 | AssertRates(tbChild2, 5000, 0, 5000, 0); | ||
102 | |||
103 | // Test: Setting a parent request greater than total children requests. | ||
104 | tbParent.RequestedDripRate = 10000; | ||
105 | |||
106 | AssertRates(tbParent, 10000, 8000, 8000, 0); | ||
107 | AssertRates(tbChild1, 3000, 0, 3000, 0); | ||
108 | AssertRates(tbChild2, 5000, 0, 5000, 0); | ||
109 | |||
110 | // Test: Setting a parent request lower than total children requests. | ||
111 | tbParent.RequestedDripRate = 6000; | ||
112 | |||
113 | AssertRates(tbParent, 6000, 8000, 6000, 0); | ||
114 | AssertRates(tbChild1, 3000, 0, 6000 / 8 * 3, 0); | ||
115 | AssertRates(tbChild2, 5000, 0, 6000 / 8 * 5, 0); | ||
116 | } | ||
117 | |||
118 | private void AssertRates( | ||
119 | TokenBucket tb, double requestedDripRate, double totalDripRequest, double dripRate, double maxDripRate) | ||
120 | { | ||
121 | Assert.AreEqual((int)requestedDripRate, tb.RequestedDripRate, "Requested drip rate"); | ||
122 | Assert.AreEqual((int)totalDripRequest, tb.TotalDripRequest, "Total drip request"); | ||
123 | Assert.AreEqual((int)dripRate, tb.DripRate, "Drip rate"); | ||
124 | Assert.AreEqual((int)maxDripRate, tb.MaxDripRate, "Max drip rate"); | ||
125 | } | ||
126 | |||
127 | [Test] | ||
128 | public void TestClientThrottleSetNoLimit() | ||
129 | { | ||
130 | TestHelpers.InMethod(); | ||
131 | // TestHelpers.EnableLogging(); | ||
132 | |||
133 | Scene scene = new SceneHelpers().SetupScene(); | ||
134 | TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene); | ||
135 | |||
136 | ScenePresence sp | ||
137 | = ClientStackHelpers.AddChildClient( | ||
138 | scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456); | ||
139 | |||
140 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; | ||
141 | |||
142 | udpServer.Throttle.DebugLevel = 1; | ||
143 | udpClient.ThrottleDebugLevel = 1; | ||
144 | |||
145 | int resendBytes = 1000; | ||
146 | int landBytes = 2000; | ||
147 | int windBytes = 3000; | ||
148 | int cloudBytes = 4000; | ||
149 | int taskBytes = 5000; | ||
150 | int textureBytes = 6000; | ||
151 | int assetBytes = 7000; | ||
152 | |||
153 | SetThrottles( | ||
154 | udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes); | ||
155 | |||
156 | // We expect this to be lower because of the minimum bound set by MTU | ||
157 | int totalBytes = LLUDPServer.MTU + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes; | ||
158 | |||
159 | AssertThrottles( | ||
160 | udpClient, | ||
161 | LLUDPServer.MTU, landBytes, windBytes, cloudBytes, taskBytes, | ||
162 | textureBytes, assetBytes, totalBytes, 0, 0); | ||
163 | } | ||
164 | |||
165 | [Test] | ||
166 | public void TestClientThrottleAdaptiveNoLimit() | ||
167 | { | ||
168 | TestHelpers.InMethod(); | ||
169 | // TestHelpers.EnableLogging(); | ||
170 | |||
171 | Scene scene = new SceneHelpers().SetupScene(); | ||
172 | |||
173 | IniConfigSource ics = new IniConfigSource(); | ||
174 | IConfig config = ics.AddConfig("ClientStack.LindenUDP"); | ||
175 | config.Set("enable_adaptive_throttles", true); | ||
176 | config.Set("adaptive_throttle_min_bps", 32000); | ||
177 | |||
178 | TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene, ics); | ||
179 | |||
180 | ScenePresence sp | ||
181 | = ClientStackHelpers.AddChildClient( | ||
182 | scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456); | ||
183 | |||
184 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; | ||
185 | |||
186 | udpServer.Throttle.DebugLevel = 1; | ||
187 | udpClient.ThrottleDebugLevel = 1; | ||
188 | |||
189 | // Total is 275000 | ||
190 | int resendBytes = 5000; // this is set low to test the minimum throttle override | ||
191 | int landBytes = 20000; | ||
192 | int windBytes = 30000; | ||
193 | int cloudBytes = 40000; | ||
194 | int taskBytes = 50000; | ||
195 | int textureBytes = 60000; | ||
196 | int assetBytes = 70000; | ||
197 | int totalBytes = resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes; | ||
198 | |||
199 | SetThrottles( | ||
200 | udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes); | ||
201 | |||
202 | // Ratio of current adaptive drip rate to requested bytes, minimum rate is 32000 | ||
203 | double commitRatio = 32000.0 / totalBytes; | ||
204 | |||
205 | AssertThrottles( | ||
206 | udpClient, | ||
207 | LLUDPServer.MTU, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio, | ||
208 | textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0); | ||
209 | |||
210 | // Test an increase in target throttle, ack of 20 packets adds 20 * LLUDPServer.MTU bytes | ||
211 | // to the throttle, recompute commitratio from those numbers | ||
212 | udpClient.FlowThrottle.AcknowledgePackets(20); | ||
213 | commitRatio = (32000.0 + 20.0 * LLUDPServer.MTU) / totalBytes; | ||
214 | |||
215 | AssertThrottles( | ||
216 | udpClient, | ||
217 | LLUDPServer.MTU, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio, | ||
218 | textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0); | ||
219 | |||
220 | // Test a decrease in target throttle, adaptive throttle should cut the rate by 50% with a floor | ||
221 | // set by the minimum adaptive rate | ||
222 | udpClient.FlowThrottle.ExpirePackets(1); | ||
223 | commitRatio = (32000.0 + (20.0 * LLUDPServer.MTU)/Math.Pow(2,1)) / totalBytes; | ||
224 | |||
225 | AssertThrottles( | ||
226 | udpClient, | ||
227 | LLUDPServer.MTU, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio, | ||
228 | textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0); | ||
229 | } | ||
230 | |||
231 | /// <summary> | ||
232 | /// Test throttle setttings where max client throttle has been limited server side. | ||
233 | /// </summary> | ||
234 | [Test] | ||
235 | public void TestSingleClientThrottleRegionLimited() | ||
236 | { | ||
237 | TestHelpers.InMethod(); | ||
238 | // TestHelpers.EnableLogging(); | ||
239 | |||
240 | int resendBytes = 6000; | ||
241 | int landBytes = 8000; | ||
242 | int windBytes = 10000; | ||
243 | int cloudBytes = 12000; | ||
244 | int taskBytes = 14000; | ||
245 | int textureBytes = 16000; | ||
246 | int assetBytes = 18000; | ||
247 | int totalBytes | ||
248 | = (int)((resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes) / 2); | ||
249 | |||
250 | Scene scene = new SceneHelpers().SetupScene(); | ||
251 | TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene); | ||
252 | udpServer.Throttle.RequestedDripRate = totalBytes; | ||
253 | |||
254 | ScenePresence sp1 | ||
255 | = ClientStackHelpers.AddChildClient( | ||
256 | scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456); | ||
257 | |||
258 | LLUDPClient udpClient1 = ((LLClientView)sp1.ControllingClient).UDPClient; | ||
259 | |||
260 | SetThrottles( | ||
261 | udpClient1, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes); | ||
262 | |||
263 | AssertThrottles( | ||
264 | udpClient1, | ||
265 | resendBytes / 2, landBytes / 2, windBytes / 2, cloudBytes / 2, taskBytes / 2, | ||
266 | textureBytes / 2, assetBytes / 2, totalBytes, 0, 0); | ||
267 | |||
268 | // Test: Now add another client | ||
269 | ScenePresence sp2 | ||
270 | = ClientStackHelpers.AddChildClient( | ||
271 | scene, udpServer, TestHelpers.ParseTail(0x10), TestHelpers.ParseTail(0x20), 123457); | ||
272 | |||
273 | LLUDPClient udpClient2 = ((LLClientView)sp2.ControllingClient).UDPClient; | ||
274 | // udpClient.ThrottleDebugLevel = 1; | ||
275 | |||
276 | SetThrottles( | ||
277 | udpClient2, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes); | ||
278 | |||
279 | AssertThrottles( | ||
280 | udpClient1, | ||
281 | resendBytes / 4, landBytes / 4, windBytes / 4, cloudBytes / 4, taskBytes / 4, | ||
282 | textureBytes / 4, assetBytes / 4, totalBytes / 2, 0, 0); | ||
283 | |||
284 | AssertThrottles( | ||
285 | udpClient2, | ||
286 | resendBytes / 4, landBytes / 4, windBytes / 4, cloudBytes / 4, taskBytes / 4, | ||
287 | textureBytes / 4, assetBytes / 4, totalBytes / 2, 0, 0); | ||
288 | } | ||
289 | |||
290 | /// <summary> | ||
291 | /// Test throttle setttings where max client throttle has been limited server side. | ||
292 | /// </summary> | ||
293 | [Test] | ||
294 | public void TestClientThrottlePerClientLimited() | ||
295 | { | ||
296 | TestHelpers.InMethod(); | ||
297 | // TestHelpers.EnableLogging(); | ||
298 | |||
299 | int resendBytes = 4000; | ||
300 | int landBytes = 6000; | ||
301 | int windBytes = 8000; | ||
302 | int cloudBytes = 10000; | ||
303 | int taskBytes = 12000; | ||
304 | int textureBytes = 14000; | ||
305 | int assetBytes = 16000; | ||
306 | int totalBytes | ||
307 | = (int)((resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes) / 2); | ||
308 | |||
309 | Scene scene = new SceneHelpers().SetupScene(); | ||
310 | TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene); | ||
311 | udpServer.ThrottleRates.Total = totalBytes; | ||
312 | |||
313 | ScenePresence sp | ||
314 | = ClientStackHelpers.AddChildClient( | ||
315 | scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456); | ||
316 | |||
317 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; | ||
318 | // udpClient.ThrottleDebugLevel = 1; | ||
319 | |||
320 | SetThrottles( | ||
321 | udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes); | ||
322 | |||
323 | AssertThrottles( | ||
324 | udpClient, | ||
325 | resendBytes / 2, landBytes / 2, windBytes / 2, cloudBytes / 2, taskBytes / 2, | ||
326 | textureBytes / 2, assetBytes / 2, totalBytes, 0, totalBytes); | ||
327 | } | ||
328 | |||
329 | [Test] | ||
330 | public void TestClientThrottlePerClientAndRegionLimited() | ||
331 | { | ||
332 | TestHelpers.InMethod(); | ||
333 | //TestHelpers.EnableLogging(); | ||
334 | |||
335 | int resendBytes = 4000; | ||
336 | int landBytes = 6000; | ||
337 | int windBytes = 8000; | ||
338 | int cloudBytes = 10000; | ||
339 | int taskBytes = 12000; | ||
340 | int textureBytes = 14000; | ||
341 | int assetBytes = 16000; | ||
342 | |||
343 | // current total 70000 | ||
344 | int totalBytes = resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes; | ||
345 | |||
346 | Scene scene = new SceneHelpers().SetupScene(); | ||
347 | TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene); | ||
348 | udpServer.ThrottleRates.Total = (int)(totalBytes * 1.1); | ||
349 | udpServer.Throttle.RequestedDripRate = (int)(totalBytes * 1.5); | ||
350 | |||
351 | ScenePresence sp1 | ||
352 | = ClientStackHelpers.AddChildClient( | ||
353 | scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456); | ||
354 | |||
355 | LLUDPClient udpClient1 = ((LLClientView)sp1.ControllingClient).UDPClient; | ||
356 | udpClient1.ThrottleDebugLevel = 1; | ||
357 | |||
358 | SetThrottles( | ||
359 | udpClient1, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes); | ||
360 | |||
361 | AssertThrottles( | ||
362 | udpClient1, | ||
363 | resendBytes, landBytes, windBytes, cloudBytes, taskBytes, | ||
364 | textureBytes, assetBytes, totalBytes, 0, totalBytes * 1.1); | ||
365 | |||
366 | // Now add another client | ||
367 | ScenePresence sp2 | ||
368 | = ClientStackHelpers.AddChildClient( | ||
369 | scene, udpServer, TestHelpers.ParseTail(0x10), TestHelpers.ParseTail(0x20), 123457); | ||
370 | |||
371 | LLUDPClient udpClient2 = ((LLClientView)sp2.ControllingClient).UDPClient; | ||
372 | udpClient2.ThrottleDebugLevel = 1; | ||
373 | |||
374 | SetThrottles( | ||
375 | udpClient2, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes); | ||
376 | |||
377 | AssertThrottles( | ||
378 | udpClient1, | ||
379 | resendBytes * 0.75, landBytes * 0.75, windBytes * 0.75, cloudBytes * 0.75, taskBytes * 0.75, | ||
380 | textureBytes * 0.75, assetBytes * 0.75, totalBytes * 0.75, 0, totalBytes * 1.1); | ||
381 | |||
382 | AssertThrottles( | ||
383 | udpClient2, | ||
384 | resendBytes * 0.75, landBytes * 0.75, windBytes * 0.75, cloudBytes * 0.75, taskBytes * 0.75, | ||
385 | textureBytes * 0.75, assetBytes * 0.75, totalBytes * 0.75, 0, totalBytes * 1.1); | ||
386 | } | ||
387 | |||
388 | private void AssertThrottles( | ||
389 | LLUDPClient udpClient, | ||
390 | double resendBytes, double landBytes, double windBytes, double cloudBytes, double taskBytes, double textureBytes, double assetBytes, | ||
391 | double totalBytes, double targetBytes, double maxBytes) | ||
392 | { | ||
393 | ClientInfo ci = udpClient.GetClientInfo(); | ||
394 | |||
395 | // Console.WriteLine( | ||
396 | // "Resend={0}, Land={1}, Wind={2}, Cloud={3}, Task={4}, Texture={5}, Asset={6}, TOTAL = {7}", | ||
397 | // ci.resendThrottle, ci.landThrottle, ci.windThrottle, ci.cloudThrottle, ci.taskThrottle, ci.textureThrottle, ci.assetThrottle, ci.totalThrottle); | ||
398 | |||
399 | Assert.AreEqual((int)resendBytes, ci.resendThrottle, "Resend"); | ||
400 | Assert.AreEqual((int)landBytes, ci.landThrottle, "Land"); | ||
401 | Assert.AreEqual((int)windBytes, ci.windThrottle, "Wind"); | ||
402 | Assert.AreEqual((int)cloudBytes, ci.cloudThrottle, "Cloud"); | ||
403 | Assert.AreEqual((int)taskBytes, ci.taskThrottle, "Task"); | ||
404 | Assert.AreEqual((int)textureBytes, ci.textureThrottle, "Texture"); | ||
405 | Assert.AreEqual((int)assetBytes, ci.assetThrottle, "Asset"); | ||
406 | Assert.AreEqual((int)totalBytes, ci.totalThrottle, "Total"); | ||
407 | Assert.AreEqual((int)targetBytes, ci.targetThrottle, "Target"); | ||
408 | Assert.AreEqual((int)maxBytes, ci.maxThrottle, "Max"); | ||
409 | } | ||
410 | |||
411 | private void SetThrottles( | ||
412 | LLUDPClient udpClient, int resendBytes, int landBytes, int windBytes, int cloudBytes, int taskBytes, int textureBytes, int assetBytes) | ||
413 | { | ||
414 | byte[] throttles = new byte[28]; | ||
415 | |||
416 | Array.Copy(BitConverter.GetBytes((float)resendBytes * 8), 0, throttles, 0, 4); | ||
417 | Array.Copy(BitConverter.GetBytes((float)landBytes * 8), 0, throttles, 4, 4); | ||
418 | Array.Copy(BitConverter.GetBytes((float)windBytes * 8), 0, throttles, 8, 4); | ||
419 | Array.Copy(BitConverter.GetBytes((float)cloudBytes * 8), 0, throttles, 12, 4); | ||
420 | Array.Copy(BitConverter.GetBytes((float)taskBytes * 8), 0, throttles, 16, 4); | ||
421 | Array.Copy(BitConverter.GetBytes((float)textureBytes * 8), 0, throttles, 20, 4); | ||
422 | Array.Copy(BitConverter.GetBytes((float)assetBytes * 8), 0, throttles, 24, 4); | ||
423 | |||
424 | udpClient.SetThrottles(throttles); | ||
425 | } | ||
426 | } | ||
427 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs index c9aac0b..7a2756b 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs | |||
@@ -58,7 +58,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
58 | 58 | ||
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> | ||
63 | /// Set the minimum rate that the adaptive throttles can set. The viewer | ||
64 | /// can still throttle lower than this, but the adaptive throttles will | ||
65 | /// never decrease rates below this no matter how many packets are dropped | ||
66 | /// </summary> | ||
67 | public Int64 MinimumAdaptiveThrottleRate; | ||
68 | |||
69 | /// <summary>Amount of the texture throttle to steal for the task throttle</summary> | ||
70 | public double CannibalizeTextureRate; | ||
71 | |||
62 | /// <summary> | 72 | /// <summary> |
63 | /// Default constructor | 73 | /// Default constructor |
64 | /// </summary> | 74 | /// </summary> |
@@ -69,6 +79,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
69 | { | 79 | { |
70 | IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"]; | 80 | IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"]; |
71 | 81 | ||
82 | // Current default total is 66750 | ||
72 | Resend = throttleConfig.GetInt("resend_default", 6625); | 83 | Resend = throttleConfig.GetInt("resend_default", 6625); |
73 | Land = throttleConfig.GetInt("land_default", 9125); | 84 | Land = throttleConfig.GetInt("land_default", 9125); |
74 | Wind = throttleConfig.GetInt("wind_default", 1750); | 85 | Wind = throttleConfig.GetInt("wind_default", 1750); |
@@ -80,6 +91,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
80 | Total = throttleConfig.GetInt("client_throttle_max_bps", 0); | 91 | Total = throttleConfig.GetInt("client_throttle_max_bps", 0); |
81 | 92 | ||
82 | AdaptiveThrottlesEnabled = throttleConfig.GetBoolean("enable_adaptive_throttles", false); | 93 | AdaptiveThrottlesEnabled = throttleConfig.GetBoolean("enable_adaptive_throttles", false); |
94 | MinimumAdaptiveThrottleRate = throttleConfig.GetInt("adaptive_throttle_min_bps", 32000); | ||
95 | |||
96 | CannibalizeTextureRate = (double)throttleConfig.GetFloat("CannibalizeTextureRate", 0.0f); | ||
97 | CannibalizeTextureRate = Util.Clamp<double>(CannibalizeTextureRate,0.0, 0.9); | ||
83 | } | 98 | } |
84 | catch (Exception) { } | 99 | catch (Exception) { } |
85 | } | 100 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs index 4c33db5..4616203 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs | |||
@@ -1,4 +1,4 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) Contributors, http://opensimulator.org/ | 2 | * Copyright (c) Contributors, http://opensimulator.org/ |
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | 3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. |
4 | * | 4 | * |
@@ -42,9 +42,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
42 | public class TokenBucket | 42 | public class TokenBucket |
43 | { | 43 | { |
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 | |
46 | 46 | public string Identifier { get; private set; } | |
47 | // private Int32 m_identifier; | 47 | |
48 | public int DebugLevel { get; set; } | ||
48 | 49 | ||
49 | /// <summary> | 50 | /// <summary> |
50 | /// Number of ticks (ms) per quantum, drip rate and max burst | 51 | /// Number of ticks (ms) per quantum, drip rate and max burst |
@@ -60,7 +61,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
60 | 61 | ||
61 | /// <summary> | 62 | /// <summary> |
62 | /// </summary> | 63 | /// </summary> |
63 | protected const Int32 m_minimumDripRate = 1400; | 64 | protected const Int32 m_minimumDripRate = LLUDPServer.MTU; |
64 | 65 | ||
65 | /// <summary>Time of the last drip, in system ticks</summary> | 66 | /// <summary>Time of the last drip, in system ticks</summary> |
66 | protected Int32 m_lastDrip; | 67 | protected Int32 m_lastDrip; |
@@ -75,20 +76,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
75 | /// Map of children buckets and their requested maximum burst rate | 76 | /// Map of children buckets and their requested maximum burst rate |
76 | /// </summary> | 77 | /// </summary> |
77 | protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>(); | 78 | protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>(); |
78 | |||
79 | #region Properties | ||
80 | 79 | ||
81 | /// <summary> | 80 | /// <summary> |
82 | /// The parent bucket of this bucket, or null if this bucket has no | 81 | /// The parent bucket of this bucket, or null if this bucket has no |
83 | /// parent. The parent bucket will limit the aggregate bandwidth of all | 82 | /// parent. The parent bucket will limit the aggregate bandwidth of all |
84 | /// of its children buckets | 83 | /// of its children buckets |
85 | /// </summary> | 84 | /// </summary> |
86 | protected TokenBucket m_parent; | 85 | public TokenBucket Parent { get; protected set; } |
87 | public TokenBucket Parent | ||
88 | { | ||
89 | get { return m_parent; } | ||
90 | set { m_parent = value; } | ||
91 | } | ||
92 | 86 | ||
93 | /// <summary> | 87 | /// <summary> |
94 | /// Maximum burst rate in bytes per second. This is the maximum number | 88 | /// Maximum burst rate in bytes per second. This is the maximum number |
@@ -114,77 +108,106 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
114 | } | 108 | } |
115 | 109 | ||
116 | /// <summary> | 110 | /// <summary> |
117 | /// The speed limit of this bucket in bytes per second. This is the | 111 | /// The requested drip rate for this particular bucket. |
118 | /// number of tokens that are added to the bucket per quantum | ||
119 | /// </summary> | 112 | /// </summary> |
120 | /// <remarks>Tokens are added to the bucket any time | 113 | /// <remarks> |
114 | /// 0 then TotalDripRequest is used instead. | ||
115 | /// Can never be above MaxDripRate. | ||
116 | /// Tokens are added to the bucket at any time | ||
121 | /// <seealso cref="RemoveTokens"/> is called, at the granularity of | 117 | /// <seealso cref="RemoveTokens"/> is called, at the granularity of |
122 | /// the system tick interval (typically around 15-22ms)</remarks> | 118 | /// the system tick interval (typically around 15-22ms) |
123 | protected Int64 m_dripRate; | 119 | /// FIXME: It is extremely confusing to be able to set a RequestedDripRate of 0 and then receive a positive |
120 | /// number on get if TotalDripRequest is set. This also stops us being able to retrieve the fact that | ||
121 | /// RequestedDripRate is set to 0. Really, this should always return m_dripRate and then we can get | ||
122 | /// (m_dripRate == 0 ? TotalDripRequest : m_dripRate) on some other properties. | ||
123 | /// </remarks> | ||
124 | public virtual Int64 RequestedDripRate | 124 | public virtual Int64 RequestedDripRate |
125 | { | 125 | { |
126 | get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } | 126 | get { return (m_dripRate == 0 ? TotalDripRequest : m_dripRate); } |
127 | set { | 127 | set |
128 | m_dripRate = (value < 0 ? 0 : value); | 128 | { |
129 | if (value <= 0) | ||
130 | m_dripRate = 0; | ||
131 | else if (MaxDripRate > 0 && value > MaxDripRate) | ||
132 | m_dripRate = MaxDripRate; | ||
133 | else | ||
134 | m_dripRate = value; | ||
135 | |||
129 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); | 136 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); |
130 | m_totalDripRequest = m_dripRate; | 137 | |
131 | if (m_parent != null) | 138 | if (Parent != null) |
132 | m_parent.RegisterRequest(this,m_dripRate); | 139 | Parent.RegisterRequest(this, m_dripRate); |
133 | } | 140 | } |
134 | } | 141 | } |
135 | 142 | ||
143 | /// <summary> | ||
144 | /// Gets the drip rate. | ||
145 | /// </summary> | ||
146 | /// <value> | ||
147 | /// DripRate can never be above max drip rate or below min drip rate. | ||
148 | /// If we are a child bucket then the drip rate return is modifed by the total load on the capacity of the | ||
149 | /// parent bucket. | ||
150 | /// </value> | ||
136 | public virtual Int64 DripRate | 151 | public virtual Int64 DripRate |
137 | { | 152 | { |
138 | get { | 153 | get |
139 | if (m_parent == null) | 154 | { |
140 | return Math.Min(RequestedDripRate,TotalDripRequest); | 155 | double rate; |
141 | 156 | ||
142 | double rate = (double)RequestedDripRate * m_parent.DripRateModifier(); | 157 | // FIXME: This doesn't properly work if we have a parent and children and a requested drip rate set |
158 | // on ourselves which is not equal to the child drip rates. | ||
159 | if (Parent == null) | ||
160 | { | ||
161 | if (TotalDripRequest > 0) | ||
162 | rate = Math.Min(RequestedDripRate, TotalDripRequest); | ||
163 | else | ||
164 | rate = RequestedDripRate; | ||
165 | } | ||
166 | else | ||
167 | { | ||
168 | rate = (double)RequestedDripRate * Parent.DripRateModifier(); | ||
169 | } | ||
170 | |||
143 | if (rate < m_minimumDripRate) | 171 | if (rate < m_minimumDripRate) |
144 | rate = m_minimumDripRate; | 172 | rate = m_minimumDripRate; |
173 | else if (MaxDripRate > 0 && rate > MaxDripRate) | ||
174 | rate = MaxDripRate; | ||
145 | 175 | ||
146 | return (Int64)rate; | 176 | return (Int64)rate; |
147 | } | 177 | } |
148 | } | 178 | } |
179 | protected Int64 m_dripRate; | ||
180 | |||
181 | // <summary> | ||
182 | // The maximum rate for flow control. Drip rate can never be greater than this. | ||
183 | // </summary> | ||
184 | public Int64 MaxDripRate { get; set; } | ||
149 | 185 | ||
150 | /// <summary> | 186 | /// <summary> |
151 | /// The current total of the requested maximum burst rates of | 187 | /// The current total of the requested maximum burst rates of children buckets. |
152 | /// this bucket's children buckets. | ||
153 | /// </summary> | 188 | /// </summary> |
154 | protected Int64 m_totalDripRequest; | 189 | public Int64 TotalDripRequest { get; protected set; } |
155 | public Int64 TotalDripRequest | ||
156 | { | ||
157 | get { return m_totalDripRequest; } | ||
158 | set { m_totalDripRequest = value; } | ||
159 | } | ||
160 | |||
161 | #endregion Properties | ||
162 | |||
163 | #region Constructor | ||
164 | 190 | ||
165 | /// <summary> | 191 | /// <summary> |
166 | /// Default constructor | 192 | /// Default constructor |
167 | /// </summary> | 193 | /// </summary> |
194 | /// <param name="identifier">Identifier for this token bucket</param> | ||
168 | /// <param name="parent">Parent bucket if this is a child bucket, or | 195 | /// <param name="parent">Parent bucket if this is a child bucket, or |
169 | /// null if this is a root bucket</param> | 196 | /// null if this is a root bucket</param> |
170 | /// <param name="maxBurst">Maximum size of the bucket in bytes, or | 197 | /// <param name="requestedDripRate"> |
171 | /// zero if this bucket has no maximum capacity</param> | 198 | /// Requested rate that the bucket fills, in bytes per |
172 | /// <param name="dripRate">Rate that the bucket fills, in bytes per | 199 | /// second. If zero, the bucket always remains full. |
173 | /// second. If zero, the bucket always remains full</param> | 200 | /// </param> |
174 | public TokenBucket(TokenBucket parent, Int64 dripRate) | 201 | public TokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate) |
175 | { | 202 | { |
176 | // m_identifier = m_counter++; | 203 | Identifier = identifier; |
177 | m_counter++; | ||
178 | 204 | ||
179 | Parent = parent; | 205 | Parent = parent; |
180 | RequestedDripRate = dripRate; | 206 | RequestedDripRate = requestedDripRate; |
181 | // TotalDripRequest = dripRate; // this will be overwritten when a child node registers | 207 | MaxDripRate = maxDripRate; |
182 | // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); | ||
183 | m_lastDrip = Util.EnvironmentTickCount(); | 208 | m_lastDrip = Util.EnvironmentTickCount(); |
184 | } | 209 | } |
185 | 210 | ||
186 | #endregion Constructor | ||
187 | |||
188 | /// <summary> | 211 | /// <summary> |
189 | /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning | 212 | /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning |
190 | /// no modification if the requested bandwidth is less than the | 213 | /// no modification if the requested bandwidth is less than the |
@@ -195,7 +218,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
195 | protected double DripRateModifier() | 218 | protected double DripRateModifier() |
196 | { | 219 | { |
197 | Int64 driprate = DripRate; | 220 | Int64 driprate = DripRate; |
198 | return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; | 221 | double modifier = driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; |
222 | |||
223 | // if (DebugLevel > 0) | ||
224 | // m_log.DebugFormat( | ||
225 | // "[TOKEN BUCKET]: Returning drip modifier {0}/{1} = {2} from {3}", | ||
226 | // driprate, TotalDripRequest, modifier, Identifier); | ||
227 | |||
228 | return modifier; | ||
199 | } | 229 | } |
200 | 230 | ||
201 | /// <summary> | 231 | /// <summary> |
@@ -217,16 +247,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
217 | lock (m_children) | 247 | lock (m_children) |
218 | { | 248 | { |
219 | m_children[child] = request; | 249 | m_children[child] = request; |
220 | // m_totalDripRequest = m_children.Values.Sum(); | ||
221 | 250 | ||
222 | m_totalDripRequest = 0; | 251 | TotalDripRequest = 0; |
223 | foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) | 252 | foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) |
224 | m_totalDripRequest += cref.Value; | 253 | TotalDripRequest += cref.Value; |
225 | } | 254 | } |
226 | 255 | ||
227 | // Pass the new values up to the parent | 256 | // Pass the new values up to the parent |
228 | if (m_parent != null) | 257 | if (Parent != null) |
229 | m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); | 258 | { |
259 | Int64 effectiveDripRate; | ||
260 | |||
261 | if (RequestedDripRate > 0) | ||
262 | effectiveDripRate = Math.Min(RequestedDripRate, TotalDripRequest); | ||
263 | else | ||
264 | effectiveDripRate = TotalDripRequest; | ||
265 | |||
266 | Parent.RegisterRequest(this, effectiveDripRate); | ||
267 | } | ||
230 | } | 268 | } |
231 | 269 | ||
232 | /// <summary> | 270 | /// <summary> |
@@ -238,17 +276,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
238 | lock (m_children) | 276 | lock (m_children) |
239 | { | 277 | { |
240 | m_children.Remove(child); | 278 | m_children.Remove(child); |
241 | // m_totalDripRequest = m_children.Values.Sum(); | ||
242 | 279 | ||
243 | m_totalDripRequest = 0; | 280 | TotalDripRequest = 0; |
244 | foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) | 281 | foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) |
245 | m_totalDripRequest += cref.Value; | 282 | TotalDripRequest += cref.Value; |
246 | } | 283 | } |
247 | |||
248 | 284 | ||
249 | // Pass the new values up to the parent | 285 | // Pass the new values up to the parent |
250 | if (m_parent != null) | 286 | if (Parent != null) |
251 | m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); | 287 | Parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); |
252 | } | 288 | } |
253 | 289 | ||
254 | /// <summary> | 290 | /// <summary> |
@@ -301,7 +337,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
301 | // with no drip rate... | 337 | // with no drip rate... |
302 | if (DripRate == 0) | 338 | if (DripRate == 0) |
303 | { | 339 | { |
304 | m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0"); | 340 | m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0 for {0}", Identifier); |
305 | return; | 341 | return; |
306 | } | 342 | } |
307 | 343 | ||
@@ -321,74 +357,108 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
321 | 357 | ||
322 | public class AdaptiveTokenBucket : TokenBucket | 358 | public class AdaptiveTokenBucket : TokenBucket |
323 | { | 359 | { |
324 | // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 360 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
361 | |||
362 | public bool AdaptiveEnabled { get; set; } | ||
325 | 363 | ||
326 | /// <summary> | 364 | /// <summary> |
327 | /// The minimum rate for flow control. Minimum drip rate is one | 365 | /// Target drip rate for this bucket. |
328 | /// packet per second. Open the throttle to 15 packets per second | ||
329 | /// or about 160kbps. | ||
330 | /// </summary> | 366 | /// </summary> |
331 | protected const Int64 m_minimumFlow = m_minimumDripRate * 15; | 367 | /// <remarks>Usually set by the client. If adaptive is enabled then throttles will increase until we reach this.</remarks> |
332 | 368 | public Int64 TargetDripRate | |
333 | // <summary> | 369 | { |
334 | // The maximum rate for flow control. Drip rate can never be | 370 | get { return m_targetDripRate; } |
335 | // greater than this. | 371 | set |
336 | // </summary> | 372 | { |
337 | protected Int64 m_maxDripRate = 0; | 373 | m_targetDripRate = Math.Max(value, m_minimumFlow); |
338 | protected Int64 MaxDripRate | 374 | } |
339 | { | ||
340 | get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); } | ||
341 | set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); } | ||
342 | } | 375 | } |
343 | 376 | protected Int64 m_targetDripRate; | |
344 | private bool m_enabled = false; | 377 | |
345 | |||
346 | // <summary> | 378 | // <summary> |
347 | // | 379 | // Adjust drip rate in response to network conditions. |
348 | // </summary> | 380 | // </summary> |
349 | public virtual Int64 AdjustedDripRate | 381 | public virtual Int64 AdjustedDripRate |
350 | { | 382 | { |
351 | get { return m_dripRate; } | 383 | get { return m_dripRate; } |
352 | set { | 384 | set |
353 | m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value,m_minimumFlow,MaxDripRate); | 385 | { |
386 | m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value, m_minimumFlow, TargetDripRate); | ||
354 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); | 387 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); |
355 | if (m_parent != null) | 388 | |
356 | m_parent.RegisterRequest(this,m_dripRate); | 389 | if (Parent != null) |
390 | Parent.RegisterRequest(this, m_dripRate); | ||
357 | } | 391 | } |
358 | } | 392 | } |
393 | |||
394 | /// <summary> | ||
395 | /// The minimum rate for adaptive flow control. | ||
396 | /// </summary> | ||
397 | protected Int64 m_minimumFlow = 32000; | ||
359 | 398 | ||
360 | // <summary> | 399 | /// <summary> |
361 | // | 400 | /// Constructor for the AdaptiveTokenBucket class |
362 | // </summary> | 401 | /// <param name="identifier">Unique identifier for the client</param> |
363 | public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate, bool enabled) : base(parent,maxDripRate) | 402 | /// <param name="parent">Parent bucket in the hierarchy</param> |
403 | /// <param name="requestedDripRate"></param> | ||
404 | /// <param name="maxDripRate">The ceiling rate for adaptation</param> | ||
405 | /// <param name="minDripRate">The floor rate for adaptation</param> | ||
406 | /// </summary> | ||
407 | public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate, Int64 minDripRate, bool enabled) | ||
408 | : base(identifier, parent, requestedDripRate, maxDripRate) | ||
364 | { | 409 | { |
365 | m_enabled = enabled; | 410 | AdaptiveEnabled = enabled; |
366 | 411 | ||
367 | if (m_enabled) | 412 | if (AdaptiveEnabled) |
368 | { | 413 | { |
369 | // m_log.DebugFormat("[TOKENBUCKET] Adaptive throttle enabled"); | 414 | // m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled"); |
370 | MaxDripRate = maxDripRate; | 415 | m_minimumFlow = minDripRate; |
416 | TargetDripRate = m_minimumFlow; | ||
371 | AdjustedDripRate = m_minimumFlow; | 417 | AdjustedDripRate = m_minimumFlow; |
372 | } | 418 | } |
373 | } | 419 | } |
374 | 420 | ||
375 | // <summary> | 421 | /// <summary> |
376 | // | 422 | /// Reliable packets sent to the client for which we never received an ack adjust the drip rate down. |
377 | // </summary> | 423 | /// <param name="packets">Number of packets that expired without successful delivery</param> |
378 | public void ExpirePackets(Int32 count) | 424 | /// </summary> |
425 | public void ExpirePackets(Int32 packets) | ||
379 | { | 426 | { |
380 | // m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count); | 427 | if (AdaptiveEnabled) |
381 | if (m_enabled) | 428 | { |
382 | AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count)); | 429 | if (DebugLevel > 0) |
430 | m_log.WarnFormat( | ||
431 | "[ADAPTIVEBUCKET] drop {0} by {1} expired packets for {2}", | ||
432 | AdjustedDripRate, packets, Identifier); | ||
433 | |||
434 | // AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,packets)); | ||
435 | |||
436 | // Compute the fallback solely on the rate allocated beyond the minimum, this | ||
437 | // should smooth out the fallback to the minimum rate | ||
438 | AdjustedDripRate = m_minimumFlow + (Int64) ((AdjustedDripRate - m_minimumFlow) / Math.Pow(2, packets)); | ||
439 | } | ||
383 | } | 440 | } |
384 | 441 | ||
385 | // <summary> | 442 | /// <summary> |
386 | // | 443 | /// Reliable packets acked by the client adjust the drip rate up. |
387 | // </summary> | 444 | /// <param name="packets">Number of packets successfully acknowledged</param> |
388 | public void AcknowledgePackets(Int32 count) | 445 | /// </summary> |
446 | public void AcknowledgePackets(Int32 packets) | ||
447 | { | ||
448 | if (AdaptiveEnabled) | ||
449 | AdjustedDripRate = AdjustedDripRate + packets * LLUDPServer.MTU; | ||
450 | } | ||
451 | |||
452 | /// <summary> | ||
453 | /// Adjust the minimum flow level for the adaptive throttle, this will drop adjusted | ||
454 | /// throttles back to the minimum levels | ||
455 | /// <param>minDripRate--the new minimum flow</param> | ||
456 | /// </summary> | ||
457 | public void ResetMinimumAdaptiveFlow(Int64 minDripRate) | ||
389 | { | 458 | { |
390 | if (m_enabled) | 459 | m_minimumFlow = minDripRate; |
391 | AdjustedDripRate = AdjustedDripRate + count; | 460 | TargetDripRate = m_minimumFlow; |
461 | AdjustedDripRate = m_minimumFlow; | ||
392 | } | 462 | } |
393 | } | 463 | } |
394 | } | 464 | } \ No newline at end of file |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs index 9d6c09e..b546a99 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs | |||
@@ -31,6 +31,9 @@ using System.Net; | |||
31 | using System.Threading; | 31 | using System.Threading; |
32 | using OpenMetaverse; | 32 | using OpenMetaverse; |
33 | 33 | ||
34 | //using System.Reflection; | ||
35 | //using log4net; | ||
36 | |||
34 | namespace OpenSim.Region.ClientStack.LindenUDP | 37 | namespace OpenSim.Region.ClientStack.LindenUDP |
35 | { | 38 | { |
36 | /// <summary> | 39 | /// <summary> |
@@ -60,6 +63,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
60 | } | 63 | } |
61 | } | 64 | } |
62 | 65 | ||
66 | //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
67 | |||
63 | /// <summary>Holds the actual unacked packet data, sorted by sequence number</summary> | 68 | /// <summary>Holds the actual unacked packet data, sorted by sequence number</summary> |
64 | private Dictionary<uint, OutgoingPacket> m_packets = new Dictionary<uint, OutgoingPacket>(); | 69 | private Dictionary<uint, OutgoingPacket> m_packets = new Dictionary<uint, OutgoingPacket>(); |
65 | /// <summary>Holds packets that need to be added to the unacknowledged list</summary> | 70 | /// <summary>Holds packets that need to be added to the unacknowledged list</summary> |
@@ -164,8 +169,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
164 | } | 169 | } |
165 | } | 170 | } |
166 | 171 | ||
167 | //if (expiredPackets != null) | 172 | // if (expiredPackets != null) |
168 | // m_log.DebugFormat("[UNACKED PACKET COLLECTION]: Found {0} expired packets on timeout of {1}", expiredPackets.Count, timeoutMS); | 173 | // m_log.DebugFormat("[UNACKED PACKET COLLECTION]: Found {0} expired packets on timeout of {1}", expiredPackets.Count, timeoutMS); |
169 | 174 | ||
170 | return expiredPackets; | 175 | return expiredPackets; |
171 | } | 176 | } |
@@ -192,7 +197,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
192 | 197 | ||
193 | // As with other network applications, assume that an acknowledged packet is an | 198 | // As with other network applications, assume that an acknowledged packet is an |
194 | // indication that the network can handle a little more load, speed up the transmission | 199 | // indication that the network can handle a little more load, speed up the transmission |
195 | ackedPacket.Client.FlowThrottle.AcknowledgePackets(ackedPacket.Buffer.DataLength); | 200 | ackedPacket.Client.FlowThrottle.AcknowledgePackets(1); |
196 | 201 | ||
197 | // Update stats | 202 | // Update stats |
198 | Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); | 203 | Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); |
@@ -207,9 +212,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
207 | } | 212 | } |
208 | else | 213 | else |
209 | { | 214 | { |
210 | //m_log.WarnFormat("[UNACKED PACKET COLLECTION]: Could not find packet with sequence number {0} to ack", pendingAcknowledgement.SequenceNumber); | 215 | // m_log.WarnFormat("[UNACKED PACKET COLLECTION]: found null packet for sequence number {0} to ack", |
216 | // pendingAcknowledgement.SequenceNumber); | ||
211 | } | 217 | } |
212 | } | 218 | } |
219 | else | ||
220 | { | ||
221 | // m_log.WarnFormat("[UNACKED PACKET COLLECTION]: Could not find packet with sequence number {0} to ack", | ||
222 | // pendingAcknowledgement.SequenceNumber); | ||
223 | } | ||
213 | } | 224 | } |
214 | 225 | ||
215 | uint pendingRemove; | 226 | uint pendingRemove; |
diff --git a/OpenSim/Region/ClientStack/Properties/AssemblyInfo.cs b/OpenSim/Region/ClientStack/Properties/AssemblyInfo.cs index e72bd86..0b6ee2f 100644 --- a/OpenSim/Region/ClientStack/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/ClientStack/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.5.*")] | 32 | [assembly: AssemblyVersion("0.7.6.*")] |
33 | [assembly: AssemblyFileVersion("1.0.0.0")] | 33 | [assembly: AssemblyFileVersion("1.0.0.0")] |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs b/OpenSim/Region/CoreModules/Framework/UserManagement/Tests/HGUserManagementModuleTests.cs index 119a677..4e3b7e5 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs +++ b/OpenSim/Region/CoreModules/Framework/UserManagement/Tests/HGUserManagementModuleTests.cs | |||
@@ -25,54 +25,51 @@ | |||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System.Net; | 28 | using System; |
29 | using Nini.Config; | ||
30 | using NUnit.Framework; | ||
29 | using OpenMetaverse; | 31 | using OpenMetaverse; |
30 | using OpenSim.Framework; | 32 | using OpenSim.Framework; |
31 | using OpenSim.Region.Framework.Scenes; | 33 | using OpenSim.Region.CoreModules.Framework.UserManagement; |
32 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; | 34 | using OpenSim.Tests.Common; |
33 | 35 | ||
34 | namespace OpenSim.Region.ClientStack.LindenUDP.Tests | 36 | namespace OpenSim.Region.CoreModules.Framework.UserManagement.Tests |
35 | { | 37 | { |
36 | /// <summary> | 38 | [TestFixture] |
37 | /// Mock scene for unit tests | 39 | public class HGUserManagementModuleTests : OpenSimTestCase |
38 | /// </summary> | 40 | { |
39 | public class MockScene : SceneBase | ||
40 | { | ||
41 | public int ObjectNameCallsReceived | ||
42 | { | ||
43 | get { return m_objectNameCallsReceived; } | ||
44 | } | ||
45 | protected int m_objectNameCallsReceived; | ||
46 | |||
47 | public MockScene() : base(new RegionInfo(1000, 1000, null, null)) | ||
48 | { | ||
49 | m_regStatus = RegionStatus.Up; | ||
50 | } | ||
51 | |||
52 | public override void Update(int frames) {} | ||
53 | public override void LoadWorldMap() {} | ||
54 | |||
55 | public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type) | ||
56 | { | ||
57 | client.OnObjectName += RecordObjectNameCall; | ||
58 | |||
59 | // FIXME | ||
60 | return null; | ||
61 | } | ||
62 | |||
63 | public override void RemoveClient(UUID agentID, bool someReason) {} | ||
64 | // public override void CloseAllAgents(uint circuitcode) {} | ||
65 | public override bool CheckClient(UUID clientId, IPEndPoint endPoint) { return true; } | ||
66 | public override void OtherRegionUp(GridRegion otherRegion) { } | ||
67 | |||
68 | public override bool TryGetScenePresence(UUID uuid, out ScenePresence sp) { sp = null; return false; } | ||
69 | |||
70 | /// <summary> | 41 | /// <summary> |
71 | /// Doesn't really matter what the call is - we're using this to test that a packet has actually been received | 42 | /// Test that a new HG agent (i.e. one without a user account) has their name cached in the UMM upon creation. |
72 | /// </summary> | 43 | /// </summary> |
73 | protected void RecordObjectNameCall(IClientAPI remoteClient, uint localID, string message) | 44 | [Test] |
45 | public void TestCachedUserNameForNewAgent() | ||
74 | { | 46 | { |
75 | m_objectNameCallsReceived++; | 47 | TestHelpers.InMethod(); |
48 | // TestHelpers.EnableLogging(); | ||
49 | |||
50 | HGUserManagementModule hgumm = new HGUserManagementModule(); | ||
51 | UUID userId = TestHelpers.ParseStem("11"); | ||
52 | string firstName = "Fred"; | ||
53 | string lastName = "Astaire"; | ||
54 | string homeUri = "example.com"; | ||
55 | |||
56 | IConfigSource config = new IniConfigSource(); | ||
57 | config.AddConfig("Modules"); | ||
58 | config.Configs["Modules"].Set("UserManagementModule", hgumm.Name); | ||
59 | |||
60 | SceneHelpers sceneHelpers = new SceneHelpers(); | ||
61 | TestScene scene = sceneHelpers.SetupScene(); | ||
62 | SceneHelpers.SetupSceneModules(scene, config, hgumm); | ||
63 | |||
64 | AgentCircuitData acd = SceneHelpers.GenerateAgentData(userId); | ||
65 | acd.firstname = firstName; | ||
66 | acd.lastname = lastName; | ||
67 | acd.ServiceURLs["HomeURI"] = "http://" + homeUri; | ||
68 | |||
69 | SceneHelpers.AddScenePresence(scene, acd); | ||
70 | |||
71 | string name = hgumm.GetUserName(userId); | ||
72 | Assert.That(name, Is.EqualTo(string.Format("{0}.{1} @{2}", firstName, lastName, homeUri))); | ||
76 | } | 73 | } |
77 | } | 74 | } |
78 | } | 75 | } \ No newline at end of file |
diff --git a/OpenSim/Region/ClientStack/IClientNetworkServer.cs b/OpenSim/Services/Interfaces/IBansService.cs index 54a441b..8fd3521 100644 --- a/OpenSim/Region/ClientStack/IClientNetworkServer.cs +++ b/OpenSim/Services/Interfaces/IBansService.cs | |||
@@ -1,4 +1,4 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) Contributors, http://opensimulator.org/ | 2 | * Copyright (c) Contributors, http://opensimulator.org/ |
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | 3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. |
4 | * | 4 | * |
@@ -24,25 +24,25 @@ | |||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 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. | 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ | 26 | */ |
27 | using System; | ||
28 | using System.Collections.Generic; | ||
27 | 29 | ||
28 | using System.Net; | ||
29 | using System.Net.Sockets; | ||
30 | using Nini.Config; | ||
31 | using OpenSim.Framework; | 30 | using OpenSim.Framework; |
31 | using OpenMetaverse; | ||
32 | 32 | ||
33 | namespace OpenSim.Region.ClientStack | 33 | namespace OpenSim.Services.Interfaces |
34 | { | 34 | { |
35 | public interface IClientNetworkServer | 35 | public interface IBansService |
36 | { | 36 | { |
37 | void Initialise( | 37 | /// <summary> |
38 | IPAddress _listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, | 38 | /// Are any of the given arguments banned from the grid? |
39 | AgentCircuitManager authenticateClass); | 39 | /// </summary> |
40 | 40 | /// <param name="userID"></param> | |
41 | void NetworkStop(); | 41 | /// <param name="ip"></param> |
42 | bool HandlesRegion(Location x); | 42 | /// <param name="id0"></param> |
43 | void AddScene(IScene x); | 43 | /// <param name="origin"></param> |
44 | 44 | /// <returns></returns> | |
45 | void Start(); | 45 | bool IsBanned(string userID, string ip, string id0, string origin); |
46 | void Stop(); | ||
47 | } | 46 | } |
47 | |||
48 | } | 48 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/TestLLUDPServer.cs b/OpenSim/Tests/Common/Mock/TestLLUDPServer.cs index 27b9e5b..26887c9 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/TestLLUDPServer.cs +++ b/OpenSim/Tests/Common/Mock/TestLLUDPServer.cs | |||
@@ -32,8 +32,9 @@ using System.Net.Sockets; | |||
32 | using Nini.Config; | 32 | using Nini.Config; |
33 | using OpenMetaverse.Packets; | 33 | using OpenMetaverse.Packets; |
34 | using OpenSim.Framework; | 34 | using OpenSim.Framework; |
35 | using OpenSim.Region.ClientStack.LindenUDP; | ||
35 | 36 | ||
36 | namespace OpenSim.Region.ClientStack.LindenUDP.Tests | 37 | namespace OpenSim.Tests.Common |
37 | { | 38 | { |
38 | /// <summary> | 39 | /// <summary> |
39 | /// This class enables regression testing of the LLUDPServer by allowing us to intercept outgoing data. | 40 | /// This class enables regression testing of the LLUDPServer by allowing us to intercept outgoing data. |