Decompiled source of Archipelago v0.2.4

APTromboneChampMod.dll

Decompiled 8 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Archipelago.MultiClient.Net;
using Archipelago.MultiClient.Net.Enums;
using Archipelago.MultiClient.Net.Helpers;
using Archipelago.MultiClient.Net.MessageLog.Messages;
using Archipelago.MultiClient.Net.Models;
using BaboonAPI.Hooks;
using BaboonAPI.Hooks.Initializer;
using BaboonAPI.Hooks.Tracks;
using BaboonAPI.Hooks.Tracks.Collections;
using BaboonAPI.Utility;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Microsoft.FSharp.Core;
using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("APTromboneChampMod")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.0.1.0")]
[assembly: AssemblyInformationalVersion("0.0.1+1caaa7df41fdce53c53ec6051452c809c1b056e9")]
[assembly: AssemblyProduct("AP Trombone")]
[assembly: AssemblyTitle("APTromboneChampMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace APTromboneChampMod
{
	public static class APHandler
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static Func<long, bool> <>9__12_0;

			public static Func<long, bool> <>9__12_1;

			public static Func<long, bool> <>9__15_0;

			public static Func<long, bool> <>9__21_0;

			public static Func<long, bool> <>9__23_1;

			public static Func<Hint, bool> <>9__23_2;

			public static Func<long, bool> <>9__24_0;

			public static Func<Hint, bool> <>9__24_1;

			public static Func<long, bool> <>9__25_0;

			public static Func<Hint, bool> <>9__25_1;

			public static Func<long, bool> <>9__26_0;

			public static ItemReceivedHandler <>9__28_0;

			public static CheckedLocationsUpdatedHandler <>9__28_1;

			public static Func<Hint, bool> <>9__28_7;

			public static Action<Hint[]> <>9__28_2;

			public static SocketClosedHandler <>9__28_3;

			public static MessageReceivedHandler <>9__28_4;

			public static Func<Hint, bool> <>9__28_10;

			public static Action<Task<Hint[]>> <>9__28_5;

			public static Func<TrackCollection, bool> <>9__38_1;

			public static Action<FSharpResult<Unit, Exception>> <>9__38_0;

			internal bool <IsTrackAvailable>b__12_0(long id)
			{
				return id == 1011;
			}

			internal bool <IsTrackAvailable>b__12_1(long id)
			{
				return id == 1004;
			}

			internal bool <GetRequiredRating>b__15_0(long id)
			{
				return id == 1001;
			}

			internal bool <GetTrackHints>b__21_0(long id)
			{
				return id == 1011;
			}

			internal bool <TryHintDifficulty>b__23_1(long id)
			{
				return id == 1011;
			}

			internal bool <TryHintDifficulty>b__23_2(Hint hint)
			{
				return hint.ReceivingPlayer == APSlot && hint.ItemId == 1011;
			}

			internal bool <TryHintRankReduction>b__24_0(long id)
			{
				return id == 1001;
			}

			internal bool <TryHintRankReduction>b__24_1(Hint hint)
			{
				return hint.ReceivingPlayer == APSlot && hint.ItemId == 1001;
			}

			internal bool <TryHintHotDog>b__25_0(long id)
			{
				return id == 1004;
			}

			internal bool <TryHintHotDog>b__25_1(Hint hint)
			{
				return hint.ReceivingPlayer == APSlot && hint.ItemId == 1004;
			}

			internal bool <HasGoaled>b__26_0(long id)
			{
				return id > 1000;
			}

			internal void <ConnectToAP>b__28_0(ReceivedItemsHelper helper)
			{
				List<long> list = new List<long>();
				while (helper.Any())
				{
					ItemInfo val = helper.PeekItem();
					if (val.ItemId == 1003)
					{
						if (DateTimeOffset.Now.ToUnixTimeMilliseconds() - LastFunFact > 1000)
						{
							LastFunFact = DateTimeOffset.Now.ToUnixTimeMilliseconds();
							if (UnseenFacts.Count == 0)
							{
								UnseenFacts.AddRange(FunFacts);
								ListExtensions.Shuffle<string>((IList<string>)UnseenFacts);
							}
							APSession.Say("FUN FACT: " + UnseenFacts[0]);
							UnseenFacts.RemoveAt(0);
						}
					}
					else if (val.ItemId != 1002)
					{
						long itemId = val.ItemId;
						if (((ulong)(itemId - 1005) <= 4uL) ? true : false)
						{
							if (DateTimeOffset.Now.ToUnixTimeMilliseconds() - ConnectTime > 10000)
							{
								long itemId2 = val.ItemId;
								long num = itemId2;
								long num2 = num - 1005;
								if ((ulong)num2 <= 4uL)
								{
									switch (num2)
									{
									case 0L:
										APTrapController.AddTrap(APTrapController.TrapType.FlipControls);
										break;
									case 1L:
										APTrapController.AddTrap(APTrapController.TrapType.SilenceTrack);
										break;
									case 2L:
										APTrapController.AddTrap(APTrapController.TrapType.SilenceTrombone);
										break;
									case 3L:
										APTrapController.AddTrap(APTrapController.TrapType.HideNotes);
										break;
									case 4L:
										APTrapController.AddTrap(APTrapController.TrapType.NoBreath);
										break;
									}
								}
							}
						}
						else
						{
							APTrapController.TrapType trap = APTrapController.TrapType.GetTrap(val.ItemId);
							if (trap != null)
							{
								if (DateTimeOffset.Now.ToUnixTimeMilliseconds() - ConnectTime > 10000)
								{
									APTrapController.AddTrap(trap);
								}
							}
							else
							{
								list.Add(val.ItemId);
							}
						}
					}
					helper.DequeueItem();
				}
				OnReceivedItems(list);
			}

			internal void <ConnectToAP>b__28_1(ReadOnlyCollection<long> locs)
			{
				lock (APSentLocations)
				{
					using IEnumerator<long> enumerator = locs.GetEnumerator();
					while (enumerator.MoveNext())
					{
						<>c__DisplayClass28_0 CS$<>8__locals0 = new <>c__DisplayClass28_0
						{
							loc = enumerator.Current
						};
						if (APSentLocations.Contains(CS$<>8__locals0.loc))
						{
							continue;
						}
						APSentLocations.Add(CS$<>8__locals0.loc);
						Hint[] aPReceivedHints = APReceivedHints;
						foreach (Hint val in aPReceivedHints)
						{
							if (val.FindingPlayer == APSlot && val.LocationId == CS$<>8__locals0.loc)
							{
								APReceivedHints = APReceivedHints.Where((Hint it) => it.FindingPlayer != APSlot || it.LocationId != CS$<>8__locals0.loc).ToArray();
								break;
							}
						}
					}
				}
				OnTrackAvailabilityChanged();
			}

			internal void <ConnectToAP>b__28_2(Hint[] hints)
			{
				APReceivedHints = hints.Where((Hint hint) => !hint.Found).ToArray();
				OnHintsChanged();
			}

			internal bool <ConnectToAP>b__28_7(Hint hint)
			{
				return !hint.Found;
			}

			internal void <ConnectToAP>b__28_3(string reason)
			{
				ArchipelagoPlugin.Logger.LogInfo((object)("Socket closed: " + reason));
				APSlot = -1;
				APSession = null;
				OnWorldSettingsChanged();
			}

			internal void <ConnectToAP>b__28_4(LogMessage message)
			{
				//IL_0162: Unknown result type (might be due to invalid IL or missing references)
				//IL_0167: Unknown result type (might be due to invalid IL or missing references)
				//IL_0174: Unknown result type (might be due to invalid IL or missing references)
				//IL_018f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0194: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
				//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
				//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
				//IL_01df: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
				//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
				//IL_0203: Unknown result type (might be due to invalid IL or missing references)
				//IL_021b: Expected O, but got Unknown
				HintItemSendLogMessage val = (HintItemSendLogMessage)(object)((message is HintItemSendLogMessage) ? message : null);
				if (val != null)
				{
					if (!((ItemSendLogMessage)val).IsRelatedToActivePlayer)
					{
						return;
					}
					<>c__DisplayClass28_1 CS$<>8__locals1 = new <>c__DisplayClass28_1
					{
						sender = ((ItemSendLogMessage)val).Sender.Slot,
						receiver = ((ItemSendLogMessage)val).Receiver.Slot
					};
					bool isFound = val.IsFound;
					long itemId = ((ItemSendLogMessage)val).Item.ItemId;
					CS$<>8__locals1.location = ((ItemSendLogMessage)val).Item.LocationId;
					bool flag = false;
					Hint[] aPReceivedHints = APReceivedHints;
					foreach (Hint val2 in aPReceivedHints)
					{
						if (val2.FindingPlayer != CS$<>8__locals1.sender || val2.ReceivingPlayer != CS$<>8__locals1.receiver || val2.LocationId != CS$<>8__locals1.location)
						{
							continue;
						}
						flag = true;
						if (isFound)
						{
							APReceivedHints = APReceivedHints.Where((Hint it) => it.FindingPlayer != CS$<>8__locals1.sender || it.ReceivingPlayer != CS$<>8__locals1.receiver || it.LocationId != CS$<>8__locals1.location).ToArray();
						}
						OnHintsChanged();
						break;
					}
					if (!isFound && !flag)
					{
						Hint[] aPReceivedHints2 = APReceivedHints;
						int num = 0;
						Hint[] array = (Hint[])(object)new Hint[1 + aPReceivedHints2.Length];
						ReadOnlySpan<Hint> val3 = default(ReadOnlySpan<Hint>);
						val3..ctor(aPReceivedHints2);
						val3.CopyTo(new Span<Hint>(array).Slice(num, val3.Length));
						num += val3.Length;
						array[num] = new Hint
						{
							Entrance = "",
							FindingPlayer = CS$<>8__locals1.sender,
							Found = false,
							ItemFlags = ((ItemSendLogMessage)val).Item.Flags,
							ItemId = itemId,
							LocationId = CS$<>8__locals1.location,
							ReceivingPlayer = CS$<>8__locals1.receiver,
							Status = (HintStatus)(((((ItemSendLogMessage)val).Item.Flags & 4) != 0) ? 20 : (((int)((ItemSendLogMessage)val).Item.Flags == 0) ? 10 : 30))
						};
						APReceivedHints = array;
						OnHintsChanged();
					}
					if (ArchipelagoPlugin.SendChatToLog)
					{
						ArchipelagoPlugin.Logger.LogInfo((object)((object)message).ToString());
					}
					return;
				}
				ItemSendLogMessage val4 = (ItemSendLogMessage)(object)((message is ItemSendLogMessage) ? message : null);
				if (val4 != null)
				{
					if (!val4.IsRelatedToActivePlayer)
					{
						return;
					}
					<>c__DisplayClass28_2 CS$<>8__locals0 = new <>c__DisplayClass28_2
					{
						sender = val4.Sender.Slot,
						receiver = val4.Receiver.Slot,
						location = val4.Item.LocationId
					};
					Hint[] aPReceivedHints3 = APReceivedHints;
					foreach (Hint val5 in aPReceivedHints3)
					{
						if (val5.FindingPlayer == CS$<>8__locals0.sender && val5.ReceivingPlayer == CS$<>8__locals0.receiver && val5.LocationId == CS$<>8__locals0.location)
						{
							APReceivedHints = APReceivedHints.Where((Hint it) => it.FindingPlayer != CS$<>8__locals0.sender || it.ReceivingPlayer != CS$<>8__locals0.receiver || it.LocationId != CS$<>8__locals0.location).ToArray();
							OnHintsChanged();
							break;
						}
					}
					if (ArchipelagoPlugin.SendChatToLog)
					{
						ArchipelagoPlugin.Logger.LogInfo((object)((object)message).ToString());
					}
				}
				else if (ArchipelagoPlugin.SendChatToLog)
				{
					ArchipelagoPlugin.Logger.LogInfo((object)((object)message).ToString());
				}
			}

			internal void <ConnectToAP>b__28_5(Task<Hint[]> task)
			{
				if (task.IsCompletedSuccessfully)
				{
					APReceivedHints = task.Result.Where((Hint hint) => !hint.Found).ToArray();
					OnHintsChanged();
				}
			}

			internal bool <ConnectToAP>b__28_10(Hint hint)
			{
				return !hint.Found;
			}

			internal bool <OnTrackAvailabilityChanged>b__38_1(TrackCollection coll)
			{
				return coll._unique_id == "all";
			}

			internal void <OnTrackAvailabilityChanged>b__38_0(FSharpResult<Unit, Exception> _)
			{
			}
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass28_0
		{
			public long loc;

			public Func<Hint, bool> <>9__6;

			internal bool <ConnectToAP>b__6(Hint it)
			{
				return it.FindingPlayer != APSlot || it.LocationId != loc;
			}
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass28_1
		{
			public int sender;

			public int receiver;

			public long location;

			public Func<Hint, bool> <>9__8;

			internal bool <ConnectToAP>b__8(Hint it)
			{
				return it.FindingPlayer != sender || it.ReceivingPlayer != receiver || it.LocationId != location;
			}
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass28_2
		{
			public int sender;

			public int receiver;

			public long location;

			public Func<Hint, bool> <>9__9;

			internal bool <ConnectToAP>b__9(Hint it)
			{
				return it.FindingPlayer != sender || it.ReceivingPlayer != receiver || it.LocationId != location;
			}
		}

		public static APSettings WorldSettings = new APSettings();

		public static Track[] FilteredTracks = Array.Empty<Track>();

		public static Track[] AvailableTracks = Array.Empty<Track>();

		public static Track? GoalTrack = null;

		public static ArchipelagoSession APSession = null;

		public static int APTeam = -1;

		public static int APSlot = -1;

		public static readonly Version APVersion = new Version(0, 6, 7);

		public static readonly List<long> APFoundItems = new List<long>();

		public static readonly List<long> APSentLocations = new List<long>();

		public static Hint[] APReceivedHints = Array.Empty<Hint>();

		private static readonly string[] RatingToString = new string[4] { "C", "B", "A", "S" };

		public static long ConnectTime;

		private static long LastFunFact;

		private static readonly string[] FunFacts = new string[26]
		{
			"It takes one-thousand workers a full year to produce a single trombone.", "The trombone is related to the trumpet (they are cousins).", "The trombone is not related to the French Horn (they are just friends).", "Some claim that Mozart's last words before dying were \"At least I got to use a trombone.\"", "A student's trombone generally costs between $100 and $300, but a professional trombone can cost over two billion dollars.", "To this day, scientists don't really know how a trombone makes sound.", "A professional trombone player is known as a \"tromboner\".", "Thirty-four countries have outlawed the use of the trombone. In six countries, playing trombone is punishable by death.", "Trombones contain \"spit valves\" that allow you to blow gobs of your nasty spit all over the floor.", "Without trombones, there could never have been \"ska\" music. Draw your own conclusions from this factoid.",
			"The average baboon can live to be over 300 years old.", "There are more baboons on Earth than humans.", "Prehistoric trombones were forty feet long and could weigh over six hundred pounds.", "Trombones do not float in water, so do not accidentally drop your trombone into the river last week.", "Cows love the sound of a trombone (because they are smart).", "Playing trombone in your apartment is a great way to make friends with your neighbors.", "Despite its name, the trombone does not have any bones.", "There are between 2 and 4 spiders living inside the average trombone.", "The first trombone was invented in 20,000,000 B.C.", "If you placed all of the trombones on Earth end-to-end, they would wrap around the solar system 4 times.",
			"There are more trombones on Earth than there are humans.", "The highest note playable on trombones is so high-pitched that only certain species of bats can hear it.", "The world record for \"Most Trombones Owned\" is held by Mike Brass of Omaha, Nebraska. He owns two trombones.", "It takes over three thousand tons of brass to produce a single trombone.", "In real life, there are over nine songs that feature a trombone.", "In England, \"trombone\" is spelled \"troumboune\"."
		};

		private static List<string> UnseenFacts = new List<string>();

		private static List<long> ItemQueue = new List<long>();

		private static object ItemQueueProcessLock = new object();

		private static object TrackUpdateLock = new object();

		public static Track? FindTrack(string name)
		{
			Track value = FilteredTracks.FirstOrDefault((Track track) => track.Name == name);
			if (value.Name != name)
			{
				return null;
			}
			return value;
		}

		public static bool IsTrackAvailable(Track track)
		{
			if (APSession == null || APSlot == -1)
			{
				return false;
			}
			if (!FilteredTracks.Contains(track))
			{
				return false;
			}
			if (WorldSettings.TrackGating && !APFoundItems.Contains(track.ID))
			{
				return false;
			}
			if (track.Difficulty > WorldSettings.MinDiff)
			{
				if (WorldSettings.DifficultyGating == APSettings.DiffGateType.ON && !APFoundItems.Contains(1010L + (long)track.Difficulty))
				{
					return false;
				}
				if (WorldSettings.DifficultyGating == APSettings.DiffGateType.PROG)
				{
					int num = track.Difficulty - WorldSettings.MinDiff;
					if (num > APFoundItems.Count((long id) => id == 1011))
					{
						return false;
					}
				}
			}
			if (track.Name == WorldSettings.GoalTrack)
			{
				int num2 = APFoundItems.Count((long id) => id == 1004);
				if (num2 < WorldSettings.HotDogs)
				{
					return false;
				}
			}
			return true;
		}

		public static string GetRatingString(int rating)
		{
			if (rating < 0)
			{
				rating = 0;
			}
			if (rating >= RatingToString.Length)
			{
				rating = RatingToString.Length - 1;
			}
			return RatingToString[rating];
		}

		public static int GetRequiredRating()
		{
			int initialRating = WorldSettings.InitialRating;
			return initialRating - APFoundItems.Count((long id) => id == 1001);
		}

		public static void SendTrack(Track track, bool beaten)
		{
			if (APSession != null && APSlot != -1 && IsTrackAvailable(track))
			{
				long[] array = ((!beaten) ? new long[1] { track.ID } : new long[2]
				{
					track.ID,
					track.ID + 1000
				});
				APSession.Locations.CompleteLocationChecksAsync(array);
				if (beaten && HasGoaled(track.ID))
				{
					APSession.SetGoalAchieved();
				}
			}
		}

		public static bool CanHint()
		{
			if (APSession == null || APSlot == -1)
			{
				return false;
			}
			return APSession.RoomState.HintPoints >= APSession.RoomState.HintCost;
		}

		public static string FormatItemHint(Hint hint)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			if (hint == null)
			{
				return "-";
			}
			if (hint.ReceivingPlayer == APSlot)
			{
				return APSession.Items.GetItemName(hint.ItemId, (string)null);
			}
			PlayerInfo playerInfo = APSession.Players.GetPlayerInfo(hint.ReceivingPlayer);
			string game = playerInfo.Game;
			string itemName = APSession.Items.GetItemName(hint.ItemId, game);
			return $"{playerInfo.Alias}'s {itemName} ({hint.Status})";
		}

		public static string FormatLocationString(Hint hint)
		{
			if (hint == null)
			{
				return "-";
			}
			if (hint.FindingPlayer == APSlot)
			{
				return APSession.Locations.GetLocationNameFromId(hint.LocationId, (string)null);
			}
			PlayerInfo playerInfo = APSession.Players.GetPlayerInfo(hint.FindingPlayer);
			string game = playerInfo.Game;
			string locationNameFromId = APSession.Locations.GetLocationNameFromId(hint.LocationId, game);
			if (hint.Entrance != "")
			{
				return playerInfo.Alias + "'s " + locationNameFromId + " (" + hint.Entrance + ")";
			}
			return playerInfo.Alias + "'s " + locationNameFromId;
		}

		public static string FormatFullHint(Hint hint)
		{
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			if (hint == null)
			{
				return "-";
			}
			PlayerInfo playerInfo = APSession.Players.GetPlayerInfo(hint.FindingPlayer);
			PlayerInfo playerInfo2 = APSession.Players.GetPlayerInfo(hint.ReceivingPlayer);
			string locationNameFromId = APSession.Locations.GetLocationNameFromId(hint.LocationId, playerInfo.Game);
			string itemName = APSession.Items.GetItemName(hint.ItemId, playerInfo2.Game);
			bool flag = hint.ReceivingPlayer == hint.FindingPlayer;
			string text = "";
			if (hint.Entrance != "")
			{
				text = " (" + hint.Entrance + ")";
			}
			if (flag)
			{
				return itemName + " is at " + locationNameFromId;
			}
			string text2 = playerInfo2.Alias + "'s ";
			if (playerInfo2.Slot == APSlot)
			{
				text2 = "";
			}
			string text3 = playerInfo.Alias + "'s ";
			if (playerInfo.Slot == APSlot)
			{
				text3 = "";
			}
			string text4 = $" ({hint.Status})";
			if (playerInfo2.Slot == APSlot)
			{
				text4 = "";
			}
			return text2 + itemName + " is at " + text3 + locationNameFromId + text + text4;
		}

		public static TrackHints GetTrackHints(Track track)
		{
			TrackHints result = new TrackHints();
			if (WorldSettings.TrackGating && !APFoundItems.Contains(track.ID))
			{
				Hint trackUnlock = null;
				Hint[] aPReceivedHints = APReceivedHints;
				foreach (Hint val in aPReceivedHints)
				{
					if (val.ReceivingPlayer == APSlot && val.ItemId == track.ID)
					{
						trackUnlock = val;
						break;
					}
				}
				result.TrackUnlock = trackUnlock;
			}
			if (track.Difficulty > WorldSettings.MinDiff)
			{
				if (WorldSettings.DifficultyGating == APSettings.DiffGateType.ON && !APFoundItems.Contains(1010L + (long)track.Difficulty))
				{
					Hint val2 = null;
					Hint[] aPReceivedHints2 = APReceivedHints;
					foreach (Hint val3 in aPReceivedHints2)
					{
						if (val3.ReceivingPlayer == APSlot && val3.ItemId == 1010L + (long)track.Difficulty)
						{
							val2 = val3;
							break;
						}
					}
					result.DifficultyUnlocks = (Hint[])(object)new Hint[1] { val2 };
				}
				if (WorldSettings.DifficultyGating == APSettings.DiffGateType.PROG)
				{
					int num = track.Difficulty - WorldSettings.MinDiff;
					int num2 = APFoundItems.Count((long id) => id == 1011);
					if (num2 < num)
					{
						int num3 = WorldSettings.MaxDiff - WorldSettings.MinDiff;
						num3 -= num2;
						Hint[] array = (Hint[])(object)new Hint[num3];
						int num4 = 0;
						Hint[] aPReceivedHints3 = APReceivedHints;
						foreach (Hint val4 in aPReceivedHints3)
						{
							if (val4.ReceivingPlayer == APSlot && val4.ItemId == 1011)
							{
								array[num4++] = val4;
							}
						}
						result.DifficultyUnlocks = array;
					}
				}
			}
			if (!APSentLocations.Contains(track.ID))
			{
				Hint playReward = null;
				Hint[] aPReceivedHints4 = APReceivedHints;
				foreach (Hint val5 in aPReceivedHints4)
				{
					if (val5.FindingPlayer == APSlot && val5.LocationId == track.ID)
					{
						playReward = val5;
						break;
					}
				}
				result.PlayReward = playReward;
			}
			if (!APSentLocations.Contains(track.ID + 1000))
			{
				Hint beatReward = null;
				Hint[] aPReceivedHints5 = APReceivedHints;
				foreach (Hint val6 in aPReceivedHints5)
				{
					if (val6.FindingPlayer == APSlot && val6.LocationId == track.ID + 1000)
					{
						beatReward = val6;
						break;
					}
				}
				result.BeatReward = beatReward;
			}
			return result;
		}

		public static void TryHintTrack(Track track)
		{
			if (WorldSettings.TrackGating && CanHint() && !APReceivedHints.Any((Hint hint) => hint.ReceivingPlayer == APSlot && hint.ItemId == track.ID))
			{
				string itemName = APSession.Items.GetItemName(track.ID, (string)null);
				if (itemName != null)
				{
					APSession.Say("!hint " + itemName);
				}
			}
		}

		public static void TryHintDifficulty(int diff)
		{
			if (WorldSettings.DifficultyGating == APSettings.DiffGateType.OFF || !CanHint() || (WorldSettings.DifficultyGating == APSettings.DiffGateType.ON && APReceivedHints.Any((Hint hint) => hint.ReceivingPlayer == APSlot && hint.ItemId == 1010L + (long)diff)))
			{
				return;
			}
			if (WorldSettings.DifficultyGating == APSettings.DiffGateType.PROG)
			{
				diff = 1;
				int num = WorldSettings.MaxDiff - WorldSettings.MinDiff - APFoundItems.Count((long id) => id == 1011);
				if (num == 0)
				{
					return;
				}
				int num2 = APReceivedHints.Count((Hint hint) => hint.ReceivingPlayer == APSlot && hint.ItemId == 1011);
				if (num2 >= num)
				{
					return;
				}
			}
			string itemName = APSession.Items.GetItemName(1010L + (long)diff, (string)null);
			if (itemName != null)
			{
				APSession.Say("!hint " + itemName);
			}
		}

		public static void TryHintRankReduction()
		{
			if (WorldSettings.GoalRating == WorldSettings.InitialRating || !CanHint())
			{
				return;
			}
			int num = WorldSettings.InitialRating - WorldSettings.GoalRating - APFoundItems.Count((long id) => id == 1001);
			if (num == 0)
			{
				return;
			}
			int num2 = APReceivedHints.Count((Hint hint) => hint.ReceivingPlayer == APSlot && hint.ItemId == 1001);
			if (num2 < num)
			{
				string itemName = APSession.Items.GetItemName(1001L, (string)null);
				if (itemName != null)
				{
					APSession.Say("!hint " + itemName);
				}
			}
		}

		public static void TryHintHotDog()
		{
			if (WorldSettings.HotDogs == 0 || !CanHint())
			{
				return;
			}
			int num = WorldSettings.HotDogs + WorldSettings.ExtraHotDogs;
			int num2 = APFoundItems.Count((long id) => id == 1004);
			if (num2 >= WorldSettings.HotDogs)
			{
				return;
			}
			num -= num2;
			num2 = APReceivedHints.Count((Hint hint) => hint.ReceivingPlayer == APSlot && hint.ItemId == 1004);
			if (num2 < num)
			{
				string itemName = APSession.Items.GetItemName(1004L, (string)null);
				if (itemName != null)
				{
					APSession.Say("!hint " + itemName);
				}
			}
		}

		public static bool HasGoaled(long lastTrackBeaten = -1L)
		{
			if (WorldSettings.GoalTracks == 0)
			{
				if (GoalTrack.HasValue)
				{
					if (lastTrackBeaten == GoalTrack.Value.ID || APSentLocations.Contains(GoalTrack.Value.ID + 1000))
					{
						return true;
					}
				}
				else
				{
					ArchipelagoPlugin.Logger.LogWarning((object)"Goal tracks is 0 and no goal track is set!");
				}
				return false;
			}
			int num = APSentLocations.Count((long id) => id > 1000);
			if (lastTrackBeaten != -1 && !APSentLocations.Contains(lastTrackBeaten + 1000))
			{
				num++;
			}
			return num >= WorldSettings.GoalTracks;
		}

		public static void ConnectToAP(string host, int port, string slot, string pass)
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Expected O, but got Unknown
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Expected O, but got Unknown
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Expected O, but got Unknown
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Expected O, but got Unknown
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Expected O, but got Unknown
			//IL_06f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			ArchipelagoPlugin.Logger.LogInfo((object)$"Connecting to {host}:{port}");
			ArchipelagoSession aPSession = APSession;
			if (aPSession != null)
			{
				aPSession.Socket.DisconnectAsync();
			}
			APSession = null;
			APSlot = -1;
			APReceivedHints = Array.Empty<Hint>();
			APFoundItems.Clear();
			APSentLocations.Clear();
			OnWorldSettingsChanged();
			try
			{
				APSession = ArchipelagoSessionFactory.CreateSession(host, port);
				IReceivedItemsHelper items = APSession.Items;
				object obj = <>c.<>9__28_0;
				if (obj == null)
				{
					ItemReceivedHandler val = delegate(ReceivedItemsHelper helper)
					{
						List<long> list = new List<long>();
						while (helper.Any())
						{
							ItemInfo val15 = helper.PeekItem();
							if (val15.ItemId == 1003)
							{
								if (DateTimeOffset.Now.ToUnixTimeMilliseconds() - LastFunFact > 1000)
								{
									LastFunFact = DateTimeOffset.Now.ToUnixTimeMilliseconds();
									if (UnseenFacts.Count == 0)
									{
										UnseenFacts.AddRange(FunFacts);
										ListExtensions.Shuffle<string>((IList<string>)UnseenFacts);
									}
									APSession.Say("FUN FACT: " + UnseenFacts[0]);
									UnseenFacts.RemoveAt(0);
								}
							}
							else if (val15.ItemId != 1002)
							{
								long itemId2 = val15.ItemId;
								if (((ulong)(itemId2 - 1005) <= 4uL) ? true : false)
								{
									if (DateTimeOffset.Now.ToUnixTimeMilliseconds() - ConnectTime > 10000)
									{
										long itemId3 = val15.ItemId;
										long num2 = itemId3;
										long num3 = num2 - 1005;
										if ((ulong)num3 <= 4uL)
										{
											switch (num3)
											{
											case 0L:
												APTrapController.AddTrap(APTrapController.TrapType.FlipControls);
												break;
											case 1L:
												APTrapController.AddTrap(APTrapController.TrapType.SilenceTrack);
												break;
											case 2L:
												APTrapController.AddTrap(APTrapController.TrapType.SilenceTrombone);
												break;
											case 3L:
												APTrapController.AddTrap(APTrapController.TrapType.HideNotes);
												break;
											case 4L:
												APTrapController.AddTrap(APTrapController.TrapType.NoBreath);
												break;
											}
										}
									}
								}
								else
								{
									APTrapController.TrapType trap = APTrapController.TrapType.GetTrap(val15.ItemId);
									if (trap != null)
									{
										if (DateTimeOffset.Now.ToUnixTimeMilliseconds() - ConnectTime > 10000)
										{
											APTrapController.AddTrap(trap);
										}
									}
									else
									{
										list.Add(val15.ItemId);
									}
								}
							}
							helper.DequeueItem();
						}
						OnReceivedItems(list);
					};
					<>c.<>9__28_0 = val;
					obj = (object)val;
				}
				items.ItemReceived += (ItemReceivedHandler)obj;
				ILocationCheckHelper locations = APSession.Locations;
				object obj2 = <>c.<>9__28_1;
				if (obj2 == null)
				{
					CheckedLocationsUpdatedHandler val2 = delegate(ReadOnlyCollection<long> locs)
					{
						lock (APSentLocations)
						{
							foreach (long loc in locs)
							{
								if (!APSentLocations.Contains(loc))
								{
									APSentLocations.Add(loc);
									Hint[] aPReceivedHints4 = APReceivedHints;
									foreach (Hint val14 in aPReceivedHints4)
									{
										if (val14.FindingPlayer == APSlot && val14.LocationId == loc)
										{
											APReceivedHints = APReceivedHints.Where((Hint it) => it.FindingPlayer != APSlot || it.LocationId != loc).ToArray();
											break;
										}
									}
								}
							}
						}
						OnTrackAvailabilityChanged();
					};
					<>c.<>9__28_1 = val2;
					obj2 = (object)val2;
				}
				locations.CheckedLocationsUpdated += (CheckedLocationsUpdatedHandler)obj2;
				APSession.Hints.TrackHints((Action<Hint[]>)delegate(Hint[] hints)
				{
					APReceivedHints = hints.Where((Hint hint) => !hint.Found).ToArray();
					OnHintsChanged();
				}, true, (int?)null, (int?)null);
				IArchipelagoSocketHelper socket = APSession.Socket;
				object obj3 = <>c.<>9__28_3;
				if (obj3 == null)
				{
					SocketClosedHandler val3 = delegate(string reason)
					{
						ArchipelagoPlugin.Logger.LogInfo((object)("Socket closed: " + reason));
						APSlot = -1;
						APSession = null;
						OnWorldSettingsChanged();
					};
					<>c.<>9__28_3 = val3;
					obj3 = (object)val3;
				}
				socket.SocketClosed += (SocketClosedHandler)obj3;
				IMessageLogHelper messageLog = APSession.MessageLog;
				object obj4 = <>c.<>9__28_4;
				if (obj4 == null)
				{
					MessageReceivedHandler val4 = delegate(LogMessage message)
					{
						//IL_0162: Unknown result type (might be due to invalid IL or missing references)
						//IL_0167: Unknown result type (might be due to invalid IL or missing references)
						//IL_0174: Unknown result type (might be due to invalid IL or missing references)
						//IL_018f: Unknown result type (might be due to invalid IL or missing references)
						//IL_0194: Unknown result type (might be due to invalid IL or missing references)
						//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
						//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
						//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
						//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
						//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
						//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
						//IL_01df: Unknown result type (might be due to invalid IL or missing references)
						//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
						//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
						//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
						//IL_0203: Unknown result type (might be due to invalid IL or missing references)
						//IL_021b: Expected O, but got Unknown
						HintItemSendLogMessage val9 = (HintItemSendLogMessage)(object)((message is HintItemSendLogMessage) ? message : null);
						if (val9 != null)
						{
							if (((ItemSendLogMessage)val9).IsRelatedToActivePlayer)
							{
								int sender2 = ((ItemSendLogMessage)val9).Sender.Slot;
								int receiver2 = ((ItemSendLogMessage)val9).Receiver.Slot;
								bool isFound = val9.IsFound;
								long itemId = ((ItemSendLogMessage)val9).Item.ItemId;
								long location2 = ((ItemSendLogMessage)val9).Item.LocationId;
								bool flag = false;
								Hint[] aPReceivedHints = APReceivedHints;
								foreach (Hint val10 in aPReceivedHints)
								{
									if (val10.FindingPlayer == sender2 && val10.ReceivingPlayer == receiver2 && val10.LocationId == location2)
									{
										flag = true;
										if (isFound)
										{
											APReceivedHints = APReceivedHints.Where((Hint it) => it.FindingPlayer != sender2 || it.ReceivingPlayer != receiver2 || it.LocationId != location2).ToArray();
										}
										OnHintsChanged();
										break;
									}
								}
								if (!isFound && !flag)
								{
									Hint[] aPReceivedHints2 = APReceivedHints;
									int num = 0;
									Hint[] array = (Hint[])(object)new Hint[1 + aPReceivedHints2.Length];
									ReadOnlySpan<Hint> val11 = default(ReadOnlySpan<Hint>);
									val11..ctor(aPReceivedHints2);
									val11.CopyTo(new Span<Hint>(array).Slice(num, val11.Length));
									num += val11.Length;
									array[num] = new Hint
									{
										Entrance = "",
										FindingPlayer = sender2,
										Found = false,
										ItemFlags = ((ItemSendLogMessage)val9).Item.Flags,
										ItemId = itemId,
										LocationId = location2,
										ReceivingPlayer = receiver2,
										Status = (HintStatus)(((((ItemSendLogMessage)val9).Item.Flags & 4) != 0) ? 20 : (((int)((ItemSendLogMessage)val9).Item.Flags == 0) ? 10 : 30))
									};
									APReceivedHints = array;
									OnHintsChanged();
								}
								if (ArchipelagoPlugin.SendChatToLog)
								{
									ArchipelagoPlugin.Logger.LogInfo((object)((object)message).ToString());
								}
							}
						}
						else
						{
							ItemSendLogMessage val12 = (ItemSendLogMessage)(object)((message is ItemSendLogMessage) ? message : null);
							if (val12 != null)
							{
								if (val12.IsRelatedToActivePlayer)
								{
									int sender = val12.Sender.Slot;
									int receiver = val12.Receiver.Slot;
									long location = val12.Item.LocationId;
									Hint[] aPReceivedHints3 = APReceivedHints;
									foreach (Hint val13 in aPReceivedHints3)
									{
										if (val13.FindingPlayer == sender && val13.ReceivingPlayer == receiver && val13.LocationId == location)
										{
											APReceivedHints = APReceivedHints.Where((Hint it) => it.FindingPlayer != sender || it.ReceivingPlayer != receiver || it.LocationId != location).ToArray();
											OnHintsChanged();
											break;
										}
									}
									if (ArchipelagoPlugin.SendChatToLog)
									{
										ArchipelagoPlugin.Logger.LogInfo((object)((object)message).ToString());
									}
								}
							}
							else if (ArchipelagoPlugin.SendChatToLog)
							{
								ArchipelagoPlugin.Logger.LogInfo((object)((object)message).ToString());
							}
						}
					};
					<>c.<>9__28_4 = val4;
					obj4 = (object)val4;
				}
				messageLog.OnMessageReceived += (MessageReceivedHandler)obj4;
				ConnectTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
				LoginResult val5 = APSession.TryConnectAndLogin("Trombone Champ", slot, (ItemsHandlingFlags)7, APVersion, Array.Empty<string>(), (string)null, pass, true);
				ConnectTime = (LastFunFact = DateTimeOffset.Now.ToUnixTimeMilliseconds());
				if (!val5.Successful)
				{
					ArchipelagoPlugin.Logger.LogWarning((object)$"Failed to connect to {host}:{port}");
					LoginFailure val6 = (LoginFailure)val5;
					string[] errors = val6.Errors;
					foreach (string text in errors)
					{
						ArchipelagoPlugin.Logger.LogWarning((object)("    " + text));
					}
					ConnectionRefusedError[] errorCodes = val6.ErrorCodes;
					foreach (ConnectionRefusedError val7 in errorCodes)
					{
						ArchipelagoPlugin.Logger.LogWarning((object)$"    {val7}");
					}
					return;
				}
				LoginSuccessful val8 = (LoginSuccessful)val5;
				APTeam = val8.Team;
				APSlot = val8.Slot;
				ArchipelagoPlugin.Logger.LogInfo((object)$"Successfully connected to {host}:{port} - Team: {APTeam}, Slot: {APSlot}");
				WorldSettings.GoalTracks = int.Parse(val8.SlotData["goal"].ToString());
				WorldSettings.GoalTrack = val8.SlotData["goal_track"].ToString();
				if (WorldSettings.GoalTracks == 0)
				{
					ArchipelagoPlugin.Logger.LogInfo((object)("Goal track: " + WorldSettings.GoalTrack));
				}
				else
				{
					ArchipelagoPlugin.Logger.LogInfo((object)$"Goal track count: {WorldSettings.GoalTracks}");
				}
				WorldSettings.GoalRating = int.Parse(val8.SlotData["rating"].ToString());
				WorldSettings.InitialRating = int.Parse(val8.SlotData["rating_start"].ToString());
				ArchipelagoPlugin.Logger.LogInfo((object)$"Goal rating: {WorldSettings.GoalRating} | Initial Rating: {WorldSettings.InitialRating}");
				WorldSettings.EasyTrackGap = int.Parse(val8.SlotData["easy_track"].ToString());
				ArchipelagoPlugin.Logger.LogInfo((object)$"Easy Track Gap: {WorldSettings.EasyTrackGap}");
				WorldSettings.HotDogs = int.Parse(val8.SlotData["hot_dogs"].ToString());
				WorldSettings.ExtraHotDogs = int.Parse(val8.SlotData["extra_hot_dogs"].ToString());
				ArchipelagoPlugin.Logger.LogInfo((object)$"HotDogs: {WorldSettings.HotDogs} + {WorldSettings.ExtraHotDogs}");
				WorldSettings.TrackGating = int.Parse(val8.SlotData["track_gating"].ToString()) > 0;
				ArchipelagoPlugin.Logger.LogInfo((object)$"Track gating: {WorldSettings.TrackGating}");
				WorldSettings.DifficultyGating = (APSettings.DiffGateType)int.Parse(val8.SlotData["difficulty_gating"].ToString());
				ArchipelagoPlugin.Logger.LogInfo((object)$"Difficulty gating: {WorldSettings.DifficultyGating}");
				WorldSettings.MinDiff = int.Parse(val8.SlotData["min_diff"].ToString());
				WorldSettings.MaxDiff = int.Parse(val8.SlotData["max_diff"].ToString());
				ArchipelagoPlugin.Logger.LogInfo((object)$"Difficulty range: {WorldSettings.MinDiff} - {WorldSettings.MaxDiff}");
				WorldSettings.Unsafe = int.Parse(val8.SlotData["unsafe"].ToString()) != 0;
				ArchipelagoPlugin.Logger.LogInfo((object)$"Unsafe: {WorldSettings.Unsafe}");
				WorldSettings.Celeste = int.Parse(val8.SlotData["celeste"].ToString()) != 0;
				ArchipelagoPlugin.Logger.LogInfo((object)$"Celeste DLC: {WorldSettings.Celeste}");
				WorldSettings.PizzaTower = int.Parse(val8.SlotData["pizza_tower"].ToString()) != 0;
				ArchipelagoPlugin.Logger.LogInfo((object)$"Pizza Tower DLC: {WorldSettings.PizzaTower}");
				WorldSettings.UndertaleDeltarune = int.Parse(val8.SlotData["undertale_deltarune"].ToString()) != 0;
				ArchipelagoPlugin.Logger.LogInfo((object)$"Undertale/Deltarune DLC: {WorldSettings.UndertaleDeltarune}");
				WorldSettings.RemovedTracks = ((JToken)(JArray)val8.SlotData["removed_tracks"]).ToObject<string[]>();
				ArchipelagoPlugin.Logger.LogInfo((object)("Removed tracks: " + string.Join(", ", WorldSettings.RemovedTracks)));
				OnWorldSettingsChanged();
				APSession.Hints.GetHintsAsync((int?)null, (int?)null).ContinueWith(delegate(Task<Hint[]> task)
				{
					if (task.IsCompletedSuccessfully)
					{
						APReceivedHints = task.Result.Where((Hint hint) => !hint.Found).ToArray();
						OnHintsChanged();
					}
				});
			}
			catch (Exception ex)
			{
				ArchipelagoPlugin.Logger.LogError((object)("Unusual error: " + ex.Message));
				ArchipelagoPlugin.Logger.LogError((object)ex.StackTrace);
			}
		}

		public static void OnReceivedItems(List<long> items)
		{
			lock (ItemQueue)
			{
				ItemQueue.AddRange(items);
			}
			if (!Monitor.TryEnter(ItemQueueProcessLock, 10))
			{
				return;
			}
			bool flag = false;
			bool flag2 = false;
			bool flag3 = false;
			while (true)
			{
				lock (APFoundItems)
				{
					lock (ItemQueue)
					{
						APFoundItems.AddRange(ItemQueue);
						ItemQueue.Clear();
					}
				}
				foreach (long item in items)
				{
					bool flag4 = item > 0 && item < 1000;
					bool flag5 = flag4;
					if (!flag5)
					{
						bool flag6 = ((item == 1001 || item == 1004) ? true : false);
						flag5 = flag6;
					}
					if (flag5 || item > 1010)
					{
						flag = true;
					}
					if ((item == 1001 || item == 1004 || item == 1011) ? true : false)
					{
						flag3 = true;
					}
					if (item > 1011)
					{
						flag2 = true;
					}
				}
				if (flag3)
				{
				}
				lock (ItemQueue)
				{
					if (ItemQueue.Count == 0)
					{
						Monitor.Exit(ItemQueueProcessLock);
						break;
					}
				}
			}
			if (flag)
			{
				OnTrackAvailabilityChanged();
			}
			else if (flag2)
			{
				OnHintsChanged();
			}
		}

		public static void OnHintsChanged()
		{
			LevelSelectController val = Object.FindObjectOfType<LevelSelectController>();
			if (Object.op_Implicit((Object)(object)val))
			{
				val.populateSongNames(false);
			}
		}

		public static void OnWorldSettingsChanged()
		{
			if (APSession == null || APSlot == -1)
			{
				WorldSettings.MinDiff = 2;
				WorldSettings.MaxDiff = 1;
			}
			FilteredTracks = APTracks.GetTrackList(WorldSettings).ToArray();
			GoalTrack = APTracks.GetGoalTrack(WorldSettings, FilteredTracks);
			OnTrackAvailabilityChanged();
			if (ArchipelagoPlugin.Instance.curGUI == 0 && APSlot != -1)
			{
				ArchipelagoPlugin.Instance.curGUI = 1;
			}
			if (ArchipelagoPlugin.Instance.curGUI == 1 && APSlot == -1)
			{
				ArchipelagoPlugin.Instance.curGUI = 0;
			}
		}

		public static void OnTrackAvailabilityChanged()
		{
			lock (TrackUpdateLock)
			{
				lock (APSentLocations)
				{
					lock (APFoundItems)
					{
						AvailableTracks = FilteredTracks.Where(IsTrackAvailable).ToArray();
						if (GlobalVariables.chosen_collection_index < 0 || GlobalVariables.chosen_collection_index >= GlobalVariables.all_track_collections.Count)
						{
							return;
						}
						TrackCollection val = GlobalVariables.all_track_collections[GlobalVariables.chosen_collection_index];
						if (TrackCollectionListener.COLLECTIONS.TryGetValue(val._unique_id, out var value))
						{
							List<TromboneTrack> list = value.BuildTrackList().ToList();
							TrackCollection allCollection = GlobalVariables.all_track_collections.First((TrackCollection coll) => coll._unique_id == "all");
							val.all_tracks = list.Select((TromboneTrack track) => allCollection.all_tracks.First((SingleTrackData data) => data.trackname_short == track.trackname_short)).ToList();
							val._trackcount = list.Count;
							LevelSelectController val2 = Object.FindObjectOfType<LevelSelectController>();
							if (Object.op_Implicit((Object)(object)val2))
							{
								string trackname_short = val2.alltrackslist[val2.songindex].trackname_short;
								val2.selectNewCollection(true);
								GlobalVariables.levelselect_index = 0;
								val2.sortTracks(GlobalVariables.sortmode, false);
								int num = -1;
								for (int i = 0; i < val2.alltrackslist.Count; i++)
								{
									if (val2.alltrackslist[i].trackname_short == trackname_short)
									{
										num = i;
										break;
									}
								}
								if (num != -1)
								{
									val2.songindex = num;
									GlobalVariables.levelselect_index = num;
									val2.populateSongNames(false);
								}
								else
								{
									val2.populateSongNames(true);
								}
							}
						}
						IEnumerator<YieldInstruction> enumerator = TrackReloader.ReloadAll((Action<Progress>)null).ForEach((Action<FSharpResult<Unit, Exception>>)delegate
						{
						});
						while (enumerator.MoveNext())
						{
						}
					}
				}
			}
		}
	}
	public struct APSettings
	{
		public enum DiffGateType
		{
			OFF,
			ON,
			PROG
		}

		public int GoalTracks;

		public string GoalTrack;

		public int GoalRating;

		public int InitialRating;

		public int EasyTrackGap;

		public int HotDogs;

		public int ExtraHotDogs;

		public bool TrackGating;

		public DiffGateType DifficultyGating;

		public int MinDiff;

		public int MaxDiff;

		public bool Unsafe;

		public bool Celeste;

		public bool PizzaTower;

		public bool UndertaleDeltarune;

		public string[] RemovedTracks;

		public APSettings()
		{
			GoalTracks = 1;
			GoalTrack = null;
			GoalRating = 3;
			InitialRating = 3;
			EasyTrackGap = 0;
			HotDogs = 0;
			ExtraHotDogs = 0;
			TrackGating = false;
			DifficultyGating = DiffGateType.OFF;
			MinDiff = 1;
			MaxDiff = 10;
			Unsafe = true;
			Celeste = true;
			PizzaTower = true;
			UndertaleDeltarune = true;
			RemovedTracks = Array.Empty<string>();
		}
	}
	public readonly struct Track : IEquatable<Track>
	{
		public readonly long ID;

		public readonly string Name;

		public readonly int Difficulty;

		public readonly bool Unsafe;

		public readonly string DLC;

		public Track(long id, string name, int difficulty, bool allowUnsafe = false, string dlc = "Base")
		{
			ID = id;
			Name = name;
			Difficulty = difficulty;
			Unsafe = allowUnsafe;
			DLC = dlc;
		}

		public bool Equals(Track other)
		{
			return ID == other.ID;
		}

		public override bool Equals(object obj)
		{
			return obj is Track other && Equals(other);
		}

		public override int GetHashCode()
		{
			return (int)ID;
		}
	}
	public class APTracks
	{
		[CompilerGenerated]
		private sealed class <GetTrackList>d__1 : IEnumerable<Track>, IEnumerable, IEnumerator<Track>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private Track <>2__current;

			private int <>l__initialThreadId;

			private APSettings settings;

			public APSettings <>3__settings;

			private Track[] <>s__1;

			private int <>s__2;

			private Track <track>5__3;

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

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

			[DebuggerHidden]
			public <GetTrackList>d__1(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>s__1 = null;
				<track>5__3 = default(Track);
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
					goto IL_016c;
				}
				<>1__state = -1;
				<>s__1 = TRACKS;
				<>s__2 = 0;
				goto IL_0187;
				IL_016c:
				<track>5__3 = default(Track);
				<>s__2++;
				goto IL_0187;
				IL_0187:
				if (<>s__2 < <>s__1.Length)
				{
					<track>5__3 = <>s__1[<>s__2];
					if (<track>5__3.Difficulty >= settings.MinDiff && <track>5__3.Difficulty <= settings.MaxDiff && (settings.Unsafe || !<track>5__3.Unsafe) && (<track>5__3.DLC == "Base" || (<track>5__3.DLC == "Celeste" && settings.Celeste) || (<track>5__3.DLC == "Pizza Tower" && settings.PizzaTower) || (<track>5__3.DLC == "UTDR" && settings.UndertaleDeltarune)) && !settings.RemovedTracks.Contains(<track>5__3.Name))
					{
						<>2__current = <track>5__3;
						<>1__state = 1;
						return true;
					}
					goto IL_016c;
				}
				<>s__1 = null;
				return false;
			}

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

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

			[DebuggerHidden]
			IEnumerator<Track> IEnumerable<Track>.GetEnumerator()
			{
				<GetTrackList>d__1 <GetTrackList>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<GetTrackList>d__ = this;
				}
				else
				{
					<GetTrackList>d__ = new <GetTrackList>d__1(0);
				}
				<GetTrackList>d__.settings = <>3__settings;
				return <GetTrackList>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<Track>)this).GetEnumerator();
			}
		}

		public static readonly Track[] TRACKS = new Track[119]
		{
			new Track(1L, "Are U Ready", 7),
			new Track(2L, "Arirang", 3),
			new Track(3L, "Auld Lang Syne", 7),
			new Track(4L, "Baboons!", 6),
			new Track(5L, "Bald Mountain", 7),
			new Track(6L, "Ball Game", 3),
			new Track(7L, "Barber of Seville", 7),
			new Track(8L, "Beethoven's Fifth", 7),
			new Track(9L, "Blue Danube", 5),
			new Track(10L, "Bumblebee", 9),
			new Track(11L, "Carol of the Bells", 7),
			new Track(12L, "Chop Waltz", 6),
			new Track(13L, "Commander Tokyo", 9),
			new Track(14L, "Danny Boy", 3),
			new Track(15L, "Danse Macabre", 8),
			new Track(16L, "Eine (CHAMP MIX)", 10),
			new Track(17L, "Eine Kleine", 5),
			new Track(18L, "Entertainer", 7),
			new Track(19L, "Four Seasons (Summer)", 9),
			new Track(20L, "Funiculi Funicula", 6),
			new Track(21L, "Gladiators", 8),
			new Track(22L, "God Save the King", 2),
			new Track(23L, "Gymnopédie", 5),
			new Track(24L, "Habanera", 5),
			new Track(25L, "Happy Birthday", 5),
			new Track(26L, "Hava Nagila", 5),
			new Track(27L, "Hello! Ma Baby", 6),
			new Track(28L, "Hino Do Brasil", 7),
			new Track(29L, "Hungarian Dance", 7, allowUnsafe: true),
			new Track(30L, "Hungarian Rhapsody", 9),
			new Track(31L, "Jarabe Tapatío", 9),
			new Track(32L, "Jasmine Flower", 3),
			new Track(33L, "Jingle Bells", 5),
			new Track(34L, "Korobeiniki", 7),
			new Track(35L, "Long-Tail Limbo", 5),
			new Track(36L, "Mars", 4),
			new Track(37L, "Marseillaise", 7),
			new Track(38L, "Martian Killbots", 3),
			new Track(39L, "Merry Gentlemen", 7),
			new Track(40L, "Mountain King", 8),
			new Track(41L, "O Canada", 3),
			new Track(42L, "O Christmas Tree", 4),
			new Track(43L, "Ode to Joy", 2),
			new Track(44L, "Oh Chanukah!", 8),
			new Track(45L, "Old Gray Mare", 5),
			new Track(46L, "Old MacDonald", 7),
			new Track(47L, "Rhapsody in Blue", 8),
			new Track(48L, "Rising Sun Blues", 4),
			new Track(49L, "Rosamunde", 6),
			new Track(50L, "Round the Mountain", 7),
			new Track(51L, "Sailor's Hornpipe", 10),
			new Track(52L, "Sakura", 2),
			new Track(53L, "Silent Night", 4),
			new Track(54L, "SkaBIRD", 6),
			new Track(55L, "Skeleton Rag", 6),
			new Track(56L, "Skip To My Lou", 5),
			new Track(57L, "St James Trombonery", 4),
			new Track(58L, "Stars & Stripes", 8, allowUnsafe: true),
			new Track(59L, "Star-Spangled", 5),
			new Track(60L, "Sugar Plum Fairy", 5),
			new Track(61L, "T. Champ Medley", 7),
			new Track(62L, "Taps", 3),
			new Track(63L, "The Can-Can", 8),
			new Track(64L, "The Ritz", 7),
			new Track(65L, "The Riverside", 5),
			new Track(66L, "The Saints", 5),
			new Track(67L, "Toccata & Fugue", 8),
			new Track(68L, "Trombone Fuerte", 9),
			new Track(69L, "Trombone Skyze", 4),
			new Track(70L, "Trombone Skyze (Nasty)", 8),
			new Track(71L, "W. Post March", 7, allowUnsafe: true),
			new Track(72L, "Warm-Up", 1),
			new Track(73L, "William Tell", 8),
			new Track(74L, "Zarathustra", 1),
			new Track(201L, "Confronting Myself", 7, allowUnsafe: true, "Celeste"),
			new Track(202L, "First Steps", 7, allowUnsafe: true, "Celeste"),
			new Track(203L, "Heart of the Mountain", 7, allowUnsafe: true, "Celeste"),
			new Track(204L, "Madeline and Theo", 6, allowUnsafe: true, "Celeste"),
			new Track(205L, "Reach for the Summit", 6, allowUnsafe: true, "Celeste"),
			new Track(206L, "Reflection", 6, allowUnsafe: true, "Celeste"),
			new Track(207L, "Resurrections", 6, allowUnsafe: true, "Celeste"),
			new Track(208L, "Scattered and Lost", 8, allowUnsafe: true, "Celeste"),
			new Track(209L, "Spirit of Hospitality", 6, allowUnsafe: true, "Celeste"),
			new Track(210L, "Starjump", 8, allowUnsafe: true, "Celeste"),
			new Track(251L, "Bye Bye There!", 7, allowUnsafe: true, "Pizza Tower"),
			new Track(252L, "Cold Spaghetti", 8, allowUnsafe: true, "Pizza Tower"),
			new Track(253L, "Death I Deservioli", 8, allowUnsafe: true, "Pizza Tower"),
			new Track(254L, "ET Wahwahs", 8, allowUnsafe: true, "Pizza Tower"),
			new Track(255L, "Funiculi Holiday", 7, allowUnsafe: true, "Pizza Tower"),
			new Track(256L, "Good Eatin'", 8, allowUnsafe: true, "Pizza Tower"),
			new Track(257L, "It's Pizza Time!", 8, allowUnsafe: true, "Pizza Tower"),
			new Track(258L, "Kid's Menu", 9, allowUnsafe: true, "Pizza Tower"),
			new Track(259L, "Oregano Mirage", 9, allowUnsafe: true, "Pizza Tower"),
			new Track(260L, "Pizza Deluxe", 7, allowUnsafe: true, "Pizza Tower"),
			new Track(261L, "Pumpin' Hot Stuff", 7, allowUnsafe: true, "Pizza Tower"),
			new Track(262L, "Put On A Show!", 7, allowUnsafe: true, "Pizza Tower"),
			new Track(263L, "Unexpectancy", 9, allowUnsafe: true, "Pizza Tower"),
			new Track(264L, "Yeehaw", 8, allowUnsafe: true, "Pizza Tower"),
			new Track(301L, "ASGORE", 8, allowUnsafe: true, "UTDR"),
			new Track(302L, "BIG SHOT", 7, allowUnsafe: true, "UTDR"),
			new Track(303L, "Black Knife", 7, allowUnsafe: true, "UTDR"),
			new Track(304L, "Bonetrousle", 8, allowUnsafe: true, "UTDR"),
			new Track(305L, "CYBER'S WORLD?", 7, allowUnsafe: true, "UTDR"),
			new Track(306L, "Dark Sanctuary", 7, allowUnsafe: true, "UTDR"),
			new Track(307L, "Dummy!", 9, allowUnsafe: true, "UTDR"),
			new Track(308L, "GUARDIAN", 8, allowUnsafe: true, "UTDR"),
			new Track(309L, "Hopes and Dreams", 8, allowUnsafe: true, "UTDR"),
			new Track(310L, "It's TV Time!", 8, allowUnsafe: true, "UTDR"),
			new Track(311L, "Killer Queen", 8, allowUnsafe: true, "UTDR"),
			new Track(312L, "Megalovania", 9, allowUnsafe: true, "UTDR"),
			new Track(313L, "Metal Crusher", 9, allowUnsafe: true, "UTDR"),
			new Track(314L, "Pandora Palace", 7, allowUnsafe: true, "UTDR"),
			new Track(315L, "Rude Buster", 9, allowUnsafe: true, "UTDR"),
			new Track(316L, "Scarlet Forest", 6, allowUnsafe: true, "UTDR"),
			new Track(317L, "SWORD", 7, allowUnsafe: true, "UTDR"),
			new Track(318L, "Third Sanctuary", 9, allowUnsafe: true, "UTDR"),
			new Track(319L, "True Hero", 8, allowUnsafe: true, "UTDR"),
			new Track(320L, "TV World", 7, allowUnsafe: true, "UTDR"),
			new Track(321L, "World Revolving", 8, allowUnsafe: true, "UTDR")
		};

		[IteratorStateMachine(typeof(<GetTrackList>d__1))]
		public static IEnumerable<Track> GetTrackList(APSettings settings)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <GetTrackList>d__1(-2)
			{
				<>3__settings = settings
			};
		}

		public static Track? GetGoalTrack(APSettings settings, Track[] tracks)
		{
			if (settings.GoalTracks > 0)
			{
				return null;
			}
			Track value = tracks.FirstOrDefault((Track track) => track.Name == settings.GoalTrack);
			if (value.Name != settings.GoalTrack)
			{
				ArchipelagoPlugin.Logger.LogWarning((object)"Goal track count is 0 and goal track failed to resolve to a track!");
				return null;
			}
			return value;
		}
	}
	public class APTrapController
	{
		public class TrapType
		{
			public class TrapFlipControls : TrapType
			{
				public TrapFlipControls()
					: base(1201L)
				{
				}

				public override void StartTrap(GameController controller)
				{
					int num = GlobalVariables.mousecontrolmode;
					switch (num)
					{
					case 0:
						num = 1;
						break;
					case 1:
						num = 0;
						break;
					case 2:
						num = 3;
						break;
					case 3:
						num = 2;
						break;
					}
					controller.gameplay_settings.mouse_controldirection = num;
				}

				public override void EndTrap(GameController controller)
				{
					controller.gameplay_settings.mouse_controldirection = GlobalVariables.mousecontrolmode;
				}

				public override float TrapDuration()
				{
					return 8f;
				}
			}

			public class TrapSilenceTrack : TrapType
			{
				private float OriginalVolume;

				public TrapSilenceTrack()
					: base(1202L)
				{
				}

				public override void StartTrap(GameController controller)
				{
					OriginalVolume = controller.musictrack.volume;
				}

				public override void ContinueTrap(GameController controller)
				{
					float num = (float)controller.musictrack.timeSamples / (float)controller.musictrack.clip.frequency;
					float num2 = (num - TrapStartTime) / (TrapEndTime - TrapStartTime);
					float num3 = num2 * 2f;
					if (num3 > 1f)
					{
						num3 = 2f - num3;
					}
					float volume = 0f;
					if (num3 < 0.25f)
					{
						volume = 1f - num3 / 0.25f;
					}
					controller.musictrack.volume = volume;
				}

				public override void EndTrap(GameController controller)
				{
					controller.musictrack.volume = OriginalVolume;
				}
			}

			public class TrapSilenceTrombone : TrapType
			{
				public TrapSilenceTrombone()
					: base(1203L)
				{
				}

				public override void ContinueTrap(GameController controller)
				{
					controller.currentnotesound.volume = 0f;
				}

				public override void EndTrap(GameController controller)
				{
					if (controller.noteactive)
					{
						controller.currentnotesound.volume = 1f;
					}
				}
			}

			public class TrapHideNotes : TrapType
			{
				public TrapHideNotes()
					: base(1204L)
				{
				}

				public override void ContinueTrap(GameController controller)
				{
					//IL_0016: Unknown result type (might be due to invalid IL or missing references)
					controller.noteholderr.anchoredPosition3D = new Vector3(0f, 100f, 0f);
				}
			}

			public class TrapNoBreath : TrapType
			{
				public TrapNoBreath()
					: base(1205L)
				{
				}

				public override void StartTrap(GameController controller)
				{
					//IL_0040: Unknown result type (might be due to invalid IL or missing references)
					controller.breathcounter = 1f;
					if (!controller.outofbreath)
					{
						controller.sfxrefs.outofbreath.Play();
						controller.breathglow.anchoredPosition3D = new Vector3(-380f, 0f, 0f);
						controller.outofbreath = true;
						controller.noteplaying = false;
						controller.setPuppetShake(false);
						controller.setPuppetBreath(true);
						controller.stopNote();
					}
				}

				public override void EndTrap(GameController controller)
				{
					controller.breathcounter = 0f;
					controller.outofbreath = false;
					controller.sfxrefs.outofbreath.Stop();
					controller.setPuppetBreath(false);
				}

				public override float TrapDuration()
				{
					return 2f;
				}
			}

			public class TrapWarbleTrombone : TrapType
			{
				private const float NUM_CYCLES = 5f;

				public TrapWarbleTrombone()
					: base(1206L)
				{
				}

				public override void ContinueTrap(GameController controller)
				{
					float num = (float)controller.musictrack.timeSamples / (float)controller.musictrack.clip.frequency;
					float num2 = (num - TrapStartTime) / (TrapEndTime - TrapStartTime);
					AudioSource currentnotesound = controller.currentnotesound;
					currentnotesound.pitch *= Mathf.Sin(num2 * 5f * (float)Math.PI * 2f) * 0.2f + 0.9f;
				}
			}

			public class TrapWarpSpeed : TrapType
			{
				private static readonly AnimationCurve SpeedCurve = new AnimationCurve((Keyframe[])(object)new Keyframe[8]
				{
					new Keyframe(0f, 1f),
					new Keyframe(0.1f, 1f),
					new Keyframe(0.3f, 0.9f),
					new Keyframe(0.35f, 0.9f),
					new Keyframe(0.65f, 1.25f),
					new Keyframe(0.7f, 1.25f),
					new Keyframe(0.9f, 1f),
					new Keyframe(1f, 1f)
				});

				public TrapWarpSpeed()
					: base(1207L)
				{
				}

				public override void ContinueTrap(GameController controller)
				{
					float num = (float)controller.musictrack.timeSamples / (float)controller.musictrack.clip.frequency;
					float num2 = (num - TrapStartTime) / (TrapEndTime - TrapStartTime);
					float num3 = SpeedCurve.Evaluate(num2);
					if (GlobalVariables.turbomode)
					{
						num3 *= 2f;
					}
					controller.musictrack.pitch = num3;
					controller.smooth_scrolling_move_mult = num3;
				}

				public override void EndTrap(GameController controller)
				{
					float num = ((!GlobalVariables.turbomode) ? 1 : 2);
					controller.musictrack.pitch = num;
					controller.smooth_scrolling_move_mult = num;
				}

				public override float TrapDuration()
				{
					return 10f;
				}
			}

			public static readonly TrapType FlipControls = new TrapFlipControls();

			public static readonly TrapType SilenceTrack = new TrapSilenceTrack();

			public static readonly TrapType SilenceTrombone = new TrapSilenceTrombone();

			public static readonly TrapType HideNotes = new TrapHideNotes();

			public static readonly TrapType NoBreath = new TrapNoBreath();

			public static readonly TrapType[] Traps = new TrapType[7]
			{
				FlipControls,
				SilenceTrack,
				SilenceTrombone,
				HideNotes,
				NoBreath,
				new TrapWarbleTrombone(),
				new TrapWarpSpeed()
			};

			public readonly long ID;

			public TrapType(long ID)
			{
				this.ID = ID;
				base..ctor();
			}

			public virtual float TrapDuration()
			{
				return 5f;
			}

			public virtual void StartTrap(GameController controller)
			{
			}

			public virtual void ContinueTrap(GameController controller)
			{
			}

			public virtual void EndTrap(GameController controller)
			{
			}

			public static TrapType GetTrap(long ID)
			{
				return Traps.FirstOrDefault((TrapType trap) => trap.ID == ID);
			}
		}

		public static List<TrapType> TrapQueue = new List<TrapType>();

		private static TrapType CurTrap;

		private static float TrapStartTime;

		private static float TrapEndTime;

		public static void AddTrap(TrapType t)
		{
			TrapQueue.Add(t);
		}

		public static void ResetState()
		{
			TrapEndTime = 0f;
			if (CurTrap != null)
			{
				ArchipelagoPlugin.Logger.LogWarning((object)"Trap controller was reset with active traps!");
				TrapQueue.Insert(0, CurTrap);
			}
			CurTrap = null;
		}

		public static void ControllerUpdate(GameController controller)
		{
			if (!Object.op_Implicit((Object)(object)controller.musictrack) || !Object.op_Implicit((Object)(object)controller.musictrack.clip))
			{
				return;
			}
			float num = (float)controller.musictrack.timeSamples / (float)controller.musictrack.clip.frequency;
			if (CurTrap != null && num >= TrapEndTime)
			{
				CurTrap.EndTrap(controller);
				CurTrap = null;
				return;
			}
			if (CurTrap == null)
			{
				if (TrapQueue.Count == 0)
				{
					return;
				}
				float num2 = TrapEndTime + 3f;
				if (num < num2)
				{
					return;
				}
				float num3 = num + TrapQueue[0].TrapDuration();
				if (num3 > controller.levelendtime - 3f)
				{
					return;
				}
				CurTrap = TrapQueue[0];
				CurTrap.StartTrap(controller);
				TrapQueue.RemoveAt(0);
				TrapStartTime = num;
				TrapEndTime = num3;
			}
			CurTrap?.ContinueTrap(controller);
		}
	}
	[BepInPlugin("APTromboneChampMod", "AP Trombone", "0.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class ArchipelagoPlugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(PointSceneController), "Awake")]
		private class ScoreScreenChecker
		{
			private static void Postfix()
			{
				if (APHandler.APSlot == -1)
				{
					Logger.LogInfo((object)"AP not connected, not sending.");
					return;
				}
				float gameplay_scoreperc = GlobalVariables.gameplay_scoreperc;
				int num = (int)(gameplay_scoreperc / 0.2f) - 2;
				if (num > 3)
				{
					num = (GlobalVariables.gameplay_perfect ? 5 : ((!((double)gameplay_scoreperc >= 1.35)) ? 3 : 4));
				}
				bool flag = num >= APHandler.GetRequiredRating();
				Track track2 = APHandler.AvailableTracks.FirstOrDefault((Track track) => track.Name == GlobalVariables.chosen_track_data.trackname_short);
				if (track2.Name == GlobalVariables.chosen_track_data.trackname_short)
				{
					Logger.LogInfo((object)("Track end screen: " + GlobalVariables.chosen_track_data.trackname_short));
					Logger.LogInfo((object)$" Score: {gameplay_scoreperc:P2}");
					Logger.LogInfo((object)("Rating: " + (new string[8] { "F", "D", "C", "B", "A", "S", "Wall (S)", "Perfect (S)" })[num + 2]));
					Logger.LogInfo((object)$"Beaten: {flag}");
					if (!APHandler.IsTrackAvailable(track2))
					{
						Logger.LogInfo((object)"Submission blocked, track is not available.");
					}
					else
					{
						APHandler.SendTrack(track2, flag);
					}
				}
				else
				{
					Logger.LogInfo((object)"Track was not in the list of AP tracks.");
				}
			}
		}

		[HarmonyPatch(typeof(LevelSelectController), "clickPlay")]
		private class PreventPlayingTrack
		{
			private static bool Prefix(LevelSelectController __instance)
			{
				if (APHandler.APSlot == -1)
				{
					return true;
				}
				if (APHandler.HasGoaled(-1L))
				{
					return true;
				}
				Track? track = APHandler.FindTrack(__instance.alltrackslist[__instance.songindex].trackname_short);
				if (!track.HasValue)
				{
					return false;
				}
				if (!APHandler.IsTrackAvailable(track.Value))
				{
					return false;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(LevelSelectController), "populateSongNames")]
		private class ChangeSongDescriptionAndDisablePlay
		{
			private static void Postfix(LevelSelectController __instance)
			{
				TrackCollection val = GlobalVariables.all_track_collections[GlobalVariables.chosen_collection_index];
				if (APHandler.APSlot != -1 && APHandler.HasGoaled(-1L))
				{
					__instance.songdesctext.text = "Goaled!\nMaybe play for fun?";
					((Behaviour)__instance.playbtn).enabled = true;
					((Component)__instance.playbtn).gameObject.SetActive(true);
					return;
				}
				if (TrackCollectionListener.COLLECTIONS.TryGetValue(val._unique_id, out var value))
				{
					APCollection aPCollection = (APCollection)value;
					if (aPCollection.HasNoTracks())
					{
						__instance.songdesctext.text = ((APHandler.APSlot == -1) ? "Not connected to AP!\nPress F1 to open connection manager." : aPCollection.GetNoTrackString());
						((Behaviour)__instance.playbtn).enabled = false;
						((Component)__instance.playbtn).gameObject.SetActive(false);
						return;
					}
				}
				bool flag = false;
				if (APHandler.APSlot == -1)
				{
					flag = true;
				}
				else
				{
					Track? track = APHandler.FindTrack(__instance.alltrackslist[__instance.songindex].trackname_short);
					if (track.HasValue)
					{
						TrackHints trackHints = APHandler.GetTrackHints(track.Value);
						bool flag2 = APHandler.APSentLocations.Contains(track.Value.ID);
						bool flag3 = APHandler.APSentLocations.Contains(track.Value.ID + 1000);
						StringBuilder stringBuilder = new StringBuilder();
						if (APHandler.GoalTrack.HasValue && APHandler.GoalTrack.Value.ID == track.Value.ID)
						{
							stringBuilder.Append("This is the goal track, beat it to win!\n");
						}
						if (APHandler.IsTrackAvailable(track.Value))
						{
							if (flag2 && flag3)
							{
								stringBuilder.Append("Track already beaten.");
							}
							else
							{
								flag = true;
								stringBuilder.Append("Can play this track!");
								stringBuilder.Append("\nRequired rating: " + APHandler.GetRatingString(APHandler.GetRequiredRating()));
								if (!flag2)
								{
									stringBuilder.Append("\nPlay reward: " + APHandler.FormatItemHint(trackHints.PlayReward));
								}
								if (!flag3)
								{
									stringBuilder.Append("\nBeat reward: " + APHandler.FormatItemHint(trackHints.BeatReward));
								}
							}
						}
						else
						{
							stringBuilder.Append("Track locked.");
							if (APHandler.GoalTrack.HasValue && APHandler.GoalTrack.Value.ID == track.Value.ID)
							{
								int hotDogs = APHandler.WorldSettings.HotDogs;
								if (hotDogs > 0)
								{
									int num = APHandler.APFoundItems.Count((long id) => id == 1004);
									if (num < hotDogs)
									{
										stringBuilder.Append($"\nHot dogs: {num}/{hotDogs}");
									}
								}
							}
							if (APHandler.WorldSettings.TrackGating && !APHandler.APFoundItems.Contains(track.Value.ID))
							{
								stringBuilder.Append("\nTrack unlock: " + APHandler.FormatLocationString(trackHints.TrackUnlock));
							}
							if (APHandler.WorldSettings.DifficultyGating == APSettings.DiffGateType.ON && !APHandler.APFoundItems.Contains((long)track.Value.Difficulty + 1010L))
							{
								stringBuilder.Append($"\nDifficulty {track.Value.Difficulty}: {APHandler.FormatLocationString(trackHints.DifficultyUnlocks[0])}");
							}
							if (APHandler.WorldSettings.DifficultyGating == APSettings.DiffGateType.PROG)
							{
								int num2 = track.Value.Difficulty - APHandler.WorldSettings.MinDiff;
								if (num2 > 0)
								{
									int num3 = APHandler.APFoundItems.Count((long id) => id == 1011);
									if (num3 < num2)
									{
										stringBuilder.Append($"\nDifficulty unlocks: {num3}/{num2}");
										Hint[] difficultyUnlocks = trackHints.DifficultyUnlocks;
										foreach (Hint hint in difficultyUnlocks)
										{
											stringBuilder.Append("\nProgressive Difficulty: " + APHandler.FormatLocationString(hint));
										}
									}
								}
							}
						}
						__instance.songdesctext.text = stringBuilder.ToString();
					}
					else
					{
						__instance.songdesctext.text = "Not an AP track.";
					}
				}
				((Behaviour)__instance.playbtn).enabled = flag;
				((Component)__instance.playbtn).gameObject.SetActive(flag);
			}
		}

		[HarmonyPatch(typeof(GameController), "Awake")]
		private class TrapReset
		{
			private static void Postfix()
			{
				APTrapController.ResetState();
			}
		}

		[HarmonyPatch(typeof(GameController), "Update")]
		private class TrapPatch
		{
			private static void Postfix(GameController __instance)
			{
				APTrapController.ControllerUpdate(__instance);
			}
		}

		private static Harmony _harmony = new Harmony("archipelago");

		public static ArchipelagoPlugin Instance;

		internal static ManualLogSource Logger;

		private string uri = "archipelago.gg";

		private string port = "38281";

		private string slotname = "";

		private string password = "";

		public int curGUI = -1;

		private Rect windowRect = new Rect(20f, 20f, 700f, 300f);

		private static GUIStyle textStyle = new GUIStyle
		{
			fontSize = 17
		};

		public static bool SendChatToLog = false;

		private Vector2 hintScroll1 = Vector2.zero;

		private Vector2 hintScroll2 = Vector2.zero;

		private const int hintEntryWidth = 240;

		private const int hintEntryInfoWidth = 70;

		private void Awake()
		{
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			GameInitializationEvent.Register(((BaseUnityPlugin)this).Info, (Action)TryInitialize);
		}

		private void TryInitialize()
		{
			ImageHandler.LoadTextures();
			TrackCollectionRegistrationEvent.EVENT.Register((Listener)(object)new TrackCollectionListener());
			_harmony.PatchAll();
		}

		private void Update()
		{
			if (Input.GetKeyDown((KeyCode)282))
			{
				int num = curGUI;
				if ((num > 1 || num == -1) ? true : false)
				{
					if (APHandler.APSlot == -1)
					{
						curGUI = 0;
					}
					else
					{
						curGUI = 1;
					}
				}
				else
				{
					curGUI = -1;
				}
			}
			if (!Input.GetKeyDown((KeyCode)283))
			{
				return;
			}
			if (curGUI != 2)
			{
				if (APHandler.APSlot == -1)
				{
					curGUI = 0;
				}
				else
				{
					curGUI = 2;
				}
			}
			else
			{
				curGUI = -1;
			}
		}

		private void OnGUI()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Expected O, but got Unknown
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0443: Unknown result type (might be due to invalid IL or missing references)
			//IL_0465: Unknown result type (might be due to invalid IL or missing references)
			//IL_0480: Unknown result type (might be due to invalid IL or missing references)
			//IL_048a: Expected O, but got Unknown
			//IL_0485: Unknown result type (might be due to invalid IL or missing references)
			//IL_048a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0492: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d4: Unknown result type (might be due to invalid IL or missing references)
			if (curGUI != -1)
			{
				windowRect = GUI.Window(curGUI, windowRect, new WindowFunction(WindowHandler), "Archipelago Menu");
			}
			if (!ImageHandler.TexturesLoaded)
			{
				return;
			}
			int height = Screen.height;
			GUI.DrawTexture(new Rect(10f, (float)(height - 50), 40f, 40f), (Texture)(object)((APHandler.APSlot == -1) ? ImageHandler.ArchipelagoGrey : ImageHandler.Archipelago));
			if (APHandler.APSlot == -1)
			{
				return;
			}
			int num = 60;
			if (APHandler.HasGoaled(-1L))
			{
				return;
			}
			if (APHandler.GoalTrack.HasValue && APHandler.IsTrackAvailable(APHandler.GoalTrack.Value))
			{
				int num2 = APHandler.APFoundItems.Count((long id) => id == 1001);
				int num3 = APHandler.WorldSettings.InitialRating - APHandler.WorldSettings.GoalRating;
				if (num2 >= num3)
				{
					return;
				}
			}
			if (APHandler.WorldSettings.HotDogs > 0)
			{
				bool flag = APHandler.APFoundItems.Count((long id) => id == 1004) >= APHandler.WorldSettings.HotDogs;
				GUI.DrawTexture(new Rect((float)num, (float)(height - 50), 40f, 40f), (Texture)(object)(flag ? ImageHandler.HotDog : ImageHandler.HotDogGrey));
				num += 50;
			}
			if (APHandler.WorldSettings.InitialRating > APHandler.WorldSettings.GoalRating)
			{
				bool flag2 = APHandler.APFoundItems.Count((long id) => id == 1001) >= APHandler.WorldSettings.InitialRating - APHandler.WorldSettings.GoalRating;
				GUI.DrawTexture(new Rect((float)num, (float)(height - 50), 40f, 40f), (Texture)(object)(flag2 ? ImageHandler.RankIndicator : ImageHandler.RankIndicatorGrey));
				string text = (new string[4] { "C", "B", "A", "S" })[APHandler.GetRequiredRating()];
				GUI.color = Color.black;
				Vector2 val = textStyle.CalcSize(new GUIContent(text));
				GUI.Label(new Rect((float)(num + 20) - val.x / 2f, (float)(height - 30) - val.y / 2f, 40f, 40f), text, textStyle);
				GUI.color = Color.white;
				num += 50;
			}
			if (APHandler.WorldSettings.DifficultyGating == APSettings.DiffGateType.OFF)
			{
				return;
			}
			List<int> list = new List<int>();
			List<int> list2 = new List<int>();
			if (APHandler.WorldSettings.DifficultyGating == APSettings.DiffGateType.PROG)
			{
				int num4 = APHandler.WorldSettings.MinDiff + APHandler.APFoundItems.Count((long id) => id == 1011);
				for (int i = APHandler.WorldSettings.MinDiff + 1; i <= APHandler.WorldSettings.MaxDiff; i++)
				{
					list.Add(i);
					if (num4 < i)
					{
						list2.Add(i);
					}
				}
			}
			if (APHandler.WorldSettings.DifficultyGating == APSettings.DiffGateType.ON)
			{
				for (int j = APHandler.WorldSettings.MinDiff + 1; j <= APHandler.WorldSettings.MaxDiff; j++)
				{
					list.Add(j);
					if (!APHandler.APFoundItems.Contains((long)j + 1010L))
					{
						list2.Add(j);
					}
				}
			}
			foreach (int item in list)
			{
				GUI.DrawTexture(new Rect((float)num, (float)(height - 50), 40f, 40f), (Texture)(object)(list2.Contains(item) ? ImageHandler.DifficultyIndicatorGrey : ImageHandler.DifficultyIndicator));
				GUI.color = Color.black;
				string text2 = item.ToString();
				Vector2 val2 = textStyle.CalcSize(new GUIContent(text2));
				GUI.Label(new Rect((float)(num + 20) - val2.x / 2f, (float)(height - 28) - val2.y / 2f, 40f, 40f), item.ToString(), textStyle);
				GUI.color = Color.white;
				num += 50;
			}
		}

		private void WindowHandler(int ID)
		{
			switch (ID)
			{
			case 0:
				ShowLoginWindow();
				break;
			case 1:
				ShowTrackerWindow();
				break;
			case 2:
				ShowHintWindow();
				break;
			default:
				Logger.LogWarning((object)$"Unknown GUI ID: {ID}");
				curGUI = -1;
				break;
			}
		}

		private void ShowLoginWindow()
		{
			GUILayout.Label("Server URI: ", Array.Empty<GUILayoutOption>());
			uri = GUILayout.TextField(uri, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) });
			GUILayout.Label("Server Port: ", Array.Empty<GUILayoutOption>());
			port = GUILayout.TextField(port, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) });
			GUILayout.Label("Server Slotname: ", Array.Empty<GUILayoutOption>());
			slotname = GUILayout.TextField(slotname, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) });
			GUILayout.Label("Server Password (Optional): ", Array.Empty<GUILayoutOption>());
			password = GUILayout.PasswordField(password, '*', (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) });
			if (GUILayout.Button("Connect Archipelago", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }) && int.TryParse(port.Trim(), out var result))
			{
				APHandler.ConnectToAP(uri, result, slotname, password);
			}
			if (GUILayout.Button("Close", Array.Empty<GUILayoutOption>()))
			{
				curGUI = -1;
			}
		}

		private void ShowTrackerWindow()
		{
			GUILayout.Label("Connected to AP server.", Array.Empty<GUILayoutOption>());
			SendChatToLog = GUILayout.Toggle(SendChatToLog, "Send Chat to Log", Array.Empty<GUILayoutOption>());
			GUILayout.Space(10f);
			Track[] array = APTracks.GetTrackList(APHandler.WorldSettings).ToArray();
			string text;
			if (APHandler.HasGoaled(-1L))
			{
				text = "Goaled!";
			}
			else if (APHandler.WorldSettings.GoalTracks == 0)
			{
				text = "Goal track: " + APHandler.WorldSettings.GoalTrack;
			}
			else
			{
				int num = array.Count((Track track) => APHandler.APSentLocations.Contains(track.ID + 1000));
				text = $"Beat tracks: {num}/{APHandler.WorldSettings.GoalTrack}";
			}
			GUILayout.Label(text, Array.Empty<GUILayoutOption>());
			if (APHandler.WorldSettings.HotDogs > 0)
			{
				int num2 = APHandler.APFoundItems.Count((long id) => id == 1004);
				if (APHandler.WorldSettings.ExtraHotDogs > 0)
				{
					int num3 = APHandler.WorldSettings.HotDogs + APHandler.WorldSettings.ExtraHotDogs;
					GUILayout.Label($"Hot Dogs: {num2}/{APHandler.WorldSettings.HotDogs} ({num3})", Array.Empty<GUILayoutOption>());
				}
				else
				{
					GUILayout.Label($"Hot Dogs: {num2}/{APHandler.WorldSettings.HotDogs}", Array.Empty<GUILayoutOption>());
				}
			}
			string[] array2 = new string[4] { "C", "B", "A", "S" };
			GUILayout.Label("Required rating: " + array2[APHandler.GetRequiredRating()], Array.Empty<GUILayoutOption>());
			if (APHandler.WorldSettings.InitialRating != APHandler.WorldSettings.GoalRating)
			{
				int num4 = APHandler.WorldSettings.InitialRating - APHandler.WorldSettings.GoalRating;
				int num5 = APHandler.APFoundItems.Count((long id) => id == 1001);
				GUILayout.Label($"Rating Reduction items: {num5}/{num4}", Array.Empty<GUILayoutOption>());
			}
			GUILayout.Space(10f);
			if (APHandler.WorldSettings.TrackGating || APHandler.WorldSettings.DifficultyGating != 0)
			{
				if (APHandler.WorldSettings.TrackGating)
				{
					int num6 = array.Count((Track track) => APHandler.APFoundItems.Contains(track.ID));
					GUILayout.Label($"Tracks unlocked: {num6}/{array.Length}", Array.Empty<GUILayoutOption>());
				}
				if (APHandler.WorldSettings.DifficultyGating == APSettings.DiffGateType.ON)
				{
					List<int> list = new List<int>(1) { APHandler.WorldSettings.MinDiff };
					for (int i = APHandler.WorldSettings.MinDiff + 1; i <= APHandler.WorldSettings.MaxDiff; i++)
					{
						if (APHandler.APFoundItems.Contains((long)i + 1010L))
						{
							list.Add(i);
						}
					}
					GUILayout.Label("Difficulties unlocked: " + string.Join(", ", list), Array.Empty<GUILayoutOption>());
				}
				if (APHandler.WorldSettings.DifficultyGating == APSettings.DiffGateType.PROG)
				{
					int num7 = APHandler.WorldSettings.MinDiff + APHandler.APFoundItems.Count((long id) => id == 1011);
					GUILayout.Label($"Max difficulty: {num7}/{APHandler.WorldSettings.MaxDiff}", Array.Empty<GUILayoutOption>());
				}
				GUILayout.Space(10f);
			}
			if (GUILayout.Button("Disconnect", Array.Empty<GUILayoutOption>()))
			{
				APHandler.APSession.Socket.DisconnectAsync();
				APHandler.APSlot = -1;
				curGUI = 0;
			}
			if (GUILayout.Button("Close", Array.Empty<GUILayoutOption>()))
			{
				curGUI = -1;
			}
		}

		private void ShowHintWindow()
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_09d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_09f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_09f5: Unknown result type (might be due to invalid IL or missing references)
			if (APHandler.APSlot == -1)
			{
				curGUI = 0;
				return;
			}
			bool flag = APHandler.CanHint();
			GUILayout.Label($"Hint points: {APHandler.APSession.RoomState.HintPoints}/{APHandler.APSession.RoomState.HintCost}", Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
			hintScroll1 = GUILayout.BeginScrollView(hintScroll1, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(340f) });
			bool flag2 = false;
			if (APHandler.WorldSettings.DifficultyGating == APSettings.DiffGateType.ON)
			{
				List<int> list = new List<int>();
				for (int i = APHandler.WorldSettings.MinDiff + 1; i <= APHandler.WorldSettings.MaxDiff; i++)
				{
					if (!APHandler.APFoundItems.Contains((long)i + 1010L))
					{
						list.Add(i);
					}
				}
				if (list.Count > 0)
				{
					flag2 = true;
					GUILayout.Label($"Missing difficulties: {list.Count}", Array.Empty<GUILayoutOption>());
					foreach (int diff in list)
					{
						GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
						GUILayout.Label($"Difficulty {diff}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(240f) });
						Hint val = ((IEnumerable<Hint>)APHandler.APReceivedHints).FirstOrDefault((Func<Hint, bool>)((Hint hint) => hint.ReceivingPlayer == APHandler.APSlot && hint.ItemId == (long)diff + 1010L));
						if (val != null && val.ReceivingPlayer == APHandler.APSlot && val.ItemId == (long)diff + 1010L)
						{
							GUI.enabled = false;
							GUILayout.Button("HINTED", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
							GUI.enabled = true;
						}
						else if (flag)
						{
							if (GUILayout.Button("Hint", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }))
							{
								APHandler.TryHintDifficulty(diff);
							}
						}
						else
						{
							GUI.enabled = false;
							GUILayout.Button("MISSING", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
							GUI.enabled = true;
						}
						GUILayout.EndHorizontal();
					}
				}
			}
			if (APHandler.WorldSettings.DifficultyGating == APSettings.DiffGateType.PROG)
			{
				int num = APHandler.WorldSettings.MaxDiff - APHandler.WorldSettings.MinDiff;
				int num2 = APHandler.APFoundItems.Count((long id) => id == 1011);
				if (num2 < num)
				{
					flag2 = true;
					Hint[] array = APHandler.APReceivedHints.Where((Hint hint) => hint.ReceivingPlayer == APHandler.APSlot && hint.ItemId == 1011).ToArray();
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					GUILayout.Label($"Progressive Difficulty ({num2}/{num})", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(240f) });
					int num3 = num - num2;
					if (flag)
					{
						if (GUILayout.Button($"Hint {array.Length}/{num3}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }))
						{
							APHandler.TryHintDifficulty(1);
						}
					}
					else if (array.Length != 0)
					{
						GUI.enabled = false;
						GUILayout.Button($"HINTED {array.Length}/{num3}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
						GUI.enabled = true;
					}
					else
					{
						GUI.enabled = false;
						GUILayout.Button("MISSING", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
						GUI.enabled = true;
					}
					GUILayout.EndHorizontal();
				}
			}
			if (APHandler.WorldSettings.InitialRating != APHandler.WorldSettings.GoalRating)
			{
				int num4 = APHandler.WorldSettings.InitialRating - APHandler.WorldSettings.GoalRating;
				int num5 = APHandler.APFoundItems.Count((long id) => id == 1001);
				if (num5 < num4)
				{
					flag2 = true;
					Hint[] array2 = APHandler.APReceivedHints.Where((Hint hint) => hint.ReceivingPlayer == APHandler.APSlot && hint.ItemId == 1001).ToArray();
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					GUILayout.Label($"Rank Reduction ({num5}/{num4})", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(240f) });
					int num6 = num4 - num5;
					if (flag)
					{
						if (GUILayout.Button($"Hint {array2.Length}/{num6}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }))
						{
							APHandler.TryHintRankReduction();
						}
					}
					else if (array2.Length != 0)
					{
						GUI.enabled = false;
						GUILayout.Button($"HINTED {array2.Length}/{num6}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
						GUI.enabled = true;
					}
					else
					{
						GUI.enabled = false;
						GUILayout.Button("MISSING", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
						GUI.enabled = true;
					}
					GUILayout.EndHorizontal();
				}
			}
			if (APHandler.WorldSettings.HotDogs > 0)
			{
				int num7 = APHandler.WorldSettings.HotDogs + APHandler.WorldSettings.ExtraHotDogs;
				int num8 = APHandler.APFoundItems.Count((long id) => id == 1004);
				if (num8 < APHandler.WorldSettings.HotDogs)
				{
					flag2 = true;
					Hint[] array3 = APHandler.APReceivedHints.Where((Hint hint) => hint.ReceivingPlayer == APHandler.APSlot && hint.ItemId == 1004).ToArray();
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					GUILayout.Label($"Hot Dog ({num8}/{APHandler.WorldSettings.HotDogs} ({num7}))", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(240f) });
					int num9 = num7 - num8;
					if (flag)
					{
						if (GUILayout.Button($"Hint {array3.Length}/{num9}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }))
						{
							APHandler.TryHintHotDog();
						}
					}
					else if (array3.Length != 0)
					{
						GUI.enabled = false;
						GUILayout.Button($"HINTED {array3.Length}/{num9}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
						GUI.enabled = true;
					}
					else
					{
						GUI.enabled = false;
						GUILayout.Button("MISSING", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
						GUI.enabled = true;
					}
					GUILayout.EndHorizontal();
				}
			}
			if (APHandler.WorldSettings.TrackGating)
			{
				Track[] array4 = APHandler.FilteredTracks.Where((Track track) => !APHandler.APFoundItems.Contains(track.ID)).ToArray();
				if (array4.Length != 0)
				{
					flag2 = true;
					GUILayout.Label($"Track unlocks remaining: {array4.Length}", Array.Empty<GUILayoutOption>());
					Track[] array5 = array4;
					for (int j = 0; j < array5.Length; j++)
					{
						Track track2 = array5[j];
						GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
						GUILayout.Label(track2.Name, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(240f) });
						Hint val2 = ((IEnumerable<Hint>)APHandler.APReceivedHints).FirstOrDefault((Func<Hint, bool>)((Hint hint) => hint.ReceivingPlayer == APHandler.APSlot && hint.ItemId == track2.ID));
						if (val2 != null && val2.ReceivingPlayer == APHandler.APSlot && val2.ItemId == track2.ID)
						{
							GUI.enabled = false;
							GUILayout.Button("HINTED", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
							GUI.enabled = true;
						}
						else if (flag)
						{
							if (GUILayout.Button("Hint", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }))
							{
								APHandler.TryHintTrack(track2);
							}
						}
						else
						{
							GUI.enabled = false;
							GUILayout.Button("MISSING", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
							GUI.enabled = true;
						}
						GUILayout.EndHorizontal();
					}
				}
			}
			if (!flag2)
			{
				GUILayout.Label("No more items!", Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndScrollView();
			GUILayout.EndVertical();
			GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
			hintScroll2 = GUILayout.BeginScrollView(hintScroll2, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(340f) });
			Hint[] array6 = APHandler.APReceivedHints.Where((Hint hint) => hint.ReceivingPlayer != APHandler.APSlot || (hint.ItemId != 1002 && hint.ItemId != 1003)).ToArray();
			if (array6.Length == 0)
			{
				GUILayout.Label("No hints!", Array.Empty<GUILayoutOption>());
			}
			else
			{
				Hint[] array7 = array6.Where((Hint hint) => hint.FindingPlayer == APHandler.APSlot && hint.ReceivingPlayer == APHandler.APSlot).ToArray();
				Hint[] array8 = array6.Where((Hint hint) => hint.FindingPlayer != APHandler.APSlot && hint.ReceivingPlayer == APHandler.APSlot).ToArray();
				Hint[] array9 = array6.Where((Hint hint) => hint.ReceivingPlayer != APHandler.APSlot).ToArray();
				Hint[] array10 = array7;
				foreach (Hint hint2 in array10)
				{
					GUILayout.Label(APHandler.FormatFullHint(hint2), Array.Empty<GUILayoutOption>());
				}
				Hint[] array11 = array8;
				foreach (Hint hint3 in array11)
				{
					GUILayout.Label(APHandler.FormatFullHint(hint3), Array.Empty<GUILayoutOption>());
				}
				Hint[] array12 = array9;
				foreach (Hint hint4 in array12)
				{
					GUILayout.Label(APHandler.FormatFullHint(hint4), Array.Empty<GUILayoutOption>());
				}
			}
			GUILayout.EndScrollView();
			GUILayout.EndVertical();
			GUILayout.EndHorizontal();
			if (GUILayout.Button("Disconnect", Array.Empty<GUILayoutOption>()))
			{
				APHandler.APSession.Socket.DisconnectAsync();
				APHandler.APSlot = -1;
				curGUI = 0;
			}
			if (GUILayout.Button("Close", Array.Empty<GUILayoutOption>()))
			{
				curGUI = -1;
			}
		}
	}
	public static class ImageHandler
	{
		public static Sprite ArchipelagoCollection;

		public static Sprite ArchipelagoCollectionFiltered;

		public static Sprite ArchipelagoCollectionLocked;

		public static Texture2D Archipelago;

		public static Texture2D ArchipelagoGrey;

		public static Texture2D HotDog;

		public static Texture2D HotDogGrey;

		public static Texture2D RankIndicator;

		public static Texture2D RankIndicatorGrey;

		public static Texture2D DifficultyIndicator;

		public static Texture2D DifficultyIndicatorGrey;

		public static bool TexturesLoaded;

		public static void LoadTextures()
		{
			FromFile("ap");
			FromFile("ap_grey");
			FromFile("difficulty");
			FromFile("difficulty_grey");
			FromFile("hotdog");
			FromFile("hotdog_grey");
			FromFile("rank");
			FromFile("rank_grey");
			FromFileSprite("coll_ap");
			FromFileSprite("coll_ap_filter");
			FromFileSprite("coll_ap_locked");
		}

		private static void FromFile(string name)
		{
			string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)ArchipelagoPlugin.Instance).Info.Location), name + ".png");
			ArchipelagoPlugin.Logger.LogInfo((object)("Loading texture from " + text));
			UnityWebRequest req = UnityWebRequestTexture.GetTexture(text);
			UnityWebRequestAsyncOperation val = req.SendWebRequest();
			bool done = false;
			((AsyncOperation)val).completed += delegate
			{
				done = true;
				SetTexture(req.downloadHandler.data, name);
			};
			if (((AsyncOperation)val).isDone && !done)
			{
				SetTexture(req.downloadHandler.data, name);
			}
		}

		private static void SetTexture(byte[] data, string name)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Expected O, but got Unknown
			Texture2D val = new Texture2D(1, 1);
			ImageConversion.LoadImage(val, data);
			switch (name)
			{
			case "ap":
				Archipelago = val;
				break;
			case "ap_grey":
				ArchipelagoGrey = val;
				break;
			case "difficulty":
				DifficultyIndicator = val;
				break;
			case "difficulty_grey":
				DifficultyIndicatorGrey = val;
				break;
			case "hotdog":
				HotDog = val;
				break;
			case "hotdog_grey":
				HotDogGrey = val;
				break;
			case "rank":
				RankIndicator = val;
				break;
			case "rank_grey":
				RankIndicatorGrey = val;
				break;
			default:
				ArchipelagoPlugin.Logger.LogWarning((object)("Texture loaded but unknown name: " + name));
				break;
			}
			if (Object.op_Implicit((Object)(object)Archipelago) && Object.op_Implicit((Object)(object)ArchipelagoGrey) && Object.op_Implicit((Object)(object)DifficultyIndicator) && Object.op_Implicit((Object)(object)DifficultyIndicatorGrey) && Object.op_Implicit((Object)(object)HotDog) && Object.op_Implicit((Object)(object)HotDogGrey) && Object.op_Implicit((Object)(object)RankIndicator) && Object.op_Implicit((Object)(object)RankIndicatorGrey) && Object.op_Implicit((Object)(object)ArchipelagoCollection) && Object.op_Implicit((Object)(object)ArchipelagoCollectionFiltered) && Object.op_Implicit((Object)(object)ArchipelagoCollectionLocked))
			{
				TexturesLoaded = true;
			}
		}

		private static void FromFileSprite(string name)
		{
			string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)ArchipelagoPlugin.Instance).Info.Location), name + ".png");
			ArchipelagoPlugin.Logger.LogInfo((object)("Loading texture from " + text));
			UnityWebRequest req = UnityWebRequestTexture.GetTexture(text);
			UnityWebRequestAsyncOperation val = req.SendWebRequest();
			bool done = false;
			((AsyncOperation)val).completed += delegate
			{
				done = true;
				SetSprite(req.downloadHandler.data, name);
			};
			if (((AsyncOperation)val).isDone && !done)
			{
				SetSprite(req.downloadHandler.data, name);
			}
		}

		private static void SetSprite(byte[] data, string name)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Expected O, but got Unknown
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(1, 1);
			ImageConversion.LoadImage(val, data);
			Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
			switch (name)
			{
			case "coll_ap":
				ArchipelagoCollection = val2;
				break;
			case "coll_ap_filter":
				ArchipelagoCollectionFiltered = val2;
				break;
			case "coll_ap_locked":
				ArchipelagoCollectionLocked = val2;
				break;
			default:
				ArchipelagoPlugin.Logger.LogWarning((object)("Sprite loaded but unknown name: " + name));
				break;
			}
			if (Object.op_Implicit((Object)(object)Archipelago) && Object.op_Implicit((Object)(object)ArchipelagoGrey) && Object.op_Implicit((Object)(object)DifficultyIndicator) && Object.op_Implicit((Object)(object)DifficultyIndicatorGrey) && Object.op_Implicit((Object)(object)HotDog) && Object.op_Implicit((Object)(object)HotDogGrey) && Object.op_Implicit((Object)(object)RankIndicator) && Object.op_Implicit((Object)(object)RankIndicatorGrey) && Object.op_Implicit((Object)(object)ArchipelagoCollection) && Object.op_Implicit((Object)(object)ArchipelagoCollectionFiltered) && Object.op_Implicit((Object)(object)ArchipelagoCollectionLocked))
			{
				TexturesLoaded = true;
			}
		}
	}
	public interface APCollection
	{
		bool HasNoTracks();

		string GetNoTrackString();
	}
	public class TrackCollectionAllAP : BaseTromboneCollection, APCollection
	{
		[CompilerGenerated]
		private sealed class <BuildTrackList>d__1 : IEnumerable<TromboneTrack>, IEnumerable, IEnumerator<TromboneTrack>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private TromboneTrack <>2__current;

			private int <>l__initialThreadId;

			public TrackCollectionAllAP <>4__this;

			private int <yielded>5__1;

			private ArrayList <unseen>5__2;

			private List<string> <unknownTracks>5__3;

			private IEnumerator<TromboneTrack> <>s__4;

			private TromboneTrack <track>5__5;

			private Track? <match>5__6;

			private IEnumerator <>s__7;

			private Track <trackDef>5__8;

			private IEnumerator <>s__9;

			private Track <missed>5__10;

			private List<string>.Enumerator <>s__11;

			private string <unknown>5__12;

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

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

			[DebuggerHidden]
			public <BuildTrackList>d__1(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<unseen>5__2 = null;
				<unknownTracks>5__3 = null;
				<>s__4 = null;
				<track>5__5 = null;
				<match>5__6 = null;
				<>s__7 = null;
				<trackDef>5__8 = default(Track);
				<>s__9 = null;
				<missed>5__10 = default(Track);
				<>s__11 = default(List<string>.Enumerator);
				<unknown>5__12 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				try
				{
					switch (<>1__sta

Archipelago.MultiClient.Net.dll

Decompiled 8 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;
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(".NETFramework,Version=v4.5", FrameworkDisplayName = ".NET Framework 4.5")]
[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;
			SecurityProtocolType securityProtocolType = SecurityProtocolType.Tls13;
			ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | securityProtocolType;
		}

		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)
		{
			Get