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(); val.AddComponent(); 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 activeSpeakers = new Dictionary(); 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 list = null; foreach (KeyValuePair 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())).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 list = new List(); lock (Core.lockObj) { foreach (KeyValuePair 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; } } }