Decompiled source of ChatPoll v1.0.0

ChatPoll.dll

Decompiled 4 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using LethalCompanyInputUtils.Api;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;

[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("ChatPoll")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("A mod for Lethal Company that allows the host to start polls in chat that players can vote on")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ChatPoll")]
[assembly: AssemblyTitle("ChatPoll")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 ChatPoll
{
	internal class InputActions : LcInputActions
	{
		[InputAction(/*Could not decode attribute arguments.*/)]
		internal InputAction OpenPollCreator { get; private set; }

		[InputAction(/*Could not decode attribute arguments.*/)]
		internal InputAction EndPoll { get; private set; }

		internal static bool ShouldInputBeIgnored()
		{
			if ((Object)(object)StartOfRound.Instance == (Object)null)
			{
				return true;
			}
			if (StartOfRound.Instance.localPlayerController.isTypingChat)
			{
				return true;
			}
			if (StartOfRound.Instance.localPlayerController.inTerminalMenu)
			{
				return true;
			}
			if (StartOfRound.Instance.localPlayerController.quickMenuManager.isMenuOpen)
			{
				return true;
			}
			return false;
		}
	}
	[BepInPlugin("BGN.ChatPoll", "Chat Poll", "1.0.0")]
	[BepInProcess("Lethal Company.exe")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string GUID = "BGN.ChatPoll";

		public const string NAME = "Chat Poll";

		public const string VERSION = "1.0.0";

		internal static ManualLogSource Logger;

		internal static readonly InputActions inputActions = new InputActions();

		private static readonly MethodInfo hudManagerAddChatMessageMethod = typeof(HUDManager).GetMethod("AddChatMessage", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly MethodInfo hudManagerAddTextMessageServerRpcMethod = typeof(HUDManager).GetMethod("AddTextMessageServerRpc", BindingFlags.Instance | BindingFlags.NonPublic);

		private void Awake()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null);
			Logger.LogInfo((object)"Plugin BGN.ChatPoll is loaded!");
		}

		internal static void PrintToChatLocally(string message)
		{
			hudManagerAddChatMessageMethod.Invoke(HUDManager.Instance, new object[2] { message, "" });
		}

		internal static void PrintToChat(string message)
		{
			hudManagerAddTextMessageServerRpcMethod.Invoke(HUDManager.Instance, new object[1] { message });
		}
	}
	internal class PollCreatorUI : MonoBehaviour
	{
		private const string WindowTitle = "Poll Creator";

		private const int WindowWidth = 600;

		private const int WindowHeight = 350;

		private static readonly GUILayoutOption[] WindowOptions = (GUILayoutOption[])(object)new GUILayoutOption[2]
		{
			GUILayout.Width(600f),
			GUILayout.Height(350f)
		};

		private bool isWindowOpen;

		private Vector2 windowPollOptionsScrollPosition;

		private string pollTitle = string.Empty;

		private bool hasDuration;

		private float duration = 40f;

		private const float MinDuration = 10f;

		private const float MaxDuration = 90f;

		private readonly List<string> pollOptions = new List<string>();

		private const int MinimumPollOptions = 2;

		private const int MaximumPollOptions = 10;

		private void Start()
		{
			for (int i = 0; i < 2; i++)
			{
				pollOptions.Add(string.Empty);
			}
			Plugin.inputActions.OpenPollCreator.performed += OpenPollCreator_performed;
		}

		private void OnDestroy()
		{
			Plugin.inputActions.OpenPollCreator.performed -= OpenPollCreator_performed;
		}

		private void OpenPollCreator_performed(CallbackContext context)
		{
			if (!isWindowOpen && !InputActions.ShouldInputBeIgnored())
			{
				isWindowOpen = true;
				StartOfRound.Instance.localPlayerController.quickMenuManager.isMenuOpen = true;
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
			}
		}

		private void Update()
		{
			if (!StartOfRound.Instance.localPlayerController.quickMenuManager.isMenuOpen)
			{
				isWindowOpen = false;
			}
		}

		private void OnGUI()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			if (isWindowOpen)
			{
				int num = (Screen.width - 600) / 2;
				int num2 = (Screen.height - 350) / 2;
				GUILayout.Window(GUIUtility.GetControlID((FocusType)2), new Rect((float)num, (float)num2, 600f, 350f), new WindowFunction(DrawUI), "Poll Creator", WindowOptions);
			}
		}

		private void DrawUI(int id)
		{
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayoutOption val = GUILayout.MaxWidth(300f);
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { val });
			DrawSettingsUI();
			GUILayout.EndVertical();
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { val });
			DrawOptionsUI();
			GUILayout.EndVertical();
			GUILayout.EndHorizontal();
		}

		private void DrawSettingsUI()
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Title:", Array.Empty<GUILayoutOption>());
			pollTitle = GUILayout.TextArea(pollTitle, 80, Array.Empty<GUILayoutOption>());
			hasDuration = GUILayout.Toggle(hasDuration, " Duration: (" + (hasDuration ? $"{duration} seconds" : "disabled") + ")", Array.Empty<GUILayoutOption>());
			GUI.enabled = hasDuration;
			duration = Mathf.Round(GUILayout.HorizontalSlider(duration, 10f, 90f, Array.Empty<GUILayoutOption>()));
			GUI.enabled = true;
			GUILayout.FlexibleSpace();
			GUILayout.EndVertical();
			Color color = GUI.color;
			if (PollManager.Instance.IsPollActive)
			{
				GUI.color = Color.red;
				if (GUILayout.Button("End poll", Array.Empty<GUILayoutOption>()))
				{
					PollManager.Instance.EndPoll();
				}
			}
			else
			{
				GUI.color = Color.green;
				if (GUILayout.Button("Start poll", Array.Empty<GUILayoutOption>()))
				{
					PollManager.Instance.StartPoll(pollTitle, hasDuration ? duration : float.PositiveInfinity, pollOptions.ToArray());
				}
			}
			GUI.color = color;
		}

		private void DrawOptionsUI()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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_006b: 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)
			windowPollOptionsScrollPosition = GUILayout.BeginScrollView(windowPollOptionsScrollPosition, GUI.skin.box);
			int num = -1;
			for (int i = 0; i < pollOptions.Count; i++)
			{
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				pollOptions[i] = GUILayout.TextArea(pollOptions[i], 80, Array.Empty<GUILayoutOption>());
				GUI.enabled = pollOptions.Count > 2;
				Color color = GUI.color;
				GUI.color = Color.red;
				if (GUILayout.Button("X", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
				{
					num = i;
				}
				GUI.color = color;
				GUI.enabled = true;
				GUILayout.EndHorizontal();
			}
			if (num >= 0)
			{
				pollOptions.RemoveAt(num);
			}
			GUILayout.EndScrollView();
			GUI.enabled = pollOptions.Count < 10;
			if (GUILayout.Button("Add option", Array.Empty<GUILayoutOption>()))
			{
				pollOptions.Add(string.Empty);
			}
			GUI.enabled = true;
		}
	}
	internal class PollManager : MonoBehaviour
	{
		private const string PrimaryTextColorCode = "green";

		private const string SecondaryTextColorCode = "yellow";

		private const string TertiaryTextColorCode = "white";

		private float timer;

		private string[] options;

		private readonly Dictionary<ulong, int> votersOptions = new Dictionary<ulong, int>();

		internal static PollManager Instance { get; private set; }

		internal bool IsPollActive { get; private set; }

		private void Awake()
		{
			Instance = this;
		}

		private void Start()
		{
			Plugin.inputActions.EndPoll.performed += EndPoll_performed;
		}

		private void OnDestroy()
		{
			Plugin.inputActions.EndPoll.performed -= EndPoll_performed;
		}

		private void EndPoll_performed(CallbackContext context)
		{
			if (!InputActions.ShouldInputBeIgnored())
			{
				EndPoll();
			}
		}

		private void Update()
		{
			if (IsPollActive)
			{
				timer -= Time.deltaTime;
				if (timer <= 0f)
				{
					EndPoll();
				}
			}
		}

		internal void StartPoll(string title, float duration, string[] options)
		{
			if (IsPollActive)
			{
				return;
			}
			IsPollActive = true;
			timer = duration;
			this.options = options;
			votersOptions.Clear();
			StringBuilder stringBuilder = new StringBuilder("<color=green>");
			stringBuilder.AppendLine("<align=flush>╔ POLL ╗</align>");
			if (!string.IsNullOrEmpty(title))
			{
				stringBuilder.AppendLine("<align=justified><color=yellow>\"" + title + "\"</color></align>");
			}
			stringBuilder.Append("<color=white>");
			for (int i = 0; i < options.Length; i++)
			{
				stringBuilder.Append($"{i + 1}. {options[i]}");
				if (i < options.Length - 1)
				{
					stringBuilder.AppendLine();
				}
			}
			stringBuilder.AppendLine("</color>");
			stringBuilder.AppendLine("<align=justified><color=yellow>^ Vote by sending the number!</color></align>");
			stringBuilder.Append("<align=flush>╚╝</align>");
			stringBuilder.Append("</color>");
			Plugin.PrintToChat(stringBuilder.ToString());
		}

		internal void EndPoll()
		{
			if (!IsPollActive)
			{
				return;
			}
			IsPollActive = false;
			Dictionary<int, int> dictionary = new Dictionary<int, int>(options.Length);
			for (int i = 0; i < options.Length; i++)
			{
				dictionary[i] = 0;
			}
			foreach (int value in votersOptions.Values)
			{
				dictionary[value]++;
			}
			StringBuilder stringBuilder = new StringBuilder("<color=green>");
			stringBuilder.AppendLine("<align=flush>╔ RESULTS ╗</align>");
			if (votersOptions.Count > 0)
			{
				stringBuilder.Append("<color=white>");
				for (int j = 0; j < options.Length; j++)
				{
					float num = (float)dictionary[j] / (float)votersOptions.Count * 100f;
					stringBuilder.Append($"({num:n0}%) {options[j]}");
					if (j < options.Length - 1)
					{
						stringBuilder.AppendLine();
					}
				}
				stringBuilder.AppendLine("</color>");
			}
			else
			{
				stringBuilder.AppendLine("<color=yellow>No one voted...</color>");
			}
			stringBuilder.Append("<align=flush>╚╝</align>");
			stringBuilder.Append("</color>");
			Plugin.PrintToChat(stringBuilder.ToString());
		}

		internal void AddVote(ulong clientId, int optionIndex)
		{
			if (optionIndex >= 0 && optionIndex < options.Length)
			{
				votersOptions[clientId] = optionIndex;
			}
		}
	}
}
namespace ChatPoll.Patches
{
	[HarmonyPatch]
	internal static class PollPatch
	{
		private static PollManager pollManager;

