Decompiled source of ContentWarningArchipelago v1.0.3

ContentWarningArchipelago.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading.Tasks;
using Archipelago.MultiClient.Net;
using Archipelago.MultiClient.Net.Enums;
using Archipelago.MultiClient.Net.Helpers;
using Archipelago.MultiClient.Net.Models;
using Archipelago.MultiClient.Net.Packets;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ContentWarningArchipelago.Core;
using ContentWarningArchipelago.Data;
using ContentWarningArchipelago.Patches;
using ContentWarningArchipelago.UI;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MyceliumNetworking;
using Newtonsoft.Json;
using Photon.Pun;
using Photon.Realtime;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using Zorro.Core;
using pworld.Scripts.Extensions;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ContentWarningArchipelago")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Archipelago Randomizer for Content Warning")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+7a8fe43c6a672dcf7da9254c9a58671e13d29218")]
[assembly: AssemblyProduct("Content Warning Archipelago")]
[assembly: AssemblyTitle("ContentWarningArchipelago")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ContentWarningArchipelago
{
	[BepInPlugin("automagic.cw-archipelago", "Content Warning Archipelago", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		public static ArchipelagoClient connection;

		internal const uint MyceliumModId = 2718281828u;

		public static APConfig APConfig { get; private set; }

		public static string apAddress => APConfig?.address.Value ?? "archipelago.gg";

		public static int apPort => APConfig?.port.Value ?? 38281;

		public static string apPassword => APConfig?.password.Value ?? "";

		public static string apSlot => APConfig?.slot.Value ?? "";

		public static Plugin Instance { get; private set; }

		public static bool isConnecting { get; private set; }

		private void Awake()
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			Logger.LogInfo((object)"[CWArch] automagic.cw-archipelago v0.1.0 loading…");
			APConfig = new APConfig(((BaseUnityPlugin)this).Config);
			Logger.LogInfo((object)$"[CWArch] Config loaded. AP address: {apAddress}:{APConfig.port.Value}");
			ItemData.Init();
			LocationData.Init();
			new Harmony("automagic.cw-archipelago").PatchAll();
			MyceliumNetwork.RegisterNetworkObject((object)this, 2718281828u, 0);
			Logger.LogInfo((object)"[CWArch] Harmony patches applied.");
		}

		private void Start()
		{
			connection = new ArchipelagoClient();
			Logger.LogInfo((object)"[CWArch] ArchipelagoClient created. Waiting for connection.");
		}

		private void OnDestroy()
		{
			MyceliumNetwork.DeregisterNetworkObject((object)this, 2718281828u, 0);
		}

		private void Update()
		{
			if (connection.connected)
			{
				connection.checkItemsReceived?.MoveNext();
				connection.incomingItemHandler?.MoveNext();
				connection.outgoingCheckHandler?.MoveNext();
			}
		}

		[CustomRPC]
		internal void LocationFound(string locName, RPCInfo info)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (info.SenderSteamID != SteamUser.GetSteamID())
			{
				Logger.LogDebug((object)("[CWArch] Received remote location-found notification: '" + locName + "'"));
				APNotificationUI.ShowLocationFound(locName);
			}
		}

		public static async void Connect()
		{
			if (isConnecting || connection.connected)
			{
				return;
			}
			isConnecting = true;
			try
			{
				await connection.TryConnect(apAddress, apPort, apPassword, apSlot);
			}
			finally
			{
				isConnecting = false;
			}
		}

		public static void Disconnect()
		{
			connection.TryDisconnect();
		}

		public static void SendCheck(long locationId)
		{
			connection.ActivateCheck(locationId);
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "automagic.cw-archipelago";

		public const string PLUGIN_NAME = "Content Warning Archipelago";

		public const string PLUGIN_VERSION = "0.1.0";
	}
}
namespace ContentWarningArchipelago.UI
{
	public class APConnectionPanelUI : MonoBehaviour
	{
		private TMP_InputField _addressField;

		private TMP_InputField _slotField;

		private TMP_InputField _passwordField;

		private Button _connectButton;

		private Button _disconnectButton;

		private TextMeshProUGUI _statusLabel;

		private static readonly Color ColDisconnected = new Color(0.9f, 0.22f, 0.22f);

		private static readonly Color ColConnecting = new Color(1f, 0.85f, 0.1f);

		private static readonly Color ColConnected = new Color(0.2f, 0.88f, 0.2f);

		private void Awake()
		{
			BuildUI();
			PopulateFromConfig();
		}

		private void Update()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if (_statusLabel != null)
			{
				if (Plugin.isConnecting)
				{
					((TMP_Text)_statusLabel).text = "○  Connecting…";
					((Graphic)_statusLabel).color = ColConnecting;
				}
				else if (Plugin.connection != null && Plugin.connection.connected)
				{
					((TMP_Text)_statusLabel).text = "●  Connected!";
					((Graphic)_statusLabel).color = ColConnected;
				}
				else
				{
					((TMP_Text)_statusLabel).text = "●  Disconnected";
					((Graphic)_statusLabel).color = ColDisconnected;
				}
			}
		}

		public void PopulateFromConfig()
		{
			if (Plugin.APConfig != null)
			{
				if (_addressField != null)
				{
					_addressField.text = $"{Plugin.APConfig.address.Value}:{Plugin.APConfig.port.Value}";
				}
				if (_slotField != null)
				{
					_slotField.text = Plugin.APConfig.slot.Value;
				}
				if (_passwordField != null)
				{
					_passwordField.text = Plugin.APConfig.password.Value;
				}
			}
		}

		private void OnAddressChanged(string value)
		{
			if (Plugin.APConfig == null)
			{
				return;
			}
			int num = value.LastIndexOf(':');
			if (num > 0 && num < value.Length - 1)
			{
				string value2 = value.Substring(0, num);
				string s = value.Substring(num + 1);
				Plugin.APConfig.address.Value = value2;
				if (int.TryParse(s, out var result) && result > 0 && result <= 65535)
				{
					Plugin.APConfig.port.Value = result;
				}
			}
			else if (num < 0)
			{
				Plugin.APConfig.address.Value = value.Trim();
			}
			Plugin.Logger.LogDebug((object)$"[APPanel] Address updated: {Plugin.APConfig.address.Value}:{Plugin.APConfig.port.Value}");
		}

		private void OnSlotChanged(string value)
		{
			if (Plugin.APConfig != null)
			{
				Plugin.APConfig.slot.Value = value;
				Plugin.Logger.LogDebug((object)("[APPanel] Slot updated: " + value));
			}
		}

		private void OnPasswordChanged(string value)
		{
			if (Plugin.APConfig != null)
			{
				Plugin.APConfig.password.Value = value;
				Plugin.Logger.LogDebug((object)"[APPanel] Password updated.");
			}
		}

		private void OnConnectClicked()
		{
			if (!Plugin.isConnecting)
			{
				ArchipelagoClient connection = Plugin.connection;
				if (connection == null || !connection.connected)
				{
					Plugin.Logger.LogInfo((object)"[APPanel] Connect button clicked.");
					Plugin.Connect();
				}
			}
		}

		private void OnDisconnectClicked()
		{
			Plugin.Logger.LogInfo((object)"[APPanel] Disconnect button clicked.");
			Plugin.Disconnect();
		}

		private void BuildUI()
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			if (((Component)this).gameObject.GetComponent<RectTransform>() == null)
			{
				((Component)this).gameObject.AddComponent<RectTransform>();
			}
			ContentSizeFitter obj = ((Component)this).gameObject.AddComponent<ContentSizeFitter>();
			obj.verticalFit = (FitMode)2;
			obj.horizontalFit = (FitMode)0;
			VerticalLayoutGroup obj2 = ((Component)this).gameObject.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj2).padding = new RectOffset(10, 10, 8, 8);
			((HorizontalOrVerticalLayoutGroup)obj2).spacing = 5f;
			((LayoutGroup)obj2).childAlignment = (TextAnchor)0;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandHeight = false;
			((Graphic)((Component)this).gameObject.AddComponent<Image>()).color = new Color(0.05f, 0.05f, 0.05f, 0.72f);
			CreateLabel(((Component)this).transform, "── Archipelago Connection ──", 16f, (FontStyles)1, (TextAlignmentOptions)514, 26f, new Color(1f, 0.92f, 0.016f));
			_addressField = CreateLabeledField(((Component)this).transform, "Address : Port", $"{Plugin.apAddress}:{Plugin.apPort}", isPassword: false);
			_slotField = CreateLabeledField(((Component)this).transform, "Slot Name", Plugin.apSlot, isPassword: false);
			_passwordField = CreateLabeledField(((Component)this).transform, "Password", Plugin.apPassword, isPassword: true);
			((UnityEvent<string>)(object)_addressField.onEndEdit).AddListener((UnityAction<string>)OnAddressChanged);
			((UnityEvent<string>)(object)_slotField.onEndEdit).AddListener((UnityAction<string>)OnSlotChanged);
			((UnityEvent<string>)(object)_passwordField.onEndEdit).AddListener((UnityAction<string>)OnPasswordChanged);
			CreateButtonRow(((Component)this).transform);
		}

		private static TMP_InputField CreateLabeledField(Transform parent, string labelText, string initialValue, bool isPassword)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("APRow_" + labelText);
			val.transform.SetParent(parent, false);
			val.AddComponent<LayoutElement>().preferredHeight = 28f;
			HorizontalLayoutGroup obj = val.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).spacing = 6f;
			((LayoutGroup)obj).childAlignment = (TextAnchor)3;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			GameObject val2 = new GameObject("Label");
			val2.transform.SetParent(val.transform, false);
			LayoutElement obj2 = val2.AddComponent<LayoutElement>();
			obj2.preferredWidth = 110f;
			obj2.preferredHeight = 28f;
			TextMeshProUGUI obj3 = val2.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj3).text = labelText;
			((TMP_Text)obj3).fontSize = 13f;
			((TMP_Text)obj3).alignment = (TextAlignmentOptions)4100;
			((Graphic)obj3).color = new Color(0.85f, 0.85f, 0.85f);
			TMP_InputField val3 = CreateInputField(val.transform, labelText, isPassword);
			LayoutElement obj4 = ((Component)val3).gameObject.GetComponent<LayoutElement>() ?? ((Component)val3).gameObject.AddComponent<LayoutElement>();
			obj4.flexibleWidth = 1f;
			obj4.preferredHeight = 28f;
			val3.text = initialValue;
			return val3;
		}

		private void CreateButtonRow(Transform parent)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Expected O, but got Unknown
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Expected O, but got Unknown
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("APButtonRow");
			val.transform.SetParent(parent, false);
			val.AddComponent<LayoutElement>().preferredHeight = 32f;
			HorizontalLayoutGroup obj = val.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).spacing = 8f;
			((LayoutGroup)obj).childAlignment = (TextAnchor)3;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			_connectButton = CreateButton(val.transform, "Connect", new Color(0.16f, 0.56f, 0.16f), 88f);
			_disconnectButton = CreateButton(val.transform, "Disconnect", new Color(0.5f, 0.13f, 0.13f), 100f);
			((UnityEvent)_connectButton.onClick).AddListener(new UnityAction(OnConnectClicked));
			((UnityEvent)_disconnectButton.onClick).AddListener(new UnityAction(OnDisconnectClicked));
			GameObject val2 = new GameObject("StatusLabel");
			val2.transform.SetParent(val.transform, false);
			LayoutElement obj2 = val2.AddComponent<LayoutElement>();
			obj2.flexibleWidth = 1f;
			obj2.preferredHeight = 32f;
			_statusLabel = val2.AddComponent<TextMeshProUGUI>();
			((TMP_Text)_statusLabel).fontSize = 13f;
			((TMP_Text)_statusLabel).alignment = (TextAlignmentOptions)4097;
			((TMP_Text)_statusLabel).text = "●  Disconnected";
			((Graphic)_statusLabel).color = ColDisconnected;
		}

		private static TextMeshProUGUI CreateLabel(Transform parent, string text, float fontSize, FontStyles style, TextAlignmentOptions alignment, float height, Color color = default(Color))
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("APLabel");
			val.transform.SetParent(parent, false);
			val.AddComponent<LayoutElement>().preferredHeight = height;
			TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj).text = text;
			((TMP_Text)obj).fontSize = fontSize;
			((TMP_Text)obj).fontStyle = style;
			((TMP_Text)obj).alignment = alignment;
			((Graphic)obj).color = ((color == default(Color)) ? Color.white : color);
			return obj;
		}

		private static TMP_InputField CreateInputField(Transform parent, string placeholder, bool isPassword)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("InputField");
			val.transform.SetParent(parent, false);
			val.AddComponent<RectTransform>();
			((Graphic)val.AddComponent<Image>()).color = new Color(0.12f, 0.12f, 0.12f, 0.95f);
			TMP_InputField val2 = val.AddComponent<TMP_InputField>();
			GameObject val3 = new GameObject("Text Area");
			val3.transform.SetParent(val.transform, false);
			RectTransform val4 = val3.AddComponent<RectTransform>();
			val4.anchorMin = Vector2.zero;
			val4.anchorMax = Vector2.one;
			val4.offsetMin = new Vector2(5f, 2f);
			val4.offsetMax = new Vector2(-5f, -2f);
			val3.AddComponent<RectMask2D>();
			GameObject val5 = new GameObject("Placeholder");
			val5.transform.SetParent(val3.transform, false);
			RectTransform obj = val5.AddComponent<RectTransform>();
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.offsetMin = Vector2.zero;
			obj.offsetMax = Vector2.zero;
			TextMeshProUGUI val6 = val5.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val6).text = placeholder;
			((TMP_Text)val6).fontSize = 13f;
			((Graphic)val6).color = new Color(0.65f, 0.65f, 0.65f, 0.65f);
			((TMP_Text)val6).alignment = (TextAlignmentOptions)4097;
			((TMP_Text)val6).fontStyle = (FontStyles)2;
			((Graphic)val6).raycastTarget = false;
			GameObject val7 = new GameObject("Text");
			val7.transform.SetParent(val3.transform, false);
			RectTransform obj2 = val7.AddComponent<RectTransform>();
			obj2.anchorMin = Vector2.zero;
			obj2.anchorMax = Vector2.one;
			obj2.offsetMin = Vector2.zero;
			obj2.offsetMax = Vector2.zero;
			TextMeshProUGUI val8 = val7.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val8).fontSize = 13f;
			((Graphic)val8).color = Color.white;
			((TMP_Text)val8).alignment = (TextAlignmentOptions)4097;
			((Graphic)val8).raycastTarget = false;
			val2.textViewport = val4;
			val2.textComponent = (TMP_Text)(object)val8;
			val2.placeholder = (Graphic)(object)val6;
			if (isPassword)
			{
				val2.contentType = (ContentType)7;
				val2.inputType = (InputType)2;
			}
			else
			{
				val2.contentType = (ContentType)0;
			}
			return val2;
		}

		private static Button CreateButton(Transform parent, string label, Color bgColor, float width)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Btn_" + label);
			val.transform.SetParent(parent, false);
			LayoutElement obj = val.AddComponent<LayoutElement>();
			obj.preferredWidth = width;
			obj.preferredHeight = 28f;
			Image val2 = val.AddComponent<Image>();
			((Graphic)val2).color = bgColor;
			Button obj2 = val.AddComponent<Button>();
			ColorBlock colors = ((Selectable)obj2).colors;
			((ColorBlock)(ref colors)).normalColor = bgColor;
			((ColorBlock)(ref colors)).highlightedColor = bgColor * 1.25f;
			((ColorBlock)(ref colors)).pressedColor = bgColor * 0.75f;
			((ColorBlock)(ref colors)).disabledColor = new Color(0.35f, 0.35f, 0.35f);
			((Selectable)obj2).colors = colors;
			((Selectable)obj2).targetGraphic = (Graphic)(object)val2;
			GameObject val3 = new GameObject("Text");
			val3.transform.SetParent(val.transform, false);
			RectTransform obj3 = val3.AddComponent<RectTransform>();
			obj3.anchorMin = Vector2.zero;
			obj3.anchorMax = Vector2.one;
			obj3.offsetMin = Vector2.zero;
			obj3.offsetMax = Vector2.zero;
			TextMeshProUGUI obj4 = val3.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj4).text = label;
			((TMP_Text)obj4).fontSize = 13f;
			((TMP_Text)obj4).fontStyle = (FontStyles)1;
			((TMP_Text)obj4).alignment = (TextAlignmentOptions)514;
			((Graphic)obj4).color = Color.white;
			((Graphic)obj4).raycastTarget = false;
			return obj2;
		}
	}
	internal class APNotificationDriver : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <AnimateRoutine>d__2 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public APNotificationDriver <>4__this;

			public string line2;

			public string line1;

			public float duration;

			private float <elapsed>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <AnimateRoutine>d__2(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00df: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
				//IL_0130: Unknown result type (might be due to invalid IL or missing references)
				//IL_0140: Unknown result type (might be due to invalid IL or missing references)
				//IL_0150: Unknown result type (might be due to invalid IL or missing references)
				//IL_015f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0188: Unknown result type (might be due to invalid IL or missing references)
				//IL_0192: Expected O, but got Unknown
				//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
				//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
				//IL_01db: Unknown result type (might be due to invalid IL or missing references)
				//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
				//IL_0200: Unknown result type (might be due to invalid IL or missing references)
				//IL_008c: Unknown result type (might be due to invalid IL or missing references)
				int num = <>1__state;
				APNotificationDriver aPNotificationDriver = <>4__this;
				float num2;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					if (aPNotificationDriver.label == null)
					{
						return false;
					}
					((TMP_Text)aPNotificationDriver.label).text = (string.IsNullOrEmpty(line2) ? line1 : (line1 + "\n<size=70%>" + line2 + "</size>"));
					((Graphic)aPNotificationDriver.label).color = new Color(1f, 0.92f, 0.016f, 0f);
					((Behaviour)aPNotificationDriver.label).enabled = true;
					<elapsed>5__2 = 0f;
					goto IL_0117;
				case 1:
					<>1__state = -1;
					goto IL_0117;
				case 2:
					<>1__state = -1;
					<elapsed>5__2 = 0f;
					break;
				case 3:
					{
						<>1__state = -1;
						break;
					}
					IL_0117:
					if (<elapsed>5__2 < 0.3f)
					{
						<elapsed>5__2 += Time.deltaTime;
						Color color = ((Graphic)aPNotificationDriver.label).color;
						((Graphic)aPNotificationDriver.label).color = new Color(color.r, color.g, color.b, Mathf.Clamp01(<elapsed>5__2 / 0.3f));
						<>2__current = null;
						<>1__state = 1;
						return true;
					}
					((Graphic)aPNotificationDriver.label).color = new Color(((Graphic)aPNotificationDriver.label).color.r, ((Graphic)aPNotificationDriver.label).color.g, ((Graphic)aPNotificationDriver.label).color.b, 1f);
					num2 = Mathf.Max(0f, duration - 0.3f - 0.6f);
					<>2__current = (object)new WaitForSeconds(num2);
					<>1__state = 2;
					return true;
				}
				if (<elapsed>5__2 < 0.6f)
				{
					<elapsed>5__2 += Time.deltaTime;
					Color color2 = ((Graphic)aPNotificationDriver.label).color;
					((Graphic)aPNotificationDriver.label).color = new Color(color2.r, color2.g, color2.b, Mathf.Clamp01(1f - <elapsed>5__2 / 0.6f));
					<>2__current = null;
					<>1__state = 3;
					return true;
				}
				Object.Destroy((Object)(object)((Component)aPNotificationDriver).gameObject);
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		internal TextMeshProUGUI label;

		internal void Show(string line1, string line2, float duration = 3.5f)
		{
			((MonoBehaviour)this).StartCoroutine(AnimateRoutine(line1, line2, duration));
		}

		[IteratorStateMachine(typeof(<AnimateRoutine>d__2))]
		private IEnumerator AnimateRoutine(string line1, string line2, float duration)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <AnimateRoutine>d__2(0)
			{
				<>4__this = this,
				line1 = line1,
				line2 = line2,
				duration = duration
			};
		}
	}
	public static class APNotificationUI
	{
		private const float Duration = 3.5f;

		public static void ShowItemReceived(string itemName, string senderName = "")
		{
			string text = "Received: " + itemName;
			string text2 = (string.IsNullOrEmpty(senderName) ? "" : ("from " + senderName));
			try
			{
				UserInterface.ShowMoneyNotification(text, text2, (MoneyCellType)2);
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[APNotif] ShowMoneyNotification (MetaCoins) failed: " + ex.Message + " — using fallback label."));
				SpawnHUDLabel(text, text2);
			}
		}

		public static void ShowLocationFound(string locationName)
		{
			try
			{
				UserInterface.ShowMoneyNotification("Location Found!", locationName, (MoneyCellType)1);
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[APNotif] ShowMoneyNotification (Revenue) failed: " + ex.Message + " — using fallback label."));
				SpawnHUDLabel("Location Found!", locationName);
			}
		}

		public static void ShowMoneyReceived(string displayLabel, int amount, MoneyCellType cellType)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				UserInterface.ShowMoneyNotification(displayLabel, amount.ToString(), cellType);
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[APNotif] UserInterface.ShowMoneyNotification failed: " + ex.Message));
				SpawnHUDLabel("[AP] Currency Received!", $"+{amount}  {displayLabel}");
			}
		}

		private static void SpawnHUDLabel(string line1, string line2)
		{
			//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)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Canvas val = FindHUDCanvas();
				if (val == null)
				{
					Plugin.Logger.LogDebug((object)("[APNotif] No HUD canvas — logging instead: " + line1 + " | " + line2));
					return;
				}
				GameObject val2 = new GameObject("AP_Notification");
				val2.transform.SetParent(((Component)val).transform, false);
				RectTransform obj = val2.AddComponent<RectTransform>();
				obj.anchorMin = new Vector2(0.5f, 1f);
				obj.anchorMax = new Vector2(0.5f, 1f);
				obj.pivot = new Vector2(0.5f, 1f);
				obj.anchoredPosition = new Vector2(0f, -120f);
				obj.sizeDelta = new Vector2(700f, 80f);
				TextMeshProUGUI val3 = val2.AddComponent<TextMeshProUGUI>();
				((TMP_Text)val3).alignment = (TextAlignmentOptions)514;
				((TMP_Text)val3).fontSize = 26f;
				((TMP_Text)val3).fontStyle = (FontStyles)1;
				((Graphic)val3).raycastTarget = false;
				((Behaviour)val3).enabled = false;
				APNotificationDriver aPNotificationDriver = val2.AddComponent<APNotificationDriver>();
				aPNotificationDriver.label = val3;
				aPNotificationDriver.Show(line1, line2);
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[APNotif] SpawnHUDLabel failed: " + ex.Message));
			}
		}

		private static Canvas FindHUDCanvas()
		{
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			if (Player.localPlayer != null)
			{
				string[] array = new string[4] { "HUD", "PlayerHUD", "Canvas_HUD", "UI" };
				foreach (string text in array)
				{
					Transform val = ((Component)Player.localPlayer).transform.Find(text);
					if (val != null)
					{
						Canvas component = ((Component)val).GetComponent<Canvas>();
						if (component != null)
						{
							return component;
						}
					}
				}
			}
			Canvas[] array2 = Object.FindObjectsOfType<Canvas>();
			Canvas[] array3 = array2;
			foreach (Canvas val2 in array3)
			{
				string text2 = ((Object)val2).name.ToLowerInvariant();
				if (text2.Contains("hud") || text2.Contains("playerhud"))
				{
					return val2;
				}
			}
			array3 = array2;
			foreach (Canvas val3 in array3)
			{
				if ((int)val3.renderMode == 0)
				{
					return val3;
				}
			}
			return null;
		}
	}
}
namespace ContentWarningArchipelago.Patches
{
	[HarmonyPatch]
	internal static class ChorbySpawnPatch
	{
		private static object? _chorbyItem;

