Decompiled source of Archipelago v0.6.13

plugins/gg.archipelago.rhythmdoctor/RhythmDoctor.Archipelago.dll

Decompiled 14 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Archipelago.MultiClient.Net;
using Archipelago.MultiClient.Net.BounceFeatures.DeathLink;
using Archipelago.MultiClient.Net.Enums;
using Archipelago.MultiClient.Net.Helpers;
using Archipelago.MultiClient.Net.MessageLog.Messages;
using Archipelago.MultiClient.Net.Models;
using Archipelago.MultiClient.Net.Packets;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json.Linq;
using PulseLib;
using PulseLib.Extensions;
using PulseLib.Localization;
using RDLevelEditor;
using RhythmDoctor.Archipelago.Client;
using RhythmDoctor.Archipelago.Extensions;
using RhythmDoctor.Archipelago.Helpers;
using RhythmDoctor.Archipelago.Interfaces;
using RhythmDoctor.Archipelago.Patches;
using RhythmDoctor.Archipelago.Patches.Gameplay;
using RhythmDoctor.Archipelago.Patches.Gameplay.Powerups;
using RhythmDoctor.Archipelago.Patches.Gameplay.Traps;
using RhythmDoctor.Archipelago.Patches.Menu;
using RhythmDoctor.Archipelago.World;
using RhythmDoctor.Archipelago.World.Data;
using RhythmWeightlifter;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("gg.archipelago.rhythmdoctor")]
[assembly: AssemblyConfiguration("Release-BepInEx5")]
[assembly: AssemblyFileVersion("0.6.13.0")]
[assembly: AssemblyInformationalVersion("0.6.13+71162631dc2f6e169ab8e9362130d32633f21ee2")]
[assembly: AssemblyProduct("RhythmDoctor.Archipelago")]
[assembly: AssemblyTitle("gg.archipelago.rhythmdoctor")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.6.13.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
[CompilerGenerated]
internal sealed class <>z__ReadOnlyArray<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
	int ICollection.Count => _items.Length;

	bool ICollection.IsSynchronized => false;

	object ICollection.SyncRoot => this;

	object IList.this[int index]
	{
		get
		{
			return _items[index];
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	bool IList.IsFixedSize => true;

	bool IList.IsReadOnly => true;

	int IReadOnlyCollection<T>.Count => _items.Length;

	T IReadOnlyList<T>.this[int index] => _items[index];

	int ICollection<T>.Count => _items.Length;

	bool ICollection<T>.IsReadOnly => true;

	T IList<T>.this[int index]
	{
		get
		{
			return _items[index];
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	public <>z__ReadOnlyArray(T[] items)
	{
		_items = items;
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return ((IEnumerable)_items).GetEnumerator();
	}

	void ICollection.CopyTo(Array array, int index)
	{
		((ICollection)_items).CopyTo(array, index);
	}

	int IList.Add(object value)
	{
		throw new NotSupportedException();
	}

	void IList.Clear()
	{
		throw new NotSupportedException();
	}

	bool IList.Contains(object value)
	{
		return ((IList)_items).Contains(value);
	}

	int IList.IndexOf(object value)
	{
		return ((IList)_items).IndexOf(value);
	}

	void IList.Insert(int index, object value)
	{
		throw new NotSupportedException();
	}

	void IList.Remove(object value)
	{
		throw new NotSupportedException();
	}

	void IList.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}

	IEnumerator<T> IEnumerable<T>.GetEnumerator()
	{
		return ((IEnumerable<T>)_items).GetEnumerator();
	}

	void ICollection<T>.Add(T item)
	{
		throw new NotSupportedException();
	}

	void ICollection<T>.Clear()
	{
		throw new NotSupportedException();
	}

	bool ICollection<T>.Contains(T item)
	{
		return ((ICollection<T>)_items).Contains(item);
	}

	void ICollection<T>.CopyTo(T[] array, int arrayIndex)
	{
		((ICollection<T>)_items).CopyTo(array, arrayIndex);
	}

	bool ICollection<T>.Remove(T item)
	{
		throw new NotSupportedException();
	}

	int IList<T>.IndexOf(T item)
	{
		return ((IList<T>)_items).IndexOf(item);
	}

	void IList<T>.Insert(int index, T item)
	{
		throw new NotSupportedException();
	}

	void IList<T>.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}
}
[CompilerGenerated]
internal sealed class <>z__ReadOnlySingleElementList<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
	private sealed class Enumerator : IDisposable, IEnumerator, IEnumerator<T>
	{
		object IEnumerator.Current => _item;

		T IEnumerator<T>.Current => _item;

		public Enumerator(T item)
		{
			_item = item;
		}

		bool IEnumerator.MoveNext()
		{
			if (!_moveNextCalled)
			{
				return _moveNextCalled = true;
			}
			return false;
		}

		void IEnumerator.Reset()
		{
			_moveNextCalled = false;
		}

		void IDisposable.Dispose()
		{
		}
	}

	int ICollection.Count => 1;

	bool ICollection.IsSynchronized => false;

	object ICollection.SyncRoot => this;

	object IList.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	bool IList.IsFixedSize => true;

	bool IList.IsReadOnly => true;

	int IReadOnlyCollection<T>.Count => 1;

	T IReadOnlyList<T>.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
	}

	int ICollection<T>.Count => 1;

	bool ICollection<T>.IsReadOnly => true;

	T IList<T>.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	public <>z__ReadOnlySingleElementList(T item)
	{
		_item = item;
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return new Enumerator(_item);
	}

	void ICollection.CopyTo(Array array, int index)
	{
		array.SetValue(_item, index);
	}

	int IList.Add(object value)
	{
		throw new NotSupportedException();
	}

	void IList.Clear()
	{
		throw new NotSupportedException();
	}

	bool IList.Contains(object value)
	{
		return EqualityComparer<T>.Default.Equals(_item, (T)value);
	}

	int IList.IndexOf(object value)
	{
		if (!EqualityComparer<T>.Default.Equals(_item, (T)value))
		{
			return -1;
		}
		return 0;
	}

	void IList.Insert(int index, object value)
	{
		throw new NotSupportedException();
	}

	void IList.Remove(object value)
	{
		throw new NotSupportedException();
	}

	void IList.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}

	IEnumerator<T> IEnumerable<T>.GetEnumerator()
	{
		return new Enumerator(_item);
	}

	void ICollection<T>.Add(T item)
	{
		throw new NotSupportedException();
	}

	void ICollection<T>.Clear()
	{
		throw new NotSupportedException();
	}

	bool ICollection<T>.Contains(T item)
	{
		return EqualityComparer<T>.Default.Equals(_item, item);
	}

	void ICollection<T>.CopyTo(T[] array, int arrayIndex)
	{
		array[arrayIndex] = _item;
	}

	bool ICollection<T>.Remove(T item)
	{
		throw new NotSupportedException();
	}

	int IList<T>.IndexOf(T item)
	{
		if (!EqualityComparer<T>.Default.Equals(_item, item))
		{
			return -1;
		}
		return 0;
	}

	void IList<T>.Insert(int index, T item)
	{
		throw new NotSupportedException();
	}

	void IList<T>.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Microsoft.CodeAnalysis
{
	[Embedded]
	[AttributeUsage(AttributeTargets.All)]
	[ExcludeFromCodeCoverage]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace Archipelago.MultiClient.Net
{
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
	internal sealed class DataStoragePropertyAttribute : Attribute
	{
		public string? SessionVariable { get; }

		public Scope Scope { get; }

		public string Key { get; }

		public DataStoragePropertyAttribute(string sessionVariable, Scope scope, string key)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			SessionVariable = sessionVariable;
			Scope = scope;
			Key = key;
		}

		public DataStoragePropertyAttribute(string sessionVariable, string key)
			: this(sessionVariable, (Scope)0, key)
		{
		}
	}
}
namespace RhythmDoctor.Archipelago
{
	internal static class Configuration
	{
		internal enum DeathLinkConfig
		{
			FollowSlot,
			On,
			Off
		}

		private static ConfigEntry<DeathLinkConfig> _deathLink;

		private static ConfigEntry<int> _autoReconnectMaxRetries;

		private static ConfigEntry<int> _remoteTrapClearsTimeout;

		private static ConfigEntry<int> _slotToUse;

		internal static void Bind(ConfigFile config)
		{
			Plugin.Logger.LogInfo((object)"Binding configuration");
			_deathLink = config.Bind<DeathLinkConfig>("Gameplay", "DeathLink", DeathLinkConfig.FollowSlot, "Whether to enable DeathLink when connected to an Archipelago slot.\nSee the respective Game Page in Archipelago for more info.");
			_autoReconnectMaxRetries = config.Bind<int>("zzzDebugDoNotTouchUnlessAsked", "AutoReconnectMaxRetries", 3, "How many times to attempt to reconnect to an Archipelago server before the client is considered unsalvagable and the Client is abandoned.");
			_remoteTrapClearsTimeout = config.Bind<int>("zzzDebugDoNotTouchUnlessAsked", "RemoteTrapClearsTimeout", 3000, "How long to wait in milliseconds until getting remote trap clear status times out and defaults to 0/last known good value.");
			_slotToUse = config.Bind<int>("zzzDebugDoNotTouchUnlessAsked", "SlotToUse", 0, "Slot to use for Archipelago.");
		}

		internal static async Task<DeathLinkConfig> GetDeathLink()
		{
			if (Plugin.Client == null || Plugin.Client.Session == null)
			{
				return _deathLink.Value;
			}
			return (!(await ((IDataStorageWrapper)Plugin.Client.Session.DataStorage).GetRaceModeAsync())) ? _deathLink.Value : DeathLinkConfig.FollowSlot;
		}

		internal static int GetSlotToUse()
		{
			return _slotToUse.Value;
		}

		internal static int GetAutoReconnectMaxRetries()
		{
			return _autoReconnectMaxRetries.Value;
		}

		internal static int GetRemoteTrapClearsTimeout()
		{
			return _remoteTrapClearsTimeout.Value;
		}
	}
	internal static class Paths
	{
		internal static string Assembly;

		internal static string Assets;

		internal static string Localization;

		internal static string WardIcons;

		internal static void PopulatePaths()
		{
			Assembly = Path.GetDirectoryName(((BaseUnityPlugin)Plugin.Instance).Info.Location);
			Assets = Path.Combine(Assembly, "Assets");
			Localization = Path.Combine(Assets, "Localization");
			WardIcons = Path.Combine(Assets, "WardIcons");
		}
	}
	[BepInPlugin("gg.archipelago.rhythmdoctor", "RhythmDoctor.Archipelago", "0.6.13")]
	[BepInDependency("io.github.nonperforming.pulse", "0.0.6")]
	[BepInProcess("Rhythm Doctor.exe")]
	public class Plugin : BaseUnityPlugin
	{
		internal static RhythmDoctor.Archipelago.Client.Client Client = null;

		internal static ManualLogSource Logger = null;

		internal static readonly Random Random = new Random();

		internal static readonly ConcurrentQueue<Action> ToExecuteOnMainThread = new ConcurrentQueue<Action>();

		internal static Plugin Instance = null;

		internal const string PATCH_ID_ALWAYS_ACTIVE = "gg.archipelago.rhythmdoctor";

		internal const string PATCH_ID_ARCHIPELAGO_MENU = "gg.archipelago.rhythmdoctor.cls";

		internal const string PATCH_ID_POST_LOGIN = "gg.archipelago.rhythmdoctor.post";

		internal const string PATCH_ID_TRAP = "gg.archipelago.rhythmdoctor.trap";

		internal const string PATCH_ID_SLEEVE_PAINT = "gg.archipelago.rhythmdoctor.sleevepaint";

		private static readonly Type[] AlwaysActivePatches = new Type[3]
		{
			typeof(ArchipelagoMainMenuOptionPatch),
			typeof(VersionTextPatch),
			typeof(scnGameExtensions)
		};

		private static readonly Type[] PostLoginPatches = new Type[15]
		{
			typeof(Act5Patch),
			typeof(ClearLocationPatch),
			typeof(DeathLinkPatch),
			typeof(LevelSelectVisualFixesPatch),
			typeof(RhythmDogtorLevelPatch),
			typeof(RhythmWeightlifterPatch),
			typeof(RunningCharactersPatch),
			typeof(SkipCutscenePatch),
			typeof(SkipTutorialPatch),
			typeof(StateReplicationPatch),
			typeof(TrapManagerPatch),
			typeof(UnlockItemPatch),
			typeof(WelcomeBackPatch),
			typeof(SavingPatch),
			typeof(UnapplyPatchesPatch)
		};

