Decompiled source of PalaCompany v1.0.1

pala_company.dll

Decompiled 8 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using KickChat;
using LethalConfig;
using LethalConfig.ConfigItems;
using LethalConfig.ConfigItems.Options;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("pala_company")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+0a8a90a57f180d726af3619ae00f80e4f4eb29c0")]
[assembly: AssemblyProduct("pala_company")]
[assembly: AssemblyTitle("pala_company")]
[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 PalaCompany
{
	internal static class LethalConfigSupport
	{
		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static void Register(ConfigEntry<float> userCooldown, ConfigEntry<float> globalCooldown, ConfigEntry<int> maxLive, ConfigEntry<float> spawnRadius, ConfigEntry<string> perEnemyLimits, ConfigEntry<int> defaultPerEnemyMax, ConfigEntry<bool> enableDebugKeys, ConfigEntry<bool> countOnlyModEnemies, ConfigEntry<bool> showSpawnTips, ConfigEntry<bool> kickEnabled, ConfigEntry<string> prefix, ConfigEntry<string> blocklist, ConfigEntry<string> kickChatroomId, ConfigEntry<string> kickAppKey, ConfigEntry<string> kickCluster, Action restartKick)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			//IL_0078: 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)
			//IL_0084: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Expected O, but got Unknown
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Expected O, but got Unknown
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Expected O, but got Unknown
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Expected O, but got Unknown
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Expected O, but got Unknown
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Expected O, but got Unknown
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Expected O, but got Unknown
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Expected O, but got Unknown
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Expected O, but got Unknown
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Expected O, but got Unknown
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Expected O, but got Unknown
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Expected O, but got Unknown
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Expected O, but got Unknown
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Expected O, but got Unknown
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Expected O, but got Unknown
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Expected O, but got Unknown
			LethalConfigManager.SkipAutoGen();
			FloatSliderOptions val = new FloatSliderOptions();
			((BaseRangeOptions<float>)val).Min = 0f;
			((BaseRangeOptions<float>)val).Max = 300f;
			((BaseOptions)val).RequiresRestart = false;
			LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(userCooldown, val));
			FloatSliderOptions val2 = new FloatSliderOptions();
			((BaseRangeOptions<float>)val2).Min = 0f;
			((BaseRangeOptions<float>)val2).Max = 120f;
			((BaseOptions)val2).RequiresRestart = false;
			LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(globalCooldown, val2));
			IntSliderOptions val3 = new IntSliderOptions();
			((BaseRangeOptions<int>)val3).Min = 0;
			((BaseRangeOptions<int>)val3).Max = 50;
			((BaseOptions)val3).RequiresRestart = false;
			LethalConfigManager.AddConfigItem((BaseConfigItem)new IntSliderConfigItem(maxLive, val3));
			IntSliderOptions val4 = new IntSliderOptions();
			((BaseRangeOptions<int>)val4).Min = 0;
			((BaseRangeOptions<int>)val4).Max = 10;
			((BaseOptions)val4).RequiresRestart = false;
			LethalConfigManager.AddConfigItem((BaseConfigItem)new IntSliderConfigItem(defaultPerEnemyMax, val4));
			FloatSliderOptions val5 = new FloatSliderOptions();
			((BaseRangeOptions<float>)val5).Min = 1f;
			((BaseRangeOptions<float>)val5).Max = 50f;
			((BaseOptions)val5).RequiresRestart = false;
			LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(spawnRadius, val5));
			LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(enableDebugKeys, false));
			LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(countOnlyModEnemies, false));
			LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(showSpawnTips, false));
			LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(kickEnabled, false));
			LethalConfigManager.AddConfigItem((BaseConfigItem)new TextInputFieldConfigItem(prefix, false));
			LethalConfigManager.AddConfigItem((BaseConfigItem)new TextInputFieldConfigItem(blocklist, false));
			LethalConfigManager.AddConfigItem((BaseConfigItem)new TextInputFieldConfigItem(perEnemyLimits, false));
			LethalConfigManager.AddConfigItem((BaseConfigItem)new TextInputFieldConfigItem(kickChatroomId, false));
			LethalConfigManager.AddConfigItem((BaseConfigItem)new TextInputFieldConfigItem(kickAppKey, false));
			LethalConfigManager.AddConfigItem((BaseConfigItem)new TextInputFieldConfigItem(kickCluster, false));
			LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem("Kick", "Reconnect Kick", "Stop and restart the Kick chat connection using the current settings.", "Reconnect Kick", (GenericButtonHandler)delegate
			{
				restartKick();
			}));
		}
	}
	public class ChatMessage
	{
		public string User;

		public string Text;
	}
	[BepInPlugin("miskin213.pala_company", "Pala Company", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private static readonly ConcurrentQueue<ChatMessage> Inbox = new ConcurrentQueue<ChatMessage>();

		private static readonly Dictionary<string, string> Aliases = new Dictionary<string, string>
		{
			{ "bracken", "flowerman" },
			{ "snareflea", "centipede" },
			{ "flea", "centipede" },
			{ "thumper", "crawler" },
			{ "hygrodere", "blob" },
			{ "ghostgirl", "girl" },
			{ "ghost", "girl" },
			{ "eyelessdog", "mouthdog" },
			{ "dog", "mouthdog" },
			{ "coilhead", "spring" },
			{ "sporelizard", "puffer" },
			{ "lizard", "puffer" },
			{ "lassoman", "lasso" },
			{ "spider", "bunkerspider" },
			{ "hoarder", "hoardingbug" },
			{ "bug", "hoardingbug" },
			{ "giant", "forestgiant" },
			{ "leviathan", "earthleviathan" },
			{ "baboon", "baboonhawk" },
			{ "hawk", "baboonhawk" },
			{ "bees", "redlocustbees" },
			{ "killerbees", "redlocustbees" }
		};

		private static readonly string[] TestMessages = new string[5] { "!spawn hygrodere", "!spawn baboon", "!spawn notarealenemy", "just chatting", "!spawn spore lizard" };

		private ConfigEntry<string> prefix;

		private ConfigEntry<string> blocklist;

		private ConfigEntry<string> kickChatroomId;

		private ConfigEntry<string> kickAppKey;

		private ConfigEntry<string> kickCluster;

		private ConfigEntry<string> perEnemyLimits;

		private ConfigEntry<float> userCooldown;

		private ConfigEntry<float> globalCooldown;

		private ConfigEntry<float> spawnRadius;

		private ConfigEntry<int> maxLive;

		private ConfigEntry<int> defaultPerEnemyMax;

		private ConfigEntry<bool> enableDebugKeys;

		private ConfigEntry<bool> countOnlyModEnemies;

		private ConfigEntry<bool> showSpawnTips;

		private ConfigEntry<bool> kickEnabled;

		private ConfigEntry<bool> logMenuHierarchy;

		private ConfigEntry<bool> customTitle;

		private KickChatClient kickChatClient;

		private bool kickChatStopped;

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

		private readonly List<EnemyAI> modSpawnedEnemies = new List<EnemyAI>();

		private readonly HashSet<string> loggedInvalidPerEnemyLimitEntries = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private float lastGlobal = -999f;

		private Dictionary<string, EnemyType> enemyCache;

		private int testIndex;

		private int fakeViewer;

		public static void EnqueueChat(string user, string text)
		{
			Inbox.Enqueue(new ChatMessage
			{
				User = user,
				Text = text
			});
		}

		private void Awake()
		{
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			prefix = ((BaseUnityPlugin)this).Config.Bind<string>("Commands", "Prefix", "!spawn", "Chat command that triggers a spawn.");
			userCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Limits", "UserCooldownSeconds", 30f, "Seconds between spawns per viewer.");
			globalCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Limits", "GlobalCooldownSeconds", 5f, "Seconds between any two spawns.");
			maxLive = ((BaseUnityPlugin)this).Config.Bind<int>("Limits", "MaxLiveEnemies", 8, "No spawns while this many enemies are alive.");
			spawnRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Limits", "SpawnDistance", 9f, "How far from the target player enemies appear.");
			blocklist = ((BaseUnityPlugin)this).Config.Bind<string>("Limits", "Blocklist", "", "Comma-separated enemy names chat may NOT spawn.");
			perEnemyLimits = ((BaseUnityPlugin)this).Config.Bind<string>("Limits", "PerEnemyLimits", "", "Per-enemy max alive, e.g. \"Jester=1, Nutcracker=1, Bracken=2\". Names match chat commands incl. aliases. Values must be 1 or higher.");
			defaultPerEnemyMax = ((BaseUnityPlugin)this).Config.Bind<int>("Limits", "DefaultPerEnemyMax", 0, "Default per-enemy max alive; 0 disables the default limit.");
			enableDebugKeys = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableDebugKeys", false, "Enable F9/F10/F11 debug keys.");
			countOnlyModEnemies = ((BaseUnityPlugin)this).Config.Bind<bool>("Limits", "CountOnlyModEnemies", true, "Count only enemies spawned by this mod toward the live enemy limit.");
			showSpawnTips = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowSpawnTips", true, "Show a HUD tip after a successful spawn.");
			logMenuHierarchy = ((BaseUnityPlugin)this).Config.Bind<bool>("Menu", "LogHierarchy", false, "Log UI hierarchy details when the MainMenu scene loads.");
			customTitle = ((BaseUnityPlugin)this).Config.Bind<bool>("Menu", "CustomTitle", true, "Replace the MainMenu title image with title.png.");
			kickEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Kick", "Enabled", false, "Connect to Kick chat.");
			kickChatroomId = ((BaseUnityPlugin)this).Config.Bind<string>("Kick", "ChatroomId", "0", "Kick chatroom ID.");
			kickAppKey = ((BaseUnityPlugin)this).Config.Bind<string>("Kick", "AppKey", "34bf7a0ff419a2a775b9", "Kick Pusher app key.");
			kickCluster = ((BaseUnityPlugin)this).Config.Bind<string>("Kick", "Cluster", "us2", "Kick Pusher cluster.");
			if (Chainloader.PluginInfos.ContainsKey("ainavt.lc.lethalconfig"))
			{
				LethalConfigSupport.Register(userCooldown, globalCooldown, maxLive, spawnRadius, perEnemyLimits, defaultPerEnemyMax, enableDebugKeys, countOnlyModEnemies, showSpawnTips, kickEnabled, prefix, blocklist, kickChatroomId, kickAppKey, kickCluster, RestartKick);
			}
			SceneManager.sceneLoaded += OnSceneLoaded;
			if (kickEnabled.Value)
			{
				StartKickClient();
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Pala Company Phase2 loaded!");
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			//IL_0045: 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)
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Scene loaded: " + ((Scene)(ref scene)).name));
			if (customTitle.Value && ((Scene)(ref scene)).name == "MainMenu")
			{
				((MonoBehaviour)this).StartCoroutine(ApplyCustomTitle(scene));
			}
			if (logMenuHierarchy.Value && ((Scene)(ref scene)).name == "MainMenu")
			{
				((MonoBehaviour)this).StartCoroutine(LogMenuHierarchyAfterDelay(scene));
			}
		}

		private IEnumerator ApplyCustomTitle(Scene scene)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			yield return (object)new WaitForSecondsRealtime(1f);
			Image image = null;
			Sprite customSprite = null;
			if (!TryApplyCustomTitle(out image, out customSprite))
			{
				yield break;
			}
			for (int elapsedMilliseconds = 0; elapsedMilliseconds < 10000; elapsedMilliseconds += 250)
			{
				yield return (object)new WaitForSecondsRealtime(0.25f);
				try
				{
					if ((Object)(object)image != (Object)null && (Object)(object)image.sprite != (Object)(object)customSprite)
					{
						string replacedName = (((Object)(object)image.sprite != (Object)null) ? ((Object)image.sprite).name : "<none>");
						image.sprite = customSprite;
						((BaseUnityPlugin)this).Logger.LogInfo((object)("Restored custom menu title after replacing sprite: " + replacedName));
					}
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Failed to restore custom menu title: " + ex));
					break;
				}
			}
		}

		private bool TryApplyCustomTitle(out Image image, out Sprite customSprite)
		{
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Expected O, but got Unknown
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			image = null;
			customSprite = null;
			try
			{
				GameObject val = GameObject.Find("Canvas/MenuContainer/MainButtons/HeaderImage");
				if ((Object)(object)val == (Object)null)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Custom menu title not applied: HeaderImage was not found.");
					return false;
				}
				image = val.GetComponent<Image>();
				if ((Object)(object)image == (Object)null)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Custom menu title not applied: HeaderImage has no Image component.");
					return false;
				}
				byte[] array = null;
				string path = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "title.png");
				if (File.Exists(path))
				{
					array = File.ReadAllBytes(path);
				}
				else
				{
					Assembly assembly = ((object)this).GetType().Assembly;
					string text = assembly.GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith("title.png", StringComparison.OrdinalIgnoreCase));
					if (text != null)
					{
						using Stream stream = assembly.GetManifestResourceStream(text);
						using MemoryStream memoryStream = new MemoryStream();
						stream.CopyTo(memoryStream);
						array = memoryStream.ToArray();
					}
				}
				if (array == null)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Custom menu title not applied: title.png was not found beside the plugin DLL or as an embedded resource.");
					return false;
				}
				Texture2D val2 = new Texture2D(2, 2, (TextureFormat)4, false);
				if (!ImageConversion.LoadImage(val2, array, false))
				{
					Object.Destroy((Object)(object)val2);
					((BaseUnityPlugin)this).Logger.LogError((object)"Custom menu title not applied: title.png could not be loaded.");
					return false;
				}
				customSprite = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.5f));
				((Object)customSprite).name = "PalaCompanyTitle";
				Sprite sprite = image.sprite;
				ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
				string[] obj = new string[6]
				{
					"Replacing menu title sprite: name=",
					((Object)(object)sprite != (Object)null) ? ((Object)sprite).name : "<none>",
					" rectSize=",
					null,
					null,
					null
				};
				object obj2;
				if (!((Object)(object)sprite != (Object)null))
				{
					obj2 = "<none>";
				}
				else
				{
					Rect rect = sprite.rect;
					obj2 = ((object)((Rect)(ref rect)).size/*cast due to .constrained prefix*/).ToString();
				}
				obj[3] = (string)obj2;
				obj[4] = " textureSize=";
				obj[5] = (((Object)(object)sprite != (Object)null) ? (((Texture)sprite.texture).width + "x" + ((Texture)sprite.texture).height) : "<none>");
				logger.LogInfo((object)string.Concat(obj));
				image.sprite = customSprite;
				return true;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Failed to apply custom menu title: " + ex));
				return false;
			}
		}

		private unsafe IEnumerator LogMenuHierarchyAfterDelay(Scene scene)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			yield return (object)new WaitForSecondsRealtime(2f);
			try
			{
				GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
				foreach (GameObject root in rootGameObjects)
				{
					Image[] componentsInChildren = root.GetComponentsInChildren<Image>(true);
					Rect rect;
					foreach (Image image in componentsInChildren)
					{
						rect = ((Graphic)image).rectTransform.rect;
						Vector2 rectSize = ((Rect)(ref rect)).size;
						ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
						string[] obj = new string[10]
						{
							"[menu-ui] path=",
							GetHierarchyPath(((Component)image).transform),
							" activeInHierarchy=",
							((Component)image).gameObject.activeInHierarchy.ToString(),
							" component=",
							((object)image).GetType().Name,
							" sprite=",
							((Object)(object)image.sprite != (Object)null) ? ((Object)image.sprite).name : "<none>",
							" rectSize=",
							null
						};
						Vector2 val = rectSize;
						obj[9] = ((object)(*(Vector2*)(&val))/*cast due to .constrained prefix*/).ToString();
						logger.LogInfo((object)string.Concat(obj));
					}
					TMP_Text[] componentsInChildren2 = root.GetComponentsInChildren<TMP_Text>(true);
					foreach (TMP_Text text in componentsInChildren2)
					{
						rect = text.rectTransform.rect;
						Vector2 rectSize2 = ((Rect)(ref rect)).size;
						ManualLogSource logger2 = ((BaseUnityPlugin)this).Logger;
						string[] obj2 = new string[10]
						{
							"[menu-ui] path=",
							GetHierarchyPath(text.transform),
							" activeInHierarchy=",
							((Component)text).gameObject.activeInHierarchy.ToString(),
							" component=",
							((object)text).GetType().Name,
							" text=",
							text.text,
							" rectSize=",
							null
						};
						Vector2 val = rectSize2;
						obj2[9] = ((object)(*(Vector2*)(&val))/*cast due to .constrained prefix*/).ToString();
						logger2.LogInfo((object)string.Concat(obj2));
					}
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Failed to log MainMenu UI hierarchy: " + ex));
			}
		}

		private static string GetHierarchyPath(Transform transform)
		{
			string text = ((Object)transform).name;
			while ((Object)(object)transform.parent != (Object)null)
			{
				transform = transform.parent;
				text = ((Object)transform).name + "/" + text;
			}
			return text;
		}

		public void RestartKick()
		{
			try
			{
				StopKickChatClient();
				kickChatClient = null;
				kickChatStopped = false;
				if (kickEnabled.Value)
				{
					StartKickClient();
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Failed to restart Kick chat: " + ex));
			}
		}

		private void StartKickClient()
		{
			if (!long.TryParse(kickChatroomId.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result <= 0)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Kick chat is enabled, but Kick.ChatroomId is empty or invalid; the connection remains disabled.");
				return;
			}
			kickChatClient = new KickChatClient(result, kickAppKey.Value, kickCluster.Value, (Action<string>)((BaseUnityPlugin)this).Logger.LogInfo);
			kickChatClient.OnMessage += HandleKickMessage;
			kickChatStopped = false;
			kickChatClient.Start();
		}

		private void HandleKickMessage(KickMessage msg)
		{
			if (msg != null && msg.MessageType == "message")
			{
				EnqueueChat(msg.Username, msg.Content);
			}
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			StopKickChatClient();
		}

		private void OnApplicationQuit()
		{
			StopKickChatClient();
		}

		private void StopKickChatClient()
		{
			if (kickChatClient != null && !kickChatStopped)
			{
				kickChatStopped = true;
				kickChatClient.Stop();
			}
		}

		private void Update()
		{
			try
			{
				Keyboard current = Keyboard.current;
				if (enableDebugKeys.Value && current != null)
				{
					if (((ButtonControl)current.f9Key).wasPressedThisFrame)
					{
						ListEnemies();
					}
					if (((ButtonControl)current.f10Key).wasPressedThisFrame)
					{
						string text = TestMessages[testIndex++ % TestMessages.Length];
						int num = ++fakeViewer;
						string text2 = "viewer" + num;
						((BaseUnityPlugin)this).Logger.LogInfo((object)("[fake] " + text2 + ": " + text));
						EnqueueChat(text2, text);
					}
					if (((ButtonControl)current.f11Key).wasPressedThisFrame)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)"[fake] spammer: !spawn hoarding bug");
						EnqueueChat("spammer", "!spawn hoarding bug");
					}
				}
				int num2 = 0;
				ChatMessage result;
				while (num2 < 3 && Inbox.TryDequeue(out result))
				{
					num2++;
					Handle(result);
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)ex);
			}
		}

		private static string Norm(string s)
		{
			return new string((s ?? "").Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).ToArray());
		}

		private bool TryResolveEnemyType(string name, out EnemyType type)
		{
			string key = Norm(name);
			if (Aliases.TryGetValue(key, out var value))
			{
				key = value;
			}
			return GetEnemyTypes().TryGetValue(key, out type);
		}

		private Dictionary<EnemyType, int> ParsePerEnemyLimits()
		{
			Dictionary<EnemyType, int> dictionary = new Dictionary<EnemyType, int>();
			string[] array = perEnemyLimits.Value.Split(',');
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.Length == 0)
				{
					continue;
				}
				int num = text2.IndexOf('=');
				if (num <= 0 || num != text2.LastIndexOf('='))
				{
					LogInvalidPerEnemyLimit(text2);
					continue;
				}
				string text3 = text2.Substring(0, num).Trim();
				string s = text2.Substring(num + 1).Trim();
				if (text3.Length == 0 || !int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result < 1 || !TryResolveEnemyType(text3, out var type))
				{
					LogInvalidPerEnemyLimit(text2);
				}
				else
				{
					dictionary[type] = result;
				}
			}
			return dictionary;
		}

		private void LogInvalidPerEnemyLimit(string entry)
		{
			if (loggedInvalidPerEnemyLimitEntries.Add(entry))
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Ignoring invalid Limits.PerEnemyLimits entry: '" + entry + "'."));
			}
		}

		private Dictionary<string, EnemyType> GetEnemyTypes()
		{
			if (enemyCache != null && enemyCache.Count > 0)
			{
				return enemyCache;
			}
			Dictionary<string, EnemyType> dictionary = new Dictionary<string, EnemyType>();
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return dictionary;
			}
			SelectableLevel[] levels = instance.levels;
			foreach (SelectableLevel val in levels)
			{
				List<SpawnableEnemyWithRarity>[] array = new List<SpawnableEnemyWithRarity>[3] { val.Enemies, val.DaytimeEnemies, val.OutsideEnemies };
				foreach (List<SpawnableEnemyWithRarity> list in array)
				{
					foreach (SpawnableEnemyWithRarity item in list)
					{
						if (item != null && !((Object)(object)item.enemyType == (Object)null))
						{
							dictionary[Norm(item.enemyType.enemyName)] = item.enemyType;
						}
					}
				}
			}
			enemyCache = dictionary;
			return dictionary;
		}

		private void ListEnemies()
		{
			Dictionary<string, EnemyType> enemyTypes = GetEnemyTypes();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Found " + enemyTypes.Count + " enemy types:"));
			foreach (EnemyType value in enemyTypes.Values)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Enemy: " + value.enemyName));
			}
		}

		private void Reject(string user, string reason)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[rejected] " + user + ": " + reason));
		}

		private void Handle(ChatMessage msg)
		{
			//IL_0522: Unknown result type (might be due to invalid IL or missing references)
			//IL_0527: Unknown result type (might be due to invalid IL or missing references)
			//IL_052b: Unknown result type (might be due to invalid IL or missing references)
			//IL_053b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0540: Unknown result type (might be due to invalid IL or missing references)
			//IL_0549: Unknown result type (might be due to invalid IL or missing references)
			//IL_054e: Unknown result type (might be due to invalid IL or missing references)
			//IL_055a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0561: Unknown result type (might be due to invalid IL or missing references)
			//IL_0566: Unknown result type (might be due to invalid IL or missing references)
			//IL_056b: Unknown result type (might be due to invalid IL or missing references)
			//IL_056d: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_06f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_071a: Unknown result type (might be due to invalid IL or missing references)
			//IL_072b: Unknown result type (might be due to invalid IL or missing references)
			string text = (msg.Text ?? "").Trim();
			string text2 = prefix.Value.Trim();
			if (!text.StartsWith(text2 + " ", StringComparison.OrdinalIgnoreCase))
			{
				return;
			}
			string text3 = text.Substring(text2.Length).Trim();
			if (text3.Length == 0)
			{
				return;
			}
			RoundManager instance = RoundManager.Instance;
			StartOfRound instance2 = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null)
			{
				Reject(msg.User, "not in a game");
				return;
			}
			if (!((NetworkBehaviour)instance).IsServer)
			{
				Reject(msg.User, "not the host");
				return;
			}
			if (!instance2.shipHasLanded)
			{
				Reject(msg.User, "ship hasn't landed");
				return;
			}
			if (!TryResolveEnemyType(text3, out var type))
			{
				Reject(msg.User, "unknown enemy '" + text3 + "'");
				return;
			}
			HashSet<string> hashSet = new HashSet<string>(blocklist.Value.Split(',').Select(Norm));
			if (hashSet.Contains(Norm(type.enemyName)))
			{
				Reject(msg.User, type.enemyName + " is blocked");
				return;
			}
			if (type.isOutsideEnemy && (Object)(object)type.nestSpawnPrefab != (Object)null && type.requireNestObjectsToSpawn && !instance.enemyNestSpawnObjects.Any((EnemyAINestSpawnObject n) => (Object)(object)n != (Object)null && (Object)(object)n.enemyType == (Object)(object)type))
			{
				Reject(msg.User, type.enemyName + " needs a nest that doesn't exist on this moon");
				return;
			}
			float time = Time.time;
			if (time - lastGlobal < globalCooldown.Value)
			{
				Reject(msg.User, "global cooldown");
				return;
			}
			if (lastByUser.TryGetValue(msg.User, out var value) && time - value < userCooldown.Value)
			{
				Reject(msg.User, "your cooldown: " + Mathf.CeilToInt(userCooldown.Value - (time - value)) + "s left");
				return;
			}
			int num;
			if (countOnlyModEnemies.Value)
			{
				modSpawnedEnemies.RemoveAll((EnemyAI e) => (Object)(object)e == (Object)null || e.isEnemyDead);
				num = modSpawnedEnemies.Count;
			}
			else
			{
				num = instance.SpawnedEnemies.Count((EnemyAI e) => (Object)(object)e != (Object)null && !e.isEnemyDead);
			}
			if (num >= maxLive.Value)
			{
				Reject(msg.User, "too many enemies alive (" + num + ")");
				return;
			}
			Dictionary<EnemyType, int> dictionary = ParsePerEnemyLimits();
			if (!dictionary.TryGetValue(type, out var value2))
			{
				value2 = defaultPerEnemyMax.Value;
			}
			if (value2 > 0)
			{
				if (countOnlyModEnemies.Value)
				{
					modSpawnedEnemies.RemoveAll((EnemyAI e) => (Object)(object)e == (Object)null || e.isEnemyDead);
				}
				List<EnemyAI> source = (countOnlyModEnemies.Value ? modSpawnedEnemies : instance.SpawnedEnemies);
				int num2 = source.Count((EnemyAI e) => (Object)(object)e != (Object)null && !e.isEnemyDead && (Object)(object)e.enemyType == (Object)(object)type);
				if (num2 >= value2)
				{
					Reject(msg.User, type.enemyName + " limit reached (" + num2 + " alive)");
					return;
				}
			}
			bool wantInside = !type.isOutsideEnemy;
			List<PlayerControllerB> list = instance2.allPlayerScripts.Where((PlayerControllerB p) => (Object)(object)p != (Object)null && p.isPlayerControlled && !p.isPlayerDead && !p.isInHangarShipRoom && p.isInsideFactory == wantInside).ToList();
			if (list.Count == 0)
			{
				Reject(msg.User, type.enemyName + " needs a player " + (wantInside ? "inside the facility" : "outside"));
				return;
			}
			PlayerControllerB val = list[Random.Range(0, list.Count)];
			Vector2 insideUnitCircle = Random.insideUnitCircle;
			Vector2 val2 = ((Vector2)(ref insideUnitCircle)).normalized * spawnRadius.Value;
			Vector3 val3 = ((Component)val).transform.position + new Vector3(val2.x, 0f, val2.y);
			NavMeshHit val4 = default(NavMeshHit);
			if (!NavMesh.SamplePosition(val3, ref val4, 10f, -1))
			{
				Reject(msg.User, "no walkable spot near target");
				return;
			}
			NetworkObjectReference val5 = instance.SpawnEnemyGameObject(((NavMeshHit)(ref val4)).position, 0f, -1, type);
			lastGlobal = time;
			lastByUser[msg.User] = time;
			NetworkObject val6 = default(NetworkObject);
			if (!((NetworkObjectReference)(ref val5)).TryGet(ref val6, (NetworkManager)null))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not track spawned " + type.enemyName + " for the live enemy cap."));
				return;
			}
			EnemyAI component = ((Component)val6).GetComponent<EnemyAI>();
			if ((Object)(object)component == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Spawned " + type.enemyName + " has no EnemyAI component; it cannot be tracked for the live enemy cap."));
				return;
			}
			modSpawnedEnemies.Add(component);
			if (showSpawnTips.Value && (Object)(object)HUDManager.Instance != (Object)null)
			{
				HUDManager.Instance.DisplayTip(msg.User, type.enemyName, false, false, "LC_Tip1");
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[spawned] " + type.enemyName + " for " + msg.User + " | dist=" + Vector3.Distance(((NavMeshHit)(ref val4)).position, ((Component)val).transform.position).ToString("F1") + " dy=" + (((NavMeshHit)(ref val4)).position.y - ((Component)val).transform.position.y).ToString("F1") + " playerInside=" + val.isInsideFactory));
		}
	}
}
namespace KickChat
{
	public sealed class KickMessage
	{
		public string Username { get; private set; }