		private static MethodBase? TargetMethod()
		{
			Type type = AccessTools.TypeByName("RoundArtifactSpawner");
			if (type == null)
			{
				Plugin.Logger.LogWarning((object)"[ChorbySpawnPatch] RoundArtifactSpawner type not found — Chorby spawn-cap disabled.");
				return null;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "GetArtifactsToSpawn", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Logger.LogWarning((object)"[ChorbySpawnPatch] GetArtifactsToSpawn method not found — Chorby spawn-cap disabled.");
				return null;
			}
			Plugin.Logger.LogInfo((object)("[ChorbySpawnPatch] Patching " + type.Name + "." + methodInfo.Name));
			return methodInfo;
		}

		[HarmonyPostfix]
		private static void Postfix(object __instance, object __result)
		{
			try
			{
				if (!(__result is IList list))
				{
					Plugin.Logger.LogWarning((object)"[ChorbySpawnPatch] GetArtifactsToSpawn returned a non-IList — Chorby skipped this dive.");
					return;
				}
				if (_chorbyItem == null)
				{
					_chorbyItem = ResolveChorby(__instance);
				}
				if (_chorbyItem == null)
				{
					Plugin.Logger.LogWarning((object)"[ChorbySpawnPatch] Chorby Item not found in possibleSpawns — Chorby skipped this dive.");
					return;
				}
				int num = 0;
				for (int num2 = list.Count - 1; num2 >= 0; num2--)
				{
					if (list[num2] == _chorbyItem)
					{
						list.RemoveAt(num2);
						num++;
					}
				}
				list.Insert(0, _chorbyItem);
				Plugin.Logger.LogInfo((object)("[ChorbySpawnPatch] Forced 1 Chorby into the artifact list " + string.Format("(removed {0} vanilla pick{1}, ", num, (num == 1) ? "" : "s") + $"final count={list.Count})."));
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[ChorbySpawnPatch] Postfix failed: {arg}");
			}
		}

		private static object? ResolveChorby(object spawnerInstance)
		{
			FieldInfo fieldInfo = AccessTools.Field(spawnerInstance.GetType(), "possibleSpawns");
			if (fieldInfo == null)
			{
				return null;
			}
			if (!(fieldInfo.GetValue(spawnerInstance) is IEnumerable enumerable))
			{
				return null;
			}
			foreach (object item in enumerable)
			{
				Object val = (Object)((item is Object) ? item : null);
				if (val != null && string.Equals(val.name, "Chorby", StringComparison.Ordinal))
				{
					return item;
				}
			}
			return null;
		}
	}
	[HarmonyPatch]
	internal static class ChorbyPickupPatch
	{
		private static MethodBase? TargetMethod()
		{
			Type type = AccessTools.TypeByName("Pickup");
			if (type == null)
			{
				Plugin.Logger.LogWarning((object)"[ChorbyPickupPatch] Pickup type not found — Chorby pickup intercept disabled.");
				return null;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "RPC_RequestPickup", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Logger.LogWarning((object)"[ChorbyPickupPatch] Pickup.RPC_RequestPickup not found — Chorby pickup intercept disabled.");
				return null;
			}
			Plugin.Logger.LogInfo((object)("[ChorbyPickupPatch] Patching " + type.Name + "." + methodInfo.Name));
			return methodInfo;
		}

		[HarmonyPrefix]
		private static bool Prefix(object __instance)
		{
			try
			{
				if (!IsChorby(__instance, out string itemName))
				{
					return true;
				}
				if (!PhotonNetwork.IsMasterClient)
				{
					Plugin.Logger.LogDebug((object)"[ChorbyPickupPatch] Non-master client received RPC_RequestPickup — letting vanilla handle it.");
					return true;
				}
				Plugin.Logger.LogInfo((object)("[ChorbyPickupPatch] Chorby pickup intercepted (" + itemName + ")."));
				if (Plugin.connection.connected && APSave.saveData.chorbiesFound >= APSave.saveData.quotaCount)
				{
					if (!APSave.saveData.allChorbyChecksFoundNotified)
					{
						BroadcastAllChorbyChecksFound();
						APSave.saveData.allChorbyChecksFoundNotified = true;
						APSave.Flush();
					}
					DestroyPickup(__instance);
					return false;
				}
				if (Plugin.connection.connected)
				{
					APSave.saveData.chorbiesFound++;
					int chorbiesFound = APSave.saveData.chorbiesFound;
					string text = "Found Chorby " + chorbiesFound;
					long id = LocationData.GetId(text);
					if (id > 0)
					{
						Plugin.Logger.LogInfo((object)$"[ChorbyPickupPatch] Chorby #{chorbiesFound} picked up → {text}");
						Plugin.SendCheck(id);
					}
					else
					{
						Plugin.Logger.LogDebug((object)$"[ChorbyPickupPatch] Chorby #{chorbiesFound} above the 21-check ceiling — no AP check sent.");
					}
					APSave.Flush();
				}
				DestroyPickup(__instance);
				return false;
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[ChorbyPickupPatch] Prefix failed: {arg}");
				return true;
			}
		}

