Decompiled source of AfkEyes v0.1.0

AfkEyes.dll

Decompiled 3 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 ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
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("AfkEyes")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+9688d4009f6fef42019e05f3adc2e94f83cd9091")]
[assembly: AssemblyProduct("AfkEyes")]
[assembly: AssemblyTitle("AfkEyes")]
[assembly: AssemblyVersion("0.1.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 AfkEyes
{
	public class AfkEyesBehavior : MonoBehaviour
	{
		private static readonly string[] ClosedNameHints = new string[3] { "clos", "shut", "sleep" };

		private float lastActivityTime;

		private Vector3 lastMousePosition;

		private bool mouseInit;

		private bool afkSustaining;

		private int closedEyesIndex = -2;

		private bool loggedNames;

		private bool overrideMissingLogged;

		private bool broadcastAfk;

		private void Awake()
		{
			lastActivityTime = Time.time;
		}

		private void Update()
		{
			bool flag = true;
			float num = 60f;
			int configIndex = -1;
			try
			{
				if (ConfigSettings.Enabled != null)
				{
					flag = ConfigSettings.Enabled.Value;
				}
				if (ConfigSettings.AfkSeconds != null)
				{
					num = ConfigSettings.AfkSeconds.Value;
				}
				if (ConfigSettings.ClosedEyesIndex != null)
				{
					configIndex = ConfigSettings.ClosedEyesIndex.Value;
				}
			}
			catch
			{
			}
			if (!flag)
			{
				SetBroadcast(afk: false);
				afkSustaining = false;
				return;
			}
			float time = Time.time;
			if (!InGameplayLevel() || MenuOpen() || !LocalAvatarAlive(out var avatar))
			{
				lastActivityTime = time;
				SetBroadcast(afk: false);
				afkSustaining = false;
				return;
			}
			if (ActivityNow())
			{
				lastActivityTime = time;
			}
			if (time - lastActivityTime < num)
			{
				SetBroadcast(afk: false);
				afkSustaining = false;
				return;
			}
			object value;
			try
			{
				value = Traverse.Create((object)avatar).Field("playerExpression").GetValue();
			}
			catch
			{
				return;
			}
			if (value == null)
			{
				return;
			}
			int num2 = ResolveIndex(value, configIndex);
			if (num2 < 0)
			{
				return;
			}
			if (!afkSustaining)
			{
				if (IsExpressing(value, num2))
				{
					return;
				}
				afkSustaining = true;
				SetBroadcast(afk: true);
				Plugin.Log("AfkEyes: AFK " + num.ToString("F0") + "s, ojos cerrados (gesto " + num2 + ")");
			}
			AfkEyesNetworking.RefreshIfNeeded(sustaining: true);
			try
			{
				Traverse.Create(value).Method("OverrideExpressionSet", new object[2] { num2, 100f }).GetValue();
			}
			catch (Exception ex)
			{
				afkSustaining = false;
				if (!overrideMissingLogged)
				{
					overrideMissingLogged = true;
					Plugin.Log("AfkEyes: OverrideExpressionSet no disponible (" + ex.Message + ")");
				}
			}
		}

		private void SetBroadcast(bool afk)
		{
			if (broadcastAfk != afk)
			{
				broadcastAfk = afk;
				AfkEyesNetworking.BroadcastAfk(afk);
			}
		}

		private int ResolveIndex(object expression, int configIndex)
		{
			if (configIndex >= 0)
			{
				return configIndex;
			}
			if (closedEyesIndex != -2)
			{
				return closedEyesIndex;
			}
			try
			{
				if (Traverse.Create(expression).Field("expressions").GetValue() is IList list)
				{
					string text = "";
					for (int i = 0; i < list.Count; i++)
					{
						string text2 = Traverse.Create(list[i]).Field("expressionName").GetValue<string>() ?? "";
						text = text + ((i > 0) ? ", " : "") + i + ":" + text2;
						string text3 = text2.ToLowerInvariant();
						string[] closedNameHints = ClosedNameHints;
						foreach (string value in closedNameHints)
						{
							if (text3.Contains(value))
							{
								closedEyesIndex = i;
								Plugin.Log("AfkEyes: gesto ojos cerrados = " + i + " (" + text2 + ")");
								return closedEyesIndex;
							}
						}
					}
					if (!loggedNames)
					{
						loggedNames = true;
						Plugin.Log("AfkEyes: no se encontro gesto de ojos cerrados en [" + text + "], fijar ClosedEyesIndex manual");
					}
				}
			}
			catch (Exception ex)
			{
				if (!loggedNames)
				{
					loggedNames = true;
					Plugin.Log("AfkEyes: no se pudo leer expresiones (" + ex.Message + ")");
				}
			}
			closedEyesIndex = -1;
			return -1;
		}

		private static bool IsExpressing(object expression, int index)
		{
			try
			{
				if (!(Traverse.Create(expression).Field("expressions").GetValue() is IList list) || index < 0 || index >= list.Count)
				{
					return false;
				}
				return Traverse.Create(list[index]).Field("isExpressing").GetValue<bool>();
			}
			catch
			{
				return false;
			}
		}

		private bool ActivityNow()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_004f: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Input.anyKey)
				{
					return true;
				}
				Vector3 mousePosition = Input.mousePosition;
				if (!mouseInit)
				{
					lastMousePosition = mousePosition;
					mouseInit = true;
				}
				else
				{
					Vector3 val = mousePosition - lastMousePosition;
					if (((Vector3)(ref val)).sqrMagnitude > 0.5f)
					{
						lastMousePosition = mousePosition;
						return true;
					}
				}
				Vector2 mouseScrollDelta = Input.mouseScrollDelta;
				if (Mathf.Abs(mouseScrollDelta.x) + Mathf.Abs(mouseScrollDelta.y) > 0.01f)
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		internal static bool InGameplayLevel()
		{
			try
			{
				if ((Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated)
				{
					return false;
				}
				RunManager instance = RunManager.instance;
				if ((Object)(object)instance == (Object)null || (Object)(object)instance.levelCurrent == (Object)null)
				{
					return false;
				}
				return instance.levelCurrent.HasEnemies;
			}
			catch
			{
				return false;
			}
		}

		private static bool MenuOpen()
		{
			try
			{
				MenuManager instance = MenuManager.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return false;
				}
				return Traverse.Create((object)instance).Field("currentMenuPage").GetValue() != null;
			}
			catch
			{
				return false;
			}
		}

		private static bool LocalAvatarAlive(out PlayerAvatar avatar)
		{
			avatar = null;
			try
			{
				avatar = PlayerAvatar.instance;
				if ((Object)(object)avatar == (Object)null)
				{
					avatar = null;
					return false;
				}
				Traverse val = Traverse.Create((object)avatar);
				if (!val.Field("isLocal").GetValue<bool>())
				{
					avatar = null;
					return false;
				}
				if (val.Field("isDisabled").GetValue<bool>())
				{
					return false;
				}
				if (val.Field("deadSet").GetValue<bool>())
				{
					return false;
				}
				return true;
			}
			catch
			{
				avatar = null;
				return false;
			}
		}
	}
	public class AfkEyesNetworking : MonoBehaviour, IOnEventCallback
	{
		private const byte AfkStateEvent = 76;

		private const float RefreshInterval = 5f;

		private const float EntryTimeout = 12f;

		private const float LabelHeight = 2.2f;

		private readonly Dictionary<int, float> afkSince = new Dictionary<int, float>();

		private readonly Dictionary<PlayerAvatar, TextMeshPro> labels = new Dictionary<PlayerAvatar, TextMeshPro>();

		private readonly List<PlayerAvatar> deadScratch = new List<PlayerAvatar>();

		private bool registered;

		private float nextRefresh;

		private float nextLabelPoll;

		public static AfkEyesNetworking Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
		}

		private void OnDisable()
		{
			Unregister();
			ClearLabels();
			afkSince.Clear();
		}

		private void EnsureRegistered()
		{
			if (!registered && PhotonNetwork.InRoom)
			{
				PhotonNetwork.AddCallbackTarget((object)this);
				registered = true;
			}
		}

		private void Unregister()
		{
			if (registered)
			{
				PhotonNetwork.RemoveCallbackTarget((object)this);
				registered = false;
			}
		}

		private static bool ShouldNetwork()
		{
			Room currentRoom = PhotonNetwork.CurrentRoom;
			if (currentRoom != null)
			{
				return currentRoom.PlayerCount > 1;
			}
			return false;
		}

		public static void BroadcastAfk(bool afk)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			if ((Object)(object)Instance == (Object)null || !ShouldNetwork())
			{
				return;
			}
			int num = ((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : (-1));
			if (num < 0)
			{
				return;
			}
			try
			{
				PhotonNetwork.RaiseEvent((byte)76, (object)new int[2]
				{
					num,
					afk ? 1 : 0
				}, new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)0
				}, SendOptions.SendReliable);
			}
			catch
			{
			}
		}

		public static void RefreshIfNeeded(bool sustaining)
		{
			if (!((Object)(object)Instance == (Object)null) && sustaining && ShouldNetwork() && !(Time.time < Instance.nextRefresh))
			{
				Instance.nextRefresh = Time.time + 5f;
				BroadcastAfk(afk: true);
			}
		}

		public void OnEvent(EventData eventData)
		{
			if (eventData.Code != 76 || !(eventData.CustomData is int[] array) || array.Length < 2)
			{
				return;
			}
			int num = array[0];
			if (num != ((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : (-1)))
			{
				if (array[1] == 1)
				{
					afkSince[num] = Time.time;
				}
				else
				{
					afkSince.Remove(num);
				}
			}
		}

		private void Update()
		{
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: 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_00fa: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)RunManager.instance == (Object)null || (Object)(object)GameDirector.instance == (Object)null || !SemiFunc.RunIsLevel())
			{
				Unregister();
				ClearLabels();
				afkSince.Clear();
				return;
			}
			EnsureRegistered();
			ExpireEntries();
			Camera main = Camera.main;
			deadScratch.Clear();
			foreach (KeyValuePair<PlayerAvatar, TextMeshPro> label in labels)
			{
				if ((Object)(object)label.Key == (Object)null || (Object)(object)label.Value == (Object)null)
				{
					deadScratch.Add(label.Key);
				}
				else if ((Object)(object)main != (Object)null && ((Component)label.Value).gameObject.activeSelf)
				{
					Vector3 val = label.Value.transform.position - ((Component)main).transform.position;
					if (((Vector3)(ref val)).sqrMagnitude > 0.001f)
					{
						label.Value.transform.rotation = Quaternion.LookRotation(val);
					}
				}
			}
			foreach (PlayerAvatar item in deadScratch)
			{
				labels.Remove(item);
			}
			deadScratch.Clear();
			if (!(Time.time < nextLabelPoll))
			{
				nextLabelPoll = Time.time + 0.25f;
				PollLabels();
			}
		}

		private void ExpireEntries()
		{
			List<int> list = new List<int>();
			foreach (KeyValuePair<int, float> item in afkSince)
			{
				if (Time.time - item.Value > 12f)
				{
					list.Add(item.Key);
				}
			}
			foreach (int item2 in list)
			{
				afkSince.Remove(item2);
			}
		}

		private void PollLabels()
		{
			//IL_00a9: 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)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			if (!AfkEyesBehavior.InGameplayLevel())
			{
				ClearLabels();
				return;
			}
			PlayerAvatar[] array;
			try
			{
				array = Object.FindObjectsOfType<PlayerAvatar>();
			}
			catch
			{
				return;
			}
			HashSet<PlayerAvatar> hashSet = new HashSet<PlayerAvatar>();
			int num = ((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : (-1));
			PlayerAvatar[] array2 = array;
			foreach (PlayerAvatar val in array2)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				int num2 = ActorOf(val);
				if (num2 >= 0 && num2 != num && afkSince.TryGetValue(num2, out var value) && !(Time.time - value > 12f))
				{
					hashSet.Add(val);
					TextMeshPro val2 = Ensure(val);
					val2.transform.position = ((Component)val).transform.position + Vector3.up * 2.2f;
					if (!((Component)val2).gameObject.activeSelf)
					{
						((Component)val2).gameObject.SetActive(true);
					}
				}
			}
			deadScratch.Clear();
			foreach (KeyValuePair<PlayerAvatar, TextMeshPro> label in labels)
			{
				if (!hashSet.Contains(label.Key) && (Object)(object)label.Value != (Object)null && ((Component)label.Value).gameObject.activeSelf)
				{
					((Component)label.Value).gameObject.SetActive(false);
				}
			}
			deadScratch.Clear();
		}

		private static int ActorOf(PlayerAvatar avatar)
		{
			try
			{
				PhotonView value = Traverse.Create((object)avatar).Field("photonView").GetValue<PhotonView>();
				if ((Object)(object)value == (Object)null || value.IsMine)
				{
					return -1;
				}
				return (value.Owner != null) ? value.Owner.ActorNumber : (-1);
			}
			catch
			{
				return -1;
			}
		}

		private TextMeshPro Ensure(PlayerAvatar avatar)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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_0077: Unknown result type (might be due to invalid IL or missing references)
			if (labels.TryGetValue(avatar, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			GameObject val = new GameObject("AfkEyesLabel");
			TextMeshPro val2 = val.AddComponent<TextMeshPro>();
			((TMP_Text)val2).alignment = (TextAlignmentOptions)514;
			((TMP_Text)val2).fontSize = 48f;
			((Graphic)val2).color = new Color(1f, 0.85f, 0.2f);
			((TMP_Text)val2).text = "AFK";
			val.transform.localScale = Vector3.one * 0.04f;
			val.SetActive(false);
			labels[avatar] = val2;
			return val2;
		}

		private void ClearLabels()
		{
			foreach (KeyValuePair<PlayerAvatar, TextMeshPro> label in labels)
			{
				if ((Object)(object)label.Value != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)label.Value).gameObject);
				}
			}
			labels.Clear();
		}
	}
	public static class ConfigSettings
	{
		public static ConfigEntry<bool> Enabled { get; private set; }

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

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

		public static void Bind(ConfigFile config)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			Enabled = config.Bind<bool>("AfkEyes", "Enabled", true, "Pone el gesto vainilla de ojos cerrados tras el tiempo AFK configurado.");
			AfkSeconds = config.Bind<float>("AfkEyes", "AfkSeconds", 60f, new ConfigDescription("Segundos sin input (teclas, mouse, scroll) antes de cerrar los ojos. Solo en niveles con enemigos.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 600f), Array.Empty<object>()));
			ClosedEyesIndex = config.Bind<int>("AfkEyes", "ClosedEyesIndex", -1, new ConfigDescription("-1 = detectar el gesto por nombre (sleep/closed/shut). Si la deteccion falla (ver log), fijar el indice manual.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-1, 20), Array.Empty<object>()));
		}
	}
	[BepInPlugin("yo.AfkEyes", "AfkEyes", "0.1.0")]
	[BepInProcess("REPO.exe")]
	public class Plugin : BaseUnityPlugin
	{
		private static ManualLogSource logSource;

		public static Plugin Instance { get; private set; }

		private void Awake()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			ConfigSettings.Bind(((BaseUnityPlugin)this).Config);
			logSource = Logger.CreateLogSource("AfkEyes");
			GameObject val = new GameObject("AfkEyes");
			Object.DontDestroyOnLoad((Object)val);
			((Object)val).hideFlags = (HideFlags)61;
			val.AddComponent<AfkEyesBehavior>();
			val.AddComponent<AfkEyesNetworking>();
			Log("AfkEyes v0.1.0 loaded | Enabled=" + ConfigSettings.Enabled.Value);
		}

		public static void Log(string message)
		{
			logSource.LogInfo((object)message);
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "yo.AfkEyes";

		public const string PLUGIN_NAME = "AfkEyes";

		public const string PLUGIN_VERSION = "0.1.0";
	}
}