diff options
Diffstat (limited to '')
21 files changed, 4023 insertions, 1386 deletions
diff --git a/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs b/OpenSim/Framework/LogWriter.cs index fd8d5e3..2e0bf4a 100755 --- a/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs +++ b/OpenSim/Framework/LogWriter.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 | * |
@@ -30,7 +30,7 @@ using System.IO; | |||
30 | using System.Text; | 30 | using System.Text; |
31 | using log4net; | 31 | using log4net; |
32 | 32 | ||
33 | namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging | 33 | namespace OpenSim.Framework |
34 | { | 34 | { |
35 | /// <summary> | 35 | /// <summary> |
36 | /// Class for writing a high performance, high volume log file. | 36 | /// Class for writing a high performance, high volume log file. |
@@ -52,6 +52,7 @@ namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging | |||
52 | private TimeSpan m_logFileLife; | 52 | private TimeSpan m_logFileLife; |
53 | private DateTime m_logFileEndTime; | 53 | private DateTime m_logFileEndTime; |
54 | private Object m_logFileWriteLock = new Object(); | 54 | private Object m_logFileWriteLock = new Object(); |
55 | private bool m_flushWrite; | ||
55 | 56 | ||
56 | // set externally when debugging. If let 'null', this does not write any error messages. | 57 | // set externally when debugging. If let 'null', this does not write any error messages. |
57 | public ILog ErrorLogger = null; | 58 | public ILog ErrorLogger = null; |
@@ -73,7 +74,9 @@ namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging | |||
73 | /// <param name="dir">The directory to create the log file in. May be 'null' for default.</param> | 74 | /// <param name="dir">The directory to create the log file in. May be 'null' for default.</param> |
74 | /// <param name="headr">The characters that begin the log file name. May be 'null' for default.</param> | 75 | /// <param name="headr">The characters that begin the log file name. May be 'null' for default.</param> |
75 | /// <param name="maxFileTime">Maximum age of a log file in minutes. If zero, will set default.</param> | 76 | /// <param name="maxFileTime">Maximum age of a log file in minutes. If zero, will set default.</param> |
76 | public LogWriter(string dir, string headr, int maxFileTime) | 77 | /// <param name="flushWrite">Whether to do a flush after every log write. Best left off but |
78 | /// if one is looking for a crash, this is a good thing to turn on.</param> | ||
79 | public LogWriter(string dir, string headr, int maxFileTime, bool flushWrite) | ||
77 | { | 80 | { |
78 | m_logDirectory = dir == null ? "." : dir; | 81 | m_logDirectory = dir == null ? "." : dir; |
79 | 82 | ||
@@ -86,8 +89,14 @@ namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging | |||
86 | m_logFileLife = new TimeSpan(0, m_logMaxFileTimeMin, 0); | 89 | m_logFileLife = new TimeSpan(0, m_logMaxFileTimeMin, 0); |
87 | m_logFileEndTime = DateTime.Now + m_logFileLife; | 90 | m_logFileEndTime = DateTime.Now + m_logFileLife; |
88 | 91 | ||
92 | m_flushWrite = flushWrite; | ||
93 | |||
89 | Enabled = true; | 94 | Enabled = true; |
90 | } | 95 | } |
96 | // Constructor that assumes flushWrite is off. | ||
97 | public LogWriter(string dir, string headr, int maxFileTime) : this(dir, headr, maxFileTime, false) | ||
98 | { | ||
99 | } | ||
91 | 100 | ||
92 | public void Dispose() | 101 | public void Dispose() |
93 | { | 102 | { |
@@ -127,7 +136,7 @@ namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging | |||
127 | { | 136 | { |
128 | lock (m_logFileWriteLock) | 137 | lock (m_logFileWriteLock) |
129 | { | 138 | { |
130 | DateTime now = DateTime.Now; | 139 | DateTime now = DateTime.UtcNow; |
131 | if (m_logFile == null || now > m_logFileEndTime) | 140 | if (m_logFile == null || now > m_logFileEndTime) |
132 | { | 141 | { |
133 | if (m_logFile != null) | 142 | if (m_logFile != null) |
@@ -142,7 +151,7 @@ namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging | |||
142 | string path = (m_logDirectory.Length > 0 ? m_logDirectory | 151 | string path = (m_logDirectory.Length > 0 ? m_logDirectory |
143 | + System.IO.Path.DirectorySeparatorChar.ToString() : "") | 152 | + System.IO.Path.DirectorySeparatorChar.ToString() : "") |
144 | + String.Format("{0}{1}.log", LogFileHeader, now.ToString("yyyyMMddHHmmss")); | 153 | + String.Format("{0}{1}.log", LogFileHeader, now.ToString("yyyyMMddHHmmss")); |
145 | m_logFile = new StreamWriter(File.Open(path, FileMode.Append, FileAccess.Write)); | 154 | m_logFile = new StreamWriter(File.Open(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)); |
146 | } | 155 | } |
147 | if (m_logFile != null) | 156 | if (m_logFile != null) |
148 | { | 157 | { |
@@ -153,6 +162,8 @@ namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging | |||
153 | buff.Append(line); | 162 | buff.Append(line); |
154 | buff.Append("\r\n"); | 163 | buff.Append("\r\n"); |
155 | m_logFile.Write(buff.ToString()); | 164 | m_logFile.Write(buff.ToString()); |
165 | if (m_flushWrite) | ||
166 | m_logFile.Flush(); | ||
156 | } | 167 | } |
157 | } | 168 | } |
158 | } | 169 | } |
diff --git a/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs b/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs index 8329af0..817ef85 100644 --- a/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs +++ b/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs | |||
@@ -28,6 +28,7 @@ | |||
28 | using System; | 28 | using System; |
29 | using System.Collections; | 29 | using System.Collections; |
30 | using System.Collections.Generic; | 30 | using System.Collections.Generic; |
31 | using System.Linq; | ||
31 | using System.Reflection; | 32 | using System.Reflection; |
32 | using System.Text; | 33 | using System.Text; |
33 | using log4net; | 34 | using log4net; |
@@ -37,6 +38,7 @@ using OpenMetaverse; | |||
37 | using OpenSim.Framework; | 38 | using OpenSim.Framework; |
38 | using OpenSim.Framework.Console; | 39 | using OpenSim.Framework.Console; |
39 | using OpenSim.Framework.Servers; | 40 | using OpenSim.Framework.Servers; |
41 | using OpenSim.Framework.Servers.HttpServer; | ||
40 | using OpenSim.Region.Framework.Interfaces; | 42 | using OpenSim.Region.Framework.Interfaces; |
41 | using OpenSim.Region.Framework.Scenes; | 43 | using OpenSim.Region.Framework.Scenes; |
42 | using Caps=OpenSim.Framework.Capabilities.Caps; | 44 | using Caps=OpenSim.Framework.Capabilities.Caps; |
@@ -57,8 +59,9 @@ namespace OpenSim.Region.CoreModules.Framework | |||
57 | /// </summary> | 59 | /// </summary> |
58 | protected Dictionary<UUID, Caps> m_capsObjects = new Dictionary<UUID, Caps>(); | 60 | protected Dictionary<UUID, Caps> m_capsObjects = new Dictionary<UUID, Caps>(); |
59 | 61 | ||
60 | protected Dictionary<UUID, string> capsPaths = new Dictionary<UUID, string>(); | 62 | protected Dictionary<UUID, string> m_capsPaths = new Dictionary<UUID, string>(); |
61 | protected Dictionary<UUID, Dictionary<ulong, string>> childrenSeeds | 63 | |
64 | protected Dictionary<UUID, Dictionary<ulong, string>> m_childrenSeeds | ||
62 | = new Dictionary<UUID, Dictionary<ulong, string>>(); | 65 | = new Dictionary<UUID, Dictionary<ulong, string>>(); |
63 | 66 | ||
64 | public void Initialise(IConfigSource source) | 67 | public void Initialise(IConfigSource source) |
@@ -70,9 +73,24 @@ namespace OpenSim.Region.CoreModules.Framework | |||
70 | m_scene = scene; | 73 | m_scene = scene; |
71 | m_scene.RegisterModuleInterface<ICapabilitiesModule>(this); | 74 | m_scene.RegisterModuleInterface<ICapabilitiesModule>(this); |
72 | 75 | ||
73 | MainConsole.Instance.Commands.AddCommand("Comms", false, "show caps", | 76 | MainConsole.Instance.Commands.AddCommand( |
74 | "show caps", | 77 | "Comms", false, "show caps list", |
75 | "Shows all registered capabilities for users", HandleShowCapsCommand); | 78 | "show caps list", |
79 | "Shows list of registered capabilities for users.", HandleShowCapsListCommand); | ||
80 | |||
81 | MainConsole.Instance.Commands.AddCommand( | ||
82 | "Comms", false, "show caps stats by user", | ||
83 | "show caps stats by user [<first-name> <last-name>]", | ||
84 | "Shows statistics on capabilities use by user.", | ||
85 | "If a user name is given, then prints a detailed breakdown of caps use ordered by number of requests received.", | ||
86 | HandleShowCapsStatsByUserCommand); | ||
87 | |||
88 | MainConsole.Instance.Commands.AddCommand( | ||
89 | "Comms", false, "show caps stats by cap", | ||
90 | "show caps stats by cap [<cap-name>]", | ||
91 | "Shows statistics on capabilities use by capability.", | ||
92 | "If a capability name is given, then prints a detailed breakdown of use by each user.", | ||
93 | HandleShowCapsStatsByCapCommand); | ||
76 | } | 94 | } |
77 | 95 | ||
78 | public void RegionLoaded(Scene scene) | 96 | public void RegionLoaded(Scene scene) |
@@ -105,35 +123,43 @@ namespace OpenSim.Region.CoreModules.Framework | |||
105 | if (m_scene.RegionInfo.EstateSettings.IsBanned(agentId)) | 123 | if (m_scene.RegionInfo.EstateSettings.IsBanned(agentId)) |
106 | return; | 124 | return; |
107 | 125 | ||
126 | Caps caps; | ||
108 | String capsObjectPath = GetCapsPath(agentId); | 127 | String capsObjectPath = GetCapsPath(agentId); |
109 | 128 | ||
110 | if (m_capsObjects.ContainsKey(agentId)) | 129 | lock (m_capsObjects) |
111 | { | 130 | { |
112 | Caps oldCaps = m_capsObjects[agentId]; | 131 | if (m_capsObjects.ContainsKey(agentId)) |
113 | 132 | { | |
114 | m_log.DebugFormat( | 133 | Caps oldCaps = m_capsObjects[agentId]; |
115 | "[CAPS]: Recreating caps for agent {0}. Old caps path {1}, new caps path {2}. ", | 134 | |
116 | agentId, oldCaps.CapsObjectPath, capsObjectPath); | 135 | //m_log.WarnFormat( |
117 | // This should not happen. The caller code is confused. We need to fix that. | 136 | // "[CAPS]: Recreating caps for agent {0} in region {1}. Old caps path {2}, new caps path {3}. ", |
118 | // CAPs can never be reregistered, or the client will be confused. | 137 | // agentId, m_scene.RegionInfo.RegionName, oldCaps.CapsObjectPath, capsObjectPath); |
119 | // Hence this return here. | 138 | } |
120 | //return; | ||
121 | } | ||
122 | 139 | ||
123 | Caps caps = new Caps(MainServer.Instance, m_scene.RegionInfo.ExternalHostName, | 140 | // m_log.DebugFormat( |
124 | (MainServer.Instance == null) ? 0: MainServer.Instance.Port, | 141 | // "[CAPS]: Adding capabilities for agent {0} in {1} with path {2}", |
125 | capsObjectPath, agentId, m_scene.RegionInfo.RegionName); | 142 | // agentId, m_scene.RegionInfo.RegionName, capsObjectPath); |
126 | 143 | ||
127 | m_capsObjects[agentId] = caps; | 144 | caps = new Caps(MainServer.Instance, m_scene.RegionInfo.ExternalHostName, |
145 | (MainServer.Instance == null) ? 0: MainServer.Instance.Port, | ||
146 | capsObjectPath, agentId, m_scene.RegionInfo.RegionName); | ||
147 | |||
148 | m_capsObjects[agentId] = caps; | ||
149 | } | ||
128 | 150 | ||
129 | m_scene.EventManager.TriggerOnRegisterCaps(agentId, caps); | 151 | m_scene.EventManager.TriggerOnRegisterCaps(agentId, caps); |
130 | } | 152 | } |
131 | 153 | ||
132 | public void RemoveCaps(UUID agentId) | 154 | public void RemoveCaps(UUID agentId) |
133 | { | 155 | { |
134 | if (childrenSeeds.ContainsKey(agentId)) | 156 | m_log.DebugFormat("[CAPS]: Remove caps for agent {0} in region {1}", agentId, m_scene.RegionInfo.RegionName); |
157 | lock (m_childrenSeeds) | ||
135 | { | 158 | { |
136 | childrenSeeds.Remove(agentId); | 159 | if (m_childrenSeeds.ContainsKey(agentId)) |
160 | { | ||
161 | m_childrenSeeds.Remove(agentId); | ||
162 | } | ||
137 | } | 163 | } |
138 | 164 | ||
139 | lock (m_capsObjects) | 165 | lock (m_capsObjects) |
@@ -168,16 +194,22 @@ namespace OpenSim.Region.CoreModules.Framework | |||
168 | 194 | ||
169 | public void SetAgentCapsSeeds(AgentCircuitData agent) | 195 | public void SetAgentCapsSeeds(AgentCircuitData agent) |
170 | { | 196 | { |
171 | capsPaths[agent.AgentID] = agent.CapsPath; | 197 | lock (m_capsPaths) |
172 | childrenSeeds[agent.AgentID] | 198 | m_capsPaths[agent.AgentID] = agent.CapsPath; |
173 | = ((agent.ChildrenCapSeeds == null) ? new Dictionary<ulong, string>() : agent.ChildrenCapSeeds); | 199 | |
200 | lock (m_childrenSeeds) | ||
201 | m_childrenSeeds[agent.AgentID] | ||
202 | = ((agent.ChildrenCapSeeds == null) ? new Dictionary<ulong, string>() : agent.ChildrenCapSeeds); | ||
174 | } | 203 | } |
175 | 204 | ||
176 | public string GetCapsPath(UUID agentId) | 205 | public string GetCapsPath(UUID agentId) |
177 | { | 206 | { |
178 | if (capsPaths.ContainsKey(agentId)) | 207 | lock (m_capsPaths) |
179 | { | 208 | { |
180 | return capsPaths[agentId]; | 209 | if (m_capsPaths.ContainsKey(agentId)) |
210 | { | ||
211 | return m_capsPaths[agentId]; | ||
212 | } | ||
181 | } | 213 | } |
182 | 214 | ||
183 | return null; | 215 | return null; |
@@ -186,17 +218,24 @@ namespace OpenSim.Region.CoreModules.Framework | |||
186 | public Dictionary<ulong, string> GetChildrenSeeds(UUID agentID) | 218 | public Dictionary<ulong, string> GetChildrenSeeds(UUID agentID) |
187 | { | 219 | { |
188 | Dictionary<ulong, string> seeds = null; | 220 | Dictionary<ulong, string> seeds = null; |
189 | if (childrenSeeds.TryGetValue(agentID, out seeds)) | 221 | |
190 | return seeds; | 222 | lock (m_childrenSeeds) |
223 | if (m_childrenSeeds.TryGetValue(agentID, out seeds)) | ||
224 | return seeds; | ||
225 | |||
191 | return new Dictionary<ulong, string>(); | 226 | return new Dictionary<ulong, string>(); |
192 | } | 227 | } |
193 | 228 | ||
194 | public void DropChildSeed(UUID agentID, ulong handle) | 229 | public void DropChildSeed(UUID agentID, ulong handle) |
195 | { | 230 | { |
196 | Dictionary<ulong, string> seeds; | 231 | Dictionary<ulong, string> seeds; |
197 | if (childrenSeeds.TryGetValue(agentID, out seeds)) | 232 | |
233 | lock (m_childrenSeeds) | ||
198 | { | 234 | { |
199 | seeds.Remove(handle); | 235 | if (m_childrenSeeds.TryGetValue(agentID, out seeds)) |
236 | { | ||
237 | seeds.Remove(handle); | ||
238 | } | ||
200 | } | 239 | } |
201 | } | 240 | } |
202 | 241 | ||
@@ -204,53 +243,327 @@ namespace OpenSim.Region.CoreModules.Framework | |||
204 | { | 243 | { |
205 | Dictionary<ulong, string> seeds; | 244 | Dictionary<ulong, string> seeds; |
206 | string returnval; | 245 | string returnval; |
207 | if (childrenSeeds.TryGetValue(agentID, out seeds)) | 246 | |
247 | lock (m_childrenSeeds) | ||
208 | { | 248 | { |
209 | if (seeds.TryGetValue(handle, out returnval)) | 249 | if (m_childrenSeeds.TryGetValue(agentID, out seeds)) |
210 | return returnval; | 250 | { |
251 | if (seeds.TryGetValue(handle, out returnval)) | ||
252 | return returnval; | ||
253 | } | ||
211 | } | 254 | } |
255 | |||
212 | return null; | 256 | return null; |
213 | } | 257 | } |
214 | 258 | ||
215 | public void SetChildrenSeed(UUID agentID, Dictionary<ulong, string> seeds) | 259 | public void SetChildrenSeed(UUID agentID, Dictionary<ulong, string> seeds) |
216 | { | 260 | { |
217 | //m_log.DebugFormat(" !!! Setting child seeds in {0} to {1}", m_scene.RegionInfo.RegionName, seeds.Count); | 261 | //m_log.DebugFormat(" !!! Setting child seeds in {0} to {1}", m_scene.RegionInfo.RegionName, seeds.Count); |
218 | childrenSeeds[agentID] = seeds; | 262 | |
263 | lock (m_childrenSeeds) | ||
264 | m_childrenSeeds[agentID] = seeds; | ||
219 | } | 265 | } |
220 | 266 | ||
221 | public void DumpChildrenSeeds(UUID agentID) | 267 | public void DumpChildrenSeeds(UUID agentID) |
222 | { | 268 | { |
223 | m_log.Info("================ ChildrenSeed "+m_scene.RegionInfo.RegionName+" ================"); | 269 | m_log.Info("================ ChildrenSeed "+m_scene.RegionInfo.RegionName+" ================"); |
224 | foreach (KeyValuePair<ulong, string> kvp in childrenSeeds[agentID]) | 270 | |
271 | lock (m_childrenSeeds) | ||
272 | { | ||
273 | foreach (KeyValuePair<ulong, string> kvp in m_childrenSeeds[agentID]) | ||
274 | { | ||
275 | uint x, y; | ||
276 | Util.RegionHandleToRegionLoc(kvp.Key, out x, out y); | ||
277 | m_log.Info(" >> "+x+", "+y+": "+kvp.Value); | ||
278 | } | ||
279 | } | ||
280 | } | ||
281 | |||
282 | private void HandleShowCapsListCommand(string module, string[] cmdParams) | ||
283 | { | ||
284 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene) | ||
285 | return; | ||
286 | |||
287 | StringBuilder capsReport = new StringBuilder(); | ||
288 | capsReport.AppendFormat("Region {0}:\n", m_scene.RegionInfo.RegionName); | ||
289 | |||
290 | lock (m_capsObjects) | ||
225 | { | 291 | { |
226 | uint x, y; | 292 | foreach (KeyValuePair<UUID, Caps> kvp in m_capsObjects) |
227 | Utils.LongToUInts(kvp.Key, out x, out y); | 293 | { |
228 | x = x / Constants.RegionSize; | 294 | capsReport.AppendFormat("** User {0}:\n", kvp.Key); |
229 | y = y / Constants.RegionSize; | 295 | Caps caps = kvp.Value; |
230 | m_log.Info(" >> "+x+", "+y+": "+kvp.Value); | 296 | |
297 | for (IDictionaryEnumerator kvp2 = caps.CapsHandlers.GetCapsDetails(false, null).GetEnumerator(); kvp2.MoveNext(); ) | ||
298 | { | ||
299 | Uri uri = new Uri(kvp2.Value.ToString()); | ||
300 | capsReport.AppendFormat(m_showCapsCommandFormat, kvp2.Key, uri.PathAndQuery); | ||
301 | } | ||
302 | |||
303 | foreach (KeyValuePair<string, PollServiceEventArgs> kvp2 in caps.GetPollHandlers()) | ||
304 | capsReport.AppendFormat(m_showCapsCommandFormat, kvp2.Key, kvp2.Value.Url); | ||
305 | |||
306 | foreach (KeyValuePair<string, string> kvp3 in caps.ExternalCapsHandlers) | ||
307 | capsReport.AppendFormat(m_showCapsCommandFormat, kvp3.Key, kvp3.Value); | ||
308 | } | ||
231 | } | 309 | } |
310 | |||
311 | MainConsole.Instance.Output(capsReport.ToString()); | ||
232 | } | 312 | } |
233 | 313 | ||
234 | private void HandleShowCapsCommand(string module, string[] cmdparams) | 314 | private void HandleShowCapsStatsByCapCommand(string module, string[] cmdParams) |
235 | { | 315 | { |
236 | StringBuilder caps = new StringBuilder(); | 316 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene) |
237 | caps.AppendFormat("Region {0}:\n", m_scene.RegionInfo.RegionName); | 317 | return; |
318 | |||
319 | if (cmdParams.Length != 5 && cmdParams.Length != 6) | ||
320 | { | ||
321 | MainConsole.Instance.Output("Usage: show caps stats by cap [<cap-name>]"); | ||
322 | return; | ||
323 | } | ||
324 | |||
325 | StringBuilder sb = new StringBuilder(); | ||
326 | sb.AppendFormat("Region {0}:\n", m_scene.Name); | ||
327 | |||
328 | if (cmdParams.Length == 5) | ||
329 | { | ||
330 | BuildSummaryStatsByCapReport(sb); | ||
331 | } | ||
332 | else if (cmdParams.Length == 6) | ||
333 | { | ||
334 | BuildDetailedStatsByCapReport(sb, cmdParams[5]); | ||
335 | } | ||
336 | |||
337 | MainConsole.Instance.Output(sb.ToString()); | ||
338 | } | ||
339 | |||
340 | private void BuildDetailedStatsByCapReport(StringBuilder sb, string capName) | ||
341 | { | ||
342 | sb.AppendFormat("Capability name {0}\n", capName); | ||
343 | |||
344 | ConsoleDisplayTable cdt = new ConsoleDisplayTable(); | ||
345 | cdt.AddColumn("User Name", 34); | ||
346 | cdt.AddColumn("Req Received", 12); | ||
347 | cdt.AddColumn("Req Handled", 12); | ||
348 | cdt.Indent = 2; | ||
349 | |||
350 | Dictionary<string, int> receivedStats = new Dictionary<string, int>(); | ||
351 | Dictionary<string, int> handledStats = new Dictionary<string, int>(); | ||
352 | |||
353 | m_scene.ForEachScenePresence( | ||
354 | sp => | ||
355 | { | ||
356 | Caps caps = m_scene.CapsModule.GetCapsForUser(sp.UUID); | ||
238 | 357 | ||
239 | foreach (KeyValuePair<UUID, Caps> kvp in m_capsObjects) | 358 | if (caps == null) |
359 | return; | ||
360 | |||
361 | Dictionary<string, IRequestHandler> capsHandlers = caps.CapsHandlers.GetCapsHandlers(); | ||
362 | |||
363 | IRequestHandler reqHandler; | ||
364 | if (capsHandlers.TryGetValue(capName, out reqHandler)) | ||
365 | { | ||
366 | receivedStats[sp.Name] = reqHandler.RequestsReceived; | ||
367 | handledStats[sp.Name] = reqHandler.RequestsHandled; | ||
368 | } | ||
369 | else | ||
370 | { | ||
371 | PollServiceEventArgs pollHandler = null; | ||
372 | if (caps.TryGetPollHandler(capName, out pollHandler)) | ||
373 | { | ||
374 | receivedStats[sp.Name] = pollHandler.RequestsReceived; | ||
375 | handledStats[sp.Name] = pollHandler.RequestsHandled; | ||
376 | } | ||
377 | } | ||
378 | } | ||
379 | ); | ||
380 | |||
381 | foreach (KeyValuePair<string, int> kvp in receivedStats.OrderByDescending(kp => kp.Value)) | ||
240 | { | 382 | { |
241 | caps.AppendFormat("** User {0}:\n", kvp.Key); | 383 | cdt.AddRow(kvp.Key, kvp.Value, handledStats[kvp.Key]); |
384 | } | ||
385 | |||
386 | sb.Append(cdt.ToString()); | ||
387 | } | ||
388 | |||
389 | private void BuildSummaryStatsByCapReport(StringBuilder sb) | ||
390 | { | ||
391 | ConsoleDisplayTable cdt = new ConsoleDisplayTable(); | ||
392 | cdt.AddColumn("Name", 34); | ||
393 | cdt.AddColumn("Req Received", 12); | ||
394 | cdt.AddColumn("Req Handled", 12); | ||
395 | cdt.Indent = 2; | ||
396 | |||
397 | Dictionary<string, int> receivedStats = new Dictionary<string, int>(); | ||
398 | Dictionary<string, int> handledStats = new Dictionary<string, int>(); | ||
242 | 399 | ||
243 | for (IDictionaryEnumerator kvp2 = kvp.Value.CapsHandlers.GetCapsDetails(false).GetEnumerator(); kvp2.MoveNext(); ) | 400 | m_scene.ForEachScenePresence( |
401 | sp => | ||
244 | { | 402 | { |
245 | Uri uri = new Uri(kvp2.Value.ToString()); | 403 | Caps caps = m_scene.CapsModule.GetCapsForUser(sp.UUID); |
246 | caps.AppendFormat(m_showCapsCommandFormat, kvp2.Key, uri.PathAndQuery); | 404 | |
405 | if (caps == null) | ||
406 | return; | ||
407 | |||
408 | foreach (IRequestHandler reqHandler in caps.CapsHandlers.GetCapsHandlers().Values) | ||
409 | { | ||
410 | string reqName = reqHandler.Name ?? ""; | ||
411 | |||
412 | if (!receivedStats.ContainsKey(reqName)) | ||
413 | { | ||
414 | receivedStats[reqName] = reqHandler.RequestsReceived; | ||
415 | handledStats[reqName] = reqHandler.RequestsHandled; | ||
416 | } | ||
417 | else | ||
418 | { | ||
419 | receivedStats[reqName] += reqHandler.RequestsReceived; | ||
420 | handledStats[reqName] += reqHandler.RequestsHandled; | ||
421 | } | ||
422 | } | ||
423 | |||
424 | foreach (KeyValuePair<string, PollServiceEventArgs> kvp in caps.GetPollHandlers()) | ||
425 | { | ||
426 | string name = kvp.Key; | ||
427 | PollServiceEventArgs pollHandler = kvp.Value; | ||
428 | |||
429 | if (!receivedStats.ContainsKey(name)) | ||
430 | { | ||
431 | receivedStats[name] = pollHandler.RequestsReceived; | ||
432 | handledStats[name] = pollHandler.RequestsHandled; | ||
433 | } | ||
434 | else | ||
435 | { | ||
436 | receivedStats[name] += pollHandler.RequestsReceived; | ||
437 | handledStats[name] += pollHandler.RequestsHandled; | ||
438 | } | ||
439 | } | ||
247 | } | 440 | } |
441 | ); | ||
442 | |||
443 | foreach (KeyValuePair<string, int> kvp in receivedStats.OrderByDescending(kp => kp.Value)) | ||
444 | cdt.AddRow(kvp.Key, kvp.Value, handledStats[kvp.Key]); | ||
445 | |||
446 | sb.Append(cdt.ToString()); | ||
447 | } | ||
448 | |||
449 | private void HandleShowCapsStatsByUserCommand(string module, string[] cmdParams) | ||
450 | { | ||
451 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene) | ||
452 | return; | ||
453 | |||
454 | if (cmdParams.Length != 5 && cmdParams.Length != 7) | ||
455 | { | ||
456 | MainConsole.Instance.Output("Usage: show caps stats by user [<first-name> <last-name>]"); | ||
457 | return; | ||
458 | } | ||
459 | |||
460 | StringBuilder sb = new StringBuilder(); | ||
461 | sb.AppendFormat("Region {0}:\n", m_scene.Name); | ||
462 | |||
463 | if (cmdParams.Length == 5) | ||
464 | { | ||
465 | BuildSummaryStatsByUserReport(sb); | ||
466 | } | ||
467 | else if (cmdParams.Length == 7) | ||
468 | { | ||
469 | string firstName = cmdParams[5]; | ||
470 | string lastName = cmdParams[6]; | ||
471 | |||
472 | ScenePresence sp = m_scene.GetScenePresence(firstName, lastName); | ||
473 | |||
474 | if (sp == null) | ||
475 | return; | ||
248 | 476 | ||
249 | foreach (KeyValuePair<string, string> kvp3 in kvp.Value.ExternalCapsHandlers) | 477 | BuildDetailedStatsByUserReport(sb, sp); |
250 | caps.AppendFormat(m_showCapsCommandFormat, kvp3.Key, kvp3.Value); | ||
251 | } | 478 | } |
252 | 479 | ||
253 | MainConsole.Instance.Output(caps.ToString()); | 480 | MainConsole.Instance.Output(sb.ToString()); |
481 | } | ||
482 | |||
483 | private void BuildDetailedStatsByUserReport(StringBuilder sb, ScenePresence sp) | ||
484 | { | ||
485 | sb.AppendFormat("Avatar name {0}, type {1}\n", sp.Name, sp.IsChildAgent ? "child" : "root"); | ||
486 | |||
487 | ConsoleDisplayTable cdt = new ConsoleDisplayTable(); | ||
488 | cdt.AddColumn("Cap Name", 34); | ||
489 | cdt.AddColumn("Req Received", 12); | ||
490 | cdt.AddColumn("Req Handled", 12); | ||
491 | cdt.Indent = 2; | ||
492 | |||
493 | Caps caps = m_scene.CapsModule.GetCapsForUser(sp.UUID); | ||
494 | |||
495 | if (caps == null) | ||
496 | return; | ||
497 | |||
498 | List<CapTableRow> capRows = new List<CapTableRow>(); | ||
499 | |||
500 | foreach (IRequestHandler reqHandler in caps.CapsHandlers.GetCapsHandlers().Values) | ||
501 | capRows.Add(new CapTableRow(reqHandler.Name, reqHandler.RequestsReceived, reqHandler.RequestsHandled)); | ||
502 | |||
503 | foreach (KeyValuePair<string, PollServiceEventArgs> kvp in caps.GetPollHandlers()) | ||
504 | capRows.Add(new CapTableRow(kvp.Key, kvp.Value.RequestsReceived, kvp.Value.RequestsHandled)); | ||
505 | |||
506 | foreach (CapTableRow ctr in capRows.OrderByDescending(ctr => ctr.RequestsReceived)) | ||
507 | cdt.AddRow(ctr.Name, ctr.RequestsReceived, ctr.RequestsHandled); | ||
508 | |||
509 | sb.Append(cdt.ToString()); | ||
510 | } | ||
511 | |||
512 | private void BuildSummaryStatsByUserReport(StringBuilder sb) | ||
513 | { | ||
514 | ConsoleDisplayTable cdt = new ConsoleDisplayTable(); | ||
515 | cdt.AddColumn("Name", 32); | ||
516 | cdt.AddColumn("Type", 5); | ||
517 | cdt.AddColumn("Req Received", 12); | ||
518 | cdt.AddColumn("Req Handled", 12); | ||
519 | cdt.Indent = 2; | ||
520 | |||
521 | m_scene.ForEachScenePresence( | ||
522 | sp => | ||
523 | { | ||
524 | Caps caps = m_scene.CapsModule.GetCapsForUser(sp.UUID); | ||
525 | |||
526 | if (caps == null) | ||
527 | return; | ||
528 | |||
529 | Dictionary<string, IRequestHandler> capsHandlers = caps.CapsHandlers.GetCapsHandlers(); | ||
530 | |||
531 | int totalRequestsReceived = 0; | ||
532 | int totalRequestsHandled = 0; | ||
533 | |||
534 | foreach (IRequestHandler reqHandler in capsHandlers.Values) | ||
535 | { | ||
536 | totalRequestsReceived += reqHandler.RequestsReceived; | ||
537 | totalRequestsHandled += reqHandler.RequestsHandled; | ||
538 | } | ||
539 | |||
540 | Dictionary<string, PollServiceEventArgs> capsPollHandlers = caps.GetPollHandlers(); | ||
541 | |||
542 | foreach (PollServiceEventArgs handler in capsPollHandlers.Values) | ||
543 | { | ||
544 | totalRequestsReceived += handler.RequestsReceived; | ||
545 | totalRequestsHandled += handler.RequestsHandled; | ||
546 | } | ||
547 | |||
548 | cdt.AddRow(sp.Name, sp.IsChildAgent ? "child" : "root", totalRequestsReceived, totalRequestsHandled); | ||
549 | } | ||
550 | ); | ||
551 | |||
552 | sb.Append(cdt.ToString()); | ||
553 | } | ||
554 | |||
555 | private class CapTableRow | ||
556 | { | ||
557 | public string Name { get; set; } | ||
558 | public int RequestsReceived { get; set; } | ||
559 | public int RequestsHandled { get; set; } | ||
560 | |||
561 | public CapTableRow(string name, int requestsReceived, int requestsHandled) | ||
562 | { | ||
563 | Name = name; | ||
564 | RequestsReceived = requestsReceived; | ||
565 | RequestsHandled = requestsHandled; | ||
566 | } | ||
254 | } | 567 | } |
255 | } | 568 | } |
256 | } | 569 | } \ No newline at end of file |
diff --git a/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DAExampleModule.cs b/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DAExampleModule.cs new file mode 100644 index 0000000..0c632b1 --- /dev/null +++ b/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DAExampleModule.cs | |||
@@ -0,0 +1,124 @@ | |||
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 log4net; | ||
32 | using Mono.Addins; | ||
33 | using Nini.Config; | ||
34 | using OpenMetaverse; | ||
35 | using OpenMetaverse.Packets; | ||
36 | using OpenMetaverse.StructuredData; | ||
37 | using OpenSim.Framework; | ||
38 | using OpenSim.Region.Framework; | ||
39 | using OpenSim.Region.Framework.Interfaces; | ||
40 | using OpenSim.Region.Framework.Scenes; | ||
41 | |||
42 | namespace OpenSim.Region.CoreModules.Framework.DynamicAttributes.DAExampleModule | ||
43 | { | ||
44 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "DAExampleModule")] | ||
45 | public class DAExampleModule : INonSharedRegionModule | ||
46 | { | ||
47 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
48 | |||
49 | private readonly bool ENABLED = false; // enable for testing | ||
50 | |||
51 | public const string Namespace = "Example"; | ||
52 | public const string StoreName = "DA"; | ||
53 | |||
54 | protected Scene m_scene; | ||
55 | protected IDialogModule m_dialogMod; | ||
56 | |||
57 | public string Name { get { return "DAExample Module"; } } | ||
58 | public Type ReplaceableInterface { get { return null; } } | ||
59 | |||
60 | public void Initialise(IConfigSource source) {} | ||
61 | |||
62 | public void AddRegion(Scene scene) | ||
63 | { | ||
64 | if (ENABLED) | ||
65 | { | ||
66 | m_scene = scene; | ||
67 | m_scene.EventManager.OnSceneGroupMove += OnSceneGroupMove; | ||
68 | m_dialogMod = m_scene.RequestModuleInterface<IDialogModule>(); | ||
69 | |||
70 | m_log.DebugFormat("[DA EXAMPLE MODULE]: Added region {0}", m_scene.Name); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | public void RemoveRegion(Scene scene) | ||
75 | { | ||
76 | if (ENABLED) | ||
77 | { | ||
78 | m_scene.EventManager.OnSceneGroupMove -= OnSceneGroupMove; | ||
79 | } | ||
80 | } | ||
81 | |||
82 | public void RegionLoaded(Scene scene) {} | ||
83 | |||
84 | public void Close() | ||
85 | { | ||
86 | RemoveRegion(m_scene); | ||
87 | } | ||
88 | |||
89 | protected bool OnSceneGroupMove(UUID groupId, Vector3 delta) | ||
90 | { | ||
91 | OSDMap attrs = null; | ||
92 | SceneObjectPart sop = m_scene.GetSceneObjectPart(groupId); | ||
93 | |||
94 | if (sop == null) | ||
95 | return true; | ||
96 | |||
97 | if (!sop.DynAttrs.TryGetStore(Namespace, StoreName, out attrs)) | ||
98 | attrs = new OSDMap(); | ||
99 | |||
100 | OSDInteger newValue; | ||
101 | |||
102 | // We have to lock on the entire dynamic attributes map to avoid race conditions with serialization code. | ||
103 | lock (sop.DynAttrs) | ||
104 | { | ||
105 | if (!attrs.ContainsKey("moves")) | ||
106 | newValue = new OSDInteger(1); | ||
107 | else | ||
108 | newValue = new OSDInteger(attrs["moves"].AsInteger() + 1); | ||
109 | |||
110 | attrs["moves"] = newValue; | ||
111 | |||
112 | sop.DynAttrs.SetStore(Namespace, StoreName, attrs); | ||
113 | } | ||
114 | |||
115 | sop.ParentGroup.HasGroupChanged = true; | ||
116 | |||
117 | string msg = string.Format("{0} {1} moved {2} times", sop.Name, sop.UUID, newValue); | ||
118 | m_log.DebugFormat("[DA EXAMPLE MODULE]: {0}", msg); | ||
119 | m_dialogMod.SendGeneralAlert(msg); | ||
120 | |||
121 | return true; | ||
122 | } | ||
123 | } | ||
124 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DOExampleModule.cs b/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DOExampleModule.cs new file mode 100644 index 0000000..166a994 --- /dev/null +++ b/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DOExampleModule.cs | |||
@@ -0,0 +1,139 @@ | |||
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 log4net; | ||
32 | using Mono.Addins; | ||
33 | using Nini.Config; | ||
34 | using OpenMetaverse; | ||
35 | using OpenMetaverse.Packets; | ||
36 | using OpenMetaverse.StructuredData; | ||
37 | using OpenSim.Framework; | ||
38 | using OpenSim.Region.Framework; | ||
39 | using OpenSim.Region.CoreModules.Framework.DynamicAttributes.DAExampleModule; | ||
40 | using OpenSim.Region.Framework.Interfaces; | ||
41 | using OpenSim.Region.Framework.Scenes; | ||
42 | |||
43 | namespace OpenSim.Region.Framework.DynamicAttributes.DOExampleModule | ||
44 | { | ||
45 | /// <summary> | ||
46 | /// Example module for experimenting with and demonstrating dynamic object ideas. | ||
47 | /// </summary> | ||
48 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "DOExampleModule")] | ||
49 | public class DOExampleModule : INonSharedRegionModule | ||
50 | { | ||
51 | public class MyObject | ||
52 | { | ||
53 | public int Moves { get; set; } | ||
54 | |||
55 | public MyObject(int moves) | ||
56 | { | ||
57 | Moves = moves; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
62 | |||
63 | private static readonly bool ENABLED = false; // enable for testing | ||
64 | |||
65 | private Scene m_scene; | ||
66 | private IDialogModule m_dialogMod; | ||
67 | |||
68 | public string Name { get { return "DO"; } } | ||
69 | public Type ReplaceableInterface { get { return null; } } | ||
70 | |||
71 | public void Initialise(IConfigSource source) {} | ||
72 | |||
73 | public void AddRegion(Scene scene) | ||
74 | { | ||
75 | if (ENABLED) | ||
76 | { | ||
77 | m_scene = scene; | ||
78 | m_scene.EventManager.OnObjectAddedToScene += OnObjectAddedToScene; | ||
79 | m_scene.EventManager.OnSceneGroupMove += OnSceneGroupMove; | ||
80 | m_dialogMod = m_scene.RequestModuleInterface<IDialogModule>(); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | public void RemoveRegion(Scene scene) | ||
85 | { | ||
86 | if (ENABLED) | ||
87 | { | ||
88 | m_scene.EventManager.OnSceneGroupMove -= OnSceneGroupMove; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | public void RegionLoaded(Scene scene) {} | ||
93 | |||
94 | public void Close() | ||
95 | { | ||
96 | RemoveRegion(m_scene); | ||
97 | } | ||
98 | |||
99 | private void OnObjectAddedToScene(SceneObjectGroup so) | ||
100 | { | ||
101 | SceneObjectPart rootPart = so.RootPart; | ||
102 | |||
103 | OSDMap attrs; | ||
104 | |||
105 | int movesSoFar = 0; | ||
106 | |||
107 | // Console.WriteLine("Here for {0}", so.Name); | ||
108 | |||
109 | if (rootPart.DynAttrs.TryGetStore(DAExampleModule.Namespace, DAExampleModule.StoreName, out attrs)) | ||
110 | { | ||
111 | movesSoFar = attrs["moves"].AsInteger(); | ||
112 | |||
113 | m_log.DebugFormat( | ||
114 | "[DO EXAMPLE MODULE]: Found saved moves {0} for {1} in {2}", movesSoFar, so.Name, m_scene.Name); | ||
115 | } | ||
116 | |||
117 | rootPart.DynObjs.Add(DAExampleModule.Namespace, Name, new MyObject(movesSoFar)); | ||
118 | } | ||
119 | |||
120 | private bool OnSceneGroupMove(UUID groupId, Vector3 delta) | ||
121 | { | ||
122 | SceneObjectGroup so = m_scene.GetSceneObjectGroup(groupId); | ||
123 | |||
124 | if (so == null) | ||
125 | return true; | ||
126 | |||
127 | object rawObj = so.RootPart.DynObjs.Get(Name); | ||
128 | |||
129 | if (rawObj != null) | ||
130 | { | ||
131 | MyObject myObj = (MyObject)rawObj; | ||
132 | |||
133 | m_dialogMod.SendGeneralAlert(string.Format("{0} {1} moved {2} times", so.Name, so.UUID, ++myObj.Moves)); | ||
134 | } | ||
135 | |||
136 | return true; | ||
137 | } | ||
138 | } | ||
139 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index c43edd2..a2417c4 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs | |||
@@ -33,9 +33,10 @@ using System.Threading; | |||
33 | using OpenSim.Framework; | 33 | using OpenSim.Framework; |
34 | using OpenSim.Framework.Capabilities; | 34 | using OpenSim.Framework.Capabilities; |
35 | using OpenSim.Framework.Client; | 35 | using OpenSim.Framework.Client; |
36 | using OpenSim.Framework.Monitoring; | ||
36 | using OpenSim.Region.Framework.Interfaces; | 37 | using OpenSim.Region.Framework.Interfaces; |
37 | using OpenSim.Region.Framework.Scenes; | 38 | using OpenSim.Region.Framework.Scenes; |
38 | using OpenSim.Region.Physics.Manager; | 39 | using OpenSim.Region.PhysicsModules.SharedBase; |
39 | using OpenSim.Services.Interfaces; | 40 | using OpenSim.Services.Interfaces; |
40 | 41 | ||
41 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; | 42 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; |
@@ -51,15 +52,61 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
51 | public class EntityTransferModule : INonSharedRegionModule, IEntityTransferModule | 52 | public class EntityTransferModule : INonSharedRegionModule, IEntityTransferModule |
52 | { | 53 | { |
53 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 54 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
55 | private static readonly string LogHeader = "[ENTITY TRANSFER MODULE]"; | ||
54 | 56 | ||
57 | public const int DefaultMaxTransferDistance = 4095; | ||
55 | public const bool WaitForAgentArrivedAtDestinationDefault = true; | 58 | public const bool WaitForAgentArrivedAtDestinationDefault = true; |
56 | 59 | ||
57 | /// <summary> | 60 | /// <summary> |
61 | /// The maximum distance, in standard region units (256m) that an agent is allowed to transfer. | ||
62 | /// </summary> | ||
63 | public int MaxTransferDistance { get; set; } | ||
64 | |||
65 | /// <summary> | ||
58 | /// If true then on a teleport, the source region waits for a callback from the destination region. If | 66 | /// If true then on a teleport, the source region waits for a callback from the destination region. If |
59 | /// a callback fails to arrive within a set time then the user is pulled back into the source region. | 67 | /// a callback fails to arrive within a set time then the user is pulled back into the source region. |
60 | /// </summary> | 68 | /// </summary> |
61 | public bool WaitForAgentArrivedAtDestination { get; set; } | 69 | public bool WaitForAgentArrivedAtDestination { get; set; } |
62 | 70 | ||
71 | /// <summary> | ||
72 | /// If true then we ask the viewer to disable teleport cancellation and ignore teleport requests. | ||
73 | /// </summary> | ||
74 | /// <remarks> | ||
75 | /// This is useful in situations where teleport is very likely to always succeed and we want to avoid a | ||
76 | /// situation where avatars can be come 'stuck' due to a failed teleport cancellation. Unfortunately, the | ||
77 | /// nature of the teleport protocol makes it extremely difficult (maybe impossible) to make teleport | ||
78 | /// cancellation consistently suceed. | ||
79 | /// </remarks> | ||
80 | public bool DisableInterRegionTeleportCancellation { get; set; } | ||
81 | |||
82 | /// <summary> | ||
83 | /// Number of times inter-region teleport was attempted. | ||
84 | /// </summary> | ||
85 | private Stat m_interRegionTeleportAttempts; | ||
86 | |||
87 | /// <summary> | ||
88 | /// Number of times inter-region teleport was aborted (due to simultaneous client logout). | ||
89 | /// </summary> | ||
90 | private Stat m_interRegionTeleportAborts; | ||
91 | |||
92 | /// <summary> | ||
93 | /// Number of times inter-region teleport was successfully cancelled by the client. | ||
94 | /// </summary> | ||
95 | private Stat m_interRegionTeleportCancels; | ||
96 | |||
97 | /// <summary> | ||
98 | /// Number of times inter-region teleport failed due to server/client/network problems (e.g. viewer failed to | ||
99 | /// connect with destination region). | ||
100 | /// </summary> | ||
101 | /// <remarks> | ||
102 | /// This is not necessarily a problem for this simulator - in open-grid/hg conditions, viewer connectivity to | ||
103 | /// destination simulator is unknown. | ||
104 | /// </remarks> | ||
105 | private Stat m_interRegionTeleportFailures; | ||
106 | |||
107 | protected string m_ThisHomeURI; | ||
108 | protected string m_GatekeeperURI; | ||
109 | |||
63 | protected bool m_Enabled = false; | 110 | protected bool m_Enabled = false; |
64 | 111 | ||
65 | public Scene Scene { get; private set; } | 112 | public Scene Scene { get; private set; } |
@@ -70,10 +117,56 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
70 | /// </summary> | 117 | /// </summary> |
71 | private EntityTransferStateMachine m_entityTransferStateMachine; | 118 | private EntityTransferStateMachine m_entityTransferStateMachine; |
72 | 119 | ||
73 | private ExpiringCache<UUID, ExpiringCache<ulong, DateTime>> m_bannedRegions = | 120 | // For performance, we keed a cached of banned regions so we don't keep going |
74 | new ExpiringCache<UUID, ExpiringCache<ulong, DateTime>>(); | 121 | // to the grid service. |
122 | private class BannedRegionCache | ||
123 | { | ||
124 | private ExpiringCache<UUID, ExpiringCache<ulong, DateTime>> m_bannedRegions = | ||
125 | new ExpiringCache<UUID, ExpiringCache<ulong, DateTime>>(); | ||
126 | ExpiringCache<ulong, DateTime> m_idCache; | ||
127 | DateTime m_banUntil; | ||
128 | public BannedRegionCache() | ||
129 | { | ||
130 | } | ||
131 | // Return 'true' if there is a valid ban entry for this agent in this region | ||
132 | public bool IfBanned(ulong pRegionHandle, UUID pAgentID) | ||
133 | { | ||
134 | bool ret = false; | ||
135 | if (m_bannedRegions.TryGetValue(pAgentID, out m_idCache)) | ||
136 | { | ||
137 | if (m_idCache.TryGetValue(pRegionHandle, out m_banUntil)) | ||
138 | { | ||
139 | if (DateTime.Now < m_banUntil) | ||
140 | { | ||
141 | ret = true; | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | return ret; | ||
146 | } | ||
147 | // Add this agent in this region as a banned person | ||
148 | public void Add(ulong pRegionHandle, UUID pAgentID) | ||
149 | { | ||
150 | if (!m_bannedRegions.TryGetValue(pAgentID, out m_idCache)) | ||
151 | { | ||
152 | m_idCache = new ExpiringCache<ulong, DateTime>(); | ||
153 | m_bannedRegions.Add(pAgentID, m_idCache, TimeSpan.FromSeconds(45)); | ||
154 | } | ||
155 | m_idCache.Add(pRegionHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); | ||
156 | } | ||
157 | // Remove the agent from the region's banned list | ||
158 | public void Remove(ulong pRegionHandle, UUID pAgentID) | ||
159 | { | ||
160 | if (m_bannedRegions.TryGetValue(pAgentID, out m_idCache)) | ||
161 | { | ||
162 | m_idCache.Remove(pRegionHandle); | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | private BannedRegionCache m_bannedRegionCache = new BannedRegionCache(); | ||
75 | 167 | ||
76 | private IEventQueue m_eqModule; | 168 | private IEventQueue m_eqModule; |
169 | private IRegionCombinerModule m_regionCombinerModule; | ||
77 | 170 | ||
78 | #region ISharedRegionModule | 171 | #region ISharedRegionModule |
79 | 172 | ||
@@ -107,11 +200,32 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
107 | /// <param name="source"></param> | 200 | /// <param name="source"></param> |
108 | protected virtual void InitialiseCommon(IConfigSource source) | 201 | protected virtual void InitialiseCommon(IConfigSource source) |
109 | { | 202 | { |
203 | IConfig hypergridConfig = source.Configs["Hypergrid"]; | ||
204 | if (hypergridConfig != null) | ||
205 | { | ||
206 | m_ThisHomeURI = hypergridConfig.GetString("HomeURI", string.Empty); | ||
207 | if (m_ThisHomeURI != string.Empty && !m_ThisHomeURI.EndsWith("/")) | ||
208 | m_ThisHomeURI += '/'; | ||
209 | |||
210 | m_GatekeeperURI = hypergridConfig.GetString("GatekeeperURI", string.Empty); | ||
211 | if (m_GatekeeperURI != string.Empty && !m_GatekeeperURI.EndsWith("/")) | ||
212 | m_GatekeeperURI += '/'; | ||
213 | } | ||
214 | |||
110 | IConfig transferConfig = source.Configs["EntityTransfer"]; | 215 | IConfig transferConfig = source.Configs["EntityTransfer"]; |
111 | if (transferConfig != null) | 216 | if (transferConfig != null) |
112 | { | 217 | { |
218 | DisableInterRegionTeleportCancellation | ||
219 | = transferConfig.GetBoolean("DisableInterRegionTeleportCancellation", false); | ||
220 | |||
113 | WaitForAgentArrivedAtDestination | 221 | WaitForAgentArrivedAtDestination |
114 | = transferConfig.GetBoolean("wait_for_callback", WaitForAgentArrivedAtDestinationDefault); | 222 | = transferConfig.GetBoolean("wait_for_callback", WaitForAgentArrivedAtDestinationDefault); |
223 | |||
224 | MaxTransferDistance = transferConfig.GetInt("max_distance", DefaultMaxTransferDistance); | ||
225 | } | ||
226 | else | ||
227 | { | ||
228 | MaxTransferDistance = DefaultMaxTransferDistance; | ||
115 | } | 229 | } |
116 | 230 | ||
117 | m_entityTransferStateMachine = new EntityTransferStateMachine(this); | 231 | m_entityTransferStateMachine = new EntityTransferStateMachine(this); |
@@ -130,19 +244,87 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
130 | 244 | ||
131 | Scene = scene; | 245 | Scene = scene; |
132 | 246 | ||
247 | m_interRegionTeleportAttempts = | ||
248 | new Stat( | ||
249 | "InterRegionTeleportAttempts", | ||
250 | "Number of inter-region teleports attempted.", | ||
251 | "This does not count attempts which failed due to pre-conditions (e.g. target simulator refused access).\n" | ||
252 | + "You can get successfully teleports by subtracting aborts, cancels and teleport failures from this figure.", | ||
253 | "", | ||
254 | "entitytransfer", | ||
255 | Scene.Name, | ||
256 | StatType.Push, | ||
257 | null, | ||
258 | StatVerbosity.Debug); | ||
259 | |||
260 | m_interRegionTeleportAborts = | ||
261 | new Stat( | ||
262 | "InterRegionTeleportAborts", | ||
263 | "Number of inter-region teleports aborted due to client actions.", | ||
264 | "The chief action is simultaneous logout whilst teleporting.", | ||
265 | "", | ||
266 | "entitytransfer", | ||
267 | Scene.Name, | ||
268 | StatType.Push, | ||
269 | null, | ||
270 | StatVerbosity.Debug); | ||
271 | |||
272 | m_interRegionTeleportCancels = | ||
273 | new Stat( | ||
274 | "InterRegionTeleportCancels", | ||
275 | "Number of inter-region teleports cancelled by the client.", | ||
276 | null, | ||
277 | "", | ||
278 | "entitytransfer", | ||
279 | Scene.Name, | ||
280 | StatType.Push, | ||
281 | null, | ||
282 | StatVerbosity.Debug); | ||
283 | |||
284 | m_interRegionTeleportFailures = | ||
285 | new Stat( | ||
286 | "InterRegionTeleportFailures", | ||
287 | "Number of inter-region teleports that failed due to server/client/network issues.", | ||
288 | "This number may not be very helpful in open-grid/hg situations as the network connectivity/quality of destinations is uncontrollable.", | ||
289 | "", | ||
290 | "entitytransfer", | ||
291 | Scene.Name, | ||
292 | StatType.Push, | ||
293 | null, | ||
294 | StatVerbosity.Debug); | ||
295 | |||
296 | StatsManager.RegisterStat(m_interRegionTeleportAttempts); | ||
297 | StatsManager.RegisterStat(m_interRegionTeleportAborts); | ||
298 | StatsManager.RegisterStat(m_interRegionTeleportCancels); | ||
299 | StatsManager.RegisterStat(m_interRegionTeleportFailures); | ||
300 | |||
133 | scene.RegisterModuleInterface<IEntityTransferModule>(this); | 301 | scene.RegisterModuleInterface<IEntityTransferModule>(this); |
134 | scene.EventManager.OnNewClient += OnNewClient; | 302 | scene.EventManager.OnNewClient += OnNewClient; |
135 | } | 303 | } |
136 | 304 | ||
137 | protected virtual void OnNewClient(IClientAPI client) | 305 | protected virtual void OnNewClient(IClientAPI client) |
138 | { | 306 | { |
139 | client.OnTeleportHomeRequest += TeleportHome; | 307 | client.OnTeleportHomeRequest += TriggerTeleportHome; |
140 | client.OnTeleportLandmarkRequest += RequestTeleportLandmark; | 308 | client.OnTeleportLandmarkRequest += RequestTeleportLandmark; |
309 | |||
310 | if (!DisableInterRegionTeleportCancellation) | ||
311 | client.OnTeleportCancel += OnClientCancelTeleport; | ||
312 | |||
313 | client.OnConnectionClosed += OnConnectionClosed; | ||
141 | } | 314 | } |
142 | 315 | ||
143 | public virtual void Close() {} | 316 | public virtual void Close() {} |
144 | 317 | ||
145 | public virtual void RemoveRegion(Scene scene) {} | 318 | public virtual void RemoveRegion(Scene scene) |
319 | { | ||
320 | if (m_Enabled) | ||
321 | { | ||
322 | StatsManager.DeregisterStat(m_interRegionTeleportAttempts); | ||
323 | StatsManager.DeregisterStat(m_interRegionTeleportAborts); | ||
324 | StatsManager.DeregisterStat(m_interRegionTeleportCancels); | ||
325 | StatsManager.DeregisterStat(m_interRegionTeleportFailures); | ||
326 | } | ||
327 | } | ||
146 | 328 | ||
147 | public virtual void RegionLoaded(Scene scene) | 329 | public virtual void RegionLoaded(Scene scene) |
148 | { | 330 | { |
@@ -150,12 +332,32 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
150 | return; | 332 | return; |
151 | 333 | ||
152 | m_eqModule = Scene.RequestModuleInterface<IEventQueue>(); | 334 | m_eqModule = Scene.RequestModuleInterface<IEventQueue>(); |
335 | m_regionCombinerModule = Scene.RequestModuleInterface<IRegionCombinerModule>(); | ||
153 | } | 336 | } |
154 | 337 | ||
155 | #endregion | 338 | #endregion |
156 | 339 | ||
157 | #region Agent Teleports | 340 | #region Agent Teleports |
158 | 341 | ||
342 | private void OnConnectionClosed(IClientAPI client) | ||
343 | { | ||
344 | if (client.IsLoggingOut && m_entityTransferStateMachine.UpdateInTransit(client.AgentId, AgentTransferState.Aborting)) | ||
345 | { | ||
346 | m_log.DebugFormat( | ||
347 | "[ENTITY TRANSFER MODULE]: Aborted teleport request from {0} in {1} due to simultaneous logout", | ||
348 | client.Name, Scene.Name); | ||
349 | } | ||
350 | } | ||
351 | |||
352 | private void OnClientCancelTeleport(IClientAPI client) | ||
353 | { | ||
354 | m_entityTransferStateMachine.UpdateInTransit(client.AgentId, AgentTransferState.Cancelling); | ||
355 | |||
356 | m_log.DebugFormat( | ||
357 | "[ENTITY TRANSFER MODULE]: Received teleport cancel request from {0} in {1}", client.Name, Scene.Name); | ||
358 | } | ||
359 | |||
360 | // Attempt to teleport the ScenePresence to the specified position in the specified region (spec'ed by its handle). | ||
159 | public void Teleport(ScenePresence sp, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags) | 361 | public void Teleport(ScenePresence sp, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags) |
160 | { | 362 | { |
161 | if (sp.Scene.Permissions.IsGridGod(sp.UUID)) | 363 | if (sp.Scene.Permissions.IsGridGod(sp.UUID)) |
@@ -167,13 +369,26 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
167 | if (!sp.Scene.Permissions.CanTeleport(sp.UUID)) | 369 | if (!sp.Scene.Permissions.CanTeleport(sp.UUID)) |
168 | return; | 370 | return; |
169 | 371 | ||
170 | // Reset animations; the viewer does that in teleports. | ||
171 | sp.Animator.ResetAnimations(); | ||
172 | |||
173 | string destinationRegionName = "(not found)"; | 372 | string destinationRegionName = "(not found)"; |
174 | 373 | ||
374 | // Record that this agent is in transit so that we can prevent simultaneous requests and do later detection | ||
375 | // of whether the destination region completes the teleport. | ||
376 | if (!m_entityTransferStateMachine.SetInTransit(sp.UUID)) | ||
377 | { | ||
378 | m_log.DebugFormat( | ||
379 | "[ENTITY TRANSFER MODULE]: Ignoring teleport request of {0} {1} to {2}@{3} - agent is already in transit.", | ||
380 | sp.Name, sp.UUID, position, regionHandle); | ||
381 | |||
382 | sp.ControllingClient.SendTeleportFailed("Previous teleport process incomplete. Please retry shortly."); | ||
383 | |||
384 | return; | ||
385 | } | ||
386 | |||
175 | try | 387 | try |
176 | { | 388 | { |
389 | // Reset animations; the viewer does that in teleports. | ||
390 | sp.Animator.ResetAnimations(); | ||
391 | |||
177 | if (regionHandle == sp.Scene.RegionInfo.RegionHandle) | 392 | if (regionHandle == sp.Scene.RegionInfo.RegionHandle) |
178 | { | 393 | { |
179 | destinationRegionName = sp.Scene.RegionInfo.RegionName; | 394 | destinationRegionName = sp.Scene.RegionInfo.RegionName; |
@@ -182,12 +397,17 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
182 | } | 397 | } |
183 | else // Another region possibly in another simulator | 398 | else // Another region possibly in another simulator |
184 | { | 399 | { |
185 | GridRegion finalDestination; | 400 | GridRegion finalDestination = null; |
186 | TeleportAgentToDifferentRegion( | 401 | try |
187 | sp, regionHandle, position, lookAt, teleportFlags, out finalDestination); | 402 | { |
188 | 403 | TeleportAgentToDifferentRegion( | |
189 | if (finalDestination != null) | 404 | sp, regionHandle, position, lookAt, teleportFlags, out finalDestination); |
190 | destinationRegionName = finalDestination.RegionName; | 405 | } |
406 | finally | ||
407 | { | ||
408 | if (finalDestination != null) | ||
409 | destinationRegionName = finalDestination.RegionName; | ||
410 | } | ||
191 | } | 411 | } |
192 | } | 412 | } |
193 | catch (Exception e) | 413 | catch (Exception e) |
@@ -197,11 +417,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
197 | sp.Name, sp.AbsolutePosition, sp.Scene.RegionInfo.RegionName, position, destinationRegionName, | 417 | sp.Name, sp.AbsolutePosition, sp.Scene.RegionInfo.RegionName, position, destinationRegionName, |
198 | e.Message, e.StackTrace); | 418 | e.Message, e.StackTrace); |
199 | 419 | ||
200 | // Make sure that we clear the in-transit flag so that future teleport attempts don't always fail. | ||
201 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | ||
202 | |||
203 | sp.ControllingClient.SendTeleportFailed("Internal error"); | 420 | sp.ControllingClient.SendTeleportFailed("Internal error"); |
204 | } | 421 | } |
422 | finally | ||
423 | { | ||
424 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | ||
425 | } | ||
205 | } | 426 | } |
206 | 427 | ||
207 | /// <summary> | 428 | /// <summary> |
@@ -210,30 +431,21 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
210 | /// <param name="sp"></param> | 431 | /// <param name="sp"></param> |
211 | /// <param name="position"></param> | 432 | /// <param name="position"></param> |
212 | /// <param name="lookAt"></param> | 433 | /// <param name="lookAt"></param> |
213 | /// <param name="teleportFlags"></param | 434 | /// <param name="teleportFlags"></param> |
214 | private void TeleportAgentWithinRegion(ScenePresence sp, Vector3 position, Vector3 lookAt, uint teleportFlags) | 435 | private void TeleportAgentWithinRegion(ScenePresence sp, Vector3 position, Vector3 lookAt, uint teleportFlags) |
215 | { | 436 | { |
216 | m_log.DebugFormat( | 437 | m_log.DebugFormat( |
217 | "[ENTITY TRANSFER MODULE]: Teleport for {0} to {1} within {2}", | 438 | "[ENTITY TRANSFER MODULE]: Teleport for {0} to {1} within {2}", |
218 | sp.Name, position, sp.Scene.RegionInfo.RegionName); | 439 | sp.Name, position, sp.Scene.RegionInfo.RegionName); |
219 | 440 | ||
220 | if (!m_entityTransferStateMachine.SetInTransit(sp.UUID)) | ||
221 | { | ||
222 | m_log.DebugFormat( | ||
223 | "[ENTITY TRANSFER MODULE]: Ignoring within region teleport request of {0} {1} to {2} - agent is already in transit.", | ||
224 | sp.Name, sp.UUID, position); | ||
225 | |||
226 | return; | ||
227 | } | ||
228 | |||
229 | // Teleport within the same region | 441 | // Teleport within the same region |
230 | if (IsOutsideRegion(sp.Scene, position) || position.Z < 0) | 442 | if (!sp.Scene.PositionIsInCurrentRegion(position) || position.Z < 0) |
231 | { | 443 | { |
232 | Vector3 emergencyPos = new Vector3(128, 128, 128); | 444 | Vector3 emergencyPos = new Vector3(128, 128, 128); |
233 | 445 | ||
234 | m_log.WarnFormat( | 446 | m_log.WarnFormat( |
235 | "[ENTITY TRANSFER MODULE]: RequestTeleportToLocation() was given an illegal position of {0} for avatar {1}, {2}. Substituting {3}", | 447 | "[ENTITY TRANSFER MODULE]: RequestTeleportToLocation() was given an illegal position of {0} for avatar {1}, {2} in {3}. Substituting {4}", |
236 | position, sp.Name, sp.UUID, emergencyPos); | 448 | position, sp.Name, sp.UUID, Scene.Name, emergencyPos); |
237 | 449 | ||
238 | position = emergencyPos; | 450 | position = emergencyPos; |
239 | } | 451 | } |
@@ -243,10 +455,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
243 | float posZLimit = 22; | 455 | float posZLimit = 22; |
244 | 456 | ||
245 | // TODO: Check other Scene HeightField | 457 | // TODO: Check other Scene HeightField |
246 | if (position.X > 0 && position.X <= (int)Constants.RegionSize && position.Y > 0 && position.Y <= (int)Constants.RegionSize) | 458 | posZLimit = (float)sp.Scene.Heightmap[(int)position.X, (int)position.Y]; |
247 | { | ||
248 | posZLimit = (float)sp.Scene.Heightmap[(int)position.X, (int)position.Y]; | ||
249 | } | ||
250 | 459 | ||
251 | float newPosZ = posZLimit + localAVHeight; | 460 | float newPosZ = posZLimit + localAVHeight; |
252 | if (posZLimit >= (position.Z - (localAVHeight / 2)) && !(Single.IsInfinity(newPosZ) || Single.IsNaN(newPosZ))) | 461 | if (posZLimit >= (position.Z - (localAVHeight / 2)) && !(Single.IsInfinity(newPosZ) || Single.IsNaN(newPosZ))) |
@@ -254,11 +463,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
254 | position.Z = newPosZ; | 463 | position.Z = newPosZ; |
255 | } | 464 | } |
256 | 465 | ||
466 | if (sp.Flying) | ||
467 | teleportFlags |= (uint)TeleportFlags.IsFlying; | ||
468 | |||
257 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); | 469 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); |
258 | 470 | ||
259 | sp.ControllingClient.SendTeleportStart(teleportFlags); | 471 | sp.ControllingClient.SendTeleportStart(teleportFlags); |
260 | 472 | ||
261 | sp.ControllingClient.SendLocalTeleport(position, lookAt, teleportFlags); | 473 | sp.ControllingClient.SendLocalTeleport(position, lookAt, teleportFlags); |
474 | sp.TeleportFlags = (Constants.TeleportFlags)teleportFlags; | ||
262 | sp.Velocity = Vector3.Zero; | 475 | sp.Velocity = Vector3.Zero; |
263 | sp.Teleport(position); | 476 | sp.Teleport(position); |
264 | 477 | ||
@@ -270,7 +483,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
270 | } | 483 | } |
271 | 484 | ||
272 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); | 485 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); |
273 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | ||
274 | } | 486 | } |
275 | 487 | ||
276 | /// <summary> | 488 | /// <summary> |
@@ -286,21 +498,23 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
286 | ScenePresence sp, ulong regionHandle, Vector3 position, | 498 | ScenePresence sp, ulong regionHandle, Vector3 position, |
287 | Vector3 lookAt, uint teleportFlags, out GridRegion finalDestination) | 499 | Vector3 lookAt, uint teleportFlags, out GridRegion finalDestination) |
288 | { | 500 | { |
289 | uint x = 0, y = 0; | 501 | // Get destination region taking into account that the address could be an offset |
290 | Utils.LongToUInts(regionHandle, out x, out y); | 502 | // region inside a varregion. |
291 | GridRegion reg = Scene.GridService.GetRegionByPosition(sp.Scene.RegionInfo.ScopeID, (int)x, (int)y); | 503 | GridRegion reg = GetTeleportDestinationRegion(sp.Scene.GridService, sp.Scene.RegionInfo.ScopeID, regionHandle, ref position); |
292 | 504 | ||
293 | if (reg != null) | 505 | if (reg != null) |
294 | { | 506 | { |
295 | finalDestination = GetFinalDestination(reg); | 507 | string homeURI = Scene.GetAgentHomeURI(sp.ControllingClient.AgentId); |
508 | |||
509 | string message; | ||
510 | finalDestination = GetFinalDestination(reg, sp.ControllingClient.AgentId, homeURI, out message); | ||
296 | 511 | ||
297 | if (finalDestination == null) | 512 | if (finalDestination == null) |
298 | { | 513 | { |
299 | m_log.WarnFormat( | 514 | m_log.WarnFormat( "{0} Final destination is having problems. Unable to teleport {1} {2}: {3}", |
300 | "[ENTITY TRANSFER MODULE]: Final destination is having problems. Unable to teleport {0} {1}", | 515 | LogHeader, sp.Name, sp.UUID, message); |
301 | sp.Name, sp.UUID); | ||
302 | 516 | ||
303 | sp.ControllingClient.SendTeleportFailed("Problem at destination"); | 517 | sp.ControllingClient.SendTeleportFailed(message); |
304 | return; | 518 | return; |
305 | } | 519 | } |
306 | 520 | ||
@@ -321,10 +535,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
321 | return; | 535 | return; |
322 | } | 536 | } |
323 | 537 | ||
538 | if (message != null) | ||
539 | sp.ControllingClient.SendAgentAlertMessage(message, true); | ||
540 | |||
324 | // | 541 | // |
325 | // This is it | 542 | // This is it |
326 | // | 543 | // |
327 | DoTeleport(sp, reg, finalDestination, position, lookAt, teleportFlags); | 544 | DoTeleportInternal(sp, reg, finalDestination, position, lookAt, teleportFlags); |
328 | // | 545 | // |
329 | // | 546 | // |
330 | // | 547 | // |
@@ -339,12 +556,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
339 | 556 | ||
340 | // and set the map-tile to '(Offline)' | 557 | // and set the map-tile to '(Offline)' |
341 | uint regX, regY; | 558 | uint regX, regY; |
342 | Utils.LongToUInts(regionHandle, out regX, out regY); | 559 | Util.RegionHandleToRegionLoc(regionHandle, out regX, out regY); |
343 | 560 | ||
344 | MapBlockData block = new MapBlockData(); | 561 | MapBlockData block = new MapBlockData(); |
345 | block.X = (ushort)(regX / Constants.RegionSize); | 562 | block.X = (ushort)regX; |
346 | block.Y = (ushort)(regY / Constants.RegionSize); | 563 | block.Y = (ushort)regY; |
347 | block.Access = 254; // == not there | 564 | block.Access = (byte)SimAccess.Down; |
348 | 565 | ||
349 | List<MapBlockData> blocks = new List<MapBlockData>(); | 566 | List<MapBlockData> blocks = new List<MapBlockData>(); |
350 | blocks.Add(block); | 567 | blocks.Add(block); |
@@ -352,6 +569,31 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
352 | } | 569 | } |
353 | } | 570 | } |
354 | 571 | ||
572 | // The teleport address could be an address in a subregion of a larger varregion. | ||
573 | // Find the real base region and adjust the teleport location to account for the | ||
574 | // larger region. | ||
575 | private GridRegion GetTeleportDestinationRegion(IGridService gridService, UUID scope, ulong regionHandle, ref Vector3 position) | ||
576 | { | ||
577 | uint x = 0, y = 0; | ||
578 | Util.RegionHandleToWorldLoc(regionHandle, out x, out y); | ||
579 | |||
580 | // Compute the world location we're teleporting to | ||
581 | double worldX = (double)x + position.X; | ||
582 | double worldY = (double)y + position.Y; | ||
583 | |||
584 | // Find the region that contains the position | ||
585 | GridRegion reg = GetRegionContainingWorldLocation(gridService, scope, worldX, worldY); | ||
586 | |||
587 | if (reg != null) | ||
588 | { | ||
589 | // modify the position for the offset into the actual region returned | ||
590 | position.X += x - reg.RegionLocX; | ||
591 | position.Y += y - reg.RegionLocY; | ||
592 | } | ||
593 | |||
594 | return reg; | ||
595 | } | ||
596 | |||
355 | // Nothing to validate here | 597 | // Nothing to validate here |
356 | protected virtual bool ValidateGenericConditions(ScenePresence sp, GridRegion reg, GridRegion finalDestination, uint teleportFlags, out string reason) | 598 | protected virtual bool ValidateGenericConditions(ScenePresence sp, GridRegion reg, GridRegion finalDestination, uint teleportFlags, out string reason) |
357 | { | 599 | { |
@@ -359,6 +601,32 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
359 | return true; | 601 | return true; |
360 | } | 602 | } |
361 | 603 | ||
604 | /// <summary> | ||
605 | /// Determines whether this instance is within the max transfer distance. | ||
606 | /// </summary> | ||
607 | /// <param name="sourceRegion"></param> | ||
608 | /// <param name="destRegion"></param> | ||
609 | /// <returns> | ||
610 | /// <c>true</c> if this instance is within max transfer distance; otherwise, <c>false</c>. | ||
611 | /// </returns> | ||
612 | private bool IsWithinMaxTeleportDistance(RegionInfo sourceRegion, GridRegion destRegion) | ||
613 | { | ||
614 | if(MaxTransferDistance == 0) | ||
615 | return true; | ||
616 | |||
617 | // m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Source co-ords are x={0} y={1}", curRegionX, curRegionY); | ||
618 | // | ||
619 | // m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Final dest is x={0} y={1} {2}@{3}", | ||
620 | // destRegionX, destRegionY, finalDestination.RegionID, finalDestination.ServerURI); | ||
621 | |||
622 | // Insanely, RegionLoc on RegionInfo is the 256m map co-ord whilst GridRegion.RegionLoc is the raw meters position. | ||
623 | return Math.Abs(sourceRegion.RegionLocX - destRegion.RegionCoordX) <= MaxTransferDistance | ||
624 | && Math.Abs(sourceRegion.RegionLocY - destRegion.RegionCoordY) <= MaxTransferDistance; | ||
625 | } | ||
626 | |||
627 | /// <summary> | ||
628 | /// Wraps DoTeleportInternal() and manages the transfer state. | ||
629 | /// </summary> | ||
362 | public void DoTeleport( | 630 | public void DoTeleport( |
363 | ScenePresence sp, GridRegion reg, GridRegion finalDestination, | 631 | ScenePresence sp, GridRegion reg, GridRegion finalDestination, |
364 | Vector3 position, Vector3 lookAt, uint teleportFlags) | 632 | Vector3 position, Vector3 lookAt, uint teleportFlags) |
@@ -370,18 +638,45 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
370 | m_log.DebugFormat( | 638 | m_log.DebugFormat( |
371 | "[ENTITY TRANSFER MODULE]: Ignoring teleport request of {0} {1} to {2} ({3}) {4}/{5} - agent is already in transit.", | 639 | "[ENTITY TRANSFER MODULE]: Ignoring teleport request of {0} {1} to {2} ({3}) {4}/{5} - agent is already in transit.", |
372 | sp.Name, sp.UUID, reg.ServerURI, finalDestination.ServerURI, finalDestination.RegionName, position); | 640 | sp.Name, sp.UUID, reg.ServerURI, finalDestination.ServerURI, finalDestination.RegionName, position); |
373 | 641 | sp.ControllingClient.SendTeleportFailed("Agent is already in transit."); | |
374 | return; | 642 | return; |
375 | } | 643 | } |
644 | |||
645 | try | ||
646 | { | ||
647 | DoTeleportInternal(sp, reg, finalDestination, position, lookAt, teleportFlags); | ||
648 | } | ||
649 | catch (Exception e) | ||
650 | { | ||
651 | m_log.ErrorFormat( | ||
652 | "[ENTITY TRANSFER MODULE]: Exception on teleport of {0} from {1}@{2} to {3}@{4}: {5}{6}", | ||
653 | sp.Name, sp.AbsolutePosition, sp.Scene.RegionInfo.RegionName, position, finalDestination.RegionName, | ||
654 | e.Message, e.StackTrace); | ||
376 | 655 | ||
377 | if (reg == null || finalDestination == null) | 656 | sp.ControllingClient.SendTeleportFailed("Internal error"); |
657 | } | ||
658 | finally | ||
378 | { | 659 | { |
379 | sp.ControllingClient.SendTeleportFailed("Unable to locate destination"); | ||
380 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | 660 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); |
661 | } | ||
662 | } | ||
381 | 663 | ||
664 | /// <summary> | ||
665 | /// Teleports the agent to another region. | ||
666 | /// This method doesn't manage the transfer state; the caller must do that. | ||
667 | /// </summary> | ||
668 | private void DoTeleportInternal( | ||
669 | ScenePresence sp, GridRegion reg, GridRegion finalDestination, | ||
670 | Vector3 position, Vector3 lookAt, uint teleportFlags) | ||
671 | { | ||
672 | if (reg == null || finalDestination == null) | ||
673 | { | ||
674 | sp.ControllingClient.SendTeleportFailed("Unable to locate destination"); | ||
382 | return; | 675 | return; |
383 | } | 676 | } |
384 | 677 | ||
678 | string homeURI = Scene.GetAgentHomeURI(sp.ControllingClient.AgentId); | ||
679 | |||
385 | m_log.DebugFormat( | 680 | m_log.DebugFormat( |
386 | "[ENTITY TRANSFER MODULE]: Teleporting {0} {1} from {2} to {3} ({4}) {5}/{6}", | 681 | "[ENTITY TRANSFER MODULE]: Teleporting {0} {1} from {2} to {3} ({4}) {5}/{6}", |
387 | sp.Name, sp.UUID, sp.Scene.RegionInfo.RegionName, | 682 | sp.Name, sp.UUID, sp.Scene.RegionInfo.RegionName, |
@@ -389,10 +684,21 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
389 | 684 | ||
390 | RegionInfo sourceRegion = sp.Scene.RegionInfo; | 685 | RegionInfo sourceRegion = sp.Scene.RegionInfo; |
391 | 686 | ||
392 | uint newRegionX = (uint)(reg.RegionHandle >> 40); | 687 | if (!IsWithinMaxTeleportDistance(sourceRegion, finalDestination)) |
393 | uint newRegionY = (((uint)(reg.RegionHandle)) >> 8); | 688 | { |
394 | uint oldRegionX = (uint)(sp.Scene.RegionInfo.RegionHandle >> 40); | 689 | sp.ControllingClient.SendTeleportFailed( |
395 | uint oldRegionY = (((uint)(sp.Scene.RegionInfo.RegionHandle)) >> 8); | 690 | string.Format( |
691 | "Can't teleport to {0} ({1},{2}) from {3} ({4},{5}), destination is more than {6} regions way", | ||
692 | finalDestination.RegionName, finalDestination.RegionCoordX, finalDestination.RegionCoordY, | ||
693 | sourceRegion.RegionName, sourceRegion.RegionLocX, sourceRegion.RegionLocY, | ||
694 | MaxTransferDistance)); | ||
695 | |||
696 | return; | ||
697 | } | ||
698 | |||
699 | uint newRegionX, newRegionY, oldRegionX, oldRegionY; | ||
700 | Util.RegionHandleToRegionLoc(reg.RegionHandle, out newRegionX, out newRegionY); | ||
701 | Util.RegionHandleToRegionLoc(sp.Scene.RegionInfo.RegionHandle, out oldRegionX, out oldRegionY); | ||
396 | 702 | ||
397 | ulong destinationHandle = finalDestination.RegionHandle; | 703 | ulong destinationHandle = finalDestination.RegionHandle; |
398 | 704 | ||
@@ -400,11 +706,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
400 | // This may be a costly operation. The reg.ExternalEndPoint field is not a passive field, | 706 | // This may be a costly operation. The reg.ExternalEndPoint field is not a passive field, |
401 | // it's actually doing a lot of work. | 707 | // it's actually doing a lot of work. |
402 | IPEndPoint endPoint = finalDestination.ExternalEndPoint; | 708 | IPEndPoint endPoint = finalDestination.ExternalEndPoint; |
403 | 709 | if (endPoint == null || endPoint.Address == null) | |
404 | if (endPoint.Address == null) | ||
405 | { | 710 | { |
406 | sp.ControllingClient.SendTeleportFailed("Remote Region appears to be down"); | 711 | sp.ControllingClient.SendTeleportFailed("Remote Region appears to be down"); |
407 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | ||
408 | 712 | ||
409 | return; | 713 | return; |
410 | } | 714 | } |
@@ -412,35 +716,41 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
412 | if (!sp.ValidateAttachments()) | 716 | if (!sp.ValidateAttachments()) |
413 | m_log.DebugFormat( | 717 | m_log.DebugFormat( |
414 | "[ENTITY TRANSFER MODULE]: Failed validation of all attachments for teleport of {0} from {1} to {2}. Continuing.", | 718 | "[ENTITY TRANSFER MODULE]: Failed validation of all attachments for teleport of {0} from {1} to {2}. Continuing.", |
415 | sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName); | 719 | sp.Name, sp.Scene.Name, finalDestination.RegionName); |
416 | |||
417 | // if (!sp.ValidateAttachments()) | ||
418 | // { | ||
419 | // sp.ControllingClient.SendTeleportFailed("Inconsistent attachment state"); | ||
420 | // return; | ||
421 | // } | ||
422 | 720 | ||
423 | string reason; | 721 | string reason; |
424 | string version; | 722 | EntityTransferContext ctx = new EntityTransferContext(); |
723 | |||
425 | if (!Scene.SimulationService.QueryAccess( | 724 | if (!Scene.SimulationService.QueryAccess( |
426 | finalDestination, sp.ControllingClient.AgentId, Vector3.Zero, out version, out reason)) | 725 | finalDestination, sp.ControllingClient.AgentId, homeURI, true, position, sp.Scene.GetFormatsOffered(), ctx, out reason)) |
427 | { | 726 | { |
428 | sp.ControllingClient.SendTeleportFailed(reason); | 727 | sp.ControllingClient.SendTeleportFailed(reason); |
429 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | ||
430 | 728 | ||
431 | m_log.DebugFormat( | 729 | m_log.DebugFormat( |
432 | "[ENTITY TRANSFER MODULE]: {0} was stopped from teleporting from {1} to {2} because {3}", | 730 | "[ENTITY TRANSFER MODULE]: {0} was stopped from teleporting from {1} to {2} because: {3}", |
433 | sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName, reason); | 731 | sp.Name, sp.Scene.Name, finalDestination.RegionName, reason); |
434 | 732 | ||
435 | return; | 733 | return; |
436 | } | 734 | } |
437 | 735 | ||
438 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Destination is running version {0}", version); | 736 | // Before this point, teleport 'failure' is due to checkable pre-conditions such as whether the target |
737 | // simulator can be found and is explicitly prepared to allow access. Therefore, we will not count these | ||
738 | // as server attempts. | ||
739 | m_interRegionTeleportAttempts.Value++; | ||
740 | |||
741 | m_log.DebugFormat( | ||
742 | "[ENTITY TRANSFER MODULE]: {0} transfer protocol version to {1} is {2} / {3}", | ||
743 | sp.Scene.Name, finalDestination.RegionName, ctx.OutboundVersion, ctx.InboundVersion); | ||
439 | 744 | ||
440 | // Fixing a bug where teleporting while sitting results in the avatar ending up removed from | 745 | // Fixing a bug where teleporting while sitting results in the avatar ending up removed from |
441 | // both regions | 746 | // both regions |
442 | if (sp.ParentID != (uint)0) | 747 | if (sp.ParentID != (uint)0) |
443 | sp.StandUp(); | 748 | sp.StandUp(); |
749 | else if (sp.Flying) | ||
750 | teleportFlags |= (uint)TeleportFlags.IsFlying; | ||
751 | |||
752 | if (DisableInterRegionTeleportCancellation) | ||
753 | teleportFlags |= (uint)TeleportFlags.DisableCancel; | ||
444 | 754 | ||
445 | // At least on LL 3.3.4, this is not strictly necessary - a teleport will succeed without sending this to | 755 | // At least on LL 3.3.4, this is not strictly necessary - a teleport will succeed without sending this to |
446 | // the viewer. However, it might mean that the viewer does not see the black teleport screen (untested). | 756 | // the viewer. However, it might mean that the viewer does not see the black teleport screen (untested). |
@@ -454,13 +764,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
454 | // once we reach here... | 764 | // once we reach here... |
455 | //avatar.Scene.RemoveCapsHandler(avatar.UUID); | 765 | //avatar.Scene.RemoveCapsHandler(avatar.UUID); |
456 | 766 | ||
457 | string capsPath = String.Empty; | ||
458 | |||
459 | AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); | 767 | AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); |
460 | AgentCircuitData agentCircuit = sp.ControllingClient.RequestClientInfo(); | 768 | AgentCircuitData agentCircuit = sp.ControllingClient.RequestClientInfo(); |
461 | agentCircuit.startpos = position; | 769 | agentCircuit.startpos = position; |
462 | agentCircuit.child = true; | 770 | agentCircuit.child = true; |
463 | agentCircuit.Appearance = sp.Appearance; | 771 | agentCircuit.Appearance = new AvatarAppearance(); |
772 | agentCircuit.Appearance.PackLegacyWearables = true; | ||
464 | if (currentAgentCircuit != null) | 773 | if (currentAgentCircuit != null) |
465 | { | 774 | { |
466 | agentCircuit.ServiceURLs = currentAgentCircuit.ServiceURLs; | 775 | agentCircuit.ServiceURLs = currentAgentCircuit.ServiceURLs; |
@@ -471,23 +780,66 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
471 | agentCircuit.Id0 = currentAgentCircuit.Id0; | 780 | agentCircuit.Id0 = currentAgentCircuit.Id0; |
472 | } | 781 | } |
473 | 782 | ||
474 | if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) | 783 | // if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) |
784 | float dist = (float)Math.Max(sp.Scene.DefaultDrawDistance, | ||
785 | (float)Math.Max(sp.Scene.RegionInfo.RegionSizeX, sp.Scene.RegionInfo.RegionSizeY)); | ||
786 | if (NeedsNewAgent(dist, oldRegionX, newRegionX, oldRegionY, newRegionY)) | ||
475 | { | 787 | { |
476 | // brand new agent, let's create a new caps seed | 788 | // brand new agent, let's create a new caps seed |
477 | agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath(); | 789 | agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath(); |
478 | } | 790 | } |
479 | 791 | ||
480 | // Let's create an agent there if one doesn't exist yet. | 792 | // We're going to fallback to V1 if the destination gives us anything smaller than 0.2 |
793 | if (ctx.OutboundVersion >= 0.2f) | ||
794 | TransferAgent_V2(sp, agentCircuit, reg, finalDestination, endPoint, teleportFlags, oldRegionX, newRegionX, oldRegionY, newRegionY, ctx, out reason); | ||
795 | else | ||
796 | TransferAgent_V1(sp, agentCircuit, reg, finalDestination, endPoint, teleportFlags, oldRegionX, newRegionX, oldRegionY, newRegionY, ctx, out reason); | ||
797 | } | ||
798 | |||
799 | private void TransferAgent_V1(ScenePresence sp, AgentCircuitData agentCircuit, GridRegion reg, GridRegion finalDestination, | ||
800 | IPEndPoint endPoint, uint teleportFlags, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, EntityTransferContext ctx, out string reason) | ||
801 | { | ||
802 | ulong destinationHandle = finalDestination.RegionHandle; | ||
803 | AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); | ||
804 | |||
805 | m_log.DebugFormat( | ||
806 | "[ENTITY TRANSFER MODULE]: Using TP V1 for {0} going from {1} to {2}", | ||
807 | sp.Name, Scene.Name, finalDestination.RegionName); | ||
808 | |||
809 | // Let's create an agent there if one doesn't exist yet. | ||
810 | // NOTE: logout will always be false for a non-HG teleport. | ||
481 | bool logout = false; | 811 | bool logout = false; |
482 | if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout)) | 812 | if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout)) |
483 | { | 813 | { |
484 | sp.ControllingClient.SendTeleportFailed(String.Format("Teleport refused: {0}", reason)); | 814 | m_interRegionTeleportFailures.Value++; |
485 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | ||
486 | 815 | ||
487 | m_log.DebugFormat( | 816 | m_log.DebugFormat( |
488 | "[ENTITY TRANSFER MODULE]: Teleport of {0} from {1} to {2} was refused because {3}", | 817 | "[ENTITY TRANSFER MODULE]: Teleport of {0} from {1} to {2} was refused because {3}", |
489 | sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName, reason); | 818 | sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName, reason); |
490 | 819 | ||
820 | sp.ControllingClient.SendTeleportFailed(reason); | ||
821 | |||
822 | return; | ||
823 | } | ||
824 | |||
825 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling) | ||
826 | { | ||
827 | m_interRegionTeleportCancels.Value++; | ||
828 | |||
829 | m_log.DebugFormat( | ||
830 | "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request", | ||
831 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
832 | |||
833 | return; | ||
834 | } | ||
835 | else if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) | ||
836 | { | ||
837 | m_interRegionTeleportAborts.Value++; | ||
838 | |||
839 | m_log.DebugFormat( | ||
840 | "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after CreateAgent due to previous client close.", | ||
841 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
842 | |||
491 | return; | 843 | return; |
492 | } | 844 | } |
493 | 845 | ||
@@ -497,9 +849,16 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
497 | // OK, it got this agent. Let's close some child agents | 849 | // OK, it got this agent. Let's close some child agents |
498 | sp.CloseChildAgents(newRegionX, newRegionY); | 850 | sp.CloseChildAgents(newRegionX, newRegionY); |
499 | 851 | ||
500 | IClientIPEndpoint ipepClient; | 852 | IClientIPEndpoint ipepClient; |
501 | if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) | 853 | string capsPath = String.Empty; |
854 | float dist = (float)Math.Max(sp.Scene.DefaultDrawDistance, | ||
855 | (float)Math.Max(sp.Scene.RegionInfo.RegionSizeX, sp.Scene.RegionInfo.RegionSizeY)); | ||
856 | if (NeedsNewAgent(dist, oldRegionX, newRegionX, oldRegionY, newRegionY)) | ||
502 | { | 857 | { |
858 | m_log.DebugFormat( | ||
859 | "[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for incoming agent {3} from {4}", | ||
860 | finalDestination.RegionName, newRegionX, newRegionY, sp.Name, Scene.Name); | ||
861 | |||
503 | //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent..."); | 862 | //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent..."); |
504 | #region IP Translation for NAT | 863 | #region IP Translation for NAT |
505 | // Uses ipepClient above | 864 | // Uses ipepClient above |
@@ -512,21 +871,30 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
512 | 871 | ||
513 | if (m_eqModule != null) | 872 | if (m_eqModule != null) |
514 | { | 873 | { |
515 | m_eqModule.EnableSimulator(destinationHandle, endPoint, sp.UUID); | 874 | // The EnableSimulator message makes the client establish a connection with the destination |
516 | 875 | // simulator by sending the initial UseCircuitCode UDP packet to the destination containing the | |
517 | // ES makes the client send a UseCircuitCode message to the destination, | 876 | // correct circuit code. |
518 | // which triggers a bunch of things there. | 877 | m_eqModule.EnableSimulator(destinationHandle, endPoint, sp.UUID, |
519 | // So let's wait | 878 | finalDestination.RegionSizeX, finalDestination.RegionSizeY); |
879 | m_log.DebugFormat("{0} Sent EnableSimulator. regName={1}, size=<{2},{3}>", LogHeader, | ||
880 | finalDestination.RegionName, finalDestination.RegionSizeX, finalDestination.RegionSizeY); | ||
881 | |||
882 | // XXX: Is this wait necessary? We will always end up waiting on UpdateAgent for the destination | ||
883 | // simulator to confirm that it has established communication with the viewer. | ||
520 | Thread.Sleep(200); | 884 | Thread.Sleep(200); |
521 | 885 | ||
522 | // At least on LL 3.3.4 for teleports between different regions on the same simulator this appears | 886 | // At least on LL 3.3.4 for teleports between different regions on the same simulator this appears |
523 | // unnecessary - teleport will succeed and SEED caps will be requested without it (though possibly | 887 | // unnecessary - teleport will succeed and SEED caps will be requested without it (though possibly |
524 | // only on TeleportFinish). This is untested for region teleport between different simulators | 888 | // only on TeleportFinish). This is untested for region teleport between different simulators |
525 | // though this probably also works. | 889 | // though this probably also works. |
526 | m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath); | 890 | m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath, finalDestination.RegionHandle, |
891 | finalDestination.RegionSizeX, finalDestination.RegionSizeY); | ||
527 | } | 892 | } |
528 | else | 893 | else |
529 | { | 894 | { |
895 | // XXX: This is a little misleading since we're information the client of its avatar destination, | ||
896 | // which may or may not be a neighbour region of the source region. This path is probably little | ||
897 | // used anyway (with EQ being the one used). But it is currently being used for test code. | ||
530 | sp.ControllingClient.InformClientOfNeighbour(destinationHandle, endPoint); | 898 | sp.ControllingClient.InformClientOfNeighbour(destinationHandle, endPoint); |
531 | } | 899 | } |
532 | } | 900 | } |
@@ -539,31 +907,77 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
539 | // Let's send a full update of the agent. This is a synchronous call. | 907 | // Let's send a full update of the agent. This is a synchronous call. |
540 | AgentData agent = new AgentData(); | 908 | AgentData agent = new AgentData(); |
541 | sp.CopyTo(agent); | 909 | sp.CopyTo(agent); |
542 | agent.Position = position; | 910 | if (ctx.OutboundVersion < 0.5f) |
911 | agent.Appearance.PackLegacyWearables = true; | ||
912 | agent.Position = agentCircuit.startpos; | ||
543 | SetCallbackURL(agent, sp.Scene.RegionInfo); | 913 | SetCallbackURL(agent, sp.Scene.RegionInfo); |
544 | 914 | ||
545 | //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Updating agent..."); | ||
546 | 915 | ||
916 | // We will check for an abort before UpdateAgent since UpdateAgent will require an active viewer to | ||
917 | // establish th econnection to the destination which makes it return true. | ||
918 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) | ||
919 | { | ||
920 | m_interRegionTeleportAborts.Value++; | ||
921 | |||
922 | m_log.DebugFormat( | ||
923 | "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} before UpdateAgent", | ||
924 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
925 | |||
926 | return; | ||
927 | } | ||
928 | |||
929 | // A common teleport failure occurs when we can send CreateAgent to the | ||
930 | // destination region but the viewer cannot establish the connection (e.g. due to network issues between | ||
931 | // the viewer and the destination). In this case, UpdateAgent timesout after 10 seconds, although then | ||
932 | // there's a further 10 second wait whilst we attempt to tell the destination to delete the agent in Fail(). | ||
547 | if (!UpdateAgent(reg, finalDestination, agent, sp)) | 933 | if (!UpdateAgent(reg, finalDestination, agent, sp)) |
548 | { | 934 | { |
549 | // Region doesn't take it | 935 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) |
936 | { | ||
937 | m_interRegionTeleportAborts.Value++; | ||
938 | |||
939 | m_log.DebugFormat( | ||
940 | "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after UpdateAgent due to previous client close.", | ||
941 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
942 | |||
943 | return; | ||
944 | } | ||
945 | |||
550 | m_log.WarnFormat( | 946 | m_log.WarnFormat( |
551 | "[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1} from {2}. Returning avatar to source region.", | 947 | "[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1}. Keeping avatar in {2}", |
552 | sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName); | 948 | sp.Name, finalDestination.RegionName, sp.Scene.Name); |
553 | 949 | ||
554 | Fail(sp, finalDestination, logout); | 950 | Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Connection between viewer and destination region could not be established."); |
555 | return; | 951 | return; |
556 | } | 952 | } |
557 | 953 | ||
558 | sp.ControllingClient.SendTeleportProgress(teleportFlags | (uint)TeleportFlags.DisableCancel, "sending_dest"); | 954 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling) |
955 | { | ||
956 | m_interRegionTeleportCancels.Value++; | ||
957 | |||
958 | m_log.DebugFormat( | ||
959 | "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request", | ||
960 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
961 | |||
962 | CleanupFailedInterRegionTeleport(sp, currentAgentCircuit.SessionID.ToString(), finalDestination); | ||
963 | |||
964 | return; | ||
965 | } | ||
559 | 966 | ||
560 | m_log.DebugFormat( | 967 | m_log.DebugFormat( |
561 | "[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}", | 968 | "[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}", |
562 | capsPath, sp.Scene.RegionInfo.RegionName, sp.Name); | 969 | capsPath, sp.Scene.RegionInfo.RegionName, sp.Name); |
563 | 970 | ||
971 | // We need to set this here to avoid an unlikely race condition when teleporting to a neighbour simulator, | ||
972 | // where that neighbour simulator could otherwise request a child agent create on the source which then | ||
973 | // closes our existing agent which is still signalled as root. | ||
974 | sp.IsChildAgent = true; | ||
975 | |||
976 | // OK, send TPFinish to the client, so that it starts the process of contacting the destination region | ||
564 | if (m_eqModule != null) | 977 | if (m_eqModule != null) |
565 | { | 978 | { |
566 | m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID); | 979 | m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID, |
980 | finalDestination.RegionSizeX, finalDestination.RegionSizeY); | ||
567 | } | 981 | } |
568 | else | 982 | else |
569 | { | 983 | { |
@@ -571,31 +985,43 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
571 | teleportFlags, capsPath); | 985 | teleportFlags, capsPath); |
572 | } | 986 | } |
573 | 987 | ||
574 | // Let's set this to true tentatively. This does not trigger OnChildAgent | ||
575 | sp.IsChildAgent = true; | ||
576 | |||
577 | // TeleportFinish makes the client send CompleteMovementIntoRegion (at the destination), which | 988 | // TeleportFinish makes the client send CompleteMovementIntoRegion (at the destination), which |
578 | // trigers a whole shebang of things there, including MakeRoot. So let's wait for confirmation | 989 | // trigers a whole shebang of things there, including MakeRoot. So let's wait for confirmation |
579 | // that the client contacted the destination before we close things here. | 990 | // that the client contacted the destination before we close things here. |
580 | if (!m_entityTransferStateMachine.WaitForAgentArrivedAtDestination(sp.UUID)) | 991 | if (!m_entityTransferStateMachine.WaitForAgentArrivedAtDestination(sp.UUID)) |
581 | { | 992 | { |
993 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) | ||
994 | { | ||
995 | m_interRegionTeleportAborts.Value++; | ||
996 | |||
997 | m_log.DebugFormat( | ||
998 | "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after WaitForAgentArrivedAtDestination due to previous client close.", | ||
999 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
1000 | |||
1001 | return; | ||
1002 | } | ||
1003 | |||
582 | m_log.WarnFormat( | 1004 | m_log.WarnFormat( |
583 | "[ENTITY TRANSFER MODULE]: Teleport of {0} to {1} from {2} failed due to no callback from destination region. Returning avatar to source region.", | 1005 | "[ENTITY TRANSFER MODULE]: Teleport of {0} to {1} from {2} failed due to no callback from destination region. Returning avatar to source region.", |
584 | sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName); | 1006 | sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName); |
585 | 1007 | ||
586 | Fail(sp, finalDestination, logout); | 1008 | Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Destination region did not signal teleport completion."); |
1009 | |||
587 | return; | 1010 | return; |
588 | } | 1011 | } |
589 | 1012 | ||
590 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); | 1013 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); |
591 | 1014 | ||
1015 | /* | ||
1016 | // TODO: This may be 0.6. Check if still needed | ||
592 | // For backwards compatibility | 1017 | // For backwards compatibility |
593 | if (version == "Unknown" || version == string.Empty) | 1018 | if (version == 0f) |
594 | { | 1019 | { |
595 | // CrossAttachmentsIntoNewRegion is a synchronous call. We shouldn't need to wait after it | 1020 | // CrossAttachmentsIntoNewRegion is a synchronous call. We shouldn't need to wait after it |
596 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Old simulator, sending attachments one by one..."); | 1021 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Old simulator, sending attachments one by one..."); |
597 | CrossAttachmentsIntoNewRegion(finalDestination, sp, true); | 1022 | CrossAttachmentsIntoNewRegion(finalDestination, sp, true); |
598 | } | 1023 | } |
1024 | */ | ||
599 | 1025 | ||
600 | // May need to logout or other cleanup | 1026 | // May need to logout or other cleanup |
601 | AgentHasMovedAway(sp, logout); | 1027 | AgentHasMovedAway(sp, logout); |
@@ -606,12 +1032,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
606 | // Now let's make it officially a child agent | 1032 | // Now let's make it officially a child agent |
607 | sp.MakeChildAgent(); | 1033 | sp.MakeChildAgent(); |
608 | 1034 | ||
609 | // sp.Scene.CleanDroppedAttachments(); | ||
610 | |||
611 | // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone | 1035 | // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone |
612 | 1036 | ||
613 | if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) | 1037 | if (NeedsClosing(sp.Scene.DefaultDrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) |
614 | { | 1038 | { |
1039 | if (!sp.Scene.IncomingPreCloseClient(sp)) | ||
1040 | return; | ||
1041 | |||
615 | // We need to delay here because Imprudence viewers, unlike v1 or v3, have a short (<200ms, <500ms) delay before | 1042 | // We need to delay here because Imprudence viewers, unlike v1 or v3, have a short (<200ms, <500ms) delay before |
616 | // they regard the new region as the current region after receiving the AgentMovementComplete | 1043 | // they regard the new region as the current region after receiving the AgentMovementComplete |
617 | // response. If close is sent before then, it will cause the viewer to quit instead. | 1044 | // response. If close is sent before then, it will cause the viewer to quit instead. |
@@ -620,51 +1047,243 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
620 | // an agent cannot teleport back to this region if it has teleported away. | 1047 | // an agent cannot teleport back to this region if it has teleported away. |
621 | Thread.Sleep(2000); | 1048 | Thread.Sleep(2000); |
622 | 1049 | ||
623 | sp.Scene.IncomingCloseAgent(sp.UUID, false); | 1050 | sp.Scene.CloseAgent(sp.UUID, false); |
624 | } | 1051 | } |
625 | else | 1052 | else |
626 | { | 1053 | { |
627 | // now we have a child agent in this region. | 1054 | // now we have a child agent in this region. |
628 | sp.Reset(); | 1055 | sp.Reset(); |
629 | } | 1056 | } |
1057 | } | ||
630 | 1058 | ||
631 | // Commented pending deletion since this method no longer appears to do anything at all | 1059 | private void TransferAgent_V2(ScenePresence sp, AgentCircuitData agentCircuit, GridRegion reg, GridRegion finalDestination, |
632 | // // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE! | 1060 | IPEndPoint endPoint, uint teleportFlags, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, EntityTransferContext ctx, out string reason) |
633 | // if (sp.Scene.NeedSceneCacheClear(sp.UUID)) | 1061 | { |
634 | // { | 1062 | ulong destinationHandle = finalDestination.RegionHandle; |
635 | // m_log.DebugFormat( | 1063 | AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); |
636 | // "[ENTITY TRANSFER MODULE]: User {0} is going to another region, profile cache removed", | 1064 | |
637 | // sp.UUID); | 1065 | // Let's create an agent there if one doesn't exist yet. |
638 | // } | 1066 | // NOTE: logout will always be false for a non-HG teleport. |
1067 | bool logout = false; | ||
1068 | if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout)) | ||
1069 | { | ||
1070 | m_interRegionTeleportFailures.Value++; | ||
1071 | |||
1072 | m_log.DebugFormat( | ||
1073 | "[ENTITY TRANSFER MODULE]: Teleport of {0} from {1} to {2} was refused because {3}", | ||
1074 | sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName, reason); | ||
1075 | |||
1076 | sp.ControllingClient.SendTeleportFailed(reason); | ||
1077 | |||
1078 | return; | ||
1079 | } | ||
1080 | |||
1081 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling) | ||
1082 | { | ||
1083 | m_interRegionTeleportCancels.Value++; | ||
1084 | |||
1085 | m_log.DebugFormat( | ||
1086 | "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request", | ||
1087 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
1088 | |||
1089 | return; | ||
1090 | } | ||
1091 | else if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) | ||
1092 | { | ||
1093 | m_interRegionTeleportAborts.Value++; | ||
1094 | |||
1095 | m_log.DebugFormat( | ||
1096 | "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after CreateAgent due to previous client close.", | ||
1097 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
1098 | |||
1099 | return; | ||
1100 | } | ||
1101 | |||
1102 | // Past this point we have to attempt clean up if the teleport fails, so update transfer state. | ||
1103 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); | ||
1104 | |||
1105 | IClientIPEndpoint ipepClient; | ||
1106 | string capsPath = String.Empty; | ||
1107 | float dist = (float)Math.Max(sp.Scene.DefaultDrawDistance, | ||
1108 | (float)Math.Max(sp.Scene.RegionInfo.RegionSizeX, sp.Scene.RegionInfo.RegionSizeY)); | ||
1109 | if (NeedsNewAgent(dist, oldRegionX, newRegionX, oldRegionY, newRegionY)) | ||
1110 | { | ||
1111 | m_log.DebugFormat( | ||
1112 | "[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for agent {3} from {4}", | ||
1113 | finalDestination.RegionName, newRegionX, newRegionY, sp.Name, Scene.Name); | ||
1114 | |||
1115 | //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent..."); | ||
1116 | #region IP Translation for NAT | ||
1117 | // Uses ipepClient above | ||
1118 | if (sp.ClientView.TryGet(out ipepClient)) | ||
1119 | { | ||
1120 | endPoint.Address = NetworkUtil.GetIPFor(ipepClient.EndPoint, endPoint.Address); | ||
1121 | } | ||
1122 | #endregion | ||
1123 | capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath); | ||
1124 | } | ||
1125 | else | ||
1126 | { | ||
1127 | agentCircuit.CapsPath = sp.Scene.CapsModule.GetChildSeed(sp.UUID, reg.RegionHandle); | ||
1128 | capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath); | ||
1129 | } | ||
1130 | |||
1131 | // We need to set this here to avoid an unlikely race condition when teleporting to a neighbour simulator, | ||
1132 | // where that neighbour simulator could otherwise request a child agent create on the source which then | ||
1133 | // closes our existing agent which is still signalled as root. | ||
1134 | //sp.IsChildAgent = true; | ||
1135 | |||
1136 | // New protocol: send TP Finish directly, without prior ES or EAC. That's what happens in the Linden grid | ||
1137 | if (m_eqModule != null) | ||
1138 | m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID, | ||
1139 | finalDestination.RegionSizeX, finalDestination.RegionSizeY); | ||
1140 | else | ||
1141 | sp.ControllingClient.SendRegionTeleport(destinationHandle, 13, endPoint, 4, | ||
1142 | teleportFlags, capsPath); | ||
1143 | |||
1144 | m_log.DebugFormat( | ||
1145 | "[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}", | ||
1146 | capsPath, sp.Scene.RegionInfo.RegionName, sp.Name); | ||
1147 | |||
1148 | // Let's send a full update of the agent. | ||
1149 | AgentData agent = new AgentData(); | ||
1150 | sp.CopyTo(agent); | ||
1151 | if (ctx.OutboundVersion < 0.5f) | ||
1152 | agent.Appearance.PackLegacyWearables = true; | ||
1153 | agent.Position = agentCircuit.startpos; | ||
1154 | agent.SenderWantsToWaitForRoot = true; | ||
1155 | //SetCallbackURL(agent, sp.Scene.RegionInfo); | ||
1156 | |||
1157 | // Reset the do not close flag. This must be done before the destination opens child connections (here | ||
1158 | // triggered by UpdateAgent) to avoid race conditions. However, we also want to reset it as late as possible | ||
1159 | // to avoid a situation where an unexpectedly early call to Scene.NewUserConnection() wrongly results | ||
1160 | // in no close. | ||
1161 | sp.DoNotCloseAfterTeleport = false; | ||
1162 | |||
1163 | // Send the Update. If this returns true, we know the client has contacted the destination | ||
1164 | // via CompleteMovementIntoRegion, so we can let go. | ||
1165 | // If it returns false, something went wrong, and we need to abort. | ||
1166 | if (!UpdateAgent(reg, finalDestination, agent, sp)) | ||
1167 | { | ||
1168 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) | ||
1169 | { | ||
1170 | m_interRegionTeleportAborts.Value++; | ||
1171 | |||
1172 | m_log.DebugFormat( | ||
1173 | "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after UpdateAgent due to previous client close.", | ||
1174 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
1175 | |||
1176 | return; | ||
1177 | } | ||
1178 | |||
1179 | m_log.WarnFormat( | ||
1180 | "[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1}. Keeping avatar in {2}", | ||
1181 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
1182 | |||
1183 | Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Connection between viewer and destination region could not be established."); | ||
1184 | return; | ||
1185 | } | ||
1186 | |||
1187 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); | ||
1188 | |||
1189 | // Need to signal neighbours whether child agents may need closing irrespective of whether this | ||
1190 | // one needed closing. We also need to close child agents as quickly as possible to avoid complicated | ||
1191 | // race conditions with rapid agent releporting (e.g. from A1 to a non-neighbour B, back | ||
1192 | // to a neighbour A2 then off to a non-neighbour C). Closing child agents any later requires complex | ||
1193 | // distributed checks to avoid problems in rapid reteleporting scenarios and where child agents are | ||
1194 | // abandoned without proper close by viewer but then re-used by an incoming connection. | ||
1195 | sp.CloseChildAgents(newRegionX, newRegionY); | ||
1196 | |||
1197 | // May need to logout or other cleanup | ||
1198 | AgentHasMovedAway(sp, logout); | ||
1199 | |||
1200 | // Well, this is it. The agent is over there. | ||
1201 | KillEntity(sp.Scene, sp.LocalId); | ||
639 | 1202 | ||
640 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | 1203 | // Now let's make it officially a child agent |
1204 | sp.MakeChildAgent(); | ||
1205 | |||
1206 | // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone | ||
1207 | if (NeedsClosing(sp.Scene.DefaultDrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) | ||
1208 | { | ||
1209 | if (!sp.Scene.IncomingPreCloseClient(sp)) | ||
1210 | return; | ||
1211 | |||
1212 | // RED ALERT!!!! | ||
1213 | // PLEASE DO NOT DECREASE THIS WAIT TIME UNDER ANY CIRCUMSTANCES. | ||
1214 | // THE VIEWERS SEEM TO NEED SOME TIME AFTER RECEIVING MoveAgentIntoRegion | ||
1215 | // BEFORE THEY SETTLE IN THE NEW REGION. | ||
1216 | // DECREASING THE WAIT TIME HERE WILL EITHER RESULT IN A VIEWER CRASH OR | ||
1217 | // IN THE AVIE BEING PLACED IN INFINITY FOR A COUPLE OF SECONDS. | ||
1218 | Thread.Sleep(15000); | ||
1219 | |||
1220 | // OK, it got this agent. Let's close everything | ||
1221 | // If we shouldn't close the agent due to some other region renewing the connection | ||
1222 | // then this will be handled in IncomingCloseAgent under lock conditions | ||
1223 | m_log.DebugFormat( | ||
1224 | "[ENTITY TRANSFER MODULE]: Closing agent {0} in {1} after teleport", sp.Name, Scene.Name); | ||
1225 | |||
1226 | sp.Scene.CloseAgent(sp.UUID, false); | ||
1227 | } | ||
1228 | else | ||
1229 | { | ||
1230 | // now we have a child agent in this region. | ||
1231 | sp.Reset(); | ||
1232 | } | ||
641 | } | 1233 | } |
642 | 1234 | ||
643 | protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout) | 1235 | /// <summary> |
1236 | /// Clean up an inter-region teleport that did not complete, either because of simulator failure or cancellation. | ||
1237 | /// </summary> | ||
1238 | /// <remarks> | ||
1239 | /// All operations here must be idempotent so that we can call this method at any point in the teleport process | ||
1240 | /// up until we send the TeleportFinish event quene event to the viewer. | ||
1241 | /// <remarks> | ||
1242 | /// <param name='sp'> </param> | ||
1243 | /// <param name='finalDestination'></param> | ||
1244 | protected virtual void CleanupFailedInterRegionTeleport(ScenePresence sp, string auth_token, GridRegion finalDestination) | ||
644 | { | 1245 | { |
645 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); | 1246 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); |
646 | 1247 | ||
647 | // Client never contacted destination. Let's restore everything back | 1248 | if (sp.IsChildAgent) // We had set it to child before attempted TP (V1) |
648 | sp.ControllingClient.SendTeleportFailed("Problems connecting to destination."); | 1249 | { |
1250 | sp.IsChildAgent = false; | ||
1251 | ReInstantiateScripts(sp); | ||
649 | 1252 | ||
650 | // Fail. Reset it back | 1253 | EnableChildAgents(sp); |
651 | sp.IsChildAgent = false; | 1254 | } |
652 | ReInstantiateScripts(sp); | 1255 | // Finally, kill the agent we just created at the destination. |
1256 | // XXX: Possibly this should be done asynchronously. | ||
1257 | Scene.SimulationService.CloseAgent(finalDestination, sp.UUID, auth_token); | ||
1258 | } | ||
653 | 1259 | ||
654 | EnableChildAgents(sp); | 1260 | /// <summary> |
1261 | /// Signal that the inter-region teleport failed and perform cleanup. | ||
1262 | /// </summary> | ||
1263 | /// <param name='sp'></param> | ||
1264 | /// <param name='finalDestination'></param> | ||
1265 | /// <param name='logout'></param> | ||
1266 | /// <param name='reason'>Human readable reason for teleport failure. Will be sent to client.</param> | ||
1267 | protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout, string auth_code, string reason) | ||
1268 | { | ||
1269 | CleanupFailedInterRegionTeleport(sp, auth_code, finalDestination); | ||
655 | 1270 | ||
656 | // Finally, kill the agent we just created at the destination. | 1271 | m_interRegionTeleportFailures.Value++; |
657 | Scene.SimulationService.CloseAgent(finalDestination, sp.UUID); | ||
658 | 1272 | ||
659 | sp.Scene.EventManager.TriggerTeleportFail(sp.ControllingClient, logout); | 1273 | sp.ControllingClient.SendTeleportFailed( |
1274 | string.Format( | ||
1275 | "Problems connecting to destination {0}, reason: {1}", finalDestination.RegionName, reason)); | ||
660 | 1276 | ||
661 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | 1277 | sp.Scene.EventManager.TriggerTeleportFail(sp.ControllingClient, logout); |
662 | } | 1278 | } |
663 | 1279 | ||
664 | protected virtual bool CreateAgent(ScenePresence sp, GridRegion reg, GridRegion finalDestination, AgentCircuitData agentCircuit, uint teleportFlags, out string reason, out bool logout) | 1280 | protected virtual bool CreateAgent(ScenePresence sp, GridRegion reg, GridRegion finalDestination, AgentCircuitData agentCircuit, uint teleportFlags, out string reason, out bool logout) |
665 | { | 1281 | { |
1282 | GridRegion source = new GridRegion(Scene.RegionInfo); | ||
1283 | source.RawServerURI = m_GatekeeperURI; | ||
1284 | |||
666 | logout = false; | 1285 | logout = false; |
667 | bool success = Scene.SimulationService.CreateAgent(finalDestination, agentCircuit, teleportFlags, out reason); | 1286 | bool success = Scene.SimulationService.CreateAgent(source, finalDestination, agentCircuit, teleportFlags, out reason); |
668 | 1287 | ||
669 | if (success) | 1288 | if (success) |
670 | sp.Scene.EventManager.TriggerTeleportStart(sp.ControllingClient, reg, finalDestination, teleportFlags, logout); | 1289 | sp.Scene.EventManager.TriggerTeleportStart(sp.ControllingClient, reg, finalDestination, teleportFlags, logout); |
@@ -702,14 +1321,32 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
702 | scene.SendKillObject(new List<uint> { localID }); | 1321 | scene.SendKillObject(new List<uint> { localID }); |
703 | } | 1322 | } |
704 | 1323 | ||
705 | protected virtual GridRegion GetFinalDestination(GridRegion region) | 1324 | protected virtual GridRegion GetFinalDestination(GridRegion region, UUID agentID, string agentHomeURI, out string message) |
706 | { | 1325 | { |
1326 | message = null; | ||
707 | return region; | 1327 | return region; |
708 | } | 1328 | } |
709 | 1329 | ||
1330 | // This returns 'true' if the new region already has a child agent for our | ||
1331 | // incoming agent. The implication is that, if 'false', we have to create the | ||
1332 | // child and then teleport into the region. | ||
710 | protected virtual bool NeedsNewAgent(float drawdist, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY) | 1333 | protected virtual bool NeedsNewAgent(float drawdist, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY) |
711 | { | 1334 | { |
712 | return Util.IsOutsideView(drawdist, oldRegionX, newRegionX, oldRegionY, newRegionY); | 1335 | if (m_regionCombinerModule != null && m_regionCombinerModule.IsRootForMegaregion(Scene.RegionInfo.RegionID)) |
1336 | { | ||
1337 | Vector2 swCorner, neCorner; | ||
1338 | GetMegaregionViewRange(out swCorner, out neCorner); | ||
1339 | |||
1340 | m_log.DebugFormat( | ||
1341 | "[ENTITY TRANSFER MODULE]: Megaregion view of {0} is from {1} to {2} with new agent check for {3},{4}", | ||
1342 | Scene.Name, swCorner, neCorner, newRegionX, newRegionY); | ||
1343 | |||
1344 | return !(newRegionX >= swCorner.X && newRegionX <= neCorner.X && newRegionY >= swCorner.Y && newRegionY <= neCorner.Y); | ||
1345 | } | ||
1346 | else | ||
1347 | { | ||
1348 | return Util.IsOutsideView(drawdist, oldRegionX, newRegionX, oldRegionY, newRegionY); | ||
1349 | } | ||
713 | } | 1350 | } |
714 | 1351 | ||
715 | protected virtual bool NeedsClosing(float drawdist, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, GridRegion reg) | 1352 | protected virtual bool NeedsClosing(float drawdist, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, GridRegion reg) |
@@ -717,20 +1354,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
717 | return Util.IsOutsideView(drawdist, oldRegionX, newRegionX, oldRegionY, newRegionY); | 1354 | return Util.IsOutsideView(drawdist, oldRegionX, newRegionX, oldRegionY, newRegionY); |
718 | } | 1355 | } |
719 | 1356 | ||
720 | protected virtual bool IsOutsideRegion(Scene s, Vector3 pos) | ||
721 | { | ||
722 | if (s.TestBorderCross(pos, Cardinals.N)) | ||
723 | return true; | ||
724 | if (s.TestBorderCross(pos, Cardinals.S)) | ||
725 | return true; | ||
726 | if (s.TestBorderCross(pos, Cardinals.E)) | ||
727 | return true; | ||
728 | if (s.TestBorderCross(pos, Cardinals.W)) | ||
729 | return true; | ||
730 | |||
731 | return false; | ||
732 | } | ||
733 | |||
734 | #endregion | 1357 | #endregion |
735 | 1358 | ||
736 | #region Landmark Teleport | 1359 | #region Landmark Teleport |
@@ -758,7 +1381,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
758 | 1381 | ||
759 | #region Teleport Home | 1382 | #region Teleport Home |
760 | 1383 | ||
761 | public virtual void TeleportHome(UUID id, IClientAPI client) | 1384 | public virtual void TriggerTeleportHome(UUID id, IClientAPI client) |
1385 | { | ||
1386 | TeleportHome(id, client); | ||
1387 | } | ||
1388 | |||
1389 | public virtual bool TeleportHome(UUID id, IClientAPI client) | ||
762 | { | 1390 | { |
763 | m_log.DebugFormat( | 1391 | m_log.DebugFormat( |
764 | "[ENTITY TRANSFER MODULE]: Request to teleport {0} {1} home", client.Name, client.AgentId); | 1392 | "[ENTITY TRANSFER MODULE]: Request to teleport {0} {1} home", client.Name, client.AgentId); |
@@ -768,12 +1396,20 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
768 | 1396 | ||
769 | if (uinfo != null) | 1397 | if (uinfo != null) |
770 | { | 1398 | { |
1399 | if (uinfo.HomeRegionID == UUID.Zero) | ||
1400 | { | ||
1401 | // can't find the Home region: Tell viewer and abort | ||
1402 | m_log.ErrorFormat("{0} No grid user info found for {1} {2}. Cannot send home.", | ||
1403 | LogHeader, client.Name, client.AgentId); | ||
1404 | client.SendTeleportFailed("You don't have a home position set."); | ||
1405 | return false; | ||
1406 | } | ||
771 | GridRegion regionInfo = Scene.GridService.GetRegionByUUID(UUID.Zero, uinfo.HomeRegionID); | 1407 | GridRegion regionInfo = Scene.GridService.GetRegionByUUID(UUID.Zero, uinfo.HomeRegionID); |
772 | if (regionInfo == null) | 1408 | if (regionInfo == null) |
773 | { | 1409 | { |
774 | // can't find the Home region: Tell viewer and abort | 1410 | // can't find the Home region: Tell viewer and abort |
775 | client.SendTeleportFailed("Your home region could not be found."); | 1411 | client.SendTeleportFailed("Your home region could not be found."); |
776 | return; | 1412 | return false; |
777 | } | 1413 | } |
778 | 1414 | ||
779 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Home region of {0} is {1} ({2}-{3})", | 1415 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Home region of {0} is {1} ({2}-{3})", |
@@ -783,13 +1419,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
783 | ((Scene)(client.Scene)).RequestTeleportLocation( | 1419 | ((Scene)(client.Scene)).RequestTeleportLocation( |
784 | client, regionInfo.RegionHandle, uinfo.HomePosition, uinfo.HomeLookAt, | 1420 | client, regionInfo.RegionHandle, uinfo.HomePosition, uinfo.HomeLookAt, |
785 | (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaHome)); | 1421 | (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaHome)); |
1422 | return true; | ||
786 | } | 1423 | } |
787 | else | 1424 | else |
788 | { | 1425 | { |
789 | m_log.ErrorFormat( | 1426 | // can't find the Home region: Tell viewer and abort |
790 | "[ENTITY TRANSFER MODULE]: No grid user information found for {0} {1}. Cannot send home.", | 1427 | client.SendTeleportFailed("Your home region could not be found."); |
791 | client.Name, client.AgentId); | ||
792 | } | 1428 | } |
1429 | return false; | ||
793 | } | 1430 | } |
794 | 1431 | ||
795 | #endregion | 1432 | #endregion |
@@ -797,230 +1434,112 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
797 | 1434 | ||
798 | #region Agent Crossings | 1435 | #region Agent Crossings |
799 | 1436 | ||
800 | public bool Cross(ScenePresence agent, bool isFlying) | 1437 | // Given a position relative to the current region (which has previously been tested to |
1438 | // see that it is actually outside the current region), find the new region that the | ||
1439 | // point is actually in. | ||
1440 | // Returns the coordinates and information of the new region or 'null' of it doesn't exist. | ||
1441 | public GridRegion GetDestination(Scene scene, UUID agentID, Vector3 pos, | ||
1442 | EntityTransferContext ctx, out Vector3 newpos, out string failureReason) | ||
801 | { | 1443 | { |
802 | Scene scene = agent.Scene; | 1444 | newpos = pos; |
803 | Vector3 pos = agent.AbsolutePosition; | 1445 | failureReason = string.Empty; |
804 | Vector3 newpos = new Vector3(pos.X, pos.Y, pos.Z); | 1446 | string homeURI = scene.GetAgentHomeURI(agentID); |
805 | uint neighbourx = scene.RegionInfo.RegionLocX; | ||
806 | uint neighboury = scene.RegionInfo.RegionLocY; | ||
807 | const float boundaryDistance = 1.7f; | ||
808 | Vector3 northCross = new Vector3(0, boundaryDistance, 0); | ||
809 | Vector3 southCross = new Vector3(0, -1 * boundaryDistance, 0); | ||
810 | Vector3 eastCross = new Vector3(boundaryDistance, 0, 0); | ||
811 | Vector3 westCross = new Vector3(-1 * boundaryDistance, 0, 0); | ||
812 | |||
813 | // distance into new region to place avatar | ||
814 | const float enterDistance = 0.5f; | ||
815 | 1447 | ||
816 | if (scene.TestBorderCross(pos + westCross, Cardinals.W)) | 1448 | // m_log.DebugFormat( |
817 | { | 1449 | // "[ENTITY TRANSFER MODULE]: Crossing agent {0} at pos {1} in {2}", agent.Name, pos, scene.Name); |
818 | if (scene.TestBorderCross(pos + northCross, Cardinals.N)) | ||
819 | { | ||
820 | Border b = scene.GetCrossedBorder(pos + northCross, Cardinals.N); | ||
821 | neighboury += (uint)(int)(b.BorderLine.Z / (int)Constants.RegionSize); | ||
822 | } | ||
823 | else if (scene.TestBorderCross(pos + southCross, Cardinals.S)) | ||
824 | { | ||
825 | Border b = scene.GetCrossedBorder(pos + southCross, Cardinals.S); | ||
826 | if (b.TriggerRegionX == 0 && b.TriggerRegionY == 0) | ||
827 | { | ||
828 | neighboury--; | ||
829 | newpos.Y = Constants.RegionSize - enterDistance; | ||
830 | } | ||
831 | else | ||
832 | { | ||
833 | agent.IsInTransit = true; | ||
834 | |||
835 | neighboury = b.TriggerRegionY; | ||
836 | neighbourx = b.TriggerRegionX; | ||
837 | |||
838 | Vector3 newposition = pos; | ||
839 | newposition.X += (scene.RegionInfo.RegionLocX - neighbourx) * Constants.RegionSize; | ||
840 | newposition.Y += (scene.RegionInfo.RegionLocY - neighboury) * Constants.RegionSize; | ||
841 | agent.ControllingClient.SendAgentAlertMessage( | ||
842 | String.Format("Moving you to region {0},{1}", neighbourx, neighboury), false); | ||
843 | InformClientToInitateTeleportToLocation(agent, neighbourx, neighboury, newposition, scene); | ||
844 | return true; | ||
845 | } | ||
846 | } | ||
847 | |||
848 | Border ba = scene.GetCrossedBorder(pos + westCross, Cardinals.W); | ||
849 | if (ba.TriggerRegionX == 0 && ba.TriggerRegionY == 0) | ||
850 | { | ||
851 | neighbourx--; | ||
852 | newpos.X = Constants.RegionSize - enterDistance; | ||
853 | } | ||
854 | else | ||
855 | { | ||
856 | agent.IsInTransit = true; | ||
857 | 1450 | ||
858 | neighboury = ba.TriggerRegionY; | 1451 | // Compute world location of the object's position |
859 | neighbourx = ba.TriggerRegionX; | 1452 | double presenceWorldX = (double)scene.RegionInfo.WorldLocX + pos.X; |
1453 | double presenceWorldY = (double)scene.RegionInfo.WorldLocY + pos.Y; | ||
860 | 1454 | ||
861 | Vector3 newposition = pos; | 1455 | // Call the grid service to lookup the region containing the new position. |
862 | newposition.X += (scene.RegionInfo.RegionLocX - neighbourx) * Constants.RegionSize; | 1456 | GridRegion neighbourRegion = GetRegionContainingWorldLocation(scene.GridService, scene.RegionInfo.ScopeID, |
863 | newposition.Y += (scene.RegionInfo.RegionLocY - neighboury) * Constants.RegionSize; | 1457 | presenceWorldX, presenceWorldY, |
864 | agent.ControllingClient.SendAgentAlertMessage( | 1458 | Math.Max(scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY)); |
865 | String.Format("Moving you to region {0},{1}", neighbourx, neighboury), false); | ||
866 | InformClientToInitateTeleportToLocation(agent, neighbourx, neighboury, newposition, scene); | ||
867 | 1459 | ||
868 | return true; | 1460 | if (neighbourRegion != null) |
869 | } | ||
870 | |||
871 | } | ||
872 | else if (scene.TestBorderCross(pos + eastCross, Cardinals.E)) | ||
873 | { | 1461 | { |
874 | Border b = scene.GetCrossedBorder(pos + eastCross, Cardinals.E); | 1462 | // Compute the entity's position relative to the new region |
875 | neighbourx += (uint)(int)(b.BorderLine.Z / (int)Constants.RegionSize); | 1463 | newpos = new Vector3((float)(presenceWorldX - (double)neighbourRegion.RegionLocX), |
876 | newpos.X = enterDistance; | 1464 | (float)(presenceWorldY - (double)neighbourRegion.RegionLocY), |
1465 | pos.Z); | ||
877 | 1466 | ||
878 | if (scene.TestBorderCross(pos + southCross, Cardinals.S)) | 1467 | if (m_bannedRegionCache.IfBanned(neighbourRegion.RegionHandle, agentID)) |
879 | { | ||
880 | Border ba = scene.GetCrossedBorder(pos + southCross, Cardinals.S); | ||
881 | if (ba.TriggerRegionX == 0 && ba.TriggerRegionY == 0) | ||
882 | { | ||
883 | neighboury--; | ||
884 | newpos.Y = Constants.RegionSize - enterDistance; | ||
885 | } | ||
886 | else | ||
887 | { | ||
888 | agent.IsInTransit = true; | ||
889 | |||
890 | neighboury = ba.TriggerRegionY; | ||
891 | neighbourx = ba.TriggerRegionX; | ||
892 | Vector3 newposition = pos; | ||
893 | newposition.X += (scene.RegionInfo.RegionLocX - neighbourx) * Constants.RegionSize; | ||
894 | newposition.Y += (scene.RegionInfo.RegionLocY - neighboury) * Constants.RegionSize; | ||
895 | agent.ControllingClient.SendAgentAlertMessage( | ||
896 | String.Format("Moving you to region {0},{1}", neighbourx, neighboury), false); | ||
897 | InformClientToInitateTeleportToLocation(agent, neighbourx, neighboury, newposition, scene); | ||
898 | return true; | ||
899 | } | ||
900 | } | ||
901 | else if (scene.TestBorderCross(pos + northCross, Cardinals.N)) | ||
902 | { | 1468 | { |
903 | Border c = scene.GetCrossedBorder(pos + northCross, Cardinals.N); | 1469 | failureReason = "Cannot region cross into banned parcel"; |
904 | neighboury += (uint)(int)(c.BorderLine.Z / (int)Constants.RegionSize); | 1470 | neighbourRegion = null; |
905 | newpos.Y = enterDistance; | ||
906 | } | ||
907 | } | ||
908 | else if (scene.TestBorderCross(pos + southCross, Cardinals.S)) | ||
909 | { | ||
910 | Border b = scene.GetCrossedBorder(pos + southCross, Cardinals.S); | ||
911 | if (b.TriggerRegionX == 0 && b.TriggerRegionY == 0) | ||
912 | { | ||
913 | neighboury--; | ||
914 | newpos.Y = Constants.RegionSize - enterDistance; | ||
915 | } | 1471 | } |
916 | else | 1472 | else |
917 | { | 1473 | { |
918 | agent.IsInTransit = true; | 1474 | // If not banned, make sure this agent is not in the list. |
919 | 1475 | m_bannedRegionCache.Remove(neighbourRegion.RegionHandle, agentID); | |
920 | neighboury = b.TriggerRegionY; | ||
921 | neighbourx = b.TriggerRegionX; | ||
922 | Vector3 newposition = pos; | ||
923 | newposition.X += (scene.RegionInfo.RegionLocX - neighbourx) * Constants.RegionSize; | ||
924 | newposition.Y += (scene.RegionInfo.RegionLocY - neighboury) * Constants.RegionSize; | ||
925 | agent.ControllingClient.SendAgentAlertMessage( | ||
926 | String.Format("Moving you to region {0},{1}", neighbourx, neighboury), false); | ||
927 | InformClientToInitateTeleportToLocation(agent, neighbourx, neighboury, newposition, scene); | ||
928 | return true; | ||
929 | } | 1476 | } |
930 | } | ||
931 | else if (scene.TestBorderCross(pos + northCross, Cardinals.N)) | ||
932 | { | ||
933 | Border b = scene.GetCrossedBorder(pos + northCross, Cardinals.N); | ||
934 | neighboury += (uint)(int)(b.BorderLine.Z / (int)Constants.RegionSize); | ||
935 | newpos.Y = enterDistance; | ||
936 | } | ||
937 | |||
938 | /* | ||
939 | |||
940 | if (pos.X < boundaryDistance) //West | ||
941 | { | ||
942 | neighbourx--; | ||
943 | newpos.X = Constants.RegionSize - enterDistance; | ||
944 | } | ||
945 | else if (pos.X > Constants.RegionSize - boundaryDistance) // East | ||
946 | { | ||
947 | neighbourx++; | ||
948 | newpos.X = enterDistance; | ||
949 | } | ||
950 | 1477 | ||
951 | if (pos.Y < boundaryDistance) // South | 1478 | // Check to see if we have access to the target region. |
952 | { | 1479 | if (neighbourRegion != null |
953 | neighboury--; | 1480 | && !scene.SimulationService.QueryAccess(neighbourRegion, agentID, homeURI, false, newpos, scene.GetFormatsOffered(), ctx, out failureReason)) |
954 | newpos.Y = Constants.RegionSize - enterDistance; | ||
955 | } | ||
956 | else if (pos.Y > Constants.RegionSize - boundaryDistance) // North | ||
957 | { | ||
958 | neighboury++; | ||
959 | newpos.Y = enterDistance; | ||
960 | } | ||
961 | */ | ||
962 | |||
963 | ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize)); | ||
964 | |||
965 | int x = (int)(neighbourx * Constants.RegionSize), y = (int)(neighboury * Constants.RegionSize); | ||
966 | |||
967 | ExpiringCache<ulong, DateTime> r; | ||
968 | DateTime banUntil; | ||
969 | |||
970 | if (m_bannedRegions.TryGetValue(agent.ControllingClient.AgentId, out r)) | ||
971 | { | ||
972 | if (r.TryGetValue(neighbourHandle, out banUntil)) | ||
973 | { | 1481 | { |
974 | if (DateTime.Now < banUntil) | 1482 | // remember banned |
975 | return false; | 1483 | m_bannedRegionCache.Add(neighbourRegion.RegionHandle, agentID); |
976 | r.Remove(neighbourHandle); | 1484 | neighbourRegion = null; |
977 | } | 1485 | } |
978 | } | 1486 | } |
979 | else | 1487 | else |
980 | { | 1488 | { |
981 | r = null; | 1489 | // The destination region just doesn't exist |
1490 | failureReason = "Cannot cross into non-existent region"; | ||
982 | } | 1491 | } |
983 | 1492 | ||
984 | GridRegion neighbourRegion = scene.GridService.GetRegionByPosition(scene.RegionInfo.ScopeID, (int)x, (int)y); | 1493 | if (neighbourRegion == null) |
1494 | m_log.DebugFormat("{0} GetDestination: region not found. Old region name={1} at <{2},{3}> of size <{4},{5}>. Old pos={6}", | ||
1495 | LogHeader, scene.RegionInfo.RegionName, | ||
1496 | scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY, | ||
1497 | scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY, | ||
1498 | pos); | ||
1499 | else | ||
1500 | m_log.DebugFormat("{0} GetDestination: new region={1} at <{2},{3}> of size <{4},{5}>, newpos=<{6},{7}>", | ||
1501 | LogHeader, neighbourRegion.RegionName, | ||
1502 | neighbourRegion.RegionLocX, neighbourRegion.RegionLocY, neighbourRegion.RegionSizeX, neighbourRegion.RegionSizeY, | ||
1503 | newpos.X, newpos.Y); | ||
985 | 1504 | ||
986 | string reason; | 1505 | return neighbourRegion; |
987 | string version; | 1506 | } |
988 | if (!scene.SimulationService.QueryAccess(neighbourRegion, agent.ControllingClient.AgentId, newpos, out version, out reason)) | ||
989 | { | ||
990 | agent.ControllingClient.SendAlertMessage("Cannot region cross into banned parcel"); | ||
991 | if (r == null) | ||
992 | { | ||
993 | r = new ExpiringCache<ulong, DateTime>(); | ||
994 | r.Add(neighbourHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); | ||
995 | 1507 | ||
996 | m_bannedRegions.Add(agent.ControllingClient.AgentId, r, TimeSpan.FromSeconds(45)); | 1508 | public bool Cross(ScenePresence agent, bool isFlying) |
997 | } | 1509 | { |
998 | else | 1510 | Vector3 newpos; |
999 | { | 1511 | EntityTransferContext ctx = new EntityTransferContext(); |
1000 | r.Add(neighbourHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); | 1512 | string failureReason; |
1001 | } | 1513 | |
1514 | GridRegion neighbourRegion = GetDestination(agent.Scene, agent.UUID, agent.AbsolutePosition, | ||
1515 | ctx, out newpos, out failureReason); | ||
1516 | if (neighbourRegion == null) | ||
1517 | { | ||
1518 | agent.ControllingClient.SendAlertMessage(failureReason); | ||
1002 | return false; | 1519 | return false; |
1003 | } | 1520 | } |
1004 | 1521 | ||
1005 | agent.IsInTransit = true; | 1522 | agent.IsInTransit = true; |
1006 | 1523 | ||
1007 | CrossAgentToNewRegionDelegate d = CrossAgentToNewRegionAsync; | 1524 | CrossAgentToNewRegionDelegate d = CrossAgentToNewRegionAsync; |
1008 | d.BeginInvoke(agent, newpos, neighbourx, neighboury, neighbourRegion, isFlying, version, CrossAgentToNewRegionCompleted, d); | 1525 | d.BeginInvoke(agent, newpos, neighbourRegion, isFlying, ctx, CrossAgentToNewRegionCompleted, d); |
1526 | |||
1527 | Scene.EventManager.TriggerCrossAgentToNewRegion(agent, isFlying, neighbourRegion); | ||
1009 | 1528 | ||
1010 | return true; | 1529 | return true; |
1011 | } | 1530 | } |
1012 | 1531 | ||
1013 | 1532 | ||
1014 | public delegate void InformClientToInitateTeleportToLocationDelegate(ScenePresence agent, uint regionX, uint regionY, | 1533 | public delegate void InformClientToInitiateTeleportToLocationDelegate(ScenePresence agent, uint regionX, uint regionY, |
1015 | Vector3 position, | 1534 | Vector3 position, |
1016 | Scene initiatingScene); | 1535 | Scene initiatingScene); |
1017 | 1536 | ||
1018 | private void InformClientToInitateTeleportToLocation(ScenePresence agent, uint regionX, uint regionY, Vector3 position, Scene initiatingScene) | 1537 | private void InformClientToInitiateTeleportToLocation(ScenePresence agent, uint regionX, uint regionY, Vector3 position, Scene initiatingScene) |
1019 | { | 1538 | { |
1020 | 1539 | ||
1021 | // This assumes that we know what our neighbours are. | 1540 | // This assumes that we know what our neighbours are. |
1022 | 1541 | ||
1023 | InformClientToInitateTeleportToLocationDelegate d = InformClientToInitiateTeleportToLocationAsync; | 1542 | InformClientToInitiateTeleportToLocationDelegate d = InformClientToInitiateTeleportToLocationAsync; |
1024 | d.BeginInvoke(agent, regionX, regionY, position, initiatingScene, | 1543 | d.BeginInvoke(agent, regionX, regionY, position, initiatingScene, |
1025 | InformClientToInitiateTeleportToLocationCompleted, | 1544 | InformClientToInitiateTeleportToLocationCompleted, |
1026 | d); | 1545 | d); |
@@ -1030,16 +1549,24 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1030 | Scene initiatingScene) | 1549 | Scene initiatingScene) |
1031 | { | 1550 | { |
1032 | Thread.Sleep(10000); | 1551 | Thread.Sleep(10000); |
1033 | 1552 | ||
1553 | m_log.DebugFormat( | ||
1554 | "[ENTITY TRANSFER MODULE]: Auto-reteleporting {0} to correct megaregion location {1},{2},{3} from {4}", | ||
1555 | agent.Name, regionX, regionY, position, initiatingScene.Name); | ||
1556 | |||
1557 | agent.Scene.RequestTeleportLocation( | ||
1558 | agent.ControllingClient, | ||
1559 | Util.RegionLocToHandle(regionX, regionY), | ||
1560 | position, | ||
1561 | agent.Lookat, | ||
1562 | (uint)Constants.TeleportFlags.ViaLocation); | ||
1563 | |||
1564 | /* | ||
1034 | IMessageTransferModule im = initiatingScene.RequestModuleInterface<IMessageTransferModule>(); | 1565 | IMessageTransferModule im = initiatingScene.RequestModuleInterface<IMessageTransferModule>(); |
1035 | if (im != null) | 1566 | if (im != null) |
1036 | { | 1567 | { |
1037 | UUID gotoLocation = Util.BuildFakeParcelID( | 1568 | UUID gotoLocation = Util.BuildFakeParcelID( |
1038 | Util.UIntsToLong( | 1569 | Util.RegionLocToHandle(regionX, regionY), |
1039 | (regionX * | ||
1040 | (uint)Constants.RegionSize), | ||
1041 | (regionY * | ||
1042 | (uint)Constants.RegionSize)), | ||
1043 | (uint)(int)position.X, | 1570 | (uint)(int)position.X, |
1044 | (uint)(int)position.Y, | 1571 | (uint)(int)position.Y, |
1045 | (uint)(int)position.Z); | 1572 | (uint)(int)position.Z); |
@@ -1065,53 +1592,73 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1065 | }); | 1592 | }); |
1066 | 1593 | ||
1067 | } | 1594 | } |
1595 | */ | ||
1068 | } | 1596 | } |
1069 | 1597 | ||
1070 | private void InformClientToInitiateTeleportToLocationCompleted(IAsyncResult iar) | 1598 | private void InformClientToInitiateTeleportToLocationCompleted(IAsyncResult iar) |
1071 | { | 1599 | { |
1072 | InformClientToInitateTeleportToLocationDelegate icon = | 1600 | InformClientToInitiateTeleportToLocationDelegate icon = |
1073 | (InformClientToInitateTeleportToLocationDelegate)iar.AsyncState; | 1601 | (InformClientToInitiateTeleportToLocationDelegate)iar.AsyncState; |
1074 | icon.EndInvoke(iar); | 1602 | icon.EndInvoke(iar); |
1075 | } | 1603 | } |
1076 | 1604 | ||
1077 | public delegate ScenePresence CrossAgentToNewRegionDelegate(ScenePresence agent, Vector3 pos, uint neighbourx, uint neighboury, GridRegion neighbourRegion, bool isFlying, string version); | 1605 | public bool CrossAgentToNewRegionPrep(ScenePresence agent, GridRegion neighbourRegion) |
1606 | { | ||
1607 | if (neighbourRegion == null) | ||
1608 | return false; | ||
1609 | |||
1610 | m_entityTransferStateMachine.SetInTransit(agent.UUID); | ||
1611 | |||
1612 | agent.RemoveFromPhysicalScene(); | ||
1613 | |||
1614 | return true; | ||
1615 | } | ||
1078 | 1616 | ||
1079 | /// <summary> | 1617 | /// <summary> |
1080 | /// This Closes child agents on neighbouring regions | 1618 | /// This Closes child agents on neighbouring regions |
1081 | /// Calls an asynchronous method to do so.. so it doesn't lag the sim. | 1619 | /// Calls an asynchronous method to do so.. so it doesn't lag the sim. |
1082 | /// </summary> | 1620 | /// </summary> |
1083 | protected ScenePresence CrossAgentToNewRegionAsync( | 1621 | public ScenePresence CrossAgentToNewRegionAsync( |
1084 | ScenePresence agent, Vector3 pos, uint neighbourx, uint neighboury, GridRegion neighbourRegion, | 1622 | ScenePresence agent, Vector3 pos, GridRegion neighbourRegion, |
1085 | bool isFlying, string version) | 1623 | bool isFlying, EntityTransferContext ctx) |
1086 | { | 1624 | { |
1087 | if (neighbourRegion == null) | ||
1088 | return agent; | ||
1089 | |||
1090 | try | 1625 | try |
1091 | { | 1626 | { |
1092 | m_entityTransferStateMachine.SetInTransit(agent.UUID); | 1627 | m_log.DebugFormat("{0}: CrossAgentToNewRegionAsync: new region={1} at <{2},{3}>. newpos={4}", |
1628 | LogHeader, neighbourRegion.RegionName, neighbourRegion.RegionLocX, neighbourRegion.RegionLocY, pos); | ||
1093 | 1629 | ||
1094 | ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize)); | 1630 | if (!CrossAgentToNewRegionPrep(agent, neighbourRegion)) |
1095 | 1631 | { | |
1096 | m_log.DebugFormat( | 1632 | m_log.DebugFormat("{0}: CrossAgentToNewRegionAsync: prep failed. Resetting transfer state", LogHeader); |
1097 | "[ENTITY TRANSFER MODULE]: Crossing agent {0} {1} to {2}-{3} running version {4}", | 1633 | m_entityTransferStateMachine.ResetFromTransit(agent.UUID); |
1098 | agent.Firstname, agent.Lastname, neighbourx, neighboury, version); | 1634 | } |
1099 | |||
1100 | Scene m_scene = agent.Scene; | ||
1101 | 1635 | ||
1102 | if (!agent.ValidateAttachments()) | 1636 | if (!CrossAgentIntoNewRegionMain(agent, pos, neighbourRegion, isFlying, ctx)) |
1103 | m_log.DebugFormat( | 1637 | { |
1104 | "[ENTITY TRANSFER MODULE]: Failed validation of all attachments for region crossing of {0} from {1} to {2}. Continuing.", | 1638 | m_log.DebugFormat("{0}: CrossAgentToNewRegionAsync: cross main failed. Resetting transfer state", LogHeader); |
1105 | agent.Name, agent.Scene.RegionInfo.RegionName, neighbourRegion.RegionName); | 1639 | m_entityTransferStateMachine.ResetFromTransit(agent.UUID); |
1640 | } | ||
1106 | 1641 | ||
1107 | pos = pos + agent.Velocity; | 1642 | CrossAgentToNewRegionPost(agent, pos, neighbourRegion, isFlying, ctx); |
1108 | Vector3 vel2 = new Vector3(agent.Velocity.X, agent.Velocity.Y, 0); | 1643 | } |
1644 | catch (Exception e) | ||
1645 | { | ||
1646 | m_log.Error(string.Format("{0}: CrossAgentToNewRegionAsync: failed with exception ", LogHeader), e); | ||
1647 | } | ||
1109 | 1648 | ||
1110 | agent.RemoveFromPhysicalScene(); | 1649 | return agent; |
1650 | } | ||
1111 | 1651 | ||
1652 | public bool CrossAgentIntoNewRegionMain(ScenePresence agent, Vector3 pos, GridRegion neighbourRegion, bool isFlying, EntityTransferContext ctx) | ||
1653 | { | ||
1654 | try | ||
1655 | { | ||
1112 | AgentData cAgent = new AgentData(); | 1656 | AgentData cAgent = new AgentData(); |
1113 | agent.CopyTo(cAgent); | 1657 | agent.CopyTo(cAgent); |
1658 | if (ctx.OutboundVersion < 0.5f) | ||
1659 | cAgent.Appearance.PackLegacyWearables = true; | ||
1114 | cAgent.Position = pos; | 1660 | cAgent.Position = pos; |
1661 | |||
1115 | if (isFlying) | 1662 | if (isFlying) |
1116 | cAgent.ControlFlags |= (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY; | 1663 | cAgent.ControlFlags |= (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY; |
1117 | 1664 | ||
@@ -1121,102 +1668,121 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1121 | // Beyond this point, extra cleanup is needed beyond removing transit state | 1668 | // Beyond this point, extra cleanup is needed beyond removing transit state |
1122 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.Transferring); | 1669 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.Transferring); |
1123 | 1670 | ||
1124 | if (!m_scene.SimulationService.UpdateAgent(neighbourRegion, cAgent)) | 1671 | if (!agent.Scene.SimulationService.UpdateAgent(neighbourRegion, cAgent)) |
1125 | { | 1672 | { |
1126 | // region doesn't take it | 1673 | // region doesn't take it |
1127 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp); | 1674 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp); |
1128 | 1675 | ||
1676 | m_log.WarnFormat( | ||
1677 | "[ENTITY TRANSFER MODULE]: Region {0} would not accept update for agent {1} on cross attempt. Returning to original region.", | ||
1678 | neighbourRegion.RegionName, agent.Name); | ||
1679 | |||
1129 | ReInstantiateScripts(agent); | 1680 | ReInstantiateScripts(agent); |
1130 | agent.AddToPhysicalScene(isFlying); | 1681 | agent.AddToPhysicalScene(isFlying); |
1131 | m_entityTransferStateMachine.ResetFromTransit(agent.UUID); | ||
1132 | 1682 | ||
1133 | return agent; | 1683 | return false; |
1134 | } | 1684 | } |
1135 | 1685 | ||
1136 | //AgentCircuitData circuitdata = m_controllingClient.RequestClientInfo(); | 1686 | } |
1137 | agent.ControllingClient.RequestClientInfo(); | 1687 | catch (Exception e) |
1688 | { | ||
1689 | m_log.ErrorFormat( | ||
1690 | "[ENTITY TRANSFER MODULE]: Problem crossing user {0} to new region {1} from {2}. Exception {3}{4}", | ||
1691 | agent.Name, neighbourRegion.RegionName, agent.Scene.RegionInfo.RegionName, e.Message, e.StackTrace); | ||
1692 | |||
1693 | // TODO: Might be worth attempting other restoration here such as reinstantiation of scripts, etc. | ||
1694 | return false; | ||
1695 | } | ||
1138 | 1696 | ||
1139 | //m_log.Debug("BEFORE CROSS"); | 1697 | return true; |
1140 | //Scene.DumpChildrenSeeds(UUID); | 1698 | } |
1141 | //DumpKnownRegions(); | ||
1142 | string agentcaps; | ||
1143 | if (!agent.KnownRegions.TryGetValue(neighbourRegion.RegionHandle, out agentcaps)) | ||
1144 | { | ||
1145 | m_log.ErrorFormat("[ENTITY TRANSFER MODULE]: No ENTITY TRANSFER MODULE information for region handle {0}, exiting CrossToNewRegion.", | ||
1146 | neighbourRegion.RegionHandle); | ||
1147 | return agent; | ||
1148 | } | ||
1149 | // No turning back | ||
1150 | agent.IsChildAgent = true; | ||
1151 | 1699 | ||
1152 | string capsPath = neighbourRegion.ServerURI + CapsUtil.GetCapsSeedPath(agentcaps); | 1700 | public void CrossAgentToNewRegionPost(ScenePresence agent, Vector3 pos, GridRegion neighbourRegion, |
1701 | bool isFlying, EntityTransferContext ctx) | ||
1702 | { | ||
1703 | agent.ControllingClient.RequestClientInfo(); | ||
1153 | 1704 | ||
1154 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} to client {1}", capsPath, agent.UUID); | 1705 | string agentcaps; |
1706 | if (!agent.KnownRegions.TryGetValue(neighbourRegion.RegionHandle, out agentcaps)) | ||
1707 | { | ||
1708 | m_log.ErrorFormat("[ENTITY TRANSFER MODULE]: No ENTITY TRANSFER MODULE information for region handle {0}, exiting CrossToNewRegion.", | ||
1709 | neighbourRegion.RegionHandle); | ||
1710 | return; | ||
1711 | } | ||
1155 | 1712 | ||
1156 | if (m_eqModule != null) | 1713 | // No turning back |
1157 | { | 1714 | agent.IsChildAgent = true; |
1158 | m_eqModule.CrossRegion( | 1715 | |
1159 | neighbourHandle, pos, vel2 /* agent.Velocity */, neighbourRegion.ExternalEndPoint, | 1716 | string capsPath = neighbourRegion.ServerURI + CapsUtil.GetCapsSeedPath(agentcaps); |
1160 | capsPath, agent.UUID, agent.ControllingClient.SessionId); | ||
1161 | } | ||
1162 | else | ||
1163 | { | ||
1164 | agent.ControllingClient.CrossRegion(neighbourHandle, pos, agent.Velocity, neighbourRegion.ExternalEndPoint, | ||
1165 | capsPath); | ||
1166 | } | ||
1167 | 1717 | ||
1168 | // SUCCESS! | 1718 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} to client {1}", capsPath, agent.UUID); |
1169 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.ReceivedAtDestination); | ||
1170 | 1719 | ||
1171 | // Unlike a teleport, here we do not wait for the destination region to confirm the receipt. | 1720 | Vector3 vel2 = new Vector3(agent.Velocity.X, agent.Velocity.Y, 0); |
1172 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp); | ||
1173 | 1721 | ||
1174 | agent.MakeChildAgent(); | 1722 | if (m_eqModule != null) |
1723 | { | ||
1724 | m_eqModule.CrossRegion( | ||
1725 | neighbourRegion.RegionHandle, pos + agent.Velocity, vel2 /* agent.Velocity */, | ||
1726 | neighbourRegion.ExternalEndPoint, | ||
1727 | capsPath, agent.UUID, agent.ControllingClient.SessionId, | ||
1728 | neighbourRegion.RegionSizeX, neighbourRegion.RegionSizeY); | ||
1729 | } | ||
1730 | else | ||
1731 | { | ||
1732 | m_log.ErrorFormat("{0} Using old CrossRegion packet. Varregion will not work!!", LogHeader); | ||
1733 | agent.ControllingClient.CrossRegion(neighbourRegion.RegionHandle, pos + agent.Velocity, agent.Velocity, neighbourRegion.ExternalEndPoint, | ||
1734 | capsPath); | ||
1735 | } | ||
1175 | 1736 | ||
1176 | // FIXME: Possibly this should occur lower down after other commands to close other agents, | 1737 | // SUCCESS! |
1177 | // but not sure yet what the side effects would be. | 1738 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.ReceivedAtDestination); |
1178 | m_entityTransferStateMachine.ResetFromTransit(agent.UUID); | ||
1179 | 1739 | ||
1180 | // now we have a child agent in this region. Request all interesting data about other (root) agents | 1740 | // Unlike a teleport, here we do not wait for the destination region to confirm the receipt. |
1181 | agent.SendOtherAgentsAvatarDataToMe(); | 1741 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp); |
1182 | agent.SendOtherAgentsAppearanceToMe(); | ||
1183 | 1742 | ||
1184 | // Backwards compatibility. Best effort | 1743 | agent.MakeChildAgent(); |
1185 | if (version == "Unknown" || version == string.Empty) | ||
1186 | { | ||
1187 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: neighbor with old version, passing attachments one by one..."); | ||
1188 | Thread.Sleep(3000); // wait a little now that we're not waiting for the callback | ||
1189 | CrossAttachmentsIntoNewRegion(neighbourRegion, agent, true); | ||
1190 | } | ||
1191 | 1744 | ||
1192 | // Next, let's close the child agent connections that are too far away. | 1745 | // FIXME: Possibly this should occur lower down after other commands to close other agents, |
1193 | agent.CloseChildAgents(neighbourx, neighboury); | 1746 | // but not sure yet what the side effects would be. |
1747 | m_entityTransferStateMachine.ResetFromTransit(agent.UUID); | ||
1194 | 1748 | ||
1195 | AgentHasMovedAway(agent, false); | 1749 | // now we have a child agent in this region. Request all interesting data about other (root) agents |
1750 | agent.SendOtherAgentsAvatarDataToClient(); | ||
1751 | agent.SendOtherAgentsAppearanceToClient(); | ||
1196 | 1752 | ||
1197 | // // the user may change their profile information in other region, | 1753 | // TODO: Check since what version this wasn't needed anymore. May be as old as 0.6 |
1198 | // // so the userinfo in UserProfileCache is not reliable any more, delete it | 1754 | /* |
1199 | // // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE! | 1755 | // Backwards compatibility. Best effort |
1200 | // if (agent.Scene.NeedSceneCacheClear(agent.UUID)) | 1756 | if (version == 0f) |
1201 | // { | ||
1202 | // m_log.DebugFormat( | ||
1203 | // "[ENTITY TRANSFER MODULE]: User {0} is going to another region", agent.UUID); | ||
1204 | // } | ||
1205 | |||
1206 | //m_log.Debug("AFTER CROSS"); | ||
1207 | //Scene.DumpChildrenSeeds(UUID); | ||
1208 | //DumpKnownRegions(); | ||
1209 | } | ||
1210 | catch (Exception e) | ||
1211 | { | 1757 | { |
1212 | m_log.ErrorFormat( | 1758 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: neighbor with old version, passing attachments one by one..."); |
1213 | "[ENTITY TRANSFER MODULE]: Problem crossing user {0} to new region {1} from {2}. Exception {3}{4}", | 1759 | Thread.Sleep(3000); // wait a little now that we're not waiting for the callback |
1214 | agent.Name, neighbourRegion.RegionName, agent.Scene.RegionInfo.RegionName, e.Message, e.StackTrace); | 1760 | CrossAttachmentsIntoNewRegion(neighbourRegion, agent, true); |
1215 | |||
1216 | // TODO: Might be worth attempting other restoration here such as reinstantiation of scripts, etc. | ||
1217 | } | 1761 | } |
1762 | */ | ||
1763 | // Next, let's close the child agent connections that are too far away. | ||
1764 | uint neighbourx; | ||
1765 | uint neighboury; | ||
1766 | Util.RegionHandleToRegionLoc(neighbourRegion.RegionHandle, out neighbourx, out neighboury); | ||
1218 | 1767 | ||
1219 | return agent; | 1768 | agent.CloseChildAgents(neighbourx, neighboury); |
1769 | |||
1770 | AgentHasMovedAway(agent, false); | ||
1771 | |||
1772 | // the user may change their profile information in other region, | ||
1773 | // so the userinfo in UserProfileCache is not reliable any more, delete it | ||
1774 | // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE! | ||
1775 | // if (agent.Scene.NeedSceneCacheClear(agent.UUID)) | ||
1776 | // { | ||
1777 | // m_log.DebugFormat( | ||
1778 | // "[ENTITY TRANSFER MODULE]: User {0} is going to another region", agent.UUID); | ||
1779 | // } | ||
1780 | |||
1781 | //m_log.Debug("AFTER CROSS"); | ||
1782 | //Scene.DumpChildrenSeeds(UUID); | ||
1783 | //DumpKnownRegions(); | ||
1784 | |||
1785 | return; | ||
1220 | } | 1786 | } |
1221 | 1787 | ||
1222 | private void CrossAgentToNewRegionCompleted(IAsyncResult iar) | 1788 | private void CrossAgentToNewRegionCompleted(IAsyncResult iar) |
@@ -1256,7 +1822,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1256 | agent.InventoryFolder = UUID.Zero; | 1822 | agent.InventoryFolder = UUID.Zero; |
1257 | agent.startpos = new Vector3(128, 128, 70); | 1823 | agent.startpos = new Vector3(128, 128, 70); |
1258 | agent.child = true; | 1824 | agent.child = true; |
1259 | agent.Appearance = sp.Appearance; | 1825 | agent.Appearance = new AvatarAppearance(); |
1826 | agent.Appearance.PackLegacyWearables = true; | ||
1260 | agent.CapsPath = CapsUtil.GetRandomCapsObjectPath(); | 1827 | agent.CapsPath = CapsUtil.GetRandomCapsObjectPath(); |
1261 | 1828 | ||
1262 | agent.ChildrenCapSeeds = new Dictionary<ulong, string>(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID)); | 1829 | agent.ChildrenCapSeeds = new Dictionary<ulong, string>(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID)); |
@@ -1270,7 +1837,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1270 | //foreach (ulong h in agent.ChildrenCapSeeds.Keys) | 1837 | //foreach (ulong h in agent.ChildrenCapSeeds.Keys) |
1271 | // m_log.DebugFormat("[XXX] --> {0}", h); | 1838 | // m_log.DebugFormat("[XXX] --> {0}", h); |
1272 | //m_log.DebugFormat("[XXX] Adding {0}", region.RegionHandle); | 1839 | //m_log.DebugFormat("[XXX] Adding {0}", region.RegionHandle); |
1273 | agent.ChildrenCapSeeds.Add(region.RegionHandle, agent.CapsPath); | 1840 | if (agent.ChildrenCapSeeds.ContainsKey(region.RegionHandle)) |
1841 | { | ||
1842 | m_log.WarnFormat( | ||
1843 | "[ENTITY TRANSFER]: Overwriting caps seed {0} with {1} for region {2} (handle {3}) for {4} in {5}", | ||
1844 | agent.ChildrenCapSeeds[region.RegionHandle], agent.CapsPath, | ||
1845 | region.RegionName, region.RegionHandle, sp.Name, Scene.Name); | ||
1846 | } | ||
1847 | |||
1848 | agent.ChildrenCapSeeds[region.RegionHandle] = agent.CapsPath; | ||
1274 | 1849 | ||
1275 | if (sp.Scene.CapsModule != null) | 1850 | if (sp.Scene.CapsModule != null) |
1276 | { | 1851 | { |
@@ -1287,10 +1862,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1287 | agent.Id0 = currentAgentCircuit.Id0; | 1862 | agent.Id0 = currentAgentCircuit.Id0; |
1288 | } | 1863 | } |
1289 | 1864 | ||
1290 | InformClientOfNeighbourDelegate d = InformClientOfNeighbourAsync; | 1865 | IPEndPoint external = region.ExternalEndPoint; |
1291 | d.BeginInvoke(sp, agent, region, region.ExternalEndPoint, true, | 1866 | if (external != null) |
1867 | { | ||
1868 | InformClientOfNeighbourDelegate d = InformClientOfNeighbourAsync; | ||
1869 | d.BeginInvoke(sp, agent, region, external, true, | ||
1292 | InformClientOfNeighbourCompleted, | 1870 | InformClientOfNeighbourCompleted, |
1293 | d); | 1871 | d); |
1872 | } | ||
1294 | } | 1873 | } |
1295 | #endregion | 1874 | #endregion |
1296 | 1875 | ||
@@ -1310,7 +1889,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1310 | 1889 | ||
1311 | if (m_regionInfo != null) | 1890 | if (m_regionInfo != null) |
1312 | { | 1891 | { |
1313 | neighbours = RequestNeighbours(sp, m_regionInfo.RegionLocX, m_regionInfo.RegionLocY); | 1892 | neighbours = GetNeighbours(sp, m_regionInfo.RegionLocX, m_regionInfo.RegionLocY); |
1314 | } | 1893 | } |
1315 | else | 1894 | else |
1316 | { | 1895 | { |
@@ -1336,10 +1915,10 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1336 | List<ulong> newRegions = NewNeighbours(neighbourHandles, previousRegionNeighbourHandles); | 1915 | List<ulong> newRegions = NewNeighbours(neighbourHandles, previousRegionNeighbourHandles); |
1337 | List<ulong> oldRegions = OldNeighbours(neighbourHandles, previousRegionNeighbourHandles); | 1916 | List<ulong> oldRegions = OldNeighbours(neighbourHandles, previousRegionNeighbourHandles); |
1338 | 1917 | ||
1339 | //Dump("Current Neighbors", neighbourHandles); | 1918 | // Dump("Current Neighbors", neighbourHandles); |
1340 | //Dump("Previous Neighbours", previousRegionNeighbourHandles); | 1919 | // Dump("Previous Neighbours", previousRegionNeighbourHandles); |
1341 | //Dump("New Neighbours", newRegions); | 1920 | // Dump("New Neighbours", newRegions); |
1342 | //Dump("Old Neighbours", oldRegions); | 1921 | // Dump("Old Neighbours", oldRegions); |
1343 | 1922 | ||
1344 | /// Update the scene presence's known regions here on this region | 1923 | /// Update the scene presence's known regions here on this region |
1345 | sp.DropOldNeighbours(oldRegions); | 1924 | sp.DropOldNeighbours(oldRegions); |
@@ -1347,8 +1926,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1347 | /// Collect as many seeds as possible | 1926 | /// Collect as many seeds as possible |
1348 | Dictionary<ulong, string> seeds; | 1927 | Dictionary<ulong, string> seeds; |
1349 | if (sp.Scene.CapsModule != null) | 1928 | if (sp.Scene.CapsModule != null) |
1350 | seeds | 1929 | seeds = new Dictionary<ulong, string>(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID)); |
1351 | = new Dictionary<ulong, string>(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID)); | ||
1352 | else | 1930 | else |
1353 | seeds = new Dictionary<ulong, string>(); | 1931 | seeds = new Dictionary<ulong, string>(); |
1354 | 1932 | ||
@@ -1368,7 +1946,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1368 | agent.InventoryFolder = UUID.Zero; | 1946 | agent.InventoryFolder = UUID.Zero; |
1369 | agent.startpos = sp.AbsolutePosition + CalculateOffset(sp, neighbour); | 1947 | agent.startpos = sp.AbsolutePosition + CalculateOffset(sp, neighbour); |
1370 | agent.child = true; | 1948 | agent.child = true; |
1371 | agent.Appearance = sp.Appearance; | 1949 | agent.Appearance = new AvatarAppearance(); |
1950 | agent.Appearance.PackLegacyWearables = true; | ||
1372 | if (currentAgentCircuit != null) | 1951 | if (currentAgentCircuit != null) |
1373 | { | 1952 | { |
1374 | agent.ServiceURLs = currentAgentCircuit.ServiceURLs; | 1953 | agent.ServiceURLs = currentAgentCircuit.ServiceURLs; |
@@ -1418,6 +1997,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1418 | newAgent = true; | 1997 | newAgent = true; |
1419 | else | 1998 | else |
1420 | newAgent = false; | 1999 | newAgent = false; |
2000 | // continue; | ||
1421 | 2001 | ||
1422 | if (neighbour.RegionHandle != sp.Scene.RegionInfo.RegionHandle) | 2002 | if (neighbour.RegionHandle != sp.Scene.RegionInfo.RegionHandle) |
1423 | { | 2003 | { |
@@ -1464,15 +2044,195 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1464 | } | 2044 | } |
1465 | } | 2045 | } |
1466 | 2046 | ||
2047 | // Computes the difference between two region bases. | ||
2048 | // Returns a vector of world coordinates (meters) from base of first region to the second. | ||
2049 | // The first region is the home region of the passed scene presence. | ||
1467 | Vector3 CalculateOffset(ScenePresence sp, GridRegion neighbour) | 2050 | Vector3 CalculateOffset(ScenePresence sp, GridRegion neighbour) |
1468 | { | 2051 | { |
1469 | int rRegionX = (int)sp.Scene.RegionInfo.RegionLocX; | 2052 | /* |
1470 | int rRegionY = (int)sp.Scene.RegionInfo.RegionLocY; | 2053 | int rRegionX = (int)sp.Scene.RegionInfo.LegacyRegionLocX; |
2054 | int rRegionY = (int)sp.Scene.RegionInfo.LegacyRegionLocY; | ||
1471 | int tRegionX = neighbour.RegionLocX / (int)Constants.RegionSize; | 2055 | int tRegionX = neighbour.RegionLocX / (int)Constants.RegionSize; |
1472 | int tRegionY = neighbour.RegionLocY / (int)Constants.RegionSize; | 2056 | int tRegionY = neighbour.RegionLocY / (int)Constants.RegionSize; |
1473 | int shiftx = (rRegionX - tRegionX) * (int)Constants.RegionSize; | 2057 | int shiftx = (rRegionX - tRegionX) * (int)Constants.RegionSize; |
1474 | int shifty = (rRegionY - tRegionY) * (int)Constants.RegionSize; | 2058 | int shifty = (rRegionY - tRegionY) * (int)Constants.RegionSize; |
1475 | return new Vector3(shiftx, shifty, 0f); | 2059 | return new Vector3(shiftx, shifty, 0f); |
2060 | */ | ||
2061 | return new Vector3( sp.Scene.RegionInfo.WorldLocX - neighbour.RegionLocX, | ||
2062 | sp.Scene.RegionInfo.WorldLocY - neighbour.RegionLocY, | ||
2063 | 0f); | ||
2064 | } | ||
2065 | |||
2066 | public GridRegion GetRegionContainingWorldLocation(IGridService pGridService, UUID pScopeID, double px, double py) | ||
2067 | { | ||
2068 | // Since we don't know how big the regions could be, we have to search a very large area | ||
2069 | // to find possible regions. | ||
2070 | return GetRegionContainingWorldLocation(pGridService, pScopeID, px, py, Constants.MaximumRegionSize); | ||
2071 | } | ||
2072 | |||
2073 | #region NotFoundLocationCache class | ||
2074 | // A collection of not found locations to make future lookups 'not found' lookups quick. | ||
2075 | // A simple expiring cache that keeps not found locations for some number of seconds. | ||
2076 | // A 'not found' location is presumed to be anywhere in the minimum sized region that | ||
2077 | // contains that point. A conservitive estimate. | ||
2078 | private class NotFoundLocationCache | ||
2079 | { | ||
2080 | private struct NotFoundLocation | ||
2081 | { | ||
2082 | public double minX, maxX, minY, maxY; | ||
2083 | public DateTime expireTime; | ||
2084 | } | ||
2085 | private List<NotFoundLocation> m_notFoundLocations = new List<NotFoundLocation>(); | ||
2086 | public NotFoundLocationCache() | ||
2087 | { | ||
2088 | } | ||
2089 | // Add an area to the list of 'not found' places. The area is the snapped region | ||
2090 | // area around the added point. | ||
2091 | public void Add(double pX, double pY) | ||
2092 | { | ||
2093 | lock (m_notFoundLocations) | ||
2094 | { | ||
2095 | if (!LockedContains(pX, pY)) | ||
2096 | { | ||
2097 | NotFoundLocation nfl = new NotFoundLocation(); | ||
2098 | // A not found location is not found for at least a whole region sized area | ||
2099 | nfl.minX = pX - (pX % (double)Constants.RegionSize); | ||
2100 | nfl.minY = pY - (pY % (double)Constants.RegionSize); | ||
2101 | nfl.maxX = nfl.minX + (double)Constants.RegionSize; | ||
2102 | nfl.maxY = nfl.minY + (double)Constants.RegionSize; | ||
2103 | nfl.expireTime = DateTime.Now + TimeSpan.FromSeconds(30); | ||
2104 | m_notFoundLocations.Add(nfl); | ||
2105 | } | ||
2106 | } | ||
2107 | |||
2108 | } | ||
2109 | // Test to see of this point is in any of the 'not found' areas. | ||
2110 | // Return 'true' if the point is found inside the 'not found' areas. | ||
2111 | public bool Contains(double pX, double pY) | ||
2112 | { | ||
2113 | bool ret = false; | ||
2114 | lock (m_notFoundLocations) | ||
2115 | ret = LockedContains(pX, pY); | ||
2116 | return ret; | ||
2117 | } | ||
2118 | private bool LockedContains(double pX, double pY) | ||
2119 | { | ||
2120 | bool ret = false; | ||
2121 | this.DoExpiration(); | ||
2122 | foreach (NotFoundLocation nfl in m_notFoundLocations) | ||
2123 | { | ||
2124 | if (pX >= nfl.minX && pX < nfl.maxX && pY >= nfl.minY && pY < nfl.maxY) | ||
2125 | { | ||
2126 | ret = true; | ||
2127 | break; | ||
2128 | } | ||
2129 | } | ||
2130 | return ret; | ||
2131 | } | ||
2132 | private void DoExpiration() | ||
2133 | { | ||
2134 | List<NotFoundLocation> m_toRemove = null; | ||
2135 | DateTime now = DateTime.Now; | ||
2136 | foreach (NotFoundLocation nfl in m_notFoundLocations) | ||
2137 | { | ||
2138 | if (nfl.expireTime < now) | ||
2139 | { | ||
2140 | if (m_toRemove == null) | ||
2141 | m_toRemove = new List<NotFoundLocation>(); | ||
2142 | m_toRemove.Add(nfl); | ||
2143 | } | ||
2144 | } | ||
2145 | if (m_toRemove != null) | ||
2146 | { | ||
2147 | foreach (NotFoundLocation nfl in m_toRemove) | ||
2148 | m_notFoundLocations.Remove(nfl); | ||
2149 | m_toRemove.Clear(); | ||
2150 | } | ||
2151 | } | ||
2152 | } | ||
2153 | #endregion // NotFoundLocationCache class | ||
2154 | private NotFoundLocationCache m_notFoundLocationCache = new NotFoundLocationCache(); | ||
2155 | |||
2156 | // Given a world position (fractional meter coordinate), get the GridRegion info for | ||
2157 | // the region containing that point. | ||
2158 | // Someday this should be a method on GridService. | ||
2159 | // 'pSizeHint' is the size of the source region but since the destination point can be anywhere | ||
2160 | // the size of the target region is unknown thus the search area might have to be very large. | ||
2161 | // Return 'null' if no such region exists. | ||
2162 | public GridRegion GetRegionContainingWorldLocation(IGridService pGridService, UUID pScopeID, | ||
2163 | double px, double py, uint pSizeHint) | ||
2164 | { | ||
2165 | m_log.DebugFormat("{0} GetRegionContainingWorldLocation: query, loc=<{1},{2}>", LogHeader, px, py); | ||
2166 | GridRegion ret = null; | ||
2167 | const double fudge = 2.0; | ||
2168 | |||
2169 | // One problem with this routine is negative results. That is, this can be called lots of times | ||
2170 | // for regions that don't exist. m_notFoundLocationCache remembers 'not found' results so they | ||
2171 | // will be quick 'not found's next time. | ||
2172 | // NotFoundLocationCache is an expiring cache so it will eventually forget about 'not found' and | ||
2173 | // thus re-ask the GridService about the location. | ||
2174 | if (m_notFoundLocationCache.Contains(px, py)) | ||
2175 | { | ||
2176 | m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Not found via cache. loc=<{1},{2}>", LogHeader, px, py); | ||
2177 | return null; | ||
2178 | } | ||
2179 | |||
2180 | // As an optimization, since most regions will be legacy sized regions (256x256), first try to get | ||
2181 | // the region at the appropriate legacy region location. | ||
2182 | uint possibleX = (uint)Math.Floor(px); | ||
2183 | possibleX -= possibleX % Constants.RegionSize; | ||
2184 | uint possibleY = (uint)Math.Floor(py); | ||
2185 | possibleY -= possibleY % Constants.RegionSize; | ||
2186 | ret = pGridService.GetRegionByPosition(pScopeID, (int)possibleX, (int)possibleY); | ||
2187 | if (ret != null) | ||
2188 | { | ||
2189 | m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Found region using legacy size. rloc=<{1},{2}>. Rname={3}", | ||
2190 | LogHeader, possibleX, possibleY, ret.RegionName); | ||
2191 | } | ||
2192 | |||
2193 | if (ret == null) | ||
2194 | { | ||
2195 | // If the simple lookup failed, search the larger area for a region that contains this point | ||
2196 | double range = (double)pSizeHint + fudge; | ||
2197 | while (ret == null && range <= (Constants.MaximumRegionSize + Constants.RegionSize)) | ||
2198 | { | ||
2199 | // Get from the grid service a list of regions that might contain this point. | ||
2200 | // The region origin will be in the zero direction so only subtract the range. | ||
2201 | List<GridRegion> possibleRegions = pGridService.GetRegionRange(pScopeID, | ||
2202 | (int)(px - range), (int)(px), | ||
2203 | (int)(py - range), (int)(py)); | ||
2204 | m_log.DebugFormat("{0} GetRegionContainingWorldLocation: possibleRegions cnt={1}, range={2}", | ||
2205 | LogHeader, possibleRegions.Count, range); | ||
2206 | if (possibleRegions != null && possibleRegions.Count > 0) | ||
2207 | { | ||
2208 | // If we found some regions, check to see if the point is within | ||
2209 | foreach (GridRegion gr in possibleRegions) | ||
2210 | { | ||
2211 | m_log.DebugFormat("{0} GetRegionContainingWorldLocation: possibleRegion nm={1}, regionLoc=<{2},{3}>, regionSize=<{4},{5}>", | ||
2212 | LogHeader, gr.RegionName, gr.RegionLocX, gr.RegionLocY, gr.RegionSizeX, gr.RegionSizeY); | ||
2213 | if (px >= (double)gr.RegionLocX && px < (double)(gr.RegionLocX + gr.RegionSizeX) | ||
2214 | && py >= (double)gr.RegionLocY && py < (double)(gr.RegionLocY + gr.RegionSizeY)) | ||
2215 | { | ||
2216 | // Found a region that contains the point | ||
2217 | ret = gr; | ||
2218 | m_log.DebugFormat("{0} GetRegionContainingWorldLocation: found. RegionName={1}", LogHeader, ret.RegionName); | ||
2219 | break; | ||
2220 | } | ||
2221 | } | ||
2222 | } | ||
2223 | // Larger search area for next time around if not found | ||
2224 | range *= 2; | ||
2225 | } | ||
2226 | } | ||
2227 | |||
2228 | if (ret == null) | ||
2229 | { | ||
2230 | // remember this location was not found so we can quickly not find it next time | ||
2231 | m_notFoundLocationCache.Add(px, py); | ||
2232 | m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Not found. Remembering loc=<{1},{2}>", LogHeader, px, py); | ||
2233 | } | ||
2234 | |||
2235 | return ret; | ||
1476 | } | 2236 | } |
1477 | 2237 | ||
1478 | private void InformClientOfNeighbourCompleted(IAsyncResult iar) | 2238 | private void InformClientOfNeighbourCompleted(IAsyncResult iar) |
@@ -1500,7 +2260,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1500 | Thread.Sleep(500); | 2260 | Thread.Sleep(500); |
1501 | 2261 | ||
1502 | Scene scene = sp.Scene; | 2262 | Scene scene = sp.Scene; |
1503 | 2263 | ||
1504 | m_log.DebugFormat( | 2264 | m_log.DebugFormat( |
1505 | "[ENTITY TRANSFER MODULE]: Informing {0} {1} about neighbour {2} {3} at ({4},{5})", | 2265 | "[ENTITY TRANSFER MODULE]: Informing {0} {1} about neighbour {2} {3} at ({4},{5})", |
1506 | sp.Name, sp.UUID, reg.RegionName, endPoint, reg.RegionCoordX, reg.RegionCoordY); | 2266 | sp.Name, sp.UUID, reg.RegionName, endPoint, reg.RegionCoordX, reg.RegionCoordY); |
@@ -1509,7 +2269,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1509 | 2269 | ||
1510 | string reason = String.Empty; | 2270 | string reason = String.Empty; |
1511 | 2271 | ||
1512 | bool regionAccepted = scene.SimulationService.CreateAgent(reg, a, (uint)TeleportFlags.Default, out reason); | 2272 | bool regionAccepted = scene.SimulationService.CreateAgent(null, reg, a, (uint)TeleportFlags.Default, out reason); |
1513 | 2273 | ||
1514 | if (regionAccepted && newAgent) | 2274 | if (regionAccepted && newAgent) |
1515 | { | 2275 | { |
@@ -1523,12 +2283,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1523 | } | 2283 | } |
1524 | #endregion | 2284 | #endregion |
1525 | 2285 | ||
1526 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: {0} is sending {1} EnableSimulator for neighbour region {2} @ {3} " + | 2286 | m_log.DebugFormat("{0} {1} is sending {2} EnableSimulator for neighbour region {3}(loc=<{4},{5}>,siz=<{6},{7}>) " + |
1527 | "and EstablishAgentCommunication with seed cap {4}", | 2287 | "and EstablishAgentCommunication with seed cap {8}", LogHeader, |
1528 | scene.RegionInfo.RegionName, sp.Name, reg.RegionName, reg.RegionHandle, capsPath); | 2288 | scene.RegionInfo.RegionName, sp.Name, |
2289 | reg.RegionName, reg.RegionLocX, reg.RegionLocY, reg.RegionSizeX, reg.RegionSizeY , capsPath); | ||
1529 | 2290 | ||
1530 | m_eqModule.EnableSimulator(reg.RegionHandle, endPoint, sp.UUID); | 2291 | m_eqModule.EnableSimulator(reg.RegionHandle, endPoint, sp.UUID, reg.RegionSizeX, reg.RegionSizeY); |
1531 | m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath); | 2292 | m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath, reg.RegionHandle, reg.RegionSizeX, reg.RegionSizeY); |
1532 | } | 2293 | } |
1533 | else | 2294 | else |
1534 | { | 2295 | { |
@@ -1546,68 +2307,86 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1546 | } | 2307 | } |
1547 | 2308 | ||
1548 | /// <summary> | 2309 | /// <summary> |
1549 | /// Return the list of regions that are considered to be neighbours to the given scene. | 2310 | /// Gets the range considered in view of this megaregion (assuming this is a megaregion). |
2311 | /// </summary> | ||
2312 | /// <remarks>Expressed in 256m units</remarks> | ||
2313 | /// <param name='swCorner'></param> | ||
2314 | /// <param name='neCorner'></param> | ||
2315 | private void GetMegaregionViewRange(out Vector2 swCorner, out Vector2 neCorner) | ||
2316 | { | ||
2317 | Vector2 extent = Vector2.Zero; | ||
2318 | |||
2319 | if (m_regionCombinerModule != null) | ||
2320 | { | ||
2321 | Vector2 megaRegionSize = m_regionCombinerModule.GetSizeOfMegaregion(Scene.RegionInfo.RegionID); | ||
2322 | extent.X = (float)Util.WorldToRegionLoc((uint)megaRegionSize.X); | ||
2323 | extent.Y = (float)Util.WorldToRegionLoc((uint)megaRegionSize.Y); | ||
2324 | } | ||
2325 | |||
2326 | swCorner.X = Scene.RegionInfo.RegionLocX - 1; | ||
2327 | swCorner.Y = Scene.RegionInfo.RegionLocY - 1; | ||
2328 | neCorner.X = Scene.RegionInfo.RegionLocX + extent.X; | ||
2329 | neCorner.Y = Scene.RegionInfo.RegionLocY + extent.Y; | ||
2330 | } | ||
2331 | |||
2332 | /// <summary> | ||
2333 | /// Return the list of online regions that are considered to be neighbours to the given scene. | ||
1550 | /// </summary> | 2334 | /// </summary> |
1551 | /// <param name="pScene"></param> | 2335 | /// <param name="avatar"></param> |
1552 | /// <param name="pRegionLocX"></param> | 2336 | /// <param name="pRegionLocX"></param> |
1553 | /// <param name="pRegionLocY"></param> | 2337 | /// <param name="pRegionLocY"></param> |
1554 | /// <returns></returns> | 2338 | /// <returns></returns> |
1555 | protected List<GridRegion> RequestNeighbours(ScenePresence avatar, uint pRegionLocX, uint pRegionLocY) | 2339 | protected List<GridRegion> GetNeighbours(ScenePresence avatar, uint pRegionLocX, uint pRegionLocY) |
1556 | { | 2340 | { |
1557 | Scene pScene = avatar.Scene; | 2341 | Scene pScene = avatar.Scene; |
1558 | RegionInfo m_regionInfo = pScene.RegionInfo; | 2342 | RegionInfo m_regionInfo = pScene.RegionInfo; |
1559 | 2343 | List<GridRegion> neighbours; | |
1560 | Border[] northBorders = pScene.NorthBorders.ToArray(); | ||
1561 | Border[] southBorders = pScene.SouthBorders.ToArray(); | ||
1562 | Border[] eastBorders = pScene.EastBorders.ToArray(); | ||
1563 | Border[] westBorders = pScene.WestBorders.ToArray(); | ||
1564 | 2344 | ||
1565 | // Leaving this as a "megaregions" computation vs "non-megaregions" computation; it isn't | 2345 | // Leaving this as a "megaregions" computation vs "non-megaregions" computation; it isn't |
1566 | // clear what should be done with a "far view" given that megaregions already extended the | 2346 | // clear what should be done with a "far view" given that megaregions already extended the |
1567 | // view to include everything in the megaregion | 2347 | // view to include everything in the megaregion |
1568 | if (northBorders.Length <= 1 && southBorders.Length <= 1 && eastBorders.Length <= 1 && westBorders.Length <= 1) | 2348 | if (m_regionCombinerModule == null || !m_regionCombinerModule.IsRootForMegaregion(Scene.RegionInfo.RegionID)) |
1569 | { | 2349 | { |
1570 | int dd = avatar.DrawDistance < Constants.RegionSize ? (int)Constants.RegionSize : (int)avatar.DrawDistance; | 2350 | // The area to check is as big as the current region. |
1571 | 2351 | // We presume all adjacent regions are the same size as this region. | |
1572 | int startX = (int)pRegionLocX * (int)Constants.RegionSize - dd + (int)(Constants.RegionSize/2); | 2352 | uint dd = Math.Max((uint)avatar.Scene.DefaultDrawDistance, |
1573 | int startY = (int)pRegionLocY * (int)Constants.RegionSize - dd + (int)(Constants.RegionSize/2); | 2353 | Math.Max(Scene.RegionInfo.RegionSizeX, Scene.RegionInfo.RegionSizeY)); |
1574 | 2354 | ||
1575 | int endX = (int)pRegionLocX * (int)Constants.RegionSize + dd + (int)(Constants.RegionSize/2); | 2355 | uint startX = Util.RegionToWorldLoc(pRegionLocX) - dd + Constants.RegionSize/2; |
1576 | int endY = (int)pRegionLocY * (int)Constants.RegionSize + dd + (int)(Constants.RegionSize/2); | 2356 | uint startY = Util.RegionToWorldLoc(pRegionLocY) - dd + Constants.RegionSize/2; |
1577 | 2357 | ||
1578 | List<GridRegion> neighbours = | 2358 | uint endX = Util.RegionToWorldLoc(pRegionLocX) + dd + Constants.RegionSize/2; |
1579 | avatar.Scene.GridService.GetRegionRange(m_regionInfo.ScopeID, startX, endX, startY, endY); | 2359 | uint endY = Util.RegionToWorldLoc(pRegionLocY) + dd + Constants.RegionSize/2; |
1580 | 2360 | ||
1581 | neighbours.RemoveAll(delegate(GridRegion r) { return r.RegionID == m_regionInfo.RegionID; }); | 2361 | neighbours |
1582 | return neighbours; | 2362 | = avatar.Scene.GridService.GetRegionRange( |
2363 | m_regionInfo.ScopeID, (int)startX, (int)endX, (int)startY, (int)endY); | ||
1583 | } | 2364 | } |
1584 | else | 2365 | else |
1585 | { | 2366 | { |
1586 | Vector2 extent = Vector2.Zero; | 2367 | Vector2 swCorner, neCorner; |
1587 | for (int i = 0; i < eastBorders.Length; i++) | 2368 | GetMegaregionViewRange(out swCorner, out neCorner); |
1588 | { | ||
1589 | extent.X = (eastBorders[i].BorderLine.Z > extent.X) ? eastBorders[i].BorderLine.Z : extent.X; | ||
1590 | } | ||
1591 | for (int i = 0; i < northBorders.Length; i++) | ||
1592 | { | ||
1593 | extent.Y = (northBorders[i].BorderLine.Z > extent.Y) ? northBorders[i].BorderLine.Z : extent.Y; | ||
1594 | } | ||
1595 | 2369 | ||
1596 | // Loss of fraction on purpose | 2370 | neighbours |
1597 | extent.X = ((int)extent.X / (int)Constants.RegionSize) + 1; | 2371 | = pScene.GridService.GetRegionRange( |
1598 | extent.Y = ((int)extent.Y / (int)Constants.RegionSize) + 1; | 2372 | m_regionInfo.ScopeID, |
1599 | 2373 | (int)Util.RegionToWorldLoc((uint)swCorner.X), (int)Util.RegionToWorldLoc((uint)neCorner.X), | |
1600 | int startX = (int)(pRegionLocX - 1) * (int)Constants.RegionSize; | 2374 | (int)Util.RegionToWorldLoc((uint)swCorner.Y), (int)Util.RegionToWorldLoc((uint)neCorner.Y)); |
1601 | int startY = (int)(pRegionLocY - 1) * (int)Constants.RegionSize; | 2375 | } |
1602 | 2376 | ||
1603 | int endX = ((int)pRegionLocX + (int)extent.X) * (int)Constants.RegionSize; | 2377 | // neighbours.ForEach( |
1604 | int endY = ((int)pRegionLocY + (int)extent.Y) * (int)Constants.RegionSize; | 2378 | // n => |
2379 | // m_log.DebugFormat( | ||
2380 | // "[ENTITY TRANSFER MODULE]: Region flags for {0} as seen by {1} are {2}", | ||
2381 | // n.RegionName, Scene.Name, n.RegionFlags != null ? n.RegionFlags.ToString() : "not present")); | ||
1605 | 2382 | ||
1606 | List<GridRegion> neighbours = pScene.GridService.GetRegionRange(m_regionInfo.ScopeID, startX, endX, startY, endY); | 2383 | // The r.RegionFlags == null check only needs to be made for simulators before 2015-01-14 (pre 0.8.1). |
1607 | neighbours.RemoveAll(delegate(GridRegion r) { return r.RegionID == m_regionInfo.RegionID; }); | 2384 | neighbours.RemoveAll( |
2385 | r => | ||
2386 | r.RegionID == m_regionInfo.RegionID | ||
2387 | || (r.RegionFlags != null && (r.RegionFlags & OpenSim.Framework.RegionFlags.RegionOnline) == 0)); | ||
1608 | 2388 | ||
1609 | return neighbours; | 2389 | return neighbours; |
1610 | } | ||
1611 | } | 2390 | } |
1612 | 2391 | ||
1613 | private List<ulong> NewNeighbours(List<ulong> currentNeighbours, List<ulong> previousNeighbours) | 2392 | private List<ulong> NewNeighbours(List<ulong> currentNeighbours, List<ulong> previousNeighbours) |
@@ -1665,10 +2444,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1665 | /// Move the given scene object into a new region depending on which region its absolute position has moved | 2444 | /// Move the given scene object into a new region depending on which region its absolute position has moved |
1666 | /// into. | 2445 | /// into. |
1667 | /// | 2446 | /// |
1668 | /// This method locates the new region handle and offsets the prim position for the new region | 2447 | /// Using the objects new world location, ask the grid service for a the new region and adjust the prim |
2448 | /// position to be relative to the new region. | ||
1669 | /// </summary> | 2449 | /// </summary> |
1670 | /// <param name="attemptedPosition">the attempted out of region position of the scene object</param> | ||
1671 | /// <param name="grp">the scene object that we're crossing</param> | 2450 | /// <param name="grp">the scene object that we're crossing</param> |
2451 | /// <param name="attemptedPosition">the attempted out of region position of the scene object. This position is | ||
2452 | /// relative to the region the object currently is in.</param> | ||
2453 | /// <param name="silent">if 'true', the deletion of the client from the region is not broadcast to the clients</param> | ||
1672 | public void Cross(SceneObjectGroup grp, Vector3 attemptedPosition, bool silent) | 2454 | public void Cross(SceneObjectGroup grp, Vector3 attemptedPosition, bool silent) |
1673 | { | 2455 | { |
1674 | if (grp == null) | 2456 | if (grp == null) |
@@ -1694,204 +2476,49 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1694 | return; | 2476 | return; |
1695 | } | 2477 | } |
1696 | 2478 | ||
1697 | int thisx = (int)scene.RegionInfo.RegionLocX; | 2479 | // Remember the old group position in case the region lookup fails so position can be restored. |
1698 | int thisy = (int)scene.RegionInfo.RegionLocY; | 2480 | Vector3 oldGroupPosition = grp.RootPart.GroupPosition; |
1699 | Vector3 EastCross = new Vector3(0.1f, 0, 0); | ||
1700 | Vector3 WestCross = new Vector3(-0.1f, 0, 0); | ||
1701 | Vector3 NorthCross = new Vector3(0, 0.1f, 0); | ||
1702 | Vector3 SouthCross = new Vector3(0, -0.1f, 0); | ||
1703 | |||
1704 | |||
1705 | // use this if no borders were crossed! | ||
1706 | ulong newRegionHandle | ||
1707 | = Util.UIntsToLong((uint)((thisx) * Constants.RegionSize), | ||
1708 | (uint)((thisy) * Constants.RegionSize)); | ||
1709 | |||
1710 | Vector3 pos = attemptedPosition; | ||
1711 | |||
1712 | int changeX = 1; | ||
1713 | int changeY = 1; | ||
1714 | |||
1715 | if (scene.TestBorderCross(attemptedPosition + WestCross, Cardinals.W)) | ||
1716 | { | ||
1717 | if (scene.TestBorderCross(attemptedPosition + SouthCross, Cardinals.S)) | ||
1718 | { | ||
1719 | |||
1720 | Border crossedBorderx = scene.GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W); | ||
1721 | |||
1722 | if (crossedBorderx.BorderLine.Z > 0) | ||
1723 | { | ||
1724 | pos.X = ((pos.X + crossedBorderx.BorderLine.Z)); | ||
1725 | changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize); | ||
1726 | } | ||
1727 | else | ||
1728 | pos.X = ((pos.X + Constants.RegionSize)); | ||
1729 | |||
1730 | Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); | ||
1731 | //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) | ||
1732 | |||
1733 | if (crossedBordery.BorderLine.Z > 0) | ||
1734 | { | ||
1735 | pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); | ||
1736 | changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); | ||
1737 | } | ||
1738 | else | ||
1739 | pos.Y = ((pos.Y + Constants.RegionSize)); | ||
1740 | |||
1741 | |||
1742 | |||
1743 | newRegionHandle | ||
1744 | = Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize), | ||
1745 | (uint)((thisy - changeY) * Constants.RegionSize)); | ||
1746 | // x - 1 | ||
1747 | // y - 1 | ||
1748 | } | ||
1749 | else if (scene.TestBorderCross(attemptedPosition + NorthCross, Cardinals.N)) | ||
1750 | { | ||
1751 | Border crossedBorderx = scene.GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W); | ||
1752 | |||
1753 | if (crossedBorderx.BorderLine.Z > 0) | ||
1754 | { | ||
1755 | pos.X = ((pos.X + crossedBorderx.BorderLine.Z)); | ||
1756 | changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize); | ||
1757 | } | ||
1758 | else | ||
1759 | pos.X = ((pos.X + Constants.RegionSize)); | ||
1760 | |||
1761 | |||
1762 | Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); | ||
1763 | //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) | ||
1764 | |||
1765 | if (crossedBordery.BorderLine.Z > 0) | ||
1766 | { | ||
1767 | pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); | ||
1768 | changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); | ||
1769 | } | ||
1770 | else | ||
1771 | pos.Y = ((pos.Y + Constants.RegionSize)); | ||
1772 | |||
1773 | newRegionHandle | ||
1774 | = Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize), | ||
1775 | (uint)((thisy + changeY) * Constants.RegionSize)); | ||
1776 | // x - 1 | ||
1777 | // y + 1 | ||
1778 | } | ||
1779 | else | ||
1780 | { | ||
1781 | Border crossedBorderx = scene.GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W); | ||
1782 | |||
1783 | if (crossedBorderx.BorderLine.Z > 0) | ||
1784 | { | ||
1785 | pos.X = ((pos.X + crossedBorderx.BorderLine.Z)); | ||
1786 | changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize); | ||
1787 | } | ||
1788 | else | ||
1789 | pos.X = ((pos.X + Constants.RegionSize)); | ||
1790 | |||
1791 | newRegionHandle | ||
1792 | = Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize), | ||
1793 | (uint)(thisy * Constants.RegionSize)); | ||
1794 | // x - 1 | ||
1795 | } | ||
1796 | } | ||
1797 | else if (scene.TestBorderCross(attemptedPosition + EastCross, Cardinals.E)) | ||
1798 | { | ||
1799 | if (scene.TestBorderCross(attemptedPosition + SouthCross, Cardinals.S)) | ||
1800 | { | ||
1801 | |||
1802 | pos.X = ((pos.X - Constants.RegionSize)); | ||
1803 | Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); | ||
1804 | //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) | ||
1805 | |||
1806 | if (crossedBordery.BorderLine.Z > 0) | ||
1807 | { | ||
1808 | pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); | ||
1809 | changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); | ||
1810 | } | ||
1811 | else | ||
1812 | pos.Y = ((pos.Y + Constants.RegionSize)); | ||
1813 | |||
1814 | 2481 | ||
1815 | newRegionHandle | 2482 | // Compute the absolute position of the object. |
1816 | = Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize), | 2483 | double objectWorldLocX = (double)scene.RegionInfo.WorldLocX + attemptedPosition.X; |
1817 | (uint)((thisy - changeY) * Constants.RegionSize)); | 2484 | double objectWorldLocY = (double)scene.RegionInfo.WorldLocY + attemptedPosition.Y; |
1818 | // x + 1 | ||
1819 | // y - 1 | ||
1820 | } | ||
1821 | else if (scene.TestBorderCross(attemptedPosition + NorthCross, Cardinals.N)) | ||
1822 | { | ||
1823 | pos.X = ((pos.X - Constants.RegionSize)); | ||
1824 | pos.Y = ((pos.Y - Constants.RegionSize)); | ||
1825 | newRegionHandle | ||
1826 | = Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize), | ||
1827 | (uint)((thisy + changeY) * Constants.RegionSize)); | ||
1828 | // x + 1 | ||
1829 | // y + 1 | ||
1830 | } | ||
1831 | else | ||
1832 | { | ||
1833 | pos.X = ((pos.X - Constants.RegionSize)); | ||
1834 | newRegionHandle | ||
1835 | = Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize), | ||
1836 | (uint)(thisy * Constants.RegionSize)); | ||
1837 | // x + 1 | ||
1838 | } | ||
1839 | } | ||
1840 | else if (scene.TestBorderCross(attemptedPosition + SouthCross, Cardinals.S)) | ||
1841 | { | ||
1842 | Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); | ||
1843 | //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) | ||
1844 | 2485 | ||
1845 | if (crossedBordery.BorderLine.Z > 0) | 2486 | // Ask the grid service for the region that contains the passed address |
1846 | { | 2487 | GridRegion destination = GetRegionContainingWorldLocation(scene.GridService, scene.RegionInfo.ScopeID, |
1847 | pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); | 2488 | objectWorldLocX, objectWorldLocY); |
1848 | changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); | ||
1849 | } | ||
1850 | else | ||
1851 | pos.Y = ((pos.Y + Constants.RegionSize)); | ||
1852 | 2489 | ||
1853 | newRegionHandle | 2490 | Vector3 pos = Vector3.Zero; |
1854 | = Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy - changeY) * Constants.RegionSize)); | 2491 | if (destination != null) |
1855 | // y - 1 | ||
1856 | } | ||
1857 | else if (scene.TestBorderCross(attemptedPosition + NorthCross, Cardinals.N)) | ||
1858 | { | 2492 | { |
1859 | 2493 | // Adjust the object's relative position from the old region (attemptedPosition) | |
1860 | pos.Y = ((pos.Y - Constants.RegionSize)); | 2494 | // to be relative to the new region (pos). |
1861 | newRegionHandle | 2495 | pos = new Vector3( (float)(objectWorldLocX - (double)destination.RegionLocX), |
1862 | = Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy + changeY) * Constants.RegionSize)); | 2496 | (float)(objectWorldLocY - (double)destination.RegionLocY), |
1863 | // y + 1 | 2497 | attemptedPosition.Z); |
1864 | } | 2498 | } |
1865 | 2499 | ||
1866 | // Offset the positions for the new region across the border | ||
1867 | Vector3 oldGroupPosition = grp.RootPart.GroupPosition; | ||
1868 | |||
1869 | // If we fail to cross the border, then reset the position of the scene object on that border. | ||
1870 | uint x = 0, y = 0; | ||
1871 | Utils.LongToUInts(newRegionHandle, out x, out y); | ||
1872 | GridRegion destination = scene.GridService.GetRegionByPosition(scene.RegionInfo.ScopeID, (int)x, (int)y); | ||
1873 | |||
1874 | if (destination == null || !CrossPrimGroupIntoNewRegion(destination, pos, grp, silent)) | 2500 | if (destination == null || !CrossPrimGroupIntoNewRegion(destination, pos, grp, silent)) |
1875 | { | 2501 | { |
1876 | m_log.InfoFormat("[ENTITY TRANSFER MODULE] cross region transfer failed for object {0}",grp.UUID); | 2502 | m_log.InfoFormat("[ENTITY TRANSFER MODULE] cross region transfer failed for object {0}", grp.UUID); |
1877 | 2503 | ||
1878 | // We are going to move the object back to the old position so long as the old position | 2504 | // We are going to move the object back to the old position so long as the old position |
1879 | // is in the region | 2505 | // is in the region |
1880 | oldGroupPosition.X = Util.Clamp<float>(oldGroupPosition.X,1.0f,(float)Constants.RegionSize-1); | 2506 | oldGroupPosition.X = Util.Clamp<float>(oldGroupPosition.X, 1.0f, (float)(scene.RegionInfo.RegionSizeX - 1)); |
1881 | oldGroupPosition.Y = Util.Clamp<float>(oldGroupPosition.Y,1.0f,(float)Constants.RegionSize-1); | 2507 | oldGroupPosition.Y = Util.Clamp<float>(oldGroupPosition.Y, 1.0f, (float)(scene.RegionInfo.RegionSizeY - 1)); |
1882 | oldGroupPosition.Z = Util.Clamp<float>(oldGroupPosition.Z,1.0f,4096.0f); | 2508 | oldGroupPosition.Z = Util.Clamp<float>(oldGroupPosition.Z, 1.0f, Constants.RegionHeight); |
1883 | 2509 | ||
1884 | grp.RootPart.GroupPosition = oldGroupPosition; | 2510 | grp.AbsolutePosition = oldGroupPosition; |
2511 | grp.Velocity = Vector3.Zero; | ||
2512 | if (grp.RootPart.PhysActor != null) | ||
2513 | grp.RootPart.PhysActor.CrossingFailure(); | ||
1885 | 2514 | ||
1886 | // Need to turn off the physics flags, otherwise the object will continue to attempt to | 2515 | if (grp.RootPart.KeyframeMotion != null) |
1887 | // move out of the region creating an infinite loop of failed attempts to cross | 2516 | grp.RootPart.KeyframeMotion.CrossingFailure(); |
1888 | grp.UpdatePrimFlags(grp.RootPart.LocalId,false,grp.IsTemporary,grp.IsPhantom,false); | ||
1889 | 2517 | ||
1890 | grp.ScheduleGroupForFullUpdate(); | 2518 | grp.ScheduleGroupForFullUpdate(); |
1891 | } | 2519 | } |
1892 | } | 2520 | } |
1893 | 2521 | ||
1894 | |||
1895 | /// <summary> | 2522 | /// <summary> |
1896 | /// Move the given scene object into a new region | 2523 | /// Move the given scene object into a new region |
1897 | /// </summary> | 2524 | /// </summary> |
@@ -1942,17 +2569,30 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1942 | grp, e); | 2569 | grp, e); |
1943 | } | 2570 | } |
1944 | } | 2571 | } |
2572 | /* | ||
2573 | * done on caller ( not in attachments crossing for now) | ||
1945 | else | 2574 | else |
1946 | { | 2575 | { |
2576 | |||
1947 | if (!grp.IsDeleted) | 2577 | if (!grp.IsDeleted) |
1948 | { | 2578 | { |
1949 | PhysicsActor pa = grp.RootPart.PhysActor; | 2579 | PhysicsActor pa = grp.RootPart.PhysActor; |
1950 | if (pa != null) | 2580 | if (pa != null) |
2581 | { | ||
1951 | pa.CrossingFailure(); | 2582 | pa.CrossingFailure(); |
2583 | if (grp.RootPart.KeyframeMotion != null) | ||
2584 | { | ||
2585 | // moved to KeyframeMotion.CrossingFailure | ||
2586 | // grp.RootPart.Velocity = Vector3.Zero; | ||
2587 | grp.RootPart.KeyframeMotion.CrossingFailure(); | ||
2588 | // grp.SendGroupRootTerseUpdate(); | ||
2589 | } | ||
2590 | } | ||
1952 | } | 2591 | } |
1953 | 2592 | ||
1954 | m_log.ErrorFormat("[ENTITY TRANSFER MODULE]: Prim crossing failed for {0}", grp); | 2593 | m_log.ErrorFormat("[ENTITY TRANSFER MODULE]: Prim crossing failed for {0}", grp); |
1955 | } | 2594 | } |
2595 | */ | ||
1956 | } | 2596 | } |
1957 | else | 2597 | else |
1958 | { | 2598 | { |
@@ -2007,7 +2647,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
2007 | 2647 | ||
2008 | public bool IsInTransit(UUID id) | 2648 | public bool IsInTransit(UUID id) |
2009 | { | 2649 | { |
2010 | return m_entityTransferStateMachine.IsInTransit(id); | 2650 | return m_entityTransferStateMachine.GetAgentTransferState(id) != null; |
2011 | } | 2651 | } |
2012 | 2652 | ||
2013 | protected void ReInstantiateScripts(ScenePresence sp) | 2653 | protected void ReInstantiateScripts(ScenePresence sp) |
@@ -2036,5 +2676,69 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
2036 | } | 2676 | } |
2037 | #endregion | 2677 | #endregion |
2038 | 2678 | ||
2679 | public virtual bool HandleIncomingSceneObject(SceneObjectGroup so, Vector3 newPosition) | ||
2680 | { | ||
2681 | // If the user is banned, we won't let any of their objects | ||
2682 | // enter. Period. | ||
2683 | // | ||
2684 | if (Scene.RegionInfo.EstateSettings.IsBanned(so.OwnerID)) | ||
2685 | { | ||
2686 | m_log.DebugFormat( | ||
2687 | "[ENTITY TRANSFER MODULE]: Denied prim crossing of {0} {1} into {2} for banned avatar {3}", | ||
2688 | so.Name, so.UUID, Scene.Name, so.OwnerID); | ||
2689 | |||
2690 | return false; | ||
2691 | } | ||
2692 | |||
2693 | if (newPosition != Vector3.Zero) | ||
2694 | so.RootPart.GroupPosition = newPosition; | ||
2695 | |||
2696 | if (!Scene.AddSceneObject(so)) | ||
2697 | { | ||
2698 | m_log.DebugFormat( | ||
2699 | "[ENTITY TRANSFER MODULE]: Problem adding scene object {0} {1} into {2} ", | ||
2700 | so.Name, so.UUID, Scene.Name); | ||
2701 | |||
2702 | return false; | ||
2703 | } | ||
2704 | |||
2705 | if (!so.IsAttachment) | ||
2706 | { | ||
2707 | // FIXME: It would be better to never add the scene object at all rather than add it and then delete | ||
2708 | // it | ||
2709 | if (!Scene.Permissions.CanObjectEntry(so.UUID, true, so.AbsolutePosition)) | ||
2710 | { | ||
2711 | // Deny non attachments based on parcel settings | ||
2712 | // | ||
2713 | m_log.Info("[ENTITY TRANSFER MODULE]: Denied prim crossing because of parcel settings"); | ||
2714 | |||
2715 | Scene.DeleteSceneObject(so, false); | ||
2716 | |||
2717 | return false; | ||
2718 | } | ||
2719 | |||
2720 | // For attachments, we need to wait until the agent is root | ||
2721 | // before we restart the scripts, or else some functions won't work. | ||
2722 | so.RootPart.ParentGroup.CreateScriptInstances( | ||
2723 | 0, false, Scene.DefaultScriptEngine, GetStateSource(so)); | ||
2724 | |||
2725 | so.ResumeScripts(); | ||
2726 | |||
2727 | if (so.RootPart.KeyframeMotion != null) | ||
2728 | so.RootPart.KeyframeMotion.UpdateSceneObject(so); | ||
2729 | } | ||
2730 | |||
2731 | return true; | ||
2732 | } | ||
2733 | |||
2734 | private int GetStateSource(SceneObjectGroup sog) | ||
2735 | { | ||
2736 | ScenePresence sp = Scene.GetScenePresence(sog.OwnerID); | ||
2737 | |||
2738 | if (sp != null) | ||
2739 | return sp.GetStateSource(); | ||
2740 | |||
2741 | return 2; // StateSource.PrimCrossing | ||
2742 | } | ||
2039 | } | 2743 | } |
2040 | } \ No newline at end of file | 2744 | } |
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs index d0cab49..a3109e0 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs | |||
@@ -38,7 +38,7 @@ using OpenSim.Framework.Capabilities; | |||
38 | using OpenSim.Framework.Client; | 38 | using OpenSim.Framework.Client; |
39 | using OpenSim.Region.Framework.Interfaces; | 39 | using OpenSim.Region.Framework.Interfaces; |
40 | using OpenSim.Region.Framework.Scenes; | 40 | using OpenSim.Region.Framework.Scenes; |
41 | using OpenSim.Region.Physics.Manager; | 41 | using OpenSim.Region.PhysicsModules.SharedBase; |
42 | using OpenSim.Services.Interfaces; | 42 | using OpenSim.Services.Interfaces; |
43 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; | 43 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; |
44 | 44 | ||
@@ -51,10 +51,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
51 | /// This is a state machine. | 51 | /// This is a state machine. |
52 | /// | 52 | /// |
53 | /// [Entry] => Preparing | 53 | /// [Entry] => Preparing |
54 | /// Preparing => { Transferring || CleaningUp || [Exit] } | 54 | /// Preparing => { Transferring || Cancelling || CleaningUp || Aborting || [Exit] } |
55 | /// Transferring => { ReceivedAtDestination || CleaningUp } | 55 | /// Transferring => { ReceivedAtDestination || Cancelling || CleaningUp || Aborting } |
56 | /// ReceivedAtDestination => CleaningUp | 56 | /// Cancelling => CleaningUp || Aborting |
57 | /// ReceivedAtDestination => CleaningUp || Aborting | ||
57 | /// CleaningUp => [Exit] | 58 | /// CleaningUp => [Exit] |
59 | /// Aborting => [Exit] | ||
58 | /// | 60 | /// |
59 | /// In other words, agents normally travel throwing Preparing => Transferring => ReceivedAtDestination => CleaningUp | 61 | /// In other words, agents normally travel throwing Preparing => Transferring => ReceivedAtDestination => CleaningUp |
60 | /// However, any state can transition to CleaningUp if the teleport has failed. | 62 | /// However, any state can transition to CleaningUp if the teleport has failed. |
@@ -64,7 +66,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
64 | Preparing, // The agent is being prepared for transfer | 66 | Preparing, // The agent is being prepared for transfer |
65 | Transferring, // The agent is in the process of being transferred to a destination | 67 | Transferring, // The agent is in the process of being transferred to a destination |
66 | ReceivedAtDestination, // The destination has notified us that the agent has been successfully received | 68 | ReceivedAtDestination, // The destination has notified us that the agent has been successfully received |
67 | CleaningUp // The agent is being changed to child/removed after a transfer | 69 | CleaningUp, // The agent is being changed to child/removed after a transfer |
70 | Cancelling, // The user has cancelled the teleport but we have yet to act upon this. | ||
71 | Aborting // The transfer is aborting. Unlike Cancelling, no compensating actions should be performed | ||
68 | } | 72 | } |
69 | 73 | ||
70 | /// <summary> | 74 | /// <summary> |
@@ -73,6 +77,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
73 | public class EntityTransferStateMachine | 77 | public class EntityTransferStateMachine |
74 | { | 78 | { |
75 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 79 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
80 | private static readonly string LogHeader = "[ENTITY TRANSFER STATE MACHINE]"; | ||
76 | 81 | ||
77 | /// <summary> | 82 | /// <summary> |
78 | /// If true then on a teleport, the source region waits for a callback from the destination region. If | 83 | /// If true then on a teleport, the source region waits for a callback from the destination region. If |
@@ -96,6 +101,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
96 | /// <returns>true if the agent was not already in transit, false if it was</returns> | 101 | /// <returns>true if the agent was not already in transit, false if it was</returns> |
97 | internal bool SetInTransit(UUID id) | 102 | internal bool SetInTransit(UUID id) |
98 | { | 103 | { |
104 | m_log.DebugFormat("{0} SetInTransit. agent={1}, newState=Preparing", LogHeader, id); | ||
99 | lock (m_agentsInTransit) | 105 | lock (m_agentsInTransit) |
100 | { | 106 | { |
101 | if (!m_agentsInTransit.ContainsKey(id)) | 107 | if (!m_agentsInTransit.ContainsKey(id)) |
@@ -115,42 +121,117 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
115 | /// <param name='newState'></param> | 121 | /// <param name='newState'></param> |
116 | /// <returns></returns> | 122 | /// <returns></returns> |
117 | /// <exception cref='Exception'>Illegal transitions will throw an Exception</exception> | 123 | /// <exception cref='Exception'>Illegal transitions will throw an Exception</exception> |
118 | internal void UpdateInTransit(UUID id, AgentTransferState newState) | 124 | internal bool UpdateInTransit(UUID id, AgentTransferState newState) |
119 | { | 125 | { |
126 | m_log.DebugFormat("{0} UpdateInTransit. agent={1}, newState={2}", LogHeader, id, newState); | ||
127 | |||
128 | bool transitionOkay = false; | ||
129 | |||
130 | // We don't want to throw an exception on cancel since this can come it at any time. | ||
131 | bool failIfNotOkay = true; | ||
132 | |||
133 | // Should be a failure message if failure is not okay. | ||
134 | string failureMessage = null; | ||
135 | |||
136 | AgentTransferState? oldState = null; | ||
137 | |||
120 | lock (m_agentsInTransit) | 138 | lock (m_agentsInTransit) |
121 | { | 139 | { |
122 | // Illegal to try and update an agent that's not actually in transit. | 140 | // Illegal to try and update an agent that's not actually in transit. |
123 | if (!m_agentsInTransit.ContainsKey(id)) | 141 | if (!m_agentsInTransit.ContainsKey(id)) |
124 | throw new Exception( | 142 | { |
125 | string.Format( | 143 | if (newState != AgentTransferState.Cancelling && newState != AgentTransferState.Aborting) |
126 | "Agent with ID {0} is not registered as in transit in {1}", | 144 | failureMessage = string.Format( |
127 | id, m_mod.Scene.RegionInfo.RegionName)); | 145 | "Agent with ID {0} is not registered as in transit in {1}", |
128 | 146 | id, m_mod.Scene.RegionInfo.RegionName); | |
129 | AgentTransferState oldState = m_agentsInTransit[id]; | 147 | else |
148 | failIfNotOkay = false; | ||
149 | } | ||
150 | else | ||
151 | { | ||
152 | oldState = m_agentsInTransit[id]; | ||
130 | 153 | ||
131 | bool transitionOkay = false; | 154 | if (newState == AgentTransferState.Aborting) |
155 | { | ||
156 | transitionOkay = true; | ||
157 | } | ||
158 | else if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp) | ||
159 | { | ||
160 | transitionOkay = true; | ||
161 | } | ||
162 | else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing) | ||
163 | { | ||
164 | transitionOkay = true; | ||
165 | } | ||
166 | else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring) | ||
167 | { | ||
168 | transitionOkay = true; | ||
169 | } | ||
170 | else | ||
171 | { | ||
172 | if (newState == AgentTransferState.Cancelling | ||
173 | && (oldState == AgentTransferState.Preparing || oldState == AgentTransferState.Transferring)) | ||
174 | { | ||
175 | transitionOkay = true; | ||
176 | } | ||
177 | else | ||
178 | { | ||
179 | failIfNotOkay = false; | ||
180 | } | ||
181 | } | ||
132 | 182 | ||
133 | if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp) | 183 | if (!transitionOkay) |
134 | transitionOkay = true; | 184 | failureMessage |
135 | else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing) | 185 | = string.Format( |
136 | transitionOkay = true; | 186 | "Agent with ID {0} is not allowed to move from old transit state {1} to new state {2} in {3}", |
137 | else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring) | 187 | id, oldState, newState, m_mod.Scene.RegionInfo.RegionName); |
138 | transitionOkay = true; | 188 | } |
139 | 189 | ||
140 | if (transitionOkay) | 190 | if (transitionOkay) |
191 | { | ||
141 | m_agentsInTransit[id] = newState; | 192 | m_agentsInTransit[id] = newState; |
142 | else | 193 | |
143 | throw new Exception( | 194 | // m_log.DebugFormat( |
144 | string.Format( | 195 | // "[ENTITY TRANSFER STATE MACHINE]: Changed agent with id {0} from state {1} to {2} in {3}", |
145 | "Agent with ID {0} is not allowed to move from old transit state {1} to new state {2} in {3}", | 196 | // id, oldState, newState, m_mod.Scene.Name); |
146 | id, oldState, newState, m_mod.Scene.RegionInfo.RegionName)); | 197 | } |
198 | else if (failIfNotOkay) | ||
199 | { | ||
200 | m_log.DebugFormat("{0} UpdateInTransit. Throwing transition failure = {1}", LogHeader, failureMessage); | ||
201 | throw new Exception(failureMessage); | ||
202 | } | ||
203 | // else | ||
204 | // { | ||
205 | // if (oldState != null) | ||
206 | // m_log.DebugFormat( | ||
207 | // "[ENTITY TRANSFER STATE MACHINE]: Ignored change of agent with id {0} from state {1} to {2} in {3}", | ||
208 | // id, oldState, newState, m_mod.Scene.Name); | ||
209 | // else | ||
210 | // m_log.DebugFormat( | ||
211 | // "[ENTITY TRANSFER STATE MACHINE]: Ignored change of agent with id {0} to state {1} in {2} since agent not in transit", | ||
212 | // id, newState, m_mod.Scene.Name); | ||
213 | // } | ||
147 | } | 214 | } |
215 | |||
216 | return transitionOkay; | ||
148 | } | 217 | } |
149 | 218 | ||
150 | internal bool IsInTransit(UUID id) | 219 | /// <summary> |
220 | /// Gets the current agent transfer state. | ||
221 | /// </summary> | ||
222 | /// <returns>Null if the agent is not in transit</returns> | ||
223 | /// <param name='id'> | ||
224 | /// Identifier. | ||
225 | /// </param> | ||
226 | internal AgentTransferState? GetAgentTransferState(UUID id) | ||
151 | { | 227 | { |
152 | lock (m_agentsInTransit) | 228 | lock (m_agentsInTransit) |
153 | return m_agentsInTransit.ContainsKey(id); | 229 | { |
230 | if (!m_agentsInTransit.ContainsKey(id)) | ||
231 | return null; | ||
232 | else | ||
233 | return m_agentsInTransit[id]; | ||
234 | } | ||
154 | } | 235 | } |
155 | 236 | ||
156 | /// <summary> | 237 | /// <summary> |
@@ -203,14 +284,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
203 | 284 | ||
204 | lock (m_agentsInTransit) | 285 | lock (m_agentsInTransit) |
205 | { | 286 | { |
206 | if (!IsInTransit(id)) | 287 | AgentTransferState? currentState = GetAgentTransferState(id); |
288 | |||
289 | if (currentState == null) | ||
207 | throw new Exception( | 290 | throw new Exception( |
208 | string.Format( | 291 | string.Format( |
209 | "Asked to wait for destination callback for agent with ID {0} in {1} but agent is not in transit", | 292 | "Asked to wait for destination callback for agent with ID {0} in {1} but agent is not in transit", |
210 | id, m_mod.Scene.RegionInfo.RegionName)); | 293 | id, m_mod.Scene.RegionInfo.RegionName)); |
211 | 294 | ||
212 | AgentTransferState currentState = m_agentsInTransit[id]; | ||
213 | |||
214 | if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination) | 295 | if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination) |
215 | throw new Exception( | 296 | throw new Exception( |
216 | string.Format( | 297 | string.Format( |
@@ -222,8 +303,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
222 | 303 | ||
223 | // There should be no race condition here since no other code should be removing the agent transfer or | 304 | // There should be no race condition here since no other code should be removing the agent transfer or |
224 | // changing the state to another other than Transferring => ReceivedAtDestination. | 305 | // changing the state to another other than Transferring => ReceivedAtDestination. |
225 | while (m_agentsInTransit[id] != AgentTransferState.ReceivedAtDestination && count-- > 0) | 306 | |
307 | while (count-- > 0) | ||
226 | { | 308 | { |
309 | lock (m_agentsInTransit) | ||
310 | { | ||
311 | if (m_agentsInTransit[id] == AgentTransferState.ReceivedAtDestination) | ||
312 | break; | ||
313 | } | ||
314 | |||
227 | // m_log.Debug(" >>> Waiting... " + count); | 315 | // m_log.Debug(" >>> Waiting... " + count); |
228 | Thread.Sleep(100); | 316 | Thread.Sleep(100); |
229 | } | 317 | } |
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs index b188741..fa23590 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.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 | * |
@@ -31,6 +31,7 @@ using System.Reflection; | |||
31 | 31 | ||
32 | using OpenSim.Framework; | 32 | using OpenSim.Framework; |
33 | using OpenSim.Framework.Client; | 33 | using OpenSim.Framework.Client; |
34 | using OpenSim.Framework.Monitoring; | ||
34 | using OpenSim.Region.Framework.Interfaces; | 35 | using OpenSim.Region.Framework.Interfaces; |
35 | using OpenSim.Region.Framework.Scenes; | 36 | using OpenSim.Region.Framework.Scenes; |
36 | using OpenSim.Services.Connectors.Hypergrid; | 37 | using OpenSim.Services.Connectors.Hypergrid; |
@@ -55,6 +56,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
55 | private int m_levelHGTeleport = 0; | 56 | private int m_levelHGTeleport = 0; |
56 | 57 | ||
57 | private GatekeeperServiceConnector m_GatekeeperConnector; | 58 | private GatekeeperServiceConnector m_GatekeeperConnector; |
59 | private IUserAgentService m_UAS; | ||
58 | 60 | ||
59 | protected bool m_RestrictAppearanceAbroad; | 61 | protected bool m_RestrictAppearanceAbroad; |
60 | protected string m_AccountName; | 62 | protected string m_AccountName; |
@@ -109,6 +111,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
109 | } | 111 | } |
110 | } | 112 | } |
111 | 113 | ||
114 | /// <summary> | ||
115 | /// Used for processing analysis of incoming attachments in a controlled fashion. | ||
116 | /// </summary> | ||
117 | private JobEngine m_incomingSceneObjectEngine; | ||
118 | |||
112 | #region ISharedRegionModule | 119 | #region ISharedRegionModule |
113 | 120 | ||
114 | public override string Name | 121 | public override string Name |
@@ -152,39 +159,33 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
152 | if (m_Enabled) | 159 | if (m_Enabled) |
153 | { | 160 | { |
154 | scene.RegisterModuleInterface<IUserAgentVerificationModule>(this); | 161 | scene.RegisterModuleInterface<IUserAgentVerificationModule>(this); |
155 | scene.EventManager.OnIncomingSceneObject += OnIncomingSceneObject; | 162 | //scene.EventManager.OnIncomingSceneObject += OnIncomingSceneObject; |
156 | } | 163 | |
157 | } | 164 | m_incomingSceneObjectEngine |
158 | 165 | = new JobEngine( | |
159 | void OnIncomingSceneObject(SceneObjectGroup so) | 166 | string.Format("HG Incoming Scene Object Engine ({0})", scene.Name), |
160 | { | 167 | "HG INCOMING SCENE OBJECT ENGINE"); |
161 | if (!so.IsAttachment) | 168 | |
162 | return; | 169 | StatsManager.RegisterStat( |
163 | 170 | new Stat( | |
164 | if (so.Scene.UserManagementModule.IsLocalGridUser(so.AttachedAvatar)) | 171 | "HGIncomingAttachmentsWaiting", |
165 | return; | 172 | "Number of incoming attachments waiting for processing.", |
166 | 173 | "", | |
167 | // foreign user | 174 | "", |
168 | AgentCircuitData aCircuit = so.Scene.AuthenticateHandler.GetAgentCircuitData(so.AttachedAvatar); | 175 | "entitytransfer", |
169 | if (aCircuit != null && (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0) | 176 | Name, |
170 | { | 177 | StatType.Pull, |
171 | if (aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("AssetServerURI")) | 178 | MeasuresOfInterest.None, |
172 | { | 179 | stat => stat.Value = m_incomingSceneObjectEngine.JobsWaiting, |
173 | string url = aCircuit.ServiceURLs["AssetServerURI"].ToString(); | 180 | StatVerbosity.Debug)); |
174 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Incoming attachement {0} for HG user {1} with asset server {2}", so.Name, so.AttachedAvatar, url); | 181 | |
175 | Dictionary<UUID, AssetType> ids = new Dictionary<UUID, AssetType>(); | 182 | m_incomingSceneObjectEngine.Start(); |
176 | HGUuidGatherer uuidGatherer = new HGUuidGatherer(so.Scene.AssetService, url); | ||
177 | uuidGatherer.GatherAssetUuids(so, ids); | ||
178 | |||
179 | foreach (KeyValuePair<UUID, AssetType> kvp in ids) | ||
180 | uuidGatherer.FetchAsset(kvp.Key); | ||
181 | } | ||
182 | } | 183 | } |
183 | } | 184 | } |
184 | 185 | ||
185 | protected override void OnNewClient(IClientAPI client) | 186 | protected override void OnNewClient(IClientAPI client) |
186 | { | 187 | { |
187 | client.OnTeleportHomeRequest += TeleportHome; | 188 | client.OnTeleportHomeRequest += TriggerTeleportHome; |
188 | client.OnTeleportLandmarkRequest += RequestTeleportLandmark; | 189 | client.OnTeleportLandmarkRequest += RequestTeleportLandmark; |
189 | client.OnConnectionClosed += new Action<IClientAPI>(OnConnectionClosed); | 190 | client.OnConnectionClosed += new Action<IClientAPI>(OnConnectionClosed); |
190 | } | 191 | } |
@@ -194,34 +195,44 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
194 | base.RegionLoaded(scene); | 195 | base.RegionLoaded(scene); |
195 | 196 | ||
196 | if (m_Enabled) | 197 | if (m_Enabled) |
198 | { | ||
197 | m_GatekeeperConnector = new GatekeeperServiceConnector(scene.AssetService); | 199 | m_GatekeeperConnector = new GatekeeperServiceConnector(scene.AssetService); |
200 | m_UAS = scene.RequestModuleInterface<IUserAgentService>(); | ||
201 | if (m_UAS == null) | ||
202 | m_UAS = new UserAgentServiceConnector(m_ThisHomeURI); | ||
203 | |||
204 | } | ||
198 | } | 205 | } |
199 | 206 | ||
200 | public override void RemoveRegion(Scene scene) | 207 | public override void RemoveRegion(Scene scene) |
201 | { | 208 | { |
202 | base.AddRegion(scene); | 209 | base.RemoveRegion(scene); |
203 | 210 | ||
204 | if (m_Enabled) | 211 | if (m_Enabled) |
212 | { | ||
205 | scene.UnregisterModuleInterface<IUserAgentVerificationModule>(this); | 213 | scene.UnregisterModuleInterface<IUserAgentVerificationModule>(this); |
214 | m_incomingSceneObjectEngine.Stop(); | ||
215 | } | ||
206 | } | 216 | } |
207 | 217 | ||
208 | #endregion | 218 | #endregion |
209 | 219 | ||
210 | #region HG overrides of IEntiryTransferModule | 220 | #region HG overrides of IEntityTransferModule |
211 | 221 | ||
212 | protected override GridRegion GetFinalDestination(GridRegion region) | 222 | protected override GridRegion GetFinalDestination(GridRegion region, UUID agentID, string agentHomeURI, out string message) |
213 | { | 223 | { |
214 | int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, region.RegionID); | 224 | int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, region.RegionID); |
215 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: region {0} flags: {1}", region.RegionName, flags); | 225 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: region {0} flags: {1}", region.RegionName, flags); |
226 | message = null; | ||
216 | 227 | ||
217 | if ((flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0) | 228 | if ((flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0) |
218 | { | 229 | { |
219 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Destination region is hyperlink"); | 230 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Destination region is hyperlink"); |
220 | GridRegion real_destination = m_GatekeeperConnector.GetHyperlinkRegion(region, region.RegionID); | 231 | GridRegion real_destination = m_GatekeeperConnector.GetHyperlinkRegion(region, region.RegionID, agentID, agentHomeURI, out message); |
221 | if (real_destination != null) | 232 | if (real_destination != null) |
222 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: GetFinalDestination serveruri -> {0}", real_destination.ServerURI); | 233 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: GetFinalDestination: ServerURI={0}", real_destination.ServerURI); |
223 | else | 234 | else |
224 | m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: GetHyperlinkRegion to Gatekeeper {0} failed", region.ServerURI); | 235 | m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: GetHyperlinkRegion of region {0} from Gatekeeper {1} failed: {2}", region.RegionID, region.ServerURI, message); |
225 | return real_destination; | 236 | return real_destination; |
226 | } | 237 | } |
227 | 238 | ||
@@ -272,12 +283,21 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
272 | if (agentCircuit.ServiceURLs.ContainsKey("HomeURI")) | 283 | if (agentCircuit.ServiceURLs.ContainsKey("HomeURI")) |
273 | { | 284 | { |
274 | string userAgentDriver = agentCircuit.ServiceURLs["HomeURI"].ToString(); | 285 | string userAgentDriver = agentCircuit.ServiceURLs["HomeURI"].ToString(); |
275 | IUserAgentService connector = new UserAgentServiceConnector(userAgentDriver); | 286 | IUserAgentService connector; |
276 | bool success = connector.LoginAgentToGrid(agentCircuit, reg, finalDestination, out reason); | 287 | |
288 | if (userAgentDriver.Equals(m_ThisHomeURI) && m_UAS != null) | ||
289 | connector = m_UAS; | ||
290 | else | ||
291 | connector = new UserAgentServiceConnector(userAgentDriver); | ||
292 | |||
293 | GridRegion source = new GridRegion(Scene.RegionInfo); | ||
294 | source.RawServerURI = m_GatekeeperURI; | ||
295 | |||
296 | bool success = connector.LoginAgentToGrid(source, agentCircuit, reg, finalDestination, false, out reason); | ||
277 | logout = success; // flag for later logout from this grid; this is an HG TP | 297 | logout = success; // flag for later logout from this grid; this is an HG TP |
278 | 298 | ||
279 | if (success) | 299 | if (success) |
280 | sp.Scene.EventManager.TriggerTeleportStart(sp.ControllingClient, reg, finalDestination, teleportFlags, logout); | 300 | Scene.EventManager.TriggerTeleportStart(sp.ControllingClient, reg, finalDestination, teleportFlags, logout); |
281 | 301 | ||
282 | return success; | 302 | return success; |
283 | } | 303 | } |
@@ -409,7 +429,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
409 | // return base.UpdateAgent(reg, finalDestination, agentData, sp); | 429 | // return base.UpdateAgent(reg, finalDestination, agentData, sp); |
410 | //} | 430 | //} |
411 | 431 | ||
412 | public override void TeleportHome(UUID id, IClientAPI client) | 432 | public override void TriggerTeleportHome(UUID id, IClientAPI client) |
433 | { | ||
434 | TeleportHome(id, client); | ||
435 | } | ||
436 | |||
437 | public override bool TeleportHome(UUID id, IClientAPI client) | ||
413 | { | 438 | { |
414 | m_log.DebugFormat( | 439 | m_log.DebugFormat( |
415 | "[ENTITY TRANSFER MODULE]: Request to teleport {0} {1} home", client.Name, client.AgentId); | 440 | "[ENTITY TRANSFER MODULE]: Request to teleport {0} {1} home", client.Name, client.AgentId); |
@@ -420,8 +445,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
420 | { | 445 | { |
421 | // local grid user | 446 | // local grid user |
422 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: User is local"); | 447 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: User is local"); |
423 | base.TeleportHome(id, client); | 448 | return base.TeleportHome(id, client); |
424 | return; | ||
425 | } | 449 | } |
426 | 450 | ||
427 | // Foreign user wants to go home | 451 | // Foreign user wants to go home |
@@ -431,17 +455,27 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
431 | { | 455 | { |
432 | client.SendTeleportFailed("Your information has been lost"); | 456 | client.SendTeleportFailed("Your information has been lost"); |
433 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Unable to locate agent's gateway information"); | 457 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Unable to locate agent's gateway information"); |
434 | return; | 458 | return false; |
435 | } | 459 | } |
436 | 460 | ||
437 | IUserAgentService userAgentService = new UserAgentServiceConnector(aCircuit.ServiceURLs["HomeURI"].ToString()); | 461 | IUserAgentService userAgentService = new UserAgentServiceConnector(aCircuit.ServiceURLs["HomeURI"].ToString()); |
438 | Vector3 position = Vector3.UnitY, lookAt = Vector3.UnitY; | 462 | Vector3 position = Vector3.UnitY, lookAt = Vector3.UnitY; |
439 | GridRegion finalDestination = userAgentService.GetHomeRegion(aCircuit.AgentID, out position, out lookAt); | 463 | |
464 | GridRegion finalDestination = null; | ||
465 | try | ||
466 | { | ||
467 | finalDestination = userAgentService.GetHomeRegion(aCircuit.AgentID, out position, out lookAt); | ||
468 | } | ||
469 | catch (Exception e) | ||
470 | { | ||
471 | m_log.Debug("[HG ENTITY TRANSFER MODULE]: GetHomeRegion call failed ", e); | ||
472 | } | ||
473 | |||
440 | if (finalDestination == null) | 474 | if (finalDestination == null) |
441 | { | 475 | { |
442 | client.SendTeleportFailed("Your home region could not be found"); | 476 | client.SendTeleportFailed("Your home region could not be found"); |
443 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Agent's home region not found"); | 477 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Agent's home region not found"); |
444 | return; | 478 | return false; |
445 | } | 479 | } |
446 | 480 | ||
447 | ScenePresence sp = ((Scene)(client.Scene)).GetScenePresence(client.AgentId); | 481 | ScenePresence sp = ((Scene)(client.Scene)).GetScenePresence(client.AgentId); |
@@ -449,7 +483,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
449 | { | 483 | { |
450 | client.SendTeleportFailed("Internal error"); | 484 | client.SendTeleportFailed("Internal error"); |
451 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Agent not found in the scene where it is supposed to be"); | 485 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Agent not found in the scene where it is supposed to be"); |
452 | return; | 486 | return false; |
453 | } | 487 | } |
454 | 488 | ||
455 | GridRegion homeGatekeeper = MakeRegion(aCircuit); | 489 | GridRegion homeGatekeeper = MakeRegion(aCircuit); |
@@ -460,6 +494,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
460 | DoTeleport( | 494 | DoTeleport( |
461 | sp, homeGatekeeper, finalDestination, | 495 | sp, homeGatekeeper, finalDestination, |
462 | position, lookAt, (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaHome)); | 496 | position, lookAt, (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaHome)); |
497 | return true; | ||
463 | } | 498 | } |
464 | 499 | ||
465 | /// <summary> | 500 | /// <summary> |
@@ -484,35 +519,174 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
484 | // Local region? | 519 | // Local region? |
485 | if (info != null) | 520 | if (info != null) |
486 | { | 521 | { |
487 | ((Scene)(remoteClient.Scene)).RequestTeleportLocation(remoteClient, info.RegionHandle, lm.Position, | 522 | Scene.RequestTeleportLocation( |
523 | remoteClient, info.RegionHandle, lm.Position, | ||
488 | Vector3.Zero, (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaLandmark)); | 524 | Vector3.Zero, (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaLandmark)); |
489 | |||
490 | return; | ||
491 | } | 525 | } |
492 | else | 526 | else |
493 | { | 527 | { |
494 | // Foreign region | 528 | // Foreign region |
495 | Scene scene = (Scene)(remoteClient.Scene); | ||
496 | GatekeeperServiceConnector gConn = new GatekeeperServiceConnector(); | 529 | GatekeeperServiceConnector gConn = new GatekeeperServiceConnector(); |
497 | GridRegion gatekeeper = new GridRegion(); | 530 | GridRegion gatekeeper = new GridRegion(); |
498 | gatekeeper.ServerURI = lm.Gatekeeper; | 531 | gatekeeper.ServerURI = lm.Gatekeeper; |
499 | GridRegion finalDestination = gConn.GetHyperlinkRegion(gatekeeper, new UUID(lm.RegionID)); | 532 | string homeURI = Scene.GetAgentHomeURI(remoteClient.AgentId); |
533 | |||
534 | string message; | ||
535 | GridRegion finalDestination = gConn.GetHyperlinkRegion(gatekeeper, new UUID(lm.RegionID), remoteClient.AgentId, homeURI, out message); | ||
500 | 536 | ||
501 | if (finalDestination != null) | 537 | if (finalDestination != null) |
502 | { | 538 | { |
503 | ScenePresence sp = scene.GetScenePresence(remoteClient.AgentId); | 539 | ScenePresence sp = Scene.GetScenePresence(remoteClient.AgentId); |
504 | IEntityTransferModule transferMod = scene.RequestModuleInterface<IEntityTransferModule>(); | ||
505 | 540 | ||
506 | if (transferMod != null && sp != null) | 541 | if (sp != null) |
507 | transferMod.DoTeleport( | 542 | { |
543 | if (message != null) | ||
544 | sp.ControllingClient.SendAgentAlertMessage(message, true); | ||
545 | |||
546 | // Validate assorted conditions | ||
547 | string reason = string.Empty; | ||
548 | if (!ValidateGenericConditions(sp, gatekeeper, finalDestination, 0, out reason)) | ||
549 | { | ||
550 | sp.ControllingClient.SendTeleportFailed(reason); | ||
551 | return; | ||
552 | } | ||
553 | |||
554 | DoTeleport( | ||
508 | sp, gatekeeper, finalDestination, lm.Position, Vector3.UnitX, | 555 | sp, gatekeeper, finalDestination, lm.Position, Vector3.UnitX, |
509 | (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaLandmark)); | 556 | (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaLandmark)); |
557 | } | ||
558 | } | ||
559 | else | ||
560 | { | ||
561 | remoteClient.SendTeleportFailed(message); | ||
510 | } | 562 | } |
511 | 563 | ||
512 | } | 564 | } |
565 | } | ||
566 | |||
567 | private void RemoveIncomingSceneObjectJobs(string commonIdToRemove) | ||
568 | { | ||
569 | List<JobEngine.Job> jobsToReinsert = new List<JobEngine.Job>(); | ||
570 | int jobsRemoved = 0; | ||
513 | 571 | ||
514 | // can't find the region: Tell viewer and abort | 572 | JobEngine.Job job; |
515 | remoteClient.SendTeleportFailed("The teleport destination could not be found."); | 573 | while ((job = m_incomingSceneObjectEngine.RemoveNextJob()) != null) |
574 | { | ||
575 | if (job.CommonId != commonIdToRemove) | ||
576 | jobsToReinsert.Add(job); | ||
577 | else | ||
578 | jobsRemoved++; | ||
579 | } | ||
580 | |||
581 | m_log.DebugFormat( | ||
582 | "[HG ENTITY TRANSFER]: Removing {0} jobs with common ID {1} and reinserting {2} other jobs", | ||
583 | jobsRemoved, commonIdToRemove, jobsToReinsert.Count); | ||
584 | |||
585 | if (jobsToReinsert.Count > 0) | ||
586 | { | ||
587 | foreach (JobEngine.Job jobToReinsert in jobsToReinsert) | ||
588 | m_incomingSceneObjectEngine.QueueJob(jobToReinsert); | ||
589 | } | ||
590 | } | ||
591 | |||
592 | public override bool HandleIncomingSceneObject(SceneObjectGroup so, Vector3 newPosition) | ||
593 | { | ||
594 | // FIXME: We must make it so that we can use SOG.IsAttachment here. At the moment it is always null! | ||
595 | if (!so.IsAttachmentCheckFull()) | ||
596 | return base.HandleIncomingSceneObject(so, newPosition); | ||
597 | |||
598 | // Equally, we can't use so.AttachedAvatar here. | ||
599 | if (so.OwnerID == UUID.Zero || Scene.UserManagementModule.IsLocalGridUser(so.OwnerID)) | ||
600 | return base.HandleIncomingSceneObject(so, newPosition); | ||
601 | |||
602 | // foreign user | ||
603 | AgentCircuitData aCircuit = Scene.AuthenticateHandler.GetAgentCircuitData(so.OwnerID); | ||
604 | if (aCircuit != null) | ||
605 | { | ||
606 | if ((aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) == 0) | ||
607 | { | ||
608 | // We have already pulled the necessary attachments from the source grid. | ||
609 | base.HandleIncomingSceneObject(so, newPosition); | ||
610 | } | ||
611 | else | ||
612 | { | ||
613 | if (aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("AssetServerURI")) | ||
614 | { | ||
615 | m_incomingSceneObjectEngine.QueueJob( | ||
616 | string.Format("HG UUID Gather for attachment {0} for {1}", so.Name, aCircuit.Name), | ||
617 | () => | ||
618 | { | ||
619 | string url = aCircuit.ServiceURLs["AssetServerURI"].ToString(); | ||
620 | // m_log.DebugFormat( | ||
621 | // "[HG ENTITY TRANSFER MODULE]: Incoming attachment {0} for HG user {1} with asset service {2}", | ||
622 | // so.Name, so.AttachedAvatar, url); | ||
623 | |||
624 | IDictionary<UUID, sbyte> ids = new Dictionary<UUID, sbyte>(); | ||
625 | HGUuidGatherer uuidGatherer | ||
626 | = new HGUuidGatherer(Scene.AssetService, url, ids); | ||
627 | uuidGatherer.AddForInspection(so); | ||
628 | |||
629 | while (!uuidGatherer.Complete) | ||
630 | { | ||
631 | int tickStart = Util.EnvironmentTickCount(); | ||
632 | |||
633 | UUID? nextUuid = uuidGatherer.NextUuidToInspect; | ||
634 | uuidGatherer.GatherNext(); | ||
635 | |||
636 | // m_log.DebugFormat( | ||
637 | // "[HG ENTITY TRANSFER]: Gathered attachment asset uuid {0} for object {1} for HG user {2} took {3} ms with asset service {4}", | ||
638 | // nextUuid, so.Name, so.OwnerID, Util.EnvironmentTickCountSubtract(tickStart), url); | ||
639 | |||
640 | int ticksElapsed = Util.EnvironmentTickCountSubtract(tickStart); | ||
641 | |||
642 | if (ticksElapsed > 30000) | ||
643 | { | ||
644 | m_log.WarnFormat( | ||
645 | "[HG ENTITY TRANSFER]: Removing incoming scene object jobs for HG user {0} as gather of {1} from {2} took {3} ms to respond (> {4} ms)", | ||
646 | so.OwnerID, so.Name, url, ticksElapsed, 30000); | ||
647 | |||
648 | RemoveIncomingSceneObjectJobs(so.OwnerID.ToString()); | ||
649 | |||
650 | return; | ||
651 | } | ||
652 | } | ||
653 | |||
654 | // m_log.DebugFormat( | ||
655 | // "[HG ENTITY TRANSFER]: Fetching {0} assets for attachment {1} for HG user {2} with asset service {3}", | ||
656 | // ids.Count, so.Name, so.OwnerID, url); | ||
657 | |||
658 | foreach (KeyValuePair<UUID, sbyte> kvp in ids) | ||
659 | { | ||
660 | int tickStart = Util.EnvironmentTickCount(); | ||
661 | |||
662 | uuidGatherer.FetchAsset(kvp.Key); | ||
663 | |||
664 | int ticksElapsed = Util.EnvironmentTickCountSubtract(tickStart); | ||
665 | |||
666 | if (ticksElapsed > 30000) | ||
667 | { | ||
668 | m_log.WarnFormat( | ||
669 | "[HG ENTITY TRANSFER]: Removing incoming scene object jobs for HG user {0} as fetch of {1} from {2} took {3} ms to respond (> {4} ms)", | ||
670 | so.OwnerID, kvp.Key, url, ticksElapsed, 30000); | ||
671 | |||
672 | RemoveIncomingSceneObjectJobs(so.OwnerID.ToString()); | ||
673 | |||
674 | return; | ||
675 | } | ||
676 | } | ||
677 | |||
678 | base.HandleIncomingSceneObject(so, newPosition); | ||
679 | |||
680 | // m_log.DebugFormat( | ||
681 | // "[HG ENTITY TRANSFER MODULE]: Completed incoming attachment {0} for HG user {1} with asset server {2}", | ||
682 | // so.Name, so.OwnerID, url); | ||
683 | }, | ||
684 | so.OwnerID.ToString()); | ||
685 | } | ||
686 | } | ||
687 | } | ||
688 | |||
689 | return true; | ||
516 | } | 690 | } |
517 | 691 | ||
518 | #endregion | 692 | #endregion |
@@ -549,12 +723,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
549 | if (uMan != null && uMan.IsLocalGridUser(obj.AgentId)) | 723 | if (uMan != null && uMan.IsLocalGridUser(obj.AgentId)) |
550 | { | 724 | { |
551 | // local grid user | 725 | // local grid user |
726 | m_UAS.LogoutAgent(obj.AgentId, obj.SessionId); | ||
552 | return; | 727 | return; |
553 | } | 728 | } |
554 | 729 | ||
555 | AgentCircuitData aCircuit = ((Scene)(obj.Scene)).AuthenticateHandler.GetAgentCircuitData(obj.CircuitCode); | 730 | AgentCircuitData aCircuit = ((Scene)(obj.Scene)).AuthenticateHandler.GetAgentCircuitData(obj.CircuitCode); |
556 | 731 | if (aCircuit != null && aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("HomeURI")) | |
557 | if (aCircuit.ServiceURLs.ContainsKey("HomeURI")) | ||
558 | { | 732 | { |
559 | string url = aCircuit.ServiceURLs["HomeURI"].ToString(); | 733 | string url = aCircuit.ServiceURLs["HomeURI"].ToString(); |
560 | IUserAgentService security = new UserAgentServiceConnector(url); | 734 | IUserAgentService security = new UserAgentServiceConnector(url); |
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs index 7871eda..f54298c 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs | |||
@@ -35,6 +35,7 @@ using System.Xml; | |||
35 | using log4net; | 35 | using log4net; |
36 | using OpenMetaverse; | 36 | using OpenMetaverse; |
37 | using OpenSim.Framework; | 37 | using OpenSim.Framework; |
38 | using OpenSim.Framework.Serialization.External; | ||
38 | 39 | ||
39 | using OpenSim.Region.Framework.Scenes; | 40 | using OpenSim.Region.Framework.Scenes; |
40 | using OpenSim.Region.Framework.Scenes.Serialization; | 41 | using OpenSim.Region.Framework.Scenes.Serialization; |
@@ -73,6 +74,9 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
73 | 74 | ||
74 | private AssetMetadata FetchMetadata(string url, UUID assetID) | 75 | private AssetMetadata FetchMetadata(string url, UUID assetID) |
75 | { | 76 | { |
77 | if (string.IsNullOrEmpty(url)) | ||
78 | return null; | ||
79 | |||
76 | if (!url.EndsWith("/") && !url.EndsWith("=")) | 80 | if (!url.EndsWith("/") && !url.EndsWith("=")) |
77 | url = url + "/"; | 81 | url = url + "/"; |
78 | 82 | ||
@@ -92,6 +96,9 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
92 | AssetBase asset = m_scene.AssetService.Get(assetID.ToString()); | 96 | AssetBase asset = m_scene.AssetService.Get(assetID.ToString()); |
93 | if (asset == null) | 97 | if (asset == null) |
94 | { | 98 | { |
99 | if (string.IsNullOrEmpty(url)) | ||
100 | return null; | ||
101 | |||
95 | if (!url.EndsWith("/") && !url.EndsWith("=")) | 102 | if (!url.EndsWith("/") && !url.EndsWith("=")) |
96 | url = url + "/"; | 103 | url = url + "/"; |
97 | 104 | ||
@@ -109,45 +116,47 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
109 | 116 | ||
110 | public bool PostAsset(string url, AssetBase asset) | 117 | public bool PostAsset(string url, AssetBase asset) |
111 | { | 118 | { |
112 | if (asset != null) | 119 | if (string.IsNullOrEmpty(url)) |
113 | { | 120 | return false; |
114 | if (!url.EndsWith("/") && !url.EndsWith("=")) | ||
115 | url = url + "/"; | ||
116 | 121 | ||
117 | bool success = true; | 122 | if (!url.EndsWith("/") && !url.EndsWith("=")) |
118 | // See long comment in AssetCache.AddAsset | 123 | url = url + "/"; |
119 | if (!asset.Temporary || asset.Local) | ||
120 | { | ||
121 | // We need to copy the asset into a new asset, because | ||
122 | // we need to set its ID to be URL+UUID, so that the | ||
123 | // HGAssetService dispatches it to the remote grid. | ||
124 | // It's not pretty, but the best that can be done while | ||
125 | // not having a global naming infrastructure | ||
126 | AssetBase asset1 = new AssetBase(asset.FullID, asset.Name, asset.Type, asset.Metadata.CreatorID); | ||
127 | Copy(asset, asset1); | ||
128 | asset1.ID = url + asset.ID; | ||
129 | |||
130 | AdjustIdentifiers(asset1.Metadata); | ||
131 | if (asset1.Metadata.Type == (sbyte)AssetType.Object) | ||
132 | asset1.Data = AdjustIdentifiers(asset.Data); | ||
133 | else | ||
134 | asset1.Data = asset.Data; | ||
135 | 124 | ||
136 | string id = m_scene.AssetService.Store(asset1); | 125 | if (asset == null) |
137 | if (id == string.Empty) | 126 | { |
138 | { | 127 | m_log.Warn("[HG ASSET MAPPER]: Tried to post asset to remote server, but asset not in local cache."); |
139 | m_log.DebugFormat("[HG ASSET MAPPER]: Asset server {0} did not accept {1}", url, asset.ID); | 128 | return false; |
140 | success = false; | ||
141 | } | ||
142 | else | ||
143 | m_log.DebugFormat("[HG ASSET MAPPER]: Posted copy of asset {0} from local asset server to {1}", asset1.ID, url); | ||
144 | } | ||
145 | return success; | ||
146 | } | 129 | } |
130 | |||
131 | // See long comment in AssetCache.AddAsset | ||
132 | if (asset.Temporary || asset.Local) | ||
133 | return true; | ||
134 | |||
135 | // We need to copy the asset into a new asset, because | ||
136 | // we need to set its ID to be URL+UUID, so that the | ||
137 | // HGAssetService dispatches it to the remote grid. | ||
138 | // It's not pretty, but the best that can be done while | ||
139 | // not having a global naming infrastructure | ||
140 | AssetBase asset1 = new AssetBase(asset.FullID, asset.Name, asset.Type, asset.Metadata.CreatorID); | ||
141 | Copy(asset, asset1); | ||
142 | asset1.ID = url + asset.ID; | ||
143 | |||
144 | AdjustIdentifiers(asset1.Metadata); | ||
145 | if (asset1.Metadata.Type == (sbyte)AssetType.Object) | ||
146 | asset1.Data = AdjustIdentifiers(asset.Data); | ||
147 | else | 147 | else |
148 | m_log.Warn("[HG ASSET MAPPER]: Tried to post asset to remote server, but asset not in local cache."); | 148 | asset1.Data = asset.Data; |
149 | 149 | ||
150 | return false; | 150 | string id = m_scene.AssetService.Store(asset1); |
151 | if (String.IsNullOrEmpty(id)) | ||
152 | { | ||
153 | m_log.DebugFormat("[HG ASSET MAPPER]: Asset server {0} did not accept {1}", url, asset.ID); | ||
154 | return false; | ||
155 | } | ||
156 | else { | ||
157 | m_log.DebugFormat("[HG ASSET MAPPER]: Posted copy of asset {0} from local asset server to {1}", asset1.ID, url); | ||
158 | return true; | ||
159 | } | ||
151 | } | 160 | } |
152 | 161 | ||
153 | private void Copy(AssetBase from, AssetBase to) | 162 | private void Copy(AssetBase from, AssetBase to) |
@@ -165,7 +174,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
165 | 174 | ||
166 | private void AdjustIdentifiers(AssetMetadata meta) | 175 | private void AdjustIdentifiers(AssetMetadata meta) |
167 | { | 176 | { |
168 | if (meta.CreatorID != null && meta.CreatorID != string.Empty) | 177 | if (!string.IsNullOrEmpty(meta.CreatorID)) |
169 | { | 178 | { |
170 | UUID uuid = UUID.Zero; | 179 | UUID uuid = UUID.Zero; |
171 | UUID.TryParse(meta.CreatorID, out uuid); | 180 | UUID.TryParse(meta.CreatorID, out uuid); |
@@ -181,49 +190,10 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
181 | return Utils.StringToBytes(RewriteSOP(xml)); | 190 | return Utils.StringToBytes(RewriteSOP(xml)); |
182 | } | 191 | } |
183 | 192 | ||
184 | protected string RewriteSOP(string xml) | 193 | protected string RewriteSOP(string xmlData) |
185 | { | 194 | { |
186 | XmlDocument doc = new XmlDocument(); | 195 | // Console.WriteLine("Input XML [{0}]", xmlData); |
187 | doc.LoadXml(xml); | 196 | return ExternalRepresentationUtils.RewriteSOP(xmlData, m_scene.Name, m_HomeURI, m_scene.UserAccountService, m_scene.RegionInfo.ScopeID); |
188 | XmlNodeList sops = doc.GetElementsByTagName("SceneObjectPart"); | ||
189 | |||
190 | foreach (XmlNode sop in sops) | ||
191 | { | ||
192 | UserAccount creator = null; | ||
193 | bool hasCreatorData = false; | ||
194 | XmlNodeList nodes = sop.ChildNodes; | ||
195 | foreach (XmlNode node in nodes) | ||
196 | { | ||
197 | if (node.Name == "CreatorID") | ||
198 | { | ||
199 | UUID uuid = UUID.Zero; | ||
200 | UUID.TryParse(node.InnerText, out uuid); | ||
201 | creator = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid); | ||
202 | } | ||
203 | if (node.Name == "CreatorData" && node.InnerText != null && node.InnerText != string.Empty) | ||
204 | hasCreatorData = true; | ||
205 | |||
206 | //if (node.Name == "OwnerID") | ||
207 | //{ | ||
208 | // UserAccount owner = GetUser(node.InnerText); | ||
209 | // if (owner != null) | ||
210 | // node.InnerText = m_ProfileServiceURL + "/" + node.InnerText + "/" + owner.FirstName + " " + owner.LastName; | ||
211 | //} | ||
212 | } | ||
213 | |||
214 | if (!hasCreatorData && creator != null) | ||
215 | { | ||
216 | XmlElement creatorData = doc.CreateElement("CreatorData"); | ||
217 | creatorData.InnerText = m_HomeURI + ";" + creator.FirstName + " " + creator.LastName; | ||
218 | sop.AppendChild(creatorData); | ||
219 | } | ||
220 | } | ||
221 | |||
222 | using (StringWriter wr = new StringWriter()) | ||
223 | { | ||
224 | doc.Save(wr); | ||
225 | return wr.ToString(); | ||
226 | } | ||
227 | 197 | ||
228 | } | 198 | } |
229 | 199 | ||
@@ -251,12 +221,13 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
251 | 221 | ||
252 | // The act of gathering UUIDs downloads some assets from the remote server | 222 | // The act of gathering UUIDs downloads some assets from the remote server |
253 | // but not all... | 223 | // but not all... |
254 | Dictionary<UUID, AssetType> ids = new Dictionary<UUID, AssetType>(); | ||
255 | HGUuidGatherer uuidGatherer = new HGUuidGatherer(m_scene.AssetService, userAssetURL); | 224 | HGUuidGatherer uuidGatherer = new HGUuidGatherer(m_scene.AssetService, userAssetURL); |
256 | uuidGatherer.GatherAssetUuids(assetID, (AssetType)meta.Type, ids); | 225 | uuidGatherer.AddForInspection(assetID); |
257 | m_log.DebugFormat("[HG ASSET MAPPER]: Preparing to get {0} assets", ids.Count); | 226 | uuidGatherer.GatherAll(); |
227 | |||
228 | m_log.DebugFormat("[HG ASSET MAPPER]: Preparing to get {0} assets", uuidGatherer.GatheredUuids.Count); | ||
258 | bool success = true; | 229 | bool success = true; |
259 | foreach (UUID uuid in ids.Keys) | 230 | foreach (UUID uuid in uuidGatherer.GatheredUuids.Keys) |
260 | if (FetchAsset(userAssetURL, uuid) == null) | 231 | if (FetchAsset(userAssetURL, uuid) == null) |
261 | success = false; | 232 | success = false; |
262 | 233 | ||
@@ -267,39 +238,92 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
267 | m_log.DebugFormat("[HG ASSET MAPPER]: Successfully got item {0} from asset server {1}", assetID, userAssetURL); | 238 | m_log.DebugFormat("[HG ASSET MAPPER]: Successfully got item {0} from asset server {1}", assetID, userAssetURL); |
268 | } | 239 | } |
269 | 240 | ||
270 | |||
271 | public void Post(UUID assetID, UUID ownerID, string userAssetURL) | 241 | public void Post(UUID assetID, UUID ownerID, string userAssetURL) |
272 | { | 242 | { |
273 | // Post the item from the local AssetCache onto the remote asset server | 243 | m_log.DebugFormat("[HG ASSET MAPPER]: Starting to send asset {0} with children to asset server {1}", assetID, userAssetURL); |
274 | // and place an entry in m_assetMap | 244 | |
245 | // Find all the embedded assets | ||
275 | 246 | ||
276 | m_log.Debug("[HG ASSET MAPPER]: Posting object " + assetID + " to asset server " + userAssetURL); | ||
277 | AssetBase asset = m_scene.AssetService.Get(assetID.ToString()); | 247 | AssetBase asset = m_scene.AssetService.Get(assetID.ToString()); |
278 | if (asset != null) | 248 | if (asset == null) |
249 | { | ||
250 | m_log.DebugFormat("[HG ASSET MAPPER]: Something wrong with asset {0}, it could not be found", assetID); | ||
251 | return; | ||
252 | } | ||
253 | |||
254 | HGUuidGatherer uuidGatherer = new HGUuidGatherer(m_scene.AssetService, string.Empty); | ||
255 | uuidGatherer.AddForInspection(asset.FullID); | ||
256 | uuidGatherer.GatherAll(); | ||
257 | |||
258 | // Check which assets already exist in the destination server | ||
259 | |||
260 | string url = userAssetURL; | ||
261 | if (!url.EndsWith("/") && !url.EndsWith("=")) | ||
262 | url = url + "/"; | ||
263 | |||
264 | string[] remoteAssetIDs = new string[uuidGatherer.GatheredUuids.Count]; | ||
265 | int i = 0; | ||
266 | foreach (UUID id in uuidGatherer.GatheredUuids.Keys) | ||
267 | remoteAssetIDs[i++] = url + id.ToString(); | ||
268 | |||
269 | bool[] exist = m_scene.AssetService.AssetsExist(remoteAssetIDs); | ||
270 | |||
271 | var existSet = new HashSet<string>(); | ||
272 | i = 0; | ||
273 | foreach (UUID id in uuidGatherer.GatheredUuids.Keys) | ||
274 | { | ||
275 | if (exist[i]) | ||
276 | existSet.Add(id.ToString()); | ||
277 | ++i; | ||
278 | } | ||
279 | |||
280 | // Send only those assets which don't already exist in the destination server | ||
281 | |||
282 | bool success = true; | ||
283 | |||
284 | foreach (UUID uuid in uuidGatherer.GatheredUuids.Keys) | ||
279 | { | 285 | { |
280 | Dictionary<UUID, AssetType> ids = new Dictionary<UUID, AssetType>(); | 286 | if (!existSet.Contains(uuid.ToString())) |
281 | HGUuidGatherer uuidGatherer = new HGUuidGatherer(m_scene.AssetService, string.Empty); | ||
282 | uuidGatherer.GatherAssetUuids(asset.FullID, (AssetType)asset.Type, ids); | ||
283 | bool success = false; | ||
284 | foreach (UUID uuid in ids.Keys) | ||
285 | { | 287 | { |
286 | asset = m_scene.AssetService.Get(uuid.ToString()); | 288 | asset = m_scene.AssetService.Get(uuid.ToString()); |
287 | if (asset == null) | 289 | if (asset == null) |
290 | { | ||
288 | m_log.DebugFormat("[HG ASSET MAPPER]: Could not find asset {0}", uuid); | 291 | m_log.DebugFormat("[HG ASSET MAPPER]: Could not find asset {0}", uuid); |
292 | } | ||
289 | else | 293 | else |
290 | success = PostAsset(userAssetURL, asset); | 294 | { |
295 | try | ||
296 | { | ||
297 | success &= PostAsset(userAssetURL, asset); | ||
298 | } | ||
299 | catch (Exception e) | ||
300 | { | ||
301 | m_log.Error( | ||
302 | string.Format( | ||
303 | "[HG ASSET MAPPER]: Failed to post asset {0} (type {1}, length {2}) referenced from {3} to {4} with exception ", | ||
304 | asset.ID, asset.Type, asset.Data.Length, assetID, userAssetURL), | ||
305 | e); | ||
306 | |||
307 | // For debugging purposes for now we will continue to throw the exception up the stack as was already happening. However, after | ||
308 | // debugging we may want to simply report the failure if we can tell this is due to a failure | ||
309 | // with a particular asset and not a destination network failure where all asset posts will fail (and | ||
310 | // generate large amounts of log spam). | ||
311 | throw e; | ||
312 | } | ||
313 | } | ||
291 | } | 314 | } |
292 | |||
293 | // maybe all pieces got there... | ||
294 | if (!success) | ||
295 | m_log.DebugFormat("[HG ASSET MAPPER]: Problems posting item {0} to asset server {1}", assetID, userAssetURL); | ||
296 | else | 315 | else |
297 | m_log.DebugFormat("[HG ASSET MAPPER]: Successfully posted item {0} to asset server {1}", assetID, userAssetURL); | 316 | { |
298 | 317 | m_log.DebugFormat( | |
318 | "[HG ASSET MAPPER]: Didn't post asset {0} referenced from {1} because it already exists in asset server {2}", | ||
319 | uuid, assetID, userAssetURL); | ||
320 | } | ||
299 | } | 321 | } |
300 | else | ||
301 | m_log.DebugFormat("[HG ASSET MAPPER]: Something wrong with asset {0}, it could not be found", assetID); | ||
302 | 322 | ||
323 | if (!success) | ||
324 | m_log.DebugFormat("[HG ASSET MAPPER]: Problems sending asset {0} with children to asset server {1}", assetID, userAssetURL); | ||
325 | else | ||
326 | m_log.DebugFormat("[HG ASSET MAPPER]: Successfully sent asset {0} with children to asset server {1}", assetID, userAssetURL); | ||
303 | } | 327 | } |
304 | 328 | ||
305 | #endregion | 329 | #endregion |
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs index 964efda..582b267 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs | |||
@@ -62,6 +62,17 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
62 | private string m_ThisGatekeeper; | 62 | private string m_ThisGatekeeper; |
63 | private bool m_RestrictInventoryAccessAbroad; | 63 | private bool m_RestrictInventoryAccessAbroad; |
64 | 64 | ||
65 | private bool m_bypassPermissions = true; | ||
66 | |||
67 | // This simple check makes it possible to support grids in which all the simulators | ||
68 | // share all central services of the Robust server EXCEPT assets. In other words, | ||
69 | // grids where the simulators' assets are kept in one DB and the users' inventory assets | ||
70 | // are kept on another. When users rez items from inventory or take objects from world, | ||
71 | // an HG-like asset copy takes place between the 2 servers, the world asset server and | ||
72 | // the user's asset server. | ||
73 | private bool m_CheckSeparateAssets = false; | ||
74 | private string m_LocalAssetsURL = string.Empty; | ||
75 | |||
65 | // private bool m_Initialized = false; | 76 | // private bool m_Initialized = false; |
66 | 77 | ||
67 | #region INonSharedRegionModule | 78 | #region INonSharedRegionModule |
@@ -88,16 +99,26 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
88 | IConfig thisModuleConfig = source.Configs["HGInventoryAccessModule"]; | 99 | IConfig thisModuleConfig = source.Configs["HGInventoryAccessModule"]; |
89 | if (thisModuleConfig != null) | 100 | if (thisModuleConfig != null) |
90 | { | 101 | { |
91 | // legacy configuration [obsolete] | 102 | m_HomeURI = Util.GetConfigVarFromSections<string>(source, "HomeURI", |
92 | m_HomeURI = thisModuleConfig.GetString("ProfileServerURI", string.Empty); | 103 | new string[] { "Startup", "Hypergrid", "HGInventoryAccessModule" }, String.Empty); |
93 | // preferred | 104 | m_ThisGatekeeper = Util.GetConfigVarFromSections<string>(source, "GatekeeperURI", |
94 | m_HomeURI = thisModuleConfig.GetString("HomeURI", m_HomeURI); | 105 | new string[] { "Startup", "Hypergrid", "HGInventoryAccessModule" }, String.Empty); |
106 | // Legacy. Renove soon! | ||
107 | m_ThisGatekeeper = thisModuleConfig.GetString("Gatekeeper", m_ThisGatekeeper); | ||
108 | |||
95 | m_OutboundPermission = thisModuleConfig.GetBoolean("OutboundPermission", true); | 109 | m_OutboundPermission = thisModuleConfig.GetBoolean("OutboundPermission", true); |
96 | m_ThisGatekeeper = thisModuleConfig.GetString("Gatekeeper", string.Empty); | ||
97 | m_RestrictInventoryAccessAbroad = thisModuleConfig.GetBoolean("RestrictInventoryAccessAbroad", true); | 110 | m_RestrictInventoryAccessAbroad = thisModuleConfig.GetBoolean("RestrictInventoryAccessAbroad", true); |
111 | m_CheckSeparateAssets = thisModuleConfig.GetBoolean("CheckSeparateAssets", false); | ||
112 | m_LocalAssetsURL = thisModuleConfig.GetString("RegionHGAssetServerURI", string.Empty); | ||
113 | m_LocalAssetsURL = m_LocalAssetsURL.Trim(new char[] { '/' }); | ||
114 | |||
98 | } | 115 | } |
99 | else | 116 | else |
100 | m_log.Warn("[HG INVENTORY ACCESS MODULE]: HGInventoryAccessModule configs not found. ProfileServerURI not set!"); | 117 | m_log.Warn("[HG INVENTORY ACCESS MODULE]: HGInventoryAccessModule configs not found. ProfileServerURI not set!"); |
118 | |||
119 | m_bypassPermissions = !Util.GetConfigVarFromSections<bool>(source, "serverside_object_permissions", | ||
120 | new string[] { "Startup", "Permissions" }, true); | ||
121 | |||
101 | } | 122 | } |
102 | } | 123 | } |
103 | } | 124 | } |
@@ -109,9 +130,14 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
109 | 130 | ||
110 | base.AddRegion(scene); | 131 | base.AddRegion(scene); |
111 | m_assMapper = new HGAssetMapper(scene, m_HomeURI); | 132 | m_assMapper = new HGAssetMapper(scene, m_HomeURI); |
112 | scene.EventManager.OnNewInventoryItemUploadComplete += UploadInventoryItem; | 133 | scene.EventManager.OnNewInventoryItemUploadComplete += PostInventoryAsset; |
113 | scene.EventManager.OnTeleportStart += TeleportStart; | 134 | scene.EventManager.OnTeleportStart += TeleportStart; |
114 | scene.EventManager.OnTeleportFail += TeleportFail; | 135 | scene.EventManager.OnTeleportFail += TeleportFail; |
136 | |||
137 | // We're fgoing to enforce some stricter permissions if Outbound is false | ||
138 | scene.Permissions.OnTakeObject += CanTakeObject; | ||
139 | scene.Permissions.OnTakeCopyObject += CanTakeObject; | ||
140 | scene.Permissions.OnTransferUserInventory += OnTransferUserInventory; | ||
115 | } | 141 | } |
116 | 142 | ||
117 | #endregion | 143 | #endregion |
@@ -133,7 +159,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
133 | if (sp is ScenePresence) | 159 | if (sp is ScenePresence) |
134 | { | 160 | { |
135 | AgentCircuitData aCircuit = ((ScenePresence)sp).Scene.AuthenticateHandler.GetAgentCircuitData(client.AgentId); | 161 | AgentCircuitData aCircuit = ((ScenePresence)sp).Scene.AuthenticateHandler.GetAgentCircuitData(client.AgentId); |
136 | if ((aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0) | 162 | if (aCircuit != null && (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0) |
137 | { | 163 | { |
138 | if (m_RestrictInventoryAccessAbroad) | 164 | if (m_RestrictInventoryAccessAbroad) |
139 | { | 165 | { |
@@ -183,8 +209,11 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
183 | } | 209 | } |
184 | } | 210 | } |
185 | 211 | ||
186 | public void UploadInventoryItem(UUID avatarID, UUID assetID, string name, int userlevel) | 212 | public void PostInventoryAsset(UUID avatarID, AssetType type, UUID assetID, string name, int userlevel) |
187 | { | 213 | { |
214 | if (type == AssetType.Link) | ||
215 | return; | ||
216 | |||
188 | string userAssetServer = string.Empty; | 217 | string userAssetServer = string.Empty; |
189 | if (IsForeignUser(avatarID, out userAssetServer) && userAssetServer != string.Empty && m_OutboundPermission) | 218 | if (IsForeignUser(avatarID, out userAssetServer) && userAssetServer != string.Empty && m_OutboundPermission) |
190 | { | 219 | { |
@@ -219,18 +248,32 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
219 | { | 248 | { |
220 | UUID newAssetID = base.CapsUpdateInventoryItemAsset(remoteClient, itemID, data); | 249 | UUID newAssetID = base.CapsUpdateInventoryItemAsset(remoteClient, itemID, data); |
221 | 250 | ||
222 | UploadInventoryItem(remoteClient.AgentId, newAssetID, "", 0); | 251 | PostInventoryAsset(remoteClient.AgentId, AssetType.Unknown, newAssetID, "", 0); |
223 | 252 | ||
224 | return newAssetID; | 253 | return newAssetID; |
225 | } | 254 | } |
226 | 255 | ||
256 | /// | ||
257 | /// UpdateInventoryItemAsset | ||
258 | /// | ||
259 | public override bool UpdateInventoryItemAsset(UUID ownerID, InventoryItemBase item, AssetBase asset) | ||
260 | { | ||
261 | if (base.UpdateInventoryItemAsset(ownerID, item, asset)) | ||
262 | { | ||
263 | PostInventoryAsset(ownerID, (AssetType)asset.Type, asset.FullID, asset.Name, 0); | ||
264 | return true; | ||
265 | } | ||
266 | |||
267 | return false; | ||
268 | } | ||
269 | |||
227 | /// | 270 | /// |
228 | /// Used in DeleteToInventory | 271 | /// Used in DeleteToInventory |
229 | /// | 272 | /// |
230 | protected override void ExportAsset(UUID agentID, UUID assetID) | 273 | protected override void ExportAsset(UUID agentID, UUID assetID) |
231 | { | 274 | { |
232 | if (!assetID.Equals(UUID.Zero)) | 275 | if (!assetID.Equals(UUID.Zero)) |
233 | UploadInventoryItem(agentID, assetID, "", 0); | 276 | PostInventoryAsset(agentID, AssetType.Unknown, assetID, "", 0); |
234 | else | 277 | else |
235 | m_log.Debug("[HGScene]: Scene.Inventory did not create asset"); | 278 | m_log.Debug("[HGScene]: Scene.Inventory did not create asset"); |
236 | } | 279 | } |
@@ -242,7 +285,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
242 | UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, | 285 | UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, |
243 | bool RezSelected, bool RemoveItem, UUID fromTaskID, bool attachment) | 286 | bool RezSelected, bool RemoveItem, UUID fromTaskID, bool attachment) |
244 | { | 287 | { |
245 | m_log.DebugFormat("[HGScene] RezObject itemID={0} fromTaskID={1}", itemID, fromTaskID); | 288 | m_log.DebugFormat("[HGScene]: RezObject itemID={0} fromTaskID={1}", itemID, fromTaskID); |
246 | 289 | ||
247 | //if (fromTaskID.Equals(UUID.Zero)) | 290 | //if (fromTaskID.Equals(UUID.Zero)) |
248 | //{ | 291 | //{ |
@@ -268,50 +311,98 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
268 | SceneObjectGroup sog = base.RezObject(remoteClient, itemID, RayEnd, RayStart, RayTargetID, BypassRayCast, RayEndIsIntersection, | 311 | SceneObjectGroup sog = base.RezObject(remoteClient, itemID, RayEnd, RayStart, RayTargetID, BypassRayCast, RayEndIsIntersection, |
269 | RezSelected, RemoveItem, fromTaskID, attachment); | 312 | RezSelected, RemoveItem, fromTaskID, attachment); |
270 | 313 | ||
271 | if (sog == null) | ||
272 | remoteClient.SendAgentAlertMessage("Unable to rez: problem accessing inventory or locating assets", false); | ||
273 | |||
274 | return sog; | 314 | return sog; |
275 | 315 | ||
276 | } | 316 | } |
277 | 317 | ||
278 | public override void TransferInventoryAssets(InventoryItemBase item, UUID sender, UUID receiver) | 318 | public override void TransferInventoryAssets(InventoryItemBase item, UUID sender, UUID receiver) |
279 | { | 319 | { |
280 | string userAssetServer = string.Empty; | 320 | string senderAssetServer = string.Empty; |
281 | if (IsForeignUser(sender, out userAssetServer) && userAssetServer != string.Empty) | 321 | string receiverAssetServer = string.Empty; |
282 | m_assMapper.Get(item.AssetID, sender, userAssetServer); | 322 | bool isForeignSender, isForeignReceiver; |
323 | isForeignSender = IsForeignUser(sender, out senderAssetServer); | ||
324 | isForeignReceiver = IsForeignUser(receiver, out receiverAssetServer); | ||
325 | |||
326 | // They're both local. Nothing to do. | ||
327 | if (!isForeignSender && !isForeignReceiver) | ||
328 | return; | ||
329 | |||
330 | // At least one of them is foreign. | ||
331 | // If both users have the same asset server, no need to transfer the asset | ||
332 | if (senderAssetServer.Equals(receiverAssetServer)) | ||
333 | { | ||
334 | m_log.DebugFormat("[HGScene]: Asset transfer between foreign users, but they have the same server. No transfer."); | ||
335 | return; | ||
336 | } | ||
337 | |||
338 | if (isForeignSender && senderAssetServer != string.Empty) | ||
339 | m_assMapper.Get(item.AssetID, sender, senderAssetServer); | ||
283 | 340 | ||
284 | if (IsForeignUser(receiver, out userAssetServer) && userAssetServer != string.Empty && m_OutboundPermission) | 341 | if (isForeignReceiver && receiverAssetServer != string.Empty && m_OutboundPermission) |
285 | m_assMapper.Post(item.AssetID, receiver, userAssetServer); | 342 | m_assMapper.Post(item.AssetID, receiver, receiverAssetServer); |
286 | } | 343 | } |
287 | 344 | ||
288 | public override bool IsForeignUser(UUID userID, out string assetServerURL) | 345 | public override bool IsForeignUser(UUID userID, out string assetServerURL) |
289 | { | 346 | { |
290 | assetServerURL = string.Empty; | 347 | assetServerURL = string.Empty; |
291 | 348 | ||
292 | if (UserManagementModule != null && !UserManagementModule.IsLocalGridUser(userID)) | 349 | if (UserManagementModule != null) |
293 | { // foreign | 350 | { |
294 | ScenePresence sp = null; | 351 | if (!m_CheckSeparateAssets) |
295 | if (m_Scene.TryGetScenePresence(userID, out sp)) | ||
296 | { | 352 | { |
297 | AgentCircuitData aCircuit = m_Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); | 353 | if (!UserManagementModule.IsLocalGridUser(userID)) |
298 | if (aCircuit.ServiceURLs.ContainsKey("AssetServerURI")) | 354 | { // foreign |
299 | { | 355 | ScenePresence sp = null; |
300 | assetServerURL = aCircuit.ServiceURLs["AssetServerURI"].ToString(); | 356 | if (m_Scene.TryGetScenePresence(userID, out sp)) |
301 | assetServerURL = assetServerURL.Trim(new char[] { '/' }); | 357 | { |
358 | AgentCircuitData aCircuit = m_Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); | ||
359 | if (aCircuit != null && aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("AssetServerURI")) | ||
360 | { | ||
361 | assetServerURL = aCircuit.ServiceURLs["AssetServerURI"].ToString(); | ||
362 | assetServerURL = assetServerURL.Trim(new char[] { '/' }); | ||
363 | } | ||
364 | } | ||
365 | else | ||
366 | { | ||
367 | assetServerURL = UserManagementModule.GetUserServerURL(userID, "AssetServerURI"); | ||
368 | assetServerURL = assetServerURL.Trim(new char[] { '/' }); | ||
369 | } | ||
370 | return true; | ||
302 | } | 371 | } |
303 | } | 372 | } |
304 | else | 373 | else |
305 | { | 374 | { |
306 | assetServerURL = UserManagementModule.GetUserServerURL(userID, "AssetServerURI"); | 375 | if (IsLocalInventoryAssetsUser(userID, out assetServerURL)) |
307 | assetServerURL = assetServerURL.Trim(new char[] { '/' }); | 376 | { |
377 | m_log.DebugFormat("[HGScene]: user {0} has local assets {1}", userID, assetServerURL); | ||
378 | return false; | ||
379 | } | ||
380 | else | ||
381 | { | ||
382 | m_log.DebugFormat("[HGScene]: user {0} has foreign assets {1}", userID, assetServerURL); | ||
383 | return true; | ||
384 | } | ||
308 | } | 385 | } |
309 | return true; | ||
310 | } | 386 | } |
311 | |||
312 | return false; | 387 | return false; |
313 | } | 388 | } |
314 | 389 | ||
390 | private bool IsLocalInventoryAssetsUser(UUID uuid, out string assetsURL) | ||
391 | { | ||
392 | assetsURL = UserManagementModule.GetUserServerURL(uuid, "AssetServerURI"); | ||
393 | if (assetsURL == string.Empty) | ||
394 | { | ||
395 | AgentCircuitData agent = m_Scene.AuthenticateHandler.GetAgentCircuitData(uuid); | ||
396 | if (agent != null) | ||
397 | { | ||
398 | assetsURL = agent.ServiceURLs["AssetServerURI"].ToString(); | ||
399 | assetsURL = assetsURL.Trim(new char[] { '/' }); | ||
400 | } | ||
401 | } | ||
402 | return m_LocalAssetsURL.Equals(assetsURL); | ||
403 | } | ||
404 | |||
405 | |||
315 | protected override InventoryItemBase GetItem(UUID agentID, UUID itemID) | 406 | protected override InventoryItemBase GetItem(UUID agentID, UUID itemID) |
316 | { | 407 | { |
317 | InventoryItemBase item = base.GetItem(agentID, itemID); | 408 | InventoryItemBase item = base.GetItem(agentID, itemID); |
@@ -346,7 +437,15 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
346 | InventoryFolderBase root = m_Scene.InventoryService.GetRootFolder(client.AgentId); | 437 | InventoryFolderBase root = m_Scene.InventoryService.GetRootFolder(client.AgentId); |
347 | InventoryCollection content = m_Scene.InventoryService.GetFolderContent(client.AgentId, root.ID); | 438 | InventoryCollection content = m_Scene.InventoryService.GetFolderContent(client.AgentId, root.ID); |
348 | 439 | ||
349 | inv.SendBulkUpdateInventory(content.Folders.ToArray(), content.Items.ToArray()); | 440 | List<InventoryFolderBase> keep = new List<InventoryFolderBase>(); |
441 | |||
442 | foreach (InventoryFolderBase f in content.Folders) | ||
443 | { | ||
444 | if (f.Name != "My Suitcase" && f.Name != "Current Outfit") | ||
445 | keep.Add(f); | ||
446 | } | ||
447 | |||
448 | inv.SendBulkUpdateInventory(keep.ToArray(), content.Items.ToArray()); | ||
350 | } | 449 | } |
351 | } | 450 | } |
352 | } | 451 | } |
@@ -379,7 +478,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
379 | 478 | ||
380 | foreach (InventoryFolderBase f in content.Folders) | 479 | foreach (InventoryFolderBase f in content.Folders) |
381 | { | 480 | { |
382 | if (f.Name != "My Suitcase") | 481 | if (f.Name != "My Suitcase" && f.Name != "Current Outfit") |
383 | { | 482 | { |
384 | f.Name = f.Name + " (Unavailable)"; | 483 | f.Name = f.Name + " (Unavailable)"; |
385 | keep.Add(f); | 484 | keep.Add(f); |
@@ -404,5 +503,36 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
404 | } | 503 | } |
405 | 504 | ||
406 | #endregion | 505 | #endregion |
506 | |||
507 | #region Permissions | ||
508 | |||
509 | private bool CanTakeObject(UUID objectID, UUID stealer, Scene scene) | ||
510 | { | ||
511 | if (m_bypassPermissions) return true; | ||
512 | |||
513 | if (!m_OutboundPermission && !UserManagementModule.IsLocalGridUser(stealer)) | ||
514 | { | ||
515 | SceneObjectGroup sog = null; | ||
516 | if (m_Scene.TryGetSceneObjectGroup(objectID, out sog) && sog.OwnerID == stealer) | ||
517 | return true; | ||
518 | |||
519 | return false; | ||
520 | } | ||
521 | |||
522 | return true; | ||
523 | } | ||
524 | |||
525 | private bool OnTransferUserInventory(UUID itemID, UUID userID, UUID recipientID) | ||
526 | { | ||
527 | if (m_bypassPermissions) return true; | ||
528 | |||
529 | if (!m_OutboundPermission && !UserManagementModule.IsLocalGridUser(recipientID)) | ||
530 | return false; | ||
531 | |||
532 | return true; | ||
533 | } | ||
534 | |||
535 | |||
536 | #endregion | ||
407 | } | 537 | } |
408 | } \ No newline at end of file | 538 | } \ No newline at end of file |
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index 8b7c16e..5a9efb8 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs | |||
@@ -47,6 +47,7 @@ using OpenMetaverse; | |||
47 | using log4net; | 47 | using log4net; |
48 | using Nini.Config; | 48 | using Nini.Config; |
49 | using Mono.Addins; | 49 | using Mono.Addins; |
50 | using PermissionMask = OpenSim.Framework.PermissionMask; | ||
50 | 51 | ||
51 | namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | 52 | namespace OpenSim.Region.CoreModules.Framework.InventoryAccess |
52 | { | 53 | { |
@@ -202,7 +203,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
202 | m_Scene.AssetService.Store(asset); | 203 | m_Scene.AssetService.Store(asset); |
203 | m_Scene.CreateNewInventoryItem( | 204 | m_Scene.CreateNewInventoryItem( |
204 | remoteClient, remoteClient.AgentId.ToString(), string.Empty, folderID, | 205 | remoteClient, remoteClient.AgentId.ToString(), string.Empty, folderID, |
205 | name, description, 0, callbackID, asset, invType, nextOwnerMask, creationDate); | 206 | name, description, 0, callbackID, asset.FullID, asset.Type, invType, nextOwnerMask, creationDate); |
206 | } | 207 | } |
207 | else | 208 | else |
208 | { | 209 | { |
@@ -259,7 +260,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
259 | return UUID.Zero; | 260 | return UUID.Zero; |
260 | } | 261 | } |
261 | 262 | ||
262 | remoteClient.SendAgentAlertMessage("Notecard saved", false); | 263 | remoteClient.SendAlertMessage("Notecard saved"); |
263 | } | 264 | } |
264 | else if ((InventoryType)item.InvType == InventoryType.LSL) | 265 | else if ((InventoryType)item.InvType == InventoryType.LSL) |
265 | { | 266 | { |
@@ -269,7 +270,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
269 | return UUID.Zero; | 270 | return UUID.Zero; |
270 | } | 271 | } |
271 | 272 | ||
272 | remoteClient.SendAgentAlertMessage("Script saved", false); | 273 | remoteClient.SendAlertMessage("Script saved"); |
273 | } | 274 | } |
274 | 275 | ||
275 | AssetBase asset = | 276 | AssetBase asset = |
@@ -291,7 +292,34 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
291 | 292 | ||
292 | return UUID.Zero; | 293 | return UUID.Zero; |
293 | } | 294 | } |
294 | 295 | ||
296 | public virtual bool UpdateInventoryItemAsset(UUID ownerID, InventoryItemBase item, AssetBase asset) | ||
297 | { | ||
298 | if (item != null && item.Owner == ownerID && asset != null) | ||
299 | { | ||
300 | // m_log.DebugFormat( | ||
301 | // "[INVENTORY ACCESS MODULE]: Updating item {0} {1} with new asset {2}", | ||
302 | // item.Name, item.ID, asset.ID); | ||
303 | |||
304 | item.AssetID = asset.FullID; | ||
305 | item.Description = asset.Description; | ||
306 | item.Name = asset.Name; | ||
307 | item.AssetType = asset.Type; | ||
308 | item.InvType = (int)InventoryType.Object; | ||
309 | |||
310 | m_Scene.AssetService.Store(asset); | ||
311 | m_Scene.InventoryService.UpdateItem(item); | ||
312 | |||
313 | return true; | ||
314 | } | ||
315 | else | ||
316 | { | ||
317 | m_log.ErrorFormat("[INVENTORY ACCESS MODULE]: Given invalid item for inventory update: {0}", | ||
318 | (item == null || asset == null? "null item or asset" : "wrong owner")); | ||
319 | return false; | ||
320 | } | ||
321 | } | ||
322 | |||
295 | public virtual List<InventoryItemBase> CopyToInventory( | 323 | public virtual List<InventoryItemBase> CopyToInventory( |
296 | DeRezAction action, UUID folderID, | 324 | DeRezAction action, UUID folderID, |
297 | List<SceneObjectGroup> objectGroups, IClientAPI remoteClient, bool asAttachment) | 325 | List<SceneObjectGroup> objectGroups, IClientAPI remoteClient, bool asAttachment) |
@@ -352,23 +380,32 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
352 | bool asAttachment) | 380 | bool asAttachment) |
353 | { | 381 | { |
354 | CoalescedSceneObjects coa = new CoalescedSceneObjects(UUID.Zero); | 382 | CoalescedSceneObjects coa = new CoalescedSceneObjects(UUID.Zero); |
355 | Dictionary<UUID, Vector3> originalPositions = new Dictionary<UUID, Vector3>(); | 383 | // Dictionary<UUID, Vector3> originalPositions = new Dictionary<UUID, Vector3>(); |
384 | |||
385 | Dictionary<SceneObjectGroup, KeyframeMotion> group2Keyframe = new Dictionary<SceneObjectGroup, KeyframeMotion>(); | ||
356 | 386 | ||
357 | foreach (SceneObjectGroup objectGroup in objlist) | 387 | foreach (SceneObjectGroup objectGroup in objlist) |
358 | { | 388 | { |
359 | Vector3 inventoryStoredPosition = new Vector3 | 389 | if (objectGroup.RootPart.KeyframeMotion != null) |
360 | (((objectGroup.AbsolutePosition.X > (int)Constants.RegionSize) | 390 | { |
361 | ? 250 | 391 | objectGroup.RootPart.KeyframeMotion.Pause(); |
362 | : objectGroup.AbsolutePosition.X) | 392 | group2Keyframe.Add(objectGroup, objectGroup.RootPart.KeyframeMotion); |
363 | , | 393 | objectGroup.RootPart.KeyframeMotion = null; |
364 | (objectGroup.AbsolutePosition.Y > (int)Constants.RegionSize) | 394 | } |
365 | ? 250 | ||
366 | : objectGroup.AbsolutePosition.Y, | ||
367 | objectGroup.AbsolutePosition.Z); | ||
368 | |||
369 | originalPositions[objectGroup.UUID] = objectGroup.AbsolutePosition; | ||
370 | 395 | ||
371 | objectGroup.AbsolutePosition = inventoryStoredPosition; | 396 | // Vector3 inventoryStoredPosition = new Vector3 |
397 | // (((objectGroup.AbsolutePosition.X > (int)Constants.RegionSize) | ||
398 | // ? 250 | ||
399 | // : objectGroup.AbsolutePosition.X) | ||
400 | // , | ||
401 | // (objectGroup.AbsolutePosition.Y > (int)Constants.RegionSize) | ||
402 | // ? 250 | ||
403 | // : objectGroup.AbsolutePosition.Y, | ||
404 | // objectGroup.AbsolutePosition.Z); | ||
405 | // | ||
406 | // originalPositions[objectGroup.UUID] = objectGroup.AbsolutePosition; | ||
407 | // | ||
408 | // objectGroup.AbsolutePosition = inventoryStoredPosition; | ||
372 | 409 | ||
373 | // Make sure all bits but the ones we want are clear | 410 | // Make sure all bits but the ones we want are clear |
374 | // on take. | 411 | // on take. |
@@ -377,7 +414,8 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
377 | objectGroup.RootPart.NextOwnerMask &= | 414 | objectGroup.RootPart.NextOwnerMask &= |
378 | ((uint)PermissionMask.Copy | | 415 | ((uint)PermissionMask.Copy | |
379 | (uint)PermissionMask.Transfer | | 416 | (uint)PermissionMask.Transfer | |
380 | (uint)PermissionMask.Modify); | 417 | (uint)PermissionMask.Modify | |
418 | (uint)PermissionMask.Export); | ||
381 | objectGroup.RootPart.NextOwnerMask |= | 419 | objectGroup.RootPart.NextOwnerMask |= |
382 | (uint)PermissionMask.Move; | 420 | (uint)PermissionMask.Move; |
383 | 421 | ||
@@ -395,9 +433,9 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
395 | else | 433 | else |
396 | itemXml = SceneObjectSerializer.ToOriginalXmlFormat(objlist[0], !asAttachment); | 434 | itemXml = SceneObjectSerializer.ToOriginalXmlFormat(objlist[0], !asAttachment); |
397 | 435 | ||
398 | // Restore the position of each group now that it has been stored to inventory. | 436 | // // Restore the position of each group now that it has been stored to inventory. |
399 | foreach (SceneObjectGroup objectGroup in objlist) | 437 | // foreach (SceneObjectGroup objectGroup in objlist) |
400 | objectGroup.AbsolutePosition = originalPositions[objectGroup.UUID]; | 438 | // objectGroup.AbsolutePosition = originalPositions[objectGroup.UUID]; |
401 | 439 | ||
402 | InventoryItemBase item = CreateItemForObject(action, remoteClient, objlist[0], folderID); | 440 | InventoryItemBase item = CreateItemForObject(action, remoteClient, objlist[0], folderID); |
403 | 441 | ||
@@ -407,17 +445,28 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
407 | 445 | ||
408 | if (item == null) | 446 | if (item == null) |
409 | return null; | 447 | return null; |
448 | |||
449 | item.CreatorId = objlist[0].RootPart.CreatorID.ToString(); | ||
450 | item.CreatorData = objlist[0].RootPart.CreatorData; | ||
410 | 451 | ||
411 | // Can't know creator is the same, so null it in inventory | ||
412 | if (objlist.Count > 1) | 452 | if (objlist.Count > 1) |
413 | { | 453 | { |
414 | item.CreatorId = UUID.Zero.ToString(); | ||
415 | item.Flags = (uint)InventoryItemFlags.ObjectHasMultipleItems; | 454 | item.Flags = (uint)InventoryItemFlags.ObjectHasMultipleItems; |
455 | |||
456 | // If the objects have different creators then don't specify a creator at all | ||
457 | foreach (SceneObjectGroup objectGroup in objlist) | ||
458 | { | ||
459 | if ((objectGroup.RootPart.CreatorID.ToString() != item.CreatorId) | ||
460 | || (objectGroup.RootPart.CreatorData.ToString() != item.CreatorData)) | ||
461 | { | ||
462 | item.CreatorId = UUID.Zero.ToString(); | ||
463 | item.CreatorData = string.Empty; | ||
464 | break; | ||
465 | } | ||
466 | } | ||
416 | } | 467 | } |
417 | else | 468 | else |
418 | { | 469 | { |
419 | item.CreatorId = objlist[0].RootPart.CreatorID.ToString(); | ||
420 | item.CreatorData = objlist[0].RootPart.CreatorData; | ||
421 | item.SaleType = objlist[0].RootPart.ObjectSaleType; | 470 | item.SaleType = objlist[0].RootPart.ObjectSaleType; |
422 | item.SalePrice = objlist[0].RootPart.SalePrice; | 471 | item.SalePrice = objlist[0].RootPart.SalePrice; |
423 | } | 472 | } |
@@ -438,13 +487,13 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
438 | } | 487 | } |
439 | else | 488 | else |
440 | { | 489 | { |
441 | AddPermissions(item, objlist[0], objlist, remoteClient); | ||
442 | |||
443 | item.CreationDate = Util.UnixTimeSinceEpoch(); | 490 | item.CreationDate = Util.UnixTimeSinceEpoch(); |
444 | item.Description = asset.Description; | 491 | item.Description = asset.Description; |
445 | item.Name = asset.Name; | 492 | item.Name = asset.Name; |
446 | item.AssetType = asset.Type; | 493 | item.AssetType = asset.Type; |
447 | 494 | ||
495 | AddPermissions(item, objlist[0], objlist, remoteClient); | ||
496 | |||
448 | m_Scene.AddInventoryItem(item); | 497 | m_Scene.AddInventoryItem(item); |
449 | 498 | ||
450 | if (remoteClient != null && item.Owner == remoteClient.AgentId) | 499 | if (remoteClient != null && item.Owner == remoteClient.AgentId) |
@@ -461,6 +510,13 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
461 | } | 510 | } |
462 | } | 511 | } |
463 | 512 | ||
513 | // Restore KeyframeMotion | ||
514 | foreach (SceneObjectGroup objectGroup in group2Keyframe.Keys) | ||
515 | { | ||
516 | objectGroup.RootPart.KeyframeMotion = group2Keyframe[objectGroup]; | ||
517 | objectGroup.RootPart.KeyframeMotion.Start(); | ||
518 | } | ||
519 | |||
464 | // This is a hook to do some per-asset post-processing for subclasses that need that | 520 | // This is a hook to do some per-asset post-processing for subclasses that need that |
465 | if (remoteClient != null) | 521 | if (remoteClient != null) |
466 | ExportAsset(remoteClient.AgentId, asset.FullID); | 522 | ExportAsset(remoteClient.AgentId, asset.FullID); |
@@ -485,49 +541,65 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
485 | InventoryItemBase item, SceneObjectGroup so, List<SceneObjectGroup> objsForEffectivePermissions, | 541 | InventoryItemBase item, SceneObjectGroup so, List<SceneObjectGroup> objsForEffectivePermissions, |
486 | IClientAPI remoteClient) | 542 | IClientAPI remoteClient) |
487 | { | 543 | { |
488 | uint effectivePerms = (uint)(PermissionMask.Copy | PermissionMask.Transfer | PermissionMask.Modify | PermissionMask.Move) | 7; | 544 | uint effectivePerms = (uint)(PermissionMask.Copy | PermissionMask.Transfer | PermissionMask.Modify | PermissionMask.Move | PermissionMask.Export) | 7; |
545 | uint allObjectsNextOwnerPerms = 0x7fffffff; | ||
546 | uint allObjectsEveryOnePerms = 0x7fffffff; | ||
547 | uint allObjectsGroupPerms = 0x7fffffff; | ||
548 | |||
489 | foreach (SceneObjectGroup grp in objsForEffectivePermissions) | 549 | foreach (SceneObjectGroup grp in objsForEffectivePermissions) |
550 | { | ||
490 | effectivePerms &= grp.GetEffectivePermissions(); | 551 | effectivePerms &= grp.GetEffectivePermissions(); |
552 | allObjectsNextOwnerPerms &= grp.RootPart.NextOwnerMask; | ||
553 | allObjectsEveryOnePerms &= grp.RootPart.EveryoneMask; | ||
554 | allObjectsGroupPerms &= grp.RootPart.GroupMask; | ||
555 | } | ||
491 | effectivePerms |= (uint)PermissionMask.Move; | 556 | effectivePerms |= (uint)PermissionMask.Move; |
492 | 557 | ||
558 | //PermissionsUtil.LogPermissions(item.Name, "Before AddPermissions", item.BasePermissions, item.CurrentPermissions, item.NextPermissions); | ||
559 | |||
493 | if (remoteClient != null && (remoteClient.AgentId != so.RootPart.OwnerID) && m_Scene.Permissions.PropagatePermissions()) | 560 | if (remoteClient != null && (remoteClient.AgentId != so.RootPart.OwnerID) && m_Scene.Permissions.PropagatePermissions()) |
494 | { | 561 | { |
562 | // Changing ownership, so apply the "Next Owner" permissions to all of the | ||
563 | // inventory item's permissions. | ||
564 | |||
495 | uint perms = effectivePerms; | 565 | uint perms = effectivePerms; |
496 | uint nextPerms = (perms & 7) << 13; | 566 | PermissionsUtil.ApplyFoldedPermissions(effectivePerms, ref perms); |
497 | if ((nextPerms & (uint)PermissionMask.Copy) == 0) | 567 | |
498 | perms &= ~(uint)PermissionMask.Copy; | 568 | item.BasePermissions = perms & allObjectsNextOwnerPerms; |
499 | if ((nextPerms & (uint)PermissionMask.Transfer) == 0) | ||
500 | perms &= ~(uint)PermissionMask.Transfer; | ||
501 | if ((nextPerms & (uint)PermissionMask.Modify) == 0) | ||
502 | perms &= ~(uint)PermissionMask.Modify; | ||
503 | |||
504 | item.BasePermissions = perms & so.RootPart.NextOwnerMask; | ||
505 | item.CurrentPermissions = item.BasePermissions; | 569 | item.CurrentPermissions = item.BasePermissions; |
506 | item.NextPermissions = perms & so.RootPart.NextOwnerMask; | 570 | item.NextPermissions = perms & allObjectsNextOwnerPerms; |
507 | item.EveryOnePermissions = so.RootPart.EveryoneMask & so.RootPart.NextOwnerMask; | 571 | item.EveryOnePermissions = allObjectsEveryOnePerms & allObjectsNextOwnerPerms; |
508 | item.GroupPermissions = so.RootPart.GroupMask & so.RootPart.NextOwnerMask; | 572 | item.GroupPermissions = allObjectsGroupPerms & allObjectsNextOwnerPerms; |
509 | 573 | ||
510 | // Magic number badness. Maybe this deserves an enum. | 574 | // apply next owner perms on rez |
511 | // bit 4 (16) is the "Slam" bit, it means treat as passed | 575 | item.CurrentPermissions |= SceneObjectGroup.SLAM; |
512 | // and apply next owner perms on rez | ||
513 | item.CurrentPermissions |= 16; // Slam! | ||
514 | } | 576 | } |
515 | else | 577 | else |
516 | { | 578 | { |
579 | // Not changing ownership. | ||
580 | // In this case we apply the permissions in the object's items ONLY to the inventory | ||
581 | // item's "Next Owner" permissions, but NOT to its "Current", "Base", etc. permissions. | ||
582 | // E.g., if the object contains a No-Transfer item then the item's "Next Owner" | ||
583 | // permissions are also No-Transfer. | ||
584 | PermissionsUtil.ApplyFoldedPermissions(effectivePerms, ref allObjectsNextOwnerPerms); | ||
585 | |||
517 | item.BasePermissions = effectivePerms; | 586 | item.BasePermissions = effectivePerms; |
518 | item.CurrentPermissions = effectivePerms; | 587 | item.CurrentPermissions = effectivePerms; |
519 | item.NextPermissions = so.RootPart.NextOwnerMask & effectivePerms; | 588 | item.NextPermissions = allObjectsNextOwnerPerms & effectivePerms; |
520 | item.EveryOnePermissions = so.RootPart.EveryoneMask & effectivePerms; | 589 | item.EveryOnePermissions = allObjectsEveryOnePerms & effectivePerms; |
521 | item.GroupPermissions = so.RootPart.GroupMask & effectivePerms; | 590 | item.GroupPermissions = allObjectsGroupPerms & effectivePerms; |
522 | 591 | ||
523 | item.CurrentPermissions &= | 592 | item.CurrentPermissions &= |
524 | ((uint)PermissionMask.Copy | | 593 | ((uint)PermissionMask.Copy | |
525 | (uint)PermissionMask.Transfer | | 594 | (uint)PermissionMask.Transfer | |
526 | (uint)PermissionMask.Modify | | 595 | (uint)PermissionMask.Modify | |
527 | (uint)PermissionMask.Move | | 596 | (uint)PermissionMask.Move | |
597 | (uint)PermissionMask.Export | | ||
528 | 7); // Preserve folded permissions | 598 | 7); // Preserve folded permissions |
529 | } | 599 | } |
530 | 600 | ||
601 | //PermissionsUtil.LogPermissions(item.Name, "After AddPermissions", item.BasePermissions, item.CurrentPermissions, item.NextPermissions); | ||
602 | |||
531 | return item; | 603 | return item; |
532 | } | 604 | } |
533 | 605 | ||
@@ -542,6 +614,10 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
542 | protected InventoryItemBase CreateItemForObject( | 614 | protected InventoryItemBase CreateItemForObject( |
543 | DeRezAction action, IClientAPI remoteClient, SceneObjectGroup so, UUID folderID) | 615 | DeRezAction action, IClientAPI remoteClient, SceneObjectGroup so, UUID folderID) |
544 | { | 616 | { |
617 | // m_log.DebugFormat( | ||
618 | // "[BASIC INVENTORY ACCESS MODULE]: Creating item for object {0} {1} for folder {2}, action {3}", | ||
619 | // so.Name, so.UUID, folderID, action); | ||
620 | // | ||
545 | // Get the user info of the item destination | 621 | // Get the user info of the item destination |
546 | // | 622 | // |
547 | UUID userID = UUID.Zero; | 623 | UUID userID = UUID.Zero; |
@@ -619,18 +695,18 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
619 | if (remoteClient == null || | 695 | if (remoteClient == null || |
620 | so.OwnerID != remoteClient.AgentId) | 696 | so.OwnerID != remoteClient.AgentId) |
621 | { | 697 | { |
622 | folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.LostAndFoundFolder); | 698 | folder = m_Scene.InventoryService.GetFolderForType(userID, FolderType.LostAndFound); |
623 | } | 699 | } |
624 | else | 700 | else |
625 | { | 701 | { |
626 | folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.TrashFolder); | 702 | folder = m_Scene.InventoryService.GetFolderForType(userID, FolderType.Trash); |
627 | } | 703 | } |
628 | } | 704 | } |
629 | else if (action == DeRezAction.Return) | 705 | else if (action == DeRezAction.Return) |
630 | { | 706 | { |
631 | // Dump to lost + found unconditionally | 707 | // Dump to lost + found unconditionally |
632 | // | 708 | // |
633 | folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.LostAndFoundFolder); | 709 | folder = m_Scene.InventoryService.GetFolderForType(userID, FolderType.LostAndFound); |
634 | } | 710 | } |
635 | 711 | ||
636 | if (folderID == UUID.Zero && folder == null) | 712 | if (folderID == UUID.Zero && folder == null) |
@@ -639,21 +715,22 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
639 | { | 715 | { |
640 | // Deletes go to trash by default | 716 | // Deletes go to trash by default |
641 | // | 717 | // |
642 | folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.TrashFolder); | 718 | folder = m_Scene.InventoryService.GetFolderForType(userID, FolderType.Trash); |
643 | } | 719 | } |
644 | else | 720 | else |
645 | { | 721 | { |
646 | if (remoteClient == null || so.OwnerID != remoteClient.AgentId) | 722 | if (remoteClient == null || so.RootPart.OwnerID != remoteClient.AgentId) |
647 | { | 723 | { |
648 | // Taking copy of another person's item. Take to | 724 | // Taking copy of another person's item. Take to |
649 | // Objects folder. | 725 | // Objects folder. |
650 | folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.Object); | 726 | folder = m_Scene.InventoryService.GetFolderForType(userID, FolderType.Object); |
727 | so.FromFolderID = UUID.Zero; | ||
651 | } | 728 | } |
652 | else | 729 | else |
653 | { | 730 | { |
654 | // Catch all. Use lost & found | 731 | // Catch all. Use lost & found |
655 | // | 732 | // |
656 | folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.LostAndFoundFolder); | 733 | folder = m_Scene.InventoryService.GetFolderForType(userID, FolderType.LostAndFound); |
657 | } | 734 | } |
658 | } | 735 | } |
659 | } | 736 | } |
@@ -663,10 +740,16 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
663 | // | 740 | // |
664 | if (action == DeRezAction.Take || action == DeRezAction.TakeCopy) | 741 | if (action == DeRezAction.Take || action == DeRezAction.TakeCopy) |
665 | { | 742 | { |
666 | if (so.FromFolderID != UUID.Zero && userID == remoteClient.AgentId) | 743 | if (so.FromFolderID != UUID.Zero && so.RootPart.OwnerID == remoteClient.AgentId) |
667 | { | 744 | { |
668 | InventoryFolderBase f = new InventoryFolderBase(so.FromFolderID, userID); | 745 | InventoryFolderBase f = new InventoryFolderBase(so.FromFolderID, userID); |
669 | folder = m_Scene.InventoryService.GetFolder(f); | 746 | folder = m_Scene.InventoryService.GetFolder(f); |
747 | |||
748 | if(folder.Type == 14 || folder.Type == 16) | ||
749 | { | ||
750 | // folder.Type = 6; | ||
751 | folder = m_Scene.InventoryService.GetFolderForType(userID, FolderType.Object); | ||
752 | } | ||
670 | } | 753 | } |
671 | } | 754 | } |
672 | 755 | ||
@@ -722,7 +805,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
722 | UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, | 805 | UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, |
723 | bool RezSelected, bool RemoveItem, UUID fromTaskID, bool attachment) | 806 | bool RezSelected, bool RemoveItem, UUID fromTaskID, bool attachment) |
724 | { | 807 | { |
725 | AssetBase rezAsset = m_Scene.AssetService.Get(assetID.ToString()); | 808 | AssetBase rezAsset = m_Scene.AssetService.Get(assetID.ToString()); |
726 | 809 | ||
727 | if (rezAsset == null) | 810 | if (rezAsset == null) |
728 | { | 811 | { |
@@ -731,12 +814,14 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
731 | m_log.WarnFormat( | 814 | m_log.WarnFormat( |
732 | "[InventoryAccessModule]: Could not find asset {0} for item {1} {2} for {3} in RezObject()", | 815 | "[InventoryAccessModule]: Could not find asset {0} for item {1} {2} for {3} in RezObject()", |
733 | assetID, item.Name, item.ID, remoteClient.Name); | 816 | assetID, item.Name, item.ID, remoteClient.Name); |
817 | remoteClient.SendAgentAlertMessage(string.Format("Unable to rez: could not find asset {0} for item {1}.", assetID, item.Name), false); | ||
734 | } | 818 | } |
735 | else | 819 | else |
736 | { | 820 | { |
737 | m_log.WarnFormat( | 821 | m_log.WarnFormat( |
738 | "[INVENTORY ACCESS MODULE]: Could not find asset {0} for {1} in RezObject()", | 822 | "[INVENTORY ACCESS MODULE]: Could not find asset {0} for {1} in RezObject()", |
739 | assetID, remoteClient.Name); | 823 | assetID, remoteClient.Name); |
824 | remoteClient.SendAgentAlertMessage(string.Format("Unable to rez: could not find asset {0}.", assetID), false); | ||
740 | } | 825 | } |
741 | 826 | ||
742 | return null; | 827 | return null; |
@@ -744,67 +829,34 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
744 | 829 | ||
745 | SceneObjectGroup group = null; | 830 | SceneObjectGroup group = null; |
746 | 831 | ||
747 | string xmlData = Utils.BytesToString(rezAsset.Data); | 832 | List<SceneObjectGroup> objlist; |
748 | List<SceneObjectGroup> objlist = new List<SceneObjectGroup>(); | 833 | List<Vector3> veclist; |
749 | List<Vector3> veclist = new List<Vector3>(); | 834 | Vector3 bbox; |
835 | float offsetHeight; | ||
750 | byte bRayEndIsIntersection = (byte)(RayEndIsIntersection ? 1 : 0); | 836 | byte bRayEndIsIntersection = (byte)(RayEndIsIntersection ? 1 : 0); |
751 | Vector3 pos; | 837 | Vector3 pos; |
752 | 838 | ||
753 | XmlDocument doc = new XmlDocument(); | 839 | bool single |
754 | doc.LoadXml(xmlData); | 840 | = m_Scene.GetObjectsToRez( |
755 | XmlElement e = (XmlElement)doc.SelectSingleNode("/CoalescedObject"); | 841 | rezAsset.Data, attachment, out objlist, out veclist, out bbox, out offsetHeight); |
756 | if (e == null || attachment) // Single | ||
757 | { | ||
758 | SceneObjectGroup g = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); | ||
759 | |||
760 | objlist.Add(g); | ||
761 | veclist.Add(new Vector3(0, 0, 0)); | ||
762 | 842 | ||
763 | float offsetHeight = 0; | 843 | if (single) |
844 | { | ||
764 | pos = m_Scene.GetNewRezLocation( | 845 | pos = m_Scene.GetNewRezLocation( |
765 | RayStart, RayEnd, RayTargetID, Quaternion.Identity, | 846 | RayStart, RayEnd, RayTargetID, Quaternion.Identity, |
766 | BypassRayCast, bRayEndIsIntersection, true, g.GetAxisAlignedBoundingBox(out offsetHeight), false); | 847 | BypassRayCast, bRayEndIsIntersection, true, bbox, false); |
767 | pos.Z += offsetHeight; | 848 | pos.Z += offsetHeight; |
768 | } | 849 | } |
769 | else | 850 | else |
770 | { | 851 | { |
771 | XmlElement coll = (XmlElement)e; | ||
772 | float bx = Convert.ToSingle(coll.GetAttribute("x")); | ||
773 | float by = Convert.ToSingle(coll.GetAttribute("y")); | ||
774 | float bz = Convert.ToSingle(coll.GetAttribute("z")); | ||
775 | Vector3 bbox = new Vector3(bx, by, bz); | ||
776 | |||
777 | pos = m_Scene.GetNewRezLocation(RayStart, RayEnd, | 852 | pos = m_Scene.GetNewRezLocation(RayStart, RayEnd, |
778 | RayTargetID, Quaternion.Identity, | 853 | RayTargetID, Quaternion.Identity, |
779 | BypassRayCast, bRayEndIsIntersection, true, | 854 | BypassRayCast, bRayEndIsIntersection, true, |
780 | bbox, false); | 855 | bbox, false); |
781 | |||
782 | pos -= bbox / 2; | 856 | pos -= bbox / 2; |
783 | |||
784 | XmlNodeList groups = e.SelectNodes("SceneObjectGroup"); | ||
785 | foreach (XmlNode n in groups) | ||
786 | { | ||
787 | SceneObjectGroup g = SceneObjectSerializer.FromOriginalXmlFormat(n.OuterXml); | ||
788 | |||
789 | objlist.Add(g); | ||
790 | XmlElement el = (XmlElement)n; | ||
791 | |||
792 | string rawX = el.GetAttribute("offsetx"); | ||
793 | string rawY = el.GetAttribute("offsety"); | ||
794 | string rawZ = el.GetAttribute("offsetz"); | ||
795 | // | ||
796 | // m_log.DebugFormat( | ||
797 | // "[INVENTORY ACCESS MODULE]: Converting coalesced object {0} offset <{1}, {2}, {3}>", | ||
798 | // g.Name, rawX, rawY, rawZ); | ||
799 | |||
800 | float x = Convert.ToSingle(rawX); | ||
801 | float y = Convert.ToSingle(rawY); | ||
802 | float z = Convert.ToSingle(rawZ); | ||
803 | veclist.Add(new Vector3(x, y, z)); | ||
804 | } | ||
805 | } | 857 | } |
806 | 858 | ||
807 | if (item != null && !DoPreRezWhenFromItem(remoteClient, item, objlist, pos, attachment)) | 859 | if (item != null && !DoPreRezWhenFromItem(remoteClient, item, objlist, pos, veclist, attachment)) |
808 | return null; | 860 | return null; |
809 | 861 | ||
810 | for (int i = 0; i < objlist.Count; i++) | 862 | for (int i = 0; i < objlist.Count; i++) |
@@ -823,11 +875,23 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
823 | m_log.Debug("[INVENTORY ACCESS MODULE]: Object has UUID.Zero! Position 3"); | 875 | m_log.Debug("[INVENTORY ACCESS MODULE]: Object has UUID.Zero! Position 3"); |
824 | } | 876 | } |
825 | 877 | ||
826 | foreach (SceneObjectPart part in group.Parts) | 878 | // if this was previously an attachment and is now being rezzed, |
879 | // save the old attachment info. | ||
880 | if (group.IsAttachment == false && group.RootPart.Shape.State != 0) | ||
827 | { | 881 | { |
828 | // Make the rezzer the owner, as this is not necessarily set correctly in the serialized asset. | 882 | group.RootPart.AttachedPos = group.AbsolutePosition; |
829 | part.LastOwnerID = part.OwnerID; | 883 | group.RootPart.Shape.LastAttachPoint = (byte)group.AttachmentPoint; |
830 | part.OwnerID = remoteClient.AgentId; | 884 | } |
885 | |||
886 | if (item == null) | ||
887 | { | ||
888 | // Change ownership. Normally this is done in DoPreRezWhenFromItem(), but in this case we must do it here. | ||
889 | foreach (SceneObjectPart part in group.Parts) | ||
890 | { | ||
891 | // Make the rezzer the owner, as this is not necessarily set correctly in the serialized asset. | ||
892 | part.LastOwnerID = part.OwnerID; | ||
893 | part.OwnerID = remoteClient.AgentId; | ||
894 | } | ||
831 | } | 895 | } |
832 | 896 | ||
833 | if (!attachment) | 897 | if (!attachment) |
@@ -855,7 +919,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
855 | // one full update during the attachment | 919 | // one full update during the attachment |
856 | // process causes some clients to fail to display the | 920 | // process causes some clients to fail to display the |
857 | // attachment properly. | 921 | // attachment properly. |
858 | m_Scene.AddNewSceneObject(group, true, false); | 922 | m_Scene.AddNewSceneObject(group, !attachment, false); |
859 | 923 | ||
860 | // if attachment we set it's asset id so object updates | 924 | // if attachment we set it's asset id so object updates |
861 | // can reflect that, if not, we set it's position in world. | 925 | // can reflect that, if not, we set it's position in world. |
@@ -902,10 +966,15 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
902 | /// <param name="item"></param> | 966 | /// <param name="item"></param> |
903 | /// <param name="objlist"></param> | 967 | /// <param name="objlist"></param> |
904 | /// <param name="pos"></param> | 968 | /// <param name="pos"></param> |
969 | /// <param name="veclist"> | ||
970 | /// List of vector position adjustments for a coalesced objects. For ordinary objects | ||
971 | /// this list will contain just Vector3.Zero. The order of adjustments must match the order of objlist | ||
972 | /// </param> | ||
905 | /// <param name="isAttachment"></param> | 973 | /// <param name="isAttachment"></param> |
906 | /// <returns>true if we can processed with rezzing, false if we need to abort</returns> | 974 | /// <returns>true if we can processed with rezzing, false if we need to abort</returns> |
907 | private bool DoPreRezWhenFromItem( | 975 | private bool DoPreRezWhenFromItem( |
908 | IClientAPI remoteClient, InventoryItemBase item, List<SceneObjectGroup> objlist, Vector3 pos, bool isAttachment) | 976 | IClientAPI remoteClient, InventoryItemBase item, List<SceneObjectGroup> objlist, |
977 | Vector3 pos, List<Vector3> veclist, bool isAttachment) | ||
909 | { | 978 | { |
910 | UUID fromUserInventoryItemId = UUID.Zero; | 979 | UUID fromUserInventoryItemId = UUID.Zero; |
911 | 980 | ||
@@ -928,28 +997,29 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
928 | } | 997 | } |
929 | } | 998 | } |
930 | 999 | ||
931 | int primcount = 0; | 1000 | for (int i = 0; i < objlist.Count; i++) |
932 | foreach (SceneObjectGroup g in objlist) | ||
933 | primcount += g.PrimCount; | ||
934 | |||
935 | if (!m_Scene.Permissions.CanRezObject( | ||
936 | primcount, remoteClient.AgentId, pos) | ||
937 | && !isAttachment) | ||
938 | { | 1001 | { |
939 | // The client operates in no fail mode. It will | 1002 | SceneObjectGroup g = objlist[i]; |
940 | // have already removed the item from the folder | ||
941 | // if it's no copy. | ||
942 | // Put it back if it's not an attachment | ||
943 | // | ||
944 | if (((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0) && (!isAttachment)) | ||
945 | remoteClient.SendBulkUpdateInventory(item); | ||
946 | 1003 | ||
947 | ILandObject land = m_Scene.LandChannel.GetLandObject(pos.X, pos.Y); | 1004 | if (!m_Scene.Permissions.CanRezObject( |
948 | remoteClient.SendAlertMessage(string.Format( | 1005 | g.PrimCount, remoteClient.AgentId, pos + veclist[i]) |
949 | "Can't rez object '{0}' at <{1:F3}, {2:F3}, {3:F3}> on parcel '{4}' in region {5}.", | 1006 | && !isAttachment) |
950 | item.Name, pos.X, pos.Y, pos.Z, land != null ? land.LandData.Name : "Unknown", m_Scene.RegionInfo.RegionName)); | 1007 | { |
1008 | // The client operates in no fail mode. It will | ||
1009 | // have already removed the item from the folder | ||
1010 | // if it's no copy. | ||
1011 | // Put it back if it's not an attachment | ||
1012 | // | ||
1013 | if (((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0) && (!isAttachment)) | ||
1014 | remoteClient.SendBulkUpdateInventory(item); | ||
951 | 1015 | ||
952 | return false; | 1016 | ILandObject land = m_Scene.LandChannel.GetLandObject(pos.X, pos.Y); |
1017 | remoteClient.SendAlertMessage(string.Format( | ||
1018 | "Can't rez object '{0}' at <{1:F3}, {2:F3}, {3:F3}> on parcel '{4}' in region {5}.", | ||
1019 | item.Name, pos.X, pos.Y, pos.Z, land != null ? land.LandData.Name : "Unknown", m_Scene.Name)); | ||
1020 | |||
1021 | return false; | ||
1022 | } | ||
953 | } | 1023 | } |
954 | 1024 | ||
955 | for (int i = 0; i < objlist.Count; i++) | 1025 | for (int i = 0; i < objlist.Count; i++) |
@@ -977,44 +1047,19 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
977 | // "[INVENTORY ACCESS MODULE]: rootPart.OwnedID {0}, item.Owner {1}, item.CurrentPermissions {2:X}", | 1047 | // "[INVENTORY ACCESS MODULE]: rootPart.OwnedID {0}, item.Owner {1}, item.CurrentPermissions {2:X}", |
978 | // rootPart.OwnerID, item.Owner, item.CurrentPermissions); | 1048 | // rootPart.OwnerID, item.Owner, item.CurrentPermissions); |
979 | 1049 | ||
980 | if ((rootPart.OwnerID != item.Owner) || | 1050 | if ((rootPart.OwnerID != item.Owner) || (item.CurrentPermissions & SceneObjectGroup.SLAM) != 0) |
981 | (item.CurrentPermissions & 16) != 0) | ||
982 | { | 1051 | { |
983 | //Need to kill the for sale here | 1052 | //Need to kill the for sale here |
984 | rootPart.ObjectSaleType = 0; | 1053 | rootPart.ObjectSaleType = 0; |
985 | rootPart.SalePrice = 10; | 1054 | rootPart.SalePrice = 10; |
986 | |||
987 | if (m_Scene.Permissions.PropagatePermissions()) | ||
988 | { | ||
989 | foreach (SceneObjectPart part in so.Parts) | ||
990 | { | ||
991 | if ((item.Flags & (uint)InventoryItemFlags.ObjectHasMultipleItems) == 0) | ||
992 | { | ||
993 | part.EveryoneMask = item.EveryOnePermissions; | ||
994 | part.NextOwnerMask = item.NextPermissions; | ||
995 | } | ||
996 | part.GroupMask = 0; // DO NOT propagate here | ||
997 | } | ||
998 | |||
999 | so.ApplyNextOwnerPermissions(); | ||
1000 | } | ||
1001 | } | 1055 | } |
1002 | 1056 | ||
1003 | foreach (SceneObjectPart part in so.Parts) | 1057 | foreach (SceneObjectPart part in so.Parts) |
1004 | { | 1058 | { |
1005 | part.FromUserInventoryItemID = fromUserInventoryItemId; | 1059 | part.FromUserInventoryItemID = fromUserInventoryItemId; |
1006 | 1060 | part.ApplyPermissionsOnRez(item, true, m_Scene); | |
1007 | if ((part.OwnerID != item.Owner) || | ||
1008 | (item.CurrentPermissions & 16) != 0) | ||
1009 | { | ||
1010 | part.Inventory.ChangeInventoryOwner(item.Owner); | ||
1011 | part.GroupMask = 0; // DO NOT propagate here | ||
1012 | } | ||
1013 | |||
1014 | part.EveryoneMask = item.EveryOnePermissions; | ||
1015 | part.NextOwnerMask = item.NextPermissions; | ||
1016 | } | 1061 | } |
1017 | 1062 | ||
1018 | rootPart.TrimPermissions(); | 1063 | rootPart.TrimPermissions(); |
1019 | 1064 | ||
1020 | if (isAttachment) | 1065 | if (isAttachment) |
@@ -1150,4 +1195,4 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
1150 | 1195 | ||
1151 | #endregion | 1196 | #endregion |
1152 | } | 1197 | } |
1153 | } \ No newline at end of file | 1198 | } |
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/HGAssetMapperTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/HGAssetMapperTests.cs new file mode 100644 index 0000000..007ff63 --- /dev/null +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/HGAssetMapperTests.cs | |||
@@ -0,0 +1,146 @@ | |||
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.Threading; | ||
30 | using System.Xml; | ||
31 | using Nini.Config; | ||
32 | using NUnit.Framework; | ||
33 | using OpenMetaverse; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Region.CoreModules.Framework.InventoryAccess; | ||
36 | using OpenSim.Region.Framework.Scenes; | ||
37 | using OpenSim.Region.ScriptEngine.XEngine; | ||
38 | using OpenSim.Services.Interfaces; | ||
39 | using OpenSim.Tests.Common; | ||
40 | |||
41 | namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests | ||
42 | { | ||
43 | [TestFixture] | ||
44 | public class HGAssetMapperTests : OpenSimTestCase | ||
45 | { | ||
46 | [Test] | ||
47 | public void TestPostAssetRewrite() | ||
48 | { | ||
49 | TestHelpers.InMethod(); | ||
50 | // TestHelpers.EnableLogging(); | ||
51 | |||
52 | XEngine xengine = new OpenSim.Region.ScriptEngine.XEngine.XEngine(); | ||
53 | xengine.DebugLevel = 1; | ||
54 | |||
55 | IniConfigSource configSource = new IniConfigSource(); | ||
56 | |||
57 | IConfig startupConfig = configSource.AddConfig("Startup"); | ||
58 | startupConfig.Set("DefaultScriptEngine", "XEngine"); | ||
59 | |||
60 | IConfig xEngineConfig = configSource.AddConfig("XEngine"); | ||
61 | xEngineConfig.Set("Enabled", "true"); | ||
62 | xEngineConfig.Set("StartDelay", "0"); | ||
63 | xEngineConfig.Set("AppDomainLoading", "false"); | ||
64 | |||
65 | string homeUrl = "http://hg.HomeTestPostAssetRewriteGrid.com"; | ||
66 | string foreignUrl = "http://hg.ForeignTestPostAssetRewriteGrid.com"; | ||
67 | int soIdTail = 0x1; | ||
68 | UUID assetId = TestHelpers.ParseTail(0x10); | ||
69 | UUID userId = TestHelpers.ParseTail(0x100); | ||
70 | UUID sceneId = TestHelpers.ParseTail(0x1000); | ||
71 | string userFirstName = "TestPostAsset"; | ||
72 | string userLastName = "Rewrite"; | ||
73 | int soPartsCount = 3; | ||
74 | |||
75 | Scene scene = new SceneHelpers().SetupScene("TestPostAssetRewriteScene", sceneId, 1000, 1000, configSource); | ||
76 | SceneHelpers.SetupSceneModules(scene, configSource, xengine); | ||
77 | scene.StartScripts(); | ||
78 | |||
79 | HGAssetMapper hgam = new HGAssetMapper(scene, homeUrl); | ||
80 | UserAccount ua | ||
81 | = UserAccountHelpers.CreateUserWithInventory(scene, userFirstName, userLastName, userId, "password"); | ||
82 | |||
83 | SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, soPartsCount, ua.PrincipalID, "part", soIdTail); | ||
84 | RezScript( | ||
85 | scene, so.UUID, "default { state_entry() { llSay(0, \"Hello World\"); } }", "item1", ua.PrincipalID); | ||
86 | |||
87 | AssetBase asset = AssetHelpers.CreateAsset(assetId, so); | ||
88 | asset.CreatorID = foreignUrl; | ||
89 | hgam.PostAsset(foreignUrl, asset); | ||
90 | |||
91 | // Check transformed asset. | ||
92 | AssetBase ncAssetGet = scene.AssetService.Get(assetId.ToString()); | ||
93 | Assert.AreEqual(foreignUrl, ncAssetGet.CreatorID); | ||
94 | string xmlData = Utils.BytesToString(ncAssetGet.Data); | ||
95 | XmlDocument ncAssetGetXmlDoc = new XmlDocument(); | ||
96 | ncAssetGetXmlDoc.LoadXml(xmlData); | ||
97 | |||
98 | // Console.WriteLine(ncAssetGetXmlDoc.OuterXml); | ||
99 | |||
100 | XmlNodeList creatorDataNodes = ncAssetGetXmlDoc.GetElementsByTagName("CreatorData"); | ||
101 | |||
102 | Assert.AreEqual(soPartsCount, creatorDataNodes.Count); | ||
103 | //Console.WriteLine("creatorDataNodes {0}", creatorDataNodes.Count); | ||
104 | |||
105 | foreach (XmlNode creatorDataNode in creatorDataNodes) | ||
106 | { | ||
107 | Assert.AreEqual( | ||
108 | string.Format("{0};{1} {2}", homeUrl, ua.FirstName, ua.LastName), creatorDataNode.InnerText); | ||
109 | } | ||
110 | |||
111 | // Check that saved script nodes have attributes | ||
112 | XmlNodeList savedScriptStateNodes = ncAssetGetXmlDoc.GetElementsByTagName("SavedScriptState"); | ||
113 | |||
114 | Assert.AreEqual(1, savedScriptStateNodes.Count); | ||
115 | Assert.AreEqual(1, savedScriptStateNodes[0].Attributes.Count); | ||
116 | XmlNode uuidAttribute = savedScriptStateNodes[0].Attributes.GetNamedItem("UUID"); | ||
117 | Assert.NotNull(uuidAttribute); | ||
118 | // XXX: To check the actual UUID attribute we would have to do some work to retreive the UUID of the task | ||
119 | // item created earlier. | ||
120 | } | ||
121 | |||
122 | private void RezScript(Scene scene, UUID soId, string script, string itemName, UUID userId) | ||
123 | { | ||
124 | InventoryItemBase itemTemplate = new InventoryItemBase(); | ||
125 | // itemTemplate.ID = itemId; | ||
126 | itemTemplate.Name = itemName; | ||
127 | itemTemplate.Folder = soId; | ||
128 | itemTemplate.InvType = (int)InventoryType.LSL; | ||
129 | |||
130 | // XXX: Ultimately it would be better to be able to directly manipulate the script engine to rez a script | ||
131 | // immediately for tests rather than chunter through it's threaded mechanisms. | ||
132 | AutoResetEvent chatEvent = new AutoResetEvent(false); | ||
133 | |||
134 | scene.EventManager.OnChatFromWorld += (s, c) => | ||
135 | { | ||
136 | // Console.WriteLine("Got chat [{0}]", c.Message); | ||
137 | chatEvent.Set(); | ||
138 | }; | ||
139 | |||
140 | scene.RezNewScript(userId, itemTemplate, script); | ||
141 | |||
142 | // Console.WriteLine("HERE"); | ||
143 | Assert.IsTrue(chatEvent.WaitOne(60000), "Chat event in HGAssetMapperTests.RezScript not received"); | ||
144 | } | ||
145 | } | ||
146 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs index ac25a93..1d91165 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs | |||
@@ -37,14 +37,12 @@ using OpenSim.Data; | |||
37 | using OpenSim.Framework; | 37 | using OpenSim.Framework; |
38 | using OpenSim.Framework.Serialization; | 38 | using OpenSim.Framework.Serialization; |
39 | using OpenSim.Framework.Serialization.External; | 39 | using OpenSim.Framework.Serialization.External; |
40 | using OpenSim.Framework.Communications; | ||
41 | using OpenSim.Region.CoreModules.Avatar.Inventory.Archiver; | 40 | using OpenSim.Region.CoreModules.Avatar.Inventory.Archiver; |
42 | using OpenSim.Region.CoreModules.Framework.InventoryAccess; | 41 | using OpenSim.Region.CoreModules.Framework.InventoryAccess; |
43 | using OpenSim.Region.Framework.Scenes; | 42 | using OpenSim.Region.Framework.Scenes; |
44 | using OpenSim.Region.Framework.Scenes.Serialization; | 43 | using OpenSim.Region.Framework.Scenes.Serialization; |
45 | using OpenSim.Services.Interfaces; | 44 | using OpenSim.Services.Interfaces; |
46 | using OpenSim.Tests.Common; | 45 | using OpenSim.Tests.Common; |
47 | using OpenSim.Tests.Common.Mock; | ||
48 | 46 | ||
49 | namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests | 47 | namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests |
50 | { | 48 | { |
@@ -109,8 +107,9 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests | |||
109 | item1.AssetID = asset1.FullID; | 107 | item1.AssetID = asset1.FullID; |
110 | item1.ID = item1Id; | 108 | item1.ID = item1Id; |
111 | InventoryFolderBase objsFolder | 109 | InventoryFolderBase objsFolder |
112 | = InventoryArchiveUtils.FindFolderByPath(m_scene.InventoryService, m_userId, "Objects")[0]; | 110 | = InventoryArchiveUtils.FindFoldersByPath(m_scene.InventoryService, m_userId, "Objects")[0]; |
113 | item1.Folder = objsFolder.ID; | 111 | item1.Folder = objsFolder.ID; |
112 | item1.Flags |= (uint)InventoryItemFlags.ObjectHasMultipleItems; | ||
114 | m_scene.AddInventoryItem(item1); | 113 | m_scene.AddInventoryItem(item1); |
115 | 114 | ||
116 | SceneObjectGroup so | 115 | SceneObjectGroup so |
@@ -159,7 +158,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests | |||
159 | item1.AssetID = asset1.FullID; | 158 | item1.AssetID = asset1.FullID; |
160 | item1.ID = item1Id; | 159 | item1.ID = item1Id; |
161 | InventoryFolderBase objsFolder | 160 | InventoryFolderBase objsFolder |
162 | = InventoryArchiveUtils.FindFolderByPath(m_scene.InventoryService, m_userId, "Objects")[0]; | 161 | = InventoryArchiveUtils.FindFoldersByPath(m_scene.InventoryService, m_userId, "Objects")[0]; |
163 | item1.Folder = objsFolder.ID; | 162 | item1.Folder = objsFolder.ID; |
164 | m_scene.AddInventoryItem(item1); | 163 | m_scene.AddInventoryItem(item1); |
165 | 164 | ||
diff --git a/OpenSim/Region/CoreModules/Framework/Library/LibraryModule.cs b/OpenSim/Region/CoreModules/Framework/Library/LibraryModule.cs index ec22146..862f0b7 100644 --- a/OpenSim/Region/CoreModules/Framework/Library/LibraryModule.cs +++ b/OpenSim/Region/CoreModules/Framework/Library/LibraryModule.cs | |||
@@ -30,7 +30,6 @@ using System.IO; | |||
30 | using System.Reflection; | 30 | using System.Reflection; |
31 | 31 | ||
32 | using OpenSim.Framework; | 32 | using OpenSim.Framework; |
33 | using OpenSim.Framework.Communications; | ||
34 | 33 | ||
35 | using OpenSim.Region.CoreModules.Avatar.Inventory.Archiver; | 34 | using OpenSim.Region.CoreModules.Avatar.Inventory.Archiver; |
36 | using OpenSim.Region.Framework; | 35 | using OpenSim.Region.Framework; |
@@ -43,6 +42,7 @@ using OpenMetaverse; | |||
43 | using log4net; | 42 | using log4net; |
44 | using Mono.Addins; | 43 | using Mono.Addins; |
45 | using Nini.Config; | 44 | using Nini.Config; |
45 | using PermissionMask = OpenSim.Framework.PermissionMask; | ||
46 | 46 | ||
47 | namespace OpenSim.Region.CoreModules.Framework.Library | 47 | namespace OpenSim.Region.CoreModules.Framework.Library |
48 | { | 48 | { |
@@ -175,7 +175,7 @@ namespace OpenSim.Region.CoreModules.Framework.Library | |||
175 | m_log.InfoFormat("[LIBRARY MODULE]: Loading library archive {0} ({1})...", iarFileName, simpleName); | 175 | m_log.InfoFormat("[LIBRARY MODULE]: Loading library archive {0} ({1})...", iarFileName, simpleName); |
176 | simpleName = GetInventoryPathFromName(simpleName); | 176 | simpleName = GetInventoryPathFromName(simpleName); |
177 | 177 | ||
178 | InventoryArchiveReadRequest archread = new InventoryArchiveReadRequest(m_MockScene, uinfo, simpleName, iarFileName, false); | 178 | InventoryArchiveReadRequest archread = new InventoryArchiveReadRequest(m_MockScene.InventoryService, m_MockScene.AssetService, m_MockScene.UserAccountService, uinfo, simpleName, iarFileName, false); |
179 | try | 179 | try |
180 | { | 180 | { |
181 | HashSet<InventoryNodeBase> nodes = archread.Execute(); | 181 | HashSet<InventoryNodeBase> nodes = archread.Execute(); |
@@ -184,7 +184,7 @@ namespace OpenSim.Region.CoreModules.Framework.Library | |||
184 | // didn't find the subfolder with the given name; place it on the top | 184 | // didn't find the subfolder with the given name; place it on the top |
185 | m_log.InfoFormat("[LIBRARY MODULE]: Didn't find {0} in library. Placing archive on the top level", simpleName); | 185 | m_log.InfoFormat("[LIBRARY MODULE]: Didn't find {0} in library. Placing archive on the top level", simpleName); |
186 | archread.Close(); | 186 | archread.Close(); |
187 | archread = new InventoryArchiveReadRequest(m_MockScene, uinfo, "/", iarFileName, false); | 187 | archread = new InventoryArchiveReadRequest(m_MockScene.InventoryService, m_MockScene.AssetService, m_MockScene.UserAccountService, uinfo, "/", iarFileName, false); |
188 | archread.Execute(); | 188 | archread.Execute(); |
189 | } | 189 | } |
190 | 190 | ||
diff --git a/OpenSim/Region/CoreModules/Framework/Library/LocalInventoryService.cs b/OpenSim/Region/CoreModules/Framework/Library/LocalInventoryService.cs index 49589fd..e1e1838 100644 --- a/OpenSim/Region/CoreModules/Framework/Library/LocalInventoryService.cs +++ b/OpenSim/Region/CoreModules/Framework/Library/LocalInventoryService.cs | |||
@@ -65,7 +65,7 @@ namespace OpenSim.Region.CoreModules.Framework.Library | |||
65 | { | 65 | { |
66 | InventoryFolderImpl folder = null; | 66 | InventoryFolderImpl folder = null; |
67 | InventoryCollection inv = new InventoryCollection(); | 67 | InventoryCollection inv = new InventoryCollection(); |
68 | inv.UserID = m_Library.Owner; | 68 | inv.OwnerID = m_Library.Owner; |
69 | 69 | ||
70 | if (folderID != m_Library.ID) | 70 | if (folderID != m_Library.ID) |
71 | { | 71 | { |
@@ -87,6 +87,34 @@ namespace OpenSim.Region.CoreModules.Framework.Library | |||
87 | return inv; | 87 | return inv; |
88 | } | 88 | } |
89 | 89 | ||
90 | public virtual InventoryCollection[] GetMultipleFoldersContent(UUID principalID, UUID[] folderIDs) | ||
91 | { | ||
92 | InventoryCollection[] invColl = new InventoryCollection[folderIDs.Length]; | ||
93 | int i = 0; | ||
94 | foreach (UUID fid in folderIDs) | ||
95 | { | ||
96 | invColl[i++] = GetFolderContent(principalID, fid); | ||
97 | } | ||
98 | |||
99 | return invColl; | ||
100 | } | ||
101 | |||
102 | public virtual InventoryItemBase[] GetMultipleItems(UUID principalID, UUID[] itemIDs) | ||
103 | { | ||
104 | InventoryItemBase[] itemColl = new InventoryItemBase[itemIDs.Length]; | ||
105 | int i = 0; | ||
106 | InventoryItemBase item = new InventoryItemBase(); | ||
107 | item.Owner = principalID; | ||
108 | foreach (UUID fid in itemIDs) | ||
109 | { | ||
110 | item.ID = fid; | ||
111 | itemColl[i++] = GetItem(item); | ||
112 | } | ||
113 | |||
114 | return itemColl; | ||
115 | } | ||
116 | |||
117 | |||
90 | /// <summary> | 118 | /// <summary> |
91 | /// Add a new folder to the user's inventory | 119 | /// Add a new folder to the user's inventory |
92 | /// </summary> | 120 | /// </summary> |
@@ -142,28 +170,12 @@ namespace OpenSim.Region.CoreModules.Framework.Library | |||
142 | public List<InventoryFolderBase> GetInventorySkeleton(UUID userId) { return null; } | 170 | public List<InventoryFolderBase> GetInventorySkeleton(UUID userId) { return null; } |
143 | 171 | ||
144 | /// <summary> | 172 | /// <summary> |
145 | /// Synchronous inventory fetch. | ||
146 | /// </summary> | ||
147 | /// <param name="userID"></param> | ||
148 | /// <returns></returns> | ||
149 | public InventoryCollection GetUserInventory(UUID userID) { return null; } | ||
150 | |||
151 | /// <summary> | ||
152 | /// Request the inventory for a user. This is an asynchronous operation that will call the callback when the | ||
153 | /// inventory has been received | ||
154 | /// </summary> | ||
155 | /// <param name="userID"></param> | ||
156 | /// <param name="callback"></param> | ||
157 | public void GetUserInventory(UUID userID, InventoryReceiptCallback callback) { } | ||
158 | |||
159 | |||
160 | /// <summary> | ||
161 | /// Gets the user folder for the given folder-type | 173 | /// Gets the user folder for the given folder-type |
162 | /// </summary> | 174 | /// </summary> |
163 | /// <param name="userID"></param> | 175 | /// <param name="userID"></param> |
164 | /// <param name="type"></param> | 176 | /// <param name="type"></param> |
165 | /// <returns></returns> | 177 | /// <returns></returns> |
166 | public InventoryFolderBase GetFolderForType(UUID userID, AssetType type) { return null; } | 178 | public InventoryFolderBase GetFolderForType(UUID userID, FolderType type) { return null; } |
167 | 179 | ||
168 | 180 | ||
169 | /// <summary> | 181 | /// <summary> |
diff --git a/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs b/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs index d84460a..64feec1 100644 --- a/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs +++ b/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs | |||
@@ -33,6 +33,7 @@ using log4net; | |||
33 | using Nini.Config; | 33 | using Nini.Config; |
34 | using OpenMetaverse; | 34 | using OpenMetaverse; |
35 | using OpenSim.Framework; | 35 | using OpenSim.Framework; |
36 | using OpenSim.Framework.Monitoring; | ||
36 | using OpenSim.Framework.Servers; | 37 | using OpenSim.Framework.Servers; |
37 | using OpenSim.Region.CoreModules.Framework.Monitoring.Alerts; | 38 | using OpenSim.Region.CoreModules.Framework.Monitoring.Alerts; |
38 | using OpenSim.Region.CoreModules.Framework.Monitoring.Monitors; | 39 | using OpenSim.Region.CoreModules.Framework.Monitoring.Monitors; |
@@ -100,6 +101,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring | |||
100 | "/monitorstats/" + Uri.EscapeDataString(m_scene.RegionInfo.RegionName), StatsPage); | 101 | "/monitorstats/" + Uri.EscapeDataString(m_scene.RegionInfo.RegionName), StatsPage); |
101 | 102 | ||
102 | AddMonitors(); | 103 | AddMonitors(); |
104 | RegisterStatsManagerRegionStatistics(); | ||
103 | } | 105 | } |
104 | 106 | ||
105 | public void RemoveRegion(Scene scene) | 107 | public void RemoveRegion(Scene scene) |
@@ -109,6 +111,9 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring | |||
109 | 111 | ||
110 | MainServer.Instance.RemoveHTTPHandler("GET", "/monitorstats/" + m_scene.RegionInfo.RegionID); | 112 | MainServer.Instance.RemoveHTTPHandler("GET", "/monitorstats/" + m_scene.RegionInfo.RegionID); |
111 | MainServer.Instance.RemoveHTTPHandler("GET", "/monitorstats/" + Uri.EscapeDataString(m_scene.RegionInfo.RegionName)); | 113 | MainServer.Instance.RemoveHTTPHandler("GET", "/monitorstats/" + Uri.EscapeDataString(m_scene.RegionInfo.RegionName)); |
114 | |||
115 | UnRegisterStatsManagerRegionStatistics(); | ||
116 | |||
112 | m_scene = null; | 117 | m_scene = null; |
113 | } | 118 | } |
114 | 119 | ||
@@ -399,6 +404,45 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring | |||
399 | { | 404 | { |
400 | m_log.Error("[Monitor] " + reporter.Name + " for " + m_scene.RegionInfo.RegionName + " reports " + reason + " (Fatal: " + fatal + ")"); | 405 | m_log.Error("[Monitor] " + reporter.Name + " for " + m_scene.RegionInfo.RegionName + " reports " + reason + " (Fatal: " + fatal + ")"); |
401 | } | 406 | } |
407 | |||
408 | private List<Stat> registeredStats = new List<Stat>(); | ||
409 | private void MakeStat(string pName, string pUnitName, Action<Stat> act) | ||
410 | { | ||
411 | Stat tempStat = new Stat(pName, pName, pName, pUnitName, "scene", m_scene.RegionInfo.RegionName, StatType.Pull, act, StatVerbosity.Info); | ||
412 | StatsManager.RegisterStat(tempStat); | ||
413 | registeredStats.Add(tempStat); | ||
414 | } | ||
415 | private void RegisterStatsManagerRegionStatistics() | ||
416 | { | ||
417 | MakeStat("RootAgents", "avatars", (s) => { s.Value = m_scene.SceneGraph.GetRootAgentCount(); }); | ||
418 | MakeStat("ChildAgents", "avatars", (s) => { s.Value = m_scene.SceneGraph.GetChildAgentCount(); }); | ||
419 | MakeStat("TotalPrims", "objects", (s) => { s.Value = m_scene.SceneGraph.GetTotalObjectsCount(); }); | ||
420 | MakeStat("ActivePrims", "objects", (s) => { s.Value = m_scene.SceneGraph.GetActiveObjectsCount(); }); | ||
421 | MakeStat("ActiveScripts", "scripts", (s) => { s.Value = m_scene.SceneGraph.GetActiveScriptsCount(); }); | ||
422 | |||
423 | MakeStat("TimeDilation", "sec/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[0]; }); | ||
424 | MakeStat("SimFPS", "fps", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[1]; }); | ||
425 | MakeStat("PhysicsFPS", "fps", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[2]; }); | ||
426 | MakeStat("AgentUpdates", "updates/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[3]; }); | ||
427 | MakeStat("FrameTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[8]; }); | ||
428 | MakeStat("NetTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[9]; }); | ||
429 | MakeStat("OtherTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[12]; }); | ||
430 | MakeStat("PhysicsTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[10]; }); | ||
431 | MakeStat("AgentTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[16]; }); | ||
432 | MakeStat("ImageTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[11]; }); | ||
433 | MakeStat("ScriptLines", "lines/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[20]; }); | ||
434 | MakeStat("SimSpareMS", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[21]; }); | ||
435 | } | ||
436 | |||
437 | private void UnRegisterStatsManagerRegionStatistics() | ||
438 | { | ||
439 | foreach (Stat stat in registeredStats) | ||
440 | { | ||
441 | StatsManager.DeregisterStat(stat); | ||
442 | stat.Dispose(); | ||
443 | } | ||
444 | registeredStats.Clear(); | ||
445 | } | ||
402 | 446 | ||
403 | } | 447 | } |
404 | } | 448 | } \ No newline at end of file |
diff --git a/OpenSim/Region/CoreModules/Framework/Search/BasicSearchModule.cs b/OpenSim/Region/CoreModules/Framework/Search/BasicSearchModule.cs new file mode 100644 index 0000000..3849996 --- /dev/null +++ b/OpenSim/Region/CoreModules/Framework/Search/BasicSearchModule.cs | |||
@@ -0,0 +1,199 @@ | |||
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 | using System; | ||
28 | using System.Collections.Generic; | ||
29 | using System.IO; | ||
30 | using System.Reflection; | ||
31 | using System.Threading; | ||
32 | |||
33 | using OpenSim.Framework; | ||
34 | using OpenSim.Framework.Console; | ||
35 | using OpenSim.Framework.Monitoring; | ||
36 | using OpenSim.Region.ClientStack.LindenUDP; | ||
37 | using OpenSim.Region.Framework; | ||
38 | using OpenSim.Region.Framework.Interfaces; | ||
39 | using OpenSim.Region.Framework.Scenes; | ||
40 | using OpenSim.Services.Interfaces; | ||
41 | using OpenSim.Services.Connectors.Hypergrid; | ||
42 | |||
43 | using OpenMetaverse; | ||
44 | using OpenMetaverse.Packets; | ||
45 | using log4net; | ||
46 | using Nini.Config; | ||
47 | using Mono.Addins; | ||
48 | |||
49 | using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; | ||
50 | |||
51 | namespace OpenSim.Region.CoreModules.Framework.Search | ||
52 | { | ||
53 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "BasicSearchModule")] | ||
54 | public class BasicSearchModule : ISharedRegionModule | ||
55 | { | ||
56 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
57 | |||
58 | protected bool m_Enabled; | ||
59 | protected List<Scene> m_Scenes = new List<Scene>(); | ||
60 | |||
61 | private IGroupsModule m_GroupsService = null; | ||
62 | |||
63 | #region ISharedRegionModule | ||
64 | |||
65 | public void Initialise(IConfigSource config) | ||
66 | { | ||
67 | string umanmod = config.Configs["Modules"].GetString("SearchModule", Name); | ||
68 | if (umanmod == Name) | ||
69 | { | ||
70 | m_Enabled = true; | ||
71 | m_log.DebugFormat("[BASIC SEARCH MODULE]: {0} is enabled", Name); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | public bool IsSharedModule | ||
76 | { | ||
77 | get { return true; } | ||
78 | } | ||
79 | |||
80 | public virtual string Name | ||
81 | { | ||
82 | get { return "BasicSearchModule"; } | ||
83 | } | ||
84 | |||
85 | public Type ReplaceableInterface | ||
86 | { | ||
87 | get { return null; } | ||
88 | } | ||
89 | |||
90 | public void AddRegion(Scene scene) | ||
91 | { | ||
92 | if (m_Enabled) | ||
93 | { | ||
94 | m_Scenes.Add(scene); | ||
95 | |||
96 | scene.EventManager.OnMakeRootAgent += new Action<ScenePresence>(EventManager_OnMakeRootAgent); | ||
97 | scene.EventManager.OnMakeChildAgent += new EventManager.OnMakeChildAgentDelegate(EventManager_OnMakeChildAgent); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | public void RemoveRegion(Scene scene) | ||
102 | { | ||
103 | if (m_Enabled) | ||
104 | { | ||
105 | m_Scenes.Remove(scene); | ||
106 | |||
107 | scene.EventManager.OnMakeRootAgent -= new Action<ScenePresence>(EventManager_OnMakeRootAgent); | ||
108 | scene.EventManager.OnMakeChildAgent -= new EventManager.OnMakeChildAgentDelegate(EventManager_OnMakeChildAgent); | ||
109 | } | ||
110 | } | ||
111 | |||
112 | public void RegionLoaded(Scene s) | ||
113 | { | ||
114 | if (!m_Enabled) | ||
115 | return; | ||
116 | |||
117 | if (m_GroupsService == null) | ||
118 | { | ||
119 | m_GroupsService = s.RequestModuleInterface<IGroupsModule>(); | ||
120 | |||
121 | // No Groups Service Connector, then group search won't work... | ||
122 | if (m_GroupsService == null) | ||
123 | m_log.Warn("[BASIC SEARCH MODULE]: Could not get IGroupsModule"); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | public void PostInitialise() | ||
128 | { | ||
129 | } | ||
130 | |||
131 | public void Close() | ||
132 | { | ||
133 | m_Scenes.Clear(); | ||
134 | } | ||
135 | |||
136 | #endregion ISharedRegionModule | ||
137 | |||
138 | |||
139 | #region Event Handlers | ||
140 | |||
141 | void EventManager_OnMakeRootAgent(ScenePresence sp) | ||
142 | { | ||
143 | sp.ControllingClient.OnDirFindQuery += OnDirFindQuery; | ||
144 | } | ||
145 | |||
146 | void EventManager_OnMakeChildAgent(ScenePresence sp) | ||
147 | { | ||
148 | sp.ControllingClient.OnDirFindQuery -= OnDirFindQuery; | ||
149 | } | ||
150 | |||
151 | void OnDirFindQuery(IClientAPI remoteClient, UUID queryID, string queryText, uint queryFlags, int queryStart) | ||
152 | { | ||
153 | queryText = queryText.Trim(); | ||
154 | |||
155 | if (((DirFindFlags)queryFlags & DirFindFlags.People) == DirFindFlags.People) | ||
156 | { | ||
157 | if (string.IsNullOrEmpty(queryText)) | ||
158 | remoteClient.SendDirPeopleReply(queryID, new DirPeopleReplyData[0]); | ||
159 | |||
160 | List<UserAccount> accounts = m_Scenes[0].UserAccountService.GetUserAccounts(m_Scenes[0].RegionInfo.ScopeID, queryText); | ||
161 | DirPeopleReplyData[] hits = new DirPeopleReplyData[accounts.Count]; | ||
162 | int i = 0; | ||
163 | foreach (UserAccount acc in accounts) | ||
164 | { | ||
165 | DirPeopleReplyData d = new DirPeopleReplyData(); | ||
166 | d.agentID = acc.PrincipalID; | ||
167 | d.firstName = acc.FirstName; | ||
168 | d.lastName = acc.LastName; | ||
169 | d.online = false; | ||
170 | |||
171 | hits[i++] = d; | ||
172 | } | ||
173 | |||
174 | // TODO: This currently ignores pretty much all the query flags including Mature and sort order | ||
175 | remoteClient.SendDirPeopleReply(queryID, hits); | ||
176 | } | ||
177 | else if (((DirFindFlags)queryFlags & DirFindFlags.Groups) == DirFindFlags.Groups) | ||
178 | { | ||
179 | if (m_GroupsService == null) | ||
180 | { | ||
181 | m_log.Warn("[BASIC SEARCH MODULE]: Groups service is not available. Unable to search groups."); | ||
182 | remoteClient.SendAlertMessage("Groups search is not enabled"); | ||
183 | return; | ||
184 | } | ||
185 | |||
186 | if (string.IsNullOrEmpty(queryText)) | ||
187 | remoteClient.SendDirGroupsReply(queryID, new DirGroupsReplyData[0]); | ||
188 | |||
189 | // TODO: This currently ignores pretty much all the query flags including Mature and sort order | ||
190 | remoteClient.SendDirGroupsReply(queryID, m_GroupsService.FindGroups(remoteClient, queryText).ToArray()); | ||
191 | } | ||
192 | |||
193 | } | ||
194 | |||
195 | #endregion Event Handlers | ||
196 | |||
197 | } | ||
198 | |||
199 | } | ||
diff --git a/OpenSim/Region/CoreModules/Framework/ServiceThrottle/ServiceThrottleModule.cs b/OpenSim/Region/CoreModules/Framework/ServiceThrottle/ServiceThrottleModule.cs new file mode 100644 index 0000000..3abacbd --- /dev/null +++ b/OpenSim/Region/CoreModules/Framework/ServiceThrottle/ServiceThrottleModule.cs | |||
@@ -0,0 +1,256 @@ | |||
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.Threading; | ||
32 | using log4net; | ||
33 | using Mono.Addins; | ||
34 | using Nini.Config; | ||
35 | using OpenMetaverse; | ||
36 | using OpenSim.Framework; | ||
37 | using OpenSim.Region.Framework.Interfaces; | ||
38 | using OpenSim.Framework.Monitoring; | ||
39 | using OpenSim.Region.Framework.Scenes; | ||
40 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; | ||
41 | |||
42 | namespace OpenSim.Region.CoreModules.Framework | ||
43 | { | ||
44 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GridServiceThrottleModule")] | ||
45 | public class ServiceThrottleModule : ISharedRegionModule, IServiceThrottleModule | ||
46 | { | ||
47 | private static readonly ILog m_log = LogManager.GetLogger( | ||
48 | MethodBase.GetCurrentMethod().DeclaringType); | ||
49 | |||
50 | private readonly List<Scene> m_scenes = new List<Scene>(); | ||
51 | private System.Timers.Timer m_timer = new System.Timers.Timer(); | ||
52 | |||
53 | private Queue<Action> m_RequestQueue = new Queue<Action>(); | ||
54 | private Dictionary<string, List<string>> m_Pending = new Dictionary<string, List<string>>(); | ||
55 | private int m_Interval; | ||
56 | |||
57 | #region ISharedRegionModule | ||
58 | |||
59 | public void Initialise(IConfigSource config) | ||
60 | { | ||
61 | m_Interval = Util.GetConfigVarFromSections<int>(config, "Interval", new string[] { "ServiceThrottle" }, 5000); | ||
62 | |||
63 | m_timer = new System.Timers.Timer(); | ||
64 | m_timer.AutoReset = false; | ||
65 | m_timer.Enabled = true; | ||
66 | m_timer.Interval = 15000; // 15 secs at first | ||
67 | m_timer.Elapsed += ProcessQueue; | ||
68 | m_timer.Start(); | ||
69 | |||
70 | //WorkManager.StartThread( | ||
71 | // ProcessQueue, | ||
72 | // "GridServiceRequestThread", | ||
73 | // ThreadPriority.BelowNormal, | ||
74 | // true, | ||
75 | // false); | ||
76 | } | ||
77 | |||
78 | public void AddRegion(Scene scene) | ||
79 | { | ||
80 | lock (m_scenes) | ||
81 | { | ||
82 | m_scenes.Add(scene); | ||
83 | scene.RegisterModuleInterface<IServiceThrottleModule>(this); | ||
84 | scene.EventManager.OnNewClient += OnNewClient; | ||
85 | scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | public void RegionLoaded(Scene scene) | ||
90 | { | ||
91 | } | ||
92 | |||
93 | public void RemoveRegion(Scene scene) | ||
94 | { | ||
95 | lock (m_scenes) | ||
96 | { | ||
97 | m_scenes.Remove(scene); | ||
98 | scene.EventManager.OnNewClient -= OnNewClient; | ||
99 | } | ||
100 | } | ||
101 | |||
102 | public void PostInitialise() | ||
103 | { | ||
104 | } | ||
105 | |||
106 | public void Close() | ||
107 | { | ||
108 | } | ||
109 | |||
110 | public string Name | ||
111 | { | ||
112 | get { return "ServiceThrottleModule"; } | ||
113 | } | ||
114 | |||
115 | public Type ReplaceableInterface | ||
116 | { | ||
117 | get { return null; } | ||
118 | } | ||
119 | |||
120 | #endregion ISharedRegionMOdule | ||
121 | |||
122 | #region Events | ||
123 | |||
124 | void OnNewClient(IClientAPI client) | ||
125 | { | ||
126 | client.OnRegionHandleRequest += OnRegionHandleRequest; | ||
127 | } | ||
128 | |||
129 | void OnMakeRootAgent(ScenePresence obj) | ||
130 | { | ||
131 | lock (m_timer) | ||
132 | { | ||
133 | if (!m_timer.Enabled) | ||
134 | { | ||
135 | m_timer.Interval = m_Interval; | ||
136 | m_timer.Enabled = true; | ||
137 | m_timer.Start(); | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | |||
142 | public void OnRegionHandleRequest(IClientAPI client, UUID regionID) | ||
143 | { | ||
144 | //m_log.DebugFormat("[SERVICE THROTTLE]: RegionHandleRequest {0}", regionID); | ||
145 | ulong handle = 0; | ||
146 | if (IsLocalRegionHandle(regionID, out handle)) | ||
147 | { | ||
148 | client.SendRegionHandle(regionID, handle); | ||
149 | return; | ||
150 | } | ||
151 | |||
152 | Action action = delegate | ||
153 | { | ||
154 | GridRegion r = m_scenes[0].GridService.GetRegionByUUID(UUID.Zero, regionID); | ||
155 | |||
156 | if (r != null && r.RegionHandle != 0) | ||
157 | client.SendRegionHandle(regionID, r.RegionHandle); | ||
158 | }; | ||
159 | |||
160 | Enqueue("region", regionID.ToString(), action); | ||
161 | } | ||
162 | |||
163 | #endregion Events | ||
164 | |||
165 | #region IServiceThrottleModule | ||
166 | |||
167 | public void Enqueue(string category, string itemid, Action continuation) | ||
168 | { | ||
169 | lock (m_RequestQueue) | ||
170 | { | ||
171 | if (m_Pending.ContainsKey(category)) | ||
172 | { | ||
173 | if (m_Pending[category].Contains(itemid)) | ||
174 | // Don't enqueue, it's already pending | ||
175 | return; | ||
176 | } | ||
177 | else | ||
178 | m_Pending.Add(category, new List<string>()); | ||
179 | |||
180 | m_Pending[category].Add(itemid); | ||
181 | |||
182 | m_RequestQueue.Enqueue(delegate | ||
183 | { | ||
184 | lock (m_RequestQueue) | ||
185 | m_Pending[category].Remove(itemid); | ||
186 | |||
187 | continuation(); | ||
188 | }); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | #endregion IServiceThrottleModule | ||
193 | |||
194 | #region Process Continuation Queue | ||
195 | |||
196 | private void ProcessQueue(object sender, System.Timers.ElapsedEventArgs e) | ||
197 | { | ||
198 | //m_log.DebugFormat("[YYY]: Process queue with {0} continuations", m_RequestQueue.Count); | ||
199 | |||
200 | while (m_RequestQueue.Count > 0) | ||
201 | { | ||
202 | Action continuation = null; | ||
203 | lock (m_RequestQueue) | ||
204 | continuation = m_RequestQueue.Dequeue(); | ||
205 | |||
206 | if (continuation != null) | ||
207 | continuation(); | ||
208 | } | ||
209 | |||
210 | if (AreThereRootAgents()) | ||
211 | { | ||
212 | lock (m_timer) | ||
213 | { | ||
214 | m_timer.Interval = 1000; // 1 sec | ||
215 | m_timer.Enabled = true; | ||
216 | m_timer.Start(); | ||
217 | } | ||
218 | } | ||
219 | else | ||
220 | lock (m_timer) | ||
221 | m_timer.Enabled = false; | ||
222 | |||
223 | } | ||
224 | |||
225 | #endregion Process Continuation Queue | ||
226 | |||
227 | #region Misc | ||
228 | |||
229 | private bool IsLocalRegionHandle(UUID regionID, out ulong regionHandle) | ||
230 | { | ||
231 | regionHandle = 0; | ||
232 | foreach (Scene s in m_scenes) | ||
233 | if (s.RegionInfo.RegionID == regionID) | ||
234 | { | ||
235 | regionHandle = s.RegionInfo.RegionHandle; | ||
236 | return true; | ||
237 | } | ||
238 | return false; | ||
239 | } | ||
240 | |||
241 | private bool AreThereRootAgents() | ||
242 | { | ||
243 | foreach (Scene s in m_scenes) | ||
244 | { | ||
245 | foreach (ScenePresence sp in s.GetScenePresences()) | ||
246 | if (!sp.IsChildAgent) | ||
247 | return true; | ||
248 | } | ||
249 | |||
250 | return false; | ||
251 | } | ||
252 | |||
253 | #endregion Misc | ||
254 | } | ||
255 | |||
256 | } | ||
diff --git a/OpenSim/Region/CoreModules/Framework/Statistics/Logging/BinaryLoggingModule.cs b/OpenSim/Region/CoreModules/Framework/Statistics/Logging/BinaryLoggingModule.cs index fb74cc6..f3436d1 100644 --- a/OpenSim/Region/CoreModules/Framework/Statistics/Logging/BinaryLoggingModule.cs +++ b/OpenSim/Region/CoreModules/Framework/Statistics/Logging/BinaryLoggingModule.cs | |||
@@ -57,7 +57,7 @@ namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging | |||
57 | try | 57 | try |
58 | { | 58 | { |
59 | IConfig statConfig = source.Configs["Statistics.Binary"]; | 59 | IConfig statConfig = source.Configs["Statistics.Binary"]; |
60 | if (statConfig.Contains("enabled") && statConfig.GetBoolean("enabled")) | 60 | if (statConfig != null && statConfig.Contains("enabled") && statConfig.GetBoolean("enabled")) |
61 | { | 61 | { |
62 | if (statConfig.Contains("collect_region_stats")) | 62 | if (statConfig.Contains("collect_region_stats")) |
63 | { | 63 | { |
diff --git a/OpenSim/Region/CoreModules/Framework/UserManagement/HGUserManagementModule.cs b/OpenSim/Region/CoreModules/Framework/UserManagement/HGUserManagementModule.cs index 4ef57fe..7b89c2c 100644 --- a/OpenSim/Region/CoreModules/Framework/UserManagement/HGUserManagementModule.cs +++ b/OpenSim/Region/CoreModules/Framework/UserManagement/HGUserManagementModule.cs | |||
@@ -50,16 +50,15 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
50 | { | 50 | { |
51 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 51 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
52 | 52 | ||
53 | |||
54 | #region ISharedRegionModule | 53 | #region ISharedRegionModule |
55 | 54 | ||
56 | public new void Initialise(IConfigSource config) | 55 | public new void Initialise(IConfigSource config) |
57 | { | 56 | { |
58 | string umanmod = config.Configs["Modules"].GetString("UserManagementModule", base.Name); | 57 | string umanmod = config.Configs["Modules"].GetString("UserManagementModule", null); |
59 | if (umanmod == Name) | 58 | if (umanmod == Name) |
60 | { | 59 | { |
61 | m_Enabled = true; | 60 | m_Enabled = true; |
62 | RegisterConsoleCmds(); | 61 | Init(); |
63 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: {0} is enabled", Name); | 62 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: {0} is enabled", Name); |
64 | } | 63 | } |
65 | } | 64 | } |
@@ -71,7 +70,7 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
71 | 70 | ||
72 | #endregion ISharedRegionModule | 71 | #endregion ISharedRegionModule |
73 | 72 | ||
74 | protected override void AddAdditionalUsers(UUID avatarID, string query, List<UserData> users) | 73 | protected override void AddAdditionalUsers(string query, List<UserData> users) |
75 | { | 74 | { |
76 | if (query.Contains("@")) // First.Last@foo.com, maybe? | 75 | if (query.Contains("@")) // First.Last@foo.com, maybe? |
77 | { | 76 | { |
@@ -131,7 +130,17 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
131 | } | 130 | } |
132 | 131 | ||
133 | UserAgentServiceConnector uasConn = new UserAgentServiceConnector(uriStr); | 132 | UserAgentServiceConnector uasConn = new UserAgentServiceConnector(uriStr); |
134 | UUID userID = uasConn.GetUUID(names[0], names[1]); | 133 | |
134 | UUID userID = UUID.Zero; | ||
135 | try | ||
136 | { | ||
137 | userID = uasConn.GetUUID(names[0], names[1]); | ||
138 | } | ||
139 | catch (Exception e) | ||
140 | { | ||
141 | m_log.Debug("[USER MANAGEMENT MODULE]: GetUUID call failed ", e); | ||
142 | } | ||
143 | |||
135 | if (!userID.Equals(UUID.Zero)) | 144 | if (!userID.Equals(UUID.Zero)) |
136 | { | 145 | { |
137 | UserData ud = new UserData(); | 146 | UserData ud = new UserData(); |
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/CoreModules/Framework/UserManagement/UserManagementModule.cs b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs index 77e8b00..7ecbd26 100644 --- a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs +++ b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs | |||
@@ -28,9 +28,11 @@ using System; | |||
28 | using System.Collections.Generic; | 28 | using System.Collections.Generic; |
29 | using System.IO; | 29 | using System.IO; |
30 | using System.Reflection; | 30 | using System.Reflection; |
31 | using System.Threading; | ||
31 | 32 | ||
32 | using OpenSim.Framework; | 33 | using OpenSim.Framework; |
33 | using OpenSim.Framework.Console; | 34 | using OpenSim.Framework.Console; |
35 | using OpenSim.Framework.Monitoring; | ||
34 | using OpenSim.Region.ClientStack.LindenUDP; | 36 | using OpenSim.Region.ClientStack.LindenUDP; |
35 | using OpenSim.Region.Framework; | 37 | using OpenSim.Region.Framework; |
36 | using OpenSim.Region.Framework.Interfaces; | 38 | using OpenSim.Region.Framework.Interfaces; |
@@ -44,28 +46,24 @@ using log4net; | |||
44 | using Nini.Config; | 46 | using Nini.Config; |
45 | using Mono.Addins; | 47 | using Mono.Addins; |
46 | 48 | ||
49 | using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; | ||
50 | |||
47 | namespace OpenSim.Region.CoreModules.Framework.UserManagement | 51 | namespace OpenSim.Region.CoreModules.Framework.UserManagement |
48 | { | 52 | { |
49 | public class UserData | ||
50 | { | ||
51 | public UUID Id { get; set; } | ||
52 | public string FirstName { get; set; } | ||
53 | public string LastName { get; set; } | ||
54 | public string HomeURL { get; set; } | ||
55 | public Dictionary<string, object> ServerURLs { get; set; } | ||
56 | } | ||
57 | |||
58 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "UserManagementModule")] | 53 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "UserManagementModule")] |
59 | public class UserManagementModule : ISharedRegionModule, IUserManagement | 54 | public class UserManagementModule : ISharedRegionModule, IUserManagement, IPeople |
60 | { | 55 | { |
61 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 56 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
62 | 57 | ||
63 | protected bool m_Enabled; | 58 | protected bool m_Enabled; |
64 | protected List<Scene> m_Scenes = new List<Scene>(); | 59 | protected List<Scene> m_Scenes = new List<Scene>(); |
65 | 60 | ||
61 | protected IServiceThrottleModule m_ServiceThrottle; | ||
66 | // The cache | 62 | // The cache |
67 | protected Dictionary<UUID, UserData> m_UserCache = new Dictionary<UUID, UserData>(); | 63 | protected Dictionary<UUID, UserData> m_UserCache = new Dictionary<UUID, UserData>(); |
68 | 64 | ||
65 | protected bool m_DisplayChangingHomeURI = false; | ||
66 | |||
69 | #region ISharedRegionModule | 67 | #region ISharedRegionModule |
70 | 68 | ||
71 | public void Initialise(IConfigSource config) | 69 | public void Initialise(IConfigSource config) |
@@ -74,9 +72,20 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
74 | if (umanmod == Name) | 72 | if (umanmod == Name) |
75 | { | 73 | { |
76 | m_Enabled = true; | 74 | m_Enabled = true; |
77 | RegisterConsoleCmds(); | 75 | Init(); |
78 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: {0} is enabled", Name); | 76 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: {0} is enabled", Name); |
79 | } | 77 | } |
78 | |||
79 | if(!m_Enabled) | ||
80 | { | ||
81 | return; | ||
82 | } | ||
83 | |||
84 | IConfig userManagementConfig = config.Configs["UserManagement"]; | ||
85 | if (userManagementConfig == null) | ||
86 | return; | ||
87 | |||
88 | m_DisplayChangingHomeURI = userManagementConfig.GetBoolean("DisplayChangingHomeURI", false); | ||
80 | } | 89 | } |
81 | 90 | ||
82 | public bool IsSharedModule | 91 | public bool IsSharedModule |
@@ -98,9 +107,13 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
98 | { | 107 | { |
99 | if (m_Enabled) | 108 | if (m_Enabled) |
100 | { | 109 | { |
101 | m_Scenes.Add(scene); | 110 | lock (m_Scenes) |
111 | { | ||
112 | m_Scenes.Add(scene); | ||
113 | } | ||
102 | 114 | ||
103 | scene.RegisterModuleInterface<IUserManagement>(this); | 115 | scene.RegisterModuleInterface<IUserManagement>(this); |
116 | scene.RegisterModuleInterface<IPeople>(this); | ||
104 | scene.EventManager.OnNewClient += new EventManager.OnNewClientDelegate(EventManager_OnNewClient); | 117 | scene.EventManager.OnNewClient += new EventManager.OnNewClientDelegate(EventManager_OnNewClient); |
105 | scene.EventManager.OnPrimsLoaded += new EventManager.PrimsLoaded(EventManager_OnPrimsLoaded); | 118 | scene.EventManager.OnPrimsLoaded += new EventManager.PrimsLoaded(EventManager_OnPrimsLoaded); |
106 | } | 119 | } |
@@ -111,12 +124,17 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
111 | if (m_Enabled) | 124 | if (m_Enabled) |
112 | { | 125 | { |
113 | scene.UnregisterModuleInterface<IUserManagement>(this); | 126 | scene.UnregisterModuleInterface<IUserManagement>(this); |
114 | m_Scenes.Remove(scene); | 127 | lock (m_Scenes) |
128 | { | ||
129 | m_Scenes.Remove(scene); | ||
130 | } | ||
115 | } | 131 | } |
116 | } | 132 | } |
117 | 133 | ||
118 | public void RegionLoaded(Scene s) | 134 | public void RegionLoaded(Scene s) |
119 | { | 135 | { |
136 | if (m_Enabled && m_ServiceThrottle == null) | ||
137 | m_ServiceThrottle = s.RequestModuleInterface<IServiceThrottleModule>(); | ||
120 | } | 138 | } |
121 | 139 | ||
122 | public void PostInitialise() | 140 | public void PostInitialise() |
@@ -125,7 +143,10 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
125 | 143 | ||
126 | public void Close() | 144 | public void Close() |
127 | { | 145 | { |
128 | m_Scenes.Clear(); | 146 | lock (m_Scenes) |
147 | { | ||
148 | m_Scenes.Clear(); | ||
149 | } | ||
129 | 150 | ||
130 | lock (m_UserCache) | 151 | lock (m_UserCache) |
131 | m_UserCache.Clear(); | 152 | m_UserCache.Clear(); |
@@ -133,7 +154,7 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
133 | 154 | ||
134 | #endregion ISharedRegionModule | 155 | #endregion ISharedRegionModule |
135 | 156 | ||
136 | 157 | ||
137 | #region Event Handlers | 158 | #region Event Handlers |
138 | 159 | ||
139 | void EventManager_OnPrimsLoaded(Scene s) | 160 | void EventManager_OnPrimsLoaded(Scene s) |
@@ -143,7 +164,6 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
143 | s.ForEachSOG(delegate(SceneObjectGroup sog) { CacheCreators(sog); }); | 164 | s.ForEachSOG(delegate(SceneObjectGroup sog) { CacheCreators(sog); }); |
144 | } | 165 | } |
145 | 166 | ||
146 | |||
147 | void EventManager_OnNewClient(IClientAPI client) | 167 | void EventManager_OnNewClient(IClientAPI client) |
148 | { | 168 | { |
149 | client.OnConnectionClosed += new Action<IClientAPI>(HandleConnectionClosed); | 169 | client.OnConnectionClosed += new Action<IClientAPI>(HandleConnectionClosed); |
@@ -157,21 +177,52 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
157 | client.OnAvatarPickerRequest -= new AvatarPickerRequest(HandleAvatarPickerRequest); | 177 | client.OnAvatarPickerRequest -= new AvatarPickerRequest(HandleAvatarPickerRequest); |
158 | } | 178 | } |
159 | 179 | ||
160 | void HandleUUIDNameRequest(UUID uuid, IClientAPI remote_client) | 180 | void HandleUUIDNameRequest(UUID uuid, IClientAPI client) |
161 | { | 181 | { |
182 | // m_log.DebugFormat( | ||
183 | // "[USER MANAGEMENT MODULE]: Handling request for name binding of UUID {0} from {1}", | ||
184 | // uuid, remote_client.Name); | ||
185 | |||
162 | if (m_Scenes[0].LibraryService != null && (m_Scenes[0].LibraryService.LibraryRootFolder.Owner == uuid)) | 186 | if (m_Scenes[0].LibraryService != null && (m_Scenes[0].LibraryService.LibraryRootFolder.Owner == uuid)) |
163 | { | 187 | { |
164 | remote_client.SendNameReply(uuid, "Mr", "OpenSim"); | 188 | client.SendNameReply(uuid, "Mr", "OpenSim"); |
165 | } | 189 | } |
166 | else | 190 | else |
167 | { | 191 | { |
168 | string[] names = GetUserNames(uuid); | 192 | UserData user; |
169 | if (names.Length == 2) | 193 | /* bypass that continuation here when entry is already available */ |
194 | lock (m_UserCache) | ||
170 | { | 195 | { |
171 | //m_log.DebugFormat("[XXX] HandleUUIDNameRequest {0} is {1} {2}", uuid, names[0], names[1]); | 196 | if (m_UserCache.TryGetValue(uuid, out user)) |
172 | remote_client.SendNameReply(uuid, names[0], names[1]); | 197 | { |
198 | if (!user.IsUnknownUser && user.HasGridUserTried) | ||
199 | { | ||
200 | client.SendNameReply(uuid, user.FirstName, user.LastName); | ||
201 | return; | ||
202 | } | ||
203 | } | ||
173 | } | 204 | } |
174 | 205 | ||
206 | // Not found in cache, queue continuation | ||
207 | m_ServiceThrottle.Enqueue("name", uuid.ToString(), delegate | ||
208 | { | ||
209 | //m_log.DebugFormat("[YYY]: Name request {0}", uuid); | ||
210 | |||
211 | // As least upto September 2013, clients permanently cache UUID -> Name bindings. Some clients | ||
212 | // appear to clear this when the user asks it to clear the cache, but others may not. | ||
213 | // | ||
214 | // So to avoid clients | ||
215 | // (particularly Hypergrid clients) permanently binding "Unknown User" to a given UUID, we will | ||
216 | // instead drop the request entirely. | ||
217 | if (GetUser(uuid, out user)) | ||
218 | { | ||
219 | client.SendNameReply(uuid, user.FirstName, user.LastName); | ||
220 | } | ||
221 | // else | ||
222 | // m_log.DebugFormat( | ||
223 | // "[USER MANAGEMENT MODULE]: No bound name for {0} found, ignoring request from {1}", | ||
224 | // uuid, client.Name); | ||
225 | }); | ||
175 | } | 226 | } |
176 | } | 227 | } |
177 | 228 | ||
@@ -181,29 +232,7 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
181 | 232 | ||
182 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: HandleAvatarPickerRequest for {0}", query); | 233 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: HandleAvatarPickerRequest for {0}", query); |
183 | 234 | ||
184 | // searhc the user accounts service | 235 | List<UserData> users = GetUserData(query, 500, 1); |
185 | List<UserAccount> accs = m_Scenes[0].UserAccountService.GetUserAccounts(m_Scenes[0].RegionInfo.ScopeID, query); | ||
186 | |||
187 | List<UserData> users = new List<UserData>(); | ||
188 | if (accs != null) | ||
189 | { | ||
190 | foreach (UserAccount acc in accs) | ||
191 | { | ||
192 | UserData ud = new UserData(); | ||
193 | ud.FirstName = acc.FirstName; | ||
194 | ud.LastName = acc.LastName; | ||
195 | ud.Id = acc.PrincipalID; | ||
196 | users.Add(ud); | ||
197 | } | ||
198 | } | ||
199 | |||
200 | // search the local cache | ||
201 | foreach (UserData data in m_UserCache.Values) | ||
202 | if (users.Find(delegate(UserData d) { return d.Id == data.Id; }) == null && | ||
203 | (data.FirstName.StartsWith(query) || data.LastName.StartsWith(query))) | ||
204 | users.Add(data); | ||
205 | |||
206 | AddAdditionalUsers(avatarID, query, users); | ||
207 | 236 | ||
208 | AvatarPickerReplyPacket replyPacket = (AvatarPickerReplyPacket)PacketPool.Instance.GetPacket(PacketType.AvatarPickerReply); | 237 | AvatarPickerReplyPacket replyPacket = (AvatarPickerReplyPacket)PacketPool.Instance.GetPacket(PacketType.AvatarPickerReply); |
209 | // TODO: don't create new blocks if recycling an old packet | 238 | // TODO: don't create new blocks if recycling an old packet |
@@ -249,60 +278,62 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
249 | client.SendAvatarPickerReply(agent_data, data_args); | 278 | client.SendAvatarPickerReply(agent_data, data_args); |
250 | } | 279 | } |
251 | 280 | ||
252 | protected virtual void AddAdditionalUsers(UUID avatarID, string query, List<UserData> users) | 281 | protected virtual void AddAdditionalUsers(string query, List<UserData> users) |
253 | { | 282 | { |
254 | } | 283 | } |
255 | 284 | ||
256 | #endregion Event Handlers | 285 | #endregion Event Handlers |
257 | 286 | ||
258 | private void CacheCreators(SceneObjectGroup sog) | 287 | #region IPeople |
259 | { | ||
260 | //m_log.DebugFormat("[USER MANAGEMENT MODULE]: processing {0} {1}; {2}", sog.RootPart.Name, sog.RootPart.CreatorData, sog.RootPart.CreatorIdentification); | ||
261 | AddUser(sog.RootPart.CreatorID, sog.RootPart.CreatorData); | ||
262 | |||
263 | foreach (SceneObjectPart sop in sog.Parts) | ||
264 | { | ||
265 | AddUser(sop.CreatorID, sop.CreatorData); | ||
266 | foreach (TaskInventoryItem item in sop.TaskInventory.Values) | ||
267 | AddUser(item.CreatorID, item.CreatorData); | ||
268 | } | ||
269 | } | ||
270 | 288 | ||
271 | private string[] GetUserNames(UUID uuid) | 289 | public List<UserData> GetUserData(string query, int page_size, int page_number) |
272 | { | 290 | { |
273 | string[] returnstring = new string[2]; | 291 | // search the user accounts service |
292 | List<UserAccount> accs = m_Scenes[0].UserAccountService.GetUserAccounts(m_Scenes[0].RegionInfo.ScopeID, query); | ||
274 | 293 | ||
275 | lock (m_UserCache) | 294 | List<UserData> users = new List<UserData>(); |
295 | if (accs != null) | ||
276 | { | 296 | { |
277 | if (m_UserCache.ContainsKey(uuid)) | 297 | foreach (UserAccount acc in accs) |
278 | { | 298 | { |
279 | returnstring[0] = m_UserCache[uuid].FirstName; | 299 | UserData ud = new UserData(); |
280 | returnstring[1] = m_UserCache[uuid].LastName; | 300 | ud.FirstName = acc.FirstName; |
281 | return returnstring; | 301 | ud.LastName = acc.LastName; |
302 | ud.Id = acc.PrincipalID; | ||
303 | ud.HasGridUserTried = true; | ||
304 | ud.IsUnknownUser = false; | ||
305 | users.Add(ud); | ||
282 | } | 306 | } |
283 | } | 307 | } |
284 | 308 | ||
285 | UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(UUID.Zero, uuid); | 309 | // search the local cache |
286 | 310 | foreach (UserData data in m_UserCache.Values) | |
287 | if (account != null) | ||
288 | { | 311 | { |
289 | returnstring[0] = account.FirstName; | 312 | if (data.Id != UUID.Zero && !data.IsUnknownUser && |
290 | returnstring[1] = account.LastName; | 313 | users.Find(delegate(UserData d) { return d.Id == data.Id; }) == null && |
314 | (data.FirstName.ToLower().StartsWith(query.ToLower()) || data.LastName.ToLower().StartsWith(query.ToLower()))) | ||
315 | users.Add(data); | ||
316 | } | ||
291 | 317 | ||
292 | UserData user = new UserData(); | 318 | AddAdditionalUsers(query, users); |
293 | user.FirstName = account.FirstName; | ||
294 | user.LastName = account.LastName; | ||
295 | 319 | ||
296 | lock (m_UserCache) | 320 | return users; |
297 | m_UserCache[uuid] = user; | 321 | |
298 | } | 322 | } |
299 | else | 323 | |
324 | #endregion IPeople | ||
325 | |||
326 | private void CacheCreators(SceneObjectGroup sog) | ||
327 | { | ||
328 | //m_log.DebugFormat("[USER MANAGEMENT MODULE]: processing {0} {1}; {2}", sog.RootPart.Name, sog.RootPart.CreatorData, sog.RootPart.CreatorIdentification); | ||
329 | AddUser(sog.RootPart.CreatorID, sog.RootPart.CreatorData); | ||
330 | |||
331 | foreach (SceneObjectPart sop in sog.Parts) | ||
300 | { | 332 | { |
301 | returnstring[0] = "Unknown"; | 333 | AddUser(sop.CreatorID, sop.CreatorData); |
302 | returnstring[1] = "User"; | 334 | foreach (TaskInventoryItem item in sop.TaskInventory.Values) |
335 | AddUser(item.CreatorID, item.CreatorData); | ||
303 | } | 336 | } |
304 | |||
305 | return returnstring; | ||
306 | } | 337 | } |
307 | 338 | ||
308 | #region IUserManagement | 339 | #region IUserManagement |
@@ -338,55 +369,56 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
338 | 369 | ||
339 | public string GetUserName(UUID uuid) | 370 | public string GetUserName(UUID uuid) |
340 | { | 371 | { |
341 | string[] names = GetUserNames(uuid); | 372 | UserData user; |
342 | if (names.Length == 2) | 373 | GetUser(uuid, out user); |
343 | { | 374 | return user.FirstName + " " + user.LastName; |
344 | string firstname = names[0]; | ||
345 | string lastname = names[1]; | ||
346 | |||
347 | return firstname + " " + lastname; | ||
348 | |||
349 | } | ||
350 | return "(hippos)"; | ||
351 | } | 375 | } |
352 | 376 | ||
353 | public string GetUserHomeURL(UUID userID) | 377 | public string GetUserHomeURL(UUID userID) |
354 | { | 378 | { |
355 | lock (m_UserCache) | 379 | UserData user; |
380 | if(GetUser(userID, out user)) | ||
356 | { | 381 | { |
357 | if (m_UserCache.ContainsKey(userID)) | 382 | return user.HomeURL; |
358 | return m_UserCache[userID].HomeURL; | ||
359 | } | 383 | } |
360 | |||
361 | return string.Empty; | 384 | return string.Empty; |
362 | } | 385 | } |
363 | 386 | ||
364 | public string GetUserServerURL(UUID userID, string serverType) | 387 | public string GetUserServerURL(UUID userID, string serverType) |
365 | { | 388 | { |
366 | UserData userdata; | 389 | UserData userdata; |
367 | lock (m_UserCache) | 390 | if(!GetUser(userID, out userdata)) |
368 | m_UserCache.TryGetValue(userID, out userdata); | 391 | { |
392 | return string.Empty; | ||
393 | } | ||
394 | |||
395 | if (userdata.ServerURLs != null && userdata.ServerURLs.ContainsKey(serverType) && userdata.ServerURLs[serverType] != null) | ||
396 | { | ||
397 | return userdata.ServerURLs[serverType].ToString(); | ||
398 | } | ||
369 | 399 | ||
370 | if (userdata != null) | 400 | if (!string.IsNullOrEmpty(userdata.HomeURL)) |
371 | { | 401 | { |
372 | // m_log.DebugFormat("[USER MANAGEMENT MODULE]: Requested url type {0} for {1}", serverType, userID); | 402 | // m_log.DebugFormat("[USER MANAGEMENT MODULE]: Requested url type {0} for {1}", serverType, userID); |
373 | 403 | ||
374 | if (userdata.ServerURLs != null && userdata.ServerURLs.ContainsKey(serverType) && userdata.ServerURLs[serverType] != null) | 404 | UserAgentServiceConnector uConn = new UserAgentServiceConnector(userdata.HomeURL); |
405 | try | ||
375 | { | 406 | { |
376 | return userdata.ServerURLs[serverType].ToString(); | 407 | userdata.ServerURLs = uConn.GetServerURLs(userID); |
377 | } | 408 | } |
378 | 409 | catch(System.Net.WebException e) | |
379 | if (userdata.HomeURL != null && userdata.HomeURL != string.Empty) | ||
380 | { | 410 | { |
381 | //m_log.DebugFormat( | 411 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: GetServerURLs call failed {0}", e.Message); |
382 | // "[USER MANAGEMENT MODULE]: Did not find url type {0} so requesting urls from '{1}' for {2}", | 412 | userdata.ServerURLs = new Dictionary<string, object>(); |
383 | // serverType, userdata.HomeURL, userID); | ||
384 | |||
385 | UserAgentServiceConnector uConn = new UserAgentServiceConnector(userdata.HomeURL); | ||
386 | userdata.ServerURLs = uConn.GetServerURLs(userID); | ||
387 | if (userdata.ServerURLs != null && userdata.ServerURLs.ContainsKey(serverType) && userdata.ServerURLs[serverType] != null) | ||
388 | return userdata.ServerURLs[serverType].ToString(); | ||
389 | } | 413 | } |
414 | catch (Exception e) | ||
415 | { | ||
416 | m_log.Debug("[USER MANAGEMENT MODULE]: GetServerURLs call failed ", e); | ||
417 | userdata.ServerURLs = new Dictionary<string, object>(); | ||
418 | } | ||
419 | |||
420 | if (userdata.ServerURLs != null && userdata.ServerURLs.ContainsKey(serverType) && userdata.ServerURLs[serverType] != null) | ||
421 | return userdata.ServerURLs[serverType].ToString(); | ||
390 | } | 422 | } |
391 | 423 | ||
392 | return string.Empty; | 424 | return string.Empty; |
@@ -394,13 +426,15 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
394 | 426 | ||
395 | public string GetUserUUI(UUID userID) | 427 | public string GetUserUUI(UUID userID) |
396 | { | 428 | { |
397 | UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, userID); | 429 | string uui; |
398 | if (account != null) | 430 | GetUserUUI(userID, out uui); |
399 | return userID.ToString(); | 431 | return uui; |
432 | } | ||
400 | 433 | ||
434 | public bool GetUserUUI(UUID userID, out string uui) | ||
435 | { | ||
401 | UserData ud; | 436 | UserData ud; |
402 | lock (m_UserCache) | 437 | bool result = GetUser(userID, out ud); |
403 | m_UserCache.TryGetValue(userID, out ud); | ||
404 | 438 | ||
405 | if (ud != null) | 439 | if (ud != null) |
406 | { | 440 | { |
@@ -414,121 +448,251 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
414 | first = parts[0]; | 448 | first = parts[0]; |
415 | last = parts[1]; | 449 | last = parts[1]; |
416 | } | 450 | } |
417 | return userID + ";" + homeURL + ";" + first + " " + last; | 451 | uui = userID + ";" + homeURL + ";" + first + " " + last; |
418 | } | 452 | } |
419 | } | 453 | } |
420 | 454 | ||
421 | return userID.ToString(); | 455 | uui = userID.ToString(); |
456 | return result; | ||
422 | } | 457 | } |
423 | 458 | ||
424 | public void AddUser(UUID uuid, string first, string last) | 459 | #region Cache Management |
460 | public bool GetUser(UUID uuid, out UserData userdata) | ||
425 | { | 461 | { |
426 | lock (m_UserCache) | 462 | lock (m_UserCache) |
427 | { | 463 | { |
428 | if (m_UserCache.ContainsKey(uuid)) | 464 | if (m_UserCache.TryGetValue(uuid, out userdata)) |
429 | return; | 465 | { |
466 | if (userdata.HasGridUserTried) | ||
467 | { | ||
468 | return true; | ||
469 | } | ||
470 | } | ||
471 | else | ||
472 | { | ||
473 | userdata = new UserData(); | ||
474 | userdata.HasGridUserTried = false; | ||
475 | userdata.Id = uuid; | ||
476 | userdata.FirstName = "Unknown"; | ||
477 | userdata.LastName = "UserUMMAU42"; | ||
478 | userdata.HomeURL = string.Empty; | ||
479 | userdata.IsUnknownUser = true; | ||
480 | userdata.HasGridUserTried = false; | ||
481 | } | ||
482 | } | ||
483 | |||
484 | /* BEGIN: do not wrap this code in any lock here | ||
485 | * There are HTTP calls in here. | ||
486 | */ | ||
487 | if (!userdata.HasGridUserTried) | ||
488 | { | ||
489 | /* rewrite here */ | ||
490 | UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, uuid); | ||
491 | if (account != null) | ||
492 | { | ||
493 | userdata.FirstName = account.FirstName; | ||
494 | userdata.LastName = account.LastName; | ||
495 | userdata.HomeURL = string.Empty; | ||
496 | userdata.IsUnknownUser = false; | ||
497 | userdata.HasGridUserTried = true; | ||
498 | } | ||
430 | } | 499 | } |
431 | 500 | ||
432 | UserData user = new UserData(); | 501 | if (!userdata.HasGridUserTried) |
433 | user.Id = uuid; | 502 | { |
434 | user.FirstName = first; | 503 | GridUserInfo uInfo = null; |
435 | user.LastName = last; | 504 | if (null != m_Scenes[0].GridUserService) |
505 | { | ||
506 | uInfo = m_Scenes[0].GridUserService.GetGridUserInfo(uuid.ToString()); | ||
507 | } | ||
508 | if (uInfo != null) | ||
509 | { | ||
510 | string url, first, last, tmp; | ||
511 | UUID u; | ||
512 | if(uInfo.UserID.Length <= 36) | ||
513 | { | ||
514 | /* not a UUI */ | ||
515 | } | ||
516 | else if (Util.ParseUniversalUserIdentifier(uInfo.UserID, out u, out url, out first, out last, out tmp)) | ||
517 | { | ||
518 | if (url != string.Empty) | ||
519 | { | ||
520 | userdata.FirstName = first.Replace(" ", ".") + "." + last.Replace(" ", "."); | ||
521 | userdata.HomeURL = url; | ||
522 | try | ||
523 | { | ||
524 | userdata.LastName = "@" + new Uri(url).Authority; | ||
525 | userdata.IsUnknownUser = false; | ||
526 | } | ||
527 | catch | ||
528 | { | ||
529 | userdata.LastName = "@unknown"; | ||
530 | } | ||
531 | userdata.HasGridUserTried = true; | ||
532 | } | ||
533 | } | ||
534 | else | ||
535 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: Unable to parse UUI {0}", uInfo.UserID); | ||
536 | } | ||
537 | } | ||
538 | /* END: do not wrap this code in any lock here */ | ||
436 | 539 | ||
437 | AddUserInternal(user); | 540 | lock (m_UserCache) |
541 | { | ||
542 | m_UserCache[uuid] = userdata; | ||
543 | } | ||
544 | return !userdata.IsUnknownUser; | ||
438 | } | 545 | } |
439 | 546 | ||
440 | public void AddUser(UUID uuid, string first, string last, string homeURL) | 547 | public void AddUser(UUID uuid, string first, string last) |
441 | { | 548 | { |
442 | //m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, first {1}, last {2}, url {3}", uuid, first, last, homeURL); | 549 | lock(m_UserCache) |
443 | if (homeURL == string.Empty) | 550 | { |
444 | return; | 551 | if(!m_UserCache.ContainsKey(uuid)) |
445 | 552 | { | |
446 | AddUser(uuid, homeURL + ";" + first + " " + last); | 553 | UserData user = new UserData(); |
554 | user.Id = uuid; | ||
555 | user.FirstName = first; | ||
556 | user.LastName = last; | ||
557 | user.IsUnknownUser = false; | ||
558 | user.HasGridUserTried = false; | ||
559 | m_UserCache.Add(uuid, user); | ||
560 | } | ||
561 | } | ||
447 | } | 562 | } |
448 | 563 | ||
449 | public void AddUser (UUID id, string creatorData) | 564 | public void AddUser(UUID uuid, string first, string last, string homeURL) |
450 | { | 565 | { |
451 | //m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, creatorData {1}", id, creatorData); | 566 | //m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, first {1}, last {2}, url {3}", uuid, first, last, homeURL); |
452 | 567 | ||
453 | UserData oldUser; | 568 | UserData oldUser; |
454 | //lock the whole block - prevent concurrent update | ||
455 | lock (m_UserCache) | 569 | lock (m_UserCache) |
456 | { | 570 | { |
457 | m_UserCache.TryGetValue (id, out oldUser); | 571 | if (m_UserCache.TryGetValue(uuid, out oldUser)) |
458 | if (oldUser != null) | ||
459 | { | 572 | { |
460 | if (creatorData == null || creatorData == String.Empty) | 573 | if (!oldUser.IsUnknownUser) |
461 | { | 574 | { |
462 | //ignore updates without creator data | 575 | if (homeURL != oldUser.HomeURL && m_DisplayChangingHomeURI) |
576 | { | ||
577 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: Different HomeURI for {0} {1} ({2}): {3} and {4}", | ||
578 | first, last, uuid.ToString(), homeURL, oldUser.HomeURL); | ||
579 | } | ||
580 | /* no update needed */ | ||
463 | return; | 581 | return; |
464 | } | 582 | } |
465 | //try update unknown users | 583 | } |
466 | //and creator's home URL's | 584 | else if(!m_UserCache.ContainsKey(uuid)) |
467 | if ((oldUser.FirstName == "Unknown" && !creatorData.Contains ("Unknown")) || (oldUser.HomeURL != null && !creatorData.StartsWith (oldUser.HomeURL))) | 585 | { |
586 | oldUser = new UserData(); | ||
587 | oldUser.HasGridUserTried = false; | ||
588 | oldUser.IsUnknownUser = false; | ||
589 | if (homeURL != string.Empty) | ||
468 | { | 590 | { |
469 | m_UserCache.Remove (id); | 591 | oldUser.FirstName = first.Replace(" ", ".") + "." + last.Replace(" ", "."); |
470 | // m_log.DebugFormat("[USER MANAGEMENT MODULE]: Re-adding user with id {0}, creatorData [{1}] and old HomeURL {2}", id, creatorData,oldUser.HomeURL); | 592 | try |
593 | { | ||
594 | oldUser.LastName = "@" + new Uri(homeURL).Authority; | ||
595 | oldUser.IsUnknownUser = false; | ||
596 | } | ||
597 | catch | ||
598 | { | ||
599 | oldUser.LastName = "@unknown"; | ||
600 | } | ||
471 | } | 601 | } |
472 | else | 602 | else |
473 | { | 603 | { |
474 | //we have already a valid user within the cache | 604 | oldUser.FirstName = first; |
475 | return; | 605 | oldUser.LastName = last; |
476 | } | 606 | } |
607 | oldUser.HomeURL = homeURL; | ||
608 | oldUser.Id = uuid; | ||
609 | m_UserCache.Add(uuid, oldUser); | ||
477 | } | 610 | } |
611 | } | ||
612 | } | ||
478 | 613 | ||
479 | UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount (m_Scenes [0].RegionInfo.ScopeID, id); | 614 | public void AddUser(UUID id, string creatorData) |
615 | { | ||
616 | // m_log.InfoFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, creatorData {1}", id, creatorData); | ||
480 | 617 | ||
481 | if (account != null) | 618 | if(string.IsNullOrEmpty(creatorData)) |
619 | { | ||
620 | AddUser(id, string.Empty, string.Empty, string.Empty); | ||
621 | } | ||
622 | else | ||
623 | { | ||
624 | string homeURL; | ||
625 | string firstname = string.Empty; | ||
626 | string lastname = string.Empty; | ||
627 | |||
628 | //creatorData = <endpoint>;<name> | ||
629 | |||
630 | string[] parts = creatorData.Split(';'); | ||
631 | if(parts.Length > 1) | ||
482 | { | 632 | { |
483 | AddUser (id, account.FirstName, account.LastName); | 633 | string[] nameparts = parts[1].Split(' '); |
634 | firstname = nameparts[0]; | ||
635 | for(int xi = 1; xi < nameparts.Length; ++xi) | ||
636 | { | ||
637 | if(xi != 1) | ||
638 | { | ||
639 | lastname += " "; | ||
640 | } | ||
641 | lastname += nameparts[xi]; | ||
642 | } | ||
484 | } | 643 | } |
485 | else | 644 | else |
486 | { | 645 | { |
487 | UserData user = new UserData (); | 646 | firstname = "Unknown"; |
488 | user.Id = id; | 647 | lastname = "UserUMMAU5"; |
489 | 648 | } | |
490 | if (creatorData != null && creatorData != string.Empty) | 649 | if (parts.Length >= 1) |
650 | { | ||
651 | homeURL = parts[0]; | ||
652 | if(Uri.IsWellFormedUriString(homeURL, UriKind.Absolute)) | ||
653 | { | ||
654 | AddUser(id, firstname, lastname, homeURL); | ||
655 | } | ||
656 | else | ||
491 | { | 657 | { |
492 | //creatorData = <endpoint>;<name> | 658 | m_log.DebugFormat("[SCENE]: Unable to parse Uri {0} for CreatorID {1}", parts[0], creatorData); |
493 | 659 | ||
494 | string[] parts = creatorData.Split (';'); | 660 | lock (m_UserCache) |
495 | if (parts.Length >= 1) | ||
496 | { | 661 | { |
497 | user.HomeURL = parts [0]; | 662 | if(!m_UserCache.ContainsKey(id)) |
498 | try | ||
499 | { | ||
500 | Uri uri = new Uri (parts [0]); | ||
501 | user.LastName = "@" + uri.Authority; | ||
502 | } | ||
503 | catch (UriFormatException) | ||
504 | { | 663 | { |
505 | m_log.DebugFormat ("[SCENE]: Unable to parse Uri {0}", parts [0]); | 664 | UserData newUser = new UserData(); |
506 | user.LastName = "@unknown"; | 665 | newUser.Id = id; |
666 | newUser.FirstName = firstname + "." + lastname.Replace(' ', '.'); | ||
667 | newUser.LastName = "@unknown"; | ||
668 | newUser.HomeURL = string.Empty; | ||
669 | newUser.HasGridUserTried = false; | ||
670 | newUser.IsUnknownUser = true; /* we mark those users as Unknown user so a re-retrieve may be activated */ | ||
671 | m_UserCache.Add(id, newUser); | ||
507 | } | 672 | } |
508 | } | 673 | } |
509 | if (parts.Length >= 2) | ||
510 | user.FirstName = parts [1].Replace (' ', '.'); | ||
511 | } | 674 | } |
512 | else | 675 | } |
676 | else | ||
677 | { | ||
678 | lock(m_UserCache) | ||
513 | { | 679 | { |
514 | user.FirstName = "Unknown"; | 680 | if(!m_UserCache.ContainsKey(id)) |
515 | user.LastName = "User"; | 681 | { |
682 | UserData newUser = new UserData(); | ||
683 | newUser.Id = id; | ||
684 | newUser.FirstName = "Unknown"; | ||
685 | newUser.LastName = "UserUMMAU4"; | ||
686 | newUser.HomeURL = string.Empty; | ||
687 | newUser.IsUnknownUser = true; | ||
688 | newUser.HasGridUserTried = false; | ||
689 | m_UserCache.Add(id, newUser); | ||
690 | } | ||
516 | } | 691 | } |
517 | |||
518 | AddUserInternal (user); | ||
519 | } | 692 | } |
520 | } | 693 | } |
521 | } | 694 | } |
522 | 695 | #endregion | |
523 | void AddUserInternal(UserData user) | ||
524 | { | ||
525 | lock (m_UserCache) | ||
526 | m_UserCache[user.Id] = user; | ||
527 | |||
528 | //m_log.DebugFormat( | ||
529 | // "[USER MANAGEMENT MODULE]: Added user {0} {1} {2} {3}", | ||
530 | // user.Id, user.FirstName, user.LastName, user.HomeURL); | ||
531 | } | ||
532 | 696 | ||
533 | public bool IsLocalGridUser(UUID uuid) | 697 | public bool IsLocalGridUser(UUID uuid) |
534 | { | 698 | { |
@@ -541,36 +705,95 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement | |||
541 | 705 | ||
542 | #endregion IUserManagement | 706 | #endregion IUserManagement |
543 | 707 | ||
708 | protected void Init() | ||
709 | { | ||
710 | AddUser(UUID.Zero, "Unknown", "User"); | ||
711 | RegisterConsoleCmds(); | ||
712 | } | ||
713 | |||
544 | protected void RegisterConsoleCmds() | 714 | protected void RegisterConsoleCmds() |
545 | { | 715 | { |
546 | MainConsole.Instance.Commands.AddCommand("Users", true, | 716 | MainConsole.Instance.Commands.AddCommand("Users", true, |
717 | "show name", | ||
718 | "show name <uuid>", | ||
719 | "Show the bindings between a single user UUID and a user name", | ||
720 | String.Empty, | ||
721 | HandleShowUser); | ||
722 | |||
723 | MainConsole.Instance.Commands.AddCommand("Users", true, | ||
547 | "show names", | 724 | "show names", |
548 | "show names", | 725 | "show names", |
549 | "Show the bindings between user UUIDs and user names", | 726 | "Show the bindings between user UUIDs and user names", |
550 | String.Empty, | 727 | String.Empty, |
551 | HandleShowUsers); | 728 | HandleShowUsers); |
729 | |||
730 | MainConsole.Instance.Commands.AddCommand("Users", true, | ||
731 | "reset user cache", | ||
732 | "reset user cache", | ||
733 | "reset user cache to allow changed settings to be applied", | ||
734 | String.Empty, | ||
735 | HandleResetUserCache); | ||
552 | } | 736 | } |
553 | 737 | ||
554 | private void HandleShowUsers(string module, string[] cmd) | 738 | private void HandleResetUserCache(string module, string[] cmd) |
555 | { | 739 | { |
556 | lock (m_UserCache) | 740 | lock(m_UserCache) |
557 | { | 741 | { |
558 | if (m_UserCache.Count == 0) | 742 | m_UserCache.Clear(); |
559 | { | 743 | } |
560 | MainConsole.Instance.Output("No users found"); | 744 | } |
561 | return; | 745 | |
562 | } | 746 | private void HandleShowUser(string module, string[] cmd) |
563 | 747 | { | |
564 | MainConsole.Instance.Output("UUID User Name"); | 748 | if (cmd.Length < 3) |
565 | MainConsole.Instance.Output("-----------------------------------------------------------------------------"); | 749 | { |
566 | foreach (KeyValuePair<UUID, UserData> kvp in m_UserCache) | 750 | MainConsole.Instance.OutputFormat("Usage: show name <uuid>"); |
567 | { | ||
568 | MainConsole.Instance.Output(String.Format("{0} {1} {2} ({3})", | ||
569 | kvp.Key, kvp.Value.FirstName, kvp.Value.LastName, kvp.Value.HomeURL)); | ||
570 | } | ||
571 | |||
572 | return; | 751 | return; |
573 | } | 752 | } |
753 | |||
754 | UUID userId; | ||
755 | if (!ConsoleUtil.TryParseConsoleUuid(MainConsole.Instance, cmd[2], out userId)) | ||
756 | return; | ||
757 | |||
758 | UserData ud; | ||
759 | |||
760 | if(!GetUser(userId, out ud)) | ||
761 | { | ||
762 | MainConsole.Instance.OutputFormat("No name known for user with id {0}", userId); | ||
763 | return; | ||
764 | } | ||
765 | |||
766 | ConsoleDisplayTable cdt = new ConsoleDisplayTable(); | ||
767 | cdt.AddColumn("UUID", 36); | ||
768 | cdt.AddColumn("Name", 30); | ||
769 | cdt.AddColumn("HomeURL", 40); | ||
770 | cdt.AddRow(userId, string.Format("{0} {1}", ud.FirstName, ud.LastName), ud.HomeURL); | ||
771 | |||
772 | MainConsole.Instance.Output(cdt.ToString()); | ||
574 | } | 773 | } |
774 | |||
775 | private void HandleShowUsers(string module, string[] cmd) | ||
776 | { | ||
777 | ConsoleDisplayTable cdt = new ConsoleDisplayTable(); | ||
778 | cdt.AddColumn("UUID", 36); | ||
779 | cdt.AddColumn("Name", 30); | ||
780 | cdt.AddColumn("HomeURL", 40); | ||
781 | cdt.AddColumn("Checked", 10); | ||
782 | |||
783 | Dictionary<UUID, UserData> copyDict; | ||
784 | lock(m_UserCache) | ||
785 | { | ||
786 | copyDict = new Dictionary<UUID, UserData>(m_UserCache); | ||
787 | } | ||
788 | |||
789 | foreach(KeyValuePair<UUID, UserData> kvp in copyDict) | ||
790 | { | ||
791 | cdt.AddRow(kvp.Key, string.Format("{0} {1}", kvp.Value.FirstName, kvp.Value.LastName), kvp.Value.HomeURL, kvp.Value.HasGridUserTried ? "yes" : "no"); | ||
792 | } | ||
793 | |||
794 | MainConsole.Instance.Output(cdt.ToString()); | ||
795 | } | ||
796 | |||
575 | } | 797 | } |
576 | } \ No newline at end of file | 798 | |
799 | } | ||