using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using HarmonyLib; using MelonLoader; using Microsoft.CodeAnalysis; using MimesisTextChat; using Steamworks; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(TextChatMod), "MimesisTextChat", "0.1.0", "Codex local build", null)] [assembly: MelonGame("ReLUGames", "MIMESIS")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("MimesisTextChat")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("MimesisTextChat")] [assembly: AssemblyTitle("MimesisTextChat")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace MimesisTextChat { public sealed class TextChatMod : MelonMod { private readonly struct ChatMessage { public string SenderName { get; } public string Body { get; } public Color NameColor { get; } public ChatMessage(string senderName, string body, Color nameColor) { //IL_000f: 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) SenderName = senderName; Body = body; NameColor = nameColor; } } private readonly struct ChatLayout { public int VisibleMessages { get; } private float X { get; } private float Y { get; } private float Width { get; } private float LineHeight { get; } public ChatLayout(float x, float y, float width, float lineHeight, int visibleMessages) { X = x; Y = y; Width = width; LineHeight = lineHeight; VisibleMessages = visibleMessages; } public Rect GetLineRect(int line) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) return new Rect(X, Y + (float)line * LineHeight, Width, LineHeight); } } private const string PacketPrefix = "MIMESIS_TEXT_CHAT_V1|"; private const int MaxMessageCharacters = 180; private const int MaxVisibleMessages = 6; private const int MaxStoredMessages = 40; private const float VisibleSeconds = 7f; private const float FadeSeconds = 1.5f; private readonly List _messages = new List(); private Callback? _chatCallback; private GUIStyle? _labelStyle; private GUIStyle? _inputStyle; private string _draft = string.Empty; private bool _typing; private int _firstVisibleMessageIndex; private Keyboard? _subscribedKeyboard; private float _lastActivityTime = -999f; private float _nextCallbackAttempt; internal static bool IsTyping { get; private set; } public override void OnInitializeMelon() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) new Harmony("local.codex.mimesistextchat").PatchAll(typeof(TextChatMod).Assembly); MelonLogger.Msg("Loaded. Press Enter to open chat, Enter to send, and Escape to close."); } public override void OnUpdate() { EnsureCallback(); EnsureKeyboardSubscription(); HandleKeyboardShortcuts(); HandleMouseWheel(); } public override void OnGUI() { //IL_001e: 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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) EnsureStyles(); float overlayAlpha = GetOverlayAlpha(); if (_typing || !(overlayAlpha <= 0.01f)) { Color color = GUI.color; GUI.color = new Color(color.r, color.g, color.b, color.a * overlayAlpha); _firstVisibleMessageIndex = Mathf.Clamp(_firstVisibleMessageIndex, 0, GetNewestWindowStart()); DrawChatFeed(); if (_typing) { DrawDraft(); } GUI.color = color; } } public override void OnDeinitializeMelon() { if (_subscribedKeyboard != null) { _subscribedKeyboard.onTextInput -= OnTextInput; } } private void EnsureKeyboardSubscription() { Keyboard current = Keyboard.current; if (current != _subscribedKeyboard) { if (_subscribedKeyboard != null) { _subscribedKeyboard.onTextInput -= OnTextInput; } _subscribedKeyboard = current; if (_subscribedKeyboard != null) { _subscribedKeyboard.onTextInput += OnTextInput; } } } private void HandleKeyboardShortcuts() { Keyboard current = Keyboard.current; if (current == null) { return; } if (!_typing && (((ButtonControl)current.enterKey).wasPressedThisFrame || ((ButtonControl)current.numpadEnterKey).wasPressedThisFrame)) { OpenChat(); } else { if (!_typing) { return; } if (((ButtonControl)current.escapeKey).wasPressedThisFrame) { CloseChat(clearDraft: true); return; } if (((ButtonControl)current.enterKey).wasPressedThisFrame || ((ButtonControl)current.numpadEnterKey).wasPressedThisFrame) { SubmitDraft(); return; } if (((ButtonControl)current.backspaceKey).wasPressedThisFrame && _draft.Length > 0) { _draft = _draft.Substring(0, _draft.Length - 1); } if (((ButtonControl)current.deleteKey).wasPressedThisFrame) { _draft = string.Empty; } } } private void OnTextInput(char character) { if (_typing && _draft.Length < 180 && character != '\r' && character != '\n' && character != '\b' && !char.IsControl(character) && character != '<' && character != '>') { _draft += character; _lastActivityTime = Time.unscaledTime; } } private void OpenChat() { _typing = true; IsTyping = true; _lastActivityTime = Time.unscaledTime; } private void CloseChat(bool clearDraft) { _typing = false; IsTyping = false; if (clearDraft) { _draft = string.Empty; } _lastActivityTime = Time.unscaledTime; } private void EnsureCallback() { if (_chatCallback != null || Time.unscaledTime < _nextCallbackAttempt) { return; } _nextCallbackAttempt = Time.unscaledTime + 5f; try { _chatCallback = Callback.Create((DispatchDelegate)OnLobbyChatMessage); MelonLogger.Msg("Steam lobby-chat receiver is ready."); } catch (Exception ex) { MelonLogger.Warning("Steam lobby-chat receiver is not ready yet: " + ex.Message); } } private void SubmitDraft() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_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_007b: 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) string text = Sanitize(_draft); _draft = string.Empty; CloseChat(clearDraft: false); if (string.IsNullOrWhiteSpace(text)) { return; } CSteamID currentLobby = GetCurrentLobby(); if (currentLobby == CSteamID.Nil) { AddSystemMessage("Join or host a Steam lobby before sending chat."); return; } byte[] bytes = Encoding.UTF8.GetBytes("MIMESIS_TEXT_CHAT_V1|" + text); if (!SteamMatchmaking.SendLobbyChatMsg(currentLobby, bytes, bytes.Length)) { AddSystemMessage("Steam did not accept the chat message."); } else { AddMessage("Me", text, GetNameColor(SteamUser.GetSteamID())); } } private void OnLobbyChatMessage(LobbyChatMsg_t callback) { //IL_0000: 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_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_0012: 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) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) try { CSteamID val = new CSteamID(callback.m_ulSteamIDLobby); CSteamID nil = CSteamID.Nil; EChatEntryType val2 = (EChatEntryType)0; byte[] array = new byte[2048]; int lobbyChatEntry = SteamMatchmaking.GetLobbyChatEntry(val, (int)callback.m_iChatID, ref nil, array, array.Length, ref val2); if (lobbyChatEntry <= 0) { return; } string text = Encoding.UTF8.GetString(array, 0, lobbyChatEntry).TrimEnd('\0'); if (text.StartsWith("MIMESIS_TEXT_CHAT_V1|", StringComparison.Ordinal) && !(nil == SteamUser.GetSteamID())) { string text2 = Sanitize(SteamFriends.GetFriendPersonaName(nil)); if (string.IsNullOrWhiteSpace(text2)) { text2 = ((object)(CSteamID)(ref nil)).ToString(); } string text3 = Sanitize(text.Substring("MIMESIS_TEXT_CHAT_V1|".Length)); if (!string.IsNullOrWhiteSpace(text3)) { AddMessage(text2, text3, GetNameColor(nil)); } } } catch (Exception ex) { MelonLogger.Warning("Could not read a Steam lobby-chat message: " + ex.Message); } } private void AddSystemMessage(string message) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) AddMessage("System", message, new Color(1f, 0.86f, 0.35f)); } private void AddMessage(string senderName, string body, Color nameColor) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) _messages.Add(new ChatMessage(senderName, body, nameColor)); _lastActivityTime = Time.unscaledTime; while (_messages.Count > 40) { _messages.RemoveAt(0); } ScrollToBottom(); } private void HandleMouseWheel() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (!_typing) { return; } Mouse current = Mouse.current; if (current == null) { return; } float y = ((InputControl)(object)current.scroll).ReadValue().y; if (!(Math.Abs(y) < 0.01f)) { if (y > 0f) { _firstVisibleMessageIndex = Math.Max(0, _firstVisibleMessageIndex - 1); } else { _firstVisibleMessageIndex = Math.Min(GetNewestWindowStart(), _firstVisibleMessageIndex + 1); } _lastActivityTime = Time.unscaledTime; } } private void DrawChatFeed() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) ChatLayout chatLayout = GetChatLayout(_typing); if (chatLayout.VisibleMessages == 0) { return; } int num = 0; foreach (ChatMessage item in _messages.Skip(_firstVisibleMessageIndex).Take(6)) { DrawChatLine(chatLayout.GetLineRect(num), item); num++; } } private void DrawDraft() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) ChatLayout chatLayout = GetChatLayout(includeDraft: true); DrawShadowedLabel(chatLayout.GetLineRect(chatLayout.VisibleMessages), "> " + _draft, _inputStyle); } private ChatLayout GetChatLayout(bool includeDraft) { int num = Math.Min(6, Math.Max(0, _messages.Count - _firstVisibleMessageIndex)); int num2 = num + (includeDraft ? 1 : 0); float y = (float)Screen.height * 0.5f - (float)num2 * 34f * 0.5f; return new ChatLayout(48f, y, 840f, 34f, num); } private void DrawChatLine(Rect rect, ChatMessage message) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0029: 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_002e: 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_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) string text = message.SenderName + ": "; GUIStyle? labelStyle = _labelStyle; Vector2 val = ((labelStyle != null) ? labelStyle.CalcSize(new GUIContent(text)) : Vector2.zero); DrawShadowedLabel(rect, text, _labelStyle, message.NameColor); DrawShadowedLabel(new Rect(((Rect)(ref rect)).x + val.x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width - val.x, ((Rect)(ref rect)).height), message.Body, _labelStyle, Color.white); } private static void DrawShadowedLabel(Rect rect, string text, GUIStyle? style) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) DrawShadowedLabel(rect, text, style, Color.white); } private static void DrawShadowedLabel(Rect rect, string text, GUIStyle? style, Color textColor) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) if (style != null) { Color color = GUI.color; GUI.color = new Color(0f, 0f, 0f, color.a * 0.85f); GUI.Label(new Rect(((Rect)(ref rect)).x + 2f, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), text, style); GUI.color = new Color(textColor.r, textColor.g, textColor.b, color.a); GUI.Label(rect, text, style); GUI.color = color; } } private static Color GetNameColor(CSteamID steamId) { //IL_0000: 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) return Color.HSVToRGB((float)((ulong)steamId * 2654435761u % 360) / 360f, 0.62f, 1f); } private int GetNewestWindowStart() { return Math.Max(0, _messages.Count - 6); } private void ScrollToBottom() { _firstVisibleMessageIndex = GetNewestWindowStart(); } private float GetOverlayAlpha() { if (_typing) { return 1f; } float num = Time.unscaledTime - _lastActivityTime; if (num <= 7f) { return 1f; } return Mathf.Clamp01(1f - (num - 7f) / 1.5f); } private void EnsureStyles() { //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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_0040: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown if (_labelStyle == null || _inputStyle == null) { _labelStyle = new GUIStyle(GUI.skin.label) { fontSize = 26, wordWrap = true }; _inputStyle = new GUIStyle(GUI.skin.label) { fontSize = 28, wordWrap = true }; } } private static string Sanitize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(Math.Min(value.Length, 180)); foreach (char c in value) { if (stringBuilder.Length >= 180) { break; } if (!char.IsControl(c) && c != '<' && c != '>') { stringBuilder.Append(c); } } return stringBuilder.ToString().Trim(); } private static CSteamID GetCurrentLobby() { //IL_006a: 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_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_0073: 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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) try { Type type = Type.GetType("SteamInviteDispatcher, Assembly-CSharp"); if (type == null) { return CSteamID.Nil; } Object val = Object.FindObjectOfType(type); FieldInfo field = type.GetField("joinedLobbyID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); return (val != (Object)null && field?.GetValue(val) is CSteamID val2) ? val2 : CSteamID.Nil; } catch { return CSteamID.Nil; } } } [HarmonyPatch] public static class GameInputBlockPatch { private static MethodBase? TargetMethod() { return Type.GetType("Mimic.InputSystem.InputManager, Assembly-CSharp")?.GetMethod("Update", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } private static bool Prefix() { return !TextChatMod.IsTyping; } } }