Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Valyrim Music 2 v1.1.0
BepInEx/plugins/CustomAudio.dll
Decompiled 2 years agousing 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 HarmonyLib; using TMPro; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+525c8714e257ba3bdc5b2c433291b5d45e35fe0b")] [assembly: AssemblyVersion("1.0.0.0")] namespace CustomAudio; [BepInPlugin("aedenthorn.CustomAudio", "Custom Audio", "1.5.1")] public class BepInExPlugin : BaseUnityPlugin { [HarmonyPatch(typeof(ZSFX), "Awake")] private static class ZSFX_Awake_Patch { private static void Postfix(ZSFX __instance) { if (!modEnabled.Value) { return; } string zSFXName = GetZSFXName(__instance); if (dumpInfo.Value) { Dbgl("Checking SFX: " + zSFXName); } Dictionary<string, Dictionary<string, AudioClip>> customSFXList = BepInExPlugin.customSFXList; if (customSFXList != null && customSFXList.TryGetValue(zSFXName, out var value)) { if (dumpInfo.Value) { Dbgl("replacing SFX list by name: " + zSFXName); } __instance.m_audioClips = value.Values.ToArray(); } else { if (customSFX == null || __instance.m_audioClips == null) { return; } for (int i = 0; i < __instance.m_audioClips.Length; i++) { if (__instance.m_audioClips[i] == null) { continue; } if (dumpInfo.Value) { Dbgl("checking SFX: " + zSFXName + ", clip: " + ((Object)__instance.m_audioClips[i]).name); } if (customSFX.TryGetValue(((Object)__instance.m_audioClips[i]).name, out var value2)) { if (dumpInfo.Value) { Dbgl("replacing SFX: " + zSFXName + ", clip: " + ((Object)__instance.m_audioClips[i]).name); } __instance.m_audioClips[i] = value2; } } } } } [HarmonyPatch(typeof(MusicMan), "Awake")] private static class MusicMan_Awake_Patch { private static void Postfix(MusicMan __instance) { if (!modEnabled.Value) { return; } List<string> list = new List<string>(); for (int i = 0; i < __instance.m_music.Count; i++) { list.Add("Music list name: " + __instance.m_music[i].m_name); for (int j = 0; j < __instance.m_music[i].m_clips.Length; j++) { if (Object.op_Implicit((Object)(object)__instance.m_music[i].m_clips[j])) { list.Add("\ttrack name: " + ((Object)__instance.m_music[i].m_clips[j]).name); if (customMusic.ContainsKey(((Object)__instance.m_music[i].m_clips[j]).name)) { Dbgl("replacing music: " + __instance.m_music[i].m_name + ", clip: " + ((Object)__instance.m_music[i].m_clips[j]).name); __instance.m_music[i].m_clips[j] = customMusic[((Object)__instance.m_music[i].m_clips[j]).name]; } } } if (customMusicList.ContainsKey(__instance.m_music[i].m_name)) { Dbgl("replacing music list by name: " + __instance.m_music[i].m_name); __instance.m_music[i].m_clips = customMusicList[__instance.m_music[i].m_name].Values.ToArray(); } } if (dumpInfo.Value) { Dbgl(string.Join("\n", list)); } } } [HarmonyPatch(typeof(AudioMan), "Awake")] private static class AudioMan_Awake_Patch { private static void Postfix(AudioMan __instance, List<BiomeAmbients> ___m_randomAmbients, AudioSource ___m_oceanAmbientSource, AudioSource ___m_windLoopSource) { if (!modEnabled.Value) { return; } List<string> list = new List<string>(); for (int i = 0; i < ___m_randomAmbients.Count; i++) { list.Add("Ambient list name: " + ___m_randomAmbients[i].m_name); list.Add("\tAmbient tracks: (use " + ___m_randomAmbients[i].m_name + ")"); for (int j = 0; j < ___m_randomAmbients[i].m_randomAmbientClips.Count; j++) { list.Add("\t\ttrack name: " + ((Object)___m_randomAmbients[i].m_randomAmbientClips[j]).name); if (customAmbient.ContainsKey(((Object)___m_randomAmbients[i].m_randomAmbientClips[j]).name)) { Dbgl("replacing ambient: " + ___m_randomAmbients[i].m_name + ", clip: " + ((Object)___m_randomAmbients[i].m_randomAmbientClips[j]).name); ___m_randomAmbients[i].m_randomAmbientClips[j] = customAmbient[((Object)___m_randomAmbients[i].m_randomAmbientClips[j]).name]; } } list.Add("\tAmbient day tracks: (use " + ___m_randomAmbients[i].m_name + "_day)"); for (int k = 0; k < ___m_randomAmbients[i].m_randomAmbientClipsDay.Count; k++) { if (!((Object)(object)__instance.m_randomAmbients[i].m_randomAmbientClipsDay[k] == (Object)null)) { list.Add("\t\ttrack name: " + ((Object)___m_randomAmbients[i].m_randomAmbientClipsDay[k]).name); if (customAmbient.ContainsKey(((Object)___m_randomAmbients[i].m_randomAmbientClipsDay[k]).name)) { Dbgl("replacing ambient day: " + ___m_randomAmbients[i].m_name + ", clip: " + ((Object)___m_randomAmbients[i].m_randomAmbientClipsDay[k]).name); ___m_randomAmbients[i].m_randomAmbientClipsDay[k] = customAmbient[((Object)___m_randomAmbients[i].m_randomAmbientClipsDay[k]).name]; } } } list.Add("\tAmbient night tracks: (use " + ___m_randomAmbients[i].m_name + "_night)"); for (int l = 0; l < ___m_randomAmbients[i].m_randomAmbientClipsNight.Count; l++) { if (!((Object)(object)__instance.m_randomAmbients[i].m_randomAmbientClipsNight[l] == (Object)null)) { list.Add("\t\ttrack name: " + ((Object)___m_randomAmbients[i].m_randomAmbientClipsNight[l]).name); if (customAmbient.ContainsKey(((Object)___m_randomAmbients[i].m_randomAmbientClipsNight[l]).name)) { Dbgl("replacing ambient night: " + ___m_randomAmbients[i].m_name + ", clip: " + ((Object)___m_randomAmbients[i].m_randomAmbientClipsNight[l]).name); ___m_randomAmbients[i].m_randomAmbientClipsNight[l] = customAmbient[((Object)___m_randomAmbients[i].m_randomAmbientClipsNight[l]).name]; } } } if (customAmbientList.ContainsKey(___m_randomAmbients[i].m_name + "_day")) { Dbgl("replacing ambient day list by name: " + ___m_randomAmbients[i].m_name + "_day"); ___m_randomAmbients[i].m_randomAmbientClipsDay = new List<AudioClip>(customAmbientList[___m_randomAmbients[i].m_name + "_day"].Values.ToList()); } else if (customAmbientList.ContainsKey(___m_randomAmbients[i].m_name + "_night")) { Dbgl("replacing ambient night list by name: " + ___m_randomAmbients[i].m_name + "_night"); ___m_randomAmbients[i].m_randomAmbientClipsNight = new List<AudioClip>(customAmbientList[___m_randomAmbients[i].m_name + "_night"].Values.ToList()); } else if (customAmbientList.ContainsKey(___m_randomAmbients[i].m_name)) { Dbgl("replacing ambient list by name: " + ___m_randomAmbients[i].m_name); ___m_randomAmbients[i].m_randomAmbientClips = new List<AudioClip>(customAmbientList[___m_randomAmbients[i].m_name].Values.ToList()); } } if (dumpInfo.Value) { Dbgl(string.Join("\n", list)); } if (customAmbient.ContainsKey("ocean")) { ___m_oceanAmbientSource.clip = customAmbient["ocean"]; } if (customAmbient.ContainsKey("wind")) { ___m_windLoopSource.clip = customAmbient["wind"]; } } } [HarmonyPatch(typeof(MusicMan), "UpdateMusic")] private static class UpdateMusic_Patch { private static NamedMusic lastMusic; private static void Prefix(ref NamedMusic ___m_currentMusic, ref NamedMusic ___m_queuedMusic, AudioSource ___m_musicSource) { if (!modEnabled.Value) { return; } if (___m_queuedMusic != null) { ___m_queuedMusic.m_volume = musicVol.Value; } if ((Object)(object)((___m_musicSource != null) ? ___m_musicSource.clip : null) != (Object)null && lastMusicName != ((Object)___m_musicSource.clip).name) { if (dumpInfo.Value) { Dbgl("Switching music from " + lastMusicName + " to " + ((Object)___m_musicSource.clip).name); } lastMusicName = ((Object)___m_musicSource.clip).name; } if (___m_queuedMusic == null && !___m_musicSource.isPlaying && PlayerPrefs.GetInt("ContinousMusic", 1) == 1) { if (lastMusic != null) { lastMusic.m_lastPlayedTime = 0f; ___m_queuedMusic = lastMusic; lastMusic = null; } else if (___m_currentMusic != null) { lastMusic = ___m_currentMusic; } else { lastMusic = null; } } else { lastMusic = null; } if (___m_musicSource.isPlaying && ___m_queuedMusic != null && ___m_musicSource.loop) { Dbgl("queued " + ___m_queuedMusic?.m_name + ", setting " + ((Object)___m_musicSource).name + " loop to false"); ___m_musicSource.loop = false; } } } [HarmonyPatch(typeof(AudioMan), "QueueAmbientLoop")] private static class QueueAmbientLoop_Patch { private static void Prefix(ref float ___m_queuedAmbientVol, ref float ___m_ambientVol, ref float vol) { if (modEnabled.Value) { vol = ambientVol.Value; ___m_ambientVol = ambientVol.Value; ___m_queuedAmbientVol = ambientVol.Value; } } } [HarmonyPatch(typeof(EnvMan), "Awake")] private static class EnvMan_Awake_Patch { private static void Postfix(EnvMan __instance) { if (!modEnabled.Value) { return; } for (int i = 0; i < __instance.m_environments.Count; i++) { if (customMusicList.ContainsKey(__instance.m_environments[i].m_name + "Morning")) { AddMusicList(__instance, i, "Morning"); } if (customMusicList.ContainsKey(__instance.m_environments[i].m_name + "Day")) { AddMusicList(__instance, i, "Day"); } if (customMusicList.ContainsKey(__instance.m_environments[i].m_name + "Evening")) { AddMusicList(__instance, i, "Evening"); } if (customMusicList.ContainsKey(__instance.m_environments[i].m_name + "Night")) { AddMusicList(__instance, i, "Night"); } } } } [HarmonyPatch(typeof(TeleportWorld), "Awake")] private static class TeleportWorld_Awake_Patch { private static void Postfix(TeleportWorld __instance) { if (modEnabled.Value && customSFX.ContainsKey("portal")) { AudioSource componentInChildren = ((Component)__instance).GetComponentInChildren<AudioSource>(); componentInChildren.clip = customSFX["portal"]; ((Component)componentInChildren).gameObject.SetActive(false); ((Component)componentInChildren).gameObject.SetActive(true); } } } [HarmonyPatch(typeof(Fireplace), "Start")] private static class Fireplace_Start_Patch { private static void Postfix(Fireplace __instance) { if (!modEnabled.Value) { return; } if (((Object)__instance).name.Contains("groundtorch") && customSFX.ContainsKey("groundtorch")) { Dbgl("Replacing ground torch audio"); __instance.m_enabledObject.GetComponentInChildren<AudioSource>().clip = customSFX["groundtorch"]; } else if (((Object)__instance).name.Contains("walltorch") && customSFX.ContainsKey("walltorch")) { Dbgl("Replacing walltorch audio"); GameObject enabledObjectHigh = __instance.m_enabledObjectHigh; if (Object.op_Implicit((Object)(object)((enabledObjectHigh != null) ? enabledObjectHigh.GetComponentInChildren<AudioSource>() : null))) { __instance.m_enabledObjectHigh.GetComponentInChildren<AudioSource>().clip = customSFX["walltorch"]; return; } GameObject enabledObject = __instance.m_enabledObject; if (Object.op_Implicit((Object)(object)((enabledObject != null) ? enabledObject.GetComponentInChildren<AudioSource>() : null))) { __instance.m_enabledObject.GetComponentInChildren<AudioSource>().clip = customSFX["walltorch"]; } } else if (((Object)__instance).name.Contains("fire_pit") && customSFX.ContainsKey("fire_pit")) { Dbgl("Replacing fire_pit audio"); __instance.m_enabledObjectHigh.GetComponentInChildren<AudioSource>().clip = customSFX["fire_pit"]; } else if (((Object)__instance).name.Contains("bonfire") && customSFX.ContainsKey("bonfire")) { Dbgl("Replacing bonfire audio"); __instance.m_enabledObjectHigh.GetComponentInChildren<AudioSource>().clip = customSFX["bonfire"]; } else if (((Object)__instance).name.Contains("hearth") && customSFX.ContainsKey("hearth")) { Dbgl("Replacing hearth audio"); __instance.m_enabledObjectHigh.GetComponentInChildren<AudioSource>().clip = customSFX["hearth"]; } } } [HarmonyPatch(typeof(Terminal), "InputText")] private static class InputText_Patch { private static bool Prefix(Terminal __instance) { if (!modEnabled.Value) { return true; } string text = ((TMP_InputField)__instance.m_input).text; if (text.ToLower().Equals(typeof(BepInExPlugin).Namespace.ToLower() + " reset")) { ((BaseUnityPlugin)context).Config.Reload(); ((BaseUnityPlugin)context).Config.Save(); Traverse.Create((object)__instance).Method("AddString", new object[1] { text }).GetValue(); Traverse.Create((object)__instance).Method("AddString", new object[1] { ((BaseUnityPlugin)context).Info.Metadata.Name + " config reloaded" }).GetValue(); return false; } if (text.ToLower().Equals(typeof(BepInExPlugin).Namespace.ToLower() + " music")) { Traverse.Create((object)__instance).Method("AddString", new object[1] { text }).GetValue(); if (Object.op_Implicit((Object)(object)EnvMan.instance)) { string name; if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && Player.m_localPlayer.IsSafeInHome()) { name = "home"; } else { name = EnvMan.instance.GetAmbientMusic(); } Dbgl("Current environment: " + EnvMan.instance.GetCurrentEnvironment().m_name); Dbgl("Current music list: " + name + " " + ((IEnumerable<NamedMusic>)MusicMan.instance.m_music).FirstOrDefault((Func<NamedMusic, bool>)((NamedMusic m) => m.m_name == name))?.m_clips.Length); } Dbgl("Vanilla music list names:\n" + string.Join("\n", MusicMan.instance.m_music.Select((NamedMusic m) => m.m_name))); if (Object.op_Implicit((Object)(object)EnvMan.instance)) { List<string> list = new List<string>(); for (int i = 0; i < EnvMan.instance.m_environments.Count; i++) { list.Add(EnvMan.instance.m_environments[i].m_name + "Morning"); list.Add(EnvMan.instance.m_environments[i].m_name + "Day"); list.Add(EnvMan.instance.m_environments[i].m_name + "Evening"); list.Add(EnvMan.instance.m_environments[i].m_name + "Night"); } Dbgl("Possible music list names:\n" + string.Join("\n", list)); } Traverse.Create((object)__instance).Method("AddString", new object[1] { ((BaseUnityPlugin)context).Info.Metadata.Name + " dumped music names" }).GetValue(); return false; } if (text.ToLower().Equals(typeof(BepInExPlugin).Namespace.ToLower() + " env")) { Traverse.Create((object)__instance).Method("AddString", new object[1] { text }).GetValue(); if (!Object.op_Implicit((Object)(object)EnvMan.instance)) { Traverse.Create((object)__instance).Method("AddString", new object[1] { "Must be called in-game" }).GetValue(); } Dbgl("Current environment: " + EnvMan.instance.GetCurrentEnvironment().m_name); return false; } return true; } } public static ConfigEntry<bool> isDebug; public static ConfigEntry<bool> modEnabled; public static ConfigEntry<bool> dumpInfo; public static ConfigEntry<float> sfxVol; public static ConfigEntry<float> musicVol; public static ConfigEntry<float> ambientVol; public static Dictionary<string, AudioClip> customMusic = new Dictionary<string, AudioClip>(); public static Dictionary<string, Dictionary<string, AudioClip>> customMusicList = new Dictionary<string, Dictionary<string, AudioClip>>(); public static Dictionary<string, AudioClip> customAmbient = new Dictionary<string, AudioClip>(); public static Dictionary<string, Dictionary<string, AudioClip>> customAmbientList = new Dictionary<string, Dictionary<string, AudioClip>>(); public static Dictionary<string, AudioClip> customSFX = new Dictionary<string, AudioClip>(); public static Dictionary<string, Dictionary<string, AudioClip>> customSFXList = new Dictionary<string, Dictionary<string, AudioClip>>(); public static ConfigEntry<int> nexusID; private static string lastMusicName = ""; private static BepInExPlugin context; public static void Dbgl(string str = "", bool pref = true) { if (isDebug.Value) { Debug.Log((object)((pref ? (typeof(BepInExPlugin).Namespace + " ") : "") + str)); } } private void Awake() { context = this; modEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable this mod"); isDebug = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IsDebug", true, "Show debug log messages in the console"); dumpInfo = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DumpInfo", true, "Dump audio info to the console"); musicVol = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MusicVol", 0.6f, "Music volume, 0.0 - 1.0"); ambientVol = ((BaseUnityPlugin)this).Config.Bind<float>("General", "AmbientVol", 0.3f, "Ambient volume"); nexusID = ((BaseUnityPlugin)this).Config.Bind<int>("General", "NexusID", 90, "Nexus mod ID for updates"); if (modEnabled.Value) { PreloadAudioClips(); Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null); } } public void PreloadAudioClips() { Dbgl("Preloading audio clips."); string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "CustomAudio"); if (Directory.Exists(text)) { customMusic.Clear(); customAmbient.Clear(); customSFX.Clear(); customMusicList.Clear(); customAmbientList.Clear(); customSFXList.Clear(); if (Directory.Exists(Path.Combine(text, "Music"))) { CollectAudioFiles(Path.Combine(text, "Music"), customMusic, customMusicList); } else { Directory.CreateDirectory(Path.Combine(text, "Music")); } if (Directory.Exists(Path.Combine(text, "SFX"))) { CollectAudioFiles(Path.Combine(text, "SFX"), customSFX, customSFXList); } else { Directory.CreateDirectory(Path.Combine(text, "SFX")); } if (Directory.Exists(Path.Combine(text, "Ambient"))) { CollectAudioFiles(Path.Combine(text, "Ambient"), customAmbient, customAmbientList); } else { Directory.CreateDirectory(Path.Combine(text, "Ambient")); } } else { Dbgl("Directory " + text + " does not exist! Creating."); Directory.CreateDirectory(text); Directory.CreateDirectory(Path.Combine(text, "Ambient")); Directory.CreateDirectory(Path.Combine(text, "Music")); Directory.CreateDirectory(Path.Combine(text, "SFX")); } } public void CollectAudioFiles(string path, Dictionary<string, AudioClip> customDict, Dictionary<string, Dictionary<string, AudioClip>> customDictDict) { Dbgl("checking folder " + Path.GetFileName(path)); string[] files = Directory.GetFiles(path); string[] array = files; foreach (string path2 in array) { PreloadClipCoroutine(path2, (AudioType)0, customDict); } string[] directories = Directory.GetDirectories(path); foreach (string path3 in directories) { string fileName = Path.GetFileName(path3); files = Directory.GetFiles(path3); customDictDict[fileName] = new Dictionary<string, AudioClip>(); string[] array2 = files; foreach (string path4 in array2) { PreloadClipCoroutine(path4, (AudioType)0, customDictDict[fileName]); } } string[] directories2 = Directory.GetDirectories(path); foreach (string path5 in directories2) { string fileName2 = Path.GetFileName(path5); string[] files2 = Directory.GetFiles(path5); if (files2.Length == 1 && files2[0].ToLower().EndsWith(".txt") && customDictDict.ContainsKey(Path.GetFileNameWithoutExtension(files2[0]))) { Dbgl("\tlinking music folder " + Path.GetFileName(path5) + " to folder " + Path.GetFileNameWithoutExtension(files2[0])); customDictDict[fileName2] = customDictDict[Path.GetFileNameWithoutExtension(files2[0])]; } } } private void PreloadClipCoroutine(string path, AudioType audioType, Dictionary<string, AudioClip> whichDict) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown if (path.EndsWith(".txt") || !path.Contains(".")) { return; } Dbgl("path: " + path); path = "file:///" + path.Replace("\\", "/"); try { UnityWebRequest audioClip = UnityWebRequestMultimedia.GetAudioClip(path, audioType); audioClip.SendWebRequest(); while (!audioClip.isDone) { } if (audioClip != null) { DownloadHandlerAudioClip val = (DownloadHandlerAudioClip)audioClip.downloadHandler; if (val != null) { AudioClip audioClip2 = val.audioClip; if ((Object)(object)audioClip2 != (Object)null) { string text = (((Object)audioClip2).name = Path.GetFileNameWithoutExtension(path)); if (!whichDict.ContainsKey(text)) { whichDict[text] = audioClip2; } Dbgl("Added audio clip " + text + " to dict"); } else { Dbgl("audio clip is null. data: " + ((DownloadHandler)val).text); } } else { Dbgl("DownloadHandler is null. bytes downloaded: " + audioClip.downloadedBytes); } } else { Dbgl("www is null " + audioClip.url); } } catch { } } public static string GetZSFXName(ZSFX zfx) { string name = ((Object)zfx).name; char[] anyOf = new char[2] { '(', ' ' }; int num = name.IndexOfAny(anyOf); if (num != -1) { return name.Remove(num); } return name; } private static void AddMusicList(EnvMan envMan, int index, string which) { //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Expected O, but got Unknown string text = envMan.m_environments[index].m_name + which; Dbgl($"Adding music list by name: {text} ({customMusicList[text].Count})"); switch (which) { case "Morning": envMan.m_environments[index].m_musicMorning = text; break; case "Day": envMan.m_environments[index].m_musicDay = text; break; case "Evening": envMan.m_environments[index].m_musicEvening = text; break; case "Night": envMan.m_environments[index].m_musicNight = text; break; } MusicMan.instance.m_music.Add(new NamedMusic { m_name = text, m_clips = customMusicList[text].Values.ToArray(), m_loop = true, m_ambientMusic = true, m_resume = true }); } } public class UnityWebRequestAwaiter : INotifyCompletion { private UnityWebRequestAsyncOperation asyncOp; private Action continuation; public bool IsCompleted { get { BepInExPlugin.Dbgl("Is completed get"); return ((AsyncOperation)asyncOp).isDone; } } public UnityWebRequestAwaiter(UnityWebRequestAsyncOperation asyncOp) { this.asyncOp = asyncOp; ((AsyncOperation)asyncOp).completed += OnRequestCompleted; BepInExPlugin.Dbgl("created asyncop"); } public void GetResult() { BepInExPlugin.Dbgl("Get Result"); } public void OnCompleted(Action continuation) { this.continuation = continuation; BepInExPlugin.Dbgl("on completed"); } private void OnRequestCompleted(AsyncOperation obj) { continuation(); BepInExPlugin.Dbgl("on request completed"); } } public static class ExtensionMethods { public static UnityWebRequestAwaiter GetAwaiter(this UnityWebRequestAsyncOperation asyncOp) { return new UnityWebRequestAwaiter(asyncOp); } }