Decompiled source of WipeSong v1.0.5

plugins/WipeSong/aj.WipeSong.dll

Decompiled 16 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Networking;
using WipeSong.Configuration;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("aj.WipeSong")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.5.0")]
[assembly: AssemblyInformationalVersion("1.0.5")]
[assembly: AssemblyProduct("WipeSong")]
[assembly: AssemblyTitle("aj.WipeSong")]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was created with the assistance of generative AI (code); see the README credits for which assets.")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic")]
[assembly: AssemblyVersion("1.0.5.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace WipeSong
{
	[HarmonyPatch]
	internal static class Patches
	{
		[HarmonyPatch(typeof(StartOfRound), "Start")]
		[HarmonyPostfix]
		private static void StartOfRoundStarted(StartOfRound __instance)
		{
			SongNet.Register();
			((Component)__instance).gameObject.AddComponent<SongPlayer>();
		}

		[HarmonyPatch(typeof(StartOfRound), "ShipLeaveAutomatically")]
		[HarmonyPrefix]
		private static void BeforeShipLeavesAutomatically(StartOfRound __instance, bool leavingOnMidnight, out bool __state)
		{
			__state = !leavingOnMidnight && !__instance.shipLeftAutomatically && !__instance.shipIsLeaving;
		}

		[HarmonyPatch(typeof(StartOfRound), "ShipLeaveAutomatically")]
		[HarmonyPostfix]
		private static void AfterShipLeavesAutomatically(StartOfRound __instance, bool __state)
		{
			if (__state && __instance.shipLeftAutomatically)
			{
				SongPlayer.Begin();
				SongNet.HostTellEveryone();
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "EndOfGameClientRpc")]
		[HarmonyPrefix]
		private static void EndOfDay()
		{
			SongPlayer.End();
		}
	}
	[BepInPlugin("WipeSong", "WipeSong", "1.0.5")]
	public class Plugin : BaseUnityPlugin
	{
		private const string SongFile = "Crazy.ogg";

		internal static ManualLogSource Logger;

		internal static AudioClip? Song;

		internal static PluginConfig BoundConfig { get; private set; }

		private void Awake()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			Logger = ((BaseUnityPlugin)this).Logger;
			BoundConfig = new PluginConfig(((BaseUnityPlugin)this).Config);
			Song = LoadSong(Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "Crazy.ogg"));
			new Harmony("WipeSong").PatchAll(typeof(Patches));
			Logger.LogInfo((object)"Plugin WipeSong is loaded!");
		}

		private static AudioClip? LoadSong(string path)
		{
			//IL_0046: 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_0067: Invalid comparison between Unknown and I4
			if (!File.Exists(path))
			{
				Logger.LogError((object)("Song not found at " + path + "; no music when the crew dies."));
				return null;
			}
			UnityWebRequest audioClip = UnityWebRequestMultimedia.GetAudioClip("file:///" + path.Replace('\\', '/'), (AudioType)14);
			try
			{
				((DownloadHandlerAudioClip)audioClip.downloadHandler).compressed = true;
				audioClip.SendWebRequest();
				while (!audioClip.isDone)
				{
				}
				if ((int)audioClip.result != 1)
				{
					Logger.LogError((object)("Couldn't load the song (" + audioClip.error + "); no music when the crew dies."));
					return null;
				}
				AudioClip content = DownloadHandlerAudioClip.GetContent(audioClip);
				((Object)content).name = "WipeSong";
				return content;
			}
			finally
			{
				((IDisposable)audioClip)?.Dispose();
			}
		}
	}
	internal static class SongNet
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static HandleNamedMessageDelegate <0>__OnStart;
		}

		private const string StartMessage = "aj.WipeSong.Start";

		private static CustomMessagingManager? Messages
		{
			get
			{
				if (!((Object)(object)NetworkManager.Singleton != (Object)null))
				{
					return null;
				}
				return NetworkManager.Singleton.CustomMessagingManager;
			}
		}

		internal static void Register()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			CustomMessagingManager messages = Messages;
			if (messages == null)
			{
				Plugin.Logger.LogWarning((object)"No network messaging; each player's song starts on their own game's cue only.");
				return;
			}
			object obj = <>O.<0>__OnStart;
			if (obj == null)
			{
				HandleNamedMessageDelegate val = OnStart;
				<>O.<0>__OnStart = val;
				obj = (object)val;
			}
			messages.RegisterNamedMessageHandler("aj.WipeSong.Start", (HandleNamedMessageDelegate)obj);
		}

		internal static void Unregister()
		{
			CustomMessagingManager? messages = Messages;
			if (messages != null)
			{
				messages.UnregisterNamedMessageHandler("aj.WipeSong.Start");
			}
		}

		internal unsafe static void HostTellEveryone()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			CustomMessagingManager messages = Messages;
			if ((Object)(object)singleton == (Object)null || !singleton.IsServer || messages == null)
			{
				return;
			}
			List<ulong> list = singleton.ConnectedClientsIds.Where((ulong id) => id != 0).ToList();
			if (list.Count == 0)
			{
				return;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(0, (Allocator)2, -1);
			try
			{
				messages.SendNamedMessage("aj.WipeSong.Start", (IReadOnlyList<ulong>)list, val, (NetworkDelivery)2);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private static void OnStart(ulong sender, FastBufferReader reader)
		{
			if (sender == 0L)
			{
				SongPlayer.Begin();
			}
		}
	}
	internal class SongPlayer : MonoBehaviour
	{
		private enum State
		{
			Idle,
			FadingIn,
			Playing,
			FadingOut
		}

		private const float SongStartSeconds = 10f;

		private static SongPlayer? instance;

		private AudioSource source;

		private State state;

		private void Awake()
		{
			instance = this;
			source = ((Component)this).gameObject.AddComponent<AudioSource>();
			source.playOnAwake = false;
			source.loop = false;
			source.spatialBlend = 0f;
			source.bypassReverbZones = true;
			source.bypassListenerEffects = true;
			source.priority = 0;
			((Behaviour)this).enabled = false;
		}

		internal static void Begin()
		{
			if ((Object)(object)instance != (Object)null)
			{
				instance.StartSong();
			}
		}

		internal static void End()
		{
			if ((Object)(object)instance != (Object)null && instance.state != State.Idle)
			{
				instance.state = State.FadingOut;
			}
		}

		private void StartSong()
		{
			AudioClip song = Plugin.Song;
			if (state == State.Idle && !((Object)(object)song == (Object)null) && !(Plugin.BoundConfig.Volume.Value <= 0f))
			{
				source.clip = song;
				source.volume = 0f;
				source.Play();
				source.time = Mathf.Min(10f, song.length - 0.1f);
				state = State.FadingIn;
				((Behaviour)this).enabled = true;
			}
		}

		private void Update()
		{
			if (!source.isPlaying)
			{
				StopSong();
				return;
			}
			float num = Mathf.Clamp01(Plugin.BoundConfig.Volume.Value);
			switch (state)
			{
			case State.FadingIn:
				source.volume = Step(source.volume, num, Plugin.BoundConfig.FadeInSeconds.Value, num);
				if (source.volume >= num)
				{
					state = State.Playing;
				}
				break;
			case State.Playing:
				source.volume = num;
				break;
			case State.FadingOut:
				source.volume = Step(source.volume, 0f, Plugin.BoundConfig.FadeOutSeconds.Value, num);
				if (source.volume <= 0f)
				{
					StopSong();
				}
				break;
			}
		}

		private void StopSong()
		{
			source.Stop();
			state = State.Idle;
			((Behaviour)this).enabled = false;
		}

		private static float Step(float volume, float target, float seconds, float full)
		{
			if (seconds <= 0f || full <= 0f)
			{
				return target;
			}
			return Mathf.MoveTowards(volume, target, full / seconds * Time.deltaTime);
		}

		private void OnDestroy()
		{
			if ((Object)(object)instance == (Object)(object)this)
			{
				instance = null;
			}
			SongNet.Unregister();
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "WipeSong";

		public const string PLUGIN_NAME = "WipeSong";

		public const string PLUGIN_VERSION = "1.0.5";
	}
}
namespace WipeSong.Configuration
{
	internal class PluginConfig
	{
		public ConfigEntry<float> Volume;

		public ConfigEntry<float> FadeInSeconds;

		public ConfigEntry<float> FadeOutSeconds;

		public PluginConfig(ConfigFile cfg)
		{
			Volume = cfg.Bind<float>("Song", "Volume", 0.6f, "How loud the song plays once faded in (0 = off, 1 = full). Each player's own setting; the game's master volume still applies.");
			FadeInSeconds = cfg.Bind<float>("Song", "Fade in", 1f, "Seconds the song takes to fade in after the last player dies.");
			FadeOutSeconds = cfg.Bind<float>("Song", "Fade out", 1f, "Seconds the song takes to fade out once the ship has left and the end-of-day results come up.");
		}
	}
}