Decompiled source of TwitchIntegration v1.0.1

TwitchIntegration.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using Assets.Scripts.Actors.Enemies;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using Il2CppInterop.Runtime.Injection;
using Il2CppSystem.Threading;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
namespace TwitchIntegration;

[BepInPlugin("TwitchIntegration", "MegaBonk Twitch Integration", "1.0.0")]
public class Plugin : BasePlugin
{
	internal static ManualLogSource Log;

	private static GameObject twitchIntegrationObject;

	public override void Load()
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Expected O, but got Unknown
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Expected O, but got Unknown
		Log = ((BasePlugin)this).Log;
		PluginConfig.Initialize(((BasePlugin)this).Config);
		ClassInjector.RegisterTypeInIl2Cpp<TwitchIntegrationBase>();
		twitchIntegrationObject = new GameObject("TwitchIntegration");
		twitchIntegrationObject.AddComponent<TwitchIntegrationBase>();
		Object.DontDestroyOnLoad((Object)(object)twitchIntegrationObject);
		ManualLogSource log = Log;
		bool flag = default(bool);
		BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(18, 1, ref flag);
		if (flag)
		{
			((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Plugin ");
			((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("TwitchIntegration");
			((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" is loaded!");
		}
		log.LogInfo(val);
	}

	public override bool Unload()
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Expected O, but got Unknown
		if ((Object)(object)twitchIntegrationObject != (Object)null)
		{
			Object.Destroy((Object)(object)twitchIntegrationObject);
			twitchIntegrationObject = null;
		}
		ManualLogSource log = Log;
		bool flag = default(bool);
		BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(17, 1, ref flag);
		if (flag)
		{
			((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Plugin ");
			((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("TwitchIntegration");
			((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" unloaded!");
		}
		log.LogInfo(val);
		return true;
	}
}
public class Bosses
{
	private class BossData
	{
		public TargetOfInterestPrefab target;

		public EnemyData enemyData;

		public string name;

		public GameObject dialogCanvas;

		public TextMeshProUGUI messageText;

		public DateTime lastMessageTime;

		public float hideTimer;

		public bool isFading;
	}

	private float lastBossFindTime;

	private const float BOSS_FIND_DELAY = 0.5f;

	private readonly Random random = new Random();

	private readonly Dictionary<string, BossData> userToBoss = new Dictionary<string, BossData>();

	private Dictionary<Enemy, string> enemyToUser = new Dictionary<Enemy, string>();

	private readonly HashSet<string> chattersSet = new HashSet<string>();

	private string[] chattersBuffer;

	private int currentIndex;

	private int totalChatters;

	private readonly object bufferLock = new object();

	private float DialogSize => PluginConfig.BossesDialogSize.Value;

	private int MaxChatters => PluginConfig.BossesMaxChatters.Value;

	public void Update()
	{
		if (totalChatters > 0)
		{
			FindBosses();
		}
		UpdateBossesData();
	}

	public void Reset()
	{
		foreach (KeyValuePair<string, BossData> item in userToBoss)
		{
			Object.Destroy((Object)(object)item.Value.dialogCanvas);
		}
		userToBoss.Clear();
		enemyToUser.Clear();
		lock (bufferLock)
		{
			chattersSet.Clear();
			chattersBuffer = new string[MaxChatters];
			currentIndex = 0;
			totalChatters = 0;
		}
	}

	public void ProcessNewChatMessage(string username, string message)
	{
		AddChatter(username);
		ProcessMessage(username, message);
	}

	private void AddChatter(string username)
	{
		lock (bufferLock)
		{
			if (!chattersSet.Contains(username) && !userToBoss.ContainsKey(username))
			{
				if (totalChatters >= MaxChatters)
				{
					string item = chattersBuffer[currentIndex];
					chattersSet.Remove(item);
				}
				chattersBuffer[currentIndex] = username;
				chattersSet.Add(username);
				currentIndex = (currentIndex + 1) % MaxChatters;
				if (totalChatters < MaxChatters)
				{
					totalChatters++;
				}
			}
		}
	}

	private void ProcessMessage(string username, string message)
	{
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Expected O, but got Unknown
		if (userToBoss.TryGetValue(username, out var value))
		{
			((TMP_Text)value.messageText).text = message;
			value.lastMessageTime = DateTime.Now;
			value.hideTimer = 15f;
			value.isFading = false;
			((TMP_Text)value.messageText).alpha = 1f;
			ManualLogSource log = Plugin.Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(21, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Showing dialog for ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(username);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(message);
			}
			log.LogInfo(val);
		}
	}

	private void FindBosses()
	{
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Expected O, but got Unknown
		if (Time.time - lastBossFindTime < 0.5f)
		{
			return;
		}
		IEnumerable<TargetOfInterestPrefab> enumerable = ((IEnumerable<TargetOfInterestPrefab>)Object.FindObjectsOfType<TargetOfInterestPrefab>()).Where(delegate(TargetOfInterestPrefab t)
		{
			Enemy enemy = t.enemy;
			return enemy != null && enemy.IsBoss() && !enemyToUser.ContainsKey(t.enemy);
		});
		lastBossFindTime = Time.time;
		bool flag = default(bool);
		foreach (TargetOfInterestPrefab item in enumerable)
		{
			string text = PopRandomChatter();
			if (text == null)
			{
				break;
			}
			enemyToUser[item.enemy] = text;
			userToBoss[text] = CreateBossData(item, text);
			ManualLogSource log = Plugin.Log;
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(17, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Boss renamed to: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text);
			}
			log.LogInfo(val);
		}
	}

	private void UpdateBossesData()
	{
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Expected O, but got Unknown
		//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01da: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0205: Unknown result type (might be due to invalid IL or missing references)
		//IL_020a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0217: Unknown result type (might be due to invalid IL or missing references)
		//IL_02df: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e6: Expected O, but got Unknown
		bool flag = default(bool);
		foreach (KeyValuePair<string, BossData> item in userToBoss.ToList())
		{
			string username = item.Key;
			BossData value = item.Value;
			if ((Object)(object)value.target.enemy == (Object)null || value.target.enemy.hp <= 0f || value.target.enemy.IsDeadOrDyingNextFrame() || !value.target.enemy.IsBoss() || (Object)(object)value.enemyData != (Object)(object)value.target.enemy.enemyData)
			{
				Object.Destroy((Object)(object)value.dialogCanvas);
				userToBoss.Remove(username);
				chattersSet.Remove(username);
				enemyToUser = enemyToUser.Where((KeyValuePair<Enemy, string> e) => e.Value != username).ToDictionary((KeyValuePair<Enemy, string> e) => e.Key, (KeyValuePair<Enemy, string> e) => e.Value);
				AddChatter(username);
				ManualLogSource log = Plugin.Log;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(28, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Boss ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(username);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" removed (dead or null)");
				}
				log.LogInfo(val);
				continue;
			}
			if (((TMP_Text)value.target.t_name).text != value.name)
			{
				((TMP_Text)value.target.t_name).text = value.name;
			}
			Vector3 position = value.target.enemy.statusSymbols.boss.transform.position;
			value.dialogCanvas.transform.position = position;
			if ((Object)(object)Camera.main != (Object)null)
			{
				Vector3 position2 = ((Component)Camera.main).transform.position;
				value.dialogCanvas.transform.LookAt(position2);
				value.dialogCanvas.transform.Rotate(0f, 180f, 0f);
			}
			if (!(value.hideTimer > 0f))
			{
				continue;
			}
			value.hideTimer -= Time.deltaTime;
			if (value.hideTimer <= 2f && !value.isFading)
			{
				value.isFading = true;
			}
			if (value.isFading)
			{
				float num = value.hideTimer / 2f;
				float alpha = Mathf.Lerp(0f, 1f, num);
				((TMP_Text)value.messageText).alpha = alpha;
			}
			if (value.hideTimer <= 0f)
			{
				value.isFading = false;
				((TMP_Text)value.messageText).alpha = 0f;
				ManualLogSource log2 = Plugin.Log;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(32, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Hidden dialog for ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(username);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" after timeout");
				}
				log2.LogInfo(val);
			}
		}
	}

	private string PopRandomChatter()
	{
		lock (bufferLock)
		{
			if (totalChatters == 0)
			{
				return null;
			}
			int num = random.Next(totalChatters);
			string result = chattersBuffer[num];
			if (totalChatters > 1)
			{
				chattersBuffer[num] = chattersBuffer[totalChatters - 1];
			}
			else
			{
				chattersBuffer[num] = null;
			}
			totalChatters--;
			totalChatters = Mathf.Clamp(totalChatters, 0, MaxChatters);
			currentIndex = totalChatters % MaxChatters;
			return result;
		}
	}

	private BossData CreateBossData(TargetOfInterestPrefab target, string username)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		//IL_017e: Unknown result type (might be due to invalid IL or missing references)
		//IL_019a: Unknown result type (might be due to invalid IL or missing references)
		//IL_01af: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("BossDialog_" + username);
		Canvas obj = val.AddComponent<Canvas>();
		obj.renderMode = (RenderMode)2;
		obj.worldCamera = Camera.main;
		CanvasScaler obj2 = val.AddComponent<CanvasScaler>();
		obj2.uiScaleMode = (ScaleMode)1;
		obj2.referenceResolution = new Vector2(1920f, 1080f);
		val.GetComponent<RectTransform>().sizeDelta = new Vector2(DialogSize, 0f);
		GameObject val2 = new GameObject("NicknameText");
		val2.transform.SetParent(val.transform, false);
		TextMeshProUGUI obj3 = val2.AddComponent<TextMeshProUGUI>();
		((TMP_Text)obj3).text = username;
		((TMP_Text)obj3).fontSize = DialogSize / 15f;
		((Graphic)obj3).color = new Color(0.7f, 0.2f, 0.2f, 1f);
		((TMP_Text)obj3).alignment = (TextAlignmentOptions)514;
		((TMP_Text)obj3).margin = new Vector4(0f, 0f, 0f, 0f - (1f + DialogSize / 15f));
		GameObject val3 = new GameObject("MessageText");
		val3.transform.SetParent(val.transform, false);
		TextMeshProUGUI val4 = val3.AddComponent<TextMeshProUGUI>();
		((TMP_Text)val4).text = "";
		((TMP_Text)val4).fontSize = DialogSize / 20f;
		((Graphic)val4).color = Color.white;
		((TMP_Text)val4).horizontalAlignment = (HorizontalAlignmentOptions)2;
		((TMP_Text)val4).verticalAlignment = (VerticalAlignmentOptions)1024;
		((TMP_Text)val4).enableWordWrapping = true;
		((TMP_Text)val4).margin = new Vector4(0f, 0f, 0f, DialogSize / 20f);
		val3.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
		RectTransform component = val3.GetComponent<RectTransform>();
		component.anchorMin = new Vector2(0f, 0f);
		component.anchorMax = new Vector2(1f, 0f);
		component.pivot = new Vector2(0.5f, 0f);
		component.anchoredPosition = Vector2.zero;
		component.sizeDelta = new Vector2(0f, 0f);
		BossData obj4 = new BossData
		{
			target = target
		};
		Enemy enemy = target.enemy;
		obj4.enemyData = ((enemy != null) ? enemy.enemyData : null);
		obj4.name = username;
		obj4.dialogCanvas = val;
		obj4.messageText = val4;
		obj4.lastMessageTime = DateTime.MinValue;
		obj4.hideTimer = 0f;
		obj4.isFading = false;
		return obj4;
	}
}
public class Nicknames
{
	private class EnemyData
	{
		public Enemy enemy;

		public string name;

		public TextMeshProUGUI usernameText;

		public GameObject canvas;
	}

	private float lastEnemyFindTime;

	private const float ENEMY_FIND_DELAY = 0.25f;

	private readonly Random random = new Random();

	private string[] chattersBuffer;

	private int currentIndex;

	private int totalChatters;

	private readonly object bufferLock = new object();

	private readonly HashSet<EnemyData> enemiesSet = new HashSet<EnemyData>();

	private float NicknameSize => PluginConfig.NicknamesSize.Value;

	private int MaxChatters => PluginConfig.NicknamesMaxChatters.Value;

	public void Update()
	{
		if (totalChatters > 0)
		{
			FindEnemies();
		}
		ClearDeadEnemies();
		UpdatePositions();
	}

	public void Reset()
	{
		lock (bufferLock)
		{
			chattersBuffer = new string[MaxChatters];
			currentIndex = 0;
			totalChatters = 0;
		}
		foreach (EnemyData item in enemiesSet)
		{
			Object.Destroy((Object)(object)item.canvas);
		}
		enemiesSet.Clear();
	}

	public void AddChatter(string username, string _)
	{
		lock (bufferLock)
		{
			chattersBuffer[currentIndex] = username;
			currentIndex = (currentIndex + 1) % MaxChatters;
			if (totalChatters < MaxChatters)
			{
				totalChatters++;
			}
		}
	}

	private void FindEnemies()
	{
		if (Time.time - lastEnemyFindTime < 0.25f)
		{
			return;
		}
		Enemy[] array = ((IEnumerable<Enemy>)Object.FindObjectsOfType<Enemy>()).Where((Enemy e) => e.hp > 0f && !e.IsBoss()).ToArray();
		lastEnemyFindTime = Time.time;
		Enemy[] array2 = array;
		foreach (Enemy enemy in array2)
		{
			if (!IsEnemyAlreadyTracked(enemy))
			{
				int num = random.Next(totalChatters);
				string username = chattersBuffer[num];
				EnemyData item = CreateEnemyData(enemy, username);
				enemiesSet.Add(item);
			}
		}
	}

	private void ClearDeadEnemies()
	{
		List<EnemyData> list = new List<EnemyData>();
		foreach (EnemyData item in enemiesSet)
		{
			if ((Object)(object)item.enemy == (Object)null || item.enemy.hp <= 0f || item.enemy.IsDeadOrDyingNextFrame())
			{
				Object.Destroy((Object)(object)item.canvas);
				list.Add(item);
			}
		}
		foreach (EnemyData item2 in list)
		{
			enemiesSet.Remove(item2);
		}
	}

	private void UpdatePositions()
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
		foreach (EnemyData item in enemiesSet)
		{
			float num = Mathf.Lerp(((Graphic)item.usernameText).color.a, 0.5f, Time.deltaTime * 2f);
			((Graphic)item.usernameText).color = new Color(1f, 1f, 1f, num);
			Vector3 headPosition = item.enemy.GetHeadPosition();
			item.canvas.transform.position = headPosition;
			if ((Object)(object)Camera.main != (Object)null)
			{
				Vector3 position = ((Component)Camera.main).transform.position;
				item.canvas.transform.LookAt(position);
				item.canvas.transform.Rotate(0f, 180f, 0f);
			}
		}
	}

	private bool IsEnemyAlreadyTracked(Enemy enemy)
	{
		foreach (EnemyData item in enemiesSet)
		{
			if ((Object)(object)item.enemy == (Object)(object)enemy)
			{
				return true;
			}
		}
		return false;
	}

	private EnemyData CreateEnemyData(Enemy enemy, string username)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Expected O, but got Unknown
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: 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_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("TwitchUsername");
		Canvas obj = val.AddComponent<Canvas>();
		obj.renderMode = (RenderMode)2;
		obj.worldCamera = Camera.main;
		CanvasScaler obj2 = val.AddComponent<CanvasScaler>();
		obj2.uiScaleMode = (ScaleMode)1;
		obj2.referenceResolution = new Vector2(1920f, 1080f);
		val.GetComponent<RectTransform>().sizeDelta = new Vector2(NicknameSize, 0f);
		GameObject val2 = new GameObject("MessageText");
		val2.transform.SetParent(val.transform, false);
		TextMeshProUGUI val3 = val2.AddComponent<TextMeshProUGUI>();
		((TMP_Text)val3).text = username;
		((TMP_Text)val3).fontSize = NicknameSize / 20f;
		((Graphic)val3).color = new Color(1f, 1f, 1f, 0f);
		((TMP_Text)val3).alignment = (TextAlignmentOptions)514;
		return new EnemyData
		{
			enemy = enemy,
			name = username,
			usernameText = val3,
			canvas = val
		};
	}
}
public class TwitchIntegrationBase : MonoBehaviour
{
	private readonly Chat chat = new Chat();

	private readonly Bosses bosses = new Bosses();

	private readonly Nicknames nicknames = new Nicknames();

	private string lastTwitchChannel = string.Empty;

	public void Awake()
	{
		((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
		Plugin.Log.LogInfo((object)"Awake called");
	}

	public void Start()
	{
		Plugin.Log.LogInfo((object)"Start called");
		chat.RegisterMessageCallback(bosses.ProcessNewChatMessage);
		chat.RegisterMessageCallback(nicknames.AddChatter);
	}

	public void Update()
	{
		if (lastTwitchChannel != PluginConfig.TwitchChannel.Value)
		{
			lastTwitchChannel = PluginConfig.TwitchChannel.Value;
			chat.Reset();
			bosses.Reset();
			nicknames.Reset();
		}
		chat.Update();
		if (PluginConfig.EnableBosses.Value)
		{
			bosses.Update();
		}
		if (PluginConfig.EnableNicknames.Value)
		{
			nicknames.Update();
		}
	}

	public void OnDestroy()
	{
		chat?.DisconnectFromTwitch();
		bosses?.Reset();
		nicknames?.Reset();
	}
}
public class Chat
{
	private const string TWITCH_IRC_SERVER = "irc.chat.twitch.tv";

	private const int TWITCH_IRC_PORT = 6667;

	private Action<string, string>[] messageCallback = Array.Empty<Action<string, string>>();

	private readonly Random random = new Random();

	private TcpClient tcpClient;

	private StreamReader inputStream;

	private StreamWriter outputStream;

	private Thread ircThread;

	private volatile bool isConnected;

	private DateTime lastConnectionAttempt = DateTime.MinValue;

	private const int RECONNECT_DELAY_MS = 5000;

	private string TwitchChannel => PluginConfig.TwitchChannel.Value;

	public void Update()
	{
		if (!isConnected && (DateTime.Now - lastConnectionAttempt).TotalMilliseconds > 5000.0)
		{
			StartTwitchConnection();
		}
	}

	public void Reset()
	{
		DisconnectFromTwitch();
	}

	public void RegisterMessageCallback(Action<string, string> callback)
	{
		List<Action<string, string>> list = messageCallback.ToList();
		if (!list.Contains(callback))
		{
			list.Add(callback);
			messageCallback = list.ToArray();
		}
	}

	private void StartTwitchConnection()
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Expected O, but got Unknown
		if (!string.IsNullOrEmpty(TwitchChannel))
		{
			lastConnectionAttempt = DateTime.Now;
			if (ircThread == null || !ircThread.IsAlive)
			{
				ircThread = new Thread(ThreadStart.op_Implicit((Action)ConnectAndReadMessages));
				ircThread.Start();
			}
		}
	}

	private void ConnectAndReadMessages()
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Expected O, but got Unknown
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Expected O, but got Unknown
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Expected O, but got Unknown
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: Expected O, but got Unknown
		bool flag = default(bool);
		try
		{
			ManualLogSource log = Plugin.Log;
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(27, 0, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Connecting to Twitch IRC...");
			}
			log.LogInfo(val);
			tcpClient = new TcpClient();
			tcpClient.Connect("irc.chat.twitch.tv", 6667);
			inputStream = new StreamReader(tcpClient.GetStream());
			outputStream = new StreamWriter(tcpClient.GetStream());
			string text = $"justinfan{random.Next(10000, 99999)}";
			outputStream.WriteLine("NICK " + text);
			outputStream.WriteLine("JOIN #" + TwitchChannel);
			outputStream.Flush();
			isConnected = true;
			ManualLogSource log2 = Plugin.Log;
			val = new BepInExInfoLogInterpolatedStringHandler(14, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Connected to #");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(TwitchChannel);
			}
			log2.LogInfo(val);
			ReadIrcMessages();
		}
		catch (Exception ex)
		{
			ManualLogSource log3 = Plugin.Log;
			BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(19, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Connection failed: ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
			}
			log3.LogError(val2);
			isConnected = false;
			DisconnectFromTwitch();
			ManualLogSource log4 = Plugin.Log;
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(23, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Retrying in ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(5);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" seconds...");
			}
			log4.LogInfo(val);
			Thread.Sleep(5000);
		}
	}

	private void ReadIrcMessages()
	{
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Expected O, but got Unknown
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Expected O, but got Unknown
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Expected O, but got Unknown
		bool flag = default(bool);
		try
		{
			string text;
			while (isConnected && tcpClient.Connected && (text = inputStream.ReadLine()) != null)
			{
				if (text.StartsWith("PING"))
				{
					string value = text.Replace("PING", "PONG");
					outputStream.WriteLine(value);
					outputStream.Flush();
				}
				else
				{
					if (!text.Contains("PRIVMSG"))
					{
						continue;
					}
					try
					{
						int num = text.IndexOf(':') + 1;
						int num2 = text.IndexOf('!');
						string text2 = text.Substring(num, num2 - num);
						int startIndex = text.IndexOf("twitch.tv PRIVMSG");
						int startIndex2 = text.IndexOf(':', startIndex) + 1;
						string arg = text.Substring(startIndex2);
						ManualLogSource log = Plugin.Log;
						BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(15, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Chatter found: ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text2);
						}
						log.LogInfo(val);
						Action<string, string>[] array = messageCallback;
						for (int i = 0; i < array.Length; i++)
						{
							array[i](text2, arg);
						}
					}
					catch (Exception ex)
					{
						ManualLogSource log2 = Plugin.Log;
						BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(23, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Error parsing message: ");
							((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
						}
						log2.LogError(val2);
					}
				}
			}
		}
		catch (Exception ex2)
		{
			if (isConnected)
			{
				ManualLogSource log3 = Plugin.Log;
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(28, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Error reading IRC messages: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex2.Message);
				}
				log3.LogError(val2);
			}
		}
		finally
		{
			DisconnectFromTwitch();
		}
	}

	public void DisconnectFromTwitch()
	{
		isConnected = false;
		try
		{
			inputStream?.Dispose();
			outputStream?.Dispose();
			tcpClient?.Close();
		}
		catch (Exception)
		{
		}
		finally
		{
			inputStream = null;
			outputStream = null;
			tcpClient = null;
		}
	}
}
public static class PluginConfig
{
	public static ConfigEntry<string> TwitchChannel { get; private set; }

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

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

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

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

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

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

