Decompiled source of Ambient Muter v2.1.0

AmbientMuter.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FistVR;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")]
[assembly: AssemblyCompany("AmbientMuter")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("AmbientMuter")]
[assembly: AssemblyTitle("AmbientMuter")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace AmbientMuter;

[BepInPlugin("h3vr.invent60.ambientmuter", "AmbientMuter", "2.1.0")]
public class AmbientMuterPlugin : BaseUnityPlugin
{
	private ConfigEntry<bool> _cfgMuteMusic;

	private ConfigEntry<bool> _cfgMuteAmbient;

	private ConfigEntry<float> _cfgSweepDelay;

	private ConfigEntry<bool> _cfgKeepFootsteps;

	private ConfigEntry<bool> _cfgKeepInteractive;

	private ConfigEntry<bool> _cfgMutePlayerHurt;

	internal static ManualLogSource Log;

	private Harmony _harmony;

	private bool _calibrated;

	private int _calibrationAttempts;

	private AmbientMuterRunner _runner;

	internal static bool IsU120Plus;

	private void Awake()
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		BindConfig();
		IsU120Plus = (object)AccessTools.TypeByName("FistVR.VaultFileDisplayMode") == null;
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[AmbientMuter] H3VR 1.0/U120+ install detected: " + IsU120Plus));
		SceneManager.sceneLoaded += OnSceneLoaded;
		_harmony = new Harmony("h3vr.invent60.ambientmuter");
		_harmony.PatchAll(typeof(CursedLLPitchFix));
		try
		{
			_harmony.PatchAll(typeof(MagBoopTimerFix));
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[AmbientMuter] MagBoop slow-mo timer fix applied.");
		}
		catch
		{
		}
		_runner = ((Component)this).gameObject.AddComponent<AmbientMuterRunner>();
		_runner.Log = Log;
		((BaseUnityPlugin)this).Logger.LogInfo((object)"[AmbientMuter] Loaded.");
	}

	private void BindConfig()
	{
		_cfgMuteMusic = ((BaseUnityPlugin)this).Config.Bind<bool>("Music", "Mute map music", true, "Zeros the FMOD music bus every frame, silencing background music in all maps (TNH, outdoor assault maps, etc.). Overrides any other mod that sets music volume.");
		_cfgMuteAmbient = ((BaseUnityPlugin)this).Config.Bind<bool>("Ambient", "Mute ambient sounds", true, "Mutes map ambient AudioSources (wind loops, propaganda speakers, environment drones). Applied once per scene load after the sweep delay.");
		_cfgSweepDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Ambient", "Sweep delay seconds", 4f, "Seconds to wait after scene load before sweeping ambient sources. Increase if some ambient sounds are not being caught. (0 to 30)");
		_cfgKeepInteractive = ((BaseUnityPlugin)this).Config.Bind<bool>("Ambient", "Keep interactive object sounds", true, "Buttons, levers, switches, and other interactive map objects keep their sounds. Disable to also silence these.");
		_cfgKeepFootsteps = ((BaseUnityPlugin)this).Config.Bind<bool>("Player", "Keep footstep mod sounds", true, "Footstep mods (PlayerFootsteps, AlyxFootsteps, EFT Frame, BF4 Footsteps) are not silenced. Disable to mute footstep sounds too.");
		_cfgMutePlayerHurt = ((BaseUnityPlugin)this).Config.Bind<bool>("Player", "Mute player hurt sounds", false, "Mutes pain and impact sounds when the player takes damage.");
	}

	private void Update()
	{
		float timeScale = Time.timeScale;
		bool flag = timeScale < 0.9999f && timeScale > 0.001f;
		bool wasInSlowMo = CursedLLPitchFix.WasInSlowMo;
		if (flag)
		{
			CursedLLPitchFix.LastSlowTimeScale = timeScale;
		}
		CursedLLPitchFix.CachedTimeScale = timeScale;
		CursedLLPitchFix.WasInSlowMo = flag;
		if (wasInSlowMo && !flag)
		{
			CursedLLPitchFix.RestoreScaledSources();
		}
		if (CursedLLPitchFix.PatcherDetected && !_calibrated && _calibrationAttempts < 5 && Time.timeScale < 0.99f && Time.timeScale > 0.001f)
		{
			_calibrationAttempts++;
			MeasurePitchCompound();
			_calibrated = CursedLLPitchFix.Initialized;
		}
		if (_cfgMuteMusic.Value)
		{
			if (IsU120Plus)
			{
				MusicBusMuter.TryMute();
			}
			else
			{
				LegacyMusicBusMuter.TryMute();
			}
		}
	}

	private void MeasurePitchCompound()
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Expected O, but got Unknown
		try
		{
			float cachedTimeScale = CursedLLPitchFix.CachedTimeScale;
			if (cachedTimeScale <= 0.001f || cachedTimeScale >= 0.999f)
			{
				return;
			}
			GameObject val = new GameObject("__AMCalib");
			AudioSource val2 = val.AddComponent<AudioSource>();
			val2.volume = 0f;
			val2.mute = true;
			val2.pitch = 1f;
			float pitch = val2.pitch;
			Object.Destroy((Object)(object)val);
			if (pitch <= 0f)
			{
				return;
			}
			float num = Mathf.Log(cachedTimeScale);
			float num2 = Mathf.Log(pitch);
			if (!(Mathf.Abs(num) < 0.0001f))
			{
				int num3 = Mathf.RoundToInt(num2 / num);
				if (num3 > 0)
				{
					CursedLLPitchFix.CompoundLevels = num3;
					CursedLLPitchFix.ExtraCompounders = Mathf.Max(0, num3 - 1);
					CursedLLPitchFix.CursedActive = true;
					CursedLLPitchFix.Initialized = true;
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"[AmbientMuter] CursedLL calibrated: {num3} compound level(s), {CursedLLPitchFix.ExtraCompounders} extra (ts={cachedTimeScale:F3})");
				}
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("[AmbientMuter] CursedLL calibration error: " + ex.Message));
		}
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		if (IsU120Plus)
		{
			MusicBusMuter.Reset();
		}
		else
		{
			LegacyMusicBusMuter.Reset();
		}
		if (_cfgMuteAmbient.Value)
		{
			((MonoBehaviour)_runner).Invoke("RunMuteAmbient", _cfgSweepDelay.Value);
		}
	}

	private void MuteAmbientSources()
	{
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		bool value = _cfgKeepFootsteps.Value;
		bool value2 = _cfgKeepInteractive.Value;
		bool value3 = _cfgMutePlayerHurt.Value;
		int num = 0;
		int num2 = 0;
		AudioSource[] array = Object.FindObjectsOfType<AudioSource>();
		foreach (AudioSource val in array)
		{
			if ((Object)(object)((Component)val).GetComponent<FVRPooledAudioSource>() != (Object)null)
			{
				num2++;
				continue;
			}
			if (value)
			{
				Scene scene = ((Component)val).gameObject.scene;
				if (((Scene)(ref scene)).name == "DontDestroyOnLoad")
				{
					num2++;
					continue;
				}
			}
			if (value2 && (Object)(object)((Component)val).GetComponentInParent<FVRInteractiveObject>() != (Object)null)
			{
				num2++;
				continue;
			}
			if (!value2 && (Object)(object)((Component)val).GetComponentInParent<FVRPhysicalObject>() != (Object)null)
			{
				num2++;
				continue;
			}
			if ((Object)(object)((Component)val).GetComponentInParent<Sosig>() != (Object)null)
			{
				num2++;
				continue;
			}
			FVRMovementManager componentInParent = ((Component)val).GetComponentInParent<FVRMovementManager>();
			if ((Object)(object)componentInParent != (Object)null && !value3)
			{
				num2++;
				continue;
			}
			if (value && (Object)(object)((Component)val).GetComponentInParent<BaseUnityPlugin>() != (Object)null)
			{
				num2++;
				continue;
			}
			val.mute = true;
			num++;
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)$"[AmbientMuter] Scene sweep done. Muted: {num}, Kept: {num2}");
	}

	internal void RunMuteAmbientSources()
	{
		MuteAmbientSources();
	}
}
internal static class MusicBusMuter
{
	private static object _bus;

