using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Unity.IL2CPP; using Fusion; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Photon.Voice.Fusion; using Photon.Voice.Unity; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyVersion("0.0.0.0")] namespace PerPlayerVoiceVolume; public class NativeRowRefs { public GameObject Root; public Text LabelText; public TMP_Text LabelTmp; public Text ValueText; public TMP_Text ValueTmp; } [BepInPlugin("com.Sh4de.perplayervoicevolume", "PerPlayerVoiceVolume", "1.2.0")] [BepInProcess("ShiftAtMidnight.exe")] public class Mod : BasePlugin { internal const string GUID = "com.Sh4de.perplayervoicevolume"; internal static Mod Instance; private static Harmony harmony; private readonly Dictionary volumes = new Dictionary(); private readonly Dictionary sources = new Dictionary(); private readonly Dictionary lastLabels = new Dictionary(); private float refreshTimer; private const float RefreshInterval = 1.5f; private KeybindsManager keybindsManager; private GameObject nativeRowTemplateSource; private Transform nativeRowContainer; private int nativeRowInsertBaseIndex; private readonly Dictionary nativeRows = new Dictionary(); private float nativeSyncTimer; private const float NativeSyncInterval = 1f; public override void Load() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown Instance = this; try { harmony = new Harmony("com.Sh4de.perplayervoicevolume"); harmony.PatchAll(); } catch (Exception ex) { ((BasePlugin)this).Log.LogError((object)("Harmony PatchAll a echoue: " + ex)); } try { ((BasePlugin)this).AddComponent(); } catch (Exception ex2) { ((BasePlugin)this).Log.LogError((object)("Failed to add UpdateDriver component: " + ex2)); } } public override bool Unload() { try { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } catch (Exception ex) { ((BasePlugin)this).Log.LogError((object)("Harmony UnpatchSelf a echoue: " + ex)); } return true; } internal void OnKeybindsManagerReady(KeybindsManager instance) { try { keybindsManager = instance; Slider playerVolSlider = instance.playerVolSlider; if (!((Object)(object)playerVolSlider == (Object)null)) { Transform parent = ((Component)playerVolSlider).transform.parent; if (!((Object)(object)parent == (Object)null)) { nativeRowTemplateSource = ((Component)parent).gameObject; nativeRowContainer = parent.parent; nativeRowInsertBaseIndex = parent.GetSiblingIndex() + 1; nativeRows.Clear(); } } } catch (Exception ex) { ((BasePlugin)this).Log.LogError((object)("OnKeybindsManagerReady failed: " + ex)); } } internal void OnUpdate() { refreshTimer += Time.unscaledDeltaTime; if (refreshTimer >= 1.5f) { refreshTimer = 0f; RefreshEntries(); } foreach (KeyValuePair source in sources) { if (!((Object)(object)source.Value == (Object)null)) { float value; float volume = (volumes.TryGetValue(source.Key, out value) ? value : 1f); source.Value.volume = volume; } } nativeSyncTimer += Time.unscaledDeltaTime; if (nativeSyncTimer >= 1f) { nativeSyncTimer = 0f; SyncNativeRows(); } } private void SyncNativeRows() { try { if ((Object)(object)keybindsManager == (Object)null || (Object)(object)nativeRowTemplateSource == (Object)null || (Object)(object)nativeRowContainer == (Object)null || !((Component)keybindsManager).gameObject.activeInHierarchy) { return; } bool flag = false; foreach (string key in sources.Keys) { if (!nativeRows.ContainsKey(key)) { CreateNativeRow(key); flag = true; } } List list = new List(); foreach (string key2 in nativeRows.Keys) { if (!sources.ContainsKey(key2)) { list.Add(key2); } } foreach (string item in list) { RemoveNativeRow(item); flag = true; } if (flag) { try { RectTransform component = ((Component)nativeRowContainer).GetComponent(); if ((Object)(object)component != (Object)null) { LayoutRebuilder.ForceRebuildLayoutImmediate(component); } } catch (Exception ex) { ((BasePlugin)this).Log.LogError((object)("ForceRebuildLayoutImmediate failed: " + ex.Message)); } } foreach (KeyValuePair nativeRow in nativeRows) { string displayLabel = GetDisplayLabel(nativeRow.Key); if ((Object)(object)nativeRow.Value.LabelText != (Object)null) { nativeRow.Value.LabelText.text = displayLabel; } if ((Object)(object)nativeRow.Value.LabelTmp != (Object)null) { nativeRow.Value.LabelTmp.text = displayLabel; } float value2; float value = (volumes.TryGetValue(nativeRow.Key, out value2) ? value2 : 1f); UpdateValueDisplay(nativeRow.Value.ValueText, nativeRow.Value.ValueTmp, value); } } catch (Exception ex2) { ((BasePlugin)this).Log.LogError((object)("SyncNativeRows failed: " + ex2)); } } private static bool LooksNumeric(string s) { if (string.IsNullOrWhiteSpace(s)) { return true; } return Regex.IsMatch(s.Trim().TrimEnd('%'), "^-?\\d+([.,]\\d+)?$"); } private void DisableLocalizers(GameObject go) { if ((Object)(object)go == (Object)null) { return; } try { foreach (Behaviour component in go.GetComponents()) { if (!((Object)(object)component == (Object)null) && ((object)component).GetType().Name.IndexOf("Localiz", StringComparison.OrdinalIgnoreCase) >= 0) { component.enabled = false; } } } catch (Exception ex) { ((BasePlugin)this).Log.LogError((object)("DisableLocalizers failed: " + ex.Message)); } } private void CreateNativeRow(string key) { try { GameObject val = Object.Instantiate(nativeRowTemplateSource, nativeRowContainer); ((Object)val).name = "PPVV_Row_" + key; val.transform.SetSiblingIndex(nativeRowInsertBaseIndex + nativeRows.Count); val.SetActive(true); Slider componentInChildren = val.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null) { Object.Destroy((Object)(object)val); return; } ((UnityEventBase)componentInChildren.onValueChanged).RemoveAllListeners(); componentInChildren.minValue = 0f; componentInChildren.maxValue = 2f; float value; float num = (volumes.TryGetValue(key, out value) ? Mathf.Clamp(value, 0f, 2f) : 1f); componentInChildren.SetValueWithoutNotify(num); Text val2 = null; Text val3 = null; TMP_Text val4 = null; TMP_Text val5 = null; foreach (Text componentsInChild in val.GetComponentsInChildren(true)) { if (!((Component)componentsInChild).transform.IsChildOf(((Component)componentInChildren).transform)) { if (LooksNumeric(componentsInChild.text) && (Object)(object)val3 == (Object)null) { val3 = componentsInChild; } else if ((Object)(object)val2 == (Object)null) { val2 = componentsInChild; } } } try { foreach (TMP_Text componentsInChild2 in val.GetComponentsInChildren(true)) { if (!componentsInChild2.transform.IsChildOf(((Component)componentInChildren).transform)) { if (LooksNumeric(componentsInChild2.text) && (Object)(object)val5 == (Object)null) { val5 = componentsInChild2; } else if ((Object)(object)val4 == (Object)null) { val4 = componentsInChild2; } } } } catch (Exception ex) { ((BasePlugin)this).Log.LogError((object)("TMP_Text lookup failed: " + ex.Message)); } if ((Object)(object)val2 != (Object)null) { DisableLocalizers(((Component)val2).gameObject); } if ((Object)(object)val4 != (Object)null) { DisableLocalizers(((Component)val4).gameObject); } string displayLabel = GetDisplayLabel(key); if ((Object)(object)val2 != (Object)null) { val2.text = displayLabel; } if ((Object)(object)val4 != (Object)null) { val4.text = displayLabel; } UpdateValueDisplay(val3, val5, num); string capturedKey = key; Text capturedValueText = val3; TMP_Text capturedValueTmp = val5; Action action = delegate(float newVal) { volumes[capturedKey] = newVal; UpdateValueDisplay(capturedValueText, capturedValueTmp, newVal); }; ((UnityEvent)(object)componentInChildren.onValueChanged).AddListener(UnityAction.op_Implicit(action)); nativeRows[key] = new NativeRowRefs { Root = val, LabelText = val2, LabelTmp = val4, ValueText = val3, ValueTmp = val5 }; } catch (Exception ex2) { ((BasePlugin)this).Log.LogError((object)("CreateNativeRow failed for '" + key + "': " + ex2)); } } private void RemoveNativeRow(string key) { if (nativeRows.TryGetValue(key, out var value)) { if ((Object)(object)value.Root != (Object)null) { Object.Destroy((Object)(object)value.Root); } nativeRows.Remove(key); } } private void UpdateValueDisplay(Text valueText, TMP_Text valueTmp, float value) { string text = Mathf.RoundToInt(value * 100f) + "%"; if ((Object)(object)valueText != (Object)null) { valueText.text = text; } if ((Object)(object)valueTmp != (Object)null) { valueTmp.text = text; } } private string GetDisplayLabel(string key) { if (!lastLabels.TryGetValue(key, out var value)) { return key; } return value; } private void RefreshEntries() { //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) NetworkRunner val = null; try { foreach (NetworkRunner item in Resources.FindObjectsOfTypeAll()) { if (!((Object)(object)item == (Object)null)) { val = item; break; } } } catch (Exception ex) { ((BasePlugin)this).Log.LogError((object)("NetworkRunner lookup failed: " + ex.Message)); } VoiceNetworkObject[] array; try { array = Il2CppArrayBase.op_Implicit(Resources.FindObjectsOfTypeAll()); } catch (Exception ex2) { ((BasePlugin)this).Log.LogError((object)("VoiceNetworkObject lookup failed: " + ex2.Message)); return; } HashSet hashSet = new HashSet(); VoiceNetworkObject[] array2 = array; foreach (VoiceNetworkObject val2 in array2) { if ((Object)(object)val2 == (Object)null) { continue; } try { Speaker speakerInUse; try { speakerInUse = val2.SpeakerInUse; } catch { goto end_IL_00b8; } if ((Object)(object)speakerInUse == (Object)null) { continue; } AudioSource component; try { component = ((Component)speakerInUse).GetComponent(); } catch { goto end_IL_00b8; } if ((Object)(object)component == (Object)null) { continue; } NetworkObject val3 = null; try { val3 = ((Component)val2).GetComponent(); } catch { } if ((Object)(object)val3 != (Object)null && (Object)(object)val != (Object)null) { try { PlayerRef inputAuthority = val3.InputAuthority; if (((PlayerRef)(ref inputAuthority)).Equals(val.LocalPlayer)) { continue; } } catch { } } string key = GetKey(val3, val); string label = GetLabel(val3, val2, val); lastLabels[key] = label; hashSet.Add(key); sources[key] = component; if (!volumes.ContainsKey(key)) { volumes[key] = ((component.volume > 0f) ? component.volume : 1f); } end_IL_00b8:; } catch (Exception ex3) { ((BasePlugin)this).Log.LogError((object)("Failed to process a VoiceNetworkObject: " + ex3.Message)); } } List list = new List(); foreach (string key2 in sources.Keys) { if (!hashSet.Contains(key2)) { list.Add(key2); } } foreach (string item2 in list) { sources.Remove(item2); } } private string GetKey(NetworkObject no, NetworkRunner runner) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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) try { if ((Object)(object)no != (Object)null && (Object)(object)runner != (Object)null) { PlayerRef inputAuthority = no.InputAuthority; string playerUserId = runner.GetPlayerUserId(inputAuthority); if (!string.IsNullOrEmpty(playerUserId)) { return playerUserId; } } } catch (Exception ex) { ((BasePlugin)this).Log.LogError((object)("GetKey failed: " + ex.Message)); } if (!((Object)(object)no != (Object)null)) { return "obj_unknown"; } return "obj_" + ((Object)no).GetInstanceID(); } private string GetLabel(NetworkObject no, VoiceNetworkObject vno, NetworkRunner runner) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0137: 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_00d7: Unknown result type (might be due to invalid IL or missing references) try { PlayerManager val = null; NetworkObject val2 = null; if ((Object)(object)no != (Object)null && (Object)(object)runner != (Object)null) { try { PlayerRef inputAuthority = no.InputAuthority; val2 = runner.GetPlayerObject(inputAuthority); if ((Object)(object)val2 != (Object)null) { val = ((Component)val2).GetComponent(); if ((Object)(object)val == (Object)null) { val = ((Component)val2).GetComponentInChildren(); } } } catch { } } if ((Object)(object)val == (Object)null) { try { if ((Object)(object)no != (Object)null) { val = ((Component)no).GetComponent(); } } catch { } } if ((Object)(object)val == (Object)null) { try { val = ((Component)vno).GetComponent(); } catch { } } if ((Object)(object)val == (Object)null && (Object)(object)no != (Object)null) { try { val = ((Component)no).GetComponentInChildren(); } catch { } } if ((Object)(object)val == (Object)null && (Object)(object)no != (Object)null) { try { val = ((Component)no).GetComponentInParent(); } catch { } } if ((Object)(object)val == (Object)null && (Object)(object)no != (Object)null) { try { PlayerRef inputAuthority2 = no.InputAuthority; foreach (PlayerManager item in Resources.FindObjectsOfTypeAll()) { if ((Object)(object)item == (Object)null) { continue; } NetworkObject val3 = null; try { val3 = ((Component)item).GetComponent(); } catch { } if ((Object)(object)val3 == (Object)null) { try { val3 = ((Component)item).GetComponentInParent(); } catch { } } if (!((Object)(object)val3 == (Object)null)) { PlayerRef inputAuthority3 = val3.InputAuthority; if (((PlayerRef)(ref inputAuthority3)).Equals(inputAuthority2)) { val = item; break; } } } } catch { } } if ((Object)(object)val != (Object)null) { string text = null; try { text = val.playerName; } catch { } if (!string.IsNullOrEmpty(text)) { return text; } } if ((Object)(object)val2 != (Object)null) { string text2 = TryExtractNameFromObject(((Component)val2).gameObject); if (!string.IsNullOrEmpty(text2)) { return text2; } } } catch (Exception ex) { ((BasePlugin)this).Log.LogError((object)("GetLabel failed: " + ex)); } return GetLabelSteamFallback(no, runner); } private string TryExtractNameFromObject(GameObject go) { try { if ((Object)(object)go == (Object)null) { return null; } List list = new List(); foreach (Text componentsInChild in go.GetComponentsInChildren(true)) { if ((Object)(object)componentsInChild != (Object)null && !string.IsNullOrWhiteSpace(componentsInChild.text)) { list.Add(componentsInChild.text); } } try { foreach (TMP_Text componentsInChild2 in go.GetComponentsInChildren(true)) { if ((Object)(object)componentsInChild2 != (Object)null && !string.IsNullOrWhiteSpace(componentsInChild2.text)) { list.Add(componentsInChild2.text); } } } catch { } foreach (string item in list) { string text = item.Trim(); if (text.Length >= 2 && !LooksNumeric(text)) { return text; } } } catch (Exception ex) { ((BasePlugin)this).Log.LogError((object)("TryExtractNameFromObject failed: " + ex.Message)); } return null; } private string GetLabelSteamFallback(NetworkObject no, NetworkRunner runner) { //IL_0014: 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) try { if ((Object)(object)no != (Object)null && (Object)(object)runner != (Object)null && ulong.TryParse(runner.GetPlayerUserId(no.InputAuthority), out var result) && result != 0) { string friendPersonaName = SteamFriends.GetFriendPersonaName(new CSteamID(result)); if (!string.IsNullOrEmpty(friendPersonaName) && friendPersonaName != "[unknown]") { return friendPersonaName; } } } catch (Exception ex) { ((BasePlugin)this).Log.LogError((object)("GetLabelSteamFallback failed: " + ex)); } return "Player"; } } public class UpdateDriver : MonoBehaviour { public UpdateDriver(IntPtr ptr) : base(ptr) { } private void Update() { if (Mod.Instance == null) { return; } try { Mod.Instance.OnUpdate(); } catch (Exception ex) { ((BasePlugin)Mod.Instance).Log.LogError((object)("UpdateDriver.Update failed: " + ex)); } } } [HarmonyPatch(typeof(KeybindsManager), "Start")] internal static class KeybindsManager_Start_Patch { private static void Postfix(KeybindsManager __instance) { try { if (Mod.Instance != null) { Mod.Instance.OnKeybindsManagerReady(__instance); } } catch (Exception ex) { Mod instance = Mod.Instance; if (instance != null) { ((BasePlugin)instance).Log.LogError((object)("KeybindsManager_Start_Patch postfix failed: " + ex)); } } } }