Decompiled source of PerPlayerVoiceVolume v1.2.0

BepInEx/plugins/PerPlayerVoiceVolume/PerPlayerVoiceVolume.dll

Decompiled 11 hours ago
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<string, float> volumes = new Dictionary<string, float>();

	private readonly Dictionary<string, AudioSource> sources = new Dictionary<string, AudioSource>();

	private readonly Dictionary<string, string> lastLabels = new Dictionary<string, string>();

	private float refreshTimer;

	private const float RefreshInterval = 1.5f;

	private KeybindsManager keybindsManager;

	private GameObject nativeRowTemplateSource;

	private Transform nativeRowContainer;

	private int nativeRowInsertBaseIndex;

	private readonly Dictionary<string, NativeRowRefs> nativeRows = new Dictionary<string, NativeRowRefs>();

	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<UpdateDriver>();
		}
		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<string, AudioSource> 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<string> list = new List<string>();
			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<RectTransform>();
					if ((Object)(object)component != (Object)null)
					{
						LayoutRebuilder.ForceRebuildLayoutImmediate(component);
					}
				}
				catch (Exception ex)
				{
					((BasePlugin)this).Log.LogError((object)("ForceRebuildLayoutImmediate failed: " + ex.Message));
				}
			}
			foreach (KeyValuePair<string, NativeRowRefs> 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<Behaviour>())
			{
				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<GameObject>(nativeRowTemplateSource, nativeRowContainer);
			((Object)val).name = "PPVV_Row_" + key;
			val.transform.SetSiblingIndex(nativeRowInsertBaseIndex + nativeRows.Count);
			val.SetActive(true);
			Slider componentInChildren = val.GetComponentInChildren<Slider>(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<Text>(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<TMP_Text>(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<float> action = delegate(float newVal)
			{
				volumes[capturedKey] = newVal;
				UpdateValueDisplay(capturedValueText, capturedValueTmp, newVal);
			};
			((UnityEvent<float>)(object)componentInChildren.onValueChanged).AddListener(UnityAction<float>.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<NetworkRunner>())
			{
				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<VoiceNetworkObject>.op_Implicit(Resources.FindObjectsOfTypeAll<VoiceNetworkObject>());
		}
		catch (Exception ex2)
		{
			((BasePlugin)this).Log.LogError((object)("VoiceNetworkObject lookup failed: " + ex2.Message));
			return;
		}
		HashSet<string> hashSet = new HashSet<string>();
		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<AudioSource>();
				}
				catch
				{
					goto end_IL_00b8;
				}
				if ((Object)(object)component == (Object)null)
				{
					continue;
				}
				NetworkObject val3 = null;
				try
				{
					val3 = ((Component)val2).GetComponent<NetworkObject>();
				}
				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<string> list = new List<string>();
		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<PlayerManager>();
						if ((Object)(object)val == (Object)null)
						{
							val = ((Component)val2).GetComponentInChildren<PlayerManager>();
						}
					}
				}
				catch
				{
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				try
				{
					if ((Object)(object)no != (Object)null)
					{
						val = ((Component)no).GetComponent<PlayerManager>();
					}
				}
				catch
				{
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				try
				{
					val = ((Component)vno).GetComponent<PlayerManager>();
				}
				catch
				{
				}
			}
			if ((Object)(object)val == (Object)null && (Object)(object)no != (Object)null)
			{
				try
				{
					val = ((Component)no).GetComponentInChildren<PlayerManager>();
				}
				catch
				{
				}
			}
			if ((Object)(object)val == (Object)null && (Object)(object)no != (Object)null)
			{
				try
				{
					val = ((Component)no).GetComponentInParent<PlayerManager>();
				}
				catch
				{
				}
			}
			if ((Object)(object)val == (Object)null && (Object)(object)no != (Object)null)
			{
				try
				{
					PlayerRef inputAuthority2 = no.InputAuthority;
					foreach (PlayerManager item in Resources.FindObjectsOfTypeAll<PlayerManager>())
					{
						if ((Object)(object)item == (Object)null)
						{
							continue;
						}
						NetworkObject val3 = null;
						try
						{
							val3 = ((Component)item).GetComponent<NetworkObject>();
						}
						catch
						{
						}
						if ((Object)(object)val3 == (Object)null)
						{
							try
							{
								val3 = ((Component)item).GetComponentInParent<NetworkObject>();
							}
							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<string> list = new List<string>();
			foreach (Text componentsInChild in go.GetComponentsInChildren<Text>(true))
			{
				if ((Object)(object)componentsInChild != (Object)null && !string.IsNullOrWhiteSpace(componentsInChild.text))
				{
					list.Add(componentsInChild.text);
				}
			}
			try
			{
				foreach (TMP_Text componentsInChild2 in go.GetComponentsInChildren<TMP_Text>(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));
			}
		}
	}
}