		public string Content { get; private set; }

		public string MessageType { get; private set; }

		public string[] BadgeTypes { get; private set; }

		internal KickMessage(string username, string content, string messageType, string[] badgeTypes)
		{
			Username = username;
			Content = content;
			MessageType = messageType;
			BadgeTypes = badgeTypes;
		}
	}
	public sealed class KickChatClient
	{
		private readonly long _chatroomId;

		private readonly string _appKey;

		private readonly string _cluster;

		private readonly Action<string> _log;

		private readonly object _sync = new object();

		private CancellationTokenSource _cancellation;

		private Task _worker;

		private ClientWebSocket _socket;

		public event Action<KickMessage> OnMessage;

		public KickChatClient(long chatroomId, string appKey, string cluster, Action<string> log)
		{
			if (string.IsNullOrWhiteSpace(appKey))
			{
				throw new ArgumentException("An app key is required.", "appKey");
			}
			if (string.IsNullOrWhiteSpace(cluster))
			{
				throw new ArgumentException("A cluster is required.", "cluster");
			}
			_chatroomId = chatroomId;
			_appKey = appKey;
			_cluster = cluster;
			_log = log ?? throw new ArgumentNullException("log");
		}

		public void Start()
		{
			lock (_sync)
			{
				if (_worker == null || _worker.IsCompleted)
				{
					_cancellation = new CancellationTokenSource();
					CancellationToken token = _cancellation.Token;
					_worker = Task.Run(() => RunAsync(token));
				}
			}
		}

