Decompiled source of Gungeon Archipelago v1.0.1
Archipelago.MultiClient.Net.dll
Decompiled 3 months ago
The result has been truncated due to the large size, download it to view full contents!
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.InteropServices; using System.Runtime.Serialization; using System.Security.Authentication; using System.Text; using System.Threading; 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; using WebSocketSharp; [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: AssemblyCompany("Jarno Westhof, Hussein Farran, Zach Parks")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyDescription("A client library for use with .NET based prog-langs for interfacing with Archipelago hosts.")] [assembly: AssemblyFileVersion("6.3.0.0")] [assembly: AssemblyInformationalVersion("6.3.0+3113ea01d1b0db22ada93b5f35c6d4272a8002b2")] [assembly: AssemblyProduct("Archipelago.MultiClient.Net")] [assembly: AssemblyTitle("Archipelago.MultiClient.Net")] [assembly: AssemblyVersion("6.3.0.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); } 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; } 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 volatile bool awaitingRoomInfo; private volatile bool expectingLoginResult; private LoginResult loginResult; 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; } internal ArchipelagoSession(IArchipelagoSocketHelper socket, IReceivedItemsHelper items, ILocationCheckHelper locations, IPlayerHelper players, IRoomStateHelper roomState, ConnectionInfoHelper connectionInfoHelper, IDataStorageHelper dataStorage, IMessageLogHelper messageLog) { Socket = socket; Items = items; Locations = locations; Players = players; RoomState = roomState; connectionInfo = connectionInfoHelper; DataStorage = dataStorage; MessageLog = messageLog; socket.PacketReceived += Socket_PacketReceived; } private void Socket_PacketReceived(ArchipelagoPacketBase packet) { if (!(packet is ConnectedPacket) && !(packet is ConnectionRefusedPacket)) { if (packet is RoomInfoPacket) { awaitingRoomInfo = false; } return; } if (packet is ConnectedPacket && RoomState.Version != null && RoomState.Version >= new Version(0, 3, 8)) { LogUsedVersion(); } if (expectingLoginResult) { expectingLoginResult = false; loginResult = LoginResult.FromPacket(packet); } } private void LogUsedVersion() { try { string fileVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion; Socket.SendPacketAsync(new SetPacket { Key = ".NetUsedVersions", DefaultValue = (JToken)(object)JObject.FromObject((object)new Dictionary<string, bool>()), Operations = new OperationSpecification[1] { Operation.Update(new Dictionary<string, bool> { { ConnectionInfo.Game + ":" + fileVersion + ":NET35", true } }) } }); } catch { } } public LoginResult TryConnectAndLogin(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true) { connectionInfo.SetConnectionParameters(game, tags, itemsHandlingFlags, uuid); try { awaitingRoomInfo = true; expectingLoginResult = true; loginResult = null; Socket.Connect(); DateTime utcNow = DateTime.UtcNow; while (awaitingRoomInfo) { if (DateTime.UtcNow - utcNow > TimeSpan.FromSeconds(4.0)) { Socket.Disconnect(); return new LoginFailure("Connection timed out."); } Thread.Sleep(25); } Socket.SendPacket(BuildConnectPacket(name, password, version, requestSlotData)); utcNow = DateTime.UtcNow; while (expectingLoginResult) { if (DateTime.UtcNow - utcNow > TimeSpan.FromSeconds(4.0)) { Socket.Disconnect(); return new LoginFailure("Connection timed out."); } Thread.Sleep(25); } Thread.Sleep(50); return loginResult; } catch (ArchipelagoSocketClosedException) { return new LoginFailure("Socket closed unexpectedly."); } } 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, 4, 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 roomState = new RoomStateHelper(socket, locationCheckHelper); DataStorageHelper dataStorage = new DataStorageHelper(socket, connectionInfoHelper); MessageLogHelper messageLog = new MessageLogHelper(socket, itemInfoResolver, playerHelper, connectionInfoHelper); return new ArchipelagoSession(socket, items, locationCheckHelper, playerHelper, roomState, connectionInfoHelper, dataStorage, messageLog); } 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(StringEnumConverter))] 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 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(StringEnumConverter))] 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; } } 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 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 void GetAsync<T>(Action<T> callback) { GetAsync(delegate(JToken t) { callback(t.ToObject<T>()); }); } public void GetAsync(Action<JToken> callback) { Context.GetAsync(Context.Key, callback); } 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Invalid comparison between Unknown and I4 //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Invalid comparison between Unknown and I4 //IL_00d7: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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((object?)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 (object)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 Action<string, Action<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; } } 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 = ((object)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(StringEnumConverter), new object[] { typeof(SnakeCaseNamingStrategy) })] public JsonMessagePartType? Type { get; set; } [JsonProperty("color")] [JsonConverter(typeof(StringEnumConverter), 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; } } 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(StringEnumConverter), 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 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 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 Or(long i) { return new OperationSpecification { OperationType = OperationType.Or, Value = JToken.op_Implicit(i) }; } public static OperationSpecification And(long i) { return new OperationSpecification { OperationType = OperationType.And, Value = JToken.op_Implicit(i) }; } 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, Color.Blue) { base.Text = messagePart.Text; } } 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.Color = 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; } } } private static Color GetColor(ItemFlags flags) { if (HasFlag(flags, ItemFlags.Advancement)) { return Color.Plum; } if (HasFlag(flags, ItemFlags.NeverExclude)) { return Color.SlateBlue; } if (HasFlag(flags, ItemFlags.Trap)) { return Color.Salmon; } return Color.Cyan; } private static bool HasFlag(ItemFlags flags, ItemFlags flag) { return (flags & flag) > ItemFlags.None; } } public class LocationMessagePart : MessagePart { public long LocationId { get; } public int Player { get; } internal LocationMessagePart(IPlayerHelper players, IItemInfoResolver itemInfoResolver, JsonMessagePart part) : base(MessagePartType.Location, part, Color.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 { get; internal set; } public bool IsBackgroundColor { get; internal set; } internal MessagePart(MessagePartType type, JsonMessagePart messagePart, Color? color = null) { Type = type; Text = messagePart.Text; if (color.HasValue) { Color = color.Value; } else if (messagePart.Color.HasValue) { Color = GetColor(messagePart.Color.Value); IsBackgroundColor = messagePart.Color.Value >= JsonMessagePartColor.BlackBg; } else { Color = Color.White; } } private static Color GetColor(JsonMessagePartColor color) { switch (color) { case JsonMessagePartColor.Red: case JsonMessagePartColor.RedBg: return Color.Red; case JsonMessagePartColor.Green: case JsonMessagePartColor.GreenBg: return Color.Green; case JsonMessagePartColor.Yellow: case JsonMessagePartColor.YellowBg: return Color.Yellow; case JsonMessagePartColor.Blue: case JsonMessagePartColor.BlueBg: return Color.Blue; case JsonMessagePartColor.Magenta: case JsonMessagePartColor.MagentaBg: return Color.Magenta; case JsonMessagePartColor.Cyan: case JsonMessagePartColor.CyanBg: return Color.Cyan; case JsonMessagePartColor.Black: case JsonMessagePartColor.BlackBg: return Color.Black; case JsonMessagePartColor.White: case JsonMessagePartColor.WhiteBg: return Color.White; default: return Color.White; } } public override string ToString() { return Text; } } public enum MessagePartType { Text, Player, Item, Location, Entrance } 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.Color = GetColor(IsActivePlayer); } private static Color GetColor(bool isActivePlayer) { if (isActivePlayer) { return Color.Magenta; } return Color.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 : IArchipelagoSocketHelper { private const SslProtocols Tls13 = SslProtocols.Tls13; private const SslProtocols Tls12 = SslProtocols.Tls12; private static readonly ArchipelagoPacketConverter Converter = new ArchipelagoPacketConverter(); internal WebSocket webSocket; public Uri Uri { get; } public bool Connected { get { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 if (webSocket != null) { if ((int)webSocket.ReadyState != 1) { return (int)webSocket.ReadyState == 2; } return true; } return false; } } 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 ArchipelagoSocketHelper(Uri hostUrl) { Uri = hostUrl; } private WebSocket CreateWebSocket(Uri uri) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown WebSocket val = new WebSocket(uri.ToString(), new string[0]); if (val.IsSecure) { val.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13; } val.OnMessage += OnMessageReceived; val.OnError += OnError; val.OnClose += OnClose; val.OnOpen += OnOpen; return val; } public void Connect() { ConnectToProvidedUri(Uri); } private void ConnectToProvidedUri(Uri uri) { if (uri.Scheme != "unspecified") { try { webSocket = CreateWebSocket(uri); webSocket.Connect(); return; } catch (Exception e) { OnError(e); return; } } List<Exception> list = new List<Exception>(); try { try { ConnectToProvidedUri(uri.AsWss()); } catch (Exception item) { list.Add(item); throw; } if (webSocket.IsAlive) { return; } try { ConnectToProvidedUri(uri.AsWs()); } catch (Exception item2) { list.Add(item2); throw; } } catch { try { ConnectToProvidedUri(uri.AsWs()); } catch (Exception item3) { list.Add(item3); OnError(new Archipelago.MultiClient.Net.Exceptions.AggregateException(list)); } } } public void Disconnect() { if (webSocket != null && webSocket.IsAlive) { webSocket.Close(); } } public void DisconnectAsync() { if (webSocket != null && webSocket.IsAlive) { webSocket.CloseAsync(); } } public void SendPacket(ArchipelagoPacketBase packet) { SendMultiplePackets(packet); } public void SendMultiplePackets(List<ArchipelagoPacketBase> packets) { SendMultiplePackets(packets.ToArray()); } public void SendMultiplePackets(params ArchipelagoPacketBase[] packets) { if (webSocket != null && webSocket.IsAlive) { string text = JsonConvert.SerializeObject((object)packets); webSocket.Send(text); if (this.PacketsSent != null) { this.PacketsSent(packets); } return; } throw new ArchipelagoSocketClosedException(); } public void SendPacketAsync(ArchipelagoPacketBase packet, Action<bool> onComplete = null) { SendMultiplePacketsAsync(new List<ArchipelagoPacketBase> { packet }, onComplete); } public void SendMultiplePacketsAsync(List<ArchipelagoPacketBase> packets, Action<bool> onComplete = null) { SendMultiplePacketsAsync(onComplete, packets.ToArray()); } public void SendMultiplePacketsAsync(Action<bool> onComplete = null, params ArchipelagoPacketBase[] packets) { if (webSocket.IsAlive) { string text = JsonConvert.SerializeObject((object)packets); webSocket.SendAsync(text, onComplete); if (this.PacketsSent != null) { this.PacketsSent(packets); } return; } throw new ArchipelagoSocketClosedException(); } private void OnOpen(object sender, EventArgs e) { if (this.SocketOpened != null) { this.SocketOpened(); } } private void OnClose(object sender, CloseEventArgs e) { if ((!(Uri.Scheme == "unspecified") || sender != webSocket || !(webSocket.Url.Scheme == "wss")) && this.SocketClosed != null) { this.SocketClosed(e.Reason); } } private void OnMessageReceived(object sender, MessageEventArgs e) { if (!e.IsText || this.PacketReceived == null) { return; } List<ArchipelagoPacketBase> list = null; try { list = JsonConvert.DeserializeObject<List<ArchipelagoPacketBase>>(e.Data, (JsonConverter[])(object)new JsonConverter[1] { Converter }); } catch (Exception e2) { OnError(e2); } if (list == null) { return; } foreach (ArchipelagoPacketBase item in list) { this.PacketReceived(item); } } private void OnError(object sender, ErrorEventArgs e) { if (this.ErrorReceived != null) { this.ErrorReceived(e.Exception, e.Message); } } private void OnError(Exception e) { if (this.ErrorReceived != null) { this.ErrorReceived(e, e.Message); } } } 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, Action<JToken>> asyncRetrievalCallbacks = new Dictionary<string, Action<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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: 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 == 15 && operationSpecificCallbacks.TryGetValue((Guid)setReplyPacket.AdditionalArguments["Reference"], out var value)) { value(setReplyPacket.OriginalValue, setReplyPacket.Value, setReplyPacket.AdditionalArguments); operationSpecificCallbacks.Remove((Guid)setReplyPacket.AdditionalArguments["Reference"]); } 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 (asyncRetrievalCallbacks.TryGetValue(datum.Key, out var value3)) { value3(datum.Value); asyncRetrievalCallbacks.Remove(datum.Key); } } } private void GetAsync(string key, Action<JToken> callback) { if (!asyncRetrievalCallbacks.ContainsKey(key)) { asyncRetrievalCallbacks[key] = callback; } else { Dictionary<string, Action<JToken>> dictionary = asyncRetrievalCallbacks; dictionary[key] = (Action<JToken>)Delegate.Combine(dictionary[key], callback); } socket.SendPacketAsync(new GetPacket { Keys = new string[1] { key } }); } 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) { JToken value = null; GetAsync(key, delegate(JToken v) { value = v; }); int num = 0; while (value == null) { Thread.Sleep(10); if (++num > 200) { 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(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 value; } 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 guid = Guid.NewGuid(); operationSpecificCallbacks[guid] = e.Callbacks; dictionary["Reference"] = JToken.FromObject((object)guid); 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}"]; } public Hint[] GetHints(int? slot = null, int? team = null) { return GetHintsElement(slot, team).To<Hint[]>(); } public void GetHintsAsync(Action<Hint[]> onHintsRetrieved, int? slot = null, int? team = null) { GetHintsElement(slot, team).GetAsync(delegate(JToken t) { onHintsRetrieved((t != null) ? t.ToObject<Hint[]>() : null); }); } 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(onHintsUpdated, slot, team); } } 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 void GetSlotDataAsync(Action<Dictionary<string, object>> onSlotDataRetrieved, int? slot = null) { GetSlotDataElement(slot).GetAsync(delegate(JToken t) { onSlotDataRetrieved((t != null) ? t.ToObject<Dictionary<string, object>>() : null); }); } public void GetSlotDataAsync<T>(Action<T> onSlotDataRetrieved, int? slot = null) where T : class { GetSlotDataElement(slot).GetAsync(delegate(JToken t) { onSlotDataRetrieved((t != null) ? t.ToObject<T>() : null); }); } public Dictionary<string, string[]> GetItemNameGroups(string game = null) { return GetItemNameGroupsElement(game).To<Dictionary<string, string[]>>(); } public void GetItemNameGroupsAsync(Action<Dictionary<string, string[]>> onItemNameGroupsRetrieved, string game = null) { GetItemNameGroupsElement(game).GetAsync(delegate(JToken t) { onItemNameGroupsRetrieved((t != null) ? t.ToObject<Dictionary<string, string[]>>() : null); }); } public Dictionary<string, string[]> GetLocationNameGroups(string game = null) { return GetLocationNameGroupsElement(game).To<Dictionary<string, string[]>>(); } public void GetLocationNameGroupsAsync(Action<Dictionary<string, string[]>> onLocationNameGroupsRetrieved, string game = null) { GetLocationNameGroupsElement(game).GetAsync(delegate(JToken t) { onLocationNameGroupsRetrieved((t != null) ? t.ToObject<Dictionary<string, string[]>>() : null); }); } public ArchipelagoClientState GetClientStatus(int? slot = null, int? team = null) { return GetClientStatusElement(slot, team).To<ArchipelagoClientState?>().GetValueOrDefault(); } public void GetClientStatusAsync(Action<ArchipelagoClientState> onStatusRetrieved, int? slot = null, int? team = null) { GetClientStatusElement(slot, team).GetAsync(delegate(JToken t) { onStatusRetrieved(t.ToObject<ArchipelagoClientState?>().GetValueOrDefault()); }); } public void TrackClientStatus(Action<ArchipelagoClientState> onStatusUpdated, bool retrieveCurrentClientStatus = true, int? slot = null, int? team = null) { GetClientStatusElement(slot, team).OnValueChanged += delegate(JToken _, JToken newValue, Dictionary<string, JToken> x) { onStatusUpdated(newValue.ToObject<ArchipelagoClientState>()); }; if (retrieveCurrentClientStatus) { GetClientStatusAsync(onStatusUpdated, slot, team); } } } public interface IDataStorageWrapper { Hint[] GetHints(int? slot = null, int? team = null); void GetHintsAsync(Action<Hint[]> onHintsRetrieved, int? slot = null, int? team = null); void TrackHints(Action<Hint[]> onHintsUpdated, bool retrieveCurrentlyUnlockedHints = true, int? slot = null, int? team = null); Dictionary<string, object> GetSlotData(int? slot = null); T GetSlotData<T>(int? slot = null) where T : class; void GetSlotDataAsync(Action<Dictionary<string, object>> onSlotDataRetrieved, int? slot = null); void GetSlotDataAsync<T>(Action<T> onSlotDataRetrieved, int? slot = null) where T : class; Dictionary<string, string[]> GetItemNameGroups(string game = null); void GetItemNameGroupsAsync(Action<Dictionary<string, string[]>> onItemNameGroupsRetrieved, string game = null); Dictionary<string, string[]> GetLocationNameGroups(string game = null); void GetLocationNameGroupsAsync(Action<Dictionary<string, string[]>> onLocationNameGroupsRetrieved, string game = null); ArchipelagoClientState GetClientStatus(int? slot = null, int? team = null); void GetClientStatusAsync(Action<ArchipelagoClientState> onStatusRetrieved, int? slot = null, int? team = null); void TrackClientStatus(Action<ArchipelagoClientState> onStatusUpdated, bool retrieveCurrentClientStatus = true, int? slot = null, int? team = null); } public class ArchipelagoSocketHelperDelagates { public delegate void PacketReceivedHandler(ArchipelagoPacketBase packet); public delegate void PacketsSentHandler(ArchipelagoPacketBase[] packets); public delegate void ErrorReceivedHandler(Exception e, string message); public delegate void SocketClosedHandler(string reason); public delegate void SocketOpenedHandler(); } public interface IArchipelagoSocketHelper { Uri Uri { get; } bool Connected { get; } event ArchipelagoSocketHelperDelagates.PacketReceivedHandler PacketReceived; event ArchipelagoSocketHelperDelagates.PacketsSentHandler PacketsSent; event ArchipelagoSocketHelperDelagates.ErrorReceivedHandler ErrorReceived; event ArchipelagoSocketHelperDelagates.SocketClosedHandler SocketClosed; event ArchipelagoSocketHelperDelagates.SocketOpenedHandler SocketOpened; void SendPacket(ArchipelagoPacketBase packet); void SendMultiplePackets(List<ArchipelagoPacketBase> packets); void SendMultiplePackets(params ArchipelagoPacketBase[] packets); void Connect(); void Disconnect(); void SendPacketAsync(ArchipelagoPacketBase packet, Action<bool> onComplete = null); void SendMultiplePacketsAsync(List<ArchipelagoPacketBase> packets, Action<bool> onComplete = null); void SendMultiplePacketsAsync(Action<bool> onComplete = null, params ArchipelagoPacketBase[] packets); } public interface ILocationCheckHelper { ReadOnlyCollection<long> AllLocations { get; } ReadOnlyCollection<long> AllLocationsChecked { get; } ReadOnlyCollection<long> AllMissingLocations { get; } event LocationCheckHelper.CheckedLocationsUpdatedHandler CheckedLocationsUpdated; void CompleteLocationChecks(params long[] ids); void CompleteLocationChecksAsync(Action<bool> onComplete, params long[] ids); void ScoutLocationsAsync(Action<Dictionary<long, ScoutedItemInfo>> callback = null, HintCreationPolicy hintCreationPolicy = HintCreationPolicy.None, params long[] ids); void ScoutLocationsAsync(Action<Dictionary<long, ScoutedItemInfo>> callback = null, bool createAsHint = false, params long[] ids); void ScoutLocationsAsync(Action<Dictionary<long, ScoutedItemInfo>> callback = null, params long[] ids); long GetLocationIdFromName(string game, string locationName); string GetLocationNameFromId(long locationId, string game = null); } public class LocationCheckHelper : ILocationCheckHelper { public delegate void CheckedLocationsUpdatedHandler(ReadOnlyCollection<long> newCheckedLocations); private readonly IConcurrentHashSet<long> allLocations = new ConcurrentHashSet<long>(); private readonly IConcurrentHashSet<long> locationsChecked = new ConcurrentHashSet<long>(); private readonly IConcurrentHashSet<long> serverConfirmedChecks = new ConcurrentHashSet<long>(); private ReadOnlyCollection<long> missingLocations = new ReadOnlyCollection<long>(new long[0]); private readonly IArchipelagoSocketHelper socket; private readonly IItemInfoResolver itemInfoResolver; private readonly IConnectionInfoProvider connectionInfoProvider; private readonly IPlayerHelper players; private bool awaitingLocationInfoPacket; private Action<LocationInfoPacket> locationInfoPacketCallback; public ReadOnlyCollection<long> AllLocations => allLocations.AsToReadOnlyCollection(); public ReadOnlyCollection<long> AllLocationsChecked => locationsChecked.AsToReadOnlyCollection(); public ReadOnlyCollection<long> AllMissingLocations => missingLocations; public event CheckedLocationsUpdatedHandler CheckedLocationsUpdated; internal LocationCheckHelper(IArchipelagoSocketHelper socket, IItemInfoResolver itemInfoResolver, IConnectionInfoProvider connectionInfoProvider, IPlayerHelper players) { this.socket = socket; this.itemInfoResolver = itemInfoResolver; this.connectionInfoProvider = connectionInfoProvider; this.players = players; socket.PacketReceived += Socket_PacketReceived; } private void Socket_PacketReceived(ArchipelagoPacketBase packet) { if (!(packet is ConnectedPacket connectedPacket)) { if (!(packet is RoomUpdatePacket roomUpdatePacket)) { if (!(packet is LocationInfoPacket obj)) { if (packet is InvalidPacketPacket invalidPacketPacket && awaitingLocationInfoPacket && invalidPacketPacket.OriginalCmd == ArchipelagoPacketType.LocationScouts) { locationInfoPacketCallback(null); awaitingLocationInfoPacket = false; locationInfoPacketCallback = null; } } else if (awaitingLocationInfoPacket) { if (locationInfoPacketCallback != null) { locationInfoPacketCallback(obj); } awaitingLocationInfoPacket = false; locationInfoPacketCallback = null; } } else { CheckLocations(roomUpdatePacket.CheckedLocations); if (roomUpdatePacket.CheckedLocations != null) { serverConfirmedChecks.UnionWith(roomUpdatePacket.CheckedLocations); } } } else { allLocations.UnionWith(connectedPacket.LocationsChecked); allLocations.UnionWith(connectedPacket.MissingChecks); serverConfirmedChecks.UnionWith(connectedPacket.LocationsChecked); missingLocations = new ReadOnlyCollection<long>(connectedPacket.MissingChecks); CheckLocations(connectedPacket.LocationsChecked); } } public void CompleteLocationChecks(params long[] ids) { CheckLocations(ids); LocationChecksPacket locationChecksPacket = GetLocationChecksPacket(); if (locationChecksPacket.Locations.Any()) { socket.SendPacket(locationChecksPacket); } } public void CompleteLocationChecksAsync(Action<bool> onComplete, params long[] ids) { CheckLocations(ids); if (GetLocationChecksPacket().Locations.Any()) { socket.SendPacketAsync(GetLocationChecksPacket(), onComplete); } } private LocationChecksPacket GetLocationChecksPacket() { return new LocationChecksPacket { Locations = locationsChecked.AsToReadOnlyCollectionExcept(serverConfirmedChecks).ToArray() }; } public void ScoutLocationsAsync(Action<Dictionary<long, ScoutedItemInfo>> callback = null, HintCreationPolicy hintCreationPolicy = HintCreationPolicy.None, params long[] ids) { long[] array = ids.Where((long i) => allLocations.Contains(i)).ToArray(); if (!array.Any()) { callback?.Invoke(new Dictionary<long, ScoutedItemInfo>()); return; } socket.SendPacketAsync(new LocationScoutsPacket { Locations = array, CreateAsHint = (int)hintCreationPolicy }); awaitingLocationInfoPacket = true; locationInfoPacketCallback = delegate(LocationInfoPacket scoutResult) { Dictionary<long, ScoutedItemInfo> obj = scoutResult.Locations.ToDictionary((NetworkItem item) => item.Location, delegate(NetworkItem item) { PlayerInfo playerInfo = players.GetPlayerInfo(item.Player) ?? new PlayerInfo(); return new ScoutedItemInfo(item, playerInfo.Game, connectionInfoProvider.Game, itemInfoResolver, players, playerInfo); }); callback?.Invoke(obj); }; } public void ScoutLocationsAsync(Action<Dictionary<long, ScoutedItemInfo>> callback = null, bool createAsHint = false, params long[] ids) { ScoutLocationsAsync(callback, createAsHint ? HintCreationPolicy.CreateAndAnnounce : HintCreationPolicy.None, ids); } public void ScoutLocationsAsync(Action<Dictionary<long, ScoutedItemInfo>> callback = null, params long[] ids) { ScoutLocationsAsync(callback, createAsHint: false, ids); } public long GetLocationIdFromName(string game, string locationName) { return itemInfoResolver.Ge
GungeonArchipelago.dll
Decompiled 3 months agousing System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using Alexandria.ItemAPI; 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 Archipelago.MultiClient.Net.Packets; using BepInEx; using Microsoft.CodeAnalysis; using Newtonsoft.Json.Linq; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Mod")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Mod")] [assembly: AssemblyCopyright("Copyright © 2020")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("d6d7a494-722e-4763-959b-c2d6b6a42b01")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace GungeonArchipelago { [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("kintheinfinite.etg.archipelago", "Archipelago", "1.0.0")] public class Plugin : BaseUnityPlugin { [CompilerGenerated] private static class <>O { public static Action<Chest, PlayerController> <0>__OnChestOpen; public static Action<PlayerController, HealthHaver> <1>__OnEnemyKill; public static PacketReceivedHandler <2>__OnPacketReceived; public static MessageReceivedHandler <3>__OnMessageReceived; public static ItemReceivedHandler <4>__OnItemReceived; } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Action<string[]> <>9__12_0; public static Action<string[]> <>9__12_1; public static Action<string[]> <>9__12_2; public static DataStorageUpdatedHandler <>9__18_0; internal void <GMStart>b__12_0(string[] args) { ArchipelagoConnect(args[0], args[1], args[2]); } internal void <GMStart>b__12_1(string[] args) { ArchipelagoItems(); } internal void <GMStart>b__12_2(string[] args) { ArchipelagoChat(args[0]); } internal void <ArchipelagoConnect>b__18_0(JToken old_value, JToken new_value, Dictionary<string, JToken> _) { for (int i = 0; i < new_value.ToObject<int>(); i++) { session.Locations.CompleteLocationChecks(new long[1] { chest_starting_id + i }); } } } public const string GUID = "kintheinfinite.etg.archipelago"; public const string NAME = "Archipelago"; public const string VERSION = "1.0.0"; public const string TEXT_COLOR = "#FFFFFF"; public static ArchipelagoSession session; private static int chest_starting_id = 8755000; public static Dictionary<string, int> slot_data = new Dictionary<string, int>(); public static List<long> items_received = new List<long>(); public static Random random = new Random(); public static PassiveItem archipelago_pickup; public static bool allow_traps = false; public void Start() { ETGModMainBehaviour.WaitForGameManagerStart((Action<GameManager>)GMStart); } public void GMStart(GameManager g) { //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Expected O, but got Unknown //IL_0173: Unknown result type (might be due to invalid IL or missing references) ETGModConsole.CommandDescriptions.Add("archipelago connect", "Input the ip, port, and player name seperated by spaces"); ETGModConsole.CommandDescriptions.Add("archipelago items", "Use when you are starting a new run but are already connected to spawn your items in"); ETGModConsole.CommandDescriptions.Add("archipelago chat", "Sends a chat message to your connected server"); ETGModConsole.Commands.AddGroup("archipelago"); ETGModConsole.Commands.GetGroup("archipelago").AddGroup("connect", (Action<string[]>)delegate(string[] args) { ArchipelagoConnect(args[0], args[1], args[2]); }); ETGModConsole.Commands.GetGroup("archipelago").AddGroup("items", (Action<string[]>)delegate { ArchipelagoItems(); }); ETGModConsole.Commands.GetGroup("archipelago").AddGroup("chat", (Action<string[]>)delegate(string[] args) { ArchipelagoChat(args[0]); }); Chest.OnPostOpen = (Action<Chest, PlayerController>)Delegate.Combine(Chest.OnPostOpen, new Action<Chest, PlayerController>(OnChestOpen)); string text = "Archipelago Item"; string text2 = "GungeonArchipelago/Resources/archipelago_sprite"; GameObject val = new GameObject(text); archipelago_pickup = val.AddComponent<PassiveItem>(); ItemBuilder.AddSpriteToObject(text, text2, val, (Assembly)null); ItemBuilder.SetupItem((PickupObject)(object)archipelago_pickup, "An archipelago item.", "An archipelago item\n\nA randomized item received from the archipelago server", "Archipelago"); ((PickupObject)archipelago_pickup).quality = (ItemQuality)(-100); Log("Archipelago v1.0.0 started successfully."); } public void Update() { //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Unknown result type (might be due to invalid IL or missing references) //IL_0383: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Unknown result type (might be due to invalid IL or missing references) //IL_03cd: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Unknown result type (might be due to invalid IL or missing references) //IL_03d7: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Unknown result type (might be due to invalid IL or missing references) //IL_041c: Unknown result type (might be due to invalid IL or missing references) //IL_0421: Unknown result type (might be due to invalid IL or missing references) PlayerController player = GameManager.Instance.m_player; if ((Object)(object)player != (Object)null) { player.OnKilledEnemyContext -= OnEnemyKill; player.OnKilledEnemyContext += OnEnemyKill; } if (items_received.Count == 0) { return; } long num = items_received[0]; items_received.RemoveAt(0); long num2 = num; long num3 = num2; long num4 = num3 - 8754000; if ((ulong)num4 <= 12uL) { switch (num4) { case 0L: LootEngine.SpawnItem(((Component)PickupObjectDatabase.GetRandomGunOfQualities(random, new List<int>(), (ItemQuality[])(object)new ItemQuality[1] { (ItemQuality)1 })).gameObject, Vector2.op_Implicit(((GameActor)GameManager.Instance.PrimaryPlayer).CenterPosition), Vector2.down, 0f, true, false, false); return; case 1L: LootEngine.SpawnItem(((Component)PickupObjectDatabase.GetRandomGunOfQualities(random, new List<int>(), (ItemQuality[])(object)new ItemQuality[1] { (ItemQuality)2 })).gameObject, Vector2.op_Implicit(((GameActor)GameManager.Instance.PrimaryPlayer).CenterPosition), Vector2.down, 0f, true, false, false); return; case 2L: LootEngine.SpawnItem(((Component)PickupObjectDatabase.GetRandomGunOfQualities(random, new List<int>(), (ItemQuality[])(object)new ItemQuality[1] { (ItemQuality)3 })).gameObject, Vector2.op_Implicit(((GameActor)GameManager.Instance.PrimaryPlayer).CenterPosition), Vector2.down, 0f, true, false, false); return; case 3L: LootEngine.SpawnItem(((Component)PickupObjectDatabase.GetRandomGunOfQualities(random, new List<int>(), (ItemQuality[])(object)new ItemQuality[1] { (ItemQuality)4 })).gameObject, Vector2.op_Implicit(((GameActor)GameManager.Instance.PrimaryPlayer).CenterPosition), Vector2.down, 0f, true, false, false); return; case 4L: LootEngine.SpawnItem(((Component)PickupObjectDatabase.GetRandomGunOfQualities(random, new List<int>(), (ItemQuality[])(object)new ItemQuality[1] { (ItemQuality)5 })).gameObject, Vector2.op_Implicit(((GameActor)GameManager.Instance.PrimaryPlayer).CenterPosition), Vector2.down, 0f, true, false, false); return; case 5L: LootEngine.SpawnItem(((Component)PickupObjectDatabase.GetRandomPassiveOfQualities(random, new List<int>(), (ItemQuality[])(object)new ItemQuality[1] { (ItemQuality)1 })).gameObject, Vector2.op_Implicit(((GameActor)GameManager.Instance.PrimaryPlayer).CenterPosition), Vector2.down, 0f, true, false, false); return; case 6L: LootEngine.SpawnItem(((Component)PickupObjectDatabase.GetRandomPassiveOfQualities(random, new List<int>(), (ItemQuality[])(object)new ItemQuality[1] { (ItemQuality)2 })).gameObject, Vector2.op_Implicit(((GameActor)GameManager.Instance.PrimaryPlayer).CenterPosition), Vector2.down, 0f, true, false, false); return; case 7L: LootEngine.SpawnItem(((Component)PickupObjectDatabase.GetRandomPassiveOfQualities(random, new List<int>(), (ItemQuality[])(object)new ItemQuality[1] { (ItemQuality)3 })).gameObject, Vector2.op_Implicit(((GameActor)GameManager.Instance.PrimaryPlayer).CenterPosition), Vector2.down, 0f, true, false, false); return; case 8L: LootEngine.SpawnItem(((Component)PickupObjectDatabase.GetRandomPassiveOfQualities(random, new List<int>(), (ItemQuality[])(object)new ItemQuality[1] { (ItemQuality)4 })).gameObject, Vector2.op_Implicit(((GameActor)GameManager.Instance.PrimaryPlayer).CenterPosition), Vector2.down, 0f, true, false, false); return; case 9L: LootEngine.SpawnItem(((Component)PickupObjectDatabase.GetRandomPassiveOfQualities(random, new List<int>(), (ItemQuality[])(object)new ItemQuality[1] { (ItemQuality)5 })).gameObject, Vector2.op_Implicit(((GameActor)GameManager.Instance.PrimaryPlayer).CenterPosition), Vector2.down, 0f, true, false, false); return; case 10L: ETGModConsole.SpawnItem(new string[2] { "gnawed_key", "1" }); return; case 11L: ETGModConsole.SpawnItem(new string[2] { "old_crest", "1" }); return; case 12L: ETGModConsole.SpawnItem(new string[2] { "weird_egg", "1" }); return; } } long num5 = num3 - 8754100; if ((ulong)num5 <= 6uL) { switch (num5) { case 0L: ETGModConsole.Spawn(new string[2] { "chance_kin", "10" }); return; case 1L: ETGModConsole.SpawnItem(new string[2] { "50_casing", "1" }); return; case 2L: ETGModConsole.SpawnItem(new string[2] { "key", "1" }); return; case 3L: ETGModConsole.SpawnItem(new string[2] { "blank", "1" }); return; case 4L: ETGModConsole.SpawnItem(new string[2] { "armor", "1" }); return; case 5L: ETGModConsole.SpawnItem(new string[2] { "heart", "1" }); return; case 6L: ETGModConsole.SpawnItem(new string[2] { "ammo", "1" }); return; } } long num6 = num3 - 8754200; if ((ulong)num6 <= 7uL) { switch (num6) { case 0L: ETGModConsole.Spawn(new string[2] { "rat", "100" }); break; case 1L: ETGModConsole.Spawn(new string[2] { "shelleton", "3" }); break; case 2L: ETGModConsole.Spawn(new string[2] { "shotgrub", "3" }); break; case 3L: ETGModConsole.Spawn(new string[2] { "tanker", "12" }); ETGModConsole.Spawn(new string[2] { "professional", "2" }); break; case 4L: ETGModConsole.Spawn(new string[2] { "hollowpoint", "6" }); ETGModConsole.Spawn(new string[2] { "bombshee", "2" }); ETGModConsole.Spawn(new string[2] { "gunreaper", "1" }); break; case 5L: ETGModConsole.Spawn(new string[2] { "gun_nut", "1" }); ETGModConsole.Spawn(new string[2] { "chain_gunner", "2" }); ETGModConsole.Spawn(new string[2] { "spectral_gun_nut", "3" }); break; case 6L: ETGModConsole.Spawn(new string[2] { "jamerlengo", "3" }); ETGModConsole.Spawn(new string[2] { "spirat", "15" }); break; case 7L: GameManager.Instance.Dungeon.SpawnCurseReaper(); break; } } } public static void OnEnemyKill(PlayerController player, HealthHaver enemy) { if (session != null && session.Socket.Connected) { if (((Object)enemy).name.Equals("Blobulord(Clone)")) { session.DataStorage[(Scope)3, "Blobulord Killed"] = DataStorageElement.op_Implicit(1); CheckCompletion(); } if (((Object)enemy).name.Equals("OldBulletKing(Clone)")) { session.DataStorage[(Scope)3, "Old King Killed"] = DataStorageElement.op_Implicit(1); CheckCompletion(); } if (((Object)enemy).name.Equals("MetalGearRat(Clone)")) { session.DataStorage[(Scope)3, "Resourceful Rat Killed"] = DataStorageElement.op_Implicit(1); CheckCompletion(); } if (((Object)enemy).name.Equals("Helicopter(Clone)")) { session.DataStorage[(Scope)3, "Agunim Killed"] = DataStorageElement.op_Implicit(1); CheckCompletion(); } if (((Object)enemy).name.Equals("AdvancedDraGun(Clone)")) { session.DataStorage[(Scope)3, "Dragun Killed"] = DataStorageElement.op_Implicit(1); session.DataStorage[(Scope)3, "Advanced Dragun Killed"] = DataStorageElement.op_Implicit(1); CheckCompletion(); } if (((Object)enemy).name.Equals("DraGun(Clone)")) { session.DataStorage[(Scope)3, "Dragun Killed"] = DataStorageElement.op_Implicit(1); CheckCompletion(); } if (((Object)enemy).name.Equals("Infinilich(Clone)")) { session.DataStorage[(Scope)3, "Lich Killed"] = DataStorageElement.op_Implicit(1); CheckCompletion(); } } } public static void CheckCompletion() { //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Expected O, but got Unknown if ((slot_data["Blobulord Goal"] != 1 || DataStorageElement.op_Implicit(session.DataStorage[(Scope)3, "Blobulord Killed"])) && (slot_data["Old King Goal"] != 1 || DataStorageElement.op_Implicit(session.DataStorage[(Scope)3, "Old King Killed"])) && (slot_data["Resourceful Rat Goal"] != 1 || DataStorageElement.op_Implicit(session.DataStorage[(Scope)3, "Resourceful Rat Killed"])) && (slot_data["Agunim Goal"] != 1 || DataStorageElement.op_Implicit(session.DataStorage[(Scope)3, "Agunim Killed"])) && (slot_data["Advanced Dragun Goal"] != 1 || DataStorageElement.op_Implicit(session.DataStorage[(Scope)3, "Advanced Dragun Killed"])) && (slot_data["Goal"] != 0 || DataStorageElement.op_Implicit(session.DataStorage[(Scope)3, "Dragun Killed"])) && (slot_data["Goal"] != 1 || DataStorageElement.op_Implicit(session.DataStorage[(Scope)3, "Lich Killed"]))) { StatusUpdatePacket val = new StatusUpdatePacket(); val.Status = (ArchipelagoClientState)30; session.Socket.SendPacket((ArchipelagoPacketBase)(object)val); } } public static void Log(string text, string color = "#FFFFFF") { ETGModConsole.Log((object)("<color=" + color + ">" + text + "</color>"), false); } public static void OnChestOpen(Chest chest, PlayerController controller) { if (session != null && session.Socket.Connected) { chest.contents.Clear(); chest.ExplodeInSadness(); IDataStorageHelper dataStorage = session.DataStorage; dataStorage[(Scope)3, "ChestsOpened"] = dataStorage[(Scope)3, "ChestsOpened"] + 1; } } public static void ArchipelagoConnect(string ip, string port, string name) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Expected O, but got Unknown //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Expected O, but got Unknown //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Expected O, but got Unknown //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_03a6: Unknown result type (might be due to invalid IL or missing references) //IL_03ab: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Expected O, but got Unknown if (session != null && session.Socket.Connected) { Log("You are already connected!"); return; } Log("Connecting To: " + ip + ":" + port + " as " + name); allow_traps = false; session = ArchipelagoSessionFactory.CreateSession(ip, int.Parse(port)); IArchipelagoSocketHelper socket = session.Socket; object obj = <>O.<2>__OnPacketReceived; if (obj == null) { PacketReceivedHandler val = OnPacketReceived; <>O.<2>__OnPacketReceived = val; obj = (object)val; } socket.PacketReceived += (PacketReceivedHandler)obj; IMessageLogHelper messageLog = session.MessageLog; object obj2 = <>O.<3>__OnMessageReceived; if (obj2 == null) { MessageReceivedHandler val2 = OnMessageReceived; <>O.<3>__OnMessageReceived = val2; obj2 = (object)val2; } messageLog.OnMessageReceived += (MessageReceivedHandler)obj2; IReceivedItemsHelper items = session.Items; object obj3 = <>O.<4>__OnItemReceived; if (obj3 == null) { ItemReceivedHandler val3 = OnItemReceived; <>O.<4>__OnItemReceived = val3; obj3 = (object)val3; } items.ItemReceived += (ItemReceivedHandler)obj3; LoginResult val4 = session.TryConnectAndLogin("Enter The Gungeon", name, (ItemsHandlingFlags)3, new Version(1, 0, 0), (string[])null, (string)null, (string)null, true); if (!val4.Successful) { LoginFailure val5 = (LoginFailure)val4; string text = "Failed to Connect to " + ip + ":" + port + " as " + name; string[] errors = val5.Errors; foreach (string text2 in errors) { text = text + "\n " + text2; } ConnectionRefusedError[] errorCodes = val5.ErrorCodes; foreach (ConnectionRefusedError val6 in errorCodes) { text += $"\n {val6}"; } Log(text); return; } LoginSuccessful val7 = (LoginSuccessful)val4; foreach (string key in val7.SlotData.Keys) { int value = int.Parse(val7.SlotData[key].ToString()); slot_data[key] = value; } Log("Connected to Archipelago server."); allow_traps = true; session.DataStorage[(Scope)3, "ChestsOpened"].Initialize(JToken.op_Implicit(0)); session.DataStorage[(Scope)3, "Blobulord Killed"].Initialize(JToken.op_Implicit(0)); session.DataStorage[(Scope)3, "Old King Killed"].Initialize(JToken.op_Implicit(0)); session.DataStorage[(Scope)3, "Resourceful Rat Killed"].Initialize(JToken.op_Implicit(0)); session.DataStorage[(Scope)3, "Agunim Killed"].Initialize(JToken.op_Implicit(0)); session.DataStorage[(Scope)3, "Dragun Killed"].Initialize(JToken.op_Implicit(0)); session.DataStorage[(Scope)3, "Advanced Dragun Killed"].Initialize(JToken.op_Implicit(0)); session.DataStorage[(Scope)3, "Lich Killed"].Initialize(JToken.op_Implicit(0)); DataStorageElement obj4 = session.DataStorage[(Scope)3, "ChestsOpened"]; object obj5 = <>c.<>9__18_0; if (obj5 == null) { DataStorageUpdatedHandler val8 = delegate(JToken old_value, JToken new_value, Dictionary<string, JToken> _) { for (int k = 0; k < new_value.ToObject<int>(); k++) { session.Locations.CompleteLocationChecks(new long[1] { chest_starting_id + k }); } }; <>c.<>9__18_0 = val8; obj5 = (object)val8; } obj4.OnValueChanged += (DataStorageUpdatedHandler)obj5; } public static void ArchipelagoItems() { if (session == null || !session.Socket.Connected) { Log("You are not connected!"); return; } foreach (ItemInfo item in session.Items.AllItemsReceived) { if (item.ItemId < 8754200) { items_received.Add(item.ItemId); } } } public static void OnItemReceived(ReceivedItemsHelper helper) { ItemInfo val = helper.PeekItem(); if (allow_traps || (!allow_traps && val.ItemId < 8754200)) { items_received.Add(val.ItemId); } helper.DequeueItem(); } public static void ArchipelagoPickupNotification(string title, string text) { GameUIRoot.Instance.notificationController.DoCustomNotification(title, text, ((BraveBehaviour)archipelago_pickup).sprite.collection, ((BraveBehaviour)archipelago_pickup).sprite.spriteId, (NotificationColor)0, false, false); } public static void ArchipelagoChat(string message) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown session.Socket.SendPacketAsync((ArchipelagoPacketBase)new SayPacket { Text = message }, (Action<bool>)null); } public static void OnMessageReceived(LogMessage message) { Log(((object)message).ToString()); } public static void OnPacketReceived(ArchipelagoPacketBase packet) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) if (!(packet is ItemPrintJsonPacket)) { return; } ItemPrintJsonPacket val = (ItemPrintJsonPacket)packet; NetworkItem item; if (val.ReceivingPlayer != session.ConnectionInfo.Slot) { ReadOnlyCollection<long> allLocations = session.Locations.AllLocations; item = val.Item; if (!allLocations.Contains(((NetworkItem)(ref item)).Location)) { return; } } ReadOnlyCollection<PlayerInfo> readOnlyCollection = session.Players.Players[session.ConnectionInfo.Team]; item = val.Item; string game = readOnlyCollection[((NetworkItem)(ref item)).Player].Game; string game2 = session.Players.Players[session.ConnectionInfo.Team][val.ReceivingPlayer].Game; string playerName = session.Players.GetPlayerName(val.ReceivingPlayer); IReceivedItemsHelper items = session.Items; item = val.Item; string title = playerName + " got " + items.GetItemName(((NetworkItem)(ref item)).Item, game2); ILocationCheckHelper locations = session.Locations; item = val.Item; ArchipelagoPickupNotification(title, "from " + locations.GetLocationNameFromId(((NetworkItem)(ref item)).Location, game)); } } }
websocket-sharp.dll
Decompiled 3 months agousing System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Authentication; using System.Text; using System.Threading; using WebSocketSharp.Net; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace WebSocketSharp { internal class DllDirectory : IDisposable { private static object _lock = new object(); private bool _disposed = false; private static string _oldDllDirectory = null; private DllDirectory() { } [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern int GetDllDirectory(int nBufferLength, StringBuilder lpPathName); [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool SetDllDirectory(string lpPathName); private static void set(string newDllDirectory) { if (_oldDllDirectory != null) { throw new InvalidOperationException("Please reset dll directory before setting it again!"); } StringBuilder stringBuilder = new StringBuilder(10240); int dllDirectory = GetDllDirectory(10240, stringBuilder); if (dllDirectory > 10240) { throw new Exception("Could not SetDllDirectory"); } string oldDllDirectory = stringBuilder.ToString(0, dllDirectory); SetDllDirectory(newDllDirectory); _oldDllDirectory = oldDllDirectory; } private static void reset() { if (_oldDllDirectory != null) { SetDllDirectory(_oldDllDirectory); _oldDllDirectory = null; } } internal static void Set(string newDllDirectory) { lock (_lock) { set(newDllDirectory); } } internal static void Reset() { lock (_lock) { reset(); } } internal static DllDirectory Context(string newDllDirectory) { Monitor.Enter(_lock); try { set(newDllDirectory); return new DllDirectory(); } catch (Exception ex) { Monitor.Exit(_lock); throw ex; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed) { try { _disposed = true; reset(); } finally { Monitor.Exit(_lock); } } } ~DllDirectory() { Dispose(disposing: false); } } public class MessageEventArgs : EventArgs { private byte[] _data; private OpCode _opcode; private string _str; public bool IsBinary => _opcode == OpCode.Binary; public bool IsPing => _opcode == OpCode.Ping; public bool IsText => _opcode == OpCode.Text; public byte[] RawData => _data; public string Data { get { if (_str == null) { _str = Encoding.UTF8.GetString(_data); } return _str; } } internal MessageEventArgs(byte[] data, OpCode opcode) { _data = data; _opcode = opcode; _str = null; } } public class CloseEventArgs : EventArgs { private ushort _code; private string _reason; public ushort Code => _code; public string Reason => _reason; public bool WasClean => _code >= 1000 && _code != 1005; internal CloseEventArgs(ushort code, string reason) { _code = code; _reason = reason; } } public class ErrorEventArgs : EventArgs { private string _message; private Exception _exception; public string Message => _message; public Exception Exception => _exception; internal ErrorEventArgs(string message, Exception exception) { _message = message; _exception = exception; } } public static class Ext { public static bool IsNullOrEmpty(this string value) { return value == null || value.Length == 0; } } } namespace WebSocketSharp.Net { public class ClientSslConfiguration { public SslProtocols EnabledSslProtocols { get { return SslProtocols.None; } set { } } } } namespace WebSocketSharp { internal enum OpCode { Text = 1, Binary = 2, Ping = 8, Pong = 10 } public class WebSocket : IDisposable { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void OnMessageCallback(IntPtr data, ulong len, int opCode); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void OnOpenCallback(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void OnCloseCallback(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void OnErrorCallback(IntPtr msg); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void OnPongCallback(IntPtr data, ulong len); private Uri uri; private WebSocketWorker worker = null; private WebSocketEventDispatcher dispatcher = null; private object dispatcherLock = new object(); private List<byte[]> pings = new List<byte[]>(); private DateTime lastPong; private volatile WebSocketState readyState = WebSocketState.New; private string lastError; private int _id; private static object _lastIdLock = new object(); private static int _lastId = 0; private UIntPtr ws; private OnMessageCallback messageHandler; private OnOpenCallback openHandler; private OnCloseCallback closeHandler; private OnErrorCallback errorHandler; private OnPongCallback pongHandler; internal const CallingConvention CALLING_CONVENTION = CallingConvention.Cdecl; internal const string DLL_NAME = "c-wspp.dll"; public WebSocketState ReadyState => readyState; public bool IsAlive { get { if (readyState != WebSocketState.Open) { return false; } if (DateTime.UtcNow - lastPong < new TimeSpan(0, 0, 0, 0, 300)) { return true; } Random random = new Random(); byte[] array = new byte[16]; random.NextBytes(array); try { pingBlocking(array); return true; } catch (InvalidOperationException ex) { Console.WriteLine(ex.ToString()); return true; } catch (TimeoutException) { } catch (Exception ex3) { Console.WriteLine(ex3.ToString()); } return false; } } public bool IsSecure => uri.Scheme.ToLower() == "wss"; public Uri Url => uri; public ClientSslConfiguration SslConfiguration => new ClientSslConfiguration(); internal static string directory => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); public event EventHandler OnOpen; public event EventHandler<CloseEventArgs> OnClose; public event EventHandler<ErrorEventArgs> OnError; public event EventHandler<MessageEventArgs> OnMessage; public WebSocket(string uriString) { lock (_lastIdLock) { _id = _lastId + 1; _lastId = _id; } debug("new (\"" + uriString + "\")"); uri = new Uri(uriString); ws = wspp_new_from(uriString, directory); setHandlers(); } public WebSocket(string uriString, string[] protocols) { lock (_lastIdLock) { _id = _lastId + 1; _lastId = _id; } debug("new (\"" + uriString + "\", " + ((protocols != null) ? ("[" + string.Join(", ", protocols) + "]") : "null") + ")"); uri = new Uri(uriString); ws = wspp_new_from(uriString, directory); setHandlers(); } private void error(string message, Exception exception = null) { debug("Error: " + message); if (exception == null) { exception = new Exception(message); } ErrorEventArgs e = new ErrorEventArgs(message, exception); dispatcher.Enqueue(e); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { debug("disposing"); if (!(ws != UIntPtr.Zero)) { return; } clearHandlers(); UIntPtr uIntPtr = ws; ws = UIntPtr.Zero; debug("shutting down"); close(uIntPtr, 1001, "Going away"); try { if (worker != null) { worker.Dispose(); } } catch (Exception) { } worker = null; try { if (dispatcher != null) { dispatcher.Dispose(); } } catch (InvalidOperationException) { WebSocketEventDispatcher tmp = dispatcher; new Thread((ThreadStart)delegate { try { tmp.Dispose(); } catch (Exception) { } }).Start(); } catch (Exception) { } dispatcher = null; debug("wspp_delete"); wspp_delete(uIntPtr); openHandler = null; closeHandler = null; messageHandler = null; errorHandler = null; pongHandler = null; dispatcherLock = null; } ~WebSocket() { Dispose(disposing: false); } private void debug(string msg) { } private static void sdebug(string msg) { } private void warn(string msg) { Console.WriteLine("WARNING: WebSocket " + _id + ": " + msg); } private void pingBlocking(byte[] data, int timeout = 15000) { if (worker.IsCurrentThread) { throw new InvalidOperationException("Can't wait for reply from worker thread"); } lock (pings) { pings.Add(data); } Ping(data); for (int i = 0; i < timeout; i++) { Thread.Sleep(1); lock (pings) { bool flag = true; foreach (byte[] ping in pings) { if (ping == data) { flag = false; break; } } if (flag) { return; } } } lock (pings) { pings.Remove(data); } debug("pong timeout"); throw new TimeoutException(); } public void Connect() { lastError = ""; connect(); while (worker != null && worker.IsAlive && readyState != WebSocketState.Open) { Thread.Sleep(1); } if (readyState != WebSocketState.Open) { throw new Exception("Connect failed" + ((!(lastError == "")) ? (": " + lastError) : "")); } } public void ConnectAsync() { lastError = ""; connect(); if (readyState != WebSocketState.Open && readyState != WebSocketState.Connecting) { throw new Exception("Connect failed" + ((!(lastError == "")) ? (": " + lastError) : "")); } } private void connect() { if (readyState != WebSocketState.Closed && readyState != 0) { throw new InvalidOperationException("Invalid state: " + readyState); } if (readyState != 0) { Thread.Sleep(1); } debug("ReadyState = Connecting"); readyState = WebSocketState.Connecting; if (ws == UIntPtr.Zero) { throw new ObjectDisposedException(GetType().FullName); } if (worker != null && !worker.IsAlive) { worker = null; } lock (dispatcherLock) { if (dispatcher == null) { debug("creating dispatcher"); dispatcher = new WebSocketEventDispatcher(); dispatcher.OnOpen += dispatchOnOpen; dispatcher.OnClose += dispatchOnClose; dispatcher.OnError += dispatchOnError; dispatcher.OnMessage += dispatchOnMessage; dispatcher.Start(); } } debug("wspp_connect"); wspp_connect(ws); if (worker == null) { debug("creating worker"); worker = new WebSocketWorker(ws); worker.Start(); } else { debug("worker already running"); } } public void Close() { Close(1001); } public void Close(ushort code) { Close(code, ""); } public void Close(ushort code, string reason) { debug("Close(" + code + ", \"" + reason + "\")"); if (ws == UIntPtr.Zero) { throw new ObjectDisposedException(GetType().FullName); } debug("ReadyState = Closing"); readyState = WebSocketState.Closing; close(ws, code, reason); if (worker.IsCurrentThread) { throw new InvalidOperationException("Can't wait for reply from worker thread"); } while (worker != null && worker.IsAlive && readyState != WebSocketState.Closed) { Thread.Sleep(1); } } public void CloseAsync() { CloseAsync(1001); } public void CloseAsync(ushort code) { CloseAsync(code, ""); } public void CloseAsync(ushort code, string reason) { debug("CloseAsync(" + code + ", \"" + reason + "\")"); if (ws == UIntPtr.Zero) { throw new ObjectDisposedException(GetType().FullName); } debug("ReadyState = Closing"); readyState = WebSocketState.Closing; close(ws, code, reason); } public void Send(string message) { if (ws == UIntPtr.Zero) { throw new ObjectDisposedException(GetType().FullName); } IntPtr intPtr = StringToHGlobalUTF8(message); WsppRes wsppRes = (WsppRes)wspp_send_text(ws, intPtr); Marshal.FreeHGlobal(intPtr); if (wsppRes != 0) { throw new Exception(Enum.GetName(typeof(WsppRes), wsppRes) ?? "Unknown error"); } } public void Send(byte[] data) { if (ws == UIntPtr.Zero) { throw new ObjectDisposedException(GetType().FullName); } WsppRes wsppRes = (WsppRes)wspp_send_binary(ws, data, (ulong)data.Length); if (wsppRes != 0) { throw new Exception(Enum.GetName(typeof(WsppRes), wsppRes) ?? "Unknown error"); } } public void SendAsync(string message, Action<bool> onComplete = null) { Send(message); onComplete?.Invoke(obj: true); } public void SendAsync(byte[] data, Action<bool> onComplete = null) { Send(data); onComplete?.Invoke(obj: true); } public void Ping(byte[] data) { if (ws == UIntPtr.Zero) { throw new ObjectDisposedException(GetType().FullName); } WsppRes wsppRes = (WsppRes)wspp_ping(ws, data, (ulong)data.Length); if (wsppRes != 0) { throw new Exception(Enum.GetName(typeof(WsppRes), wsppRes) ?? "Unknown error"); } } private void dispatchOnOpen(object sender, EventArgs e) { if (this.OnOpen != null) { this.OnOpen(this, e); } } private void dispatchOnClose(object sender, CloseEventArgs e) { lock (dispatcherLock) { WebSocketEventDispatcher tmp = dispatcher; if (tmp != null) { dispatcher = null; new Thread((ThreadStart)delegate { tmp.Dispose(); }).Start(); } else { warn("duplicate close event"); } } if (this.OnError != null) { new Thread((ThreadStart)delegate { Thread.Sleep(1); this.OnClose(this, e); }).Start(); } } private void dispatchOnError(object sender, ErrorEventArgs e) { if (readyState == WebSocketState.Closed) { lock (dispatcherLock) { WebSocketEventDispatcher tmp = dispatcher; if (tmp != null) { dispatcher = null; new Thread((ThreadStart)delegate { tmp.Dispose(); }).Start(); } else { warn("duplicate close event"); } } } if (readyState == WebSocketState.Closed) { if (this.OnError != null) { new Thread((ThreadStart)delegate { Thread.Sleep(1); this.OnError(this, e); }).Start(); } } else if (this.OnError != null) { this.OnError(this, e); } } private void dispatchOnMessage(object sender, MessageEventArgs e) { if (this.OnMessage != null) { this.OnMessage(this, e); } } [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] internal static extern UIntPtr wspp_new(IntPtr uri); [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern void wspp_delete(UIntPtr ws); [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern ulong wspp_poll(UIntPtr ws); [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern ulong wspp_run(UIntPtr ws); [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern bool wspp_stopped(UIntPtr ws); [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern int wspp_connect(UIntPtr ws); [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern int wspp_close(UIntPtr ws, ushort code, IntPtr reason); [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern int wspp_send_text(UIntPtr ws, IntPtr message); [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern int wspp_send_binary(UIntPtr ws, byte[] data, ulong len); [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern int wspp_ping(UIntPtr ws, byte[] data, ulong len); [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern void wspp_set_open_handler(UIntPtr ws, OnOpenCallback f); [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern void wspp_set_close_handler(UIntPtr ws, OnCloseCallback f); [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern void wspp_set_message_handler(UIntPtr ws, OnMessageCallback f); [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern void wspp_set_error_handler(UIntPtr ws, OnErrorCallback f); [DllImport("c-wspp.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern void wspp_set_pong_handler(UIntPtr ws, OnPongCallback f); internal static IntPtr StringToHGlobalUTF8(string s, out int length) { if (s == null) { length = 0; return IntPtr.Zero; } byte[] bytes = Encoding.UTF8.GetBytes(s); IntPtr intPtr = Marshal.AllocHGlobal(bytes.Length + 1); Marshal.Copy(bytes, 0, intPtr, bytes.Length); Marshal.WriteByte(intPtr, bytes.Length, 0); length = bytes.Length; return intPtr; } internal static IntPtr StringToHGlobalUTF8(string s) { int length; return StringToHGlobalUTF8(s, out length); } internal bool sequenceEqual(byte[] a, byte[] b) { if (a.Length != b.Length) { return false; } for (int i = 0; i < a.Length; i++) { if (a[i] != b[i]) { return false; } } return true; } private void OpenHandler() { debug("on Open"); if (!(ws == UIntPtr.Zero)) { debug("ReadyState = Open"); readyState = WebSocketState.Open; EventArgs e = new EventArgs(); dispatcher.Enqueue(e); } } private void CloseHandler() { debug("on Close"); if (!(ws == UIntPtr.Zero)) { debug("ReadyState = Closed"); readyState = WebSocketState.Closed; CloseEventArgs e = new CloseEventArgs(0, ""); dispatcher.Enqueue(e); } } private void MessageHandler(IntPtr data, ulong len, int opCode) { debug("on Message"); if (!(ws == UIntPtr.Zero)) { if (len > int.MaxValue) { error("Received message that was too long"); return; } byte[] array = new byte[(uint)len]; Marshal.Copy(data, array, 0, (int)len); MessageEventArgs e = new MessageEventArgs(array, (OpCode)opCode); dispatcher.Enqueue(e); } } private void ErrorHandler(IntPtr msgPtr) { debug("on Error"); if (!(ws == UIntPtr.Zero)) { string text = "Unknown"; if (msgPtr != IntPtr.Zero) { text = Marshal.PtrToStringAnsi(msgPtr); } if (readyState == WebSocketState.Connecting) { debug("ReadyState = Closed"); readyState = WebSocketState.Closed; } else if (readyState == WebSocketState.Open) { Close(); } lastError = text; error("Connect error: " + text); } } private void PongHandler(IntPtr data, ulong len) { byte[] array = new byte[(uint)len]; Marshal.Copy(data, array, 0, (int)len); lock (pings) { foreach (byte[] ping in pings) { if (sequenceEqual(array, ping)) { pings.Remove(ping); lastPong = DateTime.UtcNow; return; } } } MessageEventArgs e = new MessageEventArgs(array, OpCode.Pong); dispatcher.Enqueue(e); } private static UIntPtr wspp_new(string uriString) { IntPtr hglobal = StringToHGlobalUTF8(uriString); try { return wspp_new(hglobal); } finally { Marshal.FreeHGlobal(hglobal); } } private static UIntPtr wspp_new_from(string uriString, string dllDirectory) { sdebug("wspp_new(\"" + uriString + "\") in c-wspp.dll from " + dllDirectory); using (DllDirectory.Context(dllDirectory)) { return wspp_new(uriString); } } private static void close(UIntPtr ws, ushort code, string reason) { sdebug("wspp_close(" + code + ", \"" + reason + "')"); IntPtr intPtr = StringToHGlobalUTF8(reason); wspp_close(ws, code, intPtr); Marshal.FreeHGlobal(intPtr); } private void setHandlers() { openHandler = OpenHandler; closeHandler = CloseHandler; messageHandler = MessageHandler; errorHandler = ErrorHandler; pongHandler = PongHandler; wspp_set_open_handler(ws, openHandler); wspp_set_close_handler(ws, closeHandler); wspp_set_message_handler(ws, messageHandler); wspp_set_error_handler(ws, errorHandler); wspp_set_pong_handler(ws, pongHandler); } private void clearHandlers() { wspp_set_open_handler(ws, null); wspp_set_close_handler(ws, null); wspp_set_message_handler(ws, null); wspp_set_error_handler(ws, null); wspp_set_pong_handler(ws, null); } } internal enum WsppRes { OK = 0, InvalidState = 1, Unknown = -1 } internal class WebSocketEventDispatcher : IDisposable { private Thread _thread; private bool _stop; private Queue<EventArgs> _queue; private int _id; private static object _lastIdLock = new object(); private static int _lastId = 0; public bool IsCurrentThread => Thread.CurrentThread == _thread; public bool IsAlive => _thread.IsAlive; public event EventHandler OnOpen; public event EventHandler<CloseEventArgs> OnClose; public event EventHandler<ErrorEventArgs> OnError; public event EventHandler<MessageEventArgs> OnMessage; public WebSocketEventDispatcher() { lock (_lastIdLock) { _id = _lastId + 1; _lastId = _id; } _thread = new Thread(work); _stop = false; _queue = new Queue<EventArgs>(); } public void Start() { _thread.Start(); } public void Join() { _thread.Join(); } private void debug(string msg) { } private void work() { debug("running"); while (!_stop) { EventArgs eventArgs; lock (_queue) { eventArgs = ((_queue.Count <= 0) ? null : _queue.Dequeue()); } if (eventArgs != null) { if (eventArgs is MessageEventArgs) { if (this.OnMessage != null) { this.OnMessage(this, (MessageEventArgs)eventArgs); } } else if (eventArgs is CloseEventArgs) { if (this.OnClose != null) { this.OnClose(this, (CloseEventArgs)eventArgs); } } else if (eventArgs is ErrorEventArgs) { if (this.OnError != null) { this.OnError(this, (ErrorEventArgs)eventArgs); } } else if (this.OnOpen != null) { this.OnOpen(this, eventArgs); } } else { Thread.Sleep(1); } } debug("stopped"); _queue = null; } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (IsCurrentThread) { throw new InvalidOperationException("Can't dispose self"); } debug("disposing"); if (!_stop) { _stop = true; _thread.Join(); debug("joined"); _queue = null; } } ~WebSocketEventDispatcher() { Dispose(disposing: false); } public void Enqueue(EventArgs e) { lock (_queue) { _queue.Enqueue(e); } } } public enum WebSocketState : ushort { New, Connecting, Open, Closing, Closed } internal class WebSocketWorker : IDisposable { private UIntPtr _ws; private Thread _thread; private bool _stop; private int _id; private static object _lastIdLock = new object(); private static int _lastId = 0; public bool IsCurrentThread => Thread.CurrentThread == _thread; public bool IsAlive => _thread.IsAlive; public WebSocketWorker(UIntPtr ws) { lock (_lastIdLock) { _id = _lastId + 1; _lastId = _id; } _ws = ws; _thread = new Thread(work); _stop = false; } public void Start() { _thread.Start(); } public void Join() { _thread.Join(); } private void debug(string msg) { } private void work() { while (!_stop && !WebSocket.wspp_stopped(_ws)) { WebSocket.wspp_poll(_ws); Thread.Sleep(1); } if (!WebSocket.wspp_stopped(_ws)) { debug("stopping"); } for (int i = 0; i < 1000; i++) { if (WebSocket.wspp_stopped(_ws)) { break; } WebSocket.wspp_poll(_ws); Thread.Sleep(1); } debug("stopped"); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { debug("disposing"); if (!_stop) { _stop = true; _thread.Join(); debug("joined"); } } ~WebSocketWorker() { Dispose(disposing: false); } } }