Decompiled source of Against The Storm for Archipelago v0.3.3
Archipelago.MultiClient.Net.dll
Decompiled 15 hours 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.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net.WebSockets; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Threading.Tasks; using Archipelago.MultiClient.Net.ConcurrentCollection; using Archipelago.MultiClient.Net.Converters; using Archipelago.MultiClient.Net.DataPackage; using Archipelago.MultiClient.Net.Enums; using Archipelago.MultiClient.Net.Exceptions; using Archipelago.MultiClient.Net.Extensions; using Archipelago.MultiClient.Net.Helpers; using Archipelago.MultiClient.Net.MessageLog.Messages; using Archipelago.MultiClient.Net.MessageLog.Parts; using Archipelago.MultiClient.Net.Models; using Archipelago.MultiClient.Net.Packets; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: ComVisible(false)] [assembly: Guid("35a803ad-85ed-42e9-b1e3-c6b72096f0c1")] [assembly: InternalsVisibleTo("Archipelago.MultiClient.Net.Tests")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Jarno Westhof, Hussein Farran, Zach Parks")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyDescription("A client library for use with .NET based prog-langs for interfacing with Archipelago hosts.")] [assembly: AssemblyFileVersion("6.1.1.0")] [assembly: AssemblyInformationalVersion("6.1.1")] [assembly: AssemblyProduct("Archipelago.MultiClient.Net")] [assembly: AssemblyTitle("Archipelago.MultiClient.Net")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/ArchipelagoMW/Archipelago.MultiClient.Net")] [assembly: AssemblyVersion("6.1.1.0")] internal interface IConcurrentHashSet<T> { bool TryAdd(T item); bool Contains(T item); void UnionWith(T[] otherSet); T[] ToArray(); ReadOnlyCollection<T> AsToReadOnlyCollection(); ReadOnlyCollection<T> AsToReadOnlyCollectionExcept(IConcurrentHashSet<T> otherSet); } 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; } Task<RoomInfoPacket> ConnectAsync(); Task<LoginResult> LoginAsync(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true); LoginResult TryConnectAndLogin(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true); } public class ArchipelagoSession : IArchipelagoSession, IArchipelagoSessionActions { private const int ArchipelagoConnectionTimeoutInSeconds = 4; private ConnectionInfoHelper connectionInfo; private TaskCompletionSource<LoginResult> loginResultTask = new TaskCompletionSource<LoginResult>(); private TaskCompletionSource<RoomInfoPacket> roomInfoPacketTask = new TaskCompletionSource<RoomInfoPacket>(); public IArchipelagoSocketHelper Socket { get; } public IReceivedItemsHelper Items { get; } public ILocationCheckHelper Locations { get; } public IPlayerHelper Players { get; } public IDataStorageHelper DataStorage { get; } public IConnectionInfoProvider ConnectionInfo => connectionInfo; public IRoomStateHelper RoomState { get; } public IMessageLogHelper MessageLog { get; } 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 result) { roomInfoPacketTask.TrySetResult(result); } return; } if (packet is ConnectedPacket && RoomState.Version != null && RoomState.Version >= new Version(0, 3, 8)) { LogUsedVersion(); } loginResultTask.TrySetResult(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 + ":NETSTANDARD2_0", true } }) } }); } catch { } } public Task<RoomInfoPacket> ConnectAsync() { roomInfoPacketTask = new TaskCompletionSource<RoomInfoPacket>(); Task.Factory.StartNew(delegate { try { Task task = Socket.ConnectAsync(); task.Wait(TimeSpan.FromSeconds(4.0)); if (!task.IsCompleted) { roomInfoPacketTask.TrySetCanceled(); } } catch (AggregateException) { roomInfoPacketTask.TrySetCanceled(); } }); return roomInfoPacketTask.Task; } public Task<LoginResult> LoginAsync(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true) { loginResultTask = new TaskCompletionSource<LoginResult>(); if (!roomInfoPacketTask.Task.IsCompleted) { loginResultTask = new TaskCompletionSource<LoginResult>(); loginResultTask.TrySetResult(new LoginFailure("You are not connected, run ConnectAsync() first")); return loginResultTask.Task; } connectionInfo.SetConnectionParameters(game, tags, itemsHandlingFlags, uuid); try { Socket.SendPacket(BuildConnectPacket(name, password, version, requestSlotData)); } catch (ArchipelagoSocketClosedException) { loginResultTask.TrySetResult(new LoginFailure("You are not connected, run ConnectAsync() first")); return loginResultTask.Task; } SetResultAfterTimeout(loginResultTask, 4, new LoginFailure("Connection timed out.")); return loginResultTask.Task; } private static void SetResultAfterTimeout<T>(TaskCompletionSource<T> task, int timeoutInSeconds, T result) { new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)).Token.Register(delegate { task.TrySetResult(result); }); } public LoginResult TryConnectAndLogin(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true) { Task<RoomInfoPacket> task = ConnectAsync(); try { task.Wait(TimeSpan.FromSeconds(4.0)); } catch (AggregateException ex) { if (ex.GetBaseException() is OperationCanceledException) { return new LoginFailure("Connection timed out."); } return new LoginFailure(ex.GetBaseException().Message); } if (!task.IsCompleted) { return new LoginFailure("Connection timed out."); } return LoginAsync(game, name, itemsHandlingFlags, version, tags, uuid, password, requestSlotData).Result; } private ConnectPacket BuildConnectPacket(string name, string password, Version version, bool requestSlotData) { return new ConnectPacket { Game = ConnectionInfo.Game, Name = name, Password = password, Tags = ConnectionInfo.Tags, Uuid = ConnectionInfo.Uuid, Version = ((version != null) ? new NetworkVersion(version) : new NetworkVersion(0, 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 static DataStorageElement operator +(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Add, JToken.Parse(b.ToString())); } public static DataStorageElement operator *(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Mul, JToken.Parse(b.ToString())); } public static DataStorageElement operator %(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Mod, JToken.Parse(b.ToString())); } public static DataStorageElement operator ^(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Pow, JToken.Parse(b.ToString())); } public static DataStorageElement operator -(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Add, JToken.Parse((-b).ToString())); } public static DataStorageElement operator /(DataStorageElement a, BigInteger b) { throw new InvalidOperationException("DataStorage[Key] / BigInterger is not supported, due to loss of precision when using integer division"); } public static implicit operator DataStorageElement(BigInteger bi) { return new DataStorageElement(OperationType.Replace, JToken.Parse(bi.ToString())); } public static implicit operator BigInteger(DataStorageElement e) { return RetrieveAndReturnBigIntegerValue<BigInteger>(e); } public static implicit operator BigInteger?(DataStorageElement e) { return RetrieveAndReturnBigIntegerValue<BigInteger?>(e); } private static T RetrieveAndReturnBigIntegerValue<T>(DataStorageElement e) { if (e.cachedValue != null) { if (!BigInteger.TryParse(((object)e.cachedValue).ToString(), out var result)) { return default(T); } return (T)Convert.ChangeType(result, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T)); } BigInteger result2; BigInteger? bigInteger = (BigInteger.TryParse(((object)e.Context.GetData(e.Context.Key)).ToString(), out result2) ? new BigInteger?(result2) : null); if (!bigInteger.HasValue && !IsNullable<T>()) { bigInteger = Activator.CreateInstance<BigInteger>(); } foreach (OperationSpecification operation in e.Operations) { if (operation.OperationType == OperationType.Floor || operation.OperationType == OperationType.Ceil) { continue; } if (!BigInteger.TryParse(((object)operation.Value).ToString(), NumberStyles.AllowLeadingSign, null, out var result3)) { throw new InvalidOperationException($"DataStorage[Key] cannot be converted to BigInterger as its value its not an integer number, value: {operation.Value}"); } switch (operation.OperationType) { case OperationType.Replace: bigInteger = result3; break; case OperationType.Add: bigInteger += result3; break; case OperationType.Mul: bigInteger *= result3; break; case OperationType.Mod: bigInteger %= result3; break; case OperationType.Pow: bigInteger = BigInteger.Pow(bigInteger.Value, (int)operation.Value); break; case OperationType.Max: { BigInteger value = result3; BigInteger? bigInteger2 = bigInteger; if (value > bigInteger2) { bigInteger = result3; } break; } case OperationType.Min: { BigInteger value = result3; BigInteger? bigInteger2 = bigInteger; if (value < bigInteger2) { bigInteger = result3; } break; } case OperationType.Xor: bigInteger ^= result3; break; case OperationType.Or: bigInteger |= result3; break; case OperationType.And: bigInteger &= result3; break; case OperationType.LeftShift: bigInteger <<= (int)operation.Value; break; case OperationType.RightShift: bigInteger >>= (int)operation.Value; break; } } e.cachedValue = JToken.Parse(bigInteger.ToString()); if (!bigInteger.HasValue) { return default(T); } return (T)Convert.ChangeType(bigInteger.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T)); } public void Initialize(JToken value) { Context.Initialize(Context.Key, value); } public void Initialize(IEnumerable value) { Context.Initialize(Context.Key, (JToken)(object)JArray.FromObject((object)value)); } public Task<T> GetAsync<T>() { return GetAsync().ContinueWith((Task<JToken> r) => r.Result.ToObject<T>()); } public Task<JToken> GetAsync() { return Context.GetAsync(Context.Key); } private static T RetrieveAndReturnArrayValue<T>(DataStorageElement e) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_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(Enumerable.Repeat(text, (int)operation.Value)); break; case OperationType.Replace: text = (string)operation.Value; break; default: throw new InvalidOperationException($"Cannot perform operation {operation.OperationType} on string value"); } } if (text == null) { e.cachedValue = (JToken)(object)JValue.CreateNull(); } else { e.cachedValue = JToken.op_Implicit(text); } return (string)e.cachedValue; } private static T RetrieveAndReturnBoolValue<T>(DataStorageElement e) { if (e.cachedValue != null) { return e.cachedValue.ToObject<T>(); } bool? flag = e.Context.GetData(e.Context.Key).ToObject<bool?>() ?? ((bool?)Activator.CreateInstance(typeof(T))); foreach (OperationSpecification operation in e.Operations) { if (operation.OperationType == OperationType.Replace) { flag = (bool?)operation.Value; continue; } throw new InvalidOperationException($"Cannot perform operation {operation.OperationType} on boolean value"); } e.cachedValue = JToken.op_Implicit(flag); if (!flag.HasValue) { return default(T); } return (T)Convert.ChangeType(flag.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T)); } private static T RetrieveAndReturnDecimalValue<T>(DataStorageElement e) { if (e.cachedValue != null) { return e.cachedValue.ToObject<T>(); } decimal? num = e.Context.GetData(e.Context.Key).ToObject<decimal?>(); if (!num.HasValue && !IsNullable<T>()) { num = Activator.CreateInstance<decimal>(); } foreach (OperationSpecification operation in e.Operations) { switch (operation.OperationType) { case OperationType.Replace: num = (decimal)operation.Value; break; case OperationType.Add: num += (decimal?)(decimal)operation.Value; break; case OperationType.Mul: num *= (decimal?)(decimal)operation.Value; break; case OperationType.Mod: num %= (decimal?)(decimal)operation.Value; break; case OperationType.Pow: num = (decimal)Math.Pow((double)num.Value, (double)operation.Value); break; case OperationType.Max: num = Math.Max(num.Value, (decimal)operation.Value); break; case OperationType.Min: num = Math.Min(num.Value, (decimal)operation.Value); break; case OperationType.Xor: num = (long)num.Value ^ (long)operation.Value; break; case OperationType.Or: num = (long)num.Value | (long)operation.Value; break; case OperationType.And: num = (long)num.Value & (long)operation.Value; break; case OperationType.LeftShift: num = (long)num.Value << (int)operation.Value; break; case OperationType.RightShift: num = (long)num.Value >> (int)operation.Value; break; case OperationType.Floor: num = Math.Floor(num.Value); break; case OperationType.Ceil: num = Math.Ceiling(num.Value); break; } } e.cachedValue = JToken.op_Implicit(num); if (!num.HasValue) { return default(T); } return (T)Convert.ChangeType(num.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T)); } private static bool IsNullable<T>() { if (typeof(T).IsGenericType) { return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition(); } return false; } public T To<T>() { if (Operations.Count != 0) { throw new InvalidOperationException("DataStorageElement.To<T>() cannot be used together with other operations on the DataStorageElement"); } return Context.GetData(Context.Key).ToObject<T>(); } public override string ToString() { return (Context?.ToString() ?? "(null)") + ", (" + ListOperations() + ")"; } private string ListOperations() { if (Operations != null) { return string.Join(", ", Operations.Select((OperationSpecification o) => o.ToString()).ToArray()); } return "none"; } } internal class DataStorageElementContext { internal string Key { get; set; } internal Action<string, DataStorageHelper.DataStorageUpdatedHandler> AddHandler { get; set; } internal Action<string, DataStorageHelper.DataStorageUpdatedHandler> RemoveHandler { get; set; } internal Func<string, JToken> GetData { get; set; } internal Action<string, JToken> Initialize { get; set; } internal Func<string, Task<JToken>> GetAsync { get; set; } public override string ToString() { return "Key: " + Key; } } public class GameData { [JsonProperty("location_name_to_id")] public Dictionary<string, long> LocationLookup { get; set; } = new Dictionary<string, long>(); [JsonProperty("item_name_to_id")] public Dictionary<string, long> ItemLookup { get; set; } = new Dictionary<string, long>(); [Obsolete("use Checksum instead")] [JsonProperty("version")] public int Version { get; set; } [JsonProperty("checksum")] public string Checksum { get; set; } } public class Hint { [JsonProperty("receiving_player")] public int ReceivingPlayer { get; set; } [JsonProperty("finding_player")] public int FindingPlayer { get; set; } [JsonProperty("item")] public long ItemId { get; set; } [JsonProperty("location")] public long LocationId { get; set; } [JsonProperty("item_flags")] public ItemFlags ItemFlags { get; set; } [JsonProperty("found")] public bool Found { get; set; } [JsonProperty("entrance")] public string Entrance { get; set; } } public class ItemInfo { private readonly IItemInfoResolver itemInfoResolver; public long ItemId { get; } public long LocationId { get; } public PlayerInfo Player { get; } public ItemFlags Flags { get; } public string ItemName => itemInfoResolver.GetItemName(ItemId, ItemGame); public string ItemDisplayName => ItemName ?? $"Item: {ItemId}"; public string LocationName => itemInfoResolver.GetLocationName(LocationId, LocationGame); public string LocationDisplayName => LocationName ?? $"Location: {LocationId}"; public string ItemGame { get; } public string LocationGame { get; } public ItemInfo(NetworkItem item, string receiverGame, string senderGame, IItemInfoResolver itemInfoResolver, PlayerInfo player) { this.itemInfoResolver = itemInfoResolver; ItemGame = receiverGame; LocationGame = senderGame; ItemId = item.Item; LocationId = item.Location; Flags = item.Flags; Player = player; } public SerializableItemInfo ToSerializable() { return new SerializableItemInfo { IsScout = (GetType() == typeof(ScoutedItemInfo)), ItemId = ItemId, LocationId = LocationId, PlayerSlot = Player, Player = Player, Flags = Flags, ItemGame = ItemGame, ItemName = ItemName, LocationGame = LocationGame, LocationName = LocationName }; } } public class ScoutedItemInfo : ItemInfo { public new PlayerInfo Player => base.Player; public ScoutedItemInfo(NetworkItem item, string receiverGame, string senderGame, IItemInfoResolver itemInfoResolver, PlayerInfo player) : base(item, receiverGame, senderGame, itemInfoResolver, 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 Min(BigInteger i) { return new OperationSpecification { OperationType = OperationType.Min, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification Max(int i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(long i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(float i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(double i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(decimal i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(JToken i) { return new OperationSpecification { OperationType = OperationType.Max, Value = i }; } public static OperationSpecification Max(BigInteger i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification Remove(JToken value) { return new OperationSpecification { OperationType = OperationType.Remove, Value = value }; } public static OperationSpecification Pop(int value) { return new OperationSpecification { OperationType = OperationType.Pop, Value = JToken.op_Implicit(value) }; } public static OperationSpecification Pop(JToken value) { return new OperationSpecification { OperationType = OperationType.Pop, Value = value }; } public static OperationSpecification Update(IDictionary dictionary) { return new OperationSpecification { OperationType = OperationType.Update, Value = (JToken)(object)JObject.FromObject((object)dictionary) }; } public static OperationSpecification Floor() { return new OperationSpecification { OperationType = OperationType.Floor, Value = null }; } public static OperationSpecification Ceiling() { return new OperationSpecification { OperationType = OperationType.Ceil, Value = null }; } } public static class Bitwise { public static OperationSpecification Xor(long i) { return new OperationSpecification { OperationType = OperationType.Xor, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Xor(BigInteger i) { return new OperationSpecification { OperationType = OperationType.Xor, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification Or(long i) { return new OperationSpecification { OperationType = OperationType.Or, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Or(BigInteger i) { return new OperationSpecification { OperationType = OperationType.Or, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification And(long i) { return new OperationSpecification { OperationType = OperationType.And, Value = JToken.op_Implicit(i) }; } public static OperationSpecification And(BigInteger i) { return new OperationSpecification { OperationType = OperationType.And, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification LeftShift(long i) { return new OperationSpecification { OperationType = OperationType.LeftShift, Value = JToken.op_Implicit(i) }; } public static OperationSpecification RightShift(long i) { return new OperationSpecification { OperationType = OperationType.RightShift, Value = JToken.op_Implicit(i) }; } } public class Callback { internal DataStorageHelper.DataStorageUpdatedHandler Method { get; set; } private Callback() { } public static Callback Add(DataStorageHelper.DataStorageUpdatedHandler callback) { return new Callback { Method = callback }; } } public class AdditionalArgument { internal string Key { get; set; } internal JToken Value { get; set; } private AdditionalArgument() { } public static AdditionalArgument Add(string name, JToken value) { return new AdditionalArgument { Key = name, Value = value }; } } public class MinimalSerializableItemInfo { public long ItemId { get; set; } public long LocationId { get; set; } public int PlayerSlot { get; set; } public ItemFlags Flags { get; set; } public string ItemGame { get; set; } public string LocationGame { get; set; } } public class SerializableItemInfo : MinimalSerializableItemInfo { public bool IsScout { get; set; } public PlayerInfo Player { get; set; } public string ItemName { get; set; } public string LocationName { get; set; } [JsonIgnore] public string ItemDisplayName => ItemName ?? $"Item: {base.ItemId}"; [JsonIgnore] public string LocationDisplayName => LocationName ?? $"Location: {base.LocationId}"; public string ToJson(bool full = false) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown MinimalSerializableItemInfo minimalSerializableItemInfo = this; if (!full) { minimalSerializableItemInfo = new MinimalSerializableItemInfo { ItemId = base.ItemId, LocationId = base.LocationId, PlayerSlot = base.PlayerSlot, Flags = base.Flags }; if (IsScout) { minimalSerializableItemInfo.ItemGame = base.ItemGame; } else { minimalSerializableItemInfo.LocationGame = base.LocationGame; } } JsonSerializerSettings val = new JsonSerializerSettings { NullValueHandling = (NullValueHandling)1, Formatting = (Formatting)0 }; return JsonConvert.SerializeObject((object)minimalSerializableItemInfo, val); } public static SerializableItemInfo FromJson(string json, IArchipelagoSession session = null) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown ItemInfoStreamingContext additional = ((session != null) ? new ItemInfoStreamingContext { Items = session.Items, Locations = session.Locations, PlayerHelper = session.Players, ConnectionInfo = session.ConnectionInfo } : null); JsonSerializerSettings val = new JsonSerializerSettings { Context = new StreamingContext(StreamingContextStates.Other, additional) }; return JsonConvert.DeserializeObject<SerializableItemInfo>(json, val); } [OnDeserialized] internal void OnDeserializedMethod(StreamingContext streamingContext) { if (base.ItemGame == null && base.LocationGame != null) { IsScout = false; } else if (base.ItemGame != null && base.LocationGame == null) { IsScout = true; } if (streamingContext.Context is ItemInfoStreamingContext itemInfoStreamingContext) { if (IsScout && base.LocationGame == null) { base.LocationGame = itemInfoStreamingContext.ConnectionInfo.Game; } else if (!IsScout && base.ItemGame == null) { base.ItemGame = itemInfoStreamingContext.ConnectionInfo.Game; } if (ItemName == null) { ItemName = itemInfoStreamingContext.Items.GetItemName(base.ItemId, base.ItemGame); } if (LocationName == null) { LocationName = itemInfoStreamingContext.Locations.GetLocationNameFromId(base.LocationId, base.LocationGame); } if (Player == null) { Player = itemInfoStreamingContext.PlayerHelper.GetPlayerInfo(base.PlayerSlot); } } } } internal class ItemInfoStreamingContext { public IReceivedItemsHelper Items { get; set; } public ILocationCheckHelper Locations { get; set; } public IPlayerHelper PlayerHelper { get; set; } public IConnectionInfoProvider ConnectionInfo { get; set; } } } namespace Archipelago.MultiClient.Net.MessageLog.Parts { public class EntranceMessagePart : MessagePart { internal EntranceMessagePart(JsonMessagePart messagePart) : base(MessagePartType.Entrance, messagePart, 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.HasFlag(flag); } } 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, IConnectionInfoProvider connectionInfo, int team, int slot, string message) : base(parts, players, connectionInfo, team, slot) { Message = message; } } public class CollectLogMessage : PlayerSpecificLogMessage { internal CollectLogMessage(MessagePart[] parts, IPlayerHelper players, IConnectionInfoProvider connectionInfo, int team, int slot) : base(parts, players, connectionInfo, 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, IConnectionInfoProvider connectionInfo, int team, int slot) : base(parts, players, connectionInfo, team, slot) { } } public class HintItemSendLogMessage : ItemSendLogMessage { public bool IsFound { get; } internal HintItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, IConnectionInfoProvider connectionInfo, int receiver, int sender, NetworkItem item, bool found, IItemInfoResolver itemInfoResolver) : base(parts, players, connectionInfo, receiver, sender, item, itemInfoResolver) { IsFound = found; } } public class ItemCheatLogMessage : ItemSendLogMessage { internal ItemCheatLogMessage(MessagePart[] parts, IPlayerHelper players, IConnectionInfoProvider connectionInfo, int team, int slot, NetworkItem item, IItemInfoResolver itemInfoResolver) : base(parts, players, connectionInfo, slot, 0, item, team, itemInfoResolver) { } } public class ItemSendLogMessage : LogMessage { public PlayerInfo Receiver { get; } public PlayerInfo Sender { get; } public bool IsReceiverTheActivePlayer { get; } public bool IsSenderTheActivePlayer { get; } public bool IsRelatedToActivePlayer { get; } public ItemInfo Item { get; } internal ItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, IConnectionInfoProvider connectionInfo, int receiver, int sender, NetworkItem item, IItemInfoResolver itemInfoResolver) : this(parts, players, connectionInfo, receiver, sender, item, connectionInfo.Team, itemInfoResolver) { } internal ItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, IConnectionInfoProvider connectionInfo, int receiver, int sender, NetworkItem item, int team, IItemInfoResolver itemInfoResolver) : base(parts) { IsReceiverTheActivePlayer = connectionInfo.Team == team && connectionInfo.Slot == receiver; IsSenderTheActivePlayer = connectionInfo.Team == team && connectionInfo.Slot == sender; Receiver = players.GetPlayerInfo(team, receiver) ?? new PlayerInfo(); Sender = players.GetPlayerInfo(team, sender) ?? new PlayerInfo(); PlayerInfo player = players.GetPlayerInfo(team, item.Player) ?? new PlayerInfo(); IsRelatedToActivePlayer = IsReceiverTheActivePlayer || IsSenderTheActivePlayer || Receiver.IsSharingGroupWith(connectionInfo.Team, connectionInfo.Slot) || Sender.IsSharingGroupWith(connectionInfo.Team, connectionInfo.Slot); Item = new ItemInfo(item, Receiver.Game, Sender.Game, itemInfoResolver, player); } } public class JoinLogMessage : PlayerSpecificLogMessage { public string[] Tags { get; } internal JoinLogMessage(MessagePart[] parts, IPlayerHelper players, IConnectionInfoProvider connectionInfo, int team, int slot, string[] tags) : base(parts, players, connectionInfo, team, slot) { Tags = tags; } } public class LeaveLogMessage : PlayerSpecificLogMessage { internal LeaveLogMessage(MessagePart[] parts, IPlayerHelper players, IConnectionInfoProvider connectionInfo, int team, int slot) : base(parts, players, connectionInfo, 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 { public PlayerInfo Player { get; } public bool IsActivePlayer { get; } public bool IsRelatedToActivePlayer { get; } internal PlayerSpecificLogMessage(MessagePart[] parts, IPlayerHelper players, IConnectionInfoProvider connectionInfo, int team, int slot) : base(parts) { ReadOnlyDictionary<int, ReadOnlyCollection<PlayerInfo>> players2 = players.Players; IsActivePlayer = connectionInfo.Team == team && connectionInfo.Slot == slot; Player = ((players2.Count > team && players2[team].Count > slot) ? players2[team][slot] : new PlayerInfo()); IsRelatedToActivePlayer = IsActivePlayer || Player.IsSharingGroupWith(connectionInfo.Team, connectionInfo.Slot); } } public class ReleaseLogMessage : PlayerSpecificLogMessage { internal ReleaseLogMessage(MessagePart[] parts, IPlayerHelper players, IConnectionInfoProvider connectionInfo, int team, int slot) : base(parts, players, connectionInfo, 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, IConnectionInfoProvider connectionInfo, int team, int slot, string[] tags) : base(parts, players, connectionInfo, team, slot) { Tags = tags; } } public class TutorialLogMessage : LogMessage { internal TutorialLogMessage(MessagePart[] parts) : base(parts) { } } } namespace Archipelago.MultiClient.Net.Helpers { public class ArchipelagoSocketHelper : IArchipelagoSocketHelper { private static readonly ArchipelagoPacketConverter Converter = new ArchipelagoPacketConverter(); private const int ReceiveChunkSize = 1024; private const int SendChunkSize = 1024; private readonly BlockingCollection<Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>> sendQueue = new BlockingCollection<Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>>(); internal ClientWebSocket webSocket; public Uri Uri { get; } public bool Connected { get { if (webSocket.State != WebSocketState.Open) { return webSocket.State == WebSocketState.CloseReceived; } return true; } } public event ArchipelagoSocketHelperDelagates.PacketReceivedHandler PacketReceived; public event ArchipelagoSocketHelperDelagates.PacketsSentHandler PacketsSent; public event ArchipelagoSocketHelperDelagates.ErrorReceivedHandler ErrorReceived; public event ArchipelagoSocketHelperDelagates.SocketClosedHandler SocketClosed; public event ArchipelagoSocketHelperDelagates.SocketOpenedHandler SocketOpened; internal ArchipelagoSocketHelper(Uri hostUri) { Uri = hostUri; webSocket = new ClientWebSocket(); } public async Task ConnectAsync() { await ConnectToProvidedUri(Uri); if (this.SocketOpened != null) { this.SocketOpened(); } Task.Run((Func<Task?>)PollingLoop); Task.Run((Func<Task?>)SendLoop); } private async Task ConnectToProvidedUri(Uri uri) { if (uri.Scheme != "unspecified") { try { await webSocket.ConnectAsync(uri, CancellationToken.None); return; } catch (Exception e) { OnError(e); throw; } } List<Exception> errors = new List<Exception>(0); try { await webSocket.ConnectAsync(uri.AsWss(), CancellationToken.None); if (webSocket.State == WebSocketState.Open) { return; } } catch (Exception item) { errors.Add(item); webSocket = new ClientWebSocket(); } try { await webSocket.ConnectAsync(uri.AsWs(), CancellationToken.None); } catch (Exception item2) { errors.Add(item2); OnError(new AggregateException(errors)); throw; } } private async Task PollingLoop() { byte[] buffer = new byte[1024]; while (webSocket.State == WebSocketState.Open) { string message = null; try { message = await ReadMessageAsync(buffer); } catch (Exception e) { OnError(e); } OnMessageReceived(message); } } private async Task SendLoop() { while (webSocket.State == WebSocketState.Open) { try { await HandleSendBuffer(); } catch (Exception e) { OnError(e); } } } private async Task<string> ReadMessageAsync(byte[] buffer) { StringBuilder stringResult = new StringBuilder(); WebSocketReceiveResult result; do { result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); if (result.MessageType == WebSocketMessageType.Close) { try { await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); } catch { } OnSocketClosed(); } else { stringResult.Append(Encoding.UTF8.GetString(buffer, 0, result.Count)); } } while (!result.EndOfMessage); return stringResult.ToString(); } public async Task DisconnectAsync() { await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closure requested by client", CancellationToken.None); OnSocketClosed(); } public void SendPacket(ArchipelagoPacketBase packet) { SendMultiplePackets(new List<ArchipelagoPacketBase> { packet }); } public void SendMultiplePackets(List<ArchipelagoPacketBase> packets) { SendMultiplePackets(packets.ToArray()); } public void SendMultiplePackets(params ArchipelagoPacketBase[] packets) { SendMultiplePacketsAsync(packets).Wait(); } public Task SendPacketAsync(ArchipelagoPacketBase packet) { return SendMultiplePacketsAsync(new List<ArchipelagoPacketBase> { packet }); } public Task SendMultiplePacketsAsync(List<ArchipelagoPacketBase> packets) { return SendMultiplePacketsAsync(packets.ToArray()); } public Task SendMultiplePacketsAsync(params ArchipelagoPacketBase[] packets) { TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>(); foreach (ArchipelagoPacketBase item in packets) { sendQueue.Add(new Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>(item, taskCompletionSource)); } return taskCompletionSource.Task; } private async Task HandleSendBuffer() { List<ArchipelagoPacketBase> list = new List<ArchipelagoPacketBase>(); List<TaskCompletionSource<bool>> tasks = new List<TaskCompletionSource<bool>>(); Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>> tuple = sendQueue.Take(); list.Add(tuple.Item1); tasks.Add(tuple.Item2); Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>> item; while (sendQueue.TryTake(out item)) { list.Add(item.Item1); tasks.Add(item.Item2); } if (!list.Any()) { return; } if (webSocket.State != WebSocketState.Open) { throw new ArchipelagoSocketClosedException(); } ArchipelagoPacketBase[] packets = list.ToArray(); string s = JsonConvert.SerializeObject((object)packets); byte[] messageBuffer = Encoding.UTF8.GetBytes(s); int messagesCount = (int)Math.Ceiling((double)messageBuffer.Length / 1024.0); for (int i = 0; i < messagesCount; i++) { int num = 1024 * i; int num2 = 1024; bool endOfMessage = i + 1 == messagesCount; if (num2 * (i + 1) > messageBuffer.Length) { num2 = messageBuffer.Length - num; } await webSocket.SendAsync(new ArraySegment<byte>(messageBuffer, num, num2), WebSocketMessageType.Text, endOfMessage, CancellationToken.None); } foreach (TaskCompletionSource<bool> item2 in tasks) { item2.TrySetResult(result: true); } OnPacketSend(packets); } private void OnPacketSend(ArchipelagoPacketBase[] packets) { try { if (this.PacketsSent != null) { this.PacketsSent(packets); } } catch (Exception e) { OnError(e); } } private void OnSocketClosed() { try { if (this.SocketClosed != null) { this.SocketClosed(""); } } catch (Exception e) { OnError(e); } } private void OnMessageReceived(string message) { try { if (string.IsNullOrEmpty(message) || this.PacketReceived == null) { return; } List<ArchipelagoPacketBase> list = JsonConvert.DeserializeObject<List<ArchipelagoPacketBase>>(message, (JsonConverter[])(object)new JsonConverter[1] { Converter }); if (list == null) { return; } foreach (ArchipelagoPacketBase item in list) { this.PacketReceived(item); } } catch (Exception e) { OnError(e); } } private void OnError(Exception e) { try { if (this.ErrorReceived != null) { this.ErrorReceived(e, e.Message); } } catch (Exception ex) { Console.Out.WriteLine("Error occured during reporting of errorOuter Errror: " + e.Message + " " + e.StackTrace + "Inner Errror: " + ex.Message + " " + ex.StackTrace); } } } public interface IConnectionInfoProvider { string Game { get; } int Team { get; } int Slot { get; } string[] Tags { get; } ItemsHandlingFlags ItemsHandlingFlags { get; } string Uuid { get; } void UpdateConnectionOptions(string[] tags); void UpdateConnectionOptions(ItemsHandlingFlags itemsHandlingFlags); void UpdateConnectionOptions(string[] tags, ItemsHandlingFlags itemsHandlingFlags); } public class ConnectionInfoHelper : IConnectionInfoProvider { private readonly IArchipelagoSocketHelper socket; public string Game { get; private set; } public int Team { get; private set; } public int Slot { get; private set; } public string[] Tags { get; internal set; } public ItemsHandlingFlags ItemsHandlingFlags { get; internal set; } public string Uuid { get; private set; } internal ConnectionInfoHelper(IArchipelagoSocketHelper socket) { this.socket = socket; Reset(); socket.PacketReceived += PacketReceived; } private void PacketReceived(ArchipelagoPacketBase packet) { if (!(packet is ConnectedPacket connectedPacket)) { if (packet is ConnectionRefusedPacket) { Reset(); } return; } Team = connectedPacket.Team; Slot = connectedPacket.Slot; if (connectedPacket.SlotInfo != null && connectedPacket.SlotInfo.ContainsKey(Slot)) { Game = connectedPacket.SlotInfo[Slot].Game; } } internal void SetConnectionParameters(string game, string[] tags, ItemsHandlingFlags itemsHandlingFlags, string uuid) { Game = game; Tags = tags ?? new string[0]; ItemsHandlingFlags = itemsHandlingFlags; Uuid = uuid ?? Guid.NewGuid().ToString(); } private void Reset() { Game = null; Team = -1; Slot = -1; Tags = new string[0]; ItemsHandlingFlags = ItemsHandlingFlags.NoItems; Uuid = null; } public void UpdateConnectionOptions(string[] tags) { UpdateConnectionOptions(tags, ItemsHandlingFlags); } public void UpdateConnectionOptions(ItemsHandlingFlags itemsHandlingFlags) { UpdateConnectionOptions(Tags, ItemsHandlingFlags); } public void UpdateConnectionOptions(string[] tags, ItemsHandlingFlags itemsHandlingFlags) { SetConnectionParameters(Game, tags, itemsHandlingFlags, Uuid); socket.SendPacket(new ConnectUpdatePacket { Tags = Tags, ItemsHandling = ItemsHandlingFlags }); } } public interface IDataStorageHelper : IDataStorageWrapper { DataStorageElement this[Scope scope, string key] { get; set; } DataStorageElement this[string key] { get; set; } } public class DataStorageHelper : IDataStorageHelper, IDataStorageWrapper { public delegate void DataStorageUpdatedHandler(JToken originalValue, JToken newValue, Dictionary<string, JToken> additionalArguments); private readonly Dictionary<string, DataStorageUpdatedHandler> onValueChangedEventHandlers = new Dictionary<string, DataStorageUpdatedHandler>(); private readonly Dictionary<Guid, DataStorageUpdatedHandler> operationSpecificCallbacks = new Dictionary<Guid, DataStorageUpdatedHandler>(); private readonly Dictionary<string, TaskCompletionSource<JToken>> asyncRetrievalTasks = new Dictionary<string, TaskCompletionSource<JToken>>(); private readonly IArchipelagoSocketHelper socket; private readonly IConnectionInfoProvider connectionInfoProvider; public DataStorageElement this[Scope scope, string key] { get { return this[AddScope(scope, key)]; } set { this[AddScope(scope, key)] = value; } } public DataStorageElement this[string key] { get { return new DataStorageElement(GetContextForKey(key)); } set { SetValue(key, value); } } internal DataStorageHelper(IArchipelagoSocketHelper socket, IConnectionInfoProvider connectionInfoProvider) { this.socket = socket; this.connectionInfoProvider = connectionInfoProvider; socket.PacketReceived += OnPacketReceived; } private void OnPacketReceived(ArchipelagoPacketBase packet) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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 (asyncRetrievalTasks.TryGetValue(datum.Key, out var value3)) { value3.TrySetResult(datum.Value); asyncRetrievalTasks.Remove(datum.Key); } } } private Task<JToken> GetAsync(string key) { if (asyncRetrievalTasks.TryGetValue(key, out var value)) { return value.Task; } TaskCompletionSource<JToken> taskCompletionSource = new TaskCompletionSource<JToken>(); asyncRetrievalTasks[key] = taskCompletionSource; socket.SendPacketAsync(new GetPacket { Keys = new string[1] { key } }); return taskCompletionSource.Task; } private void Initialize(string key, JToken value) { socket.SendPacketAsync(new SetPacket { Key = key, DefaultValue = value, Operations = new OperationSpecification[1] { new OperationSpecification { OperationType = OperationType.Default } } }); } private JToken GetValue(string key) { Task<JToken> async = GetAsync(key); if (!async.Wait(TimeSpan.FromSeconds(2.0))) { throw new TimeoutException("Timed out retrieving data for key `" + key + "`. This may be due to an attempt to retrieve a value from the DataStorageHelper in a synchronous fashion from within a PacketReceived handler. When using the DataStorageHelper from within code which runs on the websocket thread then use the asynchronous getters. Ex: `DataStorageHelper[\"" + key + "\"].GetAsync().ContinueWith(x => {});`Be aware that DataStorageHelper calls tend to cause packet responses, so making a call from within a PacketReceived handler may cause an infinite loop."); } return async.Result; } private void SetValue(string key, DataStorageElement e) { if (key.StartsWith("_read_")) { throw new InvalidOperationException("DataStorage write operation on readonly key '" + key + "' is not allowed"); } if (e == null) { e = new DataStorageElement(OperationType.Replace, (JToken)(object)JValue.CreateNull()); } if (e.Context == null) { e.Context = GetContextForKey(key); } else if (e.Context.Key != key) { e.Operations.Insert(0, new OperationSpecification { OperationType = OperationType.Replace, Value = GetValue(e.Context.Key) }); } Dictionary<string, JToken> dictionary = e.AdditionalArguments ?? new Dictionary<string, JToken>(0); if (e.Callbacks != null) { Guid 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 Task<Hint[]> GetHintsAsync(int? slot = null, int? team = null) { return GetHintsElement(slot, team).GetAsync<Hint[]>(); } public void TrackHints(Action<Hint[]> onHintsUpdated, bool retrieveCurrentlyUnlockedHints = true, int? slot = null, int? team = null) { GetHintsElement(slot, team).OnValueChanged += delegate(JToken _, JToken newValue, Dictionary<string, JToken> x) { onHintsUpdated(newValue.ToObject<Hint[]>()); }; if (retrieveCurrentlyUnlockedHints) { GetHintsAsync(slot, team).ContinueWith(delegate(Task<Hint[]> t) { onHintsUpdated(t.Result); }); } } public Dictionary<string, object> GetSlotData(int? slot = null) { return GetSlotDataElement(slot).To<Dictionary<string, object>>(); } public Task<Dictionary<string, object>> GetSlotDataAsync(int? slot = null) { return GetSlotDataElement(slot).GetAsync<Dictionary<string, object>>(); } public Dictionary<string, string[]> GetItemNameGroups(string game = null) { return GetItemNameGroupsElement(game).To<Dictionary<string, string[]>>(); } public Task<Dictionary<string, string[]>> GetItemNameGroupsAsync(string game = null) { return GetItemNameGroupsElement(game).GetAsync<Dictionary<string, string[]>>(); } public Dictionary<string, string[]> GetLocationNameGroups(string game = null) { return GetLocationNameGroupsElement(game).To<Dictionary<string, string[]>>(); } public Task<Dictionary<string, string[]>> GetLocationNameGroupsAsync(string game = null) { return GetLocationNameGroupsElement(game).GetAsync<Dictionary<string, string[]>>(); } public ArchipelagoClientState GetClientStatus(int? slot = null, int? team = null) { return GetClientStatusElement(slot, team).To<ArchipelagoClientState?>().GetValueOrDefault(); } public Task<ArchipelagoClientState> GetClientStatusAsync(int? slot = null, int? team = null) { return GetClientStatusElement(slot, team).GetAsync<ArchipelagoClientState?>().ContinueWith((Task<ArchipelagoClientState?> r) => r.Result.GetValueOrDefault()); } public void TrackClientStatus(Action<ArchipelagoClientState> onStatusUpdated, bool retrieveCurrentClientStatus = true, int? slot = null, int? team = null) { GetClientStatusElement(slot, team).OnValueChanged += delegate(JToken _, JToken newValue, Dictionary<string, JToken> x) { onStatusUpdated(newValue.ToObject<ArchipelagoClientState>()); }; if (retrieveCurrentClientStatus) { GetClientStatusAsync(slot, team).ContinueWith(delegate(Task<ArchipelagoClientState> t) { onStatusUpdated(t.Result); }); } } } public interface IDataStorageWrapper { Hint[] GetHints(int? slot = null, int? team = null); Task<Hint[]> GetHintsAsync(int? slot = n
Ryguy9999.ATS.ATSForAP.dll
Decompiled 15 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using ATS_API; using ATS_API.Effects; using ATS_API.Helpers; using Archipelago.MultiClient.Net; using Archipelago.MultiClient.Net.BounceFeatures.DeathLink; using Archipelago.MultiClient.Net.Enums; using Archipelago.MultiClient.Net.Helpers; using Archipelago.MultiClient.Net.Models; using BepInEx; using Eremite; using Eremite.Buildings; using Eremite.Characters.Villagers; using Eremite.Controller; using Eremite.Controller.Effects; using Eremite.Model; using Eremite.Model.Configs; using Eremite.Model.Effects; using Eremite.Model.Orders; using Eremite.Model.Sound; using Eremite.Services; using Eremite.Services.Monitors; using HarmonyLib; using QFSW.QC; using UniRx; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("Ryguy9999.ATS.ATSForAP")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("AP Mod for Against The Storm - by Ryguy9999")] [assembly: AssemblyFileVersion("0.3.2.0")] [assembly: AssemblyInformationalVersion("0.3.2")] [assembly: AssemblyProduct("Ryguy9999.ATS.ATSForAP")] [assembly: AssemblyTitle("Ryguy9999.ATS.ATSForAP")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.3.2.0")] [module: UnverifiableCode] namespace Ryguy9999.ATS.ATSForAP { public class APItemReceivedHook : HookLogic { public string itemName; public int amount = 1; public override HookLogicType Type => (HookLogicType)9999; public override bool CanBeDrawn() { return false; } public override string GetAmountText() { return "1"; } public override int GetIntAmount() { return 1; } public override bool HasImpactOn(BuildingModel building) { return Serviceable.RecipesService.IsBuildingProducing(((SO)building).Name, itemName); } } internal class APItemReceivedMonitor : HookMonitor<APItemReceivedHook, APItemReceivedTracker> { public override void AddHandle(APItemReceivedTracker tracker) { ((HookTracker<APItemReceivedHook>)tracker).handle.Add(ObservableExtensions.Subscribe<int>(ArchipelagoService.OnAPItemReceived(((HookTracker<APItemReceivedHook>)tracker).model.itemName), (Action<int>)tracker.Update)); } public override APItemReceivedTracker CreateTracker(HookState state, APItemReceivedHook model, HookedEffectModel effectModel, HookedEffectState effectState) { return new APItemReceivedTracker(state, model, effectModel, effectState); } public override void InitValue(APItemReceivedTracker tracker) { tracker.SetAmount(((HookMonitor<APItemReceivedHook, APItemReceivedTracker>)this).GetInitValueFor(((HookTracker<APItemReceivedHook>)tracker).model)); } } internal class APItemReceivedTracker : HookTracker<APItemReceivedHook> { public APItemReceivedTracker(HookState hookState, APItemReceivedHook model, HookedEffectModel effectModel, HookedEffectState effectState) : base(hookState, model, effectModel, effectState) { } public void SetAmount(int amount) { Update(amount - base.hookState.totalAmount); } public void Update(int amount) { HookState hookState = base.hookState; hookState.totalAmount += amount; HookState hookState2 = base.hookState; hookState2.currentAmount += amount; while (base.hookState.currentAmount >= base.model.amount) { base.Fire(); HookState hookState3 = base.hookState; hookState3.currentAmount -= base.model.amount; } } } internal class ArchipelagoEffectModel : EffectModel { public override bool IsPerk => true; public override bool IsPositive => false; public override string FormatedDescription => ((SO)this).TryFormat(base.description.Text, (object)("<sprite name=\"" + GoodsTypesExtensions.ToName((GoodsTypes)33).ToLowerInvariant() + "\"> " + base.displayName.Text), (object)((EffectModel)this).GetRawAmountText()); public override string GetDescriptionInfo => "{0} - good name, {1} - amount"; public override void OnApply(EffectContextType contextType, string contextModel, int contextId) { } public override void OnRemove(EffectContextType contextType, string contextModel, int contextId) { Plugin.Log.LogInfo((object)"Cannot remove Archipelago Effect from run once added! How did we even get here?"); } public override string GetAmountText() { return null; } public override string GetRawAmountText() { return null; } public override int GetIntAmount() { return 0; } public override Sprite GetDefaultIcon() { return TextureHelper.GetImageAsSprite("ap-icon.png", (SpriteType)0, (FilterMode)0); } public override Color GetTypeColor() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) return SO.Settings.RewardColorCommonNegative; } public override bool HasImpactOn(BuildingModel building) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair<string, GoodsTypes> item in Constants.ITEM_DICT) { if (SO.RecipesService.IsBuildingProducing(((SO)building).Name, GoodsTypesExtensions.ToName(item.Value))) { return true; } } return false; } } internal class ArchipelagoService : GameService, IGameService, IService { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ItemReceivedHandler <>9__11_0; public static DeathLinkReceivedHandler <>9__11_1; internal void <InitializeAPConnection>b__11_0(ReceivedItemsHelper receivedItemsHelper) { while (receivedItemsHelper.Any()) { ItemInfo val = receivedItemsHelper.DequeueItem(); if (GameMB.IsGameActive && val.ItemName != null) { HandleItemReceived(val.ItemName); } } } internal void <InitializeAPConnection>b__11_1(DeathLink deathLink) { if (GameMB.IsGameActive) { GameMB.VillagersService.KillVillagers(1, (VillagerLossType)1, "AP Deathlink"); } } } private static Dictionary<string, Subject<int>> itemCallbacks = new Dictionary<string, Subject<int>>(); private static ArchipelagoSession session; private static long DeathlinkState = -1L; private static DeathLinkService deathLinkService; private static List<IDisposable> GameSubscriptions = new List<IDisposable>(); private static Queue<string> LocationQueue = new Queue<string>(); private static Dictionary<string, Sprite> OriginalGoodIcons = new Dictionary<string, Sprite>(); private static bool ShowAPLockIcons = true; private static List<string> ItemsForNews = new List<string>(); private static News ApConnectionNews; [Command(/*Could not decode attribute arguments.*/)] public static void InitializeAPConnection(string url, string player) { InitializeAPConnection(url, player, null); } [Command(/*Could not decode attribute arguments.*/)] public static void InitializeAPConnection(string url, string player, string password) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Expected O, but got Unknown //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Expected O, but got Unknown if (session != null) { DisconnectFromGame(); } session = ArchipelagoSessionFactory.CreateSession(url, 38281); LoginResult val; try { val = session.TryConnectAndLogin("Against the Storm", player, (ItemsHandlingFlags)7, (Version)null, (string[])null, (string)null, password, true); } catch (Exception ex) { val = (LoginResult)new LoginFailure(ex.GetBaseException().Message); } if (!val.Successful) { LoginFailure val2 = (LoginFailure)val; string text = "Failed to Connect to " + url + " as " + player + ":"; string[] errors = val2.Errors; foreach (string text2 in errors) { text = text + "\n " + text2; } ConnectionRefusedError[] errorCodes = val2.ErrorCodes; foreach (ConnectionRefusedError val3 in errorCodes) { text += $"\n {val3}"; } Plugin.Log.LogInfo((object)text); GameMB.NewsService.PublishNews("Failed to connect to AP!", "The connection to the AP server failed. Check the BepInEx console for potentially more information.", (AlertSeverity)3, (Sprite)null, (IBroadcaster)null); return; } LoginSuccessful val4 = (LoginSuccessful)(object)((val is LoginSuccessful) ? val : null); if (GameMB.IsGameActive) { SyncProductionToAP(); if (ApConnectionNews != null) { GameMB.NewsService.RemoveNews(ApConnectionNews); } } while (LocationQueue.Any()) { CheckLocation(LocationQueue.Dequeue()); } if ((long)val4.SlotData["recipe_shuffle"] >= 1) { RandomizeBuildingRecipes((long)val4.SlotData["seed"], (long)val4.SlotData["recipe_shuffle"] == 1); if (GameMB.IsGameActive && MB.GameSaveService.IsNewGame()) { GameMB.NewsService.PublishNews("Cannot randomize recipes in a game!", "Recipe Shuffle option detected from AP. Unfortunately, recipe randomization only takes effect on creation of a new settlement. Your recipes have been shuffled! It just won't affect this settlement.", (AlertSeverity)2, (Sprite)null, (IBroadcaster)null); } } IReceivedItemsHelper items = session.Items; object obj = <>c.<>9__11_0; if (obj == null) { ItemReceivedHandler val5 = delegate(ReceivedItemsHelper receivedItemsHelper) { while (receivedItemsHelper.Any()) { ItemInfo val7 = receivedItemsHelper.DequeueItem(); if (GameMB.IsGameActive && val7.ItemName != null) { HandleItemReceived(val7.ItemName); } } }; <>c.<>9__11_0 = val5; obj = (object)val5; } items.ItemReceived += (ItemReceivedHandler)obj; DeathlinkState = (long)val4.SlotData["deathlink"]; if ((long)val4.SlotData["deathlink"] < 1) { return; } deathLinkService = DeathLinkProvider.CreateDeathLinkService(session); deathLinkService.EnableDeathLink(); DeathLinkService obj2 = deathLinkService; object obj3 = <>c.<>9__11_1; if (obj3 == null) { DeathLinkReceivedHandler val6 = delegate { if (GameMB.IsGameActive) { GameMB.VillagersService.KillVillagers(1, (VillagerLossType)1, "AP Deathlink"); } }; <>c.<>9__11_1 = val6; obj3 = (object)val6; } obj2.OnDeathLinkReceived += (DeathLinkReceivedHandler)obj3; } public static bool HasReceivedItem(string item) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) item = (Constants.ITEM_DICT.ContainsKey(item) ? GoodsTypesExtensions.ToName(Constants.ITEM_DICT[item]) : item); return !Serviceable.StateService.Effects.rawGoodsProductionBonus.ContainsKey(item) || Serviceable.StateService.Effects.rawGoodsProductionBonus[item] > -499999; } public static void SyncProductionToAP() { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) if (session == null) { Plugin.Log.LogInfo((object)"Tried to sync production to AP with a null AP session."); return; } foreach (KeyValuePair<string, GoodsTypes> pair in Constants.ITEM_DICT) { if (session.Items.AllItemsReceived.Any((ItemInfo item) => item.ItemName == pair.Key)) { if (!HasReceivedItem(pair.Key)) { HandleItemReceived(pair.Key); } continue; } if (ShowAPLockIcons) { if (!OriginalGoodIcons.Keys.Contains(GoodsTypesExtensions.ToName(pair.Value))) { OriginalGoodIcons.Add(GoodsTypesExtensions.ToName(pair.Value), MB.Settings.GetGood(GoodsTypesExtensions.ToName(pair.Value)).icon); } MB.Settings.GetGood(GoodsTypesExtensions.ToName(pair.Value)).icon = TextureHelper.GetImageAsSprite("good-locked.png", (SpriteType)0, (FilterMode)0); } if (HasReceivedItem(pair.Key)) { SO.EffectsService.GrantRawGoodProduction(GoodsTypesExtensions.ToName(pair.Value), -999999); } } } public static void EnterGame() { if (session == null) { GameMB.NewsService.PublishNews("No AP session detected! Remember to connect!", "ap.connect url:port slotName [password]", (AlertSeverity)3, (Sprite)null, (IBroadcaster)null); ApConnectionNews = (GameMB.NewsService.News.GetType().GetProperty("Value").GetValue(GameMB.NewsService.News, null) as List<News>)[0]; } SyncProductionToAP(); if (GameSubscriptions.Count > 0) { foreach (IDisposable gameSubscription in GameSubscriptions) { gameSubscription.Dispose(); } } GameSubscriptions.Add(ObservableExtensions.Subscribe<OrderState>(GameMB.OrdersService.OnOrderCompleted, (Action<OrderState>)HandleOrderRewards)); GameSubscriptions.Add(ObservableExtensions.Subscribe<ReputationChange>(GameMB.ReputationService.OnReputationChanged, (Action<ReputationChange>)HandleReputationChange)); GameSubscriptions.Add(ObservableExtensions.Subscribe<bool>(GameMB.ReputationService.OnGameResult, (Action<bool>)HandleGameResult)); GameSubscriptions.Add(ObservableExtensions.Subscribe<Hearth>((IObservable<Hearth>)GameMB.GameBlackboardService.OnHubLeveledUp, (Action<Hearth>)HandleHubLevelUp)); GameSubscriptions.Add(ObservableExtensions.Subscribe<Relic>((IObservable<Relic>)GameMB.GameBlackboardService.OnRelicResolved, (Action<Relic>)HandleRelicResolve)); GameSubscriptions.Add(UniRxExtensions.Subscribe<long>(MB.RXService.Interval(1f, true), (Action)HandleSlowUpdate)); } [Command(/*Could not decode attribute arguments.*/)] public static void ToggleLockIcons() { ToggleLockIcons(!ShowAPLockIcons); } [Command(/*Could not decode attribute arguments.*/)] public static void ToggleLockIcons(bool trueFalse) { //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if (ShowAPLockIcons == trueFalse) { return; } ShowAPLockIcons = trueFalse; foreach (KeyValuePair<string, GoodsTypes> item in Constants.ITEM_DICT) { if (ShowAPLockIcons && !HasReceivedItem(item.Key)) { if (!OriginalGoodIcons.Keys.Contains(GoodsTypesExtensions.ToName(item.Value))) { OriginalGoodIcons.Add(GoodsTypesExtensions.ToName(item.Value), MB.Settings.GetGood(GoodsTypesExtensions.ToName(item.Value)).icon); } MB.Settings.GetGood(GoodsTypesExtensions.ToName(item.Value)).icon = TextureHelper.GetImageAsSprite("good-locked.png", (SpriteType)0, (FilterMode)0); } else if (OriginalGoodIcons.Keys.Contains(GoodsTypesExtensions.ToName(item.Value))) { MB.Settings.GetGood(GoodsTypesExtensions.ToName(item.Value)).icon = OriginalGoodIcons[GoodsTypesExtensions.ToName(item.Value)]; } } } [Command(/*Could not decode attribute arguments.*/)] public static void RestoreProduction() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair<string, GoodsTypes> item in Constants.ITEM_DICT) { string text = GoodsTypesExtensions.ToName(item.Value); if (!HasReceivedItem(text)) { SO.EffectsService.GrantRawGoodProduction(text, 999999); } } } [Command(/*Could not decode attribute arguments.*/)] public static void DisconnectFromGame() { if (session != null) { session.Socket.DisconnectAsync(); session = null; } foreach (IDisposable gameSubscription in GameSubscriptions) { gameSubscription.Dispose(); } } [Command(/*Could not decode attribute arguments.*/)] public static void RandomizeBuildingRecipes(long seed, bool skipCrudeWS) { List<WorkshopRecipeModel> list = new List<WorkshopRecipeModel>(); BuildingModel[] buildings = MB.Settings.Buildings; foreach (BuildingModel val in buildings) { WorkshopModel val2 = (WorkshopModel)(object)((val is WorkshopModel) ? val : null); if (!((Object)(object)val2 == (Object)null) && (!skipCrudeWS || !((Object)(object)val2 == (Object)/*isinst with value type is only supported in some contexts*/))) { list.AddRange(val2.recipes); } } Random random = new Random(Convert.ToInt32(seed)); BuildingModel[] buildings2 = Serviceable.Settings.Buildings; foreach (BuildingModel val3 in buildings2) { WorkshopModel val4 = (WorkshopModel)(object)((val3 is WorkshopModel) ? val3 : null); if (!((Object)(object)val4 == (Object)null) && (!skipCrudeWS || !((Object)(object)val4 == (Object)/*isinst with value type is only supported in some contexts*/))) { for (int k = 0; k < val4.recipes.Length; k++) { int index = random.Next(list.Count); val4.recipes[k] = list[index]; list.RemoveAt(index); } } } ((object)Serviceable.StaticRecipesService).GetType().GetField("goodsSourcesMap", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(SO.StaticRecipesService, new Dictionary<string, List<BuildingModel>>()); ((object)Serviceable.StaticRecipesService).GetType().GetMethod("MapGoodsSources", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(SO.StaticRecipesService, new object[0]); } public static bool CheckLocation(string location) { if (session == null) { LocationQueue.Enqueue(location); } long locationIdFromName = session.Locations.GetLocationIdFromName("Against the Storm", location); if (locationIdFromName < 0 || session.Locations.AllLocationsChecked.Contains(locationIdFromName)) { return false; } Plugin.Log.LogInfo((object)("Sending AP Location: " + location)); session.Locations.CompleteLocationChecks(new long[1] { locationIdFromName }); return true; } private static string GetOrdinalSuffix(int number) { return number switch { 1 => "st", 2 => "nd", 3 => "rd", _ => "th", }; } private static void HandleOrderRewards(OrderState order) { int number = GameMB.StateService.Orders.currentOrders.FindIndex((OrderState o) => o.id == order.id) + 1; CheckLocation("Completed Order - " + number + GetOrdinalSuffix(number) + " Pack"); } private static void HandleReputationChange(ReputationChange repChange) { List<long> list = new List<long>(); if (GameMB.RacesService.HasRace("Human") && GameMB.ResolveService.GetReputationGainFor("Human") >= 1f) { CheckLocation("First Reputation through Resolve - Humans"); } if (GameMB.RacesService.HasRace("Lizard") && GameMB.ResolveService.GetReputationGainFor("Lizard") >= 1f) { CheckLocation("First Reputation through Resolve - Beavers"); } if (GameMB.RacesService.HasRace("Beaver") && GameMB.ResolveService.GetReputationGainFor("Beaver") >= 1f) { CheckLocation("First Reputation through Resolve - Lizards"); } if (GameMB.RacesService.HasRace("Harpy") && GameMB.ResolveService.GetReputationGainFor("Harpy") >= 1f) { CheckLocation("First Reputation through Resolve - Harpies"); } if (GameMB.RacesService.HasRace("Foxes") && GameMB.ResolveService.GetReputationGainFor("Foxes") >= 1f) { CheckLocation("First Reputation through Resolve - Foxes"); } float num = GameMB.ReputationService.GetReputationGainedFrom((ReputationChangeSource)1) + GameMB.ReputationService.GetReputationGainedFrom((ReputationChangeSource)0) + GameMB.ReputationService.GetReputationGainedFrom((ReputationChangeSource)3) + GameMB.ReputationService.GetReputationGainedFrom((ReputationChangeSource)2); if (num >= 1f) { CheckLocation("1st Reputation - " + MB.MetaStateService.GameConditions.biome); } if (num >= 10f) { CheckLocation("10th Reputation - " + MB.MetaStateService.GameConditions.biome); } } private static void HandleGameResult(bool gameWon) { if (gameWon) { CheckLocation("Victory - " + MB.MetaStateService.GameConditions.biome); if (GameMB.RacesService.HasRace("Human") && GetFullyUpgradedHousedAmount("Human") >= 30) { CheckLocation("Win with 30 Villagers in fully upgraded Housing - Humans"); } if (GameMB.RacesService.HasRace("Beaver") && GetFullyUpgradedHousedAmount("Beaver") >= 30) { CheckLocation("Win with 30 Villagers in fully upgraded Housing - Beavers"); } if (GameMB.RacesService.HasRace("Lizard") && GetFullyUpgradedHousedAmount("Lizard") >= 30) { CheckLocation("Win with 30 Villagers in fully upgraded Housing - Lizards"); } if (GameMB.RacesService.HasRace("Harpy") && GetFullyUpgradedHousedAmount("Harpy") >= 30) { CheckLocation("Win with 30 Villagers in fully upgraded Housing - Harpies"); } if (GameMB.RacesService.HasRace("Foxes") && GetFullyUpgradedHousedAmount("Foxes") >= 30) { CheckLocation("Win with 30 Villagers in fully upgraded Housing - Foxes"); } if (GameMB.GameSealService.IsSealedBiome() && GameMB.GameSealService.IsSealCompleted()) { session.SetGoalAchieved(); } } } private static int GetFullyUpgradedHousedAmount(string speciesName) { int num = 0; foreach (KeyValuePair<int, House> house in GameMB.BuildingsService.Houses) { if (((Building)house.Value).UpgradableState.level >= 2 && ArrayExtension.Contains<RaceModel>(house.Value.model.housingRaces, MB.Settings.GetRace(speciesName))) { num += house.Value.state.residents.Count; } } return num; } private static void HandleHubLevelUp(Hearth hearth) { int number = hearth.state.hubIndex + 1; CheckLocation("Upgraded Hearth - " + number + GetOrdinalSuffix(number) + " Tier"); } private static void HandleRelicResolve(Relic relic) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Invalid comparison between Unknown and I4 if ((int)relic.model.dangerLevel == 2) { CheckLocation("Complete a Dangerous Glade Event"); } if ((int)relic.model.dangerLevel == 3) { CheckLocation("Complete a Forbidden Glade Event"); } } private static void HandleSlowUpdate() { //IL_017a: Unknown result type (might be due to invalid IL or missing references) if (GameMB.RacesService.HasRace("Human") && GameMB.ResolveService.GetResolveFor("Human") >= 50f) { CheckLocation("50 Resolve - Humans"); } if (GameMB.RacesService.HasRace("Lizard") && GameMB.ResolveService.GetResolveFor("Lizard") >= 50f) { CheckLocation("50 Resolve - Beavers"); } if (GameMB.RacesService.HasRace("Beaver") && GameMB.ResolveService.GetResolveFor("Beaver") >= 50f) { CheckLocation("50 Resolve - Lizards"); } if (GameMB.RacesService.HasRace("Harpy") && GameMB.ResolveService.GetResolveFor("Harpy") >= 50f) { CheckLocation("50 Resolve - Harpies"); } if (GameMB.RacesService.HasRace("Foxes") && GameMB.ResolveService.GetResolveFor("Foxes") >= 50f) { CheckLocation("50 Resolve - Foxes"); } while (ItemsForNews.Any()) { string text = ItemsForNews[0]; GameMB.NewsService.PublishNews(text + " unlocked!", text + " received from AP. You can now produce, gather, and obtain " + text + ".", (AlertSeverity)1, MB.Settings.GetGoodIcon(GoodsTypesExtensions.ToName(Constants.ITEM_DICT[text])), (IBroadcaster)null); ItemsForNews.RemoveAt(0); } } public static void HandleVillagerDeath(VillagerLossType lossType, string reasonKey) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Invalid comparison between Unknown and I4 //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown if (session != null && !(reasonKey == "AP Deathlink") && ((int)lossType != 2 || DeathlinkState >= 2) && ((int)lossType != 1 || DeathlinkState >= 1)) { deathLinkService.SendDeathLink(new DeathLink(session.Players.GetPlayerName(session.ConnectionInfo.Slot), "Let villager " + (((int)lossType == 2) ? "leave" : "perish") + ".")); } } private static void HandleItemReceived(string itemName) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!Constants.ITEM_DICT.ContainsKey(itemName) || !GameMB.IsGameActive) { return; } string text = GoodsTypesExtensions.ToName(Constants.ITEM_DICT[itemName]); if (!HasReceivedItem(text)) { if (OriginalGoodIcons.Keys.Contains(text)) { MB.Settings.GetGood(text).icon = OriginalGoodIcons[text]; } SO.EffectsService.GrantRawGoodProduction(text, 999999); ItemsForNews.Add(itemName); } } public static IObservable<int> OnAPItemReceived(string itemName) { if (!itemCallbacks.ContainsKey(itemName)) { itemCallbacks.Add(itemName, new Subject<int>()); } return (IObservable<int>)itemCallbacks[itemName]; } } internal class Constants { public enum CustomHookType { APItemReceived = 9999 } public static Dictionary<string, GoodsTypes> ITEM_DICT = new Dictionary<string, GoodsTypes> { ["Berries"] = (GoodsTypes)16, ["Eggs"] = (GoodsTypes)17, ["Insects"] = (GoodsTypes)20, ["Meat"] = (GoodsTypes)21, ["Mushrooms"] = (GoodsTypes)22, ["Roots"] = (GoodsTypes)23, ["Vegetables"] = (GoodsTypes)24, ["Biscuits"] = (GoodsTypes)10, ["Jerky"] = (GoodsTypes)11, ["Pickled Goods"] = (GoodsTypes)12, ["Pie"] = (GoodsTypes)13, ["Porridge"] = (GoodsTypes)14, ["Skewers"] = (GoodsTypes)15, ["Coats"] = (GoodsTypes)43, ["Bricks"] = (GoodsTypes)26, ["Fabric"] = (GoodsTypes)27, ["Planks"] = (GoodsTypes)30, ["Pipes"] = (GoodsTypes)29, ["Parts"] = (GoodsTypes)28, ["Wildfire Essence"] = (GoodsTypes)25, ["Ale"] = (GoodsTypes)42, ["Incense"] = (GoodsTypes)44, ["Scrolls"] = (GoodsTypes)45, ["Tea"] = (GoodsTypes)47, ["Training Gear"] = (GoodsTypes)48, ["Wine"] = (GoodsTypes)49, ["Clay"] = (GoodsTypes)31, ["Copper Ore"] = (GoodsTypes)40, ["Crystallized Dew"] = (GoodsTypes)41, ["Grain"] = (GoodsTypes)18, ["Herbs"] = (GoodsTypes)19, ["Leather"] = (GoodsTypes)32, ["Plant Fiber"] = (GoodsTypes)33, ["Reeds"] = (GoodsTypes)34, ["Resin"] = (GoodsTypes)35, ["Stone"] = (GoodsTypes)37, ["Barrels"] = (GoodsTypes)59, ["Copper Bars"] = (GoodsTypes)39, ["Flour"] = (GoodsTypes)6, ["Pigment"] = (GoodsTypes)8, ["Pottery"] = (GoodsTypes)60, ["Waterskins"] = (GoodsTypes)61, ["Amber"] = (GoodsTypes)57, ["Pack of Building Materials"] = (GoodsTypes)50, ["Pack of Crops"] = (GoodsTypes)51, ["Pack of Luxury Goods"] = (GoodsTypes)52, ["Pack of Provisions"] = (GoodsTypes)53, ["Pack of Trade Goods"] = (GoodsTypes)54, ["Ancient Tablet"] = (GoodsTypes)58, ["Coal"] = (GoodsTypes)5, ["Oil"] = (GoodsTypes)7, ["Purging Fire"] = (GoodsTypes)4, ["Sea Marrow"] = (GoodsTypes)9, ["Tools"] = (GoodsTypes)56 }; public const int PRODUCTIVITY_MODIFIER = 999999; public const string AP_PROD_EFFECT_NAME = "_AP_ProductionBlocker"; public const string AP_PROD_EFFECT_CONTROL_NAME = "_AP_ProductionControl"; public const string AP_GAME_NAME = "Against the Storm"; public const long RECIPE_SHUFFLE_VANILLA = 0L; public const long RECIPE_SHUFFLE_EXCLUDE_CRUDE_WS = 1L; public const long RECIPE_SHUFFLE_FULL_SHUFFLE = 2L; public const long DEATHLINK_OFF = 0L; public const long DEATHLINK_DEATH_ONLY = 1L; public const long DEATHLINK_LEAVE_AND_DEATH = 2L; public const string DEATHLINK_REASON = "AP Deathlink"; } [BepInPlugin("Ryguy9999.ATS.ATSForAP", "Ryguy9999.ATS.ATSForAP", "0.3.2")] public class Plugin : BaseUnityPlugin { public static Plugin Instance; private Harmony harmony; private static APItemReceivedMonitor apItemReceivedMonitor = new APItemReceivedMonitor(); private void Awake() { Instance = this; harmony = Harmony.CreateAndPatchAll(typeof(Plugin), (string)null); harmony.PatchAll(typeof(StoragePatch)); harmony.PatchAll(typeof(VillagerPatch)); Plugin.Log.LogInfo((object)"Plugin Ryguy9999.ATS.ATSForAP loaded."); } [HarmonyPatch(typeof(MainController), "OnServicesReady")] [HarmonyPostfix] private static void HookMainControllerSetup() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) MB.Controller.Build.type = (BuildType)0; } [HarmonyPatch(typeof(MainController), "InitReferences")] [HarmonyPostfix] private static void PostSetupMainController() { EffectBuilder<ArchipelagoEffectModel> val = new EffectBuilder<ArchipelagoEffectModel>("Ryguy9999.ATS.ATSForAP", "Archipelago", "ap-icon.png"); val.SetRarity((EffectRarity)5); val.SetPositive(false); val.SetLabel("Core Archipelago Effect", (SystemLanguage)10); val.SetDisplayName("Aura of the Archipelago", (SystemLanguage)10); val.SetDescription("The storm's flooding has given us these islands. Resources behave in unusual and scarce ways here..."); } [HarmonyPatch(typeof(GameController), "StartGame")] [HarmonyPostfix] private static void HookEveryGameStart() { ArchipelagoService.EnterGame(); } [HarmonyPatch(typeof(HookedEffectsController), "GetMonitorFor")] [HarmonyFinalizer] private static Exception HookedEffectsController_GetMonitorFor_Finalizer(Exception __exception, HookLogicType type, ref IHookMonitor __result) { //IL_000f: 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_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 if (__exception is NotImplementedException) { if ((int)type == 9999) { __result = (IHookMonitor)(object)apItemReceivedMonitor; return null; } throw __exception; } return __exception; } } internal class StoragePatch { [HarmonyPrefix] [HarmonyPatch(typeof(StorageService), "Store")] [HarmonyPatch(new Type[] { typeof(Good), typeof(string), typeof(int), typeof(StorageOperationType) })] private static bool StorePrefix(Good good, string ownerModel, int ownerId, StorageOperationType type) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) bool flag = false; foreach (KeyValuePair<string, GoodsTypes> item in Constants.ITEM_DICT) { if (good.name == GoodsTypesExtensions.ToName(item.Value)) { flag = true; break; } } StorageOperationType[] array = new StorageOperationType[7]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); StorageOperationType[] array2 = (StorageOperationType[])(object)array; if (!flag || ArchipelagoService.HasReceivedItem(good.name) || Array.IndexOf(array2, type) > -1) { return true; } return false; } [HarmonyPostfix] [HarmonyPatch(typeof(StorageService), "Store")] [HarmonyPatch(new Type[] { typeof(Good), typeof(string), typeof(int), typeof(StorageOperationType) })] private static void StorePostfix(Good good, string ownerModel, int ownerId, StorageOperationType type) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) if (CheckForTradeLocation(good.name, GoodsTypesExtensions.ToName((GoodsTypes)57), 10) && ArchipelagoService.CheckLocation("Trade - 10 Amber")) { GameMB.StorageService.Remove(new Good(GoodsTypesExtensions.ToName((GoodsTypes)57), 10), (StorageOperationType)24); } if (CheckForTradeLocation(good.name, GoodsTypesExtensions.ToName((GoodsTypes)57), 100) && ArchipelagoService.CheckLocation("Trade - 100 Amber")) { GameMB.StorageService.Remove(new Good(GoodsTypesExtensions.ToName((GoodsTypes)57), 100), (StorageOperationType)24); } if (CheckForTradeLocation(good.name, GoodsTypesExtensions.ToName((GoodsTypes)57), 1000) && ArchipelagoService.CheckLocation("Trade - 1000 Amber")) { GameMB.StorageService.Remove(new Good(GoodsTypesExtensions.ToName((GoodsTypes)57), 1000), (StorageOperationType)24); } if (CheckForTradeLocation(good.name, GoodsTypesExtensions.ToName((GoodsTypes)50), 20) && ArchipelagoService.CheckLocation("Trade - 20 Packs of Building Materials")) { GameMB.StorageService.Remove(new Good(GoodsTypesExtensions.ToName((GoodsTypes)50), 20), (StorageOperationType)24); } if (CheckForTradeLocation(good.name, GoodsTypesExtensions.ToName((GoodsTypes)51), 20) && ArchipelagoService.CheckLocation("Trade - 20 Packs of Crops")) { GameMB.StorageService.Remove(new Good(GoodsTypesExtensions.ToName((GoodsTypes)51), 20), (StorageOperationType)24); } if (CheckForTradeLocation(good.name, GoodsTypesExtensions.ToName((GoodsTypes)53), 20) && ArchipelagoService.CheckLocation("Trade - 20 Packs of Provisions")) { GameMB.StorageService.Remove(new Good(GoodsTypesExtensions.ToName((GoodsTypes)53), 20), (StorageOperationType)24); } if (CheckForTradeLocation(good.name, GoodsTypesExtensions.ToName((GoodsTypes)52), 20) && ArchipelagoService.CheckLocation("Trade - 20 Packs of Luxury Goods")) { GameMB.StorageService.Remove(new Good(GoodsTypesExtensions.ToName((GoodsTypes)52), 20), (StorageOperationType)24); } if (CheckForTradeLocation(good.name, GoodsTypesExtensions.ToName((GoodsTypes)54), 20) && ArchipelagoService.CheckLocation("Trade - 20 Packs of Trade Goods")) { GameMB.StorageService.Remove(new Good(GoodsTypesExtensions.ToName((GoodsTypes)54), 20), (StorageOperationType)24); } if (CheckForTradeLocation(good.name, GoodsTypesExtensions.ToName((GoodsTypes)63), 200) && ArchipelagoService.CheckLocation("Trade - 200 Drizzle Water")) { GameMB.StorageService.Remove(new Good(GoodsTypesExtensions.ToName((GoodsTypes)63), 200), (StorageOperationType)24); } if (CheckForTradeLocation(good.name, GoodsTypesExtensions.ToName((GoodsTypes)62), 200) && ArchipelagoService.CheckLocation("Trade - 200 Clearance Water")) { GameMB.StorageService.Remove(new Good(GoodsTypesExtensions.ToName((GoodsTypes)62), 200), (StorageOperationType)24); } if (CheckForTradeLocation(good.name, GoodsTypesExtensions.ToName((GoodsTypes)64), 200) && ArchipelagoService.CheckLocation("Trade - 200 Storm Water")) { GameMB.StorageService.Remove(new Good(GoodsTypesExtensions.ToName((GoodsTypes)64), 200), (StorageOperationType)24); } } [HarmonyPostfix] [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPatch(new Type[] { typeof(WorkshopRecipeModel), typeof(int), typeof(bool), typeof(bool), typeof(bool) })] private static void WorkshopRecipeStatePostfix(WorkshopRecipeState __instance, WorkshopRecipeModel model, int limit, bool firstIngredientEnabled, bool secondaryIngredientsEnabled, bool allRecipesEnabled) { ((RecipeState)__instance).active = ArchipelagoService.HasReceivedItem(model.producedGood.Name); } private static bool CheckForTradeLocation(string incomingGood, string tradeGood, int amount) { if (incomingGood != tradeGood) { return false; } if (GameMB.StorageService.GetAmount(incomingGood) < amount) { return false; } return true; } } [HarmonyPatch(typeof(Villager))] internal class VillagerPatch { [HarmonyPostfix] [HarmonyPatch("Die")] [HarmonyPatch(new Type[] { typeof(VillagerLossType), typeof(string), typeof(bool), typeof(float), typeof(SoundModel) })] private static void DiePostfix(VillagerLossType lossType, string reasonKey, bool showDying = true, float duration = 25f, SoundModel extraSound = null) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) ArchipelagoService.HandleVillagerDeath(lossType, reasonKey); } [HarmonyPostfix] [HarmonyPatch("Leave")] private static void LeavePostfix() { ArchipelagoService.HandleVillagerDeath((VillagerLossType)2, ""); } } public static class PluginInfo { public const string PLUGIN_GUID = "Ryguy9999.ATS.ATSForAP"; public const string PLUGIN_NAME = "Ryguy9999.ATS.ATSForAP"; public const string PLUGIN_VERSION = "0.3.2"; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }