diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index e0a96e76ad..31aeda3ab8 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -86,8 +86,10 @@ what it is today. * Grumly57 * GuduleLapointe * Ewe Loon +* Fernando Oliveira * Fly-Man * Flyte Xevious +* Garmin Kawaguichi * Imaze Rhiano * Intimidated * Jeremy Bongio (IBM) @@ -106,6 +108,7 @@ what it is today. * M.Igarashi * maimedleech * Mana Janus +* MarcelEdward * Mic Bowman * Michelle Argus * Michael Cortez (The Flotsam Project, http://osflotsam.org/) @@ -135,6 +138,7 @@ what it is today. * Ruud Lathorp * SachaMagne * Salahzar Stenvaag +* satguru p srivastava * sempuki * SignpostMarv * SpotOn3D diff --git a/OpenSim/Region/Framework/Scenes/Animation/AvatarAnimations.cs b/OpenSim/Data/IXAssetDataPlugin.cs similarity index 57% rename from OpenSim/Region/Framework/Scenes/Animation/AvatarAnimations.cs rename to OpenSim/Data/IXAssetDataPlugin.cs index 659c3a5c54..74ad6f497d 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/AvatarAnimations.cs +++ b/OpenSim/Data/IXAssetDataPlugin.cs @@ -26,38 +26,22 @@ */ using System.Collections.Generic; -using System.Xml; using OpenMetaverse; +using OpenSim.Framework; -namespace OpenSim.Region.Framework.Scenes.Animation +namespace OpenSim.Data { - public class AvatarAnimations + /// + /// This interface exists to distinguish between the normal IAssetDataPlugin and the one used by XAssetService + /// for now. + /// + public interface IXAssetDataPlugin : IPlugin { - public Dictionary AnimsUUID = new Dictionary(); - public Dictionary AnimsNames = new Dictionary(); - public Dictionary AnimStateNames = new Dictionary(); - - public AvatarAnimations() - { - using (XmlTextReader reader = new XmlTextReader("data/avataranimations.xml")) - { - XmlDocument doc = new XmlDocument(); - doc.Load(reader); - foreach (XmlNode nod in doc.DocumentElement.ChildNodes) - { - if (nod.Attributes["name"] != null) - { - string name = (string)nod.Attributes["name"].Value; - UUID id = (UUID)nod.InnerText; - string animState = (string)nod.Attributes["state"].Value; - - AnimsUUID.Add(name, id); - AnimsNames.Add(id, name); - if (animState != "") - AnimStateNames.Add(id, animState); - } - } - } - } + AssetBase GetAsset(UUID uuid); + void StoreAsset(AssetBase asset); + bool ExistsAsset(UUID uuid); + List FetchAssetMetadataSet(int start, int count); + void Initialise(string connect); + bool Delete(string id); } -} +} \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/MSSQLFriendsData.cs b/OpenSim/Data/MSSQL/MSSQLFriendsData.cs index 09dde5e182..fef6978f33 100644 --- a/OpenSim/Data/MSSQL/MSSQLFriendsData.cs +++ b/OpenSim/Data/MSSQL/MSSQLFriendsData.cs @@ -89,5 +89,11 @@ namespace OpenSim.Data.MSSQL return DoQuery(cmd); } } + + public FriendsData[] GetFriends(Guid principalID) + { + return GetFriends(principalID.ToString()); + } + } } diff --git a/OpenSim/Data/MSSQL/MSSQLInventoryData.cs b/OpenSim/Data/MSSQL/MSSQLInventoryData.cs index 4d06377452..961593fc6f 100644 --- a/OpenSim/Data/MSSQL/MSSQLInventoryData.cs +++ b/OpenSim/Data/MSSQL/MSSQLInventoryData.cs @@ -813,7 +813,7 @@ namespace OpenSim.Data.MSSQL { try { - using (SqlCommand command = new SqlCommand("DELETE FROM inventoryfolders WHERE folderID=@folderID", connection)) + using (SqlCommand command = new SqlCommand("DELETE FROM inventoryfolders WHERE folderID=@folderID and type=-1", connection)) { command.Parameters.Add(database.CreateParameter("folderID", folderID)); diff --git a/OpenSim/Data/MSSQL/MSSQLSimulationData.cs b/OpenSim/Data/MSSQL/MSSQLSimulationData.cs index d6b1561055..d9dfe864ec 100644 --- a/OpenSim/Data/MSSQL/MSSQLSimulationData.cs +++ b/OpenSim/Data/MSSQL/MSSQLSimulationData.cs @@ -675,7 +675,7 @@ VALUES cmd.ExecuteNonQuery(); } - sql = "INSERT INTO [landaccesslist] ([LandUUID],[AccessUUID],[Flags]) VALUES (@LandUUID,@AccessUUID,@Flags)"; + sql = "INSERT INTO [landaccesslist] ([LandUUID],[AccessUUID],[Flags],[Expires]) VALUES (@LandUUID,@AccessUUID,@Flags,@Expires)"; using (SqlConnection conn = new SqlConnection(m_connectionString)) using (SqlCommand cmd = new SqlCommand(sql, conn)) @@ -1215,6 +1215,8 @@ VALUES //Store new values StoreNewRegionSettings(regionSettings); + LoadSpawnPoints(regionSettings); + return regionSettings; } @@ -1252,7 +1254,7 @@ VALUES ,[elevation_1_ne] = @elevation_1_ne ,[elevation_2_ne] = @elevation_2_ne ,[elevation_1_se] = @elevation_1_se ,[elevation_2_se] = @elevation_2_se ,[elevation_1_sw] = @elevation_1_sw ,[elevation_2_sw] = @elevation_2_sw ,[water_height] = @water_height ,[terrain_raise_limit] = @terrain_raise_limit ,[terrain_lower_limit] = @terrain_lower_limit ,[use_estate_sun] = @use_estate_sun ,[fixed_sun] = @fixed_sun ,[sun_position] = @sun_position -,[covenant] = @covenant ,[covenant_datetime] = @covenant_datetime, [sunvectorx] = @sunvectorx, [sunvectory] = @sunvectory, [sunvectorz] = @sunvectorz, [Sandbox] = @Sandbox, [loaded_creation_datetime] = @loaded_creation_datetime, [loaded_creation_id] = @loaded_creation_id +,[covenant] = @covenant ,[covenant_datetime] = @covenant_datetime, [sunvectorx] = @sunvectorx, [sunvectory] = @sunvectory, [sunvectorz] = @sunvectorz, [Sandbox] = @Sandbox, [loaded_creation_datetime] = @loaded_creation_datetime, [loaded_creation_id] = @loaded_creation_id, [map_tile_id] = @TerrainImageID, [telehubobject] = @telehubobject, [parcel_tile_id] = @ParcelImageID WHERE [regionUUID] = @regionUUID"; using (SqlConnection conn = new SqlConnection(m_connectionString)) @@ -1263,6 +1265,7 @@ VALUES cmd.ExecuteNonQuery(); } } + SaveSpawnPoints(regionSettings); } public void Shutdown() @@ -1383,6 +1386,11 @@ VALUES newSettings.LoadedCreationID = ""; else newSettings.LoadedCreationID = (String)row["loaded_creation_id"]; + + newSettings.TerrainImageID = new UUID((string)row["map_tile_ID"]); + newSettings.ParcelImageID = new UUID((Guid)row["parcel_tile_ID"]); + newSettings.TelehubObject = new UUID((Guid)row["TelehubObject"]); + return newSettings; } @@ -1454,6 +1462,13 @@ VALUES } newData.ParcelAccessList = new List(); + newData.MediaDescription = (string)row["MediaDescription"]; + newData.MediaType = (string)row["MediaType"]; + newData.MediaWidth = Convert.ToInt32((((string)row["MediaSize"]).Split(','))[0]); + newData.MediaHeight = Convert.ToInt32((((string)row["MediaSize"]).Split(','))[1]); + newData.MediaLoop = Convert.ToBoolean(row["MediaLoop"]); + newData.ObscureMusic = Convert.ToBoolean(row["ObscureMusic"]); + newData.ObscureMedia = Convert.ToBoolean(row["ObscureMedia"]); return newData; } @@ -1468,7 +1483,7 @@ VALUES LandAccessEntry entry = new LandAccessEntry(); entry.AgentID = new UUID((Guid)row["AccessUUID"]); entry.Flags = (AccessList)Convert.ToInt32(row["Flags"]); - entry.Expires = 0; + entry.Expires = Convert.ToInt32(row["Expires"]); return entry; } @@ -1497,7 +1512,8 @@ VALUES prim.TouchName = (string)primRow["TouchName"]; // permissions prim.Flags = (PrimFlags)Convert.ToUInt32(primRow["ObjectFlags"]); - prim.CreatorID = new UUID((Guid)primRow["CreatorID"]); + //prim.CreatorID = new UUID((Guid)primRow["CreatorID"]); + prim.CreatorIdentification = (string)primRow["CreatorID"]; prim.OwnerID = new UUID((Guid)primRow["OwnerID"]); prim.GroupID = new UUID((Guid)primRow["GroupID"]); prim.LastOwnerID = new UUID((Guid)primRow["LastOwnerID"]); @@ -1691,7 +1707,8 @@ VALUES taskItem.Name = (string)inventoryRow["name"]; taskItem.Description = (string)inventoryRow["description"]; taskItem.CreationDate = Convert.ToUInt32(inventoryRow["creationDate"]); - taskItem.CreatorID = new UUID((Guid)inventoryRow["creatorID"]); + //taskItem.CreatorID = new UUID((Guid)inventoryRow["creatorID"]); + taskItem.CreatorIdentification = (string)inventoryRow["creatorID"]; taskItem.OwnerID = new UUID((Guid)inventoryRow["ownerID"]); taskItem.LastOwnerID = new UUID((Guid)inventoryRow["lastOwnerID"]); taskItem.GroupID = new UUID((Guid)inventoryRow["groupID"]); @@ -1792,6 +1809,9 @@ VALUES parameters.Add(_Database.CreateParameter("covenant_datetime", settings.CovenantChangedDateTime)); parameters.Add(_Database.CreateParameter("Loaded_Creation_DateTime", settings.LoadedCreationDateTime)); parameters.Add(_Database.CreateParameter("Loaded_Creation_ID", settings.LoadedCreationID)); + parameters.Add(_Database.CreateParameter("TerrainImageID", settings.TerrainImageID)); + parameters.Add(_Database.CreateParameter("ParcelImageID", settings.ParcelImageID)); + parameters.Add(_Database.CreateParameter("TelehubObject", settings.TelehubObject)); return parameters.ToArray(); } @@ -1859,6 +1879,7 @@ VALUES parameters.Add(_Database.CreateParameter("LandUUID", parcelID)); parameters.Add(_Database.CreateParameter("AccessUUID", parcelAccessEntry.AgentID)); parameters.Add(_Database.CreateParameter("Flags", parcelAccessEntry.Flags)); + parameters.Add(_Database.CreateParameter("Expires", parcelAccessEntry.Expires)); return parameters.ToArray(); } @@ -2063,5 +2084,57 @@ VALUES #endregion #endregion + + private void LoadSpawnPoints(RegionSettings rs) + { + rs.ClearSpawnPoints(); + + string sql = "SELECT Yaw, Pitch, Distance FROM spawn_points WHERE RegionUUID = @RegionUUID"; + using (SqlConnection conn = new SqlConnection(m_connectionString)) + using (SqlCommand cmd = new SqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", rs.RegionUUID.ToString())); + conn.Open(); + using (SqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + SpawnPoint sp = new SpawnPoint(); + + sp.Yaw = (float)reader["Yaw"]; + sp.Pitch = (float)reader["Pitch"]; + sp.Distance = (float)reader["Distance"]; + + rs.AddSpawnPoint(sp); + } + } + } + } + + private void SaveSpawnPoints(RegionSettings rs) + { + string sql = "DELETE FROM spawn_points WHERE RegionUUID = @RegionUUID"; + using (SqlConnection conn = new SqlConnection(m_connectionString)) + using (SqlCommand cmd = new SqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", rs.RegionUUID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + foreach (SpawnPoint p in rs.SpawnPoints()) + { + sql = "INSERT INTO spawn_points (RegionUUID, Yaw, Pitch, Distance) VALUES (@RegionUUID, @Yaw, @Pitch, @Distance)"; + using (SqlConnection conn = new SqlConnection(m_connectionString)) + using (SqlCommand cmd = new SqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", rs.RegionUUID)); + cmd.Parameters.Add(_Database.CreateParameter("@Yaw", p.Yaw)); + cmd.Parameters.Add(_Database.CreateParameter("@Pitch", p.Pitch)); + cmd.Parameters.Add(_Database.CreateParameter("@Distance", p.Distance)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + } } } diff --git a/OpenSim/Data/MSSQL/Resources/RegionStore.migrations b/OpenSim/Data/MSSQL/Resources/RegionStore.migrations index a98690affb..d6a3be9282 100644 --- a/OpenSim/Data/MSSQL/Resources/RegionStore.migrations +++ b/OpenSim/Data/MSSQL/Resources/RegionStore.migrations @@ -1044,10 +1044,93 @@ ALTER TABLE primitems ALTER COLUMN CreatorID uniqueidentifier NOT NULL COMMIT -:VERSION 29 #--------------------- +:VERSION 29 #----------------- Region Covenant changed time BEGIN TRANSACTION ALTER TABLE regionsettings ADD covenant_datetime int NOT NULL default 0 COMMIT + +:VERSION 30 #------------------Migrate creatorID storage to varchars instead of UUIDs for HG support + +BEGIN TRANSACTION + +EXECUTE sp_rename N'dbo.prims.creatorid', N'creatoridold', 'COLUMN' +EXECUTE sp_rename N'dbo.primitems.creatorid', N'creatoridold', 'COLUMN' + +COMMIT + +:VERSION 31 #--------------------- + +BEGIN TRANSACTION + +ALTER TABLE prims ADD CreatorID varchar(255) +ALTER TABLE primitems ADD CreatorID varchar(255) + +COMMIT + +:VERSION 32 #--------------------- + +BEGIN TRANSACTION + +UPDATE prims SET prims.CreatorID = CONVERT(varchar(255), creatoridold) +UPDATE primitems SET primitems.CreatorID = CONVERT(varchar(255), creatoridold) + +COMMIT + +:VERSION 33 #--------------------- + +BEGIN TRANSACTION + +ALTER TABLE prims +ADD CONSTRAINT DF_prims_CreatorIDNew +DEFAULT '00000000-0000-0000-0000-000000000000' +FOR CreatorID + +ALTER TABLE prims ALTER COLUMN CreatorID varchar(255) NOT NULL + +ALTER TABLE primitems +ADD CONSTRAINT DF_primitems_CreatorIDNew +DEFAULT '00000000-0000-0000-0000-000000000000' +FOR CreatorID + +ALTER TABLE primitems ALTER COLUMN CreatorID varchar(255) NOT NULL + +COMMIT + +:VERSION 34 #--------------- Telehub support + +BEGIN TRANSACTION + +CREATE TABLE [dbo].[Spawn_Points]( + [RegionUUID] [uniqueidentifier] NOT NULL, + [Yaw] [float] NOT NULL, + [Pitch] [float] NOT NULL, + [Distance] [float] NOT NULL, + PRIMARY KEY CLUSTERED + ( + [RegionUUID] ASC + )WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] + +ALTER TABLE regionsettings ADD TelehubObject uniqueidentifier NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + +COMMIT + +:VERSION 35 #---------------- Parcels for sale + +BEGIN TRANSACTION + +ALTER TABLE regionsettings ADD parcel_tile_ID uniqueidentifier NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + +COMMIT + +:VERSION 36 #---------------- Timed bans/access + +BEGIN TRANSACTION + +ALTER TABLE landaccesslist ADD Expires integer NOT NULL DEFAULT 0; + +COMMIT + diff --git a/OpenSim/Data/MySQL/MySQLXAssetData.cs b/OpenSim/Data/MySQL/MySQLXAssetData.cs new file mode 100644 index 0000000000..e6ac22e09c --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLXAssetData.cs @@ -0,0 +1,500 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.IO.Compression; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using log4net; +using MySql.Data.MySqlClient; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; + +namespace OpenSim.Data.MySQL +{ + public class MySQLXAssetData : IXAssetDataPlugin + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + private bool m_enableCompression = false; + private string m_connectionString; + private object m_dbLock = new object(); + + /// + /// We can reuse this for all hashing since all methods are single-threaded through m_dbBLock + /// + private HashAlgorithm hasher = new SHA256CryptoServiceProvider(); + + #region IPlugin Members + + public string Version { get { return "1.0.0.0"; } } + + /// + /// Initialises Asset interface + /// + /// + /// Loads and initialises the MySQL storage plugin. + /// Warns and uses the obsolete mysql_connection.ini if connect string is empty. + /// Check for migration + /// + /// + /// + /// connect string + public void Initialise(string connect) + { + m_log.ErrorFormat("[MYSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[MYSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[MYSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[MYSQL XASSETDATA]: THIS PLUGIN IS STRICTLY EXPERIMENTAL."); + m_log.ErrorFormat("[MYSQL XASSETDATA]: DO NOT USE FOR ANY DATA THAT YOU DO NOT MIND LOSING."); + m_log.ErrorFormat("[MYSQL XASSETDATA]: DATABASE TABLES CAN CHANGE AT ANY TIME, CAUSING EXISTING DATA TO BE LOST."); + m_log.ErrorFormat("[MYSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[MYSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[MYSQL XASSETDATA]: ***********************************************************"); + + m_connectionString = connect; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + Migration m = new Migration(dbcon, Assembly, "XAssetStore"); + m.Update(); + } + } + + public void Initialise() + { + throw new NotImplementedException(); + } + + public void Dispose() { } + + /// + /// The name of this DB provider + /// + public string Name + { + get { return "MySQL XAsset storage engine"; } + } + + #endregion + + #region IAssetDataPlugin Members + + /// + /// Fetch Asset from database + /// + /// Asset UUID to fetch + /// Return the asset + /// On failure : throw an exception and attempt to reconnect to database + public AssetBase GetAsset(UUID assetID) + { +// m_log.DebugFormat("[MYSQL XASSET DATA]: Looking for asset {0}", assetID); + + AssetBase asset = null; + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand( + "SELECT name, description, asset_type, local, temporary, asset_flags, creator_id, data FROM xassetsmeta JOIN xassetsdata ON xassetsmeta.hash = xassetsdata.hash WHERE id=?id", + dbcon)) + { + cmd.Parameters.AddWithValue("?id", assetID.ToString()); + + try + { + using (MySqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (dbReader.Read()) + { + asset = new AssetBase(assetID, (string)dbReader["name"], (sbyte)dbReader["asset_type"], dbReader["creator_id"].ToString()); + asset.Data = (byte[])dbReader["data"]; + asset.Description = (string)dbReader["description"]; + + string local = dbReader["local"].ToString(); + if (local.Equals("1") || local.Equals("true", StringComparison.InvariantCultureIgnoreCase)) + asset.Local = true; + else + asset.Local = false; + + asset.Temporary = Convert.ToBoolean(dbReader["temporary"]); + asset.Flags = (AssetFlags)Convert.ToInt32(dbReader["asset_flags"]); + + if (m_enableCompression) + { + using (GZipStream decompressionStream = new GZipStream(new MemoryStream(asset.Data), CompressionMode.Decompress)) + { + MemoryStream outputStream = new MemoryStream(); + WebUtil.CopyStream(decompressionStream, outputStream, int.MaxValue); + // int compressedLength = asset.Data.Length; + asset.Data = outputStream.ToArray(); + + // m_log.DebugFormat( + // "[XASSET DB]: Decompressed {0} {1} to {2} bytes from {3}", + // asset.ID, asset.Name, asset.Data.Length, compressedLength); + } + } + } + } + } + catch (Exception e) + { + m_log.Error("[MYSQL XASSET DATA]: MySql failure fetching asset " + assetID + ": " + e.Message); + } + } + } + } + + return asset; + } + + /// + /// Create an asset in database, or update it if existing. + /// + /// Asset UUID to create + /// On failure : Throw an exception and attempt to reconnect to database + public void StoreAsset(AssetBase asset) + { + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlTransaction transaction = dbcon.BeginTransaction()) + { + string assetName = asset.Name; + if (asset.Name.Length > 64) + { + assetName = asset.Name.Substring(0, 64); + m_log.Warn("[XASSET DB]: Name field truncated from " + asset.Name.Length + " to " + assetName.Length + " characters on add"); + } + + string assetDescription = asset.Description; + if (asset.Description.Length > 64) + { + assetDescription = asset.Description.Substring(0, 64); + m_log.Warn("[XASSET DB]: Description field truncated from " + asset.Description.Length + " to " + assetDescription.Length + " characters on add"); + } + + if (m_enableCompression) + { + MemoryStream outputStream = new MemoryStream(); + + using (GZipStream compressionStream = new GZipStream(outputStream, CompressionMode.Compress, false)) + { + // Console.WriteLine(WebUtil.CopyTo(new MemoryStream(asset.Data), compressionStream, int.MaxValue)); + // We have to close the compression stream in order to make sure it writes everything out to the underlying memory output stream. + compressionStream.Close(); + byte[] compressedData = outputStream.ToArray(); + asset.Data = compressedData; + } + } + + byte[] hash = hasher.ComputeHash(asset.Data); + +// m_log.DebugFormat( +// "[XASSET DB]: Compressed data size for {0} {1}, hash {2} is {3}", +// asset.ID, asset.Name, hash, compressedData.Length); + + try + { + using (MySqlCommand cmd = + new MySqlCommand( + "replace INTO xassetsmeta(id, hash, name, description, asset_type, local, temporary, create_time, access_time, asset_flags, creator_id)" + + "VALUES(?id, ?hash, ?name, ?description, ?asset_type, ?local, ?temporary, ?create_time, ?access_time, ?asset_flags, ?creator_id)", + dbcon)) + { + // create unix epoch time + int now = (int)Utils.DateTimeToUnixTime(DateTime.UtcNow); + cmd.Parameters.AddWithValue("?id", asset.ID); + cmd.Parameters.AddWithValue("?hash", hash); + cmd.Parameters.AddWithValue("?name", assetName); + cmd.Parameters.AddWithValue("?description", assetDescription); + cmd.Parameters.AddWithValue("?asset_type", asset.Type); + cmd.Parameters.AddWithValue("?local", asset.Local); + cmd.Parameters.AddWithValue("?temporary", asset.Temporary); + cmd.Parameters.AddWithValue("?create_time", now); + cmd.Parameters.AddWithValue("?access_time", now); + cmd.Parameters.AddWithValue("?creator_id", asset.Metadata.CreatorID); + cmd.Parameters.AddWithValue("?asset_flags", (int)asset.Flags); + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[ASSET DB]: MySQL failure creating asset metadata {0} with name \"{1}\". Error: {2}", + asset.FullID, asset.Name, e.Message); + + transaction.Rollback(); + + return; + } + + if (!ExistsData(dbcon, transaction, hash)) + { + try + { + using (MySqlCommand cmd = + new MySqlCommand( + "INSERT INTO xassetsdata(hash, data) VALUES(?hash, ?data)", + dbcon)) + { + cmd.Parameters.AddWithValue("?hash", hash); + cmd.Parameters.AddWithValue("?data", asset.Data); + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[XASSET DB]: MySQL failure creating asset data {0} with name \"{1}\". Error: {2}", + asset.FullID, asset.Name, e.Message); + + transaction.Rollback(); + + return; + } + } + + transaction.Commit(); + } + } + } + } + +// private void UpdateAccessTime(AssetBase asset) +// { +// lock (m_dbLock) +// { +// using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) +// { +// dbcon.Open(); +// MySqlCommand cmd = +// new MySqlCommand("update assets set access_time=?access_time where id=?id", +// dbcon); +// +// // need to ensure we dispose +// try +// { +// using (cmd) +// { +// // create unix epoch time +// int now = (int)Utils.DateTimeToUnixTime(DateTime.UtcNow); +// cmd.Parameters.AddWithValue("?id", asset.ID); +// cmd.Parameters.AddWithValue("?access_time", now); +// cmd.ExecuteNonQuery(); +// cmd.Dispose(); +// } +// } +// catch (Exception e) +// { +// m_log.ErrorFormat( +// "[ASSETS DB]: " + +// "MySql failure updating access_time for asset {0} with name {1}" + Environment.NewLine + e.ToString() +// + Environment.NewLine + "Attempting reconnection", asset.FullID, asset.Name); +// } +// } +// } +// +// } + + /// + /// We assume we already have the m_dbLock. + /// + /// TODO: need to actually use the transaction. + /// + /// + /// + /// + private bool ExistsData(MySqlConnection dbcon, MySqlTransaction transaction, byte[] hash) + { +// m_log.DebugFormat("[ASSETS DB]: Checking for asset {0}", uuid); + + bool exists = false; + + using (MySqlCommand cmd = new MySqlCommand("SELECT hash FROM xassetsdata WHERE hash=?hash", dbcon)) + { + cmd.Parameters.AddWithValue("?hash", hash); + + try + { + using (MySqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (dbReader.Read()) + { +// m_log.DebugFormat("[ASSETS DB]: Found asset {0}", uuid); + exists = true; + } + } + } + catch (Exception e) + { + m_log.ErrorFormat( + "[XASSETS DB]: MySql failure in ExistsData fetching hash {0}. Exception {1}{2}", + hash, e.Message, e.StackTrace); + } + } + + return exists; + } + + /// + /// Check if the asset exists in the database + /// + /// The asset UUID + /// true if it exists, false otherwise. + public bool ExistsAsset(UUID uuid) + { +// m_log.DebugFormat("[ASSETS DB]: Checking for asset {0}", uuid); + + bool assetExists = false; + + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand("SELECT id FROM xassetsmeta WHERE id=?id", dbcon)) + { + cmd.Parameters.AddWithValue("?id", uuid.ToString()); + + try + { + using (MySqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (dbReader.Read()) + { +// m_log.DebugFormat("[ASSETS DB]: Found asset {0}", uuid); + assetExists = true; + } + } + } + catch (Exception e) + { + m_log.ErrorFormat( + "[XASSETS DB]: MySql failure fetching asset {0}" + Environment.NewLine + e.ToString(), uuid); + } + } + } + } + + return assetExists; + } + + /// + /// Returns a list of AssetMetadata objects. The list is a subset of + /// the entire data set offset by containing + /// elements. + /// + /// The number of results to discard from the total data set. + /// The number of rows the returned list should contain. + /// A list of AssetMetadata objects. + public List FetchAssetMetadataSet(int start, int count) + { + List retList = new List(count); + + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + MySqlCommand cmd = new MySqlCommand("SELECT name,description,asset_type,temporary,id,asset_flags,creator_id FROM xassetsmeta LIMIT ?start, ?count", dbcon); + cmd.Parameters.AddWithValue("?start", start); + cmd.Parameters.AddWithValue("?count", count); + + try + { + using (MySqlDataReader dbReader = cmd.ExecuteReader()) + { + while (dbReader.Read()) + { + AssetMetadata metadata = new AssetMetadata(); + metadata.Name = (string)dbReader["name"]; + metadata.Description = (string)dbReader["description"]; + metadata.Type = (sbyte)dbReader["asset_type"]; + metadata.Temporary = Convert.ToBoolean(dbReader["temporary"]); // Not sure if this is correct. + metadata.Flags = (AssetFlags)Convert.ToInt32(dbReader["asset_flags"]); + metadata.FullID = DBGuid.FromDB(dbReader["id"]); + metadata.CreatorID = dbReader["creator_id"].ToString(); + + // We'll ignore this for now - it appears unused! +// metadata.SHA1 = dbReader["hash"]); + + retList.Add(metadata); + } + } + } + catch (Exception e) + { + m_log.Error("[XASSETS DB]: MySql failure fetching asset set" + Environment.NewLine + e.ToString()); + } + } + } + + return retList; + } + + public bool Delete(string id) + { +// m_log.DebugFormat("[XASSETS DB]: Deleting asset {0}", id); + + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand("delete from xassetsmeta where id=?id", dbcon)) + { + cmd.Parameters.AddWithValue("?id", id); + cmd.ExecuteNonQuery(); + } + + // TODO: How do we deal with data from deleted assets? Probably not easily reapable unless we + // keep a reference count (?) + } + } + + return true; + } + + #endregion + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/XAssetStore.migrations b/OpenSim/Data/MySQL/Resources/XAssetStore.migrations new file mode 100644 index 0000000000..d3cca5e884 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/XAssetStore.migrations @@ -0,0 +1,27 @@ +# ----------------- +:VERSION 1 + +BEGIN; + +CREATE TABLE `xassetsmeta` ( + `id` char(36) NOT NULL, + `hash` binary(32) NOT NULL, + `name` varchar(64) NOT NULL, + `description` varchar(64) NOT NULL, + `asset_type` tinyint(4) NOT NULL, + `local` tinyint(1) NOT NULL, + `temporary` tinyint(1) NOT NULL, + `create_time` int(11) NOT NULL, + `access_time` int(11) NOT NULL, + `asset_flags` int(11) NOT NULL, + `creator_id` varchar(128) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Version 1'; + +CREATE TABLE `xassetsdata` ( + `hash` binary(32) NOT NULL, + `data` longblob NOT NULL, + PRIMARY KEY (`hash`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Version 1'; + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Framework/Console/CommandConsole.cs b/OpenSim/Framework/Console/CommandConsole.cs index 0d6288b5a3..c5d6b78686 100644 --- a/OpenSim/Framework/Console/CommandConsole.cs +++ b/OpenSim/Framework/Console/CommandConsole.cs @@ -29,6 +29,7 @@ using System; using System.Xml; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; @@ -40,6 +41,8 @@ namespace OpenSim.Framework.Console { public class Commands : ICommands { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + /// /// Encapsulates a command that can be invoked from the console /// @@ -76,12 +79,19 @@ namespace OpenSim.Framework.Console public List fn; } + public const string GeneralHelpText = "For more information, type 'help ' where is one of the following categories:"; + /// /// Commands organized by keyword in a tree /// private Dictionary tree = new Dictionary(); + /// + /// Commands organized by module + /// + private Dictionary> m_modulesCommands = new Dictionary>(); + /// /// Get help for the given help string /// @@ -98,8 +108,8 @@ namespace OpenSim.Framework.Console // General help if (helpParts.Count == 0) { - help.AddRange(CollectHelp(tree)); - help.Sort(); + help.Add(GeneralHelpText); + help.AddRange(CollectModulesHelp(tree)); } else { @@ -118,6 +128,13 @@ namespace OpenSim.Framework.Console { string originalHelpRequest = string.Join(" ", helpParts.ToArray()); List help = new List(); + + // Check modules first to see if we just need to display a list of those commands + if (TryCollectModuleHelp(originalHelpRequest, help)) + { + help.Insert(0, GeneralHelpText); + return help; + } Dictionary dict = tree; while (helpParts.Count > 0) @@ -161,25 +178,63 @@ namespace OpenSim.Framework.Console return help; } - private List CollectHelp(Dictionary dict) + /// + /// Try to collect help for the given module if that module exists. + /// + /// + /// /param> + /// true if there was the module existed, false otherwise. + private bool TryCollectModuleHelp(string moduleName, List helpText) { - List result = new List(); - - foreach (KeyValuePair kvp in dict) + lock (m_modulesCommands) { - if (kvp.Value is Dictionary) + foreach (string key in m_modulesCommands.Keys) { - result.AddRange(CollectHelp((Dictionary)kvp.Value)); - } - else - { - if (((CommandInfo)kvp.Value).long_help != String.Empty) - result.Add(((CommandInfo)kvp.Value).help_text+" - "+ - ((CommandInfo)kvp.Value).long_help); + // Allow topic help requests to succeed whether they are upper or lowercase. + if (moduleName.ToLower() == key.ToLower()) + { + List commands = m_modulesCommands[key]; + var ourHelpText = commands.ConvertAll(c => string.Format("{0} - {1}", c.help_text, c.long_help)); + ourHelpText.Sort(); + helpText.AddRange(ourHelpText); + + return true; + } } + + return false; } - return result; } + + private List CollectModulesHelp(Dictionary dict) + { + lock (m_modulesCommands) + { + List helpText = new List(m_modulesCommands.Keys); + helpText.Sort(); + return helpText; + } + } + +// private List CollectHelp(Dictionary dict) +// { +// List result = new List(); +// +// foreach (KeyValuePair kvp in dict) +// { +// if (kvp.Value is Dictionary) +// { +// result.AddRange(CollectHelp((Dictionary)kvp.Value)); +// } +// else +// { +// if (((CommandInfo)kvp.Value).long_help != String.Empty) +// result.Add(((CommandInfo)kvp.Value).help_text+" - "+ +// ((CommandInfo)kvp.Value).long_help); +// } +// } +// return result; +// } /// /// Add a command to those which can be invoked from the console. @@ -212,21 +267,19 @@ namespace OpenSim.Framework.Console Dictionary current = tree; - foreach (string s in parts) + foreach (string part in parts) { - if (current.ContainsKey(s)) + if (current.ContainsKey(part)) { - if (current[s] is Dictionary) - { - current = (Dictionary)current[s]; - } + if (current[part] is Dictionary) + current = (Dictionary)current[part]; else return; } else { - current[s] = new Dictionary(); - current = (Dictionary)current[s]; + current[part] = new Dictionary(); + current = (Dictionary)current[part]; } } @@ -250,6 +303,24 @@ namespace OpenSim.Framework.Console info.fn = new List(); info.fn.Add(fn); current[String.Empty] = info; + + // Now add command to modules dictionary + lock (m_modulesCommands) + { + List commands; + if (m_modulesCommands.ContainsKey(module)) + { + commands = m_modulesCommands[module]; + } + else + { + commands = new List(); + m_modulesCommands[module] = commands; + } + +// m_log.DebugFormat("[COMMAND CONSOLE]: Adding to category {0} command {1}", module, command); + commands.Add(info); + } } public string[] FindNextOption(string[] cmd, bool term) @@ -607,8 +678,9 @@ namespace OpenSim.Framework.Console { Commands = new Commands(); - Commands.AddCommand("console", false, "help", "help []", - "Get general command list or more detailed help on a specific command", Help); + Commands.AddCommand( + "Help", false, "help", "help []", + "Display help on a particular command or on a list of commands in a category", Help); } private void Help(string module, string[] cmd) diff --git a/OpenSim/Framework/GcNotify.cs b/OpenSim/Framework/GcNotify.cs new file mode 100644 index 0000000000..14a22a6104 --- /dev/null +++ b/OpenSim/Framework/GcNotify.cs @@ -0,0 +1,62 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Reflection; +using log4net; + +public class GcNotify +{ + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static bool Enabled + { + get { return s_initialized; } + set + { + if (!s_initialized && value) + new GcNotify(); + + s_initialized = value; + } + } + + private static bool s_initialized = false; + + private GcNotify() {} + + ~GcNotify() + { + if (!Environment.HasShutdownStarted && !AppDomain.CurrentDomain.IsFinalizingForUnload()) + { + m_log.DebugFormat("[GC NOTIFY]: Garbage collection triggered."); + + if (Enabled) + new GcNotify(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index c85e599e26..3f560d0ec4 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -1215,10 +1215,9 @@ namespace OpenSim.Framework /// The orbital position is given in radians, and must be "adjusted" for the linden client, see LLClientView void SendSunPos(Vector3 sunPos, Vector3 sunVel, ulong CurrentTime, uint SecondsPerSunCycle, uint SecondsPerYear, float OrbitalPosition); - + void SendViewerEffect(ViewerEffectPacket.EffectBlock[] effectBlocks); void SendViewerTime(int phase); - UUID GetDefaultAnimation(string name); void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, string flAbout, uint flags, UUID flImageID, UUID imageID, string profileURL, UUID partnerID); diff --git a/OpenSim/Framework/ICommandConsole.cs b/OpenSim/Framework/ICommandConsole.cs index d33b9b5772..ca0ff936ca 100644 --- a/OpenSim/Framework/ICommandConsole.cs +++ b/OpenSim/Framework/ICommandConsole.cs @@ -40,7 +40,7 @@ namespace OpenSim.Framework /// /// Get help for the given help string /// - /// Parsed parts of the help string. If empty then general help is returned. + /// Parsed parts of the help string. If empty then general help is returned. /// List GetHelp(string[] cmd); diff --git a/OpenSim/Framework/ILandObject.cs b/OpenSim/Framework/ILandObject.cs index dd73b3fe01..4f98d7bb30 100644 --- a/OpenSim/Framework/ILandObject.cs +++ b/OpenSim/Framework/ILandObject.cs @@ -71,6 +71,7 @@ namespace OpenSim.Framework bool IsEitherBannedOrRestricted(UUID avatar); bool IsBannedFromLand(UUID avatar); bool IsRestrictedFromLand(UUID avatar); + bool IsInLandAccessList(UUID avatar); void SendLandUpdateToClient(IClientAPI remote_client); void SendLandUpdateToClient(bool snap_selection, IClientAPI remote_client); List CreateAccessListArrayByFlag(AccessList flag); diff --git a/OpenSim/Framework/MultipartForm.cs b/OpenSim/Framework/MultipartForm.cs index 90c4007ddc..7a13e8b21c 100644 --- a/OpenSim/Framework/MultipartForm.cs +++ b/OpenSim/Framework/MultipartForm.cs @@ -119,7 +119,7 @@ namespace OpenSim.Framework // Copy the temporary stream to the network stream formDataStream.Seek(0, SeekOrigin.Begin); using (Stream requestStream = request.GetRequestStream()) - formDataStream.CopyTo(requestStream, (int)formDataStream.Length); + formDataStream.CopyStream(requestStream, (int)formDataStream.Length); } #endregion Stream Writing diff --git a/OpenSim/Framework/RegionInfo.cs b/OpenSim/Framework/RegionInfo.cs index 5ba3863529..a5055247c7 100644 --- a/OpenSim/Framework/RegionInfo.cs +++ b/OpenSim/Framework/RegionInfo.cs @@ -421,12 +421,18 @@ namespace OpenSim.Framework set { m_internalEndPoint = value; } } + /// + /// The x co-ordinate of this region in map tiles (e.g. 1000). + /// public uint RegionLocX { get { return m_regionLocX.Value; } set { m_regionLocX = value; } } + /// + /// The y co-ordinate of this region in map tiles (e.g. 1000). + /// public uint RegionLocY { get { return m_regionLocY.Value; } diff --git a/OpenSim/Framework/SLUtil.cs b/OpenSim/Framework/SLUtil.cs index b337e034fa..db4541ef1a 100644 --- a/OpenSim/Framework/SLUtil.cs +++ b/OpenSim/Framework/SLUtil.cs @@ -27,7 +27,9 @@ using System; using System.Collections.Generic; +using System.IO; using System.Reflection; +using System.Xml; using log4net; using OpenMetaverse; @@ -375,4 +377,4 @@ namespace OpenSim.Framework return output; } } -} +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs index 6a3135eb7d..06a8021b5f 100644 --- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs +++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs @@ -161,43 +161,43 @@ namespace OpenSim.Framework.Servers Notice(String.Format("Console log level is {0}", m_consoleAppender.Threshold)); } - m_console.Commands.AddCommand("base", false, "quit", + m_console.Commands.AddCommand("General", false, "quit", "quit", "Quit the application", HandleQuit); - m_console.Commands.AddCommand("base", false, "shutdown", + m_console.Commands.AddCommand("General", false, "shutdown", "shutdown", "Quit the application", HandleQuit); - m_console.Commands.AddCommand("base", false, "set log level", + m_console.Commands.AddCommand("General", false, "set log level", "set log level ", "Set the console logging level", HandleLogLevel); - m_console.Commands.AddCommand("base", false, "show info", + m_console.Commands.AddCommand("General", false, "show info", "show info", "Show general information about the server", HandleShow); - m_console.Commands.AddCommand("base", false, "show stats", + m_console.Commands.AddCommand("General", false, "show stats", "show stats", "Show statistics", HandleShow); - m_console.Commands.AddCommand("base", false, "show threads", + m_console.Commands.AddCommand("General", false, "show threads", "show threads", "Show thread status", HandleShow); - m_console.Commands.AddCommand("base", false, "show uptime", + m_console.Commands.AddCommand("General", false, "show uptime", "show uptime", "Show server uptime", HandleShow); - m_console.Commands.AddCommand("base", false, "show version", + m_console.Commands.AddCommand("General", false, "show version", "show version", "Show server version", HandleShow); - m_console.Commands.AddCommand("base", false, "threads abort", + m_console.Commands.AddCommand("General", false, "threads abort", "threads abort ", "Abort a managed thread. Use \"show threads\" to find possible threads.", HandleThreadsAbort); - m_console.Commands.AddCommand("base", false, "threads show", + m_console.Commands.AddCommand("General", false, "threads show", "threads show", "Show thread status. Synonym for \"show threads\"", (string module, string[] args) => Notice(GetThreadsReport())); @@ -269,15 +269,19 @@ namespace OpenSim.Framework.Servers t.Priority, t.ThreadState); - sb.Append(Environment.NewLine); + sb.Append("\n"); } - int workers = 0, ports = 0, maxWorkers = 0, maxPorts = 0; - ThreadPool.GetAvailableThreads(out workers, out ports); - ThreadPool.GetMaxThreads(out maxWorkers, out maxPorts); + sb.Append("\n"); - sb.Append(Environment.NewLine + "*** ThreadPool threads ***" + Environment.NewLine); - sb.Append("workers: " + (maxWorkers - workers) + " (" + maxWorkers + "); ports: " + (maxPorts - ports) + " (" + maxPorts + ")" + Environment.NewLine); + // For some reason mono 2.6.7 returns an empty threads set! Not going to confuse people by reporting + // zero active threads. + int totalThreads = Process.GetCurrentProcess().Threads.Count; + if (totalThreads > 0) + sb.AppendFormat("Total threads active: {0}\n\n", totalThreads); + + sb.Append("Main threadpool (excluding script engine pools)\n"); + sb.Append(Util.GetThreadPoolReport()); return sb.ToString(); } diff --git a/OpenSim/Framework/Statistics/BaseStatsCollector.cs b/OpenSim/Framework/Statistics/BaseStatsCollector.cs index a1841c568a..c9e57ce7b3 100644 --- a/OpenSim/Framework/Statistics/BaseStatsCollector.cs +++ b/OpenSim/Framework/Statistics/BaseStatsCollector.cs @@ -26,8 +26,8 @@ */ using System; +using System.Diagnostics; using System.Text; - using OpenMetaverse; using OpenMetaverse.StructuredData; @@ -46,8 +46,12 @@ namespace OpenSim.Framework.Statistics sb.Append(Environment.NewLine); sb.Append( string.Format( - "Allocated to OpenSim : {0} MB" + Environment.NewLine, + "Allocated to OpenSim objects: {0} MB\n", Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0))); + sb.Append( + string.Format( + "Process memory : {0} MB\n", + Math.Round(Process.GetCurrentProcess().WorkingSet64 / 1024.0 / 1024.0))); return sb.ToString(); } diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index efa4a7ba87..e03bb7491c 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -35,7 +35,7 @@ using System.IO; using System.IO.Compression; using System.Net; using System.Net.Sockets; -using System.Reflection; +using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; @@ -81,12 +81,15 @@ namespace OpenSim.Framework private static uint nextXferID = 5000; private static Random randomClass = new Random(); + // Get a list of invalid file characters (OS dependent) private static string regexInvalidFileChars = "[" + new String(Path.GetInvalidFileNameChars()) + "]"; private static string regexInvalidPathChars = "[" + new String(Path.GetInvalidPathChars()) + "]"; private static object XferLock = new object(); - /// Thread pool used for Util.FireAndForget if - /// FireAndForgetMethod.SmartThreadPool is used + + /// + /// Thread pool used for Util.FireAndForget if FireAndForgetMethod.SmartThreadPool is used + /// private static SmartThreadPool m_ThreadPool; // Unix-epoch starts at January 1st 1970, 00:00:00 UTC. And all our times in the server are (or at least should be) in UTC. @@ -144,7 +147,6 @@ namespace OpenSim.Framework return lerp(y, lerp(x, a, b), lerp(x, c, d)); } - public static Encoding UTF8 = Encoding.UTF8; /// @@ -376,20 +378,20 @@ namespace OpenSim.Framework } return sb.ToString(); - } - - /// - /// Is the platform Windows? - /// - /// true if so, false otherwise - public static bool IsWindows() - { - PlatformID platformId = Environment.OSVersion.Platform; - - return (platformId == PlatformID.Win32NT - || platformId == PlatformID.Win32S - || platformId == PlatformID.Win32Windows - || platformId == PlatformID.WinCE); + } + + /// + /// Is the platform Windows? + /// + /// true if so, false otherwise + public static bool IsWindows() + { + PlatformID platformId = Environment.OSVersion.Platform; + + return (platformId == PlatformID.Win32NT + || platformId == PlatformID.Win32S + || platformId == PlatformID.Win32Windows + || platformId == PlatformID.WinCE); } public static bool LoadArchSpecificWindowsDll(string libraryName) @@ -1502,27 +1504,27 @@ namespace OpenSim.Framework } return data; - } - - /// - /// Used to trigger an early library load on Windows systems. - /// - /// - /// Required to get 32-bit and 64-bit processes to automatically use the - /// appropriate native library. - /// - /// - /// - [DllImport("kernel32.dll")] - public static extern IntPtr LoadLibrary(string dllToLoad); - - /// - /// Determine whether the current process is 64 bit - /// - /// true if so, false if not - public static bool Is64BitProcess() - { - return IntPtr.Size == 8; + } + + /// + /// Used to trigger an early library load on Windows systems. + /// + /// + /// Required to get 32-bit and 64-bit processes to automatically use the + /// appropriate native library. + /// + /// + /// + [DllImport("kernel32.dll")] + public static extern IntPtr LoadLibrary(string dllToLoad); + + /// + /// Determine whether the current process is 64 bit + /// + /// true if so, false if not + public static bool Is64BitProcess() + { + return IntPtr.Size == 8; } #region FireAndForget Threading Pattern @@ -1671,6 +1673,61 @@ namespace OpenSim.Framework } } + /// + /// Get a thread pool report. + /// + /// + public static string GetThreadPoolReport() + { + string threadPoolUsed = null; + int maxThreads = 0; + int minThreads = 0; + int allocatedThreads = 0; + int inUseThreads = 0; + int waitingCallbacks = 0; + int completionPortThreads = 0; + + StringBuilder sb = new StringBuilder(); + if (FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool) + { + threadPoolUsed = "SmartThreadPool"; + maxThreads = m_ThreadPool.MaxThreads; + minThreads = m_ThreadPool.MinThreads; + inUseThreads = m_ThreadPool.InUseThreads; + allocatedThreads = m_ThreadPool.ActiveThreads; + waitingCallbacks = m_ThreadPool.WaitingCallbacks; + } + else if ( + FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem + || FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem) + { + threadPoolUsed = "BuiltInThreadPool"; + ThreadPool.GetMaxThreads(out maxThreads, out completionPortThreads); + ThreadPool.GetMinThreads(out minThreads, out completionPortThreads); + int availableThreads; + ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads); + inUseThreads = maxThreads - availableThreads; + allocatedThreads = -1; + waitingCallbacks = -1; + } + + if (threadPoolUsed != null) + { + sb.AppendFormat("Thread pool used : {0}\n", threadPoolUsed); + sb.AppendFormat("Max threads : {0}\n", maxThreads); + sb.AppendFormat("Min threads : {0}\n", minThreads); + sb.AppendFormat("Allocated threads : {0}\n", allocatedThreads < 0 ? "not applicable" : allocatedThreads.ToString()); + sb.AppendFormat("In use threads : {0}\n", inUseThreads); + sb.AppendFormat("Work items waiting : {0}\n", waitingCallbacks < 0 ? "not available" : waitingCallbacks.ToString()); + } + else + { + sb.AppendFormat("Thread pool not used\n"); + } + + return sb.ToString(); + } + private static object SmartThreadPoolCallback(object o) { object[] array = (object[])o; @@ -1696,6 +1753,20 @@ namespace OpenSim.Framework } const Int32 EnvironmentTickCountMask = 0x3fffffff; + /// + /// Environment.TickCount is an int but it counts all 32 bits so it goes positive + /// and negative every 24.9 days. Subtracts the passed value (previously fetched by + /// 'EnvironmentTickCount()') and accounts for any wrapping. + /// + /// + /// + /// subtraction of passed prevValue from current Environment.TickCount + public static Int32 EnvironmentTickCountSubtract(Int32 newValue, Int32 prevValue) + { + Int32 diff = newValue - prevValue; + return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1); + } + /// /// Environment.TickCount is an int but it counts all 32 bits so it goes positive /// and negative every 24.9 days. Subtracts the passed value (previously fetched by @@ -1704,8 +1775,7 @@ namespace OpenSim.Framework /// subtraction of passed prevValue from current Environment.TickCount public static Int32 EnvironmentTickCountSubtract(Int32 prevValue) { - Int32 diff = EnvironmentTickCount() - prevValue; - return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1); + return EnvironmentTickCountSubtract(EnvironmentTickCount(), prevValue); } // Returns value of Tick Count A - TickCount B accounting for wrapping of TickCount @@ -1870,11 +1940,12 @@ namespace OpenSim.Framework #region Universal User Identifiers /// /// - /// uuid[;endpoint[;name]] - /// - /// - /// - /// + /// uuid[;endpoint[;first last[;secret]]] + /// the uuid part + /// the endpoint part (e.g. http://foo.com) + /// the first name part (e.g. Test) + /// the last name part (e.g User) + /// the secret part public static bool ParseUniversalUserIdentifier(string value, out UUID uuid, out string url, out string firstname, out string lastname, out string secret) { uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "User"; secret = string.Empty; @@ -1903,31 +1974,64 @@ namespace OpenSim.Framework } /// - /// + /// Produces a universal (HG) system-facing identifier given the information /// /// - /// uuid[;endpoint[;name]] + /// uuid[;homeURI[;first last]] public static string ProduceUserUniversalIdentifier(AgentCircuitData acircuit) { if (acircuit.ServiceURLs.ContainsKey("HomeURI")) - { - string agentsURI = acircuit.ServiceURLs["HomeURI"].ToString(); - if (!agentsURI.EndsWith("/")) - agentsURI += "/"; - - // This is ugly, but there's no other way, given that the name is changed - // in the agent circuit data for foreigners - if (acircuit.lastname.Contains("@")) - { - string[] parts = acircuit.firstname.Split(new char[] { '.' }); - if (parts.Length == 2) - return acircuit.AgentID.ToString() + ";" + agentsURI + ";" + parts[0] + " " + parts[1]; - } - return acircuit.AgentID.ToString() + ";" + agentsURI + ";" + acircuit.firstname + " " + acircuit.lastname; - } + return UniversalIdentifier(acircuit.AgentID, acircuit.firstname, acircuit.lastname, acircuit.ServiceURLs["HomeURI"].ToString()); else return acircuit.AgentID.ToString(); - } + } + + /// + /// Produces a universal (HG) system-facing identifier given the information + /// + /// UUID of the user + /// first name (e.g Test) + /// last name (e.g. User) + /// homeURI (e.g. http://foo.com) + /// a string of the form uuid[;homeURI[;first last]] + public static string UniversalIdentifier(UUID id, String firstName, String lastName, String homeURI) + { + string agentsURI = homeURI; + if (!agentsURI.EndsWith("/")) + agentsURI += "/"; + + // This is ugly, but there's no other way, given that the name is changed + // in the agent circuit data for foreigners + if (lastName.Contains("@")) + { + string[] parts = firstName.Split(new char[] { '.' }); + if (parts.Length == 2) + return id.ToString() + ";" + agentsURI + ";" + parts[0] + " " + parts[1]; + } + return id.ToString() + ";" + agentsURI + ";" + firstName + " " + lastName; + + } + + /// + /// Produces a universal (HG) user-facing name given the information + /// + /// + /// + /// + /// string of the form first.last @foo.com or first last + public static string UniversalName(String firstName, String lastName, String homeURI) + { + Uri uri = null; + try + { + uri = new Uri(homeURI); + } + catch (UriFormatException) + { + return firstName + " " + lastName; + } + return firstName + "." + lastName + " " + "@" + uri.Authority; + } #endregion } } diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs index af25da9b04..ead8f463f7 100644 --- a/OpenSim/Framework/WebUtil.cs +++ b/OpenSim/Framework/WebUtil.cs @@ -63,6 +63,31 @@ namespace OpenSim.Framework // a "long" call for warning & debugging purposes public const int LongCallTime = 500; + // dictionary of end points + private static Dictionary m_endpointSerializer = new Dictionary(); + + + private static object EndPointLock(string url) + { + System.Uri uri = new System.Uri(url); + string endpoint = string.Format("{0}:{1}",uri.Host,uri.Port); + + lock (m_endpointSerializer) + { + object eplock = null; + + if (! m_endpointSerializer.TryGetValue(endpoint,out eplock)) + { + eplock = new object(); + m_endpointSerializer.Add(endpoint,eplock); + // m_log.WarnFormat("[WEB UTIL] add a new host to end point serializer {0}",endpoint); + } + + return eplock; + } + } + + #region JSONRequest /// @@ -95,6 +120,14 @@ namespace OpenSim.Framework } public static OSDMap ServiceOSDRequest(string url, OSDMap data, string method, int timeout, bool compressed) + { + lock (EndPointLock(url)) + { + return ServiceOSDRequestWorker(url,data,method,timeout,compressed); + } + } + + private static OSDMap ServiceOSDRequestWorker(string url, OSDMap data, string method, int timeout, bool compressed) { int reqnum = m_requestNumber++; // m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method); @@ -247,6 +280,14 @@ namespace OpenSim.Framework } public static OSDMap ServiceFormRequest(string url, NameValueCollection data, int timeout) + { + lock (EndPointLock(url)) + { + return ServiceFormRequestWorker(url,data,timeout); + } + } + + private static OSDMap ServiceFormRequestWorker(string url, NameValueCollection data, int timeout) { int reqnum = m_requestNumber++; string method = (data != null && data["RequestMethod"] != null) ? data["RequestMethod"] : "unknown"; @@ -469,8 +510,13 @@ namespace OpenSim.Framework /// /// Copying begins at the streams' current positions. The positions are /// NOT reset after copying is complete. + /// NOTE!! .NET 4.0 adds the method 'Stream.CopyTo(stream, bufferSize)'. + /// This function could be replaced with that method once we move + /// totally to .NET 4.0. For versions before, this routine exists. + /// This routine used to be named 'CopyTo' but the int parameter has + /// a different meaning so this method was renamed to avoid any confusion. /// - public static int CopyTo(this Stream copyFrom, Stream copyTo, int maximumBytesToCopy) + public static int CopyStream(this Stream copyFrom, Stream copyTo, int maximumBytesToCopy) { byte[] buffer = new byte[4096]; int readBytes; diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 145875b601..59b6b2197e 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -225,12 +225,12 @@ namespace OpenSim /// private void RegisterConsoleCommands() { - m_console.Commands.AddCommand("region", false, "force update", + m_console.Commands.AddCommand("Regions", false, "force update", "force update", "Force the update of all objects on clients", HandleForceUpdate); - m_console.Commands.AddCommand("region", false, "debug packet", + m_console.Commands.AddCommand("Comms", false, "debug packet", "debug packet [ ]", "Turn on packet debugging", "If level > 255 then all incoming and outgoing packets are logged.\n" @@ -242,7 +242,7 @@ namespace OpenSim + "If an avatar name is given then only packets from that avatar are logged", Debug); - m_console.Commands.AddCommand("region", false, "debug http", + m_console.Commands.AddCommand("Comms", false, "debug http", "debug http ", "Turn on inbound http request debugging for everything except the event queue (see debug eq).", "If level >= 2 then the handler used to service the request is logged.\n" @@ -250,37 +250,37 @@ namespace OpenSim + "If level <= 0 then no extra http logging is done.\n", Debug); - m_console.Commands.AddCommand("region", false, "debug teleport", "debug teleport", "Toggle teleport route debugging", Debug); + m_console.Commands.AddCommand("Comms", false, "debug teleport", "debug teleport", "Toggle teleport route debugging", Debug); - m_console.Commands.AddCommand("region", false, "debug scene", + m_console.Commands.AddCommand("Regions", false, "debug scene", "debug scene ", "Turn on scene debugging", Debug); - m_console.Commands.AddCommand("region", false, "change region", + m_console.Commands.AddCommand("General", false, "change region", "change region ", "Change current console region", ChangeSelectedRegion); - m_console.Commands.AddCommand("region", false, "save xml", + m_console.Commands.AddCommand("Archiving", false, "save xml", "save xml", "Save a region's data in XML format", SaveXml); - m_console.Commands.AddCommand("region", false, "save xml2", + m_console.Commands.AddCommand("Archiving", false, "save xml2", "save xml2", "Save a region's data in XML2 format", SaveXml2); - m_console.Commands.AddCommand("region", false, "load xml", + m_console.Commands.AddCommand("Archiving", false, "load xml", "load xml [-newIDs [ ]]", "Load a region's data from XML format", LoadXml); - m_console.Commands.AddCommand("region", false, "load xml2", + m_console.Commands.AddCommand("Archiving", false, "load xml2", "load xml2", "Load a region's data from XML2 format", LoadXml2); - m_console.Commands.AddCommand("region", false, "save prims xml2", + m_console.Commands.AddCommand("Archiving", false, "save prims xml2", "save prims xml2 [ ]", "Save named prim to XML2", SavePrimsXml2); - m_console.Commands.AddCommand("region", false, "load oar", + m_console.Commands.AddCommand("Archiving", false, "load oar", "load oar [--merge] [--skip-assets] []", "Load a region's data from an OAR archive.", "--merge will merge the OAR with the existing scene." + Environment.NewLine @@ -289,7 +289,7 @@ namespace OpenSim + " If this is not given then the command looks for an OAR named region.oar in the current directory.", LoadOar); - m_console.Commands.AddCommand("region", false, "save oar", + m_console.Commands.AddCommand("Archiving", false, "save oar", //"save oar [-v|--version=] [-p|--profile=] []", "save oar [-h|--home=] [--noassets] [--publish] [--perm=] []", "Save a region's data to an OAR archive.", @@ -306,54 +306,54 @@ namespace OpenSim + " If this is not given then the oar is saved to region.oar in the current directory.", SaveOar); - m_console.Commands.AddCommand("region", false, "edit scale", + m_console.Commands.AddCommand("Regions", false, "edit scale", "edit scale ", "Change the scale of a named prim", HandleEditScale); - m_console.Commands.AddCommand("region", false, "kick user", + m_console.Commands.AddCommand("Users", false, "kick user", "kick user [message]", "Kick a user off the simulator", KickUserCommand); - m_console.Commands.AddCommand("region", false, "show users", + m_console.Commands.AddCommand("Users", false, "show users", "show users [full]", "Show user data for users currently on the region", "Without the 'full' option, only users actually on the region are shown." + " With the 'full' option child agents of users in neighbouring regions are also shown.", HandleShow); - m_console.Commands.AddCommand("region", false, "show connections", + m_console.Commands.AddCommand("Comms", false, "show connections", "show connections", "Show connection data", HandleShow); - m_console.Commands.AddCommand("region", false, "show circuits", + m_console.Commands.AddCommand("Comms", false, "show circuits", "show circuits", "Show agent circuit data", HandleShow); - m_console.Commands.AddCommand("region", false, "show http-handlers", + m_console.Commands.AddCommand("Comms", false, "show http-handlers", "show http-handlers", "Show all registered http handlers", HandleShow); - m_console.Commands.AddCommand("region", false, "show pending-objects", + m_console.Commands.AddCommand("Comms", false, "show pending-objects", "show pending-objects", "Show # of objects on the pending queues of all scene viewers", HandleShow); - m_console.Commands.AddCommand("region", false, "show modules", + m_console.Commands.AddCommand("General", false, "show modules", "show modules", "Show module data", HandleShow); - m_console.Commands.AddCommand("region", false, "show regions", + m_console.Commands.AddCommand("Regions", false, "show regions", "show regions", "Show region data", HandleShow); - m_console.Commands.AddCommand("region", false, "show ratings", + m_console.Commands.AddCommand("Regions", false, "show ratings", "show ratings", "Show rating data", HandleShow); - m_console.Commands.AddCommand("region", false, "backup", + m_console.Commands.AddCommand("Regions", false, "backup", "backup", "Persist currently unsaved object changes immediately instead of waiting for the normal persistence call.", RunCommand); - m_console.Commands.AddCommand("region", false, "create region", + m_console.Commands.AddCommand("Regions", false, "create region", "create region [\"region name\"] ", "Create a new region.", "The settings for \"region name\" are read from . Paths specified with are relative to your Regions directory, unless an absolute path is given." @@ -362,62 +362,57 @@ namespace OpenSim + "If does not exist, it will be created.", HandleCreateRegion); - m_console.Commands.AddCommand("region", false, "restart", + m_console.Commands.AddCommand("Regions", false, "restart", "restart", "Restart all sims in this instance", RunCommand); - m_console.Commands.AddCommand("region", false, "config set", + m_console.Commands.AddCommand("General", false, "config set", "config set
", "Set a config option. In most cases this is not useful since changed parameters are not dynamically reloaded. Neither do changed parameters persist - you will have to change a config file manually and restart.", HandleConfig); - m_console.Commands.AddCommand("region", false, "config get", + m_console.Commands.AddCommand("General", false, "config get", "config get [
] []", "Synonym for config show", HandleConfig); - m_console.Commands.AddCommand("region", false, "config show", + m_console.Commands.AddCommand("General", false, "config show", "config show [
] []", "Show config information", "If neither section nor field are specified, then the whole current configuration is printed." + Environment.NewLine + "If a section is given but not a field, then all fields in that section are printed.", HandleConfig); - m_console.Commands.AddCommand("region", false, "config save", + m_console.Commands.AddCommand("General", false, "config save", "config save ", "Save current configuration to a file at the given path", HandleConfig); - m_console.Commands.AddCommand("region", false, "command-script", + m_console.Commands.AddCommand("General", false, "command-script", "command-script