Decompiled source of Simple Multi Player plus v1.0.1

WKChatWindow.dll

Decompiled a week ago
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));
		}
	}
}