		private static readonly Type CustomLoginScreenPatch = typeof(ArchipelagoLoginPatch);

		private bool _quitting;

		private void Awake()
		{
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			Logger.LogInfo((object)"gg.archipelago.rhythmdoctor v0.6.13 loading");
			Configuration.Bind(((BaseUnityPlugin)this).Config);
			Logger.LogInfo((object)"Setting up paths");
			Paths.PopulatePaths();
			Logger.LogInfo((object)("Registering custom localization (" + Paths.Localization + ")"));
			CustomLocalizationHelper.SearchAndRegisterDirectory(Paths.Localization);
			Logger.LogInfo((object)"Applying always active patches");
			ApplyPatches("gg.archipelago.rhythmdoctor", AlwaysActivePatches);
			Logger.LogInfo((object)"gg.archipelago.rhythmdoctor v0.6.13 loaded");
		}

		private void Start()
		{
			Version version = new Version("1.1.1");
			Version version2 = new Version(Application.version);
			if (Releases.releaseNumber < 42 || version2 < version)
			{
				Logger.LogWarning((object)"================================================================================");
				Logger.LogWarning((object)"|   ╭─╮╷  ╭─╴╭─╮╭─╮╭─╴   ╷ ╷╭─╮╶┬╮╭─╮╶┬╴╭─╴   ╷ ╷╭─╮╷ ╷╭─╮   ╭─╴╭─╮╭┬╮╭─╴╷╷╷   |");
				Logger.LogWarning((object)"|   ├─╯│  ├╴ ├─┤╰─╮├╴    │ │├─╯ ││├─┤ │ ├╴    ╰┬╯│ ││ │├┬╯   │╶╮├─┤│││├╴ ╵╵╵   |");
				Logger.LogWarning((object)"|   ╵  ╰─╴╰─╴╵ ╵╰─╯╰─╴   ╰─╯╵  ╶┴╯╵ ╵ ╵ ╰─╴    ╵ ╰─╯╰─╯╵╰╴   ╰─╯╵ ╵╵ ╵╰─╴╵╵╵   |");
				Logger.LogWarning((object)"================================================================================");
				Logger.LogWarning((object)string.Format("This version of the mod (v{0}) was built for: v{1} (release {2}, commit {3}, date {4})", "0.6.13", version, 42, "e43207b", "2026/06/15 12:42 AM"));
				Logger.LogWarning((object)$"Your version is: v{Application.version} (release {Releases.releaseNumber}, commit {Releases.buildCommit}, date {Releases.buildDate})");
				Logger.LogWarning((object)"================================================================================");
			}
			else if (Releases.releaseNumber > 42 || version2 > version)
			{
				Logger.LogWarning((object)"=================================================================================");
				Logger.LogWarning((object)"|         ╭─╮╷  ╭─╴╭─╮╭─╮╭─╴   ╭─╮╭─╮╭─╴╭╮╷   ╭─╮╭╮╷   ╷╭─╮╭─╮╷ ╷╭─╴╷╷╷         |");
				Logger.LogWarning((object)"|         ├─╯│  ├╴ ├─┤╰─╮├╴    │ │├─╯├╴ │╰┤   ├─┤│╰┤   │╰─╮╰─╮│ │├╴ ╵╵╵         |");
				Logger.LogWarning((object)"|         ╵  ╰─╴╰─╴╵ ╵╰─╯╰─╴   ╰─╯╵  ╰─╴╵ ╵   ╵ ╵╵ ╵   ╵╰─╯╰─╯╰─╯╰─╴╵╵╵         |");
				Logger.LogWarning((object)"=================================================================================");
				Logger.LogWarning((object)string.Format("This version of the mod (v{0}) was built for: v{1} (release {2}, commit {3}, date {4})", "0.6.13", version, 42, "e43207b", "2026/06/15 12:42 AM"));
				Logger.LogWarning((object)$"Your version is: v{Application.version} (release {Releases.releaseNumber}, commit {Releases.buildCommit}, date {Releases.buildDate})");
				Logger.LogWarning((object)null);
				Logger.LogWarning((object)"First, please check that there is an updated version here: https://github.com/nonperforming/RhythmDoctor.Archipelago/releases. Remember to update Pulse (https://github.com/nonperforming/Pulse/releases) as well.");
				Logger.LogWarning((object)$"If there is no update yet, please open an issue at https://github.com/nonperforming/RhythmDoctor.Archipelago/issues/new?labels=\"plugin outdated\"&title=v{Application.version}+support&body=Release:+{Releases.releaseNumber}+/+Commit:+{Releases.buildCommit}+/+Date:+{Releases.buildDate}&assignees=nonperforming");
				Logger.LogWarning((object)"=================================================================================");
			}
		}

		private void Update()
		{
			Action result;
			while (ToExecuteOnMainThread.TryDequeue(out result))
			{
				result();
			}
		}

		private static void ApplyPatches(string id, params Type[] patches)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			Logger.LogInfo((object)("Applying patches as " + id));
			Harmony val = new Harmony(id);
			foreach (Type type in patches)
			{
				Logger.LogInfo((object)("Applying " + type.Name));
				val.PatchAll(type);
			}
		}

		internal static void ApplyGameplayPatches()
		{
			Logger.LogInfo((object)"Applying gameplay patches");
			ApplyPatches("gg.archipelago.rhythmdoctor.post", PostLoginPatches);
			ApplyPatches("gg.archipelago.rhythmdoctor.sleevepaint", typeof(LockSleevePaintPatch));
		}

		internal static void UnapplyGameplayPatches()
		{
			Logger.LogInfo((object)"Unapplying gameplay patches");
			Harmony.UnpatchID("gg.archipelago.rhythmdoctor.post");
			Harmony.UnpatchID("gg.archipelago.rhythmdoctor.sleevepaint");
		}

		internal static void ApplyArchipelagoMenuPatch()
		{
			Logger.LogInfo((object)"Applying Archipelago menu patch");
			Harmony.CreateAndPatchAll(CustomLoginScreenPatch, "gg.archipelago.rhythmdoctor.cls");
		}

		internal static void UnapplyArchipelagoMenuPatch()
		{
			Logger.LogInfo((object)"Unapplying Archipelago menu patch");
			Harmony.UnpatchID("gg.archipelago.rhythmdoctor.cls");
		}

		private void OnApplicationQuit()
		{
			Logger.LogDebug((object)"Quitting...");
			_quitting = true;
		}

		private void OnDestroy()
		{
			if (!_quitting)
			{
				Logger.LogWarning((object)"Tearing down plugin. This is unsupported!");
				UnapplyPatchesPatch.TearDownClientPluginPatch();
				Harmony.UnpatchID("gg.archipelago.rhythmdoctor");
				Harmony.UnpatchID("gg.archipelago.rhythmdoctor.cls");
				Harmony.UnpatchID("gg.archipelago.rhythmdoctor.post");
				Harmony.UnpatchID("gg.archipelago.rhythmdoctor.trap");
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "gg.archipelago.rhythmdoctor";

		public const string PLUGIN_NAME = "RhythmDoctor.Archipelago";

		public const string PLUGIN_VERSION = "0.6.13";
	}
}
namespace RhythmDoctor.Archipelago.World
{
	internal enum Act
	{
		None,
		Act1,
		Act2,
		Act3,
		Act4,
		Act5,
		Act6,
		Act7
	}
	internal static class Bindings
	{
		internal const string GAME = "Rhythm Doctor";

		internal const long SLEEVE_PAINT_ITEM_ID = 56L;

		internal const Level RHYTHM_DOGTOR_LEVEL = (Level)84;

		internal static readonly Dictionary<Level, BaseStage> LevelToStage = new Dictionary<Level, BaseStage>
		{
			{
				(Level)2,
				new RegularStage(Act.Act1, 1L, 2L, 3L)
			},
			{
				(Level)3,
				new RegularStage(Act.Act1, 4L, 5L, 6L)
			},
			{
				(Level)4,
				new RegularStage(Act.Act1, 7L, 8L, 9L)
			},
			{
				(Level)5,
				new RegularStage(Act.Act1, 10L, 11L, 12L)
			},
			{
				(Level)53,
				new RegularStage(Act.Act1, 13L, 14L, 15L)
			},
			{
				(Level)54,
				new RegularStage(Act.Act1, 16L, 17L, 18L)
			},
			{
				(Level)18,
				new RegularStage(Act.Act3, 21L, 22L, 23L)
			},
			{
				(Level)19,
				new RegularStage(Act.Act3, 24L, 25L, 26L)
			},
			{
				(Level)20,
				new RegularStage(Act.Act3, 27L, 28L, 29L)
			},
			{
				(Level)21,
				new RegularStage(Act.Act3, 30L, 31L, 32L)
			},
			{
				(Level)22,
				new RegularStage(Act.Act3, 33L, 34L, 35L)
			},
			{
				(Level)23,
				new RegularStage(Act.Act3, 36L, 37L, 38L)
			},
			{
				(Level)26,
				new BossStage(Act.Act3, 39L, null, 40L)
			},
			{
				(Level)84,
				new BossStage(Act.Act3, 41L, null, 42L)
			},
			{
				(Level)8,
				new RegularStage(Act.Act2, 43L, 44L, 45L)
			},
			{
				(Level)9,
				new RegularStage(Act.Act2, 46L, 47L, 48L)
			},
			{
				(Level)10,
				new RegularStage(Act.Act2, 49L, 50L, 51L)
			},
			{
				(Level)11,
				new RegularStage(Act.Act2, 52L, 53L, 54L)
			},
			{
				(Level)12,
				new RegularStage(Act.Act2, 55L, 56L, 57L)
			},
			{
				(Level)13,
				new RegularStage(Act.Act2, 58L, 59L, 60L)
			},
			{
				(Level)14,
				new RegularStage(Act.Act2, 171L, 172L, 61L)
			},
			{
				(Level)15,
				new RegularStage(Act.Act2, 173L, 174L, 62L)
			},
			{
				(Level)17,
				new RegularStage(Act.Act2, 63L, 64L, 65L)
			},
			{
				(Level)48,
				new BossStage(Act.Act7, 66L, 67L, 68L)
			},
			{
				(Level)27,
				new RegularStage(Act.Act4, 71L, 72L, 73L)
			},
			{
				(Level)28,
				new RegularStage(Act.Act4, 74L, 75L, 76L)
			},
			{
				(Level)29,
				new RegularStage(Act.Act4, 77L, 78L, 79L)
			},
			{
				(Level)30,
				new RegularStage(Act.Act4, 80L, 81L, 82L)
			},
			{
				(Level)31,
				new RegularStage(Act.Act4, 83L, 84L, 85L)
			},
			{
				(Level)32,
				new RegularStage(Act.Act4, 86L, 87L, 88L)
			},
			{
				(Level)33,
				new RegularStage(Act.Act4, 89L, 90L, 91L)
			},
			{
				(Level)34,
				new RegularStage(Act.Act4, 92L, 93L, 94L)
			},
			{
				(Level)35,
				new RegularStage(Act.Act5, 97L, 98L, 99L)
			},
			{
				(Level)36,
				new RegularStage(Act.Act5, 100L, 101L, 102L)
			},
			{
				(Level)37,
				new RegularStage(Act.Act5, 103L, 104L, 105L)
			},
			{
				(Level)38,
				new RegularStage(Act.Act5, 106L, 107L, 108L)
			},
			{
				(Level)39,
				new RegularStage(Act.Act5, 175L, 176L, 109L)
			},
			{
				(Level)40,
				new RegularStage(Act.Act5, 110L, 111L, 112L)
			},
			{
				(Level)43,
				new RegularStage(Act.Act6, 126L, 127L, 128L)
			},
			{
				(Level)45,
				new RegularStage(Act.Act6, 129L, 130L, 131L)
			},
			{
				(Level)47,
				new RegularStage(Act.Act7, 135L, 136L, 137L)
			},
			{
				(Level)58,
				new RegularStage(Act.None, 144L, 145L, 146L)
			},
			{
				(Level)59,
				new RegularStage(Act.None, 147L, 148L, 149L)
			},
			{
				(Level)56,
				new RegularStage(Act.None, 150L, 151L, 152L)
			},
			{
				(Level)57,
				new RegularStage(Act.None, 153L, 154L, 155L)
			},
			{
				(Level)61,
				new RegularStage(Act.None, 156L, 157L, 158L)
			},
			{
				(Level)62,
				new RegularStage(Act.None, 159L, 160L, 161L)
			},
			{
				(Level)63,
				new RegularStage(Act.None, 162L, 163L, 164L)
			},
			{
				(Level)55,
				new RegularStage(Act.None, 165L, 166L, 167L)
			},
			{
				(Level)25,
				new RegularStage(Act.None, 168L, 169L, 170L)
			},
			{
				(Level)60,
				new RegularStage(Act.None, 177L, 178L, 179L)
			},
			{
				(Level)6,
				new BossStage(Act.Act1, 19L, null, 20L)
			},
			{
				(Level)16,
				new BossStage(Act.Act2, 69L, null, 70L)
			},
			{
				(Level)7,
				new BossStage(Act.Act4, 95L, null, 96L)
			},
			{
				(Level)42,
				new BossStage(Act.Act5, 123L, 124L, 125L)
			},
			{
				(Level)46,
				new BossStage(Act.Act6, 132L, null, 134L)
			},
			{
				(Level)49,
				new BossStage(Act.Act7, 138L, 139L, 140L)
			},
			{
				(Level)50,
				new BossStage(Act.Act7, 141L, 142L, 143L)
			}
		};

