using Sandbox.Diagnostics; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace Sandbox.Mounting; /// /// A class responsible for loading a single resource. It will cache the result inside. /// public abstract class ResourceLoader { protected static readonly Logger Log = new Logger( "ResourceLoader" ); internal BaseGameMount _mount; /// /// The type of resource this file can provide. /// public ResourceType Type { get; private set; } static Dictionary extensions = new() { { ResourceType.Model, ".vmdl" }, { ResourceType.Scene, ".scene" }, { ResourceType.Texture, ".vtex" }, { ResourceType.Sound, ".vsnd" }, { ResourceType.Material, ".vmat" }, }; /// /// The path to the asset /// public string Path { get; private set; } /// /// The filename of the asset, without extension /// public string Name { get; private set; } /// /// General tags. Anything you want to do that doesn't fit into the flags. /// public HashSet Tags { get; } = new HashSet( StringComparer.OrdinalIgnoreCase ); /// /// Flags allow customization of how this resource is treated by the engine. /// public ResourceFlags Flags { get; set; } /// /// The folder in which this resource resides /// public ResourceFolder Folder { get; internal set; } public ResourceLoader() { } internal void InitializeInternal( ResourceType type, string path, BaseGameMount mount ) { _mount = mount; // Standardize the path into a mount path path = path.Replace( '\\', '/' ).Trim( '/' ); path = $"mount://{_mount.Ident}/{path}"; // Force an engine specific extension, if it isn't already set if ( extensions.TryGetValue( type, out var extension ) ) { if ( !path.EndsWith( extension ) ) path += extension; } Path = path; Type = type; Name = System.IO.Path.GetFileNameWithoutExtension( path ); _mount.RegisterFileInternal( this ); } object _cachedResult; Lock _lock = new(); Task _loadTask; /// /// Should be implemented to load a specific type /// public async Task GetOrCreate() { if ( _loadTask is not null && !_loadTask.IsCompleted ) { await _loadTask; } _lock.Enter(); if ( _cachedResult is not null ) { _lock.Exit(); return _cachedResult; } try { _loadTask = LoadAsync(); if ( _loadTask is not null ) { await _loadTask; _cachedResult = _loadTask.Result; } } catch ( System.Exception e ) { Log.Warning( e, $"Exception when loading '{Path}'" ); } finally { _lock.Exit(); } _loadTask = default; return _cachedResult; } /// /// Should be implemented to load a specific type /// protected virtual Task LoadAsync() { return Task.FromResult( Load() ); } /// /// Should be implemented to load a specific type /// protected virtual object Load() { return null; ; } internal void ShutdownInternal() { Shutdown(); } protected virtual void Shutdown() { } } public abstract class ResourceLoader : ResourceLoader where T : BaseGameMount { public T Host => _mount as T; }