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 PastureLights v0.1.1
plugins/PastureLights/PastureLights.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using ConditionalConfigSync; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("PastureLights")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.1.0")] [assembly: AssemblyInformationalVersion("0.1.1")] [assembly: AssemblyProduct("PastureLights")] [assembly: AssemblyTitle("PastureLights")] [assembly: AssemblyVersion("0.1.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 PastureLights { [BepInPlugin("vercadi.PastureLights", "Pasture Lights", "0.1.1")] [BepInDependency("_shudnal.ConditionalConfigSync", "1.0.9")] public sealed class Plugin : BaseUnityPlugin { private sealed class Identity { internal readonly string Prefab; internal Identity(BaseAI ai) { Prefab = Policy.PrefabName(((Object)((Component)ai).gameObject).name); } } public const string Id = "vercadi.PastureLights"; private Harmony harmony; private ConfigSync sync; private ConfigEntry<bool> featureEnabled; private ConfigEntry<bool> debugLogging; private float nextDebugTime; private int debugLines; private bool runtimeReady; private ConfigEntry<string> animals; private ConfigEntry<string> lights; private static Plugin instance; private static Policy policy; private static int areaMask; private static Collider[] overlapBuffer = (Collider[])(object)new Collider[128]; private static readonly ConditionalWeakTable<BaseAI, Identity> identities = new ConditionalWeakTable<BaseAI, Identity>(); private static FieldRef<BaseAI, EffectArea> rememberedFire; private static readonly MethodInfo OriginalAreaQuery = AccessTools.Method(typeof(EffectArea), "IsPointInsideArea", new Type[3] { typeof(Vector3), typeof(Type), typeof(float) }, (Type[])null); private static readonly MethodInfo FilteredAreaQuery = AccessTools.Method(typeof(Plugin), "FindFire", (Type[])null, (Type[])null); private static readonly MethodInfo FilteredMovementQuery = AccessTools.Method(typeof(Plugin), "FindMovementFire", (Type[])null, (Type[])null); private void Awake() { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Expected O, but got Unknown //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Expected O, but got Unknown //IL_02be: Expected O, but got Unknown //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Expected O, but got Unknown instance = this; featureEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Ignore listed placed lights for the listed animals. Use the same settings on every client and the server."); animals = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Animal prefabs", "Deer,Boar,Sheep_TW", "Exact prefab names. Defaults: deer, boar and Wizardry Darkhorn. Other animals retain their current behavior."); lights = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Placed light prefabs", "piece_groundtorch_wood,piece_groundtorch,piece_groundtorch_green,piece_groundtorch_blue,piece_walltorch,piece_brazierfloor01,piece_brazierfloor02,piece_brazierceiling01,GB_Standing_Torch,GB_Wall_Torch,GB_Standing_Brazier,mm_horn_walltorch,mm_corewood_brazier", "Exact placed-piece names. Held torches are always preserved. Campfires and unknown sources remain frightening unless explicitly listed."); debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Debug logging", false, "Local only. Log filtered fire checks for selected animals, at most one line every two seconds and 60 lines per game launch. Shows retained/ignored sources and whether the check concerns avoidance or a walking destination. No positions or player identifiers are logged."); sync = new ConfigSync("vercadi.PastureLights") { DisplayName = "Pasture Lights", CurrentVersion = "0.1.1", MinimumRequiredVersion = "0.1.1", ModRequired = true }; ((ConditionalConfigSync)sync).AddLockingConfigEntry<bool>(((BaseUnityPlugin)this).Config.Bind<bool>("General", "Lock server settings", true, "Only server administrators may change shared gameplay settings.")); ((ConditionalConfigSync)sync).AddConfigEntry<bool>(featureEnabled, (ConfigSyncMode)0); ((ConditionalConfigSync)sync).AddConfigEntry<string>(animals, (ConfigSyncMode)0); ((ConditionalConfigSync)sync).AddConfigEntry<string>(lights, (ConfigSyncMode)0); ReloadPolicy(); animals.SettingChanged += SettingsChanged; lights.SettingChanged += SettingsChanged; try { rememberedFire = AccessTools.FieldRefAccess<BaseAI, EffectArea>("m_nearFireArea"); areaMask = LayerMask.GetMask(new string[1] { "character_trigger" }); if (areaMask == 0 || OriginalAreaQuery == null || FilteredAreaQuery == null || FilteredMovementQuery == null) { throw new NotSupportedException("Expected game fire-query contract was not found."); } MethodInfo methodInfo = AccessTools.Method(typeof(BaseAI), "AvoidFire", new Type[3] { typeof(float), typeof(Character), typeof(bool) }, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(BaseAI), "IsValidRandomMovePoint", new Type[1] { typeof(Vector3) }, (Type[])null); if (methodInfo == null || methodInfo.ReturnType != typeof(bool) || methodInfo2 == null || methodInfo2.ReturnType != typeof(bool)) { throw new NotSupportedException("Expected BaseAI fire-avoidance or walking-destination signature was not found."); } harmony = new Harmony("vercadi.PastureLights"); harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "ClearIgnoredMemory", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "FilterFireQueries", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "FilterMovementQuery", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo); Patches patchInfo2 = Harmony.GetPatchInfo((MethodBase)methodInfo2); if (patchInfo == null || patchInfo.Transpilers.Count((Patch p) => p.owner == "vercadi.PastureLights") != 1 || patchInfo.Prefixes.Count((Patch p) => p.owner == "vercadi.PastureLights") != 1 || patchInfo2 == null || patchInfo2.Transpilers.Count((Patch p) => p.owner == "vercadi.PastureLights") != 1) { throw new InvalidOperationException("Patch registration could not be verified."); } runtimeReady = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("ACTIVE: placed-light fire-avoidance and walking-destination filters registered for " + animals.Value + ". Held torches, other animals, player fear, water/lava checks and fire damage are unchanged.")); } catch (Exception ex) { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } runtimeReady = false; ((BaseUnityPlugin)this).Logger.LogError((object)("INACTIVE: no pasture-light changes applied. " + ex)); } } private void SettingsChanged(object sender, EventArgs args) { ReloadPolicy(); } private void ReloadPolicy() { policy = new Policy(animals.Value, lights.Value); } private static bool Applies(BaseAI ai) { if (Object.op_Implicit((Object)(object)instance) && instance.runtimeReady && instance.featureEnabled.Value && Object.op_Implicit((Object)(object)ai)) { return policy.AppliesTo(identities.GetValue(ai, (BaseAI key) => new Identity(key)).Prefab); } return false; } private static bool Ignored(EffectArea area) { if (!Object.op_Implicit((Object)(object)area)) { return false; } if (Object.op_Implicit((Object)(object)((Component)area).GetComponentInParent<Character>())) { return false; } Piece componentInParent = ((Component)area).GetComponentInParent<Piece>(); if (Object.op_Implicit((Object)(object)componentInParent)) { return policy.IgnoreLight(((Object)((Component)componentInParent).gameObject).name, hasPiece: true, heldByCharacter: false); } return false; } private static void ClearIgnoredMemory(BaseAI __instance) { if (Applies(__instance) && Ignored(rememberedFire.Invoke(__instance))) { rememberedFire.Invoke(__instance) = null; } } internal static IEnumerable<CodeInstruction> FilterFireQueries(IEnumerable<CodeInstruction> input) { return RewriteQueries(input, 3, FilteredAreaQuery, "AvoidFire"); } internal static IEnumerable<CodeInstruction> FilterMovementQuery(IEnumerable<CodeInstruction> input) { return RewriteQueries(input, 1, FilteredMovementQuery, "IsValidRandomMovePoint"); } private static IEnumerable<CodeInstruction> RewriteQueries(IEnumerable<CodeInstruction> input, int expected, MethodInfo replacement, string method) { List<CodeInstruction> list = input.ToList(); if (list.Count((CodeInstruction i) => CodeInstructionExtensions.Calls(i, OriginalAreaQuery)) != expected) { throw new NotSupportedException(method + " no longer has the reviewed area queries; leaving behavior unchanged."); } foreach (CodeInstruction item in list) { if (!CodeInstructionExtensions.Calls(item, OriginalAreaQuery)) { yield return item; continue; } CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_0, (object)null); val.labels.AddRange(item.labels); val.blocks.AddRange(item.blocks); yield return val; yield return new CodeInstruction(OpCodes.Call, (object)replacement); } } private static EffectArea FindFire(Vector3 point, Type type, float radius, BaseAI ai) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) return FindFilteredFire(point, type, radius, ai, "fire avoidance"); } private static EffectArea FindMovementFire(Vector3 point, Type type, float radius, BaseAI ai) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) return FindFilteredFire(point, type, radius, ai, "walking destination"); } private static EffectArea FindFilteredFire(Vector3 point, Type type, float radius, BaseAI ai, string context) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (!Applies(ai) || (int)type != 2) { return EffectArea.IsPointInsideArea(point, type, radius); } int num; while (true) { num = Physics.OverlapSphereNonAlloc(point, radius, overlapBuffer, areaMask); if (num < overlapBuffer.Length) { break; } if (overlapBuffer.Length >= 4096) { Collider[] array = Physics.OverlapSphere(point, radius, areaMask); return SelectArea(array, array.Length, type, ai, context); } Array.Resize(ref overlapBuffer, overlapBuffer.Length * 2); } return SelectArea(overlapBuffer, num, type, ai, context); } private static EffectArea SelectArea(Collider[] colliders, int count, Type type, BaseAI ai, string context) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) EffectArea val = null; bool flag = instance.debugLogging.Value && instance.debugLines < 60 && Time.realtimeSinceStartup >= instance.nextDebugTime; int num = 0; string text = null; for (int i = 0; i < count; i++) { Collider val2 = colliders[i]; colliders[i] = null; if ((Object.op_Implicit((Object)(object)val) && !flag) || !Object.op_Implicit((Object)(object)val2)) { continue; } EffectArea component = ((Component)val2).GetComponent<EffectArea>(); if (!Object.op_Implicit((Object)(object)component) || (component.m_type & type) == 0) { continue; } if (Ignored(component)) { if (flag) { num++; if (text == null) { text = DescribeSource(component); } } } else if (!Object.op_Implicit((Object)(object)val)) { val = component; } } if (flag) { instance.nextDebugTime = Time.realtimeSinceStartup + 2f; instance.debugLines++; ((BaseUnityPlugin)instance).Logger.LogInfo((object)("TRACE " + identities.GetValue(ai, (BaseAI key) => new Identity(key)).Prefab + " / " + context + ": ignored placed sources=" + num + " (first=" + (text ?? "none") + "); retained=" + DescribeSource(val) + "; alerted=" + ai.IsAlerted() + ". Query outcome only; this does not identify player/pathfinding flee decisions.")); } return val; } private static string DescribeSource(EffectArea area) { if (!Object.op_Implicit((Object)(object)area)) { return "none"; } if (Object.op_Implicit((Object)(object)((Component)area).GetComponentInParent<Character>())) { return "carried source/" + ((Object)((Component)area).gameObject).name; } Piece componentInParent = ((Component)area).GetComponentInParent<Piece>(); if (!Object.op_Implicit((Object)(object)componentInParent)) { return "unclassified/" + ((Object)((Component)area).gameObject).name; } return Policy.PrefabName(((Object)((Component)componentInParent).gameObject).name); } private void OnDestroy() { animals.SettingChanged -= SettingsChanged; lights.SettingChanged -= SettingsChanged; Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if ((Object)(object)instance == (Object)(object)this) { instance = null; } } } internal sealed class Policy { internal const string DefaultAnimals = "Deer,Boar,Sheep_TW"; internal const string DefaultLights = "piece_groundtorch_wood,piece_groundtorch,piece_groundtorch_green,piece_groundtorch_blue,piece_walltorch,piece_brazierfloor01,piece_brazierfloor02,piece_brazierceiling01,GB_Standing_Torch,GB_Wall_Torch,GB_Standing_Brazier,mm_horn_walltorch,mm_corewood_brazier"; private readonly HashSet<string> animals; private readonly HashSet<string> lights; internal Policy(string animalNames, string lightNames) { animals = Parse(animalNames); lights = Parse(lightNames); } internal bool AppliesTo(string name) { return animals.Contains(PrefabName(name)); } internal bool IgnoreLight(string name, bool hasPiece, bool heldByCharacter) { if (hasPiece && !heldByCharacter) { return lights.Contains(PrefabName(name)); } return false; } internal static string PrefabName(string name) { if (name == null) { return ""; } if (!name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name; } return name.Substring(0, name.Length - "(Clone)".Length); } private static HashSet<string> Parse(string names) { HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); string[] array = (names ?? "").Split(new char[1] { ',' }); foreach (string text in array) { if (!string.IsNullOrWhiteSpace(text)) { hashSet.Add(text.Trim()); } } return hashSet; } } }