		internal static readonly Dictionary<long, Level> ItemIdToLevel = new Dictionary<long, Level>
		{
			{
				1L,
				(Level)2
			},
			{
				2L,
				(Level)3
			},
			{
				3L,
				(Level)4
			},
			{
				4L,
				(Level)5
			},
			{
				5L,
				(Level)53
			},
			{
				6L,
				(Level)54
			},
			{
				7L,
				(Level)18
			},
			{
				8L,
				(Level)19
			},
			{
				9L,
				(Level)20
			},
			{
				10L,
				(Level)21
			},
			{
				11L,
				(Level)22
			},
			{
				12L,
				(Level)23
			},
			{
				13L,
				(Level)8
			},
			{
				14L,
				(Level)9
			},
			{
				15L,
				(Level)10
			},
			{
				16L,
				(Level)11
			},
			{
				17L,
				(Level)12
			},
			{
				18L,
				(Level)13
			},
			{
				19L,
				(Level)14
			},
			{
				20L,
				(Level)15
			},
			{
				21L,
				(Level)17
			},
			{
				22L,
				(Level)48
			},
			{
				23L,
				(Level)27
			},
			{
				24L,
				(Level)28
			},
			{
				25L,
				(Level)29
			},
			{
				26L,
				(Level)30
			},
			{
				27L,
				(Level)31
			},
			{
				28L,
				(Level)32
			},
			{
				29L,
				(Level)33
			},
			{
				30L,
				(Level)34
			},
			{
				31L,
				(Level)35
			},
			{
				32L,
				(Level)36
			},
			{
				33L,
				(Level)37
			},
			{
				34L,
				(Level)38
			},
			{
				35L,
				(Level)39
			},
			{
				36L,
				(Level)40
			},
			{
				37L,
				(Level)41
			},
			{
				38L,
				(Level)43
			},
			{
				39L,
				(Level)45
			},
			{
				40L,
				(Level)47
			},
			{
				41L,
				(Level)58
			},
			{
				42L,
				(Level)59
			},
			{
				43L,
				(Level)56
			},
			{
				44L,
				(Level)57
			},
			{
				45L,
				(Level)61
			},
			{
				46L,
				(Level)62
			},
			{
				47L,
				(Level)63
			},
			{
				48L,
				(Level)55
			},
			{
				49L,
				(Level)25
			},
			{
				67L,
				(Level)60
			}
		};

		internal static readonly Dictionary<long, Type> TrapItemIdToLevel = new Dictionary<long, Type>
		{
			{
				60L,
				typeof(FragileHeartTrapPatch)
			},
			{
				61L,
				typeof(HardDifficultyTrapPatch)
			},
			{
				62L,
				typeof(ScrambleCharactersTrapPatch)
			},
			{
				63L,
				typeof(ScrambleBeatsoundsTrapPatch)
			},
			{
				64L,
				typeof(ScrambleHitsoundsTrapPatch)
			},
			{
				65L,
				typeof(ChilliSpeedTrapPatch)
			},
			{
				66L,
				typeof(GhostTapTrapPatch)
			},
			{
				57L,
				typeof(StrongHeartPowerupPatch)
			},
			{
				58L,
				typeof(EasyDifficultyPowerupPatch)
			},
			{
				59L,
				typeof(IceSpeedTrapPatch)
			}
		};

		internal static readonly Dictionary<long, Region> KeyItemIdToWard = new Dictionary<long, Region>
		{
			{
				50L,
				Region.SVTWard
			},
			{
				51L,
				Region.Train
			},
			{
				52L,
				Region.PhysiotherapyWard
			},
			{
				55L,
				Region.RecordsRoom
			},
			{
				53L,
				Region.Basement
			},
			{
				54L,
				Region.GardenRoom
			}
		};

		internal static readonly long[] RhythmWeightlifterStageToLocationID = new long[12]
		{
			113L, 114L, 115L, 116L, 117L, 118L, 119L, 120L, 121L, 122L,
			180L, 181L
		};

		internal static readonly IReadOnlyCollection<Type> Traps = TrapItemIdToLevel.Values;

		internal static readonly IReadOnlyCollection<Level> Levels = LevelToStage.Keys;

		internal static readonly Dictionary<Level, Act> LevelToAct = LevelToStage.ToDictionary<KeyValuePair<Level, BaseStage>, Level, Act>((KeyValuePair<Level, BaseStage> pair) => pair.Key, (KeyValuePair<Level, BaseStage> pair) => pair.Value.StageAct);

		internal static readonly Dictionary<Region, long> RegionToKeyID = KeyItemIdToWard.ToDictionary((KeyValuePair<long, Region> pair) => pair.Value, (KeyValuePair<long, Region> pair) => pair.Key);

		internal static readonly Dictionary<Act, IEnumerable<Level>> LevelsInAct = (from pair in LevelToStage
			group pair by pair.Value.StageAct).ToDictionary((IGrouping<Act, KeyValuePair<Level, BaseStage>> grouping) => grouping.Key, (IGrouping<Act, KeyValuePair<Level, BaseStage>> grouping) => grouping.Select((KeyValuePair<Level, BaseStage> c) => c.Key));

		internal static readonly Level[] LevelsThatDoNotUnlockBoss = (Level[])(object)new Level[1] { (Level)41 };

		internal static readonly Dictionary<Act, Level[]> ActBoss = new Dictionary<Act, Level[]>
		{
			{
				Act.Act1,
				(Level[])(object)new Level[1] { (Level)6 }
			},
			{
				Act.Act2,
				(Level[])(object)new Level[1] { (Level)16 }
			},
			{
				Act.Act3,
				(Level[])(object)new Level[2]
				{
					(Level)26,
					(Level)84
				}
			},
			{
				Act.Act4,
				(Level[])(object)new Level[1] { (Level)7 }
			},
			{
				Act.Act5,
				(Level[])(object)new Level[1] { (Level)42 }
			},
			{
				Act.Act6,
				(Level[])(object)new Level[1] { (Level)46 }
			},
			{
				Act.Act7,
				(Level[])(object)new Level[2]
				{
					(Level)49,
					(Level)50
				}
			}
		};
	}
	internal enum Region
	{
		MainWard,
		SVTWard,
		Train,
		PhysiotherapyWard,
		RecordsRoom,
		Basement,
		GardenRoom
	}
}
namespace RhythmDoctor.Archipelago.World.Data
{
	internal abstract class BaseStage
	{
		internal Act StageAct { get; init; }

		internal abstract IEnumerable<long> GetLocationsToClear(Rank rank);
	}
	internal class BossStage : BaseStage
	{
		internal long ClearLocation { get; init; }

		internal long? CompletePlusLocation { get; init; }

		internal long PerfectLocation { get; init; }

		internal BossStage(Act act, long clearLocation, long? completePlusLocation, long perfectLocation)
		{
			base.StageAct = act;
			ClearLocation = clearLocation;
			CompletePlusLocation = completePlusLocation;
			PerfectLocation = perfectLocation;
		}

		internal override IEnumerable<long> GetLocationsToClear(Rank rank)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Logger.LogDebug((object)$"Getting locations to clear for rank {rank.internalValue}");
			List<long> list = new List<long>();
			if (((Rank)(ref rank)).perfected)
			{
				list.Add(PerfectLocation);
			}
			if (CompletePlusLocation.HasValue && (((Rank)(ref rank)).perfected || scnGame.instance.GetPassedLevelWithoutCheckpoints()))
			{
				list.Add(CompletePlusLocation.Value);
			}
			list.Add(ClearLocation);
			return list.AsReadOnly();
		}
	}
	internal class RegularStage : BaseStage
	{
		internal long? BRankLocation { get; init; }

		internal long? ARankLocation { get; init; }

		internal long SRankLocation { get; init; }

		internal RegularStage(Act act, long? bRankLocation, long? aRankLocation, long sRankLocation)
		{
			base.StageAct = act;
			BRankLocation = bRankLocation;
			ARankLocation = aRankLocation;
			SRankLocation = sRankLocation;
		}

		internal override IEnumerable<long> GetLocationsToClear(Rank rank)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Logger.LogDebug((object)$"Getting locations to clear for rank {rank.internalValue}");
			List<long> list = new List<long>();
			switch (Rank.op_Implicit(rank))
			{
			case -15:
			case 5:
			case 15:
				list.Add(SRankLocation);
				goto case -14;
			case -14:
			case 4:
			case 14:
				if (ARankLocation.HasValue)
				{
					list.Add(ARankLocation.Value);
				}
				goto case -13;
			case -13:
			case 3:
			case 13:
				if (BRankLocation.HasValue)
				{
					list.Add(BRankLocation.Value);
				}
				break;
			}
			return list.AsReadOnly();
		}
	}
}
namespace RhythmDoctor.Archipelago.Patches
{
	[HarmonyPatch(typeof(ArmSkin))]
	internal static class LockSleevePaintPatch
	{
		[HarmonyPatch("Load")]
		[HarmonyPrefix]
		private static void LoadDefaultSleevePatch(ref ArmSkin __instance, ref bool __runOriginal)
		{
			//IL_0012: 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_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Invalid comparison between Unknown and I4
			//IL_00c6: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			__runOriginal = false;
			int num = Plugin.Random.Next(0, 2);
			RDPlayer player = __instance.player;
			if ((int)player != 0)
			{
				if ((int)player == 1)
				{
					__instance.skinColor = RDConstants.data.skinColors[RDConstants.data.defaultP2SkinColor[num]];
					__instance.sleeveColor = RDConstants.data.sleeveColors[RDConstants.data.defaultP2SleeveColor[num]];
					__instance.nailsColor = RDConstants.data.nailColors[RDConstants.data.defaultP2NailColor[num]];
					__instance.palmLightness = RDConstants.data.defaultP2PalmLightness[num];
					__instance.drawing = RDConstants.data.emptyTexture;
				}
			}
			else
			{
				__instance.skinColor = RDConstants.data.skinColors[RDConstants.data.defaultP1SkinColor[num]];
				__instance.sleeveColor = RDConstants.data.sleeveColors[RDConstants.data.defaultP1SleeveColor[num]];
				__instance.nailsColor = RDConstants.data.nailColors[RDConstants.data.defaultP1NailColor[num]];
				__instance.palmLightness = RDConstants.data.defaultP1PalmLightness[num];
				__instance.drawing = RDConstants.data.emptyTexture;
			}
		}
	}
	[HarmonyPatch(typeof(RDVersionText))]
	internal static class VersionTextPatch
	{
		[HarmonyPatch("SetPage")]
		[HarmonyPostfix]
		private static void AppendClientVersionPatch(RDVersionText __instance)
		{
			Text text = __instance.text;
			string text2 = (text.text += " / Archipelago v0.6.13");
			string text4 = text2;
			__instance.text.text = text4;
		}
	}
	[HarmonyPatch]
	internal static class SavingPatch
	{
		[HarmonyPatch(typeof(PlayerPrefsJson), "Save")]
		[HarmonyPatch(typeof(PlayerPrefsJson), "SaveBackup")]
		[HarmonyPrefix]
		private static void DisableSavingToFilePatch(ref bool __runOriginal, ref PlayerPrefsJson __instance)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			__runOriginal = (int)__instance.fileType == 0;
		}

