Merge branch 'master' into careminster-presence-refactor
commit
47cf9c8fe0
|
@ -2304,6 +2304,10 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
|||
/// <description>UUID of the region</description></item>
|
||||
/// <item><term>region_name</term>
|
||||
/// <description>region name</description></item>
|
||||
/// <item><term>merge</term>
|
||||
/// <description>true if oar should be merged</description></item>
|
||||
/// <item><term>skip-assets</term>
|
||||
/// <description>true if assets should be skiped</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <code>region_uuid</code> takes precedence over
|
||||
|
@ -2363,9 +2367,21 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
|||
}
|
||||
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<IRegionArchiverModule>();
|
||||
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
|
|||
/// <description>UUID of the region</description></item>
|
||||
/// <item><term>region_name</term>
|
||||
/// <description>region name</description></item>
|
||||
/// <item><term>profile</term>
|
||||
/// <description>profile url</description></item>
|
||||
/// <item><term>noassets</term>
|
||||
/// <description>true if no assets should be saved</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <code>region_uuid</code> takes precedence over
|
||||
|
@ -2462,12 +2482,29 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
|||
}
|
||||
else throw new Exception("neither region_name nor region_uuid given");
|
||||
|
||||
Dictionary<string, object> options = new Dictionary<string, object>();
|
||||
|
||||
//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<IRegionArchiverModule>();
|
||||
|
||||
if (archiver != null)
|
||||
{
|
||||
scene.EventManager.OnOarFileSaved += RemoteAdminOarSaveCompleted;
|
||||
archiver.ArchiveRegion(filename, new Dictionary<string, object>());
|
||||
archiver.ArchiveRegion(filename, options);
|
||||
lock (m_saveOarLock) Monitor.Wait(m_saveOarLock,5000);
|
||||
scene.EventManager.OnOarFileSaved -= RemoteAdminOarSaveCompleted;
|
||||
}
|
||||
|
|
|
@ -281,6 +281,7 @@ namespace OpenSim.Framework
|
|||
|
||||
return m_id;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
UUID uuid = UUID.Zero;
|
||||
|
|
|
@ -741,7 +741,7 @@ namespace OpenSim.Framework
|
|||
bool IsActive { get; set; }
|
||||
|
||||
/// <value>
|
||||
/// Determines whether the client is logging out or not.
|
||||
/// Determines whether the client is or has been removed from a given scene
|
||||
/// </value>
|
||||
bool IsLoggingOut { get; set; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -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);
|
||||
using (StreamReader CommitFile = File.OpenText(manualVersionFileName))
|
||||
buildVersion = CommitFile.ReadLine();
|
||||
CommitFile.Close();
|
||||
|
||||
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))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -86,6 +86,8 @@ namespace Flotsam.RegionModules.AssetCache
|
|||
private List<string> m_CurrentlyWriting = new List<string>();
|
||||
#endif
|
||||
|
||||
private bool m_FileCacheEnabled = true;
|
||||
|
||||
private ExpiringCache<string, AssetBase> 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<IAssetService>();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -319,18 +318,45 @@ namespace Flotsam.RegionModules.AssetCache
|
|||
}
|
||||
}
|
||||
|
||||
public AssetBase Get(string id)
|
||||
public void Cache(AssetBase asset)
|
||||
{
|
||||
m_Requests++;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to get an asset from the in-memory cache.
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
private AssetBase GetFromMemoryCache(string id)
|
||||
{
|
||||
AssetBase asset = null;
|
||||
|
||||
if (m_MemoryCacheEnabled && m_MemoryCache.TryGetValue(id, out asset))
|
||||
{
|
||||
if (m_MemoryCache.TryGetValue(id, out asset))
|
||||
m_MemoryHits++;
|
||||
|
||||
return asset;
|
||||
}
|
||||
else
|
||||
|
||||
/// <summary>
|
||||
/// Try to get an asset from the file cache.
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
private AssetBase GetFromFileCache(string id)
|
||||
{
|
||||
AssetBase asset = null;
|
||||
|
||||
string filename = GetFileName(id);
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
|
@ -390,8 +416,21 @@ namespace Flotsam.RegionModules.AssetCache
|
|||
m_RequestsForInprogress++;
|
||||
}
|
||||
#endif
|
||||
|
||||
return asset;
|
||||
}
|
||||
|
||||
public AssetBase Get(string id)
|
||||
{
|
||||
m_Requests++;
|
||||
|
||||
AssetBase asset = null;
|
||||
|
||||
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))
|
||||
{
|
||||
m_HitRateFile = (double)m_DiskHits / m_Requests * 100.0;
|
||||
|
@ -423,12 +462,15 @@ namespace Flotsam.RegionModules.AssetCache
|
|||
m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Expiring Asset {0}.", id);
|
||||
|
||||
try
|
||||
{
|
||||
if (m_FileCacheEnabled)
|
||||
{
|
||||
string filename = GetFileName(id);
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
File.Delete(filename);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_MemoryCacheEnabled)
|
||||
m_MemoryCache.Remove(id);
|
||||
|
@ -442,12 +484,15 @@ 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.");
|
||||
|
||||
if (m_FileCacheEnabled)
|
||||
{
|
||||
foreach (string dir in Directory.GetDirectories(m_CacheDirectory))
|
||||
{
|
||||
Directory.Delete(dir);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_MemoryCacheEnabled)
|
||||
m_MemoryCache.Clear();
|
||||
|
@ -481,9 +526,9 @@ namespace Flotsam.RegionModules.AssetCache
|
|||
/// removes empty tier directories.
|
||||
/// </summary>
|
||||
/// <param name="dir"></param>
|
||||
/// <param name="purgeLine"></param>
|
||||
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");
|
||||
|
||||
if (m_FileCacheEnabled)
|
||||
{
|
||||
int fileCount = GetFileCacheCount(m_CacheDirectory);
|
||||
m_log.InfoFormat("[FLOTSAM ASSET CACHE] File Cache : {0} assets", fileCount);
|
||||
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:");
|
||||
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"));
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -760,37 +815,49 @@ namespace Flotsam.RegionModules.AssetCache
|
|||
}
|
||||
|
||||
if (clearMemory)
|
||||
{
|
||||
if (m_MemoryCacheEnabled)
|
||||
{
|
||||
m_MemoryCache.Clear();
|
||||
m_log.Info("[FLOTSAM ASSET CACHE] Memory cache cleared.");
|
||||
m_log.Info("[FLOTSAM ASSET CACHE]: Memory cache cleared.");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.Info("[FLOTSAM ASSET CACHE]: Memory cache not enabled.");
|
||||
}
|
||||
}
|
||||
|
||||
if (clearFile)
|
||||
{
|
||||
if (m_FileCacheEnabled)
|
||||
{
|
||||
ClearFileCache();
|
||||
m_log.Info("[FLOTSAM ASSET CACHE] File cache cleared.");
|
||||
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;
|
||||
}
|
||||
|
||||
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 <datetime> - 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 <datetime> - Purge assets older then the specified date & time");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// At the moment we're only test the in-memory part of the FlotsamAssetCache. This is a considerable weakness.
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send FetchInventoryReply information to clients asynchronously on a single thread rather than asynchronously via
|
||||
/// multiple threads.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public class AsyncInventorySender
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
protected Scene m_scene;
|
||||
|
||||
/// <summary>
|
||||
/// Queues fetch requests
|
||||
/// </summary>
|
||||
Queue<FetchHolder> m_fetchHolder = new Queue<FetchHolder>();
|
||||
|
||||
/// <summary>
|
||||
/// Signal whether a queue is currently being processed or not.
|
||||
/// </summary>
|
||||
protected volatile bool m_processing;
|
||||
|
||||
public AsyncInventorySender(Scene scene)
|
||||
{
|
||||
m_processing = false;
|
||||
m_scene = scene;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a fetch inventory request from the client
|
||||
/// </summary>
|
||||
/// <param name="remoteClient"></param>
|
||||
/// <param name="itemID"></param>
|
||||
/// <param name="ownerID"></param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the queue of fetches
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -51,6 +51,11 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
/// </summary>
|
||||
protected AsyncSceneObjectGroupDeleter m_asyncSceneObjectDeleter;
|
||||
|
||||
/// <summary>
|
||||
/// Allows inventory details to be sent to clients asynchronously
|
||||
/// </summary>
|
||||
protected AsyncInventorySender m_asyncInventorySender;
|
||||
|
||||
/// <summary>
|
||||
/// Start all the scripts in the scene which should be started.
|
||||
/// </summary>
|
||||
|
|
|
@ -462,31 +462,6 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Handle a fetch inventory request from the client
|
||||
/// </summary>
|
||||
/// <param name="remoteClient"></param>
|
||||
/// <param name="itemID"></param>
|
||||
/// <param name="ownerID"></param>
|
||||
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?
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tell the client about the various child items and folders contained in the requested folder.
|
||||
/// </summary>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<UUID,NPCAvatar> m_avatars = new Dictionary<UUID, NPCAvatar>();
|
||||
private Dictionary<UUID,AvatarAppearance> m_appearanceCache = new Dictionary<UUID, AvatarAppearance>();
|
||||
|
||||
// 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<INPCModule>(this);
|
||||
}
|
||||
|
||||
|
@ -92,33 +71,51 @@ namespace OpenSim.Region.OptionalModules.World.NPC
|
|||
|
||||
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<string, object>();
|
||||
|
||||
// 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<string, object>();
|
||||
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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<INPCModule>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -36,6 +36,15 @@ namespace OpenSim.Tests.Common
|
|||
{
|
||||
public class AssetHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a notecard asset with a random uuids and dummy text.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static AssetBase CreateAsset()
|
||||
{
|
||||
return CreateAsset(UUID.Random(), AssetType.Notecard, "hello", UUID.Random());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a notecard asset with a random uuid and dummy text.
|
||||
/// </summary>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse tail section into full UUID.
|
||||
/// </summary>
|
||||
/// <param name="tail"></param>
|
||||
/// <returns></returns>
|
||||
public static UUID ParseTail(int tail)
|
||||
{
|
||||
return new UUID(string.Format("00000000-0000-0000-0000-{0:X12}", tail));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,6 +291,7 @@
|
|||
;; 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
|
||||
|
@ -307,6 +307,7 @@
|
|||
; 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
|
||||
|
@ -315,6 +316,7 @@
|
|||
;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
|
||||
;; Distance in meters that whispers should travel.
|
||||
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -2985,6 +2985,7 @@
|
|||
<Files>
|
||||
<!-- SADLY the way this works means you need to keep adding these paths -->
|
||||
<Match path="Agent/TextureSender/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="Asset/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="Avatar/Inventory/Archiver/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="Framework/InventoryAccess/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="World/Archiver/Tests" pattern="*.cs" recurse="true"/>
|
||||
|
@ -3029,6 +3030,7 @@
|
|||
<Reference name="OpenSim.Region.CoreModules"/>
|
||||
<Reference name="OpenSim.Region.OptionalModules"/>
|
||||
<Reference name="OpenSim.Region.Physics.Manager"/>
|
||||
<Reference name="OpenSim.Services.AvatarService"/>
|
||||
<Reference name="OpenSim.Services.Interfaces"/>
|
||||
|
||||
<!-- Unit tests -->
|
||||
|
@ -3052,6 +3054,7 @@
|
|||
<Files>
|
||||
<!-- SADLY the way this works means you need to keep adding these paths -->
|
||||
<Match path="Avatar/XmlRpcGroups/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="World/NPC/Tests" pattern="*.cs" recurse="true"/>
|
||||
</Files>
|
||||
</Project>
|
||||
|
||||
|
|
Loading…
Reference in New Issue