using System.Collections.Concurrent; using System.Collections.Generic; using Dalamud.Logging.Internal; using Dalamud.Networking.Pipes.Rpc; using Dalamud.Utility; namespace Dalamud.Networking.Pipes.Internal; /// /// A service responsible for handling Dalamud URIs and dispatching them accordingly. /// [ServiceManager.EarlyLoadedService] internal class LinkHandlerService : IInternalDisposableService { private readonly ModuleLog log = new("LinkHandler"); // key: namespace (e.g. "plugin" or "PluginInstaller") -> list of handlers private readonly ConcurrentDictionary>> handlers = new(StringComparer.OrdinalIgnoreCase); /// /// Initializes a new instance of the class. /// /// The injected RPC host service. [ServiceManager.ServiceConstructor] public LinkHandlerService(RpcHostService rpcHostService) { rpcHostService.AddMethod("handleLink", this.HandleLinkCall); } /// public void DisposeService() { } /// /// Register a handler for a namespace. All URIs with this namespace will be dispatched to the handler. /// /// The namespace to use for this subscription. /// The command handler. public void Register(string ns, Action handler) { if (string.IsNullOrWhiteSpace(ns)) throw new ArgumentNullException(nameof(ns)); var list = this.handlers.GetOrAdd(ns, _ => []); lock (list) { list.Add(handler); } this.log.Verbose("Registered handler for {Namespace}", ns); } /// /// Unregister a handler. /// /// The namespace to use for this subscription. /// The command handler. public void Unregister(string ns, Action handler) { if (string.IsNullOrWhiteSpace(ns)) return; if (!this.handlers.TryGetValue(ns, out var list)) return; list.RemoveAll(x => x == handler); if (list.Count == 0) this.handlers.TryRemove(ns, out _); this.log.Verbose("Unregistered handler for {Namespace}", ns); } /// /// Dispatch a URI to matching handlers. /// /// The URI to parse and dispatch. public void Dispatch(DalamudUri uri) { this.log.Information("Received URI: {Uri}", uri.ToString()); var ns = uri.Namespace; if (!this.handlers.TryGetValue(ns, out var actions)) return; foreach (var h in actions) { h.InvokeSafely(uri); } } /// /// The RPC-invokable link handler. /// /// A plain-text URI to parse. public void HandleLinkCall(string uri) { if (string.IsNullOrWhiteSpace(uri)) return; var du = DalamudUri.FromUri(uri); this.Dispatch(du); } }