	private static MethodInfo _setVolume;

	private static bool _obtained;

	private static bool _unavailable;

	internal static void TryMute()
	{
		if (_unavailable || (!_obtained && !TryGetBus()))
		{
			return;
		}
		try
		{
			_setVolume.Invoke(_bus, new object[1] { 0f });
		}
		catch
		{
			_obtained = false;
		}
	}

	private static bool TryGetBus()
	{
		try
		{
			Type type = Type.GetType("FMODUnity.RuntimeManager, Assembly-CSharp-firstpass");
			if ((object)type == null)
			{
				_unavailable = true;
				return false;
			}
			MethodInfo method = type.GetMethod("GetBus", new Type[1] { typeof(string) });
			if ((object)method == null)
			{
				_unavailable = true;
				return false;
			}
			_bus = method.Invoke(null, new object[1] { "bus:/Music" });
			if (_bus == null)
			{
				_unavailable = true;
				return false;
			}
			_setVolume = _bus.GetType().GetMethod("setVolume", new Type[1] { typeof(float) });
			if ((object)_setVolume == null)
			{
				_unavailable = true;
				return false;
			}
			_obtained = true;
			return true;
		}
		catch
		{
			_unavailable = true;
			return false;
		}
	}

	internal static void Reset()
	{
		_obtained = false;
	}
}
internal static class LegacyMusicBusMuter
{
	private static object _bus;

