Decompiled source of ReviveVoice v1.0.3

BepInEx/plugins/ReviveVoice/ReviveVoice.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using Agents;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Player;
using SNetwork;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.3.0")]
[module: UnverifiableCode]
namespace HawChow.ReviveVoice;

[BepInPlugin("HawChow.ReviveVoice", "ReviveVoice", "1.0.3")]
public sealed class Plugin : BasePlugin
{
	private const string HostObjectName = "HawChow.ReviveVoice.Host";

	internal static Plugin Instance;

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<float> Volume;

	internal static ReviveAudioHost Host;

	private static GameObject _hostObject;

	private static AudioClip _clip;

	private static bool _loggedHostRebuild;

	private static bool _loggedClipReload;

	public override void Load()
	{
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Expected O, but got Unknown
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Expected O, but got Unknown
		//IL_0189: Unknown result type (might be due to invalid IL or missing references)
		//IL_018f: Expected O, but got Unknown
		Instance = this;
		Enabled = ((BasePlugin)this).Config.Bind<bool>("General", "Enabled", true, "Play the revive voice on every client that receives the game's replicated revive state.");
		Volume = ((BasePlugin)this).Config.Bind<float>("Audio", "Volume", 0.5f, new ConfigDescription("Voice playback volume, from 0.0 to 1.0. Lowered from 0.85 in 1.0.2 because the clip is a loud shout.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		ClassInjector.RegisterTypeInIl2Cpp<ReviveAudioHost>();
		Harmony val = new Harmony("HawChow.ReviveVoice");
		val.PatchAll(typeof(Plugin).Assembly);
		LogPatchReceipt(val, AccessTools.Method(typeof(Interact_Revive), "SyncReviverState", (Type[])null, (Type[])null), "Interact_Revive.SyncReviverState");
		LogPatchReceipt(val, AccessTools.Method(typeof(PLOC_Downed), "OnPlayerRevived", (Type[])null, (Type[])null), "PLOC_Downed.OnPlayerRevived");
		_clip = ReviveAudioAsset.Load(Assembly.GetExecutingAssembly(), ((BasePlugin)this).Log);
		Host = (((Object)(object)_clip != (Object)null) ? CreateHost() : null);
		if ((Object)(object)_clip == (Object)null)
		{
			((BasePlugin)this).Log.LogWarning((object)"Embedded revive voice is unavailable at load; hooks stay installed and the clip is reloaded on the first rescue.");
		}
		else if ((Object)(object)Host == (Object)null)
		{
			((BasePlugin)this).Log.LogWarning((object)"Revive audio host could not be created at load; hooks stay installed and the host is rebuilt on the first rescue.");
		}
		string text = (((Object)(object)_clip != (Object)null) ? $"{_clip.length:0.00}s" : "unavailable");
		ManualLogSource log = ((BasePlugin)this).Log;
		bool flag = default(bool);
		BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(103, 6, ref flag);
		if (flag)
		{
			((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("ReviveVoice");
			((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" ");
			((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("1.0.3");
			((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" loaded; replicated rescue audio enabled=");
			((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<bool>(Enabled.Value);
			((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("; volume=");
			((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(Volume.Value, "0.00");
			((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("; embedded clip=");
			((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(text);
			((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("; host='");
			((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("HawChow.ReviveVoice.Host");
			((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' pinned across scene loads.");
		}
		log.LogInfo(val2);
	}

	internal static ReviveAudioHost EnsureHost()
	{
		if ((Object)(object)_clip == (Object)null)
		{
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			Plugin instance = Instance;
			_clip = ReviveAudioAsset.Load(executingAssembly, (instance != null) ? ((BasePlugin)instance).Log : null);
			if ((Object)(object)_clip == (Object)null)
			{
				return null;
			}
			if (!_loggedClipReload)
			{
				_loggedClipReload = true;
				Plugin instance2 = Instance;
				if (instance2 != null)
				{
					((BasePlugin)instance2).Log.LogWarning((object)"[HOST] embedded revive clip had to be reloaded; rescue audio rearmed.");
				}
			}
			if ((Object)(object)Host != (Object)null)
			{
				Host.Initialize(_clip);
			}
		}
		if ((Object)(object)Host != (Object)null)
		{
			return Host;
		}
		ReviveAudioHost reviveAudioHost = CreateHost();
		if ((Object)(object)reviveAudioHost == (Object)null)
		{
			return null;
		}
		Host = reviveAudioHost;
		if (!_loggedHostRebuild)
		{
			_loggedHostRebuild = true;
			Plugin instance3 = Instance;
			if (instance3 != null)
			{
				((BasePlugin)instance3).Log.LogWarning((object)"[HOST] revive audio host was missing at revive time and has been recreated; rescue audio continues from here.");
			}
		}
		return Host;
	}

	private static ReviveAudioHost CreateHost()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Expected O, but got Unknown
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Expected O, but got Unknown
		try
		{
			if ((Object)(object)_hostObject == (Object)null)
			{
				_hostObject = new GameObject("HawChow.ReviveVoice.Host");
				((Object)_hostObject).hideFlags = (HideFlags)61;
				Object.DontDestroyOnLoad((Object)(object)_hostObject);
			}
			ReviveAudioHost reviveAudioHost = _hostObject.GetComponent<ReviveAudioHost>();
			if ((Object)(object)reviveAudioHost == (Object)null)
			{
				reviveAudioHost = _hostObject.AddComponent<ReviveAudioHost>();
			}
			reviveAudioHost.Initialize(_clip);
			return reviveAudioHost;
		}
		catch (Exception ex)
		{
			Plugin instance = Instance;
			if (instance != null)
			{
				ManualLogSource log = ((BasePlugin)instance).Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(37, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Revive audio host creation failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.GetType().Name);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogError(val);
			}
			return null;
		}
	}

	private void LogPatchReceipt(Harmony harmony, MethodBase target, string label)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Expected O, but got Unknown
		bool flag = default(bool);
		if (target == null)
		{
			ManualLogSource log = ((BasePlugin)this).Log;
			BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(25, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[PATCH] ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(label);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": target missing.");
			}
			log.LogError(val);
			return;
		}
		Patches patchInfo = Harmony.GetPatchInfo(target);
		int num = patchInfo?.Prefixes.Count ?? 0;
		int num2 = patchInfo?.Postfixes.Count ?? 0;
		ManualLogSource log2 = ((BasePlugin)this).Log;
		BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(29, 4, ref flag);
		if (flag)
		{
			((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[PATCH] ");
			((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(label);
			((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": pre=");
			((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(num);
			((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(",post=");
			((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(num2);
			((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("; owner=");
			((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("HawChow.ReviveVoice");
			((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(".");
		}
		log2.LogInfo(val2);
	}
}
internal static class PluginInfo
{
	internal const string Guid = "HawChow.ReviveVoice";

	internal const string Name = "ReviveVoice";

	internal const string Version = "1.0.3";
}
internal static class ReviveAudioAsset
{
	private const string ResourceName = "ReviveVoice.Audio.revive-voice.wav";

	internal static AudioClip Load(Assembly assembly, ManualLogSource log)
	{
		//IL_0239: Unknown result type (might be due to invalid IL or missing references)
		//IL_0240: Expected O, but got Unknown
		try
		{
			using Stream stream = assembly.GetManifestResourceStream("ReviveVoice.Audio.revive-voice.wav");
			if (stream == null)
			{
				throw new InvalidDataException("Missing resource ReviveVoice.Audio.revive-voice.wav.");
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			byte[] array = memoryStream.ToArray();
			if (array.Length < 12 || Encoding.ASCII.GetString(array, 0, 4) != "RIFF" || Encoding.ASCII.GetString(array, 8, 4) != "WAVE")
			{
				throw new InvalidDataException("Embedded revive voice has an invalid RIFF/WAVE header.");
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = -1;
			int num6 = 0;
			int num7 = 12;
			while (num7 <= array.Length - 8)
			{
				string text = Encoding.ASCII.GetString(array, num7, 4);
				int num8 = BitConverter.ToInt32(array, num7 + 4);
				int num9 = num7 + 8;
				if (num8 < 0 || num9 > array.Length - num8)
				{
					throw new InvalidDataException("Malformed WAV chunk length.");
				}
				if (text == "fmt ")
				{
					if (num8 < 16)
					{
						throw new InvalidDataException("WAV fmt chunk is incomplete.");
					}
					num = BitConverter.ToUInt16(array, num9);
					num2 = BitConverter.ToUInt16(array, num9 + 2);
					num3 = BitConverter.ToInt32(array, num9 + 4);
					num4 = BitConverter.ToUInt16(array, num9 + 14);
				}
				else if (text == "data")
				{
					num5 = num9;
					num6 = num8;
				}
				long num10 = (long)num9 + (long)num8 + (num8 & 1);
				if (num10 > array.Length)
				{
					throw new InvalidDataException("WAV chunk exceeds the file length.");
				}
				num7 = (int)num10;
			}
			if (num != 1 || num2 != 1 || num3 < 8000 || num3 > 48000 || num4 != 16 || num5 < 0 || num6 <= 0 || (num6 & 1) != 0)
			{
				throw new InvalidDataException("Revive voice must be mono PCM16 at 8–48 kHz.");
			}
			int num11 = num6 / 2;
			float[] array2 = new float[num11];
			for (int i = 0; i < num11; i++)
			{
				int num12 = num5 + i * 2;
				short num13 = (short)(array[num12] | (array[num12 + 1] << 8));
				array2[i] = (float)num13 / 32768f;
			}
			AudioClip val = AudioClip.Create("ReviveVoice", num11, num2, num3, false);
			if ((Object)(object)val == (Object)null || !val.SetData(new Il2CppStructArray<float>(array2), 0))
			{
				throw new InvalidOperationException("Unity failed to create the revive voice AudioClip.");
			}
			return val;
		}
		catch (Exception ex)
		{
			bool flag = default(bool);
			BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(37, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Revive voice resource load failed: ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.GetType().Name);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
			}
			log.LogError(val2);
			return null;
		}
	}
}
public sealed class ReviveAudioHost : MonoBehaviour
{
	private struct FinishedClip
	{
		internal ulong Target;

		internal AudioSource Source;
	}

	private const double StopGraceSeconds = 0.35;

	private readonly RevivePlaybackLedger _ledger = new RevivePlaybackLedger();

	private readonly Dictionary<ulong, AudioSource> _active = new Dictionary<ulong, AudioSource>();

	private readonly Dictionary<ulong, double> _pendingStop = new Dictionary<ulong, double>();

	private readonly List<FinishedClip> _finishing = new List<FinishedClip>();

	private AudioClip _clip;

	private bool _warnedPlayback;

	public ReviveAudioHost(IntPtr pointer)
		: base(pointer)
	{
	}

	internal void Initialize(AudioClip clip)
	{
		_clip = clip;
	}

	internal void Begin(ulong target, ulong reviver, string targetName, string reviverName)
	{
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Expected O, but got Unknown
		PlaybackTransition transition = _ledger.Begin(target, reviver, Time.realtimeSinceStartup);
		if (transition.Command != PlaybackCommand.None)
		{
			_pendingStop.Remove(target);
		}
		Apply(transition);
		if (transition.Command == PlaybackCommand.Start || transition.Command == PlaybackCommand.Restart)
		{
			ManualLogSource log = ((BasePlugin)Plugin.Instance).Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(53, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[REVIVE] started; reviver=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(reviverName);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; target=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(targetName);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; replicated=true.");
			}
			log.LogInfo(val);
		}
	}

	internal void Release(ulong target, ulong reviver, bool targetIsAlive)
	{
		PlaybackTransition transition = _ledger.Release(target, reviver, targetIsAlive);
		if (transition.Command != PlaybackCommand.Stop)
		{
			Apply(transition);
		}
		else
		{
			_pendingStop[target] = (double)Time.realtimeSinceStartup + 0.35;
		}
	}

	internal void Complete(ulong target)
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Expected O, but got Unknown
		_pendingStop.Remove(target);
		PlaybackTransition transition = _ledger.Complete(target);
		Apply(transition);
		if (transition.Command == PlaybackCommand.Complete)
		{
			ManualLogSource log = ((BasePlugin)Plugin.Instance).Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(59, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[REVIVE] success; targetLookup=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<ulong>(target);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; audio=finish-current-clip.");
			}
			log.LogInfo(val);
		}
	}

	private void Update()
	{
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Expected O, but got Unknown
		try
		{
			double now = Time.realtimeSinceStartup;
			List<PlaybackTransition> list = _ledger.Expire(now);
			for (int i = 0; i < list.Count; i++)
			{
				Apply(list[i]);
			}
			FlushPendingStops(now);
			for (int num = _finishing.Count - 1; num >= 0; num--)
			{
				AudioSource source = _finishing[num].Source;
				if (!((Object)(object)source != (Object)null) || !source.isPlaying)
				{
					DestroySource(source);
					_finishing.RemoveAt(num);
				}
			}
			if (Plugin.Enabled != null && !Plugin.Enabled.Value)
			{
				StopActiveSessions();
			}
		}
		catch (Exception ex)
		{
			if (_warnedPlayback)
			{
				return;
			}
			_warnedPlayback = true;
			Plugin instance = Plugin.Instance;
			if (instance != null)
			{
				ManualLogSource log = ((BasePlugin)instance).Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(30, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Revive audio update failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.GetType().Name);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogError(val);
			}
		}
	}

	private void Apply(PlaybackTransition transition)
	{
		switch (transition.Command)
		{
		case PlaybackCommand.None:
			break;
		case PlaybackCommand.Start:
			StartSource(transition.Target);
			break;
		case PlaybackCommand.Restart:
			StopSource(transition.Target);
			StartSource(transition.Target);
			break;
		case PlaybackCommand.Stop:
			StopSource(transition.Target);
			break;
		case PlaybackCommand.Complete:
		{
			if (_active.TryGetValue(transition.Target, out var value))
			{
				_active.Remove(transition.Target);
				_finishing.Add(new FinishedClip
				{
					Target = transition.Target,
					Source = value
				});
			}
			break;
		}
		}
	}

	private void FlushPendingStops(double now)
	{
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Expected O, but got Unknown
		if (_pendingStop.Count == 0)
		{
			return;
		}
		List<ulong> list = null;
		foreach (KeyValuePair<ulong, double> item in _pendingStop)
		{
			if (!(now < item.Value))
			{
				if (list == null)
				{
					list = new List<ulong>();
				}
				list.Add(item.Key);
			}
		}
		if (list == null)
		{
			return;
		}
		bool flag = default(bool);
		for (int i = 0; i < list.Count; i++)
		{
			_pendingStop.Remove(list[i]);
			StopSource(list[i]);
			Plugin instance = Plugin.Instance;
			if (instance != null)
			{
				ManualLogSource log = ((BasePlugin)instance).Log;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(51, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[REVIVE] interrupted; targetLookup=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<ulong>(list[i]);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; audio=stopped.");
				}
				log.LogInfo(val);
			}
		}
	}

	private void StartSource(ulong target)
	{
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Expected O, but got Unknown
		if ((Object)(object)_clip == (Object)null)
		{
			if (!_warnedPlayback)
			{
				_warnedPlayback = true;
				Plugin instance = Plugin.Instance;
				if (instance != null)
				{
					((BasePlugin)instance).Log.LogError((object)"Revive voice playback skipped: this host has no embedded clip.");
				}
			}
			return;
		}
		try
		{
			StopFinishingFor(target);
			AudioSource val = ((Component)this).gameObject.AddComponent<AudioSource>();
			val.playOnAwake = false;
			val.loop = false;
			val.spatialBlend = 0f;
			val.volume = Mathf.Clamp01((Plugin.Volume != null) ? Plugin.Volume.Value : 0.5f);
			val.clip = _clip;
			val.Play();
			_active[target] = val;
		}
		catch (Exception ex)
		{
			if (_warnedPlayback)
			{
				return;
			}
			_warnedPlayback = true;
			Plugin instance2 = Plugin.Instance;
			if (instance2 != null)
			{
				ManualLogSource log = ((BasePlugin)instance2).Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(32, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Revive voice playback failed: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.GetType().Name);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
				}
				log.LogError(val2);
			}
		}
	}

	private void StopSource(ulong target)
	{
		if (_active.TryGetValue(target, out var value))
		{
			_active.Remove(target);
			DestroySource(value);
		}
	}

	private void StopFinishingFor(ulong target)
	{
		for (int num = _finishing.Count - 1; num >= 0; num--)
		{
			if (_finishing[num].Target == target)
			{
				DestroySource(_finishing[num].Source);
				_finishing.RemoveAt(num);
			}
		}
	}

	private static void DestroySource(AudioSource source)
	{
		if ((Object)(object)source == (Object)null)
		{
			return;
		}
		try
		{
			source.Stop();
			Object.Destroy((Object)(object)source);
		}
		catch
		{
		}
	}

	private void StopActiveSessions()
	{
		_pendingStop.Clear();
		List<PlaybackTransition> list = _ledger.Clear();
		for (int i = 0; i < list.Count; i++)
		{
			Apply(list[i]);
		}
	}

	private void OnDestroy()
	{
		_pendingStop.Clear();
		StopActiveSessions();
		for (int i = 0; i < _finishing.Count; i++)
		{
			DestroySource(_finishing[i].Source);
		}
		_finishing.Clear();
		_clip = null;
	}
}
internal static class RevivePatchTargets
{
	internal static IEnumerable<MethodBase> Methods(Type type, string name)
	{
		MethodBase methodBase = AccessTools.Method(type, name, (Type[])null, (Type[])null);
		if (methodBase == null)
		{
			throw new MissingMethodException(type.FullName, name);
		}
		yield return methodBase;
	}
}
[HarmonyPatch]
internal static class ReviveStatePatch
{
	private const int UnresolvedWarnLimit = 5;

	private static int _unresolvedWarns;

	private static IEnumerable<MethodBase> TargetMethods()
	{
		return RevivePatchTargets.Methods(typeof(Interact_Revive), "SyncReviverState");
	}

	[HarmonyPostfix]
	private static void Postfix(pPlayerReviveState __0)
	{
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Expected O, but got Unknown
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Expected O, but got Unknown
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		bool flag3 = default(bool);
		try
		{
			if (Plugin.Enabled == null || !Plugin.Enabled.Value)
			{
				return;
			}
			ReviveAudioHost reviveAudioHost = Plugin.EnsureHost();
			if ((Object)(object)reviveAudioHost == (Object)null)
			{
				return;
			}
			PlayerAgent val = default(PlayerAgent);
			bool flag = ((pPlayerAgent)(ref __0.TargetPlayer)).TryGet(ref val) && (Object)(object)val != (Object)null && (Object)(object)val.Owner != (Object)null;
			PlayerAgent val2 = default(PlayerAgent);
			bool flag2 = ((pPlayerAgent)(ref __0.SourcePlayer)).TryGet(ref val2) && (Object)(object)val2 != (Object)null && (Object)(object)val2.Owner != (Object)null;
			if (!flag || !flag2)
			{
				if (_unresolvedWarns >= 5)
				{
					return;
				}
				_unresolvedWarns++;
				Plugin instance = Plugin.Instance;
				if (instance != null)
				{
					ManualLogSource log = ((BasePlugin)instance).Log;
					BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(155, 4, ref flag3);
					if (flag3)
					{
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[DIAG] revive state could not resolve its players (target=");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<bool>(flag);
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(", reviver=");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<bool>(flag2);
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(", active=");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<bool>(__0.State);
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("); rescue audio skipped for that packet; diagnostics stop after ");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(5);
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" such packets.");
					}
					log.LogWarning(val3);
				}
			}
			else
			{
				ulong lookup = val.Owner.Lookup;
				ulong lookup2 = val2.Owner.Lookup;
				if (__0.State)
				{
					reviveAudioHost.Begin(lookup, lookup2, val.Owner.GetName(), val2.Owner.GetName());
				}
				else
				{
					reviveAudioHost.Release(lookup, lookup2, val.m_alive);
				}
			}
		}
		catch (Exception ex)
		{
			Plugin instance = Plugin.Instance;
			if (instance != null)
			{
				ManualLogSource log2 = ((BasePlugin)instance).Log;
				BepInExErrorLogInterpolatedStringHandler val4 = new BepInExErrorLogInterpolatedStringHandler(35, 2, ref flag3);
				if (flag3)
				{
					((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("Revive state observation failed: ");
					((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(ex.GetType().Name);
					((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(": ");
					((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(ex.Message);
				}
				log2.LogError(val4);
			}
		}
	}
}
[HarmonyPatch]
internal static class ReviveCompletionPatch
{
	private static IEnumerable<MethodBase> TargetMethods()
	{
		return RevivePatchTargets.Methods(typeof(PLOC_Downed), "OnPlayerRevived");
	}

	[HarmonyPostfix]
	private static void Postfix(PLOC_Downed __instance)
	{
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Expected O, but got Unknown
		try
		{
			if (Plugin.Enabled == null || !Plugin.Enabled.Value)
			{
				return;
			}
			ReviveAudioHost reviveAudioHost = Plugin.EnsureHost();
			if (!((Object)(object)reviveAudioHost == (Object)null))
			{
				SNet_Player val = ((__instance != null && (Object)(object)((PLOC_Base)__instance).m_owner != (Object)null) ? ((PLOC_Base)__instance).m_owner.Owner : null);
				if ((Object)(object)val != (Object)null)
				{
					reviveAudioHost.Complete(val.Lookup);
				}
			}
		}
		catch (Exception ex)
		{
			Plugin instance = Plugin.Instance;
			if (instance != null)
			{
				ManualLogSource log = ((BasePlugin)instance).Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(40, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Revive completion observation failed: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.GetType().Name);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
				}
				log.LogError(val2);
			}
		}
	}
}
internal enum PlaybackCommand
{
	None,
	Start,
	Restart,
	Stop,
	Complete
}
internal readonly struct PlaybackTransition
{
	internal PlaybackCommand Command { get; }

	internal ulong Target { get; }

	internal ulong Reviver { get; }

	internal ulong PreviousReviver { get; }

	internal static PlaybackTransition None => new PlaybackTransition(PlaybackCommand.None, 0uL, 0uL, 0uL);

	internal PlaybackTransition(PlaybackCommand command, ulong target, ulong reviver, ulong previousReviver = 0uL)
	{
		Command = command;
		Target = target;
		Reviver = reviver;
		PreviousReviver = previousReviver;
	}
}
internal sealed class RevivePlaybackLedger
{
	private struct Session
	{
		internal ulong Reviver;

		internal double ExpiresAt;
	}

	internal const double ActiveTimeoutSeconds = 20.0;

	private readonly Dictionary<ulong, Session> _sessions = new Dictionary<ulong, Session>();

	internal int Count => _sessions.Count;

	internal PlaybackTransition Begin(ulong target, ulong reviver, double now)
	{
		if (target == 0L || reviver == 0L || target == reviver || !double.IsFinite(now))
		{
			return PlaybackTransition.None;
		}
		if (_sessions.TryGetValue(target, out var value))
		{
			if (value.Reviver == reviver)
			{
				value.ExpiresAt = now + 20.0;
				_sessions[target] = value;
				return PlaybackTransition.None;
			}
			_sessions[target] = new Session
			{
				Reviver = reviver,
				ExpiresAt = now + 20.0
			};
			return new PlaybackTransition(PlaybackCommand.Restart, target, reviver, value.Reviver);
		}
		_sessions.Add(target, new Session
		{
			Reviver = reviver,
			ExpiresAt = now + 20.0
		});
		return new PlaybackTransition(PlaybackCommand.Start, target, reviver, 0uL);
	}

	internal PlaybackTransition Release(ulong target, ulong reviver, bool targetIsAlive)
	{
		if (target == 0L || reviver == 0L || !_sessions.TryGetValue(target, out var value) || value.Reviver != reviver)
		{
			return PlaybackTransition.None;
		}
		_sessions.Remove(target);
		return new PlaybackTransition(targetIsAlive ? PlaybackCommand.Complete : PlaybackCommand.Stop, target, reviver, 0uL);
	}

	internal PlaybackTransition Complete(ulong target)
	{
		if (target == 0L || !_sessions.TryGetValue(target, out var value))
		{
			return PlaybackTransition.None;
		}
		_sessions.Remove(target);
		return new PlaybackTransition(PlaybackCommand.Complete, target, value.Reviver, 0uL);
	}

	internal List<PlaybackTransition> Expire(double now)
	{
		List<PlaybackTransition> list = new List<PlaybackTransition>();
		if (!double.IsFinite(now))
		{
			return list;
		}
		List<ulong> list2 = new List<ulong>();
		foreach (KeyValuePair<ulong, Session> session in _sessions)
		{
			if (!(now <= session.Value.ExpiresAt))
			{
				list2.Add(session.Key);
				list.Add(new PlaybackTransition(PlaybackCommand.Stop, session.Key, session.Value.Reviver, 0uL));
			}
		}
		for (int i = 0; i < list2.Count; i++)
		{
			_sessions.Remove(list2[i]);
		}
		return list;
	}

	internal List<PlaybackTransition> Clear()
	{
		List<PlaybackTransition> list = new List<PlaybackTransition>(_sessions.Count);
		foreach (KeyValuePair<ulong, Session> session in _sessions)
		{
			list.Add(new PlaybackTransition(PlaybackCommand.Stop, session.Key, session.Value.Reviver, 0uL));
		}
		_sessions.Clear();
		return list;
	}
}