diff --git a/TODO.md b/TODO.md index 3f6148e..38d6a20 100644 --- a/TODO.md +++ b/TODO.md @@ -11,7 +11,7 @@ ## I/O - ~~Use GUIDs and string ID maps for fetching resources instead of string IDs alone.~~ -- Reimplement unloading. +- ~~Reimplement unloading.~~ - Finalize ResourceManager and ResourceLoader APIs for 1.0. - Add async API for ResourceManager. - Virtual file system. diff --git a/Voile/Source/Resources/Loaders/FontLoader.cs b/Voile/Source/Resources/Loaders/FontLoader.cs index da0923d..9723e2f 100644 --- a/Voile/Source/Resources/Loaders/FontLoader.cs +++ b/Voile/Source/Resources/Loaders/FontLoader.cs @@ -5,7 +5,7 @@ public class FontLoader : ResourceLoader { public override IEnumerable SupportedExtensions => new string[] { - "ttf" + ".ttf" }; diff --git a/Voile/Source/Resources/Loaders/ResourceLoader.cs b/Voile/Source/Resources/Loaders/ResourceLoader.cs index 18fe5a0..0fbd237 100644 --- a/Voile/Source/Resources/Loaders/ResourceLoader.cs +++ b/Voile/Source/Resources/Loaders/ResourceLoader.cs @@ -15,7 +15,7 @@ namespace Voile.Resources public abstract IEnumerable SupportedExtensions { get; } /// - /// Loads a resource to this resource loader's resource list. + /// Loads a resource to 's resource list. /// /// File system path to the resource to load. /// A of the loaded resource that can be later retrieved with . @@ -24,15 +24,17 @@ namespace Voile.Resources var resource = LoadResource(path); var guid = Guid.NewGuid(); - var oldResourceGuid = _loadedResources.FirstOrDefault(loadedResource => loadedResource.Value.Path == path).Key; - if (_loadedResources.ContainsKey(oldResourceGuid)) + var loadedResources = ResourceManager.LoadedResources; + var oldResourceGuid = loadedResources.FirstOrDefault(loadedResource => loadedResource.Value.Path == path).Key; + + if (loadedResources.ContainsKey(oldResourceGuid)) { - _loadedResources[oldResourceGuid] = resource; + ResourceManager.ReplaceResource(oldResourceGuid, resource); return oldResourceGuid; } - _loadedResources.Add(guid, resource); + ResourceManager.AddResource(guid, resource); return guid; } @@ -42,32 +44,12 @@ namespace Voile.Resources /// public void Reload() { - foreach (var loadedResource in _loadedResources) + foreach (var loadedResource in ResourceManager.LoadedResources) { Load(loadedResource.Value.Path); } } - /// - /// Gets a resource from a GUID. - /// - /// GUID of the resource to get. - /// Retrieved resource. Will return a default resource if resource retrieval was not successful. - /// True if resource retrieval was successful. - public bool TryGet(Guid resourceGuid, [NotNullWhen(true)] out T? resource) - { - resource = default; - - if (!_loadedResources.ContainsKey(resourceGuid)) - { - return false; - } - - resource = _loadedResources[resourceGuid]; - - return true; - } - /// /// Unloads a resource. /// @@ -75,14 +57,14 @@ namespace Voile.Resources /// True if unloading was successful, otherwise false. public bool TryUnload(Guid resourceGuid) { - if (!_loadedResources.ContainsKey(resourceGuid)) + if (!ResourceManager.LoadedResources.ContainsKey(resourceGuid)) { return false; } - var resource = _loadedResources[resourceGuid]; + var resource = ResourceManager.LoadedResources[resourceGuid]; - _loadedResources.Remove(resourceGuid); + ResourceManager.RemoveResource(resourceGuid); resource.Dispose(); return true; @@ -94,7 +76,5 @@ namespace Voile.Resources /// File system path to the resource to load. /// Loaded resource. protected abstract T LoadResource(string path); - - protected Dictionary _loadedResources = new(); } } \ No newline at end of file diff --git a/Voile/Source/Resources/ResourceManager.cs b/Voile/Source/Resources/ResourceManager.cs index 89be8c6..2e62cb4 100644 --- a/Voile/Source/Resources/ResourceManager.cs +++ b/Voile/Source/Resources/ResourceManager.cs @@ -1,4 +1,6 @@ +using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; using Voile.Utils; @@ -14,6 +16,8 @@ namespace Voile.Resources /// public static string ResourceRoot { get; set; } = "Resources/"; + public static IReadOnlyDictionary LoadedResources => _loadedResources; + /// /// Emits when a resource gets loaded. /// @@ -27,6 +31,55 @@ namespace Voile.Resources /// public static Action? OnReloaded; + /// + /// Adds a resource to the list of loaded resources. + /// + /// + public static void AddResource(Guid resourceGuid, [DisallowNull] Resource resource) + { + _loadedResources.TryAdd(resourceGuid, resource); + } + + /// + /// Replaces a resource with a different one. + /// + /// GUID of a resource to replace. + /// Resource to replace with. + public static void ReplaceResource(Guid targetResourceGuid, [DisallowNull] Resource withResource) + { + _loadedResources[targetResourceGuid] = withResource; + } + + /// + /// Gets a resource from a GUID. + /// + /// GUID of the resource to get. + /// Retrieved resource. Will return a default resource if resource retrieval was not successful. + /// True if resource retrieval was successful. + public static bool GetResource(Guid resourceGuid, [NotNullWhen(true)] out T? resource) where T : Resource + { + resource = default; + + if (!_loadedResources.ContainsKey(resourceGuid)) + { + return false; + } + + resource = (T)_loadedResources[resourceGuid]; + + return true; + } + + /// + /// Removes a resource from the list of loaded resources. + /// This method only removes a resource from the list of loaded resources. If you want to unload a Resource, use Unload. + /// + /// GUID of resource to remove. + public static void RemoveResource(Guid resourceGuid) + { + _loadedResources.Remove(resourceGuid, out _); + } + /// /// Loads a resource from a given file system path. /// @@ -56,7 +109,7 @@ namespace Voile.Resources } var extension = Path.GetExtension(fullPath); - var hasExtension = loader.SupportedExtensions.Any(ext => extension[1..] == ext); + var hasExtension = loader.SupportedExtensions.Any(ext => ext == extension); if (!hasExtension) { @@ -65,7 +118,7 @@ namespace Voile.Resources var resourceGuid = loader.Load(fullPath); - if (!loader.TryGet(resourceGuid, out T? loadedResource)) + if (!GetResource(resourceGuid, out T? loadedResource)) { _logger.Error($"Failed to load resource at path \"{path}\"!"); return false; @@ -90,36 +143,49 @@ namespace Voile.Resources OnReloaded?.Invoke(); } - // TODO - public bool TryUnload(string resourceId) + /// + /// Unloads a resource at a path. + /// + /// Path to the resource. + /// + public void Unload(string resourcePath) { - _logger.Info($"Unloading resource with id \"{resourceId}\"..."); + _logger.Info($"Unloading resource at path \"{resourcePath}\"..."); - // if (!_resourceStringMap.TryGetValue(resourceId, out Guid guid)) - // { - // _logger.Error($"Resource with ID \"{resourceId}\" doesn't exist!"); - // return false; - // } + if (!_resourcePathMap.ContainsKey(resourcePath)) + { + throw new KeyNotFoundException($"Resource at path \"{resourcePath}\" was not found!"); + } - return true; + var guid = _resourcePathMap[resourcePath]; + RemoveResource(guid); } - // TODO - public bool TryUnload(Guid resourceGuid) + /// + /// Unloads a resource by GUID. + /// + /// GUID of a resource to unload. + /// + public void Unload(Guid resourceGuid) { _logger.Info($"Unloading resource with guid \"{resourceGuid}\"..."); - // if (!_loadedResources.ContainsKey(resourceGuid)) - // { - // _logger.Error($"Cannot unload resource with id \"{resourceGuid}\": resource doesn't exist!"); - // return false; - // } - // var resource = _loadedResources[resourceGuid]; + if (!_loadedResources.ContainsKey(resourceGuid)) + { + throw new KeyNotFoundException($"Resource with GUID {resourceGuid} was not found!"); + } - // _loadedResources.Remove(resourceGuid); - // resource.Dispose(); + RemoveResource(resourceGuid); + } - return true; + /// + /// Unloads a resource by a ResourceRef. + /// + /// + /// + public void Unload(ResourceRef resourceRef) where T : Resource + { + Unload(resourceRef.Guid); } public bool TrySave(string path, in T resource) where T : Resource @@ -137,42 +203,6 @@ namespace Voile.Resources return true; } - /// - /// Gets a by a file system path. - /// - /// Type of to load. - /// Path to the resource. - /// Retrieved resource. Otherwise null if nothing got retrieved. - /// True if resource got successfully retrieved, otherwise false. - public bool TryGetResource(string path, [NotNullWhen(true)] out T? resource) where T : Resource - { - resource = null; - - if (!IsResourceLoaded(path)) - { - _logger.Error($"Resource \"{path}\" has not yet been loaded!"); - return false; - } - - var resourceGuid = _resourcePathMap[path]; - - if (!TryGetLoader(out ResourceLoader? loader)) - { - _logger.Error($"No loader available for type {typeof(T)}!"); - return false; - } - - if (!loader.TryGet(resourceGuid, out T? loadedResource)) - { - _logger.Error($"No resource with id {path} found!"); - return false; - } - - resource = loadedResource; - - return true; - } - /// /// Gets a by resource's . /// @@ -187,7 +217,7 @@ namespace Voile.Resources throw new Exception($"No loader available for type {typeof(T)}!"); } - if (!loader.TryGet(resourceGuid, out T? loadedResource)) + if (!GetResource(resourceGuid, out T? loadedResource)) { throw new Exception($"No resource with GUID \"{resourceGuid}\" found!"); } @@ -327,5 +357,7 @@ namespace Voile.Resources private FileSystemWatcher? _fileWatcher; private static Dictionary _resourcePathMap = new(); + + private static ConcurrentDictionary _loadedResources = new(); } } \ No newline at end of file diff --git a/Voile/Source/Systems/ParticleSystem.cs b/Voile/Source/Systems/ParticleSystem.cs index f296ac7..9e030b3 100644 --- a/Voile/Source/Systems/ParticleSystem.cs +++ b/Voile/Source/Systems/ParticleSystem.cs @@ -55,7 +55,7 @@ public class ParticleEmitterSettingsResource : Resource public class ParticleEmitterSettingsResourceLoader : ResourceLoader { public override IEnumerable SupportedExtensions => new string[] { - "toml" + ".toml" }; protected override ParticleEmitterSettingsResource LoadResource(string path)