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<string> chatHistory = new List<string>();
private const int MaxHistoryLines = 10;
private object consoleInstance;
private MethodInfo executeCommandMethod;
private Type remoteTagType;
private PropertyInfo remoteTagMessageProp;
private bool remoteTagDiagnosticsLogged = false;
private readonly Dictionary<int, PendingTagMessage> pendingPerTag = new Dictionary<int, PendingTagMessage>();
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<ChatWorker>();
}
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<string> 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));
}
}
}