mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Add DataShare.
This commit is contained in:
parent
8565cbc5ea
commit
58192240ff
7 changed files with 209 additions and 1 deletions
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
|
@ -174,6 +175,22 @@ namespace Dalamud.Plugin
|
|||
|
||||
#region IPC
|
||||
|
||||
/// <inheritdoc cref="Ipc.Internal.DataShare.GetOrCreateData{T}"/>
|
||||
public T GetOrCreateData<T>(string tag, Func<T> dataGenerator) where T : class
|
||||
=> Service<Ipc.Internal.DataShare>.Get().GetOrCreateData(tag, dataGenerator);
|
||||
|
||||
/// <inheritdoc cref="Ipc.Internal.DataShare.RelinquishData"/>
|
||||
public void RelinquishData(string tag)
|
||||
=> Service<Ipc.Internal.DataShare>.Get().RelinquishData(tag);
|
||||
|
||||
/// <inheritdoc cref="Ipc.Internal.DataShare.TryGetData{T}"/>
|
||||
public bool TryGetData<T>(string tag, [NotNullWhen(true)] out T? data) where T : class
|
||||
=> Service<Ipc.Internal.DataShare>.Get().TryGetData(tag, out data);
|
||||
|
||||
/// <inheritdoc cref="Ipc.Internal.DataShare.GetData{T}"/>
|
||||
public T? GetData<T>(string tag) where T : class
|
||||
=> Service<Ipc.Internal.DataShare>.Get().GetData<T>(tag);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an IPC provider.
|
||||
/// </summary>
|
||||
|
|
|
|||
21
Dalamud/Plugin/Ipc/Exceptions/DataCacheCreationError.cs
Normal file
21
Dalamud/Plugin/Ipc/Exceptions/DataCacheCreationError.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
|
||||
namespace Dalamud.Plugin.Ipc.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// This exception is thrown when a null value is provided for a data cache or it does not implement the expected type.
|
||||
/// </summary>
|
||||
public class DataCacheCreationError : IpcError
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataCacheCreationError"/> class.
|
||||
/// </summary>
|
||||
/// <param name="tag">Tag of the data cache.</param>
|
||||
/// <param name="creator">The assembly name of the caller.</param>
|
||||
/// <param name="expectedType">The type expected.</param>
|
||||
/// <param name="ex">The thrown exception.</param>
|
||||
public DataCacheCreationError(string tag, string creator, Type expectedType, Exception ex)
|
||||
: base($"The creation of the {expectedType} data cache {tag} initialized by {creator} was unsuccessful.", ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
21
Dalamud/Plugin/Ipc/Exceptions/DataCacheTypeMismatchError.cs
Normal file
21
Dalamud/Plugin/Ipc/Exceptions/DataCacheTypeMismatchError.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
|
||||
namespace Dalamud.Plugin.Ipc.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// This exception is thrown when a data cache is accessed with the wrong type.
|
||||
/// </summary>
|
||||
public class DataCacheTypeMismatchError : IpcError
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataCacheTypeMismatchError"/> class.
|
||||
/// </summary>
|
||||
/// <param name="tag">Tag of the data cache.</param>
|
||||
/// <param name="creator">Assembly name of the plugin creating the cache.</param>
|
||||
/// <param name="requestedType">The requested type.</param>
|
||||
/// <param name="actualType">The stored type.</param>
|
||||
public DataCacheTypeMismatchError(string tag, string creator, Type requestedType, Type actualType)
|
||||
: base($"Data cache {tag} was requested with type {requestedType}, but {creator} created type {actualType}.")
|
||||
{
|
||||
}
|
||||
}
|
||||
19
Dalamud/Plugin/Ipc/Exceptions/DataCacheValueNullError.cs
Normal file
19
Dalamud/Plugin/Ipc/Exceptions/DataCacheValueNullError.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
|
||||
namespace Dalamud.Plugin.Ipc.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// This exception is thrown when a null value is provided for a data cache or it does not implement the expected type.
|
||||
/// </summary>
|
||||
public class DataCacheValueNullError : IpcError
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataCacheValueNullError"/> class.
|
||||
/// </summary>
|
||||
/// <param name="tag">Tag of the data cache.</param>
|
||||
/// <param name="expectedType">The type expected.</param>
|
||||
public DataCacheValueNullError(string tag, Type expectedType)
|
||||
: base($"The data cache {tag} expects a type of {expectedType} but does not implement it.")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
|
||||
namespace Dalamud.Plugin.Ipc.Exceptions;
|
||||
|
||||
|
|
|
|||
20
Dalamud/Plugin/Ipc/Internal/DataCache.cs
Normal file
20
Dalamud/Plugin/Ipc/Internal/DataCache.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Dalamud.Plugin.Ipc.Internal;
|
||||
|
||||
internal class DataCache
|
||||
{
|
||||
internal readonly string CreatorAssemblyName;
|
||||
internal readonly List<string> UserAssemblyNames;
|
||||
internal readonly Type Type;
|
||||
internal readonly object? Data;
|
||||
|
||||
internal DataCache(string creatorAssemblyName, object? data, Type type)
|
||||
{
|
||||
this.CreatorAssemblyName = creatorAssemblyName;
|
||||
this.UserAssemblyNames = new List<string>{ creatorAssemblyName };
|
||||
this.Data = data;
|
||||
this.Type = type;
|
||||
}
|
||||
}
|
||||
110
Dalamud/Plugin/Ipc/Internal/DataShare.cs
Normal file
110
Dalamud/Plugin/Ipc/Internal/DataShare.cs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using Dalamud.Plugin.Ipc.Exceptions;
|
||||
|
||||
namespace Dalamud.Plugin.Ipc.Internal;
|
||||
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
internal class DataShare : IServiceType
|
||||
{
|
||||
private readonly Dictionary<string, DataCache> caches = new();
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private DataShare()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If a data cache for <paramref name="tag"/> exists, return the data.
|
||||
/// Otherwise, call the function <paramref name="dataGenerator"/> to create data and store it as a new cache.
|
||||
/// In either case, the calling assembly will be added to the current consumers on success.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the stored data - needs to be a reference type.</typeparam>
|
||||
/// <param name="tag">The name for the data cache.</param>
|
||||
/// <param name="dataGenerator">The function that generates the data if it does not already exist.</param>
|
||||
/// <returns>Either the existing data for <paramref name="tag"/> or the data generated by <paramref name="dataGenerator"/>.</returns>
|
||||
/// <exception cref="DataCacheTypeMismatchError">Thrown if a cache for <paramref name="tag"/> exists, but contains data of a type not assignable to <typeparamref name="T>"/>.</exception>
|
||||
/// <exception cref="DataCacheValueNullError">Thrown if the stored data for a cache is null.</exception>
|
||||
/// <exception cref="DataCacheCreationError">Thrown if <paramref name="dataGenerator"/> throws an exception or returns null.</exception>
|
||||
public T GetOrCreateData<T>(string tag, Func<T> dataGenerator) where T : class
|
||||
{
|
||||
var callerName = Assembly.GetCallingAssembly().GetName().Name ?? string.Empty;
|
||||
if (this.caches.TryGetValue(tag, out var cache))
|
||||
{
|
||||
if (!cache.Type.IsAssignableTo(typeof(T)))
|
||||
throw new DataCacheTypeMismatchError(tag, cache.CreatorAssemblyName, typeof(T), cache.Type);
|
||||
cache.UserAssemblyNames.Add(callerName);
|
||||
return cache.Data as T ?? throw new DataCacheValueNullError(tag, cache.Type);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var obj = dataGenerator.Invoke();
|
||||
if (obj == null)
|
||||
throw new Exception("Returned data was null.");
|
||||
|
||||
cache = new DataCache(callerName, obj, typeof(T));
|
||||
this.caches[tag] = cache;
|
||||
return obj;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new DataCacheCreationError(tag, callerName, typeof(T), e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the DataShare that the calling assembly no longer uses the data stored for <paramref name="tag"/> (or uses it one time fewer).
|
||||
/// If no assembly uses the data anymore, the cache will be removed from the data share and if it is an IDisposable, Dispose will be called on it.
|
||||
/// </summary>
|
||||
/// <param name="tag">The name for the data cache.</param>
|
||||
public void RelinquishData(string tag)
|
||||
{
|
||||
if (!this.caches.TryGetValue(tag, out var cache))
|
||||
return;
|
||||
|
||||
var callerName = Assembly.GetCallingAssembly().GetName().Name ?? string.Empty;
|
||||
if (!cache.UserAssemblyNames.Remove(callerName) || cache.UserAssemblyNames.Count > 0)
|
||||
return;
|
||||
|
||||
this.caches.Remove(tag);
|
||||
if (cache.Data is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtain the data for the given <paramref name="tag"/>, if it exists and has the correct type.
|
||||
/// Add the calling assembly to the current consumers if true is returned.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type for the requested data - needs to be a reference type.</typeparam>
|
||||
/// <param name="tag">The name for the data cache.</param>
|
||||
/// <param name="data">The requested data on success, null otherwise.</param>
|
||||
/// <returns>True if the requested data exists and is assignable to the requested type.</returns>
|
||||
public bool TryGetData<T>(string tag, [NotNullWhen(true)] out T? data) where T : class
|
||||
{
|
||||
data = null;
|
||||
if (!this.caches.TryGetValue(tag, out var cache) || !cache.Type.IsAssignableTo(typeof(T)))
|
||||
return false;
|
||||
|
||||
var callerName = Assembly.GetCallingAssembly().GetName().Name ?? string.Empty;
|
||||
data = cache.Data as T;
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
cache.UserAssemblyNames.Add(callerName);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtain the data for the given <paramref name="tag"/>, if it exists and has the correct type.
|
||||
/// Add the calling assembly to the current consumers if non-null is returned.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type for the requested data - needs to be a reference type.</typeparam>
|
||||
/// <param name="tag">The name for the data cache.</param>
|
||||
/// <returns>The requested data on success or null.</returns>
|
||||
public T? GetData<T>(string tag) where T : class
|
||||
=> TryGetData<T>(tag, out var data) ? data : null;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue