Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TerraChat v1.2.1
plugins/TerraChat/TerraChat.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net.WebSockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Steamworks; using TMPro; using TerraChat.Chat; using TerraChat.Net; using TerraChat.UI; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.TextCore.LowLevel; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("TerraChat")] [assembly: AssemblyProduct("TerraChat")] [assembly: AssemblyFileVersion("1.2.1")] [assembly: AssemblyDescription("Created by R4V9N1")] [assembly: AssemblyVersion("1.2.1.0")] namespace TerraChat { [BepInPlugin("r4v9n1.terrachat", "TerraChat", "1.2.1")] public sealed class TerraChatPlugin : BaseUnityPlugin { public const string PluginGuid = "r4v9n1.terrachat"; public const string PluginName = "TerraChat"; public const string PluginVersion = "1.2.1"; public const string CreatorCredit = "Created by R4V9N1"; public const int ProtocolVersion = 1; public const string DefaultServerUrl = "wss://chat.100club.is"; private static readonly Regex NamePattern = new Regex("^[A-Za-z0-9 _-]{3,24}$"); internal ConfigEntry<bool> Enabled; internal ConfigEntry<bool> StayOnline; internal ConfigEntry<string> ServerUrl; internal ConfigEntry<string> Token; internal ConfigEntry<string> MutedChannels; internal ConfigEntry<float> WindowX; internal ConfigEntry<float> WindowY; internal ConfigEntry<float> WindowWidth; internal ConfigEntry<float> WindowHeight; internal ConfigEntry<float> FontSize; internal ConfigEntry<float> IdleOpacity; internal ConfigEntry<float> BackgroundOpacity; internal readonly ChatState State = new ChatState(); internal RelayClient Relay; internal string ConnectedName = string.Empty; private PmHistory pmHistory; private readonly SteamTicket steamTicket = new SteamTicket(); private bool waitingForTicket; private string characterKey; private ChatWindow window; internal static ManualLogSource Log { get; private set; } internal static TerraChatPlugin Instance { get; private set; } internal bool WindowActive { get { if (window != null) { return window.Active; } return false; } } internal bool HasWindow => window != null; private void Awake() { //IL_0259: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Use the TerraChat window instead of Valheim's chat box."); StayOnline = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Online", true, "Connect to TerraChat. Off = offline mode (Local chat only); toggle by clicking Online/Offline in the window."); ServerUrl = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ServerUrl", "wss://chat.100club.is", "TerraChat relay address (wss://...)."); Token = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Token", string.Empty, "Secret that owns your chat name. Generated automatically; never share it."); MutedChannels = ((BaseUnityPlugin)this).Config.Bind<string>("Channels", "Muted", string.Empty, "Muted global channel numbers, e.g. 2,3."); WindowX = ((BaseUnityPlugin)this).Config.Bind<float>("Window", "X", -1f, "Window left edge (-1 = bottom-right corner of the screen)."); WindowY = ((BaseUnityPlugin)this).Config.Bind<float>("Window", "Y", -1f, "Window bottom edge."); WindowWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Window", "Width", 520f, "Window width."); WindowHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Window", "Height", 260f, "Window height."); FontSize = ((BaseUnityPlugin)this).Config.Bind<float>("Window", "FontSize", 16f, "Chat text size."); IdleOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("Window", "IdleOpacity", 0.55f, "Window opacity when idle (0.1-1)."); BackgroundOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("Window", "BackgroundOpacity", 0.8f, "Background opacity (0-1)."); if (!Regex.IsMatch(Token.Value ?? string.Empty, "^[0-9a-f]{32}$")) { Token.Value = NewToken(); } State.SetMutedFromConfig(MutedChannels.Value); Relay = new RelayClient(delegate(string line) { Log.LogWarning((object)line); }); pmHistory = new PmHistory(Path.Combine(Paths.ConfigPath, "TerraChat", "pm-history")); new Harmony("r4v9n1.terrachat").PatchAll(typeof(TerraChatPlugin).Assembly); Log.LogInfo((object)"TerraChat 1.2.1 loaded."); } private void Update() { if (!Enabled.Value) { return; } if ((Object)(object)Player.m_localPlayer != (Object)null && !Relay.Running && StayOnline.Value) { Connect(); } if ((Object)(object)Player.m_localPlayer != (Object)null) { LoadCharacterHistory(); } for (int i = 0; i < 50; i++) { if (!Relay.TryReceive(out var message)) { break; } HandleRelayMessage(message); } if (window != null && !window.IsValid) { window.Dispose(); window = null; } if (window == null && (Object)(object)Chat.instance != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null) { window = ChatWindow.TryCreate(this); } if (window != null) { window.Tick(); } } private void OnDestroy() { if (Relay != null) { Relay.Stop(); } steamTicket.Dispose(); } private void Connect() { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: 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_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Expected O, but got Unknown string text = ResolveName(); if (string.IsNullOrEmpty(ServerUrl.Value) || ServerUrl.Value.Contains("example.com")) { Relay.Stop(); return; } if (!waitingForTicket) { steamTicket.Request(); waitingForTicket = true; } steamTicket.Tick(); if (!steamTicket.Pending) { waitingForTicket = false; if (steamTicket.Ticket == null) { Log.LogWarning((object)"TerraChat could not get a Steam ticket; the server will only accept an already verified player."); } ConnectedName = text; JObject val = new JObject { ["t"] = JToken.op_Implicit("hello"), ["v"] = JToken.op_Implicit(1), ["name"] = JToken.op_Implicit(text), ["token"] = JToken.op_Implicit(Token.Value), ["mod"] = JToken.op_Implicit("1.2.1") }; if (steamTicket.Ticket != null) { val["steam"] = JToken.op_Implicit(steamTicket.Ticket); } Relay.Start(ServerUrl.Value, val); } } private string ResolveName() { string text = string.Empty; if ((Object)(object)Game.instance != (Object)null && Game.instance.GetPlayerProfile() != null) { text = Regex.Replace(Game.instance.GetPlayerProfile().GetName() ?? string.Empty, "[^A-Za-z0-9 _-]", "").Trim(); } if (!NamePattern.IsMatch(text)) { text = "Viking" + Token.Value.Substring(0, 4); } return text; } private void HandleRelayMessage(JObject m) { string text = (string)m["t"]; if (text == null) { return; } switch (text.Length) { case 7: switch (text[0]) { case 'w': { if (!(text == "welcome")) { break; } ConnectedName = ((string)m["name"]) ?? ConnectedName; JToken obj = m["channels"]; JArray val = (JArray)(object)((obj is JArray) ? obj : null); if (val != null) { State.SetChannels(((IEnumerable<JToken>)val).Select((JToken c) => Tuple.Create((int)c[(object)"id"], (string)c[(object)"name"]))); foreach (JToken item in val) { State.SetChannelInfo((int)item[(object)"id"], (string)item[(object)"label"], (string)item[(object)"bridge"] == "discord"); } } State.SetDiscordLinked((string)m["discordLinked"]); State.AddSystem("TerraChat connected as " + ConnectedName + ".", "local"); Log.LogInfo((object)("TerraChat connected as " + ConnectedName + ".")); string text2 = (string)m["role"]; if (text2 == "admin" || text2 == "mod") { State.AddSystem("You are a TerraChat " + ((text2 == "admin") ? "admin. Moderation: /tc mute Name [minutes], /tc unmute Name, /tc kick Name, /tc ban Name [minutes], /tc unban Name. Admins: /tc mod Name, /tc unmod Name." : "moderator. Moderation: /tc mute Name [minutes], /tc unmute Name, /tc kick Name, /tc ban Name [minutes], /tc unban Name."), "local"); } string text3 = (string)m["motd"]; if (!string.IsNullOrEmpty(text3)) { State.AddSystem(text3, "local"); } break; } case 'd': if (text == "discord") { State.AddPmNotice("Join our Discord: " + (string)m["invite"] + " (click the link to copy it)"); State.Select("pm"); } break; } break; case 3: switch (text[0]) { case 'm': if (text == "msg") { State.AddChannelMessage((int)m["ch"], (string)m["from"], (string)m["text"], (string)m["src"] == "discord"); } break; case 'w': if (text == "who") { JToken obj2 = m["players"]; string[] array = ((IEnumerable<JToken>)((obj2 is JArray) ? obj2 : null))?.Select((JToken p) => (string)p).ToArray() ?? new string[0]; State.AddSystem("Online (" + array.Length + "): " + string.Join(", ", array)); } break; } break; case 6: switch (text[0]) { case 'l': if (text == "linked") { string text6 = (string)m["discord"]; State.SetDiscordLinked(text6); State.AddSystem((text6 != null) ? ("Linked to Discord as " + text6 + ". Your messages in Discord now show your Discord name and picture.") : "Discord link removed.", (State.DiscordTab != null) ? State.DiscordTab.Id : null); } break; case 's': if (text == "system") { State.AddSystem((string)m["text"]); } break; } break; case 2: if (text == "pm") { State.AddPrivateMessage((string)m["from"], (string)m["to"], (string)m["text"], ConnectedName); SavePm(m); } break; case 4: if (text == "link") { string text4 = (GUIUtility.systemCopyBuffer = "!link " + (string)m["code"]); State.SetPendingLinkCode(text4); State.AddSystem("Link your Discord profile: " + text4 + " is copied. In Discord, open the channel linked to this tab, paste it (Ctrl+V) and send it within " + (int?)m["minutes"] + " minutes. You only do this once.", (State.DiscordTab != null) ? State.DiscordTab.Id : null); } break; case 5: if (text == "error") { State.AddSystem("TerraChat: " + (string)m["text"]); Log.LogWarning((object)("TerraChat server error " + (string)m["code"] + ": " + (string)m["text"])); } break; } } private void LoadCharacterHistory() { string text = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null)?.m_filename; if (string.IsNullOrEmpty(text) || text == characterKey) { return; } characterKey = text; try { State.LoadPmHistory(pmHistory.Load(text)); } catch (Exception ex) { Log.LogWarning((object)("TerraChat could not read PM history: " + ex.Message)); } } private void SavePm(JObject m) { if (string.IsNullOrEmpty(characterKey)) { return; } try { string text = (string)m["from"]; pmHistory.Append(characterKey, new PmEntry { Ts = (((long?)m["ts"]) ?? DateTimeOffset.UtcNow.ToUnixTimeSeconds()), From = text, To = (string)m["to"], Text = (string)m["text"], Outgoing = string.Equals(text, ConnectedName, StringComparison.OrdinalIgnoreCase) }); } catch (Exception ex) { Log.LogWarning((object)("TerraChat could not save PM history: " + ex.Message)); } } internal void Submit(string input) { //IL_012f: 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_0149: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Expected O, but got Unknown //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Expected O, but got Unknown //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Expected O, but got Unknown //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Expected O, but got Unknown //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Expected O, but got Unknown ChatCommand chatCommand = CommandParser.Parse(input, State.ActiveTab, State.LastPmPartner); switch (chatCommand.Kind) { case ChatCommandKind.None: return; case ChatCommandKind.Local: SendLocal(chatCommand.Text); return; case ChatCommandKind.Help: State.AddSystem("TerraChat: Enter opens chat, Tab switches tabs. /g1 /g2 /g3 text = global channel, /g4 text = Heim (linked to Discord), /pm Name text (\"quoted name\" if it has spaces), /r text = reply, /who = online players, /discord = our Discord invite, /tc mute 2 | /tc unmute 2 = mute a channel, /tc link | /tc unlink = Discord profile, /tc help. Other / commands go to Valheim chat."); return; case ChatCommandKind.Error: State.AddSystem(chatCommand.Text); return; case ChatCommandKind.MuteChannel: case ChatCommandKind.UnmuteChannel: SetChannelMuted(chatCommand.Channel, chatCommand.Kind == ChatCommandKind.MuteChannel); return; } if (Relay.Status != "Online") { State.AddSystem((!StayOnline.Value) ? "You are offline. Click Offline in the title bar to go online." : ((Relay.Status == "Stopped") ? "TerraChat is stopped (see the message above)." : "TerraChat is offline; trying to reconnect.")); return; } switch (chatCommand.Kind) { case ChatCommandKind.Channel: Relay.Send(new JObject { ["t"] = JToken.op_Implicit("say"), ["ch"] = JToken.op_Implicit(chatCommand.Channel), ["text"] = JToken.op_Implicit(chatCommand.Text) }); break; case ChatCommandKind.PrivateMessage: Relay.Send(new JObject { ["t"] = JToken.op_Implicit("pm"), ["to"] = JToken.op_Implicit(chatCommand.Target), ["text"] = JToken.op_Implicit(chatCommand.Text) }); break; case ChatCommandKind.Who: Relay.Send(new JObject { ["t"] = JToken.op_Implicit("who") }); break; case ChatCommandKind.DiscordInvite: RequestDiscordInvite(); break; case ChatCommandKind.LinkDiscord: RequestDiscordLink(); break; case ChatCommandKind.UnlinkDiscord: Relay.Send(new JObject { ["t"] = JToken.op_Implicit("unlink") }); break; case ChatCommandKind.Admin: { JObject val = new JObject { ["t"] = JToken.op_Implicit("admin"), ["cmd"] = JToken.op_Implicit(chatCommand.AdminCommand), ["target"] = JToken.op_Implicit(chatCommand.Target) }; if (chatCommand.Minutes > 0.0) { val["minutes"] = JToken.op_Implicit(chatCommand.Minutes); } Relay.Send(val); break; } case ChatCommandKind.MuteChannel: case ChatCommandKind.UnmuteChannel: break; } } internal void SetOnline(bool online) { StayOnline.Value = online; if (online) { State.AddSystem("Going online..."); if ((Object)(object)Player.m_localPlayer != (Object)null && !Relay.Running) { Connect(); } } else { Relay.Stop(); State.AddSystem("You are offline. Click Offline in the title bar to go online again."); } } internal void RequestDiscordLink() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown if (Relay.Status != "Online") { State.AddSystem("TerraChat is offline; try linking again when it shows Online."); return; } Relay.Send(new JObject { ["t"] = JToken.op_Implicit("link") }); } internal void RequestDiscordInvite() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown if (Relay.Status != "Online") { State.AddSystem("TerraChat is offline; try /discord again when it shows Online."); return; } Relay.Send(new JObject { ["t"] = JToken.op_Implicit("discord") }); } internal void SetChannelMuted(int channel, bool muted) { if (State.ChannelTab(channel) == null) { State.AddSystem("No channel " + channel + "."); return; } State.SetMuted(channel, muted); MutedChannels.Value = State.MutedToConfig(); } private static void SendLocal(string text) { Chat instance = Chat.instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)((Terminal)instance).m_input == (Object)null)) { ((TMP_InputField)((Terminal)instance).m_input).text = text; instance.SendInput(); } } private static string NewToken() { byte[] array = new byte[16]; using (RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create()) { randomNumberGenerator.GetBytes(array); } StringBuilder stringBuilder = new StringBuilder(32); byte[] array2 = array; foreach (byte b in array2) { stringBuilder.Append(b.ToString("x2")); } return stringBuilder.ToString(); } } } namespace TerraChat.UI { internal sealed class ChatWindow { private sealed class SettingsRow { private const float Height = 24f; private readonly ChatWindow window; private readonly float y; private float x = 10f; internal SettingsRow(ChatWindow window, float y) { this.window = window; this.y = y; Divider(); } internal void Button(string text, Action action, float width = 0f) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI val = Text(text, width, TabColor); ((Graphic)val).raycastTarget = true; window.MakeClickable(((Component)val).gameObject, delegate { action(); window.BuildSettings(); window.RefocusIfActive(); }); } internal void Value(string text, float width) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) Text("<b>" + text + "</b>", width, new Color(1f, 0.85f, 0.55f)); } internal float End() { return y - 24f - 6f; } private TextMeshProUGUI Text(string text, float width, Color color) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown TextMeshProUGUI val = window.NewText("Item", (Transform)(object)window.settings, 14f, (TextAlignmentOptions)514); ((TMP_Text)val).text = text; ((Graphic)val).color = color; ((TMP_Text)val).textWrappingMode = (TextWrappingModes)0; if (width <= 0f) { width = Mathf.Ceil(((TMP_Text)val).preferredWidth) + 12f; } Stretch((RectTransform)((TMP_Text)val).transform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, y - 24f), new Vector2(x + width, y)); x += width; Divider(); return val; } private void Divider() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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) RectTransform obj = NewRect("Divider", (Transform)(object)window.settings); Stretch(obj, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x + 2f, y - 24f + 4f), new Vector2(x + 4f, y - 4f)); Image obj2 = ((Component)obj).gameObject.AddComponent<Image>(); ((Graphic)obj2).color = new Color(0.75f, 0.6f, 0.4f, 0.8f); ((Graphic)obj2).raycastTarget = false; x += 6f; } } private const float HeaderHeight = 28f; private const float InputHeight = 32f; private const float MinWidth = 320f; private const float MinHeight = 160f; private const float MaxWidth = 1400f; private const float MaxHeight = 900f; private const int ShownLines = 120; private const float HintHeight = 20f; private static readonly Color TabColor = new Color(0.85f, 0.8f, 0.7f); private static bool s_failed; private readonly TerraChatPlugin plugin; private readonly Chat chat; private readonly Canvas canvas; private readonly Canvas hudCanvas; private readonly RectTransform parent; private readonly RectTransform root; private readonly CanvasGroup group; private readonly Image background; private readonly RectTransform tabBar; private readonly TextMeshProUGUI output; private readonly ScrollRect scroll; private readonly TMP_InputField input; private readonly RectTransform settings; private readonly RectTransform settingsPanel; private readonly RectTransform statusMenu; private readonly TextMeshProUGUI statusMenuLabel; private readonly TextMeshProUGUI status; private readonly WindowPointer pointer; private readonly TMP_FontAsset font; private readonly Sprite buttonSprite; private readonly Type buttonSpriteType; private readonly List<KeyValuePair<string, TextMeshProUGUI>> tabLabels = new List<KeyValuePair<string, TextMeshProUGUI>>(); private string renderedTabs = string.Empty; private int renderedVersion = -1; private bool settingsOpen; private int checkFocusFrame = -1; private int caretToEndFrame = -1; private int submitFrame = -10; private bool defaultPlacement; private bool runeTabs; private readonly RectTransform viewport; private readonly TextMeshProUGUI linkHint; internal bool Active { get; private set; } internal bool IsValid { get { if ((Object)(object)root != (Object)null && (Object)(object)canvas != (Object)null && (Object)(object)chat != (Object)null) { return (Object)(object)chat == (Object)(object)Chat.instance; } return false; } } internal static ChatWindow TryCreate(TerraChatPlugin plugin) { if (s_failed) { return null; } Chat instance = Chat.instance; if ((Object)(object)instance == (Object)null || (Object)(object)((Terminal)instance).m_chatWindow == (Object)null || (Object)(object)((Terminal)instance).m_output == (Object)null || (Object)(object)((Terminal)instance).m_input == (Object)null) { return null; } try { return new ChatWindow(plugin, instance); } catch (Exception ex) { s_failed = true; TerraChatPlugin.Log.LogError((object)("TerraChat window could not be created; Valheim chat stays in use. " + ex)); return null; } } private ChatWindow(TerraChatPlugin plugin, Chat chat) { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Expected O, but got Unknown //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_0346: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_0369: Unknown result type (might be due to invalid IL or missing references) //IL_03ac: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Unknown result type (might be due to invalid IL or missing references) //IL_03ca: Unknown result type (might be due to invalid IL or missing references) //IL_03d9: Unknown result type (might be due to invalid IL or missing references) //IL_03e8: Unknown result type (might be due to invalid IL or missing references) //IL_03f2: Expected O, but got Unknown //IL_044c: Unknown result type (might be due to invalid IL or missing references) //IL_045c: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_047a: Unknown result type (might be due to invalid IL or missing references) //IL_0489: Unknown result type (might be due to invalid IL or missing references) //IL_0498: Unknown result type (might be due to invalid IL or missing references) //IL_04a2: Expected O, but got Unknown //IL_04b6: Unknown result type (might be due to invalid IL or missing references) //IL_04c4: Unknown result type (might be due to invalid IL or missing references) //IL_04d7: Unknown result type (might be due to invalid IL or missing references) //IL_04e1: Expected O, but got Unknown //IL_04ee: Unknown result type (might be due to invalid IL or missing references) //IL_050a: Unknown result type (might be due to invalid IL or missing references) //IL_052d: Unknown result type (might be due to invalid IL or missing references) //IL_0532: Unknown result type (might be due to invalid IL or missing references) //IL_0541: Unknown result type (might be due to invalid IL or missing references) //IL_0550: Unknown result type (might be due to invalid IL or missing references) //IL_058e: Unknown result type (might be due to invalid IL or missing references) //IL_059d: Unknown result type (might be due to invalid IL or missing references) //IL_05ac: Unknown result type (might be due to invalid IL or missing references) //IL_05bb: Unknown result type (might be due to invalid IL or missing references) //IL_05ca: Unknown result type (might be due to invalid IL or missing references) //IL_05d4: Expected O, but got Unknown //IL_067a: Unknown result type (might be due to invalid IL or missing references) //IL_0681: Expected O, but got Unknown //IL_068d: Unknown result type (might be due to invalid IL or missing references) //IL_06a3: Unknown result type (might be due to invalid IL or missing references) //IL_06b9: Unknown result type (might be due to invalid IL or missing references) //IL_06c7: Unknown result type (might be due to invalid IL or missing references) //IL_06cc: Unknown result type (might be due to invalid IL or missing references) //IL_06cd: Unknown result type (might be due to invalid IL or missing references) //IL_06d4: Unknown result type (might be due to invalid IL or missing references) //IL_07cf: Unknown result type (might be due to invalid IL or missing references) //IL_07de: Unknown result type (might be due to invalid IL or missing references) //IL_07ed: Unknown result type (might be due to invalid IL or missing references) //IL_07fc: Unknown result type (might be due to invalid IL or missing references) //IL_080b: Unknown result type (might be due to invalid IL or missing references) //IL_0815: Expected O, but got Unknown //IL_08d4: Unknown result type (might be due to invalid IL or missing references) //IL_08e3: Unknown result type (might be due to invalid IL or missing references) //IL_08f2: Unknown result type (might be due to invalid IL or missing references) //IL_0901: Unknown result type (might be due to invalid IL or missing references) //IL_0973: Unknown result type (might be due to invalid IL or missing references) //IL_0936: Unknown result type (might be due to invalid IL or missing references) //IL_0951: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_09c6: Unknown result type (might be due to invalid IL or missing references) //IL_09cb: Unknown result type (might be due to invalid IL or missing references) //IL_09da: Unknown result type (might be due to invalid IL or missing references) //IL_09e9: Unknown result type (might be due to invalid IL or missing references) //IL_0a52: Unknown result type (might be due to invalid IL or missing references) //IL_0a93: Unknown result type (might be due to invalid IL or missing references) //IL_0aad: Unknown result type (might be due to invalid IL or missing references) //IL_0ac7: Unknown result type (might be due to invalid IL or missing references) //IL_0add: Unknown result type (might be due to invalid IL or missing references) //IL_0ae2: Unknown result type (might be due to invalid IL or missing references) //IL_0ae3: Unknown result type (might be due to invalid IL or missing references) //IL_0aea: Unknown result type (might be due to invalid IL or missing references) //IL_0b68: Unknown result type (might be due to invalid IL or missing references) //IL_0b77: Unknown result type (might be due to invalid IL or missing references) //IL_0b86: Unknown result type (might be due to invalid IL or missing references) //IL_0b95: Unknown result type (might be due to invalid IL or missing references) //IL_0a32: Unknown result type (might be due to invalid IL or missing references) //IL_0bfe: Unknown result type (might be due to invalid IL or missing references) //IL_0c34: Unknown result type (might be due to invalid IL or missing references) //IL_0c39: Unknown result type (might be due to invalid IL or missing references) //IL_0c3e: Unknown result type (might be due to invalid IL or missing references) //IL_0c4d: Unknown result type (might be due to invalid IL or missing references) //IL_0c5c: Unknown result type (might be due to invalid IL or missing references) //IL_0c66: Expected O, but got Unknown //IL_0c78: Unknown result type (might be due to invalid IL or missing references) //IL_0cef: Unknown result type (might be due to invalid IL or missing references) //IL_0d07: Unknown result type (might be due to invalid IL or missing references) //IL_0d1f: Unknown result type (might be due to invalid IL or missing references) //IL_0d37: Unknown result type (might be due to invalid IL or missing references) //IL_0d4f: Unknown result type (might be due to invalid IL or missing references) //IL_0d54: Unknown result type (might be due to invalid IL or missing references) //IL_0d6b: Unknown result type (might be due to invalid IL or missing references) //IL_0d70: Unknown result type (might be due to invalid IL or missing references) //IL_0bde: Unknown result type (might be due to invalid IL or missing references) ChatWindow chatWindow = this; this.plugin = plugin; this.chat = chat; font = ((TMP_Text)((Terminal)chat).m_output).font; Canvas componentInParent = ((Component)((Terminal)chat).m_chatWindow).GetComponentInParent<Canvas>(true); hudCanvas = (((Object)(object)componentInParent != (Object)null) ? componentInParent.rootCanvas : null); GameObject val = new GameObject("TerraChatCanvas", new Type[3] { typeof(RectTransform), typeof(Canvas), typeof(GraphicRaycaster) }); val.layer = ((Component)((Terminal)chat).m_chatWindow).gameObject.layer; canvas = val.GetComponent<Canvas>(); canvas.renderMode = (RenderMode)0; canvas.sortingOrder = (((Object)(object)hudCanvas != (Object)null) ? (hudCanvas.sortingOrder + 1) : 10); val.AddComponent<CanvasScaler>(); val.AddComponent<GuiScaler>(); parent = (RectTransform)val.transform; Button val2 = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_takeAllButton : null); Image val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<Image>() : null); buttonSprite = (((Object)(object)val3 != (Object)null) ? val3.sprite : null); buttonSpriteType = (Type)(((Object)(object)val3 != (Object)null) ? ((int)val3.type) : 0); root = NewRect("TerraChatWindow", (Transform)(object)parent); root.anchorMin = (root.anchorMax = (root.pivot = Vector2.zero)); background = ((Component)root).gameObject.AddComponent<Image>(); Image component = ((Component)((Terminal)chat).m_chatWindow).GetComponent<Image>(); if ((Object)(object)component != (Object)null && (Object)(object)component.sprite != (Object)null) { background.sprite = component.sprite; background.type = component.type; } group = ((Component)root).gameObject.AddComponent<CanvasGroup>(); pointer = ((Component)root).gameObject.AddComponent<WindowPointer>(); PlaceFromConfig(); RectTransform val5 = NewRect("Header", (Transform)(object)root); Stretch(val5, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(4f, -28f), new Vector2(-4f, -2f)); ((Graphic)((Component)val5).gameObject.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.25f); DragHandle dragHandle = ((Component)val5).gameObject.AddComponent<DragHandle>(); dragHandle.Dragged = delegate(Vector2 delta) { //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_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) chatWindow.root.anchoredPosition = chatWindow.ClampPosition(chatWindow.root.anchoredPosition + delta / chatWindow.Scale()); }; dragHandle.Finished = SaveWindow; tabBar = NewRect("Tabs", (Transform)(object)val5); Stretch(tabBar, Vector2.zero, Vector2.one, new Vector2(2f, 2f), new Vector2(-190f, -2f)); ((Component)tabBar).gameObject.AddComponent<RectMask2D>(); status = NewText("Status", (Transform)(object)val5, 12f, (TextAlignmentOptions)516); Stretch((RectTransform)((TMP_Text)status).transform, new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(-186f, 0f), new Vector2(-112f, 0f)); ((Graphic)status).raycastTarget = true; MakeClickable(((Component)status).gameObject, ToggleStatusMenu); TextMeshProUGUI obj = NewText("Settings", (Transform)(object)val5, 15f, (TextAlignmentOptions)514); ((Graphic)obj).raycastTarget = true; ((TMP_Text)obj).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)obj).text = "Settings"; ((Graphic)obj).color = TabColor; Stretch((RectTransform)((TMP_Text)obj).transform, new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(-104f, 0f), new Vector2(-28f, 0f)); Button obj2 = ((Component)obj).gameObject.AddComponent<Button>(); ((Selectable)obj2).transition = (Transition)0; Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)obj2).navigation = navigation; ((UnityEvent)obj2.onClick).AddListener(new UnityAction(ToggleSettings)); AddDivider(val5, new Vector2(1f, 0f), -108f); AddDivider(val5, new Vector2(1f, 0f), -26f); RectTransform val6 = NewRect("Viewport", (Transform)(object)root); Stretch(val6, Vector2.zero, Vector2.one, new Vector2(8f, 42f), new Vector2(-8f, -32f)); viewport = val6; linkHint = NewText("DiscordHint", (Transform)(object)root, 13f, (TextAlignmentOptions)513); Stretch((RectTransform)((TMP_Text)linkHint).transform, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(10f, -52f), new Vector2(-10f, -32f)); ((TMP_Text)linkHint).text = string.Empty; ((TMP_Text)linkHint).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)linkHint).overflowMode = (TextOverflowModes)1; ((Graphic)linkHint).raycastTarget = true; ((Component)linkHint).gameObject.AddComponent<NameLinkClick>().Clicked = delegate(string id) { if (id == "cmd:discord") { plugin.RequestDiscordInvite(); } else if (id == "cmd:link") { plugin.RequestDiscordLink(); } chatWindow.RefocusIfActive(); }; ((Component)linkHint).gameObject.SetActive(false); ((Component)val6).gameObject.AddComponent<RectMask2D>(); output = NewText("Output", (Transform)(object)val6, plugin.FontSize.Value, (TextAlignmentOptions)1025); RectTransform val7 = (RectTransform)((TMP_Text)output).transform; val7.anchorMin = new Vector2(0f, 0f); val7.anchorMax = new Vector2(1f, 0f); val7.pivot = new Vector2(0.5f, 0f); val7.offsetMin = (val7.offsetMax = Vector2.zero); ((TMP_Text)output).textWrappingMode = (TextWrappingModes)1; ((Graphic)output).raycastTarget = true; ((Component)output).gameObject.AddComponent<NameLinkClick>().Clicked = OnLinkClicked; ((Component)output).gameObject.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2; scroll = ((Component)root).gameObject.AddComponent<ScrollRect>(); scroll.viewport = val6; scroll.content = val7; scroll.horizontal = false; scroll.vertical = true; scroll.movementType = (MovementType)2; scroll.scrollSensitivity = 25f; GameObject val8 = Object.Instantiate<GameObject>(((Component)((Terminal)chat).m_input).gameObject, (Transform)(object)root, false); ((Object)val8).name = "TerraChatInput"; val8.SetActive(true); input = val8.GetComponent<TMP_InputField>(); Stretch((RectTransform)val8.transform, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(6f, 6f), new Vector2(-24f, 38f)); ((UnityEventBase)input.onSubmit).RemoveAllListeners(); ((UnityEventBase)input.onEndEdit).RemoveAllListeners(); ((UnityEventBase)input.onSelect).RemoveAllListeners(); ((UnityEventBase)input.onDeselect).RemoveAllListeners(); ((UnityEventBase)input.onValueChanged).RemoveAllListeners(); input.text = string.Empty; input.onFocusSelectAll = false; ((UnityEvent<string>)(object)input.onSubmit).AddListener((UnityAction<string>)OnSubmit); ((UnityEvent<string>)(object)input.onDeselect).AddListener((UnityAction<string>)delegate { if (chatWindow.Active) { chatWindow.checkFocusFrame = Time.frameCount + 1; } }); RectTransform obj3 = NewRect("Resize", (Transform)(object)root); Stretch(obj3, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-20f, -21f), new Vector2(-6f, -7f)); Image val9 = ((Component)obj3).gameObject.AddComponent<Image>(); if ((Object)(object)buttonSprite != (Object)null) { val9.sprite = buttonSprite; val9.type = buttonSpriteType; ((Graphic)val9).color = new Color(1f, 0.85f, 0.6f); } else { ((Graphic)val9).color = new Color(0.35f, 0.25f, 0.15f, 0.9f); } DragHandle dragHandle2 = ((Component)obj3).gameObject.AddComponent<DragHandle>(); dragHandle2.Dragged = delegate(Vector2 delta) { //IL_000b: 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_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_0026: 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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) Vector2 val12 = chatWindow.root.sizeDelta + delta / chatWindow.Scale(); chatWindow.root.sizeDelta = new Vector2(Mathf.Clamp(val12.x, 320f, 1400f), Mathf.Clamp(val12.y, 160f, 900f)); }; dragHandle2.Finished = SaveWindow; settingsPanel = NewRect("Settings", (Transform)(object)root); Stretch(settingsPanel, Vector2.zero, Vector2.one, new Vector2(6f, 6f), new Vector2(-6f, -32f)); Image val10 = ((Component)settingsPanel).gameObject.AddComponent<Image>(); if ((Object)(object)background.sprite != (Object)null) { val10.sprite = background.sprite; val10.type = background.type; } ((Graphic)val10).color = new Color(0.12f, 0.1f, 0.08f, 0.97f); ((Component)settingsPanel).gameObject.AddComponent<RectMask2D>(); settings = NewRect("Content", (Transform)(object)settingsPanel); settings.anchorMin = new Vector2(0f, 1f); settings.anchorMax = new Vector2(1f, 1f); settings.pivot = new Vector2(0.5f, 1f); settings.offsetMin = (settings.offsetMax = Vector2.zero); ScrollRect obj4 = ((Component)settingsPanel).gameObject.AddComponent<ScrollRect>(); obj4.viewport = settingsPanel; obj4.content = settings; obj4.horizontal = false; obj4.movementType = (MovementType)2; obj4.scrollSensitivity = 25f; ((Component)settingsPanel).gameObject.SetActive(false); statusMenu = NewRect("StatusMenu", (Transform)(object)root); Stretch(statusMenu, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-196f, -58f), new Vector2(-102f, -30f)); Image val11 = ((Component)statusMenu).gameObject.AddComponent<Image>(); if ((Object)(object)background.sprite != (Object)null) { val11.sprite = background.sprite; val11.type = background.type; } ((Graphic)val11).color = new Color(0.12f, 0.1f, 0.08f, 0.98f); statusMenuLabel = NewText("Option", (Transform)(object)statusMenu, 14f, (TextAlignmentOptions)514); Stretch((RectTransform)((TMP_Text)statusMenuLabel).transform, Vector2.zero, Vector2.one, new Vector2(4f, 0f), new Vector2(-4f, 0f)); ((Graphic)statusMenuLabel).raycastTarget = true; ((Graphic)statusMenuLabel).color = TabColor; MakeClickable(((Component)statusMenuLabel).gameObject, delegate { plugin.SetOnline(!plugin.StayOnline.Value); ((Component)chatWindow.statusMenu).gameObject.SetActive(false); chatWindow.RefocusIfActive(); }); ((Component)statusMenu).gameObject.SetActive(false); ApplyStyle(); SetLayerRecursively(((Component)root).gameObject, ((Component)parent).gameObject.layer); ManualLogSource log = TerraChatPlugin.Log; object[] obj5 = new object[9] { root.anchoredPosition.x, root.anchoredPosition.y, root.sizeDelta.x, root.sizeDelta.y, null, null, null, null, null }; Rect rect = parent.rect; obj5[4] = ((Rect)(ref rect)).width; rect = parent.rect; obj5[5] = ((Rect)(ref rect)).height; obj5[6] = canvas.scaleFactor; obj5[7] = canvas.sortingOrder; obj5[8] = (((Object)(object)hudCanvas != (Object)null) ? ((Object)hudCanvas).name : "none"); log.LogInfo((object)string.Format("TerraChat window created: position {0:0},{1:0} size {2:0}x{3:0}; canvas {4:0}x{5:0} scale {6:0.##} sort {7}; HUD canvas {8}.", obj5)); } internal void Dispose() { Active = false; if ((Object)(object)canvas != (Object)null) { Object.Destroy((Object)(object)((Component)canvas).gameObject); } } internal void Tick() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_0029: 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) Vector2 val = (defaultPlacement ? DefaultPosition() : ClampPosition(root.anchoredPosition)); if (val != root.anchoredPosition) { root.anchoredPosition = val; } if (caretToEndFrame >= 0 && Time.frameCount >= caretToEndFrame) { caretToEndFrame = -1; input.MoveTextEnd(false); } bool flag = (Object)(object)Player.m_localPlayer != (Object)null; if (((Component)root).gameObject.activeSelf != flag) { ((Component)root).gameObject.SetActive(flag); } if (!flag) { Active = false; return; } if (!Active && ZInput.GetButtonDown("Chat") && Time.frameCount > submitFrame + 1 && CanOpen()) { Open(); } if (Active && ZInput.GetKeyDown((KeyCode)27, true)) { Close(); } if (Active && ZInput.GetKeyDown((KeyCode)9, true)) { NextTab(); } if (checkFocusFrame >= 0 && Time.frameCount >= checkFocusFrame) { checkFocusFrame = -1; if (Active && !input.isFocused) { if (pointer.Over || pointer.LastPressFrame >= Time.frameCount - 2) { Focus(); } else { Close(); } } } ChatState state = plugin.State; if (state.Version != renderedVersion) { Render(state); } ((TMP_Text)status).text = ((!plugin.StayOnline.Value) ? "<color=#a09a90><u>Offline</u></color>" : ((plugin.Relay.Status == "Online") ? "<color=#8fe08f><u>Online</u></color>" : ("<color=#e0a080><u>" + plugin.Relay.Status + "</u></color>"))); bool flag2 = (DateTime.UtcNow - state.LastMessageUtc).TotalSeconds < 10.0; float num = ((Active || pointer.Over || flag2) ? 1f : Mathf.Clamp(plugin.IdleOpacity.Value, 0.1f, 1f)); group.alpha = Mathf.MoveTowards(group.alpha, num, Time.deltaTime * 2f); } private bool CanOpen() { if (!Console.IsVisible() && !TextInput.IsVisible() && !Minimap.InTextInput() && !Menu.IsVisible()) { return !Hud.IsPieceSelectionVisible(); } return false; } private void Open() { Active = true; ((Transform)root).SetAsLastSibling(); Focus(); } private void Focus() { ((Component)input).gameObject.SetActive(true); ((Selectable)input).Select(); input.ActivateInputField(); } internal void Close() { Active = false; checkFocusFrame = -1; if (settingsOpen) { ToggleSettings(); } ((Component)statusMenu).gameObject.SetActive(false); input.DeactivateInputField(false); if ((Object)(object)EventSystem.current != (Object)null && (Object)(object)EventSystem.current.currentSelectedGameObject == (Object)(object)((Component)input).gameObject) { EventSystem.current.SetSelectedGameObject((GameObject)null); } } private void OnLinkClicked(string id) { if (id.StartsWith("pm:", StringComparison.Ordinal)) { StartPm(id.Substring(3)); } else if (id.StartsWith("url:", StringComparison.Ordinal)) { GUIUtility.systemCopyBuffer = id.Substring(4); plugin.State.AddSystem("Link copied: " + id.Substring(4) + " (paste it in your browser if you trust the sender)"); } } private void StartPm(string name) { if (!string.IsNullOrEmpty(name)) { InsertCommand("/pm " + (name.Contains(" ") ? ("\"" + name + "\"") : name) + " "); } } private void InsertCommand(string text) { input.text = text; if (!Active) { Open(); } else { Focus(); } caretToEndFrame = Time.frameCount + 1; } private void OnSubmit(string text) { submitFrame = Time.frameCount; input.text = string.Empty; plugin.Submit(text); Close(); } private void NextTab() { List<ChatTab> list = plugin.State.VisibleTabs.ToList(); int num = list.FindIndex((ChatTab t) => t.Id == plugin.State.ActiveTabId); plugin.State.Select(list[(num + 1) % list.Count].Id); Focus(); } private void Render(ChatState state) { //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: 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_020b: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) renderedVersion = state.Version; string text = string.Join("|", state.VisibleTabs.Select((ChatTab t) => t.Id + ":" + t.Title).ToArray()); if (text != renderedTabs) { BuildTabs(state, text); } foreach (KeyValuePair<string, TextMeshProUGUI> tabLabel in tabLabels) { ChatTab chatTab = state.Tab(tabLabel.Key); bool flag = chatTab.Id == state.ActiveTabId; ((TMP_Text)tabLabel.Value).text = (flag ? "<b>" : "") + TabName(chatTab) + (chatTab.Unread ? " <color=#ffd479>*</color>" : "") + (flag ? "</b>" : ""); ((Graphic)tabLabel.Value).color = (Color)(flag ? new Color(1f, 0.85f, 0.55f) : TabColor); } bool flag2 = state.ActiveTab.Discord && state.DiscordLinked == null; ((TMP_Text)linkHint).text = ((state.PendingLinkCode == null) ? "<color=#a09080>Join our Discord: type</color> <link=\"cmd:discord\"><color=#8ea1e1><u>/discord</u></color></link> <color=#a09080>|</color> <link=\"cmd:link\"><color=#8ea1e1><u>Link your Discord profile</u></color></link>" : ("<color=#8ea1e1>" + state.PendingLinkCode + "</color> <color=#a09080>copied: paste it in the Discord channel.</color> <link=\"cmd:link\"><u>New code</u></link> <color=#a09080>|</color> <color=#a09080>Join our Discord: type</color> <link=\"cmd:discord\"><color=#8ea1e1><u>/discord</u></color></link>")); if (((Component)linkHint).gameObject.activeSelf != flag2) { ((Component)linkHint).gameObject.SetActive(flag2); viewport.offsetMax = new Vector2(viewport.offsetMax.x, -32f - (flag2 ? 20f : 0f)); } int num; if (!(scroll.verticalNormalizedPosition <= 0.01f)) { Rect rect = scroll.content.rect; float height = ((Rect)(ref rect)).height; rect = scroll.viewport.rect; num = ((height <= ((Rect)(ref rect)).height) ? 1 : 0); } else { num = 1; } List<string> lines = state.ActiveTab.Lines; ((TMP_Text)output).text = string.Join("\n", lines.Skip(Math.Max(0, lines.Count - 120)).ToArray()); if (num != 0) { Canvas.ForceUpdateCanvases(); scroll.verticalNormalizedPosition = 0f; } } private void BuildTabs(ChatState state, string key) { //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Expected O, but got Unknown //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Expected O, but got Unknown renderedTabs = key; if (settingsOpen) { BuildSettings(); } for (int num = ((Transform)tabBar).childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)((Transform)tabBar).GetChild(num)).gameObject); } tabLabels.Clear(); TMP_FontAsset val = RuneFont.Get(font); runeTabs = (Object)(object)val != (Object)null; float num2 = AddTabSeparator(4f); foreach (ChatTab visibleTab in state.VisibleTabs) { string id = visibleTab.Id; TextMeshProUGUI val2 = NewText("Tab", (Transform)(object)tabBar, 15f, (TextAlignmentOptions)514); if ((Object)(object)val != (Object)null) { ((TMP_Text)val2).font = val; } ((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0; ((Graphic)val2).raycastTarget = true; ((TMP_Text)val2).text = "<b>" + TabName(visibleTab) + " *</b>"; float num3 = Mathf.Ceil(((TMP_Text)val2).preferredWidth) + 6f; Stretch((RectTransform)((TMP_Text)val2).transform, new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(num2, 0f), new Vector2(num2 + num3, 0f)); Button obj = ((Component)val2).gameObject.AddComponent<Button>(); ((Selectable)obj).transition = (Transition)0; Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)obj).navigation = navigation; ((UnityEvent)obj.onClick).AddListener((UnityAction)delegate { plugin.State.Select(id); RefocusIfActive(); }); tabLabels.Add(new KeyValuePair<string, TextMeshProUGUI>(id, val2)); num2 = AddTabSeparator(num2 + num3); } SetLayerRecursively(((Component)tabBar).gameObject, ((Component)parent).gameObject.layer); } private float AddTabSeparator(float x) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) AddDivider(tabBar, new Vector2(0f, 0f), x + 3f); return x + 8f; } private void AddDivider(RectTransform owner, Vector2 anchor, float x) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_0032: 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_006c: Unknown result type (might be due to invalid IL or missing references) RectTransform obj = NewRect("Divider", (Transform)(object)owner); Stretch(obj, new Vector2(anchor.x, 0.15f), new Vector2(anchor.x, 0.85f), new Vector2(x, 0f), new Vector2(x + 2f, 0f)); Image obj2 = ((Component)obj).gameObject.AddComponent<Image>(); ((Graphic)obj2).color = new Color(0.75f, 0.6f, 0.4f, 0.8f); ((Graphic)obj2).raycastTarget = false; } private string TabName(ChatTab tab) { if (tab.Id == "local") { if (!runeTabs) { return "Local"; } return "ᛊᛖᚷᛃᚨ"; } if (tab.Id == "pm") { if (!runeTabs) { return "PM"; } return "ᛈᛗ"; } if (tab.Label != null) { if (!runeTabs) { return tab.Title; } return tab.Label; } return (runeTabs ? "ᛒᛟᚱᚦ " : "Ch ") + tab.Channel; } private void ToggleSettings() { settingsOpen = !settingsOpen; ((Component)settingsPanel).gameObject.SetActive(settingsOpen); if (settingsOpen) { BuildSettings(); } RefocusIfActive(); } private void ToggleStatusMenu() { ((TMP_Text)statusMenuLabel).text = (plugin.StayOnline.Value ? "Go offline" : "Go online"); ((Component)statusMenu).gameObject.SetActive(!((Component)statusMenu).gameObject.activeSelf); ((Transform)statusMenu).SetAsLastSibling(); RefocusIfActive(); } private void MakeClickable(GameObject target, Action action) { //IL_001d: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown Button obj = target.AddComponent<Button>(); ((Selectable)obj).transition = (Transition)0; Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)obj).navigation = navigation; ((UnityEvent)obj.onClick).AddListener((UnityAction)delegate { action(); }); } private void BuildSettings() { //IL_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_031a: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Unknown result type (might be due to invalid IL or missing references) //IL_0336: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Expected O, but got Unknown //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_0378: Unknown result type (might be due to invalid IL or missing references) for (int num = ((Transform)settings).childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)((Transform)settings).GetChild(num)).gameObject); } float y = -6f; y = SettingsHeading("Channels (click to mute or unmute)", y); SettingsRow settingsRow = new SettingsRow(this, y); foreach (ChatTab item in from t in (from t in plugin.State.VisibleTabs.Concat(plugin.State.MutedChannels.Select((int c) => plugin.State.ChannelTab(c))) where t != null && t.Channel > 0 select t).Distinct() orderby t.Channel select t) { int channel = item.Channel; bool muted = plugin.State.IsMuted(channel); settingsRow.Button((muted ? "<color=#e0a080><s>" : "<color=#8fe08f>") + item.Title + (muted ? "</s></color>" : "</color>"), delegate { plugin.SetChannelMuted(channel, !muted); }); } y = settingsRow.End(); y = SettingsHeading("Text size", y); y = Stepper(y, plugin.FontSize.Value.ToString("0"), delegate { Adjust(plugin.FontSize, -1f, 10f, 28f); }, delegate { Adjust(plugin.FontSize, 1f, 10f, 28f); }); y = SettingsHeading("Opacity when idle", y); y = Stepper(y, Mathf.RoundToInt(plugin.IdleOpacity.Value * 100f) + "%", delegate { Adjust(plugin.IdleOpacity, -0.1f, 0.1f, 1f); }, delegate { Adjust(plugin.IdleOpacity, 0.1f, 0.1f, 1f); }); y = SettingsHeading("Background opacity", y); y = Stepper(y, Mathf.RoundToInt(plugin.BackgroundOpacity.Value * 100f) + "%", delegate { Adjust(plugin.BackgroundOpacity, -0.1f, 0f, 1f); }, delegate { Adjust(plugin.BackgroundOpacity, 0.1f, 0f, 1f); }); y = SettingsHeading("Window", y); settingsRow = new SettingsRow(this, y); settingsRow.Button("Reset position and size", ResetWindow); settingsRow.Button("Close settings", ToggleSettings); y = settingsRow.End(); TextMeshProUGUI obj = NewText("Hint", (Transform)(object)settings, 11f, (TextAlignmentOptions)513); Stretch((RectTransform)((TMP_Text)obj).transform, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(10f, y - 16f), new Vector2(-10f, y)); ((TMP_Text)obj).text = "<color=#a09080>Drag the title bar to move, the dot to resize. Click a name to PM. Click Online to go offline.</color>"; ((TMP_Text)obj).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)obj).overflowMode = (TextOverflowModes)1; y -= 22f; settings.sizeDelta = new Vector2(settings.sizeDelta.x, 0f - y); SetLayerRecursively(((Component)settings).gameObject, ((Component)parent).gameObject.layer); } private float SettingsHeading(string text, float y) { //IL_0021: 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_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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI obj = NewText("Heading", (Transform)(object)settings, 13f, (TextAlignmentOptions)513); Stretch((RectTransform)((TMP_Text)obj).transform, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(10f, y - 18f), new Vector2(-10f, y)); ((TMP_Text)obj).text = text; ((Graphic)obj).color = new Color(0.75f, 0.68f, 0.55f); ((TMP_Text)obj).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)obj).overflowMode = (TextOverflowModes)1; return y - 18f; } private float Stepper(float y, string value, Action down, Action up) { SettingsRow settingsRow = new SettingsRow(this, y); settingsRow.Button("-", down, 30f); settingsRow.Value(value, 48f); settingsRow.Button("+", up, 30f); return settingsRow.End(); } private void Adjust(ConfigEntry<float> entry, float step, float min, float max) { entry.Value = Mathf.Clamp(Mathf.Round((entry.Value + step) * 100f) / 100f, min, max); ApplyStyle(); } private void ApplyStyle() { //IL_0058: 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_005d: 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) ((TMP_Text)output).fontSize = Mathf.Clamp(plugin.FontSize.Value, 10f, 28f); Color color = (Color)(((Object)(object)background.sprite != (Object)null) ? Color.white : new Color(0f, 0f, 0f, 1f)); color.a = Mathf.Clamp01(plugin.BackgroundOpacity.Value); ((Graphic)background).color = color; renderedVersion = -1; } private void RefocusIfActive() { if (Active) { Focus(); } } private void PlaceFromConfig() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Clamp(plugin.WindowWidth.Value, 320f, 1400f); float num2 = Mathf.Clamp(plugin.WindowHeight.Value, 160f, 900f); root.sizeDelta = new Vector2(num, num2); if (plugin.WindowX.Value >= 0f && plugin.WindowY.Value >= 0f) { root.anchoredPosition = ClampPosition(new Vector2(plugin.WindowX.Value, plugin.WindowY.Value)); return; } defaultPlacement = true; root.anchoredPosition = DefaultPosition(); } private Vector2 DefaultPosition() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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_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) Rect rect = parent.rect; return ClampPosition(new Vector2(((Rect)(ref rect)).width - root.sizeDelta.x - 16f, 16f)); } private void ResetWindow() { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) plugin.WindowX.Value = -1f; plugin.WindowY.Value = -1f; plugin.WindowWidth.Value = (float)((ConfigEntryBase)plugin.WindowWidth).DefaultValue; plugin.WindowHeight.Value = (float)((ConfigEntryBase)plugin.WindowHeight).DefaultValue; root.sizeDelta = new Vector2(plugin.WindowWidth.Value, plugin.WindowHeight.Value); defaultPlacement = true; root.anchoredPosition = DefaultPosition(); } private void SaveWindow() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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_0078: Unknown result type (might be due to invalid IL or missing references) defaultPlacement = false; plugin.WindowX.Value = root.anchoredPosition.x; plugin.WindowY.Value = root.anchoredPosition.y; plugin.WindowWidth.Value = root.sizeDelta.x; plugin.WindowHeight.Value = root.sizeDelta.y; } private Vector2 ClampPosition(Vector2 position) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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) Rect rect = parent.rect; return new Vector2(Mathf.Clamp(position.x, 0f, Mathf.Max(0f, ((Rect)(ref rect)).width - 60f)), Mathf.Clamp(position.y, 0f, Mathf.Max(0f, ((Rect)(ref rect)).height - 28f))); } private float Scale() { Canvas componentInParent = ((Component)root).GetComponentInParent<Canvas>(); if (!((Object)(object)componentInParent != (Object)null) || !(componentInParent.scaleFactor > 0f)) { return 1f; } return componentInParent.scaleFactor; } private TextMeshProUGUI NewText(string name, Transform owner, float size, TextAlignmentOptions alignment) { //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) //IL_0020: 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_004e: 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) GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) }); val.SetActive(false); val.transform.SetParent(owner, false); TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val2).font = font; val.SetActive(true); ((TMP_Text)val2).fontSize = size; ((TMP_Text)val2).alignment = alignment; ((TMP_Text)val2).richText = true; ((Graphic)val2).raycastTarget = false; ((Graphic)val2).color = new Color(0.95f, 0.92f, 0.85f); return val2; } private static RectTransform NewRect(string name, Transform owner) { //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) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) }); val.transform.SetParent(owner, false); return (RectTransform)val.transform; } private static void Stretch(RectTransform rect, Vector2 anchorMin, Vector2 anchorMax, Vector2 offsetMin, Vector2 offsetMax) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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) rect.anchorMin = anchorMin; rect.anchorMax = anchorMax; rect.pivot = new Vector2(0.5f, 0.5f); rect.offsetMin = offsetMin; rect.offsetMax = offsetMax; } private static void SetLayerRecursively(GameObject go, int layer) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) go.layer = layer; foreach (Transform item in go.transform) { SetLayerRecursively(((Component)item).gameObject, layer); } } } internal static class RuneFont { internal const string Probe = "ᛊᛖᚷᛃᚨᛒᛟᚱᚦᚺᛁᛈᛗ"; private const string ResourceName = "TerraChat.Fonts.NotoSansRunic-Regular.ttf"; private static bool s_resolved; private static TMP_FontAsset s_font; internal static TMP_FontAsset Get(TMP_FontAsset primary) { if (s_resolved) { return s_font; } s_resolved = true; try { string text = ExtractFont(); TMP_FontAsset val = ((text != null) ? TMP_FontAsset.CreateFontAsset(text, 0, 90, 9, (GlyphRenderMode)4165, 512, 512) : null); uint[] array = default(uint[]); if ((Object)(object)val != (Object)null && val.HasCharacters("ᛊᛖᚷᛃᚨᛒᛟᚱᚦᚺᛁᛈᛗ", ref array, false, true)) { ((Object)val).name = "TerraChat Runes"; if ((Object)(object)primary != (Object)null) { val.fallbackFontAssetTable = new List<TMP_FontAsset> { primary }; } s_font = val; } } catch (Exception ex) { TerraChatPlugin.Log.LogWarning((object)("TerraChat rune font could not be loaded: " + ex.Message)); s_font = null; } TerraChatPlugin.Log.LogInfo((object)(((Object)(object)s_font != (Object)null) ? "TerraChat rune tabs use the embedded Noto Sans Runic font." : "TerraChat rune font unavailable; tabs use Latin names.")); return s_font; } private static string ExtractFont() { using Stream stream = typeof(RuneFont).Assembly.GetManifestResourceStream("TerraChat.Fonts.NotoSansRunic-Regular.ttf"); if (stream == null) { return null; } byte[] array = new byte[stream.Length]; int num; for (int i = 0; i < array.Length; i += num) { num = stream.Read(array, i, array.Length - i); if (num <= 0) { break; } } string text = Path.Combine(Paths.CachePath, "TerraChat"); string text2 = Path.Combine(text, "NotoSansRunic-Regular.ttf"); Directory.CreateDirectory(text); if (!File.Exists(text2) || new FileInfo(text2).Length != array.Length) { File.WriteAllBytes(text2, array); } return text2; } } internal sealed class DragHandle : MonoBehaviour, IDragHandler, IEventSystemHandler, IEndDragHandler { internal Action<Vector2> Dragged; internal Action Finished; public void OnDrag(PointerEventData eventData) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Dragged?.Invoke(eventData.delta); } public void OnEndDrag(PointerEventData eventData) { Finished?.Invoke(); } } internal sealed class WindowPointer : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler, IPointerDownHandler { internal bool Over; internal int LastPressFrame = -1; public void OnPointerEnter(PointerEventData eventData) { Over = true; } public void OnPointerExit(PointerEventData eventData) { Over = false; } public void OnPointerDown(PointerEventData eventData) { LastPressFrame = Time.frameCount; } } internal sealed class NameLinkClick : MonoBehaviour, IPointerClickHandler, IEventSystemHandler { internal Action<string> Clicked; public void OnPointerClick(PointerEventData eventData) { //IL_0001: 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_001e: Unknown result type (might be due to invalid IL or missing references) if ((int)eventData.button == 0) { TMP_Text component = ((Component)this).GetComponent<TMP_Text>(); Camera pressEventCamera = eventData.pressEventCamera; int num = TMP_TextUtilities.FindIntersectingLink(component, Vector2.op_Implicit(eventData.position), pressEventCamera); if (num >= 0) { string linkID = ((TMP_LinkInfo)(ref component.textInfo.linkInfo[num])).GetLinkID(); Clicked?.Invoke(linkID); } } } } } namespace TerraChat.Patches { [HarmonyPatch(typeof(Chat), "HasFocus")] internal static class ChatHasFocusPatch { private static void Postfix(ref bool __result) { TerraChatPlugin instance = TerraChatPlugin.Instance; if ((Object)(object)instance != (Object)null && instance.WindowActive) { __result = true; } } } [HarmonyPatch(typeof(Chat), "Update")] internal static class ChatUpdatePatch { private static readonly FieldRef<Terminal, bool> Focused = AccessTools.FieldRefAccess<Terminal, bool>("m_focused"); private static readonly Dictionary<KeyCode, List<string>> Binds = (Dictionary<KeyCode, List<string>>)AccessTools.Field(typeof(Terminal), "m_binds").GetValue(null); private static bool Prefix(Chat __instance) { //IL_00ec: Unknown result type (might be due to invalid IL or missing references) TerraChatPlugin instance = TerraChatPlugin.Instance; if ((Object)(object)instance == (Object)null || !instance.Enabled.Value || !instance.HasWindow) { return true; } Focused.Invoke((Terminal)(object)__instance) = false; __instance.m_wasFocused = false; if ((Object)(object)((Terminal)__instance).m_chatWindow != (Object)null && ((Component)((Terminal)__instance).m_chatWindow).gameObject.activeSelf) { ((Component)((Terminal)__instance).m_chatWindow).gameObject.SetActive(false); } if ((Object)(object)((Terminal)__instance).m_input != (Object)null && ((Component)((Terminal)__instance).m_input).gameObject.activeSelf) { ((TMP_InputField)((Terminal)__instance).m_input).DeactivateInputField(false); ((Component)((Terminal)__instance).m_input).gameObject.SetActive(false); } if (!instance.WindowActive && Binds != null && !Console.IsVisible() && !TextInput.IsVisible() && !Minimap.InTextInput()) { foreach (KeyValuePair<KeyCode, List<string>> bind in Binds) { if (!ZInput.GetKeyDown(bind.Key, true)) { continue; } foreach (string item in bind.Value) { ((Terminal)__instance).TryRunCommand(item, true, true); } } } return false; } } [HarmonyPatch(typeof(Terminal), "AddString", new Type[] { typeof(string) })] internal static class TerminalAddStringPatch { private static void Postfix(Terminal __instance, string text) { TerraChatPlugin instance = TerraChatPlugin.Instance; if ((Object)(object)instance != (Object)null && __instance is Chat) { instance.State.AddLocal(text); } } } [HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")] internal static class GameCameraMouseCapturePatch { private static void Postfix() { TerraChatPlugin instance = TerraChatPlugin.Instance; if (!((Object)(object)instance == (Object)null) && instance.WindowActive) { ZCursor.LockState = (CursorLockMode)0; ZCursor.Show(); } } } } namespace TerraChat.Net { public sealed class RelayClient { private static readonly string[] FatalErrors = new string[5] { "bad_version", "bad_name", "name_taken", "banned", "steam_required" }; private readonly ConcurrentQueue<JObject> incoming = new ConcurrentQueue<JObject>(); private readonly ConcurrentQueue<string> outgoing = new ConcurrentQueue<string>(); private readonly SemaphoreSlim outgoingSignal = new SemaphoreSlim(0); private readonly Action<string> log; private CancellationTokenSource stop; private volatile bool fatal; private volatile string status = "Offline"; public string Status => status; public bool Running { get { if (stop != null) { return !stop.IsCancellationRequested; } return false; } } public RelayClient(Action<string> log) { this.log = log ?? ((Action<string>)delegate { }); } public void Start(string url, JObject hello) { Stop(); fatal = false; stop = new CancellationTokenSource(); CancellationToken token = stop.Token; Task.Run(() => RunAsync(url, ((JToken)hello).ToString((Formatting)0, Array.Empty<JsonConverter>()), token)); } public void Stop() { if (stop != null) { stop.Cancel(); stop = null; status = "Offline"; } } public void Send(JObject message) { outgoing.Enqueue(((JToken)message).ToString((Formatting)0, Array.Empty<JsonConverter>())); outgoingSignal.Release(); } public bool TryReceive(out JObject message) { return incoming.TryDequeue(out message); } private async Task RunAsync(string url, string hello, CancellationToken token) { int delaySeconds = 2; while (!token.IsCancellationRequested && !fatal) { status = "Connecting"; try { using ClientWebSocket socket = new ClientWebSocket(); socket.Options.KeepAliveInterval = TimeSpan.FromSeconds(20.0); await socket.ConnectAsync(new Uri(url), token).ConfigureAwait(continueOnCapturedContext: false); await SendRawAsync(socket, hello, token).ConfigureAwait(continueOnCapturedContext: false); status = "Online"; delaySeconds = 2; using CancellationTokenSource connection = CancellationTokenSource.CreateLinkedTokenSource(new CancellationToken[1] { token }); Task sender = SendLoopAsync(socket, connection.Token); Task pinger = PingLoopAsync(connection.Token); await ReceiveLoopAsync(socket, connection.Token).ConfigureAwait(continueOnCapturedContext: false); connection.Cancel(); try { await Task.WhenAll(sender, pinger).ConfigureAwait(continueOnCapturedContext: false); } catch { } } catch (OperationCanceledException) when (token.IsCancellationRequested) { break; } catch (Exception ex2) { log("TerraChat connection: " + ex2.GetType().Name + ": " + ex2.Message); } status = (fatal ? "Stopped" : "Offline"); if (!token.IsCancellationRequested && !fatal) { try { await Task.Delay(TimeSpan.FromSeconds(delaySeconds), token).ConfigureAwait(continueOnCapturedContext: false); } catch (OperationCanceledException) { break; } delaySeconds = Math.Min(60, delaySeconds * 2); continue; } break; } } private async Task ReceiveLoopAsync(ClientWebSocket socket, CancellationToken token) { ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[4096]); using MemoryStream message = new MemoryStream(); while (socket.State == WebSocketState.Open && !token.IsCancellationRequested) { WebSocketReceiveResult webSocketReceiveResult = await socket.ReceiveAsync(buffer, token).ConfigureAwait(continueOnCapturedContext: false); if (webSocketReceiveResult.MessageType == WebSocketMessageType.Close) { return; } message.Write(buffer.Array, 0, webSocketReceiveResult.Count); if (message.Length > 65536) { return; } if (webSocketReceiveResult.EndOfMessage) { string text = Encoding.UTF8.GetString(message.ToArray()); message.SetLength(0L); JObject val; try { val = JObject.Parse(text); } catch { continue; } if ((string)val["t"] == "error" && Array.IndexOf(FatalErrors, (string)val["code"]) >= 0) { fatal = true; } incoming.Enqueue(val); } } } private async Task SendLoopAsync(ClientWebSocket socket, CancellationToken token) { while (!token.IsCancellationRequested) { await outgoingSignal.WaitAsync(token).ConfigureAwait(continueOnCapturedContext: false); string result; while (socket.State == WebSocketState.Open && outgoing.TryDequeue(out result)) { await SendRawAsync(socket, result, token).ConfigureAwait(continueOnCapturedContext: false); } } } private async Task PingLoopAsync(CancellationToken token) { while (!token.IsCancellationRequested) { await Task.Delay(TimeSpan.FromSeconds(30.0), token).ConfigureAwait(continueOnCapturedContext: false); Send(new JObject { ["t"] = JToken.op_Implicit("ping") }); } } private static Task SendRawAsync(ClientWebSocket socket, string text, CancellationToken token) { byte[] bytes = Encoding.UTF8.GetBytes(text); return socket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, endOfMessage: true, token); } } internal sealed class SteamTicket : IDisposable { internal const string Identity = "terrachat"; private const float TimeoutSeconds = 15f; private Callback<GetTicketForWebApiResponse_t> callback; private HAuthTicket handle = HAuthTicket.Invalid; private float requestedAt = -1f; internal string Ticket { get; private set; } internal bool Failed { get; private set; } internal bool Pending { get { if (requestedAt >= 0f && Ticket == null) { return !Failed; } return false; } } internal void Request() { //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_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) Cancel(); Ticket = null; Failed = false; try { if (!SteamAPI.IsSteamRunning() || !SteamUser.BLoggedOn()) { Failed = true; return; } if (callback == null) { callback = Callback<GetTicketForWebApiResponse_t>.Create((DispatchDelegate<GetTicketForWebApiResponse_t>)OnTicket); } handle = SteamUser.GetAuthTicketForWebApi("terrachat"); requestedAt = Time.realtimeSinceStartup; if (handle == HAuthTicket.Invalid) { Failed = true; } } catch (Exception ex) { TerraChatPlugin.Log.LogWarning((object)("TerraChat Steam ticket unavailable: " + ex.Message)); Failed = true; } } internal void Tick() { if (Pending && Time.realtimeSinceStartup - requestedAt > 15f) { Failed = true; } } private unsafe void OnTicket(GetTicketForWebApiResponse_t response) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 //IL_001d: 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_0088: 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) if (response.m_hAuthTicket != handle) { return; } if ((int)response.m_eResult != 1 || response.m_cubTicket <= 0) { TerraChatPlugin.Log.LogWarning((object)("TerraChat Steam ticket request failed: " + ((object)(*(EResult*)(&response.m_eResult))/*cast due to .constrained prefix*/).ToString())); Failed = true; return; } StringBuilder stringBuilder = new StringBuilder(response.m_cubTicket * 2); for (int i = 0; i < response.m_cubTicket; i++) { stringBuilder.Append(response.m_rgubTicket[i].ToString("x2")); } Ticket = stringBuilder.ToString(); } private void Cancel() { //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_0013: 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) try { if (handle != HAuthTicket.Invalid) { SteamUser.CancelAuthTicket(handle); } } catch { } handle = HAuthTicket.Invalid; requestedAt = -1f; } public void Dispose() { Cancel(); if (callback != null) { callback.Dispose(); callback = null; } } } } namespace TerraChat.Chat { public sealed class ChatTab { public string Id { get; } public string Title { get; internal set; } public string Label { get; internal set; } public bool Discord { get; internal set; } public int Channel { get; } public bool Unread { get; internal set; } public List<string> Lines { get; } = new List<string>(); public ChatTab(string id, string title, int channel) { Id = id; Title = title; Channel = channel; } } public sealed class ChatState { public const string LocalTab = "local"; public const string PmTab = "pm"; public const int MaxLines = 200; private static readonly string[] ChannelColors = new string[4] { "#9fd3ff", "#b5ff9f", "#ffc89f", "#e0e0a0" }; private const string PmColor = "#e6a8ff"; private const string SystemColor = "#c8c8c8"; private const string NameColor = "orange"; private const string DiscordColor = "#8ea1e1"; private readonly List<ChatTab> tabs = new List<ChatTab>(); private readonly HashSet<int> muted = new HashSet<int>(); private static readonly Regex WebAddress = new Regex("(https?://|www\\.)\\S+|\\b[a-z0-9-]+(\\.[a-z0-9-]+)*\\.(com|net|org|io|gg|is|co|me|xyz|ru|tv|info|biz|app|dev|link|site|online|top|shop|store|club|live|fun|uk|de|eu|us|ly|to|cc|pw|tk|ml|ga|cf|gq)\\b(/\\S*)?", RegexOptions.IgnoreCase); public int Version { get; private set; } public string ActiveTabId { get; private set; } public string LastPmPartner { get; private set; } public DateTime LastMessageUtc { get; private set; } public string DiscordLinked { get; private set; } public ChatTab DiscordTab => tabs.FirstOrDefault((ChatTab t) => t.Discord); public string PendingLinkCode { get; private set; } public IEnumerable<ChatTab> VisibleTabs => tabs.Where((ChatTab t) => t.Channel == 0 || !muted.Contains(t.Channel)); public ChatTab ActiveTab => tabs.First((ChatTab t) => t.Id == ActiveTabId); public IEnumerable<int> MutedChannels => muted.OrderBy((int c) => c); public ChatState() { tabs.Add(new ChatTab("local", "Local", 0)); SetChannels(new Tuple<int, string>[3] { Tuple.Create(1, "Global #1"), Tuple.Create(2, "Global #2"), Tuple.Create(3, "Global #3") }); tabs.Add(new ChatTab("pm", "PM", 0)); ActiveTabId = "local"; } public void SetDiscordLinked(string name) { DiscordLinked = (string.IsNullOrEmpty(name) ? null : name); if (DiscordLinked != null) { PendingLinkCode = null; } Changed(); } public void SetPendingLinkCode(string command) { PendingLinkCode = (string.IsNullOrEmpty(command) ? null : command); Changed(); } public ChatTab Tab(string id) { return tabs.FirstOrDefault((ChatTab t) => t.Id == id); } public ChatTab ChannelTab(int channel) { return tabs.FirstOrDefault((ChatTab t) => t.Channel == channel && channel != 0); } public void SetChannels(IEnumerable<Tuple<int, string>> channels) { foreach (Tuple<int, string> channel in channels) { ChatTab chatTab = ChannelTab(channel.Item1); if (chatTab != null) { chatTab.Title = channel.Item2; continue; } int num = tabs.FindIndex((ChatTab t) => t.Id == "pm"); ChatTab item = new ChatTab("g" + channel.Item1, channel.Item2, channel.Item1); if (num >= 0) { tabs.Insert(num, item); } else { tabs.Add(item); } } Changed(); } public void SetChannelInfo(int channel, string label, bool discord) { ChatTab chatTab = ChannelTab(channel); if (chatTab != null) { chatTab.Label = (string.IsNullOrEmpty(label) ? null : label); chatTab.Discord = discord; Changed(); } } public void Select(string tabId) { ChatTab chatTab = Tab(tabId); if (chatTab != null && (chatTab.Channel == 0 || !muted.Contains(chatTab.Channel))) { ActiveTabId = tabId; chatTab.Unread = false; Changed(); } } public bool IsMuted(int channel) { return muted.Contains(channel); } public void SetMuted(int channel, bool value) { if (ChannelTab(channel) != null) { if (value) { muted.Add(channel); } else { muted.Remove(channel); } if (value && ActiveTab.Channel == channel) { ActiveTabId = "local"; } Changed(); } } public void SetMutedFromConfig(string text) { muted.Clear(); string[] array = (text ?? string.Empty).Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { if (int.TryParse(array[i].Trim(), out var result)) { muted.Add(result); } } Changed(); } public string MutedToConfig() { return string.Join(",", MutedChannels.Select((int c) => c.ToString()).ToArray()); } public void AddLocal(string formatted) { Append(Tab("local"), formatted); } public void AddChannelMessage(int channel, string from, string text, bool fromDiscord = false) { if (!muted.Contains(channel)) { ChatTab chatTab = ChannelTab(channel); if (chatTab != null) { string text2 = ChannelColors[Math.Max(0, channel - 1) % ChannelColors.Length]; string text3 = (fromDiscord ? ("<color=#8ea1e1>[D] " + Clean(from) + "</color>") : ("<color=orange>" + NameLink(from) + "</color>")); Append(chatTab, text3 + ": <color=" + text2 + ">" + Clean(text) + "</color>"); } } } public void AddPrivateMessage(string from, string to, string text, string myName, bool fromHistory = false) { bool flag = string.Equals(from, myName, StringComparison.OrdinalIgnoreCase); LastPmPartner = (flag ? to : from); string text2 = (flag ? ("To " + NameLink(to)) : ("From " + NameLink(from))); Append(Tab("pm"), "<color=#e6a8ff>[" + text2 + "]</color> " + WebLinks(Clean(text)), !fromHistory); } public void AddPmNotice(string text) { Append(Tab("pm"), "<color=#e6a8ff>[TerraChat]</color> " + WebLinks(Clean(text))); } public void LoadPmHistory(IEnumerable<PmEntry> entries) { ChatTab chatTab = Tab("pm"); chatTab.Lines.Clear(); chatTab.Unread = false; LastPmPartner = null; foreach (PmEntry entry in entries) { AddPrivateMessage(entry.From, entry.To, entry.Text, entry.Outgoing ? entry.From : null, fromHistory: true); } Changed(); } public void AddSystem(string text, string tabId = null) { ChatTab tab = ((tabId != null) ? Tab(tabId) : null) ?? ActiveTab; Append(tab, "<color=#c8c8c8>" + Clean(text) + "</color>", markUnread: false); } public static string WebLinks(string cleaned) { return WebAddress.Replace(cleaned, delegate(Match m) { string text = m.Value.Replace("\"", ""); return "<link=\"url:" + text + "\"><color=#7fc8ff><u>" + text + "</u></color></link>"; }); } public static string NameLink(string name) { string text = Clean(name).Replace("\"", ""); return "<link=\"pm:" + text + "\"><u>" + text + "</u></link>"; } public static string Clean(string text) { return (text ?? string.Empty).Replace('<', ' ').Replace('>', ' '); } private void Append(ChatTab tab, string line, bool markUnread = true) { if (tab != null) { tab.Lines.Add(line); if (tab.Lines.Count > 200) { tab.Lines.RemoveRange(0, tab.Lines.Count - 200); } if (markUnread && tab.Id != ActiveTabId) { tab.Unread = true; } LastMessageUtc = DateTime.UtcNow; Changed(); } } private void Changed() { Version++; } } public enum ChatCommandKind { None, Local, Channel, PrivateMessage, Who, MuteChannel, UnmuteChannel, Admin, LinkDiscord, DiscordInvite, UnlinkDiscord, Help, Error } public sealed class ChatCommand { public ChatCommandKind Kind; public int Channel; public string Target; public string Text; public string AdminCommand; public double Minutes; public static ChatCommand Error(string text) { return new ChatCommand { Kind = ChatCommandKind.Error, Text = text }; } } public static class CommandParser { public const string HelpText = "TerraChat: Enter opens chat, Tab switches tabs. /g1 /g2 /g3 text = global channel, /g4 text = Heim (linked to Discord), /pm Name text (\"quoted name\" if it has spaces), /r text = reply, /who = online players, /discord = our Discord invite, /tc mute 2 | /tc unmute 2 = mute a channel, /tc link | /tc unlink = Discord profile, /tc help. Other / commands go to Valheim chat."; public const string ModeratorHelp = "Moderation: /tc mute Name [minutes], /tc unmute Name, /tc kick Name, /tc ban Name [minutes], /tc unban Name."; public const string AdminHelp = "Moderation: /tc mute Name [minutes], /tc unmute Name, /tc kick Name, /tc ban Name [minutes], /tc unban Name. Admins: /tc mod Name, /tc unmod Name."; public static ChatCommand Parse(string input, ChatTab activeTab, string lastPmPartner) { string text = (input ?? string.Empty).Trim(); if (text.Length == 0) { return new ChatCommand { Kind = ChatCommandKind.None }; } if (text[0] == '/') { string rest; string text2 = FirstWord(text, out rest); switch (text2.ToLowerInvariant()) { case "/g1": case "/g2": case "/g3": case "/g4": return Channel(int.Parse(text2.Substring(2)), rest); case "/pm": return PrivateMessage(rest); case "/r": if (string.IsNullOrEmpty(lastPmPartner)) { return ChatCommand.Error("Nobody to reply to yet. Use /pm Name text."); } return Pm(lastPmPartner, rest); case "/who": return new ChatCommand { Kind = ChatCommandKind.Who }; case "/discord": return new ChatCommand { Kind = ChatCommandKind.DiscordInvite }; case "/tc": return TerraChatCommand(rest); default: return new ChatCommand { Kind = ChatCommandKind.Local, Text = text }; } } if (activeTab == null || activeTab.Id == "local") { return new ChatCommand { Kind = ChatCommandKind.Local, Text = text }; } if (activeTab.Id == "pm") { if (string.IsNullOrEmpty(lastPmPartner)) { return ChatCommand.Error("Start a conversation with /pm Name text."); } return Pm(lastPmPartner, text); } return Channel(activeTab.Channel, text); } private static ChatCommand TerraChatCommand(string rest) { string rest2; string text = FirstWord(rest, out rest2).ToLowerInvariant(); switch (text) { case "help": case "": return new ChatCommand { Kind = ChatCommandKind.Help }; case "mute": case "unmute": if (rest2.Length == 1 && char.IsDigit(rest2[0])) { return new ChatCommand { Kind = ((text == "mute") ? ChatCommandKind.MuteChannel : ChatCommandKind.UnmuteChannel), Channel = rest2[0] - 48 }; } return Admin(text, rest2); case "link": return new ChatCommand { Kind = ChatCommandKind.LinkDiscord }; case "unlink": return new ChatCommand { Kind = ChatCommandKind.UnlinkDiscord }; case "kick": case "ban": case "mod": case "unban": case "unmod": return Admin(text, rest2); default: return ChatCommand.Error("Unknown /tc command. Try /tc help."); } } private static ChatCommand Admin(string command, string args) { string rest; string text = ReadName(args, out rest); if (text.Length == 0) { return ChatCommand.Error("Usage: /tc " + command + " Name" + ((command == "mute" || command == "ban") ? " [minutes]" : "")); } double result = 0.0; if (rest.Length > 0 && !double.TryParse(rest, NumberStyles.Float, CultureInfo.InvariantCulture, out result)) { return ChatCommand.Error("Minutes must be a number."); } return new ChatCommand { Kind = ChatCommandKind.Admin, AdminCommand = command, Target = text, Minutes = result }; } private static ChatCommand PrivateMessage(string rest) { string rest2; string text = ReadName(rest, out rest2); if (text.Length == 0 || rest2.Length == 0) { return ChatCommand.Error("Usage: /pm Name text"); } return Pm(text, rest2); } private static ChatCommand Pm(string target, string text) { if (text.Length == 0) { return new ChatCommand { Kind = ChatCommandKind.None }; } return new ChatCommand { Kind = ChatCommandKind.PrivateMessage, Target = target, Text = text }; } private static ChatCommand Channel(int channel, string text) { if (text.Length == 0) { return new ChatCommand { Kind = ChatCommandKind.None }; } return new ChatCommand { Kind = ChatCommandKind.Channel, Channel = channel, Text = text }; } public static string ReadName(string text, out string rest) { text = (text ?? string.Empty).TrimStart(Array.Empty<char>()); if (text.StartsWith("\"", StringComparison.Ordinal)) { int num = text.IndexOf('"', 1); if (num > 1) { rest = text.Substring(num + 1).Trim(); return text.Substring(1, num - 1).Trim(); } } return FirstWord(text, out rest); } private static string FirstWord(string text, out string rest) { text = (text ?? string.Empty).TrimStart(Array.Empty<char>()); int num = text.IndexOf(' '); if (num < 0) { rest = string.Empty; return text; } rest = text.Substring(num + 1).Trim(); return text.Substring(0, num); } } public sealed class PmEntry { public long Ts; public string From; public string To; public string Text; public bool Outgoing; } public sealed class PmHistory { public const int MaxEntries = 500; private readonly string folder; public PmHistory(string folder) { this.folder = folder; } public string FileFor(string characterKey) { StringBuilder stringBuilder = new StringBuilder(); string text = characterKey ?? string.Empty; foreach (char c in text) { stringBuilder.Append((char.IsLetterOrDigit(c) || c == '-' || c == '_') ? c : '_'); } return Path.Combine(folder, ((stringBuilder.Length > 0) ? stringBuilder.ToString() : "unknown") + ".jsonl"); } public List<PmEntry> Load(string characterKey) { List<PmEntry> list = new List<PmEntry>(); string path = FileFor(characterKey); if (!File.Exists(path)) { return list; } string[] array = File.ReadAllLines(path, Encoding.UTF8); foreach (string text in array) { try { JObject val = JObject.Parse(text); list.Add(new PmEntry { Ts = ((long?)val["ts"]).GetValueOrDefault(), From = (string)val["from"], To = (string)val["to"], Text = (string)val["text"], Outgoing = ((bool?)val["out"] == true) }); } catch { } } return list.Skip(Math.Max(0, list.Count - 500)).ToList(); } public void Append(string characterKey, PmEntry entry) { //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) //IL_002f: 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_005b: 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_0088: Expected O, but got Unknown Directory.CreateDirectory(folder); string text = FileFor(characterKey); JObject val = new JObject { ["ts"] = JToken.op_Implicit(entry.Ts), ["from"] = JToken.op_Implicit(entry.From), ["to"] = JToken.op_Implicit(entry