Decompiled source of ChatHistory v0.1.2

ChatHistory.dll

Decompiled 11 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
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("ChatHistory")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ChatHistory")]
[assembly: AssemblyTitle("ChatHistory")]
[assembly: AssemblyVersion("1.0.0.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 ChatHistory
{
	[BepInPlugin("com.chathistory.repo", "ChatHistory", "0.1.2")]
	[BepInProcess("REPO.exe")]
	public class ChatHistoryPlugin : BaseUnityPlugin
	{
		internal class LobbyState
		{
			public bool LastSpeaking;

			public string LastText;
		}

		private Harmony _harmony;

		private ChatLogUi _ui;

		private static readonly ConditionalWeakTable<object, LobbyState> _lobbyStates = new ConditionalWeakTable<object, LobbyState>();

		private const float DedupeWindow = 2f;

		private static readonly Dictionary<string, float> _recentKeys = new Dictionary<string, float>();

		private static FieldInfo _ttsVoiceField;

		private static FieldInfo _isSpeakingField;

		private static FieldInfo _voiceTextField;

		private static FieldInfo _playerAvatarField;

		private static FieldInfo _playerNameField;

		private static FieldInfo _avatarPlayerNameField;

		private Type _chatManagerType;

		private FieldInfo _chatActiveField;

		private object _chatManager;

		private bool _lastChatActive;

		private float _nextChatPoll;

		internal static ChatHistoryPlugin Instance { get; private set; }

		internal static ManualLogSource Logger => Instance._logger;

		private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;

		public static ConfigEntry<bool> Enabled { get; private set; }

		public static ConfigEntry<bool> ToastEnabled { get; private set; }

		public static ConfigEntry<float> ToastDuration { get; private set; }

		public static ConfigEntry<float> FadeDuration { get; private set; }

		public static ConfigEntry<int> HistoryMax { get; private set; }

		public static ConfigEntry<int> FontSize { get; private set; }

		public static ConfigEntry<float> PanelAlpha { get; private set; }

		public static ConfigEntry<bool> DebugLog { get; private set; }

		private void Awake()
		{
			Instance = this;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "总开关");
			ToastEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Toast", "Enabled", true, "左侧渐隐提示开关");
			ToastDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Toast", "Duration Seconds", 3f, "toast 停留秒数");
			FadeDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Toast", "Fade Seconds", 1.5f, "toast 渐隐秒数");
			HistoryMax = ((BaseUnityPlugin)this).Config.Bind<int>("History", "Max Entries", 200, "历史最大保留条数");
			FontSize = ((BaseUnityPlugin)this).Config.Bind<int>("Style", "Font Size", 16, "toast/历史字号");
			PanelAlpha = ((BaseUnityPlugin)this).Config.Bind<float>("Style", "Panel Alpha", 0.2f, "背景透明度 0-1");
			DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Log Chat Events", false, "记录每条捕获消息到日志");
			Patch();
			_ui = ((Component)this).gameObject.AddComponent<ChatLogUi>();
			_ui.Init();
			Logger.LogInfo((object)$"ChatHistory v{((BaseUnityPlugin)this).Info.Metadata.Version} loaded. capture=ChatMessageSpeak+LobbyChatUI, PanelAlpha={PanelAlpha.Value}");
		}

		private void Patch()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Expected O, but got Unknown
			_harmony = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
			Type type = AccessTools.TypeByName("PlayerAvatar");
			if (type == null)
			{
				Logger.LogError((object)"Type PlayerAvatar not found, remote chat capture disabled.");
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "ChatMessageSpeak", new Type[3]
			{
				typeof(string),
				typeof(bool),
				typeof(string)
			}, (Type[])null);
			if (methodInfo == null)
			{
				Logger.LogError((object)"Method PlayerAvatar.ChatMessageSpeak(string,bool,string) not found, remote chat capture disabled.");
				return;
			}
			_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(PlayerAvatarChatPatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Type type2 = AccessTools.TypeByName("LobbyChatUI");
			if (type2 == null)
			{
				Logger.LogError((object)"Type LobbyChatUI not found, fallback capture disabled.");
				return;
			}
			MethodInfo methodInfo2 = AccessTools.Method(type2, "Update", Type.EmptyTypes, (Type[])null);
			if (methodInfo2 == null)
			{
				Logger.LogError((object)"Method LobbyChatUI.Update not found, fallback capture disabled.");
			}
			else
			{
				_harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(LobbyChatUiPatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}

		private static bool TryDedupe(string name, string message)
		{
			float unscaledTime = Time.unscaledTime;
			string key = name + "\u0001" + message;
			if (_recentKeys.Count > 0)
			{
				List<string> list = null;
				foreach (KeyValuePair<string, float> recentKey in _recentKeys)
				{
					if (unscaledTime - recentKey.Value >= 2f)
					{
						if (list == null)
						{
							list = new List<string>();
						}
						list.Add(recentKey.Key);
					}
				}
				if (list != null)
				{
					for (int i = 0; i < list.Count; i++)
					{
						_recentKeys.Remove(list[i]);
					}
				}
			}
			if (_recentKeys.TryGetValue(key, out var value) && unscaledTime - value < 2f)
			{
				return true;
			}
			_recentKeys[key] = unscaledTime;
			return false;
		}

		internal void OnChatSpeak(object avatar, string message)
		{
			if (!Enabled.Value || avatar == null || string.IsNullOrWhiteSpace(message))
			{
				return;
			}
			try
			{
				string text = ReadAvatarName(avatar);
				if (!TryDedupe(text, message))
				{
					ChatEntry entry = new ChatEntry(text, message, Time.unscaledTime);
					_ui.AddMessage(entry);
					if (DebugLog.Value)
					{
						Logger.LogInfo((object)$"[Chat] {text}: {message}");
					}
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("ChatMessageSpeak capture skipped: " + ex.Message));
			}
		}

		private static string ReadAvatarName(object avatar)
		{
			try
			{
				if (_avatarPlayerNameField == null)
				{
					_avatarPlayerNameField = AccessTools.Field(avatar.GetType(), "playerName");
				}
				if (_avatarPlayerNameField == null)
				{
					return string.Empty;
				}
				object value = _avatarPlayerNameField.GetValue(avatar);
				return (value == null) ? string.Empty : value.ToString();
			}
			catch (Exception)
			{
				return string.Empty;
			}
		}

		internal void OnLobbyChatUpdate(object lobby)
		{
			if (!Enabled.Value || lobby == null)
			{
				return;
			}
			try
			{
				if (_ttsVoiceField == null)
				{
					_ttsVoiceField = AccessTools.Field(lobby.GetType(), "ttsVoice");
				}
				if (_ttsVoiceField == null)
				{
					return;
				}
				object value = _ttsVoiceField.GetValue(lobby);
				if (value == null)
				{
					return;
				}
				if (_isSpeakingField == null)
				{
					_isSpeakingField = AccessTools.Field(value.GetType(), "isSpeaking");
				}
				if (_voiceTextField == null)
				{
					_voiceTextField = AccessTools.Field(value.GetType(), "voiceText");
				}
				if (_isSpeakingField == null || _voiceTextField == null)
				{
					return;
				}
				bool flag = (bool)_isSpeakingField.GetValue(value);
				string text = (string)_voiceTextField.GetValue(value);
				LobbyState orCreateValue = _lobbyStates.GetOrCreateValue(lobby);
				if (flag && !orCreateValue.LastSpeaking && !string.IsNullOrEmpty(text))
				{
					string text2 = ReadPlayerName(value);
					if (TryDedupe(text2, text))
					{
						orCreateValue.LastSpeaking = flag;
						orCreateValue.LastText = text;
						return;
					}
					ChatEntry entry = new ChatEntry(text2, text, Time.unscaledTime);
					_ui.AddMessage(entry);
					if (DebugLog.Value)
					{
						Logger.LogInfo((object)$"[Chat] {text2}: {text}");
					}
				}
				orCreateValue.LastSpeaking = flag;
				orCreateValue.LastText = text;
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("LobbyChatUI capture skipped: " + ex.Message));
			}
		}

		private static string ReadPlayerName(object ttsVoice)
		{
			try
			{
				if (_playerAvatarField == null)
				{
					_playerAvatarField = AccessTools.Field(ttsVoice.GetType(), "playerAvatar");
				}
				if (_playerAvatarField == null)
				{
					return string.Empty;
				}
				object value = _playerAvatarField.GetValue(ttsVoice);
				if (value == null)
				{
					return string.Empty;
				}
				if (_playerNameField == null)
				{
					_playerNameField = AccessTools.Field(value.GetType(), "playerName");
				}
				if (_playerNameField == null)
				{
					return string.Empty;
				}
				object value2 = _playerNameField.GetValue(value);
				return (value2 == null) ? string.Empty : value2.ToString();
			}
			catch (Exception)
			{
				return string.Empty;
			}
		}

		private void Update()
		{
			if (!Enabled.Value || (Object)(object)_ui == (Object)null || !(Time.unscaledTime >= _nextChatPoll))
			{
				return;
			}
			_nextChatPoll = Time.unscaledTime + 0.2f;
			bool flag = ReadChatActive();
			if (flag != _lastChatActive)
			{
				_lastChatActive = flag;
				if (flag)
				{
					_ui.ShowHistory();
				}
				else
				{
					_ui.HideHistory();
				}
			}
		}

		private bool ReadChatActive()
		{
			try
			{
				if (_chatManagerType == null)
				{
					_chatManagerType = AccessTools.TypeByName("ChatManager");
				}
				if (_chatManagerType == null)
				{
					return _lastChatActive;
				}
				if (_chatManager == null)
				{
					Object[] array = Object.FindObjectsOfType(_chatManagerType);
					_chatManager = ((array != null && array.Length != 0) ? array[0] : null);
				}
				if (_chatManager == null)
				{
					return _lastChatActive;
				}
				if (_chatActiveField == null)
				{
					_chatActiveField = AccessTools.Field(_chatManagerType, "chatActive");
				}
				if (_chatActiveField == null)
				{
					return _lastChatActive;
				}
				object value = _chatActiveField.GetValue(_chatManager);
				return value is bool && (bool)value;
			}
			catch (Exception)
			{
				return _lastChatActive;
			}
		}
	}
	internal class ChatEntry
	{
		public string Name;

		public string Message;

		public float Time;

		public ChatEntry(string name, string message, float time)
		{
			Name = name;
			Message = message;
			Time = time;
		}
	}
	internal class PlayerAvatarChatPatch
	{
		public static void Postfix(object __instance, string _message)
		{
			try
			{
				if ((Object)(object)ChatHistoryPlugin.Instance != (Object)null)
				{
					ChatHistoryPlugin.Instance.OnChatSpeak(__instance, _message);
				}
			}
			catch (Exception ex)
			{
				ChatHistoryPlugin.Logger.LogError((object)("ChatMessageSpeak postfix error: " + ex));
			}
		}
	}
	internal class LobbyChatUiPatch
	{
		public static void Postfix(object __instance)
		{
			try
			{
				if ((Object)(object)ChatHistoryPlugin.Instance != (Object)null)
				{
					ChatHistoryPlugin.Instance.OnLobbyChatUpdate(__instance);
				}
			}
			catch (Exception ex)
			{
				ChatHistoryPlugin.Logger.LogError((object)("LobbyChatUI postfix error: " + ex));
			}
		}
	}
	internal class ChatLogUi : MonoBehaviour
	{
		private class ToastItem
		{
			public GameObject Root;

			public CanvasGroup Group;

			public Image Bg;

			public float AddedTime;
		}

		private readonly List<ChatEntry> _history = new List<ChatEntry>();

		private readonly List<ToastItem> _toasts = new List<ToastItem>();

		private Canvas _canvas;

		private GameObject _historyRoot;

		private GameObject _toastRoot;

		private Image _historyBg;

		private ScrollRect _scroll;

		private RectTransform _content;

		private Font _font;

		private Sprite _whiteSprite;

		private Color _panelColor;

		private Vector2 _offset = new Vector2(24f, 128f);

		private const float HistoryPanelHeight = 340f;

		private const float ToastStackGap = 12f;

		private RectTransform _toastRt;

		private bool _toastRaised;

		private static readonly int HistoryPoolCount = 32;

		private readonly List<Text> _historyLines = new List<Text>();

		public void Init()
		{
			_font = LoadFont();
			_whiteSprite = CreateWhiteSprite();
			BuildCanvas();
			BuildToastArea();
			BuildHistoryArea();
			ApplyStyle();
			if ((Object)(object)_font == (Object)null)
			{
				ChatHistoryPlugin.Logger.LogWarning((object)"No builtin font found, text may be invisible.");
			}
			ChatHistoryPlugin.Logger.LogInfo((object)"ChatHistory UI ready (left side).");
		}

		private static Font LoadFont()
		{
			string[] array = new string[3] { "LegacyRuntime.ttf", "Arial.ttf", "LiberationSans.ttf" };
			for (int i = 0; i < array.Length; i++)
			{
				try
				{
					Font builtinResource = Resources.GetBuiltinResource<Font>(array[i]);
					if ((Object)(object)builtinResource != (Object)null)
					{
						return builtinResource;
					}
				}
				catch (Exception)
				{
				}
			}
			return null;
		}

		private static Sprite CreateWhiteSprite()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: 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_002e: 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_0047: Expected O, but got Unknown
			Texture2D val = new Texture2D(1, 1);
			val.SetPixel(0, 0, Color.white);
			val.Apply();
			return Sprite.Create(val, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f));
		}

		private void BuildCanvas()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("ChatHistoryCanvas");
			val.transform.SetParent(((Component)this).transform, false);
			_canvas = val.AddComponent<Canvas>();
			_canvas.renderMode = (RenderMode)0;
			_canvas.sortingOrder = 200;
			CanvasScaler obj = val.AddComponent<CanvasScaler>();
			obj.uiScaleMode = (ScaleMode)1;
			obj.referenceResolution = new Vector2(1920f, 1080f);
			obj.matchWidthOrHeight = 0.5f;
		}

		private Image AddBackground(GameObject parent)
		{
			Image obj = parent.AddComponent<Image>();
			obj.sprite = _whiteSprite;
			obj.type = (Type)0;
			return obj;
		}

		private void BuildToastArea()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			_toastRoot = new GameObject("ToastArea");
			_toastRoot.transform.SetParent(((Component)_canvas).transform, false);
			RectTransform val = _toastRoot.AddComponent<RectTransform>();
			val.anchorMin = new Vector2(0f, 0f);
			val.anchorMax = new Vector2(0f, 0f);
			val.pivot = new Vector2(0f, 0f);
			val.anchoredPosition = _offset;
			val.sizeDelta = new Vector2(520f, 0f);
			_toastRt = val;
			VerticalLayoutGroup obj = _toastRoot.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj).childAlignment = (TextAnchor)6;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj).spacing = 6f;
			_toastRoot.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
		}

		private void BuildHistoryArea()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_004e: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Expected O, but got Unknown
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: 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_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Expected O, but got Unknown
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0337: Unknown result type (might be due to invalid IL or missing references)
			_historyRoot = new GameObject("HistoryPanel");
			_historyRoot.transform.SetParent(((Component)_canvas).transform, false);
			_historyRoot.SetActive(false);
			RectTransform obj = _historyRoot.AddComponent<RectTransform>();
			obj.anchorMin = new Vector2(0f, 0f);
			obj.anchorMax = new Vector2(0f, 0f);
			obj.pivot = new Vector2(0f, 0f);
			obj.anchoredPosition = _offset;
			obj.sizeDelta = new Vector2(560f, 340f);
			Image val = AddBackground(_historyRoot);
			((Graphic)val).color = new Color(0.05f, 0.05f, 0.05f, ChatHistoryPlugin.PanelAlpha.Value);
			_historyBg = val;
			GameObject val2 = new GameObject("Scroll");
			val2.transform.SetParent(_historyRoot.transform, false);
			RectTransform obj2 = val2.AddComponent<RectTransform>();
			obj2.anchorMin = Vector2.zero;
			obj2.anchorMax = Vector2.one;
			obj2.offsetMin = new Vector2(8f, 8f);
			obj2.offsetMax = new Vector2(-8f, -8f);
			_scroll = val2.AddComponent<ScrollRect>();
			_scroll.horizontal = false;
			_scroll.vertical = true;
			_scroll.movementType = (MovementType)2;
			_scroll.scrollSensitivity = 24f;
			GameObject val3 = new GameObject("Viewport");
			val3.transform.SetParent(val2.transform, false);
			RectTransform val4 = val3.AddComponent<RectTransform>();
			val4.anchorMin = Vector2.zero;
			val4.anchorMax = Vector2.one;
			val4.offsetMin = Vector2.zero;
			val4.offsetMax = Vector2.zero;
			val3.AddComponent<RectMask2D>();
			_scroll.viewport = val4;
			GameObject val5 = new GameObject("Content");
			val5.transform.SetParent(val3.transform, false);
			_content = val5.AddComponent<RectTransform>();
			_content.anchorMin = new Vector2(0f, 1f);
			_content.anchorMax = new Vector2(1f, 1f);
			_content.pivot = new Vector2(0f, 1f);
			_content.sizeDelta = new Vector2(0f, 0f);
			VerticalLayoutGroup obj3 = val5.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj3).childAlignment = (TextAnchor)0;
			((HorizontalOrVerticalLayoutGroup)obj3).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj3).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj3).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj3).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj3).spacing = 2f;
			val5.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
			_scroll.content = _content;
			for (int i = 0; i < HistoryPoolCount; i++)
			{
				GameObject val6 = new GameObject("HistoryLine" + i);
				val6.transform.SetParent(val5.transform, false);
				Text val7 = val6.AddComponent<Text>();
				val7.font = _font;
				val7.fontSize = ChatHistoryPlugin.FontSize.Value;
				((Graphic)val7).color = new Color(0.95f, 0.95f, 0.95f, 1f);
				val7.alignment = (TextAnchor)0;
				val7.horizontalOverflow = (HorizontalWrapMode)0;
				val7.verticalOverflow = (VerticalWrapMode)1;
				((Graphic)val7).raycastTarget = false;
				LayoutElement obj4 = val6.AddComponent<LayoutElement>();
				obj4.preferredWidth = 530f;
				obj4.preferredHeight = (float)ChatHistoryPlugin.FontSize.Value + 12f;
				_historyLines.Add(val7);
			}
		}

		public void ApplyStyle()
		{
			//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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			float value = ChatHistoryPlugin.PanelAlpha.Value;
			_panelColor = new Color(0.08f, 0.08f, 0.08f, value);
			if ((Object)(object)_historyBg != (Object)null)
			{
				((Graphic)_historyBg).color = new Color(0.05f, 0.05f, 0.05f, value);
			}
			for (int i = 0; i < _toasts.Count; i++)
			{
				ToastItem toastItem = _toasts[i];
				if (toastItem != null && (Object)(object)toastItem.Bg != (Object)null)
				{
					((Graphic)toastItem.Bg).color = _panelColor;
				}
			}
		}

		public void AddMessage(ChatEntry entry)
		{
			_history.Add(entry);
			while (_history.Count > ChatHistoryPlugin.HistoryMax.Value)
			{
				_history.RemoveAt(0);
			}
			if (ChatHistoryPlugin.ToastEnabled.Value)
			{
				SpawnToast(entry);
			}
			if ((Object)(object)_historyRoot != (Object)null && _historyRoot.activeSelf)
			{
				RebuildHistory();
			}
		}

		private void SetToastRaised(bool raised)
		{
			//IL_0052: 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)
			if (!((Object)(object)_toastRt == (Object)null) && _toastRaised != raised)
			{
				_toastRaised = raised;
				_toastRt.anchoredPosition = (Vector2)(raised ? new Vector2(_offset.x, _offset.y + 340f + 12f) : _offset);
			}
		}

		private void SpawnToast(ChatEntry entry)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_002c: 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)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Toast");
			val.transform.SetParent(_toastRoot.transform, false);
			Image val2 = AddBackground(val);
			((Graphic)val2).color = _panelColor;
			CanvasGroup val3 = val.AddComponent<CanvasGroup>();
			val3.alpha = 1f;
			LayoutElement obj = val.AddComponent<LayoutElement>();
			obj.preferredWidth = 500f;
			obj.preferredHeight = (float)ChatHistoryPlugin.FontSize.Value + 14f;
			GameObject val4 = new GameObject("Text");
			val4.transform.SetParent(val.transform, false);
			RectTransform obj2 = val4.AddComponent<RectTransform>();
			obj2.anchorMin = Vector2.zero;
			obj2.anchorMax = Vector2.one;
			obj2.offsetMin = new Vector2(10f, 5f);
			obj2.offsetMax = new Vector2(-10f, -5f);
			Text obj3 = val4.AddComponent<Text>();
			obj3.font = _font;
			obj3.fontSize = ChatHistoryPlugin.FontSize.Value;
			((Graphic)obj3).color = new Color(1f, 1f, 1f, 1f);
			obj3.alignment = (TextAnchor)3;
			obj3.horizontalOverflow = (HorizontalWrapMode)0;
			obj3.verticalOverflow = (VerticalWrapMode)1;
			((Graphic)obj3).raycastTarget = false;
			obj3.text = BuildDisplayLine(entry);
			ToastItem toastItem = new ToastItem();
			toastItem.Root = val;
			toastItem.Group = val3;
			toastItem.Bg = val2;
			toastItem.AddedTime = Time.unscaledTime;
			_toasts.Add(toastItem);
			while (_toasts.Count > 6)
			{
				Object.Destroy((Object)(object)_toasts[0].Root);
				_toasts.RemoveAt(0);
			}
		}

		private static string BuildDisplayLine(ChatEntry entry)
		{
			if (string.IsNullOrEmpty(entry.Name))
			{
				return entry.Message;
			}
			return $"{entry.Name}: {entry.Message}";
		}

		public void ShowHistory()
		{
			if (!((Object)(object)_historyRoot == (Object)null))
			{
				SetToastRaised(raised: true);
				RebuildHistory();
				_historyRoot.SetActive(true);
			}
		}

		public void HideHistory()
		{
			if (!((Object)(object)_historyRoot == (Object)null))
			{
				_historyRoot.SetActive(false);
				SetToastRaised(raised: false);
			}
		}

		private void RebuildHistory()
		{
			int num = _history.Count;
			if (num > HistoryPoolCount)
			{
				num = HistoryPoolCount;
			}
			for (int i = 0; i < _historyLines.Count; i++)
			{
				Text val = _historyLines[i];
				if (i < num)
				{
					ChatEntry entry = _history[_history.Count - num + i];
					val.text = BuildDisplayLine(entry);
					((Component)val).gameObject.SetActive(true);
				}
				else
				{
					((Component)val).gameObject.SetActive(false);
				}
			}
			((MonoBehaviour)this).StartCoroutine(ScrollToBottomNextFrame());
		}

		private IEnumerator ScrollToBottomNextFrame()
		{
			yield return null;
			if ((Object)(object)_scroll != (Object)null)
			{
				_scroll.verticalNormalizedPosition = 0f;
			}
		}

		private void Update()
		{
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			if (_toasts.Count > 0)
			{
				float unscaledTime = Time.unscaledTime;
				for (int num = _toasts.Count - 1; num >= 0; num--)
				{
					ToastItem toastItem = _toasts[num];
					float num2 = unscaledTime - toastItem.AddedTime;
					float num3 = ChatHistoryPlugin.ToastDuration.Value + ChatHistoryPlugin.FadeDuration.Value;
					if (num2 >= num3)
					{
						Object.Destroy((Object)(object)toastItem.Root);
						_toasts.RemoveAt(num);
					}
					else if (num2 > ChatHistoryPlugin.ToastDuration.Value)
					{
						float num4 = (num2 - ChatHistoryPlugin.ToastDuration.Value) / Mathf.Max(0.01f, ChatHistoryPlugin.FadeDuration.Value);
						toastItem.Group.alpha = Mathf.Clamp01(1f - num4);
					}
				}
			}
			if ((Object)(object)_historyRoot != (Object)null && _historyRoot.activeSelf && (Object)(object)_scroll != (Object)null)
			{
				float y = Input.mouseScrollDelta.y;
				if (Mathf.Abs(y) > 0.001f)
				{
					float num5 = _scroll.verticalNormalizedPosition + y * 0.08f;
					_scroll.verticalNormalizedPosition = Mathf.Clamp01(num5);
				}
			}
		}
	}
}