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