Please disclose if any significant portion of your mod was created 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 ManorClubbingRevived v1.1.0
plugins/ManorClubbingRevived/ManorClubbingRevived.dll
Decompiled 21 hours agousing System; using System.Collections; 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 ExitGames.Client.Photon; using HarmonyLib; using Photon.Pun; using Photon.Realtime; using REPOLib.Modules; using REPOLib.Objects.Sdk; using UnityEngine; using UnityEngine.Networking; [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("ManorClubbingRevived")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ManorClubbingRevived")] [assembly: AssemblyTitle("ManorClubbingRevived")] [assembly: AssemblyVersion("1.0.0.0")] namespace ManorClubbingRevived; internal class ClubAudioFix : MonoBehaviour { private void Awake() { if (!Plugin.MixClubMusic.Value) { return; } AudioManager instance = AudioManager.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.MusicMasterGroup == (Object)null) { Plugin.Log.LogWarning((object)(((Object)this).name + ": AudioManager not ready, club music stays unmixed.")); return; } AudioSource[] componentsInChildren = ((Component)this).GetComponentsInChildren<AudioSource>(true); foreach (AudioSource val in componentsInChildren) { if ((Object)(object)val.outputAudioMixerGroup == (Object)null) { val.outputAudioMixerGroup = instance.MusicMasterGroup; } } } } internal static class ClubLevel { private static readonly string[] RoomAssets = new string[3] { "manorclub/start_clubbing_1_1", "manorclub/start_clubbing_2_1", "manorclub/start_clubbing_2_2" }; private static readonly string[] SwappedChildren = new string[2] { "Truck Run", "Extraction Point" }; public static void Register() { //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown RunManager instance = RunManager.instance; Level val = ((IEnumerable<Level>)instance.levels).FirstOrDefault((Func<Level, bool>)((Level l) => (Object)(object)l != (Object)null && ((Object)l).name == "Level - Manor")) ?? ((IEnumerable<Level>)instance.levels).FirstOrDefault((Func<Level, bool>)((Level l) => (Object)(object)l != (Object)null && ((Object)l).name.Contains("Manor"))); if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)"Vanilla Manor level not found, aborting."); return; } AssetBundle val2 = AssetBundle.LoadFromFile(Plugin.BundlePath); if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogError((object)("Could not load " + Plugin.BundlePath + ".")); return; } GameObject val3 = val.StartRooms.Select((PrefabRef r) => ((PrefabRef<GameObject>)(object)r)?.Prefab).FirstOrDefault((Func<GameObject, bool>)((GameObject p) => (Object)(object)p != (Object)null && SwappedChildren.All((string c) => (Object)(object)p.transform.Find(c) != (Object)null))); if ((Object)(object)val3 == (Object)null) { Plugin.Log.LogWarning((object)"No vanilla Manor start room with a truck found, keeping the old truck."); } else { Plugin.Log.LogInfo((object)("Using vanilla start room '" + ((Object)val3).name + "' as truck donor.")); } GameObject val4 = new GameObject("ManorClub Templates"); val4.SetActive(false); Object.DontDestroyOnLoad((Object)(object)val4); List<GameObject> list = new List<GameObject>(); string[] roomAssets = RoomAssets; foreach (string text in roomAssets) { GameObject val5 = val2.LoadAsset<GameObject>(text); if ((Object)(object)val5 == (Object)null) { Plugin.Log.LogError((object)("Missing '" + text + "' in bundle.")); } else { list.Add(StartRoomBuilder.Build(val5, val3, val4.transform, SwappedChildren)); } } if (list.Count != 0) { Level val6 = Object.Instantiate<Level>(val); ((Object)val6).name = "Level - Manor Club"; val6.NarrativeName = "Manor Club"; val6.NarrativeNameLocalized = null; Dictionary<string, List<PrefabRef>> saved = ModuleLists.Snapshot(val); LevelContent obj = ScriptableObject.CreateInstance<LevelContent>(); ((Object)obj).name = "Manor Club"; SetField(obj, "_level", val6); SetField(obj, "_startRooms", list); Levels.RegisterLevel(obj); ModuleLists.Restore(val6, saved); Plugin.Log.LogInfo((object)$"Registered '{((Object)val6).name}' with {list.Count} club start rooms."); } } private static void SetField(object target, string field, object value) { target.GetType().GetField(field, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).SetValue(target, value); } } internal static class ModuleLists { private static readonly string[] Fields = new string[12] { "ModulesNormal1", "ModulesPassage1", "ModulesDeadEnd1", "ModulesExtraction1", "ModulesNormal2", "ModulesPassage2", "ModulesDeadEnd2", "ModulesExtraction2", "ModulesNormal3", "ModulesPassage3", "ModulesDeadEnd3", "ModulesExtraction3" }; public static Dictionary<string, List<PrefabRef>> Snapshot(Level level) { return Fields.ToDictionary((string f) => f, (string f) => new List<PrefabRef>(Get(level, f) ?? new List<PrefabRef>())); } public static void Restore(Level level, Dictionary<string, List<PrefabRef>> saved) { foreach (KeyValuePair<string, List<PrefabRef>> item in saved) { typeof(Level).GetField(item.Key).SetValue(level, item.Value); } } private static List<PrefabRef> Get(Level level, string field) { return (List<PrefabRef>)typeof(Level).GetField(field).GetValue(level); } } internal class ClubSongPicker : MonoBehaviour { private const string PropertyKey = "ManorClub.Song"; private const float ClientTimeout = 10f; public AudioSource music; private void Start() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown if ((Object)(object)music == (Object)null) { return; } if (!SemiFunc.IsMultiplayer()) { Apply(Pick()); return; } string text = Token(); if (PhotonNetwork.IsMasterClient) { string text2 = Pick(); PhotonNetwork.CurrentRoom.SetCustomProperties(new Hashtable { [(object)"ManorClub.Song"] = text + "|" + text2 }, (Hashtable)null, (WebFlags)null); Apply(text2); } else { ((MonoBehaviour)this).StartCoroutine(WaitForHost(text)); } } private static string Pick() { IReadOnlyList<string> names = SongLibrary.Names; if (names.Count == 0 || Random.value >= Plugin.CustomSongChance.Value) { return ""; } return names[Random.Range(0, names.Count)]; } private string Token() { int num = (from v in ((Component)this).GetComponentsInChildren<PhotonView>(true) select v.ViewID into id where id > 0 select id).DefaultIfEmpty(0).Min(); return RunManager.instance.levelsCompleted + ":" + num; } private IEnumerator WaitForHost(string token) { float waited = 0f; while (waited < 10f) { if (PhotonNetwork.CurrentRoom != null && ((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)"ManorClub.Song", out object value) && value is string text && text.StartsWith(token + "|")) { Apply(text.Substring(token.Length + 1)); yield break; } waited += Time.unscaledDeltaTime; yield return null; } Plugin.Log.LogWarning((object)"No song pick from host, playing the original."); Apply(""); } private void Apply(string song) { if (!SongLibrary.Has(song)) { if (song != "") { Plugin.Log.LogWarning((object)("Club song '" + song + "' not installed here, playing the original.")); } music.Play(); return; } Plugin.Log.LogInfo((object)("Club song: " + song)); ((MonoBehaviour)this).StartCoroutine(SongLibrary.Load(song, delegate(AudioClip clip) { if ((Object)(object)clip != (Object)null) { music.clip = clip; } music.Play(); })); } } [BepInPlugin("Slumberdac.ManorClubbingRevived", "ManorClubbingRevived", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string Guid = "Slumberdac.ManorClubbingRevived"; public const string Name = "ManorClubbingRevived"; public const string Version = "1.1.0"; public const string BundleFile = "manorclub.bundle"; internal static ManualLogSource Log; internal static string BundlePath; internal static ConfigEntry<bool> MixClubMusic; internal static ConfigEntry<float> CustomSongChance; private void Awake() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; MixClubMusic = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "MixClubMusic", false, "Route the club music through the game's music volume slider. Off keeps the original unmixed audio."); CustomSongChance = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "CustomSongChance", 0.5f, new ConfigDescription("Chance the club plays an extra song instead of its original one. Only the host's value counts.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); BundlePath = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "manorclub.bundle"); SongLibrary.Scan(); new Harmony("Slumberdac.ManorClubbingRevived").PatchAll(typeof(RunManagerAwakePatch)); Log.LogInfo((object)("ManorClubbingRevived 1.1.0 loaded, bundle: " + BundlePath)); } } [HarmonyPatch(typeof(RunManager), "Awake")] internal static class RunManagerAwakePatch { private static bool _done; [HarmonyPostfix] [HarmonyAfter(new string[] { "REPOLib" })] [HarmonyPriority(0)] private static void Postfix() { if (_done) { return; } _done = true; try { ClubLevel.Register(); } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed to register Manor Club: {arg}"); } } } internal static class SongLibrary { public const string FolderName = "ManorClubSongs"; private static readonly Dictionary<string, string> Paths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, AudioClip> Cache = new Dictionary<string, AudioClip>(StringComparer.OrdinalIgnoreCase); public static IReadOnlyList<string> Names { get; private set; } = Array.Empty<string>(); public static void Scan() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) Paths.Clear(); string[] directories = Directory.GetDirectories(Paths.PluginPath, "ManorClubSongs", SearchOption.AllDirectories); for (int i = 0; i < directories.Length; i++) { string[] files = Directory.GetFiles(directories[i]); foreach (string text in files) { if ((int)TypeOf(text) != 0) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text); if (Paths.ContainsKey(fileNameWithoutExtension)) { Plugin.Log.LogWarning((object)("Duplicate club song '" + fileNameWithoutExtension + "', keeping " + Paths[fileNameWithoutExtension] + ".")); } else { Paths[fileNameWithoutExtension] = text; } } } } Names = Paths.Keys.OrderBy<string, string>((string n) => n, StringComparer.OrdinalIgnoreCase).ToList(); Plugin.Log.LogInfo((object)$"Found {Names.Count} extra club songs."); } public static bool Has(string name) { if (!string.IsNullOrEmpty(name)) { return Paths.ContainsKey(name); } return false; } public static IEnumerator Load(string name, Action<AudioClip> done) { if (Cache.TryGetValue(name, out var value) && (Object)(object)value != (Object)null) { done(value); yield break; } string text = Paths[name]; UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(new Uri(text).AbsoluteUri, TypeOf(text)); try { ((DownloadHandlerAudioClip)req.downloadHandler).compressed = true; yield return req.SendWebRequest(); if ((int)req.result != 1) { Plugin.Log.LogError((object)("Could not load club song '" + name + "': " + req.error)); done(null); yield break; } AudioClip content = DownloadHandlerAudioClip.GetContent(req); ((Object)content).name = name; Cache[name] = content; done(content); } finally { ((IDisposable)req)?.Dispose(); } } private static AudioType TypeOf(string file) { return (AudioType)(Path.GetExtension(file).ToLowerInvariant() switch { ".ogg" => 14, ".mp3" => 13, ".wav" => 20, _ => 0, }); } } internal static class StartRoomBuilder { public static GameObject Build(GameObject source, GameObject donor, Transform parent, string[] children) { //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(source, parent); ((Object)val).name = ((Object)source).name; val.AddComponent<ClubAudioFix>(); Transform val2 = val.transform.Find("Audio Source"); AudioSource val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<AudioSource>() : null); if ((Object)(object)val3 != (Object)null) { val3.playOnAwake = false; val.AddComponent<ClubSongPicker>().music = val3; } else { Plugin.Log.LogWarning((object)(((Object)val).name + ": no club music source found, custom songs disabled for this room.")); } if ((Object)(object)donor == (Object)null) { return val; } List<(Transform, Transform)> list = new List<(Transform, Transform)>(); foreach (string text in children) { Transform val4 = val.transform.Find(text); Transform val5 = donor.transform.Find(text); if ((Object)(object)val4 == (Object)null || (Object)(object)val5 == (Object)null) { Plugin.Log.LogWarning((object)(((Object)val).name + ": cannot swap '" + text + "'.")); continue; } ((Object)val4).name = text + " (old)"; GameObject val6 = Object.Instantiate<GameObject>(((Component)val5).gameObject, val.transform); ((Object)val6).name = text; val6.transform.SetSiblingIndex(val4.GetSiblingIndex()); val6.transform.localPosition = val4.localPosition; val6.transform.localRotation = val4.localRotation; val6.transform.localScale = val4.localScale; list.Add((val4, val6.transform)); } new ReferenceFixer(val.transform, donor.transform, list).Run(); foreach (var item in list) { Object.DestroyImmediate((Object)(object)((Component)item.Item1).gameObject); } return val; } } internal class ReferenceFixer { private readonly Transform _room; private readonly Transform _donor; private readonly List<(Transform oldT, Transform newT)> _swaps; private int _fixed; private int _lost; public ReferenceFixer(Transform room, Transform donor, List<(Transform, Transform)> swaps) { _room = room; _donor = donor; _swaps = swaps; } public void Run() { MonoBehaviour[] componentsInChildren = ((Component)_room).GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour mb in componentsInChildren) { if ((Object)(object)mb == (Object)null || _swaps.Any(((Transform oldT, Transform newT) s) => IsUnder(((Component)mb).transform, s.oldT))) { continue; } foreach (FieldInfo item in SerializedFields(((object)mb).GetType())) { FixField(mb, item); } } Plugin.Log.LogInfo((object)$"{((Object)_room).name}: fixed {_fixed} references, {_lost} unresolved."); } private void FixField(MonoBehaviour owner, FieldInfo field) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown Type fieldType = field.FieldType; object value = field.GetValue(owner); if (value == null) { return; } if (typeof(Object).IsAssignableFrom(fieldType)) { Object obj = (Object)value; if (TryMap(obj, owner, field, out var mapped)) { field.SetValue(owner, mapped); } } else if (fieldType.IsArray && typeof(Object).IsAssignableFrom(fieldType.GetElementType())) { Array obj2 = (Array)value; bool flag = false; List<object> list = new List<object>(); foreach (object item in obj2) { Object val = (Object)((item is Object) ? item : null); if (val != null && val != (Object)null && TryMap(val, owner, field, out var mapped2)) { flag = true; if (mapped2 != (Object)null) { list.Add(mapped2); } } else { list.Add(item); } } if (flag) { Array array = Array.CreateInstance(fieldType.GetElementType(), list.Count); for (int i = 0; i < list.Count; i++) { array.SetValue(list[i], i); } field.SetValue(owner, array); } } else { if (!fieldType.IsGenericType || !(fieldType.GetGenericTypeDefinition() == typeof(List<>)) || !typeof(Object).IsAssignableFrom(fieldType.GetGenericArguments()[0])) { return; } IList list2 = (IList)value; for (int num = list2.Count - 1; num >= 0; num--) { object? obj3 = list2[num]; Object val2 = (Object)((obj3 is Object) ? obj3 : null); if (val2 != null && val2 != (Object)null && TryMap(val2, owner, field, out var mapped3)) { if (mapped3 != (Object)null) { list2[num] = mapped3; } else { list2.RemoveAt(num); } } } } } private bool TryMap(Object obj, MonoBehaviour owner, FieldInfo field, out Object mapped) { mapped = obj; Transform val = TransformOf(obj); if ((Object)(object)val == (Object)null) { return false; } foreach (var (val2, root) in _swaps) { if (IsUnder(val, val2)) { mapped = Resolve(obj, val, FindByPath(root, PathFrom(val2, val))); Count(mapped, obj, owner, field, val2); return true; } } if (IsUnder(val, _room)) { return false; } if (IsUnder(val, _donor)) { mapped = Resolve(obj, val, FindByPath(_room, PathFrom(_donor, val))); Count(mapped, obj, owner, field, _donor); return true; } return false; } private void Count(Object mapped, Object original, MonoBehaviour owner, FieldInfo field, Transform origin) { if (mapped != (Object)null) { _fixed++; return; } _lost++; Transform t = TransformOf(original); string text = string.Join("/", from s in PathFrom(origin, t) select s.name); Plugin.Log.LogDebug((object)(((Object)_room).name + ": unresolved " + ((object)owner).GetType().Name + "." + field.Name + " on '" + string.Join("/", from s in PathFrom(_room, ((Component)owner).transform) select s.name) + "' -> " + ((object)original).GetType().Name + " '" + ((Object)origin).name + "/" + text + "'")); } private static Object Resolve(Object obj, Transform from, Transform to) { if ((Object)(object)to == (Object)null) { return null; } if (obj is GameObject) { return (Object)(object)((Component)to).gameObject; } if (obj is Transform) { return (Object)(object)to; } Component val = (Component)(object)((obj is Component) ? obj : null); if (val != null) { Component[] components = ((Component)from).GetComponents(((object)val).GetType()); Component[] components2 = ((Component)to).GetComponents(((object)val).GetType()); int num = Array.IndexOf(components, val); if (num < 0 || num >= components2.Length) { return null; } return (Object)(object)components2[num]; } return null; } private static Transform TransformOf(Object obj) { GameObject val = (GameObject)(object)((obj is GameObject) ? obj : null); if (val != null) { return val.transform; } Component val2 = (Component)(object)((obj is Component) ? obj : null); if (val2 != null) { return val2.transform; } return null; } private static bool IsUnder(Transform t, Transform root) { Transform val = t; while ((Object)(object)val != (Object)null) { if ((Object)(object)val == (Object)(object)root) { return true; } val = val.parent; } return false; } private static List<(string name, int occurrence)> PathFrom(Transform root, Transform t) { List<(string, int)> list = new List<(string, int)>(); Transform val = t; while ((Object)(object)val != (Object)(object)root && (Object)(object)val != (Object)null) { int num = 0; Transform parent = val.parent; for (int i = 0; i < val.GetSiblingIndex(); i++) { if (((Object)parent.GetChild(i)).name == ((Object)val).name) { num++; } } list.Add((((Object)val).name, num)); val = val.parent; } list.Reverse(); return list; } private static Transform FindByPath(Transform root, List<(string name, int occurrence)> steps) { Transform val = root; foreach (var step in steps) { string item = step.name; int item2 = step.occurrence; Transform val2 = null; int num = 0; for (int i = 0; i < val.childCount; i++) { Transform child = val.GetChild(i); if (!(((Object)child).name != item) && num++ == item2) { val2 = child; break; } } if ((Object)(object)val2 == (Object)null) { return null; } val = val2; } return val; } private static IEnumerable<FieldInfo> SerializedFields(Type type) { Type t = type; while (t != null && t != typeof(MonoBehaviour)) { FieldInfo[] fields = t.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (!fieldInfo.IsNotSerialized && !fieldInfo.IsInitOnly && (fieldInfo.IsPublic || ((MemberInfo)fieldInfo).GetCustomAttribute<SerializeField>() != null)) { yield return fieldInfo; } } t = t.BaseType; } } }