Consume functionality from other plugins using IPC subscribers
CallGate subscribers allow your plugin to call functions exposed by other plugins and receive notifications from them. This enables your plugin to integrate with and extend the functionality of other plugins.
using Dalamud.Plugin.Ipc.Exceptions;try{ var result = this.subscriber.InvokeFunc("input");}catch (IpcNotReadyError ex){ // The provider hasn't registered a function yet // The provider plugin may not be installed or loaded PluginLog.Warning($"IPC not ready: {ex.Message}");}catch (IpcTypeMismatchError ex){ // Type parameters don't match the provider's registration PluginLog.Error($"Type mismatch: {ex.Message}");}catch (IpcLengthMismatchError ex){ // Wrong number of parameters PluginLog.Error($"Parameter count mismatch: {ex.Message}");}catch (Exception ex){ // Provider threw an exception during execution PluginLog.Error(ex, "IPC call failed");}
// Define matching type (or use a shared assembly)public class CharacterInfo{ public string Name { get; set; } public uint Level { get; set; } public uint ClassJob { get; set; } public string WorldName { get; set; }}public class ComplexIpcConsumer{ private ICallGateSubscriber<CharacterInfo>? infoSubscriber; public void Initialize(IDalamudPluginInterface pluginInterface) { this.infoSubscriber = pluginInterface.GetIpcSubscriber<CharacterInfo>( "MyPlugin.GetCharacterInfo"); } public void DisplayInfo() { try { var info = this.infoSubscriber?.InvokeFunc(); if (info != null) { PluginLog.Information($"Character: {info.Name}"); PluginLog.Information($"Level: {info.Level}"); PluginLog.Information($"World: {info.WorldName}"); } } catch (Exception ex) { PluginLog.Error(ex, "Failed to get character info"); } }}
Complex types are automatically deserialized from JSON. The types don’t need to be identical - compatible types with matching properties will work.
// Plugin Athis.subscriber.Subscribe(msg => PluginLog.Information("Plugin A: {0}", msg));// Plugin Bthis.subscriber.Subscribe(msg => PluginLog.Information("Plugin B: {0}", msg));// When provider sends a message, both will receive it
Notifications are delivered sequentially in the order subscriptions were registered.
public class VersionedApi{ private ICallGateSubscriber<string, int>? subscriberV2; private ICallGateSubscriber<string>? subscriberV1; public void Initialize(IDalamudPluginInterface pluginInterface) { // Try new API first this.subscriberV2 = pluginInterface.GetIpcSubscriber<string, int>( "MyPlugin.GetData.v2"); // Fallback to old API if (!this.subscriberV2.HasFunction) { this.subscriberV1 = pluginInterface.GetIpcSubscriber<string>( "MyPlugin.GetData"); } } public int GetData() { // Use new API if available if (this.subscriberV2?.HasFunction == true) { return this.subscriberV2.InvokeFunc(); } // Fallback to old API with conversion if (this.subscriberV1?.HasFunction == true) { var oldResult = this.subscriberV1.InvokeFunc(); return ConvertOldFormat(oldResult); } return 0; }}
#if DEBUGpublic class IpcTestStub{ public void SetupTestProvider(IDalamudPluginInterface pluginInterface) { // Create a test provider for development var provider = pluginInterface.GetIpcProvider<string, int>( "TestPlugin.GetLength"); provider.RegisterFunc(input => input?.Length ?? 0); }}#endif