	public static ConfigFile ConfigFile { get; private set; }

	public static void Initialize(ConfigFile config)
	{
		TwitchChannel = config.Bind<string>("Base", "TwitchChannel", "flowseal", "The Twitch channel to connect to");
		EnableBosses = config.Bind<bool>("Bosses", "EnableBosses", true, "Enable assigning random chatter to the boss");
		BossesDialogSize = config.Bind<float>("Bosses", "TextSize", 28f, "Size of the boss text");
		BossesMaxChatters = config.Bind<int>("Bosses", "MaxChatters", 1000, "Maximum number of last chatters buffer (overwrites oldest users when full)");
		EnableNicknames = config.Bind<bool>("Nicknames", "EnableNicknames", true, "Enable showing random chatter nicknames above creeps");
		NicknamesSize = config.Bind<float>("Nicknames", "NicknameSize", 12f, "Size of the nickname text for creeps");
		NicknamesMaxChatters = config.Bind<int>("Nicknames", "MaxChatters", 1000, "Maximum number of last chatters buffer (overwrites oldest users when full)");
		ConfigFile = config;
	}
}
public static class MyPluginInfo
{
	public const string PLUGIN_GUID = "TwitchIntegration";

	public const string PLUGIN_NAME = "MegaBonk Twitch Integration";

	public const string PLUGIN_VERSION = "1.0.0";
}