Decompiled source of STU Ward v1.1.9

STUWard.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using LocalizationManager;
using Microsoft.CodeAnalysis;
using STUWard;
using ServerSync;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("STUWard")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("STUWard")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("1.1.9")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.9.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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 LocalizationManager
{
	public static class Localizer
	{
		private static readonly string[] FileExtensions = new string[2] { ".json", ".yml" };

		private static readonly Dictionary<string, Dictionary<string, string>> CachedTranslations = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);

		private static readonly IDeserializer Deserializer = new DeserializerBuilder().IgnoreFields().Build();

		private static BaseUnityPlugin? _plugin;

		private static bool _registeredWithJotunn;

		private static bool _hookedJotunn;

		private static string? _lastLoggedAppliedLanguage;

		private static int _lastLoggedAppliedCount = -1;

		private static BaseUnityPlugin Plugin
		{
			get
			{
				//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00af: Expected O, but got Unknown
				if ((Object)(object)_plugin != (Object)null)
				{
					return _plugin;
				}
				IEnumerable<TypeInfo> source;
				try
				{
					source = Assembly.GetExecutingAssembly().DefinedTypes.ToList();
				}
				catch (ReflectionTypeLoadException ex)
				{
					source = from type in ex.Types
						where type != null
						select type.GetTypeInfo();
				}
				_plugin = (BaseUnityPlugin)Chainloader.ManagerObject.GetComponent((Type)source.First((TypeInfo type) => type.IsClass && typeof(BaseUnityPlugin).IsAssignableFrom(type)));
				return _plugin;
			}
		}

		private static CustomLocalization CustomLocalization => LocalizationManager.Instance.GetLocalization();

		public static event Action? OnLocalizationComplete;

		public static void Load()
		{
			_ = Plugin;
			LoadTranslations();
			RegisterWithJotunn();
			HookJotunn();
			ReloadCurrentLanguageIfAvailable();
			SafeCallLocalizeComplete();
		}

		public static void Unload()
		{
			if (_hookedJotunn)
			{
				LocalizationManager.OnLocalizationAdded -= ReloadCurrentLanguageIfAvailable;
				_hookedJotunn = false;
			}
			_plugin = null;
			_registeredWithJotunn = false;
			_lastLoggedAppliedLanguage = null;
			_lastLoggedAppliedCount = -1;
		}

		public static void ReloadCurrentLanguageIfAvailable()
		{
			if (Localization.instance != null)
			{
				LoadTranslations();
				RegisterWithJotunn();
				ApplyCurrentLanguage(Localization.instance);
			}
		}

		public static void LoadLocalizationLater()
		{
			ReloadCurrentLanguageIfAvailable();
		}

		public static void SafeCallLocalizeComplete()
		{
			Localizer.OnLocalizationComplete?.Invoke();
		}

		public static void AddText(string key, string text)
		{
			if (!CachedTranslations.TryGetValue("English", out Dictionary<string, string> value))
			{
				value = new Dictionary<string, string>(StringComparer.Ordinal);
				CachedTranslations["English"] = value;
			}
			value[key] = text;
			string text2 = "English";
			string text3 = key;
			CustomLocalization.ClearToken(ref text2, ref text3);
			CustomLocalization.AddTranslation(ref text2, ref text3, text);
			if (Localization.instance != null)
			{
				Localization.instance.AddWord(key, text);
			}
		}

		private static void LoadTranslations()
		{
			if (CachedTranslations.Count > 0)
			{
				return;
			}
			HashSet<string> availableLanguages = GetAvailableLanguages();
			Dictionary<string, string> dictionary = ReadMergedLanguage("English", null);
			if (dictionary == null || dictionary.Count == 0)
			{
				throw new InvalidOperationException("Found no English localizations in mod " + Plugin.Info.Metadata.Name + ". Expected translations/English.json or translations/English.yml.");
			}
			CachedTranslations["English"] = dictionary;
			foreach (string item in availableLanguages)
			{
				if (!item.Equals("English", StringComparison.OrdinalIgnoreCase))
				{
					Dictionary<string, string> dictionary2 = ReadMergedLanguage(item, dictionary);
					if (dictionary2 != null && dictionary2.Count > 0)
					{
						CachedTranslations[item] = dictionary2;
					}
				}
			}
			ManualLogSource log = STUWard.Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("Loaded STUWard localizations: " + string.Join(", ", CachedTranslations.Select<KeyValuePair<string, Dictionary<string, string>>, string>((KeyValuePair<string, Dictionary<string, string>> kv) => $"{kv.Key}={kv.Value.Count}"))));
			}
		}

		private static void RegisterWithJotunn()
		{
			if (_registeredWithJotunn)
			{
				return;
			}
			_registeredWithJotunn = true;
			foreach (KeyValuePair<string, Dictionary<string, string>> cachedTranslation in CachedTranslations)
			{
				cachedTranslation.Deconstruct(out var key, out var value);
				string text = key;
				Dictionary<string, string> dictionary = new Dictionary<string, string>(value, StringComparer.Ordinal);
				CustomLocalization.AddTranslation(ref text, dictionary);
			}
			ManualLogSource log = STUWard.Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("Registered STUWard localizations with Jotunn: " + string.Join(", ", CachedTranslations.Keys)));
			}
		}

		private static void HookJotunn()
		{
			if (!_hookedJotunn)
			{
				LocalizationManager.OnLocalizationAdded += ReloadCurrentLanguageIfAvailable;
				_hookedJotunn = true;
			}
		}

		private static void ApplyCurrentLanguage(Localization localization)
		{
			string selectedLanguage = localization.GetSelectedLanguage();
			Dictionary<string, string> translationsForLanguage = GetTranslationsForLanguage(selectedLanguage);
			foreach (var (text3, text4) in translationsForLanguage)
			{
				localization.AddWord(text3, text4);
			}
			if (!string.Equals(_lastLoggedAppliedLanguage, selectedLanguage, StringComparison.Ordinal) || _lastLoggedAppliedCount != translationsForLanguage.Count)
			{
				_lastLoggedAppliedLanguage = selectedLanguage;
				_lastLoggedAppliedCount = translationsForLanguage.Count;
				ManualLogSource log = STUWard.Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)$"Applied STUWard localization for language '{selectedLanguage}' with {translationsForLanguage.Count} entries.");
				}
			}
		}

		private static Dictionary<string, string> GetTranslationsForLanguage(string language)
		{
			if (CachedTranslations.TryGetValue(language, out Dictionary<string, string> value))
			{
				return value;
			}
			return CachedTranslations["English"];
		}

		private static HashSet<string> GetAvailableLanguages()
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "English" };
			string[] manifestResourceNames = typeof(Localizer).Assembly.GetManifestResourceNames();
			foreach (string text in manifestResourceNames)
			{
				string[] fileExtensions = FileExtensions;
				foreach (string text2 in fileExtensions)
				{
					_ = "translations." + text2;
					if (!text.Contains(".translations.", StringComparison.OrdinalIgnoreCase) || !text.EndsWith(text2, StringComparison.OrdinalIgnoreCase))
					{
						continue;
					}
					int num = text.LastIndexOf(".translations.", StringComparison.OrdinalIgnoreCase);
					if (num >= 0)
					{
						int num2 = num + ".translations.".Length;
						int num3 = text.Length - num2 - text2.Length;
						if (num3 > 0)
						{
							hashSet.Add(text.Substring(num2, num3));
						}
					}
				}
			}
			foreach (string item in from path in Directory.GetFiles(Paths.PluginPath, Plugin.Info.Metadata.Name + ".*", SearchOption.AllDirectories)
				where FileExtensions.Contains<string>(Path.GetExtension(path), StringComparer.OrdinalIgnoreCase)
				select path)
			{
				string[] array = Path.GetFileNameWithoutExtension(item).Split('.');
				if (array.Length >= 2 && !string.IsNullOrWhiteSpace(array[1]))
				{
					hashSet.Add(array[1]);
				}
			}
			return hashSet;
		}

		private static Dictionary<string, string>? ReadMergedLanguage(string language, IReadOnlyDictionary<string, string>? englishFallback)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			if (englishFallback != null)
			{
				MergeInto(dictionary, englishFallback);
			}
			Dictionary<string, string> dictionary2 = ReadEmbeddedLanguage(language);
			if (dictionary2 != null)
			{
				MergeInto(dictionary, dictionary2);
			}
			Dictionary<string, string> dictionary3 = ReadExternalLanguage(language);
			if (dictionary3 != null)
			{
				MergeInto(dictionary, dictionary3);
			}
			if (dictionary.Count != 0)
			{
				return dictionary;
			}
			return null;
		}

		private static Dictionary<string, string>? ReadEmbeddedLanguage(string language)
		{
			string[] fileExtensions = FileExtensions;
			foreach (string text in fileExtensions)
			{
				byte[] array = ReadEmbeddedFileBytes("translations." + language + text, typeof(Localizer).Assembly);
				if (array != null)
				{
					return DeserializeTranslations(Encoding.UTF8.GetString(array));
				}
			}
			return null;
		}

		private static Dictionary<string, string>? ReadExternalLanguage(string language)
		{
			string text = FindExternalLanguageFile(language);
			if (text != null)
			{
				return DeserializeTranslations(File.ReadAllText(text, Encoding.UTF8));
			}
			return null;
		}

		private static string? FindExternalLanguageFile(string language)
		{
			string[] fileExtensions = FileExtensions;
			foreach (string text in fileExtensions)
			{
				string searchPattern = Plugin.Info.Metadata.Name + "." + language + text;
				string text2 = Directory.GetFiles(Paths.PluginPath, searchPattern, SearchOption.AllDirectories).FirstOrDefault();
				if (!string.IsNullOrWhiteSpace(text2))
				{
					return text2;
				}
			}
			return null;
		}

		private static Dictionary<string, string> DeserializeTranslations(string rawText)
		{
			return Deserializer.Deserialize<Dictionary<string, string>>(rawText) ?? new Dictionary<string, string>(StringComparer.Ordinal);
		}

		private static void MergeInto(IDictionary<string, string> target, IReadOnlyDictionary<string, string> source)
		{
			foreach (KeyValuePair<string, string> item in source)
			{
				item.Deconstruct(out var key, out var value);
				string key2 = key;
				string value2 = value;
				target[key2] = value2;
			}
		}

		public static byte[]? ReadEmbeddedFileBytes(string resourceFileName, Assembly? containingAssembly = null)
		{
			string resourceFileName2 = resourceFileName;
			using MemoryStream memoryStream = new MemoryStream();
			if ((object)containingAssembly == null)
			{
				containingAssembly = Assembly.GetCallingAssembly();
			}
			string text = containingAssembly.GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith(resourceFileName2, StringComparison.OrdinalIgnoreCase));
			if (text == null)
			{
				return null;
			}
			containingAssembly.GetManifestResourceStream(text)?.CopyTo(memoryStream);
			return (memoryStream.Length == 0L) ? null : memoryStream.ToArray();
		}
	}
	public static class LocalizationManagerVersion
	{
		public const string Version = "1.4.1";
	}
}
namespace STUWard
{
	[BepInPlugin("sighsorry.STUWard", "STUWard", "1.1.9")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal enum Toggle
		{
			Off,
			On
		}

		internal enum PickupBlockRule
		{
			BlockAllExceptWhitelist,
			AllowAllExceptBlacklist
		}

		internal enum HostileCreatureStructureProtectionMode
		{
			Off,
			UnattendedOnly,
			Always
		}

		internal enum DiagnosticLogMode
		{
			Off,
			Failures,
			Verbose
		}

		internal const string ModName = "STUWard";

		internal const string ModVersion = "1.1.9";

		internal const string Author = "sighsorry";

		internal const string ModGuid = "sighsorry.STUWard";

		internal static readonly ConfigSync ConfigSync = new ConfigSync("sighsorry.STUWard")
		{
			DisplayName = "STUWard",
			CurrentVersion = "1.1.9",
			MinimumRequiredVersion = "1.1.9"
		};

		private Harmony _harmony;

		internal static ManualLogSource Log = null;

		internal static Plugin Instance = null;

		internal static WardGuiController WardGui = null;

		internal static ConfigEntry<Toggle> ServerConfigLocked = null;

		internal static ConfigEntry<int> MaxWardsPerSteamId = null;

		internal static ConfigEntry<float> MaxWardRadius = null;

		internal static ConfigEntry<PickupBlockRule> PickupBlockMode = null;

		internal static ConfigEntry<HostileCreatureStructureProtectionMode> HostileCreatureStructureProtection = null;

		internal static ConfigEntry<float> UnattendedWardTrustedPlayerRangeBuffer = null;

		internal static ConfigEntry<float> UnattendedWardTrustedPresenceGraceSeconds = null;

		internal static ConfigEntry<float> UnattendedWardPresenceRefreshInterval = null;

		internal static ConfigEntry<Toggle> DisableVanillaGuardStoneRecipe = null;

		internal static ConfigEntry<string> StuWardRecipe = null;

		internal static ConfigEntry<KeyboardShortcut> WardSettingsShortcut = null;

		internal static ConfigEntry<int> WardMinimapPinScale = null;

		internal static ConfigEntry<Toggle> WardMinimapActiveRanges = null;

		internal static ConfigEntry<DiagnosticLogMode> WardDiagnosticLogging = null;

		private void Awake()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			WardPluginBootstrap.InitializeCore();
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				WardPluginConfigBindings.BindAll();
				WardPluginBootstrap.InitializeFeatures();
				_harmony = new Harmony("sighsorry.STUWard");
				WardPatchRegistry.ApplyAll(_harmony);
				WardGui = CreateOrReuseWardGuiController();
				((BaseUnityPlugin)this).Config.Save();
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private void Update()
		{
			WardPluginBootstrap.Update();
		}

		private void OnDestroy()
		{
			WardPluginBootstrap.Shutdown();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			((BaseUnityPlugin)this).Config.Save();
		}

		internal static bool IsBlockedItem(string prefabName)
		{
			return WardItemPrefabPolicy.IsBlockedItem(prefabName);
		}

		internal static bool HasBlockedItems()
		{
			return WardItemPrefabPolicy.HasBlockedItems();
		}

		internal static bool IsWardSettingsShortcutDown()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (WardSettingsShortcut != null)
			{
				KeyboardShortcut value = WardSettingsShortcut.Value;
				if ((int)((KeyboardShortcut)(ref value)).MainKey != 0)
				{
					value = WardSettingsShortcut.Value;
					return ((KeyboardShortcut)(ref value)).IsDown();
				}
			}
			return false;
		}

		internal static bool HasWardSettingsShortcutBinding()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Invalid comparison between Unknown and I4
			if (WardSettingsShortcut != null)
			{
				KeyboardShortcut value = WardSettingsShortcut.Value;
				return (int)((KeyboardShortcut)(ref value)).MainKey > 0;
			}
			return false;
		}

		internal static string GetWardSettingsShortcutLabel()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Invalid comparison between Unknown and I4
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			if (WardSettingsShortcut != null)
			{
				KeyboardShortcut value = WardSettingsShortcut.Value;
				if ((int)((KeyboardShortcut)(ref value)).MainKey != 0)
				{
					KeyboardShortcut value2 = WardSettingsShortcut.Value;
					List<string> list = new List<string>();
					AddModifierLabel(list, ((KeyboardShortcut)(ref value2)).Modifiers, (KeyCode)306, (KeyCode)305, "Ctrl");
					AddModifierLabel(list, ((KeyboardShortcut)(ref value2)).Modifiers, (KeyCode)308, (KeyCode)307, "Alt");
					AddModifierLabel(list, ((KeyboardShortcut)(ref value2)).Modifiers, (KeyCode)304, (KeyCode)303, "Shift");
					foreach (KeyCode modifier in ((KeyboardShortcut)(ref value2)).Modifiers)
					{
						if (modifier - 303 > 5)
						{
							list.Add(GetKeyLabel(modifier));
						}
					}
					list.Add(GetKeyLabel(((KeyboardShortcut)(ref value2)).MainKey));
					return string.Join("+", list);
				}
			}
			return WardLocalization.Localize("$stuw_shortcut_unbound", "Unbound");
		}

		private static void AddModifierLabel(List<string> parts, IEnumerable<KeyCode> modifiers, KeyCode left, KeyCode right, string label)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//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)
			foreach (KeyCode modifier in modifiers)
			{
				if (modifier == left || modifier == right)
				{
					parts.Add(label);
					break;
				}
			}
		}

		private static string GetKeyLabel(KeyCode keyCode)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected I4, but got Unknown
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected I4, but got Unknown
			switch (keyCode - 48)
			{
			default:
				switch (keyCode - 303)
				{
				case 4:
				case 5:
					return "Alt";
				case 2:
				case 3:
					return "Ctrl";
				case 0:
				case 1:
					return "Shift";
				default:
					return ((object)(KeyCode)(ref keyCode)).ToString();
				}
			case 0:
				return "0";
			case 1:
				return "1";
			case 2:
				return "2";
			case 3:
				return "3";
			case 4:
				return "4";
			case 5:
				return "5";
			case 6:
				return "6";
			case 7:
				return "7";
			case 8:
				return "8";
			case 9:
				return "9";
			}
		}

		internal static ConfigEntry<T> BindConfigEntry<T>(string group, string name, T value, string description, bool synchronizedSetting = true)
		{
			return Instance.BindConfig(group, name, value, description, synchronizedSetting);
		}

		internal static bool ShouldLogWardDiagnosticFailures()
		{
			if (WardDiagnosticLogging != null)
			{
				return WardDiagnosticLogging.Value != DiagnosticLogMode.Off;
			}
			return false;
		}

		internal static bool ShouldLogWardDiagnosticVerbose()
		{
			if (WardDiagnosticLogging != null)
			{
				return WardDiagnosticLogging.Value == DiagnosticLogMode.Verbose;
			}
			return false;
		}

		internal static void LogWardDiagnosticFailure(string context, string message)
		{
			if (ShouldLogWardDiagnosticFailures() && Log != null)
			{
				Log.LogWarning((object)("[WardDiag:" + context + "] " + message));
			}
		}

		internal static void LogWardDiagnosticVerbose(string context, string message)
		{
			if (ShouldLogWardDiagnosticVerbose() && Log != null)
			{
				Log.LogInfo((object)("[WardDiag:" + context + "] " + message));
			}
		}

		private static WardGuiController CreateOrReuseWardGuiController()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			if ((Object)(object)WardGuiController.Instance != (Object)null)
			{
				return WardGuiController.Instance;
			}
			GameObject val = new GameObject("STUWard.WardGui");
			Object.DontDestroyOnLoad((Object)val);
			return val.AddComponent<WardGuiController>();
		}

		private ConfigEntry<T> BindConfig<T>(string group, string name, T value, string description, bool synchronizedSetting = true)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			string text = (synchronizedSetting ? "Synced with server." : "Not synced with server.");
			string text2 = (string.IsNullOrWhiteSpace(description) ? text : (description.TrimEnd() + " " + text));
			ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, new ConfigDescription(text2, (AcceptableValueBase)null, Array.Empty<object>()));
			if (synchronizedSetting)
			{
				ConfigSync.AddConfigEntry<T>(val).SynchronizedConfig = true;
			}
			return val;
		}
	}
	internal static class WardPluginBootstrap
	{
		internal static void InitializeCore()
		{
			Localizer.Load();
		}

		internal static void InitializeFeatures()
		{
			WardGuiLayoutSettings.Bind();
			ManagedWardConfigFileService.Initialize();
			WardItemPrefabPolicy.Initialize();
			WardOwnership.Initialize();
			PrefabManager.OnVanillaPrefabsAvailable += RegisterStuWardPiece;
			RegisterStuWardPiece();
		}

		internal static void Update()
		{
			ManagedWardRuntimeLifecycle.Update();
		}

		internal static void Shutdown()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterStuWardPiece;
			WardPluginConfigBindings.UnbindAll();
			WardItemPrefabPolicy.Shutdown();
			ManagedWardConfigFileService.Shutdown();
			GuildsCompat.TryShutdownHooks();
			Localizer.Unload();
		}

		internal static void ApplyRecipeSettings()
		{
			StuWardPrefab.ApplyRecipeSettings();
		}

		private static void RegisterStuWardPiece()
		{
			StuWardPrefab.Register();
			ApplyRecipeSettings();
		}
	}
	internal static class WardPluginConfigBindings
	{
		private static bool _handlersBound;

		internal static void BindAll()
		{
			UnbindAll();
			BindGeneral();
			BindClient();
			BindDebug();
			BindHandlers();
		}

		internal static void UnbindAll()
		{
			if (_handlersBound)
			{
				UnbindHandler<float>(Plugin.MaxWardRadius, HandleMaxWardRadiusChanged);
				UnbindHandler<int>(Plugin.MaxWardsPerSteamId, HandleMaxWardLimitChanged);
				UnbindHandler<Plugin.HostileCreatureStructureProtectionMode>(Plugin.HostileCreatureStructureProtection, HandleWardPresenceConfigChanged);
				UnbindHandler<float>(Plugin.UnattendedWardTrustedPlayerRangeBuffer, HandleWardPresenceConfigChanged);
				UnbindHandler<float>(Plugin.UnattendedWardTrustedPresenceGraceSeconds, HandleWardPresenceConfigChanged);
				UnbindHandler<float>(Plugin.UnattendedWardPresenceRefreshInterval, HandleWardPresenceConfigChanged);
				UnbindHandler<Plugin.Toggle>(Plugin.DisableVanillaGuardStoneRecipe, HandleRecipeSettingsChanged);
				UnbindHandler<string>(Plugin.StuWardRecipe, HandleRecipeSettingsChanged);
				UnbindHandler<int>(Plugin.WardMinimapPinScale, HandleLocalWardPinConfigChanged);
				UnbindHandler<Plugin.Toggle>(Plugin.WardMinimapActiveRanges, HandleLocalWardPinConfigChanged);
				_handlersBound = false;
			}
		}

		private static void BindGeneral()
		{
			Plugin.ServerConfigLocked = Plugin.BindConfigEntry("1 - General", "Lock Configuration", Plugin.Toggle.On, "If on, the configuration is locked and can be changed by server admins only.");
			Plugin.ConfigSync.AddLockingConfigEntry<Plugin.Toggle>(Plugin.ServerConfigLocked);
			Plugin.MaxWardsPerSteamId = Plugin.BindConfigEntry("1 - General", "Max Wards Per Steam ID", 3, "Maximum number of managed Wards allowed per Steam/platform account. Set to -1 for unlimited.");
			Plugin.MaxWardRadius = Plugin.BindConfigEntry("1 - General", "Max Ward Radius", 32f, "Maximum configurable Ward radius. Valid range: 8 to 64.");
			Plugin.PickupBlockMode = Plugin.BindConfigEntry("1 - General", "Pickup Block Mode", Plugin.PickupBlockRule.BlockAllExceptWhitelist, "Pickup rule inside a foreign enabled ward. BlockAllExceptWhitelist blocks every item pickup except pickup_whitelist. AllowAllExceptBlacklist allows item pickup except pickup_blacklist.");
			Plugin.HostileCreatureStructureProtection = Plugin.BindConfigEntry("2 - Unattended Protection", "Hostile Creature Structure Protection Mode", Plugin.HostileCreatureStructureProtectionMode.UnattendedOnly, "Controls whether building pieces inside an enabled ward ignore damage from MonsterAI-controlled attackers. Off disables this extra protection. UnattendedOnly protects while no trusted player is nearby. Always protects regardless of trusted player presence.");
			Plugin.UnattendedWardTrustedPlayerRangeBuffer = Plugin.BindConfigEntry("2 - Unattended Protection", "Unattended Ward Trusted Player Range Buffer", 16f, "Additional distance beyond the ward radius used when checking for nearby trusted players before hostile-creature structure protection turns off.");
			Plugin.UnattendedWardTrustedPresenceGraceSeconds = Plugin.BindConfigEntry("2 - Unattended Protection", "Unattended Ward Trusted Presence Grace Seconds", 10f, "How long a ward keeps counting as attended after the last nearby trusted player leaves.");
			Plugin.UnattendedWardPresenceRefreshInterval = Plugin.BindConfigEntry("2 - Unattended Protection", "Unattended Ward Presence Refresh Interval", 1f, "How often nearby trusted-player attendance is recalculated for unattended hostile-creature structure protection.");
			Plugin.DisableVanillaGuardStoneRecipe = Plugin.BindConfigEntry("1 - General", "Disable Vanilla Guard Stone Recipe", Plugin.Toggle.On, "If on, the vanilla guard_stone build recipe is removed from the Hammer piece table while STUWard remains available.");
			Plugin.StuWardRecipe = Plugin.BindConfigEntry("1 - General", "STUWard Recipe", "GreydwarfEye:1,BoneFragments:3,Flint:5,Wood:7", "STUWard recipe override. Format: ItemPrefab:Amount[:Recover], ...");
		}

		private static void BindClient()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			Plugin.WardSettingsShortcut = Plugin.BindConfigEntry<KeyboardShortcut>("3 - Client", "Ward Settings Shortcut", new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Shortcut used to open the ward settings UI while looking at your ward. Example values: LeftAlt + E, F7", synchronizedSetting: false);
			Plugin.WardMinimapPinScale = Plugin.BindConfigEntry("3 - Client", "Ward Minimap Pin Scale", 1, "0 disables ward icon pins. 1 is the default icon size. 100 means x100 icon size.", synchronizedSetting: false);
			Plugin.WardMinimapActiveRanges = Plugin.BindConfigEntry("3 - Client", "Ward Minimap Active Ranges", Plugin.Toggle.On, "If on, enabled managed wards also show their active radius on the minimap and map.", synchronizedSetting: false);
		}

		private static void BindDebug()
		{
			Plugin.WardDiagnosticLogging = Plugin.BindConfigEntry("4 - Debug", "Ward Diagnostic Logging", Plugin.DiagnosticLogMode.Off, "Local-only scalar diagnostic logging for ward ownership/toggle flows. Use Failures for rejection paths only, or Verbose for request and state tracing. Enable separately on each client/server instance you want logs from.", synchronizedSetting: false);
		}

		private static void BindHandlers()
		{
			BindHandler<float>(Plugin.MaxWardRadius, HandleMaxWardRadiusChanged);
			BindHandler<int>(Plugin.MaxWardsPerSteamId, HandleMaxWardLimitChanged);
			BindHandler<Plugin.HostileCreatureStructureProtectionMode>(Plugin.HostileCreatureStructureProtection, HandleWardPresenceConfigChanged);
			BindHandler<float>(Plugin.UnattendedWardTrustedPlayerRangeBuffer, HandleWardPresenceConfigChanged);
			BindHandler<float>(Plugin.UnattendedWardTrustedPresenceGraceSeconds, HandleWardPresenceConfigChanged);
			BindHandler<float>(Plugin.UnattendedWardPresenceRefreshInterval, HandleWardPresenceConfigChanged);
			BindHandler<Plugin.Toggle>(Plugin.DisableVanillaGuardStoneRecipe, HandleRecipeSettingsChanged);
			BindHandler<string>(Plugin.StuWardRecipe, HandleRecipeSettingsChanged);
			BindHandler<int>(Plugin.WardMinimapPinScale, HandleLocalWardPinConfigChanged);
			BindHandler<Plugin.Toggle>(Plugin.WardMinimapActiveRanges, HandleLocalWardPinConfigChanged);
			_handlersBound = true;
		}

		private static void HandleMaxWardRadiusChanged(object? _, EventArgs __)
		{
			WardSettings.HandleMaxRadiusChanged();
		}

		private static void HandleMaxWardLimitChanged(object? _, EventArgs __)
		{
			WardOwnership.HandleWardLimitPolicyChanged();
		}

		private static void HandleWardPresenceConfigChanged(object? _, EventArgs __)
		{
			ManagedWardRuntimeInvalidationService.PublishPresencePolicyChanged("ward presence config changed");
		}

		private static void HandleRecipeSettingsChanged(object? _, EventArgs __)
		{
			WardPluginBootstrap.ApplyRecipeSettings();
		}

		private static void HandleLocalWardPinConfigChanged(object? _, EventArgs __)
		{
			WardMinimapPinsManager.HandleLocalConfigChanged();
		}

		private static void BindHandler<T>(ConfigEntry<T>? entry, EventHandler handler)
		{
			if (entry != null)
			{
				entry.SettingChanged += handler;
			}
		}

		private static void UnbindHandler<T>(ConfigEntry<T>? entry, EventHandler handler)
		{
			if (entry != null)
			{
				entry.SettingChanged -= handler;
			}
		}
	}
	internal readonly struct ManagedWardRef
	{
		internal PrivateArea? Area { get; }

		internal ZNetView? NView { get; }

		internal ZDO? Zdo { get; }

		internal StuWardArea? Component { get; }

		internal bool HasArea => (Object)(object)Area != (Object)null;

		internal bool HasManagedComponent => (Object)(object)Component != (Object)null;

		internal bool IsManagedZdo => WardOwnership.IsManagedWardZdo(Zdo);

		internal bool IsManaged
		{
			get
			{
				if (!HasManagedComponent)
				{
					return IsManagedZdo;
				}
				return true;
			}
		}

		internal bool IsPlacementGhost
		{
			get
			{
				if ((Object)(object)Area != (Object)null)
				{
					return Player.IsPlacementGhost(((Component)Area).gameObject);
				}
				return false;
			}
		}

		internal bool HasValidNetworkIdentity
		{
			get
			{
				if ((Object)(object)NView != (Object)null && NView.IsValid())
				{
					return Zdo != null;
				}
				return false;
			}
		}

		internal bool IsOwner
		{
			get
			{
				if ((Object)(object)NView != (Object)null && NView.IsValid())
				{
					return NView.IsOwner();
				}
				return false;
			}
		}

		internal long CreatorPlayerId
		{
			get
			{
				ZDO? zdo = Zdo;
				if (zdo == null)
				{
					return 0L;
				}
				return zdo.GetLong(ZDOVars.s_creator, 0L);
			}
		}

		private ManagedWardRef(PrivateArea? area, ZNetView? nview, ZDO? zdo, StuWardArea? component)
		{
			Area = area;
			NView = nview;
			Zdo = zdo;
			Component = component;
		}

		internal static ManagedWardRef FromArea(PrivateArea? area)
		{
			return FromArea(area, null);
		}

		internal static ManagedWardRef FromArea(PrivateArea? area, ZDO? knownZdo)
		{
			ZNetView nView = WardPrivateAreaSafeAccess.GetNView(area);
			ZDO zdo = ((knownZdo != null && knownZdo.IsValid()) ? knownZdo : WardPrivateAreaSafeAccess.GetZdo(nView));
			return new ManagedWardRef(area, nView, zdo, ((Object)(object)area != (Object)null) ? ((Component)area).GetComponent<StuWardArea>() : null);
		}

		internal ManagedWardRef EnsureManagedComponent(out bool added)
		{
			added = false;
			if ((Object)(object)Area == (Object)null || HasManagedComponent || !IsManagedZdo || IsPlacementGhost)
			{
				return this;
			}
			((Component)Area).gameObject.AddComponent<StuWardArea>();
			added = true;
			return FromArea(Area, Zdo);
		}
	}
	internal static class WardPatchRegistry
	{
		internal static void ApplyAll(Harmony harmony)
		{
			List<string> list = new List<string>();
			PatchWardCore(harmony, list);
			PatchInteractions(harmony, list);
			PatchBuildAndDamage(harmony, list);
			PatchItemAndCombat(harmony);
			PatchCompat(harmony);
			if (list.Count == 0)
			{
				return;
			}
			string text = "Failed to apply required patches: " + string.Join(", ", list);
			Plugin.Log.LogError((object)text);
			throw new InvalidOperationException(text);
		}

		private static void PatchWardCore(Harmony harmony, ICollection<string> failedRequiredPatches)
		{
			PatchRequired(harmony, typeof(PrivateAreaAwakePatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(PrivateAreaOnDestroyPatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(PrivateAreaUpdateStatusPatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(PrivateAreaSetupPatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(ZNetSceneCreateObjectManagedWardPatch), failedRequiredPatches);
			PatchOptional(harmony, typeof(PrivateAreaShowAreaMarkerPatch));
			PatchOptional(harmony, typeof(PrivateAreaGetHoverTextPatch));
			PatchOptional(harmony, typeof(PrivateAreaHideMarkerPatch));
			PatchOptional(harmony, typeof(PrivateAreaHaveLocalAccessManagedPatch));
			PatchOptional(harmony, typeof(PrivateAreaCheckAccessManagedPatch));
			PatchOptional(harmony, typeof(PrivateAreaInteractAdminDebugPatch));
			PatchRequired(harmony, typeof(PrivateAreaRpcTogglePermittedManagedPatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(PrivateAreaRpcToggleEnabledAdminDebugPatch), failedRequiredPatches);
			PatchOptional(harmony, typeof(PrivateAreaRpcFlashShieldVolumePatch));
			PatchOptional(harmony, typeof(PrivateAreaAddPermittedSnapshotPatch));
			PatchOptional(harmony, typeof(PrivateAreaRemovePermittedSnapshotPatch));
			PatchOptional(harmony, typeof(PrivateAreaSetEnabledWardMinimapVisibilityPatch));
			PatchOptional(harmony, typeof(CircleProjectorCreateSegmentsPatch));
			PatchOptional(harmony, typeof(DoorRpcUseDoorPatch));
			PatchOptional(harmony, typeof(ZNetAwakeWardOwnershipPatch));
			PatchOptional(harmony, typeof(ZNetRpcPeerInfoWardOwnershipPatch));
			PatchOptional(harmony, typeof(ZNetRpcCharacterIdWardOwnershipPatch));
			PatchOptional(harmony, typeof(ZNetDisconnectWardOwnershipPatch));
			PatchOptional(harmony, typeof(PlayerStartWardOwnershipPatch));
			PatchOptional(harmony, typeof(PlayerOnDeathWardUiPatch));
			PatchOptional(harmony, typeof(PlayerOnRespawnWardUiPatch));
			PatchOptional(harmony, typeof(PlayerUpdateWardAdminDebugPatch));
			PatchOptional(harmony, typeof(MinimapSetMapModeWardPinsPatch));
			PatchOptional(harmony, typeof(PlayerUpdateWardPinsPatch));
			PatchOptional(harmony, typeof(ObjectDBAwakePatch));
			PatchOptional(harmony, typeof(ObjectDBCopyOtherDbPatch));
		}

		private static void PatchInteractions(Harmony harmony, ICollection<string> failedRequiredPatches)
		{
			PatchRequired(harmony, typeof(DirectInteractionPatches), failedRequiredPatches);
			PatchOptional(harmony, typeof(ContainerCheckAccessManagedPatch));
			PatchRequired(harmony, typeof(UseItemInteractionPatches), failedRequiredPatches);
			PatchOptional(harmony, typeof(StationUsePatches));
			PatchOptional(harmony, typeof(ProcessingInteractionPatches));
			PatchOptional(harmony, typeof(TeleportWorldTeleportPatch));
			PatchOptional(harmony, typeof(TeleportWorldTriggerPatch));
			PatchRequired(harmony, typeof(ItemDropPickupPatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(HumanoidPickupPatch), failedRequiredPatches);
		}

		private static void PatchBuildAndDamage(Harmony harmony, ICollection<string> failedRequiredPatches)
		{
			PatchRequired(harmony, typeof(PlayerTryPlacePiecePatch), failedRequiredPatches);
			PatchOptional(harmony, typeof(PlayerSetupPlacementGhostPatch));
			PatchOptional(harmony, typeof(PlayerUpdatePlacementGhostPatch));
			PatchRequired(harmony, typeof(PlayerPlacePiecePatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(PlayerCheckCanRemovePiecePatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(PlayerRemovePiecePatch), failedRequiredPatches);
			PatchOptional(harmony, typeof(PlayerRepairPatch));
			PatchRequired(harmony, typeof(ZNetSceneDestroyPatch), failedRequiredPatches);
			PatchOptional(harmony, typeof(AttackSpawnOnHitTerrainPatch));
			PatchOptional(harmony, typeof(TerrainOpAwakePatch));
			PatchRequired(harmony, typeof(WearNTearDamagePatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(WearNTearRpcDamagePatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(WearNTearRemovePatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(WearNTearRpcRemovePatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(DestructibleDamagePatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(DestructibleRpcDamagePatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(TreeBaseDamagePatch), failedRequiredPatches);
			PatchRequired(harmony, typeof(TreeBaseRpcDamagePatch), failedRequiredPatches);
		}

		private static void PatchItemAndCombat(Harmony harmony)
		{
			PatchOptional(harmony, typeof(PlayerUseHotbarItemPatch));
			PatchOptional(harmony, typeof(HumanoidUseItemPatch));
			PatchOptional(harmony, typeof(HumanoidUpdateEquipmentPatch));
			PatchOptional(harmony, typeof(HumanoidEquipItemPatch));
			PatchOptional(harmony, typeof(HumanoidStartAttackPatch));
			PatchOptional(harmony, typeof(AttackStartBlockedItemTargetPatch));
			PatchOptional(harmony, typeof(InventoryGuiOnRightClickItemPatch));
			PatchOptional(harmony, typeof(TameableCollectorCollectorItemPatch));
			PatchOptional(harmony, typeof(AzuCraftyBoxesNearbyContainersPatch));
			PatchOptional(harmony, typeof(FeastRpcTryEatPatch));
			PatchOptional(harmony, typeof(PlayerAutoPickupPatch));
			PatchOptional(harmony, typeof(TerminalAwakeWardReportCommandPatch));
			PatchOptional(harmony, typeof(TerminalTryRunCommandWardReportPatch));
		}

		private static void PatchCompat(Harmony harmony)
		{
			Harmony harmony2 = harmony;
			PatchOptionalCompat("GuildsCompat", delegate
			{
				GuildsCompat.TryPatch(harmony2);
			});
			PatchOptionalCompat("TargetPortalCompat", delegate
			{
				TargetPortalCompat.TryPatch(harmony2);
			});
		}

		private static void PatchRequired(Harmony harmony, Type patchType, ICollection<string> failedRequiredPatches)
		{
			try
			{
				harmony.CreateClassProcessor(patchType).Patch();
			}
			catch (Exception ex)
			{
				failedRequiredPatches.Add(patchType.Name);
				Plugin.Log.LogError((object)("Failed to patch required " + patchType.Name + ": " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static void PatchOptional(Harmony harmony, Type patchType)
		{
			try
			{
				harmony.CreateClassProcessor(patchType).Patch();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to patch optional " + patchType.Name + ": " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static void PatchOptionalCompat(string name, Action patchAction)
		{
			try
			{
				patchAction();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to patch " + name + ": " + ex.GetType().Name + ": " + ex.Message));
			}
		}
	}
	[DisallowMultipleComponent]
	internal sealed class StuWardArea : MonoBehaviour
	{
		internal const string PrefabName = "piece_stuward";

		internal const string BasePrefabName = "guard_stone";

		internal const string DisplayName = "$stuw_piece_name";

		internal const string Description = "$stuw_piece_desc";

		internal static bool IsManaged(PrivateArea? area)
		{
			if ((Object)(object)area != (Object)null)
			{
				return (Object)(object)((Component)area).GetComponent<StuWardArea>() != (Object)null;
			}
			return false;
		}
	}
	internal static class ManagedWardIdentity
	{
		internal static bool IsManaged(PrivateArea? area)
		{
			bool matchedByComponent;
			bool matchedByZdo;
			return TryResolve(ManagedWardRef.FromArea(area), repairComponent: false, out matchedByComponent, out matchedByZdo);
		}

		internal static bool EnsureManagedComponent(PrivateArea? area)
		{
			return EnsureManagedComponent(ManagedWardRef.FromArea(area));
		}

		internal static bool EnsureManagedComponent(PrivateArea? area, ZDO? zdo)
		{
			return EnsureManagedComponent(ManagedWardRef.FromArea(area, zdo));
		}

		internal static bool EnsureManagedComponent(ManagedWardRef ward)
		{
			bool matchedByComponent;
			bool matchedByZdo;
			return TryResolve(ward, repairComponent: true, out matchedByComponent, out matchedByZdo) && matchedByComponent;
		}

		internal static bool TryResolve(PrivateArea? area, ZDO? zdo, bool repairComponent, out bool matchedByComponent, out bool matchedByZdo)
		{
			return TryResolve(ManagedWardRef.FromArea(area, zdo), repairComponent, out matchedByComponent, out matchedByZdo);
		}

		internal static bool TryResolve(ManagedWardRef ward, bool repairComponent, out bool matchedByComponent, out bool matchedByZdo)
		{
			matchedByComponent = ward.HasManagedComponent;
			matchedByZdo = ward.IsManagedZdo;
			if (!ward.HasArea)
			{
				return matchedByZdo;
			}
			if ((!matchedByComponent & matchedByZdo) && repairComponent)
			{
				bool added;
				ManagedWardRef managedWardRef = ward.EnsureManagedComponent(out added);
				matchedByComponent = managedWardRef.HasManagedComponent;
				if (added)
				{
					Plugin.LogWardDiagnosticVerbose("Placement.Identity", "Restored missing StuWardArea component from managed ward ZDO identity. " + WardDiagnosticInfo.DescribeWard(managedWardRef.Area));
				}
			}
			return matchedByComponent | matchedByZdo;
		}
	}
	internal sealed class StuWardPlacedHook : MonoBehaviour, IPlaced
	{
		public void OnPlaced()
		{
			PrivateArea component = ((Component)this).GetComponent<PrivateArea>();
			ManagedWardRef ward = ManagedWardRef.FromArea(component);
			if (ManagedWardIdentity.EnsureManagedComponent(ward))
			{
				WardOwnership.TryStampLocalManagedWardOwnerAccount(ward);
				WardOwnership.NotifyServerManagedWardPlaced(ward);
				ManagedWardMapStateService.NotifyLiveWardMutation(component, ManagedWardMapMutationKind.IndexAndPins, "local managed ward placed");
				Plugin.LogWardDiagnosticVerbose("Placement.OnPlaced", "IPlaced.OnPlaced hit for managed ward. " + WardDiagnosticInfo.DescribeWard(component));
			}
		}
	}
	internal static class StuWardPrefab
	{
		private static bool _registered;

		private static GameObject? _stuWardPrefab;

		private static GameObject? _vanillaGuardStonePrefab;

		private static int _vanillaGuardStoneIndex = -1;

		private static Requirement[]? _defaultStuWardRequirements;

		private static string? _lastLoggedPieceIconState;

		internal static void Register()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			if (_registered || PieceManager.Instance.GetPiece("piece_stuward") != null)
			{
				_registered = true;
			}
			else
			{
				if ((Object)(object)PrefabManager.Instance.GetPrefab("guard_stone") == (Object)null)
				{
					return;
				}
				PieceConfig val = new PieceConfig
				{
					PieceTable = "Hammer",
					Name = "$stuw_piece_name",
					Description = "$stuw_piece_desc"
				};
				CustomPiece val2 = new CustomPiece("piece_stuward", "guard_stone", val);
				GameObject piecePrefab = val2.PiecePrefab;
				Piece piece = val2.Piece;
				PrivateArea val3 = (((Object)(object)piecePrefab != (Object)null) ? piecePrefab.GetComponent<PrivateArea>() : null);
				if ((Object)(object)piecePrefab == (Object)null || (Object)(object)piece == (Object)null || (Object)(object)val3 == (Object)null)
				{
					Plugin.Log.LogWarning((object)"Failed to create STUWard clone prefab from guard_stone.");
					return;
				}
				if ((Object)(object)piecePrefab.GetComponent<StuWardArea>() == (Object)null)
				{
					piecePrefab.AddComponent<StuWardArea>();
				}
				if ((Object)(object)piecePrefab.GetComponent<StuWardPlacedHook>() == (Object)null)
				{
					piecePrefab.AddComponent<StuWardPlacedHook>();
				}
				piece.m_name = "$stuw_piece_name";
				piece.m_description = "$stuw_piece_desc";
				piece.m_resources = CloneRequirements(piece.m_resources);
				val3.m_name = "$stuw_piece_name";
				val3.m_radius = 8f;
				if ((Object)(object)val3.m_areaMarker != (Object)null)
				{
					val3.m_areaMarker.m_radius = 8f;
				}
				_stuWardPrefab = piecePrefab;
				_defaultStuWardRequirements = CloneRequirements(piece.m_resources);
				PieceManager.Instance.AddPiece(val2);
				_registered = PieceManager.Instance.GetPiece("piece_stuward") != null;
				if (_registered)
				{
					Plugin.Log.LogInfo((object)"Registered STUWard clone piece.");
				}
			}
		}

		internal static void ApplyRecipeSettings()
		{
			ApplyVanillaGuardStoneRecipeSetting();
			ApplyStuWardRecipeSetting();
		}

		internal static Sprite? GetPieceIcon()
		{
			Piece stuWardPiece = GetStuWardPiece();
			if ((Object)(object)stuWardPiece != (Object)null && (Object)(object)stuWardPiece.m_icon != (Object)null)
			{
				return LogPieceIconResolution(stuWardPiece.m_icon, "stuWardPrefab piece icon '" + ((Object)stuWardPiece.m_icon).name + "'");
			}
			CustomPiece piece = PieceManager.Instance.GetPiece("piece_stuward");
			if ((Object)(object)((piece != null) ? piece.Piece : null) != (Object)null && (Object)(object)piece.Piece.m_icon != (Object)null)
			{
				return LogPieceIconResolution(piece.Piece.m_icon, "registered piece icon '" + ((Object)piece.Piece.m_icon).name + "'");
			}
			GameObject val = ((piece != null) ? piece.PiecePrefab : null) ?? PrefabManager.Instance.GetPrefab("piece_stuward") ?? PrefabManager.Instance.GetPrefab("guard_stone");
			Sprite val2 = ((!((Object)(object)val != (Object)null)) ? null : val.GetComponent<Piece>()?.m_icon);
			if ((Object)(object)val2 != (Object)null)
			{
				string text = (((Object)(object)val != (Object)null) ? ((Object)val).name : "null");
				return LogPieceIconResolution(val2, "prefab '" + text + "' piece icon '" + ((Object)val2).name + "'");
			}
			return LogMissingPieceIcon(string.Format("stuWardPrefabPresent={0}, registeredPiecePresent={1}, registeredPiecePrefabPresent={2}, fallbackPrefab='{3}'", (Object)(object)_stuWardPrefab != (Object)null, (Object)(object)((piece != null) ? piece.Piece : null) != (Object)null, (Object)(object)((piece != null) ? piece.PiecePrefab : null) != (Object)null, ((val != null) ? ((Object)val).name : null) ?? "null"));
		}

		internal static Requirement[] GetCurrentStuWardRequirements()
		{
			return CloneRequirements(GetStuWardPiece()?.m_resources);
		}

		private static Piece? GetStuWardPiece()
		{
			Piece val = (((Object)(object)_stuWardPrefab != (Object)null) ? _stuWardPrefab.GetComponent<Piece>() : null);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			CustomPiece piece = PieceManager.Instance.GetPiece("piece_stuward");
			if (piece == null)
			{
				return null;
			}
			return piece.Piece;
		}

		private static Sprite LogPieceIconResolution(Sprite icon, string source)
		{
			string text = "resolved:" + source;
			if (_lastLoggedPieceIconState != text)
			{
				_lastLoggedPieceIconState = text;
				Plugin.LogWardDiagnosticVerbose("WardPins.Icon", "Resolved piece_stuward icon from " + source + ".");
			}
			return icon;
		}

		private static Sprite? LogMissingPieceIcon(string context)
		{
			string text = "missing:" + context;
			if (_lastLoggedPieceIconState != text)
			{
				_lastLoggedPieceIconState = text;
				Plugin.LogWardDiagnosticFailure("WardPins.Icon", "Failed to resolve piece_stuward icon. " + context);
			}
			return null;
		}

		internal static void ApplyVanillaGuardStoneRecipeSetting()
		{
			PieceTable? hammerPieceTable = GetHammerPieceTable();
			List<GameObject> list = hammerPieceTable?.m_pieces;
			GameObject prefab = PrefabManager.Instance.GetPrefab("guard_stone");
			if ((Object)(object)hammerPieceTable == (Object)null || list == null || (Object)(object)prefab == (Object)null)
			{
				return;
			}
			if (_vanillaGuardStonePrefab == null)
			{
				_vanillaGuardStonePrefab = prefab;
			}
			List<int> matchingGuardStoneIndexes = GetMatchingGuardStoneIndexes(list, prefab);
			if (_vanillaGuardStoneIndex < 0 && matchingGuardStoneIndexes.Count > 0)
			{
				_vanillaGuardStoneIndex = matchingGuardStoneIndexes[0];
			}
			if (Plugin.DisableVanillaGuardStoneRecipe != null && Plugin.DisableVanillaGuardStoneRecipe.Value == Plugin.Toggle.On)
			{
				for (int num = matchingGuardStoneIndexes.Count - 1; num >= 0; num--)
				{
					list.RemoveAt(matchingGuardStoneIndexes[num]);
				}
			}
			else if (matchingGuardStoneIndexes.Count == 0 && (Object)(object)_vanillaGuardStonePrefab != (Object)null)
			{
				int index = ((_vanillaGuardStoneIndex >= 0) ? Mathf.Clamp(_vanillaGuardStoneIndex, 0, list.Count) : list.Count);
				list.Insert(index, _vanillaGuardStonePrefab);
			}
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				localPlayer.UpdateAvailablePiecesList();
			}
		}

		private static void ApplyStuWardRecipeSetting()
		{
			Piece val = (((Object)(object)_stuWardPrefab != (Object)null) ? _stuWardPrefab.GetComponent<Piece>() : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			string text = Plugin.StuWardRecipe?.Value?.Trim() ?? string.Empty;
			Requirement[] requirements;
			if (string.IsNullOrWhiteSpace(text))
			{
				if (_defaultStuWardRequirements != null)
				{
					val.m_resources = CloneRequirements(_defaultStuWardRequirements);
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						localPlayer.UpdateAvailablePiecesList();
					}
				}
			}
			else if (!TryParseRequirements(text, out requirements))
			{
				Plugin.Log.LogWarning((object)("Invalid STUWard recipe override '" + text + "'. Keeping previous recipe."));
			}
			else
			{
				val.m_resources = requirements;
				Player localPlayer2 = Player.m_localPlayer;
				if (localPlayer2 != null)
				{
					localPlayer2.UpdateAvailablePiecesList();
				}
			}
		}

		private static PieceTable? GetHammerPieceTable()
		{
			GameObject prefab = PrefabManager.Instance.GetPrefab("Hammer");
			return (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<ItemDrop>() : null)?.m_itemData?.m_shared?.m_buildPieces;
		}

		private static List<int> GetMatchingGuardStoneIndexes(List<GameObject> pieces, GameObject guardStonePrefab)
		{
			List<int> list = new List<int>();
			for (int i = 0; i < pieces.Count; i++)
			{
				GameObject val = pieces[i];
				if (!((Object)(object)val == (Object)null) && ((Object)(object)val == (Object)(object)guardStonePrefab || ((Object)val).name == "guard_stone"))
				{
					list.Add(i);
				}
			}
			return list;
		}

		private static Requirement[] CloneRequirements(Requirement[]? source)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			if (source == null || source.Length == 0)
			{
				return Array.Empty<Requirement>();
			}
			Requirement[] array = (Requirement[])(object)new Requirement[source.Length];
			for (int i = 0; i < source.Length; i++)
			{
				Requirement val = source[i];
				array[i] = new Requirement
				{
					m_resItem = val.m_resItem,
					m_amount = val.m_amount,
					m_extraAmountOnlyOneIngredient = val.m_extraAmountOnlyOneIngredient,
					m_amountPerLevel = val.m_amountPerLevel,
					m_recover = val.m_recover
				};
			}
			return array;
		}

		private static bool TryParseRequirements(string value, out Requirement[] requirements)
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Expected O, but got Unknown
			requirements = Array.Empty<Requirement>();
			string[] array = value.Split(new char[4] { ',', ';', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 0)
			{
				return false;
			}
			List<Requirement> list = new List<Requirement>(array.Length);
			string[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				string[] array3 = array2[i].Split(':');
				int num = array3.Length;
				if ((num < 2 || num > 3) ? true : false)
				{
					return false;
				}
				string text = array3[0].Trim();
				if (string.IsNullOrWhiteSpace(text) || !int.TryParse(array3[1], out var result) || result <= 0)
				{
					return false;
				}
				GameObject val = ResolveItemPrefab(text);
				ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val2 == (Object)null)
				{
					Plugin.Log.LogWarning((object)("Unable to resolve STUWard recipe item prefab '" + text + "'."));
					return false;
				}
				bool result2 = true;
				if (array3.Length == 3 && !TryParseBool(array3[2], out result2))
				{
					return false;
				}
				list.Add(new Requirement
				{
					m_resItem = val2,
					m_amount = result,
					m_amountPerLevel = 1,
					m_recover = result2
				});
			}
			requirements = list.ToArray();
			return true;
		}

		private static GameObject? ResolveItemPrefab(string prefabName)
		{
			if (string.IsNullOrWhiteSpace(prefabName))
			{
				return null;
			}
			ObjectDB instance = ObjectDB.instance;
			GameObject val = ((instance != null) ? instance.GetItemPrefab(prefabName) : null);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			return PrefabManager.Instance.GetPrefab(prefabName);
		}

		private static bool TryParseBool(string value, out bool result)
		{
			switch (value.Trim().ToLowerInvariant())
			{
			case "1":
			case "yes":
			case "on":
			case "true":
				result = true;
				return true;
			case "0":
			case "off":
			case "no":
			case "false":
				result = false;
				return true;
			default:
				result = false;
				return false;
			}
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "Awake")]
	internal static class ObjectDBAwakePatch
	{
		private static void Postfix()
		{
			Localizer.ReloadCurrentLanguageIfAvailable();
			StuWardPrefab.ApplyRecipeSettings();
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
	internal static class ObjectDBCopyOtherDbPatch
	{
		private static void Postfix()
		{
			Localizer.ReloadCurrentLanguageIfAvailable();
			StuWardPrefab.ApplyRecipeSettings();
		}
	}
	internal readonly struct CachedWardGuildIdentity
	{
		internal bool HasGuild { get; }

		internal int GuildId { get; }

		internal string GuildName { get; }

		internal DateTime ExpiresAtUtc { get; }

		internal CachedWardGuildIdentity(bool hasGuild, int guildId, string guildName, DateTime expiresAtUtc)
		{
			HasGuild = hasGuild;
			GuildId = guildId;
			GuildName = guildName;
			ExpiresAtUtc = expiresAtUtc;
		}
	}
	internal readonly struct CachedPlayerPlatformIdentity
	{
		internal bool HasPlatformId { get; }

		internal string PlatformId { get; }

		internal DateTime ExpiresAtUtc { get; }

		internal CachedPlayerPlatformIdentity(bool hasPlatformId, string platformId, DateTime expiresAtUtc)
		{
			HasPlatformId = hasPlatformId;
			PlatformId = platformId;
			ExpiresAtUtc = expiresAtUtc;
		}
	}
	internal readonly struct WardGuildCharacterIdentity
	{
		internal long PlayerId { get; }

		internal string AccountId { get; }

		internal string PlayerName { get; }

		internal bool HasPlayerId => PlayerId != 0;

		internal bool HasAccountAndName
		{
			get
			{
				if (!string.IsNullOrWhiteSpace(AccountId))
				{
					return !string.IsNullOrWhiteSpace(PlayerName);
				}
				return false;
			}
		}

		internal WardGuildCharacterIdentity(long playerId, string accountId, string playerName)
		{
			PlayerId = playerId;
			AccountId = WardOwnership.NormalizeAccountIdValue(accountId);
			PlayerName = playerName?.Trim() ?? string.Empty;
		}
	}
	internal static class GuildsCompat
	{
		private enum AvailabilityState
		{
			Unknown,
			Available,
			Unavailable
		}

		private sealed class PendingWardGuildProjectionRefreshState
		{
			internal readonly Dictionary<long, WardGuildCharacterIdentity> TargetIdentitiesByPlayerId = new Dictionary<long, WardGuildCharacterIdentity>();

			internal readonly Dictionary<string, WardGuildCharacterIdentity> TargetIdentitiesByCharacterKey = new Dictionary<string, WardGuildCharacterIdentity>(StringComparer.Ordinal);

			internal readonly HashSet<int> AffectedGuildIds = new HashSet<int>();

			internal bool PendingFullRefresh;

			internal bool PendingLiveDisplayRefresh;

			internal string PendingLiveDisplayReason = string.Empty;

			internal DateTime FlushAtUtc = DateTime.MinValue;
		}

		private const string GuildIdKey = "stuw_guild_id";

		private const string GuildNameKey = "stuw_guild_name";

		private static readonly TimeSpan GuildLookupCacheDuration = TimeSpan.FromSeconds(30.0);

		private const string SyncPlayerGuildRpc = "STUWard_SyncPlayerGuild";

		private static readonly TimeSpan PendingPlayerGuildSyncLifetime = TimeSpan.FromSeconds(15.0);

		private static readonly Dictionary<long, SyncedWardGuildIdentity> ServerSyncedGuildByPlayerId = new Dictionary<long, SyncedWardGuildIdentity>();

		private static readonly Dictionary<string, SyncedWardGuildIdentity> ServerSyncedGuildByCharacterKey = new Dictionary<string, SyncedWardGuildIdentity>(StringComparer.Ordinal);

		private static readonly Dictionary<long, PendingPlayerGuildSync> PendingPlayerGuildSyncsBySender = new Dictionary<long, PendingPlayerGuildSync>();

		private static bool _syncRpcsRegistered;

		private static bool _localGuildSyncPending = true;

		private static long _lastSyncedLocalPlayerId;

		private static int _lastSyncedLocalGuildId = int.MinValue;

		private static string _lastSyncedLocalGuildName = string.Empty;

		private const string GuildsPluginGuid = "org.bepinex.plugins.guilds";

		private static readonly TimeSpan AvailabilityProbeBackoff = TimeSpan.FromSeconds(2.0);

		private static readonly Assembly? GuildsAssembly = GetPluginAssembly("org.bepinex.plugins.guilds");

		private static readonly Type? ApiType = GuildsAssembly?.GetType("Guilds.API");

		private static readonly Type? GuildType = GuildsAssembly?.GetType("Guilds.Guild");

		private static readonly Type? GuildGeneralType = GuildsAssembly?.GetType("Guilds.GuildGeneral");

		private static readonly Type? PlayerReferenceType = GuildsAssembly?.GetType("Guilds.PlayerReference");

		private static readonly Type? GuildJoinedDelegateType = ApiType?.GetNestedType("GuildJoined", BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly Type? GuildLeftDelegateType = ApiType?.GetNestedType("GuildLeft", BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly Type? GuildCreatedDelegateType = ApiType?.GetNestedType("GuildCreated", BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly Type? GuildDeletedDelegateType = ApiType?.GetNestedType("GuildDeleted", BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo? IsLoadedMethod = ((ApiType != null) ? AccessTools.Method(ApiType, "IsLoaded", (Type[])null, (Type[])null) : null);

		private static readonly MethodInfo? GetPlayerGuildByPlayerMethod = ((ApiType != null) ? AccessTools.Method(ApiType, "GetPlayerGuild", new Type[1] { typeof(Player) }, (Type[])null) : null);

		private static readonly MethodInfo? GetPlayerGuildByReferenceMethod = ((ApiType != null && PlayerReferenceType != null) ? AccessTools.Method(ApiType, "GetPlayerGuild", new Type[1] { PlayerReferenceType }, (Type[])null) : null);

		private static readonly MethodInfo? GetGuildsMethod = ((ApiType != null) ? AccessTools.Method(ApiType, "GetGuilds", Type.EmptyTypes, (Type[])null) : null);

		private static readonly MethodInfo? GetGuildByIdMethod = ((ApiType != null) ? AccessTools.Method(ApiType, "GetGuild", new Type[1] { typeof(int) }, (Type[])null) : null);

		private static readonly MethodInfo? PlayerReferenceFromStringMethod = ((PlayerReferenceType != null) ? AccessTools.Method(PlayerReferenceType, "fromString", new Type[1] { typeof(string) }, (Type[])null) : null);

		private static readonly MethodInfo? RegisterOnGuildJoinedMethod = ((ApiType != null && GuildJoinedDelegateType != null) ? AccessTools.Method(ApiType, "RegisterOnGuildJoined", new Type[1] { GuildJoinedDelegateType }, (Type[])null) : null);

		private static readonly MethodInfo? RegisterOnGuildLeftMethod = ((ApiType != null && GuildLeftDelegateType != null) ? AccessTools.Method(ApiType, "RegisterOnGuildLeft", new Type[1] { GuildLeftDelegateType }, (Type[])null) : null);

		private static readonly MethodInfo? RegisterOnGuildCreatedMethod = ((ApiType != null && GuildCreatedDelegateType != null) ? AccessTools.Method(ApiType, "RegisterOnGuildCreated", new Type[1] { GuildCreatedDelegateType }, (Type[])null) : null);

		private static readonly MethodInfo? RegisterOnGuildDeletedMethod = ((ApiType != null && GuildDeletedDelegateType != null) ? AccessTools.Method(ApiType, "RegisterOnGuildDeleted", new Type[1] { GuildDeletedDelegateType }, (Type[])null) : null);

		private static readonly MethodInfo? SaveGuildMethod = ((ApiType != null && GuildType != null) ? AccessTools.Method(ApiType, "SaveGuild", new Type[1] { GuildType }, (Type[])null) : null);

		private static readonly FieldInfo? PlayerInfoUserInfoField = AccessTools.Field(typeof(PlayerInfo), "m_userInfo");

		private static readonly FieldInfo? UserInfoIdField = ((PlayerInfoUserInfoField?.FieldType != null) ? AccessTools.Field(PlayerInfoUserInfoField.FieldType, "m_id") : null);

		private static readonly FieldInfo? GuildNameField = ((GuildType != null) ? AccessTools.Field(GuildType, "Name") : null);

		private static readonly FieldInfo? GuildGeneralField = ((GuildType != null) ? AccessTools.Field(GuildType, "General") : null);

		private static readonly FieldInfo? GuildGeneralIdField = ((GuildGeneralType != null) ? AccessTools.Field(GuildGeneralType, "id") : null);

		private static readonly FieldInfo? GuildMembersField = ((GuildType != null) ? AccessTools.Field(GuildType, "Members") : null);

		private static readonly FieldInfo? PlayerReferenceIdField = ((PlayerReferenceType != null) ? AccessTools.Field(PlayerReferenceType, "id") : null);

		private static readonly FieldInfo? PlayerReferenceNameField = ((PlayerReferenceType != null) ? (AccessTools.Field(PlayerReferenceType, "name") ?? AccessTools.Field(PlayerReferenceType, "Name")) : null);

		private static readonly bool HasGuildsApiSurface = ApiType != null && IsLoadedMethod != null;

		private static AvailabilityState _availabilityState = AvailabilityState.Unknown;

		private static DateTime _nextAvailabilityProbeUtc = DateTime.MinValue;

		private static readonly TimeSpan PendingWardGuildProjectionRefreshDebounce = TimeSpan.FromMilliseconds(250.0);

		private static readonly PendingWardGuildProjectionRefreshState PendingWardGuildProjectionRefresh = new PendingWardGuildProjectionRefreshState();

		private static readonly Dictionary<long, CachedWardGuildIdentity> PlayerGuildCache = new Dictionary<long, CachedWardGuildIdentity>();

		private static readonly Dictionary<long, CachedPlayerPlatformIdentity> PlayerPlatformIdCache = new Dictionary<long, CachedPlayerPlatformIdentity>();

		private static bool _guildHooksRegistered;

		private static bool _guildHooksActive;

		private static bool _saveGuildPatched;

		internal static void ResetRuntimeState()
		{
			PlayerGuildCache.Clear();
			PlayerPlatformIdCache.Clear();
			ResetPendingWardGuildProjectionRefreshes();
			ResetSyncedGuildState();
			_availabilityState = AvailabilityState.Unknown;
			_nextAvailabilityProbeUtc = DateTime.MinValue;
		}

		internal static void EnsureRuntimeBindings()
		{
			RegisterSyncRpcs();
		}

		internal static void OnZNetAwake()
		{
			ResetRuntimeState();
			EnsureRuntimeBindings();
		}

		internal static WardGuildIdentity GetPlayerGuildIdentity(Player? player)
		{
			if (!TryGetGuild(player, out var guild))
			{
				return default(WardGuildIdentity);
			}
			return guild;
		}

		internal static WardGuildIdentity GetPlayerGuildIdentity(long playerId)
		{
			if (!TryGetGuild(playerId, out var guild))
			{
				return default(WardGuildIdentity);
			}
			return guild;
		}

		internal static WardGuildIdentity GetWardGuildIdentity(PrivateArea? area)
		{
			return new WardGuildIdentity(GetWardGuildId(area), GetWardGuildName(area));
		}

		internal static WardGuildIdentity GetWardGuildIdentity(ZDO? zdo)
		{
			return new WardGuildIdentity(GetWardGuildId(zdo), GetWardGuildName(zdo));
		}

		internal static int GetPlayerGuildId(Player? player)
		{
			if (!TryGetGuild(player, out var guild))
			{
				return 0;
			}
			return guild.Id;
		}

		internal static int GetPlayerGuildId(long playerId)
		{
			if (!TryGetGuild(playerId, out var guild))
			{
				return 0;
			}
			return guild.Id;
		}

		internal static string GetPlayerGuildName(long playerId)
		{
			if (!TryGetGuild(playerId, out var guild))
			{
				return string.Empty;
			}
			return guild.Name;
		}

		internal static int GetWardGuildId(ZDO? zdo)
		{
			if (zdo == null)
			{
				return 0;
			}
			return zdo.GetInt("stuw_guild_id", 0);
		}

		internal static string GetWardGuildName(ZDO? zdo)
		{
			return ((zdo != null) ? zdo.GetString("stuw_guild_name", string.Empty) : null) ?? string.Empty;
		}

		internal static string BuildCharacterIdentityKey(string accountId, string playerName)
		{
			string text = WardOwnership.NormalizeAccountIdValue(accountId);
			string text2 = playerName?.Trim() ?? string.Empty;
			if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(text2))
			{
				return string.Empty;
			}
			return text + "\n" + text2;
		}

		internal static void Update()
		{
			SyncLocalPlayerGuildIfNeeded(force: false);
			ProcessPendingPlayerGuildSyncs();
			ProcessPendingWardGuildProjectionRefreshes();
		}

		internal static void OnLocalPlayerStarted(Player? player)
		{
			if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer))
			{
				_localGuildSyncPending = true;
				SyncLocalPlayerGuildIfNeeded(force: true);
			}
		}

		internal static void ResetSyncedGuildState()
		{
			ServerSyncedGuildByPlayerId.Clear();
			ServerSyncedGuildByCharacterKey.Clear();
			PendingPlayerGuildSyncsBySender.Clear();
			_syncRpcsRegistered = false;
			_localGuildSyncPending = true;
			_lastSyncedLocalPlayerId = 0L;
			_lastSyncedLocalGuildId = int.MinValue;
			_lastSyncedLocalGuildName = string.Empty;
		}

		internal static bool TryGetSyncedGuildIdentity(long playerId, string accountId, string playerName, out WardGuildIdentity guild)
		{
			guild = default(WardGuildIdentity);
			if (playerId != 0L && ServerSyncedGuildByPlayerId.TryGetValue(playerId, out var value))
			{
				guild = (value.HasGuild ? new WardGuildIdentity(value.GuildId, value.GuildName) : default(WardGuildIdentity));
				return true;
			}
			string text = BuildCharacterIdentityKey(accountId, playerName);
			if (string.IsNullOrWhiteSpace(text) || !ServerSyncedGuildByCharacterKey.TryGetValue(text, out var value2))
			{
				return false;
			}
			guild = (value2.HasGuild ? new WardGuildIdentity(value2.GuildId, value2.GuildName) : default(WardGuildIdentity));
			return true;
		}

		internal static bool TryGetSyncedGuildIdentity(string accountId, string playerName, out WardGuildIdentity guild)
		{
			return TryGetSyncedGuildIdentity(0L, accountId, playerName, out guild);
		}

		private static void RegisterSyncRpcs()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (!_syncRpcsRegistered && instance != null)
			{
				instance.Register<ZPackage>("STUWard_SyncPlayerGuild", (Action<long, ZPackage>)HandleSyncPlayerGuild);
				_syncRpcsRegistered = true;
			}
		}

		private static void SyncLocalPlayerGuildIfNeeded(bool force)
		{
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Expected O, but got Unknown
			Player localPlayer = Player.m_localPlayer;
			ZNet instance = ZNet.instance;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)instance == (Object)null)
			{
				return;
			}
			long playerID = localPlayer.GetPlayerID();
			string playerAccountId = WardOwnership.GetPlayerAccountId(localPlayer);
			if (playerID == 0L || string.IsNullOrWhiteSpace(playerAccountId))
			{
				return;
			}
			WardGuildIdentity playerGuildIdentity = GetPlayerGuildIdentity(localPlayer);
			string text = playerGuildIdentity.Name ?? string.Empty;
			bool flag = _localGuildSyncPending || playerID != _lastSyncedLocalPlayerId || playerGuildIdentity.Id != _lastSyncedLocalGuildId || !string.Equals(text, _lastSyncedLocalGuildName, StringComparison.Ordinal);
			if (!force && !flag)
			{
				return;
			}
			_lastSyncedLocalPlayerId = playerID;
			_lastSyncedLocalGuildId = playerGuildIdentity.Id;
			_lastSyncedLocalGuildName = text;
			_localGuildSyncPending = false;
			if (instance.IsServer())
			{
				if (UpsertSyncedGuildIdentity(playerID, playerAccountId, localPlayer.GetPlayerName(), playerGuildIdentity, out var previousGuild))
				{
					RefreshWardGuildProjectionForCharacter(new WardGuildCharacterIdentity(playerID, playerAccountId, localPlayer.GetPlayerName()), liveDisplayRefresh: true, playerGuildIdentity.Id, previousGuild.Id);
				}
				return;
			}
			ZRoutedRpc instance2 = ZRoutedRpc.instance;
			if (instance2 != null)
			{
				long serverPeerID = instance2.GetServerPeerID();
				if (serverPeerID != 0L)
				{
					ZPackage val = new ZPackage();
					val.Write(playerGuildIdentity.Id);
					val.Write(text);
					instance2.InvokeRoutedRPC(serverPeerID, "STUWard_SyncPlayerGuild", new object[1] { val });
				}
			}
		}

		private static void HandleSyncPlayerGuild(long sender, ZPackage pkg)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				int guildId = pkg.ReadInt();
				string guildName = pkg.ReadString();
				if (!TryApplySyncedGuildIdentity(sender, guildId, guildName))
				{
					PendingPlayerGuildSyncsBySender[sender] = new PendingPlayerGuildSync(sender, guildId, guildName, DateTime.UtcNow);
				}
			}
		}

		private static void ProcessPendingPlayerGuildSyncs()
		{
			if (PendingPlayerGuildSyncsBySender.Count == 0 || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			List<long> list = null;
			List<long> list2 = null;
			DateTime utcNow = DateTime.UtcNow;
			foreach (KeyValuePair<long, PendingPlayerGuildSync> item in PendingPlayerGuildSyncsBySender)
			{
				if (utcNow - item.Value.FirstSeenUtc > PendingPlayerGuildSyncLifetime)
				{
					if (list == null)
					{
						list = new List<long>();
					}
					list.Add(item.Key);
				}
				else if (TryApplySyncedGuildIdentity(item.Value.SenderUid, item.Value.GuildId, item.Value.GuildName))
				{
					if (list2 == null)
					{
						list2 = new List<long>();
					}
					list2.Add(item.Key);
				}
			}
			if (list != null)
			{
				foreach (long item2 in list)
				{
					PendingPlayerGuildSyncsBySender.Remove(item2);
				}
			}
			if (list2 == null)
			{
				return;
			}
			foreach (long item3 in list2)
			{
				PendingPlayerGuildSyncsBySender.Remove(item3);
			}
		}

		private static bool TryApplySyncedGuildIdentity(long sender, int guildId, string guildName)
		{
			if (!WardOwnership.TryResolveAuthoritativePlayerIdFromSender(sender, "GuildsCompat.Sync", out var playerId))
			{
				return false;
			}
			string text = WardOwnership.GetAuthoritativeAccountIdFromSender(sender, playerId);
			if (string.IsNullOrWhiteSpace(text))
			{
				text = WardOwnership.GetPlayerAccountId(playerId);
			}
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			WardGuildIdentity guild = ((guildId != 0) ? new WardGuildIdentity(guildId, guildName) : default(WardGuildIdentity));
			string playerName = WardOwnership.GetPlayerName(playerId);
			if (UpsertSyncedGuildIdentity(playerId, text, playerName, guild, out var previousGuild))
			{
				WardOwnership.RefreshServerPlayerAccountIdForResolvedPlayer(playerId, text);
				RefreshWardGuildProjectionForCharacter(new WardGuildCharacterIdentity(playerId, text, playerName), liveDisplayRefresh: true, guild.Id, previousGuild.Id);
			}
			return true;
		}

		private static void NotifyGuildProjectionRefreshApplied(string reason, bool fullRefresh, HashSet<long>? targetPlayerIds, HashSet<string>? targetCharacterKeys, HashSet<int>? affectedGuildIds)
		{
			string reason2 = (string.IsNullOrWhiteSpace(reason) ? "guild projection refreshed" : reason);
			HashSet<long> hashSet = null;
			if (!fullRefresh)
			{
				if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
				{
					return;
				}
				List<ZNetPeer> peers = ZNet.instance.GetPeers();
				if (peers == null)
				{
					return;
				}
				hashSet = CollectGuildProjectionRefreshRecipients(peers, targetPlayerIds, targetCharacterKeys, affectedGuildIds);
				if (hashSet.Count == 0)
				{
					return;
				}
			}
			ManagedWardMapStateService.NotifyViewerProjectionChanged(reason2, fullRefresh, hashSet, refreshImmediatelyIfVisible: true);
		}

		private static HashSet<long> CollectGuildProjectionRefreshRecipients(List<ZNetPeer> peers, HashSet<long>? targetPlayerIds, HashSet<string>? targetCharacterKeys, HashSet<int>? affectedGuildIds)
		{
			HashSet<long> hashSet = new HashSet<long>();
			for (int i = 0; i < peers.Count; i++)
			{
				ZNetPeer val = peers[i];
				if (val != null && val.m_uid != 0L)
				{
					long playerIdFromSender = WardOwnership.GetPlayerIdFromSender(val.m_uid);
					string authoritativeAccountIdFromSender = WardOwnership.GetAuthoritativeAccountIdFromSender(val.m_uid, playerIdFromSender);
					string playerName = ((playerIdFromSender != 0L) ? WardOwnership.GetPlayerName(playerIdFromSender) : string.Empty);
					if (ShouldReceiveGuildProjectionRefresh(playerIdFromSender, authoritativeAccountIdFromSender, playerName, targetPlayerIds, targetCharacterKeys, affectedGuildIds))
					{
						hashSet.Add(val.m_uid);
					}
				}
			}
			return hashSet;
		}

		private static bool ShouldReceiveGuildProjectionRefresh(long playerId, string accountId, string playerName, HashSet<long>? targetPlayerIds, HashSet<string>? targetCharacterKeys, HashSet<int>? affectedGuildIds)
		{
			if (playerId != 0L && WardAdminDebugAccess.IsPlayerAdminDebugController(playerId))
			{
				return true;
			}
			if (targetPlayerIds != null && targetPlayerIds.Count > 0 && playerId != 0L && targetPlayerIds.Contains(playerId))
			{
				return true;
			}
			if (targetCharacterKeys != null && targetCharacterKeys.Count > 0)
			{
				string text = BuildCharacterIdentityKey(accountId, playerName);
				if (!string.IsNullOrWhiteSpace(text) && targetCharacterKeys.Contains(text))
				{
					return true;
				}
			}
			if (affectedGuildIds == null || affectedGuildIds.Count == 0)
			{
				return false;
			}
			if (TryGetSyncedGuildIdentity(playerId, accountId, playerName, out var guild) && guild.Id != 0)
			{
				return affectedGuildIds.Contains(guild.Id);
			}
			return false;
		}

		private static bool UpsertSyncedGuildIdentity(long playerId, string accountId, string playerName, WardGuildIdentity guild, out WardGuildIdentity previousGuild)
		{
			previousGuild = default(WardGuildIdentity);
			bool result = false;
			SyncedWardGuildIdentity value = new SyncedWardGuildIdentity(guild.Id != 0, guild.Id, guild.Name);
			if (playerId != 0L)
			{
				if (ServerSyncedGuildByPlayerId.TryGetValue(playerId, out var value2))
				{
					previousGuild = ToWardGuildIdentity(value2);
				}
				if (!ServerSyncedGuildByPlayerId.TryGetValue(playerId, out value2) || value2.HasGuild != value.HasGuild || value2.GuildId != value.GuildId || !string.Equals(value2.GuildName, value.GuildName, StringComparison.Ordinal))
				{
					ServerSyncedGuildByPlayerId[playerId] = value;
					result = true;
				}
			}
			string text = BuildCharacterIdentityKey(accountId, playerName);
			if (string.IsNullOrWhiteSpace(text))
			{
				return result;
			}
			if (previousGuild.Id == 0 && ServerSyncedGuildByCharacterKey.TryGetValue(text, out var value3))
			{
				previousGuild = ToWardGuildIdentity(value3);
			}
			if (!ServerSyncedGuildByCharacterKey.TryGetValue(text, out var value4) || value4.HasGuild != value.HasGuild || value4.GuildId != value.GuildId || !string.Equals(value4.GuildName, value.GuildName, StringComparison.Ordinal))
			{
				ServerSyncedGuildByCharacterKey[text] = value;
				result = true;
			}
			return result;
		}

		private static WardGuildIdentity ToWardGuildIdentity(SyncedWardGuildIdentity syncedGuild)
		{
			if (!syncedGuild.HasGuild)
			{
				return default(WardGuildIdentity);
			}
			return new WardGuildIdentity(syncedGuild.GuildId, syncedGuild.GuildName);
		}

		private static Assembly? GetPluginAssembly(string pluginGuid)
		{
			if (!Chainloader.PluginInfos.TryGetValue(pluginGuid, out var value))
			{
				return null;
			}
			return ((object)value.Instance)?.GetType().Assembly;
		}

		internal static bool IsAvailable()
		{
			if (!HasGuildsApiSurface)
			{
				return false;
			}
			if (_availabilityState == AvailabilityState.Available)
			{
				return true;
			}
			DateTime utcNow = DateTime.UtcNow;
			if (_availabilityState == AvailabilityState.Unavailable && utcNow < _nextAvailabilityProbeUtc)
			{
				return false;
			}
			try
			{
				if ((IsLoadedMethod.Invoke(null, Array.Empty<object>()) as bool?).GetValueOrDefault())
				{
					_availabilityState = AvailabilityState.Available;
					_nextAvailabilityProbeUtc = DateTime.MaxValue;
					return true;
				}
			}
			catch
			{
			}
			_availabilityState = AvailabilityState.Unavailable;
			_nextAvailabilityProbeUtc = utcNow + AvailabilityProbeBackoff;
			return false;
		}

		internal static void ResetPendingWardGuildProjectionRefreshes()
		{
			PendingWardGuildProjectionRefresh.TargetIdentitiesByPlayerId.Clear();
			PendingWardGuildProjectionRefresh.TargetIdentitiesByCharacterKey.Clear();
			PendingWardGuildProjectionRefresh.AffectedGuildIds.Clear();
			PendingWardGuildProjectionRefresh.PendingFullRefresh = false;
			PendingWardGuildProjectionRefresh.PendingLiveDisplayRefresh = false;
			PendingWardGuildProjectionRefresh.PendingLiveDisplayReason = string.Empty;
			PendingWardGuildProjectionRefresh.FlushAtUtc = DateTime.MinValue;
		}

		internal static bool TryStampLocalWardGuildMetadata(PrivateArea? area)
		{
			return TryStampLocalWardGuildMetadata(ManagedWardRef.FromArea(area));
		}

		internal static bool TryStampLocalWardGuildMetadata(ManagedWardRef ward)
		{
			if ((Object)(object)ward.Area == (Object)null || !ManagedWardIdentity.EnsureManagedComponent(ward))
			{
				return false;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return false;
			}
			if (!WardAccess.IsDirectWardOwner(ward, localPlayer.GetPlayerID()))
			{
				return false;
			}
			if (!ward.HasValidNetworkIdentity || !ward.IsOwner)
			{
				return false;
			}
			ZDO zdo = ward.Zdo;
			if (zdo == null)
			{
				return false;
			}
			WardGuildIdentity guild;
			WardGuildIdentity guild2 = ((TryGetGuild(localPlayer, out guild) && guild.Id != 0) ? new WardGuildIdentity(guild.Id, guild.Name ?? string.Empty) : default(WardGuildIdentity));
			ManagedWardProjection projection = ManagedWardProjectionService.ResolveExplicitProjection(localPlayer.GetPlayerID(), WardOwnership.GetPlayerAccountId(localPlayer), guild2);
			return ManagedWardMetadataMutationService.ApplyOwnedLocalProjection(zdo, projection, ManagedWardMapMutationKind.IndexAndPins, "local ward guild metadata stamp", forceSendWhenMetadataChanged: false).ProjectionResult.GuildChanged;
		}

		internal static int GetWardGuildId(PrivateArea? area)
		{
			if (TryGetStoredWardGuildIdentity(GetWardZdo(area), out var guild))
			{
				return guild.Id;
			}
			if (!TryResolveWardGuildIdentity(area, allowMetadataStamp: false, out var guild2))
			{
				return 0;
			}
			return guild2.Id;
		}

		internal static string GetWardGuildName(PrivateArea? area)
		{
			if (TryGetStoredWardGuildIdentity(GetWardZdo(area), out var guild) && !string.IsNullOrWhiteSpace(guild.Name))
			{
				return guild.Name;
			}
			if (TryResolveWardGuildIdentity(area, allowMetadataStamp: false, out var guild2))
			{
				return guild2.Name;
			}
			int id = guild.Id;
			if (id != 0 && TryGetGuildById(id, out guild2))
			{
				return guild2.Name;
			}
			Player localPlayer = Player.m_localPlayer;
			if (id != 0 && (Object)(object)localPlayer != (Object)null && TryGetGuild(localPlayer, out guild2) && guild2.Id == id)
			{
				return guild2.Name;
			}
			return string.Empty;
		}

		internal static WardGuildIdentity ResolveWardGuildIdentityReadOnly(ZDO? zdo)
		{
			if (TryGetStoredWardGuildIdentity(zdo, out var guild) && guild.Id != 0)
			{
				return guild;
			}
			if (zdo == null)
			{
				return default(WardGuildIdentity);
			}
			long @long = zdo.GetLong(ZDOVars.s_creator, 0L);
			string wardSteamAccountId = WardOwnership.ResolveWardSteamAccountId(zdo, @long, WardOwnership.GetWardSteamAccountId(zdo));
			string wardOwnerNameForProjection = GetWardOwnerNameForProjection(zdo);
			if (!TryResolveWardGuildIdentityReadOnly(@long, wardSteamAccountId, wardOwnerNameForProjection, treatResolvedNoGuildAsResolved: false, out var guild2))
			{
				return default(WardGuildIdentity);
			}
			return guild2;
		}

		private static bool TryGetStoredWardGuildIdentity(ZDO? zdo, out WardGuildIdentity guild)
		{
			guild = default(WardGuildIdentity);
			if (zdo == null)
			{
				return false;
			}
			int @int = zdo.GetInt("stuw_guild_id", 0);
			string text = zdo.GetString("stuw_guild_name", string.Empty) ?? string.Empty;
			if (@int == 0 && string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			guild = new WardGuildIdentity(@int, text.Trim());
			return true;
		}

		private static ZDO? GetWardZdo(PrivateArea? area)
		{
			return WardPrivateAreaSafeAccess.GetZdo(area);
		}

		private static bool TryResolveWardGuildIdentity(PrivateArea? area, bool allowMetadataStamp, out WardGuildIdentity guild)
		{
			guild = default(WardGuildIdentity);
			if ((Object)(object)area == (Object)null)
			{
				return false;
			}
			long canonicalCreatorPlayerId = WardAccess.GetCanonicalCreatorPlayerId(area);
			string wardSteamAccountId = WardOwnership.ResolveWardSteamAccountId(GetWardZdo(area), canonicalCreatorPlayerId, WardOwnership.GetWardSteamAccountId(area));
			string wardOwnerName = GetWardOwnerName(area);
			if (TryResolveWardGuildIdentityReadOnly(canonicalCreatorPlayerId, wardSteamAccountId, wardOwnerName, treatResolvedNoGuildAsResolved: true, out guild))
			{
				if (allowMetadataStamp)
				{
					StampResolvedWardGuildMetadata(area, canonicalCreatorPlayerId, wardSteamAccountId, guild);
				}
				return true;
			}
			return false;
		}

		private static string GetWardOwnerName(PrivateArea? area)
		{
			if ((Object)(object)area == (Object)null)
			{
				return string.Empty;
			}
			string creatorName = WardPrivateAreaSafeAccess.GetCreatorName(area);
			if (!string.IsNullOrWhiteSpace(creatorName))
			{
				return creatorName.Trim();
			}
			return GetWardOwnerNameForProjection(GetWardZdo(area));
		}

		internal static string GetWardOwnerNameForProjection(ZDO? zdo)
		{
			return WardPrivateAreaSafeAccess.GetCreatorName(zdo);
		}

		internal static bool TryResolveProjectedGuildIdentity(long ownerPlayerId, string normalizedAccountId, string ownerName, out WardGuildIdentity guild)
		{
			return TryResolveWardGuildIdentityReadOnly(ownerPlayerId, normalizedAccountId, ownerName, treatResolvedNoGuildAsResolved: true, out guild);
		}

		private static bool TryResolveWardGuildIdentityReadOnly(long ownerPlayerId, string wardSteamAccountId, string ownerName, bool treatResolvedNoGuildAsResolved, out WardGuildIdentity guild)
		{
			guild = default(WardGuildIdentity);
			string text = WardOwnership.NormalizeAccountIdValue(wardSteamAccountId);
			string text2 = ownerName?.Trim() ?? string.Empty;
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && TryGetSyncedGuildIdentity(ownerPlayerId, text, text2, out guild))
			{
				if (!treatResolvedNoGuildAsResolved)
				{
					return guild.Id != 0;
				}
				return true;
			}
			if (ownerPlayerId != 0L && TryGetGuild(ownerPlayerId, out guild))
			{
				return true;
			}
			if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(text2))
			{
				return TryGetGuildByAccountAndName(text, text2, out guild);
			}
			return false;
		}

		private static void StampResolvedWardGuildMetadata(PrivateArea? area, long ownerPlayerId, string wardSteamAccountId, WardGuildIdentity guild)
		{
			if (guild.Id != 0 && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				ZDO wardZdo = GetWardZdo(area);
				if (wardZdo != null)
				{
					ManagedWardMetadataMutationService.ApplyExplicitProjection(wardZdo, ManagedWardProjectionService.ResolveExplicitProjection(ownerPlayerId, wardSteamAccountId, guild), ManagedWardMapMutationKind.IndexAndPins, "resolved ward guild metadata");
				}
			}
		}

		internal static void HandleGuildSaved(object? guild)
		{
			Plugin.LogWardDiagnosticVerbose("GuildsCompat.Refresh", "Queued guild save refresh for guild=" + DescribeGuildObject(guild) + ".");
			RefreshWardGuildProjectionForGuild(guild);
		}

		internal static void RefreshAllWardGuildProjections(bool liveDisplayRefresh = false)
		{
			Plugin.LogWardDiagnosticVerbose("GuildsCompat.Refresh", "Queued full ward guild projection refresh.");
			QueueWardGuildProjectionRefreshForAll(liveDisplayRefresh, "full guild projection refresh");
		}

		private static void RefreshWardGuildProjectionForGuild(object? guild)
		{
			WardGuildIdentity guild2;
			int affectedGuildId = (TryParseGuild(guild, out guild2) ? guild2.Id : 0);
			bool hadUnresolvedMembers;
			List<WardGuildCharacterIdentity> list = CollectGuildMemberCharacterIdentities(guild, out hadUnresolvedMembers);
			if (list.Count == 0 || hadUnresolvedMembers)
			{
				Plugin.LogWardDiagnosticFailure("GuildsCompat.Refresh", "Could not extract complete guild member character identities from guild=" + DescribeGuildObject(guild) + ". Falling back to full ward guild refresh.");
				QueueWardGuildProjectionRefreshForAll(liveDisplayRefresh: false, "full guild projection refresh");
				return;
			}
			Plugin.LogWardDiagnosticVerbose("GuildsCompat.Refresh", $"Queued ward guild projection refresh for {list.Count} guild member character(s) from guild={DescribeGuildObject(guild)}.");
			foreach (WardGuildCharacterIdentity item in list)
			{
				RefreshWardGuildProjectionForCharacter(item, liveDisplayRefresh: false, affectedGuildId);
			}
		}

		private static void RefreshWardGuildProjectionForCharacter(WardGuildCharacterIdentity identity, bool liveDisplayRefresh = false, int affectedGuildId = 0, int previousGuildId = 0)
		{
			if (identity.HasPlayerId || identity.HasAccountAndName)
			{
				Plugin.LogWardDiagnosticVerbose("GuildsCompat.Refresh", $"Queued ward guild projection refresh for character playerId={identity.PlayerId}, accountId='{identity.AccountId}', playerName='{identity.PlayerName}'.");
				if (!string.IsNullOrWhiteSpace(identity.AccountId))
				{
					InvalidateGuildCacheForAccountId(identity.AccountId);
				}
				QueueWardGuildProjectionRefreshForCharacter(identity, liveDisplayRefresh, $"guild projection refresh for playerId={identity.PlayerId}, accountId='{identity.AccountId}'", affectedGuildId, previousGuildId);
			}
		}

		internal static void ProcessPendingWardGuildProjectionRefreshes()
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				PendingWardGuildProjectionRefreshState pendingWardGuildProjectionRefresh = PendingWardGuildProjectionRefresh;
				if ((pendingWardGuildProjectionRefresh.PendingFullRefresh || pendingWardGuildProjectionRefresh.TargetIdentitiesByPlayerId.Count != 0 || pendingWardGuildProjectionRefresh.TargetIdentitiesByCharacterKey.Count != 0 || pendingWardGuildProjectionRefresh.AffectedGuildIds.Count != 0) && !(pendingWardGuildProjectionRefresh.FlushAtUtc > DateTime.UtcNow))
				{
					bool pendingFullRefresh = pendingWardGuildProjectionRefresh.PendingFullRefresh;
					bool pendingLiveDisplayRefresh = pendingWardGuildProjectionRefresh.PendingLiveDisplayRefresh;
					string liveDisplayReason = (string.IsNullOrWhiteSpace(pendingWardGuildProjectionRefresh.PendingLiveDisplayReason) ? "guild projection refreshed" : pendingWardGuildProjectionRefresh.PendingLiveDisplayReason);
					HashSet<long> targetPlayerIds = ((pendingWardGuildProjectionRefresh.TargetIdentitiesByPlayerId.Count == 0) ? null : new HashSet<long>(pendingWardGuildProjectionRefresh.TargetIdentitiesByPlayerId.Keys));
					HashSet<string> targetCharacterKeys = ((pendingWardGuildProjectionRefresh.TargetIdentitiesByCharacterKey.Count == 0) ? null : new HashSet<string>(pendingWardGuildProjectionRefresh.TargetIdentitiesByCharacterKey.Keys, StringComparer.Ordinal));
					HashSet<int> affectedGuildIds = ((pendingWardGuildProjectionRefresh.AffectedGuildIds.Count == 0) ? null : new HashSet<int>(pendingWardGuildProjectionRefresh.AffectedGuildIds));
					ResetPendingWardGuildProjectionRefreshes();
					RefreshWardGuildProjectionForManagedWards(targetPlayerIds, targetCharacterKeys, affectedGuildIds, pendingFullRefresh, pendingLiveDisplayRefresh, liveDisplayReason);
				}
			}
		}

		private static void QueueWardGuildProjectionRefreshForAll(bool liveDisplayRefresh, string liveDisplayReason)
		{
			PendingWardGuildProjectionRefresh.PendingFullRefresh = true;
			PendingWardGuildProjectionRefresh.TargetIdentitiesByPlayerId.Clear();
			PendingWardGuildProjectionRefresh.TargetIdentitiesByCharacterKey.Clear();
			UpdatePendingWardGuildProjectionRefreshWindow(liveDisplayRefresh, liveDisplayReason);
		}

		private static void QueueWardGuildProjectionRefreshForCharacter(WardGuildCharacterIdentity identity, bool liveDisplayRefresh, string liveDisplayReason, int affectedGuildId, int previousGuildId)
		{
			if (!PendingWardGuildProjectionRefresh.PendingFullRefresh)
			{
				if (identity.HasPlayerId)
				{
					PendingWardGuildProjectionRefresh.TargetIdentitiesByPlayerId[identity.PlayerId] = MergeQueuedWardGuildCharacterIdentity(PendingWardGuildProjectionRefresh.TargetIdentitiesByPlayerId.TryGetValue(identity.PlayerId, out var value) ? value : default(WardGuildCharacterIdentity), identity);
				}
				if (identity.HasAccountAndName)
				{
					string text = BuildCharacterIdentityKey(identity.AccountId, identity.PlayerName);
					if (!string.IsNullOrWhiteSpace(text))
					{
						PendingWardGuildProjectionRefresh.TargetIdentitiesByCharacterKey[text] = MergeQueuedWardGuildCharacterIdentity(PendingWardGuildProjectionRefresh.TargetIdentitiesByCharacterKey.TryGetValue(text, out var value2) ? value2 : default(WardGuildCharacterIdentity), identity);
					}
				}
			}
			if (affectedGuildId != 0)
			{
				PendingWardGuildProjectionRefresh.AffectedGuildIds.Add(affectedGuildId);
			}
			if (previousGuildId != 0)
			{
				PendingWardGuildProjectionRefresh.AffectedGuildIds.Add(previousGuildId);
			}
			UpdatePendingWardGuildProjectionRefreshWindow(liveDisplayRefresh, liveDisplayReason);
		}

		private static void UpdatePendingWardGuildProjectionRefreshWindow(bool liveDisplayRefresh, string liveDisplayReason)
		{
			if (liveDisplayRefresh)
			{
				PendingWardGuildProjectionRefresh.PendingLiveDisplayRefresh = true;
			}
			if (string.IsNullOrWhiteSpace(PendingWardGuildProjectionRefresh.PendingLiveDisplayReason))
			{
				PendingWardGuildProjectionRefresh.PendingLiveDisplayReason = (string.IsNullOrWhiteSpace(liveDisplayReason) ? "guild projection refreshed" : liveDisplayReason);
			}
			if (PendingWardGuildProjectionRefresh.FlushAtUtc == DateTime.MinValue)
			{
				PendingWardGuildProjectionRefresh.FlushAtUtc = DateTime.UtcNow + PendingWardGuildProjectionRefreshDebounce;
			}
		}

		private static WardGuildCharacterIdentity MergeQueuedWardGuildCharacterIdentity(WardGuildCharacterIdentity existingIdentity, WardGuildCharacterIdentity incomingIdentity)
		{
			long playerId = (existingIdentity.HasPlayerId ? existingIdentity.PlayerId : incomingIdentity.PlayerId);
			string accountId = ((!string.IsNullOrWhiteSpace(existingIdentity.AccountId)) ? existingIdentity.AccountId : incomingIdentity.AccountId);
			string playerName = ((!string.IsNullOrWhiteSpace(existingIdentity.PlayerName)) ? existingIdentity.PlayerName : incomingIdentity.PlayerName);
			return new WardGuildCharacterIdentity(playerId, accountId, playerName);
		}

		private static bool RefreshWardGuildProjectionForManagedWards(HashSet<long>? targetPlayerIds, HashSet<string>? targetCharacterKeys, HashSet<int>? affectedGuildIds, bool fullRefresh, bool liveDisplayRefresh, string liveDisplayReason)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return false;
			}
			if (!fullRefresh && (targetPlayerIds == null || targetPlayerIds.Count == 0) && (targetCharacterKeys == null || targetCharacterKeys.Count == 0) && (affectedGuildIds == null || affectedGuildIds.Count == 0))
			{
				return false;
			}
			if (fullRefresh)
			{
				InvalidateAllGuildCaches();
			}
			HashSet<ZDOID> hashSet = new HashSet<ZDOID>();
			int num = ManagedWardRegistry.CollectCandidateIds(hashSet, targetPlayerIds, targetCharacterKeys, affectedGuildIds, fullRefresh);
			int num2 = 0;
			int num3 = 0;
			foreach (ZDOID item in hashSet)
			{
				ZDOMan instance = ZDOMan.instance;
				ZDO val = ((instance != null) ? instance.GetZDO(item) : null);
				if (val == null || !WardOwnership.IsManagedWardZdo(val))
				{
					ManagedWardRegistry.RemoveEntry(item);
					continue;
				}
				long @long = val.GetLong(ZDOVars.s_creator, 0L);
				num3++;
				string wardSteamAccountId = WardOwnership.NormalizeAccountIdValue(WardOwnership.ResolveWardSteamAccountId(val, @long, WardOwnership.GetWardSteamAccountId(val)));
				if (ManagedWardMetadataMutationService.RefreshProjectedMetadata(val, @long, wardSteamAccountId, ManagedWardMapMutationKind.IndexOnly, "guild projection refreshed").ProjectionResult.AnyChanged)
				{
					num2++;
				}
			}
			if (num2 > 0 && liveDisplayRefresh)
			{
				NotifyGuildProjectionRefreshApplied(liveDisplayReason, fullRefresh, targetPlayerIds, targetCharacterKeys, affectedGuildIds);
			}
			Plugin.LogWardDiagnosticVerbose("GuildsCompat.Refresh", string.Format("Scanned {0} managed ward(s) for guild projection refresh out of {1} registry candidate(s) and {2} indexed ward(s){3}{4}{5}{6}, changed={7}.", num3, num, ManagedWardRegistry.GetIndexedCount(), fullRefresh ? " on full refresh" : string.Empty, (!fullRefresh && targetPlayerIds != null && targetPlayerIds.Count > 0) ? $" targeting {targetPlayerIds.Count} playerId(s)" : string.Empty, (!fullRefresh && targetCharacterKeys != null && targetCharacterKeys.Count > 0) ? $" and {targetCharacterKeys.Count} account/name identities" : string.Empty, (!fullRefresh && affectedGuildIds != null && affectedGuildIds.Count > 0) ? $" across {affectedGuildIds.Count} affected guild(s)" : string.Empty, num2));
			return num2 > 0;
		}

		private static List<WardGuildCharacterIdentity> CollectGuildMemberCharacterIdentities(object? guild, out bool hadUnresolvedMembers)
		{
			Dictionary<string, WardGuildCharacterIdentity> dictionary = new Dictionary<string, WardGuildCharacterIdentity>(StringComparer.Ordinal);
			hadUnresolvedMembers = false;
			if (guild == null || GuildMembersField == null)
			{
				return new List<WardGuildCharacterIdentity>();
			}
			object value = GuildMembersField.GetValue(guild);
			if (value is IDictionary dictionary2)
			{
				foreach (object key in dictionary2.Keys)
				{
					if (!TryCreateCharacterIdentityFromPlayerReference(key, out var identity))
					{
						hadUnresolvedMembers = true;
					}
					else
					{
						dictionary[BuildCharacterIdentityKey(identity.AccountId, identity.PlayerName)] = identity;