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 MobChaseControl v1.2.0
plugins/MobChaseControl.dll
Decompiled 15 hours agousing System; using System.Collections.Generic; using System.Globalization; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace MobChaseControl; [BepInPlugin("flo.mobchasecontrol", "MobChaseControl", "1.2.0")] public class MobChasePlugin : BaseUnityPlugin { internal class ConfigurationManagerAttributes { public bool? ReadOnly; } private struct Orig { public float view; public float hear; public float chase; } public const string GUID = "flo.mobchasecontrol"; public const string NAME = "MobChaseControl"; public const string VERSION = "1.2.0"; internal const float VanillaGiveUpTime = 30f; private const string RpcName = "MobChaseControl_Cfg"; internal const string RpcSet = "MobChaseControl_Set"; private const float PushInterval = 2f; internal static ManualLogSource Log; internal static MobChasePlugin Instance; internal static ConfigEntry<bool> cfgEnabled; internal static ConfigEntry<bool> cfgServerSync; internal static ConfigEntry<float> cfgMaxChaseDistance; internal static ConfigEntry<float> cfgGiveUpTime; internal static ConfigEntry<float> cfgViewRangeMult; internal static ConfigEntry<float> cfgHearRangeMult; internal static ConfigEntry<bool> cfgExcludeBosses; internal static ConfigEntry<bool> cfgExcludeTamed; internal static ConfigEntry<string> cfgExcludedPrefabs; internal static bool ServerOverride; internal static bool IsAdminHere; private static bool applyingFromServer; private static bool sEnabled = true; private static float sMaxChase = -1f; private static float sGiveUp = -1f; private static float sView = 1f; private static float sHear = 1f; private static bool sExclBoss = true; private static bool sExclTamed = true; private static HashSet<string> sExcluded = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly List<ConfigurationManagerAttributes> LockAttributes = new List<ConfigurationManagerAttributes>(); private static HashSet<string> sLocalExcludedCache; private static string sLocalExcludedRaw; private static readonly Dictionary<string, Orig> Originals = new Dictionary<string, Orig>(); private static readonly ConditionalWeakTable<MonsterAI, float[]> LastApplied = new ConditionalWeakTable<MonsterAI, float[]>(); private Harmony harmony; internal float pushTimer; private float checkTimer; private string lastSignature = ""; private int lastPeerCount = -1; internal static bool EffEnabled { get { if (ServerOverride) { return sEnabled; } if (IsRemoteClient) { return false; } return cfgEnabled.Value; } } internal static float EffMaxChase => (!ServerOverride) ? cfgMaxChaseDistance.Value : sMaxChase; internal static float EffGiveUp => (!ServerOverride) ? cfgGiveUpTime.Value : sGiveUp; internal static float EffView => (!ServerOverride) ? cfgViewRangeMult.Value : sView; internal static float EffHear => (!ServerOverride) ? cfgHearRangeMult.Value : sHear; internal static bool EffExclBoss => (!ServerOverride) ? cfgExcludeBosses.Value : sExclBoss; internal static bool EffExclTamed => (!ServerOverride) ? cfgExcludeTamed.Value : sExclTamed; internal static HashSet<string> EffExcluded => (!ServerOverride) ? ParseList(cfgExcludedPrefabs.Value) : sExcluded; internal static bool IsRemoteClient => (Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer(); private static ConfigDescription Desc(string text, AcceptableValueBase range) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes(); configurationManagerAttributes.ReadOnly = false; ConfigurationManagerAttributes configurationManagerAttributes2 = configurationManagerAttributes; LockAttributes.Add(configurationManagerAttributes2); return (range != null) ? new ConfigDescription(text, range, new object[1] { configurationManagerAttributes2 }) : new ConfigDescription(text, (AcceptableValueBase)null, new object[1] { configurationManagerAttributes2 }); } private static void SetLocked(bool locked) { for (int i = 0; i < LockAttributes.Count; i++) { LockAttributes[i].ReadOnly = locked; } } private static HashSet<string> ParseList(string raw) { if (sLocalExcludedCache != null && sLocalExcludedRaw == raw) { return sLocalExcludedCache; } HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); if (!string.IsNullOrEmpty(raw)) { string[] array = raw.Split(','); foreach (string text in array) { string text2 = text.Trim(); if (text2.Length > 0) { hashSet.Add(text2); } } } sLocalExcludedRaw = raw; sLocalExcludedCache = hashSet; return hashSet; } private static void Remember(MonsterAI ai) { LastApplied.Remove(ai); LastApplied.Add(ai, new float[3] { ((BaseAI)ai).m_viewRange, ((BaseAI)ai).m_hearRange, ai.m_maxChaseDistance }); } private static bool ForeignChange(MonsterAI ai) { if (!LastApplied.TryGetValue(ai, out var value)) { return false; } return Math.Abs(value[0] - ((BaseAI)ai).m_viewRange) > 0.001f || Math.Abs(value[1] - ((BaseAI)ai).m_hearRange) > 0.001f || Math.Abs(value[2] - ai.m_maxChaseDistance) > 0.001f; } private void Awake() { //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; cfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Allgemein", "Enabled", true, Desc("Mod aktiv. false = alles bleibt Vanilla.\nAuf Clients ohne Wirkung - dort gilt immer die Server-Einstellung.", null)); cfgMaxChaseDistance = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Verfolgung", "MaxChaseDistance", 60f, Desc("Maximale Verfolgungsdistanz in Metern. Ab dieser Entfernung verliert ein Mob sein Ziel.\n-1 = Vanilla-Wert des jeweiligen Mobs unverändert lassen\n 0 = unbegrenzt (Mob gibt nie wegen Entfernung auf)\n>0 = Meter (greift erst, wenn der Mob das Ziel >1s nicht mehr wahrgenommen hat)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 2000f))); cfgGiveUpTime = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Verfolgung", "GiveUpTime", 30f, Desc("Sekunden ohne Wahrnehmung des Ziels, bis der Mob aufgibt (Vanilla: 30).\n-1 = unverändert lassen.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 3600f))); cfgViewRangeMult = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Wahrnehmung", "ViewRangeMultiplier", 1f, Desc("Multiplikator für die Sichtweite aller Mobs (1 = Vanilla).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 20f))); cfgHearRangeMult = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Wahrnehmung", "HearRangeMultiplier", 1f, Desc("Multiplikator für die Hörweite aller Mobs (1 = Vanilla).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 20f))); cfgExcludeBosses = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Ausnahmen", "ExcludeBosses", true, Desc("Bosse (Eikthyr, Elder, Bonemass, Moder, Yagluth, Queen, Fader) nicht verändern.", null)); cfgExcludeTamed = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Ausnahmen", "ExcludeTamed", true, Desc("Gezähmte Tiere nicht verändern.", null)); cfgExcludedPrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("4 - Ausnahmen", "ExcludedPrefabs", "", Desc("Zusätzliche Prefab-Namen, kommagetrennt, die unverändert bleiben. Beispiel: Greydwarf,Neck,Boar", null)); cfgServerSync = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Server", "ServerSync", true, "Nur auf dem Server relevant: true = der Server erzwingt seine Werte bei allen Clients.\nAuf Clients hat diese Option keine Wirkung."); ((BaseUnityPlugin)this).Config.SettingChanged += OnLocalSettingChanged; harmony = new Harmony("flo.mobchasecontrol"); harmony.PatchAll(Assembly.GetExecutingAssembly()); Log.LogInfo((object)"MobChaseControl 1.2.0 geladen."); } private void OnDestroy() { if (harmony != null) { harmony.UnpatchSelf(); } } private void Update() { //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown if ((Object)(object)ZNet.instance == (Object)null) { if (ServerOverride) { ServerOverride = false; IsAdminHere = false; SetLocked(locked: false); } return; } checkTimer -= Time.deltaTime; if (checkTimer <= 0f) { checkTimer = 1f; string text = BuildSignature(); if (text != lastSignature) { lastSignature = text; ApplyToAll(); } } if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !cfgServerSync.Value || ZRoutedRpc.instance == null) { return; } int count = ZNet.instance.GetPeers().Count; pushTimer -= Time.deltaTime; if (pushTimer > 0f && count == lastPeerCount) { return; } lastPeerCount = count; pushTimer = 2f; try { string text2 = Serialize(); List<ZNetPeer> peers = ZNet.instance.GetPeers(); for (int i = 0; i < peers.Count; i++) { ZNetPeer val = peers[i]; if (val != null) { bool flag = val.m_socket != null && ZNet.instance.IsAdmin(val.m_socket.GetHostName()); ZPackage val2 = new ZPackage(); val2.Write(text2 + "|" + ((!flag) ? "0" : "1")); ZRoutedRpc.instance.InvokeRoutedRPC(val.m_uid, "MobChaseControl_Cfg", new object[1] { val2 }); } } } catch (Exception ex) { Log.LogWarning((object)("Config-Push fehlgeschlagen: " + ex.Message)); } } private static string BuildSignature() { return string.Join("|", (!EffEnabled) ? "0" : "1", EffMaxChase.ToString(CultureInfo.InvariantCulture), EffGiveUp.ToString(CultureInfo.InvariantCulture), EffView.ToString(CultureInfo.InvariantCulture), EffHear.ToString(CultureInfo.InvariantCulture), (!EffExclBoss) ? "0" : "1", (!EffExclTamed) ? "0" : "1", string.Join(",", new List<string>(EffExcluded).ToArray())); } private static string Serialize() { return "1;" + BuildSignature(); } internal static void RPC_Config(long sender, ZPackage pkg) { try { if (((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) || pkg == null) { return; } string text = pkg.ReadString(); if (string.IsNullOrEmpty(text)) { return; } int num = text.IndexOf(';'); if (num < 0) { return; } string[] array = text.Substring(num + 1).Split('|'); if (array.Length < 8) { return; } bool flag = array[0] == "1"; float num2 = float.Parse(array[1], CultureInfo.InvariantCulture); float num3 = float.Parse(array[2], CultureInfo.InvariantCulture); float num4 = float.Parse(array[3], CultureInfo.InvariantCulture); float num5 = float.Parse(array[4], CultureInfo.InvariantCulture); bool flag2 = array[5] == "1"; bool flag3 = array[6] == "1"; HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); string[] array2 = array[7].Split(','); foreach (string text2 in array2) { string text3 = text2.Trim(); if (text3.Length > 0) { hashSet.Add(text3); } } bool flag4 = !ServerOverride || flag != sEnabled || num2 != sMaxChase || num3 != sGiveUp || num4 != sView || num5 != sHear || flag2 != sExclBoss || flag3 != sExclTamed || !hashSet.SetEquals(sExcluded); sEnabled = flag; sMaxChase = num2; sGiveUp = num3; sView = num4; sHear = num5; sExclBoss = flag2; sExclTamed = flag3; sExcluded = hashSet; ServerOverride = true; IsAdminHere = array.Length > 8 && array[8] == "1"; SetLocked(!IsAdminHere); if (IsAdminHere) { MirrorToConfig(); } if (flag4) { Log.LogInfo((object)("Server-Konfiguration übernommen: Distanz=" + num2 + "m, GiveUp=" + num3 + "s, View x" + num4 + ", Hear x" + num5)); ApplyToAll(); } } catch (Exception ex) { Log.LogWarning((object)("Server-Config konnte nicht gelesen werden: " + ex.Message)); } } private static string ConfigString() { return string.Join("|", (!cfgEnabled.Value) ? "0" : "1", cfgMaxChaseDistance.Value.ToString(CultureInfo.InvariantCulture), cfgGiveUpTime.Value.ToString(CultureInfo.InvariantCulture), cfgViewRangeMult.Value.ToString(CultureInfo.InvariantCulture), cfgHearRangeMult.Value.ToString(CultureInfo.InvariantCulture), (!cfgExcludeBosses.Value) ? "0" : "1", (!cfgExcludeTamed.Value) ? "0" : "1", cfgExcludedPrefabs.Value.Replace('|', ',')); } private static void MirrorToConfig() { applyingFromServer = true; try { cfgEnabled.Value = sEnabled; cfgMaxChaseDistance.Value = sMaxChase; cfgGiveUpTime.Value = sGiveUp; cfgViewRangeMult.Value = sView; cfgHearRangeMult.Value = sHear; cfgExcludeBosses.Value = sExclBoss; cfgExcludeTamed.Value = sExclTamed; cfgExcludedPrefabs.Value = string.Join(",", new List<string>(sExcluded).ToArray()); } catch (Exception ex) { Log.LogWarning((object)("Spiegeln: " + ex.Message)); } finally { applyingFromServer = false; } } private void OnLocalSettingChanged(object sender, SettingChangedEventArgs e) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown if (applyingFromServer || !IsRemoteClient || !IsAdminHere) { return; } try { if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(ConfigString()); ZRoutedRpc.instance.InvokeRoutedRPC("MobChaseControl_Set", new object[1] { val }); } } catch (Exception ex) { Log.LogWarning((object)("Senden an Server fehlgeschlagen: " + ex.Message)); } } private static bool SenderIsAdmin(long sender) { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null) { return false; } ZNetPeer peer = instance.GetPeer(sender); if (peer == null || peer.m_socket == null) { return false; } return instance.IsAdmin(peer.m_socket.GetHostName()); } internal static void RPC_SetConfig(long sender, ZPackage pkg) { try { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || pkg == null) { return; } if (!SenderIsAdmin(sender)) { Log.LogWarning((object)("Konfigurationsänderung von Nicht-Admin abgelehnt (Peer " + sender + ").")); return; } string[] array = pkg.ReadString().Split('|'); if (array.Length >= 8) { applyingFromServer = true; try { cfgEnabled.Value = array[0] == "1"; cfgMaxChaseDistance.Value = float.Parse(array[1], CultureInfo.InvariantCulture); cfgGiveUpTime.Value = float.Parse(array[2], CultureInfo.InvariantCulture); cfgViewRangeMult.Value = float.Parse(array[3], CultureInfo.InvariantCulture); cfgHearRangeMult.Value = float.Parse(array[4], CultureInfo.InvariantCulture); cfgExcludeBosses.Value = array[5] == "1"; cfgExcludeTamed.Value = array[6] == "1"; cfgExcludedPrefabs.Value = array[7]; } finally { applyingFromServer = false; } Log.LogInfo((object)("Konfiguration durch Admin geändert (Peer " + sender + ").")); ApplyToAll(); if ((Object)(object)Instance != (Object)null) { Instance.pushTimer = 0f; } } } catch (Exception ex) { Log.LogWarning((object)("Adminänderung konnte nicht übernommen werden: " + ex.Message)); } } internal static string PrefabName(Component c) { if ((Object)(object)c == (Object)null) { return ""; } string name = ((Object)c.gameObject).name; int num = name.IndexOf("(Clone)"); return (num < 0) ? name : name.Substring(0, num); } internal static bool IsExcluded(MonsterAI ai, string prefab) { if (EffExcluded.Contains(prefab)) { return true; } Character component = ((Component)ai).GetComponent<Character>(); if ((Object)(object)component != (Object)null) { if (EffExclBoss && component.IsBoss()) { return true; } if (EffExclTamed && component.IsTamed()) { return true; } } return false; } internal static void Apply(MonsterAI ai) { if (!((Object)(object)ai == (Object)null)) { string text = PrefabName((Component)(object)ai); if (!Originals.TryGetValue(text, out var value)) { value = new Orig { view = ((BaseAI)ai).m_viewRange, hear = ((BaseAI)ai).m_hearRange, chase = ai.m_maxChaseDistance }; Originals[text] = value; } if (!EffEnabled || IsExcluded(ai, text)) { ((BaseAI)ai).m_viewRange = value.view; ((BaseAI)ai).m_hearRange = value.hear; ai.m_maxChaseDistance = value.chase; Remember(ai); } else { ((BaseAI)ai).m_viewRange = value.view * EffView; ((BaseAI)ai).m_hearRange = value.hear * EffHear; float effMaxChase = EffMaxChase; ai.m_maxChaseDistance = ((!(effMaxChase < 0f)) ? effMaxChase : value.chase); Remember(ai); } } } internal static void ApplyToAll() { try { List<BaseAI> baseAIInstances = BaseAI.BaseAIInstances; if (baseAIInstances == null) { return; } for (int i = 0; i < baseAIInstances.Count; i++) { BaseAI obj = baseAIInstances[i]; MonsterAI val = (MonsterAI)(object)((obj is MonsterAI) ? obj : null); if (!((Object)(object)val == (Object)null) && !ForeignChange(val)) { Apply(val); } } } catch (Exception ex) { Log.LogWarning((object)("ApplyToAll: " + ex.Message)); } } public static float GiveUpTimeFor(MonsterAI ai) { if (!EffEnabled) { return 30f; } float effGiveUp = EffGiveUp; if (effGiveUp < 0f) { return 30f; } if ((Object)(object)ai != (Object)null && IsExcluded(ai, PrefabName((Component)(object)ai))) { return 30f; } return effGiveUp; } } [HarmonyPatch(typeof(MonsterAI), "Awake")] internal static class Patch_MonsterAI_Awake { private static void Postfix(MonsterAI __instance) { MobChasePlugin.Apply(__instance); } } [HarmonyPatch(typeof(MonsterAI), "MakeTame")] internal static class Patch_MonsterAI_MakeTame { private static void Postfix(MonsterAI __instance) { MobChasePlugin.Apply(__instance); } } [HarmonyPatch(typeof(MonsterAI), "UpdateTarget")] internal static class Patch_MonsterAI_UpdateTarget { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Expected O, but got Unknown FieldInfo fieldInfo = AccessTools.Field(typeof(MonsterAI), "m_timeSinceSensedTargetCreature"); MethodInfo methodInfo = AccessTools.Method(typeof(MobChasePlugin), "GiveUpTimeFor", (Type[])null, (Type[])null); List<CodeInstruction> list = new List<CodeInstruction>(instructions); bool flag = false; for (int i = 1; i < list.Count; i++) { if (flag) { break; } if (!(list[i].opcode != OpCodes.Ldc_R4) && list[i].operand is float && !(Math.Abs((float)list[i].operand - 30f) > 0.001f) && !(list[i - 1].opcode != OpCodes.Ldfld) && list[i - 1].operand is FieldInfo && !((FieldInfo)list[i - 1].operand != fieldInfo)) { list[i].opcode = OpCodes.Ldarg_0; list[i].operand = null; list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)methodInfo)); flag = true; } } if (!flag) { MobChasePlugin.Log.LogError((object)"Transpiler: GiveUpTime-Konstante in MonsterAI.UpdateTarget nicht gefunden - GiveUpTime bleibt bei 30s (Spielversion geändert?)."); } else { MobChasePlugin.Log.LogInfo((object)"Transpiler: GiveUpTime patchbar."); } return list; } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class Patch_ZRoutedRpc_Ctor { private static void Postfix(ZRoutedRpc __instance) { __instance.Register<ZPackage>("MobChaseControl_Cfg", (Action<long, ZPackage>)MobChasePlugin.RPC_Config); __instance.Register<ZPackage>("MobChaseControl_Set", (Action<long, ZPackage>)MobChasePlugin.RPC_SetConfig); } }