using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using LongChat.Config; using LongChat.Diagnostics; using LongChat.Models; using LongChat.Patches; using LongChat.Protocol; using LongChat.Services; using Microsoft.CodeAnalysis; using ProjectM; using ProjectM.Network; using ProjectM.UI; using TMPro; using Unity.Entities; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("LongChat")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("LongChat")] [assembly: AssemblyTitle("LongChat")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LongChat { internal static class LongChatRuntime { private static SendQueueService _sendQueue; private static AssemblyService _assemblyService; private static AssemblyService _overheadAssemblyService; private static LongChatConfig _config; private static HUDChatWindow? _chatWindow; private static bool _incomingReconstructionEnabled; private static bool _overheadReconstructionEnabled; private static bool _bypassSubmissionRouting; private static bool _renderingReconstructedMessage; internal static bool IncomingReconstructionEnabled => _incomingReconstructionEnabled; internal static int EffectiveFrameByteLimit => TransportLimits.EffectiveFrameByteLimit(_config.FrameByteLimit.Value); private static TimeSpan SendDelay => TimeSpan.FromMilliseconds(Math.Max(0, _config.SendDelayMilliseconds.Value)); private static TimeSpan AcknowledgementTimeout => TimeSpan.FromMilliseconds(Math.Max(250, _config.AcknowledgementTimeoutMilliseconds.Value)); public static void Initialize(LongChatConfig config) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Expected O, but got Unknown _config = config; _sendQueue = new SendQueueService(); _assemblyService = new AssemblyService(Math.Max(1, config.MaximumChunksPerMessage.Value), Math.Max(1, config.MaximumActiveAssemblies.Value), Math.Max(1024, config.MaximumBufferedBytes.Value)); _overheadAssemblyService = new AssemblyService(Math.Max(1, config.MaximumChunksPerMessage.Value), Math.Max(1, config.MaximumActiveAssemblies.Value), Math.Max(1024, config.MaximumBufferedBytes.Value)); int num = TransportLimits.EffectiveFrameByteLimit(config.FrameByteLimit.Value); if (num != config.FrameByteLimit.Value) { ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(72, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Configured FrameByteLimit "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(config.FrameByteLimit.Value); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" is outside the supported range; using "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" bytes."); } log.LogWarning(val); } } public static void ConfigureIncomingReconstruction(bool enabled) { _incomingReconstructionEnabled = enabled; if (!enabled) { _assemblyService.Clear(); } } public static void ConfigureOverheadReconstruction(bool enabled) { _overheadReconstructionEnabled = enabled; if (!enabled) { _overheadAssemblyService.Clear(); } } public static bool IsChannelEnabled(ChatMessageType messageType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected I4, but got Unknown switch ((int)messageType) { case 0: return _config.EnableGlobal.Value; case 2: return _config.EnableClan.Value; case 1: case 4: return _config.EnableLocal.Value; default: return false; } } public static bool IsCommand(string text) { if (_config.DisableSplittingForCommands.Value && text.Length > 0) { return _config.CommandPrefixes.Value.IndexOf(text[0]) >= 0; } return false; } public static bool HandleInputEndEdit(ClientChatSystem chatSystem, string? submittedText) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected I4, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected I4, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) if (_bypassSubmissionRouting) { return true; } string text = submittedText ?? string.Empty; ChatMessageType defaultMode = chatSystem._DefaultMode; DiagnosticLog.Stage("SUBMISSION_ROUTED", $"length={text.Length} displayMode={(int)chatSystem._ChatMode} sendType={(int)defaultMode}"); if (!_config.Enabled.Value || string.IsNullOrEmpty(text) || !ChatSendTypeSupport.IsSupported(defaultMode) || !IsChannelEnabled(defaultMode) || !TransportLimits.ExceedsFrameLimit(text, _config.FrameByteLimit.Value)) { return true; } EntityManager entityManager = ((ComponentSystemBase)chatSystem).EntityManager; if (IsCommand(text)) { DisplayLocalWarning(entityManager, "LongChat: Commands cannot be split into multiple chat messages."); CompleteVanillaSubmission(chatSystem); return false; } if (!TrySendMultipart(chatSystem, text, defaultMode, out string error)) { CompleteVanillaSubmission(chatSystem); DisplayLocalWarning(entityManager, error ?? "LongChat: Unable to send the message."); RestoreInputText(text); } return false; } private static bool TrySendMultipart(ClientChatSystem chatSystem, string text, ChatMessageType messageType, out string? error) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) error = null; try { IReadOnlyList readOnlyList = FrameEncoder.Encode(text, EffectiveFrameByteLimit, Math.Clamp(_config.MaximumChunksPerMessage.Value, 1, 999)); if (!FrameParser.TryParse(readOnlyList[0], out var frame)) { error = "LongChat: Unable to parse the encoded first frame."; return false; } OutgoingMessage message = new OutgoingMessage(((ComponentSystemBase)chatSystem).EntityManager, messageType, frame.MessageId, text, readOnlyList, delegate(string frame2) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) SubmitInternalFrame(chatSystem, messageType, frame2); }); DateTime utcNow = DateTime.UtcNow; if (!_sendQueue.TryEnqueue(message, Math.Max(1, _config.MaximumQueuedMessages.Value))) { error = "LongChat: The outgoing message queue is full."; return false; } TickSendQueue(utcNow); DiagnosticLog.Stage("MULTIPART_ACCEPTED", $"length={text.Length} frames={readOnlyList.Count} queue={_sendQueue.Count}"); return true; } catch (Exception ex) { ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(29, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unable to send long message: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } log.LogWarning(val); _sendQueue.Clear(); error = "LongChat: " + ex.Message; return false; } } public static bool HandleFinalFilteredMessage(__c__DisplayClass55_1 callback, string userNameFiltered) { //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Expected I4, but got Unknown //IL_0161: Unknown result type (might be due to invalid IL or missing references) __c__DisplayClass55_0 field_Public___c__DisplayClass55_0_ = callback.field_Public___c__DisplayClass55_0_0; string text = callback.filteredText ?? string.Empty; string userNameFiltered2 = userNameFiltered ?? string.Empty; string timeStamp = ((field_Public___c__DisplayClass55_0_ != null) ? field_Public___c__DisplayClass55_0_.timeStamp : null) ?? string.Empty; if (!_config.Enabled.Value || !_incomingReconstructionEnabled || _renderingReconstructedMessage || field_Public___c__DisplayClass55_0_ == null || !FrameParser.TryParse(text, out var frame)) { return true; } DiagnosticLog.Stage("INCOMING_FRAME", $"index={frame.Index} total={frame.Total} payload={frame.Payload.Length} raw={text.Length}"); if (field_Public___c__DisplayClass55_0_.isSelf && _sendQueue.Acknowledge(frame.MessageId, frame.Index, DateTime.UtcNow, SendDelay)) { DiagnosticLog.Stage("FRAME_ACKNOWLEDGED", $"index={frame.Index} total={frame.Total} queue={_sendQueue.Count}"); } string senderId = $"{field_Public___c__DisplayClass55_0_.fromUser}:{field_Public___c__DisplayClass55_0_.fromCharacterId}"; AssemblyKey key = new AssemblyKey(senderId, (int)field_Public___c__DisplayClass55_0_.messageType, frame.MessageId); IncomingContext context = new IncomingContext(callback, field_Public___c__DisplayClass55_0_.messageType, userNameFiltered2, timeStamp, field_Public___c__DisplayClass55_0_.showTimeStamp, field_Public___c__DisplayClass55_0_.isSelf, field_Public___c__DisplayClass55_0_.isUserAdmin); AssemblyAddResult assemblyAddResult = _assemblyService.Add(key, frame, text, context, DateTime.UtcNow); switch (assemblyAddResult.Status) { case AssemblyAddStatus.Complete: callback.filteredText = assemblyAddResult.Text; return true; case AssemblyAddStatus.Pending: case AssemblyAddStatus.Duplicate: return false; case AssemblyAddStatus.Conflict: callback.filteredText = string.Join(" ", assemblyAddResult.RawFrames); return true; default: return true; } } public unsafe static bool HandleOverheadChatMessage(NetworkId networkId, ref string text) { string text2 = text ?? string.Empty; if (!_config.Enabled.Value || !_config.EnableOverheadReconstruction.Value || !_overheadReconstructionEnabled || !FrameParser.TryParse(text2, out var frame)) { return true; } DiagnosticLog.Stage("OVERHEAD_FRAME", $"index={frame.Index} total={frame.Total} payload={frame.Payload.Length} raw={text2.Length}"); string senderId = ((object)(*(NetworkId*)(&networkId))/*cast due to .constrained prefix*/).ToString(); AssemblyKey key = new AssemblyKey(senderId, 0, frame.MessageId); AssemblyAddResult assemblyAddResult = _overheadAssemblyService.Add(key, frame, text2, null, DateTime.UtcNow); switch (assemblyAddResult.Status) { case AssemblyAddStatus.Complete: text = FormatOverheadText(assemblyAddResult.Text); DiagnosticLog.Stage("OVERHEAD_COMPLETE", $"length={assemblyAddResult.Text.Length} rendered={text.Length}"); return true; case AssemblyAddStatus.Pending: case AssemblyAddStatus.Duplicate: return false; case AssemblyAddStatus.Conflict: text = FormatOverheadText(string.Join(" ", assemblyAddResult.RawFrames)); DiagnosticLog.Stage("OVERHEAD_CONFLICT", $"frames={assemblyAddResult.RawFrames.Count} rendered={text.Length}"); return true; default: return true; } } public static void ObserveChatWindow(HUDChatWindow? chatWindow) { if (chatWindow != null) { _chatWindow = chatWindow; ApplyInputLimit(chatWindow); } } public static void ApplyInputLimit(HUDChatWindow? chatWindow) { if (_config.Enabled.Value && ((chatWindow != null) ? chatWindow.ChatInputField : null) != null) { int num = Math.Max(1, _config.MaxInputCharacters.Value); int characterLimit = chatWindow.ChatInputField.characterLimit; chatWindow.ChatInputField.characterLimit = num; if (characterLimit != num) { DiagnosticLog.Stage("INPUT_LIMIT_APPLIED", $"before={characterLimit} after={num}"); } } } public static void Tick() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown if (!_config.Enabled.Value) { return; } if (_chatWindow != null && (Object)(object)_chatWindow == (Object)null) { _chatWindow = null; } else if (_chatWindow != null) { ApplyInputLimit(_chatWindow); } try { TickSendQueue(DateTime.UtcNow); } catch (Exception ex) { ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(34, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Sending a multipart frame failed: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } log.LogWarning(val); OutgoingMessage outgoingMessage = _sendQueue.FailActive(); if ((object)outgoingMessage != null) { HandleTransportFailure(outgoingMessage, "LongChat: Multipart transport failed; the original text was restored."); } } if (IncomingReconstructionEnabled) { foreach (ExpiredAssembly item in _assemblyService.Expire(DateTime.UtcNow, TimeSpan.FromMilliseconds(Math.Max(250, _config.AssemblyTimeoutMilliseconds.Value)))) { if (item.Context is IncomingContext context) { RenderThroughFinalCallback(context, "[Incomplete multipart message] " + item.PartialText); } } } if (!_overheadReconstructionEnabled) { return; } foreach (ExpiredAssembly item2 in _overheadAssemblyService.Expire(DateTime.UtcNow, TimeSpan.FromMilliseconds(Math.Max(250, _config.AssemblyTimeoutMilliseconds.Value)))) { DiagnosticLog.Stage("OVERHEAD_EXPIRED", $"partial={item2.PartialText.Length}"); } } public static void DisplayLocalWarning(EntityManager entityManager, string warning) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) DisplayLocalMessage(entityManager, warning, (ServerChatMessageType)6); } private static void DisplayLocalMessage(EntityManager entityManager, string text, ServerChatMessageType messageType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown try { ClientSystemChatUtils.AddLocalMessage(entityManager, text, messageType); } catch (Exception ex) { ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(33, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unable to display local warning: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } log.LogWarning(val); } } public static void ClearSession(string? reason = null) { if (_sendQueue.Count > 0) { DiagnosticLog.Stage("QUEUE_CLEARED", string.Format("count={0} reason={1}", _sendQueue.Count, reason ?? "session reset")); } _sendQueue.Clear(); _assemblyService.Clear(); _overheadAssemblyService.Clear(); } private static void SendFrame(OutgoingMessage message, string frame) { message.SubmitFrame(frame); } private static void SubmitInternalFrame(ClientChatSystem chatSystem, ChatMessageType messageType, string frame) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected I4, but got Unknown FrameParser.TryParse(frame, out var frame2); ChatMessageType defaultMode = chatSystem._DefaultMode; try { _bypassSubmissionRouting = true; chatSystem._DefaultMode = messageType; DiagnosticLog.Stage("INTERNAL_FRAME_SUBMIT", $"frame={frame2.Index}/{frame2.Total} length={frame.Length} sendType={(int)messageType} queue={_sendQueue.Count}"); chatSystem._OnInputEndEdit(frame); } finally { chatSystem._DefaultMode = defaultMode; _bypassSubmissionRouting = false; } } private static void TickSendQueue(DateTime now) { _sendQueue.Tick(now, AcknowledgementTimeout, SendFrame, delegate(OutgoingMessage message) { HandleTransportFailure(message, "LongChat: The server did not acknowledge the multipart message; the original text was restored."); }); } private static void HandleTransportFailure(OutgoingMessage message, string warning) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) DisplayLocalWarning(message.EntityManager, warning); RestoreInputText(message.OriginalText); } private static void RestoreInputText(string text) { HUDChatWindow? chatWindow = _chatWindow; TMP_InputField val = ((chatWindow != null) ? chatWindow.ChatInputField : null); if (val == null) { Plugin.Log.LogWarning((object)"Unable to restore long chat text because the chat input is unavailable."); return; } val.text = text; val.MoveTextEnd(false); DiagnosticLog.Stage("INPUT_RESTORED", $"length={text.Length}"); } private static void CompleteVanillaSubmission(ClientChatSystem chatSystem) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown try { _bypassSubmissionRouting = true; chatSystem._OnInputEndEdit(string.Empty); } catch (Exception ex) { ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(41, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unable to complete vanilla chat cleanup: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } log.LogWarning(val); HUDChatWindow? chatWindow = _chatWindow; if (chatWindow != null) { chatWindow.ClearInputText(); } } finally { _bypassSubmissionRouting = false; } } private static void RenderThroughFinalCallback(IncomingContext context, string text) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) string filteredText = context.Callback.filteredText; try { _renderingReconstructedMessage = true; context.Callback.filteredText = text; context.Callback._FilterChatMessages_b__1(context.UserNameFiltered); } catch (Exception ex) { ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(37, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unable to render multipart fallback: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.GetBaseException().Message); } log.LogWarning(val); DisplayLocalMessage(((ComponentSystemBase)context.Callback.field_Public___c__DisplayClass55_0_0.__4__this).EntityManager, text, context.MessageType); } finally { context.Callback.filteredText = filteredText; _renderingReconstructedMessage = false; } } private static string FormatOverheadText(string text) { return OverheadTextFormatter.Wrap(OverheadTextFormatter.Limit(text, _config.MaxOverheadCharacters.Value), _config.MaxOverheadLineCharacters.Value, _config.EnableOverheadWrapping.Value); } } [BepInPlugin("com.yssena.longchat", "LongChat", "1.0.0")] public sealed class Plugin : BasePlugin { private Harmony? _harmony; internal static ManualLogSource Log { get; private set; } internal static LongChatConfig Settings { get; private set; } public override void Load() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown Log = ((BasePlugin)this).Log; Settings = new LongChatConfig(((BasePlugin)this).Config); LongChatRuntime.Initialize(Settings); _harmony = new Harmony("com.yssena.longchat"); PatchFeatures(_harmony); bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val; if (Settings.LogPatchDiscovery.Value) { foreach (MethodBase patchedMethod in _harmony.GetPatchedMethods()) { ManualLogSource log = Log; val = new BepInExInfoLogInterpolatedStringHandler(9, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Patched "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(patchedMethod.DeclaringType?.FullName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("."); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(patchedMethod.Name); } log.LogInfo(val); } } ManualLogSource log2 = Log; val = new BepInExInfoLogInterpolatedStringHandler(9, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("LongChat"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("1.0.0"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded."); } log2.LogInfo(val); DiagnosticLog.Stage("STARTUP", "version=1.0.0"); } private static void PatchFeatures(Harmony harmony) { TryPatch(harmony, "chat input expansion", ResolveMethod(typeof(ClientChatSystem), "InitOrUpdateChatWindow", typeof(bool)), null, typeof(ChatInputPatch).GetMethod("Postfix", BindingFlags.Static | BindingFlags.NonPublic)); TryPatch(harmony, "long chat submission routing", ResolveMethod(typeof(ClientChatSystem), "_OnInputEndEdit", typeof(string)), typeof(ChatEndEditPatch).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic)); TryPatch(harmony, "chat update scheduler", ResolveMethod(typeof(ClientChatSystem), "OnUpdate"), null, typeof(ChatUpdatePatch).GetMethod("Postfix", BindingFlags.Static | BindingFlags.NonPublic)); TryPatch(harmony, "client session cleanup", ResolveMethod(typeof(ClientBootstrapSystem), "OnDestroy"), typeof(ClientSessionPatch).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic)); LongChatRuntime.ConfigureIncomingReconstruction(TryPatch(harmony, "final multipart reconstruction", ResolveMethod(typeof(__c__DisplayClass55_1), "_FilterChatMessages_b__1", typeof(string)), typeof(FinalChatFilterPatch).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic))); LongChatRuntime.ConfigureOverheadReconstruction(TryPatch(harmony, "overhead multipart reconstruction", ResolveMethod(typeof(ClientChatSystem), "CreateLocalChatMessageSCT", typeof(NetworkIdLookupMap).MakeByRefType(), typeof(NetworkId), typeof(string)), typeof(OverheadChatPatch).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic))); } private static MethodInfo? ResolveMethod(Type type, string name, params Type[] parameterTypes) { return type.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null); } private static bool TryPatch(Harmony harmony, string feature, MethodInfo? target, MethodInfo? prefix = null, MethodInfo? postfix = null) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown bool flag = default(bool); if ((object)target == null) { ManualLogSource log = Log; BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(57, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("LongChat disabled "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(feature); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": compatible game method was not found."); } log.LogWarning(val); return false; } try { harmony.Patch((MethodBase)target, ((object)prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(prefix), ((object)postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(postfix), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ManualLogSource log2 = Log; BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(18, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("LongChat enabled "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(feature); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("."); } log2.LogInfo(val2); return true; } catch (Exception ex) { ManualLogSource log3 = Log; bool flag2 = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(20, 2, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("LongChat disabled "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(feature); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.GetBaseException().Message); } log3.LogWarning(val); return false; } } public override bool Unload() { LongChatRuntime.ClearSession("plugin unloaded"); Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } return true; } } internal static class PluginInfo { public const string Guid = "com.yssena.longchat"; public const string Name = "LongChat"; public const string Version = "1.0.0"; } } namespace LongChat.Services { public enum AssemblyAddStatus { Pending, Complete, Duplicate, Conflict, Rejected } public sealed record AssemblyAddResult(AssemblyAddStatus Status, string? Text = null, IReadOnlyList? RawFrames = null); public sealed record ExpiredAssembly(AssemblyKey Key, string PartialText, IReadOnlyList RawFrames, object? Context); public sealed class AssemblyService { private sealed class State { public int ExpectedChunks { get; } public object? Context { get; } public Dictionary Payloads { get; } = new Dictionary(); public List RawFrames { get; } = new List(); public int Bytes { get; set; } public DateTime LastReceived { get; set; } public State(int expectedChunks, object? context, DateTime now) { ExpectedChunks = expectedChunks; Context = context; LastReceived = now; } } private readonly Dictionary _assemblies = new Dictionary(); private readonly int _maximumChunks; private readonly int _maximumActiveAssemblies; private readonly int _maximumBufferedBytes; private int _bufferedBytes; public AssemblyService(int maximumChunks, int maximumActiveAssemblies, int maximumBufferedBytes) { _maximumChunks = maximumChunks; _maximumActiveAssemblies = maximumActiveAssemblies; _maximumBufferedBytes = maximumBufferedBytes; } public AssemblyAddResult Add(AssemblyKey key, ParsedFrame frame, string rawFrame, object? context, DateTime now) { if (frame.Total > _maximumChunks) { return new AssemblyAddResult(AssemblyAddStatus.Rejected); } if (!_assemblies.TryGetValue(key, out State state)) { if (_assemblies.Count >= _maximumActiveAssemblies) { RemoveOldest(); } state = new State(frame.Total, context, now); _assemblies.Add(key, state); } else if (state.ExpectedChunks != frame.Total) { string[] rawFrames = state.RawFrames.Append(rawFrame).ToArray(); Remove(key, state); return new AssemblyAddResult(AssemblyAddStatus.Conflict, null, rawFrames); } if (state.Payloads.TryGetValue(frame.Index, out string value)) { if (value == frame.Payload) { state.LastReceived = now; return new AssemblyAddResult(AssemblyAddStatus.Duplicate); } string[] rawFrames2 = state.RawFrames.Append(rawFrame).ToArray(); Remove(key, state); return new AssemblyAddResult(AssemblyAddStatus.Conflict, null, rawFrames2); } int byteCount = Encoding.UTF8.GetByteCount(frame.Payload); while (_assemblies.Count > 0 && _bufferedBytes + byteCount > _maximumBufferedBytes) { KeyValuePair? keyValuePair = ((IEnumerable>)(from pair in _assemblies where !pair.Key.Equals(key) orderby pair.Value.LastReceived select pair)).Select((Func, KeyValuePair?>)((KeyValuePair pair) => pair)).FirstOrDefault(); if (!keyValuePair.HasValue) { Remove(key, state); return new AssemblyAddResult(AssemblyAddStatus.Rejected); } Remove(keyValuePair.Value.Key, keyValuePair.Value.Value); } if (byteCount > _maximumBufferedBytes) { return new AssemblyAddResult(AssemblyAddStatus.Rejected); } state.Payloads.Add(frame.Index, frame.Payload); state.RawFrames.Add(rawFrame); state.Bytes += byteCount; state.LastReceived = now; _bufferedBytes += byteCount; if (state.Payloads.Count != state.ExpectedChunks) { return new AssemblyAddResult(AssemblyAddStatus.Pending); } string text = string.Concat(from index in Enumerable.Range(1, state.ExpectedChunks) select state.Payloads[index]); Remove(key, state); return new AssemblyAddResult(AssemblyAddStatus.Complete, text); } public IReadOnlyList Expire(DateTime now, TimeSpan timeout) { ExpiredAssembly[] array = (from pair in _assemblies where now - pair.Value.LastReceived >= timeout select new ExpiredAssembly(pair.Key, string.Concat(from chunk in pair.Value.Payloads orderby chunk.Key select chunk.Value), pair.Value.RawFrames.ToArray(), pair.Value.Context)).ToArray(); ExpiredAssembly[] array2 = array; foreach (ExpiredAssembly expiredAssembly in array2) { if (_assemblies.TryGetValue(expiredAssembly.Key, out State value)) { Remove(expiredAssembly.Key, value); } } return array; } public void Clear() { _assemblies.Clear(); _bufferedBytes = 0; } private void RemoveOldest() { KeyValuePair keyValuePair = _assemblies.OrderBy, DateTime>((KeyValuePair pair) => pair.Value.LastReceived).First(); Remove(keyValuePair.Key, keyValuePair.Value); } private void Remove(AssemblyKey key, State state) { _assemblies.Remove(key); _bufferedBytes -= state.Bytes; } } internal static class ChatSendTypeSupport { public static bool IsSupported(ChatMessageType messageType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)messageType <= 2 || (int)messageType == 4) { return true; } return false; } } internal sealed class SendQueueService { private sealed class QueuedMessage { public OutgoingMessage Message { get; } public int NextFrame { get; set; } public int AwaitingIndex { get; set; } public DateTime SentAt { get; set; } public DateTime NextSendAt { get; set; } public QueuedMessage(OutgoingMessage message) { Message = message; } } private readonly Queue _messages = new Queue(); private QueuedMessage? _active; public int Count => _messages.Count + ((_active != null) ? 1 : 0); public bool TryEnqueue(OutgoingMessage message, int maximumQueuedMessages) { if (Count >= maximumQueuedMessages) { return false; } _messages.Enqueue(new QueuedMessage(message)); return true; } public void Tick(DateTime now, TimeSpan acknowledgementTimeout, Action send, Action timeout) { if (_active == null && _messages.Count > 0) { _active = _messages.Dequeue(); } if (_active == null) { return; } if (_active.AwaitingIndex > 0) { if (now - _active.SentAt >= acknowledgementTimeout) { OutgoingMessage message = _active.Message; _active = null; timeout(message); } } else if (!(now < _active.NextSendAt)) { string arg = _active.Message.Frames[_active.NextFrame]; _active.AwaitingIndex = _active.NextFrame + 1; _active.SentAt = now; send(_active.Message, arg); } } public bool Acknowledge(string messageId, int frameIndex, DateTime now, TimeSpan delay) { if (_active == null || _active.Message.MessageId != messageId || _active.AwaitingIndex != frameIndex) { return false; } _active.NextFrame++; _active.AwaitingIndex = 0; if (_active.NextFrame >= _active.Message.Frames.Count) { _active = null; } else { _active.NextSendAt = now + delay; } return true; } public OutgoingMessage? FailActive() { OutgoingMessage result = _active?.Message; _active = null; return result; } public void Clear() { _messages.Clear(); _active = null; } } } namespace LongChat.Protocol { public static class FrameEncoder { public static IReadOnlyList Encode(string text, int frameByteLimit, int maximumChunks, string? messageId = null) { if (text == null) { throw new ArgumentNullException("text"); } if (maximumChunks < 1) { throw new ArgumentOutOfRangeException("maximumChunks"); } if (messageId == null) { messageId = MessageIdGenerator.Create(); } if (messageId.Length != 6 || messageId.Any(delegate(char character) { bool flag; switch (character) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': flag = true; break; default: flag = false; break; } return !flag; })) { throw new ArgumentException("Message IDs must contain exactly six uppercase hexadecimal characters.", "messageId"); } string s = FrameParser.Header(messageId, maximumChunks, maximumChunks); int maximumPayloadBytes = frameByteLimit - Encoding.UTF8.GetByteCount(s); IReadOnlyList payloads = Utf8Chunker.Split(text, maximumPayloadBytes); if (payloads.Count > maximumChunks) { throw new InvalidOperationException($"Message requires {payloads.Count} chunks; the configured maximum is {maximumChunks}."); } string[] array = payloads.Select((string payload, int index) => FrameParser.Header(messageId, index + 1, payloads.Count) + payload).ToArray(); if (array.Any((string frame) => Encoding.UTF8.GetByteCount(frame) > frameByteLimit)) { throw new InvalidOperationException("An encoded frame exceeded the configured byte limit."); } return array; } } public static class FrameParser { private const string Prefix = "[[LC1|"; public static bool TryParse(string text, out ParsedFrame frame) { frame = default(ParsedFrame); if (string.IsNullOrEmpty(text) || !text.StartsWith("[[LC1|", StringComparison.Ordinal)) { return false; } int num = text.IndexOf("]]", "[[LC1|".Length, StringComparison.Ordinal); if (num < 0) { return false; } string[] array = text.AsSpan("[[LC1|".Length, num - "[[LC1|".Length).ToString().Split('|'); if (array.Length != 3 || array[0].Length != 6 || array[0].Any((char character) => !IsUpperHex(character)) || !int.TryParse(array[1], out var result) || !int.TryParse(array[2], out var result2) || result < 1 || result2 < 1 || result > result2) { return false; } string messageId = array[0]; int index = result; int total = result2; int num2 = num + 2; frame = new ParsedFrame(messageId, index, total, text.Substring(num2, text.Length - num2)); return true; } public static string Header(string messageId, int index, int total) { return $"[[LC1|{messageId}|{index}|{total}]]"; } private static bool IsUpperHex(char character) { switch (character) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': return true; default: return false; } } } public static class MessageIdGenerator { public static string Create() { Span data = stackalloc byte[3]; RandomNumberGenerator.Fill(data); return string.Concat(from value in data.ToArray() select value.ToString("X2")); } } internal static class OverheadTextFormatter { public static string Limit(string text, int maximumCharacters) { string text2 = text ?? string.Empty; int num = Math.Max(1, maximumCharacters); if (text2.Length <= num) { return text2; } if (num != 1) { return text2.Substring(0, num - 1) + "…"; } return "…"; } public static string Wrap(string text, int maximumLineCharacters, bool enabled) { string text2 = text ?? string.Empty; if (!enabled) { return text2; } int limit = Math.Max(1, maximumLineCharacters); string[] array = text2.Replace("\r\n", "\n", StringComparison.Ordinal).Replace('\r', '\n').Split('\n'); for (int i = 0; i < array.Length; i++) { array[i] = WrapParagraph(array[i], limit); } return string.Join("\n", array); } private static string WrapParagraph(string paragraph, int limit) { if (paragraph.Length <= limit) { return paragraph; } string[] array = paragraph.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return paragraph; } StringBuilder stringBuilder = new StringBuilder(paragraph.Length + paragraph.Length / limit + 1); StringBuilder stringBuilder2 = new StringBuilder(limit); string[] array2 = array; foreach (string word in array2) { AppendWord(stringBuilder, stringBuilder2, word, limit); } if (stringBuilder2.Length > 0) { AppendLine(stringBuilder, stringBuilder2.ToString()); } return stringBuilder.ToString(); } private static void AppendWord(StringBuilder output, StringBuilder currentLine, string word, int limit) { if (word.Length > limit) { if (currentLine.Length > 0) { AppendLine(output, currentLine.ToString()); currentLine.Clear(); } int i; for (i = 0; word.Length - i > limit; i += limit) { AppendLine(output, word.Substring(i, limit)); } if (i < word.Length) { int num = i; currentLine.Append(word.Substring(num, word.Length - num)); } } else if (currentLine.Length == 0) { currentLine.Append(word); } else if (currentLine.Length + 1 + word.Length <= limit) { currentLine.Append(' ').Append(word); } else { AppendLine(output, currentLine.ToString()); currentLine.Clear(); currentLine.Append(word); } } private static void AppendLine(StringBuilder output, string line) { if (output.Length > 0) { output.Append('\n'); } output.Append(line); } } public static class TransportLimits { public const int MinimumFrameByteLimit = 64; public const int MaximumFrameByteLimit = 120; public static int EffectiveFrameByteLimit(int configuredLimit) { return Math.Clamp(configuredLimit, 64, 120); } public static bool ExceedsFrameLimit(string? text, int configuredLimit) { return Encoding.UTF8.GetByteCount(text ?? string.Empty) > EffectiveFrameByteLimit(configuredLimit); } } public static class Utf8Chunker { public static IReadOnlyList Split(string text, int maximumPayloadBytes) { if (text == null) { throw new ArgumentNullException("text"); } if (maximumPayloadBytes < 4) { throw new ArgumentOutOfRangeException("maximumPayloadBytes"); } List list = new List(); int num = 0; while (num < text.Length) { int num2 = FindEnd(text, num, maximumPayloadBytes); if (num2 <= num) { throw new InvalidOperationException("The payload budget cannot contain the next Unicode scalar."); } int num3 = num; list.Add(text.Substring(num3, num2 - num3)); num = num2; } return list; } private static int FindEnd(string text, int start, int maximumBytes) { int num = 0; int num2 = start; int num3 = start; int num4 = -1; int num5 = -1; int num6 = -1; int num7 = -1; while (num2 < text.Length) { char c = text[num2]; int num8 = ((!char.IsHighSurrogate(c) || num2 + 1 >= text.Length || !char.IsLowSurrogate(text[num2 + 1])) ? 1 : 2); int num9 = ((num8 == 2) ? char.ConvertToUtf32(c, text[num2 + 1]) : c); int num10 = ((num9 <= 2047) ? ((num9 <= 127) ? 1 : 2) : ((num9 > 65535) ? 4 : 3)); int num11 = num10; if (num + num11 > maximumBytes) { break; } num += num11; num2 += num8; num3 = num2; bool flag; switch (num9) { case 10: num4 = num2 - num8; continue; case 33: case 46: case 63: case 12290: case 65281: case 65311: flag = true; break; default: flag = false; break; } if (flag) { num5 = num2; continue; } UnicodeCategory unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(text, num2 - num8); if ((uint)(unicodeCategory - 18) <= 6u) { num6 = num2; } else if (num8 == 1 && char.IsWhiteSpace(c)) { num7 = num2 - num8; } } if (num3 == text.Length) { return num3; } int num12 = start + Math.Max(1, (num3 - start) / 2); int[] array = new int[4] { num4, num5, num6, num7 }; foreach (int num13 in array) { if (num13 >= num12 && num13 > start) { return num13; } } int result = num3; while (num3 > start && char.IsWhiteSpace(text[num3 - 1])) { num3--; } if (num3 <= start) { return result; } return num3; } } } namespace LongChat.Patches { internal static class ChatEndEditPatch { internal static bool Prefix(ClientChatSystem __instance, string text) { return LongChatRuntime.HandleInputEndEdit(__instance, text); } } internal static class ChatInputPatch { internal static void Postfix(ClientChatSystem __instance) { LongChatRuntime.ObserveChatWindow(__instance._ChatWindow); } } internal static class ChatUpdatePatch { internal static void Postfix() { LongChatRuntime.Tick(); } } internal static class ClientSessionPatch { internal static void Prefix() { LongChatRuntime.ClearSession("client session ended"); } } internal static class FinalChatFilterPatch { internal static bool Prefix(__c__DisplayClass55_1 __instance, string userNameFiltered) { DiagnosticLog.IncomingFiltered(__instance.filteredText, __instance.field_Public___c__DisplayClass55_0_0, userNameFiltered); return LongChatRuntime.HandleFinalFilteredMessage(__instance, userNameFiltered); } } internal static class OverheadChatPatch { internal static bool Prefix(ClientChatSystem __instance, ref NetworkIdLookupMap networkIdMap, NetworkId fromCharacterId, ref string filteredText) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) DiagnosticLog.OverheadRaw(filteredText, fromCharacterId); return LongChatRuntime.HandleOverheadChatMessage(fromCharacterId, ref filteredText); } } } namespace LongChat.Models { public readonly record struct AssemblyKey(string SenderId, int Channel, string MessageId); internal sealed record IncomingContext(__c__DisplayClass55_1 Callback, ServerChatMessageType MessageType, string UserNameFiltered, string TimeStamp, bool ShowTimeStamp, bool IsLocalUser, bool IsUserAdmin) { [CompilerGenerated] public void Deconstruct(out __c__DisplayClass55_1 Callback, out ServerChatMessageType MessageType, out string UserNameFiltered, out string TimeStamp, out bool ShowTimeStamp, out bool IsLocalUser, out bool IsUserAdmin) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected I4, but got Unknown Callback = this.Callback; MessageType = (ServerChatMessageType)(int)this.MessageType; UserNameFiltered = this.UserNameFiltered; TimeStamp = this.TimeStamp; ShowTimeStamp = this.ShowTimeStamp; IsLocalUser = this.IsLocalUser; IsUserAdmin = this.IsUserAdmin; } } internal sealed record OutgoingMessage(EntityManager EntityManager, ChatMessageType MessageType, string MessageId, string OriginalText, IReadOnlyList Frames, Action SubmitFrame) { [CompilerGenerated] public void Deconstruct(out EntityManager EntityManager, out ChatMessageType MessageType, out string MessageId, out string OriginalText, out IReadOnlyList Frames, out Action SubmitFrame) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected I4, but got Unknown EntityManager = this.EntityManager; MessageType = (ChatMessageType)(int)this.MessageType; MessageId = this.MessageId; OriginalText = this.OriginalText; Frames = this.Frames; SubmitFrame = this.SubmitFrame; } } public readonly record struct ParsedFrame(string MessageId, int Index, int Total, string Payload); } namespace LongChat.Diagnostics { internal static class DiagnosticLog { private const string Prefix = "[LC-DIAG]"; private const string RawPrefix = "[LC-RAW]"; public static void Stage(string stage, string details) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown if (Plugin.Settings.LogTransportDiagnostics.Value) { ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(2, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("[LC-DIAG]"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(stage); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(details); } log.LogInfo(val); } } public static void IncomingFiltered(string? filteredText, __c__DisplayClass55_0? context, string? userNameFiltered) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Settings.LogIncomingRawMessages.Value) { string text = filteredText ?? string.Empty; ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(123, 12, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("[LC-RAW]"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" chars="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text.Length); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" bytes="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(Encoding.UTF8.GetByteCount(text)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("type="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted((context != null) ? ((int)context.messageType) : 0); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" fromUser=\""); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(RawLogEscaper.Escape((context != null) ? ((object)context.fromUser/*cast due to .constrained prefix*/).ToString() : null)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\" "); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("fromCharacter=\""); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(RawLogEscaper.Escape((context != null) ? ((object)context.fromCharacterId/*cast due to .constrained prefix*/).ToString() : null)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\" "); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("isLocalUser="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(context != null && context.isSelf); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" userName=\""); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(RawLogEscaper.Escape(userNameFiltered)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\" "); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("timeStamp=\""); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(RawLogEscaper.Escape((context != null) ? context.timeStamp : null)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\" showTimeStamp="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(context != null && context.showTimeStamp); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("isUserAdmin="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(context != null && context.isUserAdmin); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" text=\""); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(RawLogEscaper.Escape(text)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\""); } log.LogInfo(val); } } public unsafe static void OverheadRaw(string? rawText, NetworkId networkId) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown if (Plugin.Settings.LogIncomingRawMessages.Value) { string text = rawText ?? string.Empty; ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(49, 5, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("[LC-RAW]"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" overhead=true chars="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text.Length); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" bytes="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(Encoding.UTF8.GetByteCount(text)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("networkId=\""); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(RawLogEscaper.Escape(((object)(*(NetworkId*)(&networkId))/*cast due to .constrained prefix*/).ToString())); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\" text=\""); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(RawLogEscaper.Escape(text)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\""); } log.LogInfo(val); } } } public static class RawLogEscaper { public static string Escape(string? value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(value.Length); foreach (char c in value) { switch (c) { case '\\': stringBuilder.Append("\\\\"); continue; case '"': stringBuilder.Append("\\\""); continue; case '\r': stringBuilder.Append("\\r"); continue; case '\n': stringBuilder.Append("\\n"); continue; case '\t': stringBuilder.Append("\\t"); continue; } if (char.IsControl(c)) { stringBuilder.Append("\\u"); int num = c; stringBuilder.Append(num.ToString("X4")); } else { stringBuilder.Append(c); } } return stringBuilder.ToString(); } } } namespace LongChat.Config { internal sealed class LongChatConfig { public ConfigEntry Enabled { get; } public ConfigEntry MaxInputCharacters { get; } public ConfigEntry FrameByteLimit { get; } public ConfigEntry SendDelayMilliseconds { get; } public ConfigEntry AcknowledgementTimeoutMilliseconds { get; } public ConfigEntry MaximumChunksPerMessage { get; } public ConfigEntry MaximumQueuedMessages { get; } public ConfigEntry AssemblyTimeoutMilliseconds { get; } public ConfigEntry MaximumActiveAssemblies { get; } public ConfigEntry MaximumBufferedBytes { get; } public ConfigEntry EnableOverheadReconstruction { get; } public ConfigEntry MaxOverheadCharacters { get; } public ConfigEntry EnableOverheadWrapping { get; } public ConfigEntry MaxOverheadLineCharacters { get; } public ConfigEntry EnableLocal { get; } public ConfigEntry EnableGlobal { get; } public ConfigEntry EnableClan { get; } public ConfigEntry DisableSplittingForCommands { get; } public ConfigEntry CommandPrefixes { get; } public ConfigEntry LogTransportDiagnostics { get; } public ConfigEntry LogIncomingRawMessages { get; } public ConfigEntry LogPatchDiscovery { get; } public LongChatConfig(ConfigFile config) { Enabled = config.Bind("General", "Enabled", true, "Enable LongChat."); MaxInputCharacters = config.Bind("General", "MaxInputCharacters", 4096, "Maximum characters accepted by the chat input."); FrameByteLimit = config.Bind("Transport", "FrameByteLimit", 120, "Maximum UTF-8 bytes in each transmitted frame. Values are clamped to 64-120."); SendDelayMilliseconds = config.Bind("Transport", "SendDelayMilliseconds", 1, "Delay between multipart frames."); AcknowledgementTimeoutMilliseconds = config.Bind("Transport", "AcknowledgementTimeoutMilliseconds", 3000, "Time to wait for the server to echo each multipart frame."); MaximumChunksPerMessage = config.Bind("Transport", "MaximumChunksPerMessage", 32, "Maximum frames in one long message."); MaximumQueuedMessages = config.Bind("Transport", "MaximumQueuedMessages", 8, "Maximum long messages waiting to send."); AssemblyTimeoutMilliseconds = config.Bind("Receiving", "AssemblyTimeoutMilliseconds", 3000, "Time before an incomplete message is displayed partially."); MaximumActiveAssemblies = config.Bind("Receiving", "MaximumActiveAssemblies", 32, "Maximum simultaneous incoming multipart messages."); MaximumBufferedBytes = config.Bind("Receiving", "MaximumBufferedBytes", 262144, "Maximum incoming multipart payload bytes."); EnableOverheadReconstruction = config.Bind("Receiving", "EnableOverheadReconstruction", true, "Suppress multipart transport frames in overhead chat bubbles and show one reconstructed bubble."); MaxOverheadCharacters = config.Bind("Receiving", "MaxOverheadCharacters", 500, "Maximum characters shown in a reconstructed overhead chat bubble. Main chat still shows the full message."); EnableOverheadWrapping = config.Bind("Receiving", "EnableOverheadWrapping", true, "Insert line breaks into reconstructed overhead chat bubbles."); MaxOverheadLineCharacters = config.Bind("Receiving", "MaxOverheadLineCharacters", 80, "Maximum characters per reconstructed overhead chat bubble line."); EnableLocal = config.Bind("Channels", "EnableLocal", true, "Enable Region and Local chat splitting."); EnableGlobal = config.Bind("Channels", "EnableGlobal", true, "Enable Global chat splitting."); EnableClan = config.Bind("Channels", "EnableClan", true, "Enable Clan chat splitting."); DisableSplittingForCommands = config.Bind("Commands", "DisableSplittingForCommands", true, "Reject oversized command messages instead of splitting them."); CommandPrefixes = config.Bind("Commands", "CommandPrefixes", ".,/!", "Characters that identify chat commands."); LogTransportDiagnostics = config.Bind("Debug", "LogTransportDiagnostics", false, "Log content-free multipart routing, acknowledgement, and recovery diagnostics."); LogIncomingRawMessages = config.Bind("Debug", "LogIncomingRawMessages", false, "Log complete formatted incoming chat text and metadata for temporary diagnostics. This may record private chat."); LogPatchDiscovery = config.Bind("Debug", "LogPatchDiscovery", false, "Log resolved Harmony patch targets."); } } } namespace System.Runtime.CompilerServices { internal static class IsExternalInit { } }