		private static bool IsChorby(object pickupInstance, out string itemName)
		{
			itemName = string.Empty;
			FieldInfo fieldInfo = AccessTools.Field(pickupInstance.GetType(), "m_itemID");
			if (fieldInfo == null)
			{
				return false;
			}
			if (!(fieldInfo.GetValue(pickupInstance) is byte b))
			{
				return false;
			}
			Type type = AccessTools.TypeByName("ItemDatabase");
			MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "TryGetItemFromID", (Type[])null, (Type[])null) : null);
			if (methodInfo == null)
			{
				return false;
			}
			object[] array = new object[2] { b, null };
			if ((bool)(methodInfo.Invoke(null, array) ?? ((object)false)))
			{
				object obj = array[1];
				Object val = (Object)((obj is Object) ? obj : null);
				if (val != null)
				{
					itemName = val.name ?? string.Empty;
					return string.Equals(itemName, "Chorby", StringComparison.Ordinal);
				}
			}
			return false;
		}

		private static void DestroyPickup(object pickupInstance)
		{
			try
			{
				object? obj = AccessTools.Field(pickupInstance.GetType(), "m_photonView")?.GetValue(pickupInstance);
				PhotonView val = (PhotonView)((obj is PhotonView) ? obj : null);
				if (val != null)
				{
					val.RPC("RPC_Remove", (RpcTarget)2, Array.Empty<object>());
				}
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[ChorbyPickupPatch] DestroyPickup failed: " + ex.Message));
			}
		}

		private static void BroadcastAllChorbyChecksFound()
		{
			string text = "All Chorby checks found";
			APNotificationUI.ShowLocationFound(text);
			Plugin.Logger.LogInfo((object)("[ChorbyPickupPatch] " + text + " (post-quota Chorby pickup)."));
			if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom.PlayerCount > 1)
			{
				try
				{
					MyceliumNetwork.RPC(2718281828u, "LocationFound", (ReliableType)1, new object[1] { text });
				}
				catch (Exception ex)
				{
					Plugin.Logger.LogWarning((object)("[ChorbyPickupPatch] Mycelium broadcast failed: " + ex.Message));
				}
			}
		}
	}
	[HarmonyPatch(typeof(DivingBellSuitCellUI))]
	internal static class DivingBellAPStatusPatch
	{
		internal static string LastItemName = "";

		[HarmonyPostfix]
		[HarmonyPatch("Set")]
		private static void Set(Player player, float dst, DivingBellSuitCellUI __instance)
		{
			if (!player.IsLocal)
			{
				return;
			}
			try
			{
				bool connected = Plugin.connection.connected;
				string text = (connected ? "<size=70%><color=#00FF88>[AP] Connected</color></size>" : "<size=70%><color=#FF4444>[AP] Disconnected</color></size>");
				if (connected && !string.IsNullOrEmpty(LastItemName))
				{
					text = text + "\n<size=60%>Last item: " + LastItemName + "</size>";
				}
				if (connected)
				{
					APSaveData saveData = APSave.saveData;
					if (saveData.viewsChecks || saveData.viewsGoal || saveData.viralSensationGoal)
					{
						text += $"\n<size=60%>Lifetime views: {saveData.lifetimeViews:N0}";
						if (saveData.viewsGoal && saveData.viewsGoalTarget > 0)
						{
							text += $" / {saveData.viewsGoalTarget:N0}";
						}
						text += "</size>";
						if (saveData.viralSensationGoal)
						{
							text += $"\n<size=60%>This quota: {saveData.currentQuotaViews:N0} / 1,000,000</size>";
						}
					}
				}
				string text2 = ((TMP_Text)__instance.m_oxygenText).text;
				int num = text2.IndexOf("\n<size=70%><color=#");
				if (num >= 0)
				{
					text2 = text2.Substring(0, num);
				}
				((TMP_Text)__instance.m_oxygenText).text = text2 + "\n" + text;
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogDebug((object)("[DiveBellAPStatus] Postfix exception: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class DivingBellRechargePatch
	{
		private const float ChargeRatePerSecond = 0.05f;

		[HarmonyPostfix]
		private static void Postfix(Player __instance)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			PlayerInventory val = default(PlayerInventory);
			if (!__instance.IsLocal || !Plugin.connection.connected || !APSave.saveData.diveBellChargerUnlocked || !__instance.data.isInDiveBell || !__instance.TryGetInventory(ref val))
			{
				return;
			}
			float num = 0.05f * Time.deltaTime;
			InventorySlot[] slots = val.slots;
			BatteryEntry val3 = default(BatteryEntry);
			foreach (InventorySlot val2 in slots)
			{
				if (val2 != null && !((Object)(object)val2.ItemInSlot.item == (Object)null))
				{
					ItemInstanceData data = val2.ItemInSlot.data;
					if (data != null && data.TryGetEntry<BatteryEntry>(ref val3) && !(val3.m_maxCharge <= val3.m_charge))
					{
						val3.AddCharge(num * val3.m_maxCharge);
						Plugin.Logger.LogDebug((object)("[DiveBellCharger] Charged " + ((Object)val2.ItemInSlot.item).name + ": " + $"{val3.m_charge:F1}/{val3.m_maxCharge:F1} " + $"({val3.GetPercentage() * 100f:F0}%)"));
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(HatShop), "RPCA_StockShop")]
	internal static class HatShopFixedSeedPatch
	{
		[HarmonyPrefix]
		private static void Prefix(ref int seed)
		{
			if (Plugin.connection.connected)
			{
				ArchipelagoSession? session = Plugin.connection.session;
				string text = ((session != null) ? session.RoomState.Seed : null);
				if (!string.IsNullOrEmpty(text))
				{
					int num = TryGetCurrentDay();
					seed = $"{text}:{num}".GetHashCode();
					Plugin.Logger.LogInfo((object)("[HatShopFixedSeedPatch] Hat shop seed overridden " + $"(apSeed + day {num}): {seed}"));
				}
			}
		}

		private static int TryGetCurrentDay()
		{
			Type type = AccessTools.TypeByName("GameAPI");
			if (type != null)
			{
				PropertyInfo propertyInfo = AccessTools.Property(type, "CurrentDay");
				if (propertyInfo != null && propertyInfo.GetValue(null) is int num && num > 0)
				{
					return num;
				}
			}
			Type type2 = AccessTools.TypeByName("SurfaceNetworkHandler");
			if (type2 != null)
			{
				PropertyInfo propertyInfo2 = AccessTools.Property(type2, "RoomStats");
				if (propertyInfo2 != null)
				{
					object value = propertyInfo2.GetValue(null);
					if (value != null)
					{
						PropertyInfo propertyInfo3 = AccessTools.Property(value.GetType(), "CurrentDay");
						if (propertyInfo3 != null && propertyInfo3.GetValue(value) is int num2 && num2 > 0)
						{
							return num2;
						}
					}
				}
			}
			return 0;
		}
	}
	[HarmonyPatch(typeof(HatShop), "Restock")]
	internal static class HatShopFilterRestockPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(HatShop __instance)
		{
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.connection.connected)
			{
				return true;
			}
			if ((Object)(object)HatDatabase.instance == (Object)null || HatDatabase.instance.hats == null)
			{
				return true;
			}
			FieldInfo fieldInfo = AccessTools.Field(typeof(HatShop), "savedSeed");
			if (fieldInfo == null)
			{
				return true;
			}
			int num = (int)(fieldInfo.GetValue(__instance) ?? ((object)0));
			State state = Random.state;
			Random.InitState(num);
			try
			{
				ArchipelagoSession? session = Plugin.connection.session;
				ReadOnlyCollection<long> checkedLocs = ((session != null) ? session.Locations.AllLocationsChecked : null);
				List<Hat> list = new List<Hat>();
				Hat[] hats = HatDatabase.instance.hats;
				foreach (Hat val in hats)
				{
					if (!((Object)(object)val == (Object)null) && !IsHatLocationChecked(val, checkedLocs))
					{
						list.Add(val);
					}
				}
				int count = __instance.hatBuyInteractables.Count;
				int num2 = Math.Min(count, list.Count);
				List<Hat> list2 = ((num2 > 0) ? ExtCollections.GetRandomNoDuplicates<Hat>((IEnumerable<Hat>)list, num2) : new List<Hat>());
				for (int j = 0; j < count; j++)
				{
					HatBuyInteractable val2 = __instance.hatBuyInteractables[j];
					if ((Object)(object)val2 == (Object)null)
					{
						continue;
					}
					if (j < list2.Count)
					{
						Hat val3 = list2[j];
						int num3 = Mathf.RoundToInt((float)val3.GetBasePrice() * Random.Range(0.5f, 2f) / 10f) * 10;
						val2.LoadHat(((Component)val3).gameObject, num3);
						continue;
					}
					val2.ClearHat();
					if ((Object)(object)val2.nameText != (Object)null)
					{
						((TMP_Text)val2.nameText).text = string.Empty;
					}
					if ((Object)(object)val2.priceText != (Object)null)
					{
						((TMP_Text)val2.priceText).text = string.Empty;
					}
				}
				Plugin.Logger.LogInfo((object)("[HatShopFilterRestockPatch] Filtered restock: " + $"{num2}/{count} slots filled, " + $"{list.Count} unbought hats in pool."));
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[HatShopFilterRestockPatch] Exception: {arg}");
			}
			finally
			{
				Random.state = state;
			}
			return false;
		}

		private static bool IsHatLocationChecked(Hat hat, IReadOnlyCollection<long>? checkedLocs)
		{
			if (checkedLocs == null || checkedLocs.Count == 0)
			{
				return false;
			}
			string text = null;
			if (!string.IsNullOrEmpty(hat.displayName) && HatShopAPLabelPatch.HatNameToLocation.TryGetValue(hat.displayName, out string value))
			{
				text = value;
			}
			else
			{
				string name = hat.GetName();
				if (!string.IsNullOrEmpty(name) && HatShopAPLabelPatch.HatNameToLocation.TryGetValue(name, out string value2))
				{
					text = value2;
				}
			}
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			long id = LocationData.GetId(text);
			if (id < 0)
			{
				return false;
			}
			return checkedLocs.Contains(id);
		}
	}
	[HarmonyPatch(typeof(HatShop), "Restock")]
	internal static class HatShopAPLabelPatch
	{
		internal static readonly Dictionary<string, string> HatNameToLocation = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "Beanie", "Bought Beanie" },
			{ "Bucket Hat", "Bought Bucket Hat" },
			{ "Floppy Hat", "Bought Floppy Hat" },
			{ "Homburg", "Bought Homburg" },
			{ "Bowler Hat", "Bought Bowler Hat" },
			{ "Cap", "Bought Cap" },
			{ "News Cap", "Bought News Cap" },
			{ "Newsboy Cap", "Bought News Cap" },
			{ "Sports Helmet", "Bought Sports Helmet" },
			{ "Hard Hat", "Bought Hard Hat" },
			{ "Chefs Hat", "Bought Chefs Hat" },
			{ "Chef's Hat", "Bought Chefs Hat" },
			{ "Propeller Hat", "Bought Propeller Hat" },
			{ "Cowboy Hat", "Bought Cowboy Hat" },
			{ "Horns", "Bought Horns" },
			{ "Hotdog Hat", "Bought Hotdog Hat" },
			{ "Hot Dog Hat", "Bought Hotdog Hat" },
			{ "Milk Hat", "Bought Milk Hat" },
			{ "Pirate Hat", "Bought Pirate Hat" },
			{ "Top Hat", "Bought Top Hat" },
			{ "Party Hat", "Bought Party Hat" },
			{ "Ushanka", "Bought Ushanka" },
			{ "Balaclava", "Bought Balaclava" },
			{ "Cat Ears", "Bought Cat Ears" },
			{ "Curly Hair", "Bought Curly Hair" },
			{ "Clown Hair", "Bought Clown Hair" },
			{ "Crown", "Bought Crown" },
			{ "Halo", "Bought Halo" },
			{ "Jesters Hat", "Bought Jesters Hat" },
			{ "Jester's Hat", "Bought Jesters Hat" },
			{ "Ghost Hat", "Bought Ghost Hat" },
			{ "Tooop Hat", "Bought Tooop Hat" },
			{ "Shroom Hat", "Bought Shroom Hat" },
			{ "Witch Hat", "Bought Witch Hat" },
			{ "Savannah Hair", "Bought Savannah Hair" }
		};

		[HarmonyPostfix]
		private static void Postfix(HatShop __instance)
		{
			if (Plugin.connection.connected && PhotonNetwork.IsMasterClient)
			{
				(((Component)__instance).GetComponent<HatShopAPSyncBehaviour>() ?? ((Component)__instance).gameObject.AddComponent<HatShopAPSyncBehaviour>()).LaunchScout(__instance);
			}
		}
	}
	internal static class HatShopRestockLabelPatch
	{
		internal static readonly Dictionary<HatBuyInteractable, string> ScoutedNames = new Dictionary<HatBuyInteractable, string>();

		internal static readonly Dictionary<HatBuyInteractable, string> ScoutedAPItemNames = new Dictionary<HatBuyInteractable, string>();
	}
	internal class HatShopAPSyncBehaviour : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <ScoutAndLabelCoroutine>d__7 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public HatShop hatShop;

			private Dictionary<HatBuyInteractable, long> <slotToLocId>5__2;

			private Task<Dictionary<long, string>> <scoutTask>5__3;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ScoutAndLabelCoroutine>d__7(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<slotToLocId>5__2 = null;
				<scoutTask>5__3 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
				{
					<>1__state = -1;
					<slotToLocId>5__2 = new Dictionary<HatBuyInteractable, long>();
					List<long> list = new List<long>();
					foreach (HatBuyInteractable hatBuyInteractable in hatShop.hatBuyInteractables)
					{
						if ((Object)(object)hatBuyInteractable == (Object)null || hatBuyInteractable.IsEmpty || (Object)(object)hatBuyInteractable.ihat == (Object)null)
						{
							continue;
						}
						string text = ResolveLocationName(hatBuyInteractable.ihat);
						if (text != null)
						{
							long id = LocationData.GetId(text);
							if (id < 0)
							{
								Plugin.Logger.LogDebug((object)("[HatShopAPSyncBehaviour] '" + text + "' not in LocationData table."));
								continue;
							}
							<slotToLocId>5__2[hatBuyInteractable] = id;
							list.Add(id);
						}
					}
					if (list.Count == 0)
					{
						Plugin.Logger.LogDebug((object)"[HatShopAPSyncBehaviour] No valid hat AP locations to scout.");
						return false;
					}
					Plugin.Logger.LogInfo((object)$"[HatShopAPSyncBehaviour] Scouting {list.Count} hat location(s)…");
					<scoutTask>5__3 = Plugin.connection.ScoutLocationsAsync(list);
					break;
				}
				case 1:
					<>1__state = -1;
					break;
				}
				if (!<scoutTask>5__3.IsCompleted)
				{
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				Dictionary<long, string> dictionary;
				try
				{
					dictionary = <scoutTask>5__3.Result ?? new Dictionary<long, string>();
				}
				catch (Exception ex)
				{
					Plugin.Logger.LogWarning((object)("[HatShopAPSyncBehaviour] Scout task threw: " + ex.Message));
					return false;
				}
				HatShopRestockLabelPatch.ScoutedNames.Clear();
				HatShopRestockLabelPatch.ScoutedAPItemNames.Clear();
				string[] array = new string[hatShop.hatBuyInteractables.Count];
				for (int i = 0; i < hatShop.hatBuyInteractables.Count; i++)
				{
					HatBuyInteractable val = hatShop.hatBuyInteractables[i];
					array[i] = string.Empty;
					if (!((Object)(object)val == (Object)null) && !val.IsEmpty && !((Object)(object)val.ihat == (Object)null) && <slotToLocId>5__2.TryGetValue(val, out var value) && dictionary.TryGetValue(value, out var value2) && !string.IsNullOrEmpty(value2))
					{
						string name = val.ihat.GetName();
						string text2 = name + " [" + value2 + "]";
						if ((Object)(object)val.nameText != (Object)null)
						{
							((TMP_Text)val.nameText).text = text2;
							ApplyAutoSizing(val.nameText);
						}
						HatShopRestockLabelPatch.ScoutedNames[val] = text2;
						HatShopRestockLabelPatch.ScoutedAPItemNames[val] = value2;
						array[i] = text2;
						Plugin.Logger.LogInfo((object)$"[HatShopAPSyncBehaviour] Labelled slot {i}: '{name}' → '{value2}'");
					}
				}
				Plugin.Logger.LogInfo((object)$"[HatShopAPSyncBehaviour] Applied {HatShopRestockLabelPatch.ScoutedNames.Count} label(s).");
				if (PhotonNetwork.CurrentRoom != null && PhotonNetwork.CurrentRoom.PlayerCount > 1)
				{
					PhotonView component = ((Component)hatShop).GetComponent<PhotonView>();
					if ((Object)(object)component != (Object)null)
					{
						component.RPC("RPCA_SyncArchipelagoLabels", (RpcTarget)1, new object[1] { array });
						Plugin.Logger.LogInfo((object)"[HatShopAPSyncBehaviour] Broadcast hat labels to other clients.");
					}
					else
					{
						Plugin.Logger.LogWarning((object)"[HatShopAPSyncBehaviour] HatShop has no PhotonView — cannot broadcast labels.");
					}
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

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

		private void Awake()
		{
			Instance = this;
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		internal void LaunchScout(HatShop hatShop)
		{
			((MonoBehaviour)this).StopAllCoroutines();
			((MonoBehaviour)this).StartCoroutine(ScoutAndLabelCoroutine(hatShop));
		}

		[IteratorStateMachine(typeof(<ScoutAndLabelCoroutine>d__7))]
		private IEnumerator ScoutAndLabelCoroutine(HatShop hatShop)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ScoutAndLabelCoroutine>d__7(0)
			{
				hatShop = hatShop
			};
		}

		[PunRPC]
		public void RPCA_SyncArchipelagoLabels(string[] labels)
		{
			if ((Object)(object)HatShop.instance == (Object)null)
			{
				Plugin.Logger.LogWarning((object)"[HatShopAPSyncBehaviour] RPCA_SyncArchipelagoLabels: HatShop.instance is null.");
				return;
			}
			List<HatBuyInteractable> hatBuyInteractables = HatShop.instance.hatBuyInteractables;
			for (int i = 0; i < labels.Length && i < hatBuyInteractables.Count; i++)
			{
				string text = labels[i];
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				HatBuyInteractable val = hatBuyInteractables[i];
				if ((Object)(object)val == (Object)null || val.IsEmpty || (Object)(object)val.ihat == (Object)null)
				{
					continue;
				}
				if ((Object)(object)val.nameText != (Object)null)
				{
					((TMP_Text)val.nameText).text = text;
					ApplyAutoSizing(val.nameText);
				}
				HatShopRestockLabelPatch.ScoutedNames[val] = text;
				int num = text.LastIndexOf('[');
				int num2 = text.LastIndexOf(']');
				if (num >= 0 && num2 > num)
				{
					string value = text.Substring(num + 1, num2 - num - 1);
					if (!string.IsNullOrEmpty(value))
					{
						HatShopRestockLabelPatch.ScoutedAPItemNames[val] = value;
					}
				}
			}
			Plugin.Logger.LogInfo((object)$"[HatShopAPSyncBehaviour] RPCA_SyncArchipelagoLabels: applied {labels.Length} label(s).");
		}

		private static void ApplyAutoSizing(TextMeshPro tmp)
		{
			if (!((Object)(object)tmp == (Object)null))
			{
				((TMP_Text)tmp).enableWordWrapping = true;
				((TMP_Text)tmp).enableAutoSizing = true;
				((TMP_Text)tmp).fontSizeMax = Mathf.Max(((TMP_Text)tmp).fontSize, (((TMP_Text)tmp).fontSizeMax > 0f) ? ((TMP_Text)tmp).fontSizeMax : ((TMP_Text)tmp).fontSize);
				((TMP_Text)tmp).fontSizeMin = 8f;
				((TMP_Text)tmp).overflowMode = (TextOverflowModes)3;
			}
		}

		private static string? ResolveLocationName(Hat hat)
		{
			if (!string.IsNullOrEmpty(hat.displayName) && HatShopAPLabelPatch.HatNameToLocation.TryGetValue(hat.displayName, out string value))
			{
				return value;
			}
			string name = hat.GetName();
			if (!string.IsNullOrEmpty(name) && HatShopAPLabelPatch.HatNameToLocation.TryGetValue(name, out string value2))
			{
				return value2;
			}
			Plugin.Logger.LogDebug((object)("[HatShopAPSyncBehaviour] No location mapping for hat displayName='" + hat.displayName + "' / GetName()='" + hat.GetName() + "'."));
			return null;
		}
	}
	[HarmonyPatch(typeof(HatShop), "RPCA_BuyHat")]
	internal static class HatShopBuyAPCheckPatch
	{
		[HarmonyPrefix]
		private static void Prefix(HatShop __instance, int hatBuyIndex)
		{
			if (!Plugin.connection.connected || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			try
			{
				if (hatBuyIndex < 0 || hatBuyIndex >= __instance.hatBuyInteractables.Count)
				{
					return;
				}
				HatBuyInteractable val = __instance.hatBuyInteractables[hatBuyIndex];
				if ((Object)(object)val == (Object)null || val.IsEmpty || (Object)(object)val.ihat == (Object)null)
				{
					return;
				}
				string text = null;
				if (!string.IsNullOrEmpty(val.ihat.displayName) && HatShopAPLabelPatch.HatNameToLocation.TryGetValue(val.ihat.displayName, out string value))
				{
					text = value;
				}
				else
				{
					string name = val.ihat.GetName();
					if (!string.IsNullOrEmpty(name) && HatShopAPLabelPatch.HatNameToLocation.TryGetValue(name, out string value2))
					{
						text = value2;
					}
				}
				if (text == null)
				{
					string text2 = ((!string.IsNullOrEmpty(val.ihat.displayName)) ? val.ihat.displayName : val.ihat.GetName());
					if (!string.IsNullOrEmpty(text2))
					{
						text = "Bought " + text2;
					}
				}
				if (!string.IsNullOrEmpty(text))
				{
					long id = LocationData.GetId(text);
					if (id < 0)
					{
						Plugin.Logger.LogDebug((object)("[HatShopBuyAPCheckPatch] '" + text + "' not in AP location table."));
						return;
					}
					Plugin.Logger.LogInfo((object)("[HatShopBuyAPCheckPatch] Hat purchase → sending check: " + text));
					Plugin.SendCheck(id);
				}
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[HatShopBuyAPCheckPatch] Exception: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(HatShop), "Restock")]
	internal static class HatShopRestockPatch
	{
		private static readonly Dictionary<string, int> HatStagePrices = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
		{
			{ "Beanie", 500 },
			{ "Bucket Hat", 500 },
			{ "Floppy Hat", 500 },
			{ "Homburg", 500 },
			{ "Bowler Hat", 500 },
			{ "Cap", 500 },
			{ "News Cap", 500 },
			{ "Newsboy Cap", 500 },
			{ "Sports Helmet", 500 },
			{ "Hard Hat", 500 },
			{ "Chefs Hat", 1000 },
			{ "Chef's Hat", 1000 },
			{ "Propeller Hat", 1000 },
			{ "Cowboy Hat", 1000 },
			{ "Horns", 1000 },
			{ "Hotdog Hat", 1000 },
			{ "Hot Dog Hat", 1000 },
			{ "Milk Hat", 1000 },
			{ "Pirate Hat", 1000 },
			{ "Top Hat", 1000 },
			{ "Party Hat", 1000 },
			{ "Ushanka", 1000 },
			{ "Balaclava", 2000 },
			{ "Cat Ears", 2000 },
			{ "Curly Hair", 2000 },
			{ "Clown Hair", 2000 },
			{ "Crown", 2000 },
			{ "Halo", 2000 },
			{ "Jesters Hat", 2000 },
			{ "Jester's Hat", 2000 },
			{ "Ghost Hat", 2000 },
			{ "Tooop Hat", 2000 },
			{ "Shroom Hat", 2000 },
			{ "Witch Hat", 2000 },
			{ "Savannah Hair", 2000 }
		};

		private static int RarityFallbackPrice(RARITY rarity)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Invalid comparison between Unknown and I4
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			if ((int)rarity <= 10000)
			{
				if ((int)rarity == 100 || (int)rarity == 1000)
				{
					return 500;
				}
				if ((int)rarity == 10000)
				{
					goto IL_003f;
				}
			}
			else
			{
				if ((int)rarity == 100000)
				{
					goto IL_003f;
				}
				if ((int)rarity == 1000000 || (int)rarity == 10000000)
				{
					return 2000;
				}
			}
			return 500;
			IL_003f:
			return 1000;
		}

		[HarmonyPostfix]
		private static void Postfix(HatShop __instance)
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			foreach (HatBuyInteractable hatBuyInteractable in __instance.hatBuyInteractables)
			{
				if ((Object)(object)hatBuyInteractable == (Object)null || hatBuyInteractable.IsEmpty || (Object)(object)hatBuyInteractable.ihat == (Object)null)
				{
					continue;
				}
				bool flag = true;
				if (!HatStagePrices.TryGetValue(hatBuyInteractable.ihat.displayName, out var value))
				{
					string name = hatBuyInteractable.ihat.GetName();
					if (!HatStagePrices.TryGetValue(name, out value))
					{
						value = RarityFallbackPrice(hatBuyInteractable.ihat.rarity);
						flag = false;
						Plugin.Logger.LogDebug((object)("[HatShopRestockPatch] No stage mapping for '" + hatBuyInteractable.ihat.displayName + "' / '" + name + "' " + $"(rarity: {hatBuyInteractable.ihat.rarity}) — fallback {value} MC."));
					}
				}
				hatBuyInteractable.ihat.priceToday = value;
				((TMP_Text)hatBuyInteractable.priceText).text = value + " MC";
				Plugin.Logger.LogDebug((object)("[HatShopRestockPatch] '" + hatBuyInteractable.ihat.GetName() + "' → " + string.Format("{0} MC ({1})", value, flag ? "table" : "rarity fallback")));
			}
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class HatBuyInteractableHoverTextPatch
	{
		[HarmonyPostfix]
		private static void Postfix(HatBuyInteractable __instance, ref string __result)
		{
			if (!string.IsNullOrEmpty(__result) && !((Object)(object)__instance == (Object)null) && HatShopRestockLabelPatch.ScoutedAPItemNames.TryGetValue(__instance, out string value) && !string.IsNullOrEmpty(value))
			{
				__result = __result + " [" + value + "]";
			}
		}
	}
	[HarmonyPatch]
	public static class ShopBuyPatch
	{
		private static readonly Dictionary<string, string> _shopNameOverrides = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "AncientGestures3", "Bought Ancient Gestures 3" },
			{ "Ancient Gestures 3", "Bought Ancient Gestures 3" },
			{ "AncientGestures2", "Bought Ancient Gestures 2" },
			{ "Ancient Gestures 2", "Bought Ancient Gestures 2" },
			{ "AncientGestures1", "Bought Ancient Gestures 1" },
			{ "Ancient Gestures 1", "Bought Ancient Gestures 1" },
			{ "Backflip", "Bought Backflip" },
			{ "Backflip_Emote", "Bought Backflip" },
			{ "HalfFlip", "Bought Backflip" },
			{ "Half_Flip", "Bought Backflip" },
			{ "Emote_HalfBackflip", "Bought Backflip" },
			{ "Emote_HalfBackflip_01", "Bought Backflip" },
			{ "Dance101", "Bought Dance 101" },
			{ "Dance 101", "Bought Dance 101" },
			{ "Dance102", "Bought Dance 102" },
			{ "Dance 102", "Bought Dance 102" },
			{ "Dance103", "Bought Dance 103" },
			{ "Dance 103", "Bought Dance 103" },
			{ "Workout1", "Bought Workout 1" },
			{ "Workout 1", "Bought Workout 1" },
			{ "Workout2", "Bought Workout 2" },
			{ "Workout 2", "Bought Workout 2" },
			{ "Thumbnail1", "Bought Thumbnail 1" },
			{ "Thumbnail 1", "Bought Thumbnail 1" },
			{ "Thumbnail2", "Bought Thumbnail 2" },
			{ "Thumbnail 2", "Bought Thumbnail 2" },
			{ "Gymnastics", "Bought Gymnastics" },
			{ "Caring", "Bought Caring" },
			{ "Yoga", "Bought Yoga" },
			{ "PartyPopper", "Bought Party Popper" }
		};

		private static MethodBase? TargetMethod()
		{
			Type type = AccessTools.TypeByName("ShopHandler");
			if (type == null)
			{
				Plugin.Logger.LogWarning((object)"[ShopBuyPatch] Could not find type 'ShopHandler'. Patch skipped.");
				return null;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "RPCA_SpawnDrone", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				Plugin.Logger.LogInfo((object)("[ShopBuyPatch] Patching " + type.Name + "." + methodInfo.Name));
				return methodInfo;
			}
			Plugin.Logger.LogWarning((object)"[ShopBuyPatch] Could not find 'RPCA_SpawnDrone' on ShopHandler. Patch skipped.");
			return null;
		}

		[HarmonyPostfix]
		private static void Postfix(object __instance, byte[] itemIDs)
		{
			if (!Plugin.connection.connected || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			try
			{
				foreach (byte b in itemIDs)
				{
					TryGetItemDisplayName(b, out string displayName, out string assetName);
					string text = null;
					if (!string.IsNullOrEmpty(assetName) && _shopNameOverrides.TryGetValue(assetName, out string value))
					{
						text = value;
					}
					if (text == null && !string.IsNullOrEmpty(displayName) && _shopNameOverrides.TryGetValue(displayName, out string value2))
					{
						text = value2;
					}
					if (text == null && !string.IsNullOrEmpty(displayName))
					{
						text = "Bought " + displayName;
					}
					if (text == null && !string.IsNullOrEmpty(assetName))
					{
						text = "Bought " + assetName;
					}
					if (string.IsNullOrEmpty(text))
					{
						Plugin.Logger.LogDebug((object)$"[ShopBuyPatch] Could not resolve any name for item ID {b}.");
						continue;
					}
					long id = LocationData.GetId(text);
					if (id < 0)
					{
						Plugin.Logger.LogDebug((object)("[ShopBuyPatch] '" + text + "' is not an AP location (non-AP item)."));
						continue;
					}
					Plugin.Logger.LogInfo((object)("[ShopBuyPatch] Purchase confirmed → sending check: " + text));
					Plugin.SendCheck(id);
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[ShopBuyPatch] Exception in postfix: {arg}");
			}
		}

		private static void TryGetItemDisplayName(byte itemId, out string? displayName, out string? assetName)
		{
			displayName = null;
			assetName = null;
			Type type = AccessTools.TypeByName("ItemDatabase");
			if (type == null)
			{
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "TryGetItemFromID", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return;
			}
			object[] array = new object[2] { itemId, null };
			if (!(bool)(methodInfo.Invoke(null, array) ?? ((object)false)) || array[1] == null)
			{
				return;
			}
			object obj = array[1];
			Object val = (Object)((obj is Object) ? obj : null);
			if (val != null && !string.IsNullOrWhiteSpace(val.name))
			{
				assetName = val.name;
			}
			FieldInfo fieldInfo = AccessTools.Field(obj.GetType(), "displayName");
			if (fieldInfo != null)
			{
				string text = fieldInfo.GetValue(obj)?.ToString();
				if (!string.IsNullOrWhiteSpace(text))
				{
					displayName = text;
				}
			}
		}
	}
	[HarmonyPatch]
	public static class PickupPatch
	{
		private static MethodBase? TargetMethod()
		{
			Type type = AccessTools.TypeByName("Pickup");
			if (type == null)
			{
				Plugin.Logger.LogWarning((object)"[PickupPatch] Could not find type 'Pickup'. Patch skipped.");
				return null;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "RPC_RequestPickup", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Logger.LogWarning((object)"[PickupPatch] Could not find 'RPC_RequestPickup' on Pickup. Patch skipped.");
				return null;
			}
			Plugin.Logger.LogInfo((object)("[PickupPatch] Patching " + type.Name + "." + methodInfo.Name));
			return methodInfo;
		}

		[HarmonyPostfix]
		private static void Postfix(object __instance)
		{
			if (!Plugin.connection.connected)
			{
				return;
			}
			try
			{
				string text = TryGetPickupName(__instance);
				Plugin.Logger.LogDebug((object)("[PickupPatch] Item picked up: " + (text ?? "(unknown)")));
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[PickupPatch] Exception in postfix: {arg}");
			}
		}

		private static string? TryGetPickupName(object instance)
		{
			FieldInfo fieldInfo = AccessTools.Field(instance.GetType(), "itemInstance");
			if (fieldInfo != null)
			{
				object value = fieldInfo.GetValue(instance);
				if (value != null)
				{
					FieldInfo fieldInfo2 = AccessTools.Field(value.GetType(), "item");
					if (fieldInfo2 != null)
					{
						object value2 = fieldInfo2.GetValue(value);
						if (value2 != null)
						{
							FieldInfo fieldInfo3 = AccessTools.Field(value2.GetType(), "displayName");
							if (fieldInfo3 != null)
							{
								string text = fieldInfo3.GetValue(value2)?.ToString();
								if (!string.IsNullOrEmpty(text))
								{
									return text;
								}
							}
							Object val = (Object)((value2 is Object) ? value2 : null);
							if (val != null)
							{
								return val.name;
							}
						}
					}
				}
			}
			Component val2 = (Component)((instance is Component) ? instance : null);
			if (val2 != null)
			{
				return ((Object)val2.gameObject).name;
			}
			return null;
		}
	}
	[HarmonyPatch]
	public static class ContentEvaluatorPatch
	{
		private static readonly HashSet<string> _monstersFilmedThisDay = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static MethodBase? TargetMethod()
		{
			string[] array = new string[1] { "ContentEvaluator" };
			for (int i = 0; i < array.Length; i++)
			{
				Type type = AccessTools.TypeByName(array[i]);
				if (!(type == null))
				{
					MethodInfo methodInfo = AccessTools.Method(type, "EvaluateRecording", (Type[])null, (Type[])null);
					if (methodInfo != null)
					{
						Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] Patching " + type.Name + "." + methodInfo.Name));
						return methodInfo;
					}
				}
			}
			Plugin.Logger.LogWarning((object)"[ContentEvaluatorPatch] Could not find ContentEvaluator.EvaluateRecording. Extraction and filming checks will not fire. Verify the class name in Assembly-CSharp.");
			return null;
		}

		[HarmonyPostfix]
		private static void Postfix(bool __result)
		{
			if (!__result || !Plugin.connection.connected || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			try
			{
				long id = LocationData.GetId("Any Extraction");
				if (id >= 0)
				{
					Plugin.Logger.LogInfo((object)"[ContentEvaluatorPatch] Sending check: Any Extraction");
					Plugin.SendCheck(id);
					BroadcastAPCheckNotification("Any Extraction");
				}
				int num = TryGetCurrentDay();
				if (num > 0 && num <= 63)
				{
					string text = "Extracted Footage on Day " + num;
					long id2 = LocationData.GetId(text);
					if (id2 >= 0)
					{
						Plugin.Logger.LogInfo((object)$"[ContentEvaluatorPatch] Day {num} extraction → sending check: {text}");
						Plugin.SendCheck(id2);
						BroadcastAPCheckNotification(text);
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[ContentEvaluatorPatch] Exception in extraction postfix: {arg}");
			}
		}

		[HarmonyPostfix]
		private static void FilmingPostfix(bool __result, object buffer)
		{
			if (!__result || !Plugin.connection.connected || buffer == null)
			{
				return;
			}
			try
			{
				int viewsMultiplierLevel = APSave.saveData.viewsMultiplierLevel;
				if (viewsMultiplierLevel > 0)
				{
					float num = 1f + (float)viewsMultiplierLevel * 0.1f;
					if (AccessTools.Field(buffer.GetType(), "buffer")?.GetValue(buffer) is IList list)
					{
						foreach (object item in list)
						{
							if (item != null)
							{
								FieldInfo fieldInfo = AccessTools.Field(item.GetType(), "score");
								if (!(fieldInfo == null))
								{
									float num2 = (float)(fieldInfo.GetValue(item) ?? ((object)0f));
									fieldInfo.SetValue(item, num2 * num);
								}
							}
						}
						Plugin.Logger.LogInfo((object)($"[ContentEvaluatorPatch] Progressive Views: applied {num:F1}× " + $"score multiplier (level {viewsMultiplierLevel}) to {list.Count} buffer entries."));
					}
				}
				if (PhotonNetwork.IsMasterClient)
				{
					FireFilmingChecks(buffer);
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[ContentEvaluatorPatch] Exception in filming postfix: {arg}");
			}
		}

		public static void ResetDailyFilmingState()
		{
			int count = _monstersFilmedThisDay.Count;
			_monstersFilmedThisDay.Clear();
			Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] Daily filming state reset " + string.Format("({0} entr{1} cleared).", count, (count == 1) ? "y" : "ies")));
		}

		private static void FireFilmingChecks(object contentBuffer)
		{
			HashSet<string> fired = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			FieldInfo fieldInfo = AccessTools.Field(contentBuffer.GetType(), "buffer");
			if (fieldInfo == null)
			{
				Plugin.Logger.LogWarning((object)"[ContentEvaluatorPatch] Could not find 'buffer' field on ContentBuffer. Filming checks skipped.");
			}
			else
			{
				if (!(fieldInfo.GetValue(contentBuffer) is IEnumerable enumerable))
				{
					return;
				}
				foreach (object item in enumerable)
				{
					if (item == null)
					{
						continue;
					}
					FieldInfo fieldInfo2 = AccessTools.Field(item.GetType(), "frame");
					if (fieldInfo2 == null)
					{
						continue;
					}
					object value = fieldInfo2.GetValue(item);
					if (value == null)
					{
						continue;
					}
					FieldInfo fieldInfo3 = AccessTools.Field(value.GetType(), "contentEvent");
					if (fieldInfo3 == null)
					{
						continue;
					}
					object value2 = fieldInfo3.GetValue(value);
					if (value2 == null)
					{
						continue;
					}
					Type type = value2.GetType();
					ushort num = 0;
					MethodInfo method = type.GetMethod("GetID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (method != null)
					{
						object obj = method.Invoke(value2, null);
						if (obj != null)
						{
							try
							{
								num = Convert.ToUInt16(obj);
							}
							catch
							{
							}
						}
					}
					string arg = string.Empty;
					MethodInfo method2 = type.GetMethod("GetName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (method2 != null)
					{
						arg = method2.Invoke(value2, null)?.ToString() ?? string.Empty;
					}
					string name = type.Name;
					Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] Buffer scan — type='" + name + "', " + $"GetName()='{arg}', GetID()={num}"));
					if (name.IndexOf("artifact", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("prop", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						Plugin.Logger.LogInfo((object)"[ContentEvaluatorPatch] >> Artifact/prop event — dumping all fields:");
						Type type2 = type;
						while (type2 != null && type2 != typeof(object))
						{
							FieldInfo[] fields = type2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							foreach (FieldInfo fieldInfo4 in fields)
							{
								try
								{
									object value3 = fieldInfo4.GetValue(value2);
									Object val = (Object)((value3 is Object) ? value3 : null);
									string text = ((val != null) ? ("UnityObject(name='" + val.name + "', type='" + ((object)val).GetType().Name + "')") : (value3?.ToString() ?? "null"));
									Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch]   [" + type2.Name + "]." + fieldInfo4.Name + " = " + text));
								}
								catch (Exception ex)
								{
									Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch]   [" + type2.Name + "]." + fieldInfo4.Name + " = <error: " + ex.Message + ">"));
								}
							}
							type2 = type2.BaseType;
						}
					}
					string text2 = null;
					if (name.Equals("ArtifactContentEvent", StringComparison.Ordinal))
					{
						FieldInfo fieldInfo5 = null;
						string text3 = null;
						string[] array = new string[3] { "artifact", "content", "item" };
						foreach (string text4 in array)
						{
							fieldInfo5 = AccessTools.Field(type, text4);
							if (fieldInfo5 != null)
							{
								text3 = text4;
								break;
							}
						}
						if (fieldInfo5 != null)
						{
							Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] ArtifactContentEvent: resolved field name = '" + text3 + "'"));
							object value4 = fieldInfo5.GetValue(value2);
							Object val2 = (Object)((value4 is Object) ? value4 : null);
							if (val2 != null)
							{
								text2 = val2.name;
								Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] ArtifactContentEvent: " + $"assetName='{text2}', ID={num}"));
							}
							else
							{
								Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] ArtifactContentEvent: field '" + text3 + "' value is not UnityEngine.Object (actual type: '" + (value4?.GetType().Name ?? "null") + "')"));
							}
						}
						else
						{
							FieldInfo[] fields2 = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							string text5 = string.Join(", ", Array.ConvertAll(fields2, (FieldInfo f) => f.Name));
							Plugin.Logger.LogWarning((object)("[ContentEvaluatorPatch] ArtifactContentEvent: could not find 'content'/'item'/'artifact' field. Available fields: [" + text5 + "]"));
						}
					}
					TryFireEntityCheck(name, num, text2, fired);
				}
			}
		}

		private static void TryFireEntityCheck(string rawName, ushort id, string? artifactDisplayName, HashSet<string> fired)
		{
			string text = null;
			if (id != 0)
			{
				text = FilmingLocationData.TryGetLocationById(id);
			}
			if (text == null && !string.IsNullOrEmpty(artifactDisplayName))
			{
				text = FilmingLocationData.TryGetLocationByTypeName(artifactDisplayName);
			}
			if (text == null)
			{
				text = FilmingLocationData.TryGetLocationByTypeName(rawName);
			}
			if (text == null && rawName.EndsWith("ContentEvent", StringComparison.OrdinalIgnoreCase))
			{
				text = FilmingLocationData.TryGetLocationByTypeName(rawName.Substring(0, rawName.Length - "ContentEvent".Length));
			}
			if (text == null)
			{
				Plugin.Logger.LogDebug((object)($"[ContentEvaluatorPatch] No AP location for: class='{rawName}', ID={id}, " + "artifactName='" + (artifactDisplayName ?? "n/a") + "'. Add to FilmingLocationData if this is a new monster/artifact."));
			}
			else
			{
				if (fired.Contains(text))
				{
					return;
				}
				fired.Add(text);
				string text2 = text;
				if (_monstersFilmedThisDay.Contains(text2))
				{
					Plugin.Logger.LogDebug((object)("[ContentEvaluatorPatch] '" + text2 + "' already filmed this dive — skipping tier advancement."));
					return;
				}
				_monstersFilmedThisDay.Add(text2);
				bool monsterTiersEnabled = APSave.saveData.monsterTiersEnabled;
				long id2 = LocationData.GetId(text2);
				long num = (monsterTiersEnabled ? LocationData.GetId(text2 + " 2") : (-1));
				long num2 = (monsterTiersEnabled ? LocationData.GetId(text2 + " 3") : (-1));
				string text3 = null;
				if (id2 >= 0 && !APSave.IsLocationChecked(id2))
				{
					text3 = text2;
				}
				else if (num >= 0 && !APSave.IsLocationChecked(num))
				{
					text3 = text2 + " 2";
				}
				else if (num2 >= 0 && !APSave.IsLocationChecked(num2))
				{
					text3 = text2 + " 3";
				}
				if (text3 == null)
				{
					Plugin.Logger.LogDebug((object)("[ContentEvaluatorPatch] All tiers for '" + text2 + "' already checked."));
					return;
				}
				long id3 = LocationData.GetId(text3);
				if (id3 < 0)
				{
					Plugin.Logger.LogDebug((object)("[ContentEvaluatorPatch] '" + text3 + "' not in AP location table."));
					return;
				}
				Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] Filming check → " + text3));
				Plugin.SendCheck(id3);
				BroadcastAPCheckNotification(text3);
			}
		}

		private static void BroadcastAPCheckNotification(string locationName)
		{
			if (string.IsNullOrEmpty(locationName))
			{
				return;
			}
			try
			{
				SurfaceNetworkHandler instance = SurfaceNetworkHandler.Instance;
				if (!((Object)(object)instance == (Object)null))
				{
					if ((Object)(object)((Component)instance).GetComponent<SurfaceAPNotificationBroadcaster>() == (Object)null)
					{
						((Component)instance).gameObject.AddComponent<SurfaceAPNotificationBroadcaster>();
					}
					((MonoBehaviourPun)instance).photonView.RPC("RPCA_BroadcastAPCheckNotification", (RpcTarget)0, new object[1] { locationName });
					Plugin.Logger.LogDebug((object)("[ContentEvaluatorPatch] Broadcast AP notification RPC: '" + locationName + "'"));
				}
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[ContentEvaluatorPatch] BroadcastAPCheckNotification failed: " + ex.Message));
			}
		}

		private static int TryGetCurrentDay()
		{
			Type type = AccessTools.TypeByName("GameAPI");
			if (type != null)
			{
				PropertyInfo propertyInfo = AccessTools.Property(type, "CurrentDay");
				if (propertyInfo != null && propertyInfo.GetValue(null) is int num && num > 0)
				{
					return num;
				}
			}
			Type type2 = AccessTools.TypeByName("SurfaceNetworkHandler");
			if (type2 != null)
			{
				PropertyInfo propertyInfo2 = AccessTools.Property(type2, "RoomStats");
				if (propertyInfo2 != null)
				{
					object value = propertyInfo2.GetValue(null);
					if (value != null)
					{
						PropertyInfo propertyInfo3 = AccessTools.Property(value.GetType(), "CurrentDay");
						if (propertyInfo3 != null && propertyInfo3.GetValue(value) is int num2 && num2 > 0)
						{
							return num2;
						}
					}
				}
			}
			return 0;
		}
	}
	internal class SurfaceAPNotificationBroadcaster : MonoBehaviour
	{
		[PunRPC]
		public void RPCA_BroadcastAPCheckNotification(string locationName)
		{
			if (!string.IsNullOrEmpty(locationName) && !PhotonNetwork.IsMasterClient)
			{
				Plugin.Logger.LogInfo((object)("[SurfaceAPNotificationBroadcaster] Showing AP check notification: '" + locationName + "'"));
				APNotificationUI.ShowLocationFound(locationName);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerHandler))]
	[HarmonyPriority(400)]
	internal static class LateJoinSyncPatch
	{
		internal const string KeyDiveBellCharger = "AP_DBC";

		internal const string KeyDiveBellO2 = "AP_O2";

		internal const string KeyPendingMoney = "AP_PM";

		[HarmonyPostfix]
		[HarmonyPatch("AddPlayer")]
		private static void AddPlayerPostfix(Player player)
		{
			if (PhotonNetwork.IsMasterClient && !player.IsLocal && Plugin.connection.connected)
			{
				Player controller = player.refs.view.Controller;
				Plugin.Logger.LogInfo((object)("[LateJoinSync] New player '" + controller.NickName + "' joined — broadcasting AP world state."));
				BroadcastWorldState();
				SyncHatLabelsToPlayer(controller);
			}
		}

		private static void SyncHatLabelsToPlayer(Player photonPlayer)
		{
			try
			{
				if ((Object)(object)HatShopAPSyncBehaviour.Instance == (Object)null)
				{
					Plugin.Logger.LogDebug((object)"[LateJoinSync] SyncHatLabelsToPlayer: HatShopAPSyncBehaviour not ready — skipping targeted sync.");
					return;
				}
				Type type = AccessTools.TypeByName("HatShop");
				object obj = ((type != null) ? AccessTools.Field(type, "instance") : null)?.GetValue(null);
				if (obj == null)
				{
					Plugin.Logger.LogDebug((object)"[LateJoinSync] SyncHatLabelsToPlayer: HatShop.instance is null — skipping targeted sync.");
					return;
				}
				if (!(AccessTools.Field(obj.GetType(), "hatBuyInteractables")?.GetValue(obj) is List<HatBuyInteractable> list) || list.Count == 0)
				{
					Plugin.Logger.LogDebug((object)"[LateJoinSync] SyncHatLabelsToPlayer: hatBuyInteractables empty — skipping targeted sync.");
					return;
				}
				string[] array = new string[list.Count];
				for (int i = 0; i < list.Count; i++)
				{
					HatBuyInteractable val = list[i];
					string value = string.Empty;
					if ((Object)(object)val != (Object)null)
					{
						HatShopRestockLabelPatch.ScoutedNames.TryGetValue(val, out value);
					}
					array[i] = value;
				}
				object? obj2 = AccessTools.Field(obj.GetType(), "view")?.GetValue(obj);
				PhotonView val2 = (PhotonView)((obj2 is PhotonView) ? obj2 : null);
				if ((Object)(object)val2 == (Object)null)
				{
					Plugin.Logger.LogWarning((object)"[LateJoinSync] SyncHatLabelsToPlayer: HatShop PhotonView is null — cannot send targeted label sync.");
					return;
				}
				val2.RPC("RPCA_SyncArchipelagoLabels", photonPlayer, new object[1] { array });
				Plugin.Logger.LogInfo((object)$"[LateJoinSync] Sent {array.Length} hat label(s) directly to '{photonPlayer.NickName}'.");
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[LateJoinSync] SyncHatLabelsToPlayer exception: " + ex.Message));
			}
		}

		internal static void BroadcastWorldState()
		{
			//IL_0008: 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)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			if (PhotonNetwork.IsMasterClient)
			{
				Hashtable val = new Hashtable
				{
					[(object)"AP_DBC"] = APSave.saveData.diveBellChargerUnlocked,
					[(object)"AP_O2"] = APSave.saveData.diveBellO2Unlocked,
					[(object)"AP_PM"] = APSave.saveData.pendingMoney
				};
				Room currentRoom = PhotonNetwork.CurrentRoom;
				if (currentRoom != null)
				{
					currentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
				}
				Plugin.Logger.LogDebug((object)"[LateJoinSync] Room properties updated with AP world state.");
			}
		}

		internal static void ApplyRoomProperties()
		{
			Room currentRoom = PhotonNetwork.CurrentRoom;
			Hashtable val = ((currentRoom != null) ? ((RoomInfo)currentRoom).CustomProperties : null);
			if (val != null)
			{
				if (((Dictionary<object, object>)(object)val).TryGetValue((object)"AP_DBC", out object value) && value is bool && (bool)value && !APSave.saveData.diveBellChargerUnlocked)
				{
					APSave.saveData.diveBellChargerUnlocked = true;
					Plugin.Logger.LogInfo((object)"[LateJoinSync] Applied DiveBellCharger from room properties.");
				}
				if (((Dictionary<object, object>)(object)val).TryGetValue((object)"AP_O2", out object value2) && value2 is bool && (bool)value2 && !APSave.saveData.diveBellO2Unlocked)
				{
					APSave.saveData.diveBellO2Unlocked = true;
					Plugin.Logger.LogInfo((object)"[LateJoinSync] Applied DiveBellO2 from room properties.");
				}
				if (((Dictionary<object, object>)(object)val).TryGetValue((object)"AP_PM", out object value3) && value3 is int num && num > 0)
				{
					Plugin.Logger.LogDebug((object)$"[LateJoinSync] Room has {num} pending AP money (master will apply).");
				}
				APSave.Flush();
			}
		}
	}
	[HarmonyPatch(typeof(SurfaceNetworkHandler))]
	[HarmonyPriority(400)]
	internal static class SurfaceApplyRoomPropsPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("RPCM_StartGame")]
		private static void RPCM_StartGamePostfix()
		{
			ContentEvaluatorPatch.ResetDailyFilmingState();
			if (Plugin.connection.connected)
			{
				LateJoinSyncPatch.ApplyRoomProperties();
				if (PhotonNetwork.IsMasterClient)
				{
					LateJoinSyncPatch.BroadcastWorldState();
				}
			}
		}
	}
	[HarmonyPatch(typeof(SurfaceNetworkHandler), "RPCA_Sleep")]
	[HarmonyPriority(400)]
	internal static class SurfaceSleepResetPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			ContentEvaluatorPatch.ResetDailyFilmingState();
		}
	}
	[HarmonyPatch(typeof(MainMenuHandler), "Start")]
	internal static class MainMenuAPPatch
	{
		[CompilerGenerated]
		private sealed class <InjectPanelDelayed>d__1 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public MainMenuHandler menu;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <InjectPanelDelayed>d__1(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(3f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if ((Object)(object)GameObject.Find("AP_ConnectionPanel") != (Object)null)
					{
						return false;
					}
					InjectPanel(menu);
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static void Postfix(MainMenuHandler __instance)
		{
			if (!((Object)(object)GameObject.Find("AP_ConnectionPanel") != (Object)null))
			{
				Plugin.Logger.LogDebug((object)"[MainMenuAPPatch] MainMenuHandler.Start() completed — starting 3-second coroutine before injecting AP panel.");
				((MonoBehaviour)__instance).StartCoroutine(InjectPanelDelayed(__instance));
			}
		}

		[IteratorStateMachine(typeof(<InjectPanelDelayed>d__1))]
		private static IEnumerator InjectPanelDelayed(MainMenuHandler menu)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <InjectPanelDelayed>d__1(0)
			{
				menu = menu
			};
		}

		private static void InjectPanel(MainMenuHandler menu)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Plugin.Logger.LogInfo((object)"[MainMenuAPPatch] Injecting AP panel into main-menu UI.");
				Transform val = (((Object)(object)menu.UIHandler != (Object)null) ? ((Component)menu.UIHandler).transform : ((Component)menu).transform);
				Plugin.Logger.LogDebug((object)("[MainMenuAPPatch] Using parent: " + ((Object)val).name + " (full path: " + GetPath(val) + ")"));
				GameObject val2 = new GameObject("AP_ConnectionPanel");
				val2.transform.SetParent(val, false);
				val2.transform.localScale = Vector3.one;
				RectTransform obj = val2.GetComponent<RectTransform>() ?? val2.AddComponent<RectTransform>();
				obj.anchorMin = new Vector2(1f, 1f);
				obj.anchorMax = new Vector2(1f, 1f);
				obj.pivot = new Vector2(1f, 1f);
				obj.anchoredPosition = new Vector2(-10f, -10f);
				obj.sizeDelta = new Vector2(350f, 0f);
				val2.AddComponent<APConnectionPanelUI>();
				val2.SetActive(true);
				Transform val3 = val;
				while ((Object)(object)val3 != (Object)null)
				{
					if (!((Component)val3).gameObject.activeSelf)
					{
						Plugin.Logger.LogDebug((object)("[MainMenuAPPatch] Activating inactive ancestor: " + GetPath(val3)));
						((Component)val3).gameObject.SetActive(true);
					}
					val3 = val3.parent;
				}
				val2.transform.SetAsLastSibling();
				Plugin.Logger.LogInfo((object)"[MainMenuAPPatch] AP connection panel successfully injected into main-menu UI.");
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[MainMenuAPPatch] Exception while injecting AP panel: {arg}");
			}
		}

		private static string GetPath(Transform t)
		{
			if ((Object)(object)t.parent == (Object)null)
			{
				return ((Object)t).name;
			}
			return GetPath(t.parent) + "/" + ((Object)t).name;
		}
	}
	[HarmonyPatch(typeof(MetaProgressionHandler), "GetUnlockedHats")]
	internal static class GetUnlockedHatsPatch
	{
		[HarmonyPostfix]
		private static void Postfix(ref int[] __result)
		{
			if (Plugin.connection.connected)
			{
				int[] array = new int[APSave.saveData.sessionUnlockedHats.Count];
				APSave.saveData.sessionUnlockedHats.CopyTo(array);
				__result = array;
				Plugin.Logger.LogDebug((object)$"[GetUnlockedHatsPatch] AP active — returning {array.Length} session hat(s).");
			}
		}
	}
	[HarmonyPatch(typeof(MetaProgressionHandler), "UnlockHat")]
	internal static class UnlockHatPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(int hat)
		{
			if (!Plugin.connection.connected)
			{
				return true;
			}
			if (APSave.saveData.sessionUnlockedHats.Add(hat))
			{
				Plugin.Logger.LogInfo((object)$"[UnlockHatPatch] AP active — hat {hat} added to session (no disk write).");
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(MetaProgressionHandler), "AddMetaCoins")]
	internal static class AddMetaCoinsPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(int amount)
		{
			if (!Plugin.connection.connected)
			{
				return true;
			}
			Plugin.Logger.LogDebug((object)$"[AddMetaCoinsPatch] AP active — vanilla AddMetaCoins({amount}) suppressed.");
			return false;
		}
	}
	[HarmonyPatch(typeof(MetaProgressionHandler), "RemoveMetaCoins")]
	internal static class RemoveMetaCoinsPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(int amount)
		{
			if (!Plugin.connection.connected)
			{
				return true;
			}
			if (PhotonNetwork.IsMasterClient)
			{
				Plugin.connection.AddMetaCoinsDelta(-amount);
				Plugin.Logger.LogInfo((object)$"[RemoveMetaCoinsPatch] AP active — sent -{amount} MC to DataStorage.");
			}
			else
			{
				Plugin.Logger.LogDebug((object)("[RemoveMetaCoinsPatch] AP active — non-master client " + $"swallowing local RemoveMetaCoins({amount}); listener will sync."));
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(MetaProgressionHandler), "UpdateAndSave")]
	internal static class UpdateAndSavePatch
	{
		[HarmonyPrefix]
		private static bool Prefix()
		{
			return !Plugin.connection.connected;
		}
	}
	internal static class ModManagerAPPatch
	{
		internal static void TryApplyPatch(Harmony harmony)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("ModManagerUI");
			if (type == null)
			{
				Plugin.Logger.LogWarning((object)"[ModManagerAPPatch] ModManagerUI type not found in any loaded assembly. The AP connection panel will not be injected into the Mod Manager. This is expected if the game's built-in Mod Manager is absent.");
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "OnEnable", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Logger.LogWarning((object)"[ModManagerAPPatch] Could not find ModManagerUI.OnEnable() — skipping patch.");
				return;
			}
			HarmonyMethod val = new HarmonyMethod(typeof(ModManagerAPPatch), "OnEnable_Postfix", (Type[])null);
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Plugin.Logger.LogInfo((object)"[ModManagerAPPatch] Successfully patched ModManagerUI.OnEnable() with AP panel injector.");
		}

		private static void OnEnable_Postfix(MonoBehaviour __instance)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Plugin.Logger.LogInfo((object)"[ModManagerAPPatch] OnEnable_Postfix fired — checking for existing AP panel.");
				if ((Object)(object)GameObject.Find("AP_ConnectionPanel") != (Object)null)
				{
					Plugin.Logger.LogDebug((object)"[ModManagerAPPatch] AP_ConnectionPanel already exists in scene — skipping.");
					return;
				}
				Transform transform = ((Component)__instance).transform;
				FieldInfo fieldInfo = AccessTools.Field(((object)__instance).GetType(), "modlist");
				if (fieldInfo != null)
				{
					object? value = fieldInfo.GetValue(__instance);
					Object val = (Object)((value is Object) ? value : null);
					Component val2 = (Component)(object)((val is Component) ? val : null);
					if (val2 != null)
					{
						transform = val2.transform;
					}
					else
					{
						GameObject val3 = (GameObject)(object)((val is GameObject) ? val : null);
						if (val3 != null)
						{
							transform = val3.transform;
						}
						else
						{
							Plugin.Logger.LogWarning((object)"[ModManagerAPPatch] 'modlist' field found but could not be cast to Component or GameObject — falling back to root transform.");
						}
					}
				}
				else
				{
					Plugin.Logger.LogWarning((object)"[ModManagerAPPatch] Could not find 'modlist' field on ModManagerUI — falling back to root transform.");
				}
				GameObject val4 = new GameObject("AP_ConnectionPanel");
				val4.transform.SetParent(transform, false);
				LayoutElement obj = val4.AddComponent<LayoutElement>();
				obj.preferredWidth = -1f;
				obj.preferredHeight = 155f;
				val4.AddComponent<APConnectionPanelUI>();
				val4.transform.SetAsLastSibling();
				Plugin.Logger.LogInfo((object)"[ModManagerAPPatch] AP connection panel successfully injected into ModManagerUI.");
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[ModManagerAPPatch] Exception while injecting AP panel: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(ShopHandler))]
	internal static class MoneyPatch
	{
		[HarmonyPatch("InitShop")]
		[HarmonyPostfix]
		private static void InitShopPostfix(ShopHandler __instance)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				int pendingMoney = APSave.saveData.pendingMoney;
				if (pendingMoney > 0)
				{
					Plugin.Logger.LogInfo((object)$"[MoneyPatch] Draining {pendingMoney} pending AP money

Archipelago.MultiClient.Net.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Archipelago.MultiClient.Net.Colors;
using Archipelago.MultiClient.Net.ConcurrentCollection;
using Archipelago.MultiClient.Net.Converters;
using Archipelago.MultiClient.Net.DataPackage;
using Archipelago.MultiClient.Net.Enums;
using Archipelago.MultiClient.Net.Exceptions;
using Archipelago.MultiClient.Net.Extensions;
using Archipelago.MultiClient.Net.Helpers;
using Archipelago.MultiClient.Net.MessageLog.Messages;
using Archipelago.MultiClient.Net.MessageLog.Parts;
using Archipelago.MultiClient.Net.Models;
using Archipelago.MultiClient.Net.Packets;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: ComVisible(false)]
[assembly: Guid("35a803ad-85ed-42e9-b1e3-c6b72096f0c1")]
[assembly: InternalsVisibleTo("Archipelago.MultiClient.Net.Tests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("Jarno Westhof, Hussein Farran, Zach Parks")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyDescription("A client library for use with .NET based prog-langs for interfacing with Archipelago hosts.")]
[assembly: AssemblyFileVersion("6.7.1.0")]
[assembly: AssemblyInformationalVersion("6.7.1+0c57591db30f2497b0b4fef87164aa2bbe2e51b2")]
[assembly: AssemblyProduct("Archipelago.MultiClient.Net")]
[assembly: AssemblyTitle("Archipelago.MultiClient.Net")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/ArchipelagoMW/Archipelago.MultiClient.Net")]
[assembly: AssemblyVersion("6.7.1.0")]
internal interface IConcurrentHashSet<T>
{
	bool TryAdd(T item);

	bool Contains(T item);

	void UnionWith(T[] otherSet);

	T[] ToArray();

	ReadOnlyCollection<T> AsToReadOnlyCollection();

	ReadOnlyCollection<T> AsToReadOnlyCollectionExcept(IConcurrentHashSet<T> otherSet);
}
public class AttemptingStringEnumConverter : StringEnumConverter
{
	public AttemptingStringEnumConverter()
	{
	}

	public AttemptingStringEnumConverter(Type namingStrategyType)
		: base(namingStrategyType)
	{
	}

	public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
	{
		try
		{
			return ((StringEnumConverter)this).ReadJson(reader, objectType, existingValue, serializer);
		}
		catch (JsonSerializationException)
		{
			return objectType.IsValueType ? Activator.CreateInstance(objectType) : null;
		}
	}
}
namespace Archipelago.MultiClient.Net
{
	[Serializable]
	public abstract class ArchipelagoPacketBase
	{
		[JsonIgnore]
		internal JObject jobject;

		[JsonProperty("cmd")]
		[JsonConverter(typeof(StringEnumConverter))]
		public abstract ArchipelagoPacketType PacketType { get; }

		public JObject ToJObject()
		{
			return jobject;
		}
	}
	public interface IArchipelagoSession : IArchipelagoSessionActions
	{
		IArchipelagoSocketHelper Socket { get; }

		IReceivedItemsHelper Items { get; }

		ILocationCheckHelper Locations { get; }

		IPlayerHelper Players { get; }

		IDataStorageHelper DataStorage { get; }

		IConnectionInfoProvider ConnectionInfo { get; }

		IRoomStateHelper RoomState { get; }

		IMessageLogHelper MessageLog { get; }

		IHintsHelper Hints { get; }

		Task<RoomInfoPacket> ConnectAsync();

		Task<LoginResult> LoginAsync(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true);

		LoginResult TryConnectAndLogin(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true);
	}
	public class ArchipelagoSession : IArchipelagoSession, IArchipelagoSessionActions
	{
		private const int ArchipelagoConnectionTimeoutInSeconds = 4;

		private ConnectionInfoHelper connectionInfo;

		private TaskCompletionSource<LoginResult> loginResultTask = new TaskCompletionSource<LoginResult>();

		private TaskCompletionSource<RoomInfoPacket> roomInfoPacketTask = new TaskCompletionSource<RoomInfoPacket>();

		public IArchipelagoSocketHelper Socket { get; }

		public IReceivedItemsHelper Items { get; }

		public ILocationCheckHelper Locations { get; }

		public IPlayerHelper Players { get; }

		public IDataStorageHelper DataStorage { get; }

		public IConnectionInfoProvider ConnectionInfo => connectionInfo;

		public IRoomStateHelper RoomState { get; }

		public IMessageLogHelper MessageLog { get; }

		public IHintsHelper Hints { get; }

		internal ArchipelagoSession(IArchipelagoSocketHelper socket, IReceivedItemsHelper items, ILocationCheckHelper locations, IPlayerHelper players, IRoomStateHelper roomState, ConnectionInfoHelper connectionInfoHelper, IDataStorageHelper dataStorage, IMessageLogHelper messageLog, IHintsHelper createHints)
		{
			Socket = socket;
			Items = items;
			Locations = locations;
			Players = players;
			RoomState = roomState;
			connectionInfo = connectionInfoHelper;
			DataStorage = dataStorage;
			MessageLog = messageLog;
			Hints = createHints;
			socket.PacketReceived += Socket_PacketReceived;
		}

		private void Socket_PacketReceived(ArchipelagoPacketBase packet)
		{
			if (!(packet is ConnectedPacket) && !(packet is ConnectionRefusedPacket))
			{
				if (packet is RoomInfoPacket result)
				{
					roomInfoPacketTask.TrySetResult(result);
				}
			}
			else
			{
				loginResultTask.TrySetResult(LoginResult.FromPacket(packet));
			}
		}

		public Task<RoomInfoPacket> ConnectAsync()
		{
			roomInfoPacketTask = new TaskCompletionSource<RoomInfoPacket>();
			Task.Factory.StartNew(delegate
			{
				try
				{
					Task task = Socket.ConnectAsync();
					task.Wait(TimeSpan.FromSeconds(4.0));
					if (!task.IsCompleted)
					{
						roomInfoPacketTask.TrySetCanceled();
					}
				}
				catch (AggregateException)
				{
					roomInfoPacketTask.TrySetCanceled();
				}
			});
			return roomInfoPacketTask.Task;
		}

		public Task<LoginResult> LoginAsync(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true)
		{
			loginResultTask = new TaskCompletionSource<LoginResult>();
			if (!roomInfoPacketTask.Task.IsCompleted)
			{
				loginResultTask.TrySetResult(new LoginFailure("You are not connected, run ConnectAsync() first"));
				return loginResultTask.Task;
			}
			connectionInfo.SetConnectionParameters(game, tags, itemsHandlingFlags, uuid);
			try
			{
				Socket.SendPacket(BuildConnectPacket(name, password, version, requestSlotData));
			}
			catch (ArchipelagoSocketClosedException)
			{
				loginResultTask.TrySetResult(new LoginFailure("You are not connected, run ConnectAsync() first"));
				return loginResultTask.Task;
			}
			SetResultAfterTimeout(loginResultTask, 4, new LoginFailure("Connection timed out."));
			return loginResultTask.Task;
		}

		private static void SetResultAfterTimeout<T>(TaskCompletionSource<T> task, int timeoutInSeconds, T result)
		{
			new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)).Token.Register(delegate
			{
				task.TrySetResult(result);
			});
		}

		public LoginResult TryConnectAndLogin(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true)
		{
			Task<RoomInfoPacket> task = ConnectAsync();
			try
			{
				task.Wait(TimeSpan.FromSeconds(4.0));
			}
			catch (AggregateException ex)
			{
				if (ex.GetBaseException() is OperationCanceledException)
				{
					return new LoginFailure("Connection timed out.");
				}
				return new LoginFailure(ex.GetBaseException().Message);
			}
			if (!task.IsCompleted)
			{
				return new LoginFailure("Connection timed out.");
			}
			return LoginAsync(game, name, itemsHandlingFlags, version, tags, uuid, password, requestSlotData).Result;
		}

		private ConnectPacket BuildConnectPacket(string name, string password, Version version, bool requestSlotData)
		{
			return new ConnectPacket
			{
				Game = ConnectionInfo.Game,
				Name = name,
				Password = password,
				Tags = ConnectionInfo.Tags,
				Uuid = ConnectionInfo.Uuid,
				Version = ((version != null) ? new NetworkVersion(version) : new NetworkVersion(0, 6, 0)),
				ItemsHandling = ConnectionInfo.ItemsHandlingFlags,
				RequestSlotData = requestSlotData
			};
		}

		public void Say(string message)
		{
			Socket.SendPacket(new SayPacket
			{
				Text = message
			});
		}

		public void SetClientState(ArchipelagoClientState state)
		{
			Socket.SendPacket(new StatusUpdatePacket
			{
				Status = state
			});
		}

		public void SetGoalAchieved()
		{
			SetClientState(ArchipelagoClientState.ClientGoal);
		}
	}
	public interface IArchipelagoSessionActions
	{
		void Say(string message);

		void SetClientState(ArchipelagoClientState state);

		void SetGoalAchieved();
	}
	public static class ArchipelagoSessionFactory
	{
		public static ArchipelagoSession CreateSession(Uri uri)
		{
			ArchipelagoSocketHelper socket = new ArchipelagoSocketHelper(uri);
			DataPackageCache cache = new DataPackageCache(socket);
			ConnectionInfoHelper connectionInfoHelper = new ConnectionInfoHelper(socket);
			PlayerHelper playerHelper = new PlayerHelper(socket, connectionInfoHelper);
			ItemInfoResolver itemInfoResolver = new ItemInfoResolver(cache, connectionInfoHelper);
			LocationCheckHelper locationCheckHelper = new LocationCheckHelper(socket, itemInfoResolver, connectionInfoHelper, playerHelper);
			ReceivedItemsHelper items = new ReceivedItemsHelper(socket, locationCheckHelper, itemInfoResolver, connectionInfoHelper, playerHelper);
			RoomStateHelper roomStateHelper = new RoomStateHelper(socket, locationCheckHelper);
			DataStorageHelper dataStorageHelper = new DataStorageHelper(socket, connectionInfoHelper);
			MessageLogHelper messageLog = new MessageLogHelper(socket, itemInfoResolver, playerHelper, connectionInfoHelper);
			HintsHelper createHints = new HintsHelper(socket, playerHelper, locationCheckHelper, roomStateHelper, dataStorageHelper);
			return new ArchipelagoSession(socket, items, locationCheckHelper, playerHelper, roomStateHelper, connectionInfoHelper, dataStorageHelper, messageLog, createHints);
		}

		public static ArchipelagoSession CreateSession(string hostname, int port = 38281)
		{
			return CreateSession(ParseUri(hostname, port));
		}

		internal static Uri ParseUri(string hostname, int port)
		{
			string text = hostname;
			if (!text.StartsWith("ws://") && !text.StartsWith("wss://"))
			{
				text = "unspecified://" + text;
			}
			if (!text.Substring(text.IndexOf("://", StringComparison.Ordinal) + 3).Contains(":"))
			{
				text += $":{port}";
			}
			if (text.EndsWith(":"))
			{
				text += port;
			}
			return new Uri(text);
		}
	}
	public abstract class LoginResult
	{
		public abstract bool Successful { get; }

		public static LoginResult FromPacket(ArchipelagoPacketBase packet)
		{
			if (!(packet is ConnectedPacket connectedPacket))
			{
				if (packet is ConnectionRefusedPacket connectionRefusedPacket)
				{
					return new LoginFailure(connectionRefusedPacket);
				}
				throw new ArgumentOutOfRangeException("packet", "packet is not a connection result packet");
			}
			return new LoginSuccessful(connectedPacket);
		}
	}
	public class LoginSuccessful : LoginResult
	{
		public override bool Successful => true;

		public int Team { get; }

		public int Slot { get; }

		public Dictionary<string, object> SlotData { get; }

		public LoginSuccessful(ConnectedPacket connectedPacket)
		{
			Team = connectedPacket.Team;
			Slot = connectedPacket.Slot;
			SlotData = connectedPacket.SlotData;
		}
	}
	public class LoginFailure : LoginResult
	{
		public override bool Successful => false;

		public ConnectionRefusedError[] ErrorCodes { get; }

		public string[] Errors { get; }

		public LoginFailure(ConnectionRefusedPacket connectionRefusedPacket)
		{
			if (connectionRefusedPacket.Errors != null)
			{
				ErrorCodes = connectionRefusedPacket.Errors.ToArray();
				Errors = ErrorCodes.Select(GetErrorMessage).ToArray();
			}
			else
			{
				ErrorCodes = new ConnectionRefusedError[0];
				Errors = new string[0];
			}
		}

		public LoginFailure(string message)
		{
			ErrorCodes = new ConnectionRefusedError[0];
			Errors = new string[1] { message };
		}

		private static string GetErrorMessage(ConnectionRefusedError errorCode)
		{
			return errorCode switch
			{
				ConnectionRefusedError.InvalidSlot => "The slot name did not match any slot on the server.", 
				ConnectionRefusedError.InvalidGame => "The slot is set to a different game on the server.", 
				ConnectionRefusedError.SlotAlreadyTaken => "The slot already has a connection with a different uuid established.", 
				ConnectionRefusedError.IncompatibleVersion => "The client and server version mismatch.", 
				ConnectionRefusedError.InvalidPassword => "The password is invalid.", 
				ConnectionRefusedError.InvalidItemsHandling => "The item handling flags provided are invalid.", 
				_ => $"Unknown error: {errorCode}.", 
			};
		}
	}
	internal class TwoWayLookup<TA, TB> : IEnumerable<KeyValuePair<TB, TA>>, IEnumerable
	{
		private readonly Dictionary<TA, TB> aToB = new Dictionary<TA, TB>();

		private readonly Dictionary<TB, TA> bToA = new Dictionary<TB, TA>();

		public TA this[TB b] => bToA[b];

		public TB this[TA a] => aToB[a];

		public void Add(TA a, TB b)
		{
			aToB[a] = b;
			bToA[b] = a;
		}

		public void Add(TB b, TA a)
		{
			Add(a, b);
		}

		public bool TryGetValue(TA a, out TB b)
		{
			return aToB.TryGetValue(a, out b);
		}

		public bool TryGetValue(TB b, out TA a)
		{
			return bToA.TryGetValue(b, out a);
		}

		public IEnumerator<KeyValuePair<TB, TA>> GetEnumerator()
		{
			return bToA.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
	}
}
namespace Archipelago.MultiClient.Net.Packets
{
	public class BouncedPacket : BouncePacket
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Bounced;
	}
	public class BouncePacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Bounce;

		[JsonProperty("games")]
		public List<string> Games { get; set; } = new List<string>();


		[JsonProperty("slots")]
		public List<int> Slots { get; set; } = new List<int>();


		[JsonProperty("tags")]
		public List<string> Tags { get; set; } = new List<string>();


		[JsonProperty("data")]
		public Dictionary<string, JToken> Data { get; set; }
	}
	public class ConnectedPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Connected;

		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }

		[JsonProperty("players")]
		public NetworkPlayer[] Players { get; set; }

		[JsonProperty("missing_locations")]
		public long[] MissingChecks { get; set; }

		[JsonProperty("checked_locations")]
		public long[] LocationsChecked { get; set; }

		[JsonProperty("slot_data")]
		public Dictionary<string, object> SlotData { get; set; }

		[JsonProperty("slot_info")]
		public Dictionary<int, NetworkSlot> SlotInfo { get; set; }

		[JsonProperty("hint_points")]
		public int? HintPoints { get; set; }
	}
	public class ConnectionRefusedPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.ConnectionRefused;

		[JsonProperty("errors", ItemConverterType = typeof(AttemptingStringEnumConverter))]
		public ConnectionRefusedError[] Errors { get; set; }
	}
	public class ConnectPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Connect;

		[JsonProperty("password")]
		public string Password { get; set; }

		[JsonProperty("game")]
		public string Game { get; set; }

		[JsonProperty("name")]
		public string Name { get; set; }

		[JsonProperty("uuid")]
		public string Uuid { get; set; }

		[JsonProperty("version")]
		public NetworkVersion Version { get; set; }

		[JsonProperty("tags")]
		public string[] Tags { get; set; }

		[JsonProperty("items_handling")]
		public ItemsHandlingFlags ItemsHandling { get; set; }

		[JsonProperty("slot_data")]
		public bool RequestSlotData { get; set; }
	}
	public class ConnectUpdatePacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.ConnectUpdate;

		[JsonProperty("tags")]
		public string[] Tags { get; set; }

		[JsonProperty("items_handling")]
		public ItemsHandlingFlags? ItemsHandling { get; set; }
	}
	public class CreateHintsPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.CreateHints;

		[JsonProperty("locations")]
		public long[] Locations { get; set; }

		[JsonProperty("player")]
		public int Player { get; set; }

		[JsonProperty("status")]
		public HintStatus Status { get; set; }
	}
	public class DataPackagePacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.DataPackage;

		[JsonProperty("data")]
		public Archipelago.MultiClient.Net.Models.DataPackage DataPackage { get; set; }
	}
	public class GetDataPackagePacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.GetDataPackage;

		[JsonProperty("games")]
		public string[] Games { get; set; }
	}
	public class GetPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Get;

		[JsonProperty("keys")]
		public string[] Keys { get; set; }
	}
	public class InvalidPacketPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.InvalidPacket;

		[JsonProperty("type")]
		public InvalidPacketErrorType ErrorType { get; set; }

		[JsonProperty("text")]
		public string ErrorText { get; set; }

		[JsonProperty("original_cmd")]
		public ArchipelagoPacketType OriginalCmd { get; set; }
	}
	public class LocationChecksPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.LocationChecks;

		[JsonProperty("locations")]
		public long[] Locations { get; set; }
	}
	public class LocationInfoPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.LocationInfo;

		[JsonProperty("locations")]
		public NetworkItem[] Locations { get; set; }
	}
	public class LocationScoutsPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.LocationScouts;

		[JsonProperty("locations")]
		public long[] Locations { get; set; }

		[JsonProperty("create_as_hint")]
		public int CreateAsHint { get; set; }
	}
	public class PrintJsonPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.PrintJSON;

		[JsonProperty("data")]
		public JsonMessagePart[] Data { get; set; }

		[JsonProperty("type")]
		[JsonConverter(typeof(AttemptingStringEnumConverter))]
		public JsonMessageType? MessageType { get; set; }
	}
	public class ItemPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("receiving")]
		public int ReceivingPlayer { get; set; }

		[JsonProperty("item")]
		public NetworkItem Item { get; set; }
	}
	public class ItemCheatPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("receiving")]
		public int ReceivingPlayer { get; set; }

		[JsonProperty("item")]
		public NetworkItem Item { get; set; }

		[JsonProperty("team")]
		public int Team { get; set; }
	}
	public class HintPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("receiving")]
		public int ReceivingPlayer { get; set; }

		[JsonProperty("item")]
		public NetworkItem Item { get; set; }

		[JsonProperty("found")]
		public bool? Found { get; set; }
	}
	public class JoinPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }

		[JsonProperty("tags")]
		public string[] Tags { get; set; }
	}
	public class LeavePrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }
	}
	public class ChatPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }

		[JsonProperty("message")]
		public string Message { get; set; }
	}
	public class ServerChatPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("message")]
		public string Message { get; set; }
	}
	public class TutorialPrintJsonPacket : PrintJsonPacket
	{
	}
	public class TagsChangedPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }

		[JsonProperty("tags")]
		public string[] Tags { get; set; }
	}
	public class CommandResultPrintJsonPacket : PrintJsonPacket
	{
	}
	public class AdminCommandResultPrintJsonPacket : PrintJsonPacket
	{
	}
	public class GoalPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }
	}
	public class ReleasePrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }
	}
	public class CollectPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }
	}
	public class CountdownPrintJsonPacket : PrintJsonPacket
	{
		[JsonProperty("countdown")]
		public int RemainingSeconds { get; set; }
	}
	public class ReceivedItemsPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.ReceivedItems;

		[JsonProperty("index")]
		public int Index { get; set; }

		[JsonProperty("items")]
		public NetworkItem[] Items { get; set; }
	}
	public class RetrievedPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Retrieved;

		[JsonProperty("keys")]
		public Dictionary<string, JToken> Data { get; set; }
	}
	public class RoomInfoPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.RoomInfo;

		[JsonProperty("version")]
		public NetworkVersion Version { get; set; }

		[JsonProperty("generator_version")]
		public NetworkVersion GeneratorVersion { get; set; }

		[JsonProperty("tags")]
		public string[] Tags { get; set; }

		[JsonProperty("password")]
		public bool Password { get; set; }

		[JsonProperty("permissions")]
		public Dictionary<string, Permissions> Permissions { get; set; }

		[JsonProperty("hint_cost")]
		public int HintCostPercentage { get; set; }

		[JsonProperty("location_check_points")]
		public int LocationCheckPoints { get; set; }

		[JsonProperty("players")]
		public NetworkPlayer[] Players { get; set; }

		[JsonProperty("games")]
		public string[] Games { get; set; }

		[JsonProperty("datapackage_checksums")]
		public Dictionary<string, string> DataPackageChecksums { get; set; }

		[JsonProperty("seed_name")]
		public string SeedName { get; set; }

		[JsonProperty("time")]
		public double Timestamp { get; set; }
	}
	public class RoomUpdatePacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.RoomUpdate;

		[JsonProperty("tags")]
		public string[] Tags { get; set; }

		[JsonProperty("password")]
		public bool? Password { get; set; }

		[JsonProperty("permissions")]
		public Dictionary<string, Permissions> Permissions { get; set; } = new Dictionary<string, Permissions>();


		[JsonProperty("hint_cost")]
		public int? HintCostPercentage { get; set; }

		[JsonProperty("location_check_points")]
		public int? LocationCheckPoints { get; set; }

		[JsonProperty("players")]
		public NetworkPlayer[] Players { get; set; }

		[JsonProperty("hint_points")]
		public int? HintPoints { get; set; }

		[JsonProperty("checked_locations")]
		public long[] CheckedLocations { get; set; }
	}
	public class SayPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Say;

		[JsonProperty("text")]
		public string Text { get; set; }
	}
	public class SetNotifyPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.SetNotify;

		[JsonProperty("keys")]
		public string[] Keys { get; set; }
	}
	public class SetPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Set;

		[JsonProperty("key")]
		public string Key { get; set; }

		[JsonProperty("default")]
		public JToken DefaultValue { get; set; }

		[JsonProperty("operations")]
		public OperationSpecification[] Operations { get; set; }

		[JsonProperty("want_reply")]
		public bool WantReply { get; set; }

		[JsonExtensionData]
		public Dictionary<string, JToken> AdditionalArguments { get; set; }

		[OnDeserialized]
		internal void OnDeserializedMethod(StreamingContext context)
		{
			AdditionalArguments?.Remove("cmd");
		}
	}
	public class SetReplyPacket : SetPacket
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.SetReply;

		[JsonProperty("value")]
		public JToken Value { get; set; }

		[JsonProperty("original_value")]
		public JToken OriginalValue { get; set; }
	}
	public class StatusUpdatePacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.StatusUpdate;

		[JsonProperty("status")]
		public ArchipelagoClientState Status { get; set; }
	}
	public class SyncPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Sync;
	}
	internal class UnknownPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Unknown;
	}
	public class UpdateHintPacket : ArchipelagoPacketBase
	{
		public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.UpdateHint;

		[JsonProperty("player")]
		public int Player { get; set; }

		[JsonProperty("location")]
		public long Location { get; set; }

		[JsonProperty("status")]
		public HintStatus Status { get; set; }
	}
}
namespace Archipelago.MultiClient.Net.Models
{
	public struct Color : IEquatable<Color>
	{
		public static Color Red = new Color(byte.MaxValue, 0, 0);

