diff options
Diffstat (limited to 'OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs')
-rw-r--r-- | OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs b/OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs new file mode 100644 index 0000000..1eedbef --- /dev/null +++ b/OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs | |||
@@ -0,0 +1,482 @@ | |||
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.IO; | ||
31 | using System.Net; | ||
32 | using System.Reflection; | ||
33 | using System.Text; | ||
34 | using System.Collections; | ||
35 | |||
36 | using OpenSim.Framework; | ||
37 | using OpenSim.Services.Interfaces; | ||
38 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; | ||
39 | |||
40 | using OpenMetaverse; | ||
41 | using OpenMetaverse.StructuredData; | ||
42 | using log4net; | ||
43 | using Nini.Config; | ||
44 | |||
45 | namespace OpenSim.Services.Connectors.Simulation | ||
46 | { | ||
47 | public class SimulationServiceConnector : ISimulationService | ||
48 | { | ||
49 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
50 | |||
51 | // we use this dictionary to track the pending updateagent requests, maps URI --> position update | ||
52 | private Dictionary<string,AgentPosition> m_updateAgentQueue = new Dictionary<string,AgentPosition>(); | ||
53 | |||
54 | //private GridRegion m_Region; | ||
55 | |||
56 | public SimulationServiceConnector() | ||
57 | { | ||
58 | } | ||
59 | |||
60 | public SimulationServiceConnector(IConfigSource config) | ||
61 | { | ||
62 | //m_Region = region; | ||
63 | } | ||
64 | |||
65 | public IScene GetScene(UUID regionId) | ||
66 | { | ||
67 | return null; | ||
68 | } | ||
69 | |||
70 | public ISimulationService GetInnerService() | ||
71 | { | ||
72 | return null; | ||
73 | } | ||
74 | |||
75 | #region Agents | ||
76 | |||
77 | protected virtual string AgentPath() | ||
78 | { | ||
79 | return "agent/"; | ||
80 | } | ||
81 | |||
82 | protected virtual void PackData(OSDMap args, GridRegion source, AgentCircuitData aCircuit, GridRegion destination, uint flags) | ||
83 | { | ||
84 | if (source != null) | ||
85 | { | ||
86 | args["source_x"] = OSD.FromString(source.RegionLocX.ToString()); | ||
87 | args["source_y"] = OSD.FromString(source.RegionLocY.ToString()); | ||
88 | args["source_name"] = OSD.FromString(source.RegionName); | ||
89 | args["source_uuid"] = OSD.FromString(source.RegionID.ToString()); | ||
90 | if (!String.IsNullOrEmpty(source.RawServerURI)) | ||
91 | args["source_server_uri"] = OSD.FromString(source.RawServerURI); | ||
92 | } | ||
93 | |||
94 | args["destination_x"] = OSD.FromString(destination.RegionLocX.ToString()); | ||
95 | args["destination_y"] = OSD.FromString(destination.RegionLocY.ToString()); | ||
96 | args["destination_name"] = OSD.FromString(destination.RegionName); | ||
97 | args["destination_uuid"] = OSD.FromString(destination.RegionID.ToString()); | ||
98 | args["teleport_flags"] = OSD.FromString(flags.ToString()); | ||
99 | } | ||
100 | |||
101 | public bool CreateAgent(GridRegion source, GridRegion destination, AgentCircuitData aCircuit, uint flags, out string reason) | ||
102 | { | ||
103 | string tmp = String.Empty; | ||
104 | return CreateAgent(source, destination, aCircuit, flags, out tmp, out reason); | ||
105 | } | ||
106 | |||
107 | public bool CreateAgent(GridRegion source, GridRegion destination, AgentCircuitData aCircuit, uint flags, out string myipaddress, out string reason) | ||
108 | { | ||
109 | m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: Creating agent at {0}", destination.ServerURI); | ||
110 | reason = String.Empty; | ||
111 | myipaddress = String.Empty; | ||
112 | |||
113 | if (destination == null) | ||
114 | { | ||
115 | m_log.Debug("[REMOTE SIMULATION CONNECTOR]: Given destination is null"); | ||
116 | return false; | ||
117 | } | ||
118 | |||
119 | string uri = destination.ServerURI + AgentPath() + aCircuit.AgentID + "/"; | ||
120 | |||
121 | try | ||
122 | { | ||
123 | OSDMap args = aCircuit.PackAgentCircuitData(); | ||
124 | PackData(args, source, aCircuit, destination, flags); | ||
125 | |||
126 | OSDMap result = WebUtil.PostToServiceCompressed(uri, args, 30000); | ||
127 | bool success = result["success"].AsBoolean(); | ||
128 | if (success && result.ContainsKey("_Result")) | ||
129 | { | ||
130 | OSDMap data = (OSDMap)result["_Result"]; | ||
131 | |||
132 | reason = data["reason"].AsString(); | ||
133 | success = data["success"].AsBoolean(); | ||
134 | myipaddress = data["your_ip"].AsString(); | ||
135 | return success; | ||
136 | } | ||
137 | |||
138 | // Try the old version, uncompressed | ||
139 | result = WebUtil.PostToService(uri, args, 30000, false); | ||
140 | |||
141 | if (result["Success"].AsBoolean()) | ||
142 | { | ||
143 | if (result.ContainsKey("_Result")) | ||
144 | { | ||
145 | OSDMap data = (OSDMap)result["_Result"]; | ||
146 | |||
147 | reason = data["reason"].AsString(); | ||
148 | success = data["success"].AsBoolean(); | ||
149 | myipaddress = data["your_ip"].AsString(); | ||
150 | m_log.WarnFormat( | ||
151 | "[REMOTE SIMULATION CONNECTOR]: Remote simulator {0} did not accept compressed transfer, suggest updating it.", destination.RegionName); | ||
152 | return success; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | m_log.WarnFormat( | ||
157 | "[REMOTE SIMULATION CONNECTOR]: Failed to create agent {0} {1} at remote simulator {2}", | ||
158 | aCircuit.firstname, aCircuit.lastname, destination.RegionName); | ||
159 | reason = result["Message"] != null ? result["Message"].AsString() : "error"; | ||
160 | return false; | ||
161 | } | ||
162 | catch (Exception e) | ||
163 | { | ||
164 | m_log.Warn("[REMOTE SIMULATION CONNECTOR]: CreateAgent failed with exception: " + e.ToString()); | ||
165 | reason = e.Message; | ||
166 | } | ||
167 | |||
168 | return false; | ||
169 | } | ||
170 | |||
171 | /// <summary> | ||
172 | /// Send complete data about an agent in this region to a neighbor | ||
173 | /// </summary> | ||
174 | public bool UpdateAgent(GridRegion destination, AgentData data) | ||
175 | { | ||
176 | return UpdateAgent(destination, (IAgentData)data, 200000); // yes, 200 seconds | ||
177 | } | ||
178 | |||
179 | private ExpiringCache<string, bool> _failedSims = new ExpiringCache<string, bool>(); | ||
180 | /// <summary> | ||
181 | /// Send updated position information about an agent in this region to a neighbor | ||
182 | /// This operation may be called very frequently if an avatar is moving about in | ||
183 | /// the region. | ||
184 | /// </summary> | ||
185 | public bool UpdateAgent(GridRegion destination, AgentPosition data) | ||
186 | { | ||
187 | bool v = true; | ||
188 | if (_failedSims.TryGetValue(destination.ServerURI, out v)) | ||
189 | return false; | ||
190 | |||
191 | // The basic idea of this code is that the first thread that needs to | ||
192 | // send an update for a specific avatar becomes the worker for any subsequent | ||
193 | // requests until there are no more outstanding requests. Further, only send the most | ||
194 | // recent update; this *should* never be needed but some requests get | ||
195 | // slowed down and once that happens the problem with service end point | ||
196 | // limits kicks in and nothing proceeds | ||
197 | string uri = destination.ServerURI + AgentPath() + data.AgentID + "/"; | ||
198 | lock (m_updateAgentQueue) | ||
199 | { | ||
200 | if (m_updateAgentQueue.ContainsKey(uri)) | ||
201 | { | ||
202 | // Another thread is already handling | ||
203 | // updates for this simulator, just update | ||
204 | // the position and return, overwrites are | ||
205 | // not a problem since we only care about the | ||
206 | // last update anyway | ||
207 | m_updateAgentQueue[uri] = data; | ||
208 | return true; | ||
209 | } | ||
210 | |||
211 | // Otherwise update the reference and start processing | ||
212 | m_updateAgentQueue[uri] = data; | ||
213 | } | ||
214 | |||
215 | AgentPosition pos = null; | ||
216 | bool success = true; | ||
217 | while (success) | ||
218 | { | ||
219 | lock (m_updateAgentQueue) | ||
220 | { | ||
221 | // save the position | ||
222 | AgentPosition lastpos = pos; | ||
223 | |||
224 | pos = m_updateAgentQueue[uri]; | ||
225 | |||
226 | // this is true if no one put a new | ||
227 | // update in the map since the last | ||
228 | // one we processed, if thats the | ||
229 | // case then we are done | ||
230 | if (pos == lastpos) | ||
231 | { | ||
232 | m_updateAgentQueue.Remove(uri); | ||
233 | return true; | ||
234 | } | ||
235 | } | ||
236 | |||
237 | success = UpdateAgent(destination, (IAgentData)pos, 10000); | ||
238 | } | ||
239 | // we get here iff success == false | ||
240 | // blacklist sim for 2 minutes | ||
241 | lock (m_updateAgentQueue) | ||
242 | { | ||
243 | _failedSims.AddOrUpdate(destination.ServerURI, true, 120); | ||
244 | m_updateAgentQueue.Remove(uri); | ||
245 | } | ||
246 | return false; | ||
247 | } | ||
248 | |||
249 | /// <summary> | ||
250 | /// This is the worker function to send AgentData to a neighbor region | ||
251 | /// </summary> | ||
252 | private bool UpdateAgent(GridRegion destination, IAgentData cAgentData, int timeout) | ||
253 | { | ||
254 | // m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: UpdateAgent in {0}", destination.ServerURI); | ||
255 | |||
256 | // Eventually, we want to use a caps url instead of the agentID | ||
257 | string uri = destination.ServerURI + AgentPath() + cAgentData.AgentID + "/"; | ||
258 | |||
259 | try | ||
260 | { | ||
261 | OSDMap args = cAgentData.Pack(); | ||
262 | |||
263 | args["destination_x"] = OSD.FromString(destination.RegionLocX.ToString()); | ||
264 | args["destination_y"] = OSD.FromString(destination.RegionLocY.ToString()); | ||
265 | args["destination_name"] = OSD.FromString(destination.RegionName); | ||
266 | args["destination_uuid"] = OSD.FromString(destination.RegionID.ToString()); | ||
267 | |||
268 | OSDMap result = WebUtil.PutToServiceCompressed(uri, args, timeout); | ||
269 | if (result["Success"].AsBoolean()) | ||
270 | return true; | ||
271 | |||
272 | result = WebUtil.PutToService(uri, args, timeout); | ||
273 | |||
274 | return result["Success"].AsBoolean(); | ||
275 | } | ||
276 | catch (Exception e) | ||
277 | { | ||
278 | m_log.Warn("[REMOTE SIMULATION CONNECTOR]: UpdateAgent failed with exception: " + e.ToString()); | ||
279 | } | ||
280 | |||
281 | return false; | ||
282 | } | ||
283 | |||
284 | |||
285 | public bool QueryAccess(GridRegion destination, UUID agentID, string agentHomeURI, bool viaTeleport, Vector3 position, string myversion, List<UUID> featuresAvailable, out string version, out string reason) | ||
286 | { | ||
287 | reason = "Failed to contact destination"; | ||
288 | version = "Unknown"; | ||
289 | |||
290 | // m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: QueryAccess start, position={0}", position); | ||
291 | |||
292 | IPEndPoint ext = destination.ExternalEndPoint; | ||
293 | if (ext == null) return false; | ||
294 | |||
295 | // Eventually, we want to use a caps url instead of the agentID | ||
296 | string uri = destination.ServerURI + AgentPath() + agentID + "/" + destination.RegionID.ToString() + "/"; | ||
297 | |||
298 | OSDMap request = new OSDMap(); | ||
299 | request.Add("viaTeleport", OSD.FromBoolean(viaTeleport)); | ||
300 | request.Add("position", OSD.FromString(position.ToString())); | ||
301 | request.Add("my_version", OSD.FromString(myversion)); | ||
302 | |||
303 | OSDArray features = new OSDArray(); | ||
304 | foreach (UUID feature in featuresAvailable) | ||
305 | features.Add(OSD.FromString(feature.ToString())); | ||
306 | |||
307 | request.Add("features", features); | ||
308 | |||
309 | if (agentHomeURI != null) | ||
310 | request.Add("agent_home_uri", OSD.FromString(agentHomeURI)); | ||
311 | |||
312 | try | ||
313 | { | ||
314 | OSDMap result = WebUtil.ServiceOSDRequest(uri, request, "QUERYACCESS", 30000, false, false); | ||
315 | bool success = result["success"].AsBoolean(); | ||
316 | if (result.ContainsKey("_Result")) | ||
317 | { | ||
318 | OSDMap data = (OSDMap)result["_Result"]; | ||
319 | |||
320 | // FIXME: If there is a _Result map then it's the success key here that indicates the true success | ||
321 | // or failure, not the sibling result node. | ||
322 | success = data["success"]; | ||
323 | |||
324 | reason = data["reason"].AsString(); | ||
325 | if (data["version"] != null && data["version"].AsString() != string.Empty) | ||
326 | version = data["version"].AsString(); | ||
327 | |||
328 | m_log.DebugFormat( | ||
329 | "[REMOTE SIMULATION CONNECTOR]: QueryAccess to {0} returned {1}, reason {2}, version {3} ({4})", | ||
330 | uri, success, reason, version, data["version"].AsString()); | ||
331 | } | ||
332 | |||
333 | if (!success) | ||
334 | { | ||
335 | // If we don't check this then OpenSimulator 0.7.3.1 and some period before will never see the | ||
336 | // actual failure message | ||
337 | if (!result.ContainsKey("_Result")) | ||
338 | { | ||
339 | if (result.ContainsKey("Message")) | ||
340 | { | ||
341 | string message = result["Message"].AsString(); | ||
342 | if (message == "Service request failed: [MethodNotAllowed] MethodNotAllowed") // Old style region | ||
343 | { | ||
344 | m_log.Info("[REMOTE SIMULATION CONNECTOR]: The above web util error was caused by a TP to a sim that doesn't support QUERYACCESS and can be ignored"); | ||
345 | return true; | ||
346 | } | ||
347 | |||
348 | reason = result["Message"]; | ||
349 | } | ||
350 | else | ||
351 | { | ||
352 | reason = "Communications failure"; | ||
353 | } | ||
354 | } | ||
355 | |||
356 | return false; | ||
357 | } | ||
358 | |||
359 | |||
360 | featuresAvailable.Clear(); | ||
361 | |||
362 | if (result.ContainsKey("features")) | ||
363 | { | ||
364 | OSDArray array = (OSDArray)result["features"]; | ||
365 | |||
366 | foreach (OSD o in array) | ||
367 | featuresAvailable.Add(new UUID(o.AsString())); | ||
368 | } | ||
369 | |||
370 | return success; | ||
371 | } | ||
372 | catch (Exception e) | ||
373 | { | ||
374 | m_log.WarnFormat("[REMOTE SIMULATION CONNECTOR] QueryAcesss failed with exception; {0}",e.ToString()); | ||
375 | } | ||
376 | |||
377 | return false; | ||
378 | } | ||
379 | |||
380 | /// <summary> | ||
381 | /// </summary> | ||
382 | public bool ReleaseAgent(UUID origin, UUID id, string uri) | ||
383 | { | ||
384 | // m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: ReleaseAgent start"); | ||
385 | |||
386 | try | ||
387 | { | ||
388 | WebUtil.ServiceOSDRequest(uri, null, "DELETE", 10000, false, false); | ||
389 | } | ||
390 | catch (Exception e) | ||
391 | { | ||
392 | m_log.WarnFormat("[REMOTE SIMULATION CONNECTOR] ReleaseAgent failed with exception; {0}",e.ToString()); | ||
393 | } | ||
394 | |||
395 | return true; | ||
396 | } | ||
397 | |||
398 | /// <summary> | ||
399 | /// </summary> | ||
400 | public bool CloseAgent(GridRegion destination, UUID id, string auth_code) | ||
401 | { | ||
402 | string uri = destination.ServerURI + AgentPath() + id + "/" + destination.RegionID.ToString() + "/?auth=" + auth_code; | ||
403 | m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: CloseAgent {0}", uri); | ||
404 | |||
405 | try | ||
406 | { | ||
407 | WebUtil.ServiceOSDRequest(uri, null, "DELETE", 10000, false, false); | ||
408 | } | ||
409 | catch (Exception e) | ||
410 | { | ||
411 | m_log.WarnFormat("[REMOTE SIMULATION CONNECTOR] CloseAgent failed with exception; {0}",e.ToString()); | ||
412 | } | ||
413 | |||
414 | return true; | ||
415 | } | ||
416 | |||
417 | #endregion Agents | ||
418 | |||
419 | #region Objects | ||
420 | |||
421 | protected virtual string ObjectPath() | ||
422 | { | ||
423 | return "object/"; | ||
424 | } | ||
425 | |||
426 | /// <summary> | ||
427 | /// | ||
428 | /// </summary> | ||
429 | public bool CreateObject(GridRegion destination, Vector3 newPosition, ISceneObject sog, bool isLocalCall) | ||
430 | { | ||
431 | // m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: CreateObject start"); | ||
432 | |||
433 | string uri = destination.ServerURI + ObjectPath() + sog.UUID + "/"; | ||
434 | |||
435 | try | ||
436 | { | ||
437 | OSDMap args = new OSDMap(2); | ||
438 | |||
439 | args["sog"] = OSD.FromString(sog.ToXml2()); | ||
440 | args["extra"] = OSD.FromString(sog.ExtraToXmlString()); | ||
441 | args["modified"] = OSD.FromBoolean(sog.HasGroupChanged); | ||
442 | args["new_position"] = newPosition.ToString(); | ||
443 | |||
444 | string state = sog.GetStateSnapshot(); | ||
445 | if (state.Length > 0) | ||
446 | args["state"] = OSD.FromString(state); | ||
447 | |||
448 | // Add the input general arguments | ||
449 | args["destination_x"] = OSD.FromString(destination.RegionLocX.ToString()); | ||
450 | args["destination_y"] = OSD.FromString(destination.RegionLocY.ToString()); | ||
451 | args["destination_name"] = OSD.FromString(destination.RegionName); | ||
452 | args["destination_uuid"] = OSD.FromString(destination.RegionID.ToString()); | ||
453 | |||
454 | OSDMap result = WebUtil.PostToService(uri, args, 40000, false); | ||
455 | |||
456 | if (result == null) | ||
457 | return false; | ||
458 | bool success = result["success"].AsBoolean(); | ||
459 | if (!success) | ||
460 | return false; | ||
461 | } | ||
462 | catch (Exception e) | ||
463 | { | ||
464 | m_log.WarnFormat("[REMOTE SIMULATION CONNECTOR] CreateObject failed with exception; {0}",e.ToString()); | ||
465 | return false; | ||
466 | } | ||
467 | |||
468 | return true; | ||
469 | } | ||
470 | |||
471 | /// <summary> | ||
472 | /// | ||
473 | /// </summary> | ||
474 | public bool CreateObject(GridRegion destination, UUID userID, UUID itemID) | ||
475 | { | ||
476 | // TODO, not that urgent | ||
477 | return false; | ||
478 | } | ||
479 | |||
480 | #endregion Objects | ||
481 | } | ||
482 | } | ||