diff --git a/OpenSim/Data/IProfilesData.cs b/OpenSim/Data/IProfilesData.cs new file mode 100644 index 0000000000..0de7f68af9 --- /dev/null +++ b/OpenSim/Data/IProfilesData.cs @@ -0,0 +1,56 @@ +/* + * 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 OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; + +namespace OpenSim.Data +{ + + public interface IProfilesData + { + OSDArray GetClassifiedRecords(UUID creatorId); + bool UpdateClassifiedRecord(UserClassifiedAdd ad, ref string result); + bool DeleteClassifiedRecord(UUID recordId); + OSDArray GetAvatarPicks(UUID avatarId); + UserProfilePick GetPickInfo(UUID avatarId, UUID pickId); + bool UpdatePicksRecord(UserProfilePick pick); + bool DeletePicksRecord(UUID pickId); + bool GetAvatarNotes(ref UserProfileNotes note); + bool UpdateAvatarNotes(ref UserProfileNotes note, ref string result); + bool GetAvatarProperties(ref UserProfileProperties props, ref string result); + bool UpdateAvatarProperties(ref UserProfileProperties props, ref string result); + bool UpdateAvatarInterests(UserProfileProperties up, ref string result); + bool GetClassifiedInfo(ref UserClassifiedAdd ad, ref string result); + bool GetUserAppData(ref UserAppData props, ref string result); + bool SetUserAppData(UserAppData props, ref string result); + OSDArray GetUserImageAssets(UUID avatarId); + } +} + diff --git a/OpenSim/Data/MySQL/MySQLUserProfilesData.cs b/OpenSim/Data/MySQL/MySQLUserProfilesData.cs new file mode 100644 index 0000000000..4c6c8e3aa2 --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLUserProfilesData.cs @@ -0,0 +1,1096 @@ +/* + * 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.Data; +using System.Reflection; +using OpenSim.Data; +using OpenSim.Framework; +using MySql.Data.MySqlClient; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using log4net; + +namespace OpenSim.Data.MySQL +{ + public class UserProfilesData: IProfilesData + { + static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + #region Properites + string ConnectionString + { + get; set; + } + + protected object Lock + { + get; set; + } + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + #endregion Properties + + #region class Member Functions + public UserProfilesData(string connectionString) + { + ConnectionString = connectionString; + Init(); + } + + void Init() + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + + Migration m = new Migration(dbcon, Assembly, "UserProfiles"); + m.Update(); + } + } + #endregion Member Functions + + #region Classifieds Queries + /// + /// Gets the classified records. + /// + /// + /// Array of classified records + /// + /// + /// Creator identifier. + /// + public OSDArray GetClassifiedRecords(UUID creatorId) + { + OSDArray data = new OSDArray(); + + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + string query = "SELECT classifieduuid, name FROM classifieds WHERE creatoruuid = ?Id"; + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", creatorId); + using( MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + OSDMap n = new OSDMap(); + UUID Id = UUID.Zero; + + string Name = null; + try + { + UUID.TryParse(Convert.ToString( reader["classifieduuid"]), out Id); + Name = Convert.ToString(reader["name"]); + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": UserAccount exception {0}", e.Message); + } + n.Add("classifieduuid", OSD.FromUUID(Id)); + n.Add("name", OSD.FromString(Name)); + data.Add(n); + } + } + } + } + } + return data; + } + + public bool UpdateClassifiedRecord(UserClassifiedAdd ad, ref string result) + { + string query = string.Empty; + + + query += "INSERT INTO classifieds ("; + query += "`classifieduuid`,"; + query += "`creatoruuid`,"; + query += "`creationdate`,"; + query += "`expirationdate`,"; + query += "`category`,"; + query += "`name`,"; + query += "`description`,"; + query += "`parceluuid`,"; + query += "`parentestate`,"; + query += "`snapshotuuid`,"; + query += "`simname`,"; + query += "`posglobal`,"; + query += "`parcelname`,"; + query += "`classifiedflags`,"; + query += "`priceforlisting`) "; + query += "VALUES ("; + query += "?ClassifiedId,"; + query += "?CreatorId,"; + query += "?CreatedDate,"; + query += "?ExpirationDate,"; + query += "?Category,"; + query += "?Name,"; + query += "?Description,"; + query += "?ParcelId,"; + query += "?ParentEstate,"; + query += "?SnapshotId,"; + query += "?SimName,"; + query += "?GlobalPos,"; + query += "?ParcelName,"; + query += "?Flags,"; + query += "?ListingPrice ) "; + query += "ON DUPLICATE KEY UPDATE "; + query += "category=?Category, "; + query += "expirationdate=?ExpirationDate, "; + query += "name=?Name, "; + query += "description=?Description, "; + query += "parentestate=?ParentEstate, "; + query += "posglobal=?GlobalPos, "; + query += "parcelname=?ParcelName, "; + query += "classifiedflags=?Flags, "; + query += "priceforlisting=?ListingPrice, "; + query += "snapshotuuid=?SnapshotId"; + + if(string.IsNullOrEmpty(ad.ParcelName)) + ad.ParcelName = "Unknown"; + if(ad.ParcelId == null) + ad.ParcelId = UUID.Zero; + if(string.IsNullOrEmpty(ad.Description)) + ad.Description = "No Description"; + + DateTime epoch = new DateTime(1970, 1, 1); + DateTime now = DateTime.Now; + TimeSpan epochnow = now - epoch; + TimeSpan duration; + DateTime expiration; + TimeSpan epochexp; + + if(ad.Flags == 2) + { + duration = new TimeSpan(7,0,0,0); + expiration = now.Add(duration); + epochexp = expiration - epoch; + } + else + { + duration = new TimeSpan(365,0,0,0); + expiration = now.Add(duration); + epochexp = expiration - epoch; + } + ad.CreationDate = (int)epochnow.TotalSeconds; + ad.ExpirationDate = (int)epochexp.TotalSeconds; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?ClassifiedId", ad.ClassifiedId.ToString()); + cmd.Parameters.AddWithValue("?CreatorId", ad.CreatorId.ToString()); + cmd.Parameters.AddWithValue("?CreatedDate", ad.CreationDate.ToString()); + cmd.Parameters.AddWithValue("?ExpirationDate", ad.ExpirationDate.ToString()); + cmd.Parameters.AddWithValue("?Category", ad.Category.ToString()); + cmd.Parameters.AddWithValue("?Name", ad.Name.ToString()); + cmd.Parameters.AddWithValue("?Description", ad.Description.ToString()); + cmd.Parameters.AddWithValue("?ParcelId", ad.ParcelId.ToString()); + cmd.Parameters.AddWithValue("?ParentEstate", ad.ParentEstate.ToString()); + cmd.Parameters.AddWithValue("?SnapshotId", ad.SnapshotId.ToString ()); + cmd.Parameters.AddWithValue("?SimName", ad.SimName.ToString()); + cmd.Parameters.AddWithValue("?GlobalPos", ad.GlobalPos.ToString()); + cmd.Parameters.AddWithValue("?ParcelName", ad.ParcelName.ToString()); + cmd.Parameters.AddWithValue("?Flags", ad.Flags.ToString()); + cmd.Parameters.AddWithValue("?ListingPrice", ad.Price.ToString ()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": ClassifiedesUpdate exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool DeleteClassifiedRecord(UUID recordId) + { + string query = string.Empty; + + query += "DELETE FROM classifieds WHERE "; + query += "classifieduuid = ?ClasifiedId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?ClassifiedId", recordId.ToString()); + + lock(Lock) + { + cmd.ExecuteNonQuery(); + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": DeleteClassifiedRecord exception {0}", e.Message); + return false; + } + return true; + } + + public bool GetClassifiedInfo(ref UserClassifiedAdd ad, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM classifieds WHERE "; + query += "classifieduuid = ?AdId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?AdId", ad.ClassifiedId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.Read ()) + { + ad.CreatorId = new UUID(reader.GetGuid("creatoruuid")); + ad.ParcelId = new UUID(reader.GetGuid("parceluuid")); + ad.SnapshotId = new UUID(reader.GetGuid("snapshotuuid")); + ad.CreationDate = Convert.ToInt32(reader["creationdate"]); + ad.ExpirationDate = Convert.ToInt32(reader["expirationdate"]); + ad.ParentEstate = Convert.ToInt32(reader["parentestate"]); + ad.Flags = (byte)reader.GetUInt32("classifiedflags"); + ad.Category = reader.GetInt32("category"); + ad.Price = reader.GetInt16("priceforlisting"); + ad.Name = reader.GetString("name"); + ad.Description = reader.GetString("description"); + ad.SimName = reader.GetString("simname"); + ad.GlobalPos = reader.GetString("posglobal"); + ad.ParcelName = reader.GetString("parcelname"); + + } + } + } + dbcon.Close(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetPickInfo exception {0}", e.Message); + } + return true; + } + #endregion Classifieds Queries + + #region Picks Queries + public OSDArray GetAvatarPicks(UUID avatarId) + { + string query = string.Empty; + + query += "SELECT `pickuuid`,`name` FROM userpicks WHERE "; + query += "creatoruuid = ?Id"; + OSDArray data = new OSDArray(); + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", avatarId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.HasRows) + { + while (reader.Read()) + { + OSDMap record = new OSDMap(); + + record.Add("pickuuid",OSD.FromString((string)reader["pickuuid"])); + record.Add("name",OSD.FromString((string)reader["name"])); + data.Add(record); + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetAvatarPicks exception {0}", e.Message); + } + return data; + } + + public UserProfilePick GetPickInfo(UUID avatarId, UUID pickId) + { + string query = string.Empty; + UserProfilePick pick = new UserProfilePick(); + + query += "SELECT * FROM userpicks WHERE "; + query += "creatoruuid = ?CreatorId AND "; + query += "pickuuid = ?PickId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?CreatorId", avatarId.ToString()); + cmd.Parameters.AddWithValue("?PickId", pickId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.HasRows) + { + reader.Read(); + + string description = (string)reader["description"]; + + if (string.IsNullOrEmpty(description)) + description = "No description given."; + + UUID.TryParse((string)reader["pickuuid"], out pick.PickId); + UUID.TryParse((string)reader["creatoruuid"], out pick.CreatorId); + UUID.TryParse((string)reader["parceluuid"], out pick.ParcelId); + UUID.TryParse((string)reader["snapshotuuid"], out pick.SnapshotId); + pick.GlobalPos = (string)reader["posglobal"]; + bool.TryParse((string)reader["toppick"], out pick.TopPick); + bool.TryParse((string)reader["enabled"], out pick.Enabled); + pick.Name = (string)reader["name"]; + pick.Desc = description; + pick.User = (string)reader["user"]; + pick.OriginalName = (string)reader["originalname"]; + pick.SimName = (string)reader["simname"]; + pick.SortOrder = (int)reader["sortorder"]; + } + } + } + dbcon.Close(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetPickInfo exception {0}", e.Message); + } + return pick; + } + + public bool UpdatePicksRecord(UserProfilePick pick) + { + string query = string.Empty; + + query += "INSERT INTO userpicks VALUES ("; + query += "?PickId,"; + query += "?CreatorId,"; + query += "?TopPick,"; + query += "?ParcelId,"; + query += "?Name,"; + query += "?Desc,"; + query += "?SnapshotId,"; + query += "?User,"; + query += "?Original,"; + query += "?SimName,"; + query += "?GlobalPos,"; + query += "?SortOrder,"; + query += "?Enabled) "; + query += "ON DUPLICATE KEY UPDATE "; + query += "parceluuid=?ParcelId,"; + query += "name=?Name,"; + query += "description=?Desc,"; + query += "snapshotuuid=?SnapshotId,"; + query += "pickuuid=?PickId,"; + query += "posglobal=?GlobalPos"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?PickId", pick.PickId.ToString()); + cmd.Parameters.AddWithValue("?CreatorId", pick.CreatorId.ToString()); + cmd.Parameters.AddWithValue("?TopPick", pick.TopPick.ToString()); + cmd.Parameters.AddWithValue("?ParcelId", pick.ParcelId.ToString()); + cmd.Parameters.AddWithValue("?Name", pick.Name.ToString()); + cmd.Parameters.AddWithValue("?Desc", pick.Desc.ToString()); + cmd.Parameters.AddWithValue("?SnapshotId", pick.SnapshotId.ToString()); + cmd.Parameters.AddWithValue("?User", pick.User.ToString()); + cmd.Parameters.AddWithValue("?Original", pick.OriginalName.ToString()); + cmd.Parameters.AddWithValue("?SimName",pick.SimName.ToString()); + cmd.Parameters.AddWithValue("?GlobalPos", pick.GlobalPos); + cmd.Parameters.AddWithValue("?SortOrder", pick.SortOrder.ToString ()); + cmd.Parameters.AddWithValue("?Enabled", pick.Enabled.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": UpdateAvatarNotes exception {0}", e.Message); + return false; + } + return true; + } + + public bool DeletePicksRecord(UUID pickId) + { + string query = string.Empty; + + query += "DELETE FROM userpicks WHERE "; + query += "pickuuid = ?PickId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?PickId", pickId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": DeleteUserPickRecord exception {0}", e.Message); + return false; + } + return true; + } + #endregion Picks Queries + + #region Avatar Notes Queries + public bool GetAvatarNotes(ref UserProfileNotes notes) + { // WIP + string query = string.Empty; + + query += "SELECT `notes` FROM usernotes WHERE "; + query += "useruuid = ?Id AND "; + query += "targetuuid = ?TargetId"; + OSDArray data = new OSDArray(); + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", notes.UserId.ToString()); + cmd.Parameters.AddWithValue("?TargetId", notes.TargetId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + reader.Read(); + notes.Notes = OSD.FromString((string)reader["notes"]); + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetAvatarNotes exception {0}", e.Message); + } + return true; + } + + public bool UpdateAvatarNotes(ref UserProfileNotes note, ref string result) + { + string query = string.Empty; + bool remove; + + if(string.IsNullOrEmpty(note.Notes)) + { + remove = true; + query += "DELETE FROM usernotes WHERE "; + query += "useruuid=?UserId AND "; + query += "targetuuid=?TargetId"; + } + else + { + remove = false; + query += "INSERT INTO usernotes VALUES ( "; + query += "?UserId,"; + query += "?TargetId,"; + query += "?Notes )"; + query += "ON DUPLICATE KEY "; + query += "UPDATE "; + query += "notes=?Notes"; + } + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + if(!remove) + cmd.Parameters.AddWithValue("?Notes", note.Notes); + cmd.Parameters.AddWithValue("?TargetId", note.TargetId.ToString ()); + cmd.Parameters.AddWithValue("?UserId", note.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": UpdateAvatarNotes exception {0}", e.Message); + return false; + } + return true; + + } + #endregion Avatar Notes Queries + + #region Avatar Properties + public bool GetAvatarProperties(ref UserProfileProperties props, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM userprofile WHERE "; + query += "useruuid = ?Id"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", props.UserId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Getting data for {0}.", props.UserId); + reader.Read(); + props.WebUrl = (string)reader["profileURL"]; + UUID.TryParse((string)reader["profileImage"], out props.ImageId); + props.AboutText = (string)reader["profileAboutText"]; + UUID.TryParse((string)reader["profileFirstImage"], out props.FirstLifeImageId); + props.FirstLifeText = (string)reader["profileFirstText"]; + UUID.TryParse((string)reader["profilePartner"], out props.PartnerId); + props.WantToMask = (int)reader["profileWantToMask"]; + props.WantToText = (string)reader["profileWantToText"]; + props.SkillsMask = (int)reader["profileSkillsMask"]; + props.SkillsText = (string)reader["profileSkillsText"]; + props.Language = (string)reader["profileLanguages"]; + } + else + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": No data for {0}", props.UserId); + + props.WebUrl = string.Empty; + props.ImageId = UUID.Zero; + props.AboutText = string.Empty; + props.FirstLifeImageId = UUID.Zero; + props.FirstLifeText = string.Empty; + props.PartnerId = UUID.Zero; + props.WantToMask = 0; + props.WantToText = string.Empty; + props.SkillsMask = 0; + props.SkillsText = string.Empty; + props.Language = string.Empty; + props.PublishProfile = false; + props.PublishMature = false; + + query = "INSERT INTO userprofile ("; + query += "useruuid, "; + query += "profilePartner, "; + query += "profileAllowPublish, "; + query += "profileMaturePublish, "; + query += "profileURL, "; + query += "profileWantToMask, "; + query += "profileWantToText, "; + query += "profileSkillsMask, "; + query += "profileSkillsText, "; + query += "profileLanguages, "; + query += "profileImage, "; + query += "profileAboutText, "; + query += "profileFirstImage, "; + query += "profileFirstText) VALUES ("; + query += "?userId, "; + query += "?profilePartner, "; + query += "?profileAllowPublish, "; + query += "?profileMaturePublish, "; + query += "?profileURL, "; + query += "?profileWantToMask, "; + query += "?profileWantToText, "; + query += "?profileSkillsMask, "; + query += "?profileSkillsText, "; + query += "?profileLanguages, "; + query += "?profileImage, "; + query += "?profileAboutText, "; + query += "?profileFirstImage, "; + query += "?profileFirstText)"; + + dbcon.Close(); + dbcon.Open(); + + using (MySqlCommand put = new MySqlCommand(query, dbcon)) + { + put.Parameters.AddWithValue("?userId", props.UserId.ToString()); + put.Parameters.AddWithValue("?profilePartner", props.PartnerId.ToString()); + put.Parameters.AddWithValue("?profileAllowPublish", props.PublishProfile); + put.Parameters.AddWithValue("?profileMaturePublish", props.PublishMature); + put.Parameters.AddWithValue("?profileURL", props.WebUrl); + put.Parameters.AddWithValue("?profileWantToMask", props.WantToMask); + put.Parameters.AddWithValue("?profileWantToText", props.WantToText); + put.Parameters.AddWithValue("?profileSkillsMask", props.SkillsMask); + put.Parameters.AddWithValue("?profileSkillsText", props.SkillsText); + put.Parameters.AddWithValue("?profileLanguages", props.Language); + put.Parameters.AddWithValue("?profileImage", props.ImageId.ToString()); + put.Parameters.AddWithValue("?profileAboutText", props.AboutText); + put.Parameters.AddWithValue("?profileFirstImage", props.FirstLifeImageId.ToString()); + put.Parameters.AddWithValue("?profileFirstText", props.FirstLifeText); + + put.ExecuteNonQuery(); + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Requst properties exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool UpdateAvatarProperties(ref UserProfileProperties props, ref string result) + { + string query = string.Empty; + + query += "UPDATE userprofile SET "; + query += "profilePartner=?profilePartner, "; + query += "profileURL=?profileURL, "; + query += "profileImage=?image, "; + query += "profileAboutText=?abouttext,"; + query += "profileFirstImage=?firstlifeimage,"; + query += "profileFirstText=?firstlifetext "; + query += "WHERE useruuid=?uuid"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?profileURL", props.WebUrl); + cmd.Parameters.AddWithValue("?profilePartner", props.PartnerId.ToString()); + cmd.Parameters.AddWithValue("?image", props.ImageId.ToString()); + cmd.Parameters.AddWithValue("?abouttext", props.AboutText); + cmd.Parameters.AddWithValue("?firstlifeimage", props.FirstLifeImageId.ToString()); + cmd.Parameters.AddWithValue("?firstlifetext", props.FirstLifeText); + cmd.Parameters.AddWithValue("?uuid", props.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": AgentPropertiesUpdate exception {0}", e.Message); + + return false; + } + return true; + } + #endregion Avatar Properties + + #region Avatar Interests + public bool UpdateAvatarInterests(UserProfileProperties up, ref string result) + { + string query = string.Empty; + + query += "UPDATE userprofile SET "; + query += "profileWantToMask=?WantMask, "; + query += "profileWantToText=?WantText,"; + query += "profileSkillsMask=?SkillsMask,"; + query += "profileSkillsText=?SkillsText, "; + query += "profileLanguages=?Languages "; + query += "WHERE useruuid=?uuid"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?WantMask", up.WantToMask); + cmd.Parameters.AddWithValue("?WantText", up.WantToText); + cmd.Parameters.AddWithValue("?SkillsMask", up.SkillsMask); + cmd.Parameters.AddWithValue("?SkillsText", up.SkillsText); + cmd.Parameters.AddWithValue("?Languages", up.Language); + cmd.Parameters.AddWithValue("?uuid", up.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": AgentInterestsUpdate exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + #endregion Avatar Interests + + public OSDArray GetUserImageAssets(UUID avatarId) + { + OSDArray data = new OSDArray(); + string query = "SELECT `snapshotuuid` FROM {0} WHERE `creatoruuid` = ?Id"; + + // Get classified image assets + + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand(string.Format (query,"`classifieds`"), dbcon)) + { + cmd.Parameters.AddWithValue("?Id", avatarId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString((string)reader["snapshotuuid"].ToString ())); + } + } + } + } + + dbcon.Close(); + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand(string.Format (query,"`userpicks`"), dbcon)) + { + cmd.Parameters.AddWithValue("?Id", avatarId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString((string)reader["snapshotuuid"].ToString ())); + } + } + } + } + + dbcon.Close(); + dbcon.Open(); + + query = "SELECT `profileImage`, `profileFirstImage` FROM `userprofile` WHERE `useruuid` = ?Id"; + + using (MySqlCommand cmd = new MySqlCommand(string.Format (query,"`userpicks`"), dbcon)) + { + cmd.Parameters.AddWithValue("?Id", avatarId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString((string)reader["profileImage"].ToString ())); + data.Add(new OSDString((string)reader["profileFirstImage"].ToString ())); + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetAvatarNotes exception {0}", e.Message); + } + return data; + } + + #region User Preferences + public OSDArray GetUserPreferences(UUID avatarId) + { + string query = string.Empty; + + query += "SELECT imviaemail,visible,email FROM "; + query += "usersettings WHERE "; + query += "useruuid = ?Id"; + + OSDArray data = new OSDArray(); + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", avatarId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.HasRows) + { + reader.Read(); + OSDMap record = new OSDMap(); + + record.Add("imviaemail",OSD.FromString((string)reader["imviaemail"])); + record.Add("visible",OSD.FromString((string)reader["visible"])); + record.Add("email",OSD.FromString((string)reader["email"])); + data.Add(record); + } + else + { + using (MySqlCommand put = new MySqlCommand(query, dbcon)) + { + query = "INSERT INTO usersettings VALUES "; + query += "(?Id,'false','false', '')"; + + lock(Lock) + { + put.ExecuteNonQuery(); + } + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Get preferences exception {0}", e.Message); + } + return data; + } + + public bool UpdateUserPreferences(bool emailIm, bool visible, UUID avatarId ) + { + string query = string.Empty; + + query += "UPDATE userpsettings SET "; + query += "imviaemail=?ImViaEmail, "; + query += "visible=?Visible,"; + query += "WHERE useruuid=?uuid"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?ImViaEmail", emailIm.ToString().ToLower ()); + cmd.Parameters.AddWithValue("?WantText", visible.ToString().ToLower ()); + cmd.Parameters.AddWithValue("?uuid", avatarId.ToString()); + + lock(Lock) + { + cmd.ExecuteNonQuery(); + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": AgentInterestsUpdate exception {0}", e.Message); + return false; + } + return true; + } + #endregion User Preferences + + #region Integration + public bool GetUserAppData(ref UserAppData props, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM `userdata` WHERE "; + query += "UserId = ?Id AND "; + query += "TagId = ?TagId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", props.UserId.ToString()); + cmd.Parameters.AddWithValue ("?TagId", props.TagId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + reader.Read(); + props.DataKey = (string)reader["DataKey"]; + props.DataVal = (string)reader["DataVal"]; + } + else + { + query += "INSERT INTO userdata VALUES ( "; + query += "?UserId,"; + query += "?TagId,"; + query += "?DataKey,"; + query += "?DataVal) "; + + using (MySqlCommand put = new MySqlCommand(query, dbcon)) + { + put.Parameters.AddWithValue("?Id", props.UserId.ToString()); + put.Parameters.AddWithValue("?TagId", props.TagId.ToString()); + put.Parameters.AddWithValue("?DataKey", props.DataKey.ToString()); + put.Parameters.AddWithValue("?DataVal", props.DataVal.ToString()); + + lock(Lock) + { + put.ExecuteNonQuery(); + } + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Requst application data exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool SetUserAppData(UserAppData props, ref string result) + { + string query = string.Empty; + + query += "UPDATE userdata SET "; + query += "TagId = ?TagId, "; + query += "DataKey = ?DataKey, "; + query += "DataVal = ?DataVal WHERE "; + query += "UserId = ?UserId AND "; + query += "TagId = ?TagId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?UserId", props.UserId.ToString()); + cmd.Parameters.AddWithValue("?TagId", props.TagId.ToString ()); + cmd.Parameters.AddWithValue("?DataKey", props.DataKey.ToString ()); + cmd.Parameters.AddWithValue("?DataVal", props.DataKey.ToString ()); + + lock(Lock) + { + cmd.ExecuteNonQuery(); + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": SetUserData exception {0}", e.Message); + return false; + } + return true; + } + #endregion Integration + } +} + diff --git a/OpenSim/Data/MySQL/Resources/UserProfiles.migrations b/OpenSim/Data/MySQL/Resources/UserProfiles.migrations new file mode 100644 index 0000000000..c29f1abf3e --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/UserProfiles.migrations @@ -0,0 +1,83 @@ +:VERSION 1 # ------------------------------- + +begin; + +CREATE TABLE IF NOT EXISTS `classifieds` ( + `classifieduuid` char(36) NOT NULL, + `creatoruuid` char(36) NOT NULL, + `creationdate` int(20) NOT NULL, + `expirationdate` int(20) NOT NULL, + `category` varchar(20) NOT NULL, + `name` varchar(255) NOT NULL, + `description` text NOT NULL, + `parceluuid` char(36) NOT NULL, + `parentestate` int(11) NOT NULL, + `snapshotuuid` char(36) NOT NULL, + `simname` varchar(255) NOT NULL, + `posglobal` varchar(255) NOT NULL, + `parcelname` varchar(255) NOT NULL, + `classifiedflags` int(8) NOT NULL, + `priceforlisting` int(5) NOT NULL, + PRIMARY KEY (`classifieduuid`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + + +CREATE TABLE IF NOT EXISTS `usernotes` ( + `useruuid` varchar(36) NOT NULL, + `targetuuid` varchar(36) NOT NULL, + `notes` text NOT NULL, + UNIQUE KEY `useruuid` (`useruuid`,`targetuuid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + + +CREATE TABLE IF NOT EXISTS `userpicks` ( + `pickuuid` varchar(36) NOT NULL, + `creatoruuid` varchar(36) NOT NULL, + `toppick` enum('true','false') NOT NULL, + `parceluuid` varchar(36) NOT NULL, + `name` varchar(255) NOT NULL, + `description` text NOT NULL, + `snapshotuuid` varchar(36) NOT NULL, + `user` varchar(255) NOT NULL, + `originalname` varchar(255) NOT NULL, + `simname` varchar(255) NOT NULL, + `posglobal` varchar(255) NOT NULL, + `sortorder` int(2) NOT NULL, + `enabled` enum('true','false') NOT NULL, + PRIMARY KEY (`pickuuid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + + +CREATE TABLE IF NOT EXISTS `userprofile` ( + `useruuid` varchar(36) NOT NULL, + `profilePartner` varchar(36) NOT NULL, + `profileAllowPublish` binary(1) NOT NULL, + `profileMaturePublish` binary(1) NOT NULL, + `profileURL` varchar(255) NOT NULL, + `profileWantToMask` int(3) NOT NULL, + `profileWantToText` text NOT NULL, + `profileSkillsMask` int(3) NOT NULL, + `profileSkillsText` text NOT NULL, + `profileLanguages` text NOT NULL, + `profileImage` varchar(36) NOT NULL, + `profileAboutText` text NOT NULL, + `profileFirstImage` varchar(36) NOT NULL, + `profileFirstText` text NOT NULL, + PRIMARY KEY (`useruuid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +commit; + +:VERSION 2 # ------------------------------- + +begin; +CREATE TABLE IF NOT EXISTS `userdata` ( + `UserId` char(36) NOT NULL, + `TagId` varchar(64) NOT NULL, + `DataKey` varchar(255), + `DataVal` varchar(255), + PRIMARY KEY (`UserId`,`TagId`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +commit; + diff --git a/OpenSim/Framework/Animation.cs b/OpenSim/Framework/Animation.cs index 232f5a1893..8bdf8f4373 100644 --- a/OpenSim/Framework/Animation.cs +++ b/OpenSim/Framework/Animation.cs @@ -120,5 +120,25 @@ namespace OpenSim.Framework sequenceNum = args["seq_num"].AsInteger(); } + public override bool Equals(object obj) + { + Animation other = obj as Animation; + if (other != null) + { + return (other.AnimID == this.AnimID + && other.SequenceNum == this.SequenceNum + && other.ObjectID == this.ObjectID); + } + + return base.Equals(obj); + } + + public override string ToString() + { + return "AnimID=" + AnimID.ToString() + + "/seq=" + SequenceNum.ToString() + + "/objID=" + ObjectID.ToString(); + } + } } diff --git a/OpenSim/Framework/UserProfiles.cs b/OpenSim/Framework/UserProfiles.cs new file mode 100644 index 0000000000..61335917e1 --- /dev/null +++ b/OpenSim/Framework/UserProfiles.cs @@ -0,0 +1,117 @@ +/* + * 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 OpenMetaverse; + +namespace OpenSim.Framework +{ + public class UserClassifiedAdd + { + public UUID ClassifiedId = UUID.Zero; + public UUID CreatorId = UUID.Zero; + public int CreationDate = 0; + public int ExpirationDate = 0; + public int Category = 0; + public string Name = string.Empty; + public string Description = string.Empty; + public UUID ParcelId = UUID.Zero; + public int ParentEstate = 0; + public UUID SnapshotId = UUID.Zero; + public string SimName = string.Empty; + public string GlobalPos = "<0,0,0>"; + public string ParcelName = string.Empty; + public byte Flags = 0; + public int Price = 0; + } + + public class UserProfileProperties + { + public UUID UserId = UUID.Zero; + public UUID PartnerId = UUID.Zero; + public bool PublishProfile = false; + public bool PublishMature = false; + public string WebUrl = string.Empty; + public int WantToMask = 0; + public string WantToText = string.Empty; + public int SkillsMask = 0; + public string SkillsText = string.Empty; + public string Language = string.Empty; + public UUID ImageId = UUID.Zero; + public string AboutText = string.Empty; + public UUID FirstLifeImageId = UUID.Zero; + public string FirstLifeText = string.Empty; + } + + public class UserProfilePick + { + public UUID PickId = UUID.Zero; + public UUID CreatorId = UUID.Zero; + public bool TopPick = false; + public string Name = string.Empty; + public string OriginalName = string.Empty; + public string Desc = string.Empty; + public UUID ParcelId = UUID.Zero; + public UUID SnapshotId = UUID.Zero; + public string User = string.Empty; + public string SimName = string.Empty; + public string GlobalPos = "<0,0,0>"; + public int SortOrder = 0; + public bool Enabled = false; + } + + public class UserProfileNotes + { + public UUID UserId; + public UUID TargetId; + public string Notes; + } + + public class UserAccountProperties + { + public string EmailAddress = string.Empty; + public string Firstname = string.Empty; + public string LastName = string.Empty; + public string Password = string.Empty; + public string UserId = string.Empty; + } + + public class UserAccountAuth + { + public string UserId = UUID.Zero.ToString(); + public string Password = string.Empty; + } + + public class UserAppData + { + public string TagId = string.Empty; + public string DataKey = string.Empty; + public string UserId = UUID.Zero.ToString(); + public string DataVal = string.Empty; + } +} + diff --git a/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs b/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs index 89a4d30664..d22f3f4aa1 100644 --- a/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs @@ -26,27 +26,25 @@ */ using System; -using System.Collections.Generic; -using Nini.Config; -using OpenMetaverse; -using OpenSim.Framework; -using OpenSim.Region.Framework.Scenes; -using OpenSim.Region.Framework.Interfaces; -using System; -using System.Reflection; using System.Collections; +using System.Collections.Generic; using System.Collections.Specialized; -using System.Reflection; using System.IO; +using System.Reflection; using System.Web; using System.Xml; using log4net; using Mono.Addins; +using Nini.Config; +using OpenMetaverse; using OpenMetaverse.Messages.Linden; using OpenMetaverse.StructuredData; +using OpenSim.Framework; using OpenSim.Framework.Capabilities; using OpenSim.Framework.Servers; using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Interfaces; using Caps = OpenSim.Framework.Capabilities.Caps; using OSDArray = OpenMetaverse.StructuredData.OSDArray; using OSDMap = OpenMetaverse.StructuredData.OSDMap; diff --git a/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs b/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs new file mode 100644 index 0000000000..5b228ee6d4 --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs @@ -0,0 +1,1328 @@ +/* + * 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.IO; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Xml; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using log4net; +using Nini.Config; +using Nwc.XmlRpc; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using Mono.Addins; +using OpenSim.Services.Connectors.Hypergrid; + +namespace OpenSim.Region.OptionalModules.Avatar.UserProfiles +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "UserProfilesModule")] + public class UserProfileModule : IProfileModule, INonSharedRegionModule + { + /// + /// Logging + /// + static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // The pair of Dictionaries are used to handle the switching of classified ads + // by maintaining a cache of classified id to creator id mappings and an interest + // count. The entries are removed when the interest count reaches 0. + Dictionary classifiedCache = new Dictionary(); + Dictionary classifiedInterest = new Dictionary(); + + public Scene Scene + { + get; private set; + } + + /// + /// Gets or sets the ConfigSource. + /// + /// + /// The configuration + /// + public IConfigSource Config { + get; + set; + } + + /// + /// Gets or sets the URI to the profile server. + /// + /// + /// The profile server URI. + /// + public string ProfileServerUri { + get; + set; + } + + IProfileModule ProfileModule + { + get; set; + } + + IUserManagement UserManagementModule + { + get; set; + } + + /// + /// Gets or sets a value indicating whether this + /// is enabled. + /// + /// + /// true if enabled; otherwise, false. + /// + public bool Enabled { + get; + set; + } + + #region IRegionModuleBase implementation + /// + /// This is called to initialize the region module. For shared modules, this is called exactly once, after + /// creating the single (shared) instance. For non-shared modules, this is called once on each instance, after + /// the instace for the region has been created. + /// + /// + /// Source. + /// + public void Initialise(IConfigSource source) + { + Config = source; + ReplaceableInterface = typeof(IProfileModule); + + IConfig profileConfig = Config.Configs["UserProfiles"]; + + if (profileConfig == null) + { + Enabled = false; + return; + } + + // If we find ProfileURL then we configure for FULL support + // else we setup for BASIC support + ProfileServerUri = profileConfig.GetString("ProfileServiceURL", ""); + if (ProfileServerUri == "") + { + Enabled = false; + return; + } + + m_log.Debug("[PROFILES]: Full Profiles Enabled"); + ReplaceableInterface = null; + Enabled = true; + } + + /// + /// Adds the region. + /// + /// + /// Scene. + /// + public void AddRegion(Scene scene) + { + if(!Enabled) + return; + + Scene = scene; + Scene.RegisterModuleInterface(this); + Scene.EventManager.OnNewClient += OnNewClient; + Scene.EventManager.OnMakeRootAgent += HandleOnMakeRootAgent; + + UserManagementModule = Scene.RequestModuleInterface(); + } + + void HandleOnMakeRootAgent (ScenePresence obj) + { + GetImageAssets(((IScenePresence)obj).UUID); + } + + /// + /// Removes the region. + /// + /// + /// Scene. + /// + public void RemoveRegion(Scene scene) + { + if(!Enabled) + return; + } + + /// + /// This will be called once for every scene loaded. In a shared module this will be multiple times in one + /// instance, while a nonshared module instance will only be called once. This method is called after AddRegion + /// has been called in all modules for that scene, providing an opportunity to request another module's + /// interface, or hook an event from another module. + /// + /// + /// Scene. + /// + public void RegionLoaded(Scene scene) + { + if(!Enabled) + return; + } + + /// + /// If this returns non-null, it is the type of an interface that this module intends to register. This will + /// cause the loader to defer loading of this module until all other modules have been loaded. If no other + /// module has registered the interface by then, this module will be activated, else it will remain inactive, + /// letting the other module take over. This should return non-null ONLY in modules that are intended to be + /// easily replaceable, e.g. stub implementations that the developer expects to be replaced by third party + /// provided modules. + /// + /// + /// The replaceable interface. + /// + public Type ReplaceableInterface + { + get; private set; + } + + /// + /// Called as the instance is closed. + /// + public void Close() + { + } + + /// + /// The name of the module + /// + /// + /// Gets the module name. + /// + public string Name + { + get { return "UserProfileModule"; } + } + #endregion IRegionModuleBase implementation + + #region Region Event Handlers + /// + /// Raises the new client event. + /// + /// + /// Client. + /// + void OnNewClient(IClientAPI client) + { + //Profile + client.OnRequestAvatarProperties += RequestAvatarProperties; + client.OnUpdateAvatarProperties += AvatarPropertiesUpdate; + client.OnAvatarInterestUpdate += AvatarInterestsUpdate; + + // Classifieds + client.AddGenericPacketHandler("avatarclassifiedsrequest", ClassifiedsRequest); + client.OnClassifiedInfoUpdate += ClassifiedInfoUpdate; + client.OnClassifiedInfoRequest += ClassifiedInfoRequest; + client.OnClassifiedDelete += ClassifiedDelete; + + // Picks + client.AddGenericPacketHandler("avatarpicksrequest", PicksRequest); + client.AddGenericPacketHandler("pickinforequest", PickInfoRequest); + client.OnPickInfoUpdate += PickInfoUpdate; + client.OnPickDelete += PickDelete; + + // Notes + client.AddGenericPacketHandler("avatarnotesrequest", NotesRequest); + client.OnAvatarNotesUpdate += NotesUpdate; + } + #endregion Region Event Handlers + + #region Classified + /// + /// + /// Handles the avatar classifieds request. + /// + /// + /// Sender. + /// + /// + /// Method. + /// + /// + /// Arguments. + /// + public void ClassifiedsRequest(Object sender, string method, List args) + { + if (!(sender is IClientAPI)) + return; + + IClientAPI remoteClient = (IClientAPI)sender; + + UUID targetID; + UUID.TryParse(args[0], out targetID); + + // Can't handle NPC yet... + ScenePresence p = FindPresence(targetID); + + if (null != p) + { + if (p.PresenceType == PresenceType.Npc) + return; + } + + string serverURI = string.Empty; + bool foreign = GetUserProfileServerURI(targetID, out serverURI); + UUID creatorId = UUID.Zero; + + OSDMap parameters= new OSDMap(); + UUID.TryParse(args[0], out creatorId); + parameters.Add("creatorId", OSD.FromUUID(creatorId)); + OSD Params = (OSD)parameters; + if(!JsonRpcRequest(ref Params, "avatarclassifiedsrequest", serverURI, UUID.Random().ToString())) + { + // Error Handling here! + // if(parameters.ContainsKey("message") + } + + parameters = (OSDMap)Params; + + OSDArray list = (OSDArray)parameters["result"]; + + Dictionary classifieds = new Dictionary(); + + foreach(OSD map in list) + { + OSDMap m = (OSDMap)map; + UUID cid = m["classifieduuid"].AsUUID(); + string name = m["name"].AsString(); + + classifieds[cid] = name; + + if(!classifiedCache.ContainsKey(cid)) + { + classifiedCache.Add(cid,creatorId); + classifiedInterest.Add(cid, 0); + } + + classifiedInterest[cid] ++; + } + + remoteClient.SendAvatarClassifiedReply(new UUID(args[0]), classifieds); + } + + public void ClassifiedInfoRequest(UUID queryClassifiedID, IClientAPI remoteClient) + { + UUID target = remoteClient.AgentId; + UserClassifiedAdd ad = new UserClassifiedAdd(); + ad.ClassifiedId = queryClassifiedID; + + if(classifiedCache.ContainsKey(queryClassifiedID)) + { + target = classifiedCache[queryClassifiedID]; + + if(classifiedInterest[queryClassifiedID] -- == 0) + { + lock(classifiedCache) + { + lock(classifiedInterest) + { + classifiedInterest.Remove(queryClassifiedID); + } + classifiedCache.Remove(queryClassifiedID); + } + } + } + + + string serverURI = string.Empty; + bool foreign = GetUserProfileServerURI(target, out serverURI); + + object Ad = (object)ad; + if(!JsonRpcRequest(ref Ad, "classifieds_info_query", serverURI, UUID.Random().ToString())) + { + remoteClient.SendAgentAlertMessage( + "Error getting classified info", false); + return; + } + ad = (UserClassifiedAdd) Ad; + + if(ad.CreatorId == UUID.Zero) + return; + + Vector3 globalPos = new Vector3(); + Vector3.TryParse(ad.GlobalPos, out globalPos); + + remoteClient.SendClassifiedInfoReply(ad.ClassifiedId, ad.CreatorId, (uint)ad.CreationDate, (uint)ad.ExpirationDate, + (uint)ad.Category, ad.Name, ad.Description, ad.ParcelId, (uint)ad.ParentEstate, + ad.SnapshotId, ad.SimName, globalPos, ad.ParcelName, ad.Flags, ad.Price); + + } + + /// + /// Classifieds info update. + /// + /// + /// Queryclassified I. + /// + /// + /// Query category. + /// + /// + /// Query name. + /// + /// + /// Query description. + /// + /// + /// Query parcel I. + /// + /// + /// Query parent estate. + /// + /// + /// Query snapshot I. + /// + /// + /// Query global position. + /// + /// + /// Queryclassified flags. + /// + /// + /// Queryclassified price. + /// + /// + /// Remote client. + /// + public void ClassifiedInfoUpdate(UUID queryclassifiedID, uint queryCategory, string queryName, string queryDescription, UUID queryParcelID, + uint queryParentEstate, UUID querySnapshotID, Vector3 queryGlobalPos, byte queryclassifiedFlags, + int queryclassifiedPrice, IClientAPI remoteClient) + { + UserClassifiedAdd ad = new UserClassifiedAdd(); + + Scene s = (Scene) remoteClient.Scene; + Vector3 pos = remoteClient.SceneAgent.AbsolutePosition; + ILandObject land = s.LandChannel.GetLandObject(pos.X, pos.Y); + ScenePresence p = FindPresence(remoteClient.AgentId); + Vector3 avaPos = p.AbsolutePosition; + + string serverURI = string.Empty; + bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI); + + if (land == null) + { + ad.ParcelName = string.Empty; + } + else + { + ad.ParcelName = land.LandData.Name; + } + + ad.CreatorId = remoteClient.AgentId; + ad.ClassifiedId = queryclassifiedID; + ad.Category = Convert.ToInt32(queryCategory); + ad.Name = queryName; + ad.Description = queryDescription; + ad.ParentEstate = Convert.ToInt32(queryParentEstate); + ad.SnapshotId = querySnapshotID; + ad.SimName = remoteClient.Scene.RegionInfo.RegionName; + ad.GlobalPos = queryGlobalPos.ToString (); + ad.Flags = queryclassifiedFlags; + ad.Price = queryclassifiedPrice; + ad.ParcelId = p.currentParcelUUID; + + object Ad = ad; + + OSD X = OSD.SerializeMembers(Ad); + + if(!JsonRpcRequest(ref Ad, "classified_update", serverURI, UUID.Random().ToString())) + { + remoteClient.SendAgentAlertMessage( + "Error updating classified", false); + } + } + + /// + /// Classifieds delete. + /// + /// + /// Query classified I. + /// + /// + /// Remote client. + /// + public void ClassifiedDelete(UUID queryClassifiedID, IClientAPI remoteClient) + { + string serverURI = string.Empty; + bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI); + + UUID classifiedId; + OSDMap parameters= new OSDMap(); + UUID.TryParse(queryClassifiedID.ToString(), out classifiedId); + parameters.Add("classifiedId", OSD.FromUUID(classifiedId)); + OSD Params = (OSD)parameters; + if(!JsonRpcRequest(ref Params, "classified_delete", serverURI, UUID.Random().ToString())) + { + remoteClient.SendAgentAlertMessage( + "Error classified delete", false); + } + + parameters = (OSDMap)Params; + } + #endregion Classified + + #region Picks + /// + /// Handles the avatar picks request. + /// + /// + /// Sender. + /// + /// + /// Method. + /// + /// + /// Arguments. + /// + public void PicksRequest(Object sender, string method, List args) + { + if (!(sender is IClientAPI)) + return; + + IClientAPI remoteClient = (IClientAPI)sender; + + UUID targetId; + UUID.TryParse(args[0], out targetId); + + // Can't handle NPC yet... + ScenePresence p = FindPresence(targetId); + + if (null != p) + { + if (p.PresenceType == PresenceType.Npc) + return; + } + + string serverURI = string.Empty; + bool foreign = GetUserProfileServerURI(targetId, out serverURI); + + OSDMap parameters= new OSDMap(); + parameters.Add("creatorId", OSD.FromUUID(targetId)); + OSD Params = (OSD)parameters; + if(!JsonRpcRequest(ref Params, "avatarpicksrequest", serverURI, UUID.Random().ToString())) + { + remoteClient.SendAgentAlertMessage( + "Error requesting picks", false); + return; + } + + parameters = (OSDMap)Params; + + OSDArray list = (OSDArray)parameters["result"]; + + Dictionary picks = new Dictionary(); + + foreach(OSD map in list) + { + OSDMap m = (OSDMap)map; + UUID cid = m["pickuuid"].AsUUID(); + string name = m["name"].AsString(); + + m_log.DebugFormat("[PROFILES]: PicksRequest {0}", name); + + picks[cid] = name; + } + remoteClient.SendAvatarPicksReply(new UUID(args[0]), picks); + } + + /// + /// Handles the pick info request. + /// + /// + /// Sender. + /// + /// + /// Method. + /// + /// + /// Arguments. + /// + public void PickInfoRequest(Object sender, string method, List args) + { + if (!(sender is IClientAPI)) + return; + + UUID targetID; + UUID.TryParse(args[0], out targetID); + string serverURI = string.Empty; + bool foreign = GetUserProfileServerURI(targetID, out serverURI); + IClientAPI remoteClient = (IClientAPI)sender; + + UserProfilePick pick = new UserProfilePick(); + UUID.TryParse(args[0], out pick.CreatorId); + UUID.TryParse(args[1], out pick.PickId); + + + object Pick = (object)pick; + if(!JsonRpcRequest(ref Pick, "pickinforequest", serverURI, UUID.Random().ToString())) + { + remoteClient.SendAgentAlertMessage( + "Error selecting pick", false); + } + pick = (UserProfilePick) Pick; + if(pick.SnapshotId == UUID.Zero) + { + // In case of a new UserPick, the data may not be ready and we would send wrong data, skip it... + m_log.DebugFormat("[PROFILES]: PickInfoRequest: SnapshotID is {0}", UUID.Zero.ToString()); + return; + } + + Vector3 globalPos; + Vector3.TryParse(pick.GlobalPos,out globalPos); + + m_log.DebugFormat("[PROFILES]: PickInfoRequest: {0} : {1}", pick.Name.ToString(), pick.SnapshotId.ToString()); + + remoteClient.SendPickInfoReply(pick.PickId,pick.CreatorId,pick.TopPick,pick.ParcelId,pick.Name, + pick.Desc,pick.SnapshotId,pick.User,pick.OriginalName,pick.SimName, + globalPos,pick.SortOrder,pick.Enabled); + } + + /// + /// Updates the userpicks + /// + /// + /// Remote client. + /// + /// + /// Pick I. + /// + /// + /// the creator of the pick + /// + /// + /// Top pick. + /// + /// + /// Name. + /// + /// + /// Desc. + /// + /// + /// Snapshot I. + /// + /// + /// Sort order. + /// + /// + /// Enabled. + /// + public void PickInfoUpdate(IClientAPI remoteClient, UUID pickID, UUID creatorID, bool topPick, string name, string desc, UUID snapshotID, int sortOrder, bool enabled) + { + + m_log.DebugFormat("[PROFILES]: Start PickInfoUpdate Name: {0} PickId: {1} SnapshotId: {2}", name, pickID.ToString(), snapshotID.ToString()); + UserProfilePick pick = new UserProfilePick(); + string serverURI = string.Empty; + bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI); + ScenePresence p = FindPresence(remoteClient.AgentId); + + Vector3 avaPos = p.AbsolutePosition; + // Getting the global position for the Avatar + Vector3 posGlobal = new Vector3(remoteClient.Scene.RegionInfo.RegionLocX*Constants.RegionSize + avaPos.X, + remoteClient.Scene.RegionInfo.RegionLocY*Constants.RegionSize + avaPos.Y, + avaPos.Z); + + string landOwnerName = string.Empty; + ILandObject land = p.Scene.LandChannel.GetLandObject(avaPos.X, avaPos.Y); + if(land.LandData.IsGroupOwned) + { + IGroupsModule groupMod = p.Scene.RequestModuleInterface(); + UUID groupId = land.LandData.GroupID; + GroupRecord groupRecord = groupMod.GetGroupRecord(groupId); + landOwnerName = groupRecord.GroupName; + } + else + { + IUserAccountService accounts = p.Scene.RequestModuleInterface(); + UserAccount user = accounts.GetUserAccount(p.Scene.RegionInfo.ScopeID, land.LandData.OwnerID); + landOwnerName = user.Name; + } + + pick.PickId = pickID; + pick.CreatorId = creatorID; + pick.TopPick = topPick; + pick.Name = name; + pick.Desc = desc; + pick.ParcelId = p.currentParcelUUID; + pick.SnapshotId = snapshotID; + pick.User = landOwnerName; + pick.SimName = remoteClient.Scene.RegionInfo.RegionName; + pick.GlobalPos = posGlobal.ToString(); + pick.SortOrder = sortOrder; + pick.Enabled = enabled; + + object Pick = (object)pick; + if(!JsonRpcRequest(ref Pick, "picks_update", serverURI, UUID.Random().ToString())) + { + remoteClient.SendAgentAlertMessage( + "Error updating pick", false); + } + + m_log.DebugFormat("[PROFILES]: Finish PickInfoUpdate {0} {1}", pick.Name, pick.PickId.ToString()); + } + + /// + /// Delete a Pick + /// + /// + /// Remote client. + /// + /// + /// Query pick I. + /// + public void PickDelete(IClientAPI remoteClient, UUID queryPickID) + { + string serverURI = string.Empty; + bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI); + + OSDMap parameters= new OSDMap(); + parameters.Add("pickId", OSD.FromUUID(queryPickID)); + OSD Params = (OSD)parameters; + if(!JsonRpcRequest(ref Params, "picks_delete", serverURI, UUID.Random().ToString())) + { + remoteClient.SendAgentAlertMessage( + "Error picks delete", false); + } + } + #endregion Picks + + #region Notes + /// + /// Handles the avatar notes request. + /// + /// + /// Sender. + /// + /// + /// Method. + /// + /// + /// Arguments. + /// + public void NotesRequest(Object sender, string method, List args) + { + UserProfileNotes note = new UserProfileNotes(); + + if (!(sender is IClientAPI)) + return; + + IClientAPI remoteClient = (IClientAPI)sender; + string serverURI = string.Empty; + bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI); + note.TargetId = remoteClient.AgentId; + UUID.TryParse(args[0], out note.UserId); + + object Note = (object)note; + if(!JsonRpcRequest(ref Note, "avatarnotesrequest", serverURI, UUID.Random().ToString())) + { + remoteClient.SendAgentAlertMessage( + "Error requesting note", false); + } + note = (UserProfileNotes) Note; + + remoteClient.SendAvatarNotesReply(note.TargetId, note.Notes); + } + + /// + /// Avatars the notes update. + /// + /// + /// Remote client. + /// + /// + /// Query target I. + /// + /// + /// Query notes. + /// + public void NotesUpdate(IClientAPI remoteClient, UUID queryTargetID, string queryNotes) + { + UserProfileNotes note = new UserProfileNotes(); + + note.UserId = remoteClient.AgentId; + note.TargetId = queryTargetID; + note.Notes = queryNotes; + + string serverURI = string.Empty; + bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI); + + object Note = note; + if(!JsonRpcRequest(ref Note, "avatar_notes_update", serverURI, UUID.Random().ToString())) + { + remoteClient.SendAgentAlertMessage( + "Error updating note", false); + } + } + #endregion Notes + + #region Avatar Properties + /// + /// Update the avatars interests . + /// + /// + /// Remote client. + /// + /// + /// Wantmask. + /// + /// + /// Wanttext. + /// + /// + /// Skillsmask. + /// + /// + /// Skillstext. + /// + /// + /// Languages. + /// + public void AvatarInterestsUpdate(IClientAPI remoteClient, uint wantmask, string wanttext, uint skillsmask, string skillstext, string languages) + { + UserProfileProperties prop = new UserProfileProperties(); + + prop.UserId = remoteClient.AgentId; + prop.WantToMask = (int)wantmask; + prop.WantToText = wanttext; + prop.SkillsMask = (int)skillsmask; + prop.SkillsText = skillstext; + prop.Language = languages; + + string serverURI = string.Empty; + bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI); + + object Param = prop; + if(!JsonRpcRequest(ref Param, "avatar_interests_update", serverURI, UUID.Random().ToString())) + { + remoteClient.SendAgentAlertMessage( + "Error updating interests", false); + } + } + + public void RequestAvatarProperties(IClientAPI remoteClient, UUID avatarID) + { + if ( String.IsNullOrEmpty(avatarID.ToString()) || String.IsNullOrEmpty(remoteClient.AgentId.ToString())) + { + // Looking for a reason that some viewers are sending null Id's + m_log.DebugFormat("[PROFILES]: This should not happen remoteClient.AgentId {0} - avatarID {1}", remoteClient.AgentId, avatarID); + return; + } + + // Can't handle NPC yet... + ScenePresence p = FindPresence(avatarID); + + if (null != p) + { + if (p.PresenceType == PresenceType.Npc) + return; + } + + string serverURI = string.Empty; + bool foreign = GetUserProfileServerURI(avatarID, out serverURI); + + UserAccount account = null; + Dictionary userInfo; + + if (!foreign) + { + account = Scene.UserAccountService.GetUserAccount(Scene.RegionInfo.ScopeID, avatarID); + } + else + { + userInfo = new Dictionary(); + } + + Byte[] charterMember = new Byte[1]; + string born = String.Empty; + uint flags = 0x00; + + if (null != account) + { + if (account.UserTitle == "") + { + charterMember[0] = (Byte)((account.UserFlags & 0xf00) >> 8); + } + else + { + charterMember = Utils.StringToBytes(account.UserTitle); + } + + born = Util.ToDateTime(account.Created).ToString( + "M/d/yyyy", CultureInfo.InvariantCulture); + flags = (uint)(account.UserFlags & 0xff); + } + else + { + if (GetUserAccountData(avatarID, out userInfo) == true) + { + if ((string)userInfo["user_title"] == "") + { + charterMember[0] = (Byte)(((Byte)userInfo["user_flags"] & 0xf00) >> 8); + } + else + { + charterMember = Utils.StringToBytes((string)userInfo["user_title"]); + } + + int val_born = (int)userInfo["user_created"]; + born = Util.ToDateTime(val_born).ToString( + "M/d/yyyy", CultureInfo.InvariantCulture); + + // picky, picky + int val_flags = (int)userInfo["user_flags"]; + flags = (uint)(val_flags & 0xff); + } + } + + UserProfileProperties props = new UserProfileProperties(); + string result = string.Empty; + + props.UserId = avatarID; + GetProfileData(ref props, out result); + + remoteClient.SendAvatarProperties(props.UserId, props.AboutText, born, charterMember , props.FirstLifeText, flags, + props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId); + + + remoteClient.SendAvatarInterestsReply(props.UserId, (uint)props.WantToMask, props.WantToText, (uint)props.SkillsMask, + props.SkillsText, props.Language); + } + + /// + /// Updates the avatar properties. + /// + /// + /// Remote client. + /// + /// + /// New profile. + /// + public void AvatarPropertiesUpdate(IClientAPI remoteClient, UserProfileData newProfile) + { + if (remoteClient.AgentId == newProfile.ID) + { + UserProfileProperties prop = new UserProfileProperties(); + + prop.UserId = remoteClient.AgentId; + prop.WebUrl = newProfile.ProfileUrl; + prop.ImageId = newProfile.Image; + prop.AboutText = newProfile.AboutText; + prop.FirstLifeImageId = newProfile.FirstLifeImage; + prop.FirstLifeText = newProfile.FirstLifeAboutText; + + string serverURI = string.Empty; + bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI); + + object Prop = prop; + + if(!JsonRpcRequest(ref Prop, "avatar_properties_update", serverURI, UUID.Random().ToString())) + { + remoteClient.SendAgentAlertMessage( + "Error updating properties", false); + } + + RequestAvatarProperties(remoteClient, newProfile.ID); + } + } + + + /// + /// Gets the profile data. + /// + /// + /// The profile data. + /// + /// + /// User I. + /// + bool GetProfileData(ref UserProfileProperties properties, out string message) + { + // Can't handle NPC yet... + ScenePresence p = FindPresence(properties.UserId); + + if (null != p) + { + if (p.PresenceType == PresenceType.Npc) + { + message = "Id points to NPC"; + return false; + } + } + + string serverURI = string.Empty; + bool foreign = GetUserProfileServerURI(properties.UserId, out serverURI); + + // This is checking a friend on the home grid + // Not HG friend + if ( String.IsNullOrEmpty(serverURI)) + { + message = "No Presence - foreign friend"; + return false; + } + + object Prop = (object)properties; + JsonRpcRequest(ref Prop, "avatar_properties_request", serverURI, UUID.Random().ToString()); + properties = (UserProfileProperties)Prop; + + message = "Success"; + return true; + } + #endregion Avatar Properties + + #region Utils + bool GetImageAssets(UUID avatarId) + { + string profileServerURI = string.Empty; + string assetServerURI = string.Empty; + + bool foreign = GetUserProfileServerURI(avatarId, out profileServerURI); + + if(!foreign) + return true; + + assetServerURI = UserManagementModule.GetUserServerURL(avatarId, "AssetServerURI"); + + OSDMap parameters= new OSDMap(); + parameters.Add("avatarId", OSD.FromUUID(avatarId)); + OSD Params = (OSD)parameters; + if(!JsonRpcRequest(ref Params, "image_assets_request", profileServerURI, UUID.Random().ToString())) + { + // Error Handling here! + // if(parameters.ContainsKey("message") + return false; + } + + parameters = (OSDMap)Params; + + OSDArray list = (OSDArray)parameters["result"]; + + foreach(OSD asset in list) + { + OSDString assetId = (OSDString)asset; + + Scene.AssetService.Get(string.Format("{0}/{1}",assetServerURI, assetId.AsString()), this, + delegate (string assetID, Object s, AssetBase a) + { + // m_log.DebugFormat("[PROFILES]: Getting Image Assets {0}", assetID); + return; + }); + } + return true; + } + + /// + /// Gets the user account data. + /// + /// + /// The user profile data. + /// + /// + /// If set to true user I. + /// + /// + /// If set to true user info. + /// + bool GetUserAccountData(UUID userID, out Dictionary userInfo) + { + Dictionary info = new Dictionary(); + + if (UserManagementModule.IsLocalGridUser(userID)) + { + // Is local + IUserAccountService uas = Scene.UserAccountService; + UserAccount account = uas.GetUserAccount(Scene.RegionInfo.ScopeID, userID); + + info["user_flags"] = account.UserFlags; + info["user_created"] = account.Created; + + if (!String.IsNullOrEmpty(account.UserTitle)) + info["user_title"] = account.UserTitle; + else + info["user_title"] = ""; + + userInfo = info; + + return false; + } + else + { + // Is Foreign + string home_url = UserManagementModule.GetUserServerURL(userID, "HomeURI"); + + if (String.IsNullOrEmpty(home_url)) + { + info["user_flags"] = 0; + info["user_created"] = 0; + info["user_title"] = "Unavailable"; + + userInfo = info; + return true; + } + + UserAgentServiceConnector uConn = new UserAgentServiceConnector(home_url); + + Dictionary account = uConn.GetUserInfo(userID); + + if (account.Count > 0) + { + if (account.ContainsKey("user_flags")) + info["user_flags"] = account["user_flags"]; + else + info["user_flags"] = ""; + + if (account.ContainsKey("user_created")) + info["user_created"] = account["user_created"]; + else + info["user_created"] = ""; + + info["user_title"] = "HG Visitor"; + } + else + { + info["user_flags"] = 0; + info["user_created"] = 0; + info["user_title"] = "HG Visitor"; + } + userInfo = info; + return true; + } + } + + /// + /// Gets the user profile server UR. + /// + /// + /// The user profile server UR. + /// + /// + /// If set to true user I. + /// + /// + /// If set to true server UR. + /// + bool GetUserProfileServerURI(UUID userID, out string serverURI) + { + bool local; + local = UserManagementModule.IsLocalGridUser(userID); + + if (!local) + { + serverURI = UserManagementModule.GetUserServerURL(userID, "ProfileServerURI"); + // Is Foreign + return true; + } + else + { + serverURI = ProfileServerUri; + // Is local + return false; + } + } + + /// + /// Finds the presence. + /// + /// + /// The presence. + /// + /// + /// Client I. + /// + ScenePresence FindPresence(UUID clientID) + { + ScenePresence p; + + p = Scene.GetScenePresence(clientID); + if (p != null && !p.IsChildAgent) + return p; + + return null; + } + #endregion Util + + #region Web Util + /// + /// Sends json-rpc request with a serializable type. + /// + /// + /// OSD Map. + /// + /// + /// Serializable type . + /// + /// + /// Json-rpc method to call. + /// + /// + /// URI of json-rpc service. + /// + /// + /// Id for our call. + /// + bool JsonRpcRequest(ref object parameters, string method, string uri, string jsonId) + { + if (jsonId == null) + throw new ArgumentNullException ("jsonId"); + if (uri == null) + throw new ArgumentNullException ("uri"); + if (method == null) + throw new ArgumentNullException ("method"); + if (parameters == null) + throw new ArgumentNullException ("parameters"); + + // Prep our payload + OSDMap json = new OSDMap(); + + json.Add("jsonrpc", OSD.FromString("2.0")); + json.Add("id", OSD.FromString(jsonId)); + json.Add("method", OSD.FromString(method)); + // Experiment + json.Add("params", OSD.SerializeMembers(parameters)); + + string jsonRequestData = OSDParser.SerializeJsonString(json); + byte[] content = Encoding.UTF8.GetBytes(jsonRequestData); + + HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uri); + // webRequest.Credentials = new NetworkCredential(rpcUser, rpcPass); + webRequest.ContentType = "application/json-rpc"; + webRequest.Method = "POST"; + + Stream dataStream = webRequest.GetRequestStream(); + dataStream.Write(content, 0, content.Length); + dataStream.Close(); + + WebResponse webResponse = null; + try + { + webResponse = webRequest.GetResponse(); + } + catch (WebException e) + { + Console.WriteLine("Web Error" + e.Message); + Console.WriteLine ("Please check input"); + return false; + } + + byte[] buf = new byte[8192]; + Stream rstream = webResponse.GetResponseStream(); + OSDMap mret = (OSDMap)OSDParser.DeserializeJson(rstream); + + if(mret.ContainsKey("error")) + return false; + + // get params... + OSD.DeserializeMembers(ref parameters, (OSDMap) mret["result"]); + return true; + } + + /// + /// Sends json-rpc request with OSD parameter. + /// + /// + /// The rpc request. + /// + /// + /// data - incoming as parameters, outgong as result/error + /// + /// + /// Json-rpc method to call. + /// + /// + /// URI of json-rpc service. + /// + /// + /// If set to true json identifier. + /// + bool JsonRpcRequest(ref OSD data, string method, string uri, string jsonId) + { + OSDMap map = new OSDMap(); + + map["jsonrpc"] = "2.0"; + if(string.IsNullOrEmpty(jsonId)) + map["id"] = UUID.Random().ToString(); + else + map["id"] = jsonId; + + map["method"] = method; + map["params"] = data; + + string jsonRequestData = OSDParser.SerializeJsonString(map); + byte[] content = Encoding.UTF8.GetBytes(jsonRequestData); + + HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uri); + webRequest.ContentType = "application/json-rpc"; + webRequest.Method = "POST"; + + Stream dataStream = webRequest.GetRequestStream(); + dataStream.Write(content, 0, content.Length); + dataStream.Close(); + + WebResponse webResponse = null; + try + { + webResponse = webRequest.GetResponse(); + } + catch (WebException e) + { + Console.WriteLine("Web Error" + e.Message); + Console.WriteLine ("Please check input"); + return false; + } + + byte[] buf = new byte[8192]; + Stream rstream = webResponse.GetResponseStream(); + + OSDMap response = new OSDMap(); + response = (OSDMap)OSDParser.DeserializeJson(rstream); + if(response.ContainsKey("error")) + { + data = response["error"]; + return false; + } + + data = response; + + return true; + } + #endregion Web Util + } +} diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsIn/UserProfiles/LocalUserProfilesServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsIn/UserProfiles/LocalUserProfilesServiceConnector.cs new file mode 100644 index 0000000000..323535ab07 --- /dev/null +++ b/OpenSim/Region/CoreModules/ServiceConnectorsIn/UserProfiles/LocalUserProfilesServiceConnector.cs @@ -0,0 +1,226 @@ + +/* + * 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 log4net; +using Mono.Addins; +using Nini.Config; +using System; +using System.Collections.Generic; +using System.Reflection; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Server.Base; +using OpenSim.Server.Handlers; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Framework.Servers; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenMetaverse; + +namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Profile +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "LocalUserProfilesServicesConnector")] + public class LocalUserProfilesServicesConnector : ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private Dictionary regions = new Dictionary(); + + public IUserProfilesService ServiceModule + { + get; private set; + } + + public bool Enabled + { + get; private set; + } + + public string Name + { + get + { + return "LocalUserProfilesServicesConnector"; + } + } + + public string ConfigName + { + get; private set; + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public LocalUserProfilesServicesConnector() + { + m_log.Debug("[LOCAL USERPROFILES SERVICE CONNECTOR]: LocalUserProfileServicesConnector no params"); + } + + public LocalUserProfilesServicesConnector(IConfigSource source) + { + m_log.Debug("[LOCAL USERPROFILES SERVICE CONNECTOR]: LocalUserProfileServicesConnector instantiated directly."); + InitialiseService(source); + } + + public void InitialiseService(IConfigSource source) + { + ConfigName = "UserProfilesService"; + + // Instantiate the request handler + IHttpServer Server = MainServer.Instance; + + IConfig config = source.Configs[ConfigName]; + if (config == null) + { + m_log.Error("[LOCAL USERPROFILES SERVICE CONNECTOR]: UserProfilesService missing from OpenSim.ini"); + return; + } + + if(!config.GetBoolean("Enabled",false)) + { + Enabled = false; + return; + } + + Enabled = true; + + string serviceDll = config.GetString("LocalServiceModule", + String.Empty); + + if (serviceDll == String.Empty) + { + m_log.Error("[LOCAL USERPROFILES SERVICE CONNECTOR]: No LocalServiceModule named in section UserProfilesService"); + return; + } + + Object[] args = new Object[] { source, ConfigName }; + ServiceModule = + ServerUtils.LoadPlugin(serviceDll, + args); + + if (ServiceModule == null) + { + m_log.Error("[LOCAL USERPROFILES SERVICE CONNECTOR]: Can't load user profiles service"); + return; + } + + Enabled = true; + + JsonRpcProfileHandlers handler = new JsonRpcProfileHandlers(ServiceModule); + + Server.AddJsonRPCHandler("avatarclassifiedsrequest", handler.AvatarClassifiedsRequest); + Server.AddJsonRPCHandler("classified_update", handler.ClassifiedUpdate); + Server.AddJsonRPCHandler("classifieds_info_query", handler.ClassifiedInfoRequest); + Server.AddJsonRPCHandler("classified_delete", handler.ClassifiedDelete); + Server.AddJsonRPCHandler("avatarpicksrequest", handler.AvatarPicksRequest); + Server.AddJsonRPCHandler("pickinforequest", handler.PickInfoRequest); + Server.AddJsonRPCHandler("picks_update", handler.PicksUpdate); + Server.AddJsonRPCHandler("picks_delete", handler.PicksDelete); + Server.AddJsonRPCHandler("avatarnotesrequest", handler.AvatarNotesRequest); + Server.AddJsonRPCHandler("avatar_notes_update", handler.NotesUpdate); + Server.AddJsonRPCHandler("avatar_properties_request", handler.AvatarPropertiesRequest); + Server.AddJsonRPCHandler("avatar_properties_update", handler.AvatarPropertiesUpdate); + Server.AddJsonRPCHandler("avatar_interests_update", handler.AvatarInterestsUpdate); + Server.AddJsonRPCHandler("image_assets_request", handler.AvatarImageAssetsRequest); + Server.AddJsonRPCHandler("user_data_request", handler.RequestUserAppData); + Server.AddJsonRPCHandler("user_data_update", handler.UpdateUserAppData); + + } + + #region ISharedRegionModule implementation + + void ISharedRegionModule.PostInitialise() + { + if(!Enabled) + return; + } + + #endregion + + #region IRegionModuleBase implementation + + void IRegionModuleBase.Initialise(IConfigSource source) + { + IConfig moduleConfig = source.Configs["Modules"]; + if (moduleConfig != null) + { + string name = moduleConfig.GetString("UserProfilesServices", ""); + if (name == Name) + { + InitialiseService(source); + m_log.Info("[LOCAL USERPROFILES SERVICE CONNECTOR]: Local user profiles connector enabled"); + } + } + } + + void IRegionModuleBase.Close() + { + return; + } + + void IRegionModuleBase.AddRegion(Scene scene) + { + if (!Enabled) + return; + + lock (regions) + { + if (regions.ContainsKey(scene.RegionInfo.RegionID)) + m_log.ErrorFormat("[LOCAL USERPROFILES SERVICE CONNECTOR]: simulator seems to have more than one region with the same UUID. Please correct this!"); + else + regions.Add(scene.RegionInfo.RegionID, scene); + } + } + + void IRegionModuleBase.RemoveRegion(Scene scene) + { + if (!Enabled) + return; + + lock (regions) + { + if (regions.ContainsKey(scene.RegionInfo.RegionID)) + regions.Remove(scene.RegionInfo.RegionID); + } + } + + void IRegionModuleBase.RegionLoaded(Scene scene) + { + if (!Enabled) + return; + } + #endregion + } +} \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs index c32820e94d..3849a3fb91 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs @@ -56,6 +56,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid public LocalGridServicesConnector() { + m_log.Debug("[LOCAL GRID SERVICE CONNECTOR]: LocalGridServicesConnector no parms."); } public LocalGridServicesConnector(IConfigSource source) diff --git a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs index 66edfed393..5dee64d0b5 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs +++ b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs @@ -28,8 +28,11 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.Text; using log4net; using OpenMetaverse; +using OpenMetaverse.StructuredData; + using OpenSim.Framework; using Animation = OpenSim.Framework.Animation; @@ -60,6 +63,12 @@ namespace OpenSim.Region.Framework.Scenes.Animation ResetDefaultAnimation(); } + public AnimationSet(OSDArray pArray) + { + ResetDefaultAnimation(); + FromOSDArray(pArray); + } + public bool HasAnimation(UUID animID) { if (m_defaultAnimation.AnimID == animID) @@ -218,5 +227,106 @@ namespace OpenSim.Region.Framework.Scenes.Animation foreach (OpenSim.Framework.Animation anim in theArray) m_animations.Add(anim); } + + // Create representation of this AnimationSet as an OSDArray. + // First two entries in the array are the default and implicitDefault animations + // followed by the other animations. + public OSDArray ToOSDArray() + { + OSDArray ret = new OSDArray(); + ret.Add(DefaultAnimation.PackUpdateMessage()); + ret.Add(ImplicitDefaultAnimation.PackUpdateMessage()); + + foreach (OpenSim.Framework.Animation anim in m_animations) + ret.Add(anim.PackUpdateMessage()); + + return ret; + } + + public void FromOSDArray(OSDArray pArray) + { + this.Clear(); + + if (pArray.Count >= 1) + { + m_defaultAnimation = new OpenSim.Framework.Animation((OSDMap)pArray[0]); + } + if (pArray.Count >= 2) + { + m_implicitDefaultAnimation = new OpenSim.Framework.Animation((OSDMap)pArray[1]); + } + for (int ii = 2; ii < pArray.Count; ii++) + { + m_animations.Add(new OpenSim.Framework.Animation((OSDMap)pArray[ii])); + } + } + + // Compare two AnimationSets and return 'true' if the default animations are the same + // and all of the animations in the list are equal. + public override bool Equals(object obj) + { + AnimationSet other = obj as AnimationSet; + if (other != null) + { + if (this.DefaultAnimation.Equals(other.DefaultAnimation) + && this.ImplicitDefaultAnimation.Equals(other.ImplicitDefaultAnimation)) + { + // The defaults are the same. Is the list of animations the same? + OpenSim.Framework.Animation[] thisAnims = this.ToArray(); + OpenSim.Framework.Animation[] otherAnims = other.ToArray(); + if (thisAnims.Length == 0 && otherAnims.Length == 0) + return true; // the common case + if (thisAnims.Length == otherAnims.Length) + { + // Do this the hard way but since the list is usually short this won't take long. + foreach (OpenSim.Framework.Animation thisAnim in thisAnims) + { + bool found = false; + foreach (OpenSim.Framework.Animation otherAnim in otherAnims) + { + if (thisAnim.Equals(otherAnim)) + { + found = true; + break; + } + } + if (!found) + { + // If anything is not in the other list, these are not equal + return false; + } + } + // Found everything in the other list. Since lists are equal length, they must be equal. + return true; + } + } + return false; + } + // Don't know what was passed, but the base system will figure it out for me. + return base.Equals(obj); + } + + public override string ToString() + { + StringBuilder buff = new StringBuilder(); + buff.Append("dflt="); + buff.Append(DefaultAnimation.ToString()); + buff.Append(",iDflt="); + if (DefaultAnimation == ImplicitDefaultAnimation) + buff.Append("same"); + else + buff.Append(ImplicitDefaultAnimation.ToString()); + if (m_animations.Count > 0) + { + buff.Append(",anims="); + foreach (OpenSim.Framework.Animation anim in m_animations) + { + buff.Append("<"); + buff.Append(anim.ToString()); + buff.Append(">,"); + } + } + return buff.ToString(); + } } } diff --git a/OpenSim/Region/Framework/Scenes/Animation/DefaultAvatarAnimations.cs b/OpenSim/Region/Framework/Scenes/Animation/DefaultAvatarAnimations.cs index c2b0468067..b79dd8f3da 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/DefaultAvatarAnimations.cs +++ b/OpenSim/Region/Framework/Scenes/Animation/DefaultAvatarAnimations.cs @@ -104,5 +104,31 @@ namespace OpenSim.Region.Framework.Scenes.Animation return UUID.Zero; } + + /// + /// Get the name of the animation given a UUID. If there is no matching animation + /// return the UUID as a string. + /// + public static string GetDefaultAnimationName(UUID uuid) + { + string ret = "unknown"; + if (AnimsUUID.ContainsValue(uuid)) + { + foreach (KeyValuePair kvp in AnimsUUID) + { + if (kvp.Value == uuid) + { + ret = kvp.Key; + break; + } + } + } + else + { + ret = uuid.ToString(); + } + + return ret; + } } } \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs b/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs index 65c279ed93..eb70eeedb6 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs +++ b/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs @@ -93,7 +93,9 @@ namespace OpenSim.Region.Framework.Scenes.Animation GetAnimName(animID), animID, m_scenePresence.Name); if (m_animations.Add(animID, m_scenePresence.ControllingClient.NextAnimationSequenceNumber, objectID)) + { SendAnimPack(); + } } // Called from scripts @@ -132,7 +134,9 @@ namespace OpenSim.Region.Framework.Scenes.Animation GetAnimName(animID), animID, m_scenePresence.Name); if (m_animations.Remove(animID, allowNoDefault)) + { SendAnimPack(); + } } public void avnChangeAnim(UUID animID, bool addRemove, bool sendPack) @@ -180,8 +184,10 @@ namespace OpenSim.Region.Framework.Scenes.Animation /// The movement animation is reserved for "main" animations /// that are mutually exclusive, e.g. flying and sitting. /// - public void TrySetMovementAnimation(string anim) + /// 'true' if the animation was updated + public bool TrySetMovementAnimation(string anim) { + bool ret = false; if (!m_scenePresence.IsChildAgent) { // m_log.DebugFormat( @@ -198,6 +204,7 @@ namespace OpenSim.Region.Framework.Scenes.Animation // 16384 is CHANGED_ANIMATION m_scenePresence.SendScriptEventToAttachments("changed", new Object[] { (int)Changed.ANIMATION}); SendAnimPack(); + ret = true; } } else @@ -206,6 +213,7 @@ namespace OpenSim.Region.Framework.Scenes.Animation "[SCENE PRESENCE ANIMATOR]: Tried to set movement animation {0} on child presence {1}", anim, m_scenePresence.Name); } + return ret; } /// @@ -439,8 +447,10 @@ namespace OpenSim.Region.Framework.Scenes.Animation /// /// Update the movement animation of this avatar according to its current state /// - public void UpdateMovementAnimations() + /// 'true' if the animation was changed + public bool UpdateMovementAnimations() { + bool ret = false; lock (m_animations) { string newMovementAnimation = DetermineMovementAnimation(); @@ -454,9 +464,10 @@ namespace OpenSim.Region.Framework.Scenes.Animation // Only set it if it's actually changed, give a script // a chance to stop a default animation - TrySetMovementAnimation(CurrentMovementAnimation); + ret = TrySetMovementAnimation(CurrentMovementAnimation); } } + return ret; } public UUID[] GetAnimationArray() diff --git a/OpenSim/Region/Framework/Scenes/EventManager.cs b/OpenSim/Region/Framework/Scenes/EventManager.cs index 47335471cf..4fec44fb7b 100644 --- a/OpenSim/Region/Framework/Scenes/EventManager.cs +++ b/OpenSim/Region/Framework/Scenes/EventManager.cs @@ -974,6 +974,8 @@ namespace OpenSim.Region.Framework.Scenes public delegate void RegionStarted(Scene scene); public event RegionStarted OnRegionStarted; + public delegate void RegionHeartbeatStart(Scene scene); + public event RegionHeartbeatStart OnRegionHeartbeatStart; public delegate void RegionHeartbeatEnd(Scene scene); public event RegionHeartbeatEnd OnRegionHeartbeatEnd; @@ -3096,6 +3098,27 @@ namespace OpenSim.Region.Framework.Scenes } } + public void TriggerRegionHeartbeatStart(Scene scene) + { + RegionHeartbeatStart handler = OnRegionHeartbeatStart; + + if (handler != null) + { + foreach (RegionHeartbeatStart d in handler.GetInvocationList()) + { + try + { + d(scene); + } + catch (Exception e) + { + m_log.ErrorFormat("[EVENT MANAGER]: Delegate for OnRegionHeartbeatStart failed - continuing {0} - {1}", + e.Message, e.StackTrace); + } + } + } + } + public void TriggerRegionHeartbeatEnd(Scene scene) { RegionHeartbeatEnd handler = OnRegionHeartbeatEnd; diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 6bac468adf..6ef83fbf4c 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1569,6 +1569,8 @@ namespace OpenSim.Region.Framework.Scenes try { + EventManager.TriggerRegionHeartbeatStart(this); + // Apply taints in terrain module to terrain in physics scene if (Frame % m_update_terrain == 0) { diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 83bd3fb993..1859cb1435 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -2310,6 +2310,7 @@ namespace OpenSim.Region.Framework.Scenes AddToPhysicalScene(false); Animator.TrySetMovementAnimation("STAND"); + TriggerScenePresenceUpdated(); } private SceneObjectPart FindNextAvailableSitTarget(UUID targetID) @@ -2408,7 +2409,7 @@ namespace OpenSim.Region.Framework.Scenes ControllingClient.SendSitResponse( part.UUID, offset, sitOrientation, false, cameraAtOffset, cameraEyeOffset, forceMouselook); - m_requestedSitTargetUUID = targetID; + m_requestedSitTargetUUID = part.UUID; HandleAgentSit(ControllingClient, UUID); @@ -2436,7 +2437,7 @@ namespace OpenSim.Region.Framework.Scenes if (part != null) { m_requestedSitTargetID = part.LocalId; - m_requestedSitTargetUUID = targetID; + m_requestedSitTargetUUID = part.UUID; } else @@ -2635,6 +2636,7 @@ namespace OpenSim.Region.Framework.Scenes } Animator.TrySetMovementAnimation(sitAnimation); SendAvatarDataToAllAgents(); + TriggerScenePresenceUpdated(); } } @@ -2643,6 +2645,7 @@ namespace OpenSim.Region.Framework.Scenes // m_updateCount = 0; // Kill animation update burst so that the SIT_G.. will stick.. m_AngularVelocity = Vector3.Zero; Animator.TrySetMovementAnimation("SIT_GROUND_CONSTRAINED"); + TriggerScenePresenceUpdated(); SitGround = true; RemoveFromPhysicalScene(); } @@ -2659,11 +2662,13 @@ namespace OpenSim.Region.Framework.Scenes public void HandleStartAnim(IClientAPI remoteClient, UUID animID) { Animator.AddAnimation(animID, UUID.Zero); + TriggerScenePresenceUpdated(); } public void HandleStopAnim(IClientAPI remoteClient, UUID animID) { Animator.RemoveAnimation(animID, false); + TriggerScenePresenceUpdated(); } public void avnHandleChangeAnim(UUID animID, bool addRemove,bool sendPack) @@ -3693,7 +3698,8 @@ namespace OpenSim.Region.Framework.Scenes // if (m_updateCount > 0) // { - Animator.UpdateMovementAnimations(); + if (Animator.UpdateMovementAnimations()) + TriggerScenePresenceUpdated(); // m_updateCount--; // } diff --git a/OpenSim/Region/OptionalModules/Avatar/Voice/VivoxVoice/VivoxVoiceModule.cs b/OpenSim/Region/OptionalModules/Avatar/Voice/VivoxVoice/VivoxVoiceModule.cs index 20affa60af..2f07c42a14 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Voice/VivoxVoice/VivoxVoiceModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Voice/VivoxVoice/VivoxVoiceModule.cs @@ -841,7 +841,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.VivoxVoice requrl = String.Format("{0}&chan_roll_off={1}", requrl, m_vivoxChannelRollOff); requrl = String.Format("{0}&chan_dist_model={1}", requrl, m_vivoxChannelDistanceModel); requrl = String.Format("{0}&chan_max_range={1}", requrl, m_vivoxChannelMaximumRange); - requrl = String.Format("{0}&chan_ckamping_distance={1}", requrl, m_vivoxChannelClampingDistance); + requrl = String.Format("{0}&chan_clamping_distance={1}", requrl, m_vivoxChannelClampingDistance); XmlElement resp = VivoxCall(requrl, true); if (XmlFind(resp, "response.level0.body.chan_uri", out channelUri)) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs index 2651e3b5f6..afd547a088 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs @@ -54,6 +54,9 @@ public static class BSParam // =================== // From: + public static bool UseSeparatePhysicsThread { get; private set; } + public static float PhysicsTimeStep { get; private set; } + // Level of Detail values kept as float because that's what the Meshmerizer wants public static float MeshLOD { get; private set; } public static float MeshCircularLOD { get; private set; } @@ -354,6 +357,11 @@ public static class BSParam // v = value (appropriate type) private static ParameterDefnBase[] ParameterDefinitions = { + new ParameterDefn("UseSeparatePhysicsThread", "If 'true', the physics engine runs independent from the simulator heartbeat", + false ), + new ParameterDefn("PhysicsTimeStep", "If separate thread, seconds to simulate each interval", + 0.1f ), + new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", true, (s) => { return ShouldMeshSculptedPrim; }, diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index e11e365b2d..95bdc7b32f 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -1513,7 +1513,8 @@ public class BSPrim : BSPhysObject CurrentEntityProperties = entprop; // Note that BSPrim can be overloaded by BSPrimLinkable which controls updates from root and children prims. - base.RequestPhysicsterseUpdate(); + + PhysScene.PostUpdate(this); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 39f5b0a93b..423c38914e 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -56,12 +56,23 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters public string BulletEngineName { get; private set; } public BSAPITemplate PE; + // If the physics engine is running on a separate thread + public Thread m_physicsThread; + public Dictionary PhysObjects; public BSShapeCollection Shapes; // Keeping track of the objects with collisions so we can report begin and end of a collision public HashSet ObjectsWithCollisions = new HashSet(); public HashSet ObjectsWithNoMoreCollisions = new HashSet(); + + // All the collision processing is protected with this lock object + public Object CollisionLock = new Object(); + + // Properties are updated here + public Object UpdateLock = new Object(); + public HashSet ObjectsWithUpdates = new HashSet(); + // Keep track of all the avatars so we can send them a collision event // every tick so OpenSim will update its animation. private HashSet m_avatars = new HashSet(); @@ -77,12 +88,19 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters public BSConstraintCollection Constraints { get; private set; } // Simulation parameters + internal float m_physicsStepTime; // if running independently, the interval simulated by default + internal int m_maxSubSteps; internal float m_fixedTimeStep; - internal long m_simulationStep = 0; - internal float NominalFrameRate { get; set; } + + internal float m_simulatedTime; // the time simulated previously. Used for physics framerate calc. + + internal long m_simulationStep = 0; // The current simulation step. public long SimulationStep { get { return m_simulationStep; } } - internal float LastTimeStep { get; private set; } + + internal float LastTimeStep { get; private set; } // The simulation time from the last invocation of Simulate() + + internal float NominalFrameRate { get; set; } // Parameterized ideal frame rate that simulation is scaled to // Physical objects can register for prestep or poststep events public delegate void PreStepAction(float timeStep); @@ -90,7 +108,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters public event PreStepAction BeforeStep; public event PostStepAction AfterStep; - // A value of the time now so all the collision and update routines do not have to get their own + // A value of the time 'now' so all the collision and update routines do not have to get their own // Set to 'now' just before all the prims and actors are called for collisions and updates public int SimulationNowTime { get; private set; } @@ -188,6 +206,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters PhysObjects = new Dictionary(); Shapes = new BSShapeCollection(this); + m_simulatedTime = 0f; + LastTimeStep = 0.1f; + // Allocate pinned memory to pass parameters. UnmanagedParams = new ConfigurationParameters[1]; @@ -227,10 +248,20 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters TerrainManager = new BSTerrainManager(this); TerrainManager.CreateInitialGroundPlaneAndTerrain(); + // Put some informational messages into the log file. m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation); InTaintTime = false; m_initialized = true; + + // If the physics engine runs on its own thread, start same. + if (BSParam.UseSeparatePhysicsThread) + { + // The physics simulation should happen independently of the heartbeat loop + m_physicsThread = new Thread(BulletSPluginPhysicsThread); + m_physicsThread.Name = BulletEngineName; + m_physicsThread.Start(); + } } // All default parameter values are set here. There should be no values set in the @@ -270,6 +301,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters } else { + // Nothing in the configuration INI file so assume unmanaged and other defaults. BulletEngineName = "BulletUnmanaged"; m_physicsLoggingEnabled = false; VehicleLoggingEnabled = false; @@ -317,6 +349,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters switch (selectionName) { + case "bullet": case "bulletunmanaged": ret = new BSAPIUnman(engineName, this); break; @@ -494,25 +527,41 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters #endregion // Prim and Avatar addition and removal #region Simulation - // Simulate one timestep + + // Call from the simulator to send physics information to the simulator objects. + // This pushes all the collision and property update events into the objects in + // the simulator and, since it is on the heartbeat thread, there is an implicit + // locking of those data structures from other heartbeat events. + // If the physics engine is running on a separate thread, the update information + // will be in the ObjectsWithCollions and ObjectsWithUpdates structures. public override float Simulate(float timeStep) + { + if (!BSParam.UseSeparatePhysicsThread) + { + DoPhysicsStep(timeStep); + } + return SendUpdatesToSimulator(timeStep); + } + + // Call the physics engine to do one 'timeStep' and collect collisions and updates + // into ObjectsWithCollisions and ObjectsWithUpdates data structures. + private void DoPhysicsStep(float timeStep) { // prevent simulation until we've been initialized - if (!m_initialized) return 5.0f; + if (!m_initialized) return; LastTimeStep = timeStep; int updatedEntityCount = 0; int collidersCount = 0; - int beforeTime = 0; + int beforeTime = Util.EnvironmentTickCount(); int simTime = 0; - // update the prim states while we know the physics engine is not busy int numTaints = _taintOperations.Count; - InTaintTime = true; // Only used for debugging so locking is not necessary. + // update the prim states while we know the physics engine is not busy ProcessTaints(); // Some of the physical objects requre individual, pre-step calls @@ -535,18 +584,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters int numSubSteps = 0; try { - if (PhysicsLogging.Enabled) - beforeTime = Util.EnvironmentTickCount(); - numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount); - if (PhysicsLogging.Enabled) - { - simTime = Util.EnvironmentTickCountSubtract(beforeTime); - DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}", - DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, - updatedEntityCount, collidersCount, ObjectsWithCollisions.Count); - } } catch (Exception e) { @@ -558,77 +597,62 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters collidersCount = 0; } + // Make the physics engine dump useful statistics periodically if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0)) PE.DumpPhysicsStatistics(World); // Get a value for 'now' so all the collision and update routines don't have to get their own. SimulationNowTime = Util.EnvironmentTickCount(); - // If there were collisions, process them by sending the event to the prim. - // Collisions must be processed before updates. - if (collidersCount > 0) + // Send collision information to the colliding objects. The objects decide if the collision + // is 'real' (like linksets don't collide with themselves) and the individual objects + // know if the simulator has subscribed to collisions. + lock (CollisionLock) { - for (int ii = 0; ii < collidersCount; ii++) + if (collidersCount > 0) { - uint cA = m_collisionArray[ii].aID; - uint cB = m_collisionArray[ii].bID; - Vector3 point = m_collisionArray[ii].point; - Vector3 normal = m_collisionArray[ii].normal; - float penetration = m_collisionArray[ii].penetration; - SendCollision(cA, cB, point, normal, penetration); - SendCollision(cB, cA, point, -normal, penetration); - } - } - - // The above SendCollision's batch up the collisions on the objects. - // Now push the collisions into the simulator. - if (ObjectsWithCollisions.Count > 0) - { - foreach (BSPhysObject bsp in ObjectsWithCollisions) - if (!bsp.SendCollisions()) + for (int ii = 0; ii < collidersCount; ii++) { - // If the object is done colliding, see that it's removed from the colliding list - ObjectsWithNoMoreCollisions.Add(bsp); - } - } - - // This is a kludge to get avatar movement updates. - // The simulator expects collisions for avatars even if there are have been no collisions. - // The event updates avatar animations and stuff. - // If you fix avatar animation updates, remove this overhead and let normal collision processing happen. - foreach (BSPhysObject bsp in m_avatars) - if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice - bsp.SendCollisions(); - - // Objects that are done colliding are removed from the ObjectsWithCollisions list. - // Not done above because it is inside an iteration of ObjectWithCollisions. - // This complex collision processing is required to create an empty collision - // event call after all real collisions have happened on an object. This enables - // the simulator to generate the 'collision end' event. - if (ObjectsWithNoMoreCollisions.Count > 0) - { - foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) - ObjectsWithCollisions.Remove(po); - ObjectsWithNoMoreCollisions.Clear(); - } - // Done with collisions. - - // If any of the objects had updated properties, tell the object it has been changed by the physics engine - if (updatedEntityCount > 0) - { - for (int ii = 0; ii < updatedEntityCount; ii++) - { - EntityProperties entprop = m_updateArray[ii]; - BSPhysObject pobj; - if (PhysObjects.TryGetValue(entprop.ID, out pobj)) - { - pobj.UpdateProperties(entprop); + uint cA = m_collisionArray[ii].aID; + uint cB = m_collisionArray[ii].bID; + Vector3 point = m_collisionArray[ii].point; + Vector3 normal = m_collisionArray[ii].normal; + float penetration = m_collisionArray[ii].penetration; + SendCollision(cA, cB, point, normal, penetration); + SendCollision(cB, cA, point, -normal, penetration); } } } + // If any of the objects had updated properties, tell the managed objects about the update + // and remember that there was a change so it will be passed to the simulator. + lock (UpdateLock) + { + if (updatedEntityCount > 0) + { + for (int ii = 0; ii < updatedEntityCount; ii++) + { + EntityProperties entprop = m_updateArray[ii]; + BSPhysObject pobj; + if (PhysObjects.TryGetValue(entprop.ID, out pobj)) + { + pobj.UpdateProperties(entprop); + } + } + } + } + + // Some actors want to know when the simulation step is complete. TriggerPostStepEvent(timeStep); + simTime = Util.EnvironmentTickCountSubtract(beforeTime); + if (PhysicsLogging.Enabled) + { + DetailLog("{0},DoPhysicsStep,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}", + DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, + updatedEntityCount, collidersCount, ObjectsWithCollisions.Count); + } + // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. // Only enable this in a limited test world with few objects. if (m_physicsPhysicalDumpEnabled) @@ -637,7 +661,84 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters // The physics engine returns the number of milliseconds it simulated this call. // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55). - return (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate; + m_simulatedTime += (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate; + } + + // Called by a BSPhysObject to note that it has changed properties and this information + // should be passed up to the simulator at the proper time. + // Note: this is called by the BSPhysObject from invocation via DoPhysicsStep() above so + // this is is under UpdateLock. + public void PostUpdate(BSPhysObject updatee) + { + ObjectsWithUpdates.Add(updatee); + } + + // The simulator thinks it is physics time so return all the collisions and position + // updates that were collected in actual physics simulation. + private float SendUpdatesToSimulator(float timeStep) + { + if (!m_initialized) return 5.0f; + + DetailLog("{0},SendUpdatesToSimulator,collisions={1},updates={2},simedTime={3}", + BSScene.DetailLogZero, ObjectsWithCollisions.Count, ObjectsWithUpdates.Count, m_simulatedTime); + // Push the collisions into the simulator. + lock (CollisionLock) + { + if (ObjectsWithCollisions.Count > 0) + { + foreach (BSPhysObject bsp in ObjectsWithCollisions) + if (!bsp.SendCollisions()) + { + // If the object is done colliding, see that it's removed from the colliding list + ObjectsWithNoMoreCollisions.Add(bsp); + } + } + + // This is a kludge to get avatar movement updates. + // The simulator expects collisions for avatars even if there are have been no collisions. + // The event updates avatar animations and stuff. + // If you fix avatar animation updates, remove this overhead and let normal collision processing happen. + foreach (BSPhysObject bsp in m_avatars) + if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice + bsp.SendCollisions(); + + // Objects that are done colliding are removed from the ObjectsWithCollisions list. + // Not done above because it is inside an iteration of ObjectWithCollisions. + // This complex collision processing is required to create an empty collision + // event call after all real collisions have happened on an object. This allows + // the simulator to generate the 'collision end' event. + if (ObjectsWithNoMoreCollisions.Count > 0) + { + foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) + ObjectsWithCollisions.Remove(po); + ObjectsWithNoMoreCollisions.Clear(); + } + } + + // Call the simulator for each object that has physics property updates. + HashSet updatedObjects = null; + lock (UpdateLock) + { + if (ObjectsWithUpdates.Count > 0) + { + updatedObjects = ObjectsWithUpdates; + ObjectsWithUpdates = new HashSet(); + } + } + if (updatedObjects != null) + { + foreach (BSPhysObject obj in updatedObjects) + { + obj.RequestPhysicsterseUpdate(); + } + updatedObjects.Clear(); + } + + // Return the framerate simulated to give the above returned results. + // (Race condition here but this is just bookkeeping so rare mistakes do not merit a lock). + float simTime = m_simulatedTime; + m_simulatedTime = 0f; + return simTime; } // Something has collided @@ -656,7 +757,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters return; } - // The terrain is not in the physical object list so 'collidee' can be null when Collide() is called. + // Note: the terrain is not in the physical object list so 'collidee' can be null when Collide() is called. BSPhysObject collidee = null; PhysObjects.TryGetValue(collidingWith, out collidee); @@ -664,13 +765,39 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration)) { - // If a collision was posted, remember to send it to the simulator + // If a collision was 'good', remember to send it to the simulator ObjectsWithCollisions.Add(collider); } return; } + public void BulletSPluginPhysicsThread() + { + while (m_initialized) + { + int beginSimulationRealtimeMS = Util.EnvironmentTickCount(); + DoPhysicsStep(BSParam.PhysicsTimeStep); + int simulationRealtimeMS = Util.EnvironmentTickCountSubtract(beginSimulationRealtimeMS); + int simulationTimeVsRealtimeDifferenceMS = ((int)(BSParam.PhysicsTimeStep*1000f)) - simulationRealtimeMS; + + if (simulationTimeVsRealtimeDifferenceMS > 0) + { + // The simulation of the time interval took less than realtime. + // Do a sleep for the rest of realtime. + DetailLog("{0},BulletSPluginPhysicsThread,sleeping={1}", BSScene.DetailLogZero, simulationTimeVsRealtimeDifferenceMS); + Thread.Sleep(simulationTimeVsRealtimeDifferenceMS); + } + else + { + // The simulation took longer than realtime. + // Do some scaling of simulation time. + // TODO. + DetailLog("{0},BulletSPluginPhysicsThread,longerThanRealtime={1}", BSScene.DetailLogZero, simulationTimeVsRealtimeDifferenceMS); + } + } + } + #endregion // Simulation public override void GetResults() { } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs index 6879ebbe97..1e19032cdc 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs @@ -37,6 +37,8 @@ using OpenSim.Region.ScriptEngine.Interfaces; using OpenSim.Region.ScriptEngine.Shared; using OpenSim.Region.ScriptEngine.Shared.Api.Plugins; using Timer=OpenSim.Region.ScriptEngine.Shared.Api.Plugins.Timer; +using System.Reflection; +using log4net; namespace OpenSim.Region.ScriptEngine.Shared.Api { @@ -45,15 +47,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// public class AsyncCommandManager { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static Thread cmdHandlerThread; private static int cmdHandlerThreadCycleSleepms; - private static List m_Scenes = new List(); + /// + /// Lock for reading/writing static components of AsyncCommandManager. + /// + /// + /// This lock exists so that multiple threads from different engines and/or different copies of the same engine + /// are prevented from running non-thread safe code (e.g. read/write of lists) concurrently. + /// + private static object staticLock = new object(); + private static List m_ScriptEngines = new List(); public IScriptEngine m_ScriptEngine; - private IScene m_Scene; private static Dictionary m_Dataserver = new Dictionary(); @@ -70,67 +81,99 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public Dataserver DataserverPlugin { - get { return m_Dataserver[m_ScriptEngine]; } + get + { + lock (staticLock) + return m_Dataserver[m_ScriptEngine]; + } } public Timer TimerPlugin { - get { return m_Timer[m_ScriptEngine]; } + get + { + lock (staticLock) + return m_Timer[m_ScriptEngine]; + } } public HttpRequest HttpRequestPlugin { - get { return m_HttpRequest[m_ScriptEngine]; } + get + { + lock (staticLock) + return m_HttpRequest[m_ScriptEngine]; + } } public Listener ListenerPlugin { - get { return m_Listener[m_ScriptEngine]; } + get + { + lock (staticLock) + return m_Listener[m_ScriptEngine]; + } } public SensorRepeat SensorRepeatPlugin { - get { return m_SensorRepeat[m_ScriptEngine]; } + get + { + lock (staticLock) + return m_SensorRepeat[m_ScriptEngine]; + } } public XmlRequest XmlRequestPlugin { - get { return m_XmlRequest[m_ScriptEngine]; } + get + { + lock (staticLock) + return m_XmlRequest[m_ScriptEngine]; + } } public IScriptEngine[] ScriptEngines { - get { return m_ScriptEngines.ToArray(); } + get + { + lock (staticLock) + return m_ScriptEngines.ToArray(); + } } public AsyncCommandManager(IScriptEngine _ScriptEngine) { m_ScriptEngine = _ScriptEngine; - m_Scene = m_ScriptEngine.World; - if (m_Scenes.Count == 0) - ReadConfig(); + // If there is more than one scene in the simulator or multiple script engines are used on the same region + // then more than one thread could arrive at this block of code simultaneously. However, it cannot be + // executed concurrently both because concurrent list operations are not thread-safe and because of other + // race conditions such as the later check of cmdHandlerThread == null. + lock (staticLock) + { + if (m_ScriptEngines.Count == 0) + ReadConfig(); - if (!m_Scenes.Contains(m_Scene)) - m_Scenes.Add(m_Scene); - if (!m_ScriptEngines.Contains(m_ScriptEngine)) - m_ScriptEngines.Add(m_ScriptEngine); + if (!m_ScriptEngines.Contains(m_ScriptEngine)) + m_ScriptEngines.Add(m_ScriptEngine); - // Create instances of all plugins - if (!m_Dataserver.ContainsKey(m_ScriptEngine)) - m_Dataserver[m_ScriptEngine] = new Dataserver(this); - if (!m_Timer.ContainsKey(m_ScriptEngine)) - m_Timer[m_ScriptEngine] = new Timer(this); - if (!m_HttpRequest.ContainsKey(m_ScriptEngine)) - m_HttpRequest[m_ScriptEngine] = new HttpRequest(this); - if (!m_Listener.ContainsKey(m_ScriptEngine)) - m_Listener[m_ScriptEngine] = new Listener(this); - if (!m_SensorRepeat.ContainsKey(m_ScriptEngine)) - m_SensorRepeat[m_ScriptEngine] = new SensorRepeat(this); - if (!m_XmlRequest.ContainsKey(m_ScriptEngine)) - m_XmlRequest[m_ScriptEngine] = new XmlRequest(this); + // Create instances of all plugins + if (!m_Dataserver.ContainsKey(m_ScriptEngine)) + m_Dataserver[m_ScriptEngine] = new Dataserver(this); + if (!m_Timer.ContainsKey(m_ScriptEngine)) + m_Timer[m_ScriptEngine] = new Timer(this); + if (!m_HttpRequest.ContainsKey(m_ScriptEngine)) + m_HttpRequest[m_ScriptEngine] = new HttpRequest(this); + if (!m_Listener.ContainsKey(m_ScriptEngine)) + m_Listener[m_ScriptEngine] = new Listener(this); + if (!m_SensorRepeat.ContainsKey(m_ScriptEngine)) + m_SensorRepeat[m_ScriptEngine] = new SensorRepeat(this); + if (!m_XmlRequest.ContainsKey(m_ScriptEngine)) + m_XmlRequest[m_ScriptEngine] = new XmlRequest(this); - StartThread(); + StartThread(); + } } private static void StartThread() @@ -179,42 +222,43 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { try { - while (true) - { - Thread.Sleep(cmdHandlerThreadCycleSleepms); + Thread.Sleep(cmdHandlerThreadCycleSleepms); - DoOneCmdHandlerPass(); + DoOneCmdHandlerPass(); - Watchdog.UpdateThread(); - } + Watchdog.UpdateThread(); } - catch + catch (Exception e) { + m_log.Error("[ASYNC COMMAND MANAGER]: Exception in command handler pass: ", e); } } } private static void DoOneCmdHandlerPass() { - // Check HttpRequests - m_HttpRequest[m_ScriptEngines[0]].CheckHttpRequests(); - - // Check XMLRPCRequests - m_XmlRequest[m_ScriptEngines[0]].CheckXMLRPCRequests(); - - foreach (IScriptEngine s in m_ScriptEngines) + lock (staticLock) { - // Check Listeners - m_Listener[s].CheckListeners(); + // Check HttpRequests + m_HttpRequest[m_ScriptEngines[0]].CheckHttpRequests(); - // Check timers - m_Timer[s].CheckTimerEvents(); + // Check XMLRPCRequests + m_XmlRequest[m_ScriptEngines[0]].CheckXMLRPCRequests(); - // Check Sensors - m_SensorRepeat[s].CheckSenseRepeaterEvents(); + foreach (IScriptEngine s in m_ScriptEngines) + { + // Check Listeners + m_Listener[s].CheckListeners(); - // Check dataserver - m_Dataserver[s].ExpireRequests(); + // Check timers + m_Timer[s].CheckTimerEvents(); + + // Check Sensors + m_SensorRepeat[s].CheckSenseRepeaterEvents(); + + // Check dataserver + m_Dataserver[s].ExpireRequests(); + } } } @@ -226,31 +270,35 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public static void RemoveScript(IScriptEngine engine, uint localID, UUID itemID) { // Remove a specific script +// m_log.DebugFormat("[ASYNC COMMAND MANAGER]: Removing facilities for script {0}", itemID); - // Remove dataserver events - m_Dataserver[engine].RemoveEvents(localID, itemID); - - // Remove from: Timers - m_Timer[engine].UnSetTimerEvents(localID, itemID); - - // Remove from: HttpRequest - IHttpRequestModule iHttpReq = engine.World.RequestModuleInterface(); - if (iHttpReq != null) - iHttpReq.StopHttpRequest(localID, itemID); - - IWorldComm comms = engine.World.RequestModuleInterface(); - if (comms != null) - comms.DeleteListener(itemID); - - IXMLRPC xmlrpc = engine.World.RequestModuleInterface(); - if (xmlrpc != null) + lock (staticLock) { - xmlrpc.DeleteChannels(itemID); - xmlrpc.CancelSRDRequests(itemID); - } + // Remove dataserver events + m_Dataserver[engine].RemoveEvents(localID, itemID); - // Remove Sensors - m_SensorRepeat[engine].UnSetSenseRepeaterEvents(localID, itemID); + // Remove from: Timers + m_Timer[engine].UnSetTimerEvents(localID, itemID); + + // Remove from: HttpRequest + IHttpRequestModule iHttpReq = engine.World.RequestModuleInterface(); + if (iHttpReq != null) + iHttpReq.StopHttpRequest(localID, itemID); + + IWorldComm comms = engine.World.RequestModuleInterface(); + if (comms != null) + comms.DeleteListener(itemID); + + IXMLRPC xmlrpc = engine.World.RequestModuleInterface(); + if (xmlrpc != null) + { + xmlrpc.DeleteChannels(itemID); + xmlrpc.CancelSRDRequests(itemID); + } + + // Remove Sensors + m_SensorRepeat[engine].UnSetSenseRepeaterEvents(localID, itemID); + } } /// @@ -260,10 +308,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// public static SensorRepeat GetSensorRepeatPlugin(IScriptEngine engine) { - if (m_SensorRepeat.ContainsKey(engine)) - return m_SensorRepeat[engine]; - else - return null; + lock (staticLock) + { + if (m_SensorRepeat.ContainsKey(engine)) + return m_SensorRepeat[engine]; + else + return null; + } } /// @@ -273,10 +324,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// public static Dataserver GetDataserverPlugin(IScriptEngine engine) { - if (m_Dataserver.ContainsKey(engine)) - return m_Dataserver[engine]; - else - return null; + lock (staticLock) + { + if (m_Dataserver.ContainsKey(engine)) + return m_Dataserver[engine]; + else + return null; + } } /// @@ -286,10 +340,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// public static Timer GetTimerPlugin(IScriptEngine engine) { - if (m_Timer.ContainsKey(engine)) - return m_Timer[engine]; - else - return null; + lock (staticLock) + { + if (m_Timer.ContainsKey(engine)) + return m_Timer[engine]; + else + return null; + } } /// @@ -299,10 +356,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// public static Listener GetListenerPlugin(IScriptEngine engine) { - if (m_Listener.ContainsKey(engine)) - return m_Listener[engine]; - else - return null; + lock (staticLock) + { + if (m_Listener.ContainsKey(engine)) + return m_Listener[engine]; + else + return null; + } } public static void StateChange(IScriptEngine engine, uint localID, UUID itemID) @@ -332,28 +392,31 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { List data = new List(); - Object[] listeners = m_Listener[engine].GetSerializationData(itemID); - if (listeners.Length > 0) + lock (staticLock) { - data.Add("listener"); - data.Add(listeners.Length); - data.AddRange(listeners); - } + Object[] listeners = m_Listener[engine].GetSerializationData(itemID); + if (listeners.Length > 0) + { + data.Add("listener"); + data.Add(listeners.Length); + data.AddRange(listeners); + } - Object[] timers=m_Timer[engine].GetSerializationData(itemID); - if (timers.Length > 0) - { - data.Add("timer"); - data.Add(timers.Length); - data.AddRange(timers); - } + Object[] timers=m_Timer[engine].GetSerializationData(itemID); + if (timers.Length > 0) + { + data.Add("timer"); + data.Add(timers.Length); + data.AddRange(timers); + } - Object[] sensors = m_SensorRepeat[engine].GetSerializationData(itemID); - if (sensors.Length > 0) - { - data.Add("sensor"); - data.Add(sensors.Length); - data.AddRange(sensors); + Object[] sensors = m_SensorRepeat[engine].GetSerializationData(itemID); + if (sensors.Length > 0) + { + data.Add("sensor"); + data.Add(sensors.Length); + data.AddRange(sensors); + } } return data.ToArray(); @@ -378,41 +441,26 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api idx+=len; + lock (staticLock) + { switch (type) { - case "listener": - m_Listener[engine].CreateFromData(localID, itemID, - hostID, item); - break; - case "timer": - m_Timer[engine].CreateFromData(localID, itemID, - hostID, item); - break; - case "sensor": - m_SensorRepeat[engine].CreateFromData(localID, - itemID, hostID, item); - break; + case "listener": + m_Listener[engine].CreateFromData(localID, itemID, + hostID, item); + break; + case "timer": + m_Timer[engine].CreateFromData(localID, itemID, + hostID, item); + break; + case "sensor": + m_SensorRepeat[engine].CreateFromData(localID, + itemID, hostID, item); + break; + } } } } } - - #region Check llRemoteData channels - - #endregion - - #region Check llListeners - - #endregion - - /// - /// If set to true then threads and stuff should try to make a graceful exit - /// - public bool PleaseShutdown - { - get { return _PleaseShutdown; } - set { _PleaseShutdown = value; } - } - private bool _PleaseShutdown = false; } } diff --git a/OpenSim/Server/Handlers/Profiles/UserProfilesConnector.cs b/OpenSim/Server/Handlers/Profiles/UserProfilesConnector.cs new file mode 100644 index 0000000000..5a24ee323e --- /dev/null +++ b/OpenSim/Server/Handlers/Profiles/UserProfilesConnector.cs @@ -0,0 +1,119 @@ +/* + * 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.Reflection; +using Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Framework; +using OpenSim.Server.Handlers.Base; +using log4net; + +namespace OpenSim.Server.Handlers.Profiles +{ + public class UserProfilesConnector: ServiceConnector + { + static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + + // Our Local Module + public IUserProfilesService ServiceModule + { + get; private set; + } + + // The HTTP server. + public IHttpServer Server + { + get; private set; + } + + public string ConfigName + { + get; private set; + } + + public bool Enabled + { + get; private set; + } + + public UserProfilesConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + ConfigName = "UserProfilesService"; + if(!string.IsNullOrEmpty(configName)) + ConfigName = configName; + + IConfig serverConfig = config.Configs[ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", ConfigName)); + + if(!serverConfig.GetBoolean("Enabled",false)) + { + Enabled = false; + return; + } + + Enabled = true; + + Server = server; + + string service = serverConfig.GetString("LocalServiceModule", String.Empty); + + Object[] args = new Object[] { config, ConfigName }; + ServiceModule = ServerUtils.LoadPlugin(service, args); + + JsonRpcProfileHandlers handler = new JsonRpcProfileHandlers(ServiceModule); + + Server.AddJsonRPCHandler("avatarclassifiedsrequest", handler.AvatarClassifiedsRequest); + Server.AddJsonRPCHandler("classified_update", handler.ClassifiedUpdate); + Server.AddJsonRPCHandler("classifieds_info_query", handler.ClassifiedInfoRequest); + Server.AddJsonRPCHandler("classified_delete", handler.ClassifiedDelete); + Server.AddJsonRPCHandler("avatarpicksrequest", handler.AvatarPicksRequest); + Server.AddJsonRPCHandler("pickinforequest", handler.PickInfoRequest); + Server.AddJsonRPCHandler("picks_update", handler.PicksUpdate); + Server.AddJsonRPCHandler("picks_delete", handler.PicksDelete); + Server.AddJsonRPCHandler("avatarnotesrequest", handler.AvatarNotesRequest); + Server.AddJsonRPCHandler("avatar_notes_update", handler.NotesUpdate); + Server.AddJsonRPCHandler("avatar_properties_request", handler.AvatarPropertiesRequest); + Server.AddJsonRPCHandler("avatar_properties_update", handler.AvatarPropertiesUpdate); + Server.AddJsonRPCHandler("avatar_interests_update", handler.AvatarInterestsUpdate); + Server.AddJsonRPCHandler("image_assets_request", handler.AvatarImageAssetsRequest); +// Server.AddJsonRPCHandler("user_preferences_request", handler.UserPreferencesRequest); +// Server.AddJsonRPCHandler("user_preferences_update", handler.UserPreferencesUpdate); +// Server.AddJsonRPCHandler("user_account_create", handler.UserAccountCreate); +// Server.AddJsonRPCHandler("user_account_auth", handler.UserAccountAuth); +// Server.AddJsonRPCHandler("user_account_test", handler.UserAccountTest); + Server.AddJsonRPCHandler("user_data_request", handler.RequestUserAppData); + Server.AddJsonRPCHandler("user_data_update", handler.UpdateUserAppData); + } + } +} + diff --git a/OpenSim/Server/Handlers/Profiles/UserProfilesHandlers.cs b/OpenSim/Server/Handlers/Profiles/UserProfilesHandlers.cs new file mode 100644 index 0000000000..f5f0794653 --- /dev/null +++ b/OpenSim/Server/Handlers/Profiles/UserProfilesHandlers.cs @@ -0,0 +1,461 @@ +/* + * 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.Reflection; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using log4net; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Framework; + +namespace OpenSim.Server.Handlers +{ + public class UserProfilesHandlers + { + public UserProfilesHandlers () + { + } + } + + public class JsonRpcProfileHandlers + { + static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + public IUserProfilesService Service + { + get; private set; + } + + public JsonRpcProfileHandlers(IUserProfilesService service) + { + Service = service; + } + + #region Classifieds + /// + /// Request avatar's classified ads. + /// + /// + /// An array containing all the calassified uuid and it's name created by the creator id + /// + /// + /// Our parameters are in the OSDMap json["params"] + /// + /// + /// If set to true response. + /// + public bool AvatarClassifiedsRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + m_log.DebugFormat ("Classified Request"); + return false; + } + + OSDMap request = (OSDMap)json["params"]; + UUID creatorId = new UUID(request["creatorId"].AsString()); + + + OSDArray data = (OSDArray) Service.AvatarClassifiedsRequest(creatorId); + response.Result = data; + + return true; + } + + public bool ClassifiedUpdate(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "Error parsing classified update request"; + m_log.DebugFormat ("Classified Update Request"); + return false; + } + + string result = string.Empty; + UserClassifiedAdd ad = new UserClassifiedAdd(); + object Ad = (object)ad; + OSD.DeserializeMembers(ref Ad, (OSDMap)json["params"]); + if(Service.ClassifiedUpdate(ad, ref result)) + { + response.Result = OSD.SerializeMembers(ad); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + + public bool ClassifiedDelete(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + m_log.DebugFormat ("Classified Delete Request"); + return false; + } + + OSDMap request = (OSDMap)json["params"]; + UUID classifiedId = new UUID(request["classifiedID"].AsString()); + + OSDMap res = new OSDMap(); + res["result"] = OSD.FromString("success"); + response.Result = res; + + return true; + + } + + public bool ClassifiedInfoRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("Classified Info Request"); + return false; + } + + string result = string.Empty; + UserClassifiedAdd ad = new UserClassifiedAdd(); + object Ad = (object)ad; + OSD.DeserializeMembers(ref Ad, (OSDMap)json["params"]); + if(Service.ClassifiedInfoRequest(ref ad, ref result)) + { + response.Result = OSD.SerializeMembers(ad); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + #endregion Classifieds + + #region Picks + public bool AvatarPicksRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + m_log.DebugFormat ("Avatar Picks Request"); + return false; + } + + OSDMap request = (OSDMap)json["params"]; + UUID creatorId = new UUID(request["creatorId"].AsString()); + + + OSDArray data = (OSDArray) Service.AvatarPicksRequest(creatorId); + response.Result = data; + + return true; + } + + public bool PickInfoRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("Avatar Picks Info Request"); + return false; + } + + string result = string.Empty; + UserProfilePick pick = new UserProfilePick(); + object Pick = (object)pick; + OSD.DeserializeMembers(ref Pick, (OSDMap)json["params"]); + if(Service.PickInfoRequest(ref pick, ref result)) + { + response.Result = OSD.SerializeMembers(pick); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + + public bool PicksUpdate(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("Avatar Picks Update Request"); + return false; + } + + string result = string.Empty; + UserProfilePick pick = new UserProfilePick(); + object Pick = (object)pick; + OSD.DeserializeMembers(ref Pick, (OSDMap)json["params"]); + if(Service.PicksUpdate(ref pick, ref result)) + { + response.Result = OSD.SerializeMembers(pick); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = "unable to update pick"; + + return false; + } + + public bool PicksDelete(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + m_log.DebugFormat ("Avatar Picks Delete Request"); + return false; + } + + OSDMap request = (OSDMap)json["params"]; + UUID pickId = new UUID(request["pickId"].AsString()); + if(Service.PicksDelete(pickId)) + return true; + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = "data error removing record"; + return false; + } + #endregion Picks + + #region Notes + public bool AvatarNotesRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "Params missing"; + m_log.DebugFormat ("Avatar Notes Request"); + return false; + } + + string result = string.Empty; + UserProfileNotes note = new UserProfileNotes(); + object Note = (object)note; + OSD.DeserializeMembers(ref Note, (OSDMap)json["params"]); + if(Service.AvatarNotesRequest(ref note)) + { + response.Result = OSD.SerializeMembers(note); + return true; + } + + object Notes = (object) note; + OSD.DeserializeMembers(ref Notes, (OSDMap)json["params"]); + return true; + } + + public bool NotesUpdate(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "No parameters"; + m_log.DebugFormat ("Avatar Notes Update Request"); + return false; + } + + string result = string.Empty; + UserProfileNotes note = new UserProfileNotes(); + object Notes = (object) note; + OSD.DeserializeMembers(ref Notes, (OSDMap)json["params"]); + if(Service.NotesUpdate(ref note, ref result)) + { + response.Result = OSD.SerializeMembers(note); + return true; + } + return true; + } + #endregion Notes + + #region Profile Properties + public bool AvatarPropertiesRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("Avatar Properties Request"); + return false; + } + + string result = string.Empty; + UserProfileProperties props = new UserProfileProperties(); + object Props = (object)props; + OSD.DeserializeMembers(ref Props, (OSDMap)json["params"]); + if(Service.AvatarPropertiesRequest(ref props, ref result)) + { + response.Result = OSD.SerializeMembers(props); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + + public bool AvatarPropertiesUpdate(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("Avatar Properties Update Request"); + return false; + } + + string result = string.Empty; + UserProfileProperties props = new UserProfileProperties(); + object Props = (object)props; + OSD.DeserializeMembers(ref Props, (OSDMap)json["params"]); + if(Service.AvatarPropertiesUpdate(ref props, ref result)) + { + response.Result = OSD.SerializeMembers(props); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + #endregion Profile Properties + + #region Interests + public bool AvatarInterestsUpdate(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("Avatar Interests Update Request"); + return false; + } + + string result = string.Empty; + UserProfileProperties props = new UserProfileProperties(); + object Props = (object)props; + OSD.DeserializeMembers(ref Props, (OSDMap)json["params"]); + if(Service.AvatarInterestsUpdate(props, ref result)) + { + response.Result = OSD.SerializeMembers(props); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + #endregion Interests + + #region Utility + public bool AvatarImageAssetsRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + m_log.DebugFormat ("Avatar Image Assets Request"); + return false; + } + + OSDMap request = (OSDMap)json["params"]; + UUID avatarId = new UUID(request["avatarId"].AsString()); + + OSDArray data = (OSDArray) Service.AvatarImageAssetsRequest(avatarId); + response.Result = data; + + return true; + } + #endregion Utiltiy + + #region UserData + public bool RequestUserAppData(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("User Application Service URL Request: No Parameters!"); + return false; + } + + string result = string.Empty; + UserAppData props = new UserAppData(); + object Props = (object)props; + OSD.DeserializeMembers(ref Props, (OSDMap)json["params"]); + if(Service.RequestUserAppData(ref props, ref result)) + { + OSDMap res = new OSDMap(); + res["result"] = OSD.FromString("success"); + res["token"] = OSD.FromString (result); + response.Result = res; + + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + + public bool UpdateUserAppData(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("User App Data Update Request"); + return false; + } + + string result = string.Empty; + UserAppData props = new UserAppData(); + object Props = (object)props; + OSD.DeserializeMembers(ref Props, (OSDMap)json["params"]); + if(Service.SetUserAppData(props, ref result)) + { + response.Result = OSD.SerializeMembers(props); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + #endregion UserData + } +} + diff --git a/OpenSim/Services/Interfaces/IUserProfilesService.cs b/OpenSim/Services/Interfaces/IUserProfilesService.cs new file mode 100644 index 0000000000..319d3075dd --- /dev/null +++ b/OpenSim/Services/Interfaces/IUserProfilesService.cs @@ -0,0 +1,75 @@ +/* + * 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 OpenSim.Framework; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Services.Interfaces +{ + public interface IUserProfilesService + { + #region Classifieds + OSD AvatarClassifiedsRequest(UUID creatorId); + bool ClassifiedUpdate(UserClassifiedAdd ad, ref string result); + bool ClassifiedInfoRequest(ref UserClassifiedAdd ad, ref string result); + bool ClassifiedDelete(UUID recordId); + #endregion Classifieds + + #region Picks + OSD AvatarPicksRequest(UUID creatorId); + bool PickInfoRequest(ref UserProfilePick pick, ref string result); + bool PicksUpdate(ref UserProfilePick pick, ref string result); + bool PicksDelete(UUID pickId); + #endregion Picks + + #region Notes + bool AvatarNotesRequest(ref UserProfileNotes note); + bool NotesUpdate(ref UserProfileNotes note, ref string result); + #endregion Notes + + #region Profile Properties + bool AvatarPropertiesRequest(ref UserProfileProperties prop, ref string result); + bool AvatarPropertiesUpdate(ref UserProfileProperties prop, ref string result); + #endregion Profile Properties + + #region Interests + bool AvatarInterestsUpdate(UserProfileProperties prop, ref string result); + #endregion Interests + + #region Utility + OSD AvatarImageAssetsRequest(UUID avatarId); + #endregion Utility + + #region UserData + bool RequestUserAppData(ref UserAppData prop, ref string result); + bool SetUserAppData(UserAppData prop, ref string result); + #endregion UserData + } +} + diff --git a/OpenSim/Services/UserProfilesService/UserProfilesService.cs b/OpenSim/Services/UserProfilesService/UserProfilesService.cs new file mode 100644 index 0000000000..d00f34d650 --- /dev/null +++ b/OpenSim/Services/UserProfilesService/UserProfilesService.cs @@ -0,0 +1,187 @@ +/* + * 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.Reflection; +using System.Text; +using Nini.Config; +using log4net; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Services.UserAccountService; +using OpenSim.Data; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; + +namespace OpenSim.Services.ProfilesService +{ + public class UserProfilesService: UserProfilesServiceBase, IUserProfilesService + { + static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + IUserAccountService userAccounts; + IAuthenticationService authService; + + public UserProfilesService(IConfigSource config, string configName): + base(config, configName) + { + IConfig Config = config.Configs[configName]; + if (Config == null) + { + m_log.Warn("[PROFILES]: No configuration found!"); + return; + } + Object[] args = null; + + args = new Object[] { config }; + string accountService = Config.GetString("UserAccountService", String.Empty); + if (accountService != string.Empty) + userAccounts = ServerUtils.LoadPlugin(accountService, args); + + args = new Object[] { config }; + string authServiceConfig = Config.GetString("AuthenticationServiceModule", String.Empty); + if (accountService != string.Empty) + authService = ServerUtils.LoadPlugin(authServiceConfig, args); + } + + #region Classifieds + public OSD AvatarClassifiedsRequest(UUID creatorId) + { + OSDArray records = ProfilesData.GetClassifiedRecords(creatorId); + + return records; + } + + public bool ClassifiedUpdate(UserClassifiedAdd ad, ref string result) + { + if(!ProfilesData.UpdateClassifiedRecord(ad, ref result)) + { + return false; + } + result = "success"; + return true; + } + + public bool ClassifiedDelete(UUID recordId) + { + if(ProfilesData.DeleteClassifiedRecord(recordId)) + return true; + + return false; + } + + public bool ClassifiedInfoRequest(ref UserClassifiedAdd ad, ref string result) + { + if(ProfilesData.GetClassifiedInfo(ref ad, ref result)) + return true; + + return false; + } + #endregion Classifieds + + #region Picks + public OSD AvatarPicksRequest(UUID creatorId) + { + OSDArray records = ProfilesData.GetAvatarPicks(creatorId); + + return records; + } + + public bool PickInfoRequest(ref UserProfilePick pick, ref string result) + { + pick = ProfilesData.GetPickInfo(pick.CreatorId, pick.PickId); + result = "OK"; + return true; + } + + public bool PicksUpdate(ref UserProfilePick pick, ref string result) + { + return ProfilesData.UpdatePicksRecord(pick); + } + + public bool PicksDelete(UUID pickId) + { + return ProfilesData.DeletePicksRecord(pickId); + } + #endregion Picks + + #region Notes + public bool AvatarNotesRequest(ref UserProfileNotes note) + { + return ProfilesData.GetAvatarNotes(ref note); + } + + public bool NotesUpdate(ref UserProfileNotes note, ref string result) + { + return ProfilesData.UpdateAvatarNotes(ref note, ref result); + } + #endregion Notes + + #region Profile Properties + public bool AvatarPropertiesRequest(ref UserProfileProperties prop, ref string result) + { + return ProfilesData.GetAvatarProperties(ref prop, ref result); + } + + public bool AvatarPropertiesUpdate(ref UserProfileProperties prop, ref string result) + { + return ProfilesData.UpdateAvatarProperties(ref prop, ref result); + } + #endregion Profile Properties + + #region Interests + public bool AvatarInterestsUpdate(UserProfileProperties prop, ref string result) + { + return ProfilesData.UpdateAvatarInterests(prop, ref result); + } + #endregion Interests + + #region Utility + public OSD AvatarImageAssetsRequest(UUID avatarId) + { + OSDArray records = ProfilesData.GetUserImageAssets(avatarId); + return records; + } + #endregion Utility + + #region UserData + public bool RequestUserAppData(ref UserAppData prop, ref string result) + { + return ProfilesData.GetUserAppData(ref prop, ref result); + } + + public bool SetUserAppData(UserAppData prop, ref string result) + { + return true; + } + #endregion UserData + } +} + diff --git a/OpenSim/Services/UserProfilesService/UserProfilesServiceBase.cs b/OpenSim/Services/UserProfilesService/UserProfilesServiceBase.cs new file mode 100644 index 0000000000..927f7c93e0 --- /dev/null +++ b/OpenSim/Services/UserProfilesService/UserProfilesServiceBase.cs @@ -0,0 +1,86 @@ +/* + * 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.Reflection; +using Nini.Config; +using log4net; +using OpenSim.Services.Base; +using OpenSim.Data; + +namespace OpenSim.Services.ProfilesService +{ + public class UserProfilesServiceBase: ServiceBase + { + static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + public IProfilesData ProfilesData; + + public string ConfigName + { + get; private set; + } + + public UserProfilesServiceBase(IConfigSource config, string configName): + base(config) + { + if(string.IsNullOrEmpty(configName)) + { + m_log.WarnFormat("[PROFILES]: Configuration section not given!"); + return; + } + + string dllName = String.Empty; + string connString = null; + string realm = String.Empty; + + IConfig dbConfig = config.Configs["DatabaseService"]; + if (dbConfig != null) + { + if (dllName == String.Empty) + dllName = dbConfig.GetString("StorageProvider", String.Empty); + if (string.IsNullOrEmpty(connString)) + connString = dbConfig.GetString("ConnectionString", String.Empty); + } + + IConfig ProfilesConfig = config.Configs[configName]; + if (ProfilesConfig != null) + { + connString = ProfilesConfig.GetString("ConnectionString", connString); + realm = ProfilesConfig.GetString("Realm", realm); + } + + ProfilesData = LoadPlugin(dllName, new Object[] { connString }); + if (ProfilesData == null) + throw new Exception("Could not find a storage interface in the given module"); + + } + } +} + diff --git a/README.md b/README.md index 552cdef866..118fe71d3e 100644 --- a/README.md +++ b/README.md @@ -111,3 +111,4 @@ project can always be found at http://opensimulator.org. Thanks for trying OpenSim, we hope it is a pleasant experience. + diff --git a/bin/HttpServer_OpenSim.dll b/bin/HttpServer_OpenSim.dll index e15493d338..38a4cb795c 100755 Binary files a/bin/HttpServer_OpenSim.dll and b/bin/HttpServer_OpenSim.dll differ diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index edd22aa7ac..1ea4609207 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -1028,6 +1028,12 @@ ;# {InitialTerrain} {} {Initial terrain type} {pinhead-island flat} pinhead-island ; InitialTerrain = "pinhead-island" +[UserProfiles] + ;# {ProfileURL} {} {Set url to UserProfilesService} {} + ;; Set the value of the url to your UserProfilesService + ;; If un-set / "" the module is disabled + ;; ProfileServiceURL = http://127.0.0.1:8002 + [Architecture] ;# {Include-Architecture} {} {Choose one of the following architectures} {config-include/Standalone.ini config-include/StandaloneHypergrid.ini config-include/Grid.ini config-include/GridHypergrid.ini config-include/SimianGrid.ini config-include/HyperSimianGrid.ini} config-include/Standalone.ini ;; Uncomment one of the following includes as required. For instance, to create a standalone OpenSim, diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index a5a43b1c86..3a961d654e 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -373,6 +373,19 @@ AllowRegionRestartFromClient = true +[UserProfiles] + ;# {ProfileURL} {} {Set url to UserProfilesService} {} + ;; Set the value of the url to your UserProfilesService + ;; If un-set / "" the module is disabled + ;; If the ProfileURL is not set, then very BASIC + ;; profile support will be configured. If the ProfileURL is set to a + ;; valid URL, then full profile support will be configured. The URL + ;; points to your grid's Robust user profiles service + ;; + ; ProfileURL = http://127.0.0.1:9000 + + + [SMTP] enabled = false @@ -917,56 +930,49 @@ ;force_simple_prim_meshing = true [BulletSim] - ; World parameters + ; All the BulletSim parameters can be displayed with the console command "physics get all" + ; and all are defined in the source file OpenSim/Regions/Physics/BulletSPlugin/BSParam.cs. ; There are two bullet physics libraries, bulletunmanaged is the default and is a native c++ dll - ; bulletxna is a managed C# dll. They have comparible functionality.. the c++ is much faster. - + ; bulletxna is a managed C# dll. They have comparible functionality but the c++ one is much faster. BulletEngine = "bulletunmanaged" ; BulletEngine = "bulletxna" - ; Terrain Implementation {1|0} 0 for HeightField, 1 for Mesh terrain. If you're using the bulletxna engine, - ; you will want to switch to the heightfield option - TerrainImplementation = 1 - ; TerrainImplementation = 0 + ; Terrain Implementation + TerrainImplementation = 1 ; 0=Heightfield, 1=mesh + ; For mesh terrain, the detail of the created mesh. '1' gives 256x256 (heightfield resolution). '2' + ; gives 512x512. Etc. Cannot be larger than '4'. Higher magnification uses lots of memory. + TerrainMeshMagnification = 2 - Gravity = -9.80665 + ; Avatar physics height adjustments. http://opensimulator.org/wiki/BulletSim#Adjusting_Avatar_Height + AvatarHeightLowFudge = -0.2 ; Adjustment at low end of height range + AvatarHeightMidFudge = 0.1 ; Adjustment at mid point of avatar height range + AvatarHeightHighFudge = 0.1 ; Adjustment at high end of height range - TerrainFriction = 0.30 - TerrainHitFraction = 0.8 - TerrainRestitution = 0 - TerrainCollisionMargin = 0.04 - - AvatarFriction = 0.2 - AvatarStandingFriction = 0.95 - AvatarRestitution = 0.0 - AvatarDensity = 3.5 - AvatarCapsuleWidth = 0.6 - AvatarCapsuleDepth = 0.45 - AvatarCapsuleHeight = 1.5 - AvatarContactProcessingThreshold = 0.1 - - MaxObjectMass = 10000.01 - - CollisionMargin = 0.04 - - ; Linkset implmentation + ; Default linkset implmentation + ; 'Constraint' uses physics constraints to hold linkset together. 'Compound' builds a compound + ; shape from the children shapes to create a single physical shape. 'Compound' uses a lot less CPU time. LinkImplementation = 1 ; 0=constraint, 1=compound - ; Whether to mesh sculpties + ; If 'true', turn scuplties into meshes MeshSculptedPrim = true ; If 'true', force simple prims (box and sphere) to be meshed + ; If 'false', the Bullet native special case shape is used for square rectangles and even dimensioned spheres ForceSimplePrimMeshing = false - ; Bullet step parameters - MaxSubSteps = 10 - FixedTimeStep = .01667 + ; If 'true', when creating meshes, remove all triangles that have two equal vertexes. + ; Happens often in sculpties. If turned off, there will be some doorways that cannot be walked through. + ShouldRemoveZeroWidthTriangles = true + ; If 'true', use convex hull definition in mesh asset if present. + ShouldUseAssetHulls = true + + ; If there are thousands of physical objects, these maximums should be increased. MaxCollisionsPerFrame = 2048 MaxUpdatesPerFrame = 8192 - ; Detailed physics debug logging + ; Detailed physics debug logging. Very verbose. PhysicsLoggingEnabled = False PhysicsLoggingDir = "." VehicleLoggingEnabled = False diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example index bc2b4cfb29..d9f1ca1725 100644 --- a/bin/Robust.HG.ini.example +++ b/bin/Robust.HG.ini.example @@ -71,10 +71,12 @@ HGInventoryServiceConnector = "HGInventoryService@8002/OpenSim.Server.Handlers.d HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:AssetServiceConnector" ;; Uncomment this if you want Groups V2, HG to work ; HGGroupsServiceConnector = "8002/OpenSim.Addons.Groups.dll:HGGroupsServiceRobustConnector" - ;; Additions for other add-on modules. For example: ;; WifiServerConnector = "8002/Diva.Wifi.dll:WifiServerConnector" +;; Uncomment for UserProfiles see [UserProfilesService] to configure... +; UserProfilesServiceConnector = "8002/OpenSim.Server.Handlers.dll:UserProfilesConnector" + ; * This is common for all services, it's the network setup for the entire ; * server instance, if none is specified above ; * @@ -595,4 +597,12 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset ;; Can overwrite the default in [Hypergrid], but probably shouldn't ; HomeURI = "http://127.0.0.1:8002" +[UserProfilesService] + LocalServiceModule = "OpenSim.Services.UserProfilesService.dll:UserProfilesService" + Enabled = false + ;; Configure this for separate profiles database + ;; ConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=*****;Old Guids=true;" + ;; Realm = UserProfiles + UserAccountService = OpenSim.Services.UserAccountService.dll:UserAccountService + AuthenticationServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index 1d66b7fcfd..7d6492bf94 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -51,6 +51,8 @@ MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnecto ;; Uncomment this if you want Groups V2 to work ;GroupsServiceConnector = "8003/OpenSim.Addons.Groups.dll:GroupsServiceRobustConnector" +;; Uncomment for UserProfiles see [UserProfilesService] to configure... +; UserProfilesServiceConnector = "8002/OpenSim.Server.Handlers.dll:UserProfilesConnector" ; * This is common for all services, it's the network setup for the entire ; * server instance, if none is specified above @@ -391,4 +393,13 @@ MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnecto ; password help: optional: page providing password assistance for users of your grid ;password = http://127.0.0.1/password +[UserProfilesService] + LocalServiceModule = "OpenSim.Services.UserProfilesService.dll:UserProfilesService" + Enabled = false + ;; Configure this for separate profiles database + ;; ConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=*****;Old Guids=true;" + ;; Realm = UserProfiles + UserAccountService = OpenSim.Services.UserAccountService.dll:UserAccountService + AuthenticationServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + diff --git a/bin/config-include/StandaloneCommon.ini.example b/bin/config-include/StandaloneCommon.ini.example index 254724417f..8c23c41418 100644 --- a/bin/config-include/StandaloneCommon.ini.example +++ b/bin/config-include/StandaloneCommon.ini.example @@ -352,3 +352,19 @@ ;; If appearance is restricted, which accounts' appearances are allowed to be exported? ;; Comma-separated list of account names AccountForAppearance = "Test User, Astronaut Smith" + +;; UserProfiles Service +;; +;; To use, set Enabled to true then configure for your site... +[UserProfilesService] + LocalServiceModule = "OpenSim.Services.UserProfilesService.dll:UserProfilesService" + Enabled = false + + ;; Configure this for separate databse + ; ConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=***;Old Guids=true;" + ; Realm = UserProfiles + + UserAccountService = OpenSim.Services.UserAccountService.dll:UserAccountService + AuthenticationServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + + diff --git a/bin/config-include/StandaloneHypergrid.ini b/bin/config-include/StandaloneHypergrid.ini index ba92030132..39c33e8ca2 100644 --- a/bin/config-include/StandaloneHypergrid.ini +++ b/bin/config-include/StandaloneHypergrid.ini @@ -19,6 +19,7 @@ GridUserServices = "LocalGridUserServicesConnector" SimulationServices = "RemoteSimulationConnectorModule" AvatarServices = "LocalAvatarServicesConnector" + UserProfilesServices = "LocalUserProfilesServicesConnector" MapImageService = "MapImageServiceModule" EntityTransferModule = "HGEntityTransferModule" InventoryAccessModule = "HGInventoryAccessModule" @@ -184,7 +185,6 @@ UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" InGatekeeper = True - ;; This should always be the very last thing on this file [Includes] Include-Common = "config-include/StandaloneCommon.ini" diff --git a/prebuild.xml b/prebuild.xml index 9a70b60b7b..2db4e2bb92 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -282,6 +282,7 @@ + @@ -1360,6 +1361,42 @@ + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + @@ -2102,6 +2139,7 @@ +