using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("WKChatWindow")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("WKChatWindow")] [assembly: AssemblyTitle("WKChatWindow")] [assembly: AssemblyVersion("1.0.0.0")] namespace WKChatWindow; internal static class TypeFinder { public static Type FindTypeByName(string typeName, ManualLogSource log) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); int num = 0; int num2 = 0; Assembly[] array = assemblies; foreach (Assembly assembly in array) { Type[] source; try { source = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { num++; num2 += ex.Types.Count((Type t) => t == null); source = ex.Types.Where((Type t) => t != null).ToArray(); } catch { num++; continue; } Type type = source.FirstOrDefault((Type t) => t.Name == typeName); if (type != null) { return type; } } if (log != null) { log.LogInfo((object)("TypeFinder: тип '" + typeName + "' не найден среди " + assemblies.Length + " сборок (сборок с ошибками загрузки типов: " + num + ", отсутствующих типов из-за этого: " + num2 + ").")); } return null; } } [BepInPlugin("yourname.wkchatwindow", "WK Chat Window", "1.0.0")] public class ChatWindowPlugin : BaseUnityPlugin { private void Awake() { ((BaseUnityPlugin)this).Logger.LogInfo((object)("WKChatWindow: [BUILD-7] плагин загружен. gameObject.name=" + ((Object)((Component)this).gameObject).name)); ChatWorker.SpawnSelf(); } } public class ChatWorker : MonoBehaviour { private class PendingTagMessage { public string LastRawValue = ""; public int LastChangeFrame = -1; public string CommittedValue = ""; } private static readonly ManualLogSource Log = Logger.CreateLogSource("WKChatWindow.Worker"); private static int spawnAttempts = 0; private const int MaxSpawnAttempts = 8; private bool chatOpen = false; private string currentInput = ""; private readonly List chatHistory = new List(); private const int MaxHistoryLines = 10; private object consoleInstance; private MethodInfo executeCommandMethod; private Type remoteTagType; private PropertyInfo remoteTagMessageProp; private bool remoteTagDiagnosticsLogged = false; private readonly Dictionary pendingPerTag = new Dictionary(); private const int StabilizeFrames = 12; private const string ControlName = "WKChatInputField"; private int frameCounter = 0; private bool selfDestructHandled = false; private Type playerType; public static void SpawnSelf() { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown spawnAttempts++; if (spawnAttempts > 8) { Log.LogError((object)("Превышено число попыток создания воркера (" + 8 + "), прекращаю попытки.")); return; } Log.LogInfo((object)("Создаю ChatWorker, попытка #" + spawnAttempts)); GameObject val = new GameObject("WKChatWindow_PersistentHost_" + spawnAttempts); Object.DontDestroyOnLoad((Object)(object)val); val.AddComponent(); } private void Awake() { Log.LogInfo((object)("ChatWorker: Awake(). gameObject.name=" + ((Object)((Component)this).gameObject).name)); } private void Start() { Log.LogInfo((object)"ChatWorker: Start() успешно вызван - объект пережил старт игры. Жду нажатия T."); } private void OnDestroy() { if (!selfDestructHandled) { Log.LogWarning((object)"ChatWorker: OnDestroy() - объект уничтожен внешне. Пробую создать новый."); SpawnSelf(); } } private void Update() { try { frameCounter++; if (frameCounter % 300 == 0) { Log.LogInfo((object)("ChatWorker: heartbeat, Update() работает. Кадр " + frameCounter)); } if (!chatOpen && Input.GetKeyDown((KeyCode)116)) { if (!IsInActiveGameplay()) { Log.LogInfo((object)"ChatWorker: T нажата, но игрок не в игровой сессии (меню) - игнорирую."); } else if (!string.IsNullOrEmpty(GUI.GetNameOfFocusedControl())) { Log.LogInfo((object)"ChatWorker: T нажата, но фокус уже занят другим полем (например, консолью) - игнорирую."); } else { Log.LogInfo((object)"ChatWorker: T нажата, открываю чат."); OpenChat(); } } if (frameCounter % 5 == 0) { PollRemoteMessages(); } } catch (Exception ex) { Log.LogError((object)("ChatWorker: исключение в Update(): " + ex)); } } private bool IsInActiveGameplay() { try { if (playerType == null) { playerType = TypeFinder.FindTypeByName("ENT_Player", Log); } if (playerType == null) { return true; } Object[] array = Resources.FindObjectsOfTypeAll(playerType); if (array == null) { return false; } Object[] array2 = array; foreach (Object val in array2) { Component val2 = (Component)(object)((val is Component) ? val : null); if ((Object)(object)val2 != (Object)null && val2.gameObject.activeInHierarchy) { return true; } } return false; } catch (Exception ex) { Log.LogError((object)("ChatWorker: ошибка проверки игровой сессии: " + ex)); return true; } } private void PollRemoteMessages() { try { if (remoteTagType == null) { remoteTagType = TypeFinder.FindTypeByName("RemoteTag", Log); if (remoteTagType == null) { return; } remoteTagMessageProp = remoteTagType.GetProperty("Message", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!remoteTagDiagnosticsLogged) { remoteTagDiagnosticsLogged = true; Log.LogInfo((object)("RemoteTag найден: " + remoteTagType.FullName + ", свойство Message найдено: " + (remoteTagMessageProp != null))); } } if (remoteTagMessageProp == null) { return; } Object[] array = Resources.FindObjectsOfTypeAll(remoteTagType); if (array == null) { return; } Object[] array2 = array; foreach (Object val in array2) { Component val2 = (Component)(object)((val is Component) ? val : null); if ((Object)(object)val2 == (Object)null || !val2.gameObject.activeInHierarchy) { continue; } string text = remoteTagMessageProp.GetValue(val, null) as string; if (!string.IsNullOrEmpty(text)) { int hashCode = ((object)val).GetHashCode(); if (!pendingPerTag.TryGetValue(hashCode, out var value)) { value = new PendingTagMessage(); pendingPerTag[hashCode] = value; } if (text != value.LastRawValue) { value.LastRawValue = text; value.LastChangeFrame = frameCounter; } else if (text != value.CommittedValue && frameCounter - value.LastChangeFrame >= 12) { value.CommittedValue = text; AddHistoryLine(text); } } } } catch (Exception ex) { Log.LogError((object)("Ошибка при опросе RemoteTag: " + ex)); } } private string GetOwnerNameForTag(Component tagComponent) { try { Transform val = tagComponent.transform; while ((Object)(object)val != (Object)null) { string name = ((Object)val).name; if (!string.IsNullOrEmpty(name) && name.IndexOf("Clone", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("RemoteTag", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Tag", StringComparison.OrdinalIgnoreCase) < 0) { return name; } val = val.parent; } } catch { } return "Игрок"; } private void AddHistoryLine(string line) { chatHistory.Add(line); if (chatHistory.Count > 50) { chatHistory.RemoveAt(0); } } private void OpenChat() { chatOpen = true; currentInput = ""; } private void CloseChat() { chatOpen = false; currentInput = ""; GUI.FocusControl((string)null); } private void SendChatMessage() { string text = ((currentInput != null) ? currentInput.Trim() : ""); if (!string.IsNullOrEmpty(text)) { bool flag = TryExecuteTalkCommand(text); AddHistoryLine("Ты: " + text); if (!flag) { Log.LogWarning((object)"Не удалось выполнить команду talk - консоль игры не найдена."); } } CloseChat(); } private bool TryExecuteTalkCommand(string message) { try { if (executeCommandMethod == null || consoleInstance == null) { FindConsole(); } if (executeCommandMethod == null) { return false; } object obj = (executeCommandMethod.IsStatic ? null : consoleInstance); if (!executeCommandMethod.IsStatic && obj == null) { return false; } ParameterInfo[] parameters = executeCommandMethod.GetParameters(); object[] array = new object[parameters.Length]; array[0] = "talk " + message; for (int i = 1; i < parameters.Length; i++) { array[i] = (parameters[i].HasDefaultValue ? parameters[i].DefaultValue : GetDefault(parameters[i].ParameterType)); } executeCommandMethod.Invoke(obj, array); return true; } catch (Exception ex) { Log.LogError((object)("Ошибка при вызове команды talk: " + ex)); return false; } } private void FindConsole() { try { Type type = TypeFinder.FindTypeByName("CommandConsole", Log); if (type == null) { Log.LogWarning((object)"Класс CommandConsole не найден ни в одной загруженной сборке."); return; } Object[] array = Resources.FindObjectsOfTypeAll(type); if (array == null || array.Length == 0) { Log.LogWarning((object)"Экземпляр CommandConsole в сцене не найден (пока)."); return; } consoleInstance = array[0]; MethodInfo methodInfo = (from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where m.Name == "ExecuteCommand" where m.GetParameters().Length != 0 && m.GetParameters()[0].ParameterType == typeof(string) orderby m.GetParameters().Length select m).FirstOrDefault(); executeCommandMethod = methodInfo; if (executeCommandMethod != null) { Log.LogInfo((object)("Найден метод консоли " + executeCommandMethod)); } else { Log.LogWarning((object)"CommandConsole найден, но метод ExecuteCommand(string,...) - нет."); } } catch (Exception ex) { Log.LogError((object)("Ошибка поиска консоли: " + ex)); } } private static object GetDefault(Type t) { return t.IsValueType ? Activator.CreateInstance(t) : null; } private void OnGUI() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Invalid comparison between Unknown and I4 //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Invalid comparison between Unknown and I4 //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Invalid comparison between Unknown and I4 //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Invalid comparison between Unknown and I4 try { float num = 420f; float num2 = 26f; float num3 = 20f; float num4 = (float)Screen.height - num2 - 20f; float num5 = 200f; if (chatHistory.Count > 0) { GUI.Box(new Rect(num3, num4 - num5 - 8f, num, num5), ""); List list = chatHistory.Skip(Math.Max(0, chatHistory.Count - 10)).ToList(); float num6 = num4 - num5 - 4f; foreach (string item in list) { GUI.Label(new Rect(num3 + 6f, num6, num - 12f, 20f), item); num6 += 20f; } } if (!chatOpen) { return; } Event current2 = Event.current; if ((int)current2.type == 4 && GUI.GetNameOfFocusedControl() == "WKChatInputField") { if ((int)current2.keyCode == 13 || (int)current2.keyCode == 271) { Log.LogInfo((object)"ChatWorker: Enter нажат (GUI event), отправляю сообщение."); current2.Use(); SendChatMessage(); return; } if ((int)current2.keyCode == 27) { Log.LogInfo((object)"ChatWorker: Escape нажат (GUI event), закрываю чат без отправки."); current2.Use(); CloseChat(); return; } } GUI.SetNextControlName("WKChatInputField"); currentInput = GUI.TextField(new Rect(num3, num4, num, num2), currentInput ?? "", 200); if (GUI.GetNameOfFocusedControl() != "WKChatInputField") { GUI.FocusControl("WKChatInputField"); } } catch (Exception ex) { Log.LogError((object)("исключение в OnGUI(): " + ex)); } } }