		[HarmonyPatch(typeof(ArmSkin), "GetDrawingPath")]
		[HarmonyPrefix]
		private unsafe static void RedirectSleevePaintFilePatch(RDPlayer player, ref string __result, ref bool __runOriginal)
		{
			__runOriginal = false;
			__result = Path.Combine(PlayerPrefsJson.GetFileFolderPath(), "scribble" + ((object)(*(RDPlayer*)(&player))/*cast due to .constrained prefix*/).ToString() + "_AP.png");
		}
	}
	[HarmonyPatch]
	internal static class UnapplyPatchesPatch
	{
		[HarmonyPatch(typeof(scnMenu), "Awake")]
		[HarmonyPatch(typeof(scnBase), "GoToMainMenu")]
		[HarmonyPrefix]
		internal static void TearDownClientPluginPatch()
		{
			Plugin.Logger.LogInfo((object)"Tearing down client plugin");
			Plugin.UnapplyGameplayPatches();
			Persistence.Load();
			try
			{
				scnMenu.instance.slots[Configuration.GetSlotToUse()].LoadSlotData();
				Plugin.Logger.LogDebug((object)"Reloaded slot data");
			}
			catch (NullReferenceException)
			{
				Plugin.Logger.LogDebug((object)"Not in main menu, could not load slot data");
			}
			Plugin.Client?.Dispose();
			Plugin.Client = null;
		}
	}
}
namespace RhythmDoctor.Archipelago.Patches.Menu
{
	[HarmonyPatch]
	internal static class ArchipelagoLoginPatch
	{
		[HarmonyPatch(typeof(scnCLS), "Start")]
		[HarmonyPostfix]
		private static void ConstructArchipelagoMenuPatch(scnCLS __instance)
		{
			Plugin.Logger.LogInfo((object)"Renaming custom level ward items");
			Plugin.Logger.LogInfo((object)"Renaming LED sign");
			GameObject.Find("Canvas/Ward Container/LEDSign Container/WardTitle Text").GetComponent<Text>().text = RDString.Get("archipelago.connectScreenTitle");
			Plugin.Logger.LogInfo((object)"Renaming ward options");
			WardOption val = __instance.wardOptions.Find((WardOption wardOption) => (int)wardOption.name == 0);
			WardOption val2 = __instance.wardOptions.Find((WardOption wardOption) => (int)wardOption.name == 1);
			WardOption? obj = __instance.wardOptions.Find((WardOption wardOption) => (int)wardOption.name == 2);
			__instance.ChangeToImportOption();
			if (val != null)
			{
				RectTransform rect = val.rect;
				if (rect != null)
				{
					Transform parent = ((Component)rect).transform.parent;
					if (parent != null)
					{
						((Component)parent).gameObject.SetActive(false);
					}
				}
				__instance.wardOptions.Remove(val);
			}
			if (val2 != null)
			{
				RectTransform rect2 = val2.rect;
				if (rect2 != null)
				{
					Transform parent2 = ((Component)rect2).transform.parent;
					if (parent2 != null)
					{
						((Component)parent2).gameObject.SetActive(false);
					}
				}
				__instance.wardOptions.Remove(val2);
			}
			Plugin.Logger.LogInfo((object)"Renaming Import to Archipelago option");
			Transform obj2 = ((Transform)obj.rect).Find("Button");
			((Component)obj2.Find("Icon Image")).GetComponent<Image>().sprite = AssetHelper.LoadSprite("WardIcons", "Archipelago.png");
			((Component)obj2.Find("Text")).GetComponent<Text>().text = RDString.Get("archipelago.loginButton");
			Plugin.Logger.LogInfo((object)"Disabling DragAndDrop");
			__instance.levelImporter.dragAndDrop.SetActive(false);
			__instance._currentWardOptionIndex = 0;
		}

		[HarmonyPatch(typeof(LevelImporter), "Install")]
		[HarmonyPostfix]
		private static IEnumerator OverrideInstallButtonPatch(IEnumerator result, LevelImporter __instance)
		{
			Plugin.Logger.LogInfo((object)"Locking input");
			((RDBase)__instance).cls.CLSPlaySound("sndImportInstallButtonClick", 1f, "CustomLevelSelect", 1f, 0f);
			__instance.ToggleInsertUrlContainer(false, true, false);
			__instance.CurrentContentName = (ContentName)2;
			__instance.stoppedInstallCoroutine = false;
			__instance.CanToggleClearButton = false;
			__instance.levelsToInstall = new List<ImportLevel>(__instance.toInstallIS.levels);
			foreach (ImportLevel item in __instance.levelsToInstall)
			{
				item.canToggleRemoveButton = false;
			}
			yield return null;
			((Selectable)__instance.stopButton).interactable = false;
			string text = ((Component)((Component)__instance).transform.Find("screen/Contents/InsertURL Container/URL InputField/Text")).GetComponent<Text>().text;
			string[] array = (from text2 in text.Split('\n')
				select text2.Trim()).ToArray();
			Plugin.Logger.LogInfo((object)("Input: '" + GeneralExtensions.Join<string>((IEnumerable<string>)array, (Func<string, string>)null, ", ") + "' (raw: " + Convert.ToBase64String(Encoding.UTF8.GetBytes(text))));
			if (array.Length < 2)
			{
				Plugin.Logger.LogError((object)"URL or Name not input, bailing out");
				__instance.CurrentContentName = (ContentName)3;
				((RDBase)__instance).cls.CLSPlaySound("sndImportInstallFinish", 1f, "CustomLevelSelect", 1f, 0f);
				__instance.AddLevelToErrorSection(__instance.levelsToInstall[0], RDString.Get("archipelago.login.fail.missingInformation"));
				yield break;
			}
			string url = array[0];
			string name = array[1];
			string password = null;
			try
			{
				password = array[2];
			}
			catch (IndexOutOfRangeException)
			{
			}
			Plugin.Logger.LogInfo((object)("URL: " + url + ", Slot Name: " + name));
			if (Utility.IsNullOrWhiteSpace(url) || Utility.IsNullOrWhiteSpace(name))
			{
				Plugin.Logger.LogError((object)"Invalid information, bailing out");
			}
			else
			{
				Plugin.Logger.LogInfo((object)"Creating client");
				Plugin.Client = new RhythmDoctor.Archipelago.Client.Client();
				Task<LoginResult> login = Task.Run(() => Plugin.Client.CreateSessionAndConnect(url, name, password));
				yield return (object)new WaitUntil((Func<bool>)(() => login.IsCompleted));
				if (login.IsCanceled || login.IsFaulted)
				{
					string arg = (login.IsFaulted ? login.Exception.ToString() : "false");
					Plugin.Logger.LogError((object)$"Login has cancelled or faulted (cancel: {login.IsCanceled} / fault: {arg})");
				}
				else
				{
					LoginResult result2 = login.Result;
					if (result2 is LoginSuccessful)
					{
						Plugin.Logger.LogInfo((object)"Logged in!");
						((RDBase)__instance).cls.CLSPlaySound("sndImportInstallFinish", 1f, "CustomLevelSelect", 1f, 0f);
						yield return null;
						UnpatchMenuPatch();
						while (!Plugin.Client.Setup)
						{
							yield return (object)new WaitForSecondsRealtime(1f);
						}
						Plugin.Logger.LogInfo((object)"Heading to Level Select...");
						scnBase.GoToScene("scnLevelSelect");
						yield break;
					}
					LoginFailure val = (LoginFailure)(object)((result2 is LoginFailure) ? result2 : null);
					if (val == null)
					{
						Plugin.Logger.LogWarning((object)"Got to the end of OverrideInstallButtonPatch - this should never happen!");
						yield break;
					}
					Plugin.Logger.LogError((object)("Got LoginFailure: " + string.Join(", ", val.ErrorCodes) + " / " + string.Join(", ", val.Errors)));
				}
				Plugin.Logger.LogError((object)"Login failed (Login)");
				UnapplyPatchesPatch.TearDownClientPluginPatch();
				Plugin.Client?.Dispose();
				Plugin.Client = null;
			}
			__instance.CurrentContentName = (ContentName)3;
			((RDBase)__instance).cls.CLSPlaySound("sndImportInstallFinish", 1f, "CustomLevelSelect", 1f, 0f);
			((Component)((Component)__instance).transform.Find("screen/Contents/Top Panel/Title Text")).GetComponent<Text>().text = RDString.Get("archipelago.login.fail.title");
		}

		[HarmonyPatch(typeof(scnCLS), "Exit")]
		[HarmonyPostfix]
		private static void UnpatchMenuPatch()
		{
			Plugin.UnapplyArchipelagoMenuPatch();
		}

		[HarmonyPatch(typeof(scnCLS), "SelectWardOption")]
		[HarmonyPostfix]
		private static void CustomSelectOptionPatch(ref bool __runOriginal, scnCLS __instance)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			__runOriginal = false;
			WardOptionName name = __instance.CurrentWardOption.name;
			if ((int)name <= 1)
			{
				Plugin.Logger.LogWarning((object)"Library/Steam Workshop selected in Archipelago login screen");
			}
			else
			{
				__runOriginal = true;
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void ArchipelagoImportScreenPatch()
		{
			GameObject gameObject = ((Component)((Component)((Component)scnCLS.instance.levelImporter).gameObject.transform.Find("screen")).gameObject.transform.Find("Contents")).gameObject;
			GameObject gameObject2 = ((Component)gameObject.transform.Find("InsertURL Container")).gameObject;
			GameObject gameObject3 = ((Component)gameObject2.transform.Find("Add Button")).gameObject;
			Text component = ((Component)((Component)gameObject2.transform.Find("URL InputField")).gameObject.transform.Find("Placeholder")).gameObject.GetComponent<Text>();
			Text component2 = ((Component)gameObject2.transform.Find("Instructions")).GetComponent<Text>();
			Text component3 = ((Component)gameObject3.transform.Find("Text")).GetComponent<Text>();
			((UnityEvent)((Component)gameObject.transform.Find("Draggable Content/AddURL Button")).GetComponent<Button>().onClick).Invoke();
			((Component)gameObject.transform.Find("Top Panel/Title Text")).GetComponent<Text>().text = RDString.Get("archipelago.login.menu.title");
			((Component)gameObject2.transform.Find("Cancel Button")).gameObject.SetActive(false);
			component2.text = RDString.Get("archipelago.login.menu.instructions");
			component.text = RDString.Get("archipelago.login.menu.hint");
			component3.text = RDString.Get("archipelago.login.menu.connectButton");
		}

		[HarmonyPatch(typeof(LevelImporter), "ValidateUrl")]
		[HarmonyPrefix]
		private static void StubValidateUrlPatch(ref bool __runOriginal, LevelImporter __instance)
		{
			__runOriginal = false;
			__instance.Install_Public();
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		private static void DisableDragAndDropInstallGetPatch(ref bool __result, ref bool __runOriginal)
		{
			__runOriginal = false;
			__result = false;
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		private static void DisableDragAndDropInstallSetPatch(ref bool __runOriginal)
		{
			__runOriginal = false;
		}
	}
	[HarmonyPatch(typeof(scnMenu))]
	internal static class ArchipelagoMainMenuOptionPatch
	{
		private const string ARCHIPELAGO_OBJECT_NAME = "archipelago";

		[HarmonyPatch("Awake")]
		[HarmonyPrefix]
		private static void RenameMusicOptionPatch(scnMenu __instance)
		{
			((Object)((Component)((Component)__instance).transform.Find("mainMenu/options/optionsContainer/music")).gameObject).name = "archipelago";
		}

		[HarmonyPatch("SelectOption")]
		[HarmonyPrefix]
		private static void HandleArchipelagoOptionSelectedPatch(ref bool __runOriginal, scnMenu __instance)
		{
			if (((Object)((Component)__instance.optionsText[__instance.currentOption]).gameObject).name == "archipelago")
			{
				__runOriginal = false;
				Plugin.ApplyArchipelagoMenuPatch();
				__instance.PlayConfirmSound();
				__instance.TransitionToScene("scnCLS");
			}
		}
	}
}
namespace RhythmDoctor.Archipelago.Patches.Gameplay
{
	[HarmonyPatch(typeof(scnLevelSelect))]
	internal static class Act5Patch
	{
		[HarmonyPatch("PerformEntityAction")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> NicoleBlockageInteractionPatch(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			return new CodeMatcher(instructions, (ILGenerator)null).MatchForward(true, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Ldstr, (object)"GoToAthleteWard", (string)null)
			}).Advance(3).RemoveInstructions(8)
				.SetOpcodeAndAdvance(OpCodes.Ldc_I4_0)
				.InstructionEnumeration();
		}

		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		private static void DematerializeNicolePatch(scnLevelSelect __instance)
		{
			((Entity)__instance.nicoleAct5Blockage).visible = false;
		}

		[HarmonyPatch("PrepareAthleteWardTransition")]
		[HarmonyPrefix]
		private static void DoNotShowDreamBubblesPatch(ref bool __runOriginal)
		{
			__runOriginal = false;
		}
	}
	[HarmonyPatch]
	internal static class ClearLocationPatch
	{
		private static Dictionary<long, ScoutedItemInfo> ItemsToSend = new Dictionary<long, ScoutedItemInfo>();

		private static long[] JustSentLocations = Array.Empty<long>();

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		private static void ScoutItemsSentPatch(LevelBase __instance)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Invalid comparison between Unknown and I4
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: 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)
			Plugin.Logger.LogDebug((object)("Scouting locations to send for " + scnGame.internalIdentifier));
			ItemsToSend.Clear();
			JustSentLocations = Array.Empty<long>();
			if (!Enum.TryParse<Level>(scnGame.internalIdentifier, out Level result))
			{
				Plugin.Logger.LogError((object)("Couldn't find Level. Level identifier: " + scnGame.internalIdentifier));
				Plugin.Client.TrapManager.ClearActiveTraps(returnToQueue: false);
				return;
			}
			if ((int)result == 26 && (__instance.dogMode || scnGame.loadDogMode))
			{
				result = (Level)84;
			}
			IEnumerable<long> locationsToClear = Bindings.LevelToStage[result].GetLocationsToClear(Rank.op_Implicit(5));
			((MonoBehaviour)Plugin.Instance).StartCoroutine(ScoutLocationChecks(locationsToClear.ToArray()));
		}

