From b963e83cbab333a37e875f93c52fe21c3b25c3f3 Mon Sep 17 00:00:00 2001 From: Critical Impact Date: Fri, 13 Feb 2026 19:04:33 +1000 Subject: [PATCH] Use effective working ID + internal name --- .../Windows/Data/Widgets/DataShareWidget.cs | 12 ++--- Dalamud/Plugin/DalamudPluginInterface.cs | 8 ++-- .../Ipc/Exceptions/DataCacheCreationError.cs | 8 ++-- .../Exceptions/DataCacheTypeMismatchError.cs | 8 ++-- Dalamud/Plugin/Ipc/Internal/DataCache.cs | 44 +++++++++++-------- .../Plugin/Ipc/Internal/DataCachePluginId.cs | 16 +++++++ Dalamud/Plugin/Ipc/Internal/DataShare.cs | 30 ++++++------- 7 files changed, 76 insertions(+), 50 deletions(-) create mode 100644 Dalamud/Plugin/Ipc/Internal/DataCachePluginId.cs diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs index 827d609a7..ebb5b6581 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs @@ -87,7 +87,7 @@ internal class DataShareWidget : IDataWindowWidget try { var dataShare = Service.Get(); - var data2 = dataShare.GetData(name, "DataShareWidget"); + var data2 = dataShare.GetData(name, new DataCachePluginId("DataShareWidget", Guid.Empty)); try { data = Encoding.UTF8.GetBytes( @@ -98,7 +98,7 @@ internal class DataShareWidget : IDataWindowWidget } finally { - dataShare.RelinquishData(name, "DataShareWidget"); + dataShare.RelinquishData(name, new DataCachePluginId("DataShareWidget", Guid.Empty)); } } catch (Exception e) @@ -284,7 +284,7 @@ internal class DataShareWidget : IDataWindowWidget ImGui.TableSetupColumn("Shared Tag"u8); ImGui.TableSetupColumn("Show"u8); - ImGui.TableSetupColumn("Creator Internal Name"u8); + ImGui.TableSetupColumn("Creator"u8); ImGui.TableSetupColumn("#"u8, ImGuiTableColumnFlags.WidthFixed, 30 * ImGuiHelpers.GlobalScale); ImGui.TableSetupColumn("Consumers"u8); ImGui.TableHeadersRow(); @@ -312,9 +312,9 @@ internal class DataShareWidget : IDataWindowWidget this.nextTab = 2 + index; } - this.DrawTextCell(share.CreatorAssembly, null, true); - this.DrawTextCell(share.Users.Length.ToString(), null, true); - this.DrawTextCell(string.Join(", ", share.Users), null, true); + this.DrawTextCell(share.CreatorPluginId.InternalName, () => share.CreatorPluginId.EffectiveWorkingId.ToString(), true); + this.DrawTextCell(share.UserPluginIds.Length.ToString(), null, true); + this.DrawTextCell(string.Join(", ", share.UserPluginIds.Select(c => c.InternalName)), () => string.Join("\n", share.UserPluginIds.Select(c => $"{c.InternalName} ({c.EffectiveWorkingId.ToString()}")), true); } } } diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs index 28affddb4..48f91b250 100644 --- a/Dalamud/Plugin/DalamudPluginInterface.cs +++ b/Dalamud/Plugin/DalamudPluginInterface.cs @@ -227,19 +227,19 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa /// public T GetOrCreateData(string tag, Func dataGenerator) where T : class - => Service.Get().GetOrCreateData(tag, this.plugin.InternalName, dataGenerator); + => Service.Get().GetOrCreateData(tag, new DataCachePluginId(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId), dataGenerator); /// public void RelinquishData(string tag) - => Service.Get().RelinquishData(tag, this.plugin.InternalName); + => Service.Get().RelinquishData(tag, new DataCachePluginId(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId)); /// public bool TryGetData(string tag, [NotNullWhen(true)] out T? data) where T : class - => Service.Get().TryGetData(tag, this.plugin.InternalName, out data); + => Service.Get().TryGetData(tag, new DataCachePluginId(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId), out data); /// public T? GetData(string tag) where T : class - => Service.Get().GetData(tag, this.plugin.InternalName); + => Service.Get().GetData(tag, new DataCachePluginId(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId)); /// public ICallGateProvider GetIpcProvider(string name) diff --git a/Dalamud/Plugin/Ipc/Exceptions/DataCacheCreationError.cs b/Dalamud/Plugin/Ipc/Exceptions/DataCacheCreationError.cs index db095bad9..38b729616 100644 --- a/Dalamud/Plugin/Ipc/Exceptions/DataCacheCreationError.cs +++ b/Dalamud/Plugin/Ipc/Exceptions/DataCacheCreationError.cs @@ -1,3 +1,5 @@ +using Dalamud.Plugin.Ipc.Internal; + namespace Dalamud.Plugin.Ipc.Exceptions; /// @@ -9,11 +11,11 @@ public class DataCacheCreationError : IpcError /// Initializes a new instance of the class. /// /// Tag of the data cache. - /// The assembly name of the caller. + /// The plugin ID of the creating plugin. /// The type expected. /// The thrown exception. - 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) + public DataCacheCreationError(string tag, DataCachePluginId creatorPluginId, Type expectedType, Exception ex) + : base($"The creation of the {expectedType} data cache {tag} initialized by {creatorPluginId.InternalName} ({creatorPluginId.EffectiveWorkingId}) was unsuccessful.", ex) { } } diff --git a/Dalamud/Plugin/Ipc/Exceptions/DataCacheTypeMismatchError.cs b/Dalamud/Plugin/Ipc/Exceptions/DataCacheTypeMismatchError.cs index e5d9cc4db..bfe09b120 100644 --- a/Dalamud/Plugin/Ipc/Exceptions/DataCacheTypeMismatchError.cs +++ b/Dalamud/Plugin/Ipc/Exceptions/DataCacheTypeMismatchError.cs @@ -1,3 +1,5 @@ +using Dalamud.Plugin.Ipc.Internal; + namespace Dalamud.Plugin.Ipc.Exceptions; /// @@ -9,11 +11,11 @@ public class DataCacheTypeMismatchError : IpcError /// Initializes a new instance of the class. /// /// Tag of the data cache. - /// Assembly name of the plugin creating the cache. + /// The plugin ID of the creating plugin. /// The requested type. /// The stored type. - public DataCacheTypeMismatchError(string tag, string creator, Type requestedType, Type actualType) - : base($"Data cache {tag} was requested with type {requestedType}, but {creator} created type {actualType}.") + public DataCacheTypeMismatchError(string tag, DataCachePluginId creatorPluginId, Type requestedType, Type actualType) + : base($"Data cache {tag} was requested with type {requestedType}, but {creatorPluginId.InternalName} ({creatorPluginId.EffectiveWorkingId}) created type {actualType}.") { } } diff --git a/Dalamud/Plugin/Ipc/Internal/DataCache.cs b/Dalamud/Plugin/Ipc/Internal/DataCache.cs index d565c8b35..cbb5bb342 100644 --- a/Dalamud/Plugin/Ipc/Internal/DataCache.cs +++ b/Dalamud/Plugin/Ipc/Internal/DataCache.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Runtime.ExceptionServices; using Dalamud.Plugin.Ipc.Exceptions; @@ -16,12 +17,12 @@ internal readonly struct DataCache /// Name of the data. internal readonly string Tag; - /// The assembly name of the initial creator. - internal readonly string CreatorAssemblyName; + /// The creating plugin ID of this DataCache entry. + internal readonly DataCachePluginId CreatorPluginId; - /// A not-necessarily distinct list of current users. + /// A distinct list of plugin IDs that are using this data. /// Also used as a reference count tracker. - internal readonly List UserAssemblyNames; + internal readonly List UserPluginIds; /// The type the data was registered as. internal readonly Type Type; @@ -33,14 +34,14 @@ internal readonly struct DataCache /// Initializes a new instance of the struct. /// /// Name of the data. - /// The assembly name of the initial creator. + /// The internal name and effective working ID of the creating plugin. /// A reference to data. /// The type of the data. - public DataCache(string tag, string creatorAssemblyName, object? data, Type type) + public DataCache(string tag, DataCachePluginId creatorPluginId, object? data, Type type) { this.Tag = tag; - this.CreatorAssemblyName = creatorAssemblyName; - this.UserAssemblyNames = []; + this.CreatorPluginId = creatorPluginId; + this.UserPluginIds = []; this.Data = data; this.Type = type; } @@ -49,40 +50,40 @@ internal readonly struct DataCache /// Creates a new instance of the struct, using the given data generator function. /// /// The name for the data cache. - /// The assembly name of the initial creator. + /// The internal name and effective working ID of the creating plugin. /// The function that generates the data if it does not already exist. /// The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin. /// The new instance of . - public static DataCache From(string tag, string creatorAssemblyName, Func dataGenerator) + public static DataCache From(string tag, DataCachePluginId creatorPluginId, Func dataGenerator) where T : class { try { - var result = new DataCache(tag, creatorAssemblyName, dataGenerator.Invoke(), typeof(T)); + var result = new DataCache(tag, creatorPluginId, dataGenerator.Invoke(), typeof(T)); Log.Verbose( "[{who}] Created new data for [{Tag:l}] for creator {Creator:l}.", nameof(DataShare), tag, - creatorAssemblyName); + creatorPluginId); return result; } catch (Exception e) { throw ExceptionDispatchInfo.SetCurrentStackTrace( - new DataCacheCreationError(tag, creatorAssemblyName, typeof(T), e)); + new DataCacheCreationError(tag, creatorPluginId, typeof(T), e)); } } /// /// Attempts to fetch the data. /// - /// The name of the caller assembly. + /// The calling plugin ID. /// The value, if succeeded. /// The exception, if failed. /// Desired type of the data. /// true on success. public bool TryGetData( - string callerName, + DataCachePluginId callingPluginId, [NotNullWhen(true)] out T? value, [NotNullWhen(false)] out Exception? ex) where T : class @@ -98,16 +99,21 @@ internal readonly struct DataCache value = data; ex = null; - // Register the access history - lock (this.UserAssemblyNames) - this.UserAssemblyNames.Add(callerName); + // Register the access history. The effective working ID is unique per plugin and persists between reloads, so only add it once. + lock (this.UserPluginIds) + { + if (this.UserPluginIds.All(c => c.EffectiveWorkingId != callingPluginId.EffectiveWorkingId)) + { + this.UserPluginIds.Add(callingPluginId); + } + } return true; default: value = null; ex = ExceptionDispatchInfo.SetCurrentStackTrace( - new DataCacheTypeMismatchError(this.Tag, this.CreatorAssemblyName, typeof(T), this.Type)); + new DataCacheTypeMismatchError(this.Tag, this.CreatorPluginId, typeof(T), this.Type)); return false; } } diff --git a/Dalamud/Plugin/Ipc/Internal/DataCachePluginId.cs b/Dalamud/Plugin/Ipc/Internal/DataCachePluginId.cs new file mode 100644 index 000000000..c68dc7c06 --- /dev/null +++ b/Dalamud/Plugin/Ipc/Internal/DataCachePluginId.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.ExceptionServices; + +using Dalamud.Plugin.Ipc.Exceptions; + +using Serilog; + +namespace Dalamud.Plugin.Ipc.Internal; + +/// +/// Stores the internal name and effective working ID of a plugin accessing datashare. +/// +/// The internal name of the plugin. +/// The effective working ID of the plugin. +public record DataCachePluginId(string InternalName, Guid EffectiveWorkingId); diff --git a/Dalamud/Plugin/Ipc/Internal/DataShare.cs b/Dalamud/Plugin/Ipc/Internal/DataShare.cs index f71d8d2c2..ffad4876e 100644 --- a/Dalamud/Plugin/Ipc/Internal/DataShare.cs +++ b/Dalamud/Plugin/Ipc/Internal/DataShare.cs @@ -33,23 +33,23 @@ internal class DataShare : IServiceType /// /// The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin. /// The name for the data cache. - /// The name of the caller. + /// The ID of the calling plugin. /// The function that generates the data if it does not already exist. /// Either the existing data for or the data generated by . /// Thrown if a cache for exists, but contains data of a type not assignable to . /// Thrown if the stored data for a cache is null. /// Thrown if throws an exception or returns null. - public T GetOrCreateData(string tag, string callerName, Func dataGenerator) + public T GetOrCreateData(string tag, DataCachePluginId callingPluginId, Func dataGenerator) where T : class { Lazy cacheLazy; lock (this.caches) { if (!this.caches.TryGetValue(tag, out cacheLazy)) - this.caches[tag] = cacheLazy = new(() => DataCache.From(tag, callerName, dataGenerator)); + this.caches[tag] = cacheLazy = new(() => DataCache.From(tag, callingPluginId, dataGenerator)); } - return cacheLazy.Value.TryGetData(callerName, out var value, out var ex) ? value : throw ex; + return cacheLazy.Value.TryGetData(callingPluginId, out var value, out var ex) ? value : throw ex; } /// @@ -57,8 +57,8 @@ internal class DataShare : IServiceType /// 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. /// /// The name for the data cache. - /// The name of the caller. - public void RelinquishData(string tag, string callerName) + /// The ID of the calling plugin. + public void RelinquishData(string tag, DataCachePluginId callingPluginId) { DataCache cache; lock (this.caches) @@ -67,7 +67,7 @@ internal class DataShare : IServiceType return; cache = cacheLazy.Value; - if (!cache.UserAssemblyNames.Remove(callerName) || cache.UserAssemblyNames.Count > 0) + if (!cache.UserPluginIds.Remove(callingPluginId) || cache.UserPluginIds.Count > 0) return; if (!this.caches.Remove(tag)) return; @@ -97,10 +97,10 @@ internal class DataShare : IServiceType /// /// The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin. /// The name for the data cache. - /// The name of the caller. + /// The ID of the calling plugin. /// The requested data on success, null otherwise. /// True if the requested data exists and is assignable to the requested type. - public bool TryGetData(string tag, string callerName, [NotNullWhen(true)] out T? data) + public bool TryGetData(string tag, DataCachePluginId callingPluginId, [NotNullWhen(true)] out T? data) where T : class { data = null; @@ -111,7 +111,7 @@ internal class DataShare : IServiceType return false; } - return cacheLazy.Value.TryGetData(callerName, out data, out _); + return cacheLazy.Value.TryGetData(callingPluginId, out data, out _); } /// @@ -120,12 +120,12 @@ internal class DataShare : IServiceType /// /// The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin. /// The name for the data cache. - /// The name of the caller. + /// The ID of the calling plugin. /// The requested data. /// Thrown if is not registered. /// Thrown if a cache for exists, but contains data of a type not assignable to . /// Thrown if the stored data for a cache is null. - public T GetData(string tag, string callerName) + public T GetData(string tag, DataCachePluginId callingPluginId) where T : class { Lazy cacheLazy; @@ -135,19 +135,19 @@ internal class DataShare : IServiceType throw new KeyNotFoundException($"The data cache [{tag}] is not registered."); } - return cacheLazy.Value.TryGetData(callerName, out var value, out var ex) ? value : throw ex; + return cacheLazy.Value.TryGetData(callingPluginId, out var value, out var ex) ? value : throw ex; } /// /// Obtain a read-only list of data shares. /// /// All currently subscribed tags, their creator names and all their users. - internal IEnumerable<(string Tag, string CreatorAssembly, string[] Users)> GetAllShares() + internal IEnumerable<(string Tag, DataCachePluginId CreatorPluginId, DataCachePluginId[] UserPluginIds)> GetAllShares() { lock (this.caches) { return this.caches.Select( - kvp => (kvp.Key, kvp.Value.Value.CreatorAssemblyName, kvp.Value.Value.UserAssemblyNames.ToArray())); + kvp => (kvp.Key, kvp.Value.Value.CreatorPluginId, kvp.Value.Value.UserPluginIds.ToArray())); } } }