	private static MethodInfo _setVolume;

	private static bool _obtained;

	private static bool _unavailable;

	internal static void TryMute()
	{
		if (_unavailable || (!_obtained && !TryGetBus()))
		{
			return;
		}
		try
		{
			_setVolume.Invoke(_bus, new object[1] { 0f });
		}
		catch
		{
			_obtained = false;
		}
	}

	private static bool TryGetBus()
	{
		try
		{
			Type type = Type.GetType("FMODUnity.RuntimeManager, Assembly-CSharp-firstpass");
			if ((object)type == null)
			{
				_unavailable = true;
				return false;
			}
			MethodInfo method = type.GetMethod("GetBus", new Type[1] { typeof(string) });
			if ((object)method == null)
			{
				_unavailable = true;
				return false;
			}
			_bus = method.Invoke(null, new object[1] { "bus:/Music" });
			if (_bus == null)
			{
				_unavailable = true;
				return false;
			}
			_setVolume = _bus.GetType().GetMethod("setVolume", new Type[1] { typeof(float) });
			if ((object)_setVolume == null)
			{
				_unavailable = true;
				return false;
			}
			_obtained = true;
			return true;
		}
		catch
		{
			_unavailable = true;
			return false;
		}
	}

	internal static void Reset()
	{
		_obtained = false;
	}
}
internal sealed class AmbientMuterRunner : MonoBehaviour
{
	internal ManualLogSource Log;

	private AmbientMuterPlugin _plugin;

	private void Awake()
	{
		_plugin = ((Component)this).GetComponent<AmbientMuterPlugin>();
	}

	private void Start()
	{
		((MonoBehaviour)this).StartCoroutine(DetectCursedLL());
	}

	internal void RunMuteAmbient()
	{
		_plugin?.RunMuteAmbientSources();
	}

