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<ChatMessage> _messages = new List<ChatMessage>();
private Callback<LobbyChatMsg_t>? _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<LobbyChatMsg_t>.Create((DispatchDelegate<LobbyChatMsg_t>)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<Vector2>)(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;
}
}
}