		public static Color Green = new Color(0, 128, 0);

		public static Color Yellow = new Color(byte.MaxValue, byte.MaxValue, 0);

		public static Color Blue = new Color(0, 0, byte.MaxValue);

		public static Color Magenta = new Color(byte.MaxValue, 0, byte.MaxValue);

		public static Color Cyan = new Color(0, byte.MaxValue, byte.MaxValue);

		public static Color Black = new Color(0, 0, 0);

		public static Color White = new Color(byte.MaxValue, byte.MaxValue, byte.MaxValue);

		public static Color SlateBlue = new Color(106, 90, 205);

		public static Color Salmon = new Color(250, 128, 114);

		public static Color Plum = new Color(221, 160, 221);

		public byte R { get; set; }

		public byte G { get; set; }

		public byte B { get; set; }

		public Color(byte r, byte g, byte b)
		{
			R = r;
			G = g;
			B = b;
		}

		public override bool Equals(object obj)
		{
			if (obj is Color color && R == color.R && G == color.G)
			{
				return B == color.B;
			}
			return false;
		}

		public bool Equals(Color other)
		{
			if (R == other.R && G == other.G)
			{
				return B == other.B;
			}
			return false;
		}

		public override int GetHashCode()
		{
			return ((-1520100960 * -1521134295 + R.GetHashCode()) * -1521134295 + G.GetHashCode()) * -1521134295 + B.GetHashCode();
		}

