OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
SimulationServiceConnector.cs
Go to the documentation of this file.
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;
39 
40 using OpenMetaverse;
41 using OpenMetaverse.StructuredData;
42 using log4net;
43 using Nini.Config;
44 
45 namespace OpenSim.Services.Connectors.Simulation
46 {
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 
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 
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, EntityTransferContext ctx, out string reason)
102  {
103  string tmp = String.Empty;
104  return CreateAgent(source, destination, aCircuit, flags, ctx, out tmp, out reason);
105  }
106 
107  public bool CreateAgent(GridRegion source, GridRegion destination, AgentCircuitData aCircuit, uint flags, EntityTransferContext ctx, 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  reason = "Destination not found";
116  m_log.Debug("[REMOTE SIMULATION CONNECTOR]: Given destination is null");
117  return false;
118  }
119 
120  string uri = destination.ServerURI + AgentPath() + aCircuit.AgentID + "/";
121 
122  try
123  {
124  OSDMap args = aCircuit.PackAgentCircuitData(ctx);
125  args["context"] = ctx.Pack();
126  PackData(args, source, aCircuit, destination, flags);
127 
128  OSDMap result = WebUtil.PostToServiceCompressed(uri, args, 30000);
129  bool success = result["success"].AsBoolean();
130  if (success && result.ContainsKey("_Result"))
131  {
132  OSDMap data = (OSDMap)result["_Result"];
133 
134  reason = data["reason"].AsString();
135  success = data["success"].AsBoolean();
136  myipaddress = data["your_ip"].AsString();
137  return success;
138  }
139 
140  // Try the old version, uncompressed
141  result = WebUtil.PostToService(uri, args, 30000, false);
142 
143  if (result["Success"].AsBoolean())
144  {
145  if (result.ContainsKey("_Result"))
146  {
147  OSDMap data = (OSDMap)result["_Result"];
148 
149  reason = data["reason"].AsString();
150  success = data["success"].AsBoolean();
151  myipaddress = data["your_ip"].AsString();
152  m_log.WarnFormat(
153  "[REMOTE SIMULATION CONNECTOR]: Remote simulator {0} did not accept compressed transfer, suggest updating it.", destination.RegionName);
154  return success;
155  }
156  }
157 
158  m_log.WarnFormat(
159  "[REMOTE SIMULATION CONNECTOR]: Failed to create agent {0} {1} at remote simulator {2}",
160  aCircuit.firstname, aCircuit.lastname, destination.RegionName);
161  reason = result["Message"] != null ? result["Message"].AsString() : "error";
162  return false;
163  }
164  catch (Exception e)
165  {
166  m_log.Warn("[REMOTE SIMULATION CONNECTOR]: CreateAgent failed with exception: " + e.ToString());
167  reason = e.Message;
168  }
169 
170  return false;
171  }
172 
176  public bool UpdateAgent(GridRegion destination, AgentData data, EntityTransferContext ctx)
177  {
178  return UpdateAgent(destination, (IAgentData)data, ctx, 200000); // yes, 200 seconds
179  }
180 
181  private ExpiringCache<string, bool> _failedSims = new ExpiringCache<string, bool>();
187  public bool UpdateAgent(GridRegion destination, AgentPosition data)
188  {
189  bool v = true;
190  if (_failedSims.TryGetValue(destination.ServerURI, out v))
191  return false;
192 
193  // The basic idea of this code is that the first thread that needs to
194  // send an update for a specific avatar becomes the worker for any subsequent
195  // requests until there are no more outstanding requests. Further, only send the most
196  // recent update; this *should* never be needed but some requests get
197  // slowed down and once that happens the problem with service end point
198  // limits kicks in and nothing proceeds
199  string uri = destination.ServerURI + AgentPath() + data.AgentID + "/";
200  lock (m_updateAgentQueue)
201  {
202  if (m_updateAgentQueue.ContainsKey(uri))
203  {
204  // Another thread is already handling
205  // updates for this simulator, just update
206  // the position and return, overwrites are
207  // not a problem since we only care about the
208  // last update anyway
209  m_updateAgentQueue[uri] = data;
210  return true;
211  }
212 
213  // Otherwise update the reference and start processing
214  m_updateAgentQueue[uri] = data;
215  }
216 
217  AgentPosition pos = null;
218  bool success = true;
219  while (success)
220  {
221  lock (m_updateAgentQueue)
222  {
223  // save the position
224  AgentPosition lastpos = pos;
225 
226  pos = m_updateAgentQueue[uri];
227 
228  // this is true if no one put a new
229  // update in the map since the last
230  // one we processed, if thats the
231  // case then we are done
232  if (pos == lastpos)
233  {
234  m_updateAgentQueue.Remove(uri);
235  return true;
236  }
237  }
238 
239  EntityTransferContext ctx = new EntityTransferContext(); // Dummy, not needed for position
240  success = UpdateAgent(destination, (IAgentData)pos, ctx, 10000);
241  }
242  // we get here iff success == false
243  // blacklist sim for 2 minutes
244  lock (m_updateAgentQueue)
245  {
246  _failedSims.AddOrUpdate(destination.ServerURI, true, 120);
247  m_updateAgentQueue.Remove(uri);
248  }
249  return false;
250  }
251 
255  private bool UpdateAgent(GridRegion destination, IAgentData cAgentData, EntityTransferContext ctx, int timeout)
256  {
257  // m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: UpdateAgent in {0}", destination.ServerURI);
258 
259  // Eventually, we want to use a caps url instead of the agentID
260  string uri = destination.ServerURI + AgentPath() + cAgentData.AgentID + "/";
261 
262  try
263  {
264  OSDMap args = cAgentData.Pack(ctx);
265 
266  args["destination_x"] = OSD.FromString(destination.RegionLocX.ToString());
267  args["destination_y"] = OSD.FromString(destination.RegionLocY.ToString());
268  args["destination_name"] = OSD.FromString(destination.RegionName);
269  args["destination_uuid"] = OSD.FromString(destination.RegionID.ToString());
270  args["context"] = ctx.Pack();
271 
272  OSDMap result = WebUtil.PutToServiceCompressed(uri, args, timeout);
273  if (result["Success"].AsBoolean())
274  return true;
275 
276  result = WebUtil.PutToService(uri, args, timeout);
277 
278  return result["Success"].AsBoolean();
279  }
280  catch (Exception e)
281  {
282  m_log.Warn("[REMOTE SIMULATION CONNECTOR]: UpdateAgent failed with exception: " + e.ToString());
283  }
284 
285  return false;
286  }
287 
288 
289  public bool QueryAccess(GridRegion destination, UUID agentID, string agentHomeURI, bool viaTeleport, Vector3 position, List<UUID> featuresAvailable, EntityTransferContext ctx, out string reason)
290  {
291  reason = "Failed to contact destination";
292 
293  // m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: QueryAccess start, position={0}", position);
294 
295  IPEndPoint ext = destination.ExternalEndPoint;
296  if (ext == null) return false;
297 
298  // Eventually, we want to use a caps url instead of the agentID
299  string uri = destination.ServerURI + AgentPath() + agentID + "/" + destination.RegionID.ToString() + "/";
300 
301  OSDMap request = new OSDMap();
302  request.Add("viaTeleport", OSD.FromBoolean(viaTeleport));
303  request.Add("position", OSD.FromString(position.ToString()));
304  // To those who still understad this field, we're telling them
305  // the lowest version just to be safe
306  request.Add("my_version", OSD.FromString(String.Format("SIMULATION/{0}", VersionInfo.SimulationServiceVersionSupportedMin)));
307  // New simulation service negotiation
308  request.Add("simulation_service_supported_min", OSD.FromReal(VersionInfo.SimulationServiceVersionSupportedMin));
309  request.Add("simulation_service_supported_max", OSD.FromReal(VersionInfo.SimulationServiceVersionSupportedMax));
310  request.Add("simulation_service_accepted_min", OSD.FromReal(VersionInfo.SimulationServiceVersionAcceptedMin));
311  request.Add("simulation_service_accepted_max", OSD.FromReal(VersionInfo.SimulationServiceVersionAcceptedMax));
312 
313  request.Add("context", ctx.Pack());
314 
315  OSDArray features = new OSDArray();
316  foreach (UUID feature in featuresAvailable)
317  features.Add(OSD.FromString(feature.ToString()));
318 
319  request.Add("features", features);
320 
321  if (agentHomeURI != null)
322  request.Add("agent_home_uri", OSD.FromString(agentHomeURI));
323 
324  try
325  {
326  OSDMap result = WebUtil.ServiceOSDRequest(uri, request, "QUERYACCESS", 30000, false, false);
327  bool success = result["success"].AsBoolean();
328  if (result.ContainsKey("_Result"))
329  {
330  OSDMap data = (OSDMap)result["_Result"];
331 
332  // FIXME: If there is a _Result map then it's the success key here that indicates the true success
333  // or failure, not the sibling result node.
334  success = data["success"];
335 
336  reason = data["reason"].AsString();
337  // We will need to plumb this and start sing the outbound version as well
338  // TODO: lay the pipe for version plumbing
339  if (data.ContainsKey("negotiated_inbound_version") && data["negotiated_inbound_version"] != null)
340  {
341  ctx.InboundVersion = (float)data["negotiated_inbound_version"].AsReal();
342  ctx.OutboundVersion = (float)data["negotiated_outbound_version"].AsReal();
343  }
344  else if (data["version"] != null && data["version"].AsString() != string.Empty)
345  {
346  string versionString = data["version"].AsString();
347  String[] parts = versionString.Split(new char[] {'/'});
348  if (parts.Length > 1)
349  {
350  ctx.InboundVersion = float.Parse(parts[1]);
351  ctx.OutboundVersion = float.Parse(parts[1]);
352  }
353  }
354 
355  m_log.DebugFormat(
356  "[REMOTE SIMULATION CONNECTOR]: QueryAccess to {0} returned {1}, reason {2}, version {3}/{4}",
357  uri, success, reason, ctx.InboundVersion, ctx.OutboundVersion);
358  }
359 
360  if (!success || ctx.InboundVersion == 0f || ctx.OutboundVersion == 0f)
361  {
362  // If we don't check this then OpenSimulator 0.7.3.1 and some period before will never see the
363  // actual failure message
364  if (!result.ContainsKey("_Result"))
365  {
366  if (result.ContainsKey("Message"))
367  {
368  string message = result["Message"].AsString();
369  if (message == "Service request failed: [MethodNotAllowed] MethodNotAllowed") // Old style region
370  {
371  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");
372  return true;
373  }
374 
375  reason = result["Message"];
376  }
377  else
378  {
379  reason = "Communications failure";
380  }
381  }
382 
383  return false;
384  }
385 
386  featuresAvailable.Clear();
387 
388  if (result.ContainsKey("features"))
389  {
390  OSDArray array = (OSDArray)result["features"];
391 
392  foreach (OSD o in array)
393  featuresAvailable.Add(new UUID(o.AsString()));
394  }
395 
396  // Version stuff
397  if (ctx.OutboundVersion < 0.5)
398  ctx.WearablesCount = AvatarWearable.LEGACY_VERSION_MAX_WEARABLES;
399  else if (ctx.OutboundVersion < 0.6)
400  ctx.WearablesCount = AvatarWearable.LEGACY_VERSION_MAX_WEARABLES + 1;
401  else
402  ctx.WearablesCount = -1; // send all (just in case..)
403 
404  return success;
405  }
406  catch (Exception e)
407  {
408  m_log.WarnFormat("[REMOTE SIMULATION CONNECTOR] QueryAcesss failed with exception; {0}",e.ToString());
409  }
410 
411  return false;
412  }
413 
416  public bool ReleaseAgent(UUID origin, UUID id, string uri)
417  {
418  // m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: ReleaseAgent start");
419 
420  try
421  {
422  WebUtil.ServiceOSDRequest(uri, null, "DELETE", 10000, false, false);
423  }
424  catch (Exception e)
425  {
426  m_log.WarnFormat("[REMOTE SIMULATION CONNECTOR] ReleaseAgent failed with exception; {0}",e.ToString());
427  }
428 
429  return true;
430  }
431 
434  public bool CloseAgent(GridRegion destination, UUID id, string auth_code)
435  {
436  string uri = destination.ServerURI + AgentPath() + id + "/" + destination.RegionID.ToString() + "/?auth=" + auth_code;
437  m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: CloseAgent {0}", uri);
438 
439  try
440  {
441  WebUtil.ServiceOSDRequest(uri, null, "DELETE", 10000, false, false);
442  }
443  catch (Exception e)
444  {
445  m_log.WarnFormat("[REMOTE SIMULATION CONNECTOR] CloseAgent failed with exception; {0}",e.ToString());
446  }
447 
448  return true;
449  }
450 
451  #endregion Agents
452 
453  #region Objects
454 
455  protected virtual string ObjectPath()
456  {
457  return "object/";
458  }
459 
463  public bool CreateObject(GridRegion destination, Vector3 newPosition, ISceneObject sog, bool isLocalCall)
464  {
465  // m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: CreateObject start");
466 
467  string uri = destination.ServerURI + ObjectPath() + sog.UUID + "/";
468 
469  try
470  {
471  OSDMap args = new OSDMap(2);
472 
473  args["sog"] = OSD.FromString(sog.ToXml2());
474  args["extra"] = OSD.FromString(sog.ExtraToXmlString());
475  args["modified"] = OSD.FromBoolean(sog.HasGroupChanged);
476  args["new_position"] = newPosition.ToString();
477 
478  string state = sog.GetStateSnapshot();
479  if (state.Length > 0)
480  args["state"] = OSD.FromString(state);
481 
482  // Add the input general arguments
483  args["destination_x"] = OSD.FromString(destination.RegionLocX.ToString());
484  args["destination_y"] = OSD.FromString(destination.RegionLocY.ToString());
485  args["destination_name"] = OSD.FromString(destination.RegionName);
486  args["destination_uuid"] = OSD.FromString(destination.RegionID.ToString());
487 
488  OSDMap result = WebUtil.PostToService(uri, args, 40000, false);
489 
490  if (result == null)
491  return false;
492  bool success = result["success"].AsBoolean();
493  if (!success)
494  return false;
495  }
496  catch (Exception e)
497  {
498  m_log.WarnFormat("[REMOTE SIMULATION CONNECTOR] CreateObject failed with exception; {0}",e.ToString());
499  return false;
500  }
501 
502  return true;
503  }
504 
508  public bool CreateObject(GridRegion destination, UUID userID, UUID itemID)
509  {
510  // TODO, not that urgent
511  return false;
512  }
513 
514  #endregion Objects
515  }
516 }
bool QueryAccess(GridRegion destination, UUID agentID, string agentHomeURI, bool viaTeleport, Vector3 position, List< UUID > featuresAvailable, EntityTransferContext ctx, out string reason)
Returns whether a propspective user is allowed to visit the region.
OpenMetaverse.StructuredData.OSDArray OSDArray
virtual void PackData(OSDMap args, GridRegion source, AgentCircuitData aCircuit, GridRegion destination, uint flags)
bool CreateAgent(GridRegion source, GridRegion destination, AgentCircuitData aCircuit, uint flags, EntityTransferContext ctx, out string reason)
Ask the simulator hosting the destination to create an agent on that region.
OpenMetaverse.StructuredData.OSDMap OSDMap
static readonly float SimulationServiceVersionAcceptedMax
Definition: VersionInfo.cs:88
static readonly float SimulationServiceVersionSupportedMin
Definition: VersionInfo.cs:89
bool UpdateAgent(GridRegion destination, AgentData data, EntityTransferContext ctx)
Send complete data about an agent in this region to a neighbor
string RawServerURI
Provides direct access to the 'm_serverURI' field, without returning a generated URL if m_serverURI i...
bool CreateAgent(GridRegion source, GridRegion destination, AgentCircuitData aCircuit, uint flags, EntityTransferContext ctx, out string myipaddress, out string reason)
OpenSim.Services.Interfaces.GridRegion GridRegion
bool CreateObject(GridRegion destination, Vector3 newPosition, ISceneObject sog, bool isLocalCall)
static readonly float SimulationServiceVersionSupportedMax
Definition: VersionInfo.cs:90
Circuit data for an agent. Connection information shared between regions that accept UDP connections ...
OpenMetaverse.StructuredData.OSD OSD
delegate void UpdateAgent(IClientAPI remoteClient, AgentUpdateArgs agentData)
Replacement for ChildAgentDataUpdate. Used over RESTComms and LocalComms.
bool CloseAgent(GridRegion destination, UUID id, string auth_code)
string ServerURI
A well-formed URI for the host region server (namely "http://" + ExternalHostName) ...
static readonly float SimulationServiceVersionAcceptedMin
This rules versioning regarding teleports, and compatibility between simulators in that regard...
Definition: VersionInfo.cs:87
Interactive OpenSim region server
Definition: OpenSim.cs:55
IScene GetScene(UUID regionId)
Retrieve the scene with the given region ID.
bool CreateObject(GridRegion destination, UUID userID, UUID itemID)
bool UpdateAgent(GridRegion destination, AgentPosition data)
Send updated position information about an agent in this region to a neighbor This operation may be c...