Decompiled source of BaseFood v0.2.0

BepInEx/plugins/BaseFood/BaseFood.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
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: InternalsVisibleTo("PatchTests")]
[assembly: AssemblyCompany("BaseFood")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0")]
[assembly: AssemblyProduct("BaseFood")]
[assembly: AssemblyTitle("BaseFood")]
[assembly: AssemblyVersion("0.2.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 BaseFood
{
	internal sealed class BaseDetector
	{
		private readonly bool requireAll;

		private readonly ProtectionClock protection = new ProtectionClock();

		private readonly TrackedStructure?[] cached = new TrackedStructure[3];

		private float nextDiscovery;

		private float nextValidation;

		private bool searching;

		private int phase;

		private int cursor;

		private int searchRevision;

		internal BaseDetector(bool requireAll = true)
		{
			this.requireAll = requireAll;
		}

		internal bool IsProtected(float now)
		{
			return protection.IsProtected(now);
		}

		internal void Reset()
		{
			protection.Reset();
			for (int i = 0; i < cached.Length; i++)
			{
				cached[i] = null;
			}
			nextDiscovery = (nextValidation = 0f);
			searching = false;
			phase = (cursor = 0);
		}

		internal void Tick(Vector3 position, float now, float radius, float grace, float discoveryInterval, float validationInterval, int budget)
		{
			//IL_000e: 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_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			float radiusSquared = radius * radius;
			if (now >= nextValidation)
			{
				Validate(position, radiusSquared, now, grace);
				nextValidation = now + validationInterval;
			}
			if (!searching && now >= nextDiscovery)
			{
				nextDiscovery = now + discoveryInterval;
				if (!Validate(position, radiusSquared, now, grace))
				{
					searching = true;
					phase = (cursor = 0);
					searchRevision = StructureRegistry.Revision;
				}
			}
			if (!searching)
			{
				return;
			}
			if (searchRevision != StructureRegistry.Revision)
			{
				phase = (cursor = 0);
				searchRevision = StructureRegistry.Revision;
			}
			while (phase < 3 && budget > 0)
			{
				TrackedStructure trackedStructure = cached[phase];
				if (Object.op_Implicit((Object)(object)trackedStructure) && trackedStructure.Qualifies(position, radiusSquared))
				{
					phase = (requireAll ? (phase + 1) : 3);
					cursor = 0;
					budget--;
					continue;
				}
				cached[phase] = null;
				List<TrackedStructure> list = StructureRegistry.Lists[phase];
				if (cursor >= list.Count)
				{
					phase++;
					cursor = 0;
					continue;
				}
				trackedStructure = list[cursor++];
				budget--;
				if (Object.op_Implicit((Object)(object)trackedStructure) && trackedStructure.Qualifies(position, radiusSquared))
				{
					cached[phase] = trackedStructure;
					phase = (requireAll ? (phase + 1) : 3);
					cursor = 0;
				}
			}
			if (phase >= 3)
			{
				searching = false;
				nextDiscovery = now + discoveryInterval;
				Validate(position, radiusSquared, now, grace);
			}
		}

		private bool Validate(Vector3 position, float radiusSquared, float now, float grace)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			bool flag = true;
			bool flag2 = false;
			for (int i = 0; i < cached.Length; i++)
			{
				TrackedStructure trackedStructure = cached[i];
				if (!Object.op_Implicit((Object)(object)trackedStructure) || !trackedStructure.Qualifies(position, radiusSquared))
				{
					cached[i] = null;
					flag = false;
				}
				else
				{
					flag2 = true;
				}
			}
			bool flag3 = (requireAll ? flag : flag2);
			if (!requireAll && protection.ConditionsMet && !flag3)
			{
				nextDiscovery = now;
			}
			protection.Observe(flag3, now, grace);
			return flag3;
		}
	}
	internal sealed class ProtectionClock
	{
		private double graceUntil = double.NegativeInfinity;

		public bool ConditionsMet { get; private set; }

		public void Observe(bool valid, double now, double graceSeconds)
		{
			if (ConditionsMet && !valid)
			{
				graceUntil = now + Math.Max(0.0, graceSeconds);
			}
			if (valid)
			{
				graceUntil = double.NegativeInfinity;
			}
			ConditionsMet = valid;
		}

		public bool IsProtected(double now)
		{
			if (!ConditionsMet)
			{
				return now < graceUntil;
			}
			return true;
		}

		public void Reset()
		{
			ConditionsMet = false;
			graceUntil = double.NegativeInfinity;
		}
	}
	internal static class FoodRules
	{
		public static float Multiplier(bool enabled, bool protectedAtBase, bool rested, float reductionPercent)
		{
			if (!enabled)
			{
				return 1f;
			}
			if (protectedAtBase)
			{
				return 0f;
			}
			if (!rested)
			{
				return 1f;
			}
			return 1f - Math.Min(100f, Math.Max(0f, reductionPercent)) / 100f;
		}
	}
	internal static class DiscoveryBudget
	{
		internal static void Split(int total, bool nearbyEnabled, ref bool extraToNearby, out int baseBudget, out int nearbyBudget)
		{
			nearbyBudget = (nearbyEnabled ? (total / 2) : 0);
			if ((nearbyEnabled && (total & 1) != 0) & extraToNearby)
			{
				nearbyBudget++;
			}
			extraToNearby = !extraToNearby;
			baseBudget = total - nearbyBudget;
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateFood", new Type[]
	{
		typeof(float),
		typeof(bool)
	})]
	internal static class FoodTimerPatch
	{
		internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Expected O, but got Unknown
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			FieldInfo fieldInfo = AccessTools.Field(typeof(Food), "m_time");
			MethodInfo methodInfo = AccessTools.Method(typeof(FoodTimerPatch), "ScaleCountdown", (Type[])null, (Type[])null);
			int num = -1;
			int num2 = 0;
			for (int i = 0; i + 3 < list.Count; i++)
			{
				if (CodeInstructionExtensions.LoadsField(list[i], fieldInfo, false) && IsLoadLocal(list[i + 1]) && list[i + 2].opcode == OpCodes.Sub && CodeInstructionExtensions.StoresField(list[i + 3], fieldInfo))
				{
					num = i + 2;
					num2++;
				}
			}
			if (num2 != 1)
			{
				throw new InvalidOperationException("Expected exactly one food countdown subtraction; found " + num2 + ". BaseFood must be updated for this game/mod combination.");
			}
			CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_0, (object)null);
			val.labels.AddRange(list[num].labels);
			list[num].labels.Clear();
			val.blocks.AddRange(list[num].blocks);
			list[num].blocks.Clear();
			list.Insert(num, val);
			list.Insert(num + 1, new CodeInstruction(OpCodes.Call, (object)methodInfo));
			ManualLogSource? log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)"Food countdown patch verified: exactly one subtraction modified; healing clock untouched.");
			}
			return list;
		}

		private static bool IsLoadLocal(CodeInstruction instruction)
		{
			if (!(instruction.opcode == OpCodes.Ldloc_0) && !(instruction.opcode == OpCodes.Ldloc_1) && !(instruction.opcode == OpCodes.Ldloc_2) && !(instruction.opcode == OpCodes.Ldloc_3) && !(instruction.opcode == OpCodes.Ldloc))
			{
				return instruction.opcode == OpCodes.Ldloc_S;
			}
			return true;
		}

		private static float ScaleCountdown(float vanillaCountdown, Player player)
		{
			return vanillaCountdown * (Plugin.Instance?.GetMultiplier(player) ?? 1f);
		}
	}
	[HarmonyPatch(typeof(Player), "TeleportTo")]
	internal static class TeleportResetPatch
	{
		private static void Postfix(Player __instance, bool __result)
		{
			if (__result)
			{
				Plugin.Instance?.ClearProtection(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class DeathResetPatch
	{
		private static void Prefix(Player __instance)
		{
			Plugin.Instance?.ClearProtection(__instance);
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class SpawnResetPatch
	{
		private static void Postfix(Player __instance)
		{
			Plugin.Instance?.ClearProtection(__instance);
		}
	}
	[BepInPlugin("local.basefood", "BaseFood", "0.2.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Id = "local.basefood";

		private Harmony? harmony;

		private readonly BaseDetector detector = new BaseDetector();

		private readonly BaseDetector nearbyDetector = new BaseDetector(requireAll: false);

		private readonly HashSet<string> allowedFires = new HashSet<string>(StringComparer.Ordinal);

		private Player? currentPlayer;

		private bool suspended;

		private bool runtimeErrorLogged;

		private bool lastProtection;

		private bool extraBudgetToNearby;

		private static readonly int RestedHash = StringExtensionMethods.GetStableHashCode("Rested");

		internal ConfigEntry<bool> Enabled;

		internal ConfigEntry<float> Radius;

		internal ConfigEntry<float> GraceSeconds;

		internal ConfigEntry<float> DiscoverySeconds;

		internal ConfigEntry<float> ValidationSeconds;

		internal ConfigEntry<int> CandidatesPerFrame;

		internal ConfigEntry<float> RestedReduction;

		internal ConfigEntry<bool> NearbyEnabled;

		internal ConfigEntry<float> NearbyRadius;

		internal ConfigEntry<float> NearbyGraceSeconds;

		private ConfigEntry<string> firePrefabs;

		private ConfigEntry<bool> logTransitions;

		internal static Plugin? Instance { get; private set; }

		internal static ManualLogSource? Log { get; private set; }

		private void Awake()
		{
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Expected O, but got Unknown
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable food countdown changes for the local player only.");
			Radius = ((BaseUnityPlugin)this).Config.Bind<float>("Base", "Radius", 75f, Range("Each of the three structures must be within this distance of the player, in metres (3D).", 1f, 200f));
			GraceSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Base", "GraceSeconds", 20f, Range("Seconds of protection after the first failed validation. Repeated failures do not extend it.", 0f, 300f));
			NearbyEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("NearbyStructure", "Enabled", true, "Pause food timers near any one qualifying burning fire, workbench, or forge. Independent of the full base rule and Rested.");
			NearbyRadius = ((BaseUnityPlugin)this).Config.Bind<float>("NearbyStructure", "Radius", 10f, Range("Distance in metres (3D) to any one qualifying structure. Two or three nearby structures also qualify.", 1f, 200f));
			NearbyGraceSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("NearbyStructure", "GraceSeconds", 5f, Range("Seconds of nearby-structure protection after the first failed validation. Independent of Base GraceSeconds; repeated failures do not extend it.", 0f, 300f));
			DiscoverySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "DiscoverySeconds", 10f, Range("Interval between searches for missing qualifying structures. Never scans all scene objects.", 1f, 60f));
			ValidationSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "ValidationSeconds", 1f, Range("Interval for validating cached structures for both protection rules.", 0.25f, 10f));
			CandidatesPerFrame = ((BaseUnityPlugin)this).Config.Bind<int>("Performance", "CandidatesPerFrame", 64, new ConfigDescription("Maximum discovery candidates examined per frame across both rules combined. Large searches continue on subsequent frames.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1024), Array.Empty<object>()));
			RestedReduction = ((BaseUnityPlugin)this).Config.Bind<float>("Rested", "TimerReductionPercent", 25f, Range("Food countdown reduction while Rested, anywhere. 25 means 75% normal ticking speed. Base protection takes priority.", 0f, 100f));
			firePrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("Base", "FirePrefabs", "fire_pit,bonfire,hearth,piece_brazierceiling,piece_brazierfloor", "Comma-separated qualifying Fireplace prefab names. Fires must be burning. Ordinary torches are excluded by default. Restart after changing this list.");
			logTransitions = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "LogProtectionChanges", false, "Log entering/leaving base protection for troubleshooting. No on-screen messages.");
			string[] array = firePrefabs.Value.Split(new char[1] { ',' });
			foreach (string text in array)
			{
				if (!string.IsNullOrWhiteSpace(text))
				{
					allowedFires.Add(text.Trim());
				}
			}
			((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged;
			try
			{
				harmony = new Harmony("local.basefood");
				harmony.PatchAll(typeof(Plugin).Assembly);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"BaseFood 0.2.0 loaded. Client-side food countdown changes; normal healing is preserved.");
			}
			catch (Exception ex)
			{
				Harmony? obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				((BaseUnityPlugin)this).Logger.LogError((object)("BaseFood could not apply its patches. Vanilla food behavior is preserved. " + ex));
				Instance = null;
				((Behaviour)this).enabled = false;
			}
		}

		private static ConfigDescription Range(string text, float min, float max)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			return new ConfigDescription(text, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>());
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			ResetDetection();
			if ((object)args.ChangedSetting == firePrefabs)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Restart Valheim to apply the changed FirePrefabs list.");
			}
		}

		internal bool AllowsFire(string prefab)
		{
			return allowedFires.Contains(prefab);
		}

		private void ResetDetection()
		{
			detector.Reset();
			nearbyDetector.Reset();
			extraBudgetToNearby = false;
		}

		private bool IsProtected(float now)
		{
			if (!detector.IsProtected(now))
			{
				if (NearbyEnabled.Value)
				{
					return nearbyDetector.IsProtected(now);
				}
				return false;
			}
			return true;
		}

		private void Update()
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)(object)currentPlayer)
				{
					ResetDetection();
					currentPlayer = localPlayer;
					suspended = false;
				}
				if (!Enabled.Value || !Object.op_Implicit((Object)(object)localPlayer) || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting())
				{
					ResetDetection();
					suspended = true;
					return;
				}
				if (suspended)
				{
					ResetDetection();
					suspended = false;
				}
				if (!(Time.deltaTime <= 0f))
				{
					DiscoveryBudget.Split(CandidatesPerFrame.Value, NearbyEnabled.Value, ref extraBudgetToNearby, out var baseBudget, out var nearbyBudget);
					Vector3 position = ((Component)localPlayer).transform.position;
					detector.Tick(position, Time.time, Radius.Value, GraceSeconds.Value, DiscoverySeconds.Value, ValidationSeconds.Value, baseBudget);
					if (NearbyEnabled.Value)
					{
						nearbyDetector.Tick(position, Time.time, NearbyRadius.Value, NearbyGraceSeconds.Value, DiscoverySeconds.Value, ValidationSeconds.Value, nearbyBudget);
					}
					bool flag = IsProtected(Time.time);
					if (logTransitions.Value && flag != lastProtection)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)(flag ? "Food protection active." : "Food protection ended."));
					}
					lastProtection = flag;
				}
			}
			catch (Exception ex)
			{
				ResetDetection();
				if (!runtimeErrorLogged)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Base detection failed; protection cleared. Further identical failures will not spam the log. " + ex));
					runtimeErrorLogged = true;
				}
			}
		}

		internal float GetMultiplier(Player player)
		{
			if (!Enabled.Value || (Object)(object)player != (Object)(object)Player.m_localPlayer || ((Character)player).IsDead() || ((Character)player).IsTeleporting())
			{
				return 1f;
			}
			bool protectedAtBase = (Object)(object)player == (Object)(object)currentPlayer && !suspended && IsProtected(Time.time);
			return FoodRules.Multiplier(enabled: true, protectedAtBase, ((Character)player).GetSEMan().HaveStatusEffect(RestedHash), RestedReduction.Value);
		}

		internal void ClearProtection(Player player)
		{
			if ((Object)(object)player == (Object)(object)Player.m_localPlayer)
			{
				ResetDetection();
				suspended = true;
			}
		}

		private void OnDestroy()
		{
			((BaseUnityPlugin)this).Config.SettingChanged -= OnSettingChanged;
			Harmony? obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			ResetDetection();
			StructureRegistry.Clear();
			Instance = null;
			Log = null;
		}
	}
	internal enum StructureKind
	{
		Fire,
		Workbench,
		Forge
	}
	internal static class StructureRegistry
	{
		internal static readonly List<TrackedStructure>[] Lists = new List<TrackedStructure>[3]
		{
			new List<TrackedStructure>(),
			new List<TrackedStructure>(),
			new List<TrackedStructure>()
		};

		internal static int Revision { get; private set; }

		internal static void Add(TrackedStructure node)
		{
			if (node.Index < 0)
			{
				List<TrackedStructure> list = Lists[(int)node.Kind];
				node.Index = list.Count;
				list.Add(node);
				Revision++;
			}
		}

		internal static void Remove(TrackedStructure node)
		{
			if (node.Index >= 0)
			{
				List<TrackedStructure> list = Lists[(int)node.Kind];
				int index = list.Count - 1;
				list[node.Index] = list[index];
				list[node.Index].Index = node.Index;
				list.RemoveAt(index);
				node.Index = -1;
				Revision++;
			}
		}

		internal static void Clear()
		{
			List<TrackedStructure>[] lists = Lists;
			foreach (List<TrackedStructure> list in lists)
			{
				foreach (TrackedStructure item in list)
				{
					item.Index = -1;
				}
				list.Clear();
			}
			Revision++;
		}
	}
	public sealed class TrackedStructure : MonoBehaviour
	{
		internal int Index = -1;

		internal StructureKind Kind;

		private Behaviour source;

		private ZNetView view;

		private Transform location;

		private bool initialized;

		internal void Initialize(StructureKind kind, Behaviour structure, ZNetView netView)
		{
			Kind = kind;
			source = structure;
			view = netView;
			location = ((Component)structure).transform;
			initialized = true;
			if (((Behaviour)this).isActiveAndEnabled)
			{
				StructureRegistry.Add(this);
			}
		}

		internal bool Qualifies(Vector3 playerPosition, float radiusSquared)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)this) || !Object.op_Implicit((Object)(object)source) || !source.isActiveAndEnabled || !Object.op_Implicit((Object)(object)view) || !view.IsValid())
			{
				return false;
			}
			Vector3 val = location.position - playerPosition;
			if (((Vector3)(ref val)).sqrMagnitude > radiusSquared)
			{
				return false;
			}
			if (Kind == StructureKind.Fire)
			{
				return ((Fireplace)source).IsBurning();
			}
			return true;
		}

		private void OnEnable()
		{
			if (initialized)
			{
				StructureRegistry.Add(this);
			}
		}

		private void OnDisable()
		{
			StructureRegistry.Remove(this);
		}

		private void OnDestroy()
		{
			StructureRegistry.Remove(this);
		}
	}
	[HarmonyPatch(typeof(Fireplace), "Awake")]
	internal static class TrackFirePatch
	{
		private static void Postfix(Fireplace __instance)
		{
			if (!((Object)(object)Plugin.Instance == (Object)null))
			{
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if (Object.op_Implicit((Object)(object)component) && component.IsValid() && Plugin.Instance.AllowsFire(Utils.GetPrefabName(((Component)__instance).gameObject)))
				{
					((Component)__instance).gameObject.AddComponent<TrackedStructure>().Initialize(StructureKind.Fire, (Behaviour)(object)__instance, component);
				}
			}
		}
	}
	[HarmonyPatch(typeof(CraftingStation), "Start")]
	internal static class TrackStationPatch
	{
		private static void Postfix(CraftingStation __instance)
		{
			if ((Object)(object)Plugin.Instance == (Object)null)
			{
				return;
			}
			ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
			{
				return;
			}
			string prefabName = Utils.GetPrefabName(((Component)__instance).gameObject);
			StructureKind kind;
			if (prefabName == "piece_workbench")
			{
				kind = StructureKind.Workbench;
			}
			else
			{
				if (!(prefabName == "forge"))
				{
					return;
				}
				kind = StructureKind.Forge;
			}
			((Component)__instance).gameObject.AddComponent<TrackedStructure>().Initialize(kind, (Behaviour)(object)__instance, component);
		}
	}
}