	internal IEnumerator DetectCursedLL()
	{
		yield return null;
		try
		{
			PropertyInfo property = typeof(AudioSource).GetProperty("pitch");
			if ((object)property == null)
			{
				yield break;
			}
			MethodInfo setMethod = property.GetSetMethod();
			if ((object)setMethod == null)
			{
				yield break;
			}
			Patches patchInfo = Harmony.GetPatchInfo((MethodBase)setMethod);
			if (patchInfo == null)
			{
				yield break;
			}
			int num = 0;
			foreach (Patch prefix in patchInfo.Prefixes)
			{
				if (!string.Equals(prefix.owner, "h3vr.invent60.ambientmuter", StringComparison.OrdinalIgnoreCase))
				{
					Log.LogInfo((object)("[AmbientMuter] pitch-setter prefix: " + prefix.owner));
					num++;
				}
			}
			foreach (Patch postfix in patchInfo.Postfixes)
			{
				if (!string.Equals(postfix.owner, "h3vr.invent60.ambientmuter", StringComparison.OrdinalIgnoreCase))
				{
					Log.LogInfo((object)("[AmbientMuter] pitch-setter postfix: " + postfix.owner));
					num++;
				}
			}
			foreach (Patch transpiler in patchInfo.Transpilers)
			{
				if (!string.Equals(transpiler.owner, "h3vr.invent60.ambientmuter", StringComparison.OrdinalIgnoreCase))
				{
					Log.LogInfo((object)("[AmbientMuter] pitch-setter transpiler: " + transpiler.owner));
					num++;
				}
			}
			if (num > 0)
			{
				CursedLLPitchFix.PatcherDetected = true;
				Log.LogInfo((object)("[AmbientMuter] pitch-setter patcher(s) found (" + num + "). Calibration deferred to first slow-mo."));
			}
			else
			{
				Log.LogInfo((object)"[AmbientMuter] CursedLL pitch fix: not needed");
			}
		}
		catch
		{
		}
	}
}
[HarmonyPatch]
public static class MagBoopTimerFix
{
	private static MethodBase TargetMethod()
	{
		return AccessTools.Method("MagBoop.MagTriggerScript:Update", (Type[])null, (Type[])null);
	}

	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		MethodInfo getMethod = typeof(Time).GetProperty("fixedUnscaledDeltaTime").GetGetMethod();
		List<CodeInstruction> list = instructions.ToList();
		for (int i = 0; i < list.Count; i++)
		{
			if (list[i].opcode == OpCodes.Call && list[i].operand is MethodInfo methodInfo && (object)methodInfo.DeclaringType == typeof(Time) && methodInfo.Name == "get_fixedDeltaTime")
			{
				list[i].operand = getMethod;
			}
		}
		return list;
	}
}
public static class CursedLLPitchFix
{
	public static bool PatcherDetected = false;

	public static bool Initialized = false;

	public static bool CursedActive = false;

	public static int CompoundLevels = 0;

	public static int ExtraCompounders = 0;

	public static float CachedTimeScale = 1f;

	public static bool WasInSlowMo = false;

	public static float LastSlowTimeScale = 1f;

	private static readonly HashSet<AudioSource> _scaledSources = new HashSet<AudioSource>();

	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	[HarmonyPostfix]
	public static void CompensatePitchRead(ref float __result)
	{
		if (Initialized && CursedActive && !(CachedTimeScale >= 0.9999f) && !(CachedTimeScale <= 0.001f))
		{
			__result /= CachedTimeScale;
		}
	}

	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	[HarmonyPrefix]
	public static void PreCompensatePitchSet(AudioSource __instance, ref float value)
	{
		if (!Initialized || !CursedActive || CachedTimeScale >= 0.9999f || CachedTimeScale <= 0.001f)
		{
			return;
		}
		if (ExtraCompounders > 0)
		{
			for (int i = 0; i < ExtraCompounders; i++)
			{
				value /= CachedTimeScale;
			}
		}
		if ((Object)(object)__instance != (Object)null)
		{
			_scaledSources.Add(__instance);
		}
	}

	public static void RestoreScaledSources()
	{
		float lastSlowTimeScale = LastSlowTimeScale;
		if (_scaledSources.Count == 0)
		{
			return;
		}
		if (lastSlowTimeScale <= 0.001f || lastSlowTimeScale >= 0.9999f)
		{
			_scaledSources.Clear();
			return;
		}
		foreach (AudioSource scaledSource in _scaledSources)
		{
			if (!((Object)(object)scaledSource == (Object)null))
			{
				scaledSource.pitch /= lastSlowTimeScale;
			}
		}
		_scaledSources.Clear();
	}
}