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 UndisturbedBossFights v1.0.1
UndisturbedBossFights.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace UndisturbedBossFights; [HarmonyPatch(typeof(Terminal), "InitTerminal")] internal static class Commands { private const float RaidSoonSeconds = 10f; private static readonly FieldInfo eventTimer = AccessTools.Field(typeof(RandEventSystem), "m_eventTimer"); private static readonly FieldInfo eventIntervalMin = AccessTools.Field(typeof(RandEventSystem), "m_eventIntervalMin"); private static void Postfix() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown new ConsoleCommand("ubf_status", "- UndisturbedBossFights: show boss fights the mod sees and when the next raid roll is due", (ConsoleEvent)delegate(ConsoleEventArgs args) { bool flag = false; foreach (string item in FightTracker.Describe()) { if (!flag) { args.Context.AddString("Boss fights:"); } flag = true; args.Context.AddString(" " + item); } if (!flag) { args.Context.AddString("No boss fight going on."); } RandEventSystem instance = RandEventSystem.instance; if ((Object)(object)instance == (Object)null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { args.Context.AddString("Raid timing is only known on the server."); } else if (Game.m_eventRate <= 0f) { args.Context.AddString("Raids are turned off in this world."); } else { float num = EventDue(instance) - (float)eventTimer.GetValue(instance); args.Context.AddString(RaidPatch.Holding ? "A raid roll is due and waiting for the boss fight to end." : ("Next raid roll in " + (num / 60f).ToString("0.0", CultureInfo.InvariantCulture) + " min.")); } }, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); new ConsoleCommand("ubf_raidsoon", "- UndisturbedBossFights: make the next raid roll due in " + 10f + " seconds (for testing)", (ConsoleEvent)delegate(ConsoleEventArgs args) { RandEventSystem instance = RandEventSystem.instance; if ((Object)(object)instance == (Object)null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { args.Context.AddString("Only works on the server (single player or the host)."); } else { eventTimer.SetValue(instance, EventDue(instance) - 10f); args.Context.AddString("The next raid roll is due in " + 10f + " seconds. Its chance to start a raid is still the usual one."); } }, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } private static float EventDue(RandEventSystem events) { return (float)eventIntervalMin.GetValue(events) * 60f * Game.m_eventRate; } } internal interface ISynced { string Key { get; } string Serialize(); void ApplyServer(string text); void ClearServer(); } public class Synced<T> : ISynced { public readonly ConfigEntry<T> Entry; private T serverValue; private bool hasServerValue; public T Value { get { if (!hasServerValue) { return Entry.Value; } return serverValue; } } public string Key => ((ConfigEntryBase)Entry).Definition.Section + "/" + ((ConfigEntryBase)Entry).Definition.Key; public event Action Changed; public Synced(ConfigEntry<T> entry) { Entry = entry; EventHandler eventHandler = delegate { RaiseChanged(); ConfigSync.OnLocalChanged(); }; entry.SettingChanged += eventHandler; ConfigSync.Register(this); } public string Serialize() { return TomlTypeConverter.ConvertToString((object)Entry.Value, typeof(T)); } public void ApplyServer(string text) { serverValue = (T)TomlTypeConverter.ConvertToValue(text, typeof(T)); hasServerValue = true; RaiseChanged(); } public void ClearServer() { if (hasServerValue) { hasServerValue = false; RaiseChanged(); } } private void RaiseChanged() { if (this.Changed != null) { this.Changed(); } } } internal static class ConfigSync { [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class NewConnectionPatch { private static void Postfix(ZNet __instance, ZNetPeer __0) { if (__instance.IsServer()) { __0.m_rpc.Register<string>("UndisturbedBossFights_Hello", (Action<ZRpc, string>)OnHello); } else { __0.m_rpc.Register<ZPackage>("UndisturbedBossFights_Config", (Action<ZRpc, ZPackage>)OnConfig); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private static class PeerInfoPatch { private static void Postfix(ZNet __instance, ZRpc __0) { if (__instance.IsServer()) { ZNetPeer val = FindPeer(__0); if (val != null) { Send(val); ((MonoBehaviour)Plugin.Instance).StartCoroutine(CheckHello(val)); } } } } [HarmonyPatch(typeof(ZNet), "OnDestroy")] private static class LeavePatch { private static void Postfix() { foreach (ISynced setting in settings) { setting.ClearServer(); } peersWithMod.Clear(); } } private const string ConfigRpc = "UndisturbedBossFights_Config"; private const string HelloRpc = "UndisturbedBossFights_Hello"; private const float HelloTimeout = 20f; private static readonly List<ISynced> settings = new List<ISynced>(); private static readonly HashSet<long> peersWithMod = new HashSet<long>(); private static bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } internal static void Register(ISynced setting) { settings.Add(setting); } internal static void OnLocalChanged() { if (!IsServer) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.m_rpc != null) { Send(peer); } } } private static void Send(ZNetPeer peer) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write("1.0.1"); val.Write(settings.Count); foreach (ISynced setting in settings) { val.Write(setting.Key); val.Write(setting.Serialize()); } peer.m_rpc.Invoke("UndisturbedBossFights_Config", new object[1] { val }); } private static void OnConfig(ZRpc rpc, ZPackage pkg) { string text = pkg.ReadString(); int num = pkg.ReadInt(); Dictionary<string, string> dictionary = new Dictionary<string, string>(); for (int i = 0; i < num; i++) { string key = pkg.ReadString(); dictionary[key] = pkg.ReadString(); } foreach (ISynced setting in settings) { if (dictionary.TryGetValue(setting.Key, out var value)) { try { setting.ApplyServer(value); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not apply server value for " + setting.Key + ": " + ex.Message)); } } } Plugin.Log.LogInfo((object)"Using the server's UndisturbedBossFights settings."); if (text != "1.0.1") { Warn("UndisturbedBossFights version mismatch: server has " + text + ", you have 1.0.1."); } rpc.Invoke("UndisturbedBossFights_Hello", new object[1] { "1.0.1" }); } private static void OnHello(ZRpc rpc, string clientVersion) { ZNetPeer val = FindPeer(rpc); if (val != null) { peersWithMod.Add(val.m_uid); if (clientVersion != "1.0.1") { Warn(val.m_playerName + " has UndisturbedBossFights " + clientVersion + ", the server has 1.0.1."); } } } private static IEnumerator CheckHello(ZNetPeer peer) { yield return (object)new WaitForSeconds(20f); if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.GetPeers().Contains(peer) && !peersWithMod.Contains(peer.m_uid)) { Warn(peer.m_playerName + " doesn't have UndisturbedBossFights installed. Raids and spawns near them may still interrupt boss fights."); } } private static ZNetPeer FindPeer(ZRpc rpc) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.m_rpc == rpc) { return peer; } } return null; } private static void Warn(string message) { Plugin.Log.LogWarning((object)message); if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)1, message, 0, (Sprite)null, false, true); } } } internal static class FightTracker { private class Fight { public string BossName; public Vector3 BossPos; public readonly List<Vector3> Players = new List<Vector3>(); public float LastSeen; } private class RemoteFight { public string PlayerName; public string BossNames; public float Until; } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class NewConnectionPatch { private static void Postfix(ZNet __instance, ZNetPeer __0) { if (__instance.IsServer()) { __0.m_rpc.Register<string>("UndisturbedBossFights_Fight", (Action<ZRpc, string>)OnReport); } } } private const float ScanInterval = 1f; private const float EndDelay = 20f; private const float ReportInterval = 2f; private const float ReportTimeout = 6f; private const string ReportRpc = "UndisturbedBossFights_Fight"; private static readonly Dictionary<Character, Fight> fights = new Dictionary<Character, Fight>(); private static readonly Dictionary<long, RemoteFight> remoteFights = new Dictionary<long, RemoteFight>(); private static readonly List<Character> toRemove = new List<Character>(); private static readonly List<long> remoteToRemove = new List<long>(); private static float scanTimer; private static float reportTimer; private static bool wasActive; internal static bool AnyFightActive { get { if (fights.Count <= 0) { return remoteFights.Count > 0; } return true; } } internal static void Update() { if ((Object)(object)ZNet.instance == (Object)null) { fights.Clear(); remoteFights.Clear(); wasActive = false; return; } scanTimer += Time.deltaTime; if (scanTimer >= 1f) { scanTimer = 0f; Scan(); ExpireRemote(); } if (!ZNet.instance.IsServer() && fights.Count > 0) { reportTimer += Time.deltaTime; if (reportTimer >= 2f) { reportTimer = 0f; Report(); } } else { reportTimer = 2f; } bool anyFightActive = AnyFightActive; if (anyFightActive != wasActive) { wasActive = anyFightActive; Plugin.Debug(anyFightActive ? "A boss fight started: raids and nearby spawns are held back." : "No boss fight anymore: raids and spawns are back to normal."); } } private static void Scan() { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) float time = Time.time; float value = Plugin.FightDistance.Value; foreach (Character allCharacter in Character.GetAllCharacters()) { if ((Object)(object)allCharacter == (Object)null || !allCharacter.IsBoss() || allCharacter.IsDead()) { continue; } BaseAI baseAI = allCharacter.GetBaseAI(); if ((Object)(object)baseAI == (Object)null || (!baseAI.IsAlerted() && !baseAI.HaveTarget())) { continue; } Vector3 position = ((Component)allCharacter).transform.position; Fight value2 = null; foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer == (Object)null || ((Character)allPlayer).IsDead()) { continue; } Vector3 position2 = ((Component)allPlayer).transform.position; if (Vector3.Distance(position2, position) > value) { continue; } if (value2 == null) { if (!fights.TryGetValue(allCharacter, out value2)) { value2 = new Fight(); value2.BossName = allCharacter.GetHoverName(); fights[allCharacter] = value2; Plugin.Debug("Boss fight with " + value2.BossName + " at " + ((Vector3)(ref position)).ToString("0") + "."); } value2.Players.Clear(); value2.BossPos = position; value2.LastSeen = time; } value2.Players.Add(position2); } } toRemove.Clear(); foreach (KeyValuePair<Character, Fight> fight in fights) { if (time - fight.Value.LastSeen > 20f) { toRemove.Add(fight.Key); } } foreach (Character item in toRemove) { Plugin.Debug("Boss fight with " + fights[item].BossName + " is over."); fights.Remove(item); } } internal static bool IsNearFight(Vector3 pos, out string bossName) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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) //IL_006d: Unknown result type (might be due to invalid IL or missing references) float value = Plugin.SpawnBlockRadius.Value; float num = value * value; foreach (Fight value2 in fights.Values) { bossName = value2.BossName; Vector3 val = value2.BossPos - pos; if (((Vector3)(ref val)).sqrMagnitude <= num) { return true; } foreach (Vector3 player in value2.Players) { Vector3 val2 = player - pos; if (((Vector3)(ref val2)).sqrMagnitude <= num) { return true; } } } bossName = null; return false; } private static string LocalBossNames() { List<string> list = new List<string>(); foreach (Fight value in fights.Values) { list.Add(value.BossName); } return string.Join(", ", list.ToArray()); } private static void Report() { ZRpc serverRPC = ZNet.instance.GetServerRPC(); if (serverRPC != null) { serverRPC.Invoke("UndisturbedBossFights_Fight", new object[1] { LocalBossNames() }); } } private static void OnReport(ZRpc rpc, string bossNames) { ZNetPeer val = null; foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.m_rpc == rpc) { val = peer; } } if (val != null) { if (!remoteFights.TryGetValue(val.m_uid, out var value)) { value = new RemoteFight(); remoteFights[val.m_uid] = value; Plugin.Debug(val.m_playerName + " is in a boss fight with " + bossNames + "."); } value.PlayerName = val.m_playerName; value.BossNames = bossNames; value.Until = Time.time + 6f; } } private static void ExpireRemote() { remoteToRemove.Clear(); foreach (KeyValuePair<long, RemoteFight> remoteFight in remoteFights) { if (Time.time > remoteFight.Value.Until) { remoteToRemove.Add(remoteFight.Key); } } foreach (long item in remoteToRemove) { Plugin.Debug(remoteFights[item].PlayerName + "'s boss fight is over."); remoteFights.Remove(item); } } internal static IEnumerable<string> Describe() { foreach (Fight fight in fights.Values) { yield return fight.BossName + " at " + ((Vector3)(ref fight.BossPos)).ToString("0") + ", " + fight.Players.Count + " player(s) near, last seen fighting " + (Time.time - fight.LastSeen).ToString("0") + " s ago"; } foreach (RemoteFight fight2 in remoteFights.Values) { yield return fight2.PlayerName + " is fighting " + fight2.BossNames + " (reported by their game)"; } } } [HarmonyPatch(typeof(RandEventSystem), "UpdateRandomEvent")] internal static class RaidPatch { private const float Margin = 0.01f; private static bool holding; internal static bool Holding => holding; private static void Prefix(float __0, ref float ___m_eventTimer, float ___m_eventIntervalMin, List<RandomEvent> ___m_events, RandomEvent ___m_activeEvent) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } if (!Plugin.DelayRaids.Value || !FightTracker.AnyFightActive || Game.m_eventRate <= 0f) { if (holding) { holding = false; Plugin.Debug("Boss fight over: the held raid roll happens now."); } return; } bool flag = false; float num = ___m_eventIntervalMin * 60f * Game.m_eventRate; if (___m_eventTimer + __0 > num) { ___m_eventTimer = num - __0 - 0.01f; flag = true; } foreach (RandomEvent ___m_event in ___m_events) { if (___m_event.m_enabled && !(___m_event.m_standaloneInterval <= 0f) && ___m_event != ___m_activeEvent) { float num2 = ___m_event.m_standaloneInterval * Game.m_eventRate; if (___m_event.m_time + __0 > num2) { ___m_event.m_time = num2 - __0 - 0.01f; flag = true; } } } if (flag && !holding) { holding = true; Plugin.Debug("A raid roll is due, but a boss fight is going on. Holding it until the fight is over."); } } } [HarmonyPatch(typeof(SpawnSystem), "Spawn")] internal static class SpawnPatch { private static bool Prefix(SpawnData __0, Vector3 __1, bool __2) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (__2 || !Plugin.BlockSpawns.Value) { return true; } if (!FightTracker.IsNearFight(__1, out var bossName)) { return true; } Plugin.Debug("Blocked a " + ((Object)__0.m_prefab).name + " from spawning at " + ((Vector3)(ref __1)).ToString("0") + " during the fight with " + bossName + "."); return false; } } [BepInPlugin("UndisturbedBossFights", "UndisturbedBossFights", "1.0.1")] public class Plugin : BaseUnityPlugin { public const string Guid = "UndisturbedBossFights"; public const string ModName = "UndisturbedBossFights"; public const string Version = "1.0.1"; internal static Synced<bool> DelayRaids; internal static Synced<bool> BlockSpawns; internal static Synced<float> SpawnBlockRadius; internal static Synced<float> FightDistance; internal static ConfigEntry<bool> DebugLog; internal static Plugin Instance; internal static ManualLogSource Log; private Synced<T> Bind<T>(string section, string key, T defaultValue, ConfigDescription description) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown return new Synced<T>(((BaseUnityPlugin)this).Config.Bind<T>(section, key, defaultValue, new ConfigDescription(description.Description + " In multiplayer, the server's value is used.", description.AcceptableValues, new object[0]))); } private Synced<T> Bind<T>(string section, string key, T defaultValue, string description) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown return Bind(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, new object[0])); } private void Awake() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown //IL_00de: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; DelayRaids = Bind("1 - General", "DelayRaids", defaultValue: true, "While a boss fight is going on, no raid (random event) can start. A raid that was due waits until the fight is over, then the game rolls for it as usual. Raids started from the console are not held back."); BlockSpawns = Bind("1 - General", "BlockSpawnsNearFight", defaultValue: true, "While a boss fight is going on, no new creatures spawn naturally near the boss or the players fighting it. Creatures already there, the boss's own summons and creatures of a raid that was already running are not affected."); SpawnBlockRadius = Bind("1 - General", "SpawnBlockRadius", 80f, new ConfigDescription("How close (in meters) to the boss or a player fighting it a new creature may not spawn. The game spawns creatures 40-80 m from a player, so 80 blocks almost all of them.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 200f), new object[0])); FightDistance = Bind("1 - General", "FightDistance", 60f, new ConfigDescription("A boss counts as being fought when it is alerted or has a target and a player is within this many meters of it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 150f), new object[0])); DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "DebugLog", false, "Writes boss fights, held-back raids and blocked spawns to the BepInEx log. Useful when reporting a problem. Not synced in multiplayer."); new Harmony("UndisturbedBossFights").PatchAll(typeof(Plugin).Assembly); } private void Update() { FightTracker.Update(); } internal static void Debug(string message) { if (DebugLog.Value) { Log.LogInfo((object)message); } } }