		public void Stop()
		{
			ClientWebSocket socket;
			lock (_sync)
			{
				if (_cancellation != null)
				{
					_cancellation.Cancel();
				}
				socket = _socket;
			}
			if (socket != null && (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseReceived))
			{
				try
				{
					socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Client stopping", CancellationToken.None).GetAwaiter().GetResult();
				}
				catch (Exception ex)
				{
					Log("Could not send the WebSocket close frame: " + ex.Message);
				}
			}
		}

		private async Task RunAsync(CancellationToken cancellationToken)
		{
			int retryDelaySeconds = 2;
			try
			{
				while (!cancellationToken.IsCancellationRequested)
				{
					ClientWebSocket socket = null;
					try
					{
						socket = new ClientWebSocket();
						lock (_sync)
						{
							_socket = socket;
						}
						string endpoint = "wss://ws-" + _cluster + ".pusher.com/app/" + _appKey + "?protocol=7&client=js&version=8.4.0&flash=false";
						await socket.ConnectAsync(new Uri(endpoint), cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
						if (await ReadFramesAsync(socket, cancellationToken).ConfigureAwait(continueOnCapturedContext: false))
						{
							break;
						}
						if (!cancellationToken.IsCancellationRequested)
						{
							Log("Kick chat disconnected; reconnecting.");
						}
					}
					catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
					{
						break;
					}
					catch (Exception ex2)
					{
						Exception exception = ex2;
						Log("Kick chat connection failed: " + exception.Message);
					}
					finally
					{
						lock (_sync)
						{
							if (_socket == socket)
							{
								_socket = null;
							}
						}
						if (socket != null)
						{
							try
							{
								socket.Dispose();
							}
							catch (Exception ex2)
							{
								Exception exception2 = ex2;
								Log("Could not dispose the WebSocket: " + exception2.Message);
							}
						}
					}
					if (cancellationToken.IsCancellationRequested)
					{
						break;
					}
					try
					{
						await Task.Delay(TimeSpan.FromSeconds(retryDelaySeconds), cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
					}
					catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
					{
						break;
					}
					retryDelaySeconds = Math.Min(retryDelaySeconds * 2, 60);
				}
			}
			catch (Exception ex2)
			{
				Exception exception3 = ex2;
				Log("Kick chat background loop failed: " + exception3.Message);
			}
		}

		private async Task<bool> ReadFramesAsync(ClientWebSocket socket, CancellationToken cancellationToken)
		{
			byte[] buffer = new byte[8192];
			using (MemoryStream frame = new MemoryStream())
			{
				while (socket.State == WebSocketState.Open)
				{
					WebSocketReceiveResult result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
					if (result.MessageType == WebSocketMessageType.Close)
					{
						return false;
					}
					frame.Write(buffer, 0, result.Count);
					if (result.EndOfMessage)
					{
						string raw = Encoding.UTF8.GetString(frame.GetBuffer(), 0, (int)frame.Length);
						frame.SetLength(0L);
						bool flag = result.MessageType == WebSocketMessageType.Text;
						bool flag2 = flag;
						if (flag2)
						{
							flag2 = await HandleFrameAsync(socket, raw, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
						}
						if (flag2)
						{
							return true;
						}
					}
				}
			}
			return false;
		}

		private async Task<bool> HandleFrameAsync(ClientWebSocket socket, string raw, CancellationToken cancellationToken)
		{
			JToken root = JToken.Parse(raw);
			switch (GetStringProperty(root, "event"))
			{
			case "pusher:connection_established":
			{
				string subscribe = JsonConvert.SerializeObject((object)new
				{
					@event = "pusher:subscribe",
					data = new
					{
						auth = "",
						channel = "chatrooms." + _chatroomId + ".v2"
					}
				});
				await SendAsync(socket, subscribe, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
				break;
			}
			case "pusher:ping":
				await SendAsync(socket, "{\"event\":\"pusher:pong\",\"data\":{}}", cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
				break;
			case "pusher:error":
			{
				JToken errorData = ParseDataObject(root);
				JToken code = GetProperty(errorData, "code");
				if (GetStringProperty(errorData, "code") == "4001" || (code != null && (int)code.Type == 6 && ((object)code).ToString() == "4001"))
				{
					Log("Kick rejected the app key (it was probably rotated). Update AppKey in the config.");
					return true;
				}
				throw new InvalidOperationException("Pusher reported a connection error.");
			}
			case "App\\Events\\ChatMessageEvent":
			{
				JToken data = ParseDataObject(root);
				JToken sender = GetProperty(data, "sender");
				JToken identity = GetProperty(sender, "identity");
				JToken badges = GetProperty(identity, "badges");
				List<string> badgeTypes = new List<string>();
				JArray badgeArray = (JArray)(object)((badges is JArray) ? badges : null);
				if (badgeArray != null)
				{
					foreach (JToken badge in badgeArray)
					{
						string badgeType = GetStringProperty(badge, "type");
						if (badgeType != null)
						{
							badgeTypes.Add(badgeType);
						}
					}
				}
				KickMessage chatMessage = new KickMessage(GetStringProperty(sender, "username") ?? "", GetStringProperty(data, "content") ?? "", GetStringProperty(data, "type") ?? "", badgeTypes.ToArray());
				Action<KickMessage> handler = this.OnMessage;
				if (handler != null)
				{
					try
					{
						handler(chatMessage);
					}
					catch (Exception ex)
					{
						Exception exception = ex;
						Log("Kick chat message handler failed: " + exception.Message);
					}
				}
				break;
			}
			}
			return false;
		}

		private static JToken ParseDataObject(JToken root)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			JToken property = GetProperty(root, "data");
			if (property != null && (int)property.Type == 8)
			{
				string text = Extensions.Value<string>((IEnumerable<JToken>)property);
				if (text == null)
				{
					return null;
				}
				return JToken.Parse(text);
			}
			return property;
		}

		private static JToken GetProperty(JToken element, string name)
		{
			JObject val = (JObject)(object)((element is JObject) ? element : null);
			return (val != null) ? val[name] : null;
		}

		private static string GetStringProperty(JToken element, string name)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			JToken property = GetProperty(element, name);
			return (property != null && (int)property.Type == 8) ? Extensions.Value<string>((IEnumerable<JToken>)property) : null;
		}

		private static Task SendAsync(ClientWebSocket socket, string message, CancellationToken cancellationToken)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(message);
			return socket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, endOfMessage: true, cancellationToken);
		}

		private void Log(string message)
		{
			try
			{
				_log(message);
			}
			catch
			{
			}
		}
	}
}