Decompiled source of ChatPlus v1.0.0
ChatPlus.dll
Decompiled 3 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Localization; using UnityEngine.Localization.Settings; using UnityEngine.UI; [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("ChatPlus")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ChatPlus")] [assembly: AssemblyTitle("ChatPlus")] [assembly: AssemblyVersion("1.0.0.0")] namespace ChatPlus; internal static class ChatCommands { public const string CommandApiGuid = "com.on-together-mods.commandapi"; private static readonly string[] Names = new string[2] { "chatplus", "chp" }; public static bool TryParse(string text, out string[] args) { args = null; if (string.IsNullOrEmpty(text) || text[0] != '/') { return false; } string[] array = text.Substring(1).Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0 || Array.IndexOf(Names, array[0].ToLowerInvariant()) < 0) { return false; } args = array.Skip(1).ToArray(); return true; } public static void Execute(string[] args) { //IL_03af: Unknown result type (might be due to invalid IL or missing references) Plugin instance = Plugin.Instance; if ((Object)(object)instance == (Object)null) { return; } Lang current = Lang.Current; if (args.Length == 0) { instance.ToggleWindow(); return; } switch (args[0].ToLowerInvariant()) { case "ui": case "window": case "settings": instance.ToggleWindow(); break; case "time": case "timestamps": Timestamps(instance, current, args); break; case "size": Size(instance, current, args); break; case "font": case "text": TextSize(instance, current, args); break; case "lines": case "limit": case "history": Lines(instance, current, args); break; case "clear": GameAccess.Notify(string.Format(current.ChatCleared, ChatLines.Clear())); break; case "clearhistory": instance.History.Clear(); GameAccess.Notify(current.HistoryCleared); break; case "folder": case "open": instance.OpenHistoryFolder(); break; case "reset": instance.ResetWindow(); GameAccess.Notify(current.WindowReset); break; case "kw": case "keywords": case "keyword": Keyword(instance, current, args); break; case "status": GameAccess.Notify(instance.DescribeStatus(current)); break; default: GameAccess.Notify(string.Format(current.Help, instance.WindowKey.Value)); break; } } private static void Timestamps(Plugin plugin, Lang lang, string[] args) { string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "toggle"); switch (text) { case "toggle": case "on": case "off": plugin.Timestamps.Value = ((text == "toggle") ? (!plugin.Timestamps.Value) : (text == "on")); GameAccess.Notify(plugin.Timestamps.Value ? lang.TimeOn : lang.TimeOff); return; case "format": if (args.Length >= 3) { string text2 = string.Join(" ", args, 2, args.Length - 2); plugin.TimeFormat.Value = text2; plugin.Timestamps.Value = true; GameAccess.Notify(string.Format(lang.TimeFormatSet, text2, ChatFormat.FormatTime(DateTime.Now, text2))); return; } break; } GameAccess.Notify(lang.UsageTime); } private static void Size(Plugin plugin, Lang lang, string[] args) { if (args.Length > 1 && args[1].Equals("reset", StringComparison.OrdinalIgnoreCase)) { ChatWindow.ResetSize(); } else { if (args.Length <= 1 || !TryParsePercent(args[1], out var percent)) { GameAccess.Notify(lang.UsageSize); return; } int percent2 = plugin.Height.Value; if (args.Length > 2 && !TryParsePercent(args[2], out percent2)) { GameAccess.Notify(lang.UsageSize); return; } plugin.Width.Value = percent; plugin.Height.Value = percent2; } GameAccess.Notify(string.Format(lang.SizeSet, plugin.Width.Value, plugin.Height.Value)); } private static void TextSize(Plugin plugin, Lang lang, string[] args) { if (args.Length < 2 || !TryParsePercent(args[1], out var percent)) { GameAccess.Notify(lang.UsageText); return; } plugin.TextSize.Value = percent; GameAccess.Notify(string.Format(lang.TextSizeSet, plugin.TextSize.Value)); } private static void Lines(Plugin plugin, Lang lang, string[] args) { if (args.Length < 2 || !int.TryParse(args[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { GameAccess.Notify(lang.UsageLimit); return; } int result2 = plugin.LocalLimit.Value; if (args.Length > 2 && !int.TryParse(args[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out result2)) { GameAccess.Notify(lang.UsageLimit); return; } plugin.GlobalLimit.Value = result; plugin.LocalLimit.Value = result2; GameAccess.Notify(string.Format(lang.LimitsSet, plugin.GlobalLimit.Value, plugin.LocalLimit.Value)); } private static void Keyword(Plugin plugin, Lang lang, string[] args) { string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "list"); List<string> list = TextUtil.ParseKeywords((args.Length > 2) ? string.Join(" ", args, 2, args.Length - 2) : null); List<string> list2 = TextUtil.ParseKeywords(plugin.Keywords.Value); switch (text) { case "add": if (list.Count == 0) { break; } foreach (string word2 in list) { if (!list2.Exists((string w) => string.Equals(w, word2, StringComparison.OrdinalIgnoreCase))) { list2.Add(word2); } } plugin.Keywords.Value = TextUtil.JoinKeywords(list2); GameAccess.Notify(string.Format(lang.KeywordAdded, TextUtil.JoinKeywords(list))); return; case "remove": case "delete": { if (list.Count == 0) { break; } int num = 0; foreach (string word in list) { num += list2.RemoveAll((string w) => string.Equals(w, word, StringComparison.OrdinalIgnoreCase)); } if (num == 0) { GameAccess.Notify(string.Format(lang.KeywordMissing, TextUtil.JoinKeywords(list))); return; } plugin.Keywords.Value = TextUtil.JoinKeywords(list2); GameAccess.Notify(string.Format(lang.KeywordRemoved, TextUtil.JoinKeywords(list))); return; } case "clear": plugin.Keywords.Value = string.Empty; GameAccess.Notify(lang.KeywordsCleared); return; case "list": GameAccess.Notify(string.Format(lang.KeywordsList, (list2.Count > 0) ? TextUtil.JoinKeywords(list2) : lang.None)); return; } GameAccess.Notify(lang.UsageKeyword); } private static bool TryParsePercent(string text, out int percent) { return int.TryParse((text ?? string.Empty).TrimEnd('%'), NumberStyles.Integer, CultureInfo.InvariantCulture, out percent); } public static void RegisterWithCommandApi() { if (!Chainloader.PluginInfos.TryGetValue("com.on-together-mods.commandapi", out var value) || (Object)(object)value.Instance == (Object)null) { return; } try { Assembly assembly = ((object)value.Instance).GetType().Assembly; Type type = assembly.GetType("CommandAPI.CommandRegistry"); Type type2 = assembly.GetType("CommandAPI.Parameter"); Type type3 = assembly.GetType("CommandAPI.ParameterType"); if (type == null || type2 == null || type3 == null) { return; } MethodInfo method = type.GetMethod("Register", new Type[5] { typeof(string), typeof(string), typeof(Action<string[]>), typeof(string), type2.MakeArrayType() }); if (!(method == null)) { object obj = Enum.Parse(type3, "String"); Array array = Array.CreateInstance(type2, 2); array.SetValue(Activator.CreateInstance(type2, "action", obj, true, null, null), 0); array.SetValue(Activator.CreateInstance(type2, "value", obj, true, null, null), 1); Action<string[]> action = Execute; string[] names = Names; foreach (string text in names) { method.Invoke(null, new object[5] { text, "ChatPlus", action, "Chat Plus: settings window, message time, chat size, text size, lines kept, clear, history folder", array }); } Plugin.Log.LogInfo((object)"Registered /chatplus with CommandAPI."); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not register with CommandAPI (the command still works): " + ex.Message)); } } } internal enum ChatKind { Player, Own, Notification } internal sealed class ChatEntry { public DateTime Time; public bool IsLocal; public ChatKind Kind; public string SteamId = string.Empty; public string Name = string.Empty; public string Message = string.Empty; public string Line = string.Empty; } internal sealed class LineStyle { public bool Timestamps = true; public string TimeFormat = "HH:mm"; public string TimeColor; public int TimeSize = 100; public bool TimeOnNotifications = true; public string DateFormat = "dd.MM"; public bool Highlight; public string HighlightColor; public string MyName = string.Empty; public IList<string> Keywords = new List<string>(); } internal static class ChatFormat { public const string DefaultTimeFormat = "HH:mm"; private static readonly CultureInfo Invariant = CultureInfo.InvariantCulture; public static string Compose(ChatEntry entry, LineStyle style, DateTime now) { string text = entry.Line ?? string.Empty; if (style == null) { return text; } if (style.Highlight && style.HighlightColor != null && entry.Kind == ChatKind.Player && TextUtil.IsMention(entry.Message, style.MyName, style.Keywords)) { text = "<mark=" + style.HighlightColor + ">" + text + "</mark>"; } if (style.Timestamps && (entry.Kind != ChatKind.Notification || style.TimeOnNotifications)) { text = Stamp(entry.Time, now, style) + text; } return text; } public static string Stamp(DateTime time, DateTime now, LineStyle style) { string text = FormatTime(time, style.TimeFormat); if (time.Date != now.Date) { text = FormatDate(time, style.DateFormat) + " " + text; } bool num = !string.IsNullOrEmpty(style.TimeColor); bool flag = style.TimeSize > 0 && style.TimeSize != 100; bool flag2 = text.IndexOf('<') >= 0 || text.IndexOf('>') >= 0; StringBuilder stringBuilder = new StringBuilder(text.Length + 48); if (num) { stringBuilder.Append("<color=").Append(style.TimeColor).Append('>'); } if (flag) { stringBuilder.Append("<size=").Append(style.TimeSize.ToString(Invariant)).Append("%>"); } if (flag2) { stringBuilder.Append("<noparse>"); } stringBuilder.Append(text); if (flag2) { stringBuilder.Append("</noparse>"); } if (flag) { stringBuilder.Append("</size>"); } if (num) { stringBuilder.Append("</color>"); } return stringBuilder.Append(' ').ToString(); } public static string FormatTime(DateTime time, string format) { if (string.IsNullOrEmpty(format)) { format = "HH:mm"; } try { return time.ToString(format, Invariant); } catch (FormatException) { return time.ToString("HH:mm", Invariant); } } private static string FormatDate(DateTime time, string format) { try { return time.ToString(string.IsNullOrEmpty(format) ? "dd.MM" : format, Invariant); } catch (FormatException) { return time.ToString("dd.MM", Invariant); } } public static string CopyText(ChatEntry entry) { return TextUtil.StripTags(entry.Message).Trim(); } public static bool TryParseColor(string value, out string normalized) { normalized = null; if (string.IsNullOrEmpty(value)) { return false; } string text = value.Trim(); if (text.StartsWith("#", StringComparison.Ordinal)) { text = text.Substring(1); } if (text.Length != 3 && text.Length != 4 && text.Length != 6 && text.Length != 8) { return false; } string text2 = text; for (int i = 0; i < text2.Length; i++) { if (!Uri.IsHexDigit(text2[i])) { return false; } } if (text.Length <= 4) { StringBuilder stringBuilder = new StringBuilder(text.Length * 2); text2 = text; foreach (char value2 in text2) { stringBuilder.Append(value2).Append(value2); } text = stringBuilder.ToString(); } normalized = "#" + text.ToUpperInvariant(); return true; } public static string WithAlpha(string normalized, byte alpha) { if (string.IsNullOrEmpty(normalized) || normalized.Length < 7) { return normalized; } return normalized.Substring(0, 7) + alpha.ToString("X2", Invariant); } } internal sealed class ChatHistory { public const string FileName = "chat-history.tsv"; public const string PreviousFileName = "chat-history.previous.tsv"; public const string LogFolderName = "logs"; private const string NewLine = "\r\n"; private static readonly Encoding Utf8Bom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true); private static readonly Encoding Utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); private readonly List<ChatEntry> _entries = new List<ChatEntry>(); private readonly int _capacity; private readonly Action<string> _warn; private bool _saving; private bool _fileBehind; private string _lastError; public string Folder { get; } public string FilePath => Path.Combine(Folder, "chat-history.tsv"); public string LogFolder => Path.Combine(Folder, "logs"); public int Count => _entries.Count; public bool Saving => _saving; public bool DailyLogs { get; set; } public ChatHistory(string folder, int capacity, Action<string> warn) { Folder = folder; _capacity = Math.Max(1, capacity); _warn = warn; } public void Open(bool keepPrevious, bool saving) { _saving = saving; if (keepPrevious) { Load(); } else { ArchivePrevious(); } } public void Add(ChatEntry entry) { if (entry == null) { return; } _entries.Add(entry); if (_entries.Count > _capacity) { _entries.RemoveRange(0, _entries.Count - _capacity); } if (_saving) { if (_fileBehind) { Rewrite(); } else { Append(entry); } } if (DailyLogs) { AppendDailyLog(entry); } } public List<ChatEntry> Snapshot() { return new List<ChatEntry>(_entries); } public void SetSaving(bool saving) { if (saving != _saving) { _saving = saving; if (saving) { Rewrite(); } } } public void Clear() { _entries.Clear(); if (_saving || File.Exists(FilePath)) { Rewrite(); } } private void Load() { string filePath = FilePath; if (!File.Exists(filePath)) { return; } string[] array; try { array = File.ReadAllLines(filePath, Utf8); } catch (Exception ex) { Warn("Could not read the chat history: " + ex.Message); return; } int num = 0; string[] array2 = array; foreach (string text in array2) { if (text.Length != 0 && !text.StartsWith("time\t", StringComparison.Ordinal)) { num++; if (HistoryFormat.TryParse(text, out var entry)) { _entries.Add(entry); } } } if (_entries.Count > _capacity) { _entries.RemoveRange(0, _entries.Count - _capacity); } if (_saving && num != _entries.Count) { Rewrite(); } } private void ArchivePrevious() { string filePath = FilePath; try { if (File.Exists(filePath)) { string text = Path.Combine(Folder, "chat-history.previous.tsv"); if (File.Exists(text)) { File.Delete(text); } File.Move(filePath, text); } } catch (Exception ex) { Warn("Could not move the old chat history aside: " + ex.Message); Rewrite(); } } private void Append(ChatEntry entry) { try { string filePath = FilePath; if (!File.Exists(filePath)) { Directory.CreateDirectory(Folder); File.WriteAllText(filePath, "time\tchannel\ttype\tsteam_id\tname\tmessage\tline\r\n", Utf8Bom); } File.AppendAllText(filePath, HistoryFormat.ToRecord(entry) + "\r\n", Utf8); _lastError = null; } catch (Exception ex) { _fileBehind = true; Warn("Could not write the chat history, will try again with the next message: " + ex.Message); } } private void Rewrite() { string filePath = FilePath; string text = filePath + ".tmp"; try { Directory.CreateDirectory(Folder); StringBuilder stringBuilder = new StringBuilder("time\tchannel\ttype\tsteam_id\tname\tmessage\tline".Length + 2 + _entries.Count * 96); stringBuilder.Append("time\tchannel\ttype\tsteam_id\tname\tmessage\tline").Append("\r\n"); foreach (ChatEntry entry in _entries) { stringBuilder.Append(HistoryFormat.ToRecord(entry)).Append("\r\n"); } File.WriteAllText(text, stringBuilder.ToString(), Utf8Bom); if (File.Exists(filePath)) { File.Delete(filePath); } File.Move(text, filePath); _fileBehind = false; _lastError = null; } catch (Exception ex) { _fileBehind = true; Warn("Could not write the chat history, will try again with the next message: " + ex.Message); try { if (File.Exists(text)) { File.Delete(text); } } catch (Exception) { } } } private void AppendDailyLog(ChatEntry entry) { try { string logFolder = LogFolder; string path = Path.Combine(logFolder, entry.Time.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); if (!File.Exists(path)) { Directory.CreateDirectory(logFolder); File.WriteAllText(path, string.Empty, Utf8Bom); } File.AppendAllText(path, HistoryFormat.ToReadable(entry) + "\r\n", Utf8); } catch (Exception ex) { Warn("Could not write the chat log: " + ex.Message); } } private void Warn(string message) { if (!(message == _lastError)) { _lastError = message; _warn?.Invoke(message); } } } internal sealed class ChatLine : MonoBehaviour, IPointerClickHandler, IEventSystemHandler { internal ChatEntry Entry; private TMP_Text _text; internal static ChatLine Attach(TMP_Text text, ChatEntry entry) { ChatLine chatLine = ((Component)text).GetComponent<ChatLine>(); if ((Object)(object)chatLine == (Object)null) { chatLine = ((Component)text).gameObject.AddComponent<ChatLine>(); } chatLine._text = text; chatLine.Entry = entry; chatLine.Render(ChatLines.Style, DateTime.Now); LineSizer.Apply(text); return chatLine; } internal void Render(LineStyle style, DateTime now) { if (!((Object)(object)_text == (Object)null) && Entry != null) { string text = ChatFormat.Compose(Entry, style, now); if (!string.Equals(_text.text, text, StringComparison.Ordinal)) { _text.text = text; } } } public void OnPointerClick(PointerEventData eventData) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if (eventData == null || (int)eventData.button != 1 || Entry == null) { return; } Plugin instance = Plugin.Instance; if (!((Object)(object)instance == (Object)null) && instance.RightClickCopy.Value) { string text = ChatFormat.CopyText(Entry); if (text.Length != 0) { GUIUtility.systemCopyBuffer = text; Toast.Show(Lang.Current.Copied); } } } } internal static class ChatLines { internal static bool Suppress; internal static int StyleVersion; private static LineStyle _style; private static int _builtVersion = -1; private static string _builtName; private static Lang _builtLang; internal static LineStyle Style { get { string localPlayerName = GameAccess.LocalPlayerName; Lang current = Lang.Current; if (_style == null || _builtVersion != StyleVersion || localPlayerName != _builtName || current != _builtLang) { _style = Plugin.Instance.BuildStyle(localPlayerName, current); _builtVersion = StyleVersion; _builtName = localPlayerName; _builtLang = current; } return _style; } } internal static void ApplyLimits() { Plugin instance = Plugin.Instance; if ((Object)(object)instance != (Object)null) { GameAccess.SetLineLimits(instance.GlobalLimit.Value, instance.LocalLimit.Value); } } internal static void Trim(List<GameObject> lines, int limit) { if (lines == null) { return; } limit = Math.Max(1, limit); while (lines.Count > limit) { GameObject val = lines[0]; lines.RemoveAt(0); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } } private static int Limit(bool isLocal) { Plugin instance = Plugin.Instance; if (!isLocal) { return instance.GlobalLimit.Value; } return instance.LocalLimit.Value; } internal static void OnMessageAdded(TextChannelManager chat, string userName, string text, bool isLocal, int senderIndex, bool own, LineMark mark) { List<GameObject> lines = GameAccess.Lines(chat, isLocal); TMP_Text val = FindNewLine(lines, mark); if (!((Object)(object)val == (Object)null)) { ChatEntry entry = new ChatEntry { Time = DateTime.Now, IsLocal = isLocal, Kind = (own ? ChatKind.Own : ChatKind.Player), SteamId = (own ? GameAccess.OwnSteamId(chat) : GameAccess.SteamIdAt(senderIndex)), Name = (userName ?? string.Empty), Message = (text ?? string.Empty), Line = (val.text ?? string.Empty) }; ChatLine.Attach(val, entry); Plugin.Instance.History.Add(entry); Trim(lines, Limit(isLocal)); } } internal static void OnNotificationAdded(TextChannelManager chat, string text, LineMark mark) { List<GameObject> lines = GameAccess.Lines(chat, isLocal: false); TMP_Text val = FindNewLine(lines, mark); if (!((Object)(object)val == (Object)null)) { ChatEntry entry = new ChatEntry { Time = DateTime.Now, IsLocal = false, Kind = ChatKind.Notification, Message = (text ?? string.Empty), Line = (val.text ?? string.Empty) }; ChatLine.Attach(val, entry); if (!Suppress) { Plugin.Instance.History.Add(entry); } Trim(lines, Limit(isLocal: false)); } } private static TMP_Text FindNewLine(List<GameObject> lines, LineMark mark) { if (lines == null || lines.Count == 0) { return null; } int num = ((mark != null) ? Math.Max(0, lines.Count - 8) : (lines.Count - 1)); for (int num2 = lines.Count - 1; num2 >= num; num2--) { GameObject val = lines[num2]; if (mark != null && val == mark.Newest) { return null; } if (!((Object)(object)val == (Object)null) && !((Object)(object)val.GetComponent<ChatLine>() != (Object)null)) { return val.GetComponent<TMP_Text>(); } } return null; } internal static void OnChatStarted(TextChannelManager chat) { GameAccess.SetChat(chat); ApplyLimits(); TMP_Text val = GameAccess.TextPrefab(chat); if ((Object)(object)val != (Object)null && val.fontSize > 1f) { LineSizer.BaseSize = val.fontSize; } Plugin instance = Plugin.Instance; if (!instance.RestoreHistory.Value) { return; } List<ChatEntry> list = instance.History.Snapshot(); if (list.Count != 0) { UIManager ui = GameAccess.FindUI(); int num = Restore(chat, ui, val, list, isLocal: false, instance.RestoreNotifications.Value) + Restore(chat, ui, val, list, isLocal: true, notifications: false); if (num > 0) { Plugin.Log.LogInfo((object)$"Restored {num} chat lines from earlier in this session."); } } } private static int Restore(TextChannelManager chat, UIManager ui, TMP_Text prefab, List<ChatEntry> entries, bool isLocal, bool notifications) { //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Expected O, but got Unknown Transform val = GameAccess.Content(ui, isLocal); List<GameObject> list = GameAccess.Lines(chat, isLocal); if ((Object)(object)val == (Object)null || list == null || (Object)(object)prefab == (Object)null) { return 0; } HashSet<ChatEntry> hashSet = new HashSet<ChatEntry>(); foreach (GameObject item in list) { ChatLine chatLine = (((Object)(object)item != (Object)null) ? item.GetComponent<ChatLine>() : null); if ((Object)(object)chatLine != (Object)null && chatLine.Entry != null) { hashSet.Add(chatLine.Entry); } } int num = Limit(isLocal) - 1; List<ChatEntry> list2 = new List<ChatEntry>(); int num2 = entries.Count - 1; while (num2 >= 0 && list2.Count < num) { ChatEntry chatEntry = entries[num2]; if (chatEntry.IsLocal == isLocal && !hashSet.Contains(chatEntry) && (chatEntry.Kind != ChatKind.Notification || notifications) && (chatEntry.Kind != ChatKind.Player || !GameAccess.IsBlocked(chatEntry.SteamId))) { list2.Add(chatEntry); } num2--; } if (list2.Count == 0) { return 0; } list2.Reverse(); int num3 = 0; foreach (ChatEntry item2 in list2) { TMP_Text val2 = CreateLine(prefab, val, item2.Line); Button component = ((Component)val2).GetComponent<Button>(); if ((Object)(object)component != (Object)null) { if (item2.Kind == ChatKind.Notification) { ((Selectable)component).interactable = false; } else if (item2.Kind == ChatKind.Player && !string.IsNullOrEmpty(item2.SteamId)) { string name = item2.Name; string steamId = item2.SteamId; ((UnityEvent)component.onClick).AddListener((UnityAction)delegate { GameAccess.OpenIdCard(name, steamId); }); } } ChatLine.Attach(val2, item2); val2.transform.SetSiblingIndex(num3); list.Insert(num3, ((Component)val2).gameObject); num3++; } TMP_Text val3 = CreateLine(prefab, val, Lang.Current.EarlierMessages); Button component2 = ((Component)val3).GetComponent<Button>(); if ((Object)(object)component2 != (Object)null) { ((Selectable)component2).interactable = false; } LineSizer.Apply(val3); val3.transform.SetSiblingIndex(num3); list.Insert(num3, ((Component)val3).gameObject); Trim(list, Limit(isLocal)); return list2.Count; } private static TMP_Text CreateLine(TMP_Text prefab, Transform parent, string text) { TMP_Text obj = Object.Instantiate<TMP_Text>(prefab, parent); obj.text = text ?? string.Empty; return obj; } internal static void RenderAll() { UIManager uI = GameAccess.UI; if ((Object)(object)uI == (Object)null) { return; } LineStyle style = Style; DateTime now = DateTime.Now; for (int i = 0; i < 2; i++) { Transform val = GameAccess.Content(uI, i == 1); if (!((Object)(object)val == (Object)null)) { ChatLine[] componentsInChildren = ((Component)val).GetComponentsInChildren<ChatLine>(true); for (int j = 0; j < componentsInChildren.Length; j++) { componentsInChildren[j].Render(style, now); } } } } internal static int Clear() { TextChannelManager chat = GameAccess.Chat; int num = 0; for (int i = 0; i < 2; i++) { List<GameObject> list = GameAccess.Lines(chat, i == 1); if (list == null) { continue; } foreach (GameObject item in list) { if ((Object)(object)item != (Object)null) { Object.Destroy((Object)(object)item); num++; } } list.Clear(); } return num; } } internal static class ChatWindow { private sealed class Viewport { public Mask Mask; public Image MaskImage; public RectMask2D RectMask; } private const string FrameName = "Panel_Bg"; private const string InputBackgroundName = "ImageInputFieldBg"; private const string MoveHandleName = "Image_Drag"; private static readonly Vector2 FrameSpriteSize = new Vector2(1644f, 1876f); private static readonly Vector4 FrameBorder = new Vector4(108f, 366f, 114f, 120f); private const float DefaultLineWidth = 697.1f; private const float TopMargin = 25f; private const float GameRightLimit = 120f; private const float MinWidthFactor = 0.7f; private const float MinHeightFactor = 0.5f; private const float GripSize = 46f; private static readonly Vector2 GripOffset = new Vector2(5f, 6f); private const float ReflowDelay = 0.3f; private static UIManager _ui; private static RectTransform _root; private static GameObject _grip; private static Vector2 _defaultSize; private static Vector2 _defaultCorner; private static float _rightMargin; private static bool _resizing; private static float _reflowAt = -1f; private static readonly List<VerticalLayoutGroup> Layouts = new List<VerticalLayoutGroup>(); private static readonly List<Viewport> Viewports = new List<Viewport>(); private static Texture2D _gripTexture; private static Sprite _gripSprite; private static Sprite _slicedFrame; private static Sprite _slicedFrameSource; internal static bool Ready { get { if ((Object)(object)_ui != (Object)null) { return (Object)(object)_root != (Object)null; } return false; } } internal static Vector2 Size { get { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_root != (Object)null)) { return Vector2.zero; } return _root.sizeDelta; } } private static Vector2 Corner => _root.anchoredPosition - Vector2.Scale(_root.pivot, _root.sizeDelta); private static Vector2 Area { get { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Transform parent = ((Transform)_root).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (!((Object)(object)val != (Object)null)) { return Vector2.zero; } Rect rect = val.rect; return ((Rect)(ref rect)).size; } } internal static void Prepare(UIManager ui) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) GameAccess.SetUI(ui); RectTransform val = GameAccess.PanelRoot(ui); if ((Object)(object)val == (Object)null || ((Object)(object)ui == (Object)(object)_ui && (Object)(object)val == (Object)(object)_root)) { return; } _ui = null; _root = null; _grip = null; _resizing = false; _reflowAt = -1f; Layouts.Clear(); Viewports.Clear(); Vector2 sizeDelta = val.sizeDelta; RectTransform val2 = GameAccess.CornerBottomLeft(ui); if ((Object)(object)val2 != (Object)null) { Reanchor(val2, Vector2.zero, Vector2.zero); } GameObject val3 = GameAccess.PanelBackground(ui); RectTransform val4 = (RectTransform)(((Object)(object)val3 != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val4 != (Object)null) { Reanchor(val4, Vector2.zero, Vector2.one); Transform obj = ((Transform)val4).Find("Panel_Bg"); RectTransform val5 = (RectTransform)(object)((obj is RectTransform) ? obj : null); if ((Object)(object)val5 != (Object)null) { SliceFrame(((Component)val5).GetComponent<Image>()); Reanchor(val5, Vector2.zero, Vector2.one); } Transform obj2 = ((Transform)val4).Find("ImageInputFieldBg"); RectTransform val6 = (RectTransform)(object)((obj2 is RectTransform) ? obj2 : null); if ((Object)(object)val6 != (Object)null) { Reanchor(val6, Vector2.zero, new Vector2(1f, 0f)); } Transform obj3 = ((Transform)val4).Find("Image_Drag"); RectTransform val7 = (RectTransform)(object)((obj3 is RectTransform) ? obj3 : null); if ((Object)(object)val7 != (Object)null) { Reanchor(val7, new Vector2(1f, 0f), new Vector2(1f, 0f)); } CreateGrip(ui, val4); } Transform obj4 = GameAccess.GlobalScroll(ui); Transform val8 = GameAccess.LocalScroll(ui); RectTransform val9 = (RectTransform)(object)((obj4 is RectTransform) ? obj4 : null); if (val9 != null) { Reanchor(val9, Vector2.zero, Vector2.one); } RectTransform val10 = (RectTransform)(object)((val8 is RectTransform) ? val8 : null); if (val10 != null) { Reanchor(val10, Vector2.zero, Vector2.one); } TMP_InputField messageInput = ui.MessageInput; if ((Object)(object)messageInput != (Object)null) { Transform transform = ((Component)messageInput).transform; RectTransform val11 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (val11 != null) { Reanchor(val11, Vector2.zero, new Vector2(1f, 0f)); } } TMP_Text val12 = GameAccess.TextPrefab(GameAccess.FindChat()); float lineWidth = (((Object)(object)val12 != (Object)null && val12.rectTransform.sizeDelta.x > 1f) ? val12.rectTransform.sizeDelta.x : 697.1f); PrepareContent(GameAccess.Content(ui, isLocal: false), lineWidth); PrepareContent(GameAccess.Content(ui, isLocal: true), lineWidth); PrepareViewport(obj4); PrepareViewport(val8); ApplyCulling(); _defaultSize = sizeDelta; _defaultCorner = val.anchoredPosition - Vector2.Scale(val.pivot, sizeDelta); _rightMargin = 120f - (1f - val.pivot.x) * sizeDelta.x; _ui = ui; _root = val; Plugin instance = Plugin.Instance; Vector2? corner = null; if (instance.RememberPosition.Value && TryParsePosition(instance.Position.Value, out var corner2)) { corner = corner2; } Apply(corner); } internal static void Apply() { Apply(null); } private static void Apply(Vector2? corner) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) if (Ready) { Plugin instance = Plugin.Instance; ? corner2 = ((??)corner) ?? Corner; Vector2 val = ClampSize(new Vector2(_defaultSize.x * (float)instance.Width.Value / 100f, _defaultSize.y * (float)instance.Height.Value / 100f)); if (Mathf.Abs(val.x - _root.sizeDelta.x) > 0.01f) { HoldReflow(); } _root.sizeDelta = val; SetCorner((Vector2)corner2); if ((Object)(object)_grip != (Object)null && _grip.activeSelf != instance.ResizeHandle.Value) { _grip.SetActive(instance.ResizeHandle.Value); } } } internal static void ApplyPosition() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) Plugin instance = Plugin.Instance; if (Ready && instance.RememberPosition.Value && TryParsePosition(instance.Position.Value, out var corner)) { Vector2 val = corner - Corner; if (((Vector2)(ref val)).sqrMagnitude > 0.25f) { SetCorner(corner); } } } internal static void Tick() { if (_reflowAt < 0f || _resizing || Time.unscaledTime < _reflowAt) { return; } _reflowAt = -1f; foreach (VerticalLayoutGroup layout in Layouts) { if ((Object)(object)layout != (Object)null) { ((HorizontalOrVerticalLayoutGroup)layout).childControlWidth = true; } } } private static void HoldReflow() { foreach (VerticalLayoutGroup layout in Layouts) { if ((Object)(object)layout != (Object)null && ((HorizontalOrVerticalLayoutGroup)layout).childControlWidth) { ((HorizontalOrVerticalLayoutGroup)layout).childControlWidth = false; } } _reflowAt = Time.unscaledTime + 0.3f; } internal static void OnBeginMove(UIManager ui) { if (Ready && (Object)(object)ui == (Object)(object)_ui) { UpdateDragLimits(includeRight: true); } } internal static void OnEndMove(UIManager ui) { if (Ready && (Object)(object)ui == (Object)(object)_ui) { SavePosition(); } } internal static void BeginResize() { if (Ready) { _resizing = true; GameAccess.SetPanelHeld(_ui, held: true); } } internal static void ResizeTo(Vector2 size) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (Ready) { size = ClampSize(size); Plugin instance = Plugin.Instance; instance.Width.Value = Mathf.RoundToInt(size.x / _defaultSize.x * 100f); instance.Height.Value = Mathf.RoundToInt(size.y / _defaultSize.y * 100f); } } internal static void EndResize() { if (_resizing) { _resizing = false; if (_reflowAt >= 0f) { _reflowAt = Time.unscaledTime; } if (Ready) { GameAccess.SetPanelHeld(_ui, held: false); SavePosition(); } } } internal static void ResetSize() { Plugin instance = Plugin.Instance; instance.Width.Value = 100; instance.Height.Value = 100; } internal static void ResetPosition() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) Plugin.Instance.Position.Value = string.Empty; if (Ready) { SetCorner(_defaultCorner); } } internal static void ApplyCulling() { bool value = Plugin.Instance.CullHiddenLines.Value; foreach (Viewport viewport in Viewports) { if ((Object)(object)viewport.Mask == (Object)null || (Object)(object)viewport.RectMask == (Object)null) { continue; } if (value) { ((Behaviour)viewport.Mask).enabled = false; if ((Object)(object)viewport.MaskImage != (Object)null && !viewport.Mask.showMaskGraphic) { ((Behaviour)viewport.MaskImage).enabled = false; } ((Behaviour)viewport.RectMask).enabled = true; } else { ((Behaviour)viewport.RectMask).enabled = false; if ((Object)(object)viewport.MaskImage != (Object)null) { ((Behaviour)viewport.MaskImage).enabled = true; } ((Behaviour)viewport.Mask).enabled = true; } } } internal static bool TryPointer(Vector2 screenPosition, Camera camera, out Vector2 point) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //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) //IL_0083: Unknown result type (might be due to invalid IL or missing references) point = Vector2.zero; if (Ready) { Transform parent = ((Transform)_root).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (val != null) { if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(val, screenPosition, camera, ref point)) { return false; } Vector3 localScale = ((Transform)_root).localScale; if (Mathf.Abs(localScale.x) > 0.0001f && Mathf.Abs(localScale.y) > 0.0001f) { point = new Vector2(point.x / localScale.x, point.y / localScale.y); } return true; } } return false; } private static void SetCorner(Vector2 corner) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) Vector2 sizeDelta = _root.sizeDelta; _root.anchoredPosition = ClampCorner(corner, sizeDelta) + Vector2.Scale(_root.pivot, sizeDelta); UpdateDragLimits(includeRight: false); } private static Vector2 ClampCorner(Vector2 corner, Vector2 size) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) Vector2 area = Area; if (area.x < 100f || area.y < 100f) { return corner; } float num = area.x - size.x - _rightMargin; float num2 = area.y - 25f - size.y - _root.pivot.y * size.y; corner.x = Mathf.Max(_defaultCorner.x, Mathf.Min(corner.x, num)); corner.y = Mathf.Max(_defaultCorner.y, Mathf.Min(corner.y, num2)); return corner; } private static Vector2 ClampSize(Vector2 size) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) float num = _defaultSize.x * 0.7f; float num2 = _defaultSize.y * 0.5f; Vector2 area = Area; float num3 = ((area.x > 100f) ? (area.x - _defaultCorner.x - _rightMargin) : float.MaxValue); float num4 = ((area.y > 100f) ? ((area.y - 25f - _defaultCorner.y) / (1f + _root.pivot.y)) : float.MaxValue); size.x = Mathf.Clamp(size.x, num, Mathf.Max(num, num3)); size.y = Mathf.Clamp(size.y, num2, Mathf.Max(num2, num4)); return size; } private static void UpdateDragLimits(bool includeRight) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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_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) Vector2 sizeDelta = _root.sizeDelta; Vector2 pivot = _root.pivot; float num = _defaultCorner.x + pivot.x * sizeDelta.x; float minY = _defaultCorner.y + pivot.y * sizeDelta.y; float? maxX = null; Vector2 area = Area; if (includeRight && area.x > 100f) { maxX = Mathf.Max(num, area.x - (1f - pivot.x) * sizeDelta.x - _rightMargin); } GameAccess.SetDragLimits(_ui, num, minY, maxX); } private static void SavePosition() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Plugin instance = Plugin.Instance; if (instance.RememberPosition.Value) { Vector2 corner = Corner; instance.Position.Value = corner.x.ToString("0.#", CultureInfo.InvariantCulture) + ";" + corner.y.ToString("0.#", CultureInfo.InvariantCulture); } } internal static bool TryParsePosition(string value, out Vector2 corner) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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) corner = Vector2.zero; if (string.IsNullOrEmpty(value)) { return false; } string[] array = value.Split(';'); if (array.Length != 2 || !float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || !float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { return false; } corner = new Vector2(result, result2); return true; } private static void Reanchor(RectTransform rect, Vector2 anchorMin, Vector2 anchorMax) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) Transform parent = ((Transform)rect).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (val != null) { Rect rect2 = val.rect; Vector2 size = ((Rect)(ref rect2)).size; Vector2 val2 = Vector2.Scale(rect.anchorMin, size) + rect.offsetMin; Vector2 val3 = Vector2.Scale(rect.anchorMax, size) + rect.offsetMax; rect.anchorMin = anchorMin; rect.anchorMax = anchorMax; rect.offsetMin = val2 - Vector2.Scale(anchorMin, size); rect.offsetMax = val3 - Vector2.Scale(anchorMax, size); } } private static void PrepareContent(Transform content, float lineWidth) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown if ((Object)(object)content == (Object)null) { return; } VerticalLayoutGroup component = ((Component)content).GetComponent<VerticalLayoutGroup>(); if ((Object)(object)component != (Object)null && !((HorizontalOrVerticalLayoutGroup)component).childControlWidth) { RectTransform val = (RectTransform)(object)((content is RectTransform) ? content : null); if (val != null) { Rect rect = val.rect; if (((Rect)(ref rect)).width > lineWidth * 0.5f) { RectOffset padding = ((LayoutGroup)component).padding; rect = val.rect; int num = Mathf.RoundToInt(((Rect)(ref rect)).width - (float)padding.left - lineWidth); ((LayoutGroup)component).padding = new RectOffset(padding.left, num, padding.top, padding.bottom); ((HorizontalOrVerticalLayoutGroup)component).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = true; Layouts.Add(component); } } } if ((Object)(object)((Component)content).GetComponent<LineSizer>() == (Object)null) { ((Component)content).gameObject.AddComponent<LineSizer>(); } } private static void PrepareViewport(Transform scroll) { ScrollRect val = (((Object)(object)scroll != (Object)null) ? ((Component)scroll).GetComponent<ScrollRect>() : null); RectTransform val2 = (((Object)(object)val != (Object)null) ? val.viewport : null); Mask val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<Mask>() : null); if (!((Object)(object)val3 == (Object)null)) { RectMask2D val4 = ((Component)val2).GetComponent<RectMask2D>(); if ((Object)(object)val4 == (Object)null) { val4 = ((Component)val2).gameObject.AddComponent<RectMask2D>(); ((Behaviour)val4).enabled = false; } Viewports.Add(new Viewport { Mask = val3, MaskImage = ((Component)val2).GetComponent<Image>(), RectMask = val4 }); } } private static void SliceFrame(Image image) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)image == (Object)null || (int)image.type != 0) { return; } Sprite sprite = image.sprite; if ((Object)(object)sprite == (Object)null || sprite.packed || sprite.border != Vector4.zero) { return; } Rect rect = sprite.rect; if (Mathf.Abs(((Rect)(ref rect)).width - FrameSpriteSize.x) > 0.5f || Mathf.Abs(((Rect)(ref rect)).height - FrameSpriteSize.y) > 0.5f) { return; } Rect rect2 = ((Graphic)image).rectTransform.rect; float width = ((Rect)(ref rect2)).width; float pixelsPerUnit = image.pixelsPerUnit; if (!(width <= 1f) && !(pixelsPerUnit <= 0f)) { if ((Object)(object)_slicedFrame == (Object)null || (Object)(object)_slicedFrameSource != (Object)(object)sprite) { _slicedFrame = Sprite.Create(sprite.texture, rect, new Vector2(sprite.pivot.x / ((Rect)(ref rect)).width, sprite.pivot.y / ((Rect)(ref rect)).height), sprite.pixelsPerUnit, 0u, (SpriteMeshType)0, FrameBorder); ((Object)_slicedFrame).name = ((Object)sprite).name + " (ChatPlus 9-slice)"; _slicedFrameSource = sprite; } image.sprite = _slicedFrame; image.type = (Type)1; image.fillCenter = true; image.pixelsPerUnitMultiplier = ((Rect)(ref rect)).width / (width * pixelsPerUnit); } } private static void CreateGrip(UIManager ui, RectTransform background) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0080: 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_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Expected O, but got Unknown //IL_00bc: 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) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("ChatPlus.ResizeHandle", new Type[1] { typeof(RectTransform) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent((Transform)(object)background, false); val2.anchorMin = Vector2.one; val2.anchorMax = Vector2.one; val2.pivot = new Vector2(0.5f, 0.5f); val2.sizeDelta = new Vector2(46f, 46f); val2.anchoredPosition = GripOffset; ((Transform)val2).SetAsLastSibling(); Image val3 = val.AddComponent<Image>(); val3.sprite = GripSprite(); ((Graphic)val3).raycastTarget = true; List<Image> list = GameAccess.PanelImages(ui); if (list != null) { if (list.Count > 0 && (Object)(object)list[0] != (Object)null) { Color color = ((Graphic)val3).color; color.a = ((Graphic)list[0]).color.a; ((Graphic)val3).color = color; } list.Add(val3); } val.AddComponent<ResizeGrip>(); val.SetActive(Plugin.Instance.ResizeHandle.Value); _grip = val; } private static Sprite GripSprite() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_gripSprite != (Object)null) { return _gripSprite; } _gripTexture = GripArt.Draw(); _gripSprite = Sprite.Create(_gripTexture, new Rect(0f, 0f, (float)((Texture)_gripTexture).width, (float)((Texture)_gripTexture).height), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0); ((Object)_gripSprite).name = "ChatPlus.ResizeHandle"; return _gripSprite; } internal static void Unload() { if ((Object)(object)_grip != (Object)null) { Object.Destroy((Object)(object)_grip); } _grip = null; if ((Object)(object)_gripSprite != (Object)null) { Object.Destroy((Object)(object)_gripSprite); } if ((Object)(object)_gripTexture != (Object)null) { Object.Destroy((Object)(object)_gripTexture); } _gripSprite = null; _gripTexture = null; } } internal static class GameAccess { private const float LookupInterval = 1f; private static TextChannelManager _chat; private static UIManager _ui; private static float _nextChatLookup; private static float _nextUiLookup; private static readonly List<string> MissingFields = new List<string>(); private static readonly FieldRef<TextChannelManager, TMP_Text> TextPrefabRef = Field<TextChannelManager, TMP_Text>("_textPrefab"); private static readonly FieldRef<TextChannelManager, List<GameObject>> GlobalLinesRef = Field<TextChannelManager, List<GameObject>>("_messageObjectsGlobal"); private static readonly FieldRef<TextChannelManager, List<GameObject>> LocalLinesRef = Field<TextChannelManager, List<GameObject>>("_messageObjectsLocal"); private static readonly FieldRef<TextChannelManager, string> OwnIdRef = Field<TextChannelManager, string>("_playerId"); private static readonly FieldRef<UIManager, RectTransform> PanelRootRef = Field<UIManager, RectTransform>("_messagePanelParent"); private static readonly FieldRef<UIManager, GameObject> PanelBackgroundRef = Field<UIManager, GameObject>("_messagePanel"); private static readonly FieldRef<UIManager, RectTransform> CornerBottomLeftRef = Field<UIManager, RectTransform>("_messageCornerTransform1"); private static readonly FieldRef<UIManager, Transform> GlobalScrollRef = Field<UIManager, Transform>("_globalMessagePanel"); private static readonly FieldRef<UIManager, Transform> LocalScrollRef = Field<UIManager, Transform>("_localMessagePanel"); private static readonly FieldRef<UIManager, List<Image>> PanelImagesRef = Field<UIManager, List<Image>>("_messagePanelImages"); private static readonly FieldRef<UIManager, float> LimitMinXRef = Field<UIManager, float>("_messageLimitMinX"); private static readonly FieldRef<UIManager, float> LimitMinYRef = Field<UIManager, float>("_messageLimitMinY"); private static readonly FieldRef<UIManager, float> LimitMaxXRef = Field<UIManager, float>("_messageLimitMaxX"); private static readonly FieldRef<UIManager, bool> MessageDragRef = Field<UIManager, bool>("_isMessageDrag"); private static readonly FieldRef<UIManager, TextMeshProUGUI> FontTextRef = Field<UIManager, TextMeshProUGUI>("_messageTextForFont"); private static readonly FieldRef<GameSettings, int> GlobalLimitRef = Field<GameSettings, int>("_globalMessageLimitCount"); private static readonly FieldRef<GameSettings, int> LocalLimitRef = Field<GameSettings, int>("_localMessageLimitCount"); public static TextChannelManager Chat => Lookup<TextChannelManager>(ref _chat, ref _nextChatLookup, (Func<TextChannelManager>)(() => NetworkSingleton<TextChannelManager>.I)); public static UIManager UI => Lookup<UIManager>(ref _ui, ref _nextUiLookup, (Func<UIManager>)(() => MonoSingleton<UIManager>.I)); public static string LocalPlayerName { get { TextChannelManager chat = Chat; if (!((Object)(object)chat != (Object)null)) { return string.Empty; } return chat.UserName ?? string.Empty; } } public static bool HasDragLimits { get { if (LimitMinXRef != null && LimitMinYRef != null) { return LimitMaxXRef != null; } return false; } } public static TMP_InputField MessageInput { get { UIManager uI = UI; if (!((Object)(object)uI != (Object)null)) { return null; } return uI.MessageInput; } } private static T Lookup<T>(ref T cached, ref float nextLookup, Func<T> find) where T : Object { if ((Object)(object)cached == (Object)null && Time.unscaledTime >= nextLookup) { nextLookup = Time.unscaledTime + 1f; try { cached = find(); } catch (Exception) { cached = default(T); } } return cached; } public static UIManager FindUI() { try { UIManager i = MonoSingleton<UIManager>.I; if ((Object)(object)i != (Object)null) { _ui = i; } return i; } catch (Exception) { return null; } } public static TextChannelManager FindChat() { try { TextChannelManager i = NetworkSingleton<TextChannelManager>.I; if ((Object)(object)i != (Object)null) { _chat = i; } return i; } catch (Exception) { return null; } } public static void SetChat(TextChannelManager chat) { if ((Object)(object)chat != (Object)null) { _chat = chat; } } public static void SetUI(UIManager ui) { if ((Object)(object)ui != (Object)null) { _ui = ui; } } public static void LogMissingFields() { if (MissingFields.Count > 0) { Plugin.Log.LogWarning((object)("Game fields not found (the game was probably updated), some features are off: " + string.Join(", ", MissingFields))); } } public static TMP_Text TextPrefab(TextChannelManager chat) { if (TextPrefabRef == null || !((Object)(object)chat != (Object)null)) { return null; } return TextPrefabRef.Invoke(chat); } public static List<GameObject> Lines(TextChannelManager chat, bool isLocal) { FieldRef<TextChannelManager, List<GameObject>> val = (isLocal ? LocalLinesRef : GlobalLinesRef); if (val == null || !((Object)(object)chat != (Object)null)) { return null; } return val.Invoke(chat); } public static string OwnSteamId(TextChannelManager chat) { if (OwnIdRef == null || !((Object)(object)chat != (Object)null)) { return string.Empty; } return OwnIdRef.Invoke(chat) ?? string.Empty; } public static RectTransform PanelRoot(UIManager ui) { return Get<RectTransform>(PanelRootRef, ui); } public static GameObject PanelBackground(UIManager ui) { return Get<GameObject>(PanelBackgroundRef, ui); } public static RectTransform CornerBottomLeft(UIManager ui) { return Get<RectTransform>(CornerBottomLeftRef, ui); } public static Transform GlobalScroll(UIManager ui) { return Get<Transform>(GlobalScrollRef, ui); } public static Transform LocalScroll(UIManager ui) { return Get<Transform>(LocalScrollRef, ui); } public static List<Image> PanelImages(UIManager ui) { return Get<List<Image>>(PanelImagesRef, ui); } public static void SetDragLimits(UIManager ui, float minX, float minY, float? maxX) { if (!((Object)(object)ui == (Object)null) && HasDragLimits) { LimitMinXRef.Invoke(ui) = minX; LimitMinYRef.Invoke(ui) = minY; if (maxX.HasValue) { LimitMaxXRef.Invoke(ui) = maxX.Value; } } } public static void SetPanelHeld(UIManager ui, bool held) { if ((Object)(object)ui != (Object)null && MessageDragRef != null) { MessageDragRef.Invoke(ui) = held; } } public static Transform Content(UIManager ui, bool isLocal) { if ((Object)(object)ui == (Object)null) { return null; } try { return isLocal ? ui.TextContentLocalTransform : ui.TextContentGlobalTransform; } catch (Exception) { return null; } } public static bool TryGetFontOutline(out bool outline) { outline = false; UIManager uI = UI; TextMeshProUGUI val = ((FontTextRef != null && (Object)(object)uI != (Object)null) ? FontTextRef.Invoke(uI) : null); Material val2 = (((Object)(object)val != (Object)null) ? ((TMP_Text)val).fontSharedMaterial : null); if ((Object)(object)val2 == (Object)null) { return false; } outline = val2.IsKeywordEnabled("UNDERLAY_ON"); return true; } public static void SetLineLimits(int global, int local) { if (GlobalLimitRef == null || LocalLimitRef == null) { return; } GameSettings i; try { i = ScriptableSingleton<GameSettings>.I; } catch (Exception) { return; } if (!((Object)(object)i == (Object)null)) { if (GlobalLimitRef.Invoke(i) != global) { GlobalLimitRef.Invoke(i) = global; } if (LocalLimitRef.Invoke(i) != local) { LocalLimitRef.Invoke(i) = local; } } } public static string SteamIdAt(int index) { try { PlayerPanelController i = NetworkSingleton<PlayerPanelController>.I; List<string> list = (((Object)(object)i != (Object)null) ? i.PlayerSteamIDs : null); return (list != null && index >= 0 && index < list.Count) ? (list[index] ?? string.Empty) : string.Empty; } catch (Exception) { return string.Empty; } } public static PlayerController PlayerBySteamId(string steamId) { if (string.IsNullOrEmpty(steamId)) { return null; } PlayerPanelController i = NetworkSingleton<PlayerPanelController>.I; if ((Object)(object)i == (Object)null || i.PlayerSteamIDs == null || i.PlayerControllers == null) { return null; } int num = i.PlayerSteamIDs.IndexOf(steamId); if (num < 0 || num >= i.PlayerControllers.Count) { return null; } return i.PlayerControllers[num]; } public static bool IsBlocked(string steamId) { if (string.IsNullOrEmpty(steamId)) { return false; } try { DataManager i = MonoSingleton<DataManager>.I; BanData val = (((Object)(object)i != (Object)null) ? i.BanData : null); if (val == null) { return false; } return (val.IgnorePlayers != null && val.IgnorePlayers.Contains(steamId)) || (val.MutedPlayers != null && val.MutedPlayers.Contains(steamId)); } catch (Exception) { return false; } } public static void OpenIdCard(string name, string steamId) { try { TemporaryPhoto i = MonoSingleton<TemporaryPhoto>.I; if (!((Object)(object)i == (Object)null)) { PlayerController val = PlayerBySteamId(steamId); if ((Object)(object)val != (Object)null) { i.CapturePhoto(val); } else { i.ButtonOpenIdCheck(name, steamId); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not open the ID card: " + ex.Message)); } } public static bool IsAnyTextFieldFocused() { EventSystem current = EventSystem.current; if ((Object)(object)current == (Object)null) { return false; } GameObject currentSelectedGameObject = current.currentSelectedGameObject; if ((Object)(object)currentSelectedGameObject == (Object)null) { return false; } TMP_InputField component = currentSelectedGameObject.GetComponent<TMP_InputField>(); if ((Object)(object)component != (Object)null && component.isFocused) { return true; } InputField component2 = currentSelectedGameObject.GetComponent<InputField>(); if ((Object)(object)component2 != (Object)null) { return component2.isFocused; } return false; } public static void Notify(string message) { TextChannelManager chat = Chat; if ((Object)(object)chat == (Object)null) { return; } string text = "<color=#9FD1FF>[ChatPlus]</color> <noparse>" + message + "</noparse>"; try { if (chat.Islocal && TryAddLocalLine(chat, text)) { return; } ChatLines.Suppress = true; try { chat.AddNotification(text); } finally { ChatLines.Suppress = false; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not show a chat notification: " + ex.Message)); } } private static bool TryAddLocalLine(TextChannelManager chat, string text) { Transform val = Content(UI, isLocal: true); TMP_Text val2 = TextPrefab(chat); List<GameObject> list = Lines(chat, isLocal: true); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || list == null) { return false; } TMP_Text val3 = Object.Instantiate<TMP_Text>(val2, val); Button component = ((Component)val3).GetComponent<Button>(); if ((Object)(object)component != (Object)null) { ((Selectable)component).interactable = false; } val3.text = text; ChatLine.Attach(val3, new ChatEntry { Time = DateTime.Now, IsLocal = true, Kind = ChatKind.Notification, Message = text, Line = text }); list.Add(((Component)val3).gameObject); ChatLines.Trim(list, Plugin.Instance.LocalLimit.Value); return true; } private static T Get<T>(FieldRef<UIManager, T> field, UIManager ui) where T : class { if (field == null || !((Object)(object)ui != (Object)null)) { return null; } return field.Invoke(ui); } private static FieldRef<TOwner, TField> Field<TOwner, TField>(string name) { try { return AccessTools.FieldRefAccess<TOwner, TField>(name); } catch (Exception) { MissingFields.Add(typeof(TOwner).Name + "." + name); return null; } } } internal static class HistoryFormat { public const string Header = "time\tchannel\ttype\tsteam_id\tname\tmessage\tline"; private const string RecordTime = "yyyy-MM-dd'T'HH:mm:ss"; private const string ReadableTime = "yyyy-MM-dd HH:mm:ss"; private const int Columns = 7; private static readonly CultureInfo Invariant = CultureInfo.InvariantCulture; public static string ToRecord(ChatEntry entry) { StringBuilder stringBuilder = new StringBuilder(64 + 2 * ((entry.Line ?? string.Empty).Length + (entry.Message ?? string.Empty).Length)); stringBuilder.Append(entry.Time.ToString("yyyy-MM-dd'T'HH:mm:ss", Invariant)).Append('\t').Append(entry.IsLocal ? 'L' : 'G') .Append('\t') .Append(KindCode(entry.Kind)) .Append('\t'); AppendEscaped(stringBuilder, entry.SteamId).Append('\t'); AppendEscaped(stringBuilder, entry.Name).Append('\t'); AppendEscaped(stringBuilder, entry.Message).Append('\t'); AppendEscaped(stringBuilder, entry.Line); return stringBuilder.ToString(); } public static bool TryParse(string record, out ChatEntry entry) { entry = null; if (string.IsNullOrEmpty(record)) { return false; } string[] array = record.Split('\t'); if (array.Length < 7) { return false; } if (!DateTime.TryParseExact(array[0], "yyyy-MM-dd'T'HH:mm:ss", Invariant, DateTimeStyles.None, out var result)) { return false; } bool isLocal; if (array[1] == "L") { isLocal = true; } else { if (!(array[1] == "G")) { return false; } isLocal = false; } if (!TryParseKind(array[2], out var kind)) { return false; } entry = new ChatEntry { Time = result, IsLocal = isLocal, Kind = kind, SteamId = Unescape(array[3]), Name = Unescape(array[4]), Message = Unescape(array[5]), Line = Unescape(array[6]) }; return true; } public static string ToReadable(ChatEntry entry) { StringBuilder stringBuilder = new StringBuilder(64 + (entry.Message ?? string.Empty).Length); stringBuilder.Append('[').Append(entry.Time.ToString("yyyy-MM-dd HH:mm:ss", Invariant)).Append("] [") .Append((entry.Kind == ChatKind.Notification) ? "Info" : (entry.IsLocal ? "Local" : "Global")) .Append("] "); if (entry.Kind != ChatKind.Notification) { stringBuilder.Append(TextUtil.Plain(entry.Name).Trim()).Append(": "); } stringBuilder.Append(TextUtil.Plain(entry.Message)); return stringBuilder.ToString(); } public static string Escape(string value) { if (!string.IsNullOrEmpty(value)) { return AppendEscaped(new StringBuilder(value.Length + 8), value).ToString(); } return string.Empty; } private static StringBuilder AppendEscaped(StringBuilder target, string value) { if (string.IsNullOrEmpty(value)) { return target; } foreach (char c in value) { switch (c) { case '\\': target.Append("\\\\"); break; case '\t': target.Append("\\t"); break; case '\n': target.Append("\\n"); break; case '\r': target.Append("\\r"); break; default: target.Append(c); break; } } return target; } public static string Unescape(string value) { if (string.IsNullOrEmpty(value) || value.IndexOf('\\') < 0) { return value ?? string.Empty; } StringBuilder stringBuilder = new StringBuilder(value.Length); for (int i = 0; i < value.Length; i++) { char c = value[i]; if (c != '\\' || i == value.Length - 1) { stringBuilder.Append(c); continue; } char c2 = value[++i]; switch (c2) { case '\\': stringBuilder.Append('\\'); break; case 't': stringBuilder.Append('\t'); break; case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; default: stringBuilder.Append('\\').Append(c2); break; } } return stringBuilder.ToString(); } private static char KindCode(ChatKind kind) { return kind switch { ChatKind.Own => 'O', ChatKind.Notification => 'N', _ => 'P', }; } private static bool TryParseKind(string code, out ChatKind kind) { switch (code) { case "P": kind = ChatKind.Player; return true; case "O": kind = ChatKind.Own; return true; case "N": kind = ChatKind.Notification; return true; default: kind = ChatKind.Player; return false; } } } public enum UiLanguage { Auto, English, Russian } internal sealed class Lang { public string Title; public string SectionTime; public string SectionHistory; public string SectionWindow; public string SectionExtras; public string ShowTime; public string TimeFormat; public string TimeColor; public string TimeSize; public string TimeOnNotifications; public string GlobalLimit; public string LocalLimit; public string LimitsHint; public string RestoreHistory; public string RestoreNotifications; public string SaveHistory; public string KeepAfterRestart; public string DailyLogs; public string HistoryCount; public string OpenFolder; public string ClearChat; public string ClearHistory; public string ConfirmClear; public string Width; public string Height; public string TextSize; public string ResizeHandle; public string ResizeHint; public string RememberPosition; public string CullHiddenLines; public string ResetWindow; public string HighlightMentions; public string HighlightColor; public string Keywords; public string KeywordsHint; public string RecallSent; public string RightClickCopy; public string Close; public string WindowHint; public string None; public string Custom; public string[] ColorNames; public string EarlierMessages; public string Copied; public string DateFormat; public string On; public string Off; public string TimeOn; public string TimeOff; public string TimeFormatSet; public string SizeSet; public string TextSizeSet; public string LimitsSet; public string ChatCleared; public string HistoryCleared; public string WindowReset; public string Status; public string KeywordAdded; public string KeywordRemoved; public string KeywordMissing; public string KeywordsList; public string KeywordsCleared; public string UsageTime; public string UsageSize; public string UsageText; public string UsageLimit; public string UsageKeyword; public string Help; public static readonly Lang English = new Lang { Title = "Chat Plus", SectionTime = "Message time", SectionHistory = "History", SectionWindow = "Chat window", SectionExtras = "Extras", ShowTime = "Show when each message was sent", TimeFormat = "Format", TimeColor = "Color", TimeSize = "Size", TimeOnNotifications = "Also for game notifications", GlobalLimit = "Lines in Global", LocalLimit = "Lines in Local", LimitsHint = "The game itself keeps 50 and 25 lines.", RestoreHistory = "Bring earlier messages back after a lobby change", RestoreNotifications = "Including game notifications", SaveHistory = "Save the history to a file", KeepAfterRestart = "Keep the history after a game restart", DailyLogs = "Also write readable logs, one file per day", HistoryCount = "This session's history: {0} lines", OpenFolder = "Open folder", ClearChat = "Clear chat", ClearHistory = "Clear history", ConfirmClear = "Click again to clear", Width = "Width", Height = "Height", TextSize = "Text size", ResizeHandle = "Resize handle on the chat", ResizeHint = "Drag the handle at the chat's top-right corner; double-click it for the normal size.", RememberPosition = "Remember where the chat was moved", CullHiddenLines = "Don't draw lines scrolled out of view (faster)", ResetWindow = "Reset size and position", HighlightMentions = "Highlight messages that mention you", HighlightColor = "Highlight", Keywords = "Keywords", KeywordsHint = "Your name always counts. More words: /chatplus keyword add <word>", RecallSent = "Up / Down in the input field: your previous messages", RightClickCopy = "Right-click a message to copy it", Close = "Close", WindowHint = "{0}: this window · /chatplus help: commands", None = "none", Custom = "Custom", ColorNames = new string[7] { "Soft", "Gray", "Gold", "Sky", "Mint", "Rose", "White" }, EarlierMessages = "<align=center><size=80%><color=#F5EDE18C>— earlier messages —</color></size></align>", Copied = "Copied", DateFormat = "MM/dd", On = "on", Off = "off", TimeOn = "Message time: on", TimeOff = "Message time: off", TimeFormatSet = "Time format: {0} (looks like {1})", SizeSet = "Chat size: {0}% × {1}%", TextSizeSet = "Text size: {0}%", LimitsSet = "Lines kept: Global {0}, Local {1}", ChatCleared = "Chat cleared ({0} lines). The history still has them.", HistoryCleared = "History cleared", WindowReset = "Chat size and position reset", Status = "Time {0} ({1}), lines Global {2} / Local {3}, size {4}% × {5}%, text {6}%, history {7} lines, file {8}", KeywordAdded = "Keyword added: {0}", KeywordRemoved = "Keyword removed: {0}", KeywordMissing = "No such keyword: {0}", KeywordsList = "Keywords: {0}", KeywordsCleared = "Keywords cleared", UsageTime = "Usage: /chatplus time on | off | format <format>, e.g. HH:mm:ss or h:mm tt", UsageSize = "Usage: /chatplus size <width %> [height %] | reset", UsageText = "Usage: /chatplus text <size %>", UsageLimit = "Usage: /chatplus lines <Global> [Local]", UsageKeyword = "Usage: /chatplus keyword add | remove <word>, /chatplus keyword list | clear", Help = "Chat Plus, commands:\n/chatplus - settings window ({0})\n/chatplus time on | off | format <format>\n/chatplus size <width %> [height %] | reset\n/chatplus text <size %>\n/chatplus lines <Global> [Local] - lines kept in the chat\n/chatplus clear - clear the chat, /chatplus clearhistory - forget the history\n/chatplus folder - open the history folder\n/chatplus keyword add | remove <word>, keyword list | clear\n/chatplus status" }; public static readonly Lang Russian = new Lang { Title = "Улучшенный чат", SectionTime = "Время сообщений", SectionHistory = "История", SectionWindow = "Окно чата", SectionExtras = "Дополнительно", ShowTime = "Показывать время каждого сообщения", TimeFormat = "Формат", TimeColor = "Цвет", TimeSize = "Размер", TimeOnNotifications = "И у уведомлений игры", GlobalLimit = "Строк в «Глобальном»", LocalLimit = "Строк в «Локальном»", LimitsHint = "Сама игра хранит 50 и 25 строк.", RestoreHistory = "Возвращать прошлые сообщения после смены лобби", RestoreNotifications = "Вместе с уведомлениями игры", SaveHistory = "Сохранять историю в файл", KeepAfterRestart = "Не очищать историю при перезапуске игры", DailyLogs = "Ещё и текстовые логи, по файлу на день", HistoryCount = "В истории этой сессии: {0} строк", OpenFolder = "Открыть папку", ClearChat = "Очистить чат", ClearHistory = "Очистить историю", ConfirmClear = "Нажмите ещё раз", Width = "Ширина", Height = "Высота", TextSize = "Размер текста", ResizeHandle = "Уголок для изменения размера", ResizeHint = "Тяните уголок в правом верхнем углу чата; двойной щелчок по нему — обычный размер.", RememberPosition = "Запоминать, куда перетащен чат", CullHiddenLines = "Не рисовать прокрученные строки (быстрее)", ResetWindow = "Сбросить размер и положение", HighlightMentions = "Подсвечивать сообщения, где упомянуты вы", HighlightColor = "Подсветка", Keywords = "Слова", KeywordsHint = "Ваше имя учитывается всегда. Ещё слова: /chatplus keyword add <слово>", RecallSent = "Вверх / вниз в поле ввода: ваши прошлые сообщения", RightClickCopy = "Правый щелчок по сообщению копирует его", Close = "Закрыть", WindowHint = "{0}: это окно · /chatplus help: команды", None = "нет", Custom = "Свой", ColorNames = new string[7] { "Мягкий", "Серый", "Золотой", "Голубой", "Мятный", "Розовый", "Белый" }, EarlierMessages = "<align=center><size=80%><color=#F5EDE18C>— прошлые сообщения —</color></size></align>", Copied = "Скопировано", DateFormat = "dd.MM", On = "вкл", Off = "выкл", TimeOn = "Время сообщений: вкл", TimeOff = "Время сообщений: выкл", TimeFormatSet = "Формат времени: {0} (выглядит так: {1})", SizeSet = "Размер чата: {0}% × {1}%", TextSizeSet = "Размер текста: {0}%", LimitsSet = "Хранится строк: «Глобальный» {0}, «Локальный» {1}", ChatCleared = "Чат очищен ({0} строк). В истории они остались.", HistoryCleared = "История очищена", WindowReset = "Размер и положение чата сброшены", Status = "Время {0} ({1}), строк «Глобальный» {2} / «Локальный» {3}, размер {4}% × {5}%, текст {6}%, в истории {7} строк, файл {8}", KeywordAdded = "Добавлено: {0}", KeywordRemoved = "Удалено: {0}", KeywordMissing = "Такого слова нет: {0}", KeywordsList = "Слова: {0}", KeywordsCleared = "Слова удалены", UsageTime = "Использование: /chatplus time on | off | format <формат>, например HH:mm:ss или h:mm tt", UsageSize = "Использование: /chatplus size <ширина %> [высота %] | reset", UsageText = "Использование: /chatplus text <размер %>", UsageLimit = "Использование: /chatplus lines <Глобальный> [Локальный]", UsageKeyword = "Использование: /chatplus keyword add | remove <слово>, /chatplus keyword list | clear", Help = "Улучшенный чат, команды:\n/chatplus - окно настроек ({0})\n/chatplus time on | off | format <формат> - время сообщений\n/chatplus size <ширина %> [высота %] | reset - размер окна чата\n/chatplus text <размер %> - размер текста\n/chatplus lines <Глобальный> [Локальный] - сколько строк хранить в чате\n/chatplus clear - очистить чат, /chatplus clearhistory - стереть историю\n/chatplus folder - открыть папку с историей\n/chatplus keyword add | remove <слово>, keyword list | clear - слова для подсветки\n/chatplus status - текущие настройки" }; private static Lang _auto = English; private static float _nextAutoCheck; public static Lang Current { get { Plugin instance = Plugin.Instance; switch (((Object)(object)instance != (Object)null) ? instance.Language.Value : UiLanguage.Auto) { case UiLanguage.English: return English; case UiLanguage.Russian: return Russian; default: if (Time.unscaledTime >= _nextAutoCheck) { _nextAutoCheck = Time.unscaledTime + 10f; _auto = (DetectRussian() ? Russian : English); } return _auto; } } } public static void Invalidate() { _nextAutoCheck = 0f; } public string ColorName(int index) { if (index < 0 || index >= ColorNames.Length) { return Custom; } return ColorNames[index]; } private static bool DetectRussian() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 try { string text = GameLocaleCode(); if (!string.IsNullOrEmpty(text)) { return text.StartsWith("ru", StringComparison.OrdinalIgnoreCase); } } catch (Exception) { } return (int)Application.systemLanguage == 30; } [MethodImpl(MethodImplOptions.NoInlining)] private static string GameLocaleCode() { //IL_0012: 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) Locale selectedLocale = LocalizationSettings.SelectedLocale; if (!((Object)(object)selectedLocale != (Object)null)) { return null; } LocaleIdentifier identifier = selectedLocale.Identifier; return ((LocaleIdentifier)(ref identifier)).Code; } } internal sealed class LineSizer : MonoBehaviour { private const float DefaultBaseSize = 46.8f; private const int RecentLines = 24; internal static float BaseSize = 46.8f; internal static int Version; private int _count = -1; private int _newestId; private int _version = -1; private static int Percent { get { Plugin instance = Plugin.Instance; if (!((Object)(object)instance != (Object)null)) { return 100; } return instance.TextSize.Value; } } private static float TargetSize => BaseSize * (float)Percent / 100f; internal static void Apply(TMP_Text text) { if (!((Object)(object)text == (Object)null) && Percent != 100) { float targetSize = TargetSize; if (Math.Abs(text.fontSize - targetSize) > 0.01f) { text.fontSize = targetSize; } } } private void LateUpdate() { Transform transform = ((Component)this).transform; int childCount = transform.childCount; int num = ((childCount > 0) ? ((Object)transform.GetChild(childCount - 1)).GetInstanceID() : 0); bool flag = _version != Version; if (!flag && childCount == _count && num == _newestId) { return; } _count = childCount; _newestId = num; _version = Version; if (!flag && Percent == 100) { return; } float targetSize = TargetSize; for (int i = ((!flag) ? Math.Max(0, childCount - 24) : 0); i < childCount; i++) { TMP_Text component = ((Component)transform.GetChild(i)).GetComponent<TMP_Text>(); if ((Object)(object)component != (Object)null && Math.Abs(component.fontSize - targetSize) > 0.01f) { component.fontSize = targetSize; } } } } internal static class OutlineResets { private static TextChannelManager _chat; private static bool _outline; private static int _extra; internal static bool ShouldRun(TextChannelManager chat) { if (!GameAccess.TryGetFontOutline(out var outline)) { return true; } if (chat != _chat) { _chat = chat; _outline = outline; _extra = 1; return true; } if (outline != _outline) { _outline = outline; _extra = 1; return true; } if (_extra > 0) { _extra--; return true; } return false; } } internal sealed class LineMark { public GameObject Newest; public static LineMark Take(TextChannelManager chat, bool isLocal) { List<GameObject> list = GameAccess.Lines(chat, isLocal); return new LineMark { Newest = ((list != null && list.Count > 0) ? list[list.Count - 1] : null) }; } } [HarmonyPatch(typeof(TextChannelManager), "AddMessageUI")] internal static class AddMessagePatch { [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix(TextChannelManager __instance, bool isLocal, out LineMark __state) { __state = null; try { ChatLines.ApplyLimits(); __state = LineMark.Take(__instance, isLocal); } catch (Exception e) { Plugin.LogError("Could not set the chat line limits: ", e); } } [HarmonyPostfix] [HarmonyPriority(800)] private static void Postfix(TextChannelManager __instance, string userName, string text, bool isLocal, int senderIndex, bool sendByPlayer, LineMark __state) { try { ChatLines.OnMessageAdded(__instance, userName, text, isLocal, senderIndex, sendByPlayer, __state); } catch (Exception e) { Plugin.LogError("Could not process a chat message: ", e); } } } [HarmonyPatch(typeof(TextChannelManager), "AddNotification")] internal static class AddNotificationPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix(TextChannelManager __instance, out LineMark __state) { __state = null; try { ChatLines.ApplyLimits(); __state = LineMark.Take(__instance, isLocal: false); } catch (Exception e) { Plugin.LogError("Could not set the chat line limits: ", e); } } [HarmonyPostfix] [HarmonyPriority(800)] private static void Postfix(TextChannelManager __instance, string text, LineMark __state) { try { ChatLines.OnNotificationAdded(__instance, text, __state); } catch (Exception e) { Plugin.LogError("Could not process a chat notification: ", e); } } } [HarmonyPatch(typeof(TextChannelManager), "Start")] internal static class ChatStartPatch { [HarmonyPostfix] private static void Postfix(TextChannelManager __instance) { try { ChatLines.OnChatStarted(__instance); } catch (Exception e) { Plugin.LogError("Could not restore the chat history: ", e); } } } [HarmonyPatch(typeof(TextChannelManager), "ResetTextMaterials")] internal static class ResetMaterialsPatch { [HarmonyPrefix] private static bool Prefix(TextChannelManager __instance) { try { return OutlineResets.ShouldRun(__instance); } catch (Exception e) { Plugin.LogError("Could not check the chat font outline: ", e); return true; } } } [HarmonyPatch(typeof(UIManager), "Start")] internal static class UiStartPatch { [HarmonyPostfix] private static void Postfix(UIManager __instance) { try { ChatWindow.Prepare(__instance); } catch (Exception e) { Plugin.LogError("Could not set up the chat window: ", e); } } } [HarmonyPatch(typeof(UIManager), "BeginDragMessage")] internal static class BeginMovePatch { [HarmonyPostfix] private static void Postfix(UIManager __instance) { try { ChatWindow.OnBeginMove(__instance); } catch (Exception e) { Plugin.LogError("Could not update the chat drag limits: ", e); } } } [HarmonyPatch(typeof(UIManager), "EndDragMessage")] internal static class EndMovePatch { [HarmonyPostfix] private static void Postfix(UIManager __instance) { try { ChatWindow.OnEndMove(__instance); } catch (Exception e) { Plugin.LogError("Could not save the chat position: ", e); } } } [HarmonyPatch(typeof(TextChannelManager), "OnEnterPressed")] internal static class EnterPatch { [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyBefore(new string[] { "ontogether.chatsounds", "ontogether.localchatrange", "com.on-together-mods.commandapi", "com.andrewlin.ontogether.alpha" })] private static void Prefix() { string[] args; try { TMP_InputField messageInput = GameAccess.MessageInput; if ((Object)(object)messageInput == (Object)null) { return; } string text = messageInput.text; Plugin.Instance.Sent.Add(text); if (!ChatCommands.TryParse(text, out args)) { return; } messageInput.text = string.Empty; } catch (Exception e) { Plugin.LogError("Could not read the chat input: ", e); return; } try { ChatCommands.Execute(args); } catch (Exception e2) { Plugin.LogError("Chat command failed: ", e2); } } } [BepInPlugin("ontogether.chatplus", "ChatPlus", "1.0.0")] [BepInProcess("OnTogether.exe")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "ontogether.chatplus"; public const string PluginName = "ChatPlus"; public const string PluginVersion = "1.0.0"; internal const int MinWidth = 70; internal const int MaxWidth = 300; internal const int MinHeight = 50; internal const int MaxHeight = 200; internal const int MinTextSize = 60; internal const int MaxTextSize = 200; private const int HistoryCapacity = 5000; private const int SentCapacity = 50; private const float ConfigPollInterval = 1f; private const float ConfigSettleTime = 0.5f; private const float SaveDelay = 0.75f; private const float RetryDelay = 2f; private const float RestyleDelay = 0.2f; internal ConfigEntry<UiLanguage> Language; internal ConfigEntry<KeyboardShortcut> WindowKey; internal ConfigEntry<bool> Timestamps; internal ConfigEntry<string> TimeFormat; internal ConfigEntry<string> TimeColor; internal ConfigEntry<int> TimeSize; internal ConfigEntry<bool> TimeOnNotifications; internal ConfigEntry<int> GlobalLimit; internal ConfigEntry<int> LocalLimit; internal ConfigEntry<bool> RestoreHistory; internal ConfigEntry<bool> RestoreNotifications; internal ConfigEntry<bool> SaveHistory; internal ConfigEntry<bool> KeepAfterRestart; internal ConfigEntry<bool> DailyLogs; internal ConfigEntry<int> Width; internal ConfigEntry<int> Height; internal ConfigEntry<int> TextSize; internal ConfigEntry<bool> ResizeHandle; internal ConfigEntry<bool> RememberPosition; internal ConfigEntry<string> Position; internal ConfigEntry<bool> CullHiddenLines; internal ConfigEntry<bool> HighlightMentions; internal ConfigEntry<string> HighlightColor; internal ConfigEntry<string> Keywords; internal ConfigEntry<bool> RecallSent; internal ConfigEntry<bool> RightClickCopy; private GameObject _host; private SettingsWindow _window; private Harmony _harmony; private List<string> _keywords = new List<string>(); private TMP_InputField _caretToEnd; private readonly Dictionary<ConfigEntryBase, object> _unsaved = new Dictionary<ConfigEntryBase, object>(); private bool _reloading; private float _saveAt = -1f; private float _restyleAt = -1f; private float _resizeTextAt = -1f; private float _nextConfigPoll; private float _reloadAt = -1f; private DateTime _configStamp; private DateTime _pendingStamp; private bool _fileErrorLogged; private static string _lastError; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal ChatHistory History { get; private set; } internal SentHistory Sent { get; } = new SentHistory(50); internal IList<string> KeywordList => _keywords; private void Awake() { //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown //IL_023b: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; BindConfig(); ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; _keywords = TextUtil.ParseKeywords(Keywords.Value); _configStamp = ConfigStamp(); History = new ChatHistory(Path.Combine(Paths.BepInExRootPath, "ChatPlus"), 5000, delegate(string message) { Log.LogWarning((object)message); }); History.DailyLogs = DailyLogs.Value; History.Open(SaveHistory.Value && KeepAfterRestart.Value, SaveHistory.Value); if (History.Count > 0) { Log.LogInfo((object)$"Loaded {History.Count} chat lines from the previous session."); } _host = new GameObject("ChatPlus") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)(object)_host); _window = _host.AddComponent<SettingsWindow>(); ((Behaviour)_window).enabled = false; _host.AddComponent<Toast>(); _harmony = new Harmony("ontogether.chatplus"); Patch(typeof(AddMessagePatch), "new messages get no time and are not saved"); Patch(typeof(AddNotificationPatch), "notifications get no time and are not saved"); Patch(typeof(ChatStartPatch), "the history is not brought back after a lobby change"); Patch(typeof(ResetMaterialsPatch), "a long chat is slower when it fades in and out"); Patch(typeof(UiStartPatch), "the chat window cannot be resized"); Patch(typeof(BeginMovePatch), "a resized chat may not be dragged to the right edge"); Patch(typeof(EndMovePatch), "the chat position is not remembered"); Patch(typeof(EnterPatch), "the /chatplus co