using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.UI;
using UnityEngine.SceneManagement;
using UnityEngine.TextCore.LowLevel;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("GWYF.TextChat")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.5.0")]
[assembly: AssemblyInformationalVersion("1.1.5+9c38cd31e5d0e83a33a4100444e82cdbf0d65f79")]
[assembly: AssemblyProduct("GWYF.TextChat")]
[assembly: AssemblyTitle("GWYF.TextChat")]
[assembly: AssemblyVersion("1.1.5.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace GWYF.TextChat
{
public sealed class ChatImeInput : BaseInput
{
private Keyboard keyboard;
internal readonly Queue<char> PendingText = new Queue<char>();
internal string Composition = "";
internal int LastCompositionFrame = -100;
internal int LastTextFrame = -100;
private IMECompositionMode mode;
private Vector2 position;
internal bool IsComposing
{
get
{
if (Composition.Length <= 0)
{
return Time.frameCount <= LastCompositionFrame + 1;
}
return true;
}
}
public override string compositionString => Composition;
public override IMECompositionMode imeCompositionMode
{
get
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
return mode;
}
set
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Invalid comparison between Unknown and I4
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Invalid comparison between Unknown and I4
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
mode = value;
Keyboard current = Keyboard.current;
if (current != null)
{
current.SetIMEEnabled((int)value != 2);
}
try
{
Input.imeCompositionMode = value;
}
catch (InvalidOperationException)
{
}
if ((int)value == 1)
{
WindowsIme.Begin();
}
}
}
public override Vector2 compositionCursorPos
{
get
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
return position;
}
set
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
position = value;
Keyboard current = Keyboard.current;
if (current != null)
{
current.SetIMECursorPosition(value);
}
}
}
internal void ResetComposition()
{
PendingText.Clear();
Composition = "";
LastCompositionFrame = (LastTextFrame = -100);
}
private void Update()
{
if (keyboard != Keyboard.current)
{
if (keyboard != null)
{
keyboard.onIMECompositionChange -= OnComposition;
keyboard.onTextInput -= OnText;
}
keyboard = Keyboard.current;
if (keyboard != null)
{
keyboard.onIMECompositionChange += OnComposition;
keyboard.onTextInput += OnText;
}
}
}
private void OnText(char character)
{
if (Plugin.IsTyping && !char.IsControl(character))
{
PendingText.Enqueue(character);
LastTextFrame = Time.frameCount;
}
}
private unsafe void OnComposition(IMECompositionString value)
{
if (Plugin.IsTyping)
{
Composition = ((object)(*(IMECompositionString*)(&value))/*cast due to .constrained prefix*/).ToString();
LastCompositionFrame = Time.frameCount;
}
}
protected override void OnDestroy()
{
if (keyboard != null)
{
keyboard.onIMECompositionChange -= OnComposition;
keyboard.onTextInput -= OnText;
}
((UIBehaviour)this).OnDestroy();
}
}
public sealed class ChatInputField : TMP_InputField
{
internal ChatImeInput Ime;
internal Action SubmitChat;
internal Action CancelChat;
internal int OpenedFrame;
private readonly Event keyEvent = new Event();
private bool submitPending;
private int submitFrame;
private float submitTime;
internal void ResetSubmission()
{
submitPending = false;
}
protected override void LateUpdate()
{
((TMP_InputField)this).LateUpdate();
if (!Plugin.IsTyping || !((TMP_InputField)this).isFocused || Time.frameCount <= OpenedFrame + 1)
{
return;
}
Keyboard current = Keyboard.current;
if (current == null)
{
return;
}
while (Ime.PendingText.Count > 0)
{
((TMP_InputField)this).Append(Ime.PendingText.Dequeue());
}
if (!submitPending && (((ButtonControl)current.enterKey).wasPressedThisFrame || ((ButtonControl)current.numpadEnterKey).wasPressedThisFrame))
{
submitPending = true;
submitFrame = Time.frameCount;
submitTime = Time.unscaledTime;
}
if (submitPending)
{
if (!Ime.IsComposing && Time.frameCount > Math.Max(submitFrame, Ime.LastTextFrame) + 1)
{
submitPending = false;
SubmitChat?.Invoke();
}
else if (Time.unscaledTime - submitTime > 1f)
{
submitPending = false;
}
}
}
public override void OnUpdateSelected(BaseEventData data)
{
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Invalid comparison between Unknown and I4
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Invalid comparison between Unknown and I4
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Invalid comparison between Unknown and I4
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Invalid comparison between Unknown and I4
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
if (!((TMP_InputField)this).isFocused)
{
return;
}
while (Ime.PendingText.Count > 0)
{
((TMP_InputField)this).Append(Ime.PendingText.Dequeue());
}
while (Event.PopEvent(keyEvent))
{
if ((int)keyEvent.rawType == 4)
{
if ((int)keyEvent.keyCode != 13 && (int)keyEvent.keyCode != 271)
{
if ((int)keyEvent.keyCode == 27)
{
CancelChat?.Invoke();
((AbstractEventData)data).Use();
return;
}
keyEvent.character = '\0';
((TMP_InputField)this).KeyPressed(keyEvent);
}
}
else if (keyEvent.commandName == "SelectAll")
{
((TMP_InputField)this).SelectAll();
}
}
((TMP_InputField)this).UpdateLabel();
((AbstractEventData)data).Use();
}
}
internal static class ChatProtocol
{
internal const int MaxCharacters = 256;
internal const int MaxPacketBytes = 2048;
private static readonly byte[] Marker = Encoding.ASCII.GetBytes("GWYF.TextChat/1\n");
private static readonly UTF8Encoding Utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
internal static string Clean(string value, int limit = 256)
{
StringBuilder stringBuilder = new StringBuilder();
if (value == null)
{
return "";
}
foreach (char c in value)
{
if (stringBuilder.Length >= limit)
{
break;
}
if (char.IsControl(c))
{
continue;
}
switch (c)
{
case '\u2028':
case '\u2029':
case '\u202a':
case '\u202b':
case '\u202c':
case '\u202d':
case '\u202e':
continue;
}
if (c < '\u2066' || c > '\u2069')
{
stringBuilder.Append(c);
}
}
if (stringBuilder.Length > 0 && char.IsHighSurrogate(stringBuilder[stringBuilder.Length - 1]))
{
stringBuilder.Length--;
}
return stringBuilder.ToString().Trim();
}
internal static byte[] Encode(string text)
{
byte[] bytes = Utf8.GetBytes(Clean(text));
byte[] array = new byte[Marker.Length + bytes.Length];
Buffer.BlockCopy(Marker, 0, array, 0, Marker.Length);
Buffer.BlockCopy(bytes, 0, array, Marker.Length, bytes.Length);
return array;
}
internal static bool TryDecode(byte[] packet, int length, out string text)
{
text = "";
if (packet == null || length <= Marker.Length || length > 2048 || length > packet.Length)
{
return false;
}
for (int i = 0; i < Marker.Length; i++)
{
if (packet[i] != Marker[i])
{
return false;
}
}
try
{
text = Clean(Utf8.GetString(packet, Marker.Length, length - Marker.Length));
}
catch (DecoderFallbackException)
{
return false;
}
return text.Length > 0;
}
}
internal static class GameChatTheme
{
internal static readonly Color Paper = Color32.op_Implicit(new Color32((byte)247, (byte)242, (byte)226, byte.MaxValue));
internal static readonly Color Gold = Color32.op_Implicit(new Color32(byte.MaxValue, (byte)219, (byte)82, byte.MaxValue));
internal static TMP_FontAsset FindFont()
{
return (from t in Resources.FindObjectsOfTypeAll<TextMeshProUGUI>().Where(delegate(TextMeshProUGUI t)
{
//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)
if ((Object)(object)((TMP_Text)t).font != (Object)null)
{
Scene scene = ((Component)t).gameObject.scene;
if (((Scene)(ref scene)).IsValid())
{
return ((Object)((TMP_Text)t).transform.root).name != "TextChatCanvas";
}
}
return false;
})
group t by ((TMP_Text)t).font into g
orderby g.Sum((TextMeshProUGUI t) => ((!((Behaviour)t).isActiveAndEnabled) ? 1 : 10) + ((((TMP_Text)t).text ?? "").Any((char c) => c >= '가' && c <= '힣') ? 25 : 0)) descending
select g.Key).FirstOrDefault() ?? TMP_Settings.defaultFontAsset;
}
}
public struct ChatWireV2 : NetworkMessage
{
public byte Kind;
public string Name;
public byte[] Body;
}
internal sealed class NetworkChat
{
private const string CapabilityKey = "gwyf.textchat.protocol";
private const string Version = "2";
private readonly Action<string, string> receive;
private readonly Dictionary<int, NetworkConnectionToClient> peers = new Dictionary<int, NetworkConnectionToClient>();
private readonly Dictionary<int, float> lastSend = new Dictionary<int, float>();
private NetworkConnectionToServer clientConnection;
private bool serverWasActive;
private float nextHello;
private float nextMetadata;
private CSteamID advertisedLobby;
private LobbySettings lobby;
private static readonly UTF8Encoding Utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
internal bool HostReady { get; private set; }
internal static bool InSession
{
get
{
//IL_001b: 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)
if (NetworkClient.isConnected && NetworkClient.ready && (Object)(object)NetworkClient.localPlayer != (Object)null)
{
Scene activeScene = SceneManager.GetActiveScene();
return IsPlayScene(((Scene)(ref activeScene)).name);
}
return false;
}
}
internal NetworkChat(Action<string, string> receive)
{
this.receive = receive;
Writer<ChatWireV2>.write = Write;
Reader<ChatWireV2>.read = Read;
RegisterServer();
RegisterClient();
}
internal static bool IsPlayScene(string scene)
{
switch (scene)
{
default:
return scene == "GameTest";
case "HomeScene":
case "CasinoScene":
case "LoseStateScene":
case "WinStateScene":
case "SummaryScene":
return true;
}
}
internal void RegisterServer()
{
NetworkServer.ReplaceHandler<ChatWireV2>((Action<NetworkConnectionToClient, ChatWireV2>)OnServerMessage, true);
}
internal void RegisterClient()
{
NetworkClient.ReplaceHandler<ChatWireV2>((Action<ChatWireV2>)OnClientMessage, true);
}
internal void ResetServer()
{
peers.Clear();
lastSend.Clear();
}
internal void ResetClient()
{
clientConnection = null;
HostReady = false;
nextHello = 0f;
}
internal void Tick()
{
//IL_0120: Unknown result type (might be due to invalid IL or missing references)
//IL_0125: Unknown result type (might be due to invalid IL or missing references)
//IL_0132: Unknown result type (might be due to invalid IL or missing references)
//IL_0138: Unknown result type (might be due to invalid IL or missing references)
//IL_013d: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
//IL_00b8: 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_00f3: 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_00ca: Unknown result type (might be due to invalid IL or missing references)
//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
if (!NetworkServer.active && serverWasActive)
{
ResetServer();
}
serverWasActive = NetworkServer.active;
if (clientConnection != NetworkClient.connection)
{
ResetClient();
clientConnection = NetworkClient.connection;
}
if (!NetworkClient.isConnected)
{
HostReady = false;
}
if (SteamManager.Initialized && Time.unscaledTime >= nextMetadata)
{
nextMetadata = Time.unscaledTime + 1f;
if ((Object)(object)lobby == (Object)null)
{
lobby = Resources.Load<LobbySettings>("LobbySettings");
}
CSteamID val = (((Object)(object)lobby != (Object)null) ? lobby.steamLobbyID : CSteamID.Nil);
if (val != advertisedLobby)
{
if (advertisedLobby != CSteamID.Nil)
{
SteamMatchmaking.SetLobbyMemberData(advertisedLobby, "gwyf.textchat.protocol", "");
}
advertisedLobby = val;
}
if (val != CSteamID.Nil)
{
SteamMatchmaking.SetLobbyMemberData(val, "gwyf.textchat.protocol", "2");
}
}
bool flag = NetworkClient.activeHost;
if (!flag && SteamManager.Initialized && advertisedLobby != CSteamID.Nil)
{
flag = SteamMatchmaking.GetLobbyMemberData(advertisedLobby, SteamMatchmaking.GetLobbyOwner(advertisedLobby), "gwyf.textchat.protocol") == "2";
}
if (!HostReady && NetworkClient.isConnected && NetworkClient.ready && flag && Time.unscaledTime >= nextHello)
{
nextHello = Time.unscaledTime + 2f;
NetworkClient.Send<ChatWireV2>(new ChatWireV2
{
Kind = 1,
Name = "2"
}, 0);
}
}
internal bool Send(string text)
{
if (!InSession || !HostReady)
{
return false;
}
NetworkClient.Send<ChatWireV2>(new ChatWireV2
{
Kind = 3,
Body = ChatProtocol.Encode(text)
}, 0);
return true;
}
private void OnServerMessage(NetworkConnectionToClient connection, ChatWireV2 message)
{
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
if (!((NetworkConnection)connection).isAuthenticated || !((NetworkConnection)connection).isReady)
{
return;
}
if (message.Kind == 1 && message.Name == "2")
{
peers[connection.connectionId] = connection;
((NetworkConnection)connection).Send<ChatWireV2>(new ChatWireV2
{
Kind = 2,
Name = "2"
}, 0);
}
else
{
if (message.Kind != 3)
{
return;
}
Scene activeScene = SceneManager.GetActiveScene();
if (!IsPlayScene(((Scene)(ref activeScene)).name) || !peers.TryGetValue(connection.connectionId, out var value) || value != connection)
{
return;
}
byte[] body = message.Body;
if (!ChatProtocol.TryDecode(body, (body != null) ? body.Length : 0, out var text) || (lastSend.TryGetValue(connection.connectionId, out var value2) && Time.unscaledTime - value2 < 0.65f))
{
return;
}
lastSend[connection.connectionId] = Time.unscaledTime;
ChatWireV2 chatWireV = new ChatWireV2
{
Kind = 4,
Name = SenderName(connection),
Body = ChatProtocol.Encode(text)
};
foreach (KeyValuePair<int, NetworkConnectionToClient> item in new List<KeyValuePair<int, NetworkConnectionToClient>>(peers))
{
if (!NetworkServer.connections.TryGetValue(item.Key, out var value3) || value3 != item.Value)
{
peers.Remove(item.Key);
lastSend.Remove(item.Key);
}
else if (((NetworkConnection)value3).isAuthenticated && ((NetworkConnection)value3).isReady)
{
((NetworkConnection)value3).Send<ChatWireV2>(chatWireV, 0);
}
}
}
}
private void OnClientMessage(ChatWireV2 message)
{
if (message.Kind == 2 && message.Name == "2")
{
HostReady = true;
return;
}
byte[] body = message.Body;
if (message.Kind == 4 && HostReady && InSession && ChatProtocol.TryDecode(body, (body != null) ? body.Length : 0, out var text))
{
receive(ChatProtocol.Clean(message.Name, 48), text);
}
}
private static string SenderName(NetworkConnectionToClient connection)
{
//IL_0012: 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)
ulong result = 0uL;
if (connection is LocalConnectionToClient && SteamManager.Initialized)
{
result = SteamUser.GetSteamID().m_SteamID;
}
if (result == 0L)
{
ulong.TryParse(connection.address, out result);
}
if (result == 0L && (Object)(object)((NetworkConnection)connection).identity != (Object)null)
{
SteamIdComponent componentInChildren = ((Component)((NetworkConnection)connection).identity).GetComponentInChildren<SteamIdComponent>();
if ((Object)(object)componentInChildren != (Object)null)
{
result = componentInChildren.SteamId;
}
}
if (result != 0L && SteamManager.Initialized)
{
string text = ChatProtocol.Clean(SteamFriends.GetFriendPersonaName(new CSteamID(result)), 48);
if (text.Length > 0)
{
return text;
}
}
return "Player " + connection.connectionId;
}
private static void Write(NetworkWriter writer, ChatWireV2 value)
{
writer.WriteByte(value.Kind);
byte[] bytes = Utf8.GetBytes(ChatProtocol.Clean(value.Name, 48));
WriteBlock(writer, bytes, 192);
WriteBlock(writer, value.Body ?? Array.Empty<byte>(), 2048);
}
private static void WriteBlock(NetworkWriter writer, byte[] bytes, int maximum)
{
if (bytes.Length > maximum)
{
throw new InvalidDataException("Chat packet exceeds limit.");
}
NetworkWriterExtensions.WriteUShort(writer, (ushort)bytes.Length);
writer.WriteBytes(bytes, 0, bytes.Length);
}
private static byte[] ReadBlock(NetworkReader reader, int maximum)
{
int num = NetworkReaderExtensions.ReadUShort(reader);
if (num > maximum)
{
throw new InvalidDataException("Chat packet exceeds limit.");
}
return reader.ReadBytes(new byte[num], num);
}
private static ChatWireV2 Read(NetworkReader reader)
{
return new ChatWireV2
{
Kind = reader.ReadByte(),
Name = Utf8.GetString(ReadBlock(reader, 192)),
Body = ReadBlock(reader, 2048)
};
}
internal void Dispose()
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: 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)
NetworkServer.UnregisterHandler<ChatWireV2>();
NetworkClient.UnregisterHandler<ChatWireV2>();
if (SteamManager.Initialized && advertisedLobby != CSteamID.Nil)
{
SteamMatchmaking.SetLobbyMemberData(advertisedLobby, "gwyf.textchat.protocol", "");
}
ResetServer();
ResetClient();
}
}
[HarmonyPatch(typeof(CustomNetworkManager), "OnStartServer")]
internal static class RegisterChatServer
{
private static void Prefix()
{
Plugin.Instance?.Chat?.RegisterServer();
}
}
[HarmonyPatch(typeof(CustomNetworkManager), "OnClientConnect")]
internal static class RegisterChatClient
{
private static void Prefix()
{
Plugin.Instance?.Chat?.RegisterClient();
}
}
[HarmonyPatch(typeof(CustomNetworkManager), "OnStopServer")]
internal static class StopChatServer
{
private static void Postfix()
{
Plugin.Instance?.Chat?.ResetServer();
}
}
[HarmonyPatch(typeof(CustomNetworkManager), "OnStopClient")]
internal static class StopChatClient
{
private static void Postfix()
{
Plugin.Instance?.Chat?.ResetClient();
}
}
[BepInPlugin("gwyf.textchat", "Text Chat", "1.1.5")]
public sealed class Plugin : BaseUnityPlugin
{
public const string Id = "gwyf.textchat";
internal static Plugin Instance;
private Harmony harmony;
internal NetworkChat Chat;
private bool sessionActive;
private GameObject canvasObject;
private GameObject panel;
private GameObject inputObject;
private GameObject ownEventSystem;
private CanvasGroup panelGroup;
private TextMeshProUGUI history;
private TextMeshProUGUI hint;
private ChatInputField field;
private ChatImeInput ime;
private TMP_FontAsset font;
private TMP_FontAsset hangulFont;
private bool ownsFont;
private bool open;
private InputLayer oldLayer;
private CursorLockMode oldLock;
private bool oldCursorVisible;
private bool oldNavigation;
private EventSystem editingSystem;
private BaseInputModule editingModule;
private BaseInput oldInput;
private GameObject oldSelection;
private float lastMessage = -100f;
private float lastSend = -100f;
private readonly Queue<string> lines = new Queue<string>();
private ConfigEntry<float> displaySeconds;
internal static bool IsTyping
{
get
{
if ((Object)(object)Instance != (Object)null)
{
return Instance.open;
}
return false;
}
}
private void Awake()
{
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Expected O, but got Unknown
Instance = this;
Chat = new NetworkChat(Receive);
displaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "VisibleSeconds", 12f, "Seconds to show recent messages when chat is closed.");
harmony = new Harmony("gwyf.textchat");
harmony.PatchAll();
SceneManager.activeSceneChanged += SceneChanged;
((BaseUnityPlugin)this).Logger.LogInfo((object)"Text Chat 1.1.5 loaded (Mirror). Enter: chat, Enter: send, Escape: cancel.");
}
private void Update()
{
Chat.Tick();
bool inSession = NetworkChat.InSession;
if (inSession != sessionActive)
{
Close();
sessionActive = inSession;
((BaseUnityPlugin)this).Logger.LogInfo((object)(inSession ? "Gameplay chat enabled." : "Gameplay chat disabled."));
lines.Clear();
if ((Object)(object)history != (Object)null)
{
((TMP_Text)history).text = "";
}
}
if (!inSession)
{
Close();
if ((Object)(object)canvasObject != (Object)null)
{
canvasObject.SetActive(false);
}
return;
}
if ((Object)(object)canvasObject == (Object)null)
{
BuildUi();
}
canvasObject.SetActive(true);
Keyboard current = Keyboard.current;
if (!open && current != null && (((ButtonControl)current.enterKey).wasPressedThisFrame || ((ButtonControl)current.numpadEnterKey).wasPressedThisFrame))
{
EventSystem current2 = EventSystem.current;
GameObject val = ((current2 != null) ? current2.currentSelectedGameObject : null);
if ((Object)(object)val == (Object)null || ((Object)(object)val.GetComponent<TMP_InputField>() == (Object)null && (Object)(object)val.GetComponent<InputField>() == (Object)null))
{
Open();
}
}
if (open && current != null && ((ButtonControl)current.escapeKey).wasPressedThisFrame)
{
Close();
}
panelGroup.alpha = ((open || Time.unscaledTime - lastMessage < Mathf.Clamp(displaySeconds.Value, 1f, 120f)) ? 1 : 0);
}
private void LateUpdate()
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Invalid comparison between Unknown and I4
if (open)
{
if ((Object)(object)EventSystem.current != (Object)(object)editingSystem || (int)InputEvents.ActiveLayer != 3)
{
Close();
return;
}
Cursor.lockState = (CursorLockMode)0;
Cursor.visible = true;
}
}
private void Open()
{
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Invalid comparison between Unknown and I4
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00a6: 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)
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
if (NetworkChat.InSession && (int)InputEvents.ActiveLayer != 1)
{
EnsureEventSystem();
editingSystem = EventSystem.current;
editingModule = editingSystem.currentInputModule ?? ((Component)editingSystem).GetComponent<BaseInputModule>();
if (!((Object)(object)editingModule == (Object)null))
{
oldInput = editingModule.inputOverride;
editingModule.inputOverride = (BaseInput)(object)ime;
oldSelection = editingSystem.currentSelectedGameObject;
oldNavigation = editingSystem.sendNavigationEvents;
editingSystem.sendNavigationEvents = false;
oldLayer = InputEvents.ActiveLayer;
oldLock = Cursor.lockState;
oldCursorVisible = Cursor.visible;
ResetGameplayInput();
InputEvents.ActiveLayer = (InputLayer)3;
open = true;
inputObject.SetActive(true);
((TMP_Text)hint).text = "ENTER Send ESC Cancel";
((TMP_InputField)field).text = "";
field.ResetSubmission();
ime.ResetComposition();
field.OpenedFrame = Time.frameCount;
editingSystem.SetSelectedGameObject(inputObject);
((TMP_InputField)field).ActivateInputField();
((BaseInput)ime).imeCompositionMode = (IMECompositionMode)1;
((BaseUnityPlugin)this).Logger.LogInfo((object)"Chat input opened.");
Cursor.lockState = (CursorLockMode)0;
Cursor.visible = true;
}
}
}
private void Close()
{
//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Invalid comparison between Unknown and I4
//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
if (!open)
{
return;
}
open = false;
field.ResetSubmission();
((TMP_InputField)field).DeactivateInputField(false);
ime.ResetComposition();
((BaseInput)ime).imeCompositionMode = (IMECompositionMode)0;
WindowsIme.End();
if ((Object)(object)editingModule != (Object)null && (Object)(object)editingModule.inputOverride == (Object)(object)ime)
{
editingModule.inputOverride = oldInput;
}
if ((Object)(object)editingSystem != (Object)null)
{
editingSystem.sendNavigationEvents = oldNavigation;
if ((Object)(object)editingSystem.currentSelectedGameObject == (Object)(object)inputObject)
{
editingSystem.SetSelectedGameObject(oldSelection);
}
}
if ((int)InputEvents.ActiveLayer == 3)
{
InputEvents.ActiveLayer = oldLayer;
}
Cursor.lockState = oldLock;
Cursor.visible = oldCursorVisible;
inputObject.SetActive(false);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Chat input closed.");
((TMP_Text)hint).text = "ENTER Chat";
}
private static void ResetGameplayInput()
{
//IL_000b: 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)
InputEvents.OnMoveEvent?.Invoke(Vector2.zero);
InputEvents.OnAimEvent?.Invoke(Vector2.zero);
InputEvents.UpdateJump(false);
InputEvents.UpdateCrouch(false);
InputEvents.UpdateSprint(false);
InputEvents.UpdateInteract(false);
InputEvents.UpdateUseItem(false);
InputEvents.UpdateThrowItem(false);
InputEvents.UpdateZoom(false);
InputEvents.UpdateEmoteWheel(false);
}
private void Send()
{
string text = ChatProtocol.Clean(((TMP_InputField)field).text);
if (text.Length == 0)
{
Close();
return;
}
if (Time.unscaledTime - lastSend < 0.8f)
{
((TMP_Text)hint).text = "Please wait a moment";
return;
}
try
{
if (!Chat.Send(text))
{
((TMP_Text)hint).text = "The host needs Text Chat 1.1 or newer";
return;
}
}
catch (EncoderFallbackException)
{
((TMP_Text)hint).text = "Please check your input";
return;
}
lastSend = Time.unscaledTime;
((BaseUnityPlugin)this).Logger.LogInfo((object)("Chat message queued via Mirror (" + text.Length + " characters)."));
Close();
}
private void Receive(string name, string text)
{
if (NetworkChat.InSession)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Chat broadcast received (" + text.Length + " characters)."));
AddLine(((name.Length == 0) ? "Player" : name) + ": " + text);
}
}
private void AddLine(string line)
{
if ((Object)(object)canvasObject == (Object)null)
{
BuildUi();
}
lines.Enqueue(line);
while (lines.Count > 30)
{
lines.Dequeue();
}
((TMP_Text)history).text = string.Join("\n", lines);
((TMP_Text)history).ForceMeshUpdate(false, false);
lastMessage = Time.unscaledTime;
panelGroup.alpha = 1f;
}
private void BuildUi()
{
//IL_020e: Unknown result type (might be due to invalid IL or missing references)
//IL_0218: Expected O, but got Unknown
//IL_025c: Unknown result type (might be due to invalid IL or missing references)
//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
//IL_02f6: 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_033c: Expected O, but got Unknown
//IL_035c: Unknown result type (might be due to invalid IL or missing references)
//IL_0361: Unknown result type (might be due to invalid IL or missing references)
//IL_0362: 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_036b: Unknown result type (might be due to invalid IL or missing references)
//IL_036c: Unknown result type (might be due to invalid IL or missing references)
//IL_0373: Unknown result type (might be due to invalid IL or missing references)
//IL_0385: Unknown result type (might be due to invalid IL or missing references)
//IL_0399: Unknown result type (might be due to invalid IL or missing references)
//IL_03b0: Unknown result type (might be due to invalid IL or missing references)
//IL_03bf: Unknown result type (might be due to invalid IL or missing references)
//IL_0408: Unknown result type (might be due to invalid IL or missing references)
//IL_0417: Unknown result type (might be due to invalid IL or missing references)
//IL_042e: Unknown result type (might be due to invalid IL or missing references)
//IL_0471: Unknown result type (might be due to invalid IL or missing references)
//IL_0476: Unknown result type (might be due to invalid IL or missing references)
//IL_0477: Unknown result type (might be due to invalid IL or missing references)
//IL_047e: Unknown result type (might be due to invalid IL or missing references)
//IL_0480: Unknown result type (might be due to invalid IL or missing references)
//IL_0481: Unknown result type (might be due to invalid IL or missing references)
//IL_0488: Unknown result type (might be due to invalid IL or missing references)
//IL_049a: Unknown result type (might be due to invalid IL or missing references)
//IL_04ae: Unknown result type (might be due to invalid IL or missing references)
//IL_04c3: Unknown result type (might be due to invalid IL or missing references)
//IL_04fe: Unknown result type (might be due to invalid IL or missing references)
//IL_0504: Expected O, but got Unknown
//IL_0523: Unknown result type (might be due to invalid IL or missing references)
//IL_052e: Unknown result type (might be due to invalid IL or missing references)
//IL_0543: Unknown result type (might be due to invalid IL or missing references)
//IL_0558: Unknown result type (might be due to invalid IL or missing references)
//IL_056e: Unknown result type (might be due to invalid IL or missing references)
//IL_057d: Unknown result type (might be due to invalid IL or missing references)
//IL_05a3: Unknown result type (might be due to invalid IL or missing references)
//IL_05ae: Unknown result type (might be due to invalid IL or missing references)
//IL_05c3: Unknown result type (might be due to invalid IL or missing references)
//IL_05ce: Unknown result type (might be due to invalid IL or missing references)
//IL_05d3: Unknown result type (might be due to invalid IL or missing references)
//IL_05d4: Unknown result type (might be due to invalid IL or missing references)
//IL_05db: Unknown result type (might be due to invalid IL or missing references)
//IL_0661: Unknown result type (might be due to invalid IL or missing references)
//IL_0685: Unknown result type (might be due to invalid IL or missing references)
//IL_06fc: Unknown result type (might be due to invalid IL or missing references)
//IL_0701: Unknown result type (might be due to invalid IL or missing references)
//IL_0711: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)font == (Object)null)
{
TMP_FontAsset val = GameChatTheme.FindFont();
string text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), "malgun.ttf");
if (File.Exists(text))
{
hangulFont = TMP_FontAsset.CreateFontAsset(text, 0, 32, 6, (GlyphRenderMode)4165, 1024, 1024);
}
if ((Object)(object)hangulFont != (Object)null)
{
hangulFont.isMultiAtlasTexturesEnabled = true;
}
if ((Object)(object)val != (Object)null)
{
font = Object.Instantiate<TMP_FontAsset>(val);
((Object)font).name = "TextChat_" + ((Object)val).name;
font.atlasPopulationMode = (AtlasPopulationMode)0;
font.atlasTextures = val.atlasTextures.Select((Texture2D t) => (!((Object)(object)t != (Object)null)) ? null : Object.Instantiate<Texture2D>(t)).ToArray();
((TMP_Asset)font).material = Object.Instantiate<Material>(((TMP_Asset)val).material);
((TMP_Asset)font).material.mainTexture = (Texture)(object)font.atlasTextures[0];
font.fallbackFontAssetTable = new List<TMP_FontAsset>(val.fallbackFontAssetTable ?? new List<TMP_FontAsset>());
if ((Object)(object)hangulFont != (Object)null)
{
font.fallbackFontAssetTable.Add(hangulFont);
}
font.ReadFontAssetDefinition();
ownsFont = true;
}
else
{
font = hangulFont;
}
if ((Object)(object)font == (Object)null)
{
throw new InvalidOperationException("No usable TextMeshPro font found.");
}
((BaseUnityPlugin)this).Logger.LogInfo((object)("Chat theme: game font=" + (((Object)(object)val != (Object)null) ? ((Object)val).name : "unavailable") + ", plain panel geometry"));
}
canvasObject = new GameObject("TextChatCanvas", new Type[4]
{
typeof(RectTransform),
typeof(Canvas),
typeof(CanvasScaler),
typeof(GraphicRaycaster)
});
Object.DontDestroyOnLoad((Object)(object)canvasObject);
Canvas component = canvasObject.GetComponent<Canvas>();
component.renderMode = (RenderMode)0;
component.sortingOrder = 32000;
CanvasScaler component2 = canvasObject.GetComponent<CanvasScaler>();
component2.uiScaleMode = (ScaleMode)1;
component2.referenceResolution = new Vector2(1920f, 1080f);
component2.matchWidthOrHeight = 0.5f;
panel = Rect("ChatPanel", canvasObject.transform);
RectTransform component3 = panel.GetComponent<RectTransform>();
Vector2 val2 = default(Vector2);
((Vector2)(ref val2))..ctor(0f, 0f);
component3.pivot = val2;
Vector2 anchorMin = (component3.anchorMax = val2);
component3.anchorMin = anchorMin;
component3.anchoredPosition = new Vector2(32f, 92f);
component3.sizeDelta = new Vector2(540f, 280f);
((Graphic)panel.GetComponent<Image>()).color = Color.clear;
panelGroup = panel.AddComponent<CanvasGroup>();
GameObject val4 = new GameObject("HistoryViewport", new Type[2]
{
typeof(RectTransform),
typeof(RectMask2D)
});
val4.transform.SetParent(panel.transform, false);
RectTransform component4 = val4.GetComponent<RectTransform>();
val2 = (component4.pivot = Vector2.zero);
anchorMin = (component4.anchorMax = val2);
component4.anchorMin = anchorMin;
component4.anchoredPosition = new Vector2(18f, 76f);
component4.sizeDelta = new Vector2(504f, 190f);
history = Text("History", val4.transform, Vector2.zero, new Vector2(504f, 190f), 20);
((TMP_Text)history).alignment = (TextAlignmentOptions)1025;
((TMP_Text)history).overflowMode = (TextOverflowModes)0;
hint = Text("Hint", panel.transform, new Vector2(18f, 12f), new Vector2(504f, 22f), 14);
((Graphic)hint).color = GameChatTheme.Paper;
((TMP_Text)hint).text = "ENTER Chat";
inputObject = Rect("Input", panel.transform);
RectTransform component5 = inputObject.GetComponent<RectTransform>();
val2 = (component5.pivot = Vector2.zero);
anchorMin = (component5.anchorMax = val2);
component5.anchorMin = anchorMin;
component5.anchoredPosition = new Vector2(16f, 37f);
component5.sizeDelta = new Vector2(508f, 34f);
((Graphic)inputObject.GetComponent<Image>()).color = Color.clear;
inputObject.SetActive(false);
GameObject val7 = new GameObject("Viewport", new Type[2]
{
typeof(RectTransform),
typeof(RectMask2D)
});
val7.transform.SetParent(inputObject.transform, false);
RectTransform component6 = val7.GetComponent<RectTransform>();
component6.anchorMin = Vector2.zero;
component6.anchorMax = Vector2.one;
component6.offsetMin = new Vector2(10f, 0f);
component6.offsetMax = new Vector2(-10f, 0f);
TextMeshProUGUI val8 = Text("Text", val7.transform, Vector2.zero, new Vector2(488f, 34f), 20);
((TMP_Text)val8).alignment = (TextAlignmentOptions)513;
((TMP_Text)val8).textWrappingMode = (TextWrappingModes)0;
RectTransform rectTransform = ((TMP_Text)val8).rectTransform;
rectTransform.anchorMin = Vector2.zero;
rectTransform.anchorMax = Vector2.one;
rectTransform.pivot = new Vector2(0.5f, 0.5f);
anchorMin = (rectTransform.offsetMax = Vector2.zero);
rectTransform.offsetMin = anchorMin;
field = inputObject.AddComponent<ChatInputField>();
ime = canvasObject.AddComponent<ChatImeInput>();
((TMP_InputField)field).textViewport = component6;
((TMP_InputField)field).textComponent = (TMP_Text)(object)val8;
((Selectable)field).targetGraphic = (Graphic)(object)inputObject.GetComponent<Image>();
((TMP_InputField)field).fontAsset = font;
((TMP_InputField)field).richText = false;
((TMP_InputField)field).customCaretColor = true;
((TMP_InputField)field).caretColor = GameChatTheme.Gold;
((TMP_InputField)field).selectionColor = new Color(1f, 0.86f, 0.32f, 0.3f);
((Selectable)field).transition = (Transition)0;
((TMP_InputField)field).characterLimit = 256;
((TMP_InputField)field).lineType = (LineType)0;
field.Ime = ime;
field.SubmitChat = Send;
field.CancelChat = Close;
Navigation navigation = ((Selectable)field).navigation;
((Navigation)(ref navigation)).mode = (Mode)0;
((Selectable)field).navigation = navigation;
((BaseUnityPlugin)this).Logger.LogInfo((object)"Chat UI created.");
}
private static GameObject Rect(string name, Transform parent)
{
//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_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Expected O, but got Unknown
GameObject val = new GameObject(name, new Type[2]
{
typeof(RectTransform),
typeof(Image)
});
val.transform.SetParent(parent, false);
((Graphic)val.GetComponent<Image>()).raycastTarget = false;
return val;
}
private TextMeshProUGUI Text(string name, Transform parent, Vector2 position, Vector2 size, int fontSize)
{
//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_0026: 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_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: 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_004a: 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_0072: Unknown result type (might be due to invalid IL or missing references)
GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
val.transform.SetParent(parent, false);
RectTransform component = val.GetComponent<RectTransform>();
Vector2 val2 = (component.pivot = Vector2.zero);
Vector2 anchorMin = (component.anchorMax = val2);
component.anchorMin = anchorMin;
component.anchoredPosition = position;
component.sizeDelta = size;
TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
((TMP_Text)obj).font = font;
((TMP_Text)obj).fontSize = fontSize;
((Graphic)obj).color = GameChatTheme.Paper;
((TMP_Text)obj).richText = false;
((Graphic)obj).raycastTarget = false;
((TMP_Text)obj).textWrappingMode = (TextWrappingModes)1;
return obj;
}
private void EnsureEventSystem()
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Expected O, but got Unknown
if (!((Object)(object)EventSystem.current != (Object)null))
{
ownEventSystem = new GameObject("TextChatEventSystem", new Type[2]
{
typeof(EventSystem),
typeof(InputSystemUIInputModule)
});
Object.DontDestroyOnLoad((Object)(object)ownEventSystem);
ownEventSystem.GetComponent<InputSystemUIInputModule>().AssignDefaultActions();
}
}
private void SceneChanged(Scene before, Scene after)
{
Close();
if ((Object)(object)canvasObject != (Object)null)
{
canvasObject.SetActive(false);
}
if ((Object)(object)ownEventSystem != (Object)null)
{
Object.Destroy((Object)(object)ownEventSystem);
}
}
private void OnApplicationFocus(bool focused)
{
if (!focused)
{
Close();
}
}
private void OnDisable()
{
Close();
}
private void OnDestroy()
{
Close();
SceneManager.activeSceneChanged -= SceneChanged;
Chat?.Dispose();
Harmony obj = harmony;
if (obj != null)
{
obj.UnpatchSelf();
}
if ((Object)(object)canvasObject != (Object)null)
{
Object.Destroy((Object)(object)canvasObject);
}
if ((Object)(object)ownEventSystem != (Object)null)
{
Object.Destroy((Object)(object)ownEventSystem);
}
if (ownsFont && (Object)(object)font != (Object)null)
{
Object.Destroy((Object)(object)font);
}
if ((Object)(object)hangulFont != (Object)null)
{
Object.Destroy((Object)(object)hangulFont);
}
Instance = null;
}
}
[HarmonyPatch]
internal static class BlockGameplayWhileTyping
{
private static IEnumerable<MethodBase> TargetMethods()
{
return from method in typeof(InputReader).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public)
where method.Name.StartsWith("On") && method.GetParameters().Length == 1 && method.GetParameters()[0].ParameterType == typeof(CallbackContext)
select method;
}
private static bool Prefix()
{
return !Plugin.IsTyping;
}
}
internal static class WindowsIme
{
private static IntPtr window;
private static bool savedOpen;
private static bool saved;
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hwnd, out uint processId);
[DllImport("kernel32.dll")]
private static extern uint GetCurrentProcessId();
[DllImport("imm32.dll")]
private static extern IntPtr ImmGetContext(IntPtr hwnd);
[DllImport("imm32.dll")]
private static extern bool ImmReleaseContext(IntPtr hwnd, IntPtr context);
[DllImport("imm32.dll")]
private static extern bool ImmAssociateContextEx(IntPtr hwnd, IntPtr context, uint flags);
[DllImport("imm32.dll")]
private static extern bool ImmGetOpenStatus(IntPtr context);
[DllImport("imm32.dll")]
private static extern bool ImmSetOpenStatus(IntPtr context, bool open);
internal static void Begin()
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Invalid comparison between Unknown and I4
if ((int)Application.platform != 2)
{
return;
}
IntPtr foregroundWindow = GetForegroundWindow();
GetWindowThreadProcessId(foregroundWindow, out var processId);
if (foregroundWindow == IntPtr.Zero || processId != GetCurrentProcessId())
{
return;
}
window = foregroundWindow;
IntPtr intPtr = ImmGetContext(window);
if (intPtr == IntPtr.Zero)
{
ImmAssociateContextEx(window, IntPtr.Zero, 16u);
intPtr = ImmGetContext(window);
}
if (!(intPtr == IntPtr.Zero))
{
if (!saved)
{
savedOpen = ImmGetOpenStatus(intPtr);
saved = true;
}
ImmReleaseContext(window, intPtr);
}
}
internal static void End()
{
if (saved && !(window == IntPtr.Zero))
{
IntPtr intPtr = ImmGetContext(window);
if (intPtr != IntPtr.Zero)
{
ImmSetOpenStatus(intPtr, savedOpen);
ImmReleaseContext(window, intPtr);
}
saved = false;
window = IntPtr.Zero;
}
}
}
}