From 604d1b2e8586ce4137dea57949acddb7f336bc49 Mon Sep 17 00:00:00 2001 From: goat Date: Thu, 18 Jul 2024 23:08:29 +0200 Subject: [PATCH] ipc: add custom serializer for IGameObject fixes #1924 --- .../Windows/Data/Widgets/PluginIpcWidget.cs | 44 ++++++++++++++++- .../Plugin/Ipc/Internal/CallGateChannel.cs | 9 +++- .../Converters/GameObjectConverter.cs | 49 +++++++++++++++++++ 3 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 Dalamud/Plugin/Ipc/Internal/Converters/GameObjectConverter.cs diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs index 8004aa4743..d67dfc1037 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs @@ -1,4 +1,7 @@ -using Dalamud.Plugin.Ipc; +using Dalamud.Game.ClientState; +using Dalamud.Game.ClientState.Objects; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Ipc.Internal; using Dalamud.Utility; using ImGuiNET; @@ -14,6 +17,11 @@ internal class PluginIpcWidget : IDataWindowWidget // IPC private ICallGateProvider? ipcPub; private ICallGateSubscriber? ipcSub; + + // IPC + private ICallGateProvider? ipcPubGo; + private ICallGateSubscriber? ipcSubGo; + private string callGateResponse = string.Empty; /// @@ -64,6 +72,28 @@ public void Draw() this.ipcSub.Subscribe(_ => throw new Exception("PONG3")); } + if (this.ipcPubGo == null) + { + this.ipcPubGo = new CallGatePubSub("dataDemo2"); + + this.ipcPubGo.RegisterAction(go => + { + Log.Information("Data action was called: {Name}", go?.Name); + }); + + this.ipcPubGo.RegisterFunc(go => + { + Log.Information("Data func was called: {Name}", go?.Name); + return "test"; + }); + } + + if (this.ipcSubGo == null) + { + this.ipcSubGo = new CallGatePubSub("dataDemo2"); + this.ipcSubGo.Subscribe(go => { Log.Information("GO: {Name}", go.Name); }); + } + if (ImGui.Button("PING")) { this.ipcPub.SendMessage("PING"); @@ -78,7 +108,17 @@ public void Draw() { this.callGateResponse = this.ipcSub.InvokeFunc("button2"); } - + + if (ImGui.Button("Action GO")) + { + this.ipcSubGo.InvokeAction(Service.Get().LocalPlayer); + } + + if (ImGui.Button("Func GO")) + { + this.callGateResponse = this.ipcSubGo.InvokeFunc(Service.Get().LocalPlayer); + } + if (!this.callGateResponse.IsNullOrEmpty()) ImGui.Text($"Response: {this.callGateResponse}"); } diff --git a/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs b/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs index ead4d8df91..ea94103f76 100644 --- a/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs +++ b/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs @@ -4,6 +4,8 @@ using System.Reflection; using Dalamud.Plugin.Ipc.Exceptions; +using Dalamud.Plugin.Ipc.Internal.Converters; + using Newtonsoft.Json; using Serilog; @@ -206,7 +208,10 @@ private IEnumerable GenerateTypes(Type? type) if (obj is null) return null; - var json = JsonConvert.SerializeObject(obj); + var settings = new JsonSerializerSettings(); + settings.Converters.Add(new GameObjectConverter()); + + var json = JsonConvert.SerializeObject(obj, settings); try { @@ -241,7 +246,7 @@ private IEnumerable GenerateTypes(Type? type) try { - return JsonConvert.DeserializeObject(json, type); + return JsonConvert.DeserializeObject(json, type, settings); } catch (Exception ex) { diff --git a/Dalamud/Plugin/Ipc/Internal/Converters/GameObjectConverter.cs b/Dalamud/Plugin/Ipc/Internal/Converters/GameObjectConverter.cs new file mode 100644 index 0000000000..ed69ad7336 --- /dev/null +++ b/Dalamud/Plugin/Ipc/Internal/Converters/GameObjectConverter.cs @@ -0,0 +1,49 @@ +using System.IO; + +using Dalamud.Game.ClientState.Objects; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Utility; + +using Newtonsoft.Json; + +namespace Dalamud.Plugin.Ipc.Internal.Converters; + +/// +/// JSON converter for IGameObject and its derived types. +/// +internal sealed class GameObjectConverter : JsonConverter +{ + /// + public override void WriteJson(JsonWriter writer, IGameObject? value, JsonSerializer serializer) => + writer.WriteValue(value?.Address.ToString()); + + /// + public override IGameObject? ReadJson( + JsonReader reader, + Type objectType, + IGameObject? existingValue, + bool hasExistingValue, + JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + if (reader.TokenType != JsonToken.String) + throw new InvalidDataException("String is expected."); + + if (!nint.TryParse(reader.Value as string, out var v)) + throw new InvalidDataException("Could not parse address."); + + if (!ThreadSafety.IsMainThread) + throw new InvalidOperationException("Cannot send GameObjects from non-main thread over IPC."); + + var ot = Service.Get(); + foreach (var go in ot) + { + if (go.Address == v) + return go; + } + + return ot.CreateObjectReference(v); + } +}