Decompiled source of PastureLights v0.1.1

plugins/PastureLights/PastureLights.dll

Decompiled a week ago
using 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;
		}
	}
}