aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorroot2011-07-05 04:01:59 +0100
committerroot2011-07-05 04:01:59 +0100
commit47cf9c8fe060af89cc14817dc762bcbfef956d5e (patch)
tree56b21e617bbb4ce3a613780c81178de41ff062d3
parentMerge branch 'careminster-presence-refactor' of ssh://3dhosting.de/var/git/ca... (diff)
parentAdd TestClearCache() (diff)
downloadopensim-SC_OLD-47cf9c8fe060af89cc14817dc762bcbfef956d5e.zip
opensim-SC_OLD-47cf9c8fe060af89cc14817dc762bcbfef956d5e.tar.gz
opensim-SC_OLD-47cf9c8fe060af89cc14817dc762bcbfef956d5e.tar.bz2
opensim-SC_OLD-47cf9c8fe060af89cc14817dc762bcbfef956d5e.tar.xz
Merge branch 'master' into careminster-presence-refactor
-rw-r--r--OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs41
-rw-r--r--OpenSim/Framework/AssetBase.cs1
-rw-r--r--OpenSim/Framework/IClientAPI.cs2
-rw-r--r--OpenSim/Framework/Servers/BaseOpenSimServer.cs57
-rw-r--r--OpenSim/Region/Application/ConfigurationLoader.cs2
-rw-r--r--OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs267
-rw-r--r--OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs127
-rw-r--r--OpenSim/Region/CoreModules/World/Land/LandObject.cs8
-rw-r--r--OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs2
-rw-r--r--OpenSim/Region/Framework/Interfaces/INPCModule.cs (renamed from OpenSim/Region/CoreModules/Avatar/NPC/INPCModule.cs)2
-rw-r--r--OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs156
-rw-r--r--OpenSim/Region/Framework/Scenes/Scene.Inventory.cs5
-rw-r--r--OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs25
-rw-r--r--OpenSim/Region/Framework/Scenes/Scene.cs8
-rw-r--r--OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs142
-rw-r--r--OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs71
-rw-r--r--OpenSim/Region/Physics/Meshing/Meshmerizer.cs21
-rw-r--r--OpenSim/Region/Physics/OdePlugin/OdePlugin.cs5
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs1
-rw-r--r--OpenSim/Tests/Common/Helpers/AssetHelpers.cs9
-rw-r--r--OpenSim/Tests/Common/TestHelper.cs11
-rw-r--r--OpenSim/Tools/Configger/ConfigurationLoader.cs2
-rw-r--r--bin/OpenSim.ini.example57
-rw-r--r--bin/OpenSimDefaults.ini23
-rw-r--r--bin/config-include/FlotsamCache.ini.example5
-rw-r--r--prebuild.xml3
26 files changed, 752 insertions, 301 deletions
diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs
index 7529e01..ee3d7c5 100644
--- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs
+++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs
@@ -2304,6 +2304,10 @@ namespace OpenSim.ApplicationPlugins.RemoteController
2304 /// <description>UUID of the region</description></item> 2304 /// <description>UUID of the region</description></item>
2305 /// <item><term>region_name</term> 2305 /// <item><term>region_name</term>
2306 /// <description>region name</description></item> 2306 /// <description>region name</description></item>
2307 /// <item><term>merge</term>
2308 /// <description>true if oar should be merged</description></item>
2309 /// <item><term>skip-assets</term>
2310 /// <description>true if assets should be skiped</description></item>
2307 /// </list> 2311 /// </list>
2308 /// 2312 ///
2309 /// <code>region_uuid</code> takes precedence over 2313 /// <code>region_uuid</code> takes precedence over
@@ -2362,10 +2366,22 @@ namespace OpenSim.ApplicationPlugins.RemoteController
2362 throw new Exception(String.Format("failed to switch to region {0}", region_name)); 2366 throw new Exception(String.Format("failed to switch to region {0}", region_name));
2363 } 2367 }
2364 else throw new Exception("neither region_name nor region_uuid given"); 2368 else throw new Exception("neither region_name nor region_uuid given");
2369
2370 bool mergeOar = false;
2371 bool skipAssets = false;
2372
2373 if ((string)requestData["merge"] == "true")
2374 {
2375 mergeOar = true;
2376 }
2377 if ((string)requestData["skip-assets"] == "true")
2378 {
2379 skipAssets = true;
2380 }
2365 2381
2366 IRegionArchiverModule archiver = scene.RequestModuleInterface<IRegionArchiverModule>(); 2382 IRegionArchiverModule archiver = scene.RequestModuleInterface<IRegionArchiverModule>();
2367 if (archiver != null) 2383 if (archiver != null)
2368 archiver.DearchiveRegion(filename); 2384 archiver.DearchiveRegion(filename, mergeOar, skipAssets, Guid.Empty);
2369 else 2385 else
2370 throw new Exception("Archiver module not present for scene"); 2386 throw new Exception("Archiver module not present for scene");
2371 2387
@@ -2405,6 +2421,10 @@ namespace OpenSim.ApplicationPlugins.RemoteController
2405 /// <description>UUID of the region</description></item> 2421 /// <description>UUID of the region</description></item>
2406 /// <item><term>region_name</term> 2422 /// <item><term>region_name</term>
2407 /// <description>region name</description></item> 2423 /// <description>region name</description></item>
2424 /// <item><term>profile</term>
2425 /// <description>profile url</description></item>
2426 /// <item><term>noassets</term>
2427 /// <description>true if no assets should be saved</description></item>
2408 /// </list> 2428 /// </list>
2409 /// 2429 ///
2410 /// <code>region_uuid</code> takes precedence over 2430 /// <code>region_uuid</code> takes precedence over
@@ -2462,12 +2482,29 @@ namespace OpenSim.ApplicationPlugins.RemoteController
2462 } 2482 }
2463 else throw new Exception("neither region_name nor region_uuid given"); 2483 else throw new Exception("neither region_name nor region_uuid given");
2464 2484
2485 Dictionary<string, object> options = new Dictionary<string, object>();
2486
2487 //if (requestData.Contains("version"))
2488 //{
2489 // options["version"] = (string)requestData["version"];
2490 //}
2491
2492 if (requestData.Contains("profile"))
2493 {
2494 options["profile"] = (string)requestData["profile"];
2495 }
2496
2497 if (requestData["noassets"] == "true")
2498 {
2499 options["noassets"] = (string)requestData["noassets"] ;
2500 }
2501
2465 IRegionArchiverModule archiver = scene.RequestModuleInterface<IRegionArchiverModule>(); 2502 IRegionArchiverModule archiver = scene.RequestModuleInterface<IRegionArchiverModule>();
2466 2503
2467 if (archiver != null) 2504 if (archiver != null)
2468 { 2505 {
2469 scene.EventManager.OnOarFileSaved += RemoteAdminOarSaveCompleted; 2506 scene.EventManager.OnOarFileSaved += RemoteAdminOarSaveCompleted;
2470 archiver.ArchiveRegion(filename, new Dictionary<string, object>()); 2507 archiver.ArchiveRegion(filename, options);
2471 lock (m_saveOarLock) Monitor.Wait(m_saveOarLock,5000); 2508 lock (m_saveOarLock) Monitor.Wait(m_saveOarLock,5000);
2472 scene.EventManager.OnOarFileSaved -= RemoteAdminOarSaveCompleted; 2509 scene.EventManager.OnOarFileSaved -= RemoteAdminOarSaveCompleted;
2473 } 2510 }
diff --git a/OpenSim/Framework/AssetBase.cs b/OpenSim/Framework/AssetBase.cs
index 8e24f91..06e040f 100644
--- a/OpenSim/Framework/AssetBase.cs
+++ b/OpenSim/Framework/AssetBase.cs
@@ -281,6 +281,7 @@ namespace OpenSim.Framework
281 281
282 return m_id; 282 return m_id;
283 } 283 }
284
284 set 285 set
285 { 286 {
286 UUID uuid = UUID.Zero; 287 UUID uuid = UUID.Zero;
diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs
index 31a45e2..0a4dcac 100644
--- a/OpenSim/Framework/IClientAPI.cs
+++ b/OpenSim/Framework/IClientAPI.cs
@@ -741,7 +741,7 @@ namespace OpenSim.Framework
741 bool IsActive { get; set; } 741 bool IsActive { get; set; }
742 742
743 /// <value> 743 /// <value>
744 /// Determines whether the client is logging out or not. 744 /// Determines whether the client is or has been removed from a given scene
745 /// </value> 745 /// </value>
746 bool IsLoggingOut { get; set; } 746 bool IsLoggingOut { get; set; }
747 747
diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
index 5c3cad4..64b9c3e 100644
--- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs
+++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
@@ -31,6 +31,7 @@ using System.Diagnostics;
31using System.IO; 31using System.IO;
32using System.Reflection; 32using System.Reflection;
33using System.Text; 33using System.Text;
34using System.Text.RegularExpressions;
34using System.Threading; 35using System.Threading;
35using System.Timers; 36using System.Timers;
36using log4net; 37using log4net;
@@ -124,7 +125,6 @@ namespace OpenSim.Framework.Servers
124 m_logFileAppender = appender; 125 m_logFileAppender = appender;
125 } 126 }
126 } 127 }
127
128 } 128 }
129 129
130 /// <summary> 130 /// <summary>
@@ -443,45 +443,68 @@ namespace OpenSim.Framework.Servers
443 { 443 {
444 string buildVersion = string.Empty; 444 string buildVersion = string.Empty;
445 445
446 // Add commit hash and date information if available
447 // The commit hash and date are stored in a file bin/.version
448 // This file can automatically created by a post
449 // commit script in the opensim git master repository or
450 // by issuing the follwoing command from the top level
451 // directory of the opensim repository
452 // git log -n 1 --pretty="format:%h: %ci" >bin/.version
453 // For the full git commit hash use %H instead of %h
454 //
455 // The subversion information is deprecated and will be removed at a later date 446 // The subversion information is deprecated and will be removed at a later date
456 // Add subversion revision information if available 447 // Add subversion revision information if available
457 // Try file "svn_revision" in the current directory first, then the .svn info. 448 // Try file "svn_revision" in the current directory first, then the .svn info.
458 // This allows to make the revision available in simulators not running from the source tree. 449 // This allows to make the revision available in simulators not running from the source tree.
459 // FIXME: Making an assumption about the directory we're currently in - we do this all over the place 450 // FIXME: Making an assumption about the directory we're currently in - we do this all over the place
460 // elsewhere as well 451 // elsewhere as well
452 string gitDir = "../.git/";
453 string gitRefPointerPath = gitDir + "HEAD";
454
461 string svnRevisionFileName = "svn_revision"; 455 string svnRevisionFileName = "svn_revision";
462 string svnFileName = ".svn/entries"; 456 string svnFileName = ".svn/entries";
463 string gitCommitFileName = ".version"; 457 string manualVersionFileName = ".version";
464 string inputLine; 458 string inputLine;
465 int strcmp; 459 int strcmp;
466 460
467 if (File.Exists(gitCommitFileName)) 461 if (File.Exists(manualVersionFileName))
468 { 462 {
469 StreamReader CommitFile = File.OpenText(gitCommitFileName); 463 using (StreamReader CommitFile = File.OpenText(manualVersionFileName))
470 buildVersion = CommitFile.ReadLine(); 464 buildVersion = CommitFile.ReadLine();
471 CommitFile.Close(); 465
472 m_version += buildVersion ?? ""; 466 m_version += buildVersion ?? "";
473 } 467 }
468 else if (File.Exists(gitRefPointerPath))
469 {
470// m_log.DebugFormat("[OPENSIM]: Found {0}", gitRefPointerPath);
471
472 string rawPointer = "";
473
474 using (StreamReader pointerFile = File.OpenText(gitRefPointerPath))
475 rawPointer = pointerFile.ReadLine();
476
477// m_log.DebugFormat("[OPENSIM]: rawPointer [{0}]", rawPointer);
478
479 Match m = Regex.Match(rawPointer, "^ref: (.+)$");
480
481 if (m.Success)
482 {
483// m_log.DebugFormat("[OPENSIM]: Matched [{0}]", m.Groups[1].Value);
484
485 string gitRef = m.Groups[1].Value;
486 string gitRefPath = gitDir + gitRef;
487 if (File.Exists(gitRefPath))
488 {
489// m_log.DebugFormat("[OPENSIM]: Found gitRefPath [{0}]", gitRefPath);
474 490
475 // Remove the else logic when subversion mirror is no longer used 491 using (StreamReader refFile = File.OpenText(gitRefPath))
492 {
493 string gitHash = refFile.ReadLine();
494 m_version += gitHash.Substring(0, 7);
495 }
496 }
497 }
498 }
476 else 499 else
477 { 500 {
501 // Remove the else logic when subversion mirror is no longer used
478 if (File.Exists(svnRevisionFileName)) 502 if (File.Exists(svnRevisionFileName))
479 { 503 {
480 StreamReader RevisionFile = File.OpenText(svnRevisionFileName); 504 StreamReader RevisionFile = File.OpenText(svnRevisionFileName);
481 buildVersion = RevisionFile.ReadLine(); 505 buildVersion = RevisionFile.ReadLine();
482 buildVersion.Trim(); 506 buildVersion.Trim();
483 RevisionFile.Close(); 507 RevisionFile.Close();
484
485 } 508 }
486 509
487 if (string.IsNullOrEmpty(buildVersion) && File.Exists(svnFileName)) 510 if (string.IsNullOrEmpty(buildVersion) && File.Exists(svnFileName))
diff --git a/OpenSim/Region/Application/ConfigurationLoader.cs b/OpenSim/Region/Application/ConfigurationLoader.cs
index 2d81ea8..d19852b 100644
--- a/OpenSim/Region/Application/ConfigurationLoader.cs
+++ b/OpenSim/Region/Application/ConfigurationLoader.cs
@@ -328,7 +328,7 @@ namespace OpenSim
328 config.Set("meshing", "Meshmerizer"); 328 config.Set("meshing", "Meshmerizer");
329 config.Set("physical_prim", true); 329 config.Set("physical_prim", true);
330 config.Set("see_into_this_sim_from_neighbor", true); 330 config.Set("see_into_this_sim_from_neighbor", true);
331 config.Set("serverside_object_permissions", false); 331 config.Set("serverside_object_permissions", true);
332 config.Set("storage_plugin", "OpenSim.Data.SQLite.dll"); 332 config.Set("storage_plugin", "OpenSim.Data.SQLite.dll");
333 config.Set("storage_connection_string", "URI=file:OpenSim.db,version=3"); 333 config.Set("storage_connection_string", "URI=file:OpenSim.db,version=3");
334 config.Set("storage_prim_inventories", true); 334 config.Set("storage_prim_inventories", true);
diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
index 9ef5bc9..d85d727 100644
--- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
+++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
@@ -86,6 +86,8 @@ namespace Flotsam.RegionModules.AssetCache
86 private List<string> m_CurrentlyWriting = new List<string>(); 86 private List<string> m_CurrentlyWriting = new List<string>();
87#endif 87#endif
88 88
89 private bool m_FileCacheEnabled = true;
90
89 private ExpiringCache<string, AssetBase> m_MemoryCache; 91 private ExpiringCache<string, AssetBase> m_MemoryCache;
90 private bool m_MemoryCacheEnabled = false; 92 private bool m_MemoryCacheEnabled = false;
91 93
@@ -146,6 +148,7 @@ namespace Flotsam.RegionModules.AssetCache
146 } 148 }
147 else 149 else
148 { 150 {
151 m_FileCacheEnabled = assetConfig.GetBoolean("FileCacheEnabled", m_FileCacheEnabled);
149 m_CacheDirectory = assetConfig.GetString("CacheDirectory", m_DefaultCacheDirectory); 152 m_CacheDirectory = assetConfig.GetString("CacheDirectory", m_DefaultCacheDirectory);
150 153
151 m_MemoryCacheEnabled = assetConfig.GetBoolean("MemoryCacheEnabled", m_MemoryCacheEnabled); 154 m_MemoryCacheEnabled = assetConfig.GetBoolean("MemoryCacheEnabled", m_MemoryCacheEnabled);
@@ -173,7 +176,7 @@ namespace Flotsam.RegionModules.AssetCache
173 176
174 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory); 177 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory);
175 178
176 if ((m_FileExpiration > TimeSpan.Zero) && (m_FileExpirationCleanupTimer > TimeSpan.Zero)) 179 if (m_FileCacheEnabled && (m_FileExpiration > TimeSpan.Zero) && (m_FileExpirationCleanupTimer > TimeSpan.Zero))
177 { 180 {
178 m_CacheCleanTimer = new System.Timers.Timer(m_FileExpirationCleanupTimer.TotalMilliseconds); 181 m_CacheCleanTimer = new System.Timers.Timer(m_FileExpirationCleanupTimer.TotalMilliseconds);
179 m_CacheCleanTimer.AutoReset = true; 182 m_CacheCleanTimer.AutoReset = true;
@@ -226,7 +229,6 @@ namespace Flotsam.RegionModules.AssetCache
226 if (m_AssetService == null) 229 if (m_AssetService == null)
227 { 230 {
228 m_AssetService = scene.RequestModuleInterface<IAssetService>(); 231 m_AssetService = scene.RequestModuleInterface<IAssetService>();
229
230 } 232 }
231 } 233 }
232 } 234 }
@@ -250,18 +252,15 @@ namespace Flotsam.RegionModules.AssetCache
250 252
251 private void UpdateMemoryCache(string key, AssetBase asset) 253 private void UpdateMemoryCache(string key, AssetBase asset)
252 { 254 {
253 if (m_MemoryCacheEnabled) 255 m_MemoryCache.AddOrUpdate(key, asset, m_MemoryExpiration);
254 m_MemoryCache.AddOrUpdate(key, asset, m_MemoryExpiration);
255 } 256 }
256 257
257 public void Cache(AssetBase asset) 258 private void UpdateFileCache(string key, AssetBase asset)
258 { 259 {
259 // TODO: Spawn this off to some seperate thread to do the actual writing 260 // TODO: Spawn this off to some seperate thread to do the actual writing
260 if (asset != null) 261 if (asset != null)
261 { 262 {
262 UpdateMemoryCache(asset.ID, asset); 263 string filename = GetFileName(key);
263
264 string filename = GetFileName(asset.ID);
265 264
266 try 265 try
267 { 266 {
@@ -278,8 +277,8 @@ namespace Flotsam.RegionModules.AssetCache
278 catch 277 catch
279 { 278 {
280 } 279 }
281 } else { 280 } else {
282 281
283 // Once we start writing, make sure we flag that we're writing 282 // Once we start writing, make sure we flag that we're writing
284 // that object to the cache so that we don't try to write the 283 // that object to the cache so that we don't try to write the
285 // same file multiple times. 284 // same file multiple times.
@@ -319,78 +318,118 @@ namespace Flotsam.RegionModules.AssetCache
319 } 318 }
320 } 319 }
321 320
322 public AssetBase Get(string id) 321 public void Cache(AssetBase asset)
323 { 322 {
324 m_Requests++; 323 // TODO: Spawn this off to some seperate thread to do the actual writing
324 if (asset != null)
325 {
326 //m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Caching asset with id {0}", asset.ID);
327
328 if (m_MemoryCacheEnabled)
329 UpdateMemoryCache(asset.ID, asset);
330
331 if (m_FileCacheEnabled)
332 UpdateFileCache(asset.ID, asset);
333 }
334 }
325 335
336 /// <summary>
337 /// Try to get an asset from the in-memory cache.
338 /// </summary>
339 /// <param name="id"></param>
340 /// <returns></returns>
341 private AssetBase GetFromMemoryCache(string id)
342 {
326 AssetBase asset = null; 343 AssetBase asset = null;
327 344
328 if (m_MemoryCacheEnabled && m_MemoryCache.TryGetValue(id, out asset)) 345 if (m_MemoryCache.TryGetValue(id, out asset))
329 {
330 m_MemoryHits++; 346 m_MemoryHits++;
331 } 347
332 else 348 return asset;
349 }
350
351 /// <summary>
352 /// Try to get an asset from the file cache.
353 /// </summary>
354 /// <param name="id"></param>
355 /// <returns></returns>
356 private AssetBase GetFromFileCache(string id)
357 {
358 AssetBase asset = null;
359
360 string filename = GetFileName(id);
361 if (File.Exists(filename))
333 { 362 {
334 string filename = GetFileName(id); 363 FileStream stream = null;
335 if (File.Exists(filename)) 364 try
336 { 365 {
337 FileStream stream = null; 366 stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
338 try 367 BinaryFormatter bformatter = new BinaryFormatter();
339 {
340 stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
341 BinaryFormatter bformatter = new BinaryFormatter();
342 368
343 asset = (AssetBase)bformatter.Deserialize(stream); 369 asset = (AssetBase)bformatter.Deserialize(stream);
344 370
345 UpdateMemoryCache(id, asset); 371 UpdateMemoryCache(id, asset);
346 372
347 m_DiskHits++; 373 m_DiskHits++;
348 } 374 }
349 catch (System.Runtime.Serialization.SerializationException e) 375 catch (System.Runtime.Serialization.SerializationException e)
350 { 376 {
351 LogException(e); 377 LogException(e);
352 378
353 // If there was a problem deserializing the asset, the asset may 379 // If there was a problem deserializing the asset, the asset may
354 // either be corrupted OR was serialized under an old format 380 // either be corrupted OR was serialized under an old format
355 // {different version of AssetBase} -- we should attempt to 381 // {different version of AssetBase} -- we should attempt to
356 // delete it and re-cache 382 // delete it and re-cache
357 File.Delete(filename); 383 File.Delete(filename);
358 } 384 }
359 catch (Exception e) 385 catch (Exception e)
360 { 386 {
361 LogException(e); 387 LogException(e);
362 }
363 finally
364 {
365 if (stream != null)
366 stream.Close();
367 }
368 } 388 }
389 finally
390 {
391 if (stream != null)
392 stream.Close();
393 }
394 }
369 395
370 396
371#if WAIT_ON_INPROGRESS_REQUESTS 397#if WAIT_ON_INPROGRESS_REQUESTS
372 // Check if we're already downloading this asset. If so, try to wait for it to 398 // Check if we're already downloading this asset. If so, try to wait for it to
373 // download. 399 // download.
374 if (m_WaitOnInprogressTimeout > 0) 400 if (m_WaitOnInprogressTimeout > 0)
375 { 401 {
376 m_RequestsForInprogress++; 402 m_RequestsForInprogress++;
377 403
378 ManualResetEvent waitEvent; 404 ManualResetEvent waitEvent;
379 if (m_CurrentlyWriting.TryGetValue(filename, out waitEvent)) 405 if (m_CurrentlyWriting.TryGetValue(filename, out waitEvent))
380 {
381 waitEvent.WaitOne(m_WaitOnInprogressTimeout);
382 return Get(id);
383 }
384 }
385#else
386 // Track how often we have the problem that an asset is requested while
387 // it is still being downloaded by a previous request.
388 if (m_CurrentlyWriting.Contains(filename))
389 { 406 {
390 m_RequestsForInprogress++; 407 waitEvent.WaitOne(m_WaitOnInprogressTimeout);
408 return Get(id);
391 } 409 }
392#endif
393 } 410 }
411#else
412 // Track how often we have the problem that an asset is requested while
413 // it is still being downloaded by a previous request.
414 if (m_CurrentlyWriting.Contains(filename))
415 {
416 m_RequestsForInprogress++;
417 }
418#endif
419
420 return asset;
421 }
422
423 public AssetBase Get(string id)
424 {
425 m_Requests++;
426
427 AssetBase asset = null;
428
429 if (m_MemoryCacheEnabled)
430 asset = GetFromMemoryCache(id);
431 else if (m_FileCacheEnabled)
432 asset = GetFromFileCache(id);
394 433
395 if (((m_LogLevel >= 1)) && (m_HitRateDisplay != 0) && (m_Requests % m_HitRateDisplay == 0)) 434 if (((m_LogLevel >= 1)) && (m_HitRateDisplay != 0) && (m_Requests % m_HitRateDisplay == 0))
396 { 435 {
@@ -424,10 +463,13 @@ namespace Flotsam.RegionModules.AssetCache
424 463
425 try 464 try
426 { 465 {
427 string filename = GetFileName(id); 466 if (m_FileCacheEnabled)
428 if (File.Exists(filename))
429 { 467 {
430 File.Delete(filename); 468 string filename = GetFileName(id);
469 if (File.Exists(filename))
470 {
471 File.Delete(filename);
472 }
431 } 473 }
432 474
433 if (m_MemoryCacheEnabled) 475 if (m_MemoryCacheEnabled)
@@ -442,11 +484,14 @@ namespace Flotsam.RegionModules.AssetCache
442 public void Clear() 484 public void Clear()
443 { 485 {
444 if (m_LogLevel >= 2) 486 if (m_LogLevel >= 2)
445 m_log.Debug("[FLOTSAM ASSET CACHE]: Clearing Cache."); 487 m_log.Debug("[FLOTSAM ASSET CACHE]: Clearing caches.");
446 488
447 foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) 489 if (m_FileCacheEnabled)
448 { 490 {
449 Directory.Delete(dir); 491 foreach (string dir in Directory.GetDirectories(m_CacheDirectory))
492 {
493 Directory.Delete(dir);
494 }
450 } 495 }
451 496
452 if (m_MemoryCacheEnabled) 497 if (m_MemoryCacheEnabled)
@@ -481,9 +526,9 @@ namespace Flotsam.RegionModules.AssetCache
481 /// removes empty tier directories. 526 /// removes empty tier directories.
482 /// </summary> 527 /// </summary>
483 /// <param name="dir"></param> 528 /// <param name="dir"></param>
529 /// <param name="purgeLine"></param>
484 private void CleanExpiredFiles(string dir, DateTime purgeLine) 530 private void CleanExpiredFiles(string dir, DateTime purgeLine)
485 { 531 {
486
487 foreach (string file in Directory.GetFiles(dir)) 532 foreach (string file in Directory.GetFiles(dir))
488 { 533 {
489 if (File.GetLastAccessTime(file) < purgeLine) 534 if (File.GetLastAccessTime(file) < purgeLine)
@@ -721,18 +766,28 @@ namespace Flotsam.RegionModules.AssetCache
721 switch (cmd) 766 switch (cmd)
722 { 767 {
723 case "status": 768 case "status":
724 m_log.InfoFormat("[FLOTSAM ASSET CACHE] Memory Cache : {0} assets", m_MemoryCache.Count); 769 if (m_MemoryCacheEnabled)
725 770 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Memory Cache : {0} assets", m_MemoryCache.Count);
726 int fileCount = GetFileCacheCount(m_CacheDirectory); 771 else
727 m_log.InfoFormat("[FLOTSAM ASSET CACHE] File Cache : {0} assets", fileCount); 772 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Memory cache disabled");
728 773
729 foreach (string s in Directory.GetFiles(m_CacheDirectory, "*.fac")) 774 if (m_FileCacheEnabled)
730 { 775 {
731 m_log.Info("[FLOTSAM ASSET CACHE] Deep Scans were performed on the following regions:"); 776 int fileCount = GetFileCacheCount(m_CacheDirectory);
732 777 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: File Cache : {0} assets", fileCount);
733 string RegionID = s.Remove(0,s.IndexOf("_")).Replace(".fac",""); 778
734 DateTime RegionDeepScanTMStamp = File.GetLastWriteTime(s); 779 foreach (string s in Directory.GetFiles(m_CacheDirectory, "*.fac"))
735 m_log.InfoFormat("[FLOTSAM ASSET CACHE] Region: {0}, {1}", RegionID, RegionDeepScanTMStamp.ToString("MM/dd/yyyy hh:mm:ss")); 780 {
781 m_log.Info("[FLOTSAM ASSET CACHE]: Deep Scans were performed on the following regions:");
782
783 string RegionID = s.Remove(0,s.IndexOf("_")).Replace(".fac","");
784 DateTime RegionDeepScanTMStamp = File.GetLastWriteTime(s);
785 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Region: {0}, {1}", RegionID, RegionDeepScanTMStamp.ToString("MM/dd/yyyy hh:mm:ss"));
786 }
787 }
788 else
789 {
790 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: File cache disabled");
736 } 791 }
737 792
738 break; 793 break;
@@ -740,7 +795,7 @@ namespace Flotsam.RegionModules.AssetCache
740 case "clear": 795 case "clear":
741 if (cmdparams.Length < 2) 796 if (cmdparams.Length < 2)
742 { 797 {
743 m_log.Warn("[FLOTSAM ASSET CACHE] Usage is fcache clear [file] [memory]"); 798 m_log.Warn("[FLOTSAM ASSET CACHE]: Usage is fcache clear [file] [memory]");
744 break; 799 break;
745 } 800 }
746 801
@@ -761,36 +816,48 @@ namespace Flotsam.RegionModules.AssetCache
761 816
762 if (clearMemory) 817 if (clearMemory)
763 { 818 {
764 m_MemoryCache.Clear(); 819 if (m_MemoryCacheEnabled)
765 m_log.Info("[FLOTSAM ASSET CACHE] Memory cache cleared."); 820 {
821 m_MemoryCache.Clear();
822 m_log.Info("[FLOTSAM ASSET CACHE]: Memory cache cleared.");
823 }
824 else
825 {
826 m_log.Info("[FLOTSAM ASSET CACHE]: Memory cache not enabled.");
827 }
766 } 828 }
767 829
768 if (clearFile) 830 if (clearFile)
769 { 831 {
770 ClearFileCache(); 832 if (m_FileCacheEnabled)
771 m_log.Info("[FLOTSAM ASSET CACHE] File cache cleared."); 833 {
834 ClearFileCache();
835 m_log.Info("[FLOTSAM ASSET CACHE]: File cache cleared.");
836 }
837 else
838 {
839 m_log.Info("[FLOTSAM ASSET CACHE]: File cache not enabled.");
840 }
772 } 841 }
773 842
774 break; 843 break;
775 844
776 845
777 case "assets": 846 case "assets":
778 m_log.Info("[FLOTSAM ASSET CACHE] Caching all assets, in all scenes."); 847 m_log.Info("[FLOTSAM ASSET CACHE]: Caching all assets, in all scenes.");
779 848
780 Util.FireAndForget(delegate { 849 Util.FireAndForget(delegate {
781 int assetsCached = CacheScenes(); 850 int assetsCached = CacheScenes();
782 m_log.InfoFormat("[FLOTSAM ASSET CACHE] Completed Scene Caching, {0} assets found.", assetsCached); 851 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Completed Scene Caching, {0} assets found.", assetsCached);
783 852
784 }); 853 });
785 854
786 break; 855 break;
787 856
788 case "expire": 857 case "expire":
789
790
791 if (cmdparams.Length < 3) 858 if (cmdparams.Length < 3)
792 { 859 {
793 m_log.InfoFormat("[FLOTSAM ASSET CACHE] Invalid parameters for Expire, please specify a valid date & time", cmd); 860 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Invalid parameters for Expire, please specify a valid date & time", cmd);
794 break; 861 break;
795 } 862 }
796 863
@@ -808,26 +875,28 @@ namespace Flotsam.RegionModules.AssetCache
808 875
809 if (!DateTime.TryParse(s_expirationDate, out expirationDate)) 876 if (!DateTime.TryParse(s_expirationDate, out expirationDate))
810 { 877 {
811 m_log.InfoFormat("[FLOTSAM ASSET CACHE] {0} is not a valid date & time", cmd); 878 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0} is not a valid date & time", cmd);
812 break; 879 break;
813 } 880 }
814 881
815 CleanExpiredFiles(m_CacheDirectory, expirationDate); 882 if (m_FileCacheEnabled)
883 CleanExpiredFiles(m_CacheDirectory, expirationDate);
884 else
885 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: File cache not active, not clearing.");
816 886
817 break; 887 break;
818 default: 888 default:
819 m_log.InfoFormat("[FLOTSAM ASSET CACHE] Unknown command {0}", cmd); 889 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Unknown command {0}", cmd);
820 break; 890 break;
821 } 891 }
822 } 892 }
823 else if (cmdparams.Length == 1) 893 else if (cmdparams.Length == 1)
824 { 894 {
825 m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache status - Display cache status"); 895 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache status - Display cache status");
826 m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache clearmem - Remove all assets cached in memory"); 896 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache clearmem - Remove all assets cached in memory");
827 m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache clearfile - Remove all assets cached on disk"); 897 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache clearfile - Remove all assets cached on disk");
828 m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache cachescenes - Attempt a deep cache of all assets in all scenes"); 898 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache cachescenes - Attempt a deep cache of all assets in all scenes");
829 m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache <datetime> - Purge assets older then the specified date & time"); 899 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache <datetime> - Purge assets older then the specified date & time");
830
831 } 900 }
832 } 901 }
833 902
diff --git a/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs b/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs
new file mode 100644
index 0000000..63b0c31
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs
@@ -0,0 +1,127 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Reflection;
32using System.Threading;
33using log4net.Config;
34using Nini.Config;
35using NUnit.Framework;
36using OpenMetaverse;
37using OpenMetaverse.Assets;
38using Flotsam.RegionModules.AssetCache;
39using OpenSim.Framework;
40using OpenSim.Region.Framework.Scenes;
41using OpenSim.Region.Framework.Scenes.Serialization;
42using OpenSim.Tests.Common;
43using OpenSim.Tests.Common.Mock;
44
45namespace OpenSim.Region.CoreModules.Asset.Tests
46{
47 /// <summary>
48 /// At the moment we're only test the in-memory part of the FlotsamAssetCache. This is a considerable weakness.
49 /// </summary>
50 [TestFixture]
51 public class FlotsamAssetCacheTests
52 {
53 protected TestScene m_scene;
54 protected FlotsamAssetCache m_cache;
55
56 [SetUp]
57 public void SetUp()
58 {
59 IConfigSource config = new IniConfigSource();
60
61 config.AddConfig("Modules");
62 config.Configs["Modules"].Set("AssetCaching", "FlotsamAssetCache");
63 config.AddConfig("AssetCache");
64 config.Configs["AssetCache"].Set("FileCacheEnabled", "false");
65 config.Configs["AssetCache"].Set("MemoryCacheEnabled", "true");
66
67 m_cache = new FlotsamAssetCache();
68 m_scene = SceneSetupHelpers.SetupScene();
69 SceneSetupHelpers.SetupSceneModules(m_scene, config, m_cache);
70 }
71
72 [Test]
73 public void TestCacheAsset()
74 {
75 TestHelper.InMethod();
76// log4net.Config.XmlConfigurator.Configure();
77
78 AssetBase asset = AssetHelpers.CreateAsset();
79 asset.ID = TestHelper.ParseTail(0x1).ToString();
80
81 // Check we don't get anything before the asset is put in the cache
82 AssetBase retrievedAsset = m_cache.Get(asset.ID.ToString());
83 Assert.That(retrievedAsset, Is.Null);
84
85 m_cache.Store(asset);
86
87 // Check that asset is now in cache
88 retrievedAsset = m_cache.Get(asset.ID.ToString());
89 Assert.That(retrievedAsset, Is.Not.Null);
90 Assert.That(retrievedAsset.ID, Is.EqualTo(asset.ID));
91 }
92
93 [Test]
94 public void TestExpireAsset()
95 {
96 TestHelper.InMethod();
97// log4net.Config.XmlConfigurator.Configure();
98
99 AssetBase asset = AssetHelpers.CreateAsset();
100 asset.ID = TestHelper.ParseTail(0x2).ToString();
101
102 m_cache.Store(asset);
103
104 m_cache.Expire(asset.ID);
105
106 AssetBase retrievedAsset = m_cache.Get(asset.ID.ToString());
107 Assert.That(retrievedAsset, Is.Null);
108 }
109
110 [Test]
111 public void TestClearCache()
112 {
113 TestHelper.InMethod();
114// log4net.Config.XmlConfigurator.Configure();
115
116 AssetBase asset = AssetHelpers.CreateAsset();
117 asset.ID = TestHelper.ParseTail(0x2).ToString();
118
119 m_cache.Store(asset);
120
121 m_cache.Clear();
122
123 AssetBase retrievedAsset = m_cache.Get(asset.ID.ToString());
124 Assert.That(retrievedAsset, Is.Null);
125 }
126 }
127} \ 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 2a6d362..04b20d4 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
449 449
450 public bool IsBannedFromLand(UUID avatar) 450 public bool IsBannedFromLand(UUID avatar)
451 { 451 {
452 if (m_scene.Permissions.IsAdministrator(avatar)) 452 if (m_scene.Permissions.CanEditParcelProperties(avatar, this, 0))
453 return false; 453 return false;
454 454
455 if (m_scene.RegionInfo.EstateSettings.IsEstateManager(avatar)) 455 if (m_scene.RegionInfo.EstateSettings.IsEstateManager(avatar))
@@ -463,7 +463,7 @@ namespace OpenSim.Region.CoreModules.World.Land
463 if (e.AgentID == avatar && e.Flags == AccessList.Ban) 463 if (e.AgentID == avatar && e.Flags == AccessList.Ban)
464 return true; 464 return true;
465 return false; 465 return false;
466 }) != -1 && LandData.OwnerID != avatar) 466 }) != -1)
467 { 467 {
468 return true; 468 return true;
469 } 469 }
@@ -473,7 +473,7 @@ namespace OpenSim.Region.CoreModules.World.Land
473 473
474 public bool IsRestrictedFromLand(UUID avatar) 474 public bool IsRestrictedFromLand(UUID avatar)
475 { 475 {
476 if (m_scene.Permissions.IsAdministrator(avatar)) 476 if (m_scene.Permissions.CanEditParcelProperties(avatar, this, 0))
477 return false; 477 return false;
478 478
479 if (m_scene.RegionInfo.EstateSettings.IsEstateManager(avatar)) 479 if (m_scene.RegionInfo.EstateSettings.IsEstateManager(avatar))
@@ -487,7 +487,7 @@ namespace OpenSim.Region.CoreModules.World.Land
487 if (e.AgentID == avatar && e.Flags == AccessList.Access) 487 if (e.AgentID == avatar && e.Flags == AccessList.Access)
488 return true; 488 return true;
489 return false; 489 return false;
490 }) == -1 && LandData.OwnerID != avatar) 490 }) == -1)
491 { 491 {
492 if (!HasGroupAccess(avatar)) 492 if (!HasGroupAccess(avatar))
493 { 493 {
diff --git a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs
index 43b37c7..2c7843f 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
134 return; 134 return;
135 135
136 m_allowGridGods = myConfig.GetBoolean("allow_grid_gods", false); 136 m_allowGridGods = myConfig.GetBoolean("allow_grid_gods", false);
137 m_bypassPermissions = !myConfig.GetBoolean("serverside_object_permissions", false); 137 m_bypassPermissions = !myConfig.GetBoolean("serverside_object_permissions", true);
138 m_propagatePermissions = myConfig.GetBoolean("propagate_permissions", true); 138 m_propagatePermissions = myConfig.GetBoolean("propagate_permissions", true);
139 m_RegionOwnerIsGod = myConfig.GetBoolean("region_owner_is_god", true); 139 m_RegionOwnerIsGod = myConfig.GetBoolean("region_owner_is_god", true);
140 m_RegionManagerIsGod = myConfig.GetBoolean("region_manager_is_god", false); 140 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
index cd2fe4f..21a755f 100644
--- a/OpenSim/Region/CoreModules/Avatar/NPC/INPCModule.cs
+++ b/OpenSim/Region/Framework/Interfaces/INPCModule.cs
@@ -28,7 +28,7 @@
28using OpenMetaverse; 28using OpenMetaverse;
29using OpenSim.Region.Framework.Scenes; 29using OpenSim.Region.Framework.Scenes;
30 30
31namespace OpenSim.Region.CoreModules.Avatar.NPC 31namespace OpenSim.Region.Framework.Interfaces
32{ 32{
33 public interface INPCModule 33 public interface INPCModule
34 { 34 {
diff --git a/OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs b/OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs
new file mode 100644
index 0000000..06cd14b
--- /dev/null
+++ b/OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs
@@ -0,0 +1,156 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using System.Threading;
32using log4net;
33using OpenMetaverse;
34using OpenSim.Framework;
35using OpenSim.Region.Framework.Interfaces;
36
37namespace OpenSim.Region.Framework.Scenes
38{
39 class FetchHolder
40 {
41 public IClientAPI Client { get; private set; }
42 public UUID ItemID { get; private set; }
43
44 public FetchHolder(IClientAPI client, UUID itemID)
45 {
46 Client = client;
47 ItemID = itemID;
48 }
49 }
50
51 /// <summary>
52 /// Send FetchInventoryReply information to clients asynchronously on a single thread rather than asynchronously via
53 /// multiple threads.
54 /// </summary>
55 /// <remarks>
56 /// If the main root inventory is right-clicked on a version 1 viewer for a user with a large inventory, a very
57 /// very large number of FetchInventory requests are sent to the simulator. Each is handled on a separate thread
58 /// by the IClientAPI, but the sheer number of requests overwhelms the number of threads available and ends up
59 /// freezing the inbound packet handling.
60 ///
61 /// This class makes the first FetchInventory packet thread process the queue. If new requests come
62 /// in while it is processing, then the subsequent threads just add the requests and leave it to the original
63 /// thread to process them.
64 ///
65 /// This might slow down outbound packets but these are limited by the IClientAPI outbound queues
66 /// anyway.
67 ///
68 /// It might be possible to ignore FetchInventory requests altogether, particularly as they are redundant wrt to
69 /// FetchInventoryDescendents requests, but this would require more investigation.
70 /// </remarks>
71 public class AsyncInventorySender
72 {
73 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
74
75 protected Scene m_scene;
76
77 /// <summary>
78 /// Queues fetch requests
79 /// </summary>
80 Queue<FetchHolder> m_fetchHolder = new Queue<FetchHolder>();
81
82 /// <summary>
83 /// Signal whether a queue is currently being processed or not.
84 /// </summary>
85 protected volatile bool m_processing;
86
87 public AsyncInventorySender(Scene scene)
88 {
89 m_processing = false;
90 m_scene = scene;
91 }
92
93 /// <summary>
94 /// Handle a fetch inventory request from the client
95 /// </summary>
96 /// <param name="remoteClient"></param>
97 /// <param name="itemID"></param>
98 /// <param name="ownerID"></param>
99 public void HandleFetchInventory(IClientAPI remoteClient, UUID itemID, UUID ownerID)
100 {
101 lock (m_fetchHolder)
102 {
103// m_log.DebugFormat(
104// "[ASYNC INVENTORY SENDER]: Putting request from {0} for {1} on queue", remoteClient.Name, itemID);
105
106 m_fetchHolder.Enqueue(new FetchHolder(remoteClient, itemID));
107 }
108
109 if (!m_processing)
110 {
111 m_processing = true;
112 ProcessQueue();
113 }
114 }
115
116 /// <summary>
117 /// Process the queue of fetches
118 /// </summary>
119 protected void ProcessQueue()
120 {
121 FetchHolder fh = null;
122
123 while (true)
124 {
125 lock (m_fetchHolder)
126 {
127// m_log.DebugFormat("[ASYNC INVENTORY SENDER]: {0} items left to process", m_fetchHolder.Count);
128
129 if (m_fetchHolder.Count == 0)
130 {
131 m_processing = false;
132 return;
133 }
134 else
135 {
136 fh = m_fetchHolder.Dequeue();
137 }
138 }
139
140 if (fh.Client.IsLoggingOut)
141 continue;
142
143// m_log.DebugFormat(
144// "[ASYNC INVENTORY SENDER]: Handling request from {0} for {1} on queue", fh.Client.Name, fh.ItemID);
145
146 InventoryItemBase item = new InventoryItemBase(fh.ItemID, fh.Client.AgentId);
147 item = m_scene.InventoryService.GetItem(item);
148
149 if (item != null)
150 fh.Client.SendInventoryItemDetails(item.Owner, item);
151
152 // TODO: Possibly log any failure
153 }
154 }
155 }
156} \ 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 28c0276..dbefb4a 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs
@@ -52,6 +52,11 @@ namespace OpenSim.Region.Framework.Scenes
52 protected AsyncSceneObjectGroupDeleter m_asyncSceneObjectDeleter; 52 protected AsyncSceneObjectGroupDeleter m_asyncSceneObjectDeleter;
53 53
54 /// <summary> 54 /// <summary>
55 /// Allows inventory details to be sent to clients asynchronously
56 /// </summary>
57 protected AsyncInventorySender m_asyncInventorySender;
58
59 /// <summary>
55 /// Start all the scripts in the scene which should be started. 60 /// Start all the scripts in the scene which should be started.
56 /// </summary> 61 /// </summary>
57 public void CreateScriptInstances() 62 public void CreateScriptInstances()
diff --git a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs
index e2d7208..44472b2 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
461 } 461 }
462 ); 462 );
463 } 463 }
464
465
466 /// <summary>
467 /// Handle a fetch inventory request from the client
468 /// </summary>
469 /// <param name="remoteClient"></param>
470 /// <param name="itemID"></param>
471 /// <param name="ownerID"></param>
472 public void HandleFetchInventory(IClientAPI remoteClient, UUID itemID, UUID ownerID)
473 {
474 if (LibraryService != null && LibraryService.LibraryRootFolder != null && ownerID == LibraryService.LibraryRootFolder.Owner)
475 {
476 //m_log.Debug("request info for library item");
477 return;
478 }
479
480 InventoryItemBase item = new InventoryItemBase(itemID, remoteClient.AgentId);
481 item = InventoryService.GetItem(item);
482
483 if (item != null)
484 {
485 remoteClient.SendInventoryItemDetails(ownerID, item);
486 }
487 // else shouldn't we send an alert message?
488 }
489 464
490 /// <summary> 465 /// <summary>
491 /// Tell the client about the various child items and folders contained in the requested folder. 466 /// 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 253adae..e02a866 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.cs
@@ -604,6 +604,8 @@ namespace OpenSim.Region.Framework.Scenes
604 m_asyncSceneObjectDeleter = new AsyncSceneObjectGroupDeleter(this); 604 m_asyncSceneObjectDeleter = new AsyncSceneObjectGroupDeleter(this);
605 m_asyncSceneObjectDeleter.Enabled = true; 605 m_asyncSceneObjectDeleter.Enabled = true;
606 606
607 m_asyncInventorySender = new AsyncInventorySender(this);
608
607 #region Region Settings 609 #region Region Settings
608 610
609 // Load region settings 611 // Load region settings
@@ -2866,14 +2868,13 @@ namespace OpenSim.Region.Framework.Scenes
2866 2868
2867 public virtual void SubscribeToClientInventoryEvents(IClientAPI client) 2869 public virtual void SubscribeToClientInventoryEvents(IClientAPI client)
2868 { 2870 {
2869
2870 client.OnLinkInventoryItem += HandleLinkInventoryItem; 2871 client.OnLinkInventoryItem += HandleLinkInventoryItem;
2871 client.OnCreateNewInventoryFolder += HandleCreateInventoryFolder; 2872 client.OnCreateNewInventoryFolder += HandleCreateInventoryFolder;
2872 client.OnUpdateInventoryFolder += HandleUpdateInventoryFolder; 2873 client.OnUpdateInventoryFolder += HandleUpdateInventoryFolder;
2873 client.OnMoveInventoryFolder += HandleMoveInventoryFolder; // 2; //!! 2874 client.OnMoveInventoryFolder += HandleMoveInventoryFolder; // 2; //!!
2874 client.OnFetchInventoryDescendents += HandleFetchInventoryDescendents; 2875 client.OnFetchInventoryDescendents += HandleFetchInventoryDescendents;
2875 client.OnPurgeInventoryDescendents += HandlePurgeInventoryDescendents; // 2; //!! 2876 client.OnPurgeInventoryDescendents += HandlePurgeInventoryDescendents; // 2; //!!
2876 client.OnFetchInventory += HandleFetchInventory; 2877 client.OnFetchInventory += m_asyncInventorySender.HandleFetchInventory;
2877 client.OnUpdateInventoryItem += UpdateInventoryItemAsset; 2878 client.OnUpdateInventoryItem += UpdateInventoryItemAsset;
2878 client.OnCopyInventoryItem += CopyInventoryItem; 2879 client.OnCopyInventoryItem += CopyInventoryItem;
2879 client.OnMoveItemsAndLeaveCopy += MoveInventoryItemsLeaveCopy; 2880 client.OnMoveItemsAndLeaveCopy += MoveInventoryItemsLeaveCopy;
@@ -2993,13 +2994,12 @@ namespace OpenSim.Region.Framework.Scenes
2993 2994
2994 public virtual void UnSubscribeToClientInventoryEvents(IClientAPI client) 2995 public virtual void UnSubscribeToClientInventoryEvents(IClientAPI client)
2995 { 2996 {
2996
2997 client.OnCreateNewInventoryFolder -= HandleCreateInventoryFolder; 2997 client.OnCreateNewInventoryFolder -= HandleCreateInventoryFolder;
2998 client.OnUpdateInventoryFolder -= HandleUpdateInventoryFolder; 2998 client.OnUpdateInventoryFolder -= HandleUpdateInventoryFolder;
2999 client.OnMoveInventoryFolder -= HandleMoveInventoryFolder; // 2; //!! 2999 client.OnMoveInventoryFolder -= HandleMoveInventoryFolder; // 2; //!!
3000 client.OnFetchInventoryDescendents -= HandleFetchInventoryDescendents; 3000 client.OnFetchInventoryDescendents -= HandleFetchInventoryDescendents;
3001 client.OnPurgeInventoryDescendents -= HandlePurgeInventoryDescendents; // 2; //!! 3001 client.OnPurgeInventoryDescendents -= HandlePurgeInventoryDescendents; // 2; //!!
3002 client.OnFetchInventory -= HandleFetchInventory; 3002 client.OnFetchInventory -= m_asyncInventorySender.HandleFetchInventory;
3003 client.OnUpdateInventoryItem -= UpdateInventoryItemAsset; 3003 client.OnUpdateInventoryItem -= UpdateInventoryItemAsset;
3004 client.OnCopyInventoryItem -= CopyInventoryItem; 3004 client.OnCopyInventoryItem -= CopyInventoryItem;
3005 client.OnMoveInventoryItem -= MoveInventoryItem; 3005 client.OnMoveInventoryItem -= MoveInventoryItem;
diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
index 48d236f..3cdd06d 100644
--- a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
+++ b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
@@ -34,7 +34,6 @@ using Nini.Config;
34using OpenMetaverse; 34using OpenMetaverse;
35using OpenSim.Region.Framework.Interfaces; 35using OpenSim.Region.Framework.Interfaces;
36using OpenSim.Region.Framework.Scenes; 36using OpenSim.Region.Framework.Scenes;
37using OpenSim.Region.CoreModules.Avatar.NPC;
38using OpenSim.Framework; 37using OpenSim.Framework;
39using Timer=System.Timers.Timer; 38using Timer=System.Timers.Timer;
40using OpenSim.Services.Interfaces; 39using OpenSim.Services.Interfaces;
@@ -47,31 +46,11 @@ namespace OpenSim.Region.OptionalModules.World.NPC
47 46
48 // private const bool m_enabled = false; 47 // private const bool m_enabled = false;
49 48
50 private Mutex m_createMutex;
51 private Timer m_timer;
52
53 private Dictionary<UUID,NPCAvatar> m_avatars = new Dictionary<UUID, NPCAvatar>(); 49 private Dictionary<UUID,NPCAvatar> m_avatars = new Dictionary<UUID, NPCAvatar>();
54 private Dictionary<UUID,AvatarAppearance> m_appearanceCache = new Dictionary<UUID, AvatarAppearance>(); 50 private Dictionary<UUID,AvatarAppearance> m_appearanceCache = new Dictionary<UUID, AvatarAppearance>();
55 51
56 // Timer vars.
57 private bool p_inUse = false;
58 private readonly object p_lock = new object();
59 // Private Temporary Variables.
60 private string p_firstname;
61 private string p_lastname;
62 private Vector3 p_position;
63 private Scene p_scene;
64 private UUID p_cloneAppearanceFrom;
65 private UUID p_returnUuid;
66
67 public void Initialise(Scene scene, IConfigSource source) 52 public void Initialise(Scene scene, IConfigSource source)
68 { 53 {
69 m_createMutex = new Mutex(false);
70
71 m_timer = new Timer(500);
72 m_timer.Elapsed += m_timer_Elapsed;
73 m_timer.Start();
74
75 scene.RegisterModuleInterface<INPCModule>(this); 54 scene.RegisterModuleInterface<INPCModule>(this);
76 } 55 }
77 56
@@ -90,35 +69,53 @@ namespace OpenSim.Region.OptionalModules.World.NPC
90 return new AvatarAppearance(); 69 return new AvatarAppearance();
91 } 70 }
92 71
93 public UUID CreateNPC(string firstname, string lastname,Vector3 position, Scene scene, UUID cloneAppearanceFrom) 72 public UUID CreateNPC(string firstname, string lastname, Vector3 position, Scene scene, UUID cloneAppearanceFrom)
94 { 73 {
74 NPCAvatar npcAvatar = new NPCAvatar(firstname, lastname, position, scene);
75 npcAvatar.CircuitCode = (uint)Util.RandomClass.Next(0, int.MaxValue);
76
95 m_log.DebugFormat( 77 m_log.DebugFormat(
96 "[NPC MODULE]: Queueing request to create NPC {0} {1} at {2} in {3} cloning appearance of {4}", 78 "[NPC MODULE]: Creating NPC {0} {1} {2} at {3} in {4}",
97 firstname, lastname, position, scene.RegionInfo.RegionName, cloneAppearanceFrom); 79 firstname, lastname, npcAvatar.AgentId, position, scene.RegionInfo.RegionName);
80
81 AgentCircuitData acd = new AgentCircuitData();
82 acd.AgentID = npcAvatar.AgentId;
83 acd.firstname = firstname;
84 acd.lastname = lastname;
85 acd.ServiceURLs = new Dictionary<string, object>();
98 86
99 // Block. 87 AvatarAppearance originalAppearance = GetAppearance(cloneAppearanceFrom, scene);
100 m_createMutex.WaitOne(); 88 AvatarAppearance npcAppearance = new AvatarAppearance(originalAppearance, true);
89 acd.Appearance = npcAppearance;
101 90
102 // Copy Temp Variables for Timer to pick up. 91 scene.AuthenticateHandler.AddNewCircuit(npcAvatar.CircuitCode, acd);
103 lock (p_lock) 92 scene.AddNewClient(npcAvatar);
93
94 ScenePresence sp;
95 if (scene.TryGetScenePresence(npcAvatar.AgentId, out sp))
104 { 96 {
105 p_firstname = firstname; 97 m_log.DebugFormat(
106 p_lastname = lastname; 98 "[NPC MODULE]: Successfully retrieved scene presence for NPC {0} {1}", sp.Name, sp.UUID);
107 p_position = position; 99
108 p_scene = scene; 100 // Shouldn't call this - temporary.
109 p_cloneAppearanceFrom = cloneAppearanceFrom; 101 sp.CompleteMovement(npcAvatar);
110 p_inUse = true;
111 p_returnUuid = UUID.Zero;
112 }
113 102
114 while (p_returnUuid == UUID.Zero) 103// sp.SendAppearanceToAllOtherAgents();
104//
105// // Send animations back to the avatar as well
106// sp.Animator.SendAnimPack();
107 }
108 else
115 { 109 {
116 Thread.Sleep(250); 110 m_log.WarnFormat("[NPC MODULE]: Could not find scene presence for NPC {0} {1}", sp.Name, sp.UUID);
117 } 111 }
118 112
119 m_createMutex.ReleaseMutex(); 113 lock (m_avatars)
114 m_avatars.Add(npcAvatar.AgentId, npcAvatar);
120 115
121 return p_returnUuid; 116 m_log.DebugFormat("[NPC MODULE]: Created NPC with id {0}", npcAvatar.AgentId);
117
118 return npcAvatar.AgentId;
122 } 119 }
123 120
124 public void Autopilot(UUID agentID, Scene scene, Vector3 pos) 121 public void Autopilot(UUID agentID, Scene scene, Vector3 pos)
@@ -157,69 +154,6 @@ namespace OpenSim.Region.OptionalModules.World.NPC
157 } 154 }
158 } 155 }
159 156
160 void m_timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
161 {
162 try
163 {
164 lock (p_lock)
165 {
166 if (p_inUse)
167 {
168 p_inUse = false;
169
170 NPCAvatar npcAvatar = new NPCAvatar(p_firstname, p_lastname, p_position, p_scene);
171 npcAvatar.CircuitCode = (uint) Util.RandomClass.Next(0, int.MaxValue);
172
173 m_log.DebugFormat(
174 "[NPC MODULE]: Creating NPC {0} {1} {2} at {3} in {4}",
175 p_firstname, p_lastname, npcAvatar.AgentId, p_position, p_scene.RegionInfo.RegionName);
176
177 AgentCircuitData acd = new AgentCircuitData();
178 acd.AgentID = npcAvatar.AgentId;
179 acd.firstname = p_firstname;
180 acd.lastname = p_lastname;
181 acd.ServiceURLs = new Dictionary<string, object>();
182
183 AvatarAppearance originalAppearance = GetAppearance(p_cloneAppearanceFrom, p_scene);
184 AvatarAppearance npcAppearance = new AvatarAppearance(originalAppearance, true);
185 acd.Appearance = npcAppearance;
186
187 p_scene.AuthenticateHandler.AddNewCircuit(npcAvatar.CircuitCode, acd);
188 p_scene.AddNewClient(npcAvatar);
189
190 ScenePresence sp;
191 if (p_scene.TryGetScenePresence(npcAvatar.AgentId, out sp))
192 {
193 m_log.DebugFormat(
194 "[NPC MODULE]: Successfully retrieved scene presence for NPC {0} {1}", sp.Name, sp.UUID);
195
196 // Shouldn't call this - temporary.
197 sp.CompleteMovement(npcAvatar);
198
199 // sp.SendAppearanceToAllOtherAgents();
200 //
201 // // Send animations back to the avatar as well
202 // sp.Animator.SendAnimPack();
203 }
204 else
205 {
206 m_log.WarnFormat("[NPC MODULE]: Could not find scene presence for NPC {0} {1}", sp.Name, sp.UUID);
207 }
208
209 m_avatars.Add(npcAvatar.AgentId, npcAvatar);
210
211 p_returnUuid = npcAvatar.AgentId;
212
213 m_log.DebugFormat("[NPC MODULE]: Created NPC with id {0}", p_returnUuid);
214 }
215 }
216 }
217 catch (Exception ex)
218 {
219 m_log.ErrorFormat("[NPC MODULE]: NPC creation failed with exception {0} {1}", ex.Message, ex.StackTrace);
220 }
221 }
222
223 public void PostInitialise() 157 public void PostInitialise()
224 { 158 {
225 } 159 }
@@ -238,4 +172,4 @@ namespace OpenSim.Region.OptionalModules.World.NPC
238 get { return true; } 172 get { return true; }
239 } 173 }
240 } 174 }
241} 175} \ 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 0000000..899e721
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs
@@ -0,0 +1,71 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Reflection;
30using Nini.Config;
31using NUnit.Framework;
32using OpenMetaverse;
33using OpenSim.Framework;
34using OpenSim.Framework.Communications;
35using OpenSim.Region.CoreModules.ServiceConnectorsOut.Avatar;
36using OpenSim.Region.Framework.Interfaces;
37using OpenSim.Region.Framework.Scenes;
38using OpenSim.Services.AvatarService;
39using OpenSim.Tests.Common;
40using OpenSim.Tests.Common.Mock;
41
42namespace OpenSim.Region.OptionalModules.World.NPC.Tests
43{
44 [TestFixture]
45 public class NPCModuleTests
46 {
47 [Test]
48 public void TestCreate()
49 {
50 TestHelper.InMethod();
51// log4net.Config.XmlConfigurator.Configure();
52
53 IConfigSource config = new IniConfigSource();
54
55 config.AddConfig("Modules");
56 config.Configs["Modules"].Set("AvatarServices", "LocalAvatarServicesConnector");
57 config.AddConfig("AvatarService");
58 config.Configs["AvatarService"].Set("LocalServiceModule", "OpenSim.Services.AvatarService.dll:AvatarService");
59 config.Configs["AvatarService"].Set("StorageProvider", "OpenSim.Data.Null.dll");
60
61 TestScene scene = SceneSetupHelpers.SetupScene();
62 SceneSetupHelpers.SetupSceneModules(scene, config, new NPCModule(), new LocalAvatarServicesConnector());
63
64 INPCModule npcModule = scene.RequestModuleInterface<INPCModule>();
65 UUID npcId = npcModule.CreateNPC("John", "Smith", new Vector3(128, 128, 30), scene, UUID.Zero);
66
67 ScenePresence npc = scene.GetScenePresence(npcId);
68 Assert.That(npc, Is.Not.Null);
69 }
70 }
71} \ No newline at end of file
diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
index 99b2d84..a5fe45b 100644
--- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
+++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
@@ -291,7 +291,14 @@ namespace OpenSim.Region.Physics.Meshing
291 { 291 {
292 try 292 try
293 { 293 {
294 meshOsd = (OSDMap)OSDParser.DeserializeLLSDBinary(data); 294 OSD osd = OSDParser.DeserializeLLSDBinary(data);
295 if (osd is OSDMap)
296 meshOsd = (OSDMap)osd;
297 else
298 {
299 m_log.Warn("[Mesh}: unable to cast mesh asset to OSDMap");
300 return null;
301 }
295 } 302 }
296 catch (Exception e) 303 catch (Exception e)
297 { 304 {
@@ -302,11 +309,17 @@ namespace OpenSim.Region.Physics.Meshing
302 309
303 if (meshOsd is OSDMap) 310 if (meshOsd is OSDMap)
304 { 311 {
312 OSDMap physicsParms = null;
305 OSDMap map = (OSDMap)meshOsd; 313 OSDMap map = (OSDMap)meshOsd;
306 OSDMap physicsParms = (OSDMap)map["physics_shape"]; // old asset format 314 if (map.ContainsKey("physics_shape"))
307 315 physicsParms = (OSDMap)map["physics_shape"]; // old asset format
308 if (physicsParms.Count == 0) 316 else if (map.ContainsKey("physics_mesh"))
309 physicsParms = (OSDMap)map["physics_mesh"]; // new asset format 317 physicsParms = (OSDMap)map["physics_mesh"]; // new asset format
318 if (physicsParms == null)
319 {
320 m_log.Warn("[Mesh]: no recognized physics mesh found in mesh asset");
321 return null;
322 }
310 323
311 int physOffset = physicsParms["offset"].AsInteger() + (int)start; 324 int physOffset = physicsParms["offset"].AsInteger() + (int)start;
312 int physSize = physicsParms["size"].AsInteger(); 325 int physSize = physicsParms["size"].AsInteger();
diff --git a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs
index a0101af..8d9f5f1 100644
--- a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs
+++ b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs
@@ -2502,7 +2502,7 @@ namespace OpenSim.Region.Physics.OdePlugin
2502 } 2502 }
2503 2503
2504 // 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 2504 // 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
2505 if (!forceSimplePrimMeshing) 2505 if (!forceSimplePrimMeshing && !pbs.SculptEntry)
2506 { 2506 {
2507 if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) 2507 if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
2508 || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 2508 || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1
@@ -2592,6 +2592,9 @@ namespace OpenSim.Region.Physics.OdePlugin
2592 } 2592 }
2593 } 2593 }
2594 2594
2595 if (pbs.SculptEntry && meshSculptedPrim)
2596 iPropertiesNotSupportedDefault++;
2597
2595 2598
2596 if (iPropertiesNotSupportedDefault == 0) 2599 if (iPropertiesNotSupportedDefault == 0)
2597 { 2600 {
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs
index 2b8155f..31222ff 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;
38using OpenSim.Framework; 38using OpenSim.Framework;
39 39
40using OpenSim.Framework.Console; 40using OpenSim.Framework.Console;
41using OpenSim.Region.CoreModules.Avatar.NPC;
42using OpenSim.Region.Framework.Interfaces; 41using OpenSim.Region.Framework.Interfaces;
43using OpenSim.Region.Framework.Scenes; 42using OpenSim.Region.Framework.Scenes;
44using OpenSim.Region.ScriptEngine.Shared; 43using OpenSim.Region.ScriptEngine.Shared;
diff --git a/OpenSim/Tests/Common/Helpers/AssetHelpers.cs b/OpenSim/Tests/Common/Helpers/AssetHelpers.cs
index aa55bcd..9b68331 100644
--- a/OpenSim/Tests/Common/Helpers/AssetHelpers.cs
+++ b/OpenSim/Tests/Common/Helpers/AssetHelpers.cs
@@ -37,6 +37,15 @@ namespace OpenSim.Tests.Common
37 public class AssetHelpers 37 public class AssetHelpers
38 { 38 {
39 /// <summary> 39 /// <summary>
40 /// Create a notecard asset with a random uuids and dummy text.
41 /// </summary>
42 /// <returns></returns>
43 public static AssetBase CreateAsset()
44 {
45 return CreateAsset(UUID.Random(), AssetType.Notecard, "hello", UUID.Random());
46 }
47
48 /// <summary>
40 /// Create a notecard asset with a random uuid and dummy text. 49 /// Create a notecard asset with a random uuid and dummy text.
41 /// </summary> 50 /// </summary>
42 /// <param name="creatorId">/param> 51 /// <param name="creatorId">/param>
diff --git a/OpenSim/Tests/Common/TestHelper.cs b/OpenSim/Tests/Common/TestHelper.cs
index 1722e59..86bd107 100644
--- a/OpenSim/Tests/Common/TestHelper.cs
+++ b/OpenSim/Tests/Common/TestHelper.cs
@@ -28,6 +28,7 @@
28using System; 28using System;
29using System.Diagnostics; 29using System.Diagnostics;
30using NUnit.Framework; 30using NUnit.Framework;
31using OpenMetaverse;
31 32
32namespace OpenSim.Tests.Common 33namespace OpenSim.Tests.Common
33{ 34{
@@ -56,5 +57,15 @@ namespace OpenSim.Tests.Common
56 Console.WriteLine(); 57 Console.WriteLine();
57 Console.WriteLine("===> In Test Method : {0} <===", stackTrace.GetFrame(1).GetMethod().Name); 58 Console.WriteLine("===> In Test Method : {0} <===", stackTrace.GetFrame(1).GetMethod().Name);
58 } 59 }
60
61 /// <summary>
62 /// Parse tail section into full UUID.
63 /// </summary>
64 /// <param name="tail"></param>
65 /// <returns></returns>
66 public static UUID ParseTail(int tail)
67 {
68 return new UUID(string.Format("00000000-0000-0000-0000-{0:X12}", tail));
69 }
59 } 70 }
60} 71}
diff --git a/OpenSim/Tools/Configger/ConfigurationLoader.cs b/OpenSim/Tools/Configger/ConfigurationLoader.cs
index 8e71b42..3914652 100644
--- a/OpenSim/Tools/Configger/ConfigurationLoader.cs
+++ b/OpenSim/Tools/Configger/ConfigurationLoader.cs
@@ -239,7 +239,7 @@ namespace OpenSim.Tools.Configger
239 config.Set("meshing", "Meshmerizer"); 239 config.Set("meshing", "Meshmerizer");
240 config.Set("physical_prim", true); 240 config.Set("physical_prim", true);
241 config.Set("see_into_this_sim_from_neighbor", true); 241 config.Set("see_into_this_sim_from_neighbor", true);
242 config.Set("serverside_object_permissions", false); 242 config.Set("serverside_object_permissions", true);
243 config.Set("storage_plugin", "OpenSim.Data.SQLite.dll"); 243 config.Set("storage_plugin", "OpenSim.Data.SQLite.dll");
244 config.Set("storage_connection_string", "URI=file:OpenSim.db,version=3"); 244 config.Set("storage_connection_string", "URI=file:OpenSim.db,version=3");
245 config.Set("storage_prim_inventories", true); 245 config.Set("storage_prim_inventories", true);
diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example
index 98bb355..60d37fb 100644
--- a/bin/OpenSim.ini.example
+++ b/bin/OpenSim.ini.example
@@ -78,20 +78,19 @@
78 ; DrawPrimOnMapTile = true 78 ; DrawPrimOnMapTile = true
79 79
80 ;# {NonPhysicalPrimMax} {} {Maximum size of nonphysical prims?} {} 256 80 ;# {NonPhysicalPrimMax} {} {Maximum size of nonphysical prims?} {} 256
81 ;; Maximum size for non-physical prims 81 ;; Maximum size for non-physical prims. Affects resizing of existing prims. This can be overriden in the region config file (as NonphysicalPrimMax!).
82 ; NonPhysicalPrimMax = 256 82 ; NonPhysicalPrimMax = 256
83 83
84 ;# {PhysicalPrimMax} {} {Maximum size of physical prims?} {} 10 84 ;# {PhysicalPrimMax} {} {Maximum size of physical prims?} {} 10
85 ;; Maximum size where a prim can be physical 85 ;; Maximum size where a prim can be physical. Affects resizing of existing prims. This can be overriden in the region config file.
86 ; PhysicalPrimMax = 10 86 ; PhysicalPrimMax = 10
87 87
88 ;; Prevent the creation, import and rez of prims that exceed the 88 ;; If a viewer attempts to rez a prim larger than the non-physical or physical prim max, clamp the dimensions to the appropriate maximum
89 ;; maximum size. 89 ;; This can be overriden in the region config file.
90 ; ClampPrimSize = false 90 ; ClampPrimSize = false
91 91
92 ;# {AllowScriptCrossing} {} {Allow scripts to cross into this region} {true false} false 92 ;# {AllowScriptCrossing} {} {Allow scripts to cross into this region} {true false} false
93 ;; Allow scripts to cross region boundaries. These are recompiled on the 93 ;; 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.
94 ;; new region.
95 ; AllowScriptCrossing = false 94 ; AllowScriptCrossing = false
96 95
97 ;# {TrustBinaries} {AllowScriptCrossing:true} {Accept compiled binary script code? (DANGEROUS!)} {true false} false 96 ;# {TrustBinaries} {AllowScriptCrossing:true} {Accept compiled binary script code? (DANGEROUS!)} {true false} false
@@ -173,7 +172,7 @@
173 ;; permission checks (allowing anybody to copy 172 ;; permission checks (allowing anybody to copy
174 ;; any item, etc. This may not yet be implemented uniformally. 173 ;; any item, etc. This may not yet be implemented uniformally.
175 ;; If set to true, then all permissions checks are carried out 174 ;; If set to true, then all permissions checks are carried out
176 ; serverside_object_permissions = false 175 ; serverside_object_permissions = true
177 176
178 ;; This allows users with a UserLevel of 200 or more to assume god 177 ;; This allows users with a UserLevel of 200 or more to assume god
179 ;; powers in the regions in this simulator. 178 ;; powers in the regions in this simulator.
@@ -292,28 +291,31 @@
292 ;; building's lights to possibly not be rendered. 291 ;; building's lights to possibly not be rendered.
293 ; DisableFacelights = "false" 292 ; DisableFacelights = "false"
294 293
294
295[ClientStack.LindenCaps] 295[ClientStack.LindenCaps]
296 ;; For the long list of capabilities, see OpenSimDefaults.ini 296 ;; For the long list of capabilities, see OpenSimDefaults.ini
297 ;; Here are the few ones you may want to change. Possible values 297 ;; Here are the few ones you may want to change. Possible values
298 ;; are: 298 ;; are:
299 ;; "" -- empty, capability disabled 299 ;; "" -- empty, capability disabled
300 ;; "localhost" -- capability enabled and served by the simulator 300 ;; "localhost" -- capability enabled and served by the simulator
301 ;; "<url>" -- capability enabled and served by some other server 301 ;; "<url>" -- capability enabled and served by some other server
302 ;; 302 ;;
303 ; These are enabled by default to localhost. Change if you see fit. 303 ; These are enabled by default to localhost. Change if you see fit.
304 Cap_GetTexture = "localhost" 304 Cap_GetTexture = "localhost"
305 Cap_GetMesh = "localhost" 305 Cap_GetMesh = "localhost"
306 ; This is disabled by default. Change if you see fit. Note that 306 ; This is disabled by default. Change if you see fit. Note that
307 ; serving this cap from the simulators may lead to poor performace. 307 ; serving this cap from the simulators may lead to poor performace.
308 Cap_WebFetchInventoryDescendents = "" 308 Cap_WebFetchInventoryDescendents = ""
309
309 310
310[SimulatorFeatures] 311[SimulatorFeatures]
311 ; Experimental new information sent in SimulatorFeatures cap for Kokua viewers 312 ; Experimental new information sent in SimulatorFeatures cap for Kokua viewers
312 ; meant to override the MapImage and search server url given at login, and varying 313 ; meant to override the MapImage and search server url given at login, and varying
313 ; on a sim-basis. 314 ; on a sim-basis.
314 ; Viewers that don't understand it, will ignore it 315 ; Viewers that don't understand it, will ignore it
315 ;MapImageServerURI = "http://127.0.0.1:9000/ 316 ;MapImageServerURI = "http://127.0.0.1:9000/
316 ;SearchServerURI = "http://127.0.0.1:9000/ 317 ;SearchServerURI = "http://127.0.0.1:9000/
318
317 319
318[Chat] 320[Chat]
319 ;# {whisper_distance} {} {Distance at which a whisper is heard, in meters?} {} 10 321 ;# {whisper_distance} {} {Distance at which a whisper is heard, in meters?} {} 10
@@ -650,6 +652,7 @@
650 ;; If using a remote connector, specify the server URL 652 ;; If using a remote connector, specify the server URL
651 ; FreeswitchServiceURL = http://my.grid.server:8004/fsapi 653 ; FreeswitchServiceURL = http://my.grid.server:8004/fsapi
652 654
655
653[Groups] 656[Groups]
654 ;# {Enabled} {} {Enable groups?} {true false} false 657 ;# {Enabled} {} {Enable groups?} {true false} false
655 ;; Enables the groups module 658 ;; Enables the groups module
@@ -707,11 +710,13 @@
707 ;; Enable media on a prim facilities 710 ;; Enable media on a prim facilities
708 ; Enabled = true; 711 ; Enabled = true;
709 712
713
710[PrimLimitsModule] 714[PrimLimitsModule]
711 ;# {EnforcePrimLimits} {} {Enforce parcel prim limits} {true false} false 715 ;# {EnforcePrimLimits} {} {Enforce parcel prim limits} {true false} false
712 ;; Enable parcel prim limits. Off by default to emulate pre-existing behavior. 716 ;; Enable parcel prim limits. Off by default to emulate pre-existing behavior.
713 ; EnforcePrimLimits = false 717 ; EnforcePrimLimits = false
714 718
719
715[Architecture] 720[Architecture]
716 ;# {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 721 ;# {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
717 ;; Uncomment one of the following includes as required. For instance, to create a standalone OpenSim, 722 ;; 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 fd39fe6..5aeadce 100644
--- a/bin/OpenSimDefaults.ini
+++ b/bin/OpenSimDefaults.ini
@@ -14,10 +14,13 @@
14 ; Place to create a PID file 14 ; Place to create a PID file
15 ; PIDFile = "/tmp/my.pid" 15 ; PIDFile = "/tmp/my.pid"
16 16
17 ; Console commands run at startup
17 startup_console_commands_file = "startup_commands.txt" 18 startup_console_commands_file = "startup_commands.txt"
19
20 ; Console commands run on shutdown
18 shutdown_console_commands_file = "shutdown_commands.txt" 21 shutdown_console_commands_file = "shutdown_commands.txt"
19 22
20 ; To run a script every few minutes, set the script filename here 23 ; Console commands run every 20 minutes
21 ; timer_Script = "filename" 24 ; timer_Script = "filename"
22 25
23 ; ## 26 ; ##
@@ -70,12 +73,17 @@
70 ; Use terrain texture for maptiles if true, use shaded green if false 73 ; Use terrain texture for maptiles if true, use shaded green if false
71 TextureOnMapTile = false 74 TextureOnMapTile = false
72 75
73 ; Maximum total size, and maximum size where a prim can be physical 76 ; Maximum size of non physical prims. Affects resizing of existing prims. This can be overriden in the region config file (as NonphysicalPrimMax!).
74 NonPhysicalPrimMax = 256 77 NonPhysicalPrimMax = 256
75 PhysicalPrimMax = 10 ; (I think this was moved to the Regions.ini!) 78
79 ; Maximum size of physical prims. Affects resizing of existing prims. This can be overriden in the region config file.
80 PhysicalPrimMax = 10
81
82 ; If a viewer attempts to rez a prim larger than the non-physical or physical prim max, clamp the dimensions to the appropriate maximum
83 ; This can be overriden in the region config file.
76 ClampPrimSize = false 84 ClampPrimSize = false
77 85
78 ; Allow scripts to cross region boundaries. These are recompiled on the new region. 86 ; 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.
79 AllowScriptCrossing = false 87 AllowScriptCrossing = false
80 88
81 ; Allow compiled script binary code to cross region boundaries. 89 ; Allow compiled script binary code to cross region boundaries.
@@ -94,7 +102,7 @@
94 ; neighbors on each side for a total of 49 regions in view. Warning, unless 102 ; neighbors on each side for a total of 49 regions in view. Warning, unless
95 ; all the regions have the same drawdistance, you will end up with strange 103 ; all the regions have the same drawdistance, you will end up with strange
96 ; effects because the agents that get closed may be inconsistent. 104 ; effects because the agents that get closed may be inconsistent.
97 ; DefaultDrawDistance = 255.0 105 DefaultDrawDistance = 255.0
98 106
99 ; If you have only one region in an instance, or to avoid the many bugs 107 ; If you have only one region in an instance, or to avoid the many bugs
100 ; that you can trigger in modules by restarting a region, set this to 108 ; that you can trigger in modules by restarting a region, set this to
@@ -102,7 +110,7 @@
102 ; This is meant to be used on systems where some external system like 110 ; This is meant to be used on systems where some external system like
103 ; Monit will restart any instance that exits, thereby making the shutdown 111 ; Monit will restart any instance that exits, thereby making the shutdown
104 ; into a restart. 112 ; into a restart.
105 ;InworldRestartShutsDown = false 113 InworldRestartShutsDown = false
106 114
107 ; ## 115 ; ##
108 ; ## PRIM STORAGE 116 ; ## PRIM STORAGE
@@ -227,7 +235,6 @@
227 235
228 ; If enabled, enableFlySlow will change the primary fly state to 236 ; If enabled, enableFlySlow will change the primary fly state to
229 ; FLYSLOW, and the "always run" state will be the regular fly. 237 ; FLYSLOW, and the "always run" state will be the regular fly.
230
231 enableflyslow = false 238 enableflyslow = false
232 239
233 ; PreJump is an additional animation state, but it probably 240 ; PreJump is an additional animation state, but it probably
@@ -236,7 +243,6 @@
236 243
237 ; This is commented so it will come on automatically once it's 244 ; This is commented so it will come on automatically once it's
238 ; supported. 245 ; supported.
239
240 ; enableprejump = true 246 ; enableprejump = true
241 247
242 ; Simulator Stats URI 248 ; Simulator Stats URI
@@ -265,6 +271,7 @@
265 DelayBeforeAppearanceSave = 5 271 DelayBeforeAppearanceSave = 5
266 DelayBeforeAppearanceSend = 2 272 DelayBeforeAppearanceSend = 2
267 273
274
268[SMTP] 275[SMTP]
269 enabled=false 276 enabled=false
270 277
diff --git a/bin/config-include/FlotsamCache.ini.example b/bin/config-include/FlotsamCache.ini.example
index ad38ad1..cd39f8c 100644
--- a/bin/config-include/FlotsamCache.ini.example
+++ b/bin/config-include/FlotsamCache.ini.example
@@ -19,9 +19,12 @@
19 ; 0 to disable 19 ; 0 to disable
20 HitRateDisplay = 100 20 HitRateDisplay = 100
21 21
22 ; Set to false for disk cache only. 22 ; Set to false for no memory cache
23 MemoryCacheEnabled = false 23 MemoryCacheEnabled = false
24 24
25 ; Set to false for no file cache
26 FileCacheEnabled = true
27
25 ; How long {in hours} to keep assets cached in memory, .5 == 30 minutes 28 ; How long {in hours} to keep assets cached in memory, .5 == 30 minutes
26 ; Optimization: for VPS or limited memory system installs set Timeout to .016 (1 minute) 29 ; Optimization: for VPS or limited memory system installs set Timeout to .016 (1 minute)
27 ; increases performance without large memory impact 30 ; increases performance without large memory impact
diff --git a/prebuild.xml b/prebuild.xml
index 0e96176..6362848 100644
--- a/prebuild.xml
+++ b/prebuild.xml
@@ -2985,6 +2985,7 @@
2985 <Files> 2985 <Files>
2986 <!-- SADLY the way this works means you need to keep adding these paths --> 2986 <!-- SADLY the way this works means you need to keep adding these paths -->
2987 <Match path="Agent/TextureSender/Tests" pattern="*.cs" recurse="true"/> 2987 <Match path="Agent/TextureSender/Tests" pattern="*.cs" recurse="true"/>
2988 <Match path="Asset/Tests" pattern="*.cs" recurse="true"/>
2988 <Match path="Avatar/Inventory/Archiver/Tests" pattern="*.cs" recurse="true"/> 2989 <Match path="Avatar/Inventory/Archiver/Tests" pattern="*.cs" recurse="true"/>
2989 <Match path="Framework/InventoryAccess/Tests" pattern="*.cs" recurse="true"/> 2990 <Match path="Framework/InventoryAccess/Tests" pattern="*.cs" recurse="true"/>
2990 <Match path="World/Archiver/Tests" pattern="*.cs" recurse="true"/> 2991 <Match path="World/Archiver/Tests" pattern="*.cs" recurse="true"/>
@@ -3029,6 +3030,7 @@
3029 <Reference name="OpenSim.Region.CoreModules"/> 3030 <Reference name="OpenSim.Region.CoreModules"/>
3030 <Reference name="OpenSim.Region.OptionalModules"/> 3031 <Reference name="OpenSim.Region.OptionalModules"/>
3031 <Reference name="OpenSim.Region.Physics.Manager"/> 3032 <Reference name="OpenSim.Region.Physics.Manager"/>
3033 <Reference name="OpenSim.Services.AvatarService"/>
3032 <Reference name="OpenSim.Services.Interfaces"/> 3034 <Reference name="OpenSim.Services.Interfaces"/>
3033 3035
3034 <!-- Unit tests --> 3036 <!-- Unit tests -->
@@ -3052,6 +3054,7 @@
3052 <Files> 3054 <Files>
3053 <!-- SADLY the way this works means you need to keep adding these paths --> 3055 <!-- SADLY the way this works means you need to keep adding these paths -->
3054 <Match path="Avatar/XmlRpcGroups/Tests" pattern="*.cs" recurse="true"/> 3056 <Match path="Avatar/XmlRpcGroups/Tests" pattern="*.cs" recurse="true"/>
3057 <Match path="World/NPC/Tests" pattern="*.cs" recurse="true"/>
3055 </Files> 3058 </Files>
3056 </Project> 3059 </Project>
3057 3060