aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs')
-rw-r--r--OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs587
1 files changed, 587 insertions, 0 deletions
diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs
new file mode 100644
index 0000000..e48b7de
--- /dev/null
+++ b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs
@@ -0,0 +1,587 @@
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
28using System;
29using System.Collections.Generic;
30using System.Collections.Specialized;
31using System.Net;
32using System.Reflection;
33using log4net;
34using Mono.Addins;
35using Nini.Config;
36using OpenSim.Framework;
37using OpenSim.Framework.Servers.HttpServer;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Region.Framework.Scenes;
40using OpenSim.Services.Interfaces;
41using OpenSim.Server.Base;
42using OpenMetaverse;
43using OpenMetaverse.StructuredData;
44
45using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
46
47namespace OpenSim.Services.Connectors.SimianGrid
48{
49 /// <summary>
50 /// Connects avatar presence information (for tracking current location and
51 /// message routing) to the SimianGrid backend
52 /// </summary>
53 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
54 public class SimianPresenceServiceConnector : IPresenceService, IGridUserService, ISharedRegionModule
55 {
56 private static readonly ILog m_log =
57 LogManager.GetLogger(
58 MethodBase.GetCurrentMethod().DeclaringType);
59
60 private string m_serverUrl = String.Empty;
61
62 #region ISharedRegionModule
63
64 public Type ReplaceableInterface { get { return null; } }
65 public void RegionLoaded(Scene scene) { }
66 public void PostInitialise() { }
67 public void Close() { }
68
69 public SimianPresenceServiceConnector() { }
70 public string Name { get { return "SimianPresenceServiceConnector"; } }
71 public void AddRegion(Scene scene)
72 {
73 if (!String.IsNullOrEmpty(m_serverUrl))
74 {
75 scene.RegisterModuleInterface<IPresenceService>(this);
76 scene.RegisterModuleInterface<IGridUserService>(this);
77
78 scene.EventManager.OnMakeRootAgent += MakeRootAgentHandler;
79 scene.EventManager.OnNewClient += NewClientHandler;
80 scene.EventManager.OnSignificantClientMovement += SignificantClientMovementHandler;
81
82 LogoutRegionAgents(scene.RegionInfo.RegionID);
83 }
84 }
85 public void RemoveRegion(Scene scene)
86 {
87 if (!String.IsNullOrEmpty(m_serverUrl))
88 {
89 scene.UnregisterModuleInterface<IPresenceService>(this);
90 scene.UnregisterModuleInterface<IGridUserService>(this);
91
92 scene.EventManager.OnMakeRootAgent -= MakeRootAgentHandler;
93 scene.EventManager.OnNewClient -= NewClientHandler;
94 scene.EventManager.OnSignificantClientMovement -= SignificantClientMovementHandler;
95
96 LogoutRegionAgents(scene.RegionInfo.RegionID);
97 }
98 }
99
100 #endregion ISharedRegionModule
101
102 public SimianPresenceServiceConnector(IConfigSource source)
103 {
104 Initialise(source);
105 }
106
107 public void Initialise(IConfigSource source)
108 {
109 if (Simian.IsSimianEnabled(source, "PresenceServices", this.Name))
110 {
111 IConfig gridConfig = source.Configs["PresenceService"];
112 if (gridConfig == null)
113 {
114 m_log.Error("[SIMIAN PRESENCE CONNECTOR]: PresenceService missing from OpenSim.ini");
115 throw new Exception("Presence connector init error");
116 }
117
118 string serviceUrl = gridConfig.GetString("PresenceServerURI");
119 if (String.IsNullOrEmpty(serviceUrl))
120 {
121 m_log.Error("[SIMIAN PRESENCE CONNECTOR]: No PresenceServerURI in section PresenceService");
122 throw new Exception("Presence connector init error");
123 }
124
125 m_serverUrl = serviceUrl;
126 }
127 }
128
129 #region IPresenceService
130
131 public bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID)
132 {
133 m_log.ErrorFormat("[SIMIAN PRESENCE CONNECTOR]: Login requested, UserID={0}, SessionID={1}, SecureSessionID={2}",
134 userID, sessionID, secureSessionID);
135
136 NameValueCollection requestArgs = new NameValueCollection
137 {
138 { "RequestMethod", "AddSession" },
139 { "UserID", userID.ToString() }
140 };
141 if (sessionID != UUID.Zero)
142 {
143 requestArgs["SessionID"] = sessionID.ToString();
144 requestArgs["SecureSessionID"] = secureSessionID.ToString();
145 }
146
147 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
148 bool success = response["Success"].AsBoolean();
149
150 if (!success)
151 m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to login agent " + userID + ": " + response["Message"].AsString());
152
153 return success;
154 }
155
156 public bool LogoutAgent(UUID sessionID)
157 {
158 m_log.InfoFormat("[SIMIAN PRESENCE CONNECTOR]: Logout requested for agent with sessionID " + sessionID);
159
160 NameValueCollection requestArgs = new NameValueCollection
161 {
162 { "RequestMethod", "RemoveSession" },
163 { "SessionID", sessionID.ToString() }
164 };
165
166 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
167 bool success = response["Success"].AsBoolean();
168
169 if (!success)
170 m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to logout agent with sessionID " + sessionID + ": " + response["Message"].AsString());
171
172 return success;
173 }
174
175 public bool LogoutRegionAgents(UUID regionID)
176 {
177 m_log.InfoFormat("[SIMIAN PRESENCE CONNECTOR]: Logout requested for all agents in region " + regionID);
178
179 NameValueCollection requestArgs = new NameValueCollection
180 {
181 { "RequestMethod", "RemoveSessions" },
182 { "SceneID", regionID.ToString() }
183 };
184
185 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
186 bool success = response["Success"].AsBoolean();
187
188 if (!success)
189 m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to logout agents from region " + regionID + ": " + response["Message"].AsString());
190
191 return success;
192 }
193
194 public bool ReportAgent(UUID sessionID, UUID regionID)
195 {
196 return ReportAgent(sessionID, regionID, Vector3.Zero, Vector3.Zero);
197 }
198
199 protected bool ReportAgent(UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt)
200 {
201 //m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Updating session data for agent with sessionID " + sessionID);
202
203 NameValueCollection requestArgs = new NameValueCollection
204 {
205 { "RequestMethod", "UpdateSession" },
206 { "SessionID", sessionID.ToString() },
207 { "SceneID", regionID.ToString() },
208 { "ScenePosition", position.ToString() },
209 { "SceneLookAt", lookAt.ToString() }
210 };
211
212 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
213 bool success = response["Success"].AsBoolean();
214
215 if (!success)
216 m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to update agent session " + sessionID + ": " + response["Message"].AsString());
217
218 return success;
219 }
220
221 public PresenceInfo GetAgent(UUID sessionID)
222 {
223 m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting session data for agent with sessionID " + sessionID);
224
225 NameValueCollection requestArgs = new NameValueCollection
226 {
227 { "RequestMethod", "GetSession" },
228 { "SessionID", sessionID.ToString() }
229 };
230
231 OSDMap sessionResponse = WebUtil.PostToService(m_serverUrl, requestArgs);
232 if (sessionResponse["Success"].AsBoolean())
233 {
234 UUID userID = sessionResponse["UserID"].AsUUID();
235 m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting user data for " + userID);
236
237 requestArgs = new NameValueCollection
238 {
239 { "RequestMethod", "GetUser" },
240 { "UserID", userID.ToString() }
241 };
242
243 OSDMap userResponse = WebUtil.PostToService(m_serverUrl, requestArgs);
244 if (userResponse["Success"].AsBoolean())
245 return ResponseToPresenceInfo(sessionResponse, userResponse);
246 else
247 m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve user data for " + userID + ": " + userResponse["Message"].AsString());
248 }
249 else
250 {
251 m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve session " + sessionID + ": " + sessionResponse["Message"].AsString());
252 }
253
254 return null;
255 }
256
257 public PresenceInfo[] GetAgents(string[] userIDs)
258 {
259 List<PresenceInfo> presences = new List<PresenceInfo>(userIDs.Length);
260
261 for (int i = 0; i < userIDs.Length; i++)
262 {
263 UUID userID;
264 if (UUID.TryParse(userIDs[i], out userID) && userID != UUID.Zero)
265 presences.AddRange(GetSessions(userID));
266 }
267
268 return presences.ToArray();
269 }
270
271 #endregion IPresenceService
272
273 #region IGridUserService
274
275 public GridUserInfo LoggedIn(string userID)
276 {
277 // never implemented at the sim
278 return null;
279 }
280
281 public bool LoggedOut(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt)
282 {
283 // Not needed for simian grid, event handler is doing it
284 return true;
285 }
286
287 public bool SetHome(string userID, UUID regionID, Vector3 position, Vector3 lookAt)
288 {
289 m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Setting home location for user " + userID);
290
291 NameValueCollection requestArgs = new NameValueCollection
292 {
293 { "RequestMethod", "AddUserData" },
294 { "UserID", userID.ToString() },
295 { "HomeLocation", SerializeLocation(regionID, position, lookAt) }
296 };
297
298 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
299 bool success = response["Success"].AsBoolean();
300
301 if (!success)
302 m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to set home location for " + userID + ": " + response["Message"].AsString());
303
304 return success;
305 }
306
307 public bool SetLastPosition(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt)
308 {
309 // Not needed for simian grid, presence detection is doing it
310 return true;
311 }
312
313 public GridUserInfo GetGridUserInfo(string user)
314 {
315 m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting session data for agent " + user);
316
317 UUID userID = new UUID(user);
318 m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting user data for " + userID);
319
320 NameValueCollection requestArgs = new NameValueCollection
321 {
322 { "RequestMethod", "GetUser" },
323 { "UserID", userID.ToString() }
324 };
325
326 OSDMap userResponse = WebUtil.PostToService(m_serverUrl, requestArgs);
327 if (userResponse["Success"].AsBoolean())
328 return ResponseToGridUserInfo(userResponse);
329 else
330 m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve user data for " + userID + ": " + userResponse["Message"].AsString());
331
332 return null;
333 }
334
335 #endregion
336
337 #region Presence Detection
338
339 private void MakeRootAgentHandler(ScenePresence sp)
340 {
341 m_log.DebugFormat("[PRESENCE DETECTOR]: Detected root presence {0} in {1}", sp.UUID, sp.Scene.RegionInfo.RegionName);
342
343 ReportAgent(sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat);
344 SetLastLocation(sp.UUID, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat);
345 }
346
347 private void NewClientHandler(IClientAPI client)
348 {
349 client.OnConnectionClosed += LogoutHandler;
350 }
351
352 private void SignificantClientMovementHandler(IClientAPI client)
353 {
354 ScenePresence sp;
355 if (client.Scene is Scene && ((Scene)client.Scene).TryGetScenePresence(client.AgentId, out sp))
356 ReportAgent(sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat);
357 }
358
359 private void LogoutHandler(IClientAPI client)
360 {
361 if (client.IsLoggingOut)
362 {
363 client.OnConnectionClosed -= LogoutHandler;
364
365 object obj;
366 if (client.Scene.TryGetScenePresence(client.AgentId, out obj) && obj is ScenePresence)
367 {
368 // The avatar is still in the scene, we can get the exact logout position
369 ScenePresence sp = (ScenePresence)obj;
370 SetLastLocation(client.AgentId, client.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat);
371 }
372 else
373 {
374 // The avatar was already removed from the scene, store LastLocation using the most recent session data
375 m_log.Warn("[PRESENCE]: " + client.Name + " has already been removed from the scene, storing approximate LastLocation");
376 SetLastLocation(client.SessionId);
377 }
378
379 LogoutAgent(client.SessionId);
380 }
381 }
382
383 #endregion Presence Detection
384
385 #region Helpers
386
387 private OSDMap GetUserData(UUID userID)
388 {
389 m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting user data for " + userID);
390
391 NameValueCollection requestArgs = new NameValueCollection
392 {
393 { "RequestMethod", "GetUser" },
394 { "UserID", userID.ToString() }
395 };
396
397 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
398 if (response["Success"].AsBoolean() && response["User"] is OSDMap)
399 return response;
400 else
401 m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve user data for " + userID + ": " + response["Message"].AsString());
402
403 return null;
404 }
405
406 private OSDMap GetSessionData(UUID sessionID)
407 {
408 m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting session data for session " + sessionID);
409
410 NameValueCollection requestArgs = new NameValueCollection
411 {
412 { "RequestMethod", "GetSession" },
413 { "SessionID", sessionID.ToString() }
414 };
415
416 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
417 if (response["Success"].AsBoolean())
418 return response;
419 else
420 m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve session data for session " + sessionID);
421
422 return null;
423 }
424
425 private List<PresenceInfo> GetSessions(UUID userID)
426 {
427 List<PresenceInfo> presences = new List<PresenceInfo>(1);
428
429 OSDMap userResponse = GetUserData(userID);
430 if (userResponse != null)
431 {
432 m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting sessions for " + userID);
433
434 NameValueCollection requestArgs = new NameValueCollection
435 {
436 { "RequestMethod", "GetSession" },
437 { "UserID", userID.ToString() }
438 };
439
440 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
441 if (response["Success"].AsBoolean())
442 {
443 PresenceInfo presence = ResponseToPresenceInfo(response, userResponse);
444 if (presence != null)
445 presences.Add(presence);
446 }
447 else
448 {
449 m_log.Debug("[SIMIAN PRESENCE CONNECTOR]: No session returned for " + userID + ": " + response["Message"].AsString());
450 }
451 }
452
453 return presences;
454 }
455
456 /// <summary>
457 /// Fetch the last known avatar location with GetSession and persist it
458 /// as user data with AddUserData
459 /// </summary>
460 private bool SetLastLocation(UUID sessionID)
461 {
462 NameValueCollection requestArgs = new NameValueCollection
463 {
464 { "RequestMethod", "GetSession" },
465 { "SessionID", sessionID.ToString() }
466 };
467
468 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
469 bool success = response["Success"].AsBoolean();
470
471 if (success)
472 {
473 UUID userID = response["UserID"].AsUUID();
474 UUID sceneID = response["SceneID"].AsUUID();
475 Vector3 position = response["ScenePosition"].AsVector3();
476 Vector3 lookAt = response["SceneLookAt"].AsVector3();
477
478 return SetLastLocation(userID, sceneID, position, lookAt);
479 }
480 else
481 {
482 m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve presence information for session " + sessionID +
483 " while saving last location: " + response["Message"].AsString());
484 }
485
486 return success;
487 }
488
489 private bool SetLastLocation(UUID userID, UUID sceneID, Vector3 position, Vector3 lookAt)
490 {
491 NameValueCollection requestArgs = new NameValueCollection
492 {
493 { "RequestMethod", "AddUserData" },
494 { "UserID", userID.ToString() },
495 { "LastLocation", SerializeLocation(sceneID, position, lookAt) }
496 };
497
498 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
499 bool success = response["Success"].AsBoolean();
500
501 if (!success)
502 m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to set last location for " + userID + ": " + response["Message"].AsString());
503
504 return success;
505 }
506
507 private PresenceInfo ResponseToPresenceInfo(OSDMap sessionResponse, OSDMap userResponse)
508 {
509 if (sessionResponse == null)
510 return null;
511
512 PresenceInfo info = new PresenceInfo();
513
514 info.Online = true;
515 info.UserID = sessionResponse["UserID"].AsUUID().ToString();
516 info.RegionID = sessionResponse["SceneID"].AsUUID();
517 info.Position = sessionResponse["ScenePosition"].AsVector3();
518 info.LookAt = sessionResponse["SceneLookAt"].AsVector3();
519
520 if (userResponse != null && userResponse["User"] is OSDMap)
521 {
522 OSDMap user = (OSDMap)userResponse["User"];
523
524 info.Login = user["LastLoginDate"].AsDate();
525 info.Logout = user["LastLogoutDate"].AsDate();
526 DeserializeLocation(user["HomeLocation"].AsString(), out info.HomeRegionID, out info.HomePosition, out info.HomeLookAt);
527 }
528
529 return info;
530 }
531
532 private GridUserInfo ResponseToGridUserInfo(OSDMap userResponse)
533 {
534 if (userResponse != null && userResponse["User"] is OSDMap)
535 {
536
537 GridUserInfo info = new GridUserInfo();
538
539 info.Online = true;
540 info.UserID = userResponse["UserID"].AsUUID().ToString();
541 info.LastRegionID = userResponse["SceneID"].AsUUID();
542 info.LastPosition = userResponse["ScenePosition"].AsVector3();
543 info.LastLookAt = userResponse["SceneLookAt"].AsVector3();
544
545 OSDMap user = (OSDMap)userResponse["User"];
546
547 info.Login = user["LastLoginDate"].AsDate();
548 info.Logout = user["LastLogoutDate"].AsDate();
549 DeserializeLocation(user["HomeLocation"].AsString(), out info.HomeRegionID, out info.HomePosition, out info.HomeLookAt);
550
551 return info;
552 }
553
554 return null;
555 }
556
557 private string SerializeLocation(UUID regionID, Vector3 position, Vector3 lookAt)
558 {
559 return "{" + String.Format("\"SceneID\":\"{0}\",\"Position\":\"{1}\",\"LookAt\":\"{2}\"", regionID, position, lookAt) + "}";
560 }
561
562 private bool DeserializeLocation(string location, out UUID regionID, out Vector3 position, out Vector3 lookAt)
563 {
564 OSDMap map = null;
565
566 try { map = OSDParser.DeserializeJson(location) as OSDMap; }
567 catch { }
568
569 if (map != null)
570 {
571 regionID = map["SceneID"].AsUUID();
572 if (Vector3.TryParse(map["Position"].AsString(), out position) &&
573 Vector3.TryParse(map["LookAt"].AsString(), out lookAt))
574 {
575 return true;
576 }
577 }
578
579 regionID = UUID.Zero;
580 position = Vector3.Zero;
581 lookAt = Vector3.Zero;
582 return false;
583 }
584
585 #endregion Helpers
586 }
587}