Decompiled source of YouAreNotWorthy v1.0.0

YouAreNotWorthy.dll

Decompiled 10 hours 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.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
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 Microsoft.CodeAnalysis;
using ServerSync;
using Steamworks;
using TMPro;
using UnityEngine;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.RepresentationModel;
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("YouAreNotWorthy")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("YouAreNotWorthy")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace YouAreNotWorthy
{
	public enum KeyQueryResult : byte
	{
		Invalid,
		Unavailable,
		PersonalMissing,
		PersonalPresent,
		SharedMissing,
		SharedPresent
	}
	public static class YouAreNotWorthyApi
	{
		public const int ApiVersion = 1;

		public static KeyQueryResult QueryLocal(string? key)
		{
			//IL_002b: 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)
			try
			{
				if (!TryPrepareKey(key, out string preparedKey, out bool isPersonal, out KeyQueryResult failure))
				{
					return failure;
				}
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && ((Character)localPlayer).IsOwner())
				{
					ZDOID zDOID = ((Character)localPlayer).GetZDOID();
					if (!((ZDOID)(ref zDOID)).IsNone())
					{
						return isPersonal ? ToPersonalResult(PlayerKeys.HasNativeKey(localPlayer, preparedKey)) : QueryShared(preparedKey);
					}
				}
				return KeyQueryResult.Unavailable;
			}
			catch (Exception arg)
			{
				YouAreNotWorthyPlugin.Log.LogError((object)$"YNW API failed to evaluate local key '{key ?? string.Empty}': {arg}");
				return KeyQueryResult.Unavailable;
			}
		}

		public static bool TryShowLocalMissingRequirement(string? key)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!TryPrepareKey(key, out string preparedKey, out bool _, out KeyQueryResult _))
				{
					return false;
				}
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && ((Character)localPlayer).IsOwner())
				{
					ZDOID zDOID = ((Character)localPlayer).GetZDOID();
					if (!((ZDOID)(ref zDOID)).IsNone())
					{
						return ItemRestriction.ShowBlockedMessage(RequirementTextResolver.Resolve(preparedKey));
					}
				}
				return false;
			}
			catch (Exception arg)
			{
				YouAreNotWorthyPlugin.Log.LogError((object)$"YNW API failed to show local missing requirement '{key ?? string.Empty}': {arg}");
				return false;
			}
		}

		public static KeyQueryResult QueryPeer(ZNetPeer? peer, string? key)
		{
			try
			{
				if (!TryPrepareKey(key, out string preparedKey, out bool isPersonal, out KeyQueryResult failure))
				{
					return failure;
				}
				if (!TryGetAuthenticatedPeerCharacter(peer, out ZDO character))
				{
					return KeyQueryResult.Unavailable;
				}
				if (!isPersonal)
				{
					return QueryShared(preparedKey);
				}
				bool hasKey;
				return (!PersonalKeySnapshot.TryHas(character, preparedKey, out hasKey)) ? KeyQueryResult.Unavailable : ToPersonalResult(hasKey);
			}
			catch (Exception arg)
			{
				YouAreNotWorthyPlugin.Log.LogError((object)$"YNW API failed to evaluate peer key '{key ?? string.Empty}': {arg}");
				return KeyQueryResult.Unavailable;
			}
		}

		private static bool TryPrepareKey(string? key, out string preparedKey, out bool isPersonal, out KeyQueryResult failure)
		{
			preparedKey = (key ?? string.Empty).Trim();
			isPersonal = false;
			failure = KeyQueryResult.Invalid;
			if (IsInvalidKey(preparedKey))
			{
				return false;
			}
			if (!YouAreNotWorthyPlugin.IsApiReady)
			{
				failure = KeyQueryResult.Unavailable;
				return false;
			}
			if (ProgressionIndex.TryRegisterPersonalKey(preparedKey, out string canonicalKey))
			{
				preparedKey = canonicalKey;
				isPersonal = true;
				return true;
			}
			return ProgressionIndex.IsSharedWorldKey(preparedKey);
		}

		private static bool IsInvalidKey(string key)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Invalid comparison between Unknown and I4
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Invalid comparison between Unknown and I4
			if (key.Length == 0)
			{
				return true;
			}
			string text = key;
			for (int i = 0; i < text.Length; i++)
			{
				if (char.IsControl(text[i]))
				{
					return true;
				}
			}
			GlobalKeys val = default(GlobalKeys);
			string keyValue = ZoneSystem.GetKeyValue(key, ref text, ref val);
			if (long.TryParse(keyValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out var _))
			{
				return true;
			}
			GlobalKeys result2;
			bool flag = Enum.TryParse<GlobalKeys>(keyValue, ignoreCase: true, out result2) && Enum.IsDefined(typeof(GlobalKeys), result2);
			if (flag)
			{
				bool flag2 = (((int)result2 == 32 || (int)result2 == 43) ? true : false);
				flag = flag2;
			}
			return flag;
		}

		private static KeyQueryResult QueryShared(string key)
		{
			if (!((Object)(object)ZoneSystem.instance == (Object)null))
			{
				if (!ProgressionIndex.HasWorldKey(key))
				{
					return KeyQueryResult.SharedMissing;
				}
				return KeyQueryResult.SharedPresent;
			}
			return KeyQueryResult.Unavailable;
		}

		private static KeyQueryResult ToPersonalResult(bool hasKey)
		{
			if (!hasKey)
			{
				return KeyQueryResult.PersonalMissing;
			}
			return KeyQueryResult.PersonalPresent;
		}

		private static bool TryGetAuthenticatedPeerCharacter(ZNetPeer? peer, out ZDO character)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			character = null;
			ZNet instance = ZNet.instance;
			ZDOMan instance2 = ZDOMan.instance;
			if (peer == null || (Object)(object)instance == (Object)null || !instance.IsServer() || instance2 == null || !peer.IsReady() || peer.m_rpc == null || !peer.m_rpc.IsConnected() || ((ZDOID)(ref peer.m_characterID)).IsNone() || ((ZDOID)(ref peer.m_characterID)).UserID != peer.m_uid || instance.GetPeer(peer.m_rpc) != peer)
			{
				return false;
			}
			ZDO zDO = instance2.GetZDO(peer.m_characterID);
			if (zDO == null || !zDO.IsValid() || zDO.GetOwner() != peer.m_uid)
			{
				return false;
			}
			Player val = null;
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (!((Object)(object)allPlayer == (Object)null) && ((Character)allPlayer).GetOwner() == peer.m_uid && !(((Character)allPlayer).GetZDOID() != peer.m_characterID))
				{
					if ((Object)(object)val != (Object)null)
					{
						return false;
					}
					val = allPlayer;
				}
			}
			long num = zDO.GetLong(ZDOVars.s_playerID, 0L);
			ZNetView val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<ZNetView>() : null);
			if (num == 0L || (Object)(object)val == (Object)null || val.GetPlayerID() != num || (Object)(object)val2 == (Object)null || !val2.IsValid() || val2.GetZDO() != zDO)
			{
				return false;
			}
			character = zDO;
			return true;
		}
	}
	internal sealed class ProgressionConfig
	{
		internal List<DefeatKeyRule> DefeatKeys { get; } = new List<DefeatKeyRule>();

		internal List<ItemTierConfig> Tiers { get; } = new List<ItemTierConfig>();
	}
	internal sealed class DefeatKeyRule
	{
		internal List<string> Prefabs { get; } = new List<string>();

		internal string Key { get; set; } = "";
	}
	internal sealed class ItemTierConfig
	{
		internal string Id { get; }

		internal string RequiredKey { get; }

		internal List<string> Resources { get; }

		internal ItemTierConfig(string id, string requiredKey, List<string> resources)
		{
			Id = id;
			RequiredKey = requiredKey;
			Resources = resources;
		}
	}
	internal static class ProgressionConfigLoader
	{
		internal const string ConfigFileName = "progression.yml";

		internal const string SyncedYamlIdentifier = "ProgressionYaml";

		internal static string ConfigDirectory => Path.Combine(Paths.ConfigPath, "YouAreNotWorthy");

		internal static string ConfigPath => Path.Combine(ConfigDirectory, "progression.yml");

		private static bool IsConfigured { get; set; }

		internal static string AppliedYaml { get; private set; } = "";

		private static string DefaultProgressionYaml => LoadEmbeddedYaml(".progression.default.yml");

		internal static bool LoadOrCreate()
		{
			if (TryReadLocal(out string yaml, createIfMissing: true) && TryApply(yaml, ConfigPath))
			{
				return true;
			}
			PreserveLastKnownGoodOrUseDefault();
			return IsConfigured;
		}

		internal static bool TryReadValidLocalYaml(out string yaml)
		{
			yaml = "";
			ProgressionConfig progression;
			if (TryReadLocal(out yaml))
			{
				return TryParseAndValidate(yaml, ConfigPath, out progression);
			}
			return false;
		}

		internal static bool ApplyLocalYaml(string yaml)
		{
			return TryApply(yaml, ConfigPath);
		}

		internal static bool ApplySyncedYaml(string yaml)
		{
			return TryApply(yaml, "ServerSync progression.yml");
		}

		private static bool TryReadLocal(out string yaml, bool createIfMissing = false)
		{
			yaml = "";
			try
			{
				Directory.CreateDirectory(ConfigDirectory);
				if (!File.Exists(ConfigPath))
				{
					if (!createIfMissing)
					{
						YouAreNotWorthyPlugin.Log.LogWarning((object)("Configuration file not found: " + ConfigPath));
						return false;
					}
					File.WriteAllText(ConfigPath, DefaultProgressionYaml);
				}
				yaml = File.ReadAllText(ConfigPath);
				return true;
			}
			catch (Exception arg)
			{
				YouAreNotWorthyPlugin.Log.LogError((object)$"Failed to read YNW YAML configuration: {arg}");
				return false;
			}
		}

		private static bool TryApply(string yaml, string source)
		{
			if (!TryParseAndValidate(yaml, source, out ProgressionConfig progression))
			{
				return false;
			}
			try
			{
				ProgressionIndex.Configure(progression);
				RestrictionEvaluator.InvalidateItemCache();
				RequirementTextResolver.InvalidateSources();
				KeyReferenceWriter.MarkItemReferenceDirty();
				AppliedYaml = yaml;
				IsConfigured = true;
				return true;
			}
			catch (Exception arg)
			{
				YouAreNotWorthyPlugin.Log.LogError((object)$"Failed to apply {source}: {arg}");
				return false;
			}
		}

		private static bool TryParseAndValidate(string yaml, string source, out ProgressionConfig progression)
		{
			progression = new ProgressionConfig();
			if (string.IsNullOrWhiteSpace(yaml))
			{
				YouAreNotWorthyPlugin.Log.LogError((object)("Invalid " + source + ": The YAML document is empty."));
				return false;
			}
			List<string> list = new List<string>();
			try
			{
				YamlStream yamlStream = new YamlStream();
				using StringReader input = new StringReader(yaml);
				yamlStream.Load(input);
				if (yamlStream.Documents.Count != 1 || !(yamlStream.Documents[0].RootNode is YamlMappingNode root))
				{
					YouAreNotWorthyPlugin.Log.LogError((object)("Invalid " + source + ": Expected one progression mapping document."));
					return false;
				}
				ParseRoot(root, progression, list);
			}
			catch (Exception arg)
			{
				YouAreNotWorthyPlugin.Log.LogError((object)$"Failed to parse {source}: {arg}");
				return false;
			}
			if (list.Count == 0)
			{
				Validate(progression, list);
			}
			if (list.Count == 0)
			{
				return true;
			}
			foreach (string item in list)
			{
				YouAreNotWorthyPlugin.Log.LogError((object)("Invalid " + source + ": " + item));
			}
			return false;
		}

		private static void ParseRoot(YamlMappingNode root, ProgressionConfig progression, List<string> errors)
		{
			bool flag = false;
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<YamlNode, YamlNode> child in root.Children)
			{
				if (!TryReadNonEmptyScalar(child.Key, out string value))
				{
					errors.Add("Every root entry must have a non-empty scalar name.");
				}
				else if (string.Equals(value, "defeatKeys", StringComparison.OrdinalIgnoreCase))
				{
					if (!string.Equals(value, "defeatKeys", StringComparison.Ordinal))
					{
						errors.Add("Reserved root entry '" + value + "' must be spelled exactly 'defeatKeys'.");
						continue;
					}
					if (flag)
					{
						errors.Add("Duplicate root entry 'defeatKeys'.");
						continue;
					}
					flag = true;
					ParseDefeatKeys(child.Value, progression.DefeatKeys, errors);
				}
				else if (!hashSet.Add(value))
				{
					errors.Add("Duplicate item tier id '" + value + "'.");
				}
				else if (child.Value is YamlSequenceNode sequence)
				{
					List<string> list = ParseResources(sequence, "tier '" + value + "'", errors);
					if (list.Count == 0)
					{
						errors.Add("Unrestricted tier '" + value + "' must contain at least one resource.");
					}
					progression.Tiers.Add(new ItemTierConfig(value, "", list));
				}
				else if (child.Value is YamlMappingNode mapping)
				{
					ParseRestrictedTier(value, mapping, progression.Tiers, errors);
				}
				else
				{
					errors.Add("Tier '" + value + "' must be either a resource sequence or a mapping with requiredKey and optional resources.");
				}
			}
		}

		private static void ParseDefeatKeys(YamlNode node, List<DefeatKeyRule> destination, List<string> errors)
		{
			if (!(node is YamlSequenceNode yamlSequenceNode))
			{
				errors.Add("defeatKeys must be a sequence.");
				return;
			}
			for (int i = 0; i < yamlSequenceNode.Children.Count; i++)
			{
				string text = $"defeatKeys[{i}]";
				if (!(yamlSequenceNode.Children[i] is YamlMappingNode yamlMappingNode))
				{
					errors.Add(text + " must be a mapping with prefabs and key.");
					continue;
				}
				DefeatKeyRule defeatKeyRule = new DefeatKeyRule();
				bool flag = false;
				bool flag2 = false;
				HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
				foreach (KeyValuePair<YamlNode, YamlNode> child in yamlMappingNode.Children)
				{
					if (!TryReadNonEmptyScalar(child.Key, out string value))
					{
						errors.Add(text + " contains an empty or non-scalar field name.");
						continue;
					}
					if (!hashSet.Add(value))
					{
						errors.Add(text + " contains duplicate field '" + value + "'.");
						continue;
					}
					if (!(value == "prefabs"))
					{
						if (value == "key")
						{
							flag2 = true;
							if (!TryReadNonEmptyScalar(child.Value, out string value2))
							{
								errors.Add(text + ".key must be a non-empty scalar.");
							}
							else
							{
								defeatKeyRule.Key = value2;
							}
						}
						else
						{
							errors.Add(text + " contains unknown field '" + value + "'.");
						}
						continue;
					}
					flag = true;
					if (!(child.Value is YamlSequenceNode yamlSequenceNode2))
					{
						errors.Add(text + ".prefabs must be a sequence.");
						continue;
					}
					foreach (YamlNode child2 in yamlSequenceNode2.Children)
					{
						if (!TryReadNonEmptyScalar(child2, out string value3))
						{
							errors.Add(text + ".prefabs contains an empty or non-scalar prefab.");
						}
						else
						{
							defeatKeyRule.Prefabs.Add(value3);
						}
					}
				}
				if (!flag)
				{
					errors.Add(text + " is missing required field 'prefabs'.");
				}
				if (!flag2)
				{
					errors.Add(text + " is missing required field 'key'.");
				}
				destination.Add(defeatKeyRule);
			}
		}

		private static void ParseRestrictedTier(string id, YamlMappingNode mapping, List<ItemTierConfig> destination, List<string> errors)
		{
			string text = "tier '" + id + "'";
			string value = "";
			List<string> resources = new List<string>();
			bool flag = false;
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (KeyValuePair<YamlNode, YamlNode> child in mapping.Children)
			{
				if (!TryReadNonEmptyScalar(child.Key, out string value2))
				{
					errors.Add(text + " contains an empty or non-scalar field name.");
				}
				else if (!hashSet.Add(value2))
				{
					errors.Add(text + " contains duplicate field '" + value2 + "'.");
				}
				else if (!(value2 == "requiredKey"))
				{
					if (value2 == "resources")
					{
						if (!(child.Value is YamlSequenceNode sequence))
						{
							errors.Add(text + ".resources must be a sequence.");
						}
						else
						{
							resources = ParseResources(sequence, text, errors);
						}
					}
					else
					{
						errors.Add(text + " contains unknown field '" + value2 + "'.");
					}
				}
				else
				{
					flag = true;
					if (!TryReadNonEmptyScalar(child.Value, out value))
					{
						errors.Add(text + ".requiredKey must be a non-empty scalar.");
					}
				}
			}
			if (!flag)
			{
				errors.Add(text + " mapping is missing required field 'requiredKey'.");
			}
			destination.Add(new ItemTierConfig(id, value, resources));
		}

		private static List<string> ParseResources(YamlSequenceNode sequence, string label, List<string> errors)
		{
			List<string> list = new List<string>();
			foreach (YamlNode child in sequence.Children)
			{
				if (!TryReadNonEmptyScalar(child, out string value))
				{
					errors.Add(label + " contains an empty or non-scalar resource.");
				}
				else
				{
					list.Add(value);
				}
			}
			return list;
		}

		private static bool TryReadNonEmptyScalar(YamlNode node, out string value)
		{
			value = ((!(node is YamlScalarNode yamlScalarNode)) ? "" : (yamlScalarNode.Value?.Trim() ?? ""));
			return value.Length > 0;
		}

		private static void Validate(ProgressionConfig progression, List<string> errors)
		{
			ValidateDefeatKeys(progression, errors);
			ValidateItemTiers(progression, errors);
			try
			{
				ProgressionIndex.ValidateConfiguration(progression);
			}
			catch (Exception ex)
			{
				errors.Add(ex.Message);
			}
		}

		private static void ValidateDefeatKeys(ProgressionConfig progression, List<string> errors)
		{
			for (int i = 0; i < progression.DefeatKeys.Count; i++)
			{
				DefeatKeyRule defeatKeyRule = progression.DefeatKeys[i];
				string text = $"defeatKeys[{i}]";
				if (string.IsNullOrWhiteSpace(defeatKeyRule.Key))
				{
					errors.Add(text + " has an empty key.");
				}
				if (defeatKeyRule.Prefabs.Count == 0)
				{
					errors.Add(text + " must contain at least one prefab.");
					continue;
				}
				HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
				foreach (string prefab in defeatKeyRule.Prefabs)
				{
					string text2 = ValheimNameUtils.NormalizePrefabName(prefab);
					if (text2.Length == 0)
					{
						errors.Add(text + " contains an empty prefab.");
					}
					else if (!hashSet.Add(text2))
					{
						errors.Add(text + " contains duplicate prefab '" + prefab + "'.");
					}
				}
			}
		}

		private static void ValidateItemTiers(ProgressionConfig progression, List<string> errors)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (ItemTierConfig tier in progression.Tiers)
			{
				foreach (string resource in tier.Resources)
				{
					string text = ValheimNameUtils.NormalizeResourceName(resource);
					if (text.Length == 0)
					{
						errors.Add("Tier '" + tier.Id + "' contains invalid resource '" + resource + "'.");
					}
					else if (!hashSet.Add(text))
					{
						errors.Add("Resource '" + resource + "' appears more than once after normalization; every resource must belong to one tier.");
					}
				}
			}
		}

		private static void PreserveLastKnownGoodOrUseDefault()
		{
			if (IsConfigured)
			{
				YouAreNotWorthyPlugin.Log.LogError((object)"Keeping the last-known-good YNW YAML configuration.");
				return;
			}
			try
			{
				if (!TryApply(DefaultProgressionYaml, "the embedded default progression YAML"))
				{
					YouAreNotWorthyPlugin.Log.LogError((object)"Failed to initialize the embedded default YNW progression YAML.");
				}
				else
				{
					YouAreNotWorthyPlugin.Log.LogWarning((object)"Using the embedded default YNW progression YAML until the local file is fixed.");
				}
			}
			catch (Exception arg)
			{
				YouAreNotWorthyPlugin.Log.LogError((object)$"Failed to initialize the embedded default YNW progression YAML: {arg}");
			}
		}

		private static string LoadEmbeddedYaml(string suffix)
		{
			string text = typeof(ProgressionConfigLoader).Assembly.GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith(suffix, StringComparison.Ordinal));
			if (string.IsNullOrEmpty(text))
			{
				throw new InvalidDataException("The embedded default YAML ending in '" + suffix + "' was not found.");
			}
			using Stream stream = typeof(ProgressionConfigLoader).Assembly.GetManifestResourceStream(text);
			if (stream == null)
			{
				throw new InvalidDataException("The embedded default YAML '" + text + "' could not be opened.");
			}
			using StreamReader streamReader = new StreamReader(stream);
			return streamReader.ReadToEnd();
		}
	}
	internal enum ItemReferenceWriteResult
	{
		Updated,
		Unchanged,
		NotReady,
		ShuttingDown
	}
	internal static class ItemReferenceWriter
	{
		private sealed class OutputItem
		{
			internal string PrefabName { get; }

			internal string ItemType { get; }

			internal string RequiredKey { get; }

			internal int RequiredKeyRank { get; }

			internal string Owner { get; }

			internal OutputItem(GuardedItemReference item, string owner)
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				PrefabName = item.PrefabName;
				ItemType = ((object)item.ItemType/*cast due to .constrained prefix*/).ToString();
				RequiredKey = item.RequiredKey;
				RequiredKeyRank = ((item.RequiredKey.Length == 0) ? (-1) : (ProgressionIndex.TryGetItemTierKeyRank(item.RequiredKey, out var rank) ? rank : int.MaxValue));
				Owner = PrefabOwnerResolver.NormalizeOwnerName(owner);
			}
		}

		internal const string ReferenceFileName = "items.reference.yml";

		private const float ExistenceCheckSeconds = 5f;

		private static readonly UTF8Encoding Utf8WithoutBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);

		private static readonly ISerializer EntrySerializer = new SerializerBuilder().Build();

		private static readonly object Sync = new object();

		private static readonly Dictionary<string, int> ItemTypeSortOrder = new Dictionary<string, int>(StringComparer.Ordinal)
		{
			["OneHandedWeapon"] = 0,
			["TwoHandedWeapon"] = 1,
			["TwoHandedWeaponLeft"] = 2,
			["Bow"] = 3,
			["Shield"] = 4,
			["Torch"] = 5,
			["Tool"] = 6,
			["Attach_Atgeir"] = 7,
			["Helmet"] = 10,
			["Chest"] = 11,
			["Legs"] = 12,
			["Shoulder"] = 13,
			["Hands"] = 14,
			["Utility"] = 15,
			["Trinket"] = 16,
			["Consumable"] = 20,
			["Fish"] = 21,
			["Ammo"] = 30,
			["AmmoNonEquipable"] = 31,
			["Material"] = 40,
			["Trophy"] = 41,
			["Misc"] = 50,
			["None"] = 51
		};

		private static bool _dirty = true;

		private static bool _shutdown;

		private static long _revision = 1L;

		private static float _nextExistenceCheck;

		internal static string ReferencePath => Path.Combine(ProgressionConfigLoader.ConfigDirectory, "items.reference.yml");

		internal static void Reset()
		{
			PrefabOwnerResolver.Invalidate();
			lock (Sync)
			{
				_shutdown = false;
				_dirty = true;
				_revision++;
				_nextExistenceCheck = 0f;
			}
		}

		internal static void MarkDirty(bool ownerSourcesChanged = false)
		{
			if (ownerSourcesChanged)
			{
				PrefabOwnerResolver.Invalidate();
			}
			lock (Sync)
			{
				if (!_shutdown)
				{
					_dirty = true;
					_revision++;
					_nextExistenceCheck = 0f;
				}
			}
		}

		internal static void Shutdown()
		{
			lock (Sync)
			{
				_shutdown = true;
				_dirty = false;
				_revision++;
				_nextExistenceCheck = 0f;
			}
		}

		internal static bool NeedsUpdate()
		{
			lock (Sync)
			{
				if (_shutdown)
				{
					return false;
				}
				if (_dirty)
				{
					return true;
				}
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				if (realtimeSinceStartup < _nextExistenceCheck)
				{
					return false;
				}
				_nextExistenceCheck = realtimeSinceStartup + 5f;
				if (File.Exists(ReferencePath))
				{
					return false;
				}
				_dirty = true;
				_revision++;
				return true;
			}
		}

		internal static bool TryWriteIfNeeded()
		{
			return TryWriteIfNeededWithResult() != ItemReferenceWriteResult.NotReady;
		}

		internal static ItemReferenceWriteResult TryWriteIfNeededWithResult()
		{
			long revision;
			lock (Sync)
			{
				if (_shutdown)
				{
					return ItemReferenceWriteResult.ShuttingDown;
				}
				if (!_dirty && File.Exists(ReferencePath))
				{
					return ItemReferenceWriteResult.Unchanged;
				}
				revision = _revision;
			}
			if (!RestrictionEvaluator.TryCreateGuardedItemReferenceSnapshot(out List<GuardedItemReference> entries))
			{
				return ItemReferenceWriteResult.NotReady;
			}
			bool flag = WriteIfChanged(BuildContent(entries));
			lock (Sync)
			{
				_nextExistenceCheck = Time.realtimeSinceStartup + 5f;
				if (!_shutdown && _revision == revision)
				{
					_dirty = false;
				}
			}
			if (!flag)
			{
				return ItemReferenceWriteResult.Unchanged;
			}
			return ItemReferenceWriteResult.Updated;
		}

		private static string BuildContent(IReadOnlyCollection<GuardedItemReference> references)
		{
			IReadOnlyDictionary<string, string> owners = PrefabOwnerResolver.Resolve(references.Select((GuardedItemReference entry) => entry.PrefabName));
			string value;
			List<OutputItem> source = references.Select((GuardedItemReference entry) => new OutputItem(entry, owners.TryGetValue(ValheimNameUtils.NormalizePrefabName(entry.PrefabName), out value) ? value : "Unknown / Untracked")).ToList();
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(BuildHeader());
			foreach (IGrouping<string, OutputItem> item in source.OrderBy((OutputItem item) => PrefabOwnerResolver.GetOwnerSortBucket(item.Owner)).ThenBy<OutputItem, string>((OutputItem item) => item.Owner, StringComparer.OrdinalIgnoreCase).ThenBy<OutputItem, string>((OutputItem item) => item.Owner, StringComparer.Ordinal)
				.GroupBy<OutputItem, string>((OutputItem item) => item.Owner, StringComparer.OrdinalIgnoreCase))
			{
				stringBuilder.Append("# ===== ").Append(PrefabOwnerResolver.NormalizeOwnerName(item.Key)).AppendLine(" =====");
				foreach (IGrouping<string, OutputItem> item2 in item.OrderBy((OutputItem item) => GetItemTypeSortOrder(item.ItemType)).ThenBy<OutputItem, string>((OutputItem item) => item.ItemType, StringComparer.Ordinal).ThenBy((OutputItem item) => (item.RequiredKey.Length != 0) ? 1 : 0)
					.ThenBy((OutputItem item) => item.RequiredKeyRank)
					.ThenBy<OutputItem, string>((OutputItem item) => item.RequiredKey, StringComparer.OrdinalIgnoreCase)
					.ThenBy<OutputItem, string>((OutputItem item) => item.RequiredKey, StringComparer.Ordinal)
					.ThenBy<OutputItem, string>((OutputItem item) => item.PrefabName, StringComparer.OrdinalIgnoreCase)
					.ThenBy<OutputItem, string>((OutputItem item) => item.PrefabName, StringComparer.Ordinal)
					.GroupBy<OutputItem, string>((OutputItem item) => item.ItemType, StringComparer.Ordinal))
				{
					stringBuilder.Append("# ----- ").Append(item2.Key).AppendLine(" -----");
					List<string> values = item2.Select((OutputItem item) => (item.RequiredKey.Length != 0) ? (item.PrefabName + ", " + item.RequiredKey) : item.PrefabName).ToList();
					AppendYamlSequence(stringBuilder, values);
					stringBuilder.AppendLine();
				}
			}
			return stringBuilder.ToString();
		}

		private static int GetItemTypeSortOrder(string itemType)
		{
			if (!ItemTypeSortOrder.TryGetValue(itemType, out var value))
			{
				return int.MaxValue;
			}
			return value;
		}

		private static void AppendYamlSequence(StringBuilder builder, List<string> values)
		{
			string text = EntrySerializer.Serialize(values).Replace("\r\n", "\n").TrimEnd(new char[1] { '\n' });
			builder.AppendLine(text.Replace("\n", Environment.NewLine));
		}

		private static string BuildHeader()
		{
			string newLine = Environment.NewLine;
			return string.Join(newLine, "# YouAreNotWorthy item restriction reference", "# Generated from the effective loaded runtime. This file is overwritten automatically.", "# It is reference-only: YNW does not read it as configuration and does not ServerSync it.", "# Only the authoritative server/listen host writes it; remote clients leave local copies untouched.", "#", "# Only statically discoverable player-facing item-use targets evaluated by YNW are listed.", "# General equipment, consumables, and ammo require a valid inventory icon, following VNEI's", "# lightweight player-item signal. Explicit Door/OfferingBowl/ItemStand items remain even without one.", "# Prefabs in VNEI 0.17.5's default blacklist are omitted unless their item is directly listed", "# under a progression.yml tier's resources; production-path inheritance is not a direct listing.", "# '- Prefab' means the target currently has no effective required personal key.", "# '- Prefab, key' means that guarded use is blocked while the player lacks that personal key.", "# The key is the final effective result after direct assignment and production-path inheritance.", "# Tier-only resources whose pickup, storage, and crafting are not guarded are omitted.", "# Explicit item-stand boss-item/supported-item declarations are included without an icon;", "# supported-type declarations contribute only icon-backed items.", "# Broad allow-all stands and runtime-only stand changes may be absent", "# unless the item is independently discoverable as equipment, consumable, ammo, Door key, or direct offering.", "# Owner and ItemType headings are presentation-only. ItemType uses the loaded Valheim enum name.", "# Within each ItemType, keyless entries come first. Keyed entries follow the first tier where", "# their requiredKey appears in progression.yml, then prefab name; repeated keys stay together.", "# Owner inference is best-effort; ambiguous or runtime-created prefabs remain Unknown / Untracked.", "#") + newLine;
		}

		private static bool WriteIfChanged(string content)
		{
			Directory.CreateDirectory(ProgressionConfigLoader.ConfigDirectory);
			string referencePath = ReferencePath;
			if (string.Equals(File.Exists(referencePath) ? File.ReadAllText(referencePath) : string.Empty, content, StringComparison.Ordinal))
			{
				return false;
			}
			WriteAtomically(referencePath, content);
			YouAreNotWorthyPlugin.Log.LogInfo((object)("Updated item restriction reference at " + referencePath + "."));
			return true;
		}

		private static void WriteAtomically(string path, string content)
		{
			string text = Path.Combine(Path.GetDirectoryName(path) ?? ProgressionConfigLoader.ConfigDirectory, $".{Path.GetFileName(path)}.{Guid.NewGuid():N}.tmp");
			try
			{
				File.WriteAllText(text, content, Utf8WithoutBom);
				if (File.Exists(path))
				{
					File.Replace(text, path, null);
				}
				else
				{
					File.Move(text, path);
				}
			}
			finally
			{
				try
				{
					if (File.Exists(text))
					{
						File.Delete(text);
					}
				}
				catch (Exception)
				{
				}
			}
		}
	}
	internal static class ItemReferenceCommands
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleOptionsFetcher <>9__9_0;

			internal List<string> <RegisterConsoleCommand>b__9_0()
			{
				return TabOptions;
			}
		}

		private const string CommandName = "ynw:items";

		private const string RpcRefreshRequest = "YNW_AdminItemReferenceRefresh";

		private const string RpcRefreshResult = "YNW_AdminItemReferenceResult";

		private const int MaxResultLength = 2048;

		private const int MaxPeerNameLength = 128;

		private static readonly List<string> TabOptions = new List<string> { "refresh" };

		private static readonly FieldInfo? TerminalCommandsField = typeof(Terminal).GetField("commands", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

		private static ConsoleCommand? _consoleCommand;

		private static bool _shutdown;

		internal static void RegisterConsoleCommand()
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			_shutdown = false;
			if (_consoleCommand != null)
			{
				return;
			}
			Dictionary<string, ConsoleCommand> terminalCommands = GetTerminalCommands();
			if (terminalCommands == null)
			{
				YouAreNotWorthyPlugin.Log.LogWarning((object)"Could not inspect Valheim's console-command registry; 'ynw:items' was not registered.");
				return;
			}
			if (terminalCommands.ContainsKey("ynw:items"))
			{
				YouAreNotWorthyPlugin.Log.LogWarning((object)"Could not register 'ynw:items' because another console command already uses that name.");
				return;
			}
			ConsoleEvent val = HandleConsoleCommand;
			object obj = <>c.<>9__9_0;
			if (obj == null)
			{
				ConsoleOptionsFetcher val2 = () => TabOptions;
				<>c.<>9__9_0 = val2;
				obj = (object)val2;
			}
			_consoleCommand = new ConsoleCommand("ynw:items", "Refresh the authoritative server items.reference.yml. Usage: ynw:items refresh", val, false, true, false, false, false, (ConsoleOptionsFetcher)obj, false, false, false);
		}

		internal static void RegisterPeer(ZNet net, ZNetPeer peer)
		{
			if (_shutdown)
			{
				return;
			}
			try
			{
				if (net.IsServer())
				{
					peer.m_rpc.Register<ZPackage>("YNW_AdminItemReferenceRefresh", (Action<ZRpc, ZPackage>)RPC_RefreshRequest);
				}
				else
				{
					peer.m_rpc.Register<string>("YNW_AdminItemReferenceResult", (Action<ZRpc, string>)RPC_RefreshResult);
				}
			}
			catch (Exception arg)
			{
				YouAreNotWorthyPlugin.Log.LogError((object)$"Failed to register YNW item-reference RPCs: {arg}");
			}
		}

		internal static void Shutdown()
		{
			_shutdown = true;
			Dictionary<string, ConsoleCommand> terminalCommands = GetTerminalCommands();
			if (_consoleCommand != null && terminalCommands != null && terminalCommands.TryGetValue("ynw:items", out var value) && value == _consoleCommand)
			{
				terminalCommands.Remove("ynw:items");
			}
			_consoleCommand = null;
		}

		private static Dictionary<string, ConsoleCommand>? GetTerminalCommands()
		{
			try
			{
				return TerminalCommandsField?.GetValue(null) as Dictionary<string, ConsoleCommand>;
			}
			catch (Exception)
			{
				return null;
			}
		}

		private static void HandleConsoleCommand(ConsoleEventArgs args)
		{
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Expected O, but got Unknown
			if (_shutdown)
			{
				return;
			}
			if (args.Length != 2 || !string.Equals(args[1], "refresh", StringComparison.OrdinalIgnoreCase))
			{
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString("Usage: ynw:items refresh");
				}
				return;
			}
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				Terminal context2 = args.Context;
				if (context2 != null)
				{
					context2.AddString("YNW item-reference commands require an active server session.");
				}
				return;
			}
			if (instance.IsServer())
			{
				ExecuteRefresh(null, args.Context);
				return;
			}
			if (!YouAreNotWorthyPlugin.IsLocalAdmin)
			{
				Terminal context3 = args.Context;
				if (context3 != null)
				{
					context3.AddString("You are not an admin on this server.");
				}
				return;
			}
			ZNetPeer serverPeer = instance.GetServerPeer();
			if (serverPeer == null || !serverPeer.m_rpc.IsConnected())
			{
				Terminal context4 = args.Context;
				if (context4 != null)
				{
					context4.AddString("The server connection is not ready.");
				}
				return;
			}
			serverPeer.m_rpc.Invoke("YNW_AdminItemReferenceRefresh", new object[1] { (object)new ZPackage() });
			Terminal context5 = args.Context;
			if (context5 != null)
			{
				context5.AddString("Requested an item-reference refresh from the server.");
			}
		}

		private static void RPC_RefreshRequest(ZRpc requester, ZPackage _)
		{
			if (!_shutdown && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				if (!IsRemoteAdmin(requester))
				{
					YouAreNotWorthyPlugin.Log.LogWarning((object)("Rejected a YNW item-reference refresh from non-admin peer '" + GetPeerHostName(requester) + "'."));
					SendResult(requester, null, "You are not an admin on this server.");
				}
				else
				{
					ExecuteRefresh(requester, null);
				}
			}
		}

		private static void ExecuteRefresh(ZRpc? requester, Terminal? terminal)
		{
			if (!_shutdown)
			{
				string text = ((requester == null) ? "local-server" : GetPeerHostName(requester));
				YouAreNotWorthyPlugin.Log.LogInfo((object)("Admin '" + text + "' requested an authoritative item-reference refresh."));
				SendResult(requester, terminal, KeyReferenceWriter.TryRefreshItemReference(YouAreNotWorthyPlugin.IsSourceOfTruth) switch
				{
					ItemReferenceRefreshResult.Updated => "Updated the server item reference from the effective runtime: " + ItemReferenceWriter.ReferencePath, 
					ItemReferenceRefreshResult.Unchanged => "The server item reference already matches the effective runtime: " + ItemReferenceWriter.ReferencePath, 
					ItemReferenceRefreshResult.NotAuthoritative => "YNW item references can be refreshed only by the authoritative server.", 
					ItemReferenceRefreshResult.RuntimeNotReady => "The server runtime is not ready for an item-reference scan. Try again after the world finishes loading.", 
					ItemReferenceRefreshResult.ShuttingDown => "YNW is shutting down; the item reference was not refreshed.", 
					_ => "The server could not refresh the item reference. Check the server log; an automatic retry remains queued.", 
				});
			}
		}

		private static void RPC_RefreshResult(ZRpc server, string message)
		{
			if (!_shutdown)
			{
				if (!IsServerConnection(server))
				{
					YouAreNotWorthyPlugin.Log.LogWarning((object)"Ignored a YNW item-reference result from a non-server connection.");
				}
				else
				{
					PrintConsole(SanitizeResult(message));
				}
			}
		}

		private static void SendResult(ZRpc? requester, Terminal? terminal, string message)
		{
			string text = SanitizeResult(message);
			YouAreNotWorthyPlugin.Log.LogInfo((object)text);
			if (requester == null)
			{
				if (terminal != null)
				{
					terminal.AddString(text);
				}
				return;
			}
			try
			{
				requester.Invoke("YNW_AdminItemReferenceResult", new object[1] { text });
			}
			catch (Exception ex)
			{
				YouAreNotWorthyPlugin.Log.LogWarning((object)("Failed to return a YNW item-reference result: " + ex.Message));
			}
		}

		private static bool IsRemoteAdmin(ZRpc requester)
		{
			try
			{
				ZNet instance = ZNet.instance;
				ZNetPeer val = ((instance != null) ? instance.GetPeer(requester) : null);
				return (Object)(object)instance != (Object)null && instance.IsServer() && val != null && val.IsReady() && val.m_rpc == requester && instance.IsAdmin(requester.GetSocket().GetHostName());
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static bool IsServerConnection(ZRpc rpc)
		{
			try
			{
				ZNet instance = ZNet.instance;
				ZNetPeer val = ((instance != null) ? instance.GetServerPeer() : null);
				return val != null && val.m_server && val.m_rpc == rpc;
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static string GetPeerHostName(ZRpc rpc)
		{
			try
			{
				string text = rpc.GetSocket().GetHostName() ?? string.Empty;
				StringBuilder stringBuilder = new StringBuilder(Math.Min(text.Length, 128));
				for (int i = 0; i < text.Length && i < 128; i++)
				{
					char c = text[i];
					stringBuilder.Append(char.IsControl(c) ? ' ' : c);
				}
				return (stringBuilder.Length == 0) ? "unknown" : stringBuilder.ToString();
			}
			catch (Exception)
			{
				return "unknown";
			}
		}

		private static string SanitizeResult(string? message)
		{
			string text = (message ?? string.Empty).Replace('\r', ' ').Replace('\n', ' ');
			if (text.Length > 2048)
			{
				return text.Substring(0, 2048);
			}
			return text;
		}

		private static void PrintConsole(string message)
		{
			if ((Object)(object)Console.instance != (Object)null)
			{
				((Terminal)Console.instance).AddString(message);
			}
		}
	}
	internal enum ItemReferenceRefreshResult
	{
		Updated,
		Unchanged,
		NotAuthoritative,
		RuntimeNotReady,
		ShuttingDown,
		Failed
	}
	internal static class KeyReferenceWriter
	{
		private readonly struct ParsedGlobalKey
		{
			internal string Identity { get; }

			internal string Spelling { get; }

			internal bool HasValue { get; }

			internal ParsedGlobalKey(string identity, string spelling, bool hasValue)
			{
				Identity = identity;
				Spelling = spelling;
				HasValue = hasValue;
			}
		}

		private sealed class ObservedGlobalKey
		{
			private readonly HashSet<string> _spellings = new HashSet<string>(StringComparer.Ordinal);

			internal string Identity { get; }

			internal bool HasValue { get; private set; }

			internal ObservedGlobalKey(string identity)
			{
				Identity = identity;
			}

			internal bool Add(string spelling, bool hasValue)
			{
				bool result = _spellings.Add(spelling);
				if (hasValue && !HasValue)
				{
					HasValue = true;
					result = true;
				}
				return result;
			}

			internal ObservedGlobalKeySnapshot Snapshot()
			{
				return new ObservedGlobalKeySnapshot(Identity, _spellings.OrderBy<string, string>((string value) => value, StringComparer.Ordinal).ToList(), HasValue);
			}
		}

		private sealed class ObservedGlobalKeySnapshot
		{
			internal string Identity { get; }

			internal List<string> Spellings { get; }

			internal bool HasValue { get; }

			internal ObservedGlobalKeySnapshot(string identity, List<string> spellings, bool hasValue)
			{
				Identity = identity;
				Spellings = spellings;
				HasValue = hasValue;
			}
		}

		private sealed class GlobalKeyBuilder
		{
			private readonly Dictionary<string, NameCandidate> _names = new Dictionary<string, NameCandidate>(StringComparer.Ordinal);

			internal string Identity { get; }

			internal bool HasValue { get; set; }

			internal HashSet<string> DefeatPrefabs { get; } = new HashSet<string>(StringComparer.Ordinal);

			internal GlobalKeyBuilder(string identity)
			{
				Identity = identity;
			}

			internal void AddName(string name, int priority, bool isWorldFallback)
			{
				if (!_names.TryGetValue(name, out var value) || priority < value.Priority || (value.IsWorldFallback && !isWorldFallback))
				{
					_names[name] = new NameCandidate(priority, isWorldFallback);
				}
			}

			internal GlobalKeyReferenceEntry Build()
			{
				string canonicalName = (from entry in _names
					orderby entry.Value.Priority, entry.Value.IsWorldFallback
					select entry).ThenBy<KeyValuePair<string, NameCandidate>, string>((KeyValuePair<string, NameCandidate> entry) => entry.Key, StringComparer.Ordinal).FirstOrDefault().Key ?? Identity;
				List<string> list = (from entry in _names
					where !entry.Value.IsWorldFallback && !string.Equals(entry.Key, canonicalName, StringComparison.Ordinal)
					select entry.Key).OrderBy<string, string>((string value) => value, StringComparer.OrdinalIgnoreCase).ThenBy<string, string>((string value) => value, StringComparer.Ordinal).ToList();
				List<string> list2 = DefeatPrefabs.OrderBy<string, string>((string value) => value, StringComparer.OrdinalIgnoreCase).ThenBy<string, string>((string value) => value, StringComparer.Ordinal).ToList();
				return new GlobalKeyReferenceEntry
				{
					Key = canonicalName,
					Handling = ((HasValue || ProgressionIndex.IsSharedWorldKey(canonicalName)) ? "sharedWorld" : "personalized"),
					Aliases = ((list.Count > 0) ? list : null),
					DefeatPrefabs = ((list2.Count > 0) ? list2 : null)
				};
			}
		}

		private readonly struct NameCandidate
		{
			internal int Priority { get; }

			internal bool IsWorldFallback { get; }

			internal NameCandidate(int priority, bool isWorldFallback)
			{
				Priority = priority;
				IsWorldFallback = isWorldFallback;
			}
		}

		private sealed class KeyReferenceDocument
		{
			[YamlMember(Order = 1)]
			public List<GlobalKeyReferenceEntry> GlobalKeys { get; set; } = new List<GlobalKeyReferenceEntry>();

			[YamlMember(Order = 2)]
			public List<string> PlayerBasedRaidKeys { get; set; } = new List<string>();
		}

		private sealed class GlobalKeyReferenceEntry
		{
			[YamlMember(Order = 1)]
			public string Key { get; set; } = string.Empty;

			[YamlMember(Order = 2)]
			public string Handling { get; set; } = string.Empty;

			[YamlMember(Order = 3)]
			public List<string>? Aliases { get; set; }

			[YamlMember(Order = 4)]
			public List<string>? DefeatPrefabs { get; set; }
		}

		internal const string ReferenceFileName = "keys.reference.yml";

		private const float ObservationDebounceSeconds = 1f;

		private const float NotReadyRetrySeconds = 1f;

		private const float FailureRetrySeconds = 5f;

		private const string PersonalizedHandling = "personalized";

		private const string SharedWorldHandling = "sharedWorld";

		private const int EnumNamePriority = 0;

		private const int DefeatKeyPriority = 1;

		private const int ObservedNamePriority = 2;

		private const int RaidNamePriority = 3;

		private const int WorldFallbackPriority = 4;

		private static readonly UTF8Encoding Utf8WithoutBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);

		private static readonly ISerializer Serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull | DefaultValuesHandling.OmitDefaults).Build();

		private static readonly object Sync = new object();

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

		private static bool _dirty = true;

		private static bool _shutdown;

		private static bool _runtimeReady;

		private static bool _wasSourceOfTruth;

		private static long _revision = 1L;

		private static float _updateAfter;

		private static float _retryAfter;

		private static ZoneSystem? _lastZoneSystem;

		private static ZNetScene? _lastNetScene;

		private static RandEventSystem? _lastEventSystem;

		private static ObjectDB? _lastObjectDb;

		internal static string ReferencePath => Path.Combine(ProgressionConfigLoader.ConfigDirectory, "keys.reference.yml");

		internal static void ObserveGlobalKey(string? rawKey)
		{
			if (!TryParseGlobalKey(rawKey, out var parsed))
			{
				return;
			}
			lock (Sync)
			{
				if (!_shutdown)
				{
					if (!ObservedGlobalKeys.TryGetValue(parsed.Identity, out ObservedGlobalKey value))
					{
						value = new ObservedGlobalKey(parsed.Identity);
						ObservedGlobalKeys[parsed.Identity] = value;
					}
					if (value.Add(parsed.Spelling, parsed.HasValue))
					{
						_dirty = true;
						_revision++;
						_updateAfter = Time.realtimeSinceStartup + 1f;
					}
				}
			}
		}

		internal static void Reset()
		{
			ItemReferenceWriter.Reset();
			lock (Sync)
			{
				ObservedGlobalKeys.Clear();
				_shutdown = false;
				_runtimeReady = false;
				_wasSourceOfTruth = false;
				_dirty = true;
				_revision++;
				_updateAfter = 0f;
				_retryAfter = 0f;
				_lastZoneSystem = null;
				_lastNetScene = null;
				_lastEventSystem = null;
				_lastObjectDb = null;
			}
		}

		internal static void MarkRuntimeReady()
		{
			lock (Sync)
			{
				if (!_shutdown)
				{
					_runtimeReady = true;
					MarkDirtyLocked();
				}
			}
		}

		internal static void MarkDirty()
		{
			lock (Sync)
			{
				if (!_shutdown)
				{
					MarkDirtyLocked();
				}
			}
		}

		internal static void MarkItemReferenceDirty(bool ownerSourcesChanged = false)
		{
			ItemReferenceWriter.MarkDirty(ownerSourcesChanged);
			MarkDirty();
		}

		internal static ItemReferenceRefreshResult TryRefreshItemReference(bool isSourceOfTruth)
		{
			if (!isSourceOfTruth || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return ItemReferenceRefreshResult.NotAuthoritative;
			}
			lock (Sync)
			{
				if (_shutdown)
				{
					return ItemReferenceRefreshResult.ShuttingDown;
				}
			}
			try
			{
				RestrictionEvaluator.InvalidateItemCache();
				ItemReferenceWriter.MarkDirty(ownerSourcesChanged: true);
				if (!IsItemReferenceRuntimeReady())
				{
					return ItemReferenceRefreshResult.RuntimeNotReady;
				}
				return ItemReferenceWriter.TryWriteIfNeededWithResult() switch
				{
					ItemReferenceWriteResult.Updated => ItemReferenceRefreshResult.Updated, 
					ItemReferenceWriteResult.Unchanged => ItemReferenceRefreshResult.Unchanged, 
					ItemReferenceWriteResult.NotReady => ItemReferenceRefreshResult.RuntimeNotReady, 
					ItemReferenceWriteResult.ShuttingDown => ItemReferenceRefreshResult.ShuttingDown, 
					_ => ItemReferenceRefreshResult.Failed, 
				};
			}
			catch (Exception ex)
			{
				lock (Sync)
				{
					if (!_shutdown)
					{
						MarkDirtyLocked();
						_retryAfter = Time.realtimeSinceStartup + 5f;
					}
				}
				YouAreNotWorthyPlugin.Log.LogWarning((object)("Failed to refresh the item restriction reference: " + ex.GetType().Name + ": " + ex.Message + ". " + $"Retrying automatically in {5f:0.#}s."));
				return ItemReferenceRefreshResult.Failed;
			}
		}

		private static bool IsItemReferenceRuntimeReady()
		{
			ObjectDB instance = ObjectDB.instance;
			ZNetScene instance2 = ZNetScene.instance;
			if (_runtimeReady && (Object)(object)instance != (Object)null && instance.m_items != null && instance.m_items.Count > 0 && instance.m_recipes != null && (Object)(object)instance2 != (Object)null)
			{
				return ValheimNameUtils.EnumerateRegisteredPrefabs(instance2).Any();
			}
			return false;
		}

		internal static void Shutdown()
		{
			ItemReferenceWriter.Shutdown();
			lock (Sync)
			{
				ObservedGlobalKeys.Clear();
				_shutdown = true;
				_runtimeReady = false;
				_wasSourceOfTruth = false;
				_dirty = false;
				_revision++;
				_updateAfter = 0f;
				_retryAfter = 0f;
				_lastZoneSystem = null;
				_lastNetScene = null;
				_lastEventSystem = null;
				_lastObjectDb = null;
			}
		}

		internal static void TryUpdate(bool isSourceOfTruth)
		{
			bool flag = isSourceOfTruth && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer();
			ZoneSystem instance = ZoneSystem.instance;
			ZNetScene instance2 = ZNetScene.instance;
			RandEventSystem instance3 = RandEventSystem.instance;
			ObjectDB instance4 = ObjectDB.instance;
			bool flag2 = flag && ItemReferenceWriter.NeedsUpdate();
			lock (Sync)
			{
				if (_shutdown)
				{
					return;
				}
				if (flag && !_wasSourceOfTruth)
				{
					MarkDirtyLocked();
				}
				_wasSourceOfTruth = flag;
				if (_lastZoneSystem != instance || _lastNetScene != instance2 || _lastEventSystem != instance3 || _lastObjectDb != instance4)
				{
					_lastZoneSystem = instance;
					_lastNetScene = instance2;
					_lastEventSystem = instance3;
					_lastObjectDb = instance4;
					ItemReferenceWriter.MarkDirty(ownerSourcesChanged: true);
					MarkDirtyLocked();
				}
				else if (flag2 && !_dirty)
				{
					MarkDirtyLocked();
				}
			}
			if (!flag)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			long revision;
			List<ObservedGlobalKeySnapshot> observed;
			lock (Sync)
			{
				if (_shutdown || !_dirty || realtimeSinceStartup < _updateAfter || realtimeSinceStartup < _retryAfter)
				{
					return;
				}
				revision = _revision;
				observed = ObservedGlobalKeys.Values.Select((ObservedGlobalKey key) => key.Snapshot()).ToList();
			}
			if (!TryGetReadyRuntime(out ZoneSystem zoneSystem, out ZNetScene netScene, out RandEventSystem eventSystem, out ObjectDB _))
			{
				lock (Sync)
				{
					if (!_shutdown && _revision == revision)
					{
						_updateAfter = realtimeSinceStartup + 1f;
					}
					return;
				}
			}
			try
			{
				WriteIfChanged(BuildContent(zoneSystem, netScene, eventSystem, observed));
				if (!ItemReferenceWriter.TryWriteIfNeeded())
				{
					lock (Sync)
					{
						if (!_shutdown && _revision == revision)
						{
							_updateAfter = realtimeSinceStartup + 1f;
						}
						return;
					}
				}
				lock (Sync)
				{
					_retryAfter = 0f;
					if (!_shutdown && _revision == revision)
					{
						_dirty = false;
					}
				}
			}
			catch (Exception ex)
			{
				lock (Sync)
				{
					if (!_shutdown)
					{
						_dirty = true;
						_retryAfter = Time.realtimeSinceStartup + 5f;
					}
				}
				YouAreNotWorthyPlugin.Log.LogWarning((object)("Failed to update runtime references: " + ex.GetType().Name + ": " + ex.Message + ". " + $"Retrying in {5f:0.#}s."));
			}
		}

		private static bool TryGetReadyRuntime(out ZoneSystem zoneSystem, out ZNetScene netScene, out RandEventSystem eventSystem, out ObjectDB objectDb)
		{
			zoneSystem = ZoneSystem.instance;
			netScene = ZNetScene.instance;
			eventSystem = RandEventSystem.instance;
			objectDb = ObjectDB.instance;
			if ((Object)(object)zoneSystem != (Object)null && (Object)(object)netScene != (Object)null && (Object)(object)eventSystem != (Object)null && (Object)(object)objectDb != (Object)null && _runtimeReady && zoneSystem.m_globalKeysValues != null && ValheimNameUtils.EnumerateRegisteredPrefabs(netScene).Any() && eventSystem.m_events != null && eventSystem.m_events.Count > 0 && objectDb.m_items != null && objectDb.m_items.Count > 0)
			{
				return objectDb.m_recipes != null;
			}
			return false;
		}

		private static string BuildContent(ZoneSystem zoneSystem, ZNetScene netScene, RandEventSystem eventSystem, IEnumerable<ObservedGlobalKeySnapshot> observed)
		{
			Dictionary<string, GlobalKeyBuilder> dictionary = new Dictionary<string, GlobalKeyBuilder>(StringComparer.OrdinalIgnoreCase);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			AddEnumGlobalKeys(dictionary);
			AddWorldFallbackKeys(zoneSystem, dictionary);
			AddRaidKeys(eventSystem, dictionary, hashSet);
			AddDefeatKeys(netScene, dictionary);
			AddObservedKeys(observed, dictionary);
			KeyReferenceDocument graph = new KeyReferenceDocument
			{
				GlobalKeys = (from key in dictionary.Values
					select key.Build() into key
					orderby string.Equals(key.Handling, "sharedWorld", StringComparison.Ordinal) ? 1 : 0
					select key).ThenBy<GlobalKeyReferenceEntry, string>((GlobalKeyReferenceEntry key) => key.Key, StringComparer.OrdinalIgnoreCase).ThenBy<GlobalKeyReferenceEntry, string>((GlobalKeyReferenceEntry key) => key.Key, StringComparer.Ordinal).ToList(),
				PlayerBasedRaidKeys = hashSet.OrderBy<string, string>((string key) => key, StringComparer.OrdinalIgnoreCase).ThenBy<string, string>((string key) => key, StringComparer.Ordinal).ToList()
			};
			return BuildHeader() + Serializer.Serialize(graph);
		}

		private static void AddEnumGlobalKeys(Dictionary<string, GlobalKeyBuilder> globalKeys)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			foreach (GlobalKeys value in Enum.GetValues(typeof(GlobalKeys)))
			{
				if ((int)value != 32 && (int)value != 43)
				{
					AddGlobalKey(globalKeys, ((object)value/*cast due to .constrained prefix*/).ToString(), 0);
				}
			}
		}

		private static void AddWorldFallbackKeys(ZoneSystem zoneSystem, Dictionary<string, GlobalKeyBuilder> globalKeys)
		{
			foreach (KeyValuePair<string, string> globalKeysValue in zoneSystem.m_globalKeysValues)
			{
				AddGlobalKey(globalKeys, globalKeysValue.Key, 4, !string.IsNullOrEmpty(globalKeysValue.Value), isWorldFallback: true);
			}
		}

		private static void AddRaidKeys(RandEventSystem eventSystem, Dictionary<string, GlobalKeyBuilder> globalKeys, HashSet<string> playerBasedRaidKeys)
		{
			foreach (RandomEvent @event in eventSystem.m_events)
			{
				if (@event != null)
				{
					AddGlobalKeys(globalKeys, @event.m_requiredGlobalKeys, 3);
					AddGlobalKeys(globalKeys, @event.m_notRequiredGlobalKeys, 3);
					AddPersonalKeys(playerBasedRaidKeys, @event.m_altRequiredPlayerKeysAny);
					AddPersonalKeys(playerBasedRaidKeys, @event.m_altRequiredPlayerKeysAll);
					AddPersonalKeys(playerBasedRaidKeys, @event.m_altNotRequiredPlayerKeys);
				}
			}
		}

		private static void AddDefeatKeys(ZNetScene netScene, Dictionary<string, GlobalKeyBuilder> globalKeys)
		{
			foreach (GameObject item in ValheimNameUtils.EnumerateRegisteredPrefabs(netScene))
			{
				Character component = item.GetComponent<Character>();
				if (!((Object)(object)component == (Object)null) && TryParseGlobalKey(component.m_defeatSetGlobalKey, out var parsed))
				{
					GlobalKeyBuilder orCreate = GetOrCreate(globalKeys, parsed.Identity);
					orCreate.AddName(parsed.Spelling, 1, isWorldFallback: false);
					orCreate.HasValue |= parsed.HasValue;
					string prefabName = ValheimNameUtils.GetPrefabName(item);
					if (prefabName.Length > 0)
					{
						orCreate.DefeatPrefabs.Add(prefabName);
					}
				}
			}
		}

		private static void AddObservedKeys(IEnumerable<ObservedGlobalKeySnapshot> observed, Dictionary<string, GlobalKeyBuilder> globalKeys)
		{
			foreach (ObservedGlobalKeySnapshot item in observed)
			{
				GlobalKeyBuilder orCreate = GetOrCreate(globalKeys, item.Identity);
				foreach (string spelling in item.Spellings)
				{
					orCreate.AddName(spelling, 2, isWorldFallback: false);
				}
				orCreate.HasValue |= item.HasValue;
			}
		}

		private static void AddGlobalKeys(Dictionary<string, GlobalKeyBuilder> globalKeys, IEnumerable<string>? keys, int priority)
		{
			if (keys == null)
			{
				return;
			}
			foreach (string key in keys)
			{
				AddGlobalKey(globalKeys, key, priority);
			}
		}

		private static void AddPersonalKeys(HashSet<string> personalKeys, IEnumerable<string>? keys)
		{
			if (keys == null)
			{
				return;
			}
			foreach (string key in keys)
			{
				string text = (key ?? string.Empty).Trim();
				if (text.Length > 0)
				{
					personalKeys.Add(text);
				}
			}
		}

		private static void AddGlobalKey(Dictionary<string, GlobalKeyBuilder> globalKeys, string? rawKey, int priority, bool hasValue = false, bool isWorldFallback = false)
		{
			if (TryParseGlobalKey(rawKey, out var parsed))
			{
				GlobalKeyBuilder orCreate = GetOrCreate(globalKeys, parsed.Identity);
				orCreate.AddName(parsed.Spelling, priority, isWorldFallback);
				orCreate.HasValue |= hasValue || parsed.HasValue;
			}
		}

		private static GlobalKeyBuilder GetOrCreate(Dictionary<string, GlobalKeyBuilder> globalKeys, string identity)
		{
			if (!globalKeys.TryGetValue(identity, out GlobalKeyBuilder value))
			{
				value = (globalKeys[identity] = new GlobalKeyBuilder(identity));
			}
			return value;
		}

		private static bool TryParseGlobalKey(string? rawKey, out ParsedGlobalKey parsed)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Invalid comparison between Unknown and I4
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Invalid comparison between Unknown and I4
			string text = (rawKey ?? string.Empty).Trim();
			if (text.Length == 0)
			{
				parsed = default(ParsedGlobalKey);
				return false;
			}
			string value = default(string);
			GlobalKeys val = default(GlobalKeys);
			string keyValue = ZoneSystem.GetKeyValue(text, ref value, ref val);
			int num = text.IndexOf(' ');
			string text2 = ((num > 0) ? text.Substring(0, num) : text).Trim();
			if (keyValue.Length == 0 || text2.Length == 0)
			{
				parsed = default(ParsedGlobalKey);
				return false;
			}
			if (Enum.TryParse<GlobalKeys>(text2, ignoreCase: true, out GlobalKeys result) && Enum.IsDefined(typeof(GlobalKeys), result) && ((int)result == 32 || (int)result == 43))
			{
				parsed = default(ParsedGlobalKey);
				return false;
			}
			parsed = new ParsedGlobalKey(keyValue, text2, !string.IsNullOrEmpty(value));
			return true;
		}

		private static string BuildHeader()
		{
			string newLine = Environment.NewLine;
			return string.Join(newLine, "# YouAreNotWorthy key reference", "# Generated from the effective loaded runtime. This file is overwritten automatically.", "# It is reference-only: YNW does not read it as configuration and does not ServerSync it.", "# Only the authoritative server/listen host writes it; remote clients leave local copies untouched.", "# This is a discovered catalog, not a world/player progress snapshot; values and presence are omitted.", "# globalKeys lists personalized entries first, followed by sharedWorld entries.", "#", "# YNW personalizes every value-less, non-reserved global key. Its world write is blocked,", $"# the same literal is granted as a native character unique key to active players within {32f:0.##}m", "# of the event position, and ordinary global-key reads are evaluated against that character key.", "# Value-bearing keys and reserved modifiers/runtime state remain shared world keys.", "#", "# PlayerEvents OFF: raids use requiredGlobalKeys and forbiddenGlobalKeys. YNW evaluates", "# personalizable entries per character while shared entries remain world conditions.", "# PlayerEvents ON: when any alternate known-item/player-key list is non-empty, Vanilla uses", "# those character conditions instead. With all alternate lists empty, global conditions remain active.", "# playerBasedRaidKeys contains only native unique-key literals; known-item conditions are omitted.", "# Different literals are not aliases. Global keys are grouped case-insensitively, while player keys", "# retain exact casing. aliases records additional loaded spellings of the same global-key identity.", "# defeatPrefabs records only root Character.m_defeatSetGlobalKey setters.", "# Runtime fields and observed Get/Set calls are discoverable; a mod's dormant hard-coded key may", "# appear only after that code path runs. Reload the world after adding or removing content mods.", "#") + newLine;
		}

		private static void MarkDirtyLocked()
		{
			_dirty = true;
			_revision++;
			_updateAfter = Time.realtimeSinceStartup + 1f;
			_retryAfter = 0f;
		}

		private static void WriteIfChanged(string content)
		{
			Directory.CreateDirectory(ProgressionConfigLoader.ConfigDirectory);
			string referencePath = ReferencePath;
			if (!string.Equals(File.Exists(referencePath) ? File.ReadAllText(referencePath) : string.Empty, content, StringComparison.Ordinal))
			{
				File.WriteAllText(referencePath, content, Utf8WithoutBom);
				YouAreNotWorthyPlugin.Log.LogInfo((object)("Updated key reference at " + referencePath + "."));
			}
		}
	}
	internal static class PersonalKeySnapshot
	{
		private const string SnapshotZdoKey = "YNW_PersonalKeys";

		private const int SnapshotMagic = 827805273;

		private const byte SnapshotVersion = 1;

		private const int MaxSnapshotKeys = 4096;

		private const int MaxSnapshotBytes = 1048576;

		internal static void PublishLocal()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && ((Character)localPlayer).IsOwner() && TryGetPlayerZdo(localPlayer, out ZDO zdo))
			{
				List<string> uniqueKeys = localPlayer.GetUniqueKeys();
				uniqueKeys.RemoveAll((string key) => string.IsNullOrWhiteSpace(key));
				uniqueKeys.Sort(StringComparer.Ordinal);
				if (uniqueKeys.Count > 4096)
				{
					YouAreNotWorthyPlugin.Log.LogWarning((object)$"Personal-key snapshot contains {uniqueKeys.Count} keys; only the first {4096} will be published.");
					uniqueKeys.RemoveRange(4096, uniqueKeys.Count - 4096);
				}
				int publishedCount;
				byte[] array = Serialize(uniqueKeys, out publishedCount);
				if (publishedCount < uniqueKeys.Count)
				{
					YouAreNotWorthyPlugin.Log.LogWarning((object)($"Personal-key snapshot exceeded {1048576} bytes; " + $"only the first {publishedCount} of {uniqueKeys.Count} keys will be published."));
				}
				if (!ByteArraysEqual(zdo.GetByteArray("YNW_PersonalKeys", Array.Empty<byte>()), array))
				{
					zdo.Set("YNW_PersonalKeys", array);
				}
			}
		}

		internal static bool TryHas(Player? player, string? key, out bool hasKey)
		{
			hasKey = false;
			if ((Object)(object)player == (Object)null || !ProgressionIndex.TryGetCanonicalPersonalKey(key, out string canonicalKey))
			{
				return false;
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer)
			{
				hasKey = PlayerKeys.HasNativeKey(player, canonicalKey);
				return true;
			}
			if (!TryGetPlayerZdo(player, out ZDO zdo))
			{
				return false;
			}
			return TryHas(zdo, canonicalKey, out hasKey);
		}

		internal static bool TryHas(ZDO? zdo, string? key, out bool hasKey)
		{
			hasKey = false;
			if (zdo == null || !ProgressionIndex.TryGetCanonicalPersonalKey(key, out string canonicalKey))
			{
				return false;
			}
			return TryRead(zdo.GetByteArray("YNW_PersonalKeys", Array.Empty<byte>()), canonicalKey, out hasKey);
		}

		private static byte[] Serialize(List<string> keys, out int publishedCount)
		{
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true))
			{
				binaryWriter.Write(827805273);
				binaryWriter.Write((byte)1);
				long position = memoryStream.Position;
				binaryWriter.Write(0);
				publishedCount = 0;
				foreach (string key in keys)
				{
					long position2 = memoryStream.Position;
					binaryWriter.Write(key);
					binaryWriter.Flush();
					if (memoryStream.Length > 1048576)
					{
						memoryStream.SetLength(position2);
						memoryStream.Position = position2;
						break;
					}
					publishedCount++;
				}
				long position3 = memoryStream.Position;
				memoryStream.Position = position;
				binaryWriter.Write(publishedCount);
				memoryStream.Position = position3;
			}
			return memoryStream.ToArray();
		}

		private static bool TryGetPlayerZdo(Player player, out ZDO zdo)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: 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)
			zdo = null;
			ZDOID zDOID = ((Character)player).GetZDOID();
			ZDOMan instance = ZDOMan.instance;
			if (((ZDOID)(ref zDOID)).IsNone() || instance == null)
			{
				return false;
			}
			zdo = instance.GetZDO(zDOID);
			return zdo != null;
		}

		private static bool TryRead(byte[] payload, string canonicalKey, out bool hasKey)
		{
			hasKey = false;
			if (payload.Length == 0 || payload.Length > 1048576)
			{
				return false;
			}
			try
			{
				using MemoryStream memoryStream = new MemoryStream(payload, writable: false);
				using BinaryReader binaryReader = new BinaryReader(memoryStream, Encoding.UTF8, leaveOpen: false);
				if (binaryReader.ReadInt32() != 827805273 || binaryReader.ReadByte() != 1)
				{
					return false;
				}
				int num = binaryReader.ReadInt32();
				if (num < 0 || num > 4096)
				{
					return false;
				}
				for (int i = 0; i < num; i++)
				{
					if (string.Equals(binaryReader.ReadString(), canonicalKey, StringComparison.OrdinalIgnoreCase))
					{
						hasKey = true;
					}
				}
				return memoryStream.Position == memoryStream.Length;
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static bool ByteArraysEqual(byte[] left, byte[] right)
		{
			if (left.Length != right.Length)
			{
				return false;
			}
			for (int i = 0; i < left.Length; i++)
			{
				if (left[i] != right[i])
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class PlayerKeyCommands
	{
		private enum AdminOperation : byte
		{
			Players = 1,
			List,
			Add,
			Remove
		}

		private enum TargetResult : byte
		{
			Added = 1,
			AlreadyPresent,
			Removed,
			NotPresent,
			InvalidKey,
			NoLocalPlayer,
			CharacterChanged,
			Listed,
			Failed
		}

		private sealed class OnlineTarget
		{
			internal string SteamId { get; }

			internal string PlayerName { get; }

			internal ZDOID CharacterId { get; }

			internal ZRpc? Rpc { get; }

			internal bool IsLocal => Rpc == null;

			internal OnlineTarget(string steamId, string playerName, ZDOID characterId, ZRpc? rpc)
			{
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				SteamId = steamId;
				PlayerName = playerName;
				CharacterId = characterId;
				Rpc = rpc;
			}
		}

		private sealed class PendingRequest
		{
			internal ZRpc? Requester { get; }

			internal ZRpc Target { get; }

			internal AdminOperation Operation { get; }

			internal string SteamId { get; }

			internal string PlayerName { get; }

			internal string Key { get; }

			internal float Deadline { get; }

			internal PendingRequest(ZRpc? requester, ZRpc target, AdminOperation operation, string steamId, string playerName, string key, float deadline)
			{
				Requester = requester;
				Target = target;
				Operation = operation;
				SteamId = steamId;
				PlayerName = playerName;
				Key = key;
				Deadline = deadline;
			}
		}

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

			public static ConsoleOptionsFetcher <>9__20_0;

			public static Predicate<string> <>9__34_0;

			public static Comparison<OnlineTarget> <>9__41_0;

			internal List<string> <RegisterConsoleCommand>b__20_0()
			{
				return TabOptions;
			}

			internal bool <WriteKeyListResponse>b__34_0(string key)
			{
				return string.IsNullOrWhiteSpace(key);
			}

			internal int <BuildOnlinePlayersMessage>b__41_0(OnlineTarget left, OnlineTarget right)
			{
				return string.Compare(left.SteamId, right.SteamId, StringComparison.Ordinal);
			}
		}

		private const string CommandName = "ynw:keys";

		private const string RpcAdminRequest = "YNW_AdminKeyRequest";

		private const string RpcAdminResult = "YNW_AdminKeyResult";

		private const string RpcTargetRequest = "YNW_AdminKeyTargetRequest";

		private const string RpcTargetResult = "YNW_AdminKeyTargetResult";

		private const float RequestTimeoutSeconds = 10f;

		private const int MaxKeyLength = 256;

		private const int MaxListedKeys = 512;

		private const int MaxListCharacters = 16384;

		private const int MaxPendingRequests = 64;

		private const int MaxPendingRequestsPerAdmin = 8;

		private static readonly List<string> TabOptions = new List<string> { "players", "list", "add", "remove" };

		private static readonly Dictionary<long, PendingRequest> PendingRequests = new Dictionary<long, PendingRequest>();

		private static readonly FieldInfo? TerminalCommandsField = typeof(Terminal).GetField("commands", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

		private static ConsoleCommand? _consoleCommand;

		private static long _nextRequestToken;

		internal static void RegisterConsoleCommand()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			if (_consoleCommand != null)
			{
				return;
			}
			Dictionary<string, ConsoleCommand> terminalCommands = GetTerminalCommands();
			if (terminalCommands == null)
			{
				YouAreNotWorthyPlugin.Log.LogWarning((object)"Could not inspect Valheim's console-command registry; 'ynw:keys' was not registered.");
				return;
			}
			if (terminalCommands.ContainsKey("ynw:keys"))
			{
				YouAreNotWorthyPlugin.Log.LogWarning((object)"Could not register 'ynw:keys' because another console command already uses that name.");
				return;
			}
			ConsoleEvent val = HandleConsoleCommand;
			object obj = <>c.<>9__20_0;
			if (obj == null)
			{
				ConsoleOptionsFetcher val2 = () => TabOptions;
				<>c.<>9__20_0 = val2;
				obj = (object)val2;
			}
			_consoleCommand = new ConsoleCommand("ynw:keys", "Manage native personal keys for an online Steam player. Usage: ynw:keys players|list|add|remove", val, false, true, false, false, false, (ConsoleOptionsFetcher)obj, false, false, false);
		}

		internal static void RegisterPeer(ZNet net, ZNetPeer peer)
		{
			try
			{
				if (net.IsServer())
				{
					peer.m_rpc.Register<ZPackage>("YNW_AdminKeyRequest", (Action<ZRpc, ZPackage>)RPC_AdminRequest);
					peer.m_rpc.Register<ZPackage>("YNW_AdminKeyTargetResult", (Action<ZRpc, ZPackage>)RPC_TargetResult);
				}
				else
				{
					peer.m_rpc.Register<string>("YNW_AdminKeyResult", (Action<ZRpc, string>)RPC_AdminResult);
					peer.m_rpc.Register<ZPackage>("YNW_AdminKeyTargetRequest", (Action<ZRpc, ZPackage>)RPC_TargetRequest);
				}
			}
			catch (Exception arg)
			{
				YouAreNotWorthyPlugin.Log.LogError((object)$"Failed to register YNW admin-key RPCs: {arg}");
			}
		}

		internal static void Update()
		{
			if (PendingRequests.Count == 0)
			{
				return;
			}
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				PendingRequests.Clear();
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			List<long> list = null;
			foreach (KeyValuePair<long, PendingRequest> pendingRequest in PendingRequests)
			{
				if (!(realtimeSinceStartup < pendingRequest.Value.Deadline))
				{
					if (list == null)
					{
						list = new List<long>();
					}
					list.Add(pendingRequest.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (long item in list)
			{
				if (PendingRequests.TryGetValue(item, out PendingRequest value))
				{
					PendingRequests.Remove(item);
					SendAdminResult(value.Requester, "Timed out while applying " + DescribeOperation(value.Operation) + " for " + FormatTarget(value.PlayerName, value.SteamId) + ".");
				}
			}
		}

		internal static void Shutdown()
		{
			PendingRequests.Clear();
			Dictionary<string, ConsoleCommand> terminalCommands = GetTerminalCommands();
			if (_consoleCommand != null && terminalCommands != null && terminalCommands.TryGetValue("ynw:keys", out var value) && value == _consoleCommand)
			{
				terminalCommands.Remove("ynw:keys");
			}
			_consoleCommand = null;
		}

		private static Dictionary<string, ConsoleCommand>? GetTerminalCommands()
		{
			try
			{
				return TerminalCommandsField?.GetValue(null) as Dictionary<string, ConsoleCommand>;
			}
			catch (Exception)
			{
				return null;
			}
		}

		private static void HandleConsoleCommand(ConsoleEventArgs args)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString("YNW player-key commands require an active server session.");
				}
				return;
			}
			if ((int)ZNet.m_onlineBackend != 0)
			{
				Terminal context2 = args.Context;
				if (context2 != null)
				{
					context2.AddString("YNW player-key commands support Steam networking only.");
				}
				return;
			}
			if (!ZNet.instance.IsServer() && !YouAreNotWorthyPlugin.IsLocalAdmin)
			{
				Terminal context3 = args.Context;
				if (context3 != null)
				{
					context3.AddString("You are not an admin on this server.");
				}
				return;
			}
			if (!TryParseCommand(args, out AdminOperation operation, out string steamId, out string key))
			{
				PrintUsage(args.Context);
				return;
			}
			ZPackage val = new ZPackage();
			val.Write((byte)operation);
			val.Write(steamId);
			val.Write(key);
			if (ZNet.instance.IsServer())
			{
				val.SetPos(0);
				HandleAdminRequest(null, val);
				return;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer == null || !serverPeer.m_rpc.IsConnected())
			{
				Terminal context4 = args.Context;
				if (context4 != null)
				{
					context4.AddString("The server connection is not ready.");
				}
			}
			else
			{
				serverPeer.m_rpc.Invoke("YNW_AdminKeyRequest", new object[1] { val });
			}
		}

		private static bool TryParseCommand(ConsoleEventArgs args, out AdminOperation operation, out string steamId, out string key)
		{
			operation = (AdminOperation)0;
			steamId = string.Empty;
			key = string.Empty;
			string a = ((args.Length >= 2) ? (args[1] ?? string.Empty).Trim() : string.Empty);
			if (string.Equals(a, "players", StringComparison.OrdinalIgnoreCase))
			{
				operation = AdminOperation.Players;
				return args.Length == 2;
			}
			if (string.Equals(a, "list", StringComparison.OrdinalIgnoreCase))
			{
				operation = AdminOperation.List;
				if (args.Length == 3)
				{
					return TryNormalizeSteamId(args[2], out steamId);
				}
				return false;
			}
			if (string.Equals(a, "add", StringComparison.OrdinalIgnoreCase))
			{
				operation = AdminOperation.Add;
			}
			else
			{
				if (!string.Equals(a, "remove", StringComparison.OrdinalIgnoreCase))
				{
					return false;
				}
				operation = AdminOperation.Remove;
			}
			if (args.Length == 4 && TryNormalizeSteamId(args[2], out steamId))
			{
				return TryNormalizeKey(args[3], out key);
			}
			return false;
		}

		private static void PrintUsage(Terminal? terminal)
		{
			if (terminal != null)
			{
				terminal.AddString("YNW online personal-key commands:");
			}
			if (terminal != null)
			{
				terminal.AddString("  ynw:keys players");
			}
			if (terminal != null)
			{
				terminal.AddString("  ynw:keys list <steamid64>");
			}
			if (terminal != null)
			{
				terminal.AddString("  ynw:keys add <steamid64> <key>");
			}
			if (terminal != null)
			{
				terminal.AddString("  ynw:keys remove <steamid64> <key>");
			}
		}

		private static void RPC_AdminRequest(ZRpc requester, ZPackage request)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				if (!IsRemoteAdmin(requester))
				{
					YouAreNotWorthyPlugin.Log.LogWarning((object)("Rejected YNW player-key command from non-admin peer '" + GetPeerHostName(requester) + "'."));
					SendAdminResult(requester, "You are not an admin on this server.");
				}
				else
				{
					HandleAdminRequest(requester, request);
				}
			}
		}

		private static void HandleAdminRequest(ZRpc? requester, ZPackage request)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((int)ZNet.m_onlineBackend != 0)
				{
					SendAdminResult(requester, "YNW player-key commands support Steam networking only.");
					return;
				}
				AdminOperation adminOperation = (AdminOperation)request.ReadByte();
				string value = request.ReadString();
				string value2 = request.ReadString();
				bool flag;
				switch (adminOperation)
				{
				case AdminOperation.Players:
					SendAdminResult(requester, BuildOnlinePlayersMessage());
					return;
				case AdminOperation.List:
				case AdminOperation.Add:
				case AdminOperation.Remove:
					flag = true;
					break;
				default:
					flag = false;
					break;
				}
				if (!flag || !TryNormalizeSteamId(value, out string steamId))
				{
					SendAdminResult(requester, "Invalid YNW player-key command request.");
					return;
				}
				string key = string.Empty;
				flag = adminOperation - 3 <= AdminOperation.Players;
				if (flag && !TryNormalizeKey(value2, out key))
				{
					SendAdminResult(requester, "The personal key is invalid or is shared world state.");
					return;
				}
				List<OnlineTarget> list = GetOnlineTargets().FindAll((OnlineTarget target) => string.Equals(target.SteamId, steamId, StringComparison.Ordinal));
				if (list.Count == 0)
				{
					SendAdminResult(requester, "Steam player " + steamId + " is not online with a loaded character.");
					return;
				}
				if (list.Count > 1)
				{
					SendAdminResult(requester, "Steam player " + steamId + " resolved to more than one connection; no key was changed.");
					return;
				}
				OnlineTarget onlineTarget = list[0];
				LogAdminRequest(requester, adminOperation, onlineTarget, key);
				if (onlineTarget.IsLocal)
				{
					SendLocalTargetResult(requester, adminOperation, onlineTarget, key);
				}
				else
				{
					SendTargetRequest(requester, adminOperation, onlineTarget, key);
				}
			}
			catch (Exception arg)
			{
				YouAreNotWorthyPlugin.Log.LogError((object)$"Failed to handle a YNW player-key admin request: {arg}");
				SendAdminResult(requester, "The YNW player-key request failed on the server. Check the server log.");
			}
		}

		private static void SendTargetRequest(ZRpc? requester, AdminOperation operation, OnlineTarget target, string key)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			if (PendingRequests.Count >= 64 || CountPendingRequests(requester) >= 8)
			{
				SendAdminResult(requester, "Too many YNW player-key requests are awaiting target responses. Try again shortly.");
				return;
			}
			ZRpc rpc = target.Rpc;
			if (!rpc.IsConnected())
			{
				SendAdminResult(requester, FormatTarget(target.PlayerName, target.SteamId) + " disconnected before the request was sent.");
				return;
			}
			long num = NextRequestToken();
			PendingRequests[num] = new PendingRequest(requester, rpc, operation, target.SteamId, target.PlayerName, key, Time.realtimeSinceStartup + 10f);
			ZPackage val = new ZPackage();
			val.Write(num);
			val.Write((byte)operation);
			val.Write(target.CharacterId);
			val.Write(key);
			try
			{
				rpc.Invoke("YNW_AdminKeyTargetRequest", new object[1] { val });
			}
			catch (Exception arg)
			{
				PendingRequests.Remove(num);
				YouAreNotWorthyPlugin.Log.LogWarning((object)$"Failed to send YNW player-key request to {FormatTarget(target.PlayerName, target.SteamId)}: {arg}");
				SendAdminResult(requester, "Could not contact " + FormatTarget(target.PlayerName, target.SteamId) + ".");
			}
		}

		private static void RPC_TargetRequest(ZRpc server, ZPackage request)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!IsServerConnection(server))
			{
				YouAreNotWorthyPlugin.Log.LogWarning((object)"Ignored a YNW player-key target request from a non-server connection.");
				return;
			}
			long num = 0L;
			ZPackage val;
			try
			{
				num = request.ReadLong();
				AdminOperation operation = (AdminOperation)request.ReadByte();
				ZDOID expectedCharacter = request.ReadZDOID();
				string requestedKey = request.ReadString();
				val = BuildTargetResponse(num, operation, expectedCharacter, requestedKey);
			}
			catch (Exception arg)
			{
				YouAreNotWorthyPlugin.Log.LogError((object)$"Failed to apply a YNW player-key target request: {arg}");
				val = new ZPackage();
				val.Write(num);
				val.Write((byte)9);
			}
			server.Invoke("YNW_AdminKeyTargetResult", new object[1] { val });
		}

		private static ZPackage BuildTargetResponse(long token, AdminOperation operation, ZDOID expectedCharacter, string requestedKey)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			ZPackage val = new ZPackage();
			val.Write(token);
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				val.Write((byte)6);
			}
			else if (((Character)localPlayer).GetZDOID() != expectedCharacter)
			{
				val.Write((byte)7);
			}
			else if (operation == AdminOperation.List)
			{
				WriteKeyListResponse(val, localPlayer);
			}
			else if (operation - 3 <= AdminOperation.Players)
			{
				WriteMutationResponse(val, requestedKey, operation == AdminOperation.Add);
			}
			else
			{
				val.Write((byte)9);
			}
			return val;
		}

		private static void WriteMutationResponse(ZPackage response, string requestedKey, bool add)
		{
			if (!TryNormalizeKey(requestedKey, out string key))
			{
				response.Write((byte)5);
				return;
			}
			TargetResult targetResult = PlayerKeys.MutateLocal(key, add) switch
			{
				PersonalKeyMutationResult.Added => TargetResult.Added, 
				PersonalKeyMutationResult.AlreadyPresent => TargetResult.AlreadyPresent, 
				PersonalKeyMutationResult.Removed => TargetResult.Removed, 
				PersonalKeyMutationResult.NotPresent => TargetResult.NotPresent, 
				PersonalKeyMutationResult.InvalidKey => TargetResult.InvalidKey, 
				PersonalKeyMutationResult.NoLocalPlayer => TargetResult.NoLocalPlayer, 
				_ => TargetResult.Failed, 
			};
			response.Write((byte)targetResult);
			if ((targetResult == TargetResult.Added || targetResult == TargetResult.Removed) ? true : false)
			{
				response.Write(TrySaveLocalProfile());
			}
		}

		private static void WriteKeyListResponse(ZPackage response, Player player)
		{
			List<string> list = new List<string>(player.GetUniqueKeys());
			list.RemoveAll((string key) => string.IsNullOrWhiteSpace(key));
			list.Sort(StringComparer.OrdinalIgnoreCase);
			int num = Math.Min(list.Count, 512);
			response.Write((byte)8);
			response.Write(list.Count);
			response.Write(num);
			for (int num2 = 0; num2 < num; num2++)
			{
				response.Write(SanitizeForConsole(list[num2], 256));
			}
		}

		private static void RPC_TargetResult(ZRpc target, ZPackage response)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			long key;
			try
			{
				key = response.ReadLong();
			}
			catch (Exception ex)
			{
				YouAreNotWorthyPlugin.Log.LogWarning((object)("Ignored a malformed YNW player-key result: " + ex.Message));
				return;
			}
			if (!PendingRequests.TryGetValue(key, out PendingRequest value))
			{
				YouAreNotWorthyPlugin.Log.LogWarning((object)"Ignored an unknown or expired YNW player-key result.");
				return;
			}
			if (value.Target != target)
			{
				YouAreNotWorthyPlugin.Log.LogWarning((object)"Ignored a YNW player-key result from the wrong peer.");
				return;
			}
			PendingRequests.Remove(key);
			try
			{
				string message = ReadTargetResultMessage(value.Operation, value.SteamId, value.PlayerName, value.Key, response);
				SendAdminResult(value.Requester, message);
			}
			catch (Exception arg)
			{
				YouAreNotWorthyPlugin.Log.LogWarning((object)$"Failed to read a YNW player-key result: {arg}");
				SendAdminResult(value.Requester, "The target returned an invalid YNW player-key result.");
			}
		}

		private static string ReadTargetResultMessage(AdminOperation operation, string steamId, string playerName, string key, ZPackage response)
		{
			TargetResult targetResult = (TargetResult)response.ReadByte();
			if (!IsCompatibleTargetResult(operation, targetResult))
			{
				throw new InvalidOperationException($"Target result '{targetResult}' is not valid for operation '{operation}'.");
			}
			return BuildTargetResultMessage(steamId, playerName, key, targetResult, response);
		}

		private static string BuildTargetResultMessage(string steamId, string playerName, string key, TargetResult result, ZPackage response)
		{
			string text = FormatTarget(playerName, steamId);
			return result switch
			{
				TargetResult.Added => AppendSaveWarning("Added personal key '" + key + "' to " + text + ".", response.ReadBool()), 
				TargetResult.AlreadyPresent => text + " already has personal key '" + key + "'.", 
				TargetResult.Removed => AppendSaveWarning("Removed personal key '" + key + "' from " + text + ".", response.ReadBool()), 
				TargetResult.NotPresent => text + " does not have personal key '" + key + "'.", 
				TargetResult.InvalidKey => "Personal key '" + key + "' was rejected by " + text + ".", 
				TargetResult.NoLocalPlayer => text + " no longer has a loaded local character.", 
				TargetResult.CharacterChanged => text + " changed character before the key operation completed.", 
				TargetResult.Listed => ReadKeyListMessage(playerName, steamId, response), 
				_ => "The personal-key operation failed for " + text + ".", 
			};
		}

		private static bool IsCompatibleTargetResult(AdminOperation operation, TargetResult result)
		{
			if ((result - 6 <= TargetResult.Added || result == TargetResult.Failed) ? true : false)
			{
				return true;
			}
			return operation switch
			{
				AdminOperation.List => result == TargetResult.Listed, 
				AdminOperation.Add => (result - 1 <= TargetResult.Added || result == TargetResult.InvalidKey) ? true : false, 
				AdminOperation.Remove => result - 3 <= TargetResult.AlreadyPresent, 
				_ => false, 
			};
		}

		private static string ReadKeyListMessage(string playerName, string steamId, ZPackage response)
		{
			int num = response.ReadInt();
			int num2 = response.ReadInt();
			if (num < 0 || num2 < 0 || num2 > num || num2 > 512)
			{
				throw new InvalidOperationException("The target returned an invalid personal-key count.");
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("Personal keys for ").Append(FormatTarget(playerName, steamId));
			stringBuilder.Append(" (").Append(num.ToString(CultureInfo.InvariantCulture)).AppendLine("):");
			int num3 = 0;
			for (int i = 0; i < num2; i++)
			{
				string text = SanitizeForConsole(response.ReadString(), 256);
				if (stringBuilder.Length + text.Length + 4 <= 16384)
				{
					stringBuilder.Append("  ").AppendLine(text);
					num3++;
				}
			}
			if (num3 < num)
			{
				stringBuilder.Append("  ... ").Append((num - num3).ToString(CultureInfo.InvariantCulture)).Append(" more key(s) omitted");
			}
			return stringBuilder.ToString().TrimEnd(Array.Empty<char>());
		}

		private static void SendLocalTargetResult(ZRpc? requester, AdminOperation operation, OnlineTarget target, string key)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			ZPackage val = BuildTargetResponse(0L, operation, target.CharacterId, key);
			val.SetPos(0);
			try
			{
				val.ReadLong();
				string message = ReadTargetResultMessage(operation, target.SteamId, target.PlayerName, key, val);
				SendAdminResult(requester, message);
			}
			catch (Exception arg)
			{
				YouAreNotWorthyPlugin.Log.LogWarning((object)$"Failed to read a YNW player-key result: {arg}");
				SendAdminResult(requester, "The target returned an invalid YNW player-key result.");
			}
		}

		private static string BuildOnlinePlayersMessage()
		{
			List<OnlineTarget> onlineTargets = GetOnlineTargets();
			onlineTargets.Sort((OnlineTarget left, OnlineTarget right) => string.Compare(left.SteamId, right.SteamId, StringComparison.Ordinal));
			if (onlineTargets.Count == 0)
			{
				return "No Steam players with loaded characters are online.";
			}
			StringBuilder stringBuilder = new StringBuilder("Online YNW player-key targets:");
			foreach (OnlineTarget item in onlineTargets)
			{
				stringBuilder.AppendLine().Append("  ").Append(SanitizeForConsole(item.PlayerName, 64))
					.Append(" | ")
					.Append(item.SteamId);
			}
			return stringBuilder.ToString();
		}

		private static List<OnlineTarget> GetOnlineTargets()
		{
			//IL_0038: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			List<OnlineTarget> list = new List<OnlineTarget>();
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || !instance.IsServer())
			{
				return list;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && TryGetLocalSteamId(out string steamId))
			{
				ZDOID zDOID = ((Character)localPlayer).GetZDOID();
				if (!((ZDOID)(ref zDOID)).IsNone())
				{
					list.Add(new OnlineTarget(steamId, localPlayer.GetPlayerName(), ((Character)localPlayer).GetZDOID(), null));
				}
			}
			foreach (ZNetPeer connectedPeer in instance.GetConnectedPeers())
			{
				if (connectedPeer.IsReady() && !((ZDOID)(ref connectedPeer.m_characterID)).IsNone() && TryNormalizeConnectedSteamId(connectedPeer.m_socket.GetHostName(), out string steamId2) && !((Object)(object)PlayerKeys.FindPlayerByOwner(connectedPeer.m_uid) == (Object)null))
				{
					list.Add(new OnlineTarget(steamId2, connectedPeer.m_playerName, connectedPeer.m_characterID, connectedPeer.m_rpc));
				}
			}
			return list;
		}

		private static bool TryGetLocalSteamId(out string steamId)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			steamId = string.Empty;
			try
			{
				return SteamUser.BLoggedOn() && TryNormalizeSteamId(((object)SteamUser.GetSteamID()/*cast due to .constrained prefix*/).ToString(), out steamId);
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static bool TryNormalizeConnectedSteamId(string? value, out string steamId)
		{
			string text = (value ?? string.Empty).Trim();
			if (text.StartsWith("Steam_", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring("Steam_".Length);
			}
			return TryNormalizeSteamId(text, out steamId);
		}

		private static bool TryNormalizeSteamId(string? value, out string steamId)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			string text = (value ?? string.Empty).Trim();
			if (text.Length == 17 && ulong.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out var result) && result != 0L)
			{
				CSteamID val = new CSteamID(result);
				if (((CSteamID)(ref val)).IsValid())
				{
					steamId = result.ToString(CultureInfo.InvariantCulture);
					return true;
				}
			}
			steamId = string.Empty;
			return false;
		}

		private static bool TryNormalizeKey(string? value, out string key)
		{
			string text = (value ?? string.Empty).Trim();
			if (text.Length == 0 || text.Length > 256)
			{
				key = string.Empty;
				return false;
			}
			string text2 = text;
			foreach (char c in text2)
			{
				if (char.IsControl(c) || char.IsWhiteSpace(c))
				{
					key = string.Empty;
					return false;
				}
			}
			return ProgressionIndex.TryResolvePersonalKey(text, out key);
		}

		private static bool TrySaveLocalProfile()
		{
			try
			{
				if ((Object)(object)Game.instance == (Object)null)
				{
					return false;
				}
				Game.instance.SavePlayerProfile(false);
				return true;
			}
			catch (Exception arg)
			{
				YouAreNotWorthyPlugin.Log.LogError((object)$"Failed to save the local character after an admin key change: {arg}");
				return false;
			}
		}

		private static bool IsRemoteAdmin(ZRpc requester)
		{
			try
			{
				ZNet instance = ZNet.instance;
				ZNetPeer val = ((instance != null) ? instance.GetPeer(requester) : null);
				return (Object)(object)instance != (Object)null && instance.IsServer() && val != null && val.IsReady() && val.m_rpc == requester && instance.IsAdmin(requester.GetSocket().GetHostName());
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static bool IsServerConnection(ZRpc rpc)
		{
			try
			{
				ZNet instance = ZNet.instance;
				ZNetPeer val = ((instance != null) ? instance.GetServerPeer() : null);
				return val != null && val.m_server && val.m_rpc == rpc;
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static void RPC_AdminResult(ZRpc server, string message)
		{
			if (!IsServerConnection(server))
			{
				YouAreNotWorthyPlugin.Log.LogWarning((object)"Ignored a YNW admin-key result from a non-server connection.");
			}
			else
			{
				PrintConsole(message);
			}
		}

		private static void SendAdminResult(ZRpc? requester, string message)
		{
			string text = SanitizeMultilineForConsole(message, 16384);
			YouAreNotWorthyPlugin.Log.LogInfo((object)text);
			if (requester == null)
			{
				PrintConsole(text);
				return;
			}
			try
			{
				requester.Invoke("YNW_AdminKeyResult", new object[1] { text });
			}
			catch (Exception ex)
			{
				YouAreNotWorthyPlugin.Log.LogWarning((object)("Failed to return a YNW admin-key result: " + ex.Message));
			}
		}

		private static void PrintConsole(string message)
		{
			if ((Object)(object)Console.instance != (Object)null)
			{
				((Terminal)Console.instance).AddString(message);
			}
		}

		private static void LogAdminRequest(ZRpc? requester, AdminOperation operation, OnlineTarget target, string key)
		{
			string text = ((requester == null) ? "local-server" : GetPeerHostName(requester));
			string text2 = ((key.Length == 0) ? string.Empty : (", key='" + key + "'"));
			YouAreNotWorthyPlugin.Log.LogInfo((object)("Admin '" + text + "' requested " + DescribeOperation(operation) + " for " + FormatTarget(target.PlayerName, target.SteamId) + text2 + "."));
		}

		private static string GetPeerHostName(ZRpc rpc)
		{
			try
			{
				return SanitizeForConsole(rpc.GetSocket().GetHostName(), 128);
			}
			catch (Exception)
			{
				return "unknown";
			}
		}

		private static string FormatTarget(string playerName, string steamId)
		{
			return SanitizeForCon