diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs index 7529e01450..ee3d7c58bc 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -2304,6 +2304,10 @@ namespace OpenSim.ApplicationPlugins.RemoteController /// UUID of the region /// region_name /// region name + /// merge + /// true if oar should be merged + /// skip-assets + /// true if assets should be skiped /// /// /// region_uuid takes precedence over @@ -2362,10 +2366,22 @@ namespace OpenSim.ApplicationPlugins.RemoteController throw new Exception(String.Format("failed to switch to region {0}", region_name)); } else throw new Exception("neither region_name nor region_uuid given"); + + bool mergeOar = false; + bool skipAssets = false; + + if ((string)requestData["merge"] == "true") + { + mergeOar = true; + } + if ((string)requestData["skip-assets"] == "true") + { + skipAssets = true; + } IRegionArchiverModule archiver = scene.RequestModuleInterface(); if (archiver != null) - archiver.DearchiveRegion(filename); + archiver.DearchiveRegion(filename, mergeOar, skipAssets, Guid.Empty); else throw new Exception("Archiver module not present for scene"); @@ -2405,6 +2421,10 @@ namespace OpenSim.ApplicationPlugins.RemoteController /// UUID of the region /// region_name /// region name + /// profile + /// profile url + /// noassets + /// true if no assets should be saved /// /// /// region_uuid takes precedence over @@ -2462,12 +2482,29 @@ namespace OpenSim.ApplicationPlugins.RemoteController } else throw new Exception("neither region_name nor region_uuid given"); + Dictionary options = new Dictionary(); + + //if (requestData.Contains("version")) + //{ + // options["version"] = (string)requestData["version"]; + //} + + if (requestData.Contains("profile")) + { + options["profile"] = (string)requestData["profile"]; + } + + if (requestData["noassets"] == "true") + { + options["noassets"] = (string)requestData["noassets"] ; + } + IRegionArchiverModule archiver = scene.RequestModuleInterface(); if (archiver != null) { scene.EventManager.OnOarFileSaved += RemoteAdminOarSaveCompleted; - archiver.ArchiveRegion(filename, new Dictionary()); + archiver.ArchiveRegion(filename, options); lock (m_saveOarLock) Monitor.Wait(m_saveOarLock,5000); scene.EventManager.OnOarFileSaved -= RemoteAdminOarSaveCompleted; } diff --git a/OpenSim/Framework/AssetBase.cs b/OpenSim/Framework/AssetBase.cs index 8e24f911c4..06e040fc11 100644 --- a/OpenSim/Framework/AssetBase.cs +++ b/OpenSim/Framework/AssetBase.cs @@ -281,6 +281,7 @@ namespace OpenSim.Framework return m_id; } + set { UUID uuid = UUID.Zero; diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 31a45e2372..0a4dcacf90 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -741,7 +741,7 @@ namespace OpenSim.Framework bool IsActive { get; set; } /// - /// Determines whether the client is logging out or not. + /// Determines whether the client is or has been removed from a given scene /// bool IsLoggingOut { get; set; } diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs index 5c3cad4e69..64b9c3ed17 100644 --- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs +++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs @@ -31,6 +31,7 @@ using System.Diagnostics; using System.IO; using System.Reflection; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Timers; using log4net; @@ -124,7 +125,6 @@ namespace OpenSim.Framework.Servers m_logFileAppender = appender; } } - } /// @@ -443,45 +443,68 @@ namespace OpenSim.Framework.Servers { string buildVersion = string.Empty; - // Add commit hash and date information if available - // The commit hash and date are stored in a file bin/.version - // This file can automatically created by a post - // commit script in the opensim git master repository or - // by issuing the follwoing command from the top level - // directory of the opensim repository - // git log -n 1 --pretty="format:%h: %ci" >bin/.version - // For the full git commit hash use %H instead of %h - // // The subversion information is deprecated and will be removed at a later date // Add subversion revision information if available // Try file "svn_revision" in the current directory first, then the .svn info. // This allows to make the revision available in simulators not running from the source tree. // FIXME: Making an assumption about the directory we're currently in - we do this all over the place // elsewhere as well + string gitDir = "../.git/"; + string gitRefPointerPath = gitDir + "HEAD"; + string svnRevisionFileName = "svn_revision"; string svnFileName = ".svn/entries"; - string gitCommitFileName = ".version"; + string manualVersionFileName = ".version"; string inputLine; int strcmp; - if (File.Exists(gitCommitFileName)) + if (File.Exists(manualVersionFileName)) { - StreamReader CommitFile = File.OpenText(gitCommitFileName); - buildVersion = CommitFile.ReadLine(); - CommitFile.Close(); + using (StreamReader CommitFile = File.OpenText(manualVersionFileName)) + buildVersion = CommitFile.ReadLine(); + m_version += buildVersion ?? ""; } + else if (File.Exists(gitRefPointerPath)) + { +// m_log.DebugFormat("[OPENSIM]: Found {0}", gitRefPointerPath); - // Remove the else logic when subversion mirror is no longer used + string rawPointer = ""; + + using (StreamReader pointerFile = File.OpenText(gitRefPointerPath)) + rawPointer = pointerFile.ReadLine(); + +// m_log.DebugFormat("[OPENSIM]: rawPointer [{0}]", rawPointer); + + Match m = Regex.Match(rawPointer, "^ref: (.+)$"); + + if (m.Success) + { +// m_log.DebugFormat("[OPENSIM]: Matched [{0}]", m.Groups[1].Value); + + string gitRef = m.Groups[1].Value; + string gitRefPath = gitDir + gitRef; + if (File.Exists(gitRefPath)) + { +// m_log.DebugFormat("[OPENSIM]: Found gitRefPath [{0}]", gitRefPath); + + using (StreamReader refFile = File.OpenText(gitRefPath)) + { + string gitHash = refFile.ReadLine(); + m_version += gitHash.Substring(0, 7); + } + } + } + } else { + // Remove the else logic when subversion mirror is no longer used if (File.Exists(svnRevisionFileName)) { StreamReader RevisionFile = File.OpenText(svnRevisionFileName); buildVersion = RevisionFile.ReadLine(); buildVersion.Trim(); RevisionFile.Close(); - } if (string.IsNullOrEmpty(buildVersion) && File.Exists(svnFileName)) diff --git a/OpenSim/Region/Application/ConfigurationLoader.cs b/OpenSim/Region/Application/ConfigurationLoader.cs index 2d81ea8432..d19852b1ee 100644 --- a/OpenSim/Region/Application/ConfigurationLoader.cs +++ b/OpenSim/Region/Application/ConfigurationLoader.cs @@ -328,7 +328,7 @@ namespace OpenSim config.Set("meshing", "Meshmerizer"); config.Set("physical_prim", true); config.Set("see_into_this_sim_from_neighbor", true); - config.Set("serverside_object_permissions", false); + config.Set("serverside_object_permissions", true); config.Set("storage_plugin", "OpenSim.Data.SQLite.dll"); config.Set("storage_connection_string", "URI=file:OpenSim.db,version=3"); config.Set("storage_prim_inventories", true); diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs index 9ef5bc95c3..d85d727986 100644 --- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs @@ -86,6 +86,8 @@ namespace Flotsam.RegionModules.AssetCache private List m_CurrentlyWriting = new List(); #endif + private bool m_FileCacheEnabled = true; + private ExpiringCache m_MemoryCache; private bool m_MemoryCacheEnabled = false; @@ -146,6 +148,7 @@ namespace Flotsam.RegionModules.AssetCache } else { + m_FileCacheEnabled = assetConfig.GetBoolean("FileCacheEnabled", m_FileCacheEnabled); m_CacheDirectory = assetConfig.GetString("CacheDirectory", m_DefaultCacheDirectory); m_MemoryCacheEnabled = assetConfig.GetBoolean("MemoryCacheEnabled", m_MemoryCacheEnabled); @@ -173,7 +176,7 @@ namespace Flotsam.RegionModules.AssetCache m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory); - if ((m_FileExpiration > TimeSpan.Zero) && (m_FileExpirationCleanupTimer > TimeSpan.Zero)) + if (m_FileCacheEnabled && (m_FileExpiration > TimeSpan.Zero) && (m_FileExpirationCleanupTimer > TimeSpan.Zero)) { m_CacheCleanTimer = new System.Timers.Timer(m_FileExpirationCleanupTimer.TotalMilliseconds); m_CacheCleanTimer.AutoReset = true; @@ -226,7 +229,6 @@ namespace Flotsam.RegionModules.AssetCache if (m_AssetService == null) { m_AssetService = scene.RequestModuleInterface(); - } } } @@ -250,18 +252,15 @@ namespace Flotsam.RegionModules.AssetCache private void UpdateMemoryCache(string key, AssetBase asset) { - if (m_MemoryCacheEnabled) - m_MemoryCache.AddOrUpdate(key, asset, m_MemoryExpiration); + m_MemoryCache.AddOrUpdate(key, asset, m_MemoryExpiration); } - public void Cache(AssetBase asset) + private void UpdateFileCache(string key, AssetBase asset) { // TODO: Spawn this off to some seperate thread to do the actual writing if (asset != null) { - UpdateMemoryCache(asset.ID, asset); - - string filename = GetFileName(asset.ID); + string filename = GetFileName(key); try { @@ -278,8 +277,8 @@ namespace Flotsam.RegionModules.AssetCache catch { } - } else { - + } else { + // Once we start writing, make sure we flag that we're writing // that object to the cache so that we don't try to write the // same file multiple times. @@ -319,78 +318,118 @@ namespace Flotsam.RegionModules.AssetCache } } + public void Cache(AssetBase asset) + { + // TODO: Spawn this off to some seperate thread to do the actual writing + if (asset != null) + { + //m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Caching asset with id {0}", asset.ID); + + if (m_MemoryCacheEnabled) + UpdateMemoryCache(asset.ID, asset); + + if (m_FileCacheEnabled) + UpdateFileCache(asset.ID, asset); + } + } + + /// + /// Try to get an asset from the in-memory cache. + /// + /// + /// + private AssetBase GetFromMemoryCache(string id) + { + AssetBase asset = null; + + if (m_MemoryCache.TryGetValue(id, out asset)) + m_MemoryHits++; + + return asset; + } + + /// + /// Try to get an asset from the file cache. + /// + /// + /// + private AssetBase GetFromFileCache(string id) + { + AssetBase asset = null; + + string filename = GetFileName(id); + if (File.Exists(filename)) + { + FileStream stream = null; + try + { + stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read); + BinaryFormatter bformatter = new BinaryFormatter(); + + asset = (AssetBase)bformatter.Deserialize(stream); + + UpdateMemoryCache(id, asset); + + m_DiskHits++; + } + catch (System.Runtime.Serialization.SerializationException e) + { + LogException(e); + + // If there was a problem deserializing the asset, the asset may + // either be corrupted OR was serialized under an old format + // {different version of AssetBase} -- we should attempt to + // delete it and re-cache + File.Delete(filename); + } + catch (Exception e) + { + LogException(e); + } + finally + { + if (stream != null) + stream.Close(); + } + } + + +#if WAIT_ON_INPROGRESS_REQUESTS + // Check if we're already downloading this asset. If so, try to wait for it to + // download. + if (m_WaitOnInprogressTimeout > 0) + { + m_RequestsForInprogress++; + + ManualResetEvent waitEvent; + if (m_CurrentlyWriting.TryGetValue(filename, out waitEvent)) + { + waitEvent.WaitOne(m_WaitOnInprogressTimeout); + return Get(id); + } + } +#else + // Track how often we have the problem that an asset is requested while + // it is still being downloaded by a previous request. + if (m_CurrentlyWriting.Contains(filename)) + { + m_RequestsForInprogress++; + } +#endif + + return asset; + } + public AssetBase Get(string id) { m_Requests++; AssetBase asset = null; - if (m_MemoryCacheEnabled && m_MemoryCache.TryGetValue(id, out asset)) - { - m_MemoryHits++; - } - else - { - string filename = GetFileName(id); - if (File.Exists(filename)) - { - FileStream stream = null; - try - { - stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read); - BinaryFormatter bformatter = new BinaryFormatter(); - - asset = (AssetBase)bformatter.Deserialize(stream); - - UpdateMemoryCache(id, asset); - - m_DiskHits++; - } - catch (System.Runtime.Serialization.SerializationException e) - { - LogException(e); - - // If there was a problem deserializing the asset, the asset may - // either be corrupted OR was serialized under an old format - // {different version of AssetBase} -- we should attempt to - // delete it and re-cache - File.Delete(filename); - } - catch (Exception e) - { - LogException(e); - } - finally - { - if (stream != null) - stream.Close(); - } - } - - -#if WAIT_ON_INPROGRESS_REQUESTS - // Check if we're already downloading this asset. If so, try to wait for it to - // download. - if (m_WaitOnInprogressTimeout > 0) - { - m_RequestsForInprogress++; - - ManualResetEvent waitEvent; - if (m_CurrentlyWriting.TryGetValue(filename, out waitEvent)) - { - waitEvent.WaitOne(m_WaitOnInprogressTimeout); - return Get(id); - } - } -#else - // Track how often we have the problem that an asset is requested while - // it is still being downloaded by a previous request. - if (m_CurrentlyWriting.Contains(filename)) - { - m_RequestsForInprogress++; - } -#endif - } + if (m_MemoryCacheEnabled) + asset = GetFromMemoryCache(id); + else if (m_FileCacheEnabled) + asset = GetFromFileCache(id); if (((m_LogLevel >= 1)) && (m_HitRateDisplay != 0) && (m_Requests % m_HitRateDisplay == 0)) { @@ -424,10 +463,13 @@ namespace Flotsam.RegionModules.AssetCache try { - string filename = GetFileName(id); - if (File.Exists(filename)) + if (m_FileCacheEnabled) { - File.Delete(filename); + string filename = GetFileName(id); + if (File.Exists(filename)) + { + File.Delete(filename); + } } if (m_MemoryCacheEnabled) @@ -442,11 +484,14 @@ namespace Flotsam.RegionModules.AssetCache public void Clear() { if (m_LogLevel >= 2) - m_log.Debug("[FLOTSAM ASSET CACHE]: Clearing Cache."); + m_log.Debug("[FLOTSAM ASSET CACHE]: Clearing caches."); - foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) + if (m_FileCacheEnabled) { - Directory.Delete(dir); + foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) + { + Directory.Delete(dir); + } } if (m_MemoryCacheEnabled) @@ -481,9 +526,9 @@ namespace Flotsam.RegionModules.AssetCache /// removes empty tier directories. /// /// + /// private void CleanExpiredFiles(string dir, DateTime purgeLine) { - foreach (string file in Directory.GetFiles(dir)) { if (File.GetLastAccessTime(file) < purgeLine) @@ -721,18 +766,28 @@ namespace Flotsam.RegionModules.AssetCache switch (cmd) { case "status": - m_log.InfoFormat("[FLOTSAM ASSET CACHE] Memory Cache : {0} assets", m_MemoryCache.Count); + if (m_MemoryCacheEnabled) + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Memory Cache : {0} assets", m_MemoryCache.Count); + else + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Memory cache disabled"); - int fileCount = GetFileCacheCount(m_CacheDirectory); - m_log.InfoFormat("[FLOTSAM ASSET CACHE] File Cache : {0} assets", fileCount); - - foreach (string s in Directory.GetFiles(m_CacheDirectory, "*.fac")) + if (m_FileCacheEnabled) { - m_log.Info("[FLOTSAM ASSET CACHE] Deep Scans were performed on the following regions:"); - - string RegionID = s.Remove(0,s.IndexOf("_")).Replace(".fac",""); - DateTime RegionDeepScanTMStamp = File.GetLastWriteTime(s); - m_log.InfoFormat("[FLOTSAM ASSET CACHE] Region: {0}, {1}", RegionID, RegionDeepScanTMStamp.ToString("MM/dd/yyyy hh:mm:ss")); + int fileCount = GetFileCacheCount(m_CacheDirectory); + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: File Cache : {0} assets", fileCount); + + foreach (string s in Directory.GetFiles(m_CacheDirectory, "*.fac")) + { + m_log.Info("[FLOTSAM ASSET CACHE]: Deep Scans were performed on the following regions:"); + + string RegionID = s.Remove(0,s.IndexOf("_")).Replace(".fac",""); + DateTime RegionDeepScanTMStamp = File.GetLastWriteTime(s); + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Region: {0}, {1}", RegionID, RegionDeepScanTMStamp.ToString("MM/dd/yyyy hh:mm:ss")); + } + } + else + { + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: File cache disabled"); } break; @@ -740,7 +795,7 @@ namespace Flotsam.RegionModules.AssetCache case "clear": if (cmdparams.Length < 2) { - m_log.Warn("[FLOTSAM ASSET CACHE] Usage is fcache clear [file] [memory]"); + m_log.Warn("[FLOTSAM ASSET CACHE]: Usage is fcache clear [file] [memory]"); break; } @@ -761,36 +816,48 @@ namespace Flotsam.RegionModules.AssetCache if (clearMemory) { - m_MemoryCache.Clear(); - m_log.Info("[FLOTSAM ASSET CACHE] Memory cache cleared."); + if (m_MemoryCacheEnabled) + { + m_MemoryCache.Clear(); + m_log.Info("[FLOTSAM ASSET CACHE]: Memory cache cleared."); + } + else + { + m_log.Info("[FLOTSAM ASSET CACHE]: Memory cache not enabled."); + } } if (clearFile) { - ClearFileCache(); - m_log.Info("[FLOTSAM ASSET CACHE] File cache cleared."); + if (m_FileCacheEnabled) + { + ClearFileCache(); + m_log.Info("[FLOTSAM ASSET CACHE]: File cache cleared."); + } + else + { + m_log.Info("[FLOTSAM ASSET CACHE]: File cache not enabled."); + } } break; case "assets": - m_log.Info("[FLOTSAM ASSET CACHE] Caching all assets, in all scenes."); + m_log.Info("[FLOTSAM ASSET CACHE]: Caching all assets, in all scenes."); Util.FireAndForget(delegate { int assetsCached = CacheScenes(); - m_log.InfoFormat("[FLOTSAM ASSET CACHE] Completed Scene Caching, {0} assets found.", assetsCached); + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Completed Scene Caching, {0} assets found.", assetsCached); }); break; case "expire": - - if (cmdparams.Length < 3) { - m_log.InfoFormat("[FLOTSAM ASSET CACHE] Invalid parameters for Expire, please specify a valid date & time", cmd); + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Invalid parameters for Expire, please specify a valid date & time", cmd); break; } @@ -808,26 +875,28 @@ namespace Flotsam.RegionModules.AssetCache if (!DateTime.TryParse(s_expirationDate, out expirationDate)) { - m_log.InfoFormat("[FLOTSAM ASSET CACHE] {0} is not a valid date & time", cmd); + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0} is not a valid date & time", cmd); break; } - CleanExpiredFiles(m_CacheDirectory, expirationDate); + if (m_FileCacheEnabled) + CleanExpiredFiles(m_CacheDirectory, expirationDate); + else + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: File cache not active, not clearing."); break; default: - m_log.InfoFormat("[FLOTSAM ASSET CACHE] Unknown command {0}", cmd); + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Unknown command {0}", cmd); break; } } else if (cmdparams.Length == 1) { - m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache status - Display cache status"); - m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache clearmem - Remove all assets cached in memory"); - m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache clearfile - Remove all assets cached on disk"); - m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache cachescenes - Attempt a deep cache of all assets in all scenes"); - m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache - Purge assets older then the specified date & time"); - + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache status - Display cache status"); + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache clearmem - Remove all assets cached in memory"); + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache clearfile - Remove all assets cached on disk"); + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache cachescenes - Attempt a deep cache of all assets in all scenes"); + m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache - Purge assets older then the specified date & time"); } } diff --git a/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs b/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs new file mode 100644 index 0000000000..63b0c311f1 --- /dev/null +++ b/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs @@ -0,0 +1,127 @@ +/* + * 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.IO; +using System.Reflection; +using System.Threading; +using log4net.Config; +using Nini.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenMetaverse.Assets; +using Flotsam.RegionModules.AssetCache; +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Scenes.Serialization; +using OpenSim.Tests.Common; +using OpenSim.Tests.Common.Mock; + +namespace OpenSim.Region.CoreModules.Asset.Tests +{ + /// + /// At the moment we're only test the in-memory part of the FlotsamAssetCache. This is a considerable weakness. + /// + [TestFixture] + public class FlotsamAssetCacheTests + { + protected TestScene m_scene; + protected FlotsamAssetCache m_cache; + + [SetUp] + public void SetUp() + { + IConfigSource config = new IniConfigSource(); + + config.AddConfig("Modules"); + config.Configs["Modules"].Set("AssetCaching", "FlotsamAssetCache"); + config.AddConfig("AssetCache"); + config.Configs["AssetCache"].Set("FileCacheEnabled", "false"); + config.Configs["AssetCache"].Set("MemoryCacheEnabled", "true"); + + m_cache = new FlotsamAssetCache(); + m_scene = SceneSetupHelpers.SetupScene(); + SceneSetupHelpers.SetupSceneModules(m_scene, config, m_cache); + } + + [Test] + public void TestCacheAsset() + { + TestHelper.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + AssetBase asset = AssetHelpers.CreateAsset(); + asset.ID = TestHelper.ParseTail(0x1).ToString(); + + // Check we don't get anything before the asset is put in the cache + AssetBase retrievedAsset = m_cache.Get(asset.ID.ToString()); + Assert.That(retrievedAsset, Is.Null); + + m_cache.Store(asset); + + // Check that asset is now in cache + retrievedAsset = m_cache.Get(asset.ID.ToString()); + Assert.That(retrievedAsset, Is.Not.Null); + Assert.That(retrievedAsset.ID, Is.EqualTo(asset.ID)); + } + + [Test] + public void TestExpireAsset() + { + TestHelper.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + AssetBase asset = AssetHelpers.CreateAsset(); + asset.ID = TestHelper.ParseTail(0x2).ToString(); + + m_cache.Store(asset); + + m_cache.Expire(asset.ID); + + AssetBase retrievedAsset = m_cache.Get(asset.ID.ToString()); + Assert.That(retrievedAsset, Is.Null); + } + + [Test] + public void TestClearCache() + { + TestHelper.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + AssetBase asset = AssetHelpers.CreateAsset(); + asset.ID = TestHelper.ParseTail(0x2).ToString(); + + m_cache.Store(asset); + + m_cache.Clear(); + + AssetBase retrievedAsset = m_cache.Get(asset.ID.ToString()); + Assert.That(retrievedAsset, Is.Null); + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/World/Land/LandObject.cs b/OpenSim/Region/CoreModules/World/Land/LandObject.cs index 2a6d362833..04b20d44df 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandObject.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandObject.cs @@ -449,7 +449,7 @@ namespace OpenSim.Region.CoreModules.World.Land public bool IsBannedFromLand(UUID avatar) { - if (m_scene.Permissions.IsAdministrator(avatar)) + if (m_scene.Permissions.CanEditParcelProperties(avatar, this, 0)) return false; if (m_scene.RegionInfo.EstateSettings.IsEstateManager(avatar)) @@ -463,7 +463,7 @@ namespace OpenSim.Region.CoreModules.World.Land if (e.AgentID == avatar && e.Flags == AccessList.Ban) return true; return false; - }) != -1 && LandData.OwnerID != avatar) + }) != -1) { return true; } @@ -473,7 +473,7 @@ namespace OpenSim.Region.CoreModules.World.Land public bool IsRestrictedFromLand(UUID avatar) { - if (m_scene.Permissions.IsAdministrator(avatar)) + if (m_scene.Permissions.CanEditParcelProperties(avatar, this, 0)) return false; if (m_scene.RegionInfo.EstateSettings.IsEstateManager(avatar)) @@ -487,7 +487,7 @@ namespace OpenSim.Region.CoreModules.World.Land if (e.AgentID == avatar && e.Flags == AccessList.Access) return true; return false; - }) == -1 && LandData.OwnerID != avatar) + }) == -1) { if (!HasGroupAccess(avatar)) { diff --git a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs index 43b37c7d35..2c7843f9ff 100644 --- a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs +++ b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs @@ -134,7 +134,7 @@ namespace OpenSim.Region.CoreModules.World.Permissions return; m_allowGridGods = myConfig.GetBoolean("allow_grid_gods", false); - m_bypassPermissions = !myConfig.GetBoolean("serverside_object_permissions", false); + m_bypassPermissions = !myConfig.GetBoolean("serverside_object_permissions", true); m_propagatePermissions = myConfig.GetBoolean("propagate_permissions", true); m_RegionOwnerIsGod = myConfig.GetBoolean("region_owner_is_god", true); m_RegionManagerIsGod = myConfig.GetBoolean("region_manager_is_god", false); diff --git a/OpenSim/Region/CoreModules/Avatar/NPC/INPCModule.cs b/OpenSim/Region/Framework/Interfaces/INPCModule.cs similarity index 97% rename from OpenSim/Region/CoreModules/Avatar/NPC/INPCModule.cs rename to OpenSim/Region/Framework/Interfaces/INPCModule.cs index cd2fe4fc85..21a755f29a 100644 --- a/OpenSim/Region/CoreModules/Avatar/NPC/INPCModule.cs +++ b/OpenSim/Region/Framework/Interfaces/INPCModule.cs @@ -28,7 +28,7 @@ using OpenMetaverse; using OpenSim.Region.Framework.Scenes; -namespace OpenSim.Region.CoreModules.Avatar.NPC +namespace OpenSim.Region.Framework.Interfaces { public interface INPCModule { diff --git a/OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs b/OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs new file mode 100644 index 0000000000..06cd14b553 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs @@ -0,0 +1,156 @@ +/* + * 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.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.Framework.Scenes +{ + class FetchHolder + { + public IClientAPI Client { get; private set; } + public UUID ItemID { get; private set; } + + public FetchHolder(IClientAPI client, UUID itemID) + { + Client = client; + ItemID = itemID; + } + } + + /// + /// Send FetchInventoryReply information to clients asynchronously on a single thread rather than asynchronously via + /// multiple threads. + /// + /// + /// If the main root inventory is right-clicked on a version 1 viewer for a user with a large inventory, a very + /// very large number of FetchInventory requests are sent to the simulator. Each is handled on a separate thread + /// by the IClientAPI, but the sheer number of requests overwhelms the number of threads available and ends up + /// freezing the inbound packet handling. + /// + /// This class makes the first FetchInventory packet thread process the queue. If new requests come + /// in while it is processing, then the subsequent threads just add the requests and leave it to the original + /// thread to process them. + /// + /// This might slow down outbound packets but these are limited by the IClientAPI outbound queues + /// anyway. + /// + /// It might be possible to ignore FetchInventory requests altogether, particularly as they are redundant wrt to + /// FetchInventoryDescendents requests, but this would require more investigation. + /// + public class AsyncInventorySender + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected Scene m_scene; + + /// + /// Queues fetch requests + /// + Queue m_fetchHolder = new Queue(); + + /// + /// Signal whether a queue is currently being processed or not. + /// + protected volatile bool m_processing; + + public AsyncInventorySender(Scene scene) + { + m_processing = false; + m_scene = scene; + } + + /// + /// Handle a fetch inventory request from the client + /// + /// + /// + /// + public void HandleFetchInventory(IClientAPI remoteClient, UUID itemID, UUID ownerID) + { + lock (m_fetchHolder) + { +// m_log.DebugFormat( +// "[ASYNC INVENTORY SENDER]: Putting request from {0} for {1} on queue", remoteClient.Name, itemID); + + m_fetchHolder.Enqueue(new FetchHolder(remoteClient, itemID)); + } + + if (!m_processing) + { + m_processing = true; + ProcessQueue(); + } + } + + /// + /// Process the queue of fetches + /// + protected void ProcessQueue() + { + FetchHolder fh = null; + + while (true) + { + lock (m_fetchHolder) + { +// m_log.DebugFormat("[ASYNC INVENTORY SENDER]: {0} items left to process", m_fetchHolder.Count); + + if (m_fetchHolder.Count == 0) + { + m_processing = false; + return; + } + else + { + fh = m_fetchHolder.Dequeue(); + } + } + + if (fh.Client.IsLoggingOut) + continue; + +// m_log.DebugFormat( +// "[ASYNC INVENTORY SENDER]: Handling request from {0} for {1} on queue", fh.Client.Name, fh.ItemID); + + InventoryItemBase item = new InventoryItemBase(fh.ItemID, fh.Client.AgentId); + item = m_scene.InventoryService.GetItem(item); + + if (item != null) + fh.Client.SendInventoryItemDetails(item.Owner, item); + + // TODO: Possibly log any failure + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 28c0276a40..dbefb4a0a3 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -51,6 +51,11 @@ namespace OpenSim.Region.Framework.Scenes /// protected AsyncSceneObjectGroupDeleter m_asyncSceneObjectDeleter; + /// + /// Allows inventory details to be sent to clients asynchronously + /// + protected AsyncInventorySender m_asyncInventorySender; + /// /// Start all the scripts in the scene which should be started. /// diff --git a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs index e2d7208ff7..44472b2cb7 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs @@ -461,31 +461,6 @@ namespace OpenSim.Region.Framework.Scenes } ); } - - - /// - /// Handle a fetch inventory request from the client - /// - /// - /// - /// - public void HandleFetchInventory(IClientAPI remoteClient, UUID itemID, UUID ownerID) - { - if (LibraryService != null && LibraryService.LibraryRootFolder != null && ownerID == LibraryService.LibraryRootFolder.Owner) - { - //m_log.Debug("request info for library item"); - return; - } - - InventoryItemBase item = new InventoryItemBase(itemID, remoteClient.AgentId); - item = InventoryService.GetItem(item); - - if (item != null) - { - remoteClient.SendInventoryItemDetails(ownerID, item); - } - // else shouldn't we send an alert message? - } /// /// Tell the client about the various child items and folders contained in the requested folder. diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 253adae92d..e02a866c4a 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -604,6 +604,8 @@ namespace OpenSim.Region.Framework.Scenes m_asyncSceneObjectDeleter = new AsyncSceneObjectGroupDeleter(this); m_asyncSceneObjectDeleter.Enabled = true; + m_asyncInventorySender = new AsyncInventorySender(this); + #region Region Settings // Load region settings @@ -2866,14 +2868,13 @@ namespace OpenSim.Region.Framework.Scenes public virtual void SubscribeToClientInventoryEvents(IClientAPI client) { - client.OnLinkInventoryItem += HandleLinkInventoryItem; client.OnCreateNewInventoryFolder += HandleCreateInventoryFolder; client.OnUpdateInventoryFolder += HandleUpdateInventoryFolder; client.OnMoveInventoryFolder += HandleMoveInventoryFolder; // 2; //!! client.OnFetchInventoryDescendents += HandleFetchInventoryDescendents; client.OnPurgeInventoryDescendents += HandlePurgeInventoryDescendents; // 2; //!! - client.OnFetchInventory += HandleFetchInventory; + client.OnFetchInventory += m_asyncInventorySender.HandleFetchInventory; client.OnUpdateInventoryItem += UpdateInventoryItemAsset; client.OnCopyInventoryItem += CopyInventoryItem; client.OnMoveItemsAndLeaveCopy += MoveInventoryItemsLeaveCopy; @@ -2993,13 +2994,12 @@ namespace OpenSim.Region.Framework.Scenes public virtual void UnSubscribeToClientInventoryEvents(IClientAPI client) { - client.OnCreateNewInventoryFolder -= HandleCreateInventoryFolder; client.OnUpdateInventoryFolder -= HandleUpdateInventoryFolder; client.OnMoveInventoryFolder -= HandleMoveInventoryFolder; // 2; //!! client.OnFetchInventoryDescendents -= HandleFetchInventoryDescendents; client.OnPurgeInventoryDescendents -= HandlePurgeInventoryDescendents; // 2; //!! - client.OnFetchInventory -= HandleFetchInventory; + client.OnFetchInventory -= m_asyncInventorySender.HandleFetchInventory; client.OnUpdateInventoryItem -= UpdateInventoryItemAsset; client.OnCopyInventoryItem -= CopyInventoryItem; client.OnMoveInventoryItem -= MoveInventoryItem; diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs index 48d236fc58..3cdd06d52c 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs @@ -34,7 +34,6 @@ using Nini.Config; using OpenMetaverse; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; -using OpenSim.Region.CoreModules.Avatar.NPC; using OpenSim.Framework; using Timer=System.Timers.Timer; using OpenSim.Services.Interfaces; @@ -47,31 +46,11 @@ namespace OpenSim.Region.OptionalModules.World.NPC // private const bool m_enabled = false; - private Mutex m_createMutex; - private Timer m_timer; - private Dictionary m_avatars = new Dictionary(); private Dictionary m_appearanceCache = new Dictionary(); - // Timer vars. - private bool p_inUse = false; - private readonly object p_lock = new object(); - // Private Temporary Variables. - private string p_firstname; - private string p_lastname; - private Vector3 p_position; - private Scene p_scene; - private UUID p_cloneAppearanceFrom; - private UUID p_returnUuid; - public void Initialise(Scene scene, IConfigSource source) { - m_createMutex = new Mutex(false); - - m_timer = new Timer(500); - m_timer.Elapsed += m_timer_Elapsed; - m_timer.Start(); - scene.RegisterModuleInterface(this); } @@ -90,35 +69,53 @@ namespace OpenSim.Region.OptionalModules.World.NPC return new AvatarAppearance(); } - public UUID CreateNPC(string firstname, string lastname,Vector3 position, Scene scene, UUID cloneAppearanceFrom) + public UUID CreateNPC(string firstname, string lastname, Vector3 position, Scene scene, UUID cloneAppearanceFrom) { + NPCAvatar npcAvatar = new NPCAvatar(firstname, lastname, position, scene); + npcAvatar.CircuitCode = (uint)Util.RandomClass.Next(0, int.MaxValue); + m_log.DebugFormat( - "[NPC MODULE]: Queueing request to create NPC {0} {1} at {2} in {3} cloning appearance of {4}", - firstname, lastname, position, scene.RegionInfo.RegionName, cloneAppearanceFrom); + "[NPC MODULE]: Creating NPC {0} {1} {2} at {3} in {4}", + firstname, lastname, npcAvatar.AgentId, position, scene.RegionInfo.RegionName); - // Block. - m_createMutex.WaitOne(); + AgentCircuitData acd = new AgentCircuitData(); + acd.AgentID = npcAvatar.AgentId; + acd.firstname = firstname; + acd.lastname = lastname; + acd.ServiceURLs = new Dictionary(); - // Copy Temp Variables for Timer to pick up. - lock (p_lock) + AvatarAppearance originalAppearance = GetAppearance(cloneAppearanceFrom, scene); + AvatarAppearance npcAppearance = new AvatarAppearance(originalAppearance, true); + acd.Appearance = npcAppearance; + + scene.AuthenticateHandler.AddNewCircuit(npcAvatar.CircuitCode, acd); + scene.AddNewClient(npcAvatar); + + ScenePresence sp; + if (scene.TryGetScenePresence(npcAvatar.AgentId, out sp)) { - p_firstname = firstname; - p_lastname = lastname; - p_position = position; - p_scene = scene; - p_cloneAppearanceFrom = cloneAppearanceFrom; - p_inUse = true; - p_returnUuid = UUID.Zero; + m_log.DebugFormat( + "[NPC MODULE]: Successfully retrieved scene presence for NPC {0} {1}", sp.Name, sp.UUID); + + // Shouldn't call this - temporary. + sp.CompleteMovement(npcAvatar); + +// sp.SendAppearanceToAllOtherAgents(); +// +// // Send animations back to the avatar as well +// sp.Animator.SendAnimPack(); + } + else + { + m_log.WarnFormat("[NPC MODULE]: Could not find scene presence for NPC {0} {1}", sp.Name, sp.UUID); } - while (p_returnUuid == UUID.Zero) - { - Thread.Sleep(250); - } + lock (m_avatars) + m_avatars.Add(npcAvatar.AgentId, npcAvatar); - m_createMutex.ReleaseMutex(); + m_log.DebugFormat("[NPC MODULE]: Created NPC with id {0}", npcAvatar.AgentId); - return p_returnUuid; + return npcAvatar.AgentId; } public void Autopilot(UUID agentID, Scene scene, Vector3 pos) @@ -157,69 +154,6 @@ namespace OpenSim.Region.OptionalModules.World.NPC } } - void m_timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) - { - try - { - lock (p_lock) - { - if (p_inUse) - { - p_inUse = false; - - NPCAvatar npcAvatar = new NPCAvatar(p_firstname, p_lastname, p_position, p_scene); - npcAvatar.CircuitCode = (uint) Util.RandomClass.Next(0, int.MaxValue); - - m_log.DebugFormat( - "[NPC MODULE]: Creating NPC {0} {1} {2} at {3} in {4}", - p_firstname, p_lastname, npcAvatar.AgentId, p_position, p_scene.RegionInfo.RegionName); - - AgentCircuitData acd = new AgentCircuitData(); - acd.AgentID = npcAvatar.AgentId; - acd.firstname = p_firstname; - acd.lastname = p_lastname; - acd.ServiceURLs = new Dictionary(); - - AvatarAppearance originalAppearance = GetAppearance(p_cloneAppearanceFrom, p_scene); - AvatarAppearance npcAppearance = new AvatarAppearance(originalAppearance, true); - acd.Appearance = npcAppearance; - - p_scene.AuthenticateHandler.AddNewCircuit(npcAvatar.CircuitCode, acd); - p_scene.AddNewClient(npcAvatar); - - ScenePresence sp; - if (p_scene.TryGetScenePresence(npcAvatar.AgentId, out sp)) - { - m_log.DebugFormat( - "[NPC MODULE]: Successfully retrieved scene presence for NPC {0} {1}", sp.Name, sp.UUID); - - // Shouldn't call this - temporary. - sp.CompleteMovement(npcAvatar); - - // sp.SendAppearanceToAllOtherAgents(); - // - // // Send animations back to the avatar as well - // sp.Animator.SendAnimPack(); - } - else - { - m_log.WarnFormat("[NPC MODULE]: Could not find scene presence for NPC {0} {1}", sp.Name, sp.UUID); - } - - m_avatars.Add(npcAvatar.AgentId, npcAvatar); - - p_returnUuid = npcAvatar.AgentId; - - m_log.DebugFormat("[NPC MODULE]: Created NPC with id {0}", p_returnUuid); - } - } - } - catch (Exception ex) - { - m_log.ErrorFormat("[NPC MODULE]: NPC creation failed with exception {0} {1}", ex.Message, ex.StackTrace); - } - } - public void PostInitialise() { } @@ -238,4 +172,4 @@ namespace OpenSim.Region.OptionalModules.World.NPC get { return true; } } } -} +} \ No newline at end of file diff --git a/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs b/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs new file mode 100644 index 0000000000..899e721767 --- /dev/null +++ b/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs @@ -0,0 +1,71 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Reflection; +using Nini.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Region.CoreModules.ServiceConnectorsOut.Avatar; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.AvatarService; +using OpenSim.Tests.Common; +using OpenSim.Tests.Common.Mock; + +namespace OpenSim.Region.OptionalModules.World.NPC.Tests +{ + [TestFixture] + public class NPCModuleTests + { + [Test] + public void TestCreate() + { + TestHelper.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + IConfigSource config = new IniConfigSource(); + + config.AddConfig("Modules"); + config.Configs["Modules"].Set("AvatarServices", "LocalAvatarServicesConnector"); + config.AddConfig("AvatarService"); + config.Configs["AvatarService"].Set("LocalServiceModule", "OpenSim.Services.AvatarService.dll:AvatarService"); + config.Configs["AvatarService"].Set("StorageProvider", "OpenSim.Data.Null.dll"); + + TestScene scene = SceneSetupHelpers.SetupScene(); + SceneSetupHelpers.SetupSceneModules(scene, config, new NPCModule(), new LocalAvatarServicesConnector()); + + INPCModule npcModule = scene.RequestModuleInterface(); + UUID npcId = npcModule.CreateNPC("John", "Smith", new Vector3(128, 128, 30), scene, UUID.Zero); + + ScenePresence npc = scene.GetScenePresence(npcId); + Assert.That(npc, Is.Not.Null); + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs index 99b2d8477a..a5fe45b216 100644 --- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs +++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs @@ -291,7 +291,14 @@ namespace OpenSim.Region.Physics.Meshing { try { - meshOsd = (OSDMap)OSDParser.DeserializeLLSDBinary(data); + OSD osd = OSDParser.DeserializeLLSDBinary(data); + if (osd is OSDMap) + meshOsd = (OSDMap)osd; + else + { + m_log.Warn("[Mesh}: unable to cast mesh asset to OSDMap"); + return null; + } } catch (Exception e) { @@ -302,11 +309,17 @@ namespace OpenSim.Region.Physics.Meshing if (meshOsd is OSDMap) { + OSDMap physicsParms = null; OSDMap map = (OSDMap)meshOsd; - OSDMap physicsParms = (OSDMap)map["physics_shape"]; // old asset format - - if (physicsParms.Count == 0) + if (map.ContainsKey("physics_shape")) + physicsParms = (OSDMap)map["physics_shape"]; // old asset format + else if (map.ContainsKey("physics_mesh")) physicsParms = (OSDMap)map["physics_mesh"]; // new asset format + if (physicsParms == null) + { + m_log.Warn("[Mesh]: no recognized physics mesh found in mesh asset"); + return null; + } int physOffset = physicsParms["offset"].AsInteger() + (int)start; int physSize = physicsParms["size"].AsInteger(); diff --git a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs index a0101af2bb..8d9f5f14d6 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs @@ -2502,7 +2502,7 @@ namespace OpenSim.Region.Physics.OdePlugin } // if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since ODE can use an internal representation for the prim - if (!forceSimplePrimMeshing) + if (!forceSimplePrimMeshing && !pbs.SculptEntry) { if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 @@ -2592,6 +2592,9 @@ namespace OpenSim.Region.Physics.OdePlugin } } + if (pbs.SculptEntry && meshSculptedPrim) + iPropertiesNotSupportedDefault++; + if (iPropertiesNotSupportedDefault == 0) { diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 2b8155fbd0..31222fff54 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -38,7 +38,6 @@ using OpenSim; using OpenSim.Framework; using OpenSim.Framework.Console; -using OpenSim.Region.CoreModules.Avatar.NPC; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.ScriptEngine.Shared; diff --git a/OpenSim/Tests/Common/Helpers/AssetHelpers.cs b/OpenSim/Tests/Common/Helpers/AssetHelpers.cs index aa55bcdc69..9b68331038 100644 --- a/OpenSim/Tests/Common/Helpers/AssetHelpers.cs +++ b/OpenSim/Tests/Common/Helpers/AssetHelpers.cs @@ -36,6 +36,15 @@ namespace OpenSim.Tests.Common { public class AssetHelpers { + /// + /// Create a notecard asset with a random uuids and dummy text. + /// + /// + public static AssetBase CreateAsset() + { + return CreateAsset(UUID.Random(), AssetType.Notecard, "hello", UUID.Random()); + } + /// /// Create a notecard asset with a random uuid and dummy text. /// diff --git a/OpenSim/Tests/Common/TestHelper.cs b/OpenSim/Tests/Common/TestHelper.cs index 1722e59864..86bd10766b 100644 --- a/OpenSim/Tests/Common/TestHelper.cs +++ b/OpenSim/Tests/Common/TestHelper.cs @@ -28,6 +28,7 @@ using System; using System.Diagnostics; using NUnit.Framework; +using OpenMetaverse; namespace OpenSim.Tests.Common { @@ -56,5 +57,15 @@ namespace OpenSim.Tests.Common Console.WriteLine(); Console.WriteLine("===> In Test Method : {0} <===", stackTrace.GetFrame(1).GetMethod().Name); } + + /// + /// Parse tail section into full UUID. + /// + /// + /// + public static UUID ParseTail(int tail) + { + return new UUID(string.Format("00000000-0000-0000-0000-{0:X12}", tail)); + } } } diff --git a/OpenSim/Tools/Configger/ConfigurationLoader.cs b/OpenSim/Tools/Configger/ConfigurationLoader.cs index 8e71b42bca..39146527b8 100644 --- a/OpenSim/Tools/Configger/ConfigurationLoader.cs +++ b/OpenSim/Tools/Configger/ConfigurationLoader.cs @@ -239,7 +239,7 @@ namespace OpenSim.Tools.Configger config.Set("meshing", "Meshmerizer"); config.Set("physical_prim", true); config.Set("see_into_this_sim_from_neighbor", true); - config.Set("serverside_object_permissions", false); + config.Set("serverside_object_permissions", true); config.Set("storage_plugin", "OpenSim.Data.SQLite.dll"); config.Set("storage_connection_string", "URI=file:OpenSim.db,version=3"); config.Set("storage_prim_inventories", true); diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 98bb355d7a..60d37fbcec 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -78,20 +78,19 @@ ; DrawPrimOnMapTile = true ;# {NonPhysicalPrimMax} {} {Maximum size of nonphysical prims?} {} 256 - ;; Maximum size for non-physical prims + ;; Maximum size for non-physical prims. Affects resizing of existing prims. This can be overriden in the region config file (as NonphysicalPrimMax!). ; NonPhysicalPrimMax = 256 ;# {PhysicalPrimMax} {} {Maximum size of physical prims?} {} 10 - ;; Maximum size where a prim can be physical + ;; Maximum size where a prim can be physical. Affects resizing of existing prims. This can be overriden in the region config file. ; PhysicalPrimMax = 10 - ;; Prevent the creation, import and rez of prims that exceed the - ;; maximum size. + ;; If a viewer attempts to rez a prim larger than the non-physical or physical prim max, clamp the dimensions to the appropriate maximum + ;; This can be overriden in the region config file. ; ClampPrimSize = false ;# {AllowScriptCrossing} {} {Allow scripts to cross into this region} {true false} false - ;; Allow scripts to cross region boundaries. These are recompiled on the - ;; new region. + ;; Allow scripts to keep running when they cross region boundaries, rather than being restarted. Script code is recompiled on the destination region and the state reloaded. ; AllowScriptCrossing = false ;# {TrustBinaries} {AllowScriptCrossing:true} {Accept compiled binary script code? (DANGEROUS!)} {true false} false @@ -173,7 +172,7 @@ ;; permission checks (allowing anybody to copy ;; any item, etc. This may not yet be implemented uniformally. ;; If set to true, then all permissions checks are carried out - ; serverside_object_permissions = false + ; serverside_object_permissions = true ;; This allows users with a UserLevel of 200 or more to assume god ;; powers in the regions in this simulator. @@ -292,28 +291,31 @@ ;; building's lights to possibly not be rendered. ; DisableFacelights = "false" + [ClientStack.LindenCaps] - ;; For the long list of capabilities, see OpenSimDefaults.ini - ;; Here are the few ones you may want to change. Possible values - ;; are: - ;; "" -- empty, capability disabled - ;; "localhost" -- capability enabled and served by the simulator - ;; "" -- capability enabled and served by some other server - ;; - ; These are enabled by default to localhost. Change if you see fit. - Cap_GetTexture = "localhost" - Cap_GetMesh = "localhost" - ; This is disabled by default. Change if you see fit. Note that - ; serving this cap from the simulators may lead to poor performace. - Cap_WebFetchInventoryDescendents = "" + ;; For the long list of capabilities, see OpenSimDefaults.ini + ;; Here are the few ones you may want to change. Possible values + ;; are: + ;; "" -- empty, capability disabled + ;; "localhost" -- capability enabled and served by the simulator + ;; "" -- capability enabled and served by some other server + ;; + ; These are enabled by default to localhost. Change if you see fit. + Cap_GetTexture = "localhost" + Cap_GetMesh = "localhost" + ; This is disabled by default. Change if you see fit. Note that + ; serving this cap from the simulators may lead to poor performace. + Cap_WebFetchInventoryDescendents = "" + [SimulatorFeatures] - ; Experimental new information sent in SimulatorFeatures cap for Kokua viewers - ; meant to override the MapImage and search server url given at login, and varying - ; on a sim-basis. - ; Viewers that don't understand it, will ignore it - ;MapImageServerURI = "http://127.0.0.1:9000/ - ;SearchServerURI = "http://127.0.0.1:9000/ + ; Experimental new information sent in SimulatorFeatures cap for Kokua viewers + ; meant to override the MapImage and search server url given at login, and varying + ; on a sim-basis. + ; Viewers that don't understand it, will ignore it + ;MapImageServerURI = "http://127.0.0.1:9000/ + ;SearchServerURI = "http://127.0.0.1:9000/ + [Chat] ;# {whisper_distance} {} {Distance at which a whisper is heard, in meters?} {} 10 @@ -650,6 +652,7 @@ ;; If using a remote connector, specify the server URL ; FreeswitchServiceURL = http://my.grid.server:8004/fsapi + [Groups] ;# {Enabled} {} {Enable groups?} {true false} false ;; Enables the groups module @@ -707,11 +710,13 @@ ;; Enable media on a prim facilities ; Enabled = true; + [PrimLimitsModule] ;# {EnforcePrimLimits} {} {Enforce parcel prim limits} {true false} false ;; Enable parcel prim limits. Off by default to emulate pre-existing behavior. ; EnforcePrimLimits = false + [Architecture] ;# {Include-Architecture} {} {Choose one of the following architectures} {config-include/Standalone.ini config-include/StandaloneHypergrid.ini config-include/Grid.ini config-include/GridHypergrid.ini config-include/SimianGrid.ini config-include/HyperSimianGrid.ini} config-include/Standalone.ini ;; Uncomment one of the following includes as required. For instance, to create a standalone OpenSim, diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index fd39fe62e6..5aeadceb43 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -14,10 +14,13 @@ ; Place to create a PID file ; PIDFile = "/tmp/my.pid" + ; Console commands run at startup startup_console_commands_file = "startup_commands.txt" + + ; Console commands run on shutdown shutdown_console_commands_file = "shutdown_commands.txt" - ; To run a script every few minutes, set the script filename here + ; Console commands run every 20 minutes ; timer_Script = "filename" ; ## @@ -70,12 +73,17 @@ ; Use terrain texture for maptiles if true, use shaded green if false TextureOnMapTile = false - ; Maximum total size, and maximum size where a prim can be physical + ; Maximum size of non physical prims. Affects resizing of existing prims. This can be overriden in the region config file (as NonphysicalPrimMax!). NonPhysicalPrimMax = 256 - PhysicalPrimMax = 10 ; (I think this was moved to the Regions.ini!) + + ; Maximum size of physical prims. Affects resizing of existing prims. This can be overriden in the region config file. + PhysicalPrimMax = 10 + + ; If a viewer attempts to rez a prim larger than the non-physical or physical prim max, clamp the dimensions to the appropriate maximum + ; This can be overriden in the region config file. ClampPrimSize = false - ; Allow scripts to cross region boundaries. These are recompiled on the new region. + ; Allow scripts to keep running when they cross region boundaries, rather than being restarted. Script code is recompiled on the destination region and the state reloaded. AllowScriptCrossing = false ; Allow compiled script binary code to cross region boundaries. @@ -94,7 +102,7 @@ ; neighbors on each side for a total of 49 regions in view. Warning, unless ; all the regions have the same drawdistance, you will end up with strange ; effects because the agents that get closed may be inconsistent. - ; DefaultDrawDistance = 255.0 + DefaultDrawDistance = 255.0 ; If you have only one region in an instance, or to avoid the many bugs ; that you can trigger in modules by restarting a region, set this to @@ -102,7 +110,7 @@ ; This is meant to be used on systems where some external system like ; Monit will restart any instance that exits, thereby making the shutdown ; into a restart. - ;InworldRestartShutsDown = false + InworldRestartShutsDown = false ; ## ; ## PRIM STORAGE @@ -227,7 +235,6 @@ ; If enabled, enableFlySlow will change the primary fly state to ; FLYSLOW, and the "always run" state will be the regular fly. - enableflyslow = false ; PreJump is an additional animation state, but it probably @@ -236,7 +243,6 @@ ; This is commented so it will come on automatically once it's ; supported. - ; enableprejump = true ; Simulator Stats URI @@ -265,6 +271,7 @@ DelayBeforeAppearanceSave = 5 DelayBeforeAppearanceSend = 2 + [SMTP] enabled=false diff --git a/bin/config-include/FlotsamCache.ini.example b/bin/config-include/FlotsamCache.ini.example index ad38ad15b5..cd39f8c740 100644 --- a/bin/config-include/FlotsamCache.ini.example +++ b/bin/config-include/FlotsamCache.ini.example @@ -19,9 +19,12 @@ ; 0 to disable HitRateDisplay = 100 - ; Set to false for disk cache only. + ; Set to false for no memory cache MemoryCacheEnabled = false + ; Set to false for no file cache + FileCacheEnabled = true + ; How long {in hours} to keep assets cached in memory, .5 == 30 minutes ; Optimization: for VPS or limited memory system installs set Timeout to .016 (1 minute) ; increases performance without large memory impact diff --git a/prebuild.xml b/prebuild.xml index 0e96176883..6362848e94 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2985,6 +2985,7 @@ + @@ -3029,6 +3030,7 @@ + @@ -3052,6 +3054,7 @@ +