		public static bool operator ==(Color left, Color right)
		{
			return left.Equals(right);
		}

		public static bool operator !=(Color left, Color right)
		{
			return !(left == right);
		}
	}
	public class DataPackage
	{
		[JsonProperty("games")]
		public Dictionary<string, GameData> Games { get; set; } = new Dictionary<string, GameData>();

	}
	public class DataStorageElement
	{
		internal DataStorageElementContext Context;

		internal List<OperationSpecification> Operations = new List<OperationSpecification>(0);

		internal DataStorageHelper.DataStorageUpdatedHandler Callbacks;

		internal Dictionary<string, JToken> AdditionalArguments = new Dictionary<string, JToken>(0);

		private JToken cachedValue;

		public event DataStorageHelper.DataStorageUpdatedHandler OnValueChanged
		{
			add
			{
				Context.AddHandler(Context.Key, value);
			}
			remove
			{
				Context.RemoveHandler(Context.Key, value);
			}
		}

		internal DataStorageElement(DataStorageElementContext context)
		{
			Context = context;
		}

		internal DataStorageElement(OperationType operationType, JToken value)
		{
			Operations = new List<OperationSpecification>(1)
			{
				new OperationSpecification
				{
					OperationType = operationType,
					Value = value
				}
			};
		}

		internal DataStorageElement(DataStorageElement source, OperationType operationType, JToken value)
			: this(source.Context)
		{
			Operations = source.Operations.ToList();
			Callbacks = source.Callbacks;
			AdditionalArguments = source.AdditionalArguments;
			Operations.Add(new OperationSpecification
			{
				OperationType = operationType,
				Value = value
			});
		}

		internal DataStorageElement(DataStorageElement source, Callback callback)
			: this(source.Context)
		{
			Operations = source.Operations.ToList();
			Callbacks = source.Callbacks;
			AdditionalArguments = source.AdditionalArguments;
			Callbacks = (DataStorageHelper.DataStorageUpdatedHandler)Delegate.Combine(Callbacks, callback.Method);
		}

		internal DataStorageElement(DataStorageElement source, AdditionalArgument additionalArgument)
			: this(source.Context)
		{
			Operations = source.Operations.ToList();
			Callbacks = source.Callbacks;
			AdditionalArguments = source.AdditionalArguments;
			AdditionalArguments[additionalArgument.Key] = additionalArgument.Value;
		}

		public static DataStorageElement operator ++(DataStorageElement a)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(1));
		}

		public static DataStorageElement operator --(DataStorageElement a)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(-1));
		}

		public static DataStorageElement operator +(DataStorageElement a, int b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator +(DataStorageElement a, long b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator +(DataStorageElement a, float b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator +(DataStorageElement a, double b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator +(DataStorageElement a, decimal b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator +(DataStorageElement a, string b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator +(DataStorageElement a, JToken b)
		{
			return new DataStorageElement(a, OperationType.Add, b);
		}

		public static DataStorageElement operator +(DataStorageElement a, IEnumerable b)
		{
			return new DataStorageElement(a, OperationType.Add, (JToken)(object)JArray.FromObject((object)b));
		}

		public static DataStorageElement operator +(DataStorageElement a, OperationSpecification s)
		{
			return new DataStorageElement(a, s.OperationType, s.Value);
		}

		public static DataStorageElement operator +(DataStorageElement a, Callback c)
		{
			return new DataStorageElement(a, c);
		}

		public static DataStorageElement operator +(DataStorageElement a, AdditionalArgument arg)
		{
			return new DataStorageElement(a, arg);
		}

		public static DataStorageElement operator *(DataStorageElement a, int b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator *(DataStorageElement a, long b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator *(DataStorageElement a, float b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator *(DataStorageElement a, double b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator *(DataStorageElement a, decimal b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator %(DataStorageElement a, int b)
		{
			return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator %(DataStorageElement a, long b)
		{
			return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator %(DataStorageElement a, float b)
		{
			return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator %(DataStorageElement a, double b)
		{
			return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator %(DataStorageElement a, decimal b)
		{
			return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator ^(DataStorageElement a, int b)
		{
			return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator ^(DataStorageElement a, long b)
		{
			return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator ^(DataStorageElement a, float b)
		{
			return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator ^(DataStorageElement a, double b)
		{
			return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator ^(DataStorageElement a, decimal b)
		{
			return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b));
		}

		public static DataStorageElement operator -(DataStorageElement a, int b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b)));
		}

		public static DataStorageElement operator -(DataStorageElement a, long b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b)));
		}

		public static DataStorageElement operator -(DataStorageElement a, float b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(0f - b)));
		}

		public static DataStorageElement operator -(DataStorageElement a, double b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(0.0 - b)));
		}

		public static DataStorageElement operator -(DataStorageElement a, decimal b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b)));
		}

		public static DataStorageElement operator /(DataStorageElement a, int b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / (decimal)b)));
		}

		public static DataStorageElement operator /(DataStorageElement a, long b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / (decimal)b)));
		}

		public static DataStorageElement operator /(DataStorageElement a, float b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1.0 / (double)b)));
		}

		public static DataStorageElement operator /(DataStorageElement a, double b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1.0 / b)));
		}

		public static DataStorageElement operator /(DataStorageElement a, decimal b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / b)));
		}

		public static implicit operator DataStorageElement(bool b)
		{
			return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(b));
		}

		public static implicit operator DataStorageElement(int i)
		{
			return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(i));
		}

		public static implicit operator DataStorageElement(long l)
		{
			return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(l));
		}

		public static implicit operator DataStorageElement(decimal m)
		{
			return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(m));
		}

		public static implicit operator DataStorageElement(double d)
		{
			return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(d));
		}

		public static implicit operator DataStorageElement(float f)
		{
			return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(f));
		}

		public static implicit operator DataStorageElement(string s)
		{
			if (s != null)
			{
				return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(s));
			}
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JValue.CreateNull());
		}

		public static implicit operator DataStorageElement(JToken o)
		{
			return new DataStorageElement(OperationType.Replace, o);
		}

		public static implicit operator DataStorageElement(Array a)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)a));
		}

		public static implicit operator DataStorageElement(List<bool> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator DataStorageElement(List<int> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator DataStorageElement(List<long> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator DataStorageElement(List<decimal> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator DataStorageElement(List<double> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator DataStorageElement(List<float> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator DataStorageElement(List<string> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator DataStorageElement(List<object> l)
		{
			return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l));
		}

		public static implicit operator bool(DataStorageElement e)
		{
			return RetrieveAndReturnBoolValue<bool>(e);
		}

		public static implicit operator bool?(DataStorageElement e)
		{
			return RetrieveAndReturnBoolValue<bool?>(e);
		}

		public static implicit operator int(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<int>(e);
		}

		public static implicit operator int?(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<int?>(e);
		}

		public static implicit operator long(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<long>(e);
		}

		public static implicit operator long?(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<long?>(e);
		}

		public static implicit operator float(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<float>(e);
		}

		public static implicit operator float?(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<float?>(e);
		}

		public static implicit operator double(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<double>(e);
		}

		public static implicit operator double?(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<double?>(e);
		}

		public static implicit operator decimal(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<decimal>(e);
		}

		public static implicit operator decimal?(DataStorageElement e)
		{
			return RetrieveAndReturnDecimalValue<decimal?>(e);
		}

		public static implicit operator string(DataStorageElement e)
		{
			return RetrieveAndReturnStringValue(e);
		}

		public static implicit operator bool[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<bool[]>(e);
		}

		public static implicit operator int[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<int[]>(e);
		}

		public static implicit operator long[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<long[]>(e);
		}

		public static implicit operator decimal[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<decimal[]>(e);
		}

		public static implicit operator double[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<double[]>(e);
		}

		public static implicit operator float[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<float[]>(e);
		}

		public static implicit operator string[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<string[]>(e);
		}

		public static implicit operator object[](DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<object[]>(e);
		}

		public static implicit operator List<bool>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<bool>>(e);
		}

		public static implicit operator List<int>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<int>>(e);
		}

		public static implicit operator List<long>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<long>>(e);
		}

		public static implicit operator List<decimal>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<decimal>>(e);
		}

		public static implicit operator List<double>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<double>>(e);
		}

		public static implicit operator List<float>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<float>>(e);
		}

		public static implicit operator List<string>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<string>>(e);
		}

		public static implicit operator List<object>(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<List<object>>(e);
		}

		public static implicit operator Array(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<Array>(e);
		}

		public static implicit operator JArray(DataStorageElement e)
		{
			return RetrieveAndReturnArrayValue<JArray>(e);
		}

		public static implicit operator JToken(DataStorageElement e)
		{
			return e.Context.GetData(e.Context.Key);
		}

		public static DataStorageElement operator +(DataStorageElement a, BigInteger b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.Parse(b.ToString()));
		}

		public static DataStorageElement operator *(DataStorageElement a, BigInteger b)
		{
			return new DataStorageElement(a, OperationType.Mul, JToken.Parse(b.ToString()));
		}

		public static DataStorageElement operator %(DataStorageElement a, BigInteger b)
		{
			return new DataStorageElement(a, OperationType.Mod, JToken.Parse(b.ToString()));
		}

		public static DataStorageElement operator ^(DataStorageElement a, BigInteger b)
		{
			return new DataStorageElement(a, OperationType.Pow, JToken.Parse(b.ToString()));
		}

		public static DataStorageElement operator -(DataStorageElement a, BigInteger b)
		{
			return new DataStorageElement(a, OperationType.Add, JToken.Parse((-b).ToString()));
		}

		public static DataStorageElement operator /(DataStorageElement a, BigInteger b)
		{
			throw new InvalidOperationException("DataStorage[Key] / BigInterger is not supported, due to loss of precision when using integer division");
		}

		public static implicit operator DataStorageElement(BigInteger bi)
		{
			return new DataStorageElement(OperationType.Replace, JToken.Parse(bi.ToString()));
		}

		public static implicit operator BigInteger(DataStorageElement e)
		{
			return RetrieveAndReturnBigIntegerValue<BigInteger>(e);
		}

		public static implicit operator BigInteger?(DataStorageElement e)
		{
			return RetrieveAndReturnBigIntegerValue<BigInteger?>(e);
		}

		private static T RetrieveAndReturnBigIntegerValue<T>(DataStorageElement e)
		{
			if (e.cachedValue != null)
			{
				if (!BigInteger.TryParse(((object)e.cachedValue).ToString(), out var result))
				{
					return default(T);
				}
				return (T)Convert.ChangeType(result, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T));
			}
			BigInteger result2;
			BigInteger? bigInteger = (BigInteger.TryParse(((object)e.Context.GetData(e.Context.Key)).ToString(), out result2) ? new BigInteger?(result2) : null);
			if (!bigInteger.HasValue && !IsNullable<T>())
			{
				bigInteger = Activator.CreateInstance<BigInteger>();
			}
			foreach (OperationSpecification operation in e.Operations)
			{
				if (operation.OperationType == OperationType.Floor || operation.OperationType == OperationType.Ceil)
				{
					continue;
				}
				if (!BigInteger.TryParse(((object)operation.Value).ToString(), NumberStyles.AllowLeadingSign, null, out var result3))
				{
					throw new InvalidOperationException($"DataStorage[Key] cannot be converted to BigInterger as its value its not an integer number, value: {operation.Value}");
				}
				switch (operation.OperationType)
				{
				case OperationType.Replace:
					bigInteger = result3;
					break;
				case OperationType.Add:
					bigInteger += result3;
					break;
				case OperationType.Mul:
					bigInteger *= result3;
					break;
				case OperationType.Mod:
					bigInteger %= result3;
					break;
				case OperationType.Pow:
					bigInteger = BigInteger.Pow(bigInteger.Value, (int)operation.Value);
					break;
				case OperationType.Max:
				{
					BigInteger value = result3;
					BigInteger? bigInteger2 = bigInteger;
					if (value > bigInteger2)
					{
						bigInteger = result3;
					}
					break;
				}
				case OperationType.Min:
				{
					BigInteger value = result3;
					BigInteger? bigInteger2 = bigInteger;
					if (value < bigInteger2)
					{
						bigInteger = result3;
					}
					break;
				}
				case OperationType.Xor:
					bigInteger ^= result3;
					break;
				case OperationType.Or:
					bigInteger |= result3;
					break;
				case OperationType.And:
					bigInteger &= result3;
					break;
				case OperationType.LeftShift:
					bigInteger <<= (int)operation.Value;
					break;
				case OperationType.RightShift:
					bigInteger >>= (int)operation.Value;
					break;
				}
			}
			e.cachedValue = JToken.Parse(bigInteger.ToString());
			if (!bigInteger.HasValue)
			{
				return default(T);
			}
			return (T)Convert.ChangeType(bigInteger.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T));
		}

		public void Initialize(JToken value)
		{
			Context.Initialize(Context.Key, value);
		}

		public void Initialize(IEnumerable value)
		{
			Context.Initialize(Context.Key, (JToken)(object)JArray.FromObject((object)value));
		}

		public Task<T> GetAsync<T>()
		{
			return GetAsync().ContinueWith((Task<JToken> r) => r.Result.ToObject<T>());
		}

		public Task<JToken> GetAsync()
		{
			return Context.GetAsync(Context.Key);
		}

		private static T RetrieveAndReturnArrayValue<T>(DataStorageElement e)
		{
			//IL_000e: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Invalid comparison between Unknown and I4
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Invalid comparison between Unknown and I4
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			if (e.cachedValue != null)
			{
				return ((JToken)(JArray)e.cachedValue).ToObject<T>();
			}
			JArray val = (JArray)(((object)e.Context.GetData(e.Context.Key).ToObject<JArray>()) ?? ((object)new JArray()));
			foreach (OperationSpecification operation in e.Operations)
			{
				switch (operation.OperationType)
				{
				case OperationType.Add:
					if ((int)operation.Value.Type != 2)
					{
						throw new InvalidOperationException($"Cannot perform operation {OperationType.Add} on Array value, with a non Array value: {operation.Value}");
					}
					((JContainer)val).Merge((object)operation.Value);
					break;
				case OperationType.Replace:
					if ((int)operation.Value.Type != 2)
					{
						throw new InvalidOperationException($"Cannot replace Array value, with a non Array value: {operation.Value}");
					}
					val = (JArray)(((object)operation.Value.ToObject<JArray>()) ?? ((object)new JArray()));
					break;
				default:
					throw new InvalidOperationException($"Cannot perform operation {operation.OperationType} on Array value");
				}
			}
			e.cachedValue = (JToken)(object)val;
			return ((JToken)val).ToObject<T>();
		}

		private static string RetrieveAndReturnStringValue(DataStorageElement e)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Invalid comparison between Unknown and I4
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Invalid comparison between Unknown and I4
			if (e.cachedValue != null)
			{
				return (string)e.cachedValue;
			}
			JToken val = e.Context.GetData(e.Context.Key);
			string text = (((int)val.Type == 10) ? null : ((object)val).ToString());
			foreach (OperationSpecification operation in e.Operations)
			{
				switch (operation.OperationType)
				{
				case OperationType.Add:
					text += (string)operation.Value;
					break;
				case OperationType.Mul:
					if ((int)operation.Value.Type != 6)
					{
						throw new InvalidOperationException($"Cannot perform operation {OperationType.Mul} on string value, with a non interger value: {operation.Value}");
					}
					text = string.Concat(Enumerable.Repeat(text, (int)operation.Value));
					break;
				case OperationType.Replace:
					text = (string)operation.Value;
					break;
				default:
					throw new InvalidOperationException($"Cannot perform operation {operation.OperationType} on string value");
				}
			}
			if (text == null)
			{
				e.cachedValue = (JToken)(object)JValue.CreateNull();
			}
			else
			{
				e.cachedValue = JToken.op_Implicit(text);
			}
			return (string)e.cachedValue;
		}

		private static T RetrieveAndReturnBoolValue<T>(DataStorageElement e)
		{
			if (e.cachedValue != null)
			{
				return e.cachedValue.ToObject<T>();
			}
			bool? flag = e.Context.GetData(e.Context.Key).ToObject<bool?>() ?? ((bool?)Activator.CreateInstance(typeof(T)));
			foreach (OperationSpecification operation in e.Operations)
			{
				if (operation.OperationType == OperationType.Replace)
				{
					flag = (bool?)operation.Value;
					continue;
				}
				throw new InvalidOperationException($"Cannot perform operation {operation.OperationType} on boolean value");
			}
			e.cachedValue = JToken.op_Implicit(flag);
			if (!flag.HasValue)
			{
				return default(T);
			}
			return (T)Convert.ChangeType(flag.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T));
		}

		private static T RetrieveAndReturnDecimalValue<T>(DataStorageElement e)
		{
			if (e.cachedValue != null)
			{
				return e.cachedValue.ToObject<T>();
			}
			decimal? num = e.Context.GetData(e.Context.Key).ToObject<decimal?>();
			if (!num.HasValue && !IsNullable<T>())
			{
				num = Activator.CreateInstance<decimal>();
			}
			foreach (OperationSpecification operation in e.Operations)
			{
				switch (operation.OperationType)
				{
				case OperationType.Replace:
					num = (decimal)operation.Value;
					break;
				case OperationType.Add:
					num += (decimal?)(decimal)operation.Value;
					break;
				case OperationType.Mul:
					num *= (decimal?)(decimal)operation.Value;
					break;
				case OperationType.Mod:
					num %= (decimal?)(decimal)operation.Value;
					break;
				case OperationType.Pow:
					num = (decimal)Math.Pow((double)num.Value, (double)operation.Value);
					break;
				case OperationType.Max:
					num = Math.Max(num.Value, (decimal)operation.Value);
					break;
				case OperationType.Min:
					num = Math.Min(num.Value, (decimal)operation.Value);
					break;
				case OperationType.Xor:
					num = (long)num.Value ^ (long)operation.Value;
					break;
				case OperationType.Or:
					num = (long)num.Value | (long)operation.Value;
					break;
				case OperationType.And:
					num = (long)num.Value & (long)operation.Value;
					break;
				case OperationType.LeftShift:
					num = (long)num.Value << (int)operation.Value;
					break;
				case OperationType.RightShift:
					num = (long)num.Value >> (int)operation.Value;
					break;
				case OperationType.Floor:
					num = Math.Floor(num.Value);
					break;
				case OperationType.Ceil:
					num = Math.Ceiling(num.Value);
					break;
				}
			}
			e.cachedValue = JToken.op_Implicit(num);
			if (!num.HasValue)
			{
				return default(T);
			}
			return (T)Convert.ChangeType(num.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T));
		}

		private static bool IsNullable<T>()
		{
			if (typeof(T).IsGenericType)
			{
				return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition();
			}
			return false;
		}

		public T To<T>()
		{
			if (Operations.Count != 0)
			{
				throw new InvalidOperationException("DataStorageElement.To<T>() cannot be used together with other operations on the DataStorageElement");
			}
			return Context.GetData(Context.Key).ToObject<T>();
		}

		public override string ToString()
		{
			return (Context?.ToString() ?? "(null)") + ", (" + ListOperations() + ")";
		}

		private string ListOperations()
		{
			if (Operations != null)
			{
				return string.Join(", ", Operations.Select((OperationSpecification o) => o.ToString()).ToArray());
			}
			return "none";
		}
	}
	internal class DataStorageElementContext
	{
		internal string Key { get; set; }

		internal Action<string, DataStorageHelper.DataStorageUpdatedHandler> AddHandler { get; set; }

		internal Action<string, DataStorageHelper.DataStorageUpdatedHandler> RemoveHandler { get; set; }

		internal Func<string, JToken> GetData { get; set; }

		internal Action<string, JToken> Initialize { get; set; }

		internal Func<string, Task<JToken>> GetAsync { get; set; }

		public override string ToString()
		{
			return "Key: " + Key;
		}
	}
	public class GameData
	{
		[JsonProperty("location_name_to_id")]
		public Dictionary<string, long> LocationLookup { get; set; } = new Dictionary<string, long>();


		[JsonProperty("item_name_to_id")]
		public Dictionary<string, long> ItemLookup { get; set; } = new Dictionary<string, long>();


		[Obsolete("use Checksum instead")]
		[JsonProperty("version")]
		public int Version { get; set; }

		[JsonProperty("checksum")]
		public string Checksum { get; set; }
	}
	public class Hint
	{
		[JsonProperty("receiving_player")]
		public int ReceivingPlayer { get; set; }

		[JsonProperty("finding_player")]
		public int FindingPlayer { get; set; }

		[JsonProperty("item")]
		public long ItemId { get; set; }

		[JsonProperty("location")]
		public long LocationId { get; set; }

		[JsonProperty("item_flags")]
		public ItemFlags ItemFlags { get; set; }

		[JsonProperty("found")]
		public bool Found { get; set; }

		[JsonProperty("entrance")]
		public string Entrance { get; set; }

		[JsonProperty("status")]
		public HintStatus Status { get; set; }
	}
	public class ItemInfo
	{
		private readonly IItemInfoResolver itemInfoResolver;

		public long ItemId { get; }

		public long LocationId { get; }

		public PlayerInfo Player { get; }

		public ItemFlags Flags { get; }

		public string ItemName => itemInfoResolver.GetItemName(ItemId, ItemGame);

		public string ItemDisplayName => ItemName ?? $"Item: {ItemId}";

		public string LocationName => itemInfoResolver.GetLocationName(LocationId, LocationGame);

		public string LocationDisplayName => LocationName ?? $"Location: {LocationId}";

		public string ItemGame { get; }

		public string LocationGame { get; }

		public ItemInfo(NetworkItem item, string receiverGame, string senderGame, IItemInfoResolver itemInfoResolver, PlayerInfo player)
		{
			this.itemInfoResolver = itemInfoResolver;
			ItemGame = receiverGame;
			LocationGame = senderGame;
			ItemId = item.Item;
			LocationId = item.Location;
			Flags = item.Flags;
			Player = player;
		}

		public SerializableItemInfo ToSerializable()
		{
			return new SerializableItemInfo
			{
				IsScout = (GetType() == typeof(ScoutedItemInfo)),
				ItemId = ItemId,
				LocationId = LocationId,
				PlayerSlot = Player,
				Player = Player,
				Flags = Flags,
				ItemGame = ItemGame,
				ItemName = ItemName,
				LocationGame = LocationGame,
				LocationName = LocationName
			};
		}
	}
	public class ScoutedItemInfo : ItemInfo
	{
		public new PlayerInfo Player => base.Player;

		public bool IsReceiverRelatedToActivePlayer { get; }

		public ScoutedItemInfo(NetworkItem item, string receiverGame, string senderGame, IItemInfoResolver itemInfoResolver, IPlayerHelper players, PlayerInfo player)
			: base(item, receiverGame, senderGame, itemInfoResolver, player)
		{
			IsReceiverRelatedToActivePlayer = (players.ActivePlayer ?? new PlayerInfo()).IsRelatedTo(player);
		}
	}
	public class JsonMessagePart
	{
		[JsonProperty("type")]
		[JsonConverter(typeof(AttemptingStringEnumConverter), new object[] { typeof(SnakeCaseNamingStrategy) })]
		public JsonMessagePartType? Type { get; set; }

		[JsonProperty("color")]
		[JsonConverter(typeof(AttemptingStringEnumConverter), new object[] { typeof(SnakeCaseNamingStrategy) })]
		public JsonMessagePartColor? Color { get; set; }

		[JsonProperty("text")]
		public string Text { get; set; }

		[JsonProperty("player")]
		public int? Player { get; set; }

		[JsonProperty("flags")]
		public ItemFlags? Flags { get; set; }

		[JsonProperty("hint_status")]
		public HintStatus? HintStatus { get; set; }
	}
	public struct NetworkItem
	{
		[JsonProperty("item")]
		public long Item { get; set; }

		[JsonProperty("location")]
		public long Location { get; set; }

		[JsonProperty("player")]
		public int Player { get; set; }

		[JsonProperty("flags")]
		public ItemFlags Flags { get; set; }
	}
	public struct NetworkPlayer
	{
		[JsonProperty("team")]
		public int Team { get; set; }

		[JsonProperty("slot")]
		public int Slot { get; set; }

		[JsonProperty("alias")]
		public string Alias { get; set; }

		[JsonProperty("name")]
		public string Name { get; set; }
	}
	public struct NetworkSlot
	{
		[JsonProperty("name")]
		public string Name { get; set; }

		[JsonProperty("game")]
		public string Game { get; set; }

		[JsonProperty("type")]
		public SlotType Type { get; set; }

		[JsonProperty("group_members")]
		public int[] GroupMembers { get; set; }
	}
	public class NetworkVersion
	{
		[JsonProperty("major")]
		public int Major { get; set; }

		[JsonProperty("minor")]
		public int Minor { get; set; }

		[JsonProperty("build")]
		public int Build { get; set; }

		[JsonProperty("class")]
		public string Class => "Version";

		public NetworkVersion()
		{
		}

		public NetworkVersion(int major, int minor, int build)
		{
			Major = major;
			Minor = minor;
			Build = build;
		}

		public NetworkVersion(Version version)
		{
			Major = version.Major;
			Minor = version.Minor;
			Build = version.Build;
		}

		public Version ToVersion()
		{
			return new Version(Major, Minor, Build);
		}
	}
	public class OperationSpecification
	{
		[JsonProperty("operation")]
		[JsonConverter(typeof(AttemptingStringEnumConverter), new object[] { typeof(SnakeCaseNamingStrategy) })]
		public OperationType OperationType;

		[JsonProperty("value")]
		public JToken Value { get; set; }

		public override string ToString()
		{
			return $"{OperationType}: {Value}";
		}
	}
	public static class Operation
	{
		public static OperationSpecification Min(int i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Min,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Min(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Min,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Min(float i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Min,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Min(double i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Min,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Min(decimal i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Min,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Min(JToken i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Min,
				Value = i
			};
		}

		public static OperationSpecification Min(BigInteger i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Min,
				Value = JToken.Parse(i.ToString())
			};
		}

		public static OperationSpecification Max(int i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Max,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Max(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Max,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Max(float i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Max,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Max(double i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Max,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Max(decimal i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Max,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Max(JToken i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Max,
				Value = i
			};
		}

		public static OperationSpecification Max(BigInteger i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Max,
				Value = JToken.Parse(i.ToString())
			};
		}

		public static OperationSpecification Remove(JToken value)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Remove,
				Value = value
			};
		}

		public static OperationSpecification Pop(int value)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Pop,
				Value = JToken.op_Implicit(value)
			};
		}

		public static OperationSpecification Pop(JToken value)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Pop,
				Value = value
			};
		}

		public static OperationSpecification Update(IDictionary dictionary)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Update,
				Value = (JToken)(object)JObject.FromObject((object)dictionary)
			};
		}

		public static OperationSpecification Floor()
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Floor,
				Value = null
			};
		}

		public static OperationSpecification Ceiling()
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Ceil,
				Value = null
			};
		}
	}
	public static class Bitwise
	{
		public static OperationSpecification Xor(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Xor,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Xor(BigInteger i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Xor,
				Value = JToken.Parse(i.ToString())
			};
		}

		public static OperationSpecification Or(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Or,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification Or(BigInteger i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.Or,
				Value = JToken.Parse(i.ToString())
			};
		}

		public static OperationSpecification And(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.And,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification And(BigInteger i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.And,
				Value = JToken.Parse(i.ToString())
			};
		}

		public static OperationSpecification LeftShift(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.LeftShift,
				Value = JToken.op_Implicit(i)
			};
		}

		public static OperationSpecification RightShift(long i)
		{
			return new OperationSpecification
			{
				OperationType = OperationType.RightShift,
				Value = JToken.op_Implicit(i)
			};
		}
	}
	public class Callback
	{
		internal DataStorageHelper.DataStorageUpdatedHandler Method { get; set; }

		private Callback()
		{
		}

		public static Callback Add(DataStorageHelper.DataStorageUpdatedHandler callback)
		{
			return new Callback
			{
				Method = callback
			};
		}
	}
	public class AdditionalArgument
	{
		internal string Key { get; set; }

		internal JToken Value { get; set; }

		private AdditionalArgument()
		{
		}

		public static AdditionalArgument Add(string name, JToken value)
		{
			return new AdditionalArgument
			{
				Key = name,
				Value = value
			};
		}
	}
	public class MinimalSerializableItemInfo
	{
		public long ItemId { get; set; }

		public long LocationId { get; set; }

		public int PlayerSlot { get; set; }

		public ItemFlags Flags { get; set; }

		public string ItemGame { get; set; }

		public string LocationGame { get; set; }
	}
	public class SerializableItemInfo : MinimalSerializableItemInfo
	{
		public bool IsScout { get; set; }

		public PlayerInfo Player { get; set; }

		public string ItemName { get; set; }

		public string LocationName { get; set; }

		[JsonIgnore]
		public string ItemDisplayName => ItemName ?? $"Item: {base.ItemId}";

		[JsonIgnore]
		public string LocationDisplayName => LocationName ?? $"Location: {base.LocationId}";

		public string ToJson(bool full = false)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			MinimalSerializableItemInfo minimalSerializableItemInfo = this;
			if (!full)
			{
				minimalSerializableItemInfo = new MinimalSerializableItemInfo
				{
					ItemId = base.ItemId,
					LocationId = base.LocationId,
					PlayerSlot = base.PlayerSlot,
					Flags = base.Flags
				};
				if (IsScout)
				{
					minimalSerializableItemInfo.ItemGame = base.ItemGame;
				}
				else
				{
					minimalSerializableItemInfo.LocationGame = base.LocationGame;
				}
			}
			JsonSerializerSettings val = new JsonSerializerSettings
			{
				NullValueHandling = (NullValueHandling)1,
				Formatting = (Formatting)0
			};
			return JsonConvert.SerializeObject((object)minimalSerializableItemInfo, val);
		}

		public static SerializableItemInfo FromJson(string json, IArchipelagoSession session = null)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			ItemInfoStreamingContext additional = ((session != null) ? new ItemInfoStreamingContext
			{
				Items = session.Items,
				Locations = session.Locations,
				PlayerHelper = session.Players,
				ConnectionInfo = session.ConnectionInfo
			} : null);
			JsonSerializerSettings val = new JsonSerializerSettings
			{
				Context = new StreamingContext(StreamingContextStates.Other, additional)
			};
			return JsonConvert.DeserializeObject<SerializableItemInfo>(json, val);
		}

		[OnDeserialized]
		internal void OnDeserializedMethod(StreamingContext streamingContext)
		{
			if (base.ItemGame == null && base.LocationGame != null)
			{
				IsScout = false;
			}
			else if (base.ItemGame != null && base.LocationGame == null)
			{
				IsScout = true;
			}
			if (streamingContext.Context is ItemInfoStreamingContext itemInfoStreamingContext)
			{
				if (IsScout && base.LocationGame == null)
				{
					base.LocationGame = itemInfoStreamingContext.ConnectionInfo.Game;
				}
				else if (!IsScout && base.ItemGame == null)
				{
					base.ItemGame = itemInfoStreamingContext.ConnectionInfo.Game;
				}
				if (ItemName == null)
				{
					ItemName = itemInfoStreamingContext.Items.GetItemName(base.ItemId, base.ItemGame);
				}
				if (LocationName == null)
				{
					LocationName = itemInfoStreamingContext.Locations.GetLocationNameFromId(base.LocationId, base.LocationGame);
				}
				if (Player == null)
				{
					Player = itemInfoStreamingContext.PlayerHelper.GetPlayerInfo(base.PlayerSlot);
				}
			}
		}
	}
	internal class ItemInfoStreamingContext
	{
		public IReceivedItemsHelper Items { get; set; }

		public ILocationCheckHelper Locations { get; set; }

		public IPlayerHelper PlayerHelper { get; set; }

		public IConnectionInfoProvider ConnectionInfo { get; set; }
	}
}
namespace Archipelago.MultiClient.Net.MessageLog.Parts
{
	public class EntranceMessagePart : MessagePart
	{
		internal EntranceMessagePart(JsonMessagePart messagePart)
			: base(MessagePartType.Entrance, messagePart, Archipelago.MultiClient.Net.Colors.PaletteColor.Blue)
		{
			base.Text = messagePart.Text;
		}
	}
	public class HintStatusMessagePart : MessagePart
	{
		internal HintStatusMessagePart(JsonMessagePart messagePart)
			: base(MessagePartType.HintStatus, messagePart)
		{
			base.Text = messagePart.Text;
			if (messagePart.HintStatus.HasValue)
			{
				base.PaletteColor = ColorUtils.GetColor(messagePart.HintStatus.Value);
			}
		}
	}
	public class ItemMessagePart : MessagePart
	{
		public ItemFlags Flags { get; }

		public long ItemId { get; }

		public int Player { get; }

		internal ItemMessagePart(IPlayerHelper players, IItemInfoResolver items, JsonMessagePart part)
			: base(MessagePartType.Item, part)
		{
			Flags = part.Flags.GetValueOrDefault();
			base.PaletteColor = ColorUtils.GetColor(Flags);
			Player = part.Player.GetValueOrDefault();
			string game = (players.GetPlayerInfo(Player) ?? new PlayerInfo()).Game;
			JsonMessagePartType? type = part.Type;
			if (type.HasValue)
			{
				switch (type.GetValueOrDefault())
				{
				case JsonMessagePartType.ItemId:
					ItemId = long.Parse(part.Text);
					base.Text = items.GetItemName(ItemId, game) ?? $"Item: {ItemId}";
					break;
				case JsonMessagePartType.ItemName:
					ItemId = 0L;
					base.Text = part.Text;
					break;
				}
			}
		}
	}
	public class LocationMessagePart : MessagePart
	{
		public long LocationId { get; }

		public int Player { get; }

		internal LocationMessagePart(IPlayerHelper players, IItemInfoResolver itemInfoResolver, JsonMessagePart part)
			: base(MessagePartType.Location, part, Archipelago.MultiClient.Net.Colors.PaletteColor.Green)
		{
			Player = part.Player.GetValueOrDefault();
			string game = (players.GetPlayerInfo(Player) ?? new PlayerInfo()).Game;
			JsonMessagePartType? type = part.Type;
			if (type.HasValue)
			{
				switch (type.GetValueOrDefault())
				{
				case JsonMessagePartType.LocationId:
					LocationId = long.Parse(part.Text);
					base.Text = itemInfoResolver.GetLocationName(LocationId, game) ?? $"Location: {LocationId}";
					break;
				case JsonMessagePartType.LocationName:
					LocationId = itemInfoResolver.GetLocationId(part.Text, game);
					base.Text = part.Text;
					break;
				}
			}
		}
	}
	public class MessagePart
	{
		public string Text { get; internal set; }

		public MessagePartType Type { get; internal set; }

		public Color Color => GetColor(BuiltInPalettes.Dark);

		public PaletteColor? PaletteColor { get; protected set; }

		public bool IsBackgroundColor { get; internal set; }

		internal MessagePart(MessagePartType type, JsonMessagePart messagePart, PaletteColor? color = null)
		{
			Type = type;
			Text = messagePart.Text;
			if (color.HasValue)
			{
				PaletteColor = color.Value;
			}
			else if (messagePart.Color.HasValue)
			{
				PaletteColor = ColorUtils.GetColor(messagePart.Color.Value);
				IsBackgroundColor = messagePart.Color.Value >= JsonMessagePartColor.BlackBg;
			}
			else
			{
				PaletteColor = null;
			}
		}

		public T GetColor<T>(Palette<T> palette)
		{
			return palette[PaletteColor];
		}

		public override string ToString()
		{
			return Text;
		}
	}
	public enum MessagePartType
	{
		Text,
		Player,
		Item,
		Location,
		Entrance,
		HintStatus
	}
	public class PlayerMessagePart : MessagePart
	{
		public bool IsActivePlayer { get; }

		public int SlotId { get; }

		internal PlayerMessagePart(IPlayerHelper players, IConnectionInfoProvider connectionInfo, JsonMessagePart part)
			: base(MessagePartType.Player, part)
		{
			switch (part.Type)
			{
			case JsonMessagePartType.PlayerId:
				SlotId = int.Parse(part.Text);
				IsActivePlayer = SlotId == connectionInfo.Slot;
				base.Text = players.GetPlayerAlias(SlotId) ?? $"Player {SlotId}";
				break;
			case JsonMessagePartType.PlayerName:
				SlotId = 0;
				IsActivePlayer = false;
				base.Text = part.Text;
				break;
			}
			base.PaletteColor = (IsActivePlayer ? Archipelago.MultiClient.Net.Colors.PaletteColor.Magenta : Archipelago.MultiClient.Net.Colors.PaletteColor.Yellow);
		}
	}
}
namespace Archipelago.MultiClient.Net.MessageLog.Messages
{
	public class AdminCommandResultLogMessage : LogMessage
	{
		internal AdminCommandResultLogMessage(MessagePart[] parts)
			: base(parts)
		{
		}
	}
	public class ChatLogMessage : PlayerSpecificLogMessage
	{
		public string Message { get; }

		internal ChatLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string message)
			: base(parts, players, team, slot)
		{
			Message = message;
		}
	}
	public class CollectLogMessage : PlayerSpecificLogMessage
	{
		internal CollectLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot)
			: base(parts, players, team, slot)
		{
		}
	}
	public class CommandResultLogMessage : LogMessage
	{
		internal CommandResultLogMessage(MessagePart[] parts)
			: base(parts)
		{
		}
	}
	public class CountdownLogMessage : LogMessage
	{
		public int RemainingSeconds { get; }

		internal CountdownLogMessage(MessagePart[] parts, int remainingSeconds)
			: base(parts)
		{
			RemainingSeconds = remainingSeconds;
		}
	}
	public class GoalLogMessage : PlayerSpecificLogMessage
	{
		internal GoalLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot)
			: base(parts, players, team, slot)
		{
		}
	}
	public class HintItemSendLogMessage : ItemSendLogMessage
	{
		public bool IsFound { get; }

		internal HintItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, bool found, IItemInfoResolver itemInfoResolver)
			: base(parts, players, receiver, sender, item, itemInfoResolver)
		{
			IsFound = found;
		}
	}
	public class ItemCheatLogMessage : ItemSendLogMessage
	{
		internal ItemCheatLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, NetworkItem item, IItemInfoResolver itemInfoResolver)
			: base(parts, players, slot, 0, item, team, itemInfoResolver)
		{
		}
	}
	public class ItemSendLogMessage : LogMessage
	{
		private PlayerInfo ActivePlayer { get; }

		public PlayerInfo Receiver { get; }

		public PlayerInfo Sender { get; }

		public bool IsReceiverTheActivePlayer => Receiver == ActivePlayer;

		public bool IsSenderTheActivePlayer => Sender == ActivePlayer;

		public bool IsRelatedToActivePlayer
		{
			get
			{
				if (!ActivePlayer.IsRelatedTo(Receiver))
				{
					return ActivePlayer.IsRelatedTo(Sender);
				}
				return true;
			}
		}

		public ItemInfo Item { get; }

		internal ItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, IItemInfoResolver itemInfoResolver)
			: this(parts, players, receiver, sender, item, players.ActivePlayer.Team, itemInfoResolver)
		{
		}

		internal ItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, int team, IItemInfoResolver itemInfoResolver)
			: base(parts)
		{
			ActivePlayer = players.ActivePlayer ?? new PlayerInfo();
			Receiver = players.GetPlayerInfo(team, receiver) ?? new PlayerInfo();
			Sender = players.GetPlayerInfo(team, sender) ?? new PlayerInfo();
			PlayerInfo player = players.GetPlayerInfo(team, item.Player) ?? new PlayerInfo();
			Item = new ItemInfo(item, Receiver.Game, Sender.Game, itemInfoResolver, player);
		}
	}
	public class JoinLogMessage : PlayerSpecificLogMessage
	{
		public string[] Tags { get; }

		internal JoinLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string[] tags)
			: base(parts, players, team, slot)
		{
			Tags = tags;
		}
	}
	public class LeaveLogMessage : PlayerSpecificLogMessage
	{
		internal LeaveLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot)
			: base(parts, players, team, slot)
		{
		}
	}
	public class LogMessage
	{
		public MessagePart[] Parts { get; }

		internal LogMessage(MessagePart[] parts)
		{
			Parts = parts;
		}

		public override string ToString()
		{
			if (Parts.Length == 1)
			{
				return Parts[0].Text;
			}
			StringBuilder stringBuilder = new StringBuilder();
			MessagePart[] parts = Parts;
			foreach (MessagePart messagePart in parts)
			{
				stringBuilder.Append(messagePart.Text);
			}
			return stringBuilder.ToString();
		}
	}
	public abstract class PlayerSpecificLogMessage : LogMessage
	{
		private PlayerInfo ActivePlayer { get; }

		public PlayerInfo Player { get; }

		public bool IsActivePlayer => Player == ActivePlayer;

		public bool IsRelatedToActivePlayer => ActivePlayer.IsRelatedTo(Player);

		internal PlayerSpecificLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot)
			: base(parts)
		{
			ActivePlayer = players.ActivePlayer ?? new PlayerInfo();
			Player = players.GetPlayerInfo(team, slot) ?? new PlayerInfo();
		}
	}
	public class ReleaseLogMessage : PlayerSpecificLogMessage
	{
		internal ReleaseLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot)
			: base(parts, players, team, slot)
		{
		}
	}
	public class ServerChatLogMessage : LogMessage
	{
		public string Message { get; }

		internal ServerChatLogMessage(MessagePart[] parts, string message)
			: base(parts)
		{
			Message = message;
		}
	}
	public class TagsChangedLogMessage : PlayerSpecificLogMessage
	{
		public string[] Tags { get; }

		internal TagsChangedLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string[] tags)
			: base(parts, players, team, slot)
		{
			Tags = tags;
		}
	}
	public class TutorialLogMessage : LogMessage
	{
		internal TutorialLogMessage(MessagePart[] parts)
			: base(parts)
		{
		}
	}
}
namespace Archipelago.MultiClient.Net.Helpers
{
	public class ArchipelagoSocketHelper : BaseArchipelagoSocketHelper<ClientWebSocket>, IArchipelagoSocketHelper
	{
		public Uri Uri { get; }

		internal ArchipelagoSocketHelper(Uri hostUri)
			: base(CreateWebSocket(), 1024)
		{
			Uri = hostUri;
		}

		private static ClientWebSocket CreateWebSocket()
		{
			return new ClientWebSocket();
		}

		public async Task ConnectAsync()
		{
			await ConnectToProvidedUri(Uri);
			StartPolling();
		}

		private async Task ConnectToProvidedUri(Uri uri)
		{
			if (uri.Scheme != "unspecified")
			{
				try
				{
					await Socket.ConnectAsync(uri, CancellationToken.None);
					return;
				}
				catch (Exception e)
				{
					OnError(e);
					throw;
				}
			}
			List<Exception> errors = new List<Exception>(0);
			try
			{
				await Socket.ConnectAsync(uri.AsWss(), CancellationToken.None);
				if (Socket.State == WebSocketState.Open)
				{
					return;
				}
			}
			catch (Exception item)
			{
				errors.Add(item);
				Socket = CreateWebSocket();
			}
			try
			{
				await Socket.ConnectAsync(uri.AsWs(), CancellationToken.None);
			}
			catch (Exception item2)
			{
				errors.Add(item2);
				OnError(new AggregateException(errors));
				throw;
			}
		}
	}
	public class BaseArchipelagoSocketHelper<T> where T : WebSocket
	{
		private static readonly ArchipelagoPacketConverter Converter = new ArchipelagoPacketConverter();

		private readonly BlockingCollection<Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>> sendQueue = new BlockingCollection<Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>>();

		internal T Socket;

		private readonly int bufferSize;

		public bool Connected
		{
			get
			{
				if (Socket.State != WebSocketState.Open)
				{
					return Socket.State == WebSocketState.CloseReceived;
				}
				return true;
			}
		}

		public event ArchipelagoSocketHelperDelagates.PacketReceivedHandler PacketReceived;

		public event ArchipelagoSocketHelperDelagates.PacketsSentHandler PacketsSent;

		public event ArchipelagoSocketHelperDelagates.ErrorReceivedHandler ErrorReceived;

		public event ArchipelagoSocketHelperDelagates.SocketClosedHandler SocketClosed;

		public event ArchipelagoSocketHelperDelagates.SocketOpenedHandler SocketOpened;

		internal BaseArchipelagoSocketHelper(T socket, int bufferSize = 1024)
		{
			Socket = socket;
			this.bufferSize = bufferSize;
		}

		internal void StartPolling()
		{
			if (this.SocketOpened != null)
			{
				this.SocketOpened();
			}
			Task.Run((Func<Task?>)PollingLoop);
			Task.Run((Func<Task?>)SendLoop);
		}

		private async Task PollingLoop()
		{
			byte[] buffer = new byte[bufferSize];
			while (Socket.State == WebSocketState.Open)
			{
				string message = null;
				try
				{
					message = await ReadMessageAsync(buffer);
				}
				catch (Exception e)
				{
					OnError(e);
				}
				OnMessageReceived(message);
			}
		}

		private async Task SendLoop()
		{
			while (Socket.State == WebSocketState.Open)
			{
				try
				{
					await HandleSendBuffer();
				}
				catch (Exception e)
				{
					OnError(e);
				}
				await Task.Delay(20);
			}
		}

		private async Task<string> ReadMessageAsync(byte[] buffer)
		{
			using MemoryStream readStream = new MemoryStream(buffer.Length);
			WebSocketReceiveResult result;
			do
			{
				result = await Socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
				if (result.MessageType == WebSocketMessageType.Close)
				{
					try
					{
						await Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
					}
					catch
					{
					}
					OnSocketClosed();
				}
				else
				{
					readStream.Write(buffer, 0, result.Count);
				}
			}
			while (!result.EndOfMessage);
			return Encoding.UTF8.GetString(readStream.ToArray());
		}

		public async Task DisconnectAsync()
		{
			await Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closure requested by client", CancellationToken.None);
			OnSocketClosed();
		}

		public void SendPacket(ArchipelagoPacketBase packet)
		{
			SendMultiplePackets(new List<ArchipelagoPacketBase> { packet });
		}

		public void SendMultiplePackets(List<ArchipelagoPacketBase> packets)
		{
			SendMultiplePackets(packets.ToArray());
		}

		public void SendMultiplePackets(params ArchipelagoPacketBase[] packets)
		{
			SendMultiplePacketsAsync(packets).Wait();
		}

		public Task SendPacketAsync(ArchipelagoPacketBase packet)
		{
			return SendMultiplePacketsAsync(new List<ArchipelagoPacketBase> { packet });
		}

		public Task SendMultiplePacketsAsync(List<ArchipelagoPacketBase> packets)
		{
			return SendMultiplePacketsAsync(packets.ToArray());
		}

		public Task SendMultiplePacketsAsync(params ArchipelagoPacketBase[] packets)
		{
			TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
			foreach (ArchipelagoPacketBase item in packets)
			{
				sendQueue.Add(new Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>(item, taskCompletionSource));
			}
			return taskCompletionSource.Task;
		}

		private async Task HandleSendBuffer()
		{
			List<ArchipelagoPacketBase> list = new List<ArchipelagoPacketBase>();
			List<TaskCompletionSource<bool>> tasks = new List<TaskCompletionSource<bool>>();
			Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>> tuple = sendQueue.Take();
			list.Add(tuple.Item1);
			tasks.Add(tuple.Item2);
			Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>> item;
			while (sendQueue.TryTake(out item))
			{
				list.Add(item.Item1);
				tasks.Add(item.Item2);
			}
			if (!list.Any())
			{
				return;
			}
			if (Socket.State != WebSocketState.Open)
			{
				throw new ArchipelagoSocketClosedException();
			}
			ArchipelagoPacketBase[] packets = list.ToArray();
			string s = JsonConvert.SerializeObject((object)packets);
			byte[] messageBuffer = Encoding.UTF8.GetBytes(s);
			int messagesCount = (int)Math.Ceiling((double)messageBuffer.Length / (double)bufferSize);
			for (int i = 0; i < messagesCount; i++)
			{
				int num = bufferSize * i;
				int num2 = bufferSize;
				bool endOfMessage = i + 1 == messagesCount;
				if (num2 * (i + 1) > messageBuffer.Length)
				{
					num2 = messageBuffer.Length - num;
				}
				await Socket.SendAsync(new ArraySegment<byte>(messageBuffer, num, num2), WebSocketMessageType.Text, endOfMessage, CancellationToken.None);
			}
			foreach (TaskCompletionSource<bool> item2 in tasks)
			{
				item2.TrySetResult(result: true);
			}
			OnPacketSend(packets);
		}

		private void OnPacketSend(ArchipelagoPacketBase[] packets)
		{
			try
			{
				if (this.PacketsSent != null)
				{
					this.PacketsSent(packets);
				}
			}
			catch (Exception e)
			{
				OnError(e);
			}
		}

		private void OnSocketClosed()
		{
			try
			{
				if (this.SocketClosed != null)
				{
					this.SocketClosed("");
				}
			}
			catch (Exception e)
			{
				OnError(e);
			}
		}

		private void OnMessageReceived(string message)
		{
			try
			{
				if (string.IsNullOrEmpty(message) || this.PacketReceived == null)
				{
					return;
				}
				List<ArchipelagoPacketBase> list = null;
				try
				{
					list = JsonConvert.DeserializeObject<List<ArchipelagoPacketBase>>(message, (JsonConverter[])(object)new JsonConverter[1] { Converter });
				}
				catch (Exception e)
				{
					OnError(e);
				}
				if (list == null)
				{
					return;
				}
				foreach (ArchipelagoPacketBase item in list)
				{
					this.PacketReceived(item);
				}
			}
			catch (Exception e2)
			{
				OnError(e2);
			}
		}

		protected void OnError(Exception e)
		{
			try
			{
				if (this.ErrorReceived != null)
				{
					this.ErrorReceived(e, e.Message);
				}
			}
			catch (Exception ex)
			{
				Console.Out.WriteLine("Error occured during reporting of errorOuter Errror: " + e.Message + " " + e.StackTrace + "Inner Errror: " + ex.Message + " " + ex.StackTrace);
			}
		}
	}
	public interface IConnectionInfoProvider
	{
		string Game { get; }

		int Team { get; }

		int Slot { get; }

		string[] Tags { get; }

		ItemsHandlingFlags ItemsHandlingFlags { get; }

		string Uuid { get; }

		void UpdateConnectionOptions(string[] tags);

		void UpdateConnectionOptions(ItemsHandlingFlags itemsHandlingFlags);

		void UpdateConnectionOptions(string[] tags, ItemsHandlingFlags itemsHandlingFlags);
	}
	public class ConnectionInfoHelper : IConnectionInfoProvider
	{
		private readonly IArchipelagoSocketHelper socket;

		public string Game { get; private set; }

		public int Team { get; private set; }

		public int Slot { get; private set; }

		public string[] Tags { get; internal set; }

		public ItemsHandlingFlags ItemsHandlingFlags { get; internal set; }

		public string Uuid { get; private set; }

		internal ConnectionInfoHelper(IArchipelagoSocketHelper socket)
		{
			this.socket = socket;
			Reset();
			socket.PacketReceived += PacketReceived;
		}

		private void PacketReceived(ArchipelagoPacketBase packet)
		{
			if (!(packet is ConnectedPacket connectedPacket))
			{
				if (packet is ConnectionRefusedPacket)
				{
					Reset();
				}
				return;
			}
			Team = connectedPacket.Team;
			Slot = connectedPacket.Slot;
			if (connectedPacket.SlotInfo != null && connectedPacket.SlotInfo.ContainsKey(Slot))
			{
				Game = connectedPacket.SlotInfo[Slot].Game;
			}
		}

		internal void SetConnectionParameters(string game, string[] tags, ItemsHandlingFlags itemsHandlingFlags, string uuid)
		{
			Game = game;
			Tags = tags ?? new string[0];
			ItemsHandlingFlags = itemsHandlingFlags;
			Uuid = uuid ?? Guid.NewGuid().ToString();
		}

		private void Reset()
		{
			Game = null;
			Team = -1;
			Slot = -1;
			Tags = new string[0];
			ItemsHandlingFlags = ItemsHandlingFlags.NoItems;
			Uuid = null;
		}

		public void UpdateConnectionOptions(string[] tags)
		{
			UpdateConnectionOptions(tags, ItemsHandlingFlags);
		}

		public void UpdateConnectionOptions(ItemsHandlingFlags itemsHandlingFlags)
		{
			UpdateConnectionOptions(Tags, itemsHandlingFlags);
		}

		public void UpdateConnectionOptions(string[] tags, ItemsHandlingFlags itemsHandlingFlags)
		{
			SetConnectionParameters(Game, tags, itemsHandlingFlags, Uuid);
			socket.SendPacket(new ConnectUpdatePacket
			{
				Tags = Tags,
				ItemsHandling = ItemsHandlingFlags
			});
		}
	}
	public interface IDataStorageHelper : IDataStorageWrapper
	{
		DataStorageElement this[Scope scope, string key] { get; set; }

		DataStorageElement this[string key] { get; set; }
	}
	public class DataStorageHelper : IDataStorageHelper, IDataStorageWrapper
	{
		public delegate void DataStorageUpdatedHandler(JToken originalValue, JToken newValue, Dictionary<string, JToken> additionalArguments);

		private readonly Dictionary<string, DataStorageUpdatedHandler> onValueChangedEventHandlers = new Dictionary<string, DataStorageUpdatedHandler>();

		private readonly Dictionary<Guid, DataStorageUpdatedHandler> operationSpecificCallbacks = new Dictionary<Guid, DataStorageUpdatedHandler>();

		private readonly Dictionary<string, TaskCompletionSource<JToken>> asyncRetrievalTasks = new Dictionary<string, TaskCompletionSource<JToken>>();

		private readonly IArchipelagoSocketHelper socket;

		private readonly IConnectionInfoProvider connectionInfoProvider;

		public DataStorageElement this[Scope scope, string key]
		{
			get
			{
				return this[AddScope(scope, key)];
			}
			set
			{
				this[AddScope(scope, key)] = value;
			}
		}

		public DataStorageElement this[string key]
		{
			get
			{
				return new DataStorageElement(GetContextForKey(key));
			}
			set
			{
				SetValue(key, value);
			}
		}

		internal DataStorageHelper(IArchipelagoSocketHelper socket, IConnectionInfoProvider connectionInfoProvider)
		{
			this.socket = socket;
			this.connectionInfoProvider = connectionInfoProvider;
			socket.PacketReceived += OnPacketReceived;
		}

		private void OnPacketReceived(ArchipelagoPacketBase packet)
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Invalid comparison between Unknown and I4
			if (!(packet is RetrievedPacket retrievedPacket))
			{
				if (packet is SetReplyPacket setReplyPacket)
				{
					if (setReplyPacket.AdditionalArguments != null && setReplyPacket.AdditionalArguments.ContainsKey("Reference") && (int)setReplyPacket.AdditionalArguments["Reference"].Type == 8 && ((string)setReplyPacket.AdditionalArguments["Reference"]).TryParseNGuid(out var g) && operationSpecificCallbacks.TryGetValue(g, out var value))
					{
						value(setReplyPacket.OriginalValue, setReplyPacket.Value, setReplyPacket.AdditionalArguments);
						operationSpecificCallbacks.Remove(g);
					}
					if (onValueChangedEventHandlers.TryGetValue(setReplyPacket.Key, out var value2))
					{
						value2(setReplyPacket.OriginalValue, setReplyPacket.Value, setReplyPacket.AdditionalArguments);
					}
				}
				return;
			}
			foreach (KeyValuePair<string, JToken> datum in retrievedPacket.Data)
			{
				if (asyncRetrievalTasks.TryGetValue(datum.Key, out var value3))
				{
					value3.TrySetResult(datum.Value);
					asyncRetrievalTasks.Remove(datum.Key);
				}
			}
		}

		private Task<JToken> GetAsync(string key)
		{
			if (asyncRetrievalTasks.TryGetValue(key, out var value))
			{
				return value.Task;
			}
			TaskCompletionSource<JToken> taskCompletionSource = new TaskCompletionSource<JToken>();
			asyncRetrievalTasks[key] = taskCompletionSource;
			socket.SendPacketAsync(new GetPacket
			{
				Keys = new string[1] { key }
			});
			return taskCompletionSource.Task;
		}

		private void Initialize(string key, JToken value)
		{
			socket.SendPacketAsync(new SetPacket
			{
				Key = key,
				DefaultValue = value,
				Operations = new OperationSpecification[1]
				{
					new OperationSpecification
					{
						OperationType = OperationType.Default
					}
				}
			});
		}

		private JToken GetValue(string key)
		{
			Task<JToken> async = GetAsync(key);
			if (!async.Wait(TimeSpan.FromSeconds(2.0)))
			{
				throw new TimeoutException("Timed out retrieving data for key `" + key + "`. This may be due to an attempt to retrieve a value from the DataStorageHelper in a synchronous fashion from within a PacketReceived handler. When using the DataStorageHelper from within code which runs on the websocket thread then use the asynchronous getters. Ex: `DataStorageHelper[\"" + key + "\"].GetAsync().ContinueWith(x => {});`Be aware that DataStorageHelper calls tend to cause packet responses, so making a call from within a PacketReceived handler may cause an infinite loop.");
			}
			return async.Result;
		}

		private void SetValue(string key, DataStorageElement e)
		{
			if (key.StartsWith("_read_"))
			{
				throw new InvalidOperationException("DataStorage write operation on readonly key '" + key + "' is not allowed");
			}
			if (e == null)
			{
				e = new DataStorageElement(OperationType.Replace, (JToken)(object)JValue.CreateNull());
			}
			if (e.Context == null)
			{
				e.Context = GetContextForKey(key);
			}
			else if (e.Context.Key != key)
			{
				e.Operations.Insert(0, new OperationSpecification
				{
					OperationType = OperationType.Replace,
					Value = GetValue(e.Context.Key)
				});
			}
			Dictionary<string, JToken> dictionary = e.AdditionalArguments ?? new Dictionary<string, JToken>(0);
			if (e.Callbacks != null)
			{
				Guid key2 = Guid.NewGuid();
				operationSpecificCallbacks[key2] = e.Callbacks;
				dictionary["Reference"] = JToken.op_Implicit(key2.ToString("N"));
				socket.SendPacketAsync(new SetPacket
				{
					Key = key,
					Operations = e.Operations.ToArray(),
					WantReply = true,
					AdditionalArguments = dictionary
				});
			}
			else
			{
				socket.SendPacketAsync(new SetPacket
				{
					Key = key,
					Operations = e.Operations.ToArray(),
					AdditionalArguments = dictionary
				});
			}
		}

		private DataStorageElementContext GetContextForKey(string key)
		{
			return new DataStorageElementContext
			{
				Key = key,
				GetData = GetValue,
				GetAsync = GetAsync,
				Initialize = Initialize,
				AddHandler = AddHandler,
				RemoveHandler = RemoveHandler
			};
		}

		private void AddHandler(string key, DataStorageUpdatedHandler handler)
		{
			if (onValueChangedEventHandlers.ContainsKey(key))
			{
				Dictionary<string, DataStorageUpdatedHandler> dictionary = onValueChangedEventHandlers;
				dictionary[key] = (DataStorageUpdatedHandler)Delegate.Combine(dictionary[key], handler);
			}
			else
			{
				onValueChangedEventHandlers[key] = handler;
			}
			socket.SendPacketAsync(new SetNotifyPacket
			{
				Keys = new string[1] { key }
			});
		}

		private void RemoveHandler(string key, DataStorageUpdatedHandler handler)
		{
			if (onValueChangedEventHandlers.ContainsKey(key))
			{
				Dictionary<string, DataStorageUpdatedHandler> dictionary = onValueChangedEventHandlers;
				dictionary[key] = (DataStorageUpdatedHandler)Delegate.Remove(dictionary[key], handler);
				if (onValueChangedEventHandlers[key] == null)
				{
					onValueChangedEventHandlers.Remove(key);
				}
			}
		}

		private string AddScope(Scope scope, string key)
		{
			return scope switch
			{
				Scope.Global => key, 
				Scope.Game => $"{scope}:{connectionInfoProvider.Game}:{key}", 
				Scope.Team => $"{scope}:{connectionInfoProvider.Team}:{key}", 
				Scope.Slot => $"{scope}:{connectionInfoProvider.Slot}:{key}", 
				Scope.ReadOnly => "_read_" + key, 
				_ => throw new ArgumentOutOfRangeException("scope", scope, "Invalid scope for key " + key), 
			};
		}

		private DataStorageElement GetHintsElement(int? slot = null, int? team = null)
		{
			return this[Scope.ReadOnly, $"hints_{team ?? connectionInfoProvider.Team}_{slot ?? connectionInfoProvider.Slot}"];
		}

		private DataStorageElement GetSlotDataElement(int? slot = null)
		{
			return this[Scope.ReadOnly, $"slot_data_{slot ?? connectionInfoProvider.Slot}"];
		}

		private DataStorageElement GetItemNameGroupsElement(string game = null)
		{
			return this[Scope.ReadOnly, "item_name_groups_" + (game ?? connectionInfoProvider.Game)];
		}

		private DataStorageElement GetLocationNameGroupsElement(string game = null)
		{
			return this[Scope.ReadOnly, "location_name_groups_" + (game ?? connectionInfoProvider.Game)];
		}

		private DataStorageElement GetClientStatusElement(int? slot = null, int? team = null)
		{
			return this[Scope.ReadOnly, $"client_status_{team ?? connectionInfoProvider.Team}_{slot ?? connectionInfoProvider.Slot}"];
		}

		private DataStorageElement GetRaceModeElement()
		{
			return this[Scope.ReadOnly, "race_mode"];
		}

		public Hint[] GetHints(int? slot = null, int? team = null)
		{
			return GetHintsElement(slot, team).To<Hint[]>();
		}

		public Task<Hint[]> GetHintsAsync(int? slot = null, int? team = null)
		{
			return GetHintsElement(slot, team).GetAsync<Hint[]>();
		}

		public void TrackHints(Action<Hint[]> onHintsUpdated, bool retrieveCurrentlyUnlockedHints = true, int? slot = null, int? team = null)
		{
			GetHintsElement(slot, team).OnValueChanged += delegate(JToken _, JToken newValue, Dictionary<string, JToken> x)
			{
				onHintsUpdated(newValue.ToObject<Hint[]>());
			};
			if (retrieveCurrentlyUnlockedHints)
			{
				GetHintsAsync(slot, team).ContinueWith(delegate(Task<Hint[]> t)
				{
					onHintsUpdated(t.Result);
				});
			}
		}

		public Dictionary<string, object> GetSlotData(int? slot = null)
		{
			return GetSlotData<Dictionary<string, object>>(slot);
		}

		public T GetSlotData<T>(int? slot = null) where T : class
		{
			return GetSlotDataElement(slot).To<T>();
		}

		public Task<Dictionary<string, object>> GetSlotDataAsync(int? slot = null)
		{
			return GetSlotDataAsync<Dictionary<string, object>>(slot);
		}

		public Task<T> GetSlotDataAsync<T>(int? slot = null) where T : class
		{
			return GetSlotDataElement(slot).GetAsync<T>();
		}

		public Dictionary<string, string[]> GetItemNameGroups(string game = null)
		{
			return GetItemNameGroupsElement(game).To<Dictionary<string, string[]>>();
		}

		public Task<Dictionary<string, string[]>> GetItemNameGroupsAsync(string game = null)
		{
			return GetItemNameGroupsElement(game).GetAsync<Dictionary<string, string[]>>();
		}

		public Dictionary<string, string[]> GetLocationNameGroups(string game = null)
		{
			return GetLocationNameGroupsElement(game).To<Dictionary<string, string[]>>();
		}

		public Task<Dictionary<string, string[]>> GetLocationNameGroupsAsync(string game = null)
		{
			return GetLocationNameGroupsElement(game).GetAsync<Dictionary<string, string[]>>();
		}

		public ArchipelagoClientState GetClientStatus(int? slot = null, int? team = null)
		{
			return GetClientStatusElement(slot, team).To<ArchipelagoClientState?>().GetValueOrDefault();
		}

		public Task<ArchipelagoClientState> GetClientStatusAsync(int? slot = null, int? team = null)
		{
			return GetClientStatusElement(slot, team).GetAsync<ArchipelagoClientState?>().ContinueWith((Task<ArchipelagoClientState?> r) => r.Result.GetValueOrDefault());
		}

		public void TrackClientStatus(Action<ArchipelagoClientState> onStatusUpdated, bool retrieveCurrentClientStatus = true, int? slot = null, int? team = null)
		{
			GetClientStatusElement(slot, team).OnValueChanged += delegate(JToken _, JToken newValue, Dictionary<string, JToken> x)
			{
				onStatusUpdated(newValue.ToObject<ArchipelagoClientState>());