		[HarmonyPatch(typeof(Rankscreen), "ShowAndSaveRank")]
		[HarmonyPrefix]
		private static void CustomClearLocationPatch(bool bossLevelFailed, bool onlySavePersistence, Rankscreen __instance)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			Level currentLevel = GetCurrentLevel();
			Rank rankFromMistakes = scnGame.instance.currentLevel.GetRankFromMistakes();
			SendLocations(currentLevel, rankFromMistakes, bossLevelFailed);
		}

		[HarmonyPatch(typeof(Level), "GetRank")]
		[HarmonyPostfix]
		private static void RhythmWeightlifterClearLocationPatch(string __result)
		{
			if (!(__result == "-"))
			{
				Plugin.Client.Session.Locations.CompleteLocationChecks(new long[1] { Bindings.RhythmWeightlifterStageToLocationID[scnRhythmWeightlifter.gameInstance.LevelIndex] });
			}
		}

		[HarmonyPatch(typeof(Rankscreen), "ShowRankDescription")]
		[HarmonyPostfix]
		private static void ShowSentItemsPatch(Rankscreen __instance)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			if (!ItemsToSend.Any())
			{
				Plugin.Logger.LogWarning((object)"Couldn't get items sent, possibly due to a network issue.");
				__instance.description.text = "<color=red>" + RDString.Get("archipelago.rankscreen.error") + "</color>";
			}
			else
			{
				Rank rankFromMistakes = scnGame.instance.currentLevel.GetRankFromMistakes();
				IEnumerable<long> stageLocationIDsToClear = GetStageLocationIDsToClear(GetCurrentLevel(), rankFromMistakes);
				long[] array = stageLocationIDsToClear.Where((long id) => !Plugin.Client.Session.Locations.AllLocationsChecked.Contains(id) || JustSentLocations.Contains(id)).ToArray();
				Plugin.Logger.LogDebug((object)("IDs: " + string.Join(", ", stageLocationIDsToClear) + " (of which " + string.Join(", ", array) + " are new)"));
				if (!stageLocationIDsToClear.Any())
				{
					__instance.description.text = RDString.Get("archipelago.rankscreen.nothing");
					return;
				}
				if (array.Length == 0)
				{
					__instance.description.text = RDString.Get("archipelago.rankscreen.nothingNew");
					return;
				}
				__instance.description.text = "";
				__instance.description.fontSize = 10;
				long[] array2 = array;
				foreach (long num2 in array2)
				{
					if (!ItemsToSend.TryGetValue(num2, out ScoutedItemInfo value))
					{
						Plugin.Logger.LogWarning((object)($"Could not get scouted item information for location ID {num2}. " + "This is normal if this ID is tied to a Perfect rank, and Perfect locations are disabled."));
						continue;
					}
					string text = "silver";
					if (((Enum)((ItemInfo)value).Flags).HasFlag((Enum)(object)(ItemFlags)1) || ((Enum)((ItemInfo)value).Flags).HasFlag((Enum)(object)(ItemFlags)2))
					{
						text = "yellow";
					}
					else if (((Enum)((ItemInfo)value).Flags).HasFlag((Enum)(object)(ItemFlags)4))
					{
						text = "red";
					}
					if (value.IsReceiverRelatedToActivePlayer)
					{
						Text description = __instance.description;
						description.text += RDStringExtensions.GetWithSubstitution("archipelago.rankscreen.found", new KeyValuePair<string, string>[1]
						{
							new KeyValuePair<string, string>("item", "<color=" + text + ">" + ((ItemInfo)value).ItemDisplayName + "</color>")
						});
					}
					else
					{
						Text description2 = __instance.description;
						description2.text += RDStringExtensions.GetWithSubstitution("archipelago.rankscreen.sentTo", new KeyValuePair<string, string>[2]
						{
							new KeyValuePair<string, string>("item", "<color=" + text + ">" + ((ItemInfo)value).ItemDisplayName + "</color>"),
							new KeyValuePair<string, string>("player", value.Player.Alias)
						});
					}
				}
			}
			ItemsToSend.Clear();
			JustSentLocations = Array.Empty<long>();
		}

		[HarmonyPatch(typeof(Level_Montage), "SetLevelAsPassed")]
		[HarmonyPostfix]
		private static void MiracleDefibrillatorClearLocationPatch(Level_Montage __instance)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			bool flag = Plugin.Client.TrapManager.IsTrapActive(ScrambleCharactersTrapPatch.name);
			int num = ((!__instance.missedOnce && !((RDClass)__instance).game.GetPassedLevelWithoutCheckpoints()) ? 5 : ((!((RDClass)__instance).game.GetPassedLevelWithoutCheckpoints()) ? 4 : 14));
			IEnumerable<long> enumerable = SendLocations((Level)49, Rank.op_Implicit(num));
			if (!ItemsToSend.Any())
			{
				Plugin.Logger.LogWarning((object)"Couldn't get items sent, possibly due to a network issue.");
				((RDClass)__instance).game.statusText.SetStatusText("Couldn't get items sent.", (Color?)Color.red, 10f, false, true);
			}
			else
			{
				long[] array = enumerable.Where((long id) => !Plugin.Client.Session.Locations.AllLocationsChecked.Contains(id) || JustSentLocations.Contains(id)).ToArray();
				Plugin.Logger.LogDebug((object)("IDs: " + string.Join(", ", enumerable) + " (of which " + string.Join(", ", array) + " are new)"));
				if (!enumerable.Any())
				{
					((RDClass)__instance).game.statusText.SetStatusText(RDString.Get("archipelago.rankscreen.nothing"), (Color?)null, 4f, false, false);
					return;
				}
				if (array.Length == 0)
				{
					((RDClass)__instance).game.statusText.SetStatusText(RDString.Get("archipelago.rankscreen.nothingNew"), (Color?)null, 4f, false, false);
					return;
				}
				IEnumerable<string> values = array.Select((long id) => ((ItemInfo)ItemsToSend[id]).ItemDisplayName);
				((RDClass)__instance).game.statusText.SetStatusText("Found " + string.Join(", ", values), (Color?)null, 10f, false, true);
			}
			if (flag)
			{
				Plugin.Logger.LogInfo((object)"Setting Room 1, Row 2 to Cole");
				((RDClass)__instance).game.rows[1].ent.character.ChangeCharacter((Character)13, (string)null);
			}
		}

		private static IEnumerable<long> SendLocations(Level level, Rank rank, bool bossLevelFailed = false)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_0040: Invalid comparison between Unknown and I4
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			if (bossLevelFailed)
			{
				Plugin.Client.TrapManager.ClearActiveTraps(returnToQueue: false);
				return Array.Empty<long>();
			}
			IEnumerable<long> ids = GetStageLocationIDsToClear(level, rank);
			if (Plugin.Client.Slot.endGoal == SlotData.EndGoal.HelpingHands && (int)level == 55 && ((Rank)(ref rank)).passed)
			{
				Plugin.Logger.LogInfo((object)"Setting goal achieved - Helping Hands");
				Plugin.Client.Session.SetGoalAchieved();
			}
			else if (Plugin.Client.Slot.endGoal != SlotData.EndGoal.HelpingHands)
			{
				bool flag = true;
				Rank val = Rank.op_Implicit(Plugin.Client.Slot.endGoal switch
				{
					SlotData.EndGoal.PerfectAll => 5, 
					SlotData.EndGoal.ARankAll => 4, 
					SlotData.EndGoal.BRankAll => 3, 
					_ => throw new ArgumentOutOfRangeException($"End Goal ({Plugin.Client.Slot.endGoal}) not valid value."), 
				});
				foreach (Level level2 in Bindings.Levels)
				{
					Rank levelRank = Persistence.GetLevelRank(level2, false, -1);
					if (Rank.op_Implicit(val) > Rank.op_Implicit(((Rank)(ref levelRank)).ToNormal()))
					{
						flag = false;
						break;
					}
				}
				if (flag)
				{
					Plugin.Logger.LogInfo((object)"Setting goal achieved - Cleared all");
					Plugin.Client.Session.SetGoalAchieved();
				}
			}
			if (ids.Any((long id) => !Plugin.Client.Session.Locations.AllLocationsChecked.Contains(id)))
			{
				JustSentLocations = ids.Where((long id) => !Plugin.Client.Session.Locations.AllLocationsChecked.Contains(id)).ToArray();
				Task.Run(() => Plugin.Client.Session.Locations.CompleteLocationChecksAsync(ids.ToArray()));
				Plugin.Client.TrapManager.ClearActiveTraps(returnToQueue: false);
			}
			else
			{
				Plugin.Client.TrapManager.ClearActiveTraps(returnToQueue: true);
			}
			return ids;
		}

		private static IEnumerator ScoutLocationChecks(long[] ids, int retries = 0)
		{
			Plugin.Logger.LogInfo((object)string.Format("Scouting location checks for ids {0}... (try {1})", string.Join(", ", ids), retries));
			Task<Dictionary<long, ScoutedItemInfo>> scout = Task.Run(() => Plugin.Client.Session.Locations.ScoutLocationsAsync((HintCreationPolicy)0, ids));
			yield return (object)new WaitUntil((Func<bool>)(() => scout.IsCompleted));
			Plugin.Logger.LogInfo((object)"Completed scouting");
			if (!scout.IsCompletedSuccessfully)
			{
				if (retries >= 3)
				{
					Plugin.Logger.LogError((object)$"Couldn't scout locations - on try #{retries}, retrying");
					((MonoBehaviour)Plugin.Instance).StartCoroutine(ScoutLocationChecks(ids, retries + 1));
				}
				else
				{
					Plugin.Logger.LogError((object)"Couldn't scout locations");
				}
			}
			else
			{
				ItemsToSend = scout.Result;
			}
		}

		private static Level GetCurrentLevel()
		{
			//IL_0065: 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_0060: Invalid comparison between Unknown and I4
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			if (!Enum.TryParse<Level>(scnGame.internalIdentifier, out Level result))
			{
				Plugin.Logger.LogError((object)("Couldn't find Level. Level identifier: " + scnGame.internalIdentifier));
				Plugin.Client.TrapManager.ClearActiveTraps(returnToQueue: false);
				throw new ArgumentOutOfRangeException("Couldn't find level " + scnGame.internalIdentifier);
			}
			if (scnGame.instance.currentLevel.dogMode && (int)result == 26)
			{
				return (Level)84;
			}
			return result;
		}

		private static IEnumerable<long> GetStageLocationIDsToClear(Level level, Rank rank)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Logger.LogDebug((object)"Getting locations to clear");
			IEnumerable<long> locationsToClear = Bindings.LevelToStage[level].GetLocationsToClear(rank);
			Plugin.Logger.LogDebug((object)string.Format("Stage to clear: {0} with rank {1} ({2})", level, rank.internalValue, string.Join(", ", locationsToClear)));
			return locationsToClear;
		}
	}
	[HarmonyPatch]
	internal static class DeathLinkPatch
	{
		internal static bool enabled = true;

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		private static void ResetDeathLinkPatch()
		{
			Plugin.Logger.LogDebug((object)"Enabling DeathLink patch");
			enabled = true;
		}

		[HarmonyPatch(typeof(RowEntity), "CrackAdvance")]
		[HarmonyPostfix]
		private static void SendDeathLinkOnCrackedHeartPatch(RowEntity __instance)
		{
			if (Plugin.Client.DeathLink != null && enabled && !(__instance.rowMisses < ((RDBase)__instance).game.currentLevel.missesToCrackHeart))
			{
				Plugin.Logger.LogInfo((object)"Sending CrackAdvance death");
				enabled = false;
				Plugin.Client.SendDeathLink();
			}
		}

		[HarmonyPatch(typeof(scnGame), "FailLevel")]
		[HarmonyPostfix]
		private static void SendDeathLinkOnLevelFailPatch()
		{
			if (enabled)
			{
				Plugin.Logger.LogInfo((object)"Sending FailLevel death");
				enabled = false;
				Plugin.Client.SendDeathLink();
			}
		}

		[HarmonyPatch(typeof(scnGame), "FailLevelLite")]
		[HarmonyPostfix]
		private static void SendDeathLinkOnLevelFailLitePatch(scnGame __instance)
		{
			if (enabled && !(__instance.levelIdentifier == "Montage"))
			{
				Plugin.Logger.LogInfo((object)"Sending FailLevelLite death");
				enabled = false;
				Plugin.Client.SendDeathLink();
			}
		}

		[HarmonyPatch(typeof(LevelBase), "RunTag")]
		[HarmonyPostfix]
		private static void SendDeathLinkOnBeansHopperLossPatch(string tag)
		{
			if (enabled && scnGame.internalIdentifier == "BeansHopper" && tag == "miss" && scnGame.instance.currentLevel.i1 < 30)
			{
				Plugin.Logger.LogInfo((object)"Sending Beans Hopper death");
				enabled = false;
				Plugin.Client.SendDeathLink();
			}
		}
	}
	[HarmonyPatch(typeof(scnLevelSelect))]
	internal static class JanitorPatch
	{
		[HarmonyPatch("PerformEntityAction")]
		[HarmonyPrefix]
		private static void InteractJanitorPatch(scnLevelSelect __instance, ref bool __runOriginal)
		{
			SelectableEntity selectedEntity = __instance.selectedEntity;
			SelectableObject val = (SelectableObject)(object)((selectedEntity is SelectableObject) ? selectedEntity : null);
			if (val == null)
			{
				Plugin.Logger.LogWarning((object)"Current selectable object is null");
			}
			else if (val.action != "talkToJanitor")
			{
				Plugin.Logger.LogDebug((object)"Current selectable object's action is not talkToJanitor");
			}
			else
			{
				Plugin.Logger.LogError((object)"TODO: Implement janitor menu patch");
			}
		}

		[HarmonyPatch("ShowRanksText")]
		[HarmonyPrefix]
		private static void TextJanitorPatch(int index, scnLevelSelect __instance, ref bool __runOriginal)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			SelectableEntity obj = __instance.selectableEntities[index];
			SelectableObject val = (SelectableObject)(object)((obj is SelectableObject) ? obj : null);
			if (val == null)
			{
				Plugin.Logger.LogDebug((object)"Selectable entities is null");
				return;
			}
			if (!((SelectableEntity)val).id.StartsWith("TalkToJanitor"))
			{
				Plugin.Logger.LogDebug((object)"Selected object is not Janitor, doing nothing");
				return;
			}
			Plugin.Logger.LogDebug((object)"Overriding Janitor text");
			__runOriginal = false;
			__instance.ChangeTextOutline(RDConstants.data.levelSelect_notPassedLevelTextOutline);
			__instance.SetDifficultyArrowsVisible(false);
			__instance.description.text = RDString.Get("mainMenu.Archipelago");
		}

		[HarmonyPatch("PlaceJanitor")]
		[HarmonyPrefix]
		private static void PlaceJanitorPatch(ref bool __runOriginal)
		{
			Plugin.Logger.LogDebug((object)"Bypassing PlaceJanitor");
			__runOriginal = false;
		}

		[HarmonyPatch("HideJanitor")]
		[HarmonyPrefix]
		private static void HideJanitorPatch(ref bool __runOriginal)
		{
			Plugin.Logger.LogWarning((object)"Bypassing HideJanitor. This should never be called assuming PlaceJanitor was bypassed!");
			__runOriginal = false;
		}
	}
	[HarmonyPatch(typeof(scnLevelSelect))]
	internal static class LevelSelectVisualFixesPatch
	{
		private static readonly Level[] ForceDisplayAsRegularLevel;

		private static readonly Level[] ForceDisplayAsBossLevel;

		[HarmonyPatch("ShowRanksText")]
		[HarmonyPrefix]
		private static void ModifyLabelVisibilitiesPatch(int index, scnLevelSelect __instance)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			SelectableEntity obj = __instance.selectableEntities[index];
			SelectableCharacter val = (SelectableCharacter)(object)((obj is SelectableCharacter) ? obj : null);
			if (val != null && val.levels.TryGetValue(__instance.currentDifficulty, out var value))
			{
				if (ForceDisplayAsRegularLevel.Contains(value))
				{
					((SelectableEntity)val).levelType = (LevelType)1;
				}
				else if (ForceDisplayAsBossLevel.Contains(value))
				{
					((SelectableEntity)val).levelType = (LevelType)2;
				}
			}
		}

		[HarmonyPatch("ShowRanksText")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> DoNotShowUnlockHintsPatch(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Expected O, but got Unknown
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Expected O, but got Unknown
			return new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Ldstr, (object)"levelSelect.condition.{0}", (string)null)
			}).SetOpcodeAndAdvance(OpCodes.Nop).SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
				{
					new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.Field(typeof(scnLevelSelect), "currentDifficulty"), (string)null)
				})
				.Advance(-1)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Nop)
				.SetOpcodeAndAdvance(OpCodes.Br_S)
				.MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
				{
					new CodeMatch((OpCode?)OpCodes.Ldstr, (object)"", (string)null)
				})
				.InstructionEnumeration();
		}

		static LevelSelectVisualFixesPatch()
		{
			Level[] array = new Level[6];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			ForceDisplayAsRegularLevel = (Level[])(object)array;
			ForceDisplayAsBossLevel = (Level[])(object)new Level[1] { (Level)16 };
		}
	}
	[HarmonyPatch]
	internal static class RhythmDogtorLevelPatch
	{
		[HarmonyPatch(typeof(scnLevelSelect), "LoadLevelData")]
		[HarmonyPostfix]
		private static void AddRhythmDogtorPatch(scnLevelSelect __instance)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: 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)
			//IL_00d9: Expected O, but got Unknown
			//IL_00d9: 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_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Expected O, but got Unknown
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Expected O, but got Unknown
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Logger.LogDebug((object)"Creating Rhythm Dogtor entity");
			SelectableCharacter val = (SelectableCharacter)__instance.FindSelectableEntity("3-X");
			val.bpmHard = val.bpmNormal;
			((SelectableEntity)val).hardEnabled = true;
			val.charactersHard = (Character[])(object)new Character[1] { (Character)22 };
			val.levels[(Difficulty)2] = (Level)84;
			val.portraitsHard = (Character[])(object)new Character[2]
			{
				(Character)23,
				(Character)22
			};
			if (!GC.levelMetadata.ContainsKey((Level)84))
			{
				Plugin.Logger.LogDebug((object)"Creating Rhythm Dogtor metadata");
				LevelMetadata val2 = GC.levelMetadata[(Level)26];
				GuestData guest = new GuestData
				{
					link = val2.guest1.link,
					linkType = val2.guest1.linkType,
					name = val2.guest1.name,
					type = "Mix"
				};
				GuestData guest2 = new GuestData
				{
					link = "https://x.com/Morphious86",
					linkType = "Twitter",
					name = "Morphious86",
					type = "VocalsAndArt"
				};
				LevelMetadata val3 = new LevelMetadata();
				val3.id = "3-DOG";
				val3.type = val2.type;
				val3.portraits = (Character[])(object)new Character[2]
				{
					(Character)23,
					(Character)22
				};
				val3.characters = (Character[])(object)new Character[1] { (Character)22 };
				val3.guest1 = guest;
				val3.guest2 = guest2;
				val3.guest3 = null;
				val3.guest4 = null;
				LevelMetadata value = val3;
				GC.levelMetadata.Add((Level)84, value);
			}
		}

		[HarmonyPatch(typeof(scnLevelSelect), "GoToLevelSequence")]
		[HarmonyPrefix]
		private static void GoToRhythmDogtorPatch(ref string levelToGo, scnLevelSelect __instance)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			if ((int)__instance.currentDifficulty == 2 && (int)RDUtils.ParseEnum<Level>(levelToGo, (Level)0) == 84)
			{
				Plugin.Logger.LogInfo((object)"Going to Rhythm Dogtor...");
				Persistence.SetLastPlayedLevel((Level)84);
				levelToGo = "Lesmis";
				scnGame.loadDogMode = true;
			}
		}

		[HarmonyPatch(typeof(Rankscreen), "ShowAndSaveRank")]
		[HarmonyPrefix]
		private static void SaveRhythmDogtorRankPatch(bool bossLevelFailed, Rankscreen __instance)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (!bossLevelFailed && !(((RDBase)__instance).game.levelIdentifier != "Lesmis") && ((RDBase)__instance).game.currentLevel.dogMode)
			{
				Plugin.Logger.LogInfo((object)"Saving Rhythm Dogtor rank...");
				Persistence.SetLevelRank((Level)84, ((RDBase)__instance).game.currentLevel.GetRankFromMistakes(), false, true);
			}
		}

		[HarmonyPatch(typeof(scnLevelSelect), "SetDifficulty")]
		[HarmonyPrefix]
		private static void ShowAdogPatch(Difficulty newDifficulty, scnLevelSelect __instance)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Invalid comparison between Unknown and I4
			Rank levelRank = Persistence.GetLevelRank((Level)26, false, -1);
			bool passed = ((Rank)(ref levelRank)).passed;
			if ((int)newDifficulty != 1)
			{
				if ((int)newDifficulty == 2)
				{
					__instance.FindSelectableEntity("3-X").gameObject.SetActive(true);
					HideSleepingPaige();
				}
			}
			else if (passed)
			{
				__instance.FindSelectableEntity("3-X").gameObject.SetActive(false);
				HideSleepingPaige();
			}
			void HideSleepingPaige()
			{
				if (((InvisibleEntity)__instance.sleepingPaige).shaderRenderer != null)
				{
					((Entity)__instance.sleepingPaige).visible = false;
				}
				else
				{
					Plugin.Logger.LogWarning((object)"[RhythmDogtorLevelPatch] Sleeping Paige shader renderer is null");
				}
			}
		}
	}
	[HarmonyPatch(typeof(scnRhythmWeightlifter))]
	internal static class RhythmWeightlifterPatch
	{
		[HarmonyPatch("PlayLevelDialogue")]
		[HarmonyPrefix]
		private static void DoNotShowDialoguePatch(ref bool __runOriginal)
		{
			__runOriginal = false;
		}
	}
	[HarmonyPatch(typeof(WalkingSelectableCharacter))]
	internal static class RunningCharactersPatch
	{
		[HarmonyPatch("UpdateCharacter")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> WalkingSelectableCharacterOverLockedLevelsPatch(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null);
			foreach (CodeInstruction item in val.Instructions())
			{
				if (item.opcode == OpCodes.Call && (MethodInfo)item.operand == AccessTools.Method(typeof(RDUtils), "Locked", new Type[1] { typeof(Level) }, (Type[])null))
				{
					item.opcode = OpCodes.Ldc_I4_0;
				}
			}
			return val.InstructionEnumeration();
		}
	}
	[HarmonyPatch]
	internal static class SkipCutscenePatch
	{
		[HarmonyPatch(typeof(Persistence), "GetPlayedPassedLevelCutscene")]
		[HarmonyPatch(typeof(Persistence), "GetPlayedPostAct2Cutscene")]
		[HarmonyPatch(typeof(Persistence), "GetPlayedPostAct3Cutscene")]
		[HarmonyPatch(typeof(Persistence), "GetPlayedPostAct4Cutscene")]
		[HarmonyPatch(typeof(Persistence), "GetPlayedPreAct5Cutscene")]
		[HarmonyPatch(typeof(Persistence), "GetPlayedPreAct6Cutscene")]
		[HarmonyPatch(typeof(Persistence), "GetPlayedHaileyDuetIntroduction")]
		[HarmonyPatch(typeof(Persistence), "GetPlayedPreBitternessCutscene")]
		[HarmonyPatch(typeof(Persistence), "GetPlayedRooftopCutscene")]
		[HarmonyPrefix]
		private static void PersistenceDoNotPlayCutscenePatch(ref bool __result, ref bool __runOriginal)
		{
			__result = true;
			__runOriginal = false;
		}

		[HarmonyPatch(typeof(scnLevelSelect), "MovePaigeToVendingMachine")]
		[HarmonyPatch(typeof(scnLevelSelect), "PlayAct5Intro")]
		[HarmonyPatch(typeof(scnLevelSelect), "CheckForCutscene")]
		[HarmonyPatch(typeof(scnBase), "GoToCutscene")]
		[HarmonyPrefix]
		private static void LevelSelectDoNotPlayCutscenePatch(ref bool __runOriginal)
		{
			__runOriginal = false;
		}

		[HarmonyPatch(typeof(scnLevelSelect), "Start")]
		[HarmonyPostfix]
		private static void SkipStoryAndCutscenePatch(scnLevelSelect __instance)
		{
			scnLevelSelect.bitternessWarningPlayed = true;
			__instance.voidItemProgress = 3;
			__instance.selectableEntities.Find((SelectableEntity entity) => entity.id == "VoidItem1").normalEnabled = true;
			__instance.selectableEntities.Find((SelectableEntity entity) => entity.id == "VoidItem2").normalEnabled = true;
			__instance.selectableEntities.Find((SelectableEntity entity) => entity.id == "VoidItem3").normalEnabled = true;
		}
	}
	[HarmonyPatch(typeof(scnGame))]
	internal static class SkipTutorialPatch
	{
		[HarmonyPatch("Start")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> FixLesmisPatch(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			return new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Ldstr, (object)"Level_", (string)null)
			}).Advance(3).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[2]
			{
				new CodeInstruction(OpCodes.Ldstr, (object)", Assembly-CSharp"),
				new CodeInstruction(OpCodes.Call, (object)AccessTools.Method("System.String:Concat", new Type[2]
				{
					typeof(string),
					typeof(string)
				}, (Type[])null))
			})
				.InstructionEnumeration();
		}

		[HarmonyPatch("Start")]
		[HarmonyPrefix]
		private static void DoNotLoadTutorialPatch()
		{
			if (scnGame.internalIdentifier == "Montage")
			{
				Plugin.Logger.LogDebug((object)"Level is 7-X, not skipping tutorial");
				return;
			}
			Plugin.Logger.LogDebug((object)$"Level {scnGame.internalIdentifier}: Forcing attemptToLoadTutorial from {scnGame.attemptToLoadTutorial} to false");
			scnGame.attemptToLoadTutorial = false;
		}

		[HarmonyPatch(typeof(Persistence), "GetFirstTimePlaying")]
		[HarmonyPrefix]
		private static void DoNotLoadIntroPatch(ref bool __result, ref bool __runOriginal)
		{
			Plugin.Logger.LogDebug((object)"Forcing first_time to false");
			__runOriginal = false;
			__result = false;
		}
	}
	[HarmonyPatch(typeof(Persistence))]
	internal static class StateReplicationPatch
	{
		[HarmonyPatch("SetPaigeEnding")]
		[HarmonyPostfix]
		private static void ReplicatePaigeStaysPatch(bool paigeStays)
		{
			Plugin.Logger.LogInfo((object)string.Format("Replicating {0} to {1}", "paige_stays", paigeStays));
			Plugin.Client.Session.DataStorage[(Scope)3, "paige_stays"] = DataStorageElement.op_Implicit(paigeStays);
		}

		[HarmonyPatch("SetIanDesktopLogin")]
		[HarmonyPostfix]
		private static void ReplicateIansDesktopUnlockedPatch(bool ianDesktopLogin)
		{
			Plugin.Logger.LogInfo((object)string.Format("Replicating {0} to {1}", "ian_desktop_login", ianDesktopLogin));
			Plugin.Client.Session.DataStorage[(Scope)3, "ian_desktop_login"] = DataStorageElement.op_Implicit(ianDesktopLogin);
		}

		internal static async Task InitializeSync()
		{
			Task task = InitializeKey<bool>("paige_stays", Persistence.SetPaigeEnding, delegate
			{
				bool flag = Plugin.Random.Next() % 2 == 1;
				Plugin.Client.Session.DataStorage[(Scope)3, "paige_stays"] = DataStorageElement.op_Implicit(flag);
				return flag;
			});
			Task task2 = InitializeKey<bool>("ian_desktop_login", Persistence.SetIanDesktopLogin, delegate
			{
				Plugin.Client.Session.DataStorage[(Scope)3, "ian_desktop_login"] = DataStorageElement.op_Implicit(false);
				return false;
			});
			Plugin.Logger.LogInfo((object)"Waiting for initialization to complete...");
			await Task.WhenAll(task, task2);
			Plugin.Logger.LogInfo((object)"Initialization completed!");
			static async Task InitializeKey<T>(string key, Action<T> executeExisting, Func<T> valueDoesntExist) where T : notnull
			{
				JToken val = await Plugin.Client.Session.DataStorage[(Scope)3, key].GetAsync();
				if ((int)val.Type != 10 && (int)val.Type != 11)
				{
					T val2 = val.ToObject<T>();
					Plugin.Logger.LogInfo((object)$"Found {key} state: {val2}");
					executeExisting(val2);
				}
				else
				{
					T val3 = valueDoesntExist();
					Plugin.Logger.LogInfo((object)$"Setting initial {key} state to {val3}");
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class TrapManagerPatch
	{
		[HarmonyPatch(typeof(scnLevelSelect), "SelectCharacter")]
		[HarmonyPrefix]
		private static void ApplyApplicableTrapPreviewPatch(scnLevelSelect __instance)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			SelectableEntity selectedEntity = __instance.selectedEntity;
			SelectableCharacter val = (SelectableCharacter)(object)((selectedEntity is SelectableCharacter) ? selectedEntity : null);
			if (val != null)
			{
				Level level = val.levels[__instance.currentDifficulty];
				Plugin.Client.TrapManager.ApplyApplicableTraps(level);
			}
		}

		[HarmonyPatch(typeof(HeartMonitor), "Show")]
		[HarmonyPostfix]
		private static void ShowTrapNameOnPhonePatch(HeartMonitor __instance)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			IEnumerable<string> previewTrapNames = Plugin.Client.TrapManager.GetPreviewTrapNames();
			if (!previewTrapNames.Any())
			{
				return;
			}
			Plugin.Logger.LogDebug((object)"Instantiating guest credit for preview traps");
			__instance.isGuestCreditShown = true;
			foreach (string item in previewTrapNames)
			{
				Plugin.Logger.LogDebug((object)("Creating guest credit for " + item));
				GuestData val = new GuestData
				{
					type = null,
					link = null,
					linkType = "other-unused",
					name = item
				};
				GameObject val2 = Object.Instantiate<GameObject>(__instance.guestPrefab, (Transform)(object)__instance.creditsContainer);
				HeartMonitorGuest component = val2.GetComponent<HeartMonitorGuest>();
				component.Setup(val);
				__instance.creditStrings.Add(val.name);
				__instance.links.Add(val.link);
				__instance.creditsElements.Add(val2);
				component.nameText.text = item;
			}
		}

		[HarmonyPatch(typeof(scnLevelSelect), "GoToLevelSequence")]
		[HarmonyPostfix]
		private static void ApplyApplicableTrapsPatch(string levelToGo, scnLevelSelect __instance)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Logger.LogInfo((object)"Promoting preview traps to active traps");
			if ((levelToGo == "Lesmis" || levelToGo == "RhythmWeightlifter") ? true : false)
			{
				Plugin.Logger.LogInfo((object)"Going to Rhythm Dogtor/Rhythm Weightlifter (dog) - applying preview traps now");
				Level level = RDUtils.ParseEnum<Level>(levelToGo, (Level)0);
				Plugin.Client.TrapManager.ApplyApplicableTraps(level);
			}
			Plugin.Client.TrapManager.PromotePreviewTrapsToActiveTraps();
		}

		[HarmonyPatch(typeof(scnBase), "GoToLevel")]
		[HarmonyPostfix]
		private static void ApplyApplicableTrapsOnLevelChangePatch(string path)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			Level val = RDUtils.ParseEnum<Level>(path, (Level)0);
			if ((int)val == 50)
			{
				Plugin.Logger.LogWarning((object)$"Applying traps for {val} immediately");
				Plugin.Client.TrapManager.ApplyApplicableTraps(val);
				Plugin.Client.TrapManager.PromotePreviewTrapsToActiveTraps();
			}
		}

		[HarmonyPatch(typeof(scnGame), "Quit")]
		[HarmonyPostfix]
		private static void RestoreActiveTrapsOnAbandonPatch()
		{
			Plugin.Logger.LogInfo((object)"Clearing active traps (returning to queue)");
			Plugin.Client.TrapManager.ClearActiveTraps(returnToQueue: true);
		}
	}
	[HarmonyPatch(typeof(scnLevelSelect))]
	internal static class UnlockItemPatch
	{
		[HarmonyPatch("LoadLevelData")]
		[HarmonyPostfix]
		private static void UnlockEntitiesWithItemsPatch(scnLevelSelect __instance)
		{
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: 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)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_057f: Unknown result type (might be due to invalid IL or missing references)
			//IL_05aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0604: Unknown result type (might be due to invalid IL or missing references)
			//IL_0609: Unknown result type (might be due to invalid IL or missing references)
			//IL_0628: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d3: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Logger.LogInfo((object)"Checking for extra unlocks");
			Plugin.Logger.LogInfo((object)"Checking for regions to unlock");
			bool flag = false;
			foreach (ItemInfo item in Plugin.Client.Session.Items.AllItemsReceived)
			{
				Plugin.Logger.LogDebug((object)$"Processing item {item.ItemName} ({item.ItemId})");
				Level value2;
				if (Bindings.KeyItemIdToWard.TryGetValue(item.ItemId, out var value))
				{
					if (value == Region.Basement)
					{
						flag = true;
					}
					Plugin.Logger.LogInfo((object)$"Unlocking entrance {value}");
					scnLevelSelect.instance.UnlockEntrance(value);
				}
				else if (Bindings.ItemIdToLevel.TryGetValue(item.ItemId, out value2))
				{
					Plugin.Logger.LogInfo((object)string.Format("[{0}] Unlocking stage item {1} ({2}, {3})", "UnlockItemPatch", item.ItemName, item.ItemId, value2));
					Persistence.SetLevelRank(value2, Rank.op_Implicit(-1), false, false);
				}
			}
			if (!flag)
			{
				Plugin.Logger.LogInfo((object)"Locking basement");
				__instance.LockEntrance("GoToBasement");
			}
			Plugin.Logger.LogInfo((object)"Moving 1-CNY");
			__instance.FindSelectableEntity("1-CNY").gamePosition.x = -564f;
			if (Plugin.Client.Slot.endGoal == SlotData.EndGoal.HelpingHands)
			{
				Plugin.Logger.LogInfo((object)"Moving X-1 to the basement");
				SelectableEntity val = __instance.FindSelectableEntity("X-1");
				val.group = 3;
				int index = __instance.selectableEntities.FindIndex((SelectableEntity entity) => ((Object)entity.gameObject).name == "BasementComputer") + 1;
				val.gamePosition = new Vector2(2480f, 53f);
				__instance.selectableEntities.Remove(val);
				__instance.selectableEntities.Insert(index, val);
				if (Bindings.ActBoss.Values.All((Level[] levels) => levels.All(delegate(Level level)
				{
					//IL_0000: Unknown result type (might be due to invalid IL or missing references)
					//IL_0003: Unknown result type (might be due to invalid IL or missing references)
					//IL_0008: Unknown result type (might be due to invalid IL or missing references)
					Rank levelRank2 = Persistence.GetLevelRank(level, false, -1);
					return ((Rank)(ref levelRank2)).passed;
				})))
				{
					Plugin.Logger.LogInfo((object)"Unlocking Art Room and X-0 - Helping Hands");
					__instance.UnlockEntrance(__instance.FindSelectableEntity("GoToArtRoom"));
					Persistence.SetLevelRank((Level)55, Rank.op_Implicit(-1), false, false);
				}
			}
			GameObject.Find("/Scene/Corridor/VendingMachinePaige").gameObject.SetActive(false);
			foreach (SelectableEntity item2 in __instance.selectableEntities.Where((SelectableEntity entity) => entity.id.StartsWith("X-") && !Regex.IsMatch(entity.id, "/[0-9]/")))
			{
				item2.normalEnabled = true;
			}
			__instance.GetSelectableEntity("2-B1").normalEnabled = true;
			__instance.GetSelectableEntity("5-B1").normalEnabled = true;
			foreach (SelectableEntity item3 in __instance.selectableEntities.Where((SelectableEntity entity) => entity.id.StartsWith("3-")))
			{
				item3.normalEnabled = true;
				if (item3.id != "3-X")
				{
					item3.hardEnabled = true;
				}
			}
			foreach (SelectableEntity item4 in __instance.selectableEntities.Where((SelectableEntity entity) => entity.id.StartsWith("5-")))
			{
				item4.normalEnabled = true;
				bool flag2;
				switch (item4.id)
				{
				case "5-1":
				case "5-2":
				case "5-3":
					flag2 = true;
					break;
				default:
					flag2 = false;
					break;
				}
				if (flag2)
				{
					item4.hardEnabled = true;
				}
			}
			GameObject.Find("/Scene/Levels Container/Vertical Movement/5-2").SetActive(true);
			GameObject.Find("/Scene/Levels Container/Vertical Movement/5-3").SetActive(true);
			GameObject.Find("/Scene/Levels Container/Vertical Movement/5-X").SetActive(true);
			SelectableEntity selectableEntity = __instance.GetSelectableEntity("1-CNY");
			selectableEntity.normalEnabled = true;
			selectableEntity.gameObject.SetActive(true);
			SelectableEntity selectableEntity2 = __instance.GetSelectableEntity("1-BOO");
			selectableEntity2.normalEnabled = true;
			selectableEntity2.gameObject.SetActive(true);
			__instance.GetSelectableEntity("MD-1").normalEnabled = true;
			__instance.GetSelectableEntity("1-X").hardEnabled = true;
			SelectableEntity selectableEntity3 = __instance.GetSelectableEntity("2-X");
			selectableEntity3.normalEnabled = true;
			selectableEntity3.hardEnabled = true;
			SelectableEntity selectableEntity4 = __instance.GetSelectableEntity("5-1");
			selectableEntity4.normalEnabled = true;
			selectableEntity4.hardEnabled = true;
			SelectableEntity[] array = (SelectableEntity[])(object)new SelectableEntity[3]
			{
				__instance.FindSelectableEntity("6-2"),
				__instance.FindSelectableEntity("6-X"),
				__instance.FindSelectableEntity("7-1")
			};
			for (int num = 0; num < array.Length; num++)
			{
				array[num].normalEnabled = true;
			}
			GameObject.Find("/Scene/Levels Container/Vertical Movement/6-2").SetActive(true);
			__instance.GetSelectableEntity("VoidItem1").normalEnabled = true;
			__instance.GetSelectableEntity("VoidItem2").normalEnabled = true;
			__instance.GetSelectableEntity("VoidItem3").normalEnabled = true;
			((tk2dBaseSprite)__instance.abandonedWard.voidItems[0]).color = new Color(1f, 1f, 1f, 1f);
			((tk2dBaseSprite)__instance.abandonedWard.voidItems[1]).color = new Color(1f, 1f, 1f, 1f);
			((tk2dBaseSprite)__instance.abandonedWard.voidItems[2]).color = new Color(1f, 1f, 1f, 1f);
			__instance.GetSelectableEntity("7-X").normalEnabled = true;
			Persistence.SetLevelRank((Level)49, Rank.op_Implicit(-1), false, true);
			Rank levelRank = Persistence.GetLevelRank((Level)49, false, -1);
			if (((Rank)(ref levelRank)).passed)
			{
				__instance.GetSelectableEntity("7-X2").normalEnabled = true;
				Persistence.SetLevelRank((Level)50, Rank.op_Implicit(-1), false, true);
			}
			SelectableEntity selectableEntity5 = __instance.GetSelectableEntity("X-0");
			selectableEntity5.normalEnabled = true;
			selectableEntity5.gameObject.SetActive(true);
			SelectableEntity selectableEntity6 = __instance.GetSelectableEntity("X-1");
			selectableEntity6.normalEnabled = true;
			selectableEntity6.gameObject.SetActive(true);
			foreach (Act value3 in Enum.GetValues(typeof(Act)))
			{
				if (!HasUnlockedBossSong(value3))
				{
					continue;
				}
				Plugin.Logger.LogDebug((object)$"Unlocking act {value3}");
				Level[] array2 = Bindings.ActBoss[value3];
				for (int num = 0; num < array2.Length; num++)
				{
					int num2 = (int)array2[num];
					Persistence.SetLevelRank((Level)num2, Rank.op_Implicit(-1), false, false);
					if (num2 == 49)
					{
						__instance.UnlockEntrance(Region.RecordsRoom);
					}
				}
			}
		}

		[HarmonyPatch("LoadLevelData")]
		[HarmonyPatch("LevelJustPassedSequenceCo")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> DoNotUnlockEntrancesPatch(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null);
			foreach (CodeInstruction item in val.Instructions())
			{
				if (item.opcode == OpCodes.Call && (MethodInfo)item.operand == AccessTools.Method(typeof(scnLevelSelect), "UnlockEntrance", new Type[1] { typeof(SelectableEntity) }, (Type[])null))
				{
					item.opcode = OpCodes.Nop;
				}
			}
			return val.InstructionEnumeration();
		}

		[HarmonyPatch("UnlockNightShiftLevel")]
		[HarmonyPrefix]
		private static void DoNotUnlockNightShiftLevelPatch(ref bool __runOriginal)
		{
			__runOriginal = false;
		}

		[HarmonyPatch("LoadLevelData")]
		[HarmonyPatch("Awake")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> DoNotUnlockLevelsPatch(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null);
			List<CodeInstruction> list = val.Instructions();
			for (int i = 0; i < list.Count; i++)
			{
				CodeInstruction val2 = list[i];
				if (!(val2.opcode == OpCodes.Call))
				{
					continue;
				}
				int num;
				if ((MethodInfo)val2.operand == AccessTools.Method(typeof(Persistence), "SetLevelRank", new Type[4]
				{
					typeof(string),
					typeof(Rank),
					typeof(bool),
					typeof(bool)
				}, (Type[])null))
				{
					num = -9;
				}
				else
				{
					if (!((MethodInfo)val2.operand == AccessTools.Method(typeof(Persistence), "SetLevelRank", new Type[4]
					{
						typeof(Level),
						typeof(Rank),
						typeof(bool),
						typeof(bool)
					}, (Type[])null)))
					{
						continue;
					}
					num = -5;
				}
				for (int j = num; j <= 1; j++)
				{
					list[i + j].opcode = OpCodes.Nop;
				}
			}
			return val.InstructionEnumeration();
		}

		[HarmonyPatch("ShowRanksText")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> CustomPopulateSelectedVerticalDestinationsPatch(IEnumerable<CodeInstruction> instructions)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected O, but got Unknown
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Expected O, but got Unknown
			int match = 0;
			return new CodeMatcher(instructions, (ILGenerator)null).MatchForward(true, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Stfld, (object)AccessTools.Field(typeof(scnLevelSelect), "selectedVerticalDestinations"), (string)null)
			}).Repeat((Action<CodeMatcher>)delegate(CodeMatcher codeMatcher)
			{
				codeMatcher.Advance(1);
				switch (match)
				{
				case 0:
					codeMatcher.Insert((CodeInstruction[])(object)new CodeInstruction[1] { Transpilers.EmitDelegate<Action>((Action)delegate
					{
						//IL_008a: Unknown result type (might be due to invalid IL or missing references)
						//IL_0094: Expected O, but got Unknown
						//IL_00db: Unknown result type (might be due to invalid IL or missing references)
						//IL_00e5: Expected O, but got Unknown
						Plugin.Logger.LogInfo((object)"Setting Ian's laptop vertical destinations");
						scnLevelSelect.instance.selectedVerticalDestinations = new List<LevelSelectDestination>();
						if (Plugin.Client.Session.Items.AllItemsReceived.Any((ItemInfo item) => item.ItemId == 56))
						{
							Plugin.Logger.LogInfo((object)"Adding sleevePaint");
							scnLevelSelect.instance.selectedVerticalDestinations.Add(new LevelSelectDestination("sleevePaint", RDString.Get("levelSelect.SleevePaint"), RDString.Get("levelSelect.SleevePaint.details")));
						}
						Plugin.Logger.LogInfo((object)"Adding IanDesktop");
						scnLevelSelect.instance.selectedVerticalDestinations.Add(new LevelSelectDestination("IanDesktop", RDString.Get("levelSelect.IanDesktop"), RDString.Get($"levelSelect.IanDesktop.details.{((!Persistence.GetIanDesktopLogin()) ? 1 : 2)}")));
					}) });
					break;
				case 1:
					codeMatcher.Insert((CodeInstruction[])(object)new CodeInstruction[1] { Transpilers.EmitDelegate<Action>((Action)delegate
					{
						//IL_008a: Unknown result type (might be due to invalid IL or missing references)
						//IL_0094: Expected O, but got Unknown
						//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
						//IL_00d8: Expected O, but got Unknown
						Plugin.Logger.LogInfo((object)"Setting main elevator vertical destinations");
						scnLevelSelect.instance.selectedVerticalDestinations = new List<LevelSelectDestination>();
						if (Plugin.Client.Session.Items.AllItemsReceived.Any((ItemInfo item) => item.ItemId == Bindings.RegionToKeyID[Region.RecordsRoom]))
						{
							Plugin.Logger.LogInfo((object)"Adding ExitRecordsRoom");
							scnLevelSelect.instance.selectedVerticalDestinations.Add(new LevelSelectDestination("ExitRecordsRoom", RDString.Get("levelSelect.act6"), RDString.Get("levelSelect.GoToRecordsRoom.day")));
						}
						if (HasUnlockedBossSong(Act.Act7))
						{
							Plugin.Logger.LogInfo((object)"Adding ExitVoid");
							scnLevelSelect.instance.selectedVerticalDestinations.Add(new LevelSelectDestination("ExitVoid", RDString.Get("levelSelect.finale"), RDString.Get("levelSelect.GoToVoid.day")));
						}
					}) });
					break;
				default:
					Plugin.Logger.LogWarning((object)"Matched more than two vertical destinations - plugin may need update!!");
					break;
				}
				match++;
			}, (Action<string>)null).Start()
				.MatchForward(true, (CodeMatch[])(object)new CodeMatch[3]
				{
					new CodeMatch((OpCode?)OpCodes.Ldc_I4_S, (object)(sbyte)48, (string)null),
					new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(RDUtils), "Passed", (Type[])null, (Type[])null), (string)null),
					new CodeMatch((OpCode?)OpCodes.Brfalse, (object)null, (string)null)
				})
				.ThrowIfInvalid("Couldn't find Level.Bitterness.Passed()")
				.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1]
				{
					new CodeInstruction(OpCodes.Pop, (object)null)
				})
				.SetOpcodeAndAdvance(OpCodes.Br)
				.InstructionEnumeration();
		}

		[HarmonyPatch("UpdateCharacters")]
		[HarmonyPostfix]
		private static void UpdateCharactersPatch(scnLe

plugins/gg.archipelago.rhythmdoctor/Archipelago.MultiClient.Net.dll

Decompiled 14 hours 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>());