2009-06-09 17:48:22 +00:00
/ *
2009-08-28 17:12:30 +00:00
* 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 .
* /
2009-06-09 17:48:22 +00:00
using System ;
using System.IO ;
using System.Collections.Generic ;
2020-07-14 16:54:58 +00:00
using System.Collections.Concurrent ;
2009-06-09 17:48:22 +00:00
using System.Reflection ;
using System.Runtime.Serialization.Formatters.Binary ;
2020-03-07 20:34:56 +00:00
using System.Text ;
2009-06-09 17:48:22 +00:00
using System.Threading ;
2020-07-29 23:02:02 +00:00
using System.Threading.Tasks ;
2009-06-09 17:48:22 +00:00
using System.Timers ;
using log4net ;
using Nini.Config ;
using Mono.Addins ;
using OpenMetaverse ;
using OpenSim.Framework ;
2014-09-05 22:20:59 +00:00
using OpenSim.Framework.Monitoring ;
2009-06-09 17:48:22 +00:00
using OpenSim.Region.Framework.Interfaces ;
using OpenSim.Region.Framework.Scenes ;
2020-03-06 19:31:07 +00:00
using OpenSim.Server.Base ;
2009-06-09 17:48:22 +00:00
using OpenSim.Services.Interfaces ;
2012-09-09 18:30:59 +00:00
namespace OpenSim.Region.CoreModules.Asset
2009-06-09 17:48:22 +00:00
{
2012-11-13 02:08:02 +00:00
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "FlotsamAssetCache")]
2016-12-29 12:34:09 +00:00
public class FlotsamAssetCache : ISharedRegionModule , IAssetCache , IAssetService
2009-06-09 17:48:22 +00:00
{
2020-07-14 16:54:58 +00:00
private struct WriteAssetInfo
{
public string filename ;
public AssetBase asset ;
}
2020-03-07 20:34:56 +00:00
private static readonly ILog m_log = LogManager . GetLogger ( MethodBase . GetCurrentMethod ( ) . DeclaringType ) ;
2009-06-09 17:48:22 +00:00
2009-10-20 17:33:23 +00:00
private bool m_Enabled ;
2016-12-25 05:46:16 +00:00
private bool m_timerRunning ;
private bool m_cleanupRunning ;
2009-06-09 17:48:22 +00:00
private const string m_ModuleName = "FlotsamAssetCache" ;
2020-07-09 15:27:11 +00:00
private string m_CacheDirectory = "assetcache" ;
2020-03-06 19:20:12 +00:00
private string m_assetLoader ;
private string m_assetLoaderArgs ;
2009-06-09 17:48:22 +00:00
2020-03-07 20:34:56 +00:00
private readonly char [ ] m_InvalidChars ;
2009-06-09 17:48:22 +00:00
2010-02-19 21:18:03 +00:00
private int m_LogLevel = 0 ;
2011-06-10 23:04:21 +00:00
private ulong m_HitRateDisplay = 100 ; // How often to display hit statistics, given in requests
2009-06-09 17:48:22 +00:00
2020-03-07 20:34:56 +00:00
private ulong m_Requests ;
private ulong m_RequestsForInprogress ;
private ulong m_DiskHits ;
private ulong m_MemoryHits ;
private ulong m_weakRefHits ;
2009-06-09 17:48:22 +00:00
2020-05-17 23:20:28 +00:00
private static HashSet < string > m_CurrentlyWriting = new HashSet < string > ( ) ;
2020-07-14 16:54:58 +00:00
private static BlockingCollection < WriteAssetInfo > m_assetFileWriteQueue = null ;
private static CancellationTokenSource m_cancelSource ;
2020-07-30 20:14:15 +00:00
private static HashSet < string > m_defaultAssets = new HashSet < string > ( ) ;
2009-06-09 17:48:22 +00:00
2011-07-04 21:51:47 +00:00
private bool m_FileCacheEnabled = true ;
2009-10-20 17:33:23 +00:00
private ExpiringCache < string , AssetBase > m_MemoryCache ;
2011-06-10 23:04:21 +00:00
private bool m_MemoryCacheEnabled = false ;
2009-06-09 17:48:22 +00:00
2016-12-29 15:47:46 +00:00
private ExpiringCache < string , object > m_negativeCache ;
private bool m_negativeCacheEnabled = true ;
private bool m_negativeCacheSliding = false ;
2009-06-09 17:48:22 +00:00
// Expiration is expressed in hours.
2016-12-29 03:10:10 +00:00
private double m_MemoryExpiration = 0.016 ;
2009-06-09 17:48:22 +00:00
private const double m_DefaultFileExpiration = 48 ;
2016-12-29 15:47:46 +00:00
// Negative cache is in seconds
private int m_negativeExpiration = 120 ;
2011-02-25 01:31:38 +00:00
private TimeSpan m_FileExpiration = TimeSpan . FromHours ( m_DefaultFileExpiration ) ;
2016-11-30 07:43:17 +00:00
private TimeSpan m_FileExpirationCleanupTimer = TimeSpan . FromHours ( 1.0 ) ;
2009-06-09 17:48:22 +00:00
2009-07-28 17:20:51 +00:00
private static int m_CacheDirectoryTiers = 1 ;
2009-07-29 17:21:36 +00:00
private static int m_CacheDirectoryTierLen = 3 ;
2009-07-28 17:20:51 +00:00
private static int m_CacheWarnAt = 30000 ;
2009-10-20 17:33:23 +00:00
private System . Timers . Timer m_CacheCleanTimer ;
2009-06-09 17:48:22 +00:00
2009-10-20 17:33:23 +00:00
private IAssetService m_AssetService ;
2009-10-12 22:21:32 +00:00
private List < Scene > m_Scenes = new List < Scene > ( ) ;
2020-07-15 00:40:53 +00:00
private readonly object timerLock = new object ( ) ;
2017-01-05 19:07:37 +00:00
2016-12-29 03:10:10 +00:00
private Dictionary < string , WeakReference > weakAssetReferences = new Dictionary < string , WeakReference > ( ) ;
2020-07-15 00:40:53 +00:00
private readonly object weakAssetReferencesLock = new object ( ) ;
2016-12-29 11:53:38 +00:00
private bool m_updateFileTimeOnCacheHit = false ;
2016-12-29 03:10:10 +00:00
2009-06-09 17:48:22 +00:00
public FlotsamAssetCache ( )
{
2020-03-07 20:34:56 +00:00
List < char > invalidChars = new List < char > ( ) ;
invalidChars . AddRange ( Path . GetInvalidPathChars ( ) ) ;
invalidChars . AddRange ( Path . GetInvalidFileNameChars ( ) ) ;
m_InvalidChars = invalidChars . ToArray ( ) ;
2009-06-09 17:48:22 +00:00
}
2017-01-05 19:07:37 +00:00
public Type ReplaceableInterface
2009-07-10 20:17:13 +00:00
{
get { return null ; }
}
2009-06-09 17:48:22 +00:00
public string Name
{
get { return m_ModuleName ; }
}
public void Initialise ( IConfigSource source )
{
IConfig moduleConfig = source . Configs [ "Modules" ] ;
2017-01-05 19:07:37 +00:00
2009-06-09 17:48:22 +00:00
if ( moduleConfig ! = null )
{
2009-10-20 17:33:23 +00:00
string name = moduleConfig . GetString ( "AssetCaching" , String . Empty ) ;
2009-06-09 17:48:22 +00:00
if ( name = = Name )
{
2009-10-20 17:33:23 +00:00
m_MemoryCache = new ExpiringCache < string , AssetBase > ( ) ;
2016-12-29 15:47:46 +00:00
m_negativeCache = new ExpiringCache < string , object > ( ) ;
2009-06-09 20:20:30 +00:00
m_Enabled = true ;
2009-10-20 17:33:23 +00:00
2009-09-08 20:31:09 +00:00
m_log . InfoFormat ( "[FLOTSAM ASSET CACHE]: {0} enabled" , this . Name ) ;
2009-06-09 20:20:30 +00:00
2009-06-09 17:48:22 +00:00
IConfig assetConfig = source . Configs [ "AssetCache" ] ;
if ( assetConfig = = null )
{
2012-06-20 00:28:55 +00:00
m_log . Debug (
2011-06-10 23:04:21 +00:00
"[FLOTSAM ASSET CACHE]: AssetCache section missing from config (not copied config-include/FlotsamCache.ini.example? Using defaults." ) ;
2009-06-09 17:48:22 +00:00
}
2011-06-10 23:04:21 +00:00
else
{
2011-07-04 21:51:47 +00:00
m_FileCacheEnabled = assetConfig . GetBoolean ( "FileCacheEnabled" , m_FileCacheEnabled ) ;
2020-07-09 15:27:11 +00:00
m_CacheDirectory = assetConfig . GetString ( "CacheDirectory" , m_CacheDirectory ) ;
m_CacheDirectory = Path . GetFullPath ( m_CacheDirectory ) ;
2009-06-09 17:48:22 +00:00
2011-06-10 23:04:21 +00:00
m_MemoryCacheEnabled = assetConfig . GetBoolean ( "MemoryCacheEnabled" , m_MemoryCacheEnabled ) ;
2016-12-25 05:46:16 +00:00
m_MemoryExpiration = assetConfig . GetDouble ( "MemoryCacheTimeout" , m_MemoryExpiration ) ;
m_MemoryExpiration * = 3600.0 ; // config in hours to seconds
2016-12-29 11:53:38 +00:00
2016-12-29 15:47:46 +00:00
m_negativeCacheEnabled = assetConfig . GetBoolean ( "NegativeCacheEnabled" , m_negativeCacheEnabled ) ;
m_negativeExpiration = assetConfig . GetInt ( "NegativeCacheTimeout" , m_negativeExpiration ) ;
m_negativeCacheSliding = assetConfig . GetBoolean ( "NegativeCacheSliding" , m_negativeCacheSliding ) ;
2016-12-29 11:53:38 +00:00
m_updateFileTimeOnCacheHit = assetConfig . GetBoolean ( "UpdateFileTimeOnCacheHit" , m_updateFileTimeOnCacheHit ) ;
2020-03-07 20:34:56 +00:00
m_updateFileTimeOnCacheHit & = m_FileCacheEnabled ;
2017-01-05 19:07:37 +00:00
2011-06-10 23:04:21 +00:00
m_LogLevel = assetConfig . GetInt ( "LogLevel" , m_LogLevel ) ;
m_HitRateDisplay = ( ulong ) assetConfig . GetLong ( "HitRateDisplay" , ( long ) m_HitRateDisplay ) ;
2009-06-09 17:48:22 +00:00
2011-06-10 23:04:21 +00:00
m_FileExpiration = TimeSpan . FromHours ( assetConfig . GetDouble ( "FileCacheTimeout" , m_DefaultFileExpiration ) ) ;
2020-05-17 23:20:28 +00:00
m_FileExpirationCleanupTimer = TimeSpan . FromHours (
2011-06-10 23:04:21 +00:00
assetConfig . GetDouble ( "FileCleanupTimer" , m_FileExpirationCleanupTimer . TotalHours ) ) ;
2009-06-09 17:48:22 +00:00
2011-06-10 23:04:21 +00:00
m_CacheDirectoryTiers = assetConfig . GetInt ( "CacheDirectoryTiers" , m_CacheDirectoryTiers ) ;
m_CacheDirectoryTierLen = assetConfig . GetInt ( "CacheDirectoryTierLength" , m_CacheDirectoryTierLen ) ;
m_CacheWarnAt = assetConfig . GetInt ( "CacheWarnAt" , m_CacheWarnAt ) ;
}
m_log . InfoFormat ( "[FLOTSAM ASSET CACHE]: Cache Directory {0}" , m_CacheDirectory ) ;
2009-06-09 17:48:22 +00:00
2009-07-28 17:20:51 +00:00
if ( m_CacheDirectoryTiers < 1 )
m_CacheDirectoryTiers = 1 ;
else if ( m_CacheDirectoryTiers > 3 )
m_CacheDirectoryTiers = 3 ;
if ( m_CacheDirectoryTierLen < 1 )
m_CacheDirectoryTierLen = 1 ;
else if ( m_CacheDirectoryTierLen > 4 )
m_CacheDirectoryTierLen = 4 ;
2020-03-06 19:20:12 +00:00
assetConfig = source . Configs [ "AssetService" ] ;
if ( assetConfig ! = null )
{
m_assetLoader = assetConfig . GetString ( "DefaultAssetLoader" , String . Empty ) ;
m_assetLoaderArgs = assetConfig . GetString ( "AssetLoaderArgs" , string . Empty ) ;
if ( string . IsNullOrWhiteSpace ( m_assetLoaderArgs ) )
m_assetLoader = string . Empty ;
2009-07-28 17:20:51 +00:00
}
2012-03-08 01:51:37 +00:00
MainConsole . Instance . Commands . AddCommand ( "Assets" , true , "fcache status" , "fcache status" , "Display cache status" , HandleConsoleCommand ) ;
MainConsole . Instance . Commands . AddCommand ( "Assets" , true , "fcache clear" , "fcache clear [file] [memory]" , "Remove all assets in the cache. If file or memory is specified then only this cache is cleared." , HandleConsoleCommand ) ;
MainConsole . Instance . Commands . AddCommand ( "Assets" , true , "fcache assets" , "fcache assets" , "Attempt a deep scan and cache of all assets in all scenes" , HandleConsoleCommand ) ;
2020-07-30 20:14:15 +00:00
MainConsole . Instance . Commands . AddCommand ( "Assets" , true , "fcache expire" , "fcache expire <datetime(mm/dd/YYYY)>" , "Purge cached assets older then the specified date/time" , HandleConsoleCommand ) ;
2020-03-06 19:20:12 +00:00
if ( ! string . IsNullOrWhiteSpace ( m_assetLoader ) )
{
2020-07-30 20:14:15 +00:00
IAssetLoader assetLoader = ServerUtils . LoadPlugin < IAssetLoader > ( m_assetLoader , new object [ ] { } ) ;
if ( assetLoader ! = null )
{
HashSet < string > ids = new HashSet < string > ( ) ;
assetLoader . ForEachDefaultXmlAsset (
m_assetLoaderArgs ,
delegate ( AssetBase a )
{
ids . Add ( a . ID ) ;
} ) ;
m_defaultAssets = ids ;
}
2020-03-06 19:20:12 +00:00
MainConsole . Instance . Commands . AddCommand ( "Assets" , true , "fcache cachedefaultassets" , "fcache cachedefaultassets" , "loads local default assets to cache. This may override grid ones. use with care" , HandleConsoleCommand ) ;
MainConsole . Instance . Commands . AddCommand ( "Assets" , true , "fcache deletedefaultassets" , "fcache deletedefaultassets" , "deletes default local assets from cache so they can be refreshed from grid. use with care" , HandleConsoleCommand ) ;
}
2009-06-09 17:48:22 +00:00
}
}
}
public void PostInitialise ( )
{
}
public void Close ( )
{
2020-07-14 16:54:58 +00:00
if ( m_Scenes . Count < = 0 )
{
if ( m_assetFileWriteQueue ! = null )
{
m_assetFileWriteQueue . Dispose ( ) ;
m_assetFileWriteQueue = null ;
}
if ( m_cancelSource ! = null )
{
m_cancelSource . Dispose ( ) ;
m_cancelSource = null ;
}
}
2009-06-09 17:48:22 +00:00
}
public void AddRegion ( Scene scene )
{
if ( m_Enabled )
2009-09-08 20:31:09 +00:00
{
2016-12-29 12:34:09 +00:00
scene . RegisterModuleInterface < IAssetCache > ( this ) ;
2009-10-12 22:21:32 +00:00
m_Scenes . Add ( scene ) ;
2009-09-08 20:31:09 +00:00
}
2009-06-09 17:48:22 +00:00
}
public void RemoveRegion ( Scene scene )
{
2009-10-12 22:21:32 +00:00
if ( m_Enabled )
{
2016-12-29 12:34:09 +00:00
scene . UnregisterModuleInterface < IAssetCache > ( this ) ;
2009-10-12 22:21:32 +00:00
m_Scenes . Remove ( scene ) ;
2016-11-30 07:43:17 +00:00
lock ( timerLock )
{
2020-07-14 16:54:58 +00:00
if ( m_Scenes . Count < = 0 )
2016-11-30 07:43:17 +00:00
{
2020-07-29 23:02:02 +00:00
m_cleanupRunning = false ;
2020-07-14 16:54:58 +00:00
if ( m_timerRunning )
{
m_timerRunning = false ;
m_CacheCleanTimer . Stop ( ) ;
m_CacheCleanTimer . Close ( ) ;
}
if ( m_FileCacheEnabled & & m_assetFileWriteQueue ! = null )
m_cancelSource . Cancel ( ) ;
2016-11-30 07:43:17 +00:00
}
}
2009-10-12 22:21:32 +00:00
}
2009-06-09 17:48:22 +00:00
}
public void RegionLoaded ( Scene scene )
{
2016-11-30 07:43:17 +00:00
if ( m_Enabled )
{
if ( m_AssetService = = null )
m_AssetService = scene . RequestModuleInterface < IAssetService > ( ) ;
lock ( timerLock )
{
2016-12-25 05:46:16 +00:00
if ( ! m_timerRunning )
2016-11-30 07:43:17 +00:00
{
if ( m_FileCacheEnabled & & ( m_FileExpiration > TimeSpan . Zero ) & & ( m_FileExpirationCleanupTimer > TimeSpan . Zero ) )
{
2020-07-15 00:40:53 +00:00
m_CacheCleanTimer = new System . Timers . Timer ( m_FileExpirationCleanupTimer . TotalMilliseconds )
{
AutoReset = false
} ;
2017-01-05 19:07:37 +00:00
m_CacheCleanTimer . Elapsed + = CleanupExpiredFiles ;
2016-11-30 07:43:17 +00:00
m_CacheCleanTimer . Start ( ) ;
2016-12-25 05:46:16 +00:00
m_timerRunning = true ;
2016-11-30 07:43:17 +00:00
}
}
2017-01-05 19:07:37 +00:00
2020-07-14 16:54:58 +00:00
if ( m_FileCacheEnabled & & m_assetFileWriteQueue = = null )
2020-03-07 20:34:56 +00:00
{
2020-07-14 16:54:58 +00:00
m_assetFileWriteQueue = new BlockingCollection < WriteAssetInfo > ( ) ;
m_cancelSource = new CancellationTokenSource ( ) ;
2020-07-15 00:40:53 +00:00
WorkManager . RunInThreadPool ( ProcessWrites , null , "FloatsamCacheWriter" , false ) ;
2020-03-07 20:34:56 +00:00
}
}
2016-11-30 07:43:17 +00:00
}
2009-06-09 17:48:22 +00:00
}
2020-07-15 00:40:53 +00:00
private void ProcessWrites ( object o )
2020-07-14 16:54:58 +00:00
{
try
{
while ( true )
{
if ( m_assetFileWriteQueue . TryTake ( out WriteAssetInfo wai , - 1 , m_cancelSource . Token ) )
{
WriteFileCache ( wai . filename , wai . asset , false ) ;
2020-07-15 00:40:53 +00:00
wai . asset = null ;
2020-07-30 20:14:15 +00:00
Thread . Sleep ( 20 ) ;
2020-07-14 16:54:58 +00:00
}
}
}
catch { }
}
2009-06-09 17:48:22 +00:00
////////////////////////////////////////////////////////////
2016-12-29 12:34:09 +00:00
// IAssetCache
2009-06-09 17:48:22 +00:00
/ /
2016-12-29 03:10:10 +00:00
private void UpdateWeakReference ( string key , AssetBase asset )
{
lock ( weakAssetReferencesLock )
2020-03-07 20:34:56 +00:00
{
2020-07-15 00:40:53 +00:00
if ( weakAssetReferences . TryGetValue ( key , out WeakReference aref ) )
2020-03-07 20:34:56 +00:00
aref . Target = asset ;
else
weakAssetReferences [ key ] = new WeakReference ( asset ) ;
}
2016-12-29 03:10:10 +00:00
}
2009-06-09 17:48:22 +00:00
private void UpdateMemoryCache ( string key , AssetBase asset )
{
2016-12-25 05:46:16 +00:00
// NOTE DO NOT USE SLIDEEXPIRE option on current libomv
2011-07-04 21:30:18 +00:00
m_MemoryCache . AddOrUpdate ( key , asset , m_MemoryExpiration ) ;
2009-06-09 17:48:22 +00:00
}
2020-03-07 20:34:56 +00:00
private void UpdateFileCache ( string key , AssetBase asset , bool replace = false )
2009-06-09 17:48:22 +00:00
{
2020-07-14 16:54:58 +00:00
if ( m_assetFileWriteQueue = = null )
return ;
2014-01-18 00:06:12 +00:00
string filename = GetFileName ( key ) ;
2013-12-11 01:39:56 +00:00
2014-01-18 00:06:12 +00:00
try
{
// If the file is already cached, don't cache it, just touch it so access time is updated
2020-03-07 20:34:56 +00:00
if ( ! replace & & File . Exists ( filename ) )
2009-06-09 17:48:22 +00:00
{
2014-12-04 01:51:01 +00:00
UpdateFileLastAccessTime ( filename ) ;
2020-03-07 20:34:56 +00:00
return ;
2017-01-05 19:07:37 +00:00
}
2020-03-07 20:34:56 +00:00
// Once we start writing, make sure we flag that we're writing
// that object to the cache so that we don't try to write the
// same file multiple times.
lock ( m_CurrentlyWriting )
2014-01-18 00:06:12 +00:00
{
2020-03-07 20:34:56 +00:00
if ( m_CurrentlyWriting . Contains ( filename ) )
return ;
else
m_CurrentlyWriting . Add ( filename ) ;
2013-12-11 01:39:56 +00:00
}
2020-03-07 20:34:56 +00:00
2020-07-15 00:40:53 +00:00
WriteAssetInfo wai = new WriteAssetInfo ( )
{
filename = filename ,
asset = asset
} ;
2020-07-14 16:54:58 +00:00
if ( m_assetFileWriteQueue ! = null )
m_assetFileWriteQueue . Add ( wai ) ;
2014-01-18 00:06:12 +00:00
}
catch ( Exception e )
{
m_log . ErrorFormat (
"[FLOTSAM ASSET CACHE]: Failed to update cache for asset {0}. Exception {1} {2}" ,
asset . ID , e . Message , e . StackTrace ) ;
2009-06-09 17:48:22 +00:00
}
}
2020-03-07 20:34:56 +00:00
public void Cache ( AssetBase asset , bool replace = false )
2009-06-09 17:48:22 +00:00
{
2011-07-04 21:30:18 +00:00
// 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);
2016-12-29 03:10:10 +00:00
UpdateWeakReference ( asset . ID , asset ) ;
2009-06-09 17:48:22 +00:00
2011-07-04 21:30:18 +00:00
if ( m_MemoryCacheEnabled )
UpdateMemoryCache ( asset . ID , asset ) ;
2011-07-04 21:51:47 +00:00
if ( m_FileCacheEnabled )
2020-03-07 20:34:56 +00:00
UpdateFileCache ( asset . ID , asset , replace ) ;
2011-07-04 21:30:18 +00:00
}
}
2016-12-29 15:47:46 +00:00
public void CacheNegative ( string id )
{
if ( m_negativeCacheEnabled )
{
if ( m_negativeCacheSliding )
m_negativeCache . AddOrUpdate ( id , null , TimeSpan . FromSeconds ( m_negativeExpiration ) ) ;
else
m_negativeCache . AddOrUpdate ( id , null , m_negativeExpiration ) ;
}
}
2014-12-04 01:51:01 +00:00
/// <summary>
/// Updates the cached file with the current time.
/// </summary>
/// <param name="filename">Filename.</param>
/// <returns><c>true</c>, if the update was successful, false otherwise.</returns>
private bool UpdateFileLastAccessTime ( string filename )
{
try
{
File . SetLastAccessTime ( filename , DateTime . Now ) ;
return true ;
}
2020-07-07 16:35:27 +00:00
catch ( FileNotFoundException )
2014-12-04 01:51:01 +00:00
{
return false ;
}
2020-07-07 16:35:27 +00:00
catch
{
return true ; // ignore other errors
}
2014-12-04 01:51:01 +00:00
}
2016-12-29 03:10:10 +00:00
private AssetBase GetFromWeakReference ( string id )
{
AssetBase asset = null ;
lock ( weakAssetReferencesLock )
{
2020-03-07 20:34:56 +00:00
if ( weakAssetReferences . TryGetValue ( id , out WeakReference aref ) )
2016-12-29 03:10:10 +00:00
{
asset = aref . Target as AssetBase ;
if ( asset = = null )
weakAssetReferences . Remove ( id ) ;
else
m_weakRefHits + + ;
}
}
return asset ;
}
2011-07-04 21:30:18 +00:00
/// <summary>
/// Try to get an asset from the in-memory cache.
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private AssetBase GetFromMemoryCache ( string id )
{
2020-07-15 00:40:53 +00:00
if ( m_MemoryCache . TryGetValue ( id , out AssetBase asset ) )
{
2009-06-09 17:48:22 +00:00
m_MemoryHits + + ;
2020-07-15 00:40:53 +00:00
return asset ;
}
return null ;
2011-07-04 21:30:18 +00:00
}
2013-12-11 01:39:56 +00:00
private bool CheckFromMemoryCache ( string id )
{
2014-01-18 00:21:53 +00:00
return m_MemoryCache . Contains ( id ) ;
2013-12-11 01:39:56 +00:00
}
2011-07-04 21:30:18 +00:00
/// <summary>
/// Try to get an asset from the file cache.
/// </summary>
/// <param name="id"></param>
2013-02-08 21:21:20 +00:00
/// <returns>An asset retrieved from the file cache. null if there was a problem retrieving an asset.</returns>
2011-07-04 21:30:18 +00:00
private AssetBase GetFromFileCache ( string id )
2017-01-05 19:07:37 +00:00
{
2011-07-04 21:30:18 +00:00
string filename = GetFileName ( id ) ;
2013-02-08 21:21:20 +00:00
2013-03-30 01:21:16 +00:00
// Track how often we have the problem that an asset is requested while
// it is still being downloaded by a previous request.
if ( m_CurrentlyWriting . Contains ( filename ) )
{
m_RequestsForInprogress + + ;
return null ;
}
AssetBase asset = null ;
2020-07-30 20:14:15 +00:00
try
2009-06-09 17:48:22 +00:00
{
2020-07-30 20:14:15 +00:00
using ( FileStream stream = File . Open ( filename , FileMode . Open , FileAccess . Read , FileShare . Read ) )
2009-06-09 17:48:22 +00:00
{
2020-07-30 20:14:15 +00:00
if ( stream . Length = = 0 ) // Empty file will trigger exception below
return null ;
BinaryFormatter bformatter = new BinaryFormatter ( ) ;
2009-06-09 17:48:22 +00:00
2020-07-30 20:14:15 +00:00
asset = ( AssetBase ) bformatter . Deserialize ( stream ) ;
2009-06-09 17:48:22 +00:00
2020-07-30 20:14:15 +00:00
m_DiskHits + + ;
2011-07-04 21:30:18 +00:00
}
}
2020-07-30 20:14:15 +00:00
catch ( FileNotFoundException )
{
}
catch ( DirectoryNotFoundException )
{
}
catch ( System . Runtime . Serialization . SerializationException e )
{
m_log . WarnFormat (
"[FLOTSAM ASSET CACHE]: Failed to get file {0} for asset {1}. Exception {2} {3}" ,
filename , id , e . Message , e . StackTrace ) ;
// If there was a problem deserializing the asset, the asset may
// either be corrupted OR was serialized under an old format
// {different version of AssetBase} -- we should attempt to
// delete it and re-cache
File . Delete ( filename ) ;
}
catch ( Exception e )
{
m_log . WarnFormat (
"[FLOTSAM ASSET CACHE]: Failed to get file {0} for asset {1}. Exception {2} {3}" ,
filename , id , e . Message , e . StackTrace ) ;
}
2009-06-09 17:48:22 +00:00
2020-07-30 20:14:15 +00:00
return asset ;
2011-07-04 21:30:18 +00:00
}
2013-12-11 01:39:56 +00:00
private bool CheckFromFileCache ( string id )
{
string filename = GetFileName ( id ) ;
2020-07-30 20:14:15 +00:00
try
2013-12-11 01:39:56 +00:00
{
2020-07-30 20:14:15 +00:00
var fileinfo = new FileInfo ( id ) ;
if ( fileinfo . Exists )
2013-12-11 01:39:56 +00:00
{
2020-07-30 20:14:15 +00:00
if ( fileinfo . Length = = 0 )
2013-12-11 01:39:56 +00:00
{
2020-07-30 20:14:15 +00:00
fileinfo . Delete ( ) ;
return false ;
2013-12-11 01:39:56 +00:00
}
2020-07-30 20:14:15 +00:00
return true ;
2013-12-11 01:39:56 +00:00
}
}
2020-07-30 20:14:15 +00:00
catch
{
}
return false ;
2013-12-11 01:39:56 +00:00
}
2017-01-30 13:59:05 +00:00
// For IAssetService
2011-07-04 21:30:18 +00:00
public AssetBase Get ( string id )
{
2020-07-15 00:40:53 +00:00
Get ( id , out AssetBase asset ) ;
2017-02-01 16:31:01 +00:00
return asset ;
2017-01-30 13:59:05 +00:00
}
2017-02-01 16:31:01 +00:00
public bool Get ( string id , out AssetBase asset )
2017-01-30 13:59:05 +00:00
{
2017-02-01 16:31:01 +00:00
asset = null ;
2017-01-30 13:59:05 +00:00
2011-07-04 21:30:18 +00:00
m_Requests + + ;
2020-07-15 00:40:53 +00:00
if ( m_negativeCache . TryGetValue ( id , out object dummy ) )
2017-02-01 16:31:01 +00:00
return false ;
2016-12-29 15:47:46 +00:00
2016-12-29 03:10:10 +00:00
asset = GetFromWeakReference ( id ) ;
2020-07-30 20:14:15 +00:00
if ( asset ! = null )
2016-12-29 11:53:38 +00:00
{
2020-07-30 20:14:15 +00:00
if ( m_updateFileTimeOnCacheHit )
{
string filename = GetFileName ( id ) ;
UpdateFileLastAccessTime ( filename ) ;
}
if ( m_MemoryCacheEnabled )
UpdateMemoryCache ( id , asset ) ;
return true ;
2016-12-29 11:53:38 +00:00
}
2011-07-04 21:30:18 +00:00
2020-07-30 20:14:15 +00:00
if ( m_MemoryCacheEnabled )
2016-12-29 03:10:10 +00:00
{
2011-07-04 21:30:18 +00:00
asset = GetFromMemoryCache ( id ) ;
2016-12-29 03:10:10 +00:00
if ( asset ! = null )
2016-12-29 11:53:38 +00:00
{
2016-12-29 03:10:10 +00:00
UpdateWeakReference ( id , asset ) ;
2016-12-29 11:53:38 +00:00
if ( m_updateFileTimeOnCacheHit )
{
string filename = GetFileName ( id ) ;
UpdateFileLastAccessTime ( filename ) ;
}
2020-07-30 20:14:15 +00:00
return true ;
2016-12-29 11:53:38 +00:00
}
2016-12-29 03:10:10 +00:00
}
2011-08-13 14:16:43 +00:00
2020-07-30 20:14:15 +00:00
if ( m_FileCacheEnabled )
2016-12-29 03:10:10 +00:00
{
2011-07-04 21:30:18 +00:00
asset = GetFromFileCache ( id ) ;
2016-12-29 03:10:10 +00:00
if ( asset ! = null )
2020-07-30 20:14:15 +00:00
{
2016-12-29 03:10:10 +00:00
UpdateWeakReference ( id , asset ) ;
2020-07-30 20:14:15 +00:00
if ( m_MemoryCacheEnabled )
UpdateMemoryCache ( id , asset ) ;
}
2016-12-29 03:10:10 +00:00
}
2017-02-01 16:31:01 +00:00
return true ;
2009-06-09 17:48:22 +00:00
}
2013-12-11 01:39:56 +00:00
public bool Check ( string id )
{
2020-07-30 20:14:15 +00:00
if ( GetFromWeakReference ( id ) ! = null )
return true ;
2013-12-11 01:39:56 +00:00
if ( m_MemoryCacheEnabled & & CheckFromMemoryCache ( id ) )
return true ;
if ( m_FileCacheEnabled & & CheckFromFileCache ( id ) )
return true ;
return false ;
}
2010-04-08 19:31:44 +00:00
public AssetBase GetCached ( string id )
{
2020-07-15 00:40:53 +00:00
Get ( id , out AssetBase asset ) ;
2017-02-01 16:31:01 +00:00
return asset ;
2010-04-08 19:31:44 +00:00
}
2009-06-09 17:48:22 +00:00
public void Expire ( string id )
{
if ( m_LogLevel > = 2 )
2011-07-30 00:35:22 +00:00
m_log . DebugFormat ( "[FLOTSAM ASSET CACHE]: Expiring Asset {0}" , id ) ;
2009-06-09 17:48:22 +00:00
try
{
2020-03-07 20:34:56 +00:00
lock ( weakAssetReferencesLock )
weakAssetReferences . Remove ( id ) ;
2020-07-09 15:27:11 +00:00
if ( m_MemoryCacheEnabled )
m_MemoryCache . Remove ( id ) ;
2011-07-04 21:51:47 +00:00
if ( m_FileCacheEnabled )
2009-06-09 17:48:22 +00:00
{
2011-07-04 21:51:47 +00:00
string filename = GetFileName ( id ) ;
2020-03-07 20:34:56 +00:00
File . Delete ( filename ) ;
2009-06-09 17:48:22 +00:00
}
}
catch ( Exception e )
{
2020-07-09 15:27:11 +00:00
if ( m_LogLevel > = 2 )
m_log . WarnFormat ( "[FLOTSAM ASSET CACHE]: Failed to expire cached file {0}. Exception {1} {2}" ,
id , e . Message , e . StackTrace ) ;
2009-06-09 17:48:22 +00:00
}
}
public void Clear ( )
{
if ( m_LogLevel > = 2 )
2011-07-04 21:51:47 +00:00
m_log . Debug ( "[FLOTSAM ASSET CACHE]: Clearing caches." ) ;
2009-06-09 17:48:22 +00:00
2017-05-26 04:32:59 +00:00
if ( m_FileCacheEnabled & & Directory . Exists ( m_CacheDirectory ) )
2009-06-09 17:48:22 +00:00
{
2011-07-04 21:51:47 +00:00
foreach ( string dir in Directory . GetDirectories ( m_CacheDirectory ) )
{
2020-07-30 20:14:15 +00:00
try
{
Directory . Delete ( dir , true ) ;
}
catch { }
2011-07-04 21:51:47 +00:00
}
2009-06-09 17:48:22 +00:00
}
2009-06-10 04:28:56 +00:00
if ( m_MemoryCacheEnabled )
2016-12-29 03:10:10 +00:00
m_MemoryCache = new ExpiringCache < string , AssetBase > ( ) ;
2016-12-29 15:47:46 +00:00
if ( m_negativeCacheEnabled )
m_negativeCache = new ExpiringCache < string , object > ( ) ;
2017-01-05 19:07:37 +00:00
2016-12-29 03:10:10 +00:00
lock ( weakAssetReferencesLock )
weakAssetReferences = new Dictionary < string , WeakReference > ( ) ;
2009-06-09 17:48:22 +00:00
}
2020-07-29 23:02:02 +00:00
private async void CleanupExpiredFiles ( object source , ElapsedEventArgs e )
2009-06-09 17:48:22 +00:00
{
2020-07-28 20:01:45 +00:00
lock ( timerLock )
{
2020-07-29 23:02:02 +00:00
if ( ! m_timerRunning | | m_cleanupRunning | | ! Directory . Exists ( m_CacheDirectory ) )
2020-07-28 20:01:45 +00:00
return ;
m_cleanupRunning = true ;
}
2020-07-29 23:02:02 +00:00
// Purge all files last accessed prior to this point
await DoCleanExpiredFiles ( DateTime . Now - m_FileExpiration ) . ConfigureAwait ( false ) ;
}
private async Task DoCleanExpiredFiles ( DateTime purgeLine )
{
2020-06-03 13:34:11 +00:00
long heap = 0 ;
2020-07-28 03:26:13 +00:00
//if (m_LogLevel >= 2)
2020-06-03 13:34:11 +00:00
{
2020-07-30 20:14:15 +00:00
m_log . InfoFormat ( "[FLOTSAM ASSET CACHE]: Start background expiring files older then {0}." , purgeLine ) ;
2020-06-03 13:34:11 +00:00
heap = GC . GetTotalMemory ( false ) ;
}
2009-06-09 17:48:22 +00:00
2012-09-15 00:08:15 +00:00
// An asset cache may contain local non-temporary assets that are not in the asset service. Therefore,
// before cleaning up expired files we must scan the objects in the scene to make sure that we retain
// such local assets if they have not been recently accessed.
2020-07-29 23:02:02 +00:00
Dictionary < UUID , sbyte > gids = await gatherSceneAssets ( ) . ConfigureAwait ( false ) ;
2020-07-28 01:57:41 +00:00
int cooldown = 0 ;
2020-07-30 20:14:15 +00:00
m_log . Info ( "[FLOTSAM ASSET CACHE] start asset files expire" ) ;
2020-07-29 23:02:02 +00:00
foreach ( string subdir in Directory . GetDirectories ( m_CacheDirectory ) )
2020-07-30 20:14:15 +00:00
{
if ( ! m_cleanupRunning )
break ;
2020-07-30 00:39:04 +00:00
cooldown = await CleanExpiredFiles ( subdir , gids , purgeLine , cooldown ) ;
2020-07-30 20:14:15 +00:00
if ( + + cooldown > = 10 )
{
await Task . Delay ( 100 ) . ConfigureAwait ( false ) ;
cooldown = 0 ;
}
}
gids = null ;
2016-11-30 07:43:17 +00:00
2020-07-29 23:02:02 +00:00
lock ( timerLock )
2016-11-30 07:43:17 +00:00
{
2020-07-29 23:02:02 +00:00
if ( m_timerRunning )
2016-11-30 07:43:17 +00:00
m_CacheCleanTimer . Start ( ) ;
2016-12-25 05:46:16 +00:00
m_cleanupRunning = false ;
2016-11-30 07:43:17 +00:00
}
2020-07-28 03:26:13 +00:00
//if (m_LogLevel >= 2)
2020-06-03 13:34:11 +00:00
{
heap = GC . GetTotalMemory ( false ) - heap ;
2020-07-29 23:02:02 +00:00
double fheap = Math . Round ( ( double ) ( heap / ( 1024 * 1024 ) ) , 3 ) ;
2020-07-30 20:14:15 +00:00
m_log . InfoFormat ( "[FLOTSAM ASSET CACHE]: Finished expiring files, heap delta: {0}MB." , fheap ) ;
2020-06-03 13:34:11 +00:00
}
2009-09-08 20:31:09 +00:00
}
2009-07-28 17:20:51 +00:00
2009-09-08 20:31:09 +00:00
/// <summary>
2017-01-05 19:07:37 +00:00
/// Recurses through specified directory checking for asset files last
/// accessed prior to the specified purge line and deletes them. Also
2009-10-12 22:21:32 +00:00
/// removes empty tier directories.
2009-09-08 20:31:09 +00:00
/// </summary>
/// <param name="dir"></param>
2011-07-04 21:30:18 +00:00
/// <param name="purgeLine"></param>
2020-07-30 00:39:04 +00:00
private async Task < int > CleanExpiredFiles ( string dir , Dictionary < UUID , sbyte > gids , DateTime purgeLine , int cooldown )
2020-07-29 23:02:02 +00:00
{
try
{
2020-07-30 20:14:15 +00:00
if ( ! m_cleanupRunning )
2020-07-30 00:39:04 +00:00
return cooldown ;
2020-07-29 23:02:02 +00:00
int dirSize = 0 ;
2020-07-30 20:14:15 +00:00
// Recurse into lower tiers
foreach ( string subdir in Directory . GetDirectories ( dir ) )
2020-07-29 23:02:02 +00:00
{
2020-07-30 20:14:15 +00:00
if ( ! m_cleanupRunning )
return cooldown ;
2020-07-29 23:02:02 +00:00
2020-07-30 20:14:15 +00:00
+ + dirSize ;
cooldown = await CleanExpiredFiles ( subdir , gids , purgeLine , cooldown ) ;
if ( + + cooldown > 10 )
{
await Task . Delay ( 100 ) . ConfigureAwait ( false ) ;
cooldown = 0 ;
}
}
foreach ( string file in Directory . GetFiles ( dir ) )
{
2020-07-29 23:02:02 +00:00
if ( ! m_cleanupRunning )
2020-07-30 00:39:04 +00:00
return cooldown ;
2020-07-29 23:02:02 +00:00
+ + dirSize ;
string id = Path . GetFileName ( file ) ;
if ( String . IsNullOrEmpty ( id ) )
continue ; //??
2020-07-30 20:14:15 +00:00
if ( m_defaultAssets . Contains ( id ) | | ( UUID . TryParse ( id , out UUID uid ) & & gids . ContainsKey ( uid ) ) )
{
+ + cooldown ;
2020-07-29 23:02:02 +00:00
continue ;
2020-07-30 20:14:15 +00:00
}
2020-07-29 23:02:02 +00:00
if ( File . GetLastAccessTime ( file ) < purgeLine )
{
2020-07-30 20:14:15 +00:00
try
{
File . Delete ( file ) ;
lock ( weakAssetReferencesLock )
weakAssetReferences . Remove ( id ) ;
}
catch { }
2020-07-30 00:40:54 +00:00
cooldown + = 5 ;
2020-07-30 20:14:15 +00:00
- - dirSize ;
2020-07-29 23:02:02 +00:00
}
2020-07-30 20:14:15 +00:00
if ( + + cooldown > = 20 )
2020-07-29 23:02:02 +00:00
{
2020-07-30 20:14:15 +00:00
await Task . Delay ( 100 ) . ConfigureAwait ( false ) ;
2020-07-29 23:02:02 +00:00
cooldown = 0 ;
}
}
// Check if a tier directory is empty, if so, delete it
if ( m_cleanupRunning & & dirSize = = 0 )
{
2020-07-30 20:14:15 +00:00
try
{
Directory . Delete ( dir ) ;
}
catch { }
cooldown + = 5 ;
if ( cooldown > = 20 )
{
await Task . Delay ( 100 ) . ConfigureAwait ( false ) ;
cooldown = 0 ;
}
2020-07-29 23:02:02 +00:00
}
else if ( dirSize > = m_CacheWarnAt )
{
m_log . WarnFormat (
"[FLOTSAM ASSET CACHE]: Cache folder exceeded CacheWarnAt limit {0} {1}. Suggest increasing tiers, tier length, or reducing cache expiration" ,
dir , dirSize ) ;
}
}
catch ( DirectoryNotFoundException )
{
// If we get here, another node on the same box has
// already removed the directory. Continue with next.
}
catch ( Exception e )
{
m_log . WarnFormat ( "[FLOTSAM ASSET CACHE]: Could not complete clean of expired files in {0}, exception {1}" , dir , e . Message ) ;
}
2020-07-30 00:39:04 +00:00
return cooldown ;
2020-07-29 23:02:02 +00:00
}
2009-10-12 22:21:32 +00:00
/// <summary>
/// Determines the filename for an AssetID stored in the file cache
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
2009-06-09 17:48:22 +00:00
private string GetFileName ( string id )
{
2020-07-09 15:27:11 +00:00
StringBuilder sb = osStringBuilderCache . Acquire ( ) ;
2020-03-07 20:34:56 +00:00
int indx = id . IndexOfAny ( m_InvalidChars ) ;
if ( indx > = 0 )
2009-06-09 17:48:22 +00:00
{
2020-07-11 19:03:31 +00:00
sb . Append ( id ) ;
2020-03-07 20:34:56 +00:00
int sublen = id . Length - indx ;
for ( int i = 0 ; i < m_InvalidChars . Length ; + + i )
{
sb . Replace ( m_InvalidChars [ i ] , '_' , indx , sublen ) ;
}
id = sb . ToString ( ) ;
2020-07-09 15:27:11 +00:00
sb . Clear ( ) ;
2009-06-09 17:48:22 +00:00
}
2020-07-09 15:27:11 +00:00
if ( m_CacheDirectoryTiers = = 1 )
{
sb . Append ( id . Substring ( 0 , m_CacheDirectoryTierLen ) ) ;
sb . Append ( Path . DirectorySeparatorChar ) ;
}
else
2009-07-28 17:20:51 +00:00
{
2020-07-09 15:27:11 +00:00
for ( int p = 0 ; p < m_CacheDirectoryTiers * m_CacheDirectoryTierLen ; p + = m_CacheDirectoryTierLen )
{
sb . Append ( id . Substring ( p , m_CacheDirectoryTierLen ) ) ;
sb . Append ( Path . DirectorySeparatorChar ) ;
}
2009-07-28 17:20:51 +00:00
}
2020-07-09 15:27:11 +00:00
sb . Append ( id ) ;
2009-07-28 17:20:51 +00:00
2020-07-09 15:27:11 +00:00
return Path . Combine ( m_CacheDirectory , osStringBuilderCache . GetStringAndRelease ( sb ) ) ;
2009-06-09 17:48:22 +00:00
}
2009-10-12 22:21:32 +00:00
/// <summary>
2017-01-05 19:07:37 +00:00
/// Writes a file to the file cache, creating any nessesary
2009-10-12 22:21:32 +00:00
/// tier directories along the way
/// </summary>
/// <param name="filename"></param>
/// <param name="asset"></param>
2020-05-17 23:20:28 +00:00
private static void WriteFileCache ( string filename , AssetBase asset , bool replace )
2009-06-09 17:48:22 +00:00
{
2020-03-07 20:34:56 +00:00
// Make sure the target cache directory exists
2009-10-04 16:56:54 +00:00
string directory = Path . GetDirectoryName ( filename ) ;
2009-10-12 22:21:32 +00:00
2017-01-05 19:07:37 +00:00
// Write file first to a temp name, so that it doesn't look
2009-10-04 16:56:54 +00:00
// like it's already cached while it's still writing.
string tempname = Path . Combine ( directory , Path . GetRandomFileName ( ) ) ;
2009-10-12 22:21:32 +00:00
2009-06-09 17:48:22 +00:00
try
{
2011-08-16 20:03:43 +00:00
try
2009-06-09 17:48:22 +00:00
{
2011-08-16 20:03:43 +00:00
if ( ! Directory . Exists ( directory ) )
{
Directory . CreateDirectory ( directory ) ;
}
2017-01-05 19:07:37 +00:00
2020-03-07 20:34:56 +00:00
using ( Stream stream = File . Open ( tempname , FileMode . Create ) )
{
BinaryFormatter bformatter = new BinaryFormatter ( ) ;
bformatter . Serialize ( stream , asset ) ;
stream . Flush ( ) ;
}
2009-06-09 17:48:22 +00:00
}
2011-08-16 20:03:43 +00:00
catch ( IOException e )
{
2013-02-08 21:21:20 +00:00
m_log . WarnFormat (
2011-08-16 20:03:43 +00:00
"[FLOTSAM ASSET CACHE]: Failed to write asset {0} to temporary location {1} (final {2}) on cache in {3}. Exception {4} {5}." ,
asset . ID , tempname , filename , directory , e . Message , e . StackTrace ) ;
2009-06-09 17:48:22 +00:00
2011-08-16 20:03:43 +00:00
return ;
}
2018-02-02 11:47:19 +00:00
catch ( UnauthorizedAccessException )
2017-02-09 23:13:37 +00:00
{
}
2011-08-16 20:03:43 +00:00
try
{
2020-03-07 20:34:56 +00:00
if ( replace )
File . Delete ( filename ) ;
2011-08-16 20:03:43 +00:00
File . Move ( tempname , filename ) ;
}
catch ( IOException )
{
// If we see an IOException here it's likely that some other competing thread has written the
// cache file first, so ignore. Other IOException errors (e.g. filesystem full) should be
// signally by the earlier temporary file writing code.
}
2009-06-09 17:48:22 +00:00
}
finally
{
// Even if the write fails with an exception, we need to make sure
// that we release the lock on that file, otherwise it'll never get
// cached
lock ( m_CurrentlyWriting )
{
2011-08-05 21:45:42 +00:00
m_CurrentlyWriting . Remove ( filename ) ;
2009-06-09 17:48:22 +00:00
}
}
}
2009-10-12 22:21:32 +00:00
/// <summary>
/// Scan through the file cache, and return number of assets currently cached.
/// </summary>
/// <param name="dir"></param>
/// <returns></returns>
2009-09-08 20:31:09 +00:00
private int GetFileCacheCount ( string dir )
{
2020-07-30 20:14:15 +00:00
try
2009-09-08 20:31:09 +00:00
{
2020-07-30 20:14:15 +00:00
int count = 0 ;
int cooldown = 0 ;
foreach ( string subdir in Directory . GetDirectories ( dir ) )
{
count + = GetFileCacheCount ( subdir ) ;
+ + cooldown ;
if ( cooldown > 50 )
{
Thread . Sleep ( 100 ) ;
cooldown = 0 ;
}
}
return count + Directory . GetFiles ( dir ) . Length ;
}
catch
{
return 0 ;
2009-06-09 17:48:22 +00:00
}
}
2009-09-08 20:31:09 +00:00
2009-10-12 22:21:32 +00:00
/// <summary>
/// This notes the last time the Region had a deep asset scan performed on it.
/// </summary>
2013-02-08 21:21:20 +00:00
/// <param name="regionID"></param>
private void StampRegionStatusFile ( UUID regionID )
2009-10-12 22:21:32 +00:00
{
2013-02-08 21:21:20 +00:00
string RegionCacheStatusFile = Path . Combine ( m_CacheDirectory , "RegionStatus_" + regionID . ToString ( ) + ".fac" ) ;
2017-01-05 19:07:37 +00:00
try
2009-10-12 22:21:32 +00:00
{
2013-02-08 21:21:20 +00:00
if ( File . Exists ( RegionCacheStatusFile ) )
{
File . SetLastWriteTime ( RegionCacheStatusFile , DateTime . Now ) ;
}
else
{
File . WriteAllText (
2017-01-05 19:07:37 +00:00
RegionCacheStatusFile ,
2013-02-08 21:21:20 +00:00
"Please do not delete this file unless you are manually clearing your Flotsam Asset Cache." ) ;
}
2009-10-12 22:21:32 +00:00
}
2013-02-08 21:21:20 +00:00
catch ( Exception e )
2009-10-12 22:21:32 +00:00
{
2020-07-09 15:27:11 +00:00
m_log . WarnFormat ( "[FLOTSAM ASSET CACHE]: Could not stamp region status file for region {0}. Exception {1}" ,
regionID , e . Message ) ;
2009-10-12 22:21:32 +00:00
}
}
/// <summary>
2017-01-05 19:07:37 +00:00
/// Iterates through all Scenes, doing a deep scan through assets
2012-09-15 00:08:15 +00:00
/// to update the access time of all assets present in the scene or referenced by assets
/// in the scene.
2009-10-12 22:21:32 +00:00
/// </summary>
2020-05-17 23:20:28 +00:00
/// <param name="tryGetUncached">
2012-09-15 00:08:15 +00:00
/// If true, then assets scanned which are not found in cache are added to the cache.
/// </param>
/// <returns>Number of distinct asset references found in the scene.</returns>
2020-07-29 23:02:02 +00:00
private async Task < int > TouchAllSceneAssets ( bool tryGetUncached )
2009-10-12 22:21:32 +00:00
{
2020-07-28 20:01:45 +00:00
m_log . Info ( "[FLOTSAM ASSET CACHE] start touch files of assets in use" ) ;
2020-07-29 23:02:02 +00:00
Dictionary < UUID , sbyte > gatheredids = await gatherSceneAssets ( ) ;
int cooldown = 0 ;
foreach ( UUID id in gatheredids . Keys )
{
if ( ! m_cleanupRunning )
break ;
string idstr = id . ToString ( ) ;
if ( ! UpdateFileLastAccessTime ( GetFileName ( idstr ) ) & & tryGetUncached )
{
cooldown + = 5 ;
m_AssetService . Get ( idstr ) ;
}
if ( + + cooldown > 50 )
{
Thread . Sleep ( 50 ) ;
cooldown = 0 ;
}
}
return gatheredids . Count ;
}
private async Task < Dictionary < UUID , sbyte > > gatherSceneAssets ( )
{
m_log . Info ( "[FLOTSAM ASSET CACHE] gather assets in use" ) ;
Dictionary < UUID , sbyte > gatheredids = new Dictionary < UUID , sbyte > ( ) ;
UuidGatherer gatherer = new UuidGatherer ( m_AssetService , gatheredids ) ;
2009-10-12 22:21:32 +00:00
2020-05-17 23:20:28 +00:00
int cooldown = 0 ;
2009-10-12 22:21:32 +00:00
foreach ( Scene s in m_Scenes )
{
2020-05-17 23:20:28 +00:00
gatherer . AddGathered ( s . RegionInfo . RegionSettings . TerrainTexture1 , ( sbyte ) AssetType . Texture ) ;
gatherer . AddGathered ( s . RegionInfo . RegionSettings . TerrainTexture2 , ( sbyte ) AssetType . Texture ) ;
gatherer . AddGathered ( s . RegionInfo . RegionSettings . TerrainTexture3 , ( sbyte ) AssetType . Texture ) ;
gatherer . AddGathered ( s . RegionInfo . RegionSettings . TerrainTexture4 , ( sbyte ) AssetType . Texture ) ;
gatherer . AddGathered ( s . RegionInfo . RegionSettings . TerrainImageID , ( sbyte ) AssetType . Texture ) ;
2009-10-12 22:21:32 +00:00
2020-07-29 23:02:02 +00:00
if ( s . RegionEnvironment ! = null )
s . RegionEnvironment . GatherAssets ( gatheredids ) ;
if ( s . LandChannel ! = null )
{
List < ILandObject > landObjects = s . LandChannel . AllParcels ( ) ;
foreach ( ILandObject lo in landObjects )
{
if ( lo . LandData ! = null & & lo . LandData . Environment ! = null )
lo . LandData . Environment . GatherAssets ( gatheredids ) ;
}
}
2020-07-13 22:26:09 +00:00
EntityBase [ ] entities = s . Entities . GetEntities ( ) ;
for ( int i = 0 ; i < entities . Length ; + + i )
2017-01-05 19:07:37 +00:00
{
2020-07-29 23:02:02 +00:00
if ( ! m_cleanupRunning )
2020-07-13 22:26:09 +00:00
break ;
2009-10-12 22:21:32 +00:00
2020-07-13 22:26:09 +00:00
EntityBase entity = entities [ i ] ;
2020-07-29 23:02:02 +00:00
if ( entity is SceneObjectGroup )
2020-07-07 16:35:27 +00:00
{
2020-07-13 22:26:09 +00:00
SceneObjectGroup e = entity as SceneObjectGroup ;
2020-07-29 23:02:02 +00:00
if ( e . IsDeleted )
2020-07-13 22:26:09 +00:00
continue ;
gatherer . AddForInspection ( e ) ;
2020-07-29 23:02:02 +00:00
while ( gatherer . GatherNext ( ) )
2020-07-13 22:26:09 +00:00
{
2020-07-28 20:01:45 +00:00
if ( + + cooldown > 50 )
2020-07-28 03:26:13 +00:00
{
2020-07-29 23:02:02 +00:00
await Task . Delay ( 60 ) . ConfigureAwait ( false ) ;
2020-07-28 03:26:13 +00:00
cooldown = 0 ;
}
2020-07-13 22:26:09 +00:00
}
2020-07-29 23:02:02 +00:00
if ( + + cooldown > 25 )
{
await Task . Delay ( 60 ) . ConfigureAwait ( false ) ;
cooldown = 0 ;
}
2020-07-07 16:35:27 +00:00
}
2020-07-13 22:26:09 +00:00
}
entities = null ;
2020-07-29 23:02:02 +00:00
if ( ! m_cleanupRunning )
2016-11-30 07:43:17 +00:00
break ;
2020-07-30 00:39:04 +00:00
StampRegionStatusFile ( s . RegionInfo . RegionID ) ;
2009-10-12 22:21:32 +00:00
}
2020-05-11 23:56:47 +00:00
gatherer . GatherAll ( ) ;
2020-05-12 02:03:31 +00:00
2020-05-11 23:56:47 +00:00
gatherer . FailedUUIDs . Clear ( ) ;
gatherer . UncertainAssetsUUIDs . Clear ( ) ;
2020-07-13 22:26:09 +00:00
gatherer = null ;
2020-05-11 23:56:47 +00:00
2020-07-30 20:18:58 +00:00
m_log . InfoFormat ( "[FLOTSAM ASSET CACHE] found {0} possible assets in use (including {1} default assets)" ,
gatheredids . Count + m_defaultAssets . Count , m_defaultAssets . Count ) ;
2020-07-29 23:02:02 +00:00
return gatheredids ;
2009-10-12 22:21:32 +00:00
}
/// <summary>
/// Deletes all cache contents
/// </summary>
private void ClearFileCache ( )
{
2017-05-26 04:32:59 +00:00
if ( ! Directory . Exists ( m_CacheDirectory ) )
return ;
2009-10-12 22:21:32 +00:00
foreach ( string dir in Directory . GetDirectories ( m_CacheDirectory ) )
{
try
{
Directory . Delete ( dir , true ) ;
}
catch ( Exception e )
{
2013-02-08 21:21:20 +00:00
m_log . WarnFormat (
2011-07-30 00:35:22 +00:00
"[FLOTSAM ASSET CACHE]: Couldn't clear asset cache directory {0} from {1}. Exception {2} {3}" ,
dir , m_CacheDirectory , e . Message , e . StackTrace ) ;
2009-10-12 22:21:32 +00:00
}
}
foreach ( string file in Directory . GetFiles ( m_CacheDirectory ) )
{
try
{
File . Delete ( file ) ;
}
catch ( Exception e )
{
2013-02-08 21:21:20 +00:00
m_log . WarnFormat (
2011-07-30 00:35:22 +00:00
"[FLOTSAM ASSET CACHE]: Couldn't clear asset cache file {0} from {1}. Exception {1} {2}" ,
file , m_CacheDirectory , e . Message , e . StackTrace ) ;
2009-10-12 22:21:32 +00:00
}
}
}
2013-10-30 18:57:51 +00:00
private List < string > GenerateCacheHitReport ( )
{
List < string > outputLines = new List < string > ( ) ;
2016-12-25 05:46:16 +00:00
double invReq = 100.0 / m_Requests ;
2016-12-29 03:10:10 +00:00
double weakHitRate = m_weakRefHits * invReq ;
2020-03-07 20:34:56 +00:00
int weakEntriesAlive = 0 ;
2020-03-11 16:04:02 +00:00
lock ( weakAssetReferencesLock )
2020-03-07 20:34:56 +00:00
{
foreach ( WeakReference aref in weakAssetReferences . Values )
{
if ( aref . IsAlive )
+ + weakEntriesAlive ;
}
}
2016-12-29 03:10:10 +00:00
int weakEntries = weakAssetReferences . Count ;
2017-01-05 19:07:37 +00:00
2016-12-25 05:46:16 +00:00
double fileHitRate = m_DiskHits * invReq ;
2016-12-29 03:10:10 +00:00
double TotalHitRate = weakHitRate + fileHitRate ;
outputLines . Add (
string . Format ( "Total requests: {0}" , m_Requests ) ) ;
outputLines . Add (
2020-03-07 20:34:56 +00:00
string . Format ( "unCollected Hit Rate: {0}% ({1} entries {2} alive)" , weakHitRate . ToString ( "0.00" ) , weakEntries , weakEntriesAlive ) ) ;
2013-10-30 18:57:51 +00:00
outputLines . Add (
2016-12-29 03:10:10 +00:00
string . Format ( "File Hit Rate: {0}%" , fileHitRate . ToString ( "0.00" ) ) ) ;
2013-10-30 18:57:51 +00:00
if ( m_MemoryCacheEnabled )
{
2016-12-25 05:46:16 +00:00
double HitRate = m_MemoryHits * invReq ;
outputLines . Add (
2016-12-29 03:10:10 +00:00
string . Format ( "Memory Hit Rate: {0}%" , HitRate . ToString ( "0.00" ) ) ) ;
2013-10-30 18:57:51 +00:00
2016-12-29 03:10:10 +00:00
TotalHitRate + = HitRate ;
2013-10-30 18:57:51 +00:00
}
2016-12-29 03:10:10 +00:00
outputLines . Add (
string . Format ( "Total Hit Rate: {0}%" , TotalHitRate . ToString ( "0.00" ) ) ) ;
2013-10-30 18:57:51 +00:00
outputLines . Add (
string . Format (
2016-12-29 03:10:10 +00:00
"Requests overlap during file writing: {0}" , m_RequestsForInprogress ) ) ;
2013-10-30 18:57:51 +00:00
return outputLines ;
}
2009-09-08 20:31:09 +00:00
#region Console Commands
private void HandleConsoleCommand ( string module , string [ ] cmdparams )
{
2013-10-30 18:32:53 +00:00
ICommandConsole con = MainConsole . Instance ;
2009-10-12 22:21:32 +00:00
if ( cmdparams . Length > = 2 )
2009-09-08 20:31:09 +00:00
{
string cmd = cmdparams [ 1 ] ;
2013-10-30 18:32:53 +00:00
2009-09-08 20:31:09 +00:00
switch ( cmd )
{
2009-10-12 22:21:32 +00:00
case "status" :
2020-07-30 20:14:15 +00:00
{
WorkManager . RunInThreadPool ( delegate
2013-10-30 18:57:51 +00:00
{
2020-07-30 20:14:15 +00:00
if ( m_MemoryCacheEnabled )
con . Output ( "[FLOTSAM ASSET CACHE] Memory Cache: {0} assets" , m_MemoryCache . Count ) ;
else
con . Output ( "[FLOTSAM ASSET CACHE] Memory cache disabled" ) ;
2013-10-30 18:57:51 +00:00
2020-07-30 20:14:15 +00:00
if ( m_FileCacheEnabled )
{
bool doingscan ;
lock ( timerLock )
{
doingscan = m_cleanupRunning ;
}
if ( doingscan )
{
con . Output ( "[FLOTSAM ASSET CACHE] a deep scan is in progress, skipping file cache assets count" ) ;
}
else
{
con . Output ( "[FLOTSAM ASSET CACHE] counting file cache assets" ) ;
int fileCount = GetFileCacheCount ( m_CacheDirectory ) ;
con . Output ( "[FLOTSAM ASSET CACHE] File Cache: {0} assets" , fileCount ) ;
}
}
else
{
con . Output ( "[FLOTSAM ASSET CACHE] File cache disabled" ) ;
}
2013-10-30 18:57:51 +00:00
2020-07-30 20:14:15 +00:00
GenerateCacheHitReport ( ) . ForEach ( l = > con . Output ( l ) ) ;
2017-01-05 19:07:37 +00:00
2020-07-30 20:14:15 +00:00
if ( m_FileCacheEnabled )
2017-01-05 19:07:37 +00:00
{
2020-07-30 20:14:15 +00:00
con . Output ( "[FLOTSAM ASSET CACHE] Deep scans have previously been performed on the following regions:" ) ;
foreach ( string s in Directory . GetFiles ( m_CacheDirectory , "*.fac" ) )
{
int start = s . IndexOf ( '_' ) ;
int end = s . IndexOf ( '.' ) ;
if ( start > 0 & & end > 0 )
{
string RegionID = s . Substring ( start + 1 , end - start ) ;
DateTime RegionDeepScanTMStamp = File . GetLastWriteTime ( s ) ;
con . Output ( "[FLOTSAM ASSET CACHE] Region: {0}, {1}" , RegionID , RegionDeepScanTMStamp . ToString ( "MM/dd/yyyy hh:mm:ss" ) ) ;
}
}
2011-07-04 21:51:47 +00:00
}
2020-07-30 20:14:15 +00:00
} , null , "CacheStatus" , false ) ;
2009-09-08 20:31:09 +00:00
break ;
2020-07-30 20:14:15 +00:00
}
2009-10-12 22:21:32 +00:00
case "clear" :
2011-06-10 23:50:20 +00:00
if ( cmdparams . Length < 2 )
2009-09-08 20:31:09 +00:00
{
2013-10-30 18:32:53 +00:00
con . Output ( "Usage is fcache clear [file] [memory]" ) ;
2009-10-12 22:21:32 +00:00
break ;
2009-09-08 20:31:09 +00:00
}
2011-06-10 23:50:20 +00:00
bool clearMemory = false , clearFile = false ;
if ( cmdparams . Length = = 2 )
{
clearMemory = true ;
clearFile = true ;
}
2009-10-12 22:21:32 +00:00
foreach ( string s in cmdparams )
2009-09-08 20:31:09 +00:00
{
2009-10-12 22:21:32 +00:00
if ( s . ToLower ( ) = = "memory" )
2011-06-10 23:50:20 +00:00
clearMemory = true ;
2009-10-12 22:21:32 +00:00
else if ( s . ToLower ( ) = = "file" )
2011-06-10 23:50:20 +00:00
clearFile = true ;
}
if ( clearMemory )
{
2011-07-04 21:51:47 +00:00
if ( m_MemoryCacheEnabled )
{
m_MemoryCache . Clear ( ) ;
2013-10-30 18:32:53 +00:00
con . Output ( "Memory cache cleared." ) ;
2011-07-04 21:51:47 +00:00
}
else
{
2013-10-30 18:32:53 +00:00
con . Output ( "Memory cache not enabled." ) ;
2011-07-04 21:51:47 +00:00
}
2009-09-08 20:31:09 +00:00
}
2017-01-05 19:07:37 +00:00
2011-06-10 23:50:20 +00:00
if ( clearFile )
{
2011-07-04 21:51:47 +00:00
if ( m_FileCacheEnabled )
{
ClearFileCache ( ) ;
2013-10-30 18:32:53 +00:00
con . Output ( "File cache cleared." ) ;
2011-07-04 21:51:47 +00:00
}
else
{
2013-10-30 18:32:53 +00:00
con . Output ( "File cache not enabled." ) ;
2011-07-04 21:51:47 +00:00
}
2011-06-10 23:50:20 +00:00
}
2009-10-12 22:21:32 +00:00
break ;
case "assets" :
2020-07-29 23:02:02 +00:00
lock ( timerLock )
2016-12-25 05:46:16 +00:00
{
2020-07-29 23:02:02 +00:00
if ( m_cleanupRunning )
2016-12-25 05:46:16 +00:00
{
2019-08-20 22:28:59 +00:00
con . Output ( "Flotsam assets check already running" ) ;
2016-12-25 05:46:16 +00:00
return ;
}
m_cleanupRunning = true ;
}
2018-06-16 15:01:09 +00:00
con . Output ( "Flotsam Ensuring assets are cached for all scenes." ) ;
2009-10-12 22:21:32 +00:00
2020-07-29 23:02:02 +00:00
WorkManager . RunInThreadPool ( async delegate
2014-09-05 22:20:59 +00:00
{
2016-11-30 07:43:17 +00:00
bool wasRunning = false ;
lock ( timerLock )
{
2016-12-25 05:46:16 +00:00
if ( m_timerRunning )
2016-11-30 07:43:17 +00:00
{
m_CacheCleanTimer . Stop ( ) ;
2016-12-25 05:46:16 +00:00
m_timerRunning = false ;
2016-11-30 07:43:17 +00:00
wasRunning = true ;
}
}
2020-07-29 23:02:02 +00:00
if ( wasRunning )
await Task . Delay ( 100 ) . ConfigureAwait ( false ) ;
int assetReferenceTotal = await TouchAllSceneAssets ( true ) . ConfigureAwait ( false ) ;
2020-07-07 16:35:27 +00:00
2016-11-30 07:43:17 +00:00
lock ( timerLock )
{
if ( wasRunning )
{
m_CacheCleanTimer . Start ( ) ;
2017-01-05 19:07:37 +00:00
m_timerRunning = true ;
2016-11-30 07:43:17 +00:00
}
2016-12-25 05:46:16 +00:00
m_cleanupRunning = false ;
2016-11-30 07:43:17 +00:00
}
2019-10-22 11:39:50 +00:00
con . Output ( "Completed check with {0} assets." , assetReferenceTotal ) ;
2018-02-02 11:47:19 +00:00
} , null , "TouchAllSceneAssets" , false ) ;
2009-09-08 20:31:09 +00:00
break ;
2009-10-12 22:21:32 +00:00
case "expire" :
2020-07-29 23:02:02 +00:00
lock ( timerLock )
{
if ( m_cleanupRunning )
{
con . Output ( "Flotsam assets check already running" ) ;
return ;
}
m_cleanupRunning = true ;
}
2010-01-29 21:52:13 +00:00
if ( cmdparams . Length < 3 )
2009-10-12 22:21:32 +00:00
{
2019-08-20 22:28:59 +00:00
con . Output ( "Invalid parameters for Expire, please specify a valid date & time" ) ;
2009-10-12 22:21:32 +00:00
break ;
}
string s_expirationDate = "" ;
DateTime expirationDate ;
if ( cmdparams . Length > 3 )
{
s_expirationDate = string . Join ( " " , cmdparams , 2 , cmdparams . Length - 2 ) ;
}
else
{
s_expirationDate = cmdparams [ 2 ] ;
}
if ( ! DateTime . TryParse ( s_expirationDate , out expirationDate ) )
{
2019-10-22 11:39:50 +00:00
con . Output ( "{0} is not a valid date & time" , cmd ) ;
2009-10-12 22:21:32 +00:00
break ;
}
2020-05-17 23:20:28 +00:00
if ( expirationDate > = DateTime . Now )
{
con . Output ( "{0} date & time must be in past" , cmd ) ;
break ;
}
2011-07-04 21:51:47 +00:00
if ( m_FileCacheEnabled )
2020-07-07 16:35:27 +00:00
{
2020-07-29 23:02:02 +00:00
WorkManager . RunInThreadPool ( async delegate
{
bool wasRunning = false ;
lock ( timerLock )
{
if ( m_timerRunning )
{
m_CacheCleanTimer . Stop ( ) ;
m_timerRunning = false ;
wasRunning = true ;
}
}
if ( wasRunning )
await Task . Delay ( 100 ) . ConfigureAwait ( false ) ;
await DoCleanExpiredFiles ( expirationDate ) . ConfigureAwait ( false ) ;
lock ( timerLock )
{
if ( wasRunning )
{
m_CacheCleanTimer . Start ( ) ;
m_timerRunning = true ;
}
m_cleanupRunning = false ;
}
} , null , "TouchAllSceneAssets" , false ) ;
2020-07-07 16:35:27 +00:00
}
2011-07-04 21:51:47 +00:00
else
2019-08-20 22:28:59 +00:00
con . Output ( "File cache not active, not clearing." ) ;
2009-10-12 22:21:32 +00:00
2020-03-06 19:20:12 +00:00
break ;
case "cachedefaultassets" :
HandleLoadDefaultAssets ( ) ;
break ;
case "deletedefaultassets" :
HandleDeleteDefaultAssets ( ) ;
2009-10-12 22:21:32 +00:00
break ;
2009-09-08 20:31:09 +00:00
default :
2019-10-22 11:39:50 +00:00
con . Output ( "Unknown command {0}" , cmd ) ;
2009-09-08 20:31:09 +00:00
break ;
}
}
else if ( cmdparams . Length = = 1 )
{
2013-10-30 18:32:53 +00:00
con . Output ( "fcache assets - Attempt a deep cache of all assets in all scenes" ) ;
con . Output ( "fcache expire <datetime> - Purge assets older then the specified date & time" ) ;
con . Output ( "fcache clear [file] [memory] - Remove cached assets" ) ;
con . Output ( "fcache status - Display cache status" ) ;
2020-03-06 19:20:12 +00:00
con . Output ( "fcache cachedefaultassets - loads default assets to cache replacing existent ones, this may override grid assets. Use with care" ) ;
con . Output ( "fcache deletedefaultassets - deletes default local assets from cache so they can be refreshed from grid" ) ;
2009-09-08 20:31:09 +00:00
}
}
# endregion
2009-10-12 22:21:32 +00:00
#region IAssetService Members
public AssetMetadata GetMetadata ( string id )
{
2020-07-15 00:40:53 +00:00
Get ( id , out AssetBase asset ) ;
2019-09-03 14:21:25 +00:00
if ( asset = = null )
return null ;
2009-10-12 22:21:32 +00:00
return asset . Metadata ;
}
public byte [ ] GetData ( string id )
{
2020-07-15 00:40:53 +00:00
Get ( id , out AssetBase asset ) ;
2019-09-03 14:21:25 +00:00
if ( asset = = null )
return null ;
2009-10-12 22:21:32 +00:00
return asset . Data ;
}
public bool Get ( string id , object sender , AssetRetrieved handler )
{
2020-07-15 00:40:53 +00:00
if ( ! Get ( id , out AssetBase asset ) )
2017-01-30 13:59:05 +00:00
return false ;
2009-10-12 22:21:32 +00:00
handler ( id , sender , asset ) ;
return true ;
}
2014-03-31 08:53:12 +00:00
public bool [ ] AssetsExist ( string [ ] ids )
{
bool [ ] exist = new bool [ ids . Length ] ;
2017-01-05 19:07:37 +00:00
2014-03-31 08:53:12 +00:00
for ( int i = 0 ; i < ids . Length ; i + + )
{
exist [ i ] = Check ( ids [ i ] ) ;
}
2017-01-05 19:07:37 +00:00
2014-03-31 08:53:12 +00:00
return exist ;
}
2009-10-12 22:21:32 +00:00
public string Store ( AssetBase asset )
{
2009-11-09 16:03:15 +00:00
if ( asset . FullID = = UUID . Zero )
2009-10-12 22:21:32 +00:00
{
asset . FullID = UUID . Random ( ) ;
}
Cache ( asset ) ;
return asset . ID ;
}
public bool UpdateContent ( string id , byte [ ] data )
{
2020-07-15 00:40:53 +00:00
if ( ! Get ( id , out AssetBase asset ) )
2017-02-01 16:31:01 +00:00
return false ;
2009-10-12 22:21:32 +00:00
asset . Data = data ;
2020-03-07 20:34:56 +00:00
Cache ( asset , true ) ;
2009-10-12 22:21:32 +00:00
return true ;
}
public bool Delete ( string id )
{
Expire ( id ) ;
return true ;
}
2020-03-06 19:20:12 +00:00
private void HandleLoadDefaultAssets ( )
{
if ( string . IsNullOrWhiteSpace ( m_assetLoader ) )
{
m_log . Info ( "[FLOTSAM ASSET CACHE] default assets loader not defined" ) ;
return ;
}
IAssetLoader assetLoader = ServerUtils . LoadPlugin < IAssetLoader > ( m_assetLoader , new object [ ] { } ) ;
if ( assetLoader = = null )
{
m_log . Info ( "[FLOTSAM ASSET CACHE] default assets loader not found" ) ;
return ;
}
m_log . Info ( "[FLOTSAM ASSET CACHE] start loading local default assets" ) ;
2020-07-30 20:14:15 +00:00
2020-03-06 19:20:12 +00:00
int count = 0 ;
2020-07-30 20:14:15 +00:00
HashSet < string > ids = new HashSet < string > ( ) ;
2020-03-06 19:20:12 +00:00
assetLoader . ForEachDefaultXmlAsset (
m_assetLoaderArgs ,
delegate ( AssetBase a )
{
2020-03-07 20:34:56 +00:00
Cache ( a , true ) ;
2020-07-30 20:14:15 +00:00
ids . Add ( a . ID ) ;
2020-03-06 19:20:12 +00:00
+ + count ;
} ) ;
2020-07-30 20:14:15 +00:00
m_defaultAssets = ids ;
2020-03-06 19:20:12 +00:00
m_log . InfoFormat ( "[FLOTSAM ASSET CACHE] loaded {0} local default assets" , count ) ;
}
private void HandleDeleteDefaultAssets ( )
{
if ( string . IsNullOrWhiteSpace ( m_assetLoader ) )
{
m_log . Info ( "[FLOTSAM ASSET CACHE] default assets loader not defined" ) ;
return ;
}
IAssetLoader assetLoader = ServerUtils . LoadPlugin < IAssetLoader > ( m_assetLoader , new object [ ] { } ) ;
if ( assetLoader = = null )
{
m_log . Info ( "[FLOTSAM ASSET CACHE] default assets loader not found" ) ;
return ;
}
m_log . Info ( "[FLOTSAM ASSET CACHE] started deleting local default assets" ) ;
int count = 0 ;
assetLoader . ForEachDefaultXmlAsset (
m_assetLoaderArgs ,
delegate ( AssetBase a )
{
Expire ( a . ID ) ;
+ + count ;
} ) ;
2020-07-30 20:14:15 +00:00
m_defaultAssets = new HashSet < string > ( ) ;
2020-03-06 19:20:12 +00:00
m_log . InfoFormat ( "[FLOTSAM ASSET CACHE] deleted {0} local default assets" , count ) ;
}
2009-10-12 22:21:32 +00:00
# endregion
2009-06-09 17:48:22 +00:00
}
2011-08-02 06:14:20 +00:00
}