Decompiled source of BetterMapCleanup v0.7.3

BepInEx/plugins/BetterMapCleanup.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
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.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("BetterMapCleanup")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.7.3.0")]
[assembly: AssemblyInformationalVersion("0.7.3+8685568ab4f812366f100901d27f3aae071b4bda")]
[assembly: AssemblyProduct("BetterMapCleanup")]
[assembly: AssemblyTitle("BetterMapCleanup")]
[assembly: AssemblyVersion("0.7.3.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 Ainzworth.BetterMapCleanup
{
	[BepInPlugin("uk.ainzworth.valheim.bettermapcleanup", "BetterMapCleanup", "0.7.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class BetterMapCleanupPlugin : BaseUnityPlugin
	{
		public enum ResourceDepletionAction
		{
			CrossOff,
			Remove
		}

		private enum RuntimeMode
		{
			MenuOffline,
			Singleplayer,
			MultiplayerHostSolo,
			MultiplayerHostActive,
			MultiplayerClient,
			DedicatedServer
		}

		private readonly struct MergePinCandidate
		{
			internal readonly PinData Pin;

			internal readonly BetterMapPinCategory Category;

			internal readonly bool Recorded;

			internal readonly string BaseLabel;

			internal readonly int Count;

			internal readonly bool Countable;

			internal readonly bool HadCountSuffix;

			internal readonly string RuleName;

			internal MergePinCandidate(PinData pin, BetterMapPinCategory category, bool recorded, string baseLabel, int count, bool countable, bool hadCountSuffix, string ruleName)
			{
				Pin = pin;
				Category = category;
				Recorded = recorded;
				BaseLabel = baseLabel;
				Count = Math.Max(1, count);
				Countable = countable;
				HadCountSuffix = hadCountSuffix;
				RuleName = ruleName ?? string.Empty;
			}
		}

		private sealed class MergeCluster
		{
			internal readonly MergePinCandidate Keeper;

			internal readonly string BaseLabel;

			internal readonly bool Countable;

			internal int Count;

			internal bool PreserveCountMarker;

			internal MergeCluster(MergePinCandidate keeper)
			{
				Keeper = keeper;
				BaseLabel = keeper.BaseLabel;
				Countable = keeper.Countable;
				Count = Math.Max(1, keeper.Count);
				PreserveCountMarker = keeper.HadCountSuffix;
			}
		}

		private sealed class ResourceMemberRecord
		{
			internal readonly string RuleName;

			internal string PrefabName;

			internal readonly Vector3 Position;

			internal bool Depleted;

			internal ResourceMemberRecord(string ruleName, string prefabName, Vector3 position, bool depleted)
			{
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				RuleName = ruleName ?? string.Empty;
				PrefabName = ResolveLifecycleRulePrefabName(NormalizePrefabName(prefabName));
				Position = position;
				Depleted = depleted;
			}
		}

		private sealed class SyncedResourceRecord
		{
			internal readonly string PrefabName;

			internal readonly Vector3 Position;

			internal readonly bool Depleted;

			internal SyncedResourceRecord(string prefabName, Vector3 position, bool depleted)
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				PrefabName = ResolveLifecycleRulePrefabName(NormalizePrefabName(prefabName));
				Position = position;
				Depleted = depleted;
			}
		}

		private readonly struct ObservedResource
		{
			internal readonly BetterMapRuleSnapshot Rule;

			internal readonly string PrefabName;

			internal readonly Vector3 Position;

			internal readonly float CurrentHealth;

			internal readonly float MaximumHealth;

			internal readonly float DepletedPercent;

			internal ObservedResource(BetterMapRuleSnapshot rule, string prefabName, Vector3 position, float currentHealth, float maximumHealth, float depletedPercent)
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				Rule = rule;
				PrefabName = NormalizePrefabName(prefabName);
				Position = position;
				CurrentHealth = currentHealth;
				MaximumHealth = maximumHealth;
				DepletedPercent = depletedPercent;
			}
		}

		private sealed class BetterMapSettingsSnapshot
		{
			internal static readonly BetterMapSettingsSnapshot Empty = new BetterMapSettingsSnapshot();

			internal readonly BetterMapRuntime Runtime;

			internal readonly List<BetterMapRuleSnapshot> Rules = new List<BetterMapRuleSnapshot>();

			internal readonly bool GlobalAutoPinsEnabled = true;

			internal readonly float MergeDistance = 5f;

			internal readonly string Signature = string.Empty;

			private BetterMapSettingsSnapshot()
			{
			}

			internal BetterMapSettingsSnapshot(BetterMapRuntime runtime, List<BetterMapRuleSnapshot> rules, bool globalAutoPinsEnabled, float mergeDistance)
			{
				Runtime = runtime;
				Rules = rules;
				GlobalAutoPinsEnabled = globalAutoPinsEnabled;
				MergeDistance = mergeDistance;
				IEnumerable<string> values = from rule in (from rule in rules
						orderby rule.BiomeValue, rule.Category.Value
						select rule).ThenBy<BetterMapRuleSnapshot, string>((BetterMapRuleSnapshot rule) => rule.Name, StringComparer.OrdinalIgnoreCase)
					select $"{rule.BiomeValue}:{rule.Category.Value}:{rule.Name}:{(rule.Enabled ? 1 : 0)}";
				Signature = (globalAutoPinsEnabled ? "1|" : "0|") + "merge=" + mergeDistance.ToString("R", CultureInfo.InvariantCulture) + "|" + string.Join("|", values);
			}
		}

		private sealed class BetterMapRuleSnapshot
		{
			internal readonly object RuntimeRule;

			internal readonly string Name;

			internal readonly string NameToken;

			internal readonly string[] Prefabs;

			internal readonly int BiomeValue;

			internal readonly string BiomeName;

			internal readonly BetterMapPinCategory Category;

			internal readonly bool Enabled;

			internal readonly bool IsLocation;

			internal BetterMapRuleSnapshot(object runtimeRule, string name, string nameToken, string[] prefabs, int biomeValue, string biomeName, BetterMapPinCategory category, bool enabled, bool isLocation)
			{
				RuntimeRule = runtimeRule;
				Name = name;
				NameToken = nameToken;
				Prefabs = prefabs;
				BiomeValue = biomeValue;
				BiomeName = biomeName;
				Category = category;
				Enabled = enabled;
				IsLocation = isLocation;
			}
		}

		private readonly struct BetterMapPinCategory
		{
			internal readonly object RuntimeValue;

			internal readonly int Value;

			internal readonly string Name;

			internal BetterMapPinCategory(object runtimeValue)
			{
				RuntimeValue = runtimeValue;
				Value = Convert.ToInt32(runtimeValue);
				Name = runtimeValue.ToString() ?? Value.ToString();
			}
		}

		private sealed class BetterMapRuntime
		{
			private const BindingFlags StaticFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

			private const BindingFlags InstanceFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

			private readonly PluginInfo _pluginInfo;

			private readonly Type _pinCategoryType;

			private readonly FieldInfo _rulesField;

			private readonly Dictionary<object, HashSet<string>> _expectedNamesCache = new Dictionary<object, HashSet<string>>();

			private readonly FieldInfo _ruleNameField;

			private readonly FieldInfo _ruleNameTokenField;

			private readonly FieldInfo _rulePrefabsField;

			private readonly FieldInfo _ruleBiomeField;

			private readonly FieldInfo _ruleCategoryField;

			private readonly FieldInfo _ruleEnabledField;

			private readonly FieldInfo _ruleIsLocationField;

			private readonly MethodInfo _pinLegendCategoryOf;

			private readonly MethodInfo _pinNamesCategory;

			private readonly MethodInfo _pinNamesFor;

			private readonly MethodInfo _pinRecordHas;

			private readonly MethodInfo? _pinRecordAdd;

			private readonly object? _portalCategoryValue;

			private readonly PropertyInfo? _portalPinTypeProperty;

			private readonly MethodInfo? _portalLabelMethod;

			private readonly FieldInfo? _globalAutoPinField;

			private readonly FieldInfo? _mergeDistanceField;

			private readonly MethodInfo? _mapPinsOf;

			private readonly int _autoPinTypeValue;

			private readonly int _vanillaPinTypeCount;

			internal int RuleCount { get; private set; }

			internal bool HasPinLegend => _pinLegendCategoryOf != null;

			internal bool HasPinNames
			{
				get
				{
					if (_pinNamesCategory != null)
					{
						return _pinNamesFor != null;
					}
					return false;
				}
			}

			internal bool HasPinRecord => _pinRecordHas != null;

			internal bool CanAddPinRecord => _pinRecordAdd != null;

			internal bool HasMapPinsAccessor => _mapPinsOf != null;

			internal bool HasPortalSupport
			{
				get
				{
					if (_portalCategoryValue != null)
					{
						return _portalPinTypeProperty != null;
					}
					return false;
				}
			}

			private BetterMapRuntime(PluginInfo pluginInfo, Type pinCategoryType, FieldInfo rulesField, FieldInfo ruleNameField, FieldInfo ruleNameTokenField, FieldInfo rulePrefabsField, FieldInfo ruleBiomeField, FieldInfo ruleCategoryField, FieldInfo ruleEnabledField, FieldInfo ruleIsLocationField, MethodInfo pinLegendCategoryOf, MethodInfo pinNamesCategory, MethodInfo pinNamesFor, MethodInfo pinRecordHas, MethodInfo? pinRecordAdd, object? portalCategoryValue, PropertyInfo? portalPinTypeProperty, MethodInfo? portalLabelMethod, FieldInfo? globalAutoPinField, FieldInfo? mergeDistanceField, MethodInfo? mapPinsOf, int autoPinTypeValue)
			{
				_pluginInfo = pluginInfo;
				_pinCategoryType = pinCategoryType;
				_rulesField = rulesField;
				_ruleNameField = ruleNameField;
				_ruleNameTokenField = ruleNameTokenField;
				_rulePrefabsField = rulePrefabsField;
				_ruleBiomeField = ruleBiomeField;
				_ruleCategoryField = ruleCategoryField;
				_ruleEnabledField = ruleEnabledField;
				_ruleIsLocationField = ruleIsLocationField;
				_pinLegendCategoryOf = pinLegendCategoryOf;
				_pinNamesCategory = pinNamesCategory;
				_pinNamesFor = pinNamesFor;
				_pinRecordHas = pinRecordHas;
				_pinRecordAdd = pinRecordAdd;
				_portalCategoryValue = portalCategoryValue;
				_portalPinTypeProperty = portalPinTypeProperty;
				_portalLabelMethod = portalLabelMethod;
				_globalAutoPinField = globalAutoPinField;
				_mergeDistanceField = mergeDistanceField;
				_mapPinsOf = mapPinsOf;
				_autoPinTypeValue = autoPinTypeValue;
				_vanillaPinTypeCount = Enum.GetValues(typeof(PinType)).Length;
			}

			internal static bool TryCreate(PluginInfo pluginInfo, out BetterMapRuntime? runtime, out string error)
			{
				runtime = null;
				error = string.Empty;
				try
				{
					Assembly assembly = ((object)pluginInfo.Instance).GetType().Assembly;
					Type type = assembly.GetType("BetterMap.Plugin");
					Type type2 = assembly.GetType("BetterMap.Scripts.Pins.PinRules");
					Type ruleType = assembly.GetType("BetterMap.Scripts.Pins.PinRules+Rule");
					Type pinCategoryType = assembly.GetType("BetterMap.Scripts.Pins.PinCategory");
					Type type3 = assembly.GetType("BetterMap.Scripts.Pins.PinLegend");
					Type type4 = assembly.GetType("BetterMap.Scripts.Pins.PinNames");
					Type type5 = assembly.GetType("BetterMap.Scripts.Pins.PinRecord");
					Type type6 = assembly.GetType("BetterMap.Scripts.Pins.PortalPins");
					if (type2 == null || ruleType == null || pinCategoryType == null || type3 == null || type4 == null || type5 == null)
					{
						error = "required BetterMap pin types were not found (expected BetterMap 1.0.x layout)";
						return false;
					}
					FieldInfo field = type2.GetField("All", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					FieldInfo field2 = ruleType.GetField("Name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					FieldInfo field3 = ruleType.GetField("NameToken", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					FieldInfo field4 = ruleType.GetField("Prefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					FieldInfo field5 = ruleType.GetField("Biome", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					FieldInfo field6 = ruleType.GetField("Category", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					FieldInfo field7 = ruleType.GetField("Enabled", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					FieldInfo field8 = ruleType.GetField("IsLocation", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					MethodInfo methodInfo = type3.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "CategoryOf" && method.GetParameters().Length == 2);
					MethodInfo methodInfo2 = type4.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "Category" && method.GetParameters().Length == 2);
					MethodInfo methodInfo3 = type4.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "For" && method.GetParameters().Length == 2 && method.GetParameters()[0].ParameterType == ruleType);
					MethodInfo methodInfo4 = type5.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "Has" && method.GetParameters().Length == 3 && method.GetParameters()[0].ParameterType == pinCategoryType);
					MethodInfo pinRecordAdd = type5.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method)
					{
						if (method.Name != "Add")
						{
							return false;
						}
						ParameterInfo[] parameters = method.GetParameters();
						return parameters.Length == 2 && parameters.Any((ParameterInfo parameter) => parameter.ParameterType == pinCategoryType) && parameters.Any((ParameterInfo parameter) => parameter.ParameterType == typeof(Vector3));
					});
					if (field == null || field2 == null || field3 == null || field4 == null || field5 == null || field6 == null || field7 == null || field8 == null || methodInfo == null || methodInfo2 == null || methodInfo3 == null || methodInfo4 == null)
					{
						error = "required BetterMap rule/category methods or fields were not found";
						return false;
					}
					FieldInfo fieldInfo = null;
					FieldInfo fieldInfo2 = null;
					foreach (Type item in SafeGetTypes(assembly))
					{
						if (fieldInfo == null)
						{
							FieldInfo field9 = item.GetField("autoPin", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
							if (field9 != null && typeof(ConfigEntry<bool>).IsAssignableFrom(field9.FieldType))
							{
								fieldInfo = field9;
							}
						}
						if (fieldInfo2 == null)
						{
							FieldInfo field10 = item.GetField("autoPinMergeDistance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
							if (field10 != null && typeof(ConfigEntry<float>).IsAssignableFrom(field10.FieldType))
							{
								fieldInfo2 = field10;
							}
						}
						if (fieldInfo != null && fieldInfo2 != null)
						{
							break;
						}
					}
					MethodInfo mapPinsOf = (from type7 in SafeGetTypes(assembly)
						where string.Equals(type7.Name, "MapPins", StringComparison.Ordinal)
						select type7).SelectMany((Type type7) => type7.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)).FirstOrDefault((MethodInfo method) => method.Name == "Of" && method.GetParameters().Length == 1 && method.GetParameters()[0].ParameterType == typeof(Minimap));
					object portalCategoryValue = null;
					try
					{
						portalCategoryValue = Enum.Parse(pinCategoryType, "Portal", ignoreCase: true);
					}
					catch
					{
					}
					PropertyInfo portalPinTypeProperty = type6?.GetProperty("PinType", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					MethodInfo portalLabelMethod = type6?.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "Label" && method.GetParameters().Length == 1 && method.GetParameters()[0].ParameterType == typeof(string));
					int autoPinTypeValue = 3;
					FieldInfo fieldInfo3 = type?.GetField("AutoPinType", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					if (fieldInfo3 != null)
					{
						object obj2 = (fieldInfo3.IsLiteral ? fieldInfo3.GetRawConstantValue() : fieldInfo3.GetValue(null));
						if (obj2 != null)
						{
							autoPinTypeValue = Convert.ToInt32(obj2);
						}
					}
					runtime = new BetterMapRuntime(pluginInfo, pinCategoryType, field, field2, field3, field4, field5, field6, field7, field8, methodInfo, methodInfo2, methodInfo3, methodInfo4, pinRecordAdd, portalCategoryValue, portalPinTypeProperty, portalLabelMethod, fieldInfo, fieldInfo2, mapPinsOf, autoPinTypeValue);
					if (!runtime.TryReadSettings(out List<BetterMapRuleSnapshot> rules, out bool _, out float _, out error))
					{
						runtime = null;
						return false;
					}
					runtime.RuleCount = rules.Count;
					return true;
				}
				catch (Exception ex)
				{
					error = ex.GetBaseException().Message;
					runtime = null;
					return false;
				}
			}

			internal bool TryReadSettings(out List<BetterMapRuleSnapshot> rules, out bool globalAutoPinsEnabled, out float mergeDistance, out string error)
			{
				_expectedNamesCache.Clear();
				rules = new List<BetterMapRuleSnapshot>();
				globalAutoPinsEnabled = true;
				mergeDistance = 5f;
				error = string.Empty;
				try
				{
					if (!(_rulesField.GetValue(null) is IEnumerable enumerable))
					{
						error = "PinRules.All was null or not enumerable";
						return false;
					}
					bool flag = default(bool);
					foreach (object item in enumerable)
					{
						if (item == null)
						{
							continue;
						}
						object value = _ruleCategoryField.GetValue(item);
						object value2 = _ruleBiomeField.GetValue(item);
						if (value != null && value2 != null)
						{
							string name = (_ruleNameField.GetValue(item) as string) ?? string.Empty;
							string nameToken = (_ruleNameTokenField.GetValue(item) as string) ?? string.Empty;
							string[] prefabs = (_rulePrefabsField.GetValue(item) as string[]) ?? Array.Empty<string>();
							bool enabled = !(_ruleEnabledField.GetValue(item) is ConfigEntry<bool> val) || val.Value;
							object value3 = _ruleIsLocationField.GetValue(item);
							int num;
							if (value3 is bool)
							{
								flag = (bool)value3;
								num = 1;
							}
							else
							{
								num = 0;
							}
							bool isLocation = (byte)((uint)num & (flag ? 1u : 0u)) != 0;
							rules.Add(new BetterMapRuleSnapshot(item, name, nameToken, prefabs, Convert.ToInt32(value2), value2.ToString() ?? Convert.ToInt32(value2).ToString(), new BetterMapPinCategory(value), enabled, isLocation));
						}
					}
					globalAutoPinsEnabled = ReadGlobalAutoPinsEnabled();
					mergeDistance = ReadMergeDistance();
					RuleCount = rules.Count;
					return true;
				}
				catch (Exception ex)
				{
					error = ex.GetBaseException().Message;
					return false;
				}
			}

			internal bool TryGetMapPins(Minimap minimap, out List<PinData> pins, out string source)
			{
				pins = new List<PinData>();
				source = string.Empty;
				if (_mapPinsOf == null)
				{
					return false;
				}
				try
				{
					if (!TryCopyPins(_mapPinsOf.Invoke(null, new object[1] { minimap }), pins))
					{
						return false;
					}
					source = "BetterMap " + (_mapPinsOf.DeclaringType?.FullName ?? "MapPins") + ".Of";
					return true;
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("BetterMap MapPins.Of lookup failed: " + ex.GetBaseException().Message));
					return false;
				}
			}

			internal bool TryGetCategory(PinData pin, out BetterMapPinCategory category, out string source)
			{
				//IL_0104: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
				category = default(BetterMapPinCategory);
				source = string.Empty;
				try
				{
					if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)ZNet.instance != (Object)null)
					{
						string key = "BetterMapSeneaL.manual." + ZNet.instance.GetWorldUID().ToString(CultureInfo.InvariantCulture) + "." + pin.m_pos.x.ToString("R", CultureInfo.InvariantCulture) + "," + pin.m_pos.z.ToString("R", CultureInfo.InvariantCulture);
						if (Player.m_localPlayer.m_customData.ContainsKey(key))
						{
							return false;
						}
					}
					if (TryGetPortalCategory(out var category2) && TryGetPortalPinType(out var pinType) && pin.m_type == pinType)
					{
						category = category2;
						source = "PortalPins/type";
						return true;
					}
					object obj = Enum.ToObject(_pinCategoryType, 0);
					int num = Convert.ToInt32(pin.m_type);
					if (num < _vanillaPinTypeCount && num != _autoPinTypeValue)
					{
						return false;
					}
					object[] array = new object[2]
					{
						StripMergeCountSuffix(pin.m_name),
						obj
					};
					object obj2 = _pinNamesCategory.Invoke(null, array);
					bool flag = default(bool);
					int num2;
					if (obj2 is bool)
					{
						flag = (bool)obj2;
						num2 = 1;
					}
					else
					{
						num2 = 0;
					}
					if (((uint)num2 & (flag ? 1u : 0u)) != 0 && array[1] != null)
					{
						category = new BetterMapPinCategory(array[1]);
						source = "PinNames/name-fallback";
						return true;
					}
					object[] array2 = new object[2] { pin.m_type, obj };
					object obj3 = _pinLegendCategoryOf.Invoke(null, array2);
					bool flag2 = default(bool);
					int num3;
					if (obj3 is bool)
					{
						flag2 = (bool)obj3;
						num3 = 1;
					}
					else
					{
						num3 = 0;
					}
					if (((uint)num3 & (flag2 ? 1u : 0u)) != 0 && array2[1] != null)
					{
						category = new BetterMapPinCategory(array2[1]);
						source = "PinLegend/type";
						return true;
					}
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("BetterMap category lookup failed for pin '" + pin.m_name + "': " + ex.GetBaseException().Message));
				}
				return false;
			}

			internal bool TryGetPortalCategory(out BetterMapPinCategory category)
			{
				if (_portalCategoryValue != null)
				{
					category = new BetterMapPinCategory(_portalCategoryValue);
					return true;
				}
				category = default(BetterMapPinCategory);
				return false;
			}

			internal bool TryGetPortalPinType(out PinType pinType)
			{
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0045: Expected I4, but got Unknown
				pinType = (PinType)0;
				if (_portalPinTypeProperty == null)
				{
					return false;
				}
				try
				{
					object value = _portalPinTypeProperty.GetValue(null, null);
					if (value == null)
					{
						return false;
					}
					pinType = (PinType)(int)(PinType)Enum.ToObject(typeof(PinType), Convert.ToInt32(value));
					return true;
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("BetterMap PortalPins.PinType lookup failed: " + ex.GetBaseException().Message));
					return false;
				}
			}

			internal bool IsPortalPin(PinData pin)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				if (pin != null && TryGetPortalPinType(out var pinType))
				{
					return pin.m_type == pinType;
				}
				return false;
			}

			internal string PortalLabel(string tag)
			{
				try
				{
					if (_portalLabelMethod?.Invoke(null, new object[1] { tag ?? string.Empty }) is string text && !string.IsNullOrEmpty(text))
					{
						return text;
					}
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("BetterMap PortalPins.Label lookup failed: " + ex.GetBaseException().Message));
				}
				if (!string.IsNullOrEmpty(tag))
				{
					return tag;
				}
				return "$piece_portal";
			}

			internal bool PortalLabelMatches(string? actual, string expected)
			{
				if (string.IsNullOrWhiteSpace(actual))
				{
					return false;
				}
				if (string.Equals(actual, expected, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
				string a = NormalizeLabel(actual);
				string b = NormalizeLabel(expected);
				if (string.Equals(a, b, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
				string value = TryLocalize(expected);
				if (!string.IsNullOrEmpty(value))
				{
					return string.Equals(a, NormalizeLabel(value), StringComparison.OrdinalIgnoreCase);
				}
				return false;
			}

			internal bool IsRecordedAutoPin(BetterMapPinCategory category, Vector3 position)
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					object obj = _pinRecordHas.Invoke(null, new object[3] { category.RuntimeValue, position, 1f });
					bool flag = default(bool);
					int num;
					if (obj is bool)
					{
						flag = (bool)obj;
						num = 1;
					}
					else
					{
						num = 0;
					}
					return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("BetterMap PinRecord lookup failed: " + ex.GetBaseException().Message));
					return false;
				}
			}

			internal bool AddPinRecord(BetterMapPinCategory category, Vector3 position)
			{
				//IL_0063: Unknown result type (might be due to invalid IL or missing references)
				if (_pinRecordAdd == null)
				{
					return false;
				}
				try
				{
					ParameterInfo[] parameters = _pinRecordAdd.GetParameters();
					object[] array = new object[2];
					for (int i = 0; i < parameters.Length; i++)
					{
						if (parameters[i].ParameterType == _pinCategoryType)
						{
							array[i] = category.RuntimeValue;
							continue;
						}
						if (parameters[i].ParameterType == typeof(Vector3))
						{
							array[i] = position;
							continue;
						}
						return false;
					}
					_pinRecordAdd.Invoke(null, array);
					return true;
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("BetterMap PinRecord add failed: " + ex.GetBaseException().Message));
					return false;
				}
			}

			internal int GetPinRecordCount()
			{
				try
				{
					object obj = (_pinRecordHas.DeclaringType?.GetProperty("Count", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(null, null);
					return (obj == null) ? (-1) : Convert.ToInt32(obj);
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("BetterMap PinRecord count lookup failed: " + ex.GetBaseException().Message));
					return -1;
				}
			}

			internal int RemoveDuplicatePinRecords()
			{
				try
				{
					Type declaringType = _pinRecordHas.DeclaringType;
					if (declaringType == null)
					{
						return 0;
					}
					FieldInfo field = declaringType.GetField("_cells", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					FieldInfo field2 = declaringType.GetField("_dirty", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					PropertyInfo property = declaringType.GetProperty("Count", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					if (field == null || field2 == null || property == null)
					{
						return 0;
					}
					object value = field.GetValue(null);
					if (!((value?.GetType().GetProperty("Values", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(value, null) is IEnumerable enumerable))
					{
						return 0;
					}
					HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
					int num = 0;
					foreach (object item2 in enumerable)
					{
						if (!(item2 is IList list))
						{
							continue;
						}
						for (int i = 0; i < list.Count; i++)
						{
							object obj = list[i];
							if (obj == null)
							{
								continue;
							}
							Type type = obj.GetType();
							FieldInfo field3 = type.GetField("Category", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							FieldInfo field4 = type.GetField("X", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							FieldInfo field5 = type.GetField("Z", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							if (field3 == null || field4 == null || field5 == null)
							{
								continue;
							}
							object value2 = field3.GetValue(obj);
							object value3 = field4.GetValue(obj);
							object value4 = field5.GetValue(obj);
							if (value2 != null && value3 != null && value4 != null)
							{
								int num2 = Convert.ToInt32(value2);
								int num3 = Mathf.RoundToInt(Convert.ToSingle(value3) * 10f);
								int num4 = Mathf.RoundToInt(Convert.ToSingle(value4) * 10f);
								string item = $"{num2}|{num3}|{num4}";
								if (!hashSet.Add(item))
								{
									list.RemoveAt(i);
									i--;
									num++;
								}
							}
						}
					}
					if (num > 0)
					{
						field2.SetValue(null, true);
						object value5 = property.GetValue(null, null);
						int num5 = ((value5 != null) ? Convert.ToInt32(value5) : 0);
						property.SetValue(null, Math.Max(0, num5 - num), null);
					}
					return num;
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("BetterMap PinRecord duplicate cleanup failed: " + ex.GetBaseException().Message));
					return 0;
				}
			}

			internal int RemoveExactPinRecord(BetterMapPinCategory category, Vector3 position, float tolerance)
			{
				//IL_0198: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					Type declaringType = _pinRecordHas.DeclaringType;
					if (declaringType == null)
					{
						return 0;
					}
					FieldInfo field = declaringType.GetField("_cells", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					FieldInfo field2 = declaringType.GetField("_dirty", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					PropertyInfo property = declaringType.GetProperty("Count", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					if (field == null || field2 == null || property == null)
					{
						return 0;
					}
					object value = field.GetValue(null);
					if (value == null)
					{
						return 0;
					}
					if (!(value.GetType().GetProperty("Values", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(value, null) is IEnumerable enumerable))
					{
						return 0;
					}
					float num = tolerance * tolerance;
					int num2 = 0;
					foreach (object item in enumerable)
					{
						if (!(item is IList list))
						{
							continue;
						}
						for (int num3 = list.Count - 1; num3 >= 0; num3--)
						{
							object obj = list[num3];
							if (obj != null)
							{
								Type type = obj.GetType();
								FieldInfo field3 = type.GetField("Category", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
								FieldInfo field4 = type.GetField("X", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
								FieldInfo field5 = type.GetField("Z", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
								if (!(field3 == null) && !(field4 == null) && !(field5 == null))
								{
									object value2 = field3.GetValue(obj);
									object value3 = field4.GetValue(obj);
									object value4 = field5.GetValue(obj);
									if (value2 != null && value3 != null && value4 != null && Convert.ToInt32(value2) == category.Value)
									{
										float num4 = Convert.ToSingle(value3) - position.x;
										float num5 = Convert.ToSingle(value4) - position.z;
										if (!(num4 * num4 + num5 * num5 > num))
										{
											list.RemoveAt(num3);
											num2++;
										}
									}
								}
							}
						}
					}
					if (num2 > 0)
					{
						field2.SetValue(null, true);
						object value5 = property.GetValue(null, null);
						int num6 = ((value5 != null) ? Convert.ToInt32(value5) : 0);
						property.SetValue(null, Math.Max(0, num6 - num2), null);
					}
					return num2;
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("BetterMap PinRecord exact-entry removal failed: " + ex.GetBaseException().Message));
					return 0;
				}
			}

			internal void FlushPinRecord()
			{
				try
				{
					(_pinRecordHas.DeclaringType?.GetMethod("Flush", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))?.Invoke(null, null);
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("BetterMap PinRecord flush failed: " + ex.GetBaseException().Message));
				}
			}

			internal bool RuleCouldOwnPin(BetterMapRuleSnapshot rule, string? pinName)
			{
				if (string.IsNullOrWhiteSpace(pinName))
				{
					return false;
				}
				string text = StripMergeCountSuffix(pinName);
				HashSet<string> hashSet = ExpectedNames(rule);
				if (hashSet.Contains(text))
				{
					return true;
				}
				string normalizedPin = NormalizeLabel(text);
				if (normalizedPin.Length > 0)
				{
					return hashSet.Any((string name) => string.Equals(NormalizeLabel(name), normalizedPin, StringComparison.OrdinalIgnoreCase));
				}
				return false;
			}

			private static string NormalizeLabel(string value)
			{
				if (string.IsNullOrWhiteSpace(value))
				{
					return string.Empty;
				}
				string text = value.Replace('\u00a0', ' ').Trim();
				StringBuilder stringBuilder = new StringBuilder(text.Length);
				bool flag = false;
				bool flag2 = false;
				string text2 = text;
				foreach (char c in text2)
				{
					if (c == '<')
					{
						flag = true;
					}
					else if (flag)
					{
						if (c == '>')
						{
							flag = false;
						}
					}
					else if (char.IsWhiteSpace(c))
					{
						if (!flag2 && stringBuilder.Length > 0)
						{
							stringBuilder.Append(' ');
						}
						flag2 = true;
					}
					else
					{
						flag2 = false;
						stringBuilder.Append(c);
					}
				}
				return stringBuilder.ToString().Trim();
			}

			internal string DescribeExpectedNames(BetterMapRuleSnapshot rule)
			{
				return string.Join(", ", ExpectedNames(rule).OrderBy<string, string>((string value) => value, StringComparer.OrdinalIgnoreCase));
			}

			private HashSet<string> ExpectedNames(BetterMapRuleSnapshot rule)
			{
				if (_expectedNamesCache.TryGetValue(rule.RuntimeRule, out HashSet<string> value))
				{
					return value;
				}
				HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				AddNameVariants(hashSet, rule.NameToken);
				AddNameVariants(hashSet, rule.Name);
				try
				{
					if (rule.Prefabs.Length == 0 || rule.IsLocation)
					{
						object obj = _pinNamesFor.Invoke(null, new object[2] { rule.RuntimeRule, null });
						AddNameVariants(hashSet, obj as string);
					}
					else if ((Object)(object)ZNetScene.instance != (Object)null)
					{
						string[] prefabs = rule.Prefabs;
						foreach (string text in prefabs)
						{
							string value2 = Regex.Replace(text.Replace('_', ' '), "([a-z])([A-Z])", "$1 $2");
							AddNameVariants(hashSet, value2);
							AddNameVariants(hashSet, "$pe" + text.Replace("_", "") + "Name");
							GameObject prefab = ZNetScene.instance.GetPrefab(text);
							object obj2 = _pinNamesFor.Invoke(null, new object[2] { rule.RuntimeRule, prefab });
							AddNameVariants(hashSet, obj2 as string);
						}
					}
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("BetterMap PinNames.For failed for rule '" + rule.Name + "': " + ex.GetBaseException().Message));
				}
				_expectedNamesCache[rule.RuntimeRule] = hashSet;
				return hashSet;
			}

			private static void AddNameVariants(HashSet<string> names, string? value)
			{
				if (!string.IsNullOrEmpty(value))
				{
					names.Add(value);
					string text = TryLocalize(value);
					if (!string.IsNullOrEmpty(text))
					{
						names.Add(text);
					}
				}
			}

			private static string? TryLocalize(string value)
			{
				try
				{
					Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
					for (int i = 0; i < assemblies.Length; i++)
					{
						Type type = assemblies[i].GetType("Localization", throwOnError: false);
						if (type == null)
						{
							continue;
						}
						object obj = type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) ?? type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null);
						if (obj != null)
						{
							MethodInfo method = type.GetMethod("Localize", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null);
							if (!(method == null))
							{
								return method.Invoke(obj, new object[1] { value }) as string;
							}
						}
					}
				}
				catch
				{
				}
				return null;
			}

			private bool ReadGlobalAutoPinsEnabled()
			{
				if (_globalAutoPinField?.GetValue(null) is ConfigEntry<bool> val)
				{
					return val.Value;
				}
				foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in _pluginInfo.Instance.Config)
				{
					if (string.Equals(item.Key.Section, "Auto Pins", StringComparison.OrdinalIgnoreCase) && string.Equals(item.Key.Key, "Enable", StringComparison.OrdinalIgnoreCase))
					{
						object boxedValue = item.Value.BoxedValue;
						if (boxedValue is bool)
						{
							return (bool)boxedValue;
						}
					}
				}
				return true;
			}

			private float ReadMergeDistance()
			{
				if (_mergeDistanceField?.GetValue(null) is ConfigEntry<float> val)
				{
					return val.Value;
				}
				foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in _pluginInfo.Instance.Config)
				{
					if (!string.Equals(item.Key.Section, "Auto Pins", StringComparison.OrdinalIgnoreCase) || !string.Equals(item.Key.Key, "Merge Distance", StringComparison.OrdinalIgnoreCase))
					{
						continue;
					}
					if (item.Value.BoxedValue is float result)
					{
						return result;
					}
					try
					{
						return Convert.ToSingle(item.Value.BoxedValue, CultureInfo.InvariantCulture);
					}
					catch
					{
					}
					break;
				}
				return 5f;
			}

			private static IEnumerable<Type> SafeGetTypes(Assembly assembly)
			{
				try
				{
					return assembly.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					return ex.Types.Where((Type type) => type != null).Cast<Type>();
				}
			}
		}

		private sealed class PortalEvent
		{
			internal ZDOID Id;

			internal string Prefab = string.Empty;

			internal Vector3 Position;

			internal float Expires;

			internal bool Historical;

			internal float RetryAt;
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static Func<ConfigEntryBase, string> <>9__31_0;

			public static Func<ConfigEntryBase, string> <>9__31_1;

			public static Func<Type, bool> <>9__46_0;

			public static Func<ParameterInfo, bool> <>9__46_1;

			public static Func<Type, bool> <>9__47_0;

			public static ConsoleEvent <>9__140_0;

			public static Func<ResourceMemberRecord, bool> <>9__145_0;

			public static Func<ResourceMemberRecord, string> <>9__145_1;

			public static Func<ResourceMemberRecord, float> <>9__145_2;

			public static Func<ResourceMemberRecord, float> <>9__145_3;

			public static Func<ResourceMemberRecord, bool> <>9__152_0;

			public static Func<ParameterInfo, string> <>9__155_3;

			public static Func<MethodInfo, bool> <>9__155_1;

			public static Func<ParameterInfo, string> <>9__155_4;

			public static Func<MethodInfo, bool> <>9__155_5;

			public static Func<ParameterInfo, string> <>9__155_6;

			public static Func<MethodInfo, bool> <>9__155_7;

			public static Func<MethodInfo, bool> <>9__181_0;

			public static Func<MethodInfo, bool> <>9__181_1;

			public static Func<ResourceMemberRecord, string> <>9__203_0;

			public static Func<ResourceMemberRecord, float> <>9__203_1;

			public static Func<ResourceMemberRecord, float> <>9__203_2;

			public static Func<SyncedResourceRecord, string> <>9__205_0;

			public static Func<SyncedResourceRecord, float> <>9__205_1;

			public static Func<SyncedResourceRecord, float> <>9__205_2;

			public static Func<SyncedResourceRecord, bool> <>9__211_0;

			public static Func<ResourceMemberRecord, bool> <>9__212_0;

			public static Func<BetterMapRuleSnapshot, bool> <>9__213_0;

			public static Func<BetterMapRuleSnapshot, string> <>9__213_1;

			public static Func<BetterMapRuleSnapshot, string> <>9__214_2;

			public static Func<BetterMapRuleSnapshot, bool> <>9__222_0;

			public static Func<BetterMapRuleSnapshot, int> <>9__222_1;

			public static Func<BetterMapRuleSnapshot, int> <>9__222_2;

			public static Func<BetterMapRuleSnapshot, string> <>9__222_3;

			public static Func<BetterMapRuleSnapshot, string> <>9__222_4;

			public static Func<BetterMapRuleSnapshot, int> <>9__222_6;

			public static Func<BetterMapRuleSnapshot, bool> <>9__222_9;

			public static Func<BetterMapRuleSnapshot, bool> <>9__222_10;

			public static Func<BetterMapRuleSnapshot, bool> <>9__222_12;

			public static Func<BetterMapRuleSnapshot, string> <>9__222_13;

			public static Func<BetterMapRuleSnapshot, bool> <>9__222_14;

			public static Func<BetterMapRuleSnapshot, string> <>9__222_15;

			public static Func<BetterMapRuleSnapshot, string> <>9__222_16;

			public static Func<KeyValuePair<string, int>, int> <>9__222_19;

			public static Func<KeyValuePair<string, int>, string> <>9__222_20;

			public static Func<BetterMapRuleSnapshot, bool> <>9__223_2;

			public static Func<BetterMapRuleSnapshot, int> <>9__223_3;

			public static Func<BetterMapRuleSnapshot, string> <>9__223_4;

			public static Func<FieldInfo, bool> <>9__228_1;

			public static Func<MethodInfo, bool> <>9__228_2;

			public static Func<(string Name, List<PinData> Pins), int> <>9__228_0;

			public static Func<PinData, bool> <>9__230_0;

			internal string <SendServerConfigSnapshot>b__31_0(ConfigEntryBase value)
			{
				return value.Definition.Section;
			}

			internal string <SendServerConfigSnapshot>b__31_1(ConfigEntryBase value)
			{
				return value.Definition.Key;
			}

			internal bool <PatchBetterMapAutoPinObservation>b__46_0(Type type)
			{
				return string.Equals(type.Name, "AutoPins", StringComparison.Ordinal);
			}

			internal bool <PatchBetterMapAutoPinObservation>b__46_1(ParameterInfo parameter)
			{
				return parameter.ParameterType == typeof(Vector3);
			}

			internal bool <SafeGetTypes>b__47_0(Type type)
			{
				return type != null;
			}

			internal void <TerminalInitTerminalPostfix>b__140_0(ConsoleEventArgs args)
			{
				Instance?.HandleConsoleCommand(args);
			}

			internal bool <PrintRegistry>b__145_0(ResourceMemberRecord member)
			{
				return !member.Depleted;
			}

			internal string <PrintRegistry>b__145_1(ResourceMemberRecord member)
			{
				return member.RuleName;
			}

			internal float <PrintRegistry>b__145_2(ResourceMemberRecord member)
			{
				return member.Position.x;
			}

			internal float <PrintRegistry>b__145_3(ResourceMemberRecord member)
			{
				return member.Position.z;
			}

			internal bool <BuildStatusLines>b__152_0(ResourceMemberRecord member)
			{
				return !member.Depleted;
			}

			internal string <PatchResourceLifecycle>b__155_3(ParameterInfo parameter)
			{
				return parameter.ParameterType.Name;
			}

			internal bool <PatchResourceLifecycle>b__155_1(MethodInfo method)
			{
				return string.Equals(method.Name, "RPC_CreateFragments", StringComparison.Ordinal);
			}

			internal string <PatchResourceLifecycle>b__155_4(ParameterInfo parameter)
			{
				return parameter.ParameterType.Name;
			}

			internal bool <PatchResourceLifecycle>b__155_5(MethodInfo method)
			{
				return string.Equals(method.Name, "Destroy", StringComparison.Ordinal);
			}

			internal string <PatchResourceLifecycle>b__155_6(ParameterInfo parameter)
			{
				return parameter.ParameterType.Name;
			}

			internal bool <PatchResourceLifecycle>b__155_7(MethodInfo method)
			{
				if (string.Equals(method.Name, "Awake", StringComparison.Ordinal))
				{
					return method.GetParameters().Length == 0;
				}
				return false;
			}

			internal bool <IsPortalAreaReadyForValidation>b__181_0(MethodInfo method)
			{
				if (!string.Equals(method.Name, "IsAreaReady", StringComparison.Ordinal) || method.ReturnType != typeof(bool))
				{
					return false;
				}
				ParameterInfo[] parameters = method.GetParameters();
				if (parameters.Length == 1)
				{
					return parameters[0].ParameterType == typeof(Vector3);
				}
				return false;
			}

			internal bool <IsPortalAreaReadyForValidation>b__181_1(MethodInfo method)
			{
				if (!string.Equals(method.Name, "GetZone", StringComparison.Ordinal))
				{
					return false;
				}
				ParameterInfo[] parameters = method.GetParameters();
				if (parameters.Length == 1)
				{
					return parameters[0].ParameterType == typeof(Vector3);
				}
				return false;
			}

			internal string <SaveResourceRegistry>b__203_0(ResourceMemberRecord r)
			{
				return r.RuleName;
			}

			internal float <SaveResourceRegistry>b__203_1(ResourceMemberRecord r)
			{
				return r.Position.x;
			}

			internal float <SaveResourceRegistry>b__203_2(ResourceMemberRecord r)
			{
				return r.Position.z;
			}

			internal string <SaveServerResourceStates>b__205_0(SyncedResourceRecord r)
			{
				return r.PrefabName;
			}

			internal float <SaveServerResourceStates>b__205_1(SyncedResourceRecord r)
			{
				return r.Position.x;
			}

			internal float <SaveServerResourceStates>b__205_2(SyncedResourceRecord r)
			{
				return r.Position.z;
			}

			internal bool <ReconcileServerRespawns>b__211_0(SyncedResourceRecord record)
			{
				return record.Depleted;
			}

			internal bool <ReconcileLoadedResources>b__212_0(ResourceMemberRecord member)
			{
				return !member.Depleted;
			}

			internal bool <CollectObservedResources>b__213_0(BetterMapRuleSnapshot candidate)
			{
				return candidate.Enabled;
			}

			internal string <CollectObservedResources>b__213_1(BetterMapRuleSnapshot candidate)
			{
				return candidate.Name;
			}

			internal string <UpdateClusterLabelsFromRegistry>b__214_2(BetterMapRuleSnapshot candidate)
			{
				return candidate.Name;
			}

			internal bool <CleanupDisabledPins>b__222_0(BetterMapRuleSnapshot rule)
			{
				return !rule.Enabled;
			}

			internal int <CleanupDisabledPins>b__222_1(BetterMapRuleSnapshot rule)
			{
				return rule.BiomeValue;
			}

			internal int <CleanupDisabledPins>b__222_2(BetterMapRuleSnapshot rule)
			{
				return rule.Category.Value;
			}

			internal string <CleanupDisabledPins>b__222_3(BetterMapRuleSnapshot rule)
			{
				return rule.Name;
			}

			internal string <CleanupDisabledPins>b__222_4(BetterMapRuleSnapshot rule)
			{
				return rule.BiomeName + "/" + rule.Category.Name + "/" + rule.Name;
			}

			internal int <CleanupDisabledPins>b__222_6(BetterMapRuleSnapshot rule)
			{
				return rule.Category.Value;
			}

			internal bool <CleanupDisabledPins>b__222_9(BetterMapRuleSnapshot rule)
			{
				return !rule.Enabled;
			}

			internal bool <CleanupDisabledPins>b__222_10(BetterMapRuleSnapshot rule)
			{
				return rule.Enabled;
			}

			internal bool <CleanupDisabledPins>b__222_12(BetterMapRuleSnapshot rule)
			{
				return !rule.Enabled;
			}

			internal string <CleanupDisabledPins>b__222_13(BetterMapRuleSnapshot rule)
			{
				return rule.BiomeName + "/" + rule.Name;
			}

			internal bool <CleanupDisabledPins>b__222_14(BetterMapRuleSnapshot rule)
			{
				return rule.Enabled;
			}

			internal string <CleanupDisabledPins>b__222_15(BetterMapRuleSnapshot rule)
			{
				return rule.BiomeName + "/" + rule.Name;
			}

			internal string <CleanupDisabledPins>b__222_16(BetterMapRuleSnapshot rule)
			{
				return rule.BiomeName + "/" + rule.Name + "=" + (rule.Enabled ? "on" : "off");
			}

			internal int <CleanupDisabledPins>b__222_19(KeyValuePair<string, int> item)
			{
				return item.Value;
			}

			internal string <CleanupDisabledPins>b__222_20(KeyValuePair<string, int> item)
			{
				return item.Key;
			}

			internal bool <MergeExistingPins>b__223_2(BetterMapRuleSnapshot candidate)
			{
				return candidate.Enabled;
			}

			internal int <MergeExistingPins>b__223_3(BetterMapRuleSnapshot candidate)
			{
				return candidate.BiomeValue;
			}

			internal string <MergeExistingPins>b__223_4(BetterMapRuleSnapshot candidate)
			{
				return candidate.Name;
			}

			internal bool <TryGetPins>b__228_1(FieldInfo field)
			{
				return IsPinCollectionType(field.FieldType);
			}

			internal bool <TryGetPins>b__228_2(MethodInfo method)
			{
				if (method.GetParameters().Length == 0 && method.ReturnType != typeof(void))
				{
					return IsPinCollectionType(method.ReturnType);
				}
				return false;
			}

			internal int <TryGetPins>b__228_0((string Name, List<PinData> Pins) candidate)
			{
				return candidate.Pins.Count;
			}

			internal bool <TryCopyPins>b__230_0(PinData pin)
			{
				return pin != null;
			}
		}

		private const int ServerConfigSyncProtocol = 1;

		private const int ConfigSnapshotMaxAttempts = 3;

		private const float ConfigSnapshotRetrySeconds = 3f;

		private const string RpcConfigSnapshotRequest = "uk.ainzworth.valheim.bettermapcleanup.ConfigSnapshotRequest";

		private const string RpcConfigSnapshot = "uk.ainzworth.valheim.bettermapcleanup.ConfigSnapshot";

		private ConfigEntry<bool> _enableServerConfigSync;

		private ConfigEntry<bool> _lockConfiguration;

		private readonly Dictionary<string, ConfigEntryBase> _synchronizedConfigEntries = new Dictionary<string, ConfigEntryBase>(StringComparer.Ordinal);

		private readonly Dictionary<string, string> _localConfigValuesBeforeServer = new Dictionary<string, string>(StringComparer.Ordinal);

		private readonly Dictionary<string, string> _serverConfigValues = new Dictionary<string, string>(StringComparer.Ordinal);

		private readonly HashSet<long> _serverConfigPeers = new HashSet<long>();

		private ZRoutedRpc? _registeredConfigRoutedRpc;

		private bool _serverConfigSnapshotRequested;

		private bool _serverConfigAvailable;

		private bool _serverConfigActive;

		private bool _serverConfigLocked;

		private bool _applyingServerConfig;

		private bool _serverConfigBroadcastPending;

		private int _configSnapshotAttempts;

		private float _nextConfigSnapshotRequestAt;

		private string _connectedServerConfigVersion = string.Empty;

		private DateTime _lastServerConfigSyncUtc = DateTime.MinValue;

		private bool _betterMapObservationHookInstalled;

		private bool _betterMapObservationChanged;

		private int _serverAuthoritativeTransitions;

		private DateTime _lastServerResourceEventUtc = DateTime.MinValue;

		private static readonly HashSet<string> KnownTrackedResourcePrefabs = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			"rock4_copper", "MineRock_Tin", "mudpile_beacon", "silvervein", "MineRock_Obsidian", "giant_brain", "giant_ribs", "giant_helmet1", "giant_helmet2", "giant_sword1",
			"giant_sword2", "LeviathanLava", "FlametalRockstand", "MineRock_Meteorite", "UnstableLavaRock", "Leviathan"
		};

		public const string PluginGuid = "uk.ainzworth.valheim.bettermapcleanup";

		public const string PluginName = "BetterMapCleanup";

		public const string PluginVersion = "0.7.3";

		public const string BetterMapGuid = "xtav1m.BetterMap";

		internal static ManualLogSource Log = null;

		private Harmony? _harmony;

		private ConfigEntry<bool> _enabled;

		private ConfigEntry<bool> _cleanupOnWorldLoad;

		private ConfigEntry<bool> _cleanupWhenConfigChanges;

		private ConfigEntry<bool> _removeAllWhenAutoPinsDisabled;

		private ConfigEntry<bool> _cleanupNow;

		private ConfigEntry<bool> _mergeExistingPins;

		private ConfigEntry<bool> _showMergeCounts;

		private ConfigEntry<bool> _repairPinRecordsOnWorldLoad;

		private ConfigEntry<bool> _resourceLifecycleCleanup;

		private ConfigEntry<bool> _reconcileLoadedResources;

		private ConfigEntry<float> _resourceReconcileInterval;

		private ConfigEntry<bool> _serverResourceSync;

		private ConfigEntry<bool> _persistResourceRegistry;

		private ConfigEntry<bool> _restoreRespawnedResources;

		private ConfigEntry<bool> _logStatusOnWorldLoad;

		private ConfigEntry<ResourceDepletionAction> _resourceDepletionAction;

		private readonly Dictionary<string, ConfigEntry<ResourceDepletionAction>> _resourceDepletionActions = new Dictionary<string, ConfigEntry<ResourceDepletionAction>>(StringComparer.OrdinalIgnoreCase);

		private ConfigEntry<float> _resourceDepletionThreshold;

		private ConfigEntry<float> _scanInterval;

		private ConfigEntry<bool> _debugLogging;

		private float _nextScanAt;

		private bool _wasInWorld;

		private bool _pendingInitialCleanup;

		private float _initialCleanupAt;

		private string _lastDisabledSignature = string.Empty;

		private readonly HashSet<string> _handledResourceDepletions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, bool> _lastReportedResourceStates = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<long> _resourceSyncPeers = new HashSet<long>();

		private readonly Dictionary<string, ResourceMemberRecord> _resourceMembers = new Dictionary<string, ResourceMemberRecord>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<int, Component> _loadedResourceComponents = new Dictionary<int, Component>();

		private readonly Dictionary<string, SyncedResourceRecord> _pendingResourceReports = new Dictionary<string, SyncedResourceRecord>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, SyncedResourceRecord> _serverResourceStates = new Dictionary<string, SyncedResourceRecord>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, SyncedResourceRecord> _pendingSyncedResourceStates = new Dictionary<string, SyncedResourceRecord>(StringComparer.OrdinalIgnoreCase);

		private long _loadedRegistryWorldUid;

		private long _loadedServerStateWorldUid;

		private ZRoutedRpc? _registeredRoutedRpc;

		private bool _resourceSnapshotRequested;

		private bool _resourceSnapshotReceived;

		private float _nextResourceSnapshotRequest;

		private bool _serverSyncAvailable;

		private bool _suppressServerReport;

		private bool _deferResourceRegistrySave;

		private bool _resourceRegistryDirty;

		private DateTime _lastReconcileUtc = DateTime.MinValue;

		private DateTime _lastSyncUtc = DateTime.MinValue;

		private DateTime _lastRespawnUtc = DateTime.MinValue;

		private int _lastReconcileObserved;

		private int _lastReconcileMissing;

		private int _lastReconcileRespawned;

		private RuntimeMode _lastRuntimeMode;

		private int _lastRemotePeerCount = -1;

		private const int ResourceSyncProtocol = 5;

		private const string RpcResourceReport = "uk.ainzworth.valheim.bettermapcleanup.ResourceReport";

		private const string RpcResourceState = "uk.ainzworth.valheim.bettermapcleanup.ResourceState";

		private const string RpcResourceSnapshotRequest = "uk.ainzworth.valheim.bettermapcleanup.ResourceSnapshotRequest";

		private const string RpcResourceSnapshot = "uk.ainzworth.valheim.bettermapcleanup.ResourceSnapshot";

		private BetterMapRuntime? _runtime;

		private Assembly? _runtimeAssembly;

		private static readonly Dictionary<string, string> LifecyclePrefabAliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { ["mudpile_frac"] = "mudpile_beacon" };

		private static readonly HashSet<string> TransitionalResourceRoots = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "rock4_copper", "mudpile_beacon", "silvervein", "giant_brain", "giant_ribs", "giant_helmet1", "giant_helmet2", "giant_sword1", "giant_sword2" };

		private long _portalHistoryWorld;

		private readonly Dictionary<ZDOID, PortalEvent> _portalHistory = new Dictionary<ZDOID, PortalEvent>();

		private readonly Dictionary<long, Queue<PortalEvent>> _portalCatchup = new Dictionary<long, Queue<PortalEvent>>();

		private const string PortalReportRpc = "uk.ainzworth.valheim.bettermapcleanup.PortalReport.v1";

		private const string PortalEventRpc = "uk.ainzworth.valheim.bettermapcleanup.PortalEvent.v1";

		private Action<ZDO>? _nativePortalDestroyed;

		private bool _replayingPortal;

		private long _portalWorld;

		private readonly Dictionary<ZDOID, PortalEvent> _outgoingPortals = new Dictionary<ZDOID, PortalEvent>();

		private readonly Dictionary<ZDOID, PortalEvent> _incomingPortals = new Dictionary<ZDOID, PortalEvent>();

		private readonly Dictionary<ZDOID, float> _relayedPortals = new Dictionary<ZDOID, float>();

		private readonly Dictionary<long, float> _nextClientReport = new Dictionary<long, float>();

		private static readonly FieldInfo ReplayPrefab = AccessTools.Field(typeof(ZDO), "m_prefab");

		private static readonly FieldInfo ReplayPosition = AccessTools.Field(typeof(ZDO), "m_position");

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

		private void BindServerConfigSyncSettings()
		{
			_enableServerConfigSync = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - ServerSync", "Enable Server Config Sync", true, "When hosting or running a dedicated server, send BetterMapCleanup gameplay settings to connected BetterMapCleanup clients. The server values become the active client values for the session. Clients keep their local config file and get it back after disconnecting.");
			_lockConfiguration = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - ServerSync", "Lock Configuration", true, "When Server Config Sync is enabled, keep synchronized BetterMapCleanup settings locked to the server values while connected. This mirrors BetterMap/ServerSync behavior: the server is the source of truth for active gameplay settings.");
		}

		private void InitializeServerConfigSyncEntries()
		{
			_synchronizedConfigEntries.Clear();
			RegisterSynchronizedConfig((ConfigEntryBase)(object)_cleanupOnWorldLoad);
			RegisterSynchronizedConfig((ConfigEntryBase)(object)_cleanupWhenConfigChanges);
			RegisterSynchronizedConfig((ConfigEntryBase)(object)_removeAllWhenAutoPinsDisabled);
			RegisterSynchronizedConfig((ConfigEntryBase)(object)_mergeExistingPins);
			RegisterSynchronizedConfig((ConfigEntryBase)(object)_showMergeCounts);
			RegisterSynchronizedConfig((ConfigEntryBase)(object)_repairPinRecordsOnWorldLoad);
			RegisterSynchronizedConfig((ConfigEntryBase)(object)_resourceLifecycleCleanup);
			RegisterSynchronizedConfig((ConfigEntryBase)(object)_serverResourceSync);
			RegisterSynchronizedConfig((ConfigEntryBase)(object)_restoreRespawnedResources);
			RegisterSynchronizedConfig((ConfigEntryBase)(object)_resourceDepletionAction);
			RegisterSynchronizedConfig((ConfigEntryBase)(object)_resourceDepletionThreshold);
			foreach (ConfigEntry<ResourceDepletionAction> value in _resourceDepletionActions.Values)
			{
				RegisterSynchronizedConfig((ConfigEntryBase)(object)value);
			}
			_enableServerConfigSync.SettingChanged += OnServerConfigSourceSettingChanged;
			_lockConfiguration.SettingChanged += OnServerConfigSourceSettingChanged;
			((BaseUnityPlugin)this).Config.SettingChanged += OnSynchronizedConfigSettingChanged;
		}

		private void RegisterSynchronizedConfig(ConfigEntryBase entry)
		{
			string configIdentifier = GetConfigIdentifier(entry.Definition);
			_synchronizedConfigEntries[configIdentifier] = entry;
		}

		private static string GetConfigIdentifier(ConfigDefinition definition)
		{
			return definition.Section + "\n" + definition.Key;
		}

		private void OnServerConfigSourceSettingChanged(object sender, EventArgs args)
		{
			if (!_applyingServerConfig && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				_serverConfigBroadcastPending = true;
			}
		}

		private void OnSynchronizedConfigSettingChanged(object sender, SettingChangedEventArgs args)
		{
			if (_applyingServerConfig)
			{
				return;
			}
			ConfigEntryBase changedSetting = args.ChangedSetting;
			string configIdentifier = GetConfigIdentifier(changedSetting.Definition);
			if (!_synchronizedConfigEntries.ContainsKey(configIdentifier))
			{
				return;
			}
			string value;
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				_serverConfigBroadcastPending = true;
			}
			else if (_serverConfigActive && _serverConfigLocked && _serverConfigValues.TryGetValue(configIdentifier, out value))
			{
				string serializedValue;
				try
				{
					serializedValue = changedSetting.GetSerializedValue();
				}
				catch
				{
					return;
				}
				if (!string.Equals(serializedValue, value, StringComparison.Ordinal))
				{
					ApplySerializedConfigValue(changedSetting, value);
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Server config lock restored [" + changedSetting.Definition.Section + "] " + changedSetting.Definition.Key + " to the server value."));
				}
			}
		}

		private void EnsureServerConfigSyncNetwork()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance == null)
			{
				if (_registeredConfigRoutedRpc != null)
				{
					ResetServerConfigSyncState(restoreLocalValues: true, resetNetworkRegistration: true);
				}
			}
			else
			{
				if (_registeredConfigRoutedRpc == instance)
				{
					return;
				}
				if (_registeredConfigRoutedRpc != null)
				{
					ResetServerConfigSyncState(restoreLocalValues: true, resetNetworkRegistration: true);
				}
				try
				{
					instance.Register<ZPackage>("uk.ainzworth.valheim.bettermapcleanup.ConfigSnapshotRequest", (Action<long, ZPackage>)RpcReceiveConfigSnapshotRequest);
					instance.Register<ZPackage>("uk.ainzworth.valheim.bettermapcleanup.ConfigSnapshot", (Action<long, ZPackage>)RpcReceiveConfigSnapshot);
					_registeredConfigRoutedRpc = instance;
					_serverConfigSnapshotRequested = false;
					_serverConfigAvailable = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer();
					_serverConfigActive = false;
					_serverConfigLocked = false;
					_configSnapshotAttempts = 0;
					_nextConfigSnapshotRequestAt = 0f;
					_serverConfigPeers.Clear();
					if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
					{
						if (_debugLogging.Value)
						{
							((BaseUnityPlugin)this).Logger.LogInfo((object)$"Server config-sync RPCs registered; enabled={_enableServerConfigSync.Value}, locked={_lockConfiguration.Value}, synchronizedSettings={_synchronizedConfigEntries.Count}.");
						}
					}
					else if (_debugLogging.Value)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)"Server config-sync RPCs registered in client mode; waiting for server capability response.");
					}
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Server config-sync RPC registration failed: " + ex.GetBaseException().Message));
				}
			}
		}

		private void RequestServerConfigSnapshotIfNeeded()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			if (ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || _serverConfigAvailable || _configSnapshotAttempts >= 3 || (_serverConfigSnapshotRequested && Time.unscaledTime < _nextConfigSnapshotRequestAt))
			{
				return;
			}
			long serverPeerId = GetServerPeerId();
			if (serverPeerId == 0L)
			{
				return;
			}
			try
			{
				ZPackage val = new ZPackage();
				val.Write(1);
				val.Write("0.7.3");
				ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerId, "uk.ainzworth.valheim.bettermapcleanup.ConfigSnapshotRequest", new object[1] { val });
				_serverConfigSnapshotRequested = true;
				_configSnapshotAttempts++;
				_nextConfigSnapshotRequestAt = Time.unscaledTime + 3f;
				if (_debugLogging.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Requested BetterMapCleanup server config snapshot (attempt {_configSnapshotAttempts}/{3}).");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not request BetterMapCleanup server config: " + ex.GetBaseException().Message));
			}
		}

		private void RpcReceiveConfigSnapshotRequest(long sender, ZPackage package)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			try
			{
				int num = package.ReadInt();
				string arg = package.ReadString();
				if (num != 1)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)$"Ignored config-sync request from peer {sender}: protocol={num}, expected={1}.");
					return;
				}
				_serverConfigPeers.Add(sender);
				if (_debugLogging.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"BetterMapCleanup config-sync request from peer {sender}; clientVersion={arg}.");
				}
				SendServerConfigSnapshot(sender);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Failed to handle config-sync request from peer {sender}: {ex.GetBaseException().Message}");
			}
		}

		private void SendServerConfigSnapshot(long peerId)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			if (ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			ZPackage val = new ZPackage();
			val.Write(1);
			val.Write("0.7.3");
			val.Write(_enableServerConfigSync.Value);
			val.Write(_lockConfiguration.Value);
			if (_enableServerConfigSync.Value)
			{
				val.Write(_synchronizedConfigEntries.Count);
				foreach (ConfigEntryBase item in _synchronizedConfigEntries.Values.OrderBy<ConfigEntryBase, string>((ConfigEntryBase value) => value.Definition.Section, StringComparer.Ordinal).ThenBy<ConfigEntryBase, string>((ConfigEntryBase value) => value.Definition.Key, StringComparer.Ordinal))
				{
					val.Write(item.Definition.Section);
					val.Write(item.Definition.Key);
					val.Write(item.GetSerializedValue());
				}
			}
			else
			{
				val.Write(0);
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(peerId, "uk.ainzworth.valheim.bettermapcleanup.ConfigSnapshot", new object[1] { val });
		}

		private void RpcReceiveConfigSnapshot(long sender, ZPackage package)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
			{
				return;
			}
			try
			{
				int num = package.ReadInt();
				if (num != 1)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)$"Ignored BetterMapCleanup server config: protocol={num}, expected={1}.");
					return;
				}
				string text = package.ReadString();
				bool flag = package.ReadBool();
				bool flag2 = package.ReadBool();
				int num2 = Math.Max(0, package.ReadInt());
				Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
				for (int i = 0; i < num2; i++)
				{
					string text2 = package.ReadString();
					string text3 = package.ReadString();
					string value = package.ReadString();
					dictionary[GetConfigIdentifier(new ConfigDefinition(text2, text3))] = value;
				}
				_serverConfigAvailable = true;
				_serverConfigSnapshotRequested = true;
				_connectedServerConfigVersion = text;
				_lastServerConfigSyncUtc = DateTime.UtcNow;
				if (!flag)
				{
					ResetServerConfigSyncState(restoreLocalValues: true, resetNetworkRegistration: false);
					_serverConfigAvailable = true;
					_connectedServerConfigVersion = text;
					((BaseUnityPlugin)this).Logger.LogInfo((object)("BetterMapCleanup server config sync is disabled by the server (server version " + text + "); local client settings remain active."));
				}
				else
				{
					ApplyServerConfigSnapshot(dictionary, flag2);
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Applied BetterMapCleanup server config: serverVersion={text}, settings={dictionary.Count}, locked={flag2}. Server values are active for this connection; the local config file was not overwritten on disk.");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to apply BetterMapCleanup server config: " + ex.GetBaseException().Message));
			}
		}

		private void ApplyServerConfigSnapshot(Dictionary<string, string> values, bool locked)
		{
			if (!_serverConfigActive)
			{
				_localConfigValuesBeforeServer.Clear();
				foreach (KeyValuePair<string, ConfigEntryBase> synchronizedConfigEntry in _synchronizedConfigEntries)
				{
					try
					{
						_localConfigValuesBeforeServer[synchronizedConfigEntry.Key] = synchronizedConfigEntry.Value.GetSerializedValue();
					}
					catch
					{
					}
				}
			}
			_serverConfigValues.Clear();
			foreach (KeyValuePair<string, string> value2 in values)
			{
				if (_synchronizedConfigEntries.ContainsKey(value2.Key))
				{
					_serverConfigValues[value2.Key] = value2.Value;
				}
			}
			_serverConfigActive = true;
			_serverConfigLocked = locked;
			_applyingServerConfig = true;
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				foreach (KeyValuePair<string, string> serverConfigValue in _serverConfigValues)
				{
					if (_synchronizedConfigEntries.TryGetValue(serverConfigValue.Key, out ConfigEntryBase value))
					{
						ApplySerializedConfigValue(value, serverConfigValue.Value);
					}
				}
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
				_applyingServerConfig = false;
			}
			_lastDisabledSignature = string.Empty;
		}

		private void ApplySerializedConfigValue(ConfigEntryBase entry, string serializedValue)
		{
			bool applyingServerConfig = _applyingServerConfig;
			_applyingServerConfig = true;
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				entry.SetSerializedValue(serializedValue);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not apply synchronized config [" + entry.Definition.Section + "] " + entry.Definition.Key + "='" + serializedValue + "': " + ex.GetBaseException().Message));
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
				_applyingServerConfig = applyingServerConfig;
			}
		}

		private void RestoreLocalConfigValues()
		{
			if (!_serverConfigActive || _localConfigValuesBeforeServer.Count == 0)
			{
				_serverConfigValues.Clear();
				_serverConfigActive = false;
				_serverConfigLocked = false;
				return;
			}
			_applyingServerConfig = true;
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				foreach (KeyValuePair<string, string> item in _localConfigValuesBeforeServer)
				{
					if (_synchronizedConfigEntries.TryGetValue(item.Key, out ConfigEntryBase value))
					{
						try
						{
							value.SetSerializedValue(item.Value);
						}
						catch (Exception ex)
						{
							((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not restore local config [" + value.Definition.Section + "] " + value.Definition.Key + ": " + ex.GetBaseException().Message));
						}
					}
				}
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
				_applyingServerConfig = false;
			}
			_localConfigValuesBeforeServer.Clear();
			_serverConfigValues.Clear();
			_serverConfigActive = false;
			_serverConfigLocked = false;
			_connectedServerConfigVersion = string.Empty;
			_lastDisabledSignature = string.Empty;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Restored local BetterMapCleanup config after leaving the server-controlled session.");
		}

		private void ResetServerConfigSyncState(bool restoreLocalValues, bool resetNetworkRegistration)
		{
			if (restoreLocalValues)
			{
				RestoreLocalConfigValues();
			}
			_serverConfigAvailable = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer();
			_serverConfigSnapshotRequested = false;
			_configSnapshotAttempts = 0;
			_nextConfigSnapshotRequestAt = 0f;
			_serverConfigPeers.Clear();
			if (resetNetworkRegistration)
			{
				_registeredConfigRoutedRpc = null;
			}
		}

		private void TickServerConfigBroadcast()
		{
			if (!_serverConfigBroadcastPending || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			_serverConfigBroadcastPending = false;
			PruneServerConfigPeers();
			long[] array = _serverConfigPeers.ToArray();
			foreach (long num in array)
			{
				try
				{
					SendServerConfigSnapshot(num);
				}
				catch (Exception ex)
				{
					if (_debugLogging.Value)
					{
						((BaseUnityPlugin)this).Logger.LogDebug((object)$"Config snapshot broadcast to peer {num} failed: {ex.GetBaseException().Message}");
					}
				}
			}
		}

		private void PruneServerConfigPeers()
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || _serverConfigPeers.Count == 0)
			{
				return;
			}
			try
			{
				if (!(AccessTools.Field(typeof(ZNet), "m_peers")?.GetValue(ZNet.instance) is IEnumerable enumerable))
				{
					return;
				}
				HashSet<long> live = new HashSet<long>();
				foreach (object item in enumerable)
				{
					ZNetPeer val = (ZNetPeer)((item is ZNetPeer) ? item : null);
					if (val != null && val.m_uid != 0L)
					{
						live.Add(val.m_uid);
					}
				}
				_serverConfigPeers.RemoveWhere((long peerId) => !live.Contains(peerId));
			}
			catch
			{
			}
		}

		private string GetServerConfigSyncStatus()
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return "offline";
			}
			if (IsTrueSingleplayer(DetectRuntimeMode(out var _)))
			{
				return "inactive-singleplayer";
			}
			if (ZNet.instance.IsServer())
			{
				if (!_enableServerConfigSync.Value)
				{
					return "server-disabled";
				}
				if (!_lockConfiguration.Value)
				{
					return "source-of-truth/unlocked";
				}
				return "source-of-truth/locked";
			}
			if (_serverConfigActive)
			{
				if (!_serverConfigLocked)
				{
					return "connected/unlocked/" + _connectedServerConfigVersion;
				}
				return "connected/locked/" + _connectedServerConfigVersion;
			}
			if (_serverConfigAvailable)
			{
				return "server-disabled/" + _connectedServerConfigVersion;
			}
			if (_configSnapshotAttempts >= 3)
			{
				return "unavailable";
			}
			if (!_serverConfigSnapshotRequested)
			{
				return "not-requested";
			}
			return "waiting";
		}

		private void ShutdownServerConfigSync()
		{
			((BaseUnityPlugin)this).Config.SettingChanged -= OnSynchronizedConfigSettingChanged;
			if (_enableServerConfigSync != null)
			{
				_enableServerConfigSync.SettingChanged -= OnServerConfigSourceSettingChanged;
			}
			if (_lockConfiguration != null)
			{
				_lockConfiguration.SettingChanged -= OnServerConfigSourceSettingChanged;
			}
			RestoreLocalConfigValues();
			_synchronizedConfigEntries.Clear();
			_serverConfigPeers.Clear();
			_registeredConfigRoutedRpc = null;
		}

		private void PatchBetterMapAutoPinObservation()
		{
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Expected O, but got Unknown
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Expected O, but got Unknown
			if (_harmony == null)
			{
				return;
			}
			if (!Chainloader.PluginInfos.TryGetValue("xtav1m.BetterMap", out var value) || (Object)(object)value.Instance == (Object)null)
			{
				if (_debugLogging.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"BetterMap AutoPins observation not installed: BetterMap is not present (expected on dedicated BMC-only servers).");
				}
				return;
			}
			try
			{
				Assembly assembly = ((object)value.Instance).GetType().Assembly;
				Type type = assembly.GetType("BetterMap.Scripts.Pins.AutoPins");
				if (type == null)
				{
					type = SafeGetTypes(assembly).FirstOrDefault((Type type2) => string.Equals(type2.Name, "AutoPins", StringComparison.Ordinal));
				}
				if (type == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"BetterMap AutoPins observation unavailable: BetterMap.Scripts.Pins.AutoPins was not found.");
					return;
				}
				MethodInfo methodInfo = AccessTools.Method(typeof(BetterMapCleanupPlugin), "BetterMapAutoPinsPlacePrefix", (Type[])null, (Type[])null);
				MethodInfo methodInfo2 = AccessTools.Method(typeof(BetterMapCleanupPlugin), "BetterMapAutoPinsSweepPostfix", (Type[])null, (Type[])null);
				int num = 0;
				int num2 = 0;
				MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo3 in methods)
				{
					if (string.Equals(methodInfo3.Name, "Place", StringComparison.Ordinal))
					{
						ParameterInfo[] parameters = methodInfo3.GetParameters();
						if (parameters.Length >= 3 && parameters.Any((ParameterInfo parameter) => parameter.ParameterType == typeof(Vector3)))
						{
							_harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
							num++;
						}
					}
					else if (string.Equals(methodInfo3.Name, "Sweep", StringComparison.Ordinal))
					{
						_harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						num2++;
					}
				}
				_betterMapObservationHookInstalled = num > 0 && num2 > 0;
				if (_betterMapObservationHookInstalled)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"BetterMap AutoPins observation installed: Place={num}, Sweep={num2}. BetterMapCleanup reuses BetterMap's own resource sweep; no automatic client world scan is scheduled.");
				}
				else
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)$"BetterMap AutoPins observation incomplete: Place={num}, Sweep={num2}. Merge-member learning may be limited, but no fallback periodic scan will be enabled automatically.");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("BetterMap AutoPins observation patch failed: " + ex.GetBaseException().Message));
			}
		}

		private static IEnumerable<Type> SafeGetTypes(Assembly assembly)
		{
			try
			{
				return assembly.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				return ex.Types.Where((Type type) => type != null).Cast<Type>();
			}
		}

		private static void BetterMapAutoPinsPlacePrefix(object[] __args)
		{
			//IL_0061: 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_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			BetterMapCleanupPlugin instance = Instance;
			if ((Object)(object)instance == (Object)null || !instance._enabled.Value || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Minimap.instance == (Object)null || __args == null || __args.Length == 0)
			{
				return;
			}
			try
			{
				object obj = __args[0];
				if (obj == null || !TryReadBetterMapOreRule(obj, out string ruleName))
				{
					return;
				}
				ZNetView val = __args.OfType<ZNetView>().FirstOrDefault();
				Vector3 val2 = Vector3.zero;
				bool flag = false;
				for (int i = 0; i < __args.Length; i++)
				{
					if (__args[i] is Vector3 val3)
					{
						val2 = val3;
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					return;
				}
				string text = (((Object)(object)val != (Object)null) ? ResolvePrefabName((Component)(object)val) : string.Empty);
				if (text.Length == 0 && (Object)(object)val != (Object)null)
				{
					text = NormalizePrefabName(((Object)((Component)val).gameObject).name ?? string.Empty);
				}
				if (text.Length == 0)
				{
					return;
				}
				ResourceMemberRecord resourceMemberRecord = instance.FindResourceMember(ruleName, val2);
				bool num = resourceMemberRecord?.Depleted ?? false;
				bool flag2 = num;
				bool flag3 = false;
				Component val4 = FindTrackedResourceComponent(val);
				if ((Object)(object)val4 != (Object)null)
				{
					string prefabName = ResolvePrefabName(val4);
					float currentHealth;
					float maximumHealth;
					float remainingFraction;
					if (IsTransitionalWrapper(val4, prefabName))
					{
						flag2 = false;
						flag3 = true;
					}
					else if (TryGetMineableHealth(val4, prefabName, out currentHealth, out maximumHealth, out remainingFraction))
					{
						flag2 = Mathf.Clamp01(1f - remainingFraction) * 100f + 0.001f >= Mathf.Clamp(instance._resourceDepletionThreshold.Value, 1f, 100f);
						flag3 = true;
					}
				}
				if (resourceMemberRecord == null && !flag3)
				{
					flag2 = false;
				}
				bool flag4 = instance.SetResourceMemberState(ruleName, CanonicalizeLifecyclePrefabForSync(text), val2, flag2, persistNow: false);
				instance._betterMapObservationChanged |= flag4;
				if (num && !flag2 && instance._restoreRespawnedResources.Value && instance.TryGetBetterMapSettings(out BetterMapSettingsSnapshot snapshot))
				{
					List<BetterMapRuleSnapshot> list = FindLifecycleRules(snapshot, text);
					if (list.Count > 0)
					{
						instance.ApplyResourcePinRespawn(snapshot, list, text, val2, "BetterMap AutoPins observation");
					}
				}
			}
			catch (Exception ex)
			{
				if (instance._debugLogging.Value)
				{
					((BaseUnityPlugin)instance).Logger.LogDebug((object)("BetterMap AutoPins candidate observation failed safely: " + ex.GetBaseException().Message));
				}
			}
		}

		private static void BetterMapAutoPinsSweepPostfix()
		{
			BetterMapCleanupPlugin instance = Instance;
			if ((Object)(object)instance == (Object)null || !instance._enabled.Value || !instance._betterMapObservationChanged || (Object)(object)Minimap.instance == (Object)null)
			{
				return;
			}
			instance._betterMapObservationChanged = false;
			try
			{
				if (instance.TryGetBetterMapSettings(out BetterMapSettingsSnapshot snapshot))
				{
					int num = instance.UpdateClusterLabelsFromRegistry(snapshot);
					if (instance._debugLogging.Value && num > 0)
					{
						((BaseUnityPlugin)instance).Logger.LogInfo((object)$"BetterMap AutoPins observation updated {num} merge label(s) after BetterMap's own sweep.");
					}
				}
			}
			catch (Exception ex)
			{
				if (instance._debugLogging.Value)
				{
					((BaseUnityPlugin)instance).Logger.LogDebug((object)("BetterMap AutoPins post-sweep label refresh failed safely: " + ex.GetBaseException().Message));
				}
			}
		}

		private static Component? FindTrackedResourceComponent(ZNetView? view)
		{
			if ((Object)(object)view == (Object)null || (Object)(object)((Component)view).gameObject == (Object)null)
			{
				return null;
			}
			Component component = (Component)(object)((Component)view).GetComponent<Pickable>();
			if ((Object)(object)component != (Object)null && TryResolveTrackedResourcePrefab(component, out string canonicalPrefab, out string observedPrefab))
			{
				return component;
			}
			component = (Component)(object)((Component)view).GetComponent<Destructible>();
			if ((Object)(object)component != (Object)null && TryResolveTrackedResourcePrefab(component, out observedPrefab, out canonicalPrefab))
			{
				return component;
			}
			component = (Component)(object)((Component)view).GetComponent<MineRock5>();
			if ((Object)(object)component != (Object)null && TryResolveTrackedResourcePrefab(component, out canonicalPrefab, out observedPrefab))
			{
				return component;
			}
			component = (Component)(object)((Component)view).GetComponent<MineRock>();
			if ((Object)(object)component != (Object)null && TryResolveTrackedResourcePrefab(component, out observedPrefab, out canonicalPrefab))
			{
				return component;
			}
			Component[] componentsInChildren = ((Component)view).GetComponentsInChildren<Component>(true);
			foreach (Component val in componentsInChildren)
			{
				if ((val is Destructible || val is MineRock5 || val is MineRock || val is Pickable) && TryResolveTrackedResourcePrefab(val, out canonicalPrefab, out observedPrefab))
				{
					return val;
				}
			}
			return null;
		}

		private static bool TryReadBetterMapOreRule(object rule, out string ruleName)
		{
			ruleName = string.Empty;
			Type type = rule.GetType();
			FieldInfo field = type.GetField("Name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			FieldInfo field2 = type.GetField("Category", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			FieldInfo field3 = type.GetField("IsLocation", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			FieldInfo field4 = type.GetField("Enabled", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			ruleName = (field?.GetValue(rule) as string) ?? string.Empty;
			if (ruleName.Length == 0)
			{
				return false;
			}
			object obj = field3?.GetValue(rule);
			bool flag = default(bool);
			int num;
			if (obj is bool)
			{
				flag = (bool)obj;
				num = 1;
			}
			else
			{
				num = 0;
			}
			if (((uint)num & (flag ? 1u : 0u)) != 0)
			{
				return false;
			}
			object obj2 = field2?.GetValue(rule);
			IEnumerable<string> enumerable = type.GetField("Prefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(rule) as IEnumerable<string>;
			bool flag2 = string.Equals(obj2?.ToString(), "Forage", StringComparison.OrdinalIgnoreCase) && enumerable != null && enumerable.Any(IsSmokePuffPrefab);
			if (obj2 == null || (!string.Equals(obj2.ToString(), "Ore", StringComparison.OrdinalIgnoreCase) && !flag2))
			{
				return false;
			}
			object obj3 = field4?.GetValue(rule);
			if (obj3 is ConfigEntry<bool> val && !val.Value)
			{
				return false;
			}
			if (obj3 is bool && !(bool)obj3)
			{
				return false;
			}
			return true;
		}

		private void PatchServerAuthoritativeResourceHooks()
		{
			MethodInfo postfix;
			HashSet<MethodBase> patched;
			List<string> installed;
			if (_harmony != null)
			{
				postfix = AccessTools.Method(typeof(BetterMapCleanupPlugin), "ServerAuthoritativeResourcePostfix", (Type[])null, (Type[])null);
				patched = new HashSet<MethodBase>();
				installed = new List<string>();
				Patch(typeof(Destructible), new string[2] { "RPC_Damage", "Damage" });
				Patch(typeof(MineRock5), new string[4] { "RPC_SetAreaHealth", "RPC_Hit", "DamageArea", "Damage" });
				Patch(typeof(MineRock), new string[2] { "RPC_Hit", "Damage" });
				((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("Server-authoritative resource hooks installed: {0} method(s) [{1}].", installed.Count, string.Join("; ", installed)));
			}
			void Patch(Type type, params string[] names)
			{
				//IL_0045: Unknown result type (might be due to invalid IL or missing references)
				//IL_0052: Expected O, but got Unknown
				MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (names.Contains(methodInfo.Name) && patched.Add(methodInfo))
					{
						try
						{
							_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(postfix), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
							installed.Add(type.Name + "." + methodInfo.Name);
						}
						catch (Exception ex)
						{
							((BaseUnityPlugin)this).Logger.LogWarning((object)("Server-authoritative resource hook could not patch " + type.Name + "." + methodInfo.Name + ": " + ex.GetBaseException().Message));
						}
					}
				}
			}
		}

		private static void ServerAuthoritativeResourcePostfix(object __instance)
		{
			BetterMapCleanupPlugin instance = Instance;
			Component val = (Component)((__instance is Component) ? __instance : null);
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)val == (Object)null) && instance._enabled.Value && instance._resourceLifecycleCleanup.Value && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				instance.ProcessServerAuthoritativeResourceState(val, forceDepleted: false, "authoritative health update");
			}
		}

		private void ProcessServerResourceAwake(Component component)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if (_serverResourceSync.Value && _restoreRespawnedResources.Value && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && TryResolveTrackedResourcePrefab(component, out string canonicalPrefab, out string _))
			{
				LoadServerResourceStatesIfNeeded();
				if (TryFindServerStateNear(canonicalPrefab, component.transform.position, out string _, out SyncedResourceRecord record) && record != null && record.Depleted)
				{
					ProcessServerAuthoritativeResourceState(component, forceDepleted: false, "resource Awake/respawn");
				}
			}
		}

		private void ProcessServerAuthoritativeResourceState(Component component, bool forceDepleted, string source)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			if (!_serverResourceSync.Value || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)component == (Object)null || (Object)(object)component.gameObject == (Object)null || !TryResolveTrackedResourcePrefab(component, out string canonicalPrefab, out string observedPrefab) || IsTransitionalWrapper(component, observedPrefab))
			{
				return;
			}
			bool flag;
			if (forceDepleted)
			{
				flag = true;
			}
			else
			{
				if (!TryGetMineableHealth(component, observedPrefab, out var _, out var _, out var remainingFraction))
				{
					return;
				}
				flag = Mathf.Clamp01(1f - remainingFraction) * 100f + 0.001f >= Mathf.Clamp(_resourceDepletionThreshold.Value, 1f, 100f);
			}
			LoadServerResourceStatesIfNeeded();
			Vector3 position = component.transform.position;
			if (TryFindServerStateNear(canonicalPrefab, position, out string key, out SyncedResourceRecord record) && record != null)
			{
				position = record.Position;
			}
			if (flag || (TryFindServerStateNear(canonicalPrefab, position, out key, out SyncedResourceRecord record2) && record2 != null && record2.Depleted))
			{
				int serverAuthoritativeTransitions = _serverAuthoritativeTransitions;
				StoreServerResourceState(new SyncedResourceRecord(canonicalPrefab, position, flag), broadcast: true);
				if (_serverAuthoritativeTransitions != serverAuthoritativeTransitions && _debugLogging.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Server authoritative resource event: prefab='{canonicalPrefab}', depleted={flag}, source={source}, position={position}.");
				}
			}
		}

		private bool TryFindServerStateNear(string canonicalPrefab, Vector3 position, out string key, out SyncedResourceRecord? record)
		{
			//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)
			key = string.Empty;
			record = null;
			float num = 6.25f;
			foreach (KeyValuePair<string, SyncedResourceRecord> serverResourceState in _serverResourceStates)
			{
				SyncedResourceRecord value = serverResourceState.Value;
				if (string.Equals(CanonicalizeLifecyclePrefabForSync(value.PrefabName), canonicalPrefab, StringComparison.OrdinalIgnoreCase))
				{
					float num2 = DistanceXZSquared(value.Position, position);
					if (num2 <= num)
					{
						num = num2;
						key = serverResourceState.Key;
						record = value;
					}
				}
			}
			return record != null;
		}

		private bool IsKnownTrackedResourceComponent(Component component)
		{
			string canonicalPrefab;
			string observedPrefab;
			return TryResolveTrackedResourcePrefab(component, out canonicalPrefab, out observedPrefab);
		}

		private static bool TryResolveTrackedResourcePrefab(Component component, out string canonicalPrefab, out string observedPrefab)
		{
			canonicalPrefab = string.Empty;
			observedPrefab = string.Empty;
			if ((Object)(object)component == (Object)null || (Object)(object)component.gameObject == (Object)null)
			{
				return false;
			}
			foreach (string item in ResolvePrefabNameCandidates(component))
			{
				string text = CanonicalizeLifecyclePrefabForSync(item);
				if (KnownTrackedResourcePrefabs.Contains(text) || IsSmokePuffPrefab(text))
				{
					observedPrefab = item;
					canonicalPrefab = text;
					return true;
				}
			}
			return false;
		}

		private static string CanonicalizeLifecyclePrefabForSync(string prefabName)
		{
			string text = NormalizePrefabName(prefabName);
			if (text.Length == 0)
			{
				return string.Empty;
			}
			return ResolveLifecycleRulePrefabName(text);
		}

		private static bool IsSmokePuffPrefab(string name)
		{
			string a = NormalizePrefabName(name).Replace("_", "");
			if (!string.Equals(a, "PickableMushroomSmokePuff", StringComparison.OrdinalIgnoreCase))
			{
				return string.Equals(a, "PickableSmokePuff", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static bool IsLifecycleRule(BetterMapRuleSnapshot rule)
		{
			if (!string.Equals(rule.Category.Name, "Ore", StringComparison.OrdinalIgnoreCase))
			{
				if (string.Equals(rule.Category.Name, "Forage", StringComparison.OrdinalIgnoreCase))
				{
					return rule.Prefabs.Any(IsSmokePuffPrefab);
				}
				return false;
			}
			return true;
		}

		private void PatchPickableLifecycle()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.Patch((MethodBase)AccessTools.Method(typeof(Pickable), "SetPicked", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(BetterMapCleanupPlugin), "PickableStatePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private static void PickableStatePostfix(Pickable __instance)
		{
			//IL_006e: 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_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			BetterMapCleanupPlugin instance = Instance;
			if ((Object)(object)instance == (Object)null || !instance._enabled.Value || !instance._resourceLifecycleCleanup.Value || (Object)(object)__instance == (Object)null || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			string prefab = ResolvePrefabName((Component)(object)__instance);
			if (!IsSmokePuffPrefab(prefab))
			{
				return;
			}
			bool picked = __instance.GetPicked();
			Vector3 position = ((Component)__instance).transform.position;
			try
			{
				if (ZNet.instance.IsServer() && instance._serverResourceSync.Value)
				{
					instance.LoadServerResourceStatesIfNeeded();
					if (picked || (instance._restoreRespawnedResources.Value && instance.TryFindServerStateNear(prefab, position, out string _, out SyncedResourceRecord record) && record != null && record.Depleted))
					{
						instance.StoreServerResourceState(new SyncedResourceRecord(prefab, position, picked), broadcast: true);
					}
				}
				if (picked || instance._resourceMembers.Values.Any((ResourceMemberRecord member) => member.Depleted && member.PrefabName == prefab && DistanceXZSquared(member.Position, position) < 2.25f))
				{
					instance.ReportResourceStateToServer(prefab, position, picked);
				}
				if (!((Object)(object)Minimap.instance == (Object)null))
				{
					SyncedResourceRecord value = new SyncedResourceRecord(prefab, position, picked);
					instance._pendingSyncedResourceStates[BuildResourceDepletionKey(prefab, position)] = value;
					instance.ApplyPendingSyncedResourceStates();
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)instance).Logger.LogWarning((object)("Smoke Puff state update failed: " + ex.GetBaseException().Message));
			}
		}

		private void Awake()
		{
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Expected O, but got Unknown
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Expected O, but got Unknown
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Expected O, but got Unknown
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			BindServerConfigSyncSettings();
			_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable BetterMapCleanup.");
			_cleanupOnWorldLoad = ((BaseUnityPlugin)this).Config.Bind<bool>("Cleanup", "Cleanup On World Load", true, "After entering a world, remove pins disabled by BetterMap rules and re-apply BetterMap Merge Distance to existing auto-pins.");
			_cleanupWhenConfigChanges = ((BaseUnityPlugin)this).Config.Bind<bool>("Cleanup", "Cleanup When Config Changes", true, "Automatically update existing pins when BetterMap Auto Pins - <Biome> settings or Merge Distance change.");
			_removeAllWhenAutoPinsDisabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Cleanup", "Remove All Resource Pins When Auto Pins Disabled", true, "When BetterMap [Auto Pins] Enable is false, remove all BetterMap resource/location auto-pins controlled by biome category settings.");
			_cleanupNow = ((BaseUnityPlugin)this).Config.Bind<bool>("Cleanup", "Cleanup Now", false, "Set to true to immediately clean disabled BetterMap pin categories, re-apply BetterMap Merge Distance, repair BetterMap PinRecord integrity. It resets itself to false.");
			_mergeExistingPins = ((BaseUnityPlugin)this).Config.Bind<bool>("Cleanup", "Apply Merge Distance To Existing Pins", true, "Re-apply BetterMap [Auto Pins] Merge Distance to existing saved BetterMap auto-pins. Rule-backed pins are merged only with the