Readded resource unloading.
This commit is contained in:
2
TODO.md
2
TODO.md
@@ -11,7 +11,7 @@
|
|||||||
## I/O
|
## I/O
|
||||||
|
|
||||||
- ~~Use GUIDs and string ID maps for fetching resources instead of string IDs alone.~~
|
- ~~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.
|
- Finalize ResourceManager and ResourceLoader APIs for 1.0.
|
||||||
- Add async API for ResourceManager.
|
- Add async API for ResourceManager.
|
||||||
- Virtual file system.
|
- Virtual file system.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ public class FontLoader : ResourceLoader<Font>
|
|||||||
{
|
{
|
||||||
public override IEnumerable<string> SupportedExtensions => new string[]
|
public override IEnumerable<string> SupportedExtensions => new string[]
|
||||||
{
|
{
|
||||||
"ttf"
|
".ttf"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Voile.Resources
|
|||||||
public abstract IEnumerable<string> SupportedExtensions { get; }
|
public abstract IEnumerable<string> SupportedExtensions { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads a resource to this resource loader's resource list.
|
/// Loads a resource to <see cref="ResourceManager"/>'s resource list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">File system path to the resource to load.</param>
|
/// <param name="path">File system path to the resource to load.</param>
|
||||||
/// <returns>A <see cref="Guid"/> of the loaded resource that can be later retrieved with <see cref="TryGet"/>.</returns>
|
/// <returns>A <see cref="Guid"/> of the loaded resource that can be later retrieved with <see cref="TryGet"/>.</returns>
|
||||||
@@ -24,15 +24,17 @@ namespace Voile.Resources
|
|||||||
var resource = LoadResource(path);
|
var resource = LoadResource(path);
|
||||||
var guid = Guid.NewGuid();
|
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;
|
return oldResourceGuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
_loadedResources.Add(guid, resource);
|
ResourceManager.AddResource(guid, resource);
|
||||||
|
|
||||||
return guid;
|
return guid;
|
||||||
}
|
}
|
||||||
@@ -42,32 +44,12 @@ namespace Voile.Resources
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Reload()
|
public void Reload()
|
||||||
{
|
{
|
||||||
foreach (var loadedResource in _loadedResources)
|
foreach (var loadedResource in ResourceManager.LoadedResources)
|
||||||
{
|
{
|
||||||
Load(loadedResource.Value.Path);
|
Load(loadedResource.Value.Path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a resource from a GUID.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="resourceGuid">GUID of the resource to get.</param>
|
|
||||||
/// <param name="resource">Retrieved resource. Will return a default resource if resource retrieval was not successful.</param>
|
|
||||||
/// <returns>True if resource retrieval was successful.</returns>
|
|
||||||
public bool TryGet(Guid resourceGuid, [NotNullWhen(true)] out T? resource)
|
|
||||||
{
|
|
||||||
resource = default;
|
|
||||||
|
|
||||||
if (!_loadedResources.ContainsKey(resourceGuid))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
resource = _loadedResources[resourceGuid];
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unloads a resource.
|
/// Unloads a resource.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -75,14 +57,14 @@ namespace Voile.Resources
|
|||||||
/// <returns>True if unloading was successful, otherwise false.</returns>
|
/// <returns>True if unloading was successful, otherwise false.</returns>
|
||||||
public bool TryUnload(Guid resourceGuid)
|
public bool TryUnload(Guid resourceGuid)
|
||||||
{
|
{
|
||||||
if (!_loadedResources.ContainsKey(resourceGuid))
|
if (!ResourceManager.LoadedResources.ContainsKey(resourceGuid))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var resource = _loadedResources[resourceGuid];
|
var resource = ResourceManager.LoadedResources[resourceGuid];
|
||||||
|
|
||||||
_loadedResources.Remove(resourceGuid);
|
ResourceManager.RemoveResource(resourceGuid);
|
||||||
resource.Dispose();
|
resource.Dispose();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -94,7 +76,5 @@ namespace Voile.Resources
|
|||||||
/// <param name="path">File system path to the resource to load.</param>
|
/// <param name="path">File system path to the resource to load.</param>
|
||||||
/// <returns>Loaded resource.</returns>
|
/// <returns>Loaded resource.</returns>
|
||||||
protected abstract T LoadResource(string path);
|
protected abstract T LoadResource(string path);
|
||||||
|
|
||||||
protected Dictionary<Guid, T> _loadedResources = new();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
using Voile.Utils;
|
using Voile.Utils;
|
||||||
|
|
||||||
@@ -14,6 +16,8 @@ namespace Voile.Resources
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ResourceRoot { get; set; } = "Resources/";
|
public static string ResourceRoot { get; set; } = "Resources/";
|
||||||
|
|
||||||
|
public static IReadOnlyDictionary<Guid, Resource> LoadedResources => _loadedResources;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Emits when a resource gets loaded.
|
/// Emits when a resource gets loaded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -27,6 +31,55 @@ namespace Voile.Resources
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static Action? OnReloaded;
|
public static Action? OnReloaded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a resource to the list of loaded resources.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resource"></param>
|
||||||
|
public static void AddResource(Guid resourceGuid, [DisallowNull] Resource resource)
|
||||||
|
{
|
||||||
|
_loadedResources.TryAdd(resourceGuid, resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces a resource with a different one.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="targetResourceGuid">GUID of a resource to replace.</param>
|
||||||
|
/// <param name="withResource">Resource to replace with.</param>
|
||||||
|
public static void ReplaceResource(Guid targetResourceGuid, [DisallowNull] Resource withResource)
|
||||||
|
{
|
||||||
|
_loadedResources[targetResourceGuid] = withResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a resource from a GUID.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceGuid">GUID of the resource to get.</param>
|
||||||
|
/// <param name="resource">Retrieved resource. Will return a default resource if resource retrieval was not successful.</param>
|
||||||
|
/// <returns>True if resource retrieval was successful.</returns>
|
||||||
|
public static bool GetResource<T>(Guid resourceGuid, [NotNullWhen(true)] out T? resource) where T : Resource
|
||||||
|
{
|
||||||
|
resource = default;
|
||||||
|
|
||||||
|
if (!_loadedResources.ContainsKey(resourceGuid))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
resource = (T)_loadedResources[resourceGuid];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceGuid">GUID of resource to remove.</param>
|
||||||
|
public static void RemoveResource(Guid resourceGuid)
|
||||||
|
{
|
||||||
|
_loadedResources.Remove(resourceGuid, out _);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads a resource from a given file system path.
|
/// Loads a resource from a given file system path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -56,7 +109,7 @@ namespace Voile.Resources
|
|||||||
}
|
}
|
||||||
|
|
||||||
var extension = Path.GetExtension(fullPath);
|
var extension = Path.GetExtension(fullPath);
|
||||||
var hasExtension = loader.SupportedExtensions.Any(ext => extension[1..] == ext);
|
var hasExtension = loader.SupportedExtensions.Any(ext => ext == extension);
|
||||||
|
|
||||||
if (!hasExtension)
|
if (!hasExtension)
|
||||||
{
|
{
|
||||||
@@ -65,7 +118,7 @@ namespace Voile.Resources
|
|||||||
|
|
||||||
var resourceGuid = loader.Load(fullPath);
|
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}\"!");
|
_logger.Error($"Failed to load resource at path \"{path}\"!");
|
||||||
return false;
|
return false;
|
||||||
@@ -90,36 +143,49 @@ namespace Voile.Resources
|
|||||||
OnReloaded?.Invoke();
|
OnReloaded?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
/// <summary>
|
||||||
public bool TryUnload(string resourceId)
|
/// Unloads a resource at a path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourcePath">Path to the resource.</param>
|
||||||
|
/// <exception cref="KeyNotFoundException"></exception>
|
||||||
|
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))
|
if (!_resourcePathMap.ContainsKey(resourcePath))
|
||||||
// {
|
{
|
||||||
// _logger.Error($"Resource with ID \"{resourceId}\" doesn't exist!");
|
throw new KeyNotFoundException($"Resource at path \"{resourcePath}\" was not found!");
|
||||||
// return false;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
return true;
|
var guid = _resourcePathMap[resourcePath];
|
||||||
|
RemoveResource(guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
/// <summary>
|
||||||
public bool TryUnload(Guid resourceGuid)
|
/// Unloads a resource by GUID.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceGuid">GUID of a resource to unload.</param>
|
||||||
|
/// <exception cref="KeyNotFoundException"></exception>
|
||||||
|
public void Unload(Guid resourceGuid)
|
||||||
{
|
{
|
||||||
_logger.Info($"Unloading resource with guid \"{resourceGuid}\"...");
|
_logger.Info($"Unloading resource with guid \"{resourceGuid}\"...");
|
||||||
|
|
||||||
// if (!_loadedResources.ContainsKey(resourceGuid))
|
if (!_loadedResources.ContainsKey(resourceGuid))
|
||||||
// {
|
{
|
||||||
// _logger.Error($"Cannot unload resource with id \"{resourceGuid}\": resource doesn't exist!");
|
throw new KeyNotFoundException($"Resource with GUID {resourceGuid} was not found!");
|
||||||
// return false;
|
}
|
||||||
// }
|
|
||||||
// var resource = _loadedResources[resourceGuid];
|
|
||||||
|
|
||||||
// _loadedResources.Remove(resourceGuid);
|
RemoveResource(resourceGuid);
|
||||||
// resource.Dispose();
|
}
|
||||||
|
|
||||||
return true;
|
/// <summary>
|
||||||
|
/// Unloads a resource by a ResourceRef.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="resourceRef"></param>
|
||||||
|
public void Unload<T>(ResourceRef<T> resourceRef) where T : Resource
|
||||||
|
{
|
||||||
|
Unload(resourceRef.Guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TrySave<T>(string path, in T resource) where T : Resource
|
public bool TrySave<T>(string path, in T resource) where T : Resource
|
||||||
@@ -137,42 +203,6 @@ namespace Voile.Resources
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a <see cref="Resource"/> by a file system path.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of <see cref="Resource"/> to load.</typeparam>
|
|
||||||
/// <param name="path">Path to the resource.</param>
|
|
||||||
/// <param name="resource">Retrieved resource. Otherwise null if nothing got retrieved.</param>
|
|
||||||
/// <returns>True if resource got successfully retrieved, otherwise false.</returns>
|
|
||||||
public bool TryGetResource<T>(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<T>? 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a <see cref="Resource"/> by resource's <see cref="Guid"/>.
|
/// Gets a <see cref="Resource"/> by resource's <see cref="Guid"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -187,7 +217,7 @@ namespace Voile.Resources
|
|||||||
throw new Exception($"No loader available for type {typeof(T)}!");
|
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!");
|
throw new Exception($"No resource with GUID \"{resourceGuid}\" found!");
|
||||||
}
|
}
|
||||||
@@ -327,5 +357,7 @@ namespace Voile.Resources
|
|||||||
|
|
||||||
private FileSystemWatcher? _fileWatcher;
|
private FileSystemWatcher? _fileWatcher;
|
||||||
private static Dictionary<string, Guid> _resourcePathMap = new();
|
private static Dictionary<string, Guid> _resourcePathMap = new();
|
||||||
|
|
||||||
|
private static ConcurrentDictionary<Guid, Resource> _loadedResources = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ public class ParticleEmitterSettingsResource : Resource
|
|||||||
public class ParticleEmitterSettingsResourceLoader : ResourceLoader<ParticleEmitterSettingsResource>
|
public class ParticleEmitterSettingsResourceLoader : ResourceLoader<ParticleEmitterSettingsResource>
|
||||||
{
|
{
|
||||||
public override IEnumerable<string> SupportedExtensions => new string[] {
|
public override IEnumerable<string> SupportedExtensions => new string[] {
|
||||||
"toml"
|
".toml"
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override ParticleEmitterSettingsResource LoadResource(string path)
|
protected override ParticleEmitterSettingsResource LoadResource(string path)
|
||||||
|
|||||||
Reference in New Issue
Block a user