		[HarmonyPrefix]
		[HarmonyPatch(typeof(StartOfRound), "Awake")]
		private static void InitializePollManagerPatch(StartOfRound __instance)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (GameNetworkManager.Instance.isHostingGame)
			{
				if ((Object)(object)pollManager != (Object)null)
				{
					Plugin.Logger.LogError((object)"Tried to create a poll manager but one already exists");
					return;
				}
				pollManager = new GameObject("PollManager").AddComponent<PollManager>();
				((Component)pollManager).gameObject.AddComponent<PollCreatorUI>();
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(HUDManager), "__rpc_handler_2930587515")]
		private static bool ProcessIncomingChatMessagePatch(ref NetworkBehaviour target, ref FastBufferReader reader, ref __RpcParams rpcParams)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (!pollManager.IsPollActive)
			{
				return true;
			}
			bool flag = default(bool);
			((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref flag, default(ForPrimitives));
			if (!flag)
			{
				return true;
			}
			string s = default(string);
			((FastBufferReader)(ref reader)).ReadValueSafe(ref s, false);
			((FastBufferReader)(ref reader)).Seek(0);
			if (!int.TryParse(s, out var result))
			{
				return true;
			}
			pollManager.AddVote(rpcParams.Server.Receive.SenderClientId, result - 1);
			return false;
		}
	}
}