Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions Robust.Client/ResourceManagement/BaseResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,8 @@ namespace Robust.Client.ResourceManagement;
/// <summary>
/// Base resource for the cache.
/// </summary>
public abstract class BaseResource : IDisposable
public abstract class BaseResource : IBaseResource, IDisposable
{
/// <summary>
/// Fallback resource path if this one does not exist.
/// </summary>
public virtual ResPath? Fallback => null;

/// <summary>
/// Disposes this resource.
/// </summary>
Expand Down
19 changes: 19 additions & 0 deletions Robust.Client/ResourceManagement/IBaseResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Robust.Shared.Utility;

namespace Robust.Client.ResourceManagement;

/// <summary>
/// Defines type-level metadata for resource types.
/// </summary>
public interface IBaseResource
{
/// <summary>
/// Whether resources of this type can be deterministically removed from the cache.
/// </summary>
static virtual bool CanBeRemoved => false;

/// <summary>
/// The fallback path for this resource type (if any).
/// </summary>
static virtual ResPath? FallbackPath => null;
}
10 changes: 7 additions & 3 deletions Robust.Client/ResourceManagement/IResourceCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ namespace Robust.Client.ResourceManagement;
public interface IResourceCache : IResourceManager
{
T GetResource<T>(string path, bool useFallback = true)
where T : BaseResource, new();
where T : BaseResource, IBaseResource, new();

T GetResource<T>(ResPath path, bool useFallback = true)
where T : BaseResource, new();
where T : BaseResource, IBaseResource, new();

bool TryGetResource<T>(string path, [NotNullWhen(true)] out T? resource)
where T : BaseResource, new();
Expand All @@ -28,6 +28,10 @@ bool TryGetResource<T>(ResPath path, [NotNullWhen(true)] out T? resource)

bool TryGetResource(AudioStream stream, [NotNullWhen(true)] out AudioResource? resource);

bool TryRemoveResource<T>(string path) where T : BaseResource, IBaseResource, new();

bool TryRemoveResource<T>(ResPath path) where T : BaseResource, IBaseResource, new();

void ReloadResource<T>(string path)
where T : BaseResource, new();

Expand All @@ -41,7 +45,7 @@ void CacheResource<T>(ResPath path, T resource)
where T : BaseResource, new();

T GetFallback<T>()
where T : BaseResource, new();
where T : BaseResource, IBaseResource, new();

IEnumerable<KeyValuePair<ResPath, T>> GetAllResources<T>() where T : BaseResource, new();

Expand Down
39 changes: 30 additions & 9 deletions Robust.Client/ResourceManagement/ResourceCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using Robust.Client.Audio;
using Robust.Shared.ContentPack;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Utility;

namespace Robust.Client.ResourceManagement;
Expand All @@ -20,12 +19,12 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
private readonly Dictionary<Type, TypeData> _cachedResources = new();
private readonly Dictionary<Type, BaseResource> _fallbacks = new();

public T GetResource<T>(string path, bool useFallback = true) where T : BaseResource, new()
public T GetResource<T>(string path, bool useFallback = true) where T : BaseResource, IBaseResource, new()
{
return GetResource<T>(new ResPath(path), useFallback);
}

public T GetResource<T>(ResPath path, bool useFallback = true) where T : BaseResource, new()
public T GetResource<T>(ResPath path, bool useFallback = true) where T : BaseResource, IBaseResource, new()
{
var cache = GetTypeData<T>();
if (cache.Resources.TryGetValue(path, out var cached))
Expand All @@ -43,11 +42,11 @@ internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInt
}
catch (Exception e)
{
if (useFallback && resource.Fallback != null)
if (useFallback && T.FallbackPath != null)
{
Sawmill.Error(
$"Exception while loading resource {typeof(T)} at '{path}', resorting to fallback.\n{Environment.StackTrace}\n{e}");
return GetResource<T>(resource.Fallback.Value, false);
return GetResource<T>(T.FallbackPath.Value, false);
}
else
{
Expand Down Expand Up @@ -107,6 +106,29 @@ public bool TryGetResource(AudioStream stream, [NotNullWhen(true)] out AudioReso
return true;
}

public bool TryRemoveResource<T>(string path) where T : BaseResource, IBaseResource, new()
=> TryRemoveResource<T>(new ResPath(path));

public bool TryRemoveResource<T>(ResPath path) where T : BaseResource, IBaseResource, new()
{
if (!T.CanBeRemoved)
throw new NotSupportedException($"Resource type '{typeof(T)}' does not support deterministic removal.");

if (T.FallbackPath == path)
return false;

var cache = GetTypeData<T>();

if (!cache.Resources.TryGetValue(path, out var resource))
return false;

cache.Resources.Remove(path);
cache.NonExistent.Remove(path);
resource.Dispose();

return true;
}

public void ReloadResource<T>(string path) where T : BaseResource, new()
{
ReloadResource<T>(new ResPath(path));
Expand Down Expand Up @@ -153,20 +175,19 @@ public bool TryGetResource(AudioStream stream, [NotNullWhen(true)] out AudioReso
GetTypeData<T>().Resources[path] = resource;
}

public T GetFallback<T>() where T : BaseResource, new()
public T GetFallback<T>() where T : BaseResource, IBaseResource, new()
{
if (_fallbacks.TryGetValue(typeof(T), out var fallback))
{
return (T) fallback;
}

var res = new T();
if (res.Fallback == null)
if (T.FallbackPath == null)
{
throw new InvalidOperationException($"Resource of type '{typeof(T)}' has no fallback.");
}

fallback = GetResource<T>(res.Fallback.Value, useFallback: false);
fallback = GetResource<T>(T.FallbackPath.Value, useFallback: false);
_fallbacks.Add(typeof(T), fallback);
return (T) fallback;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ namespace Robust.Client.ResourceManagement
/// Handles the loading code for RSI files.
/// See <see cref="RSI"/> for the RSI API itself.
/// </summary>
public sealed class RSIResource : BaseResource
public sealed class RSIResource : BaseResource, IBaseResource
{
public override ResPath? Fallback => new("/Textures/error.rsi");
static ResPath? IBaseResource.FallbackPath => new("/Textures/error.rsi");

public RSI RSI { get; private set; } = default!;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Threading;
using Robust.Client.Graphics;
using Robust.Shared.ContentPack;
Expand All @@ -13,12 +14,23 @@

namespace Robust.Client.ResourceManagement
{
public sealed class TextureResource : BaseResource
public sealed class TextureResource : BaseResource, IBaseResource
{
private OwnedTexture _texture = default!;
public override ResPath? Fallback => new("/Textures/noSprite.png");
private bool _disposed;
private readonly Lock _lock = new();

public Texture Texture => _texture;
static ResPath? IBaseResource.FallbackPath => new("/Textures/noSprite.png");
static bool IBaseResource.CanBeRemoved => true;

public Texture Texture
{
get
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _texture;
}
}

public override void Load(IDependencyCollection dependencies, ResPath path)
{
Expand Down Expand Up @@ -106,12 +118,14 @@ internal void LoadFinish(IResourceCache cache, LoadStepData data)

public override void Reload(IDependencyCollection dependencies, ResPath path, CancellationToken ct = default)
{
ObjectDisposedException.ThrowIf(_disposed, this);

var data = new LoadStepData {Path = path};

LoadTextureParameters(dependencies.Resolve<IResourceManager>(), data);
LoadPreTextureData(dependencies.Resolve<IResourceManager>(), data);

if (data.Image.Width == Texture.Width && data.Image.Height == Texture.Height)
if (data.Image.Width == _texture.Width && data.Image.Height == _texture.Height)
{
// Dimensions match, rewrite texture in place.
_texture.SetSubImage(Vector2i.Zero, data.Image);
Expand All @@ -127,6 +141,18 @@ public override void Reload(IDependencyCollection dependencies, ResPath path, Ca
data.Image.Dispose();
}

public override void Dispose()
{
lock (_lock)
{
if (_disposed) return;
_disposed = true;
_texture?.Dispose();
}

base.Dispose();
}

internal sealed class LoadStepData
{
public ResPath Path = default!;
Expand Down
Loading