namespace Aeshnidae.Mule; /// /// The Companion wire contract, shared by every mod that answers the Aelrynth /// Companion (a Decal plugin for the retail client). A mod cannot reference another /// mod's types, so each provider keeps a verbatim copy of this file as /// CompanionFeed.cs in its own namespace; keep them identical. /// /// A data command (its name ends in "-data") answers with ONE chat line: /// /// ~ael1 {"t":"inspect", ...} /// /// the prefix, a space, then a JSON object whose "t" says what it is. The plugin /// reads the line and hides it from the chat window; the numbers in it are the same /// ones the matching human command prints (/inspect, /mastery), so every panel still /// has a chat command underneath it and nothing becomes plugin-only. /// /// Why a plain chat line and not the HUD feed's chat type 0x21: that type is above /// every type the retail client knows, and a retail client is exactly who this is for. /// Broadcast is what SendMessage uses; Decal sees it and can eat it before it is drawn. /// Nothing is sent unless the client asked, so a player without the plugin only ever /// sees a line of it by typing a -data command themselves. /// /// Bump the "1" in the prefix only for a change an old plugin would misread; adding a /// field is not one (the plugin ignores fields it does not know). /// public static class CompanionFeed { public const string Prefix = "~ael1 "; // Fully qualified on purpose: this file is copied into mods with different usings. private static readonly System.Text.Json.JsonSerializerOptions Json = new() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping, DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull, }; public static void Send(Session? session, object data) { if (session is null) return; var text = Prefix + System.Text.Json.JsonSerializer.Serialize(data, Json); session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Broadcast)); } }