/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Net; using System.Reflection; using log4net; using Mono.Addins; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Servers.HttpServer; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Services.Interfaces; using OpenSim.Server.Base; using OpenMetaverse; using OpenMetaverse.StructuredData; using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; namespace OpenSim.Services.Connectors.SimianGrid { /// /// Connects avatar presence information (for tracking current location and /// message routing) to the SimianGrid backend /// [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] public class SimianPresenceServiceConnector : IPresenceService, IGridUserService, ISharedRegionModule { private static readonly ILog m_log = LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType); private string m_serverUrl = String.Empty; private SimianActivityDetector m_activityDetector; #region ISharedRegionModule public Type ReplaceableInterface { get { return null; } } public void RegionLoaded(Scene scene) { } public void PostInitialise() { } public void Close() { } public SimianPresenceServiceConnector() { m_activityDetector = new SimianActivityDetector(this); } public string Name { get { return "SimianPresenceServiceConnector"; } } public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface(this); scene.RegisterModuleInterface(this); m_activityDetector.AddRegion(scene); LogoutRegionAgents(scene.RegionInfo.RegionID); } } public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface(this); scene.UnregisterModuleInterface(this); m_activityDetector.RemoveRegion(scene); LogoutRegionAgents(scene.RegionInfo.RegionID); } } #endregion ISharedRegionModule public SimianPresenceServiceConnector(IConfigSource source) { Initialise(source); } public void Initialise(IConfigSource source) { if (Simian.IsSimianEnabled(source, "PresenceServices", this.Name)) { IConfig gridConfig = source.Configs["PresenceService"]; if (gridConfig == null) { m_log.Error("[SIMIAN PRESENCE CONNECTOR]: PresenceService missing from OpenSim.ini"); throw new Exception("Presence connector init error"); } string serviceUrl = gridConfig.GetString("PresenceServerURI"); if (String.IsNullOrEmpty(serviceUrl)) { m_log.Error("[SIMIAN PRESENCE CONNECTOR]: No PresenceServerURI in section PresenceService"); throw new Exception("Presence connector init error"); } m_serverUrl = serviceUrl; } } #region IPresenceService public bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID) { m_log.ErrorFormat("[SIMIAN PRESENCE CONNECTOR]: Login requested, UserID={0}, SessionID={1}, SecureSessionID={2}", userID, sessionID, secureSessionID); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "AddSession" }, { "UserID", userID.ToString() } }; if (sessionID != UUID.Zero) { requestArgs["SessionID"] = sessionID.ToString(); requestArgs["SecureSessionID"] = secureSessionID.ToString(); } OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); bool success = response["Success"].AsBoolean(); if (!success) m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to login agent " + userID + ": " + response["Message"].AsString()); return success; } public bool LogoutAgent(UUID sessionID) { m_log.InfoFormat("[SIMIAN PRESENCE CONNECTOR]: Logout requested for agent with sessionID " + sessionID); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "RemoveSession" }, { "SessionID", sessionID.ToString() } }; OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); bool success = response["Success"].AsBoolean(); if (!success) m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to logout agent with sessionID " + sessionID + ": " + response["Message"].AsString()); return success; } public bool LogoutRegionAgents(UUID regionID) { m_log.InfoFormat("[SIMIAN PRESENCE CONNECTOR]: Logout requested for all agents in region " + regionID); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "RemoveSessions" }, { "SceneID", regionID.ToString() } }; OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); bool success = response["Success"].AsBoolean(); if (!success) m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to logout agents from region " + regionID + ": " + response["Message"].AsString()); return success; } public bool ReportAgent(UUID sessionID, UUID regionID) { // Not needed for SimianGrid return true; } public PresenceInfo GetAgent(UUID sessionID) { m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting session data for agent with sessionID " + sessionID); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetSession" }, { "SessionID", sessionID.ToString() } }; OSDMap sessionResponse = WebUtil.PostToService(m_serverUrl, requestArgs); if (sessionResponse["Success"].AsBoolean()) { UUID userID = sessionResponse["UserID"].AsUUID(); m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting user data for " + userID); requestArgs = new NameValueCollection { { "RequestMethod", "GetUser" }, { "UserID", userID.ToString() } }; OSDMap userResponse = WebUtil.PostToService(m_serverUrl, requestArgs); if (userResponse["Success"].AsBoolean()) return ResponseToPresenceInfo(sessionResponse, userResponse); else m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve user data for " + userID + ": " + userResponse["Message"].AsString()); } else { m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve session " + sessionID + ": " + sessionResponse["Message"].AsString()); } return null; } public PresenceInfo[] GetAgents(string[] userIDs) { List presences = new List(userIDs.Length); for (int i = 0; i < userIDs.Length; i++) { UUID userID; if (UUID.TryParse(userIDs[i], out userID) && userID != UUID.Zero) presences.AddRange(GetSessions(userID)); } return presences.ToArray(); } #endregion IPresenceService #region IGridUserService public GridUserInfo LoggedIn(string userID) { // Never implemented at the sim return null; } public bool LoggedOut(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) { // Save our last position as user data NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "AddUserData" }, { "UserID", userID.ToString() }, { "LastLocation", SerializeLocation(regionID, lastPosition, lastLookAt) } }; OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); bool success = response["Success"].AsBoolean(); if (!success) m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to set last location for " + userID + ": " + response["Message"].AsString()); return success; } public bool SetHome(string userID, UUID regionID, Vector3 position, Vector3 lookAt) { m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Setting home location for user " + userID); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "AddUserData" }, { "UserID", userID.ToString() }, { "HomeLocation", SerializeLocation(regionID, position, lookAt) } }; OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); bool success = response["Success"].AsBoolean(); if (!success) m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to set home location for " + userID + ": " + response["Message"].AsString()); return success; } public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) { return UpdateSession(sessionID, regionID, lastPosition, lastLookAt); } public bool SetLastPosition(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) { // Never called return false; } public GridUserInfo GetGridUserInfo(string user) { m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting session data for agent " + user); UUID userID = new UUID(user); m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting user data for " + userID); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetUser" }, { "UserID", userID.ToString() } }; OSDMap userResponse = WebUtil.PostToService(m_serverUrl, requestArgs); if (userResponse["Success"].AsBoolean()) return ResponseToGridUserInfo(userResponse); else m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve user data for " + userID + ": " + userResponse["Message"].AsString()); return null; } #endregion #region Helpers private OSDMap GetUserData(UUID userID) { m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting user data for " + userID); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetUser" }, { "UserID", userID.ToString() } }; OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); if (response["Success"].AsBoolean() && response["User"] is OSDMap) return response; else m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve user data for " + userID + ": " + response["Message"].AsString()); return null; } // private OSDMap GetSessionData(UUID sessionID) // { // m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting session data for session " + sessionID); // // NameValueCollection requestArgs = new NameValueCollection // { // { "RequestMethod", "GetSession" }, // { "SessionID", sessionID.ToString() } // }; // // OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); // if (response["Success"].AsBoolean()) // return response; // else // m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve session data for session " + sessionID); // // return null; // } private List GetSessions(UUID userID) { List presences = new List(1); OSDMap userResponse = GetUserData(userID); if (userResponse != null) { m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting sessions for " + userID); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetSession" }, { "UserID", userID.ToString() } }; OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); if (response["Success"].AsBoolean()) { PresenceInfo presence = ResponseToPresenceInfo(response, userResponse); if (presence != null) presences.Add(presence); } else { m_log.Debug("[SIMIAN PRESENCE CONNECTOR]: No session returned for " + userID + ": " + response["Message"].AsString()); } } return presences; } private bool UpdateSession(UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) { // Save our current location as session data NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "UpdateSession" }, { "SessionID", sessionID.ToString() }, { "SceneID", regionID.ToString() }, { "ScenePosition", lastPosition.ToString() }, { "SceneLookAt", lastLookAt.ToString() } }; OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); bool success = response["Success"].AsBoolean(); if (!success) m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to update agent session " + sessionID + ": " + response["Message"].AsString()); return success; } ///// ///// Fetch the last known avatar location with GetSession and persist it ///// as user data with AddUserData ///// //private bool SetLastLocation(UUID sessionID) //{ // NameValueCollection requestArgs = new NameValueCollection // { // { "RequestMethod", "GetSession" }, // { "SessionID", sessionID.ToString() } // }; // OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); // bool success = response["Success"].AsBoolean(); // if (success) // { // UUID userID = response["UserID"].AsUUID(); // UUID sceneID = response["SceneID"].AsUUID(); // Vector3 position = response["ScenePosition"].AsVector3(); // Vector3 lookAt = response["SceneLookAt"].AsVector3(); // return SetLastLocation(userID, sceneID, position, lookAt); // } // else // { // m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve presence information for session " + sessionID + // " while saving last location: " + response["Message"].AsString()); // } // return success; //} private PresenceInfo ResponseToPresenceInfo(OSDMap sessionResponse, OSDMap userResponse) { if (sessionResponse == null) return null; PresenceInfo info = new PresenceInfo(); info.UserID = sessionResponse["UserID"].AsUUID().ToString(); info.RegionID = sessionResponse["SceneID"].AsUUID(); return info; } private GridUserInfo ResponseToGridUserInfo(OSDMap userResponse) { if (userResponse != null && userResponse["User"] is OSDMap) { GridUserInfo info = new GridUserInfo(); info.Online = true; info.UserID = userResponse["UserID"].AsUUID().ToString(); info.LastRegionID = userResponse["SceneID"].AsUUID(); info.LastPosition = userResponse["ScenePosition"].AsVector3(); info.LastLookAt = userResponse["SceneLookAt"].AsVector3(); OSDMap user = (OSDMap)userResponse["User"]; info.Login = user["LastLoginDate"].AsDate(); info.Logout = user["LastLogoutDate"].AsDate(); DeserializeLocation(user["HomeLocation"].AsString(), out info.HomeRegionID, out info.HomePosition, out info.HomeLookAt); return info; } return null; } private string SerializeLocation(UUID regionID, Vector3 position, Vector3 lookAt) { return "{" + String.Format("\"SceneID\":\"{0}\",\"Position\":\"{1}\",\"LookAt\":\"{2}\"", regionID, position, lookAt) + "}"; } private bool DeserializeLocation(string location, out UUID regionID, out Vector3 position, out Vector3 lookAt) { OSDMap map = null; try { map = OSDParser.DeserializeJson(location) as OSDMap; } catch { } if (map != null) { regionID = map["SceneID"].AsUUID(); if (Vector3.TryParse(map["Position"].AsString(), out position) && Vector3.TryParse(map["LookAt"].AsString(), out lookAt)) { return true; } } regionID = UUID.Zero; position = Vector3.Zero; lookAt = Vector3.Zero; return false; } #endregion Helpers } }