Decompiled source of MicSnitch v1.1.1

plugins/MicSnitch.dll

Decompiled 16 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace SeeWhoIsTalkingFix;

[BepInPlugin("MicSnitch", "Mic Snitch", "1.1.1")]
public class Plugin : BaseUnityPlugin
{
	public const string GUID = "MicSnitch";

	public const string NAME = "Mic Snitch";

	public const string VERSION = "1.1.1";

	internal static ManualLogSource Log;

	internal static Harmony harmony;

	private void Awake()
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Expected O, but got Unknown
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		Log.LogInfo((object)"[MicSnitch] Mic Snitch 1.1.1 loading...");
		try
		{
			Core.ResolveTypes();
		}
		catch (Exception ex)
		{
			Log.LogError((object)("[MicSnitch] ResolveTypes failed: " + ex));
		}
		harmony = new Harmony("MicSnitch");
		GameObject val = new GameObject("MicSnitch_Overlay");
		((Object)val).hideFlags = (HideFlags)61;
		Object.DontDestroyOnLoad((Object)(object)val);
		val.AddComponent<Overlay>();
		val.AddComponent<Poller>();
		Log.LogInfo((object)"[MicSnitch] components attached, Awake done");
	}

	private void OnDestroy()
	{
		try
		{
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
		catch
		{
		}
	}
}
internal class SpeakerInfo
{
	public float lastNormal;

	public float lastLoud;

	public float peakNow;
}
internal static class Core
{
	internal const float QUIET_THRESHOLD = 0.06f;

	internal const float LOUD_THRESHOLD = 0.3f;

	internal const float NORMAL_HOLD = 0.7f;

	internal const float LOUD_HOLD = 3f;

	internal static bool Visible = true;

	internal static readonly object lockObj = new object();

	internal static readonly Dictionary<string, SpeakerInfo> activeSpeakers = new Dictionary<string, SpeakerInfo>();

	private static readonly Stopwatch Clock = Stopwatch.StartNew();

	internal static MethodBase mLinkOnDecodedFrame;

	internal static FieldInfo fiLinkPlayerId;

	internal static FieldInfo fiLinkVoiceInfo;

	internal static PropertyInfo piVoiceInfoUserData;

	internal static PropertyInfo piFrameOutBuf;

	internal static PropertyInfo piCurrentRoom;

	internal static MethodInfo miGetPlayer;

	internal static int getPlayerArgc;

	internal static PropertyInfo piPlayerNickName;

	internal static PropertyInfo piPunInstance;

	internal static PropertyInfo piVoiceClient;

	internal static PropertyInfo piLocalVoices;

	internal static PropertyInfo piIsTransmitting;

	internal static PropertyInfo piMyNickName;

	private const BindingFlags ANY_INSTANCE = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

	private const BindingFlags ANY_STATIC = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

	internal static float Now => (float)Clock.Elapsed.TotalSeconds;

	internal static Type FindType(string shortName)
	{
		Type type = Type.GetType(shortName);
		if (type != null)
		{
			return type;
		}
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly in assemblies)
		{
			try
			{
				Type type2 = assembly.GetType(shortName, throwOnError: false);
				if (type2 != null)
				{
					return type2;
				}
			}
			catch
			{
			}
		}
		return null;
	}

	internal static void ResolveTypes()
	{
		ManualLogSource log = Plugin.Log;
		Type type = FindType("Photon.Voice.Unity.RemoteVoiceLink");
		Type type2 = FindType("Photon.Voice.FrameOut`1");
		Type type3 = FindType("Photon.Voice.VoiceClient");
		Type type4 = FindType("Photon.Realtime.Player");
		Type type5 = FindType("Photon.Pun.PhotonNetwork");
		Type type6 = FindType("Photon.Voice.PUN.PunVoiceClient");
		Type type7 = FindType("Photon.Voice.LocalVoice");
		if (type != null)
		{
			mLinkOnDecodedFrame = type.GetMethod("OnDecodedFrameFloatAction", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			fiLinkPlayerId = type.GetField("PlayerId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			fiLinkVoiceInfo = type.GetField("VoiceInfo", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}
		if (type2 != null)
		{
			Type type8 = type2.MakeGenericType(typeof(float));
			piFrameOutBuf = type8.GetProperty("Buf", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}
		Type type9 = FindType("Photon.Voice.VoiceInfo");
		if (type9 != null)
		{
			piVoiceInfoUserData = type9.GetProperty("UserData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}
		if (type4 != null)
		{
			piPlayerNickName = type4.GetProperty("NickName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}
		if (type5 != null)
		{
			piCurrentRoom = type5.GetProperty("CurrentRoom", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			piMyNickName = type5.GetProperty("NickName", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		}
		if (piCurrentRoom != null)
		{
			miGetPlayer = piCurrentRoom.PropertyType.GetMethod("GetPlayer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
			{
				typeof(int),
				typeof(bool)
			}, null) ?? piCurrentRoom.PropertyType.GetMethod("GetPlayer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(int) }, null);
			if (miGetPlayer != null)
			{
				getPlayerArgc = miGetPlayer.GetParameters().Length;
			}
		}
		if (type6 != null)
		{
			piPunInstance = type6.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			piVoiceClient = type6.GetProperty("VoiceClient", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}
		if (type3 != null)
		{
			piLocalVoices = type3.GetProperty("LocalVoices", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}
		if (type7 != null)
		{
			piIsTransmitting = type7.GetProperty("IsCurrentlyTransmitting", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}
		log.LogInfo((object)("[MicSnitch] Resolved: Link=" + (mLinkOnDecodedFrame != null) + ", LinkPlayerId=" + (fiLinkPlayerId != null) + ", LinkVoiceInfo=" + (fiLinkVoiceInfo != null) + ", VoiceInfo.UserData=" + (piVoiceInfoUserData != null) + ", FrameOut.Buf=" + (piFrameOutBuf != null) + ", GetPlayer(argc=" + getPlayerArgc + ")=" + (miGetPlayer != null) + ", NickName=" + (piPlayerNickName != null)));
		log.LogInfo((object)("[MicSnitch] LocalMic: PunInstance=" + (piPunInstance != null) + ", VoiceClient=" + (piVoiceClient != null) + ", LocalVoices=" + (piLocalVoices != null) + ", IsCurrentlyTransmitting=" + (piIsTransmitting != null) + ", MyNickName=" + (piMyNickName != null)));
	}

	internal static void ApplyPatches(Harmony harmony)
	{
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Expected O, but got Unknown
		if (mLinkOnDecodedFrame != null && fiLinkPlayerId != null)
		{
			MethodBase methodBase = mLinkOnDecodedFrame;
			HarmonyMethod val = new HarmonyMethod(typeof(Core).GetMethod("OnLinkFramePostfix", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
			harmony.Patch(methodBase, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Plugin.Log.LogInfo((object)"[MicSnitch] Patched RemoteVoiceLink.OnDecodedFrameFloatAction");
		}
	}

	public static void OnLinkFramePostfix(object __instance, object floats)
	{
		try
		{
			if (__instance != null)
			{
				int actor = ResolveActor(__instance);
				float peak = ComputePeak(floats);
				RecordActor(actor, peak);
			}
		}
		catch (Exception ex)
		{
			if (Plugin.Log != null)
			{
				Plugin.Log.LogWarning((object)("[MicSnitch] OnLinkFramePostfix: " + ex.Message));
			}
		}
	}

	private static int ResolveActor(object link)
	{
		if (fiLinkVoiceInfo != null && piVoiceInfoUserData != null)
		{
			object value = fiLinkVoiceInfo.GetValue(link);
			if (value != null)
			{
				object value2 = piVoiceInfoUserData.GetValue(value);
				object obj = ((value2 is int) ? value2 : null);
				int num = default(int);
				if (obj != null)
				{
					num = (int)value2;
				}
				if (obj != null && num > 0)
				{
					return num / 1000;
				}
			}
		}
		return Convert.ToInt32(fiLinkPlayerId.GetValue(link));
	}

	private static float ComputePeak(object frameOut)
	{
		if (frameOut == null || piFrameOutBuf == null)
		{
			return -1f;
		}
		if (!(piFrameOutBuf.GetValue(frameOut) is float[] array) || array.Length == 0)
		{
			return 0f;
		}
		float num = 0f;
		for (int i = 0; i < array.Length; i++)
		{
			float num2 = ((!(array[i] >= 0f)) ? (0f - array[i]) : array[i]);
			if (num2 > num)
			{
				num = num2;
			}
		}
		return num;
	}

	internal static void RecordActor(int actor, float peak)
	{
		if (peak >= 0f && peak < 0.06f)
		{
			return;
		}
		string text = ResolveNick(actor);
		if (string.IsNullOrEmpty(text))
		{
			text = "Player " + actor;
		}
		lock (lockObj)
		{
			if (!activeSpeakers.TryGetValue(text, out var value))
			{
				value = new SpeakerInfo();
				activeSpeakers[text] = value;
			}
			value.lastNormal = Now;
			value.peakNow = peak;
			if (peak < 0f || peak >= 0.3f)
			{
				value.lastLoud = Now;
			}
		}
	}

	internal static void PollLocalVoice()
	{
		try
		{
			if (piPunInstance == null || piVoiceClient == null || piLocalVoices == null || piIsTransmitting == null)
			{
				return;
			}
			object value = piPunInstance.GetValue(null);
			if (value == null)
			{
				return;
			}
			object value2 = piVoiceClient.GetValue(value);
			if (value2 == null || !(piLocalVoices.GetValue(value2) is IEnumerable enumerable))
			{
				return;
			}
			bool flag = false;
			foreach (object item in enumerable)
			{
				if (item != null && (bool)piIsTransmitting.GetValue(item))
				{
					flag = true;
					break;
				}
			}
			if (!flag)
			{
				return;
			}
			string text = ((!(piMyNickName != null)) ? null : (piMyNickName.GetValue(null) as string));
			if (string.IsNullOrEmpty(text))
			{
				text = "You";
			}
			lock (lockObj)
			{
				if (!activeSpeakers.TryGetValue(text, out var value3))
				{
					value3 = new SpeakerInfo();
					activeSpeakers[text] = value3;
				}
				value3.lastNormal = Now;
			}
		}
		catch (Exception ex)
		{
			if (Plugin.Log != null)
			{
				Plugin.Log.LogWarning((object)("[MicSnitch] PollLocalVoice: " + ex.Message));
			}
		}
	}

	internal static string ResolveNick(int actor)
	{
		try
		{
			if (piCurrentRoom == null || miGetPlayer == null || piPlayerNickName == null)
			{
				return null;
			}
			object value = piCurrentRoom.GetValue(null);
			if (value == null)
			{
				return null;
			}
			object[] parameters = ((getPlayerArgc == 2) ? new object[2] { actor, false } : new object[1] { actor });
			object obj = miGetPlayer.Invoke(value, parameters);
			if (obj == null)
			{
				return null;
			}
			return piPlayerNickName.GetValue(obj) as string;
		}
		catch
		{
			return null;
		}
	}
}
internal class Poller : MonoBehaviour
{
	private float nextPoll;

	private bool patched;

	private void Update()
	{
		if (!patched)
		{
			patched = true;
			try
			{
				Core.ApplyPatches(Plugin.harmony);
			}
			catch (Exception ex)
			{
				if (Plugin.Log != null)
				{
					Plugin.Log.LogError((object)("[MicSnitch] ApplyPatches failed: " + ex));
				}
			}
		}
		try
		{
			if (Input.GetKeyDown((KeyCode)289))
			{
				Core.Visible = !Core.Visible;
				if (Plugin.Log != null)
				{
					Plugin.Log.LogInfo((object)("[MicSnitch] F8 -> Visible=" + Core.Visible));
				}
			}
		}
		catch
		{
		}
		if (Core.Now < nextPoll)
		{
			return;
		}
		nextPoll = Core.Now + 0.15f;
		Core.PollLocalVoice();
		float now = Core.Now;
		float num = now - Mathf.Max(0.7f, 3f) - 1f;
		lock (Core.lockObj)
		{
			if (Core.activeSpeakers.Count == 0)
			{
				return;
			}
			List<string> list = null;
			foreach (KeyValuePair<string, SpeakerInfo> activeSpeaker in Core.activeSpeakers)
			{
				SpeakerInfo value = activeSpeaker.Value;
				float num2 = ((!(value.lastLoud > value.lastNormal)) ? value.lastNormal : value.lastLoud);
				if (num2 < num)
				{
					(list ?? (list = new List<string>())).Add(activeSpeaker.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (string item in list)
			{
				Core.activeSpeakers.Remove(item);
			}
		}
	}
}
internal class Overlay : MonoBehaviour
{
	private struct Row
	{
		public string name;

		public bool loud;

		public float at;
	}

	private GUIStyle styleLoud;

	private GUIStyle styleNormal;

	private GUIStyle styleHint;

	private GUIStyle styleHintFaint;

	private Font font;

	private static readonly Color LOUD = new Color(1f, 0.3f, 0.3f, 0.95f);

	private static readonly Color NORMAL = new Color(1f, 0.94f, 0.4f, 0.55f);

	private static readonly Color HINT = new Color(1f, 0.94f, 0.4f, 0.55f);

	private static readonly Color FAINT = new Color(1f, 0.94f, 0.4f, 0.18f);

	private void EnsureStyles()
	{
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Expected O, but got Unknown
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Expected O, but got Unknown
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Expected O, but got Unknown
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Expected O, but got Unknown
		//IL_0168: Unknown result type (might be due to invalid IL or missing references)
		if (styleLoud != null)
		{
			return;
		}
		try
		{
			font = Font.CreateDynamicFontFromOSFont(new string[4] { "Arial", "Helvetica", "Liberation Sans", "Verdana" }, 22);
		}
		catch (Exception ex)
		{
			if (Plugin.Log != null)
			{
				Plugin.Log.LogWarning((object)("[MicSnitch] CreateDynamicFontFromOSFont: " + ex.Message));
			}
		}
		if ((Object)(object)font == (Object)null)
		{
			font = GUI.skin.font;
		}
		styleNormal = new GUIStyle();
		styleNormal.font = font;
		styleNormal.fontSize = 22;
		styleNormal.fontStyle = (FontStyle)0;
		styleNormal.alignment = (TextAnchor)0;
		styleNormal.normal.textColor = NORMAL;
		styleLoud = new GUIStyle(styleNormal);
		styleLoud.fontStyle = (FontStyle)1;
		styleLoud.normal.textColor = LOUD;
		styleHint = new GUIStyle(styleNormal);
		styleHint.fontSize = 14;
		styleHint.normal.textColor = HINT;
		styleHintFaint = new GUIStyle(styleHint);
		styleHintFaint.normal.textColor = FAINT;
	}

	private void OnGUI()
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Expected O, but got Unknown
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_016d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Expected O, but got Unknown
		//IL_0172: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		//IL_023c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d1: Expected O, but got Unknown
		//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d7: Expected O, but got Unknown
		//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
		EnsureStyles();
		float now = Core.Now;
		if (!Core.Visible)
		{
			string text = "[F8] Mic Snitch";
			Vector2 val = styleHintFaint.CalcSize(new GUIContent(text));
			GUI.Label(new Rect((float)Screen.width - val.x - 24f, 24f, val.x + 4f, val.y + 4f), text, styleHintFaint);
			return;
		}
		List<Row> list = new List<Row>();
		lock (Core.lockObj)
		{
			foreach (KeyValuePair<string, SpeakerInfo> activeSpeaker in Core.activeSpeakers)
			{
				SpeakerInfo value = activeSpeaker.Value;
				bool flag = now - value.lastLoud < 3f;
				bool flag2 = now - value.lastNormal < 0.7f;
				if (flag || flag2)
				{
					list.Add(new Row
					{
						name = activeSpeaker.Key,
						loud = flag,
						at = ((!flag) ? value.lastNormal : value.lastLoud)
					});
				}
			}
		}
		string text2 = "Speaking [F8]";
		Vector2 val2 = styleHint.CalcSize(new GUIContent(text2));
		float num = 24f;
		float num2 = val2.x;
		foreach (Row item in list)
		{
			GUIStyle val3 = ((!item.loud) ? styleNormal : styleLoud);
			float x = val3.CalcSize(new GUIContent(item.name)).x;
			if (x > num2)
			{
				num2 = x;
			}
		}
		float num3 = (float)Screen.width - 24f;
		GUI.Label(new Rect(num3 - val2.x, num, val2.x + 4f, val2.y + 2f), text2, styleHint);
		num += val2.y + 4f;
		if (list.Count == 0)
		{
			return;
		}
		list.Sort((Row a, Row b) => (a.loud != b.loud) ? ((!a.loud) ? 1 : (-1)) : b.at.CompareTo(a.at));
		foreach (Row item2 in list)
		{
			GUIStyle val4 = ((!item2.loud) ? styleNormal : styleLoud);
			Vector2 val5 = val4.CalcSize(new GUIContent(item2.name));
			GUI.Label(new Rect(num3 - val5.x, num, val5.x + 4f, val5.y + 2f), item2.name, val4);
			num += val5.y + 1f;
		}
	}
}