Decompiled source of Against The Storm for Archipelago v1.2.0
Archipelago.Gifting.Net.dll
Decompiled a month agousing System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.Linq; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using System.Threading.Tasks; using Archipelago.Gifting.Net.Service.Result; using Archipelago.Gifting.Net.Service.TraitAcceptance; using Archipelago.Gifting.Net.Utilities; using Archipelago.Gifting.Net.Versioning; using Archipelago.Gifting.Net.Versioning.GiftBoxes; using Archipelago.Gifting.Net.Versioning.GiftBoxes.Current; using Archipelago.Gifting.Net.Versioning.GiftBoxes.Version2; using Archipelago.Gifting.Net.Versioning.Gifts; using Archipelago.Gifting.Net.Versioning.Gifts.Current; using Archipelago.Gifting.Net.Versioning.Gifts.Version1; using Archipelago.Gifting.Net.Versioning.Gifts.Version2; using Archipelago.MultiClient.Net; using Archipelago.MultiClient.Net.Enums; using Archipelago.MultiClient.Net.Helpers; using Archipelago.MultiClient.Net.Models; using Newtonsoft.Json; using Newtonsoft.Json.Linq; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: ComVisible(false)] [assembly: Guid("d87ec5d2-c3db-4f65-897d-8068f3e2da7d")] [assembly: InternalsVisibleTo("Archipelago.Gifting.Net.Tests")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("KaitoKid")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription(".NET library for Archipelago Gifting")] [assembly: AssemblyFileVersion("0.4.3")] [assembly: AssemblyInformationalVersion("0.4.3+0939ad9dbf643d73b4c0f49fac03d54700d4fa80")] [assembly: AssemblyProduct("Archipelago.Gifting.Net")] [assembly: AssemblyTitle("Archipelago.Gifting.Net")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/agilbert1412/Archipelago.Gifting.Net")] [assembly: AssemblyVersion("0.4.3.0")] namespace Archipelago.Gifting.Net.Utilities { internal class PlayerProvider { private ArchipelagoSession _session; public PlayerInfo CurrentPlayer => GetPlayer(_session.ConnectionInfo.Slot); public string CurrentPlayerName => CurrentPlayer.Name; public int CurrentPlayerTeam => _session.ConnectionInfo.Team; public int CurrentPlayerSlot => _session.ConnectionInfo.Slot; public string CurrentPlayerGame => _session.ConnectionInfo.Game; public PlayerProvider(ArchipelagoSession session) { _session = session; } public bool TryGetPlayer(string playerName, out PlayerInfo player) { return TryGetPlayer(playerName, CurrentPlayerTeam, out player); } public bool TryGetPlayer(string playerName, int playerTeam, out PlayerInfo player) { foreach (PlayerInfo item in _session.Players.Players[playerTeam]) { if (item.Name == playerName) { player = item; return true; } } player = null; int num = 0; foreach (PlayerInfo item2 in _session.Players.Players[playerTeam]) { Match match = Regex.Match(item2.Alias, "(.+) \\((.+)\\)"); if (match.Success && match.Groups.Count >= 3 && match.Groups[1].Value == playerName) { num++; player = item2; } } return num == 1; } public PlayerInfo GetPlayer(string playerName) { return GetPlayer(playerName, CurrentPlayerTeam); } public PlayerInfo GetPlayer(string playerName, int playerTeam) { return _session.Players.Players[playerTeam].First((PlayerInfo player) => player.Name == playerName); } public PlayerInfo GetPlayer(int playerSlot) { return GetPlayer(playerSlot, CurrentPlayerTeam); } public PlayerInfo GetPlayer(int playerSlot, int playerTeam) { return _session.Players.Players[playerTeam].First((PlayerInfo player) => player.Slot == playerSlot); } public IEnumerable<int> GetAllTeams() { return _session.Players.Players.Keys; } public IEnumerable<int> GetAllPlayerSlotsInTeam(int playerTeam) { return _session.Players.Players[playerTeam].Select((PlayerInfo x) => x.Slot); } } } namespace Archipelago.Gifting.Net.Utilities.CloseTraitParser { public class BKTreeCloseTraitParser<T> : ICloseTraitParser<T> { public delegate double DistanceDelegate(Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] giftTraits, Dictionary<string, Tuple<double, double>> traits, out bool isCompatible); private readonly List<T> _items; private readonly Dictionary<string, Tuple<double, double>> _traits; private readonly Dictionary<double, BKTreeCloseTraitParser<T>> _children; private readonly DistanceDelegate _distance; public BKTreeCloseTraitParser(DistanceDelegate distanceDelegate = null) { _items = new List<T>(); _traits = new Dictionary<string, Tuple<double, double>>(); _children = new Dictionary<double, BKTreeCloseTraitParser<T>>(); _distance = distanceDelegate ?? new DistanceDelegate(DefaultDistance); } private static double DefaultDistance(Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] giftTraits, Dictionary<string, Tuple<double, double>> traits, out bool isCompatible) { Dictionary<string, Tuple<double, double>> dictionary = new Dictionary<string, Tuple<double, double>>(traits); double num = 0.0; foreach (Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait giftTrait in giftTraits) { if (dictionary.TryGetValue(giftTrait.Trait, out var value)) { dictionary.Remove(giftTrait.Trait); if (value.Item1 * giftTrait.Quality <= 0.0) { num += 1.0; } else { double num2 = value.Item1 / giftTrait.Quality; num += 1.0 - ((num2 > 1.0) ? (1.0 / num2) : num2); } if (value.Item2 * giftTrait.Duration <= 0.0) { num += 1.0; continue; } double num3 = value.Item2 / giftTrait.Duration; num += 1.0 - ((num3 > 1.0) ? (1.0 / num3) : num3); } else { num += 1.0; } } num += (double)dictionary.Count; isCompatible = dictionary.Count != traits.Count; return num; } public void RegisterAvailableGift(T availableGift, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits) { if (_items.Count == 0) { _items.Add(availableGift); foreach (Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait giftTrait in traits) { if (_traits.TryGetValue(giftTrait.Trait, out var value)) { _traits[giftTrait.Trait] = new Tuple<double, double>(value.Item1 + giftTrait.Quality, value.Item2 + giftTrait.Duration); } else { _traits[giftTrait.Trait] = new Tuple<double, double>(giftTrait.Quality, giftTrait.Duration); } } return; } bool isCompatible; double num = _distance(traits, _traits, out isCompatible); if (num == 0.0) { _items.Add(availableGift); return; } if (!_children.TryGetValue(num, out var value2)) { value2 = new BKTreeCloseTraitParser<T>(_distance); _children[num] = value2; } value2.RegisterAvailableGift(availableGift, traits); } private void FindClosestAvailableGift(Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] giftTraits, ref double bestDistance, ref List<T> closestItems) { bool isCompatible; double num = _distance(giftTraits, _traits, out isCompatible); if (isCompatible) { if (Math.Abs(num - bestDistance) < 0.0001) { closestItems.AddRange(_items); } else if (num < bestDistance) { closestItems.Clear(); closestItems.AddRange(_items); bestDistance = num; } } foreach (KeyValuePair<double, BKTreeCloseTraitParser<T>> child in _children) { if (num - child.Key < bestDistance + 0.0001) { child.Value.FindClosestAvailableGift(giftTraits, ref bestDistance, ref closestItems); } } } public List<T> FindClosestAvailableGift(Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] giftTraits) { List<T> closestItems = new List<T>(); double bestDistance = double.MaxValue; FindClosestAvailableGift(giftTraits, ref bestDistance, ref closestItems); return closestItems; } } public interface ICloseTraitParser<T> { void RegisterAvailableGift(T availableGift, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits); List<T> FindClosestAvailableGift(Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits); } } namespace Archipelago.Gifting.Net.Traits { public class GiftFlag { public const string None = "None"; public const string Speed = "Speed"; public const string Consumable = "Consumable"; public const string Food = "Food"; public const string Drink = "Drink"; public const string Heal = "Heal"; public const string Mana = "Mana"; public const string Key = "Key"; public const string Trap = "Trap"; public const string Buff = "Buff"; public const string Life = "Life"; public const string Weapon = "Weapon"; public const string Armor = "Armor"; public const string Tool = "Tool"; public const string Fish = "Fish"; public const string Animal = "Animal"; public const string Cure = "Cure"; public const string Seed = "Seed"; public const string Metal = "Metal"; public const string Bomb = "Bomb"; public const string Monster = "Monster"; public const string Resource = "Resource"; public const string Material = "Material"; public const string Wood = "Wood"; public const string Stone = "Stone"; public const string Ore = "Ore"; public const string Grass = "Grass"; public const string Meat = "Meat"; public const string Vegetable = "Vegetable"; public const string Fruit = "Fruit"; public const string Egg = "Egg"; public const string Slowness = "Slowness"; public const string Currency = "Currency"; public const string Damage = "Damage"; public const string Fire = "Fire"; public const string Ice = "Ice"; public const string Energy = "Energy"; public const string Light = "Light"; public static readonly string[] AllFlags = new string[37] { "Speed", "Consumable", "Food", "Drink", "Heal", "Mana", "Key", "Trap", "Buff", "Life", "Weapon", "Armor", "Tool", "Fish", "Animal", "Cure", "Seed", "Metal", "Bomb", "Monster", "Resource", "Material", "Wood", "Stone", "Ore", "Grass", "Meat", "Vegetable", "Fruit", "Slowness", "Currency", "Damage", "Fire", "Ice", "Egg", "Energy", "Light" }; } } namespace Archipelago.Gifting.Net.Versioning { internal static class DataVersion { public const int GIFT_DATA_VERSION_1 = 1; public const int GIFT_DATA_VERSION_2 = 2; public const int GIFT_DATA_VERSION_3 = 3; public static int FirstVersion => 1; public static int Current => 3; } internal interface IVersionedConverter<TKey, TCurrentObject, TPreviousObject> { int Version { get; } int PreviousVersion { get; } Dictionary<TKey, TCurrentObject> ReadFromDataStorage(DataStorageElement element); Dictionary<TKey, TCurrentObject> ReadFromDataStorage(JToken element); TCurrentObject ConvertToCurrentVersion(TPreviousObject olderItem); TPreviousObject ConvertToPreviousVersion(TCurrentObject currentItem); IDictionary CreateDataStorageUpdateEntry(TKey key, TCurrentObject item, int version); } } namespace Archipelago.Gifting.Net.Versioning.GiftBoxes { internal class GiftBoxKeyProvider { public const string MOTHERBOX_KEY_FORMAT = "GiftBoxes;{0}"; public const string GIFTBOX_KEY_FORMAT = "GiftBox;{0};{1}"; private ArchipelagoSession _session; private PlayerProvider _playerProvider; public GiftBoxKeyProvider(ArchipelagoSession session, PlayerProvider playerProvider) { _session = session; _playerProvider = playerProvider; } public string GetMotherBoxDataStorageKey() { return GetMotherBoxDataStorageKey(_playerProvider.CurrentPlayerTeam); } public string GetMotherBoxDataStorageKey(int playerTeam) { return $"GiftBoxes;{playerTeam}"; } public string GetGiftBoxDataStorageKey() { return GetGiftBoxDataStorageKey(_playerProvider.CurrentPlayerSlot); } public string GetGiftBoxDataStorageKey(int playerSlot) { return GetGiftBoxDataStorageKey(_playerProvider.CurrentPlayerTeam, playerSlot); } public string GetGiftBoxDataStorageKey(int playerTeam, int playerSlot) { return $"GiftBox;{playerTeam};{playerSlot}"; } } internal interface IVersionedGiftBoxConverter<TCurrentGiftBox, TPreviousGiftBox> : IVersionedConverter<int, TCurrentGiftBox, TPreviousGiftBox> { } } namespace Archipelago.Gifting.Net.Versioning.GiftBoxes.Version2 { internal class GiftBox { public bool IsOpen { get; set; } public bool AcceptsAnyGift { get; set; } public string[] DesiredTraits { get; set; } public int MinimumGiftDataVersion { get; set; } public int MaximumGiftDataVersion { get; set; } internal GiftBox() { } internal GiftBox(bool isOpen) : this() { IsOpen = isOpen; AcceptsAnyGift = true; DesiredTraits = new string[0]; MinimumGiftDataVersion = DataVersion.FirstVersion; MaximumGiftDataVersion = 2; } internal GiftBox(bool acceptsAnyGift, string[] desiredTraits) : this(isOpen: true) { AcceptsAnyGift = acceptsAnyGift; DesiredTraits = desiredTraits; } } internal class GiftBoxConverter : IVersionedGiftBoxConverter<GiftBox, object>, IVersionedConverter<int, GiftBox, object> { public int Version => 1; public int PreviousVersion => Version - 1; public Dictionary<int, GiftBox> ReadFromDataStorage(DataStorageElement element) { try { return element.To<Dictionary<int, GiftBox>>() ?? new Dictionary<int, GiftBox>(); } catch (Exception) { return new Dictionary<int, GiftBox>(); } } public Dictionary<int, GiftBox> ReadFromDataStorage(JToken element) { try { return element.ToObject<Dictionary<int, GiftBox>>() ?? new Dictionary<int, GiftBox>(); } catch (Exception) { return new Dictionary<int, GiftBox>(); } } public IDictionary CreateDataStorageUpdateEntry(int ownerSlot, GiftBox giftBox, int version) { if (version < Version) { throw new VersionNotFoundException($"Tried to create a giftBox for an unknown version: {version}"); } return new Dictionary<int, GiftBox> { { ownerSlot, giftBox } }; } public GiftBox ConvertToCurrentVersion(object olderGiftBox) { throw new NotImplementedException(); } public object ConvertToPreviousVersion(GiftBox currentGiftBox) { throw new NotImplementedException(); } } } namespace Archipelago.Gifting.Net.Versioning.GiftBoxes.Current { public class GiftBox { [JsonProperty("is_open")] public bool IsOpen { get; set; } [JsonProperty("accepts_any_gift")] public bool AcceptsAnyGift { get; set; } [JsonProperty("desired_traits")] public string[] DesiredTraits { get; set; } [JsonProperty("minimum_gift_data_version")] public int MinimumGiftDataVersion { get; set; } [JsonProperty("maximum_gift_data_version")] public int MaximumGiftDataVersion { get; set; } public GiftBox() { } internal GiftBox(bool isOpen) : this() { IsOpen = isOpen; AcceptsAnyGift = true; DesiredTraits = new string[0]; MinimumGiftDataVersion = DataVersion.FirstVersion; MaximumGiftDataVersion = DataVersion.Current; } internal GiftBox(bool acceptsAnyGift, string[] desiredTraits) : this(isOpen: true) { AcceptsAnyGift = acceptsAnyGift; DesiredTraits = desiredTraits; } } internal class GiftBoxConverter : IVersionedGiftBoxConverter<GiftBox, Archipelago.Gifting.Net.Versioning.GiftBoxes.Version2.GiftBox>, IVersionedConverter<int, GiftBox, Archipelago.Gifting.Net.Versioning.GiftBoxes.Version2.GiftBox> { private IVersionedGiftBoxConverter<Archipelago.Gifting.Net.Versioning.GiftBoxes.Version2.GiftBox, object> _previousConverter; public int Version => 3; public int PreviousVersion => 2; public GiftBoxConverter() { _previousConverter = new Archipelago.Gifting.Net.Versioning.GiftBoxes.Version2.GiftBoxConverter(); } public Dictionary<int, GiftBox> ReadFromDataStorage(DataStorageElement element) { try { Dictionary<int, GiftBox> dictionary = element.To<Dictionary<int, GiftBox>>() ?? new Dictionary<int, GiftBox>(); Dictionary<int, Archipelago.Gifting.Net.Versioning.GiftBoxes.Version2.GiftBox> dictionary2 = _previousConverter.ReadFromDataStorage(element); foreach (int key in dictionary2.Keys) { try { if (!dictionary.ContainsKey(key) || HasInvalidFields(dictionary[key])) { dictionary[key] = ConvertToCurrentVersion(dictionary2[key]); } } catch (Exception) { } } return dictionary; } catch (Exception) { return new Dictionary<int, GiftBox>(); } } public Dictionary<int, GiftBox> ReadFromDataStorage(JToken element) { try { Dictionary<int, GiftBox> dictionary = element.ToObject<Dictionary<int, GiftBox>>() ?? new Dictionary<int, GiftBox>(); Dictionary<int, Archipelago.Gifting.Net.Versioning.GiftBoxes.Version2.GiftBox> dictionary2 = _previousConverter.ReadFromDataStorage(element); foreach (int key in dictionary2.Keys) { try { if (!dictionary.ContainsKey(key) || HasInvalidFields(dictionary[key])) { dictionary[key] = ConvertToCurrentVersion(dictionary2[key]); } } catch (Exception) { } } return dictionary; } catch (Exception) { return new Dictionary<int, GiftBox>(); } } private bool HasInvalidFields(GiftBox giftBox) { if (giftBox != null && giftBox.MinimumGiftDataVersion >= DataVersion.FirstVersion) { return giftBox.MaximumGiftDataVersion < DataVersion.FirstVersion; } return true; } public IDictionary CreateDataStorageUpdateEntry(int ownerSlot, GiftBox giftBox, int version) { if (version < Version) { return _previousConverter.CreateDataStorageUpdateEntry(ownerSlot, ConvertToPreviousVersion(giftBox), version); } return new Dictionary<int, GiftBox> { { ownerSlot, giftBox } }; } public GiftBox ConvertToCurrentVersion(Archipelago.Gifting.Net.Versioning.GiftBoxes.Version2.GiftBox olderGift) { return new GiftBox { AcceptsAnyGift = olderGift.AcceptsAnyGift, DesiredTraits = olderGift.DesiredTraits, IsOpen = olderGift.IsOpen, MaximumGiftDataVersion = olderGift.MaximumGiftDataVersion, MinimumGiftDataVersion = olderGift.MinimumGiftDataVersion }; } public Archipelago.Gifting.Net.Versioning.GiftBoxes.Version2.GiftBox ConvertToPreviousVersion(GiftBox currentGift) { return new Archipelago.Gifting.Net.Versioning.GiftBoxes.Version2.GiftBox { AcceptsAnyGift = currentGift.AcceptsAnyGift, DesiredTraits = currentGift.DesiredTraits, IsOpen = currentGift.IsOpen, MaximumGiftDataVersion = currentGift.MaximumGiftDataVersion, MinimumGiftDataVersion = currentGift.MinimumGiftDataVersion }; } } } namespace Archipelago.Gifting.Net.Versioning.Gifts { public class GiftItem { public string Name { get; set; } public int Amount { get; set; } public BigInteger Value { get; set; } public GiftItem(string name, int amount, BigInteger value) { Name = name; Amount = amount; Value = value; } } internal interface IVersionedGiftConverter<TCurrentGift, TPreviousGift> : IVersionedConverter<string, TCurrentGift, TPreviousGift> { } } namespace Archipelago.Gifting.Net.Versioning.Gifts.Version2 { internal class Gift { public string ID { get; set; } public string ItemName { get; set; } public int Amount { get; set; } public BigInteger ItemValue { get; set; } public GiftTrait[] Traits { get; set; } public int SenderSlot { get; set; } public int ReceiverSlot { get; set; } public int SenderTeam { get; set; } public int ReceiverTeam { get; set; } public bool IsRefund { get; set; } public Gift() { } public Gift(string itemName, int amount, BigInteger itemValue, GiftTrait[] traits, int senderSlot, int receiverSlot, int senderTeam, int receiverTeam) { ID = Guid.NewGuid().ToString(); ItemName = itemName; Amount = amount; ItemValue = itemValue; Traits = traits; SenderSlot = senderSlot; ReceiverSlot = receiverSlot; SenderTeam = senderTeam; ReceiverTeam = receiverTeam; IsRefund = false; } } internal class GiftConverter : IVersionedGiftConverter<Gift, Archipelago.Gifting.Net.Versioning.Gifts.Version1.Gift>, IVersionedConverter<string, Gift, Archipelago.Gifting.Net.Versioning.Gifts.Version1.Gift> { private PlayerProvider _playerProvider; private Validator _validator; private IVersionedGiftConverter<Archipelago.Gifting.Net.Versioning.Gifts.Version1.Gift, object> _previousConverter; public int Version => 2; public int PreviousVersion => 1; public GiftConverter(PlayerProvider playerProvider) { _playerProvider = playerProvider; _validator = new Validator(); _previousConverter = new Archipelago.Gifting.Net.Versioning.Gifts.Version1.GiftConverter(); } public Dictionary<string, Gift> ReadFromDataStorage(DataStorageElement element) { try { Dictionary<string, Gift> dictionary = element.To<Dictionary<string, Gift>>() ?? new Dictionary<string, Gift>(); if (_validator.Validate(dictionary, out var errors)) { return dictionary; } foreach (KeyValuePair<string, Archipelago.Gifting.Net.Versioning.Gifts.Version1.Gift> item in _previousConverter.ReadFromDataStorage(element)) { try { string key = item.Key; Archipelago.Gifting.Net.Versioning.Gifts.Version1.Gift value = item.Value; if (!dictionary.ContainsKey(key) || errors.Contains(key)) { dictionary[key] = ConvertToCurrentVersion(value); } } catch (Exception) { } } return dictionary; } catch (Exception) { return new Dictionary<string, Gift>(); } } public Dictionary<string, Gift> ReadFromDataStorage(JToken element) { try { Dictionary<string, Gift> dictionary = element.ToObject<Dictionary<string, Gift>>() ?? new Dictionary<string, Gift>(); if (_validator.Validate(dictionary, out var errors)) { return dictionary; } foreach (KeyValuePair<string, Archipelago.Gifting.Net.Versioning.Gifts.Version1.Gift> item in _previousConverter.ReadFromDataStorage(element)) { string key = item.Key; Archipelago.Gifting.Net.Versioning.Gifts.Version1.Gift value = item.Value; if (!dictionary.ContainsKey(key) || errors.Contains(key)) { dictionary[key] = ConvertToCurrentVersion(value); } } return dictionary; } catch (Exception) { return new Dictionary<string, Gift>(); } } public IDictionary CreateDataStorageUpdateEntry(string id, Gift gift, int version) { if (version < Version) { return _previousConverter.CreateDataStorageUpdateEntry(id, ConvertToPreviousVersion(gift), version); } return new Dictionary<string, Gift> { { id, gift } }; } public Gift ConvertToCurrentVersion(Archipelago.Gifting.Net.Versioning.Gifts.Version1.Gift olderGift) { PlayerInfo player = _playerProvider.GetPlayer(olderGift.SenderName, olderGift.SenderTeam); PlayerInfo player2 = _playerProvider.GetPlayer(olderGift.ReceiverName, olderGift.ReceiverTeam); return new Gift(olderGift.Item.Name, olderGift.Item.Amount, olderGift.Item.Value, ConvertTraits(olderGift.Traits), player.Slot, player2.Slot, olderGift.SenderTeam, olderGift.ReceiverTeam) { ID = olderGift.ID }; } public Archipelago.Gifting.Net.Versioning.Gifts.Version1.Gift ConvertToPreviousVersion(Gift currentGift) { GiftItem item = new GiftItem(currentGift.ItemName, currentGift.Amount, currentGift.ItemValue); PlayerInfo player = _playerProvider.GetPlayer(currentGift.SenderSlot, currentGift.SenderTeam); return new Archipelago.Gifting.Net.Versioning.Gifts.Version1.Gift(receiverName: _playerProvider.GetPlayer(currentGift.ReceiverSlot, currentGift.ReceiverTeam).Name, item: item, traits: ConvertTraits(currentGift.Traits), senderName: player.Name, senderTeam: currentGift.SenderTeam, receiverTeam: currentGift.ReceiverTeam) { ID = currentGift.ID }; } private GiftTrait[] ConvertTraits(Archipelago.Gifting.Net.Versioning.Gifts.Version1.GiftTrait[] olderGiftTraits) { return olderGiftTraits.Select((Archipelago.Gifting.Net.Versioning.Gifts.Version1.GiftTrait x) => new GiftTrait(x.Trait, x.Duration, x.Quality)).ToArray(); } private Archipelago.Gifting.Net.Versioning.Gifts.Version1.GiftTrait[] ConvertTraits(GiftTrait[] newerGiftTraits) { return newerGiftTraits.Select((GiftTrait x) => new Archipelago.Gifting.Net.Versioning.Gifts.Version1.GiftTrait(x.Trait, x.Duration, x.Quality)).ToArray(); } } internal class GiftTrait { public string Trait { get; set; } public double Quality { get; set; } public double Duration { get; set; } public GiftTrait(string trait, double duration, double quality) { Trait = trait; Quality = quality; Duration = duration; } } internal class Validator { public bool Validate(Dictionary<string, Gift> gifts, out IList<string> errors) { errors = new List<string>(); if (gifts == null) { return false; } bool result = true; foreach (KeyValuePair<string, Gift> gift in gifts) { if (!Validate(gift.Value)) { errors.Add(gift.Key); result = false; } } return result; } public bool Validate(Gift gift) { if (gift == null) { return false; } if (gift.ItemName == null || gift.Amount <= 0 || gift.Traits == null) { return false; } return true; } } } namespace Archipelago.Gifting.Net.Versioning.Gifts.Version1 { internal class Gift { public string ID { get; set; } public GiftItem Item { get; set; } public GiftTrait[] Traits { get; set; } public string SenderName { get; set; } public string ReceiverName { get; set; } public int SenderTeam { get; set; } public int ReceiverTeam { get; set; } public bool IsRefund { get; set; } public BigInteger GiftValue => Item.Amount * Item.Value; public Gift() { } public Gift(GiftItem item, GiftTrait[] traits, string senderName, string receiverName, int senderTeam, int receiverTeam) { ID = Guid.NewGuid().ToString(); Item = item; Traits = traits; SenderName = senderName; ReceiverName = receiverName; SenderTeam = senderTeam; ReceiverTeam = receiverTeam; IsRefund = false; } } internal class GiftConverter : IVersionedGiftConverter<Gift, object>, IVersionedConverter<string, Gift, object> { public int Version => 1; public int PreviousVersion => Version - 1; public Dictionary<string, Gift> ReadFromDataStorage(DataStorageElement element) { try { return (element.To<Dictionary<string, Gift>>() ?? new Dictionary<string, Gift>()).ToDictionary((KeyValuePair<string, Gift> x) => x.Key.ToString(), (KeyValuePair<string, Gift> x) => x.Value); } catch (Exception) { return new Dictionary<string, Gift>(); } } public Dictionary<string, Gift> ReadFromDataStorage(JToken element) { try { return (element.ToObject<Dictionary<string, Gift>>() ?? new Dictionary<string, Gift>()).ToDictionary((KeyValuePair<string, Gift> x) => x.Key.ToString(), (KeyValuePair<string, Gift> x) => x.Value); } catch (Exception) { return new Dictionary<string, Gift>(); } } public IDictionary CreateDataStorageUpdateEntry(string id, Gift gift, int version) { if (version < Version) { throw new VersionNotFoundException($"Tried to create a gift for an unknown version: {version}"); } return new Dictionary<Guid, Gift> { { Guid.Parse(id), gift } }; } public Gift ConvertToCurrentVersion(object olderGift) { throw new NotImplementedException(); } public object ConvertToPreviousVersion(Gift currentGift) { throw new NotImplementedException(); } } internal class GiftTrait { public string Trait { get; set; } public double Quality { get; set; } public double Duration { get; set; } public GiftTrait(string trait, double duration, double quality) { Trait = trait; Quality = quality; Duration = duration; } } } namespace Archipelago.Gifting.Net.Versioning.Gifts.Current { public class Gift { [JsonProperty("id")] public string ID { get; set; } [JsonProperty("item_name")] public string ItemName { get; set; } [JsonProperty("amount")] public int Amount { get; set; } [JsonProperty("item_value")] public BigInteger ItemValue { get; set; } [JsonProperty("traits")] public GiftTrait[] Traits { get; set; } [JsonProperty("sender_slot")] public int SenderSlot { get; set; } [JsonProperty("receiver_slot")] public int ReceiverSlot { get; set; } [JsonProperty("sender_team")] public int SenderTeam { get; set; } [JsonProperty("receiver_team")] public int ReceiverTeam { get; set; } [JsonProperty("is_refund")] public bool IsRefund { get; set; } public Gift() { } public Gift(string itemName, int amount, BigInteger itemValue, GiftTrait[] traits, int senderSlot, int receiverSlot, int senderTeam, int receiverTeam) { ID = Guid.NewGuid().ToString(); ItemName = itemName; Amount = amount; ItemValue = itemValue; Traits = traits; SenderSlot = senderSlot; ReceiverSlot = receiverSlot; SenderTeam = senderTeam; ReceiverTeam = receiverTeam; IsRefund = false; } } internal class GiftConverter : IVersionedGiftConverter<Gift, Archipelago.Gifting.Net.Versioning.Gifts.Version2.Gift>, IVersionedConverter<string, Gift, Archipelago.Gifting.Net.Versioning.Gifts.Version2.Gift> { private readonly Validator _validator; private readonly IVersionedGiftConverter<Archipelago.Gifting.Net.Versioning.Gifts.Version2.Gift, Archipelago.Gifting.Net.Versioning.Gifts.Version1.Gift> _previousConverter; public int Version => 3; public int PreviousVersion => 2; public GiftConverter(PlayerProvider playerProvider) { _validator = new Validator(); _previousConverter = new Archipelago.Gifting.Net.Versioning.Gifts.Version2.GiftConverter(playerProvider); } public Dictionary<string, Gift> ReadFromDataStorage(DataStorageElement element) { try { Dictionary<string, Gift> dictionary = element.To<Dictionary<string, Gift>>() ?? new Dictionary<string, Gift>(); if (_validator.Validate(dictionary, out var errors)) { return dictionary; } foreach (KeyValuePair<string, Archipelago.Gifting.Net.Versioning.Gifts.Version2.Gift> item in _previousConverter.ReadFromDataStorage(element)) { try { string key = item.Key; Archipelago.Gifting.Net.Versioning.Gifts.Version2.Gift value = item.Value; if (!dictionary.ContainsKey(key) || errors.Contains(key)) { dictionary[key] = ConvertToCurrentVersion(value); } } catch (Exception) { } } return dictionary; } catch (Exception) { return new Dictionary<string, Gift>(); } } public Dictionary<string, Gift> ReadFromDataStorage(JToken element) { try { Dictionary<string, Gift> dictionary = element.ToObject<Dictionary<string, Gift>>() ?? new Dictionary<string, Gift>(); if (_validator.Validate(dictionary, out var errors)) { return dictionary; } foreach (KeyValuePair<string, Archipelago.Gifting.Net.Versioning.Gifts.Version2.Gift> item in _previousConverter.ReadFromDataStorage(element)) { string key = item.Key; Archipelago.Gifting.Net.Versioning.Gifts.Version2.Gift value = item.Value; if (!dictionary.ContainsKey(key) || errors.Contains(key)) { dictionary[key] = ConvertToCurrentVersion(value); } } return dictionary; } catch (Exception) { return new Dictionary<string, Gift>(); } } public IDictionary CreateDataStorageUpdateEntry(string id, Gift gift, int version) { if (version < Version) { return _previousConverter.CreateDataStorageUpdateEntry(id, ConvertToPreviousVersion(gift), version); } JObject val = JObject.FromObject((object)gift); RemoveDefaultValueProperties((JToken)(object)val, new string[1] { "item_value" }, BigInteger.Zero); foreach (JProperty item in val.Properties()) { if (item.Name != "traits") { continue; } foreach (JToken item2 in (IEnumerable<JToken>)item.Value) { RemoveDefaultValueProperties(item2, new string[2] { "quality", "duration" }, 1.0); } } return new Dictionary<string, JObject> { { id, val } }; } private static void RemoveDefaultValueProperties(JToken trait, string[] propertiesToCheck, double valueToOmit) { foreach (string text in propertiesToCheck) { JToken val = trait[(object)text]; if (val != null && IsDefaultValue(val, valueToOmit)) { ((JToken)val.Parent).Remove(); } } } private static void RemoveDefaultValueProperties(JToken trait, string[] propertiesToCheck, BigInteger valueToOmit) { foreach (string text in propertiesToCheck) { JToken val = trait[(object)text]; if (val != null && IsDefaultValue(val, valueToOmit)) { ((JToken)val.Parent).Remove(); } } } private static bool IsDefaultValue(JToken quality, double defaultValue) { return Math.Abs(Extensions.Value<double>((IEnumerable<JToken>)quality) - defaultValue) < 0.001; } private static bool IsDefaultValue(JToken quality, BigInteger defaultValue) { return Extensions.Value<BigInteger>((IEnumerable<JToken>)quality) == defaultValue; } public Gift ConvertToCurrentVersion(Archipelago.Gifting.Net.Versioning.Gifts.Version2.Gift olderGift) { return new Gift(olderGift.ItemName, olderGift.Amount, olderGift.ItemValue, ConvertTraits(olderGift.Traits), olderGift.SenderSlot, olderGift.ReceiverSlot, olderGift.SenderTeam, olderGift.ReceiverTeam) { ID = olderGift.ID }; } public Archipelago.Gifting.Net.Versioning.Gifts.Version2.Gift ConvertToPreviousVersion(Gift currentGift) { return new Archipelago.Gifting.Net.Versioning.Gifts.Version2.Gift(currentGift.ItemName, currentGift.Amount, currentGift.ItemValue, ConvertTraits(currentGift.Traits), currentGift.SenderSlot, currentGift.ReceiverSlot, currentGift.SenderTeam, currentGift.ReceiverTeam) { ID = currentGift.ID }; } private GiftTrait[] ConvertTraits(Archipelago.Gifting.Net.Versioning.Gifts.Version2.GiftTrait[] olderGiftTraits) { return olderGiftTraits.Select((Archipelago.Gifting.Net.Versioning.Gifts.Version2.GiftTrait x) => new GiftTrait(x.Trait, x.Duration, x.Quality)).ToArray(); } private Archipelago.Gifting.Net.Versioning.Gifts.Version2.GiftTrait[] ConvertTraits(GiftTrait[] newerGiftTraits) { return newerGiftTraits.Select((GiftTrait x) => new Archipelago.Gifting.Net.Versioning.Gifts.Version2.GiftTrait(x.Trait, x.Duration, x.Quality)).ToArray(); } } public class GiftTrait { [JsonProperty("trait")] public string Trait { get; set; } [JsonProperty("quality")] public double Quality { get; set; } [JsonProperty("duration")] public double Duration { get; set; } public GiftTrait() : this(null) { } public GiftTrait(string trait) : this(trait, 1.0) { } public GiftTrait(string trait, double quality) : this(trait, quality, 1.0) { } public GiftTrait(string trait, double quality, double duration) { Trait = trait; Quality = quality; Duration = duration; } } internal class Validator { public bool Validate(Dictionary<string, Gift> gifts, out IList<string> errors) { errors = new List<string>(); if (gifts == null) { return false; } bool result = true; foreach (KeyValuePair<string, Gift> gift in gifts) { if (!Validate(gift.Value)) { errors.Add(gift.Key); result = false; } } return result; } public bool Validate(Gift gift) { if (gift == null) { return false; } if (gift.ItemName == null || gift.Amount <= 0 || gift.Traits == null) { return false; } return true; } } } namespace Archipelago.Gifting.Net.Service { public class GiftingService : IGiftingService, IGiftingServiceSync, IGiftingServiceAsync { public delegate void GiftReceivedHandler(Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift newGift); private ArchipelagoSession _session; private PlayerProvider _playerProvider; private GiftBoxKeyProvider _keyProvider; private Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBoxConverter _currentGiftBoxConverter; private Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftConverter _currentGiftConverter; private GiftReceivedHandler _giftReceivedHandler; private JToken EmptyMotherboxDictionary => JToken.FromObject((object)new Dictionary<int, Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox>()); private JToken EmptyGiftDictionary => JToken.FromObject((object)new Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift>()); internal PlayerProvider PlayerProvider => _playerProvider; public event GiftReceivedHandler OnNewGift { add { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown if (_giftReceivedHandler == null) { string giftBoxDataStorageKey = _keyProvider.GetGiftBoxDataStorageKey(); _session.DataStorage[giftBoxDataStorageKey].OnValueChanged += new DataStorageUpdatedHandler(GiftListener); } _giftReceivedHandler = (GiftReceivedHandler)Delegate.Combine(_giftReceivedHandler, value); } remove { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown _giftReceivedHandler = (GiftReceivedHandler)Delegate.Remove(_giftReceivedHandler, value); if (_giftReceivedHandler == null) { string giftBoxDataStorageKey = _keyProvider.GetGiftBoxDataStorageKey(); _session.DataStorage[giftBoxDataStorageKey].OnValueChanged -= new DataStorageUpdatedHandler(GiftListener); } } } public GiftingService(ArchipelagoSession session) { _session = session; _playerProvider = new PlayerProvider(_session); _keyProvider = new GiftBoxKeyProvider(_session, _playerProvider); _currentGiftBoxConverter = new Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBoxConverter(); _currentGiftConverter = new Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftConverter(_playerProvider); string motherBoxDataStorageKey = _keyProvider.GetMotherBoxDataStorageKey(); CreateMotherboxIfNeeded(motherBoxDataStorageKey); } public string GetMyGiftBoxKey() { return _keyProvider.GetGiftBoxDataStorageKey(); } public void OpenGiftBox() { UpdateGiftBox(new Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox(isOpen: true)); } public void OpenGiftBox(bool acceptAnyGift, string[] desiredTraits) { UpdateGiftBox(new Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox(acceptAnyGift, desiredTraits)); } public void CloseGiftBox() { UpdateGiftBox(new Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox(isOpen: false)); EmptyGiftBox(); } public Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox GetCurrentGiftBoxState() { int currentPlayerTeam = _playerProvider.CurrentPlayerTeam; int currentPlayerSlot = _playerProvider.CurrentPlayerSlot; Dictionary<int, Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox> motherbox = GetMotherbox(currentPlayerTeam); if (!motherbox.ContainsKey(currentPlayerSlot)) { return null; } return motherbox[currentPlayerSlot]; } internal void UpdateGiftBox(Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox entry) { string motherBoxDataStorageKey = _keyProvider.GetMotherBoxDataStorageKey(); Dictionary<int, Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox> dictionary = new Dictionary<int, Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox> { { _playerProvider.CurrentPlayerSlot, entry } }; IDataStorageHelper dataStorage = _session.DataStorage; string text = motherBoxDataStorageKey; dataStorage[(Scope)0, text] = dataStorage[(Scope)0, text] + Operation.Update((IDictionary)dictionary); } public AcceptedTraitsByTeam GetAcceptedTraitsByTeam(IEnumerable<string> giftTraits) { AcceptedTraitsByTeam acceptedTraitsByTeam = new AcceptedTraitsByTeam(); foreach (int allTeam in _playerProvider.GetAllTeams()) { AcceptedTraitsByPlayer acceptedTraitsByPlayer = GetAcceptedTraitsByPlayer(allTeam, giftTraits); if (acceptedTraitsByPlayer.Any()) { acceptedTraitsByTeam.Add(allTeam, acceptedTraitsByPlayer); } } return acceptedTraitsByTeam; } public AcceptedTraitsByPlayer GetAcceptedTraitsByPlayer(int team, IEnumerable<string> giftTraits) { AcceptedTraitsByPlayer acceptedTraitsByPlayer = new AcceptedTraitsByPlayer(); Dictionary<int, Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox> motherbox = GetMotherbox(team); if (motherbox == null || !motherbox.Any()) { return acceptedTraitsByPlayer; } foreach (KeyValuePair<int, Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox> item in motherbox) { int key = item.Key; Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox value = item.Value; AcceptedTraits acceptedTraits = GetAcceptedTraits(team, key, value, giftTraits); if (acceptedTraits.Any()) { acceptedTraitsByPlayer.Add(key, acceptedTraits); } } return acceptedTraitsByPlayer; } private static AcceptedTraits GetAcceptedTraits(int team, int player, Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox giftBox, IEnumerable<string> giftTraits) { if (giftBox == null || !giftBox.IsOpen) { return new AcceptedTraits(team, player); } IEnumerable<string> source = giftTraits.Where((string x) => giftBox.AcceptsAnyGift || giftBox.DesiredTraits.Contains(x)); return new AcceptedTraits(team, player, source.ToArray()); } public GiftingResult SendGift(GiftItem item, string playerName) { return SendGift(item, new Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[0], playerName); } public async Task<GiftingResult> SendGiftAsync(GiftItem item, string playerName) { return await SendGiftAsync(item, new Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[0], playerName); } public GiftingResult SendGift(GiftItem item, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits, string playerName) { return SendGift(item, traits, playerName, _session.ConnectionInfo.Team); } public async Task<GiftingResult> SendGiftAsync(GiftItem item, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits, string playerName) { return await SendGiftAsync(item, traits, playerName, _session.ConnectionInfo.Team); } public GiftingResult SendGift(GiftItem item, string playerName, int playerTeam) { return SendGift(item, new Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[0], playerName, playerTeam); } public async Task<GiftingResult> SendGiftAsync(GiftItem item, string playerName, int playerTeam) { return await SendGiftAsync(item, new Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[0], playerName, playerTeam); } public GiftingResult SendGift(GiftItem item, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits, string playerName, int playerTeam) { CanGiftResult canGift = CanGiftToPlayer(playerName, playerTeam, traits.Select((Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait x) => x.Trait)); return SendGift(item, traits, playerName, playerTeam, canGift); } public async Task<GiftingResult> SendGiftAsync(GiftItem item, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits, string playerName, int playerTeam) { return SendGift(item, traits, playerName, playerTeam, await CanGiftToPlayerAsync(playerName, playerTeam, traits.Select((Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait x) => x.Trait))); } private GiftingResult SendGift(GiftItem item, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits, string playerName, int playerTeam, CanGiftResult canGift) { if (!canGift.CanGift) { return new FailedGifting(canGift.Message); } if (!_playerProvider.TryGetPlayer(playerName, playerTeam, out var player)) { return new FailedGifting($"Could not find a player named {playerName} on team {playerTeam}"); } int currentPlayerSlot = _playerProvider.CurrentPlayerSlot; int currentPlayerTeam = _playerProvider.CurrentPlayerTeam; int slot = player.Slot; Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift gift = new Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift(item.Name, item.Amount, item.Value, traits, currentPlayerSlot, slot, currentPlayerTeam, playerTeam); return SendGift(gift); } public GiftingResult RefundGift(Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift gift) { if (gift.IsRefund) { return new FailedGifting("Cannot refund a gift that is already a refund"); } gift.IsRefund = true; return SendGift(gift); } private GiftingResult SendGift(Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift gift) { try { PlayerInfo val = (gift.IsRefund ? _playerProvider.GetPlayer(gift.SenderSlot, gift.SenderTeam) : _playerProvider.GetPlayer(gift.ReceiverSlot, gift.ReceiverTeam)); int num = GetMotherbox(val.Team)[val.Slot].MaximumGiftDataVersion; if (num < DataVersion.FirstVersion) { num = DataVersion.FirstVersion; } string giftBoxDataStorageKey = _keyProvider.GetGiftBoxDataStorageKey(val.Team, val.Slot); CreateGiftBoxIfNeeded(giftBoxDataStorageKey); IDictionary dictionary = _currentGiftConverter.CreateDataStorageUpdateEntry(gift.ID, gift, num); IDataStorageHelper dataStorage = _session.DataStorage; string text = giftBoxDataStorageKey; dataStorage[(Scope)0, text] = dataStorage[(Scope)0, text] + Operation.Update(dictionary); return new SuccessfulGifting(gift.ID); } catch (Exception ex) { return new FailedGifting(gift.ID, ex.Message); } } public Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift> GetAllGiftsAndEmptyGiftBox() { Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift> dictionary = CheckGiftBox(); RemoveGiftsFromGiftBox(dictionary.Keys); return dictionary; } public async Task<Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift>> GetAllGiftsAndEmptyGiftBoxAsync() { Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift> dictionary = await CheckGiftBoxAsync(); RemoveGiftsFromGiftBox(dictionary.Keys); return dictionary; } public Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift> CheckGiftBox() { string giftBoxDataStorageKey = _keyProvider.GetGiftBoxDataStorageKey(_playerProvider.CurrentPlayerTeam, _playerProvider.CurrentPlayerSlot); return GetGiftBoxContent(giftBoxDataStorageKey); } public async Task<Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift>> CheckGiftBoxAsync() { string giftBoxDataStorageKey = _keyProvider.GetGiftBoxDataStorageKey(_playerProvider.CurrentPlayerTeam, _playerProvider.CurrentPlayerSlot); return await GetGiftBoxContentAsync(giftBoxDataStorageKey); } private void EmptyGiftBox() { GetAllGiftsAndEmptyGiftBox(); } public void RemoveGiftsFromGiftBox(IEnumerable<string> giftsIds) { foreach (string giftsId in giftsIds) { RemoveGiftFromGiftBox(giftsId); } } public void RemoveGiftFromGiftBox(string giftId) { string giftBoxDataStorageKey = _keyProvider.GetGiftBoxDataStorageKey(_playerProvider.CurrentPlayerTeam, _playerProvider.CurrentPlayerSlot); IDataStorageHelper dataStorage = _session.DataStorage; string text = giftBoxDataStorageKey; dataStorage[(Scope)0, text] = dataStorage[(Scope)0, text] + Operation.Pop(JToken.op_Implicit(giftId)); } private Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift> GetGiftBoxContent(string giftBoxKey) { CreateGiftBoxIfNeeded(giftBoxKey); DataStorageElement element = _session.DataStorage[(Scope)0, giftBoxKey]; return _currentGiftConverter.ReadFromDataStorage(element); } private async Task<Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift>> GetGiftBoxContentAsync(string giftBoxKey) { CreateGiftBoxIfNeeded(giftBoxKey); JToken element = await _session.DataStorage[(Scope)0, giftBoxKey].GetAsync(); return _currentGiftConverter.ReadFromDataStorage(element); } public CanGiftResult CanGiftToPlayer(string playerName) { return CanGiftToPlayer(playerName, _playerProvider.CurrentPlayerTeam); } public async Task<CanGiftResult> CanGiftToPlayerAsync(string playerName) { return await CanGiftToPlayerAsync(playerName, _playerProvider.CurrentPlayerTeam); } public CanGiftResult CanGiftToPlayer(string playerName, int playerTeam) { return CanGiftToPlayer(playerName, playerTeam, Enumerable.Empty<string>()); } public async Task<CanGiftResult> CanGiftToPlayerAsync(string playerName, int playerTeam) { return await CanGiftToPlayerAsync(playerName, playerTeam, Enumerable.Empty<string>()); } public CanGiftResult CanGiftToPlayer(string playerName, IEnumerable<string> giftTraits) { return CanGiftToPlayer(playerName, _playerProvider.CurrentPlayerTeam, giftTraits); } public async Task<CanGiftResult> CanGiftToPlayerAsync(string playerName, IEnumerable<string> giftTraits) { return await CanGiftToPlayerAsync(playerName, _playerProvider.CurrentPlayerTeam, giftTraits); } public CanGiftResult CanGiftToPlayer(string playerName, int playerTeam, IEnumerable<string> giftTraits) { if (!_playerProvider.TryGetPlayer(playerName, out var player)) { return new CanGiftResult("Could not find a player named " + playerName); } return CanGiftToPlayer(player.Slot, playerTeam, giftTraits); } public async Task<CanGiftResult> CanGiftToPlayerAsync(string playerName, int playerTeam, IEnumerable<string> giftTraits) { if (!_playerProvider.TryGetPlayer(playerName, out var player)) { return new CanGiftResult("Could not find a player named " + playerName); } return await CanGiftToPlayerAsync(player.Slot, playerTeam, giftTraits); } public CanGiftResult CanGiftToPlayer(int playerSlot) { return CanGiftToPlayer(playerSlot, _playerProvider.CurrentPlayerTeam); } public async Task<CanGiftResult> CanGiftToPlayerAsync(int playerSlot) { return await CanGiftToPlayerAsync(playerSlot, _playerProvider.CurrentPlayerTeam); } public CanGiftResult CanGiftToPlayer(int playerSlot, int playerTeam) { return CanGiftToPlayer(playerSlot, playerTeam, Enumerable.Empty<string>()); } public async Task<CanGiftResult> CanGiftToPlayerAsync(int playerSlot, int playerTeam) { return await CanGiftToPlayerAsync(playerSlot, playerTeam, Enumerable.Empty<string>()); } public CanGiftResult CanGiftToPlayer(int playerSlot, IEnumerable<string> giftTraits) { return CanGiftToPlayer(playerSlot, _playerProvider.CurrentPlayerTeam, giftTraits); } public async Task<CanGiftResult> CanGiftToPlayerAsync(int playerSlot, IEnumerable<string> giftTraits) { return await CanGiftToPlayerAsync(playerSlot, _playerProvider.CurrentPlayerTeam, giftTraits); } public CanGiftResult CanGiftToPlayer(int playerSlot, int playerTeam, IEnumerable<string> giftTraits) { Dictionary<int, Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox> motherbox = GetMotherbox(playerTeam); return CanGiftToPlayer(playerSlot, playerTeam, giftTraits, motherbox); } public async Task<CanGiftResult> CanGiftToPlayerAsync(int playerSlot, int playerTeam, IEnumerable<string> giftTraits) { return CanGiftToPlayer(playerSlot, playerTeam, giftTraits, await GetMotherboxAsync(playerTeam)); } private CanGiftResult CanGiftToPlayer(int playerSlot, int playerTeam, IEnumerable<string> giftTraits, Dictionary<int, Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox> motherBox) { if (!motherBox.ContainsKey(playerSlot)) { return new CanGiftResult("Player does not exist or does not have a giftbox"); } Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox giftBox = motherBox[playerSlot]; if (!giftBox.IsOpen) { return new CanGiftResult("This giftbox is currently closed"); } if (giftBox.MinimumGiftDataVersion > DataVersion.Current) { return new CanGiftResult($"This player can only receive gifts from a more recent data version. Your client should update to gifting Data Version {giftBox.MinimumGiftDataVersion}. If you are not the developer, you should contact them to tell them about this."); } _playerProvider.GetPlayer(playerSlot, playerTeam); if (giftBox.AcceptsAnyGift) { return new CanGiftResult(); } if (!giftBox.DesiredTraits.Any((string trait) => giftTraits.Contains<string>(trait, StringComparer.OrdinalIgnoreCase))) { return new CanGiftResult("This player cannot accept this gift. They can only accept the following traits: [" + string.Join(",", giftBox.DesiredTraits) + "]"); } return new CanGiftResult(); } private Dictionary<int, Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox> GetMotherbox(int playerTeam) { string motherBoxDataStorageKey = _keyProvider.GetMotherBoxDataStorageKey(playerTeam); return _currentGiftBoxConverter.ReadFromDataStorage(_session.DataStorage[(Scope)0, motherBoxDataStorageKey]); } private async Task<Dictionary<int, Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox>> GetMotherboxAsync(int playerTeam) { string motherBoxDataStorageKey = _keyProvider.GetMotherBoxDataStorageKey(playerTeam); JToken element = await _session.DataStorage[(Scope)0, motherBoxDataStorageKey].GetAsync(); return _currentGiftBoxConverter.ReadFromDataStorage(element); } private void CreateMotherboxIfNeeded(string motherboxKey) { _session.DataStorage[(Scope)0, motherboxKey].Initialize(EmptyMotherboxDictionary); } private void CreateGiftBoxIfNeeded(string giftBoxKey) { _session.DataStorage[(Scope)0, giftBoxKey].Initialize(EmptyGiftDictionary); } private void GiftListener(JToken originalValue, JToken newValue, Dictionary<string, JToken> additionalArguments) { Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift> dictionary = _currentGiftConverter.ReadFromDataStorage(originalValue); Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift> dictionary2 = _currentGiftConverter.ReadFromDataStorage(newValue); foreach (string key in dictionary.Keys) { dictionary2.Remove(key); } foreach (Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift value in dictionary2.Values) { _giftReceivedHandler?.Invoke(value); } } [Obsolete("SubscribeToNewGifts is deprecated. Instead, use the event OnNewGift and subscribe by adding the handlers you need")] public void SubscribeToNewGifts(Action<Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift>> newGiftsCallback) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown string giftBoxDataStorageKey = _keyProvider.GetGiftBoxDataStorageKey(); _session.DataStorage[(Scope)0, giftBoxDataStorageKey].OnValueChanged += (DataStorageUpdatedHandler)delegate(JToken originalValue, JToken newValue, Dictionary<string, JToken> additionalArguments) { OnNewGiftObsolete(originalValue, newValue, newGiftsCallback); }; } [Obsolete("Obsolete private event used by SubscribeToNewGifts")] private void OnNewGiftObsolete(JToken originalValue, JToken newValue, Action<Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift>> newGiftsCallback) { Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift> dictionary = _currentGiftConverter.ReadFromDataStorage(newValue); if (dictionary.Any()) { newGiftsCallback(dictionary); } } [Obsolete("The overloads with out parameters are now deprecated, please use the overloads that return a GiftingResult instead")] public bool SendGift(GiftItem item, string playerName, out string giftId) { GiftingResult giftingResult = SendGift(item, playerName); giftId = giftingResult.GiftId; return giftingResult.Success; } [Obsolete("The overloads with out parameters are now deprecated, please use the overloads that return a GiftingResult instead")] public bool SendGift(GiftItem item, string playerName, int playerTeam, out string giftId) { GiftingResult giftingResult = SendGift(item, playerName, playerTeam); giftId = giftingResult.GiftId; return giftingResult.Success; } [Obsolete("The overloads with out parameters are now deprecated, please use the overloads that return a GiftingResult instead")] public bool SendGift(GiftItem item, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits, string playerName, out string giftId) { GiftingResult giftingResult = SendGift(item, traits, playerName); giftId = giftingResult.GiftId; return giftingResult.Success; } [Obsolete("The overloads with out parameters are now deprecated, please use the overloads that return a GiftingResult instead")] public bool SendGift(GiftItem item, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits, string playerName, int playerTeam, out string giftId) { GiftingResult giftingResult = SendGift(item, traits, playerName, playerTeam); giftId = giftingResult.GiftId; return giftingResult.Success; } } public interface IGiftingService : IGiftingServiceSync, IGiftingServiceAsync { event GiftingService.GiftReceivedHandler OnNewGift; } public interface IGiftingServiceAsync { Task<Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift>> CheckGiftBoxAsync(); Task<CanGiftResult> CanGiftToPlayerAsync(string playerName); Task<CanGiftResult> CanGiftToPlayerAsync(string playerName, int playerTeam); Task<CanGiftResult> CanGiftToPlayerAsync(string playerName, IEnumerable<string> giftTraits); Task<CanGiftResult> CanGiftToPlayerAsync(string playerName, int playerTeam, IEnumerable<string> giftTraits); Task<CanGiftResult> CanGiftToPlayerAsync(int playerSlot); Task<CanGiftResult> CanGiftToPlayerAsync(int playerSlot, int playerTeam); Task<CanGiftResult> CanGiftToPlayerAsync(int playerSlot, IEnumerable<string> giftTraits); Task<CanGiftResult> CanGiftToPlayerAsync(int playerSlot, int playerTeam, IEnumerable<string> giftTraits); Task<GiftingResult> SendGiftAsync(GiftItem item, string playerName); Task<GiftingResult> SendGiftAsync(GiftItem item, string playerName, int playerTeam); Task<GiftingResult> SendGiftAsync(GiftItem item, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits, string playerName); Task<GiftingResult> SendGiftAsync(GiftItem item, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits, string playerName, int playerTeam); } public interface IGiftingServiceSync { string GetMyGiftBoxKey(); void OpenGiftBox(); void OpenGiftBox(bool acceptAnyGift, string[] desiredTraits); void CloseGiftBox(); Archipelago.Gifting.Net.Versioning.GiftBoxes.Current.GiftBox GetCurrentGiftBoxState(); CanGiftResult CanGiftToPlayer(string playerName); CanGiftResult CanGiftToPlayer(string playerName, int playerTeam); CanGiftResult CanGiftToPlayer(string playerName, IEnumerable<string> giftTraits); CanGiftResult CanGiftToPlayer(string playerName, int playerTeam, IEnumerable<string> giftTraits); CanGiftResult CanGiftToPlayer(int playerSlot); CanGiftResult CanGiftToPlayer(int playerSlot, int playerTeam); CanGiftResult CanGiftToPlayer(int playerSlot, IEnumerable<string> giftTraits); CanGiftResult CanGiftToPlayer(int playerSlot, int playerTeam, IEnumerable<string> giftTraits); AcceptedTraitsByTeam GetAcceptedTraitsByTeam(IEnumerable<string> giftTraits); AcceptedTraitsByPlayer GetAcceptedTraitsByPlayer(int team, IEnumerable<string> giftTraits); GiftingResult SendGift(GiftItem item, string playerName); GiftingResult SendGift(GiftItem item, string playerName, int playerTeam); GiftingResult SendGift(GiftItem item, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits, string playerName); GiftingResult SendGift(GiftItem item, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits, string playerName, int playerTeam); GiftingResult RefundGift(Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift gift); Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift> GetAllGiftsAndEmptyGiftBox(); Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift> CheckGiftBox(); void RemoveGiftsFromGiftBox(IEnumerable<string> giftIds); void RemoveGiftFromGiftBox(string giftId); [Obsolete("SubscribeToNewGifts is deprecated. Instead, use the event OnNewGift and subscribe by adding the handlers you need")] void SubscribeToNewGifts(Action<Dictionary<string, Archipelago.Gifting.Net.Versioning.Gifts.Current.Gift>> newGiftsCallback); [Obsolete("The overloads with out parameters are now deprecated, please use the overloads that return a GiftingResult instead")] bool SendGift(GiftItem item, string playerName, out string giftId); [Obsolete("The overloads with out parameters are now deprecated, please use the overloads that return a GiftingResult instead")] bool SendGift(GiftItem item, string playerName, int playerTeam, out string giftId); [Obsolete("The overloads with out parameters are now deprecated, please use the overloads that return a GiftingResult instead")] bool SendGift(GiftItem item, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits, string playerName, out string giftId); [Obsolete("The overloads with out parameters are now deprecated, please use the overloads that return a GiftingResult instead")] bool SendGift(GiftItem item, Archipelago.Gifting.Net.Versioning.Gifts.Current.GiftTrait[] traits, string playerName, int playerTeam, out string giftId); } } namespace Archipelago.Gifting.Net.Service.TraitAcceptance { public class AcceptedTraits { public int Team { get; } public int Player { get; } public string[] Traits { get; } internal AcceptedTraits(int team, int player) : this(team, player, new string[0]) { } internal AcceptedTraits(int team, int player, string[] traits) { Team = team; Player = player; Traits = traits; } public bool Any() { return Traits.Any(); } } public class AcceptedTraitsByPlayer : IDictionary<int, AcceptedTraits>, ICollection<KeyValuePair<int, AcceptedTraits>>, IEnumerable<KeyValuePair<int, AcceptedTraits>>, IEnumerable { private IDictionary<int, AcceptedTraits> _traitsByPlayer; public int Count => _traitsByPlayer.Count; public bool IsReadOnly => _traitsByPlayer.IsReadOnly; public AcceptedTraits this[int key] { get { return _traitsByPlayer[key]; } set { _traitsByPlayer[key] = value; } } public ICollection<int> Keys => _traitsByPlayer.Keys; public ICollection<AcceptedTraits> Values => _traitsByPlayer.Values; internal AcceptedTraitsByPlayer() : this(new Dictionary<int, AcceptedTraits>()) { } internal AcceptedTraitsByPlayer(IDictionary<int, AcceptedTraits> traitsByPlayer) { _traitsByPlayer = traitsByPlayer; } public IEnumerator<KeyValuePair<int, AcceptedTraits>> GetEnumerator() { return _traitsByPlayer.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Add(KeyValuePair<int, AcceptedTraits> item) { _traitsByPlayer.Add(item); } public void Clear() { _traitsByPlayer.Clear(); } public bool Contains(KeyValuePair<int, AcceptedTraits> item) { return _traitsByPlayer.Contains(item); } public void CopyTo(KeyValuePair<int, AcceptedTraits>[] array, int arrayIndex) { _traitsByPlayer.CopyTo(array, arrayIndex); } public bool Remove(KeyValuePair<int, AcceptedTraits> item) { return _traitsByPlayer.Remove(item); } public bool ContainsKey(int key) { return _traitsByPlayer.ContainsKey(key); } public void Add(int key, AcceptedTraits value) { _traitsByPlayer.Add(key, value); } public bool Remove(int key) { return _traitsByPlayer.Remove(key); } public bool TryGetValue(int key, out AcceptedTraits value) { return _traitsByPlayer.TryGetValue(key, out value); } } public class AcceptedTraitsByTeam : IDictionary<int, AcceptedTraitsByPlayer>, ICollection<KeyValuePair<int, AcceptedTraitsByPlayer>>, IEnumerable<KeyValuePair<int, AcceptedTraitsByPlayer>>, IEnumerable { private IDictionary<int, AcceptedTraitsByPlayer> _traitsByTeam; public int Count => _traitsByTeam.Count; public bool IsReadOnly => _traitsByTeam.IsReadOnly; public AcceptedTraitsByPlayer this[int key] { get { return _traitsByTeam[key]; } set { _traitsByTeam[key] = value; } } public ICollection<int> Keys => _traitsByTeam.Keys; public ICollection<AcceptedTraitsByPlayer> Values => _traitsByTeam.Values; internal AcceptedTraitsByTeam() : this(new Dictionary<int, AcceptedTraitsByPlayer>()) { } internal AcceptedTraitsByTeam(IDictionary<int, AcceptedTraitsByPlayer> traitsByTeam) { _traitsByTeam = traitsByTeam; } public IEnumerator<KeyValuePair<int, AcceptedTraitsByPlayer>> GetEnumerator() { return _traitsByTeam.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Add(KeyValuePair<int, AcceptedTraitsByPlayer> item) { _traitsByTeam.Add(item); } public void Clear() { _traitsByTeam.Clear(); } public bool Contains(KeyValuePair<int, AcceptedTraitsByPlayer> item) { return _traitsByTeam.Contains(item); } public void CopyTo(KeyValuePair<int, AcceptedTraitsByPlayer>[] array, int arrayIndex) { _traitsByTeam.CopyTo(array, arrayIndex); } public bool Remove(KeyValuePair<int, AcceptedTraitsByPlayer> item) { return _traitsByTeam.Remove(item); } public bool ContainsKey(int key) { return _traitsByTeam.ContainsKey(key); } public void Add(int key, AcceptedTraitsByPlayer value) { _traitsByTeam.Add(key, value); } public bool Remove(int key) { return _traitsByTeam.Remove(key); } public bool TryGetValue(int key, out AcceptedTraitsByPlayer value) { return _traitsByTeam.TryGetValue(key, out value); } } } namespace Archipelago.Gifting.Net.Service.Result { public class CanGiftResult { public bool CanGift => string.IsNullOrWhiteSpace(Message); public string Message { get; } public CanGiftResult() : this(string.Empty) { } public CanGiftResult(string message) { Message = message; } } public class FailedGifting : GiftingResult { public override bool Success => false; public override string GiftId { get; } public string ErrorMessage { get; } public FailedGifting(string errorMessage) : this(string.Empty, errorMessage) { } public FailedGifting(string giftId, string errorMessage) { GiftId = giftId; ErrorMessage = errorMessage; } } public abstract class GiftingResult { public abstract bool Success { get; } public abstract string GiftId { get; } } public class SuccessfulGifting : GiftingResult { public override bool Success => true; public override string GiftId { get; } public SuccessfulGifting(string giftId) { GiftId = giftId; } } }
Archipelago.MultiClient.Net.dll
Decompiled a month 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.Colors; using Archipelago.MultiClient.Net.ConcurrentCollection; using Archipelago.MultiClient.Net.Converters; using Archipelago.MultiClient.Net.DataPackage; using Archipelago.MultiClient.Net.Enums; using Archipelago.MultiClient.Net.Exceptions; using Archipelago.MultiClient.Net.Extensions; using Archipelago.MultiClient.Net.Helpers; using Archipelago.MultiClient.Net.MessageLog.Messages; using Archipelago.MultiClient.Net.MessageLog.Parts; using Archipelago.MultiClient.Net.Models; using Archipelago.MultiClient.Net.Packets; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: ComVisible(false)] [assembly: Guid("35a803ad-85ed-42e9-b1e3-c6b72096f0c1")] [assembly: InternalsVisibleTo("Archipelago.MultiClient.Net.Tests")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Jarno Westhof, Hussein Farran, Zach Parks")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyDescription("A client library for use with .NET based prog-langs for interfacing with Archipelago hosts.")] [assembly: AssemblyFileVersion("6.6.0.0")] [assembly: AssemblyInformationalVersion("6.6.0+75d4c5e6a52bb0c8bb1d4bc368652613509c7acb")] [assembly: AssemblyProduct("Archipelago.MultiClient.Net")] [assembly: AssemblyTitle("Archipelago.MultiClient.Net")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/ArchipelagoMW/Archipelago.MultiClient.Net")] [assembly: AssemblyVersion("6.6.0.0")] internal interface IConcurrentHashSet<T> { bool TryAdd(T item); bool Contains(T item); void UnionWith(T[] otherSet); T[] ToArray(); ReadOnlyCollection<T> AsToReadOnlyCollection(); ReadOnlyCollection<T> AsToReadOnlyCollectionExcept(IConcurrentHashSet<T> otherSet); } namespace Archipelago.MultiClient.Net { [Serializable] public abstract class ArchipelagoPacketBase { [JsonIgnore] internal JObject jobject; [JsonProperty("cmd")] [JsonConverter(typeof(StringEnumConverter))] public abstract ArchipelagoPacketType PacketType { get; } public JObject ToJObject() { return jobject; } } public interface IArchipelagoSession : IArchipelagoSessionActions { IArchipelagoSocketHelper Socket { get; } IReceivedItemsHelper Items { get; } ILocationCheckHelper Locations { get; } IPlayerHelper Players { get; } IDataStorageHelper DataStorage { get; } IConnectionInfoProvider ConnectionInfo { get; } IRoomStateHelper RoomState { get; } IMessageLogHelper MessageLog { get; } 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.TrySetResult(new LoginFailure("You are not connected, run ConnectAsync() first")); return loginResultTask.Task; } connectionInfo.SetConnectionParameters(game, tags, itemsHandlingFlags, uuid); try { Socket.SendPacket(BuildConnectPacket(name, password, version, requestSlotData)); } catch (ArchipelagoSocketClosedException) { loginResultTask.TrySetResult(new LoginFailure("You are not connected, run ConnectAsync() first")); return loginResultTask.Task; } SetResultAfterTimeout(loginResultTask, 4, new LoginFailure("Connection timed out.")); return loginResultTask.Task; } private static void SetResultAfterTimeout<T>(TaskCompletionSource<T> task, int timeoutInSeconds, T result) { new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)).Token.Register(delegate { task.TrySetResult(result); }); } public LoginResult TryConnectAndLogin(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true) { Task<RoomInfoPacket> task = ConnectAsync(); try { task.Wait(TimeSpan.FromSeconds(4.0)); } catch (AggregateException ex) { if (ex.GetBaseException() is OperationCanceledException) { return new LoginFailure("Connection timed out."); } return new LoginFailure(ex.GetBaseException().Message); } if (!task.IsCompleted) { return new LoginFailure("Connection timed out."); } return LoginAsync(game, name, itemsHandlingFlags, version, tags, uuid, password, requestSlotData).Result; } private ConnectPacket BuildConnectPacket(string name, string password, Version version, bool requestSlotData) { return new ConnectPacket { Game = ConnectionInfo.Game, Name = name, Password = password, Tags = ConnectionInfo.Tags, Uuid = ConnectionInfo.Uuid, Version = ((version != null) ? new NetworkVersion(version) : new NetworkVersion(0, 6, 0)), ItemsHandling = ConnectionInfo.ItemsHandlingFlags, RequestSlotData = requestSlotData }; } public void Say(string message) { Socket.SendPacket(new SayPacket { Text = message }); } public void SetClientState(ArchipelagoClientState state) { Socket.SendPacket(new StatusUpdatePacket { Status = state }); } public void SetGoalAchieved() { SetClientState(ArchipelagoClientState.ClientGoal); } } public interface IArchipelagoSessionActions { void Say(string message); void SetClientState(ArchipelagoClientState state); void SetGoalAchieved(); } public static class ArchipelagoSessionFactory { public static ArchipelagoSession CreateSession(Uri uri) { ArchipelagoSocketHelper socket = new ArchipelagoSocketHelper(uri); DataPackageCache cache = new DataPackageCache(socket); ConnectionInfoHelper connectionInfoHelper = new ConnectionInfoHelper(socket); PlayerHelper playerHelper = new PlayerHelper(socket, connectionInfoHelper); ItemInfoResolver itemInfoResolver = new ItemInfoResolver(cache, connectionInfoHelper); LocationCheckHelper locationCheckHelper = new LocationCheckHelper(socket, itemInfoResolver, connectionInfoHelper, playerHelper); ReceivedItemsHelper items = new ReceivedItemsHelper(socket, locationCheckHelper, itemInfoResolver, connectionInfoHelper, playerHelper); RoomStateHelper 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; } public class UpdateHintPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.UpdateHint; [JsonProperty("player")] public int Player { get; set; } [JsonProperty("location")] public long Location { get; set; } [JsonProperty("status")] public HintStatus Status { get; set; } } } namespace Archipelago.MultiClient.Net.Models { public struct Color : IEquatable<Color> { public static Color Red = new Color(byte.MaxValue, 0, 0); public static Color Green = new Color(0, 128, 0); public static Color Yellow = new Color(byte.MaxValue, byte.MaxValue, 0); public static Color Blue = new Color(0, 0, byte.MaxValue); public static Color Magenta = new Color(byte.MaxValue, 0, byte.MaxValue); public static Color Cyan = new Color(0, byte.MaxValue, byte.MaxValue); public static Color Black = new Color(0, 0, 0); public static Color White = new Color(byte.MaxValue, byte.MaxValue, byte.MaxValue); public static Color SlateBlue = new Color(106, 90, 205); public static Color Salmon = new Color(250, 128, 114); public static Color Plum = new Color(221, 160, 221); public byte R { get; set; } public byte G { get; set; } public byte B { get; set; } public Color(byte r, byte g, byte b) { R = r; G = g; B = b; } public override bool Equals(object obj) { if (obj is Color color && R == color.R && G == color.G) { return B == color.B; } return false; } public bool Equals(Color other) { if (R == other.R && G == other.G) { return B == other.B; } return false; } public override int GetHashCode() { return ((-1520100960 * -1521134295 + R.GetHashCode()) * -1521134295 + G.GetHashCode()) * -1521134295 + B.GetHashCode(); } public static bool operator ==(Color left, Color right) { return left.Equals(right); } public static bool operator !=(Color left, Color right) { return !(left == right); } } public class DataPackage { [JsonProperty("games")] public Dictionary<string, GameData> Games { get; set; } = new Dictionary<string, GameData>(); } public class DataStorageElement { internal DataStorageElementContext Context; internal List<OperationSpecification> Operations = new List<OperationSpecification>(0); internal DataStorageHelper.DataStorageUpdatedHandler Callbacks; internal Dictionary<string, JToken> AdditionalArguments = new Dictionary<string, JToken>(0); private JToken cachedValue; public event DataStorageHelper.DataStorageUpdatedHandler OnValueChanged { add { Context.AddHandler(Context.Key, value); } remove { Context.RemoveHandler(Context.Key, value); } } internal DataStorageElement(DataStorageElementContext context) { Context = context; } internal DataStorageElement(OperationType operationType, JToken value) { Operations = new List<OperationSpecification>(1) { new OperationSpecification { OperationType = operationType, Value = value } }; } internal DataStorageElement(DataStorageElement source, OperationType operationType, JToken value) : this(source.Context) { Operations = source.Operations.ToList(); Callbacks = source.Callbacks; AdditionalArguments = source.AdditionalArguments; Operations.Add(new OperationSpecification { OperationType = operationType, Value = value }); } internal DataStorageElement(DataStorageElement source, Callback callback) : this(source.Context) { Operations = source.Operations.ToList(); Callbacks = source.Callbacks; AdditionalArguments = source.AdditionalArguments; Callbacks = (DataStorageHelper.DataStorageUpdatedHandler)Delegate.Combine(Callbacks, callback.Method); } internal DataStorageElement(DataStorageElement source, AdditionalArgument additionalArgument) : this(source.Context) { Operations = source.Operations.ToList(); Callbacks = source.Callbacks; AdditionalArguments = source.AdditionalArguments; AdditionalArguments[additionalArgument.Key] = additionalArgument.Value; } public static DataStorageElement operator ++(DataStorageElement a) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(1)); } public static DataStorageElement operator --(DataStorageElement a) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(-1)); } public static DataStorageElement operator +(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, string b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, JToken b) { return new DataStorageElement(a, OperationType.Add, b); } public static DataStorageElement operator +(DataStorageElement a, IEnumerable b) { return new DataStorageElement(a, OperationType.Add, (JToken)(object)JArray.FromObject((object)b)); } public static DataStorageElement operator +(DataStorageElement a, OperationSpecification s) { return new DataStorageElement(a, s.OperationType, s.Value); } public static DataStorageElement operator +(DataStorageElement a, Callback c) { return new DataStorageElement(a, c); } public static DataStorageElement operator +(DataStorageElement a, AdditionalArgument arg) { return new DataStorageElement(a, arg); } public static DataStorageElement operator *(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator *(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator *(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator *(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator *(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator -(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b))); } public static DataStorageElement operator -(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b))); } public static DataStorageElement operator -(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(0f - b))); } public static DataStorageElement operator -(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(0.0 - b))); } public static DataStorageElement operator -(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b))); } public static DataStorageElement operator /(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / (decimal)b))); } public static DataStorageElement operator /(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / (decimal)b))); } public static DataStorageElement operator /(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1.0 / (double)b))); } public static DataStorageElement operator /(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1.0 / b))); } public static DataStorageElement operator /(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / b))); } public static implicit operator DataStorageElement(bool b) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(b)); } public static implicit operator DataStorageElement(int i) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(i)); } public static implicit operator DataStorageElement(long l) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(l)); } public static implicit operator DataStorageElement(decimal m) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(m)); } public static implicit operator DataStorageElement(double d) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(d)); } public static implicit operator DataStorageElement(float f) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(f)); } public static implicit operator DataStorageElement(string s) { if (s != null) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(s)); } return new DataStorageElement(OperationType.Replace, (JToken)(object)JValue.CreateNull()); } public static implicit operator DataStorageElement(JToken o) { return new DataStorageElement(OperationType.Replace, o); } public static implicit operator DataStorageElement(Array a) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)a)); } public static implicit operator DataStorageElement(List<bool> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<int> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<long> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<decimal> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<double> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<float> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<string> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<object> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator bool(DataStorageElement e) { return RetrieveAndReturnBoolValue<bool>(e); } public static implicit operator bool?(DataStorageElement e) { return RetrieveAndReturnBoolValue<bool?>(e); } public static implicit operator int(DataStorageElement e) { return RetrieveAndReturnDecimalValue<int>(e); } public static implicit operator int?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<int?>(e); } public static implicit operator long(DataStorageElement e) { return RetrieveAndReturnDecimalValue<long>(e); } public static implicit operator long?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<long?>(e); } public static implicit operator float(DataStorageElement e) { return RetrieveAndReturnDecimalValue<float>(e); } public static implicit operator float?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<float?>(e); } public static implicit operator double(DataStorageElement e) { return RetrieveAndReturnDecimalValue<double>(e); } public static implicit operator double?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<double?>(e); } public static implicit operator decimal(DataStorageElement e) { return RetrieveAndReturnDecimalValue<decimal>(e); } public static implicit operator decimal?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<decimal?>(e); } public static implicit operator string(DataStorageElement e) { return RetrieveAndReturnStringValue(e); } public static implicit operator bool[](DataStorageElement e) { return RetrieveAndReturnArrayValue<bool[]>(e); } public static implicit operator int[](DataStorageElement e) { return RetrieveAndReturnArrayValue<int[]>(e); } public static implicit operator long[](DataStorageElement e) { return RetrieveAndReturnArrayValue<long[]>(e); } public static implicit operator decimal[](DataStorageElement e) { return RetrieveAndReturnArrayValue<decimal[]>(e); } public static implicit operator double[](DataStorageElement e) { return RetrieveAndReturnArrayValue<double[]>(e); } public static implicit operator float[](DataStorageElement e) { return RetrieveAndReturnArrayValue<float[]>(e); } public static implicit operator string[](DataStorageElement e) { return RetrieveAndReturnArrayValue<string[]>(e); } public static implicit operator object[](DataStorageElement e) { return RetrieveAndReturnArrayValue<object[]>(e); } public static implicit operator List<bool>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<bool>>(e); } public static implicit operator List<int>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<int>>(e); } public static implicit operator List<long>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<long>>(e); } public static implicit operator List<decimal>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<decimal>>(e); } public static implicit operator List<double>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<double>>(e); } public static implicit operator List<float>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<float>>(e); } public static implicit operator List<string>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<string>>(e); } public static implicit operator List<object>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<object>>(e); } public static implicit operator Array(DataStorageElement e) { return RetrieveAndReturnArrayValue<Array>(e); } public static implicit operator JArray(DataStorageElement e) { return RetrieveAndReturnArrayValue<JArray>(e); } public static implicit operator JToken(DataStorageElement e) { return e.Context.GetData(e.Context.Key); } public static DataStorageElement operator +(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Add, JToken.Parse(b.ToString())); } public static DataStorageElement operator *(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Mul, JToken.Parse(b.ToString())); } public static DataStorageElement operator %(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Mod, JToken.Parse(b.ToString())); } public static DataStorageElement operator ^(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Pow, JToken.Parse(b.ToString())); } public static DataStorageElement operator -(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Add, JToken.Parse((-b).ToString())); } public static DataStorageElement operator /(DataStorageElement a, BigInteger b) { throw new InvalidOperationException("DataStorage[Key] / BigInterger is not supported, due to loss of precision when using integer division"); } public static implicit operator DataStorageElement(BigInteger bi) { return new DataStorageElement(OperationType.Replace, JToken.Parse(bi.ToString())); } public static implicit operator BigInteger(DataStorageElement e) { return RetrieveAndReturnBigIntegerValue<BigInteger>(e); } public static implicit operator BigInteger?(DataStorageElement e) { return RetrieveAndReturnBigIntegerValue<BigInteger?>(e); } private static T RetrieveAndReturnBigIntegerValue<T>(DataStorageElement e) { if (e.cachedValue != null) { if (!BigInteger.TryParse(((object)e.cachedValue).ToString(), out var result)) { return default(T); } return (T)Convert.ChangeType(result, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T)); } BigInteger result2; BigInteger? bigInteger = (BigInteger.TryParse(((object)e.Context.GetData(e.Context.Key)).ToString(), out result2) ? new BigInteger?(result2) : null); if (!bigInteger.HasValue && !IsNullable<T>()) { bigInteger = Activator.CreateInstance<BigInteger>(); } foreach (OperationSpecification operation in e.Operations) { if (operation.OperationType == OperationType.Floor || operation.OperationType == OperationType.Ceil) { continue; } if (!BigInteger.TryParse(((object)operation.Value).ToString(), NumberStyles.AllowLeadingSign, null, out var result3)) { throw new InvalidOperationException($"DataStorage[Key] cannot be converted to BigInterger as its value its not an integer number, value: {operation.Value}"); } switch (operation.OperationType) { case OperationType.Replace: bigInteger = result3; break; case OperationType.Add: bigInteger += result3; break; case OperationType.Mul: bigInteger *= result3; break; case OperationType.Mod: bigInteger %= result3; break; case OperationType.Pow: bigInteger = BigInteger.Pow(bigInteger.Value, (int)operation.Value); break; case OperationType.Max: { BigInteger value = result3; BigInteger? bigInteger2 = bigInteger; if (value > bigInteger2) { bigInteger = result3; } break; } case OperationType.Min: { BigInteger value = result3; BigInteger? bigInteger2 = bigInteger; if (value < bigInteger2) { bigInteger = result3; } break; } case OperationType.Xor: bigInteger ^= result3; break; case OperationType.Or: bigInteger |= result3; break; case OperationType.And: bigInteger &= result3; break; case OperationType.LeftShift: bigInteger <<= (int)operation.Value; break; case OperationType.RightShift: bigInteger >>= (int)operation.Value; break; } } e.cachedValue = JToken.Parse(bigInteger.ToString()); if (!bigInteger.HasValue) { return default(T); } return (T)Convert.ChangeType(bigInteger.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T)); } public void Initialize(JToken value) { Context.Initialize(Context.Key, value); } public void Initialize(IEnumerable value) { Context.Initialize(Context.Key, (JToken)(object)JArray.FromObject((object)value)); } public Task<T> GetAsync<T>() { return GetAsync().ContinueWith((Task<JToken> r) => r.Result.ToObject<T>()); } public Task<JToken> GetAsync() { return Context.GetAsync(Context.Key); } private static T RetrieveAndReturnArrayValue<T>(DataStorageElement e) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_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; } [JsonProperty("status")] public HintStatus Status { get; set; } } public class ItemInfo { private readonly IItemInfoResolver itemInfoResolver; public long ItemId { get; } public long LocationId { get; } public PlayerInfo Player { get; } public ItemFlags Flags { get; } public string ItemName => itemInfoResolver.GetItemName(ItemId, ItemGame); public string ItemDisplayName => ItemName ?? $"Item: {ItemId}"; public string LocationName => itemInfoResolver.GetLocationName(LocationId, LocationGame); public string LocationDisplayName => LocationName ?? $"Location: {LocationId}"; public string ItemGame { get; } public string LocationGame { get; } public ItemInfo(NetworkItem item, string receiverGame, string senderGame, IItemInfoResolver itemInfoResolver, PlayerInfo player) { this.itemInfoResolver = itemInfoResolver; ItemGame = receiverGame; LocationGame = senderGame; ItemId = item.Item; LocationId = item.Location; Flags = item.Flags; Player = player; } public SerializableItemInfo ToSerializable() { return new SerializableItemInfo { IsScout = (GetType() == typeof(ScoutedItemInfo)), ItemId = ItemId, LocationId = LocationId, PlayerSlot = Player, Player = Player, Flags = Flags, ItemGame = ItemGame, ItemName = ItemName, LocationGame = LocationGame, LocationName = LocationName }; } } public class ScoutedItemInfo : ItemInfo { public new PlayerInfo Player => base.Player; public bool IsReceiverRelatedToActivePlayer { get; } public ScoutedItemInfo(NetworkItem item, string receiverGame, string senderGame, IItemInfoResolver itemInfoResolver, IPlayerHelper players, PlayerInfo player) : base(item, receiverGame, senderGame, itemInfoResolver, player) { IsReceiverRelatedToActivePlayer = (players.ActivePlayer ?? new PlayerInfo()).IsRelatedTo(player); } } public class JsonMessagePart { [JsonProperty("type")] [JsonConverter(typeof(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; } [JsonProperty("hint_status")] public HintStatus? HintStatus { get; set; } } public struct NetworkItem { [JsonProperty("item")] public long Item { get; set; } [JsonProperty("location")] public long Location { get; set; } [JsonProperty("player")] public int Player { get; set; } [JsonProperty("flags")] public ItemFlags Flags { get; set; } } public struct NetworkPlayer { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } [JsonProperty("alias")] public string Alias { get; set; } [JsonProperty("name")] public string Name { get; set; } } public struct NetworkSlot { [JsonProperty("name")] public string Name { get; set; } [JsonProperty("game")] public string Game { get; set; } [JsonProperty("type")] public SlotType Type { get; set; } [JsonProperty("group_members")] public int[] GroupMembers { get; set; } } public class NetworkVersion { [JsonProperty("major")] public int Major { get; set; } [JsonProperty("minor")] public int Minor { get; set; } [JsonProperty("build")] public int Build { get; set; } [JsonProperty("class")] public string Class => "Version"; public NetworkVersion() { } public NetworkVersion(int major, int minor, int build) { Major = major; Minor = minor; Build = build; } public NetworkVersion(Version version) { Major = version.Major; Minor = version.Minor; Build = version.Build; } public Version ToVersion() { return new Version(Major, Minor, Build); } } public class OperationSpecification { [JsonProperty("operation")] [JsonConverter(typeof(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, Archipelago.MultiClient.Net.Colors.PaletteColor.Blue) { base.Text = messagePart.Text; } } public class HintStatusMessagePart : MessagePart { internal HintStatusMessagePart(JsonMessagePart messagePart) : base(MessagePartType.HintStatus, messagePart) { base.Text = messagePart.Text; if (messagePart.HintStatus.HasValue) { base.PaletteColor = ColorUtils.GetColor(messagePart.HintStatus.Value); } } } public class ItemMessagePart : MessagePart { public ItemFlags Flags { get; } public long ItemId { get; } public int Player { get; } internal ItemMessagePart(IPlayerHelper players, IItemInfoResolver items, JsonMessagePart part) : base(MessagePartType.Item, part) { Flags = part.Flags.GetValueOrDefault(); base.PaletteColor = ColorUtils.GetColor(Flags); Player = part.Player.GetValueOrDefault(); string game = (players.GetPlayerInfo(Player) ?? new PlayerInfo()).Game; JsonMessagePartType? type = part.Type; if (type.HasValue) { switch (type.GetValueOrDefault()) { case JsonMessagePartType.ItemId: ItemId = long.Parse(part.Text); base.Text = items.GetItemName(ItemId, game) ?? $"Item: {ItemId}"; break; case JsonMessagePartType.ItemName: ItemId = 0L; base.Text = part.Text; break; } } } } public class LocationMessagePart : MessagePart { public long LocationId { get; } public int Player { get; } internal LocationMessagePart(IPlayerHelper players, IItemInfoResolver itemInfoResolver, JsonMessagePart part) : base(MessagePartType.Location, part, Archipelago.MultiClient.Net.Colors.PaletteColor.Green) { Player = part.Player.GetValueOrDefault(); string game = (players.GetPlayerInfo(Player) ?? new PlayerInfo()).Game; JsonMessagePartType? type = part.Type; if (type.HasValue) { switch (type.GetValueOrDefault()) { case JsonMessagePartType.LocationId: LocationId = long.Parse(part.Text); base.Text = itemInfoResolver.GetLocationName(LocationId, game) ?? $"Location: {LocationId}"; break; case JsonMessagePartType.LocationName: LocationId = itemInfoResolver.GetLocationId(part.Text, game); base.Text = part.Text; break; } } } } public class MessagePart { public string Text { get; internal set; } public MessagePartType Type { get; internal set; } public Color Color => GetColor(BuiltInPalettes.Dark); public PaletteColor? PaletteColor { get; protected set; } public bool IsBackgroundColor { get; internal set; } internal MessagePart(MessagePartType type, JsonMessagePart messagePart, PaletteColor? color = null) { Type = type; Text = messagePart.Text; if (color.HasValue) { PaletteColor = color.Value; } else if (messagePart.Color.HasValue) { PaletteColor = ColorUtils.GetColor(messagePart.Color.Value); IsBackgroundColor = messagePart.Color.Value >= JsonMessagePartColor.BlackBg; } else { PaletteColor = null; } } public T GetColor<T>(Palette<T> palette) { return palette[PaletteColor]; } public override string ToString() { return Text; } } public enum MessagePartType { Text, Player, Item, Location, Entrance, HintStatus } public class PlayerMessagePart : MessagePart { public bool IsActivePlayer { get; } public int SlotId { get; } internal PlayerMessagePart(IPlayerHelper players, IConnectionInfoProvider connectionInfo, JsonMessagePart part) : base(MessagePartType.Player, part) { switch (part.Type) { case JsonMessagePartType.PlayerId: SlotId = int.Parse(part.Text); IsActivePlayer = SlotId == connectionInfo.Slot; base.Text = players.GetPlayerAlias(SlotId) ?? $"Player {SlotId}"; break; case JsonMessagePartType.PlayerName: SlotId = 0; IsActivePlayer = false; base.Text = part.Text; break; } base.PaletteColor = (IsActivePlayer ? Archipelago.MultiClient.Net.Colors.PaletteColor.Magenta : Archipelago.MultiClient.Net.Colors.PaletteColor.Yellow); } } } namespace Archipelago.MultiClient.Net.MessageLog.Messages { public class AdminCommandResultLogMessage : LogMessage { internal AdminCommandResultLogMessage(MessagePart[] parts) : base(parts) { } } public class ChatLogMessage : PlayerSpecificLogMessage { public string Message { get; } internal ChatLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string message) : base(parts, players, team, slot) { Message = message; } } public class CollectLogMessage : PlayerSpecificLogMessage { internal CollectLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts, players, team, slot) { } } public class CommandResultLogMessage : LogMessage { internal CommandResultLogMessage(MessagePart[] parts) : base(parts) { } } public class CountdownLogMessage : LogMessage { public int RemainingSeconds { get; } internal CountdownLogMessage(MessagePart[] parts, int remainingSeconds) : base(parts) { RemainingSeconds = remainingSeconds; } } public class GoalLogMessage : PlayerSpecificLogMessage { internal GoalLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts, players, team, slot) { } } public class HintItemSendLogMessage : ItemSendLogMessage { public bool IsFound { get; } internal HintItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, bool found, IItemInfoResolver itemInfoResolver) : base(parts, players, receiver, sender, item, itemInfoResolver) { IsFound = found; } } public class ItemCheatLogMessage : ItemSendLogMessage { internal ItemCheatLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, NetworkItem item, IItemInfoResolver itemInfoResolver) : base(parts, players, slot, 0, item, team, itemInfoResolver) { } } public class ItemSendLogMessage : LogMessage { private PlayerInfo ActivePlayer { get; } public PlayerInfo Receiver { get; } public PlayerInfo Sender { get; } public bool IsReceiverTheActivePlayer => Receiver == ActivePlayer; public bool IsSenderTheActivePlayer => Sender == ActivePlayer; public bool IsRelatedToActivePlayer { get { if (!ActivePlayer.IsRelatedTo(Receiver)) { return ActivePlayer.IsRelatedTo(Sender); } return true; } } public ItemInfo Item { get; } internal ItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, IItemInfoResolver itemInfoResolver) : this(parts, players, receiver, sender, item, players.ActivePlayer.Team, itemInfoResolver) { } internal ItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, int team, IItemInfoResolver itemInfoResolver) : base(parts) { ActivePlayer = players.ActivePlayer ?? new PlayerInfo(); Receiver = players.GetPlayerInfo(team, receiver) ?? new PlayerInfo(); Sender = players.GetPlayerInfo(team, sender) ?? new PlayerInfo(); PlayerInfo player = players.GetPlayerInfo(team, item.Player) ?? new PlayerInfo(); Item = new ItemInfo(item, Receiver.Game, Sender.Game, itemInfoResolver, player); } } public class JoinLogMessage : PlayerSpecificLogMessage { public string[] Tags { get; } internal JoinLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string[] tags) : base(parts, players, team, slot) { Tags = tags; } } public class LeaveLogMessage : PlayerSpecificLogMessage { internal LeaveLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts, players, team, slot) { } } public class LogMessage { public MessagePart[] Parts { get; } internal LogMessage(MessagePart[] parts) { Parts = parts; } public override string ToString() { if (Parts.Length == 1) { return Parts[0].Text; } StringBuilder stringBuilder = new StringBuilder(); MessagePart[] parts = Parts; foreach (MessagePart messagePart in parts) { stringBuilder.Append(messagePart.Text); } return stringBuilder.ToString(); } } public abstract class PlayerSpecificLogMessage : LogMessage { private PlayerInfo ActivePlayer { get; } public PlayerInfo Player { get; } public bool IsActivePlayer => Player == ActivePlayer; public bool IsRelatedToActivePlayer => ActivePlayer.IsRelatedTo(Player); internal PlayerSpecificLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts) { ActivePlayer = players.ActivePlayer ?? new PlayerInfo(); Player = players.GetPlayerInfo(team, slot) ?? new PlayerInfo(); } } public class ReleaseLogMessage : PlayerSpecificLogMessage { internal ReleaseLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts, players, team, slot) { } } public class ServerChatLogMessage : LogMessage { public string Message { get; } internal ServerChatLogMessage(MessagePart[] parts, string message) : base(parts) { Message = message; } } public class TagsChangedLogMessage : PlayerSpecificLogMessage { public string[] Tags { get; } internal TagsChangedLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string[] tags) : base(parts, players, team, slot) { Tags = tags; } } public class TutorialLogMessage : LogMessage { internal TutorialLogMessage(MessagePart[] parts) : base(parts) { } } } namespace Archipelago.MultiClient.Net.Helpers { public class ArchipelagoSocketHelper : BaseArchipelagoSocketHelper<ClientWebSocket>, IArchipelagoSocketHelper { public Uri Uri { get; } internal ArchipelagoSocketHelper(Uri hostUri) : base(CreateWebSocket(), 1024) { Uri = hostUri; } private static ClientWebSocket CreateWebSocket() { return new ClientWebSocket(); } public async Task ConnectAsync() { await ConnectToProvidedUri(Uri); StartPolling(); } private async Task ConnectToProvidedUri(Uri uri) { if (uri.Scheme != "unspecified") { try { await Socket.ConnectAsync(uri, CancellationToken.None); return; } catch (Exception e) { OnError(e); throw; } } List<Exception> errors = new List<Exception>(0); try { await Socket.ConnectAsync(uri.AsWss(), CancellationToken.None); if (Socket.State == WebSocketState.Open) { return; } } catch (Exception item) { errors.Add(item); Socket = CreateWebSocket(); } try { await Socket.ConnectAsync(uri.AsWs(), CancellationToken.None); } catch (Exception item2) { errors.Add(item2); OnError(new AggregateException(errors)); throw; } } } public class BaseArchipelagoSocketHelper<T> where T : WebSocket { private static readonly ArchipelagoPacketConverter Converter = new ArchipelagoPacketConverter(); private readonly BlockingCollection<Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>> sendQueue = new BlockingCollection<Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>>(); internal T Socket; private readonly int bufferSize; public bool Connected { get { if (Socket.State != WebSocketState.Open) { return Socket.State == WebSocketState.CloseReceived; } return true; } } public event ArchipelagoSocketHelperDelagates.PacketReceivedHandler PacketReceived; public event ArchipelagoSocketHelperDelagates.PacketsSentHandler PacketsSent; public event ArchipelagoSocketHelperDelagates.ErrorReceivedHandler ErrorReceived; public event ArchipelagoSocketHelperDelagates.SocketClosedHandler SocketClosed; public event ArchipelagoSocketHelperDelagates.SocketOpenedHandler SocketOpened; internal BaseArchipelagoSocketHelper(T socket, int bufferSize = 1024) { Socket = socket; this.bufferSize = bufferSize; } internal void StartPolling() { if (this.SocketOpened != null) { this.SocketOpened(); } Task.Run((Func<Task?>)PollingLoop); Task.Run((Func<Task?>)SendLoop); } private async Task PollingLoop() { byte[] buffer = new byte[bufferSize]; while (Socket.State == WebSocketState.Open) { string message = null; try { message = await ReadMessageAsync(buffer); } catch (Exception e) { OnError(e); } OnMessageReceived(message); } } private async Task SendLoop() { while (Socket.State == WebSocketState.Open) { try { await HandleSendBuffer(); } catch (Exception e) { OnError(e); } await Task.Delay(20); } } private async Task<string> ReadMessageAsync(byte[] buffer) { using MemoryStream readStream = new MemoryStream(buffer.Length); WebSocketReceiveResult result; do { result = await Socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); if (result.MessageType == WebSocketMessageType.Close) { try { await Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); } catch { } OnSocketClosed(); } else { readStream.Write(buffer, 0, result.Count); } } while (!result.EndOfMessage); return Encoding.UTF8.GetString(readStream.ToArray()); } public async Task DisconnectAsync() { await Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closure requested by client", CancellationToken.None); OnSocketClosed(); } public void SendPacket(ArchipelagoPacketBase packet) { SendMultiplePackets(new List<ArchipelagoPacketBase> { packet }); } public void SendMultiplePackets(List<ArchipelagoPacketBase> packets) { SendMultiplePackets(packets.ToArray()); } public void SendMultiplePackets(params ArchipelagoPacketBase[] packets) { SendMultiplePacketsAsync(packets).Wait(); } public Task SendPacketAsync(ArchipelagoPacketBase packet) { return SendMultiplePacketsAsync(new List<ArchipelagoPacketBase> { packet }); } public Task SendMultiplePacketsAsync(List<ArchipelagoPacketBase> packets) { return SendMultiplePacketsAsync(packets.ToArray()); } public Task SendMultiplePacketsAsync(params ArchipelagoPacketBase[] packets) { TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>(); foreach (ArchipelagoPacketBase item in packets) { sendQueue.Add(new Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>(item, taskCompletionSource)); } return taskCompletionSource.Task; } private async Task HandleSendBuffer() { List<ArchipelagoPacketBase> list = new List<ArchipelagoPacketBase>(); List<TaskCompletionSource<bool>> tasks = new List<TaskCompletionSource<bool>>(); Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>> tuple = sendQueue.Take(); list.Add(tuple.Item1); tasks.Add(tuple.Item2); Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>> item; while (sendQueue.TryTake(out item)) { list.Add(item.Item1); tasks.Add(item.Item2); } if (!list.Any()) { return; } if (Socket.State != WebSocketState.Open) { throw new ArchipelagoSocketClosedException(); } ArchipelagoPacketBase[] packets = list.ToArray(); string s = JsonConvert.SerializeObject((object)packets); byte[] messageBuffer = Encoding.UTF8.GetBytes(s); int messagesCount = (int)Math.Ceiling((double)messageBuffer.Length / (double)bufferSize); for (int i = 0; i < messagesCount; i++) { int num = bufferSize * i; int num2 = bufferSize; bool endOfMessage = i + 1 == messagesCount; if (num2 * (i + 1) > messageBuffer.Length) { num2 = messageBuffer.Length - num; } await Socket.SendAsync(new ArraySegment<byte>(messageBuffer, num, num2), WebSocketMessageType.Text, endOfMessage, CancellationToken.None); } foreach (TaskCompletionSource<bool> item2 in tasks) { item2.TrySetResult(result: true); } OnPacketSend(packets); } private void OnPacketSend(ArchipelagoPacketBase[] packets) { try { if (this.PacketsSent != null) { this.PacketsSent(packets); } } catch (Exception e) { OnError(e); } } private void OnSocketClosed() { try { if (this.SocketClosed != null) { this.SocketClosed(""); } } catch (Exception e) { OnError(e); } } private void OnMessageReceived(string message) { try { if (string.IsNullOrEmpty(message) || this.PacketReceived == null) { return; } List<ArchipelagoPacketBase> list = null; try { list = JsonConvert.DeserializeObject<List<ArchipelagoPacketBase>>(message, (JsonConverter[])(object)new JsonConverter[1] { Converter }); } catch (Exception e) { OnError(e); } if (list == null) { return; } foreach (ArchipelagoPacketBase item in list) { this.PacketReceived(item); } } catch (Exception e2) { OnError(e2); } } protected void OnError(Exception e) { try { if (this.ErrorReceived != null) { this.ErrorReceived(e, e.Message); } } catch (Exception ex) { Console.Out.WriteLine("Error occured during reporting of errorOuter Errror: " + e.Message + " " + e.StackTrace + "Inner Errror: " + ex.Message + " " + ex.StackTrace); } } } public interface IConnectionInfoProvider { string Game { get; } int Team { get; } int Slot { get; } string[] Tags { get; } ItemsHandlingFlags ItemsHandlingFlags { get; } string Uuid { get; } void UpdateConnectionOptions(string[] tags); void UpdateConnectionOptions(ItemsHandlingFlags itemsHandlingFlags); void UpdateConnectionOptions(string[] tags, ItemsHandlingFlags itemsHandlingFlags); } public class ConnectionInfoHelper : IConnectionInfoProvider { private readonly IArchipelagoSocketHelper socket; public string Game { get; private set; } public int Team { get; private set; } public int Slot { get; private set; } public string[] Tags { get; internal set; } public ItemsHandlingFlags ItemsHandlingFlags { get; internal set; } public string Uuid { get; private set; } internal ConnectionInfoHelper(IArchipelagoSocketHelper socket) { this.socket = socket; Reset(); socket.PacketReceived += PacketReceived; } private void PacketReceived(ArchipelagoPacketBase packet) { if (!(packet is ConnectedPacket connectedPacket)) { if (packet is ConnectionRefusedPacket) { Reset(); } return; } Team = connectedPacket.Team; Slot = connectedPacket.Slot; if (connectedPacket.SlotInfo != null && connectedPacket.SlotInfo.ContainsKey(Slot)) { Game = connectedPacket.SlotInfo[Slot].Game; } } internal void SetConnectionParameters(string game, string[] tags, ItemsHandlingFlags itemsHandlingFlags, string uuid) { Game = game; Tags = tags ?? new string[0]; ItemsHandlingFlags = itemsHandlingFlags; Uuid = uuid ?? Guid.NewGuid().ToString(); } private void Reset() { Game = null; Team = -1; Slot = -1; Tags = new string[0]; ItemsHandlingFlags = ItemsHandlingFlags.NoItems; Uuid = null; } public void UpdateConnectionOptions(string[] tags) { UpdateConnectionOptions(tags, ItemsHandlingFlags); } public void UpdateConnectionOptions(ItemsHandlingFlags itemsHandlingFlags) { UpdateConnectionOptions(Tags, ItemsHandlingFlags); } public void UpdateConnectionOptions(string[] tags, ItemsHandlingFlags itemsHandlingFlags) { SetConnectionParameters(Game, tags, itemsHandlingFlags, Uuid); socket.SendPacket(new ConnectUpdatePacket { Tags = Tags, ItemsHandling = ItemsHandlingFlags }); } } public interface IDataStorageHelper : IDataStorageWrapper { DataStorageElement this[Scope scope, string key] { get; set; } DataStorageElement this[string key] { get; set; } } public class DataStorageHelper : IDataStorageHelper, IDataStorageWrapper { public delegate void DataStorageUpdatedHandler(JToken originalValue, JToken newValue, Dictionary<string, JToken> additionalArguments); private readonly Dictionary<string, DataStorageUpdatedHandler> onValueChangedEventHandlers = new Dictionary<string, DataStorageUpdatedHandler>(); private readonly Dictionary<Guid, DataStorageUpdatedHandler> operationSpecificCallbacks = new Dictionary<Guid, DataStorageUpdatedHandler>(); private readonly Dictionary<string, TaskCompletionSource<JToken>> asyncRetrievalTasks = new Dictionary<string, TaskCompletionSource<JToken>>(); private readonly IArchipelagoSocketHelper socket; private readonly IConnectionInfoProvider connectionInfoProvider; public DataStorageElement this[Scope scope, string key] { get { return this[AddScope(scope, key)]; } set { this[AddScope(scope, key)] = value; } } public DataStorageElement this[string key] { get { return new DataStorageElement(GetContextForKey(key)); } set { SetValue(key, value); } } internal DataStorageHelper(IArchipelagoSocketHelper socket, IConnectionInfoProvider connectionInfoProvider) { this.socket = socket; this.connectionInfoProvider = connectionInfoProvider; socket.PacketReceived += OnPacketReceived; } private void OnPacketReceived(ArchipelagoPacketBase packet) { //IL_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}"]; } private DataStorageElement GetRaceModeElement() { return this[Scope.ReadOnly, "race_mode"]; } public Hint[] GetHints(int? slot = null, int? team = null) { return GetHintsElement(slot, team).To<Hint[]>(); } public Task<Hint[]> GetHintsAsync(int? slot = null, int? team = null) { return GetHintsElement(slot, team).GetAsync<Hint[]>(); } public void TrackHints(Action<Hint[]> onHintsUpdated, bool retrieveCurrentlyUnlockedHints = true, int? slot = null, int? team = null) { GetHintsElement(slot, team).OnValueChanged += delegate(JToken _, JToken newValue, Dictionary<string, JToken> x) { onHintsUpdated(newValue.ToObject<Hint[]>()); }; if (retrieveCurrentlyUnlockedHints) { GetHintsAsync(slot, team).ContinueWith(delegate(Task<Hint[]> t) { onHintsUpdated(t.Result); }); } } public Dictionary<string, object> GetSlotData(int? slot = null) { return GetSlotData<Dictionary<string, object>>(slot); } public T GetSlotData<T>(int? slot = null) where T : class { return GetSlotDataElement(slot).To<T>(); } public Task<Dictionary<string, object>> GetSlotDataAsync(int? slot = null) { return GetSlotDataAsync<Dictionary<string, object>>(slot); } public Task<T> GetSlotDataAsync<T>(int? slot = null) where T : class { return GetSlotDataElement(slot).GetAsync<T>(); } public Dictionary<string, string[]> GetItemNameGroups(string game = null) { return GetItemNameGroupsElement(game).To<Dictionary<string, string[]>>(); } public Task<Dictionary<string, string[]>> GetItemNameGroupsAsync(string game = null) { return GetItemNameGroupsElement(game).GetAsync<Dictionary<string, string[]>>(); } public Dictionary<string, string[]> GetLocationNameGroups(string game = null) { return GetLocationNameGroupsElement(game).To<Dictionary<string, string[]>>(); } public Task<Dictionary<string, string[]>> GetLocationNameGroupsAsync(string game = null) { return GetLocationNameGroupsElement(game).GetAsync<Dictionary<string, string[]>>(); } public ArchipelagoClientState GetClientStatus(int? slot = null, int? team = null) { return GetClientStatusElement(slot, team).To<ArchipelagoClientState?>().GetValueOrDefault(); } public Task<ArchipelagoClientState> GetClientStatusAsync(int? slot = null, int? team = null) { return GetClientStatusElement(slot, team).GetAsync<ArchipelagoClientState?>().ContinueWith((Task<ArchipelagoClientState?> r) => r.Result.GetValueOrDefault()); } public void TrackClientStatus(Action<ArchipelagoClientState> onStatusUpdated, bool retrieveCurrentClientStatus = true, int? slot = null, int? team = null) { GetClientStatusElement(slot, team).OnValueChanged += delegate(JToken _, JToken newValue, Dictionary<string, JToken> x) { onStatusUpdated(newValue.ToObject<ArchipelagoClientState>()); }; if (retrieveCurrentClientStatus) { GetClientStatusAsync(slot, team).ContinueWith(delegate(Task<ArchipelagoClientState> t) { onStatusUpdated(t.Result); }); } } public bool GetRaceMode() { return GetRaceModeElement().To<int?>().GetValueOrDefault() > 0; } public Task<bool> GetRaceModeAsync() { return GetRaceModeElement().GetAsync<int?>().ContinueWith((Task<int?> t) => t.Result.GetValueOrDefault() > 0); } } public interface IDataStorageWrapper { Hint[] GetHints(int? slot = null, int? tea
Ryguy9999.ATS.ATSForAP.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using System.Threading.Tasks; using ATS_API.Helpers; using ATS_API.Localization; using Archipelago.Gifting.Net.Service; using Archipelago.Gifting.Net.Utilities.CloseTraitParser; using Archipelago.Gifting.Net.Versioning.Gifts; using Archipelago.Gifting.Net.Versioning.Gifts.Current; using Archipelago.MultiClient.Net; using Archipelago.MultiClient.Net.BounceFeatures.DeathLink; using Archipelago.MultiClient.Net.Enums; using Archipelago.MultiClient.Net.Helpers; using Archipelago.MultiClient.Net.MessageLog.Messages; using Archipelago.MultiClient.Net.Models; using Archipelago.MultiClient.Net.Packets; using BepInEx; using Eremite; using Eremite.Buildings; using Eremite.Buildings.UI.Ports; using Eremite.Buildings.UI.Seals; using Eremite.Characters.Behaviours; using Eremite.Characters.Villagers; using Eremite.Controller; using Eremite.Model; using Eremite.Model.Configs; using Eremite.Model.Effects; using Eremite.Model.Orders; using Eremite.Model.Sound; using Eremite.Model.State; using Eremite.Services; using Eremite.Services.Monitors; using Eremite.View.HUD; using Eremite.View.HUD.Reputation; using Eremite.View.HUD.TradeRoutes; using Eremite.View.Menu; using Eremite.View.Menu.Pick; using Eremite.WorldMap; using Eremite.WorldMap.UI.CustomGames; using HarmonyLib; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using QFSW.QC; using QFSW.QC.Actions; using TMPro; using UniRx; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [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("1.2.0.0")] [assembly: AssemblyInformationalVersion("1.2.0")] [assembly: AssemblyProduct("Ryguy9999.ATS.ATSForAP")] [assembly: AssemblyTitle("Ryguy9999.ATS.ATSForAP")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.0.0")] [module: UnverifiableCode] namespace Ryguy9999.ATS.ATSForAP { [StructLayout(LayoutKind.Sequential, Size = 1)] public struct APDeathlinkSuggestionTag : IQcSuggestorTag { } public sealed class APDeathlinkSuggestionAttribute : SuggestorTagAttribute { private readonly IQcSuggestorTag[] _tags = (IQcSuggestorTag[])(object)new IQcSuggestorTag[1] { (IQcSuggestorTag)(object)default(APDeathlinkSuggestionTag) }; public override IQcSuggestorTag[] GetSuggestorTags() { return _tags; } } public class APDeathlinkSuggestionSuggestor : BasicCachedQcSuggestor<string> { protected override bool CanProvideSuggestions(SuggestionContext context, SuggestorOptions options) { return ((SuggestionContext)(ref context)).HasTag<APDeathlinkSuggestionTag>(); } protected override IQcSuggestion ItemToSuggestion(string abilityName) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Expected O, but got Unknown return (IQcSuggestion)new RawSuggestion(abilityName, true); } protected override IEnumerable<string> GetItems(SuggestionContext context, SuggestorOptions options) { return new string[3] { "off", "death_only", "leave_and_death" }; } } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct APUrlSuggestionTag : IQcSuggestorTag { } public sealed class APUrlSuggestionAttribute : SuggestorTagAttribute { private readonly IQcSuggestorTag[] _tags = (IQcSuggestorTag[])(object)new IQcSuggestorTag[1] { (IQcSuggestorTag)(object)default(APUrlSuggestionTag) }; public override IQcSuggestorTag[] GetSuggestorTags() { return _tags; } } public class APUrlSuggestionSuggestor : BasicCachedQcSuggestor<string> { protected override bool CanProvideSuggestions(SuggestionContext context, SuggestorOptions options) { return ((SuggestionContext)(ref context)).HasTag<APUrlSuggestionTag>(); } protected override IQcSuggestion ItemToSuggestion(string abilityName) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Expected O, but got Unknown return (IQcSuggestion)new RawSuggestion(abilityName, true); } protected override IEnumerable<string> GetItems(SuggestionContext context, SuggestorOptions options) { return new string[1] { "archipelago.gg:" }; } } internal class ArchipelagoService : GameService, IGameService, IService { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Action <>9__26_6; public static ErrorReceivedHandler <>9__26_0; public static ItemReceivedHandler <>9__26_1; public static MessageReceivedHandler <>9__26_2; public static Action <>9__26_7; public static DeathLinkReceivedHandler <>9__26_3; public static Action<Task<Dictionary<long, ScoutedItemInfo>>> <>9__26_4; public static Func<TradeTownState, bool> <>9__27_0; public static Func<ItemInfo, int, (ItemInfo item, int index)> <>9__27_1; public static Action <>9__48_0; public static Converter<TradeTownState, int> <>9__55_0; internal void <InitializeAPConnection>b__26_0(Exception e, string message) { session = null; Plugin.Log(message); UnityLambdaQueue.Add(delegate { GameMB.NewsService.PublishNews("AP Connection Lost!", "Connection to AP has been lost! Make sure to reconnect, or you won't be able to send or receive items!", (AlertSeverity)3, (Sprite)null, (IBroadcaster)null); }); } internal void <InitializeAPConnection>b__26_6() { GameMB.NewsService.PublishNews("AP Connection Lost!", "Connection to AP has been lost! Make sure to reconnect, or you won't be able to send or receive items!", (AlertSeverity)3, (Sprite)null, (IBroadcaster)null); } internal void <InitializeAPConnection>b__26_1(ReceivedItemsHelper receivedItemsHelper) { ItemInfo val = session.Items.DequeueItem(); if (GameMB.IsGameActive && val.ItemName != null) { HandleItemReceived(val.ItemName); } PlayerPrefs.SetInt("ap.previouslyProcessedLength", session.Items.AllItemsReceived.Count); } internal void <InitializeAPConnection>b__26_2(LogMessage message) { Plugin.Log("[AP] " + (object)message); } internal void <InitializeAPConnection>b__26_3(DeathLink deathLink) { if (GameMB.IsGameActive && DeathlinkState >= 1) { UnityLambdaQueue.Add(delegate { GameMB.VillagersService.KillVillagers(1, (VillagerLossType)1, "AP Deathlink"); }); } } internal void <InitializeAPConnection>b__26_7() { GameMB.VillagersService.KillVillagers(1, (VillagerLossType)1, "AP Deathlink"); } internal void <InitializeAPConnection>b__26_4(Task<Dictionary<long, ScoutedItemInfo>> locationInfoPacket) { Plugin.Log("Interpreting trade location scout response..."); foreach (KeyValuePair<long, ScoutedItemInfo> item in locationInfoPacket.Result) { LocationScouts.Add(((ItemInfo)item.Value).LocationDisplayName, item.Value); } } internal bool <SyncGameStateToAP>b__27_0(TradeTownState town) { return town.id == 9999; } internal (ItemInfo item, int index) <SyncGameStateToAP>b__27_1(ItemInfo item, int index) { return (item, index); } internal void <HandleItemReceived>b__48_0() { MB.Settings.GetEffect("AncientGate_Hardships").Apply((EffectContextType)0, (string)null, 0); } internal int <HandleStandingLevelUp>b__55_0(TradeTownState tradeTown) { return tradeTown.standingLevel; } } private static Dictionary<string, Subject<int>> itemCallbacks = new Dictionary<string, Subject<int>>(); public 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 Sprite lockedGoodSprite; private static bool ShowAPLockIcons = true; private static bool ShowLocationAlerts = true; public static List<Action> UnityLambdaQueue = new List<Action>(); private static int VillagersToSpawn = 0; private static News ApConnectionNews; private static List<int> ReputationLocationIndices = new List<int>(); private static bool EnableBiomeKeys = false; public static bool EnabledKeepersDLC = false; public static bool EnabledNightwatchersDLC = false; public static bool PreventNaturalBPSelection = false; public static int RequiredSealTasks = 1; public static bool RequiredGuardianParts = false; public static Dictionary<string, ScoutedItemInfo> LocationScouts = new Dictionary<string, ScoutedItemInfo>(); private static Dictionary<long, int> locationsAlreadySent = new Dictionary<long, int>(); [Command(/*Could not decode attribute arguments.*/)] public static IEnumerator<ICommandAction> InitializeAPConnectionShortcut() { string @string = PlayerPrefs.GetString("ap.url." + MB.ProfilesService.GetProfileDisplayName()); string string2 = PlayerPrefs.GetString("ap.slotName." + MB.ProfilesService.GetProfileDisplayName()); return InitializeAPConnection(@string, string2, null); } [Command(/*Could not decode attribute arguments.*/)] public static IEnumerator<ICommandAction> InitializeAPConnectionShortcut(string password) { string @string = PlayerPrefs.GetString("ap.url." + MB.ProfilesService.GetProfileDisplayName()); string string2 = PlayerPrefs.GetString("ap.slotName." + MB.ProfilesService.GetProfileDisplayName()); return InitializeAPConnection(@string, string2, password); } [Command(/*Could not decode attribute arguments.*/)] public static IEnumerator<ICommandAction> AttemptInitializeAPConnection([APUrlSuggestion] string url, string player) { return AttemptInitializeAPConnection(url, player, null); } [Command(/*Could not decode attribute arguments.*/)] public static IEnumerator<ICommandAction> AttemptInitializeAPConnection([APUrlSuggestion] string url, string player, string password) { string savedUrl = PlayerPrefs.GetString("ap.url." + MB.ProfilesService.GetProfileDisplayName()); string savedSlot = PlayerPrefs.GetString("ap.slotName." + MB.ProfilesService.GetProfileDisplayName()); if ((savedUrl != string.Empty && savedUrl != url) || (savedSlot != string.Empty && savedSlot != player)) { if (GameMB.IsGameActive) { GameMB.NewsService.PublishNews("WARNING: Not Connected!", "Connection attempt to AP blocked! The given url and player name are different from the one saved to this profile. To overwrite it, please use ap.connectForce.", (AlertSeverity)3, (Sprite)null, (IBroadcaster)null); } yield return (ICommandAction)new Value((object)("WARNING: Connection attempt to AP blocked! The given url or player name ('" + url + "' and '" + player + "') are different from the one saved to this profile ('" + savedUrl + "' and '" + savedSlot + "'). To overwrite it, please use ap.connectForce."), true); } else { IEnumerator<ICommandAction> enumerator = InitializeAPConnection(url, player, password); while (enumerator.MoveNext()) { yield return enumerator.Current; } } } [Command(/*Could not decode attribute arguments.*/)] public static IEnumerator<ICommandAction> InitializeAPConnection([APUrlSuggestion] string url, string player) { return InitializeAPConnection(url, player, null); } [Command(/*Could not decode attribute arguments.*/)] public static IEnumerator<ICommandAction> InitializeAPConnection([APUrlSuggestion] string url, string player, string password) { if (session != null) { DisconnectFromGame(); } session = ArchipelagoSessionFactory.CreateSession(url, 38281); LoginResult loginResult; try { loginResult = session.TryConnectAndLogin("Against the Storm", player, (ItemsHandlingFlags)7, (Version)null, (string[])null, (string)null, password, true); } catch (Exception e2) { loginResult = (LoginResult)new LoginFailure(e2.GetBaseException().Message); } if (!loginResult.Successful) { LoginFailure failure = (LoginFailure)loginResult; string errorMessage = "Failed to Connect to " + url + " as " + player + ":"; string[] errors = failure.Errors; foreach (string error in errors) { errorMessage = errorMessage + "\n " + error; } ConnectionRefusedError[] errorCodes = failure.ErrorCodes; foreach (ConnectionRefusedError error2 in errorCodes) { errorMessage += $"\n {error2}"; } Plugin.Log(errorMessage); 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); yield return (ICommandAction)new Value((object)("Failed to connect to " + url + " as " + player + ". See BepInEx console for possibly more info."), true); yield break; } LoginSuccessful loginSuccess = (LoginSuccessful)(object)((loginResult is LoginSuccessful) ? loginResult : null); PlayerPrefs.SetString("ap.url." + MB.ProfilesService.GetProfileDisplayName(), url); PlayerPrefs.SetString("ap.slotName." + MB.ProfilesService.GetProfileDisplayName(), player); IArchipelagoSocketHelper socket = session.Socket; object obj = <>c.<>9__26_0; if (obj == null) { ErrorReceivedHandler val = delegate(Exception e, string message) { session = null; Plugin.Log(message); UnityLambdaQueue.Add(delegate { GameMB.NewsService.PublishNews("AP Connection Lost!", "Connection to AP has been lost! Make sure to reconnect, or you won't be able to send or receive items!", (AlertSeverity)3, (Sprite)null, (IBroadcaster)null); }); }; <>c.<>9__26_0 = val; obj = (object)val; } socket.ErrorReceived += (ErrorReceivedHandler)obj; lockedGoodSprite = TextureHelper.GetImageAsSprite("good-locked2.png", (SpriteType)0, (FilterMode)0); if (GameMB.IsGameActive) { SyncGameStateToAP(); if (ApConnectionNews != null) { GameMB.NewsService.RemoveNews(ApConnectionNews); } } if (LocationQueue.Any()) { Plugin.Log($"Detected {LocationQueue.Count} location(s) stored while disconnected from AP. Sending now."); Queue<string> flushQueue = new Queue<string>(LocationQueue); LocationQueue.Clear(); while (flushQueue.Any()) { CheckLocation(flushQueue.Dequeue()); } } Plugin.Log("Checking blueprint rando..."); if (loginSuccess.SlotData.ContainsKey("blueprint_items")) { if ((long)loginSuccess.SlotData["blueprint_items"] == 1) { if (loginSuccess.SlotData.ContainsKey("continue_blueprints_for_reputation")) { PreventNaturalBPSelection = (long)loginSuccess.SlotData["continue_blueprints_for_reputation"] != 1; } else { Plugin.Log("Could not find continue_blueprints_for_reputation in SlotData, falling back to blueprint AP items only."); PreventNaturalBPSelection = true; } } else { PreventNaturalBPSelection = false; } } else { Plugin.Log("Could not find blueprint_items in SlotData, falling back to vanilla blueprint behavior."); PreventNaturalBPSelection = false; } if (loginSuccess.SlotData.ContainsKey("rep_location_indices")) { object obj2 = loginSuccess.SlotData["rep_location_indices"]; ReputationLocationIndices = ((JToken)((obj2 is JArray) ? obj2 : null)).ToObject<List<int>>(); } else { Plugin.Log("Could not find rep_location_indices in SlotData, falling back to 1st and 10th Rep."); ReputationLocationIndices = new List<int> { 1, 10 }; } Plugin.Log("Checking recipe rando..."); if ((long)loginSuccess.SlotData["recipe_shuffle"] >= 1) { if (loginSuccess.SlotData.ContainsKey("production_recipes")) { object obj3 = loginSuccess.SlotData["production_recipes"]; Dictionary<string, List<List<JValue>>> recipeDict = ((JToken)((obj3 is JObject) ? obj3 : null)).ToObject<Dictionary<string, List<List<JValue>>>>(); LoadBuildingRecipesFromSlotData(recipeDict); } else { Plugin.Log("Could not find production_recipes in SlotData, falling back to SlotData seed."); if (loginSuccess.SlotData.ContainsKey("seed")) { Utils.RandomizeBuildingRecipes((long)loginSuccess.SlotData["recipe_shuffle"] == 2, (long)loginSuccess.SlotData["seed"]); } else { Plugin.Log("Could not find seed in SlotData, falling back to random seed."); Utils.RandomizeBuildingRecipes((long)loginSuccess.SlotData["recipe_shuffle"] == 2); } } 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); } } Plugin.Log("Checking DLC settings..."); if (loginSuccess.SlotData.ContainsKey("enable_keepers_dlc")) { EnabledKeepersDLC = (long)loginSuccess.SlotData["enable_keepers_dlc"] == 1; } else { Plugin.Log("Could not find enable_keepers_dlc in SlotData, falling back to false."); EnabledKeepersDLC = false; } if (loginSuccess.SlotData.ContainsKey("enable_nightwatchers_dlc")) { EnabledNightwatchersDLC = (long)loginSuccess.SlotData["enable_nightwatchers_dlc"] == 1; } else { Plugin.Log("Could not find enable_nightwatchers_dlc in SlotData, falling back to false."); EnabledNightwatchersDLC = false; } Plugin.Log("Checking biome key settings..."); if (loginSuccess.SlotData.ContainsKey("enable_biome_keys")) { EnableBiomeKeys = (long)loginSuccess.SlotData["enable_biome_keys"] == 1; } else { Plugin.Log("Could not find enable_biome_keys in SlotData, falling back to false."); EnableBiomeKeys = false; } Plugin.Log("Checking final map settings..."); if (loginSuccess.SlotData.ContainsKey("seal_items")) { RequiredGuardianParts = (long)loginSuccess.SlotData["seal_items"] == 1; } else { Plugin.Log("Could not find seal_items in SlotData, falling back to false."); RequiredGuardianParts = false; } if (loginSuccess.SlotData.ContainsKey("required_seal_tasks")) { RequiredSealTasks = (int)(long)loginSuccess.SlotData["required_seal_tasks"]; } else { Plugin.Log("Could not find required_seal_tasks in SlotData, falling back to 1."); RequiredSealTasks = 1; } IReceivedItemsHelper items = session.Items; object obj4 = <>c.<>9__26_1; if (obj4 == null) { ItemReceivedHandler val2 = delegate { ItemInfo val5 = session.Items.DequeueItem(); if (GameMB.IsGameActive && val5.ItemName != null) { HandleItemReceived(val5.ItemName); } PlayerPrefs.SetInt("ap.previouslyProcessedLength", session.Items.AllItemsReceived.Count); }; <>c.<>9__26_1 = val2; obj4 = (object)val2; } items.ItemReceived += (ItemReceivedHandler)obj4; while (session.Items.Any()) { session.Items.DequeueItem(); } IMessageLogHelper messageLog = session.MessageLog; object obj5 = <>c.<>9__26_2; if (obj5 == null) { MessageReceivedHandler val3 = delegate(LogMessage message) { Plugin.Log("[AP] " + (object)message); }; <>c.<>9__26_2 = val3; obj5 = (object)val3; } messageLog.OnMessageReceived += (MessageReceivedHandler)obj5; Plugin.Log("Checking deathlink..."); if (loginSuccess.SlotData.ContainsKey("deathlink")) { DeathlinkState = (long)loginSuccess.SlotData["deathlink"]; } else { Plugin.Log("Could not find deathlink in SlotData, falling back to off."); DeathlinkState = 0L; } deathLinkService = DeathLinkProvider.CreateDeathLinkService(session); deathLinkService.EnableDeathLink(); DeathLinkService obj6 = deathLinkService; object obj7 = <>c.<>9__26_3; if (obj7 == null) { DeathLinkReceivedHandler val4 = delegate { if (GameMB.IsGameActive && DeathlinkState >= 1) { UnityLambdaQueue.Add(delegate { GameMB.VillagersService.KillVillagers(1, (VillagerLossType)1, "AP Deathlink"); }); } }; <>c.<>9__26_3 = val4; obj7 = (object)val4; } obj6.OnDeathLinkReceived += (DeathLinkReceivedHandler)obj7; Plugin.Log("Initializing gifting service..."); ATSGiftingService.InitializeGifting(session); Plugin.Log("Scouting trade locations..."); List<long> tradeLocationIds = new List<long>(); foreach (long loc in session.Locations.AllMissingLocations) { string location = session.Locations.GetLocationNameFromId(loc, (string)null); Match match = new Regex("Trade - (\\d+) (.+)").Match(location); if (match.Success) { tradeLocationIds.Add(loc); } } session.Locations.ScoutLocationsAsync((HintCreationPolicy)2, tradeLocationIds.ToArray()).ContinueWith(delegate(Task<Dictionary<long, ScoutedItemInfo>> locationInfoPacket) { Plugin.Log("Interpreting trade location scout response..."); foreach (KeyValuePair<long, ScoutedItemInfo> item in locationInfoPacket.Result) { LocationScouts.Add(((ItemInfo)item.Value).LocationDisplayName, item.Value); } }); UnityLambdaQueue.Add(delegate { GameMB.NewsService.PublishNews("AP Connected", "Successfully connected to " + url + " as " + player + ". Have fun!", (AlertSeverity)1, (Sprite)null, (IBroadcaster)null); }); Plugin.Log("Connection to " + url + " as " + player + " complete!"); yield return (ICommandAction)new Value((object)("Connection to " + url + " as " + player + " complete!"), true); } public static void SyncGameStateToAP() { //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Expected O, but got Unknown //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Expected O, but got Unknown //IL_0492: Unknown result type (might be due to invalid IL or missing references) //IL_057b: Unknown result type (might be due to invalid IL or missing references) //IL_04f4: Unknown result type (might be due to invalid IL or missing references) //IL_04b7: Unknown result type (might be due to invalid IL or missing references) //IL_04cd: Unknown result type (might be due to invalid IL or missing references) //IL_037c: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) if (session == null) { Plugin.Log("Tried to sync to AP with a null AP session."); return; } if (!GameMB.StateService.Trade.tradeTowns.Any((TradeTownState town) => town.id == 9999)) { List<TownOfferState> list = new List<TownOfferState>(); foreach (long allMissingLocation in session.Locations.AllMissingLocations) { string locationNameFromId = session.Locations.GetLocationNameFromId(allMissingLocation, (string)null); Match match = new Regex("Trade - (\\d+) (.+)").Match(locationNameFromId); if (match.Success) { int num = int.Parse(match.Groups[1].ToString()); string text = match.Groups[2].ToString(); if (Constants.ITEM_DICT.ContainsKey(text)) { text = GoodsTypesExtensions.ToName(Constants.ITEM_DICT[text]); } else if (text.Contains("Water")) { text = "[Water] " + text; } list.Add(new TownOfferState { townId = 9999, townName = "Archipelago", hasStaticName = true, good = new Good(text, num), price = 0, fuel = 0, travelTime = 0.01f }); } } GameMB.StateService.Trade.tradeTowns.Add(new TradeTownState { id = 9999, distance = 0, townName = "Archipelago", hasStaticName = true, biome = "Royal Woodlands", isMaxStanding = true, offers = list }); } int @int = PlayerPrefs.GetInt("ap.previouslyProcessedLength", 0); foreach (var (val, num2) in session.Items.AllItemsReceived.Select((ItemInfo item, int index) => (item, index))) { if (Constants.ITEM_DICT.ContainsKey(val.ItemName)) { continue; } if (val.ItemName == "Survivor Bonding" && num2 >= @int) { MB.Settings.GetEffect("AncientGate_Hardships").Apply((EffectContextType)0, (string)null, 0); continue; } Match match2 = new Regex("(\\d+) Starting (.+)").Match(val.ItemName); if (match2.Success && num2 >= @int) { int num3 = int.Parse(match2.Groups[1].ToString()); string text2 = match2.Groups[2].ToString(); if (text2 == "Villagers") { VillagersToSpawn += num3; continue; } if (!Constants.ITEM_DICT.ContainsKey(text2)) { Plugin.Log("Could not find filler item: " + text2); } GameMB.StorageService.Store(new Good(GoodsTypesExtensions.ToName(Constants.ITEM_DICT[text2]), num3), (StorageOperationType)32); } else { string iDFromWorkshopName = Utils.GetIDFromWorkshopName(val.ItemName); if (MB.Settings.ContainsBuilding(iDFromWorkshopName)) { GameMB.GameContentService.Unlock(MB.Settings.GetBuilding(iDFromWorkshopName)); } } } 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 = lockedGoodSprite; if (Constants.SERVICE_MAPPING.ContainsKey(pair.Key)) { ((IEnumerable<NeedModel>)MB.Settings.Needs).FirstOrDefault((Func<NeedModel, bool>)((NeedModel need) => ((SO)need).Name == Constants.SERVICE_MAPPING[pair.Key])).presentation.overrideIcon = lockedGoodSprite; } } if (HasReceivedItem(pair.Key)) { SO.EffectsService.GrantRawGoodProduction(GoodsTypesExtensions.ToName(pair.Value), -999999); } } PlayerPrefs.SetInt("ap.previouslyProcessedLength", session.Items.AllItemsReceived.Count); } 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]; } else { PlayerPrefs.SetInt("ap.previouslyProcessedLength", 0); SyncGameStateToAP(); } ATSGiftingService.EnterGame(); 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<bool>(GameMB.ReputationService.OnGameResult, (Action<bool>)HandleGameResult)); GameSubscriptions.Add(ObservableExtensions.Subscribe<Hearth>((IObservable<Hearth>)GameMB.GameBlackboardService.OnHubLeveledUp, (Action<Hearth>)HandleHubLevelUp)); GameSubscriptions.Add(UniRxExtensions.Subscribe<long>(MB.RXService.Interval(1f, true), (Action)HandleSlowUpdate)); GameSubscriptions.Add(ObservableExtensions.Subscribe<TradeTownState>((IObservable<TradeTownState>)GameMB.TradeRoutesService.OnStandingLeveledUp, (Action<TradeTownState>)HandleStandingLevelUp)); GameSubscriptions.Add(ObservableExtensions.Subscribe<RouteState>((IObservable<RouteState>)GameMB.TradeRoutesService.OnRouteCollected, (Action<RouteState>)HandleTradeRouteCollect)); } [Command(/*Could not decode attribute arguments.*/)] public static void SendLocation(string location) { CheckLocation(location); } [Command(/*Could not decode attribute arguments.*/)] public static void ToggleLocationAlerts() { ToggleLocationAlerts(!ShowLocationAlerts); } [Command(/*Could not decode attribute arguments.*/)] public static void ToggleLocationAlerts(bool trueFalse) { ShowLocationAlerts = trueFalse; } [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_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) if (ShowAPLockIcons == trueFalse) { return; } ShowAPLockIcons = trueFalse; foreach (KeyValuePair<string, GoodsTypes> pair in Constants.ITEM_DICT) { if (ShowAPLockIcons && !HasReceivedItem(pair.Key)) { 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 = lockedGoodSprite; if (Constants.SERVICE_MAPPING.ContainsKey(pair.Key)) { ((IEnumerable<NeedModel>)MB.Settings.Needs).FirstOrDefault((Func<NeedModel, bool>)((NeedModel need) => ((SO)need).Name == Constants.SERVICE_MAPPING[pair.Key])).presentation.overrideIcon = lockedGoodSprite; } } else { if (!OriginalGoodIcons.Keys.Contains(GoodsTypesExtensions.ToName(pair.Value))) { continue; } MB.Settings.GetGood(GoodsTypesExtensions.ToName(pair.Value)).icon = OriginalGoodIcons[GoodsTypesExtensions.ToName(pair.Value)]; if (Constants.SERVICE_MAPPING.ContainsKey(pair.Key)) { ((IEnumerable<NeedModel>)MB.Settings.Needs).FirstOrDefault((Func<NeedModel, bool>)((NeedModel need) => ((SO)need).Name == Constants.SERVICE_MAPPING[pair.Key])).presentation.overrideIcon = OriginalGoodIcons[GoodsTypesExtensions.ToName(pair.Value)]; } } } } [Command(/*Could not decode attribute arguments.*/)] public static IEnumerator<ICommandAction> ToggleDeathlink() { long deathlinkState = DeathlinkState; long num = deathlinkState; if ((ulong)num <= 2uL) { switch (num) { case 0L: return ToggleDeathlink("death_only"); case 1L: return ToggleDeathlink("leave_and_death"); } } return ToggleDeathlink("off"); } [Command(/*Could not decode attribute arguments.*/)] public static IEnumerator<ICommandAction> ToggleDeathlink([APDeathlinkSuggestion] string deathlinkSetting) { switch (deathlinkSetting) { case "off": DeathlinkState = 0L; yield return (ICommandAction)new Value((object)"Deathlink turned off!", true); break; case "death_only": DeathlinkState = 1L; yield return (ICommandAction)new Value((object)"Deathlink set to deaths only!", true); break; case "leave_and_death": DeathlinkState = 2L; yield return (ICommandAction)new Value((object)"Deathlink set to leaving and deaths!", true); break; } } [Command(/*Could not decode attribute arguments.*/)] public static void DisconnectFromGame() { if (session != null) { session.Socket.DisconnectAsync(); session = null; } } [Command(/*Could not decode attribute arguments.*/)] public static void SendMessageToAP(string message) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (session != null) { session.Socket.SendPacket((ArchipelagoPacketBase)new SayPacket { Text = message }); } } private static void LoadBuildingRecipesFromSlotData(Dictionary<string, List<List<JValue>>> recipeDict) { 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)) { list.AddRange(val2.recipes); } } foreach (KeyValuePair<string, List<List<JValue>>> item in recipeDict) { if (!MB.Settings.ContainsBuilding(Utils.GetIDFromWorkshopName(item.Key))) { Plugin.Log("Unknown building key from SlotData: " + item.Key); continue; } BuildingModel building = MB.Settings.GetBuilding(Utils.GetIDFromWorkshopName(item.Key)); WorkshopModel val3 = (WorkshopModel)(object)((building is WorkshopModel) ? building : null); if ((Object)(object)val3 == (Object)null) { Plugin.Log("Null workshop model for building: " + item.Key); continue; } int num = 0; foreach (List<JValue> recipe in item.Value) { int num2 = list.FindIndex((WorkshopRecipeModel r) => ((RecipeModel)r).GetProducedGood() == GoodsTypesExtensions.ToName(Constants.ITEM_DICT[((JToken)recipe[0]).ToObject<string>()]) && ((RecipeModel)r).grade.level == ((JToken)recipe[1]).ToObject<int>()); if (num2 < 0) { Plugin.Log("========================================================================"); Plugin.Log("ERROR: recipeModelIndex was < 0. This probably means the apworld does not have the correct recipe definitions."); Plugin.Log("========================================================================"); return; } val3.recipes[num] = list[num2]; list.RemoveAt(num2); num++; } } ((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); return false; } if (!HasReceivedBiome(Utils.GetBiomeNameFromID(Serviceable.MetaStateService.GameConditions.biome))) { GameMB.NewsService.PublishNews("Forbidden biome!", "Your settlement is somehow in a biome you haven't unlocked through AP. No locations will be checked here!", (AlertSeverity)3, (Sprite)null, (IBroadcaster)null); return false; } long locationIdFromName = session.Locations.GetLocationIdFromName("Against the Storm", location); if (locationIdFromName < 0) { if (locationsAlreadySent.ContainsKey(locationIdFromName)) { locationsAlreadySent[locationIdFromName]++; if (locationsAlreadySent[locationIdFromName] == 10) { Plugin.Log("Silencing: " + location); } else if (locationsAlreadySent[locationIdFromName] < 10) { Plugin.Log("Location with unknown id: " + location); } } else { locationsAlreadySent[locationIdFromName] = 1; Plugin.Log("Location with unknown id: " + location); } return false; } if (session.Locations.AllLocationsChecked.Contains(locationIdFromName)) { if (locationsAlreadySent.ContainsKey(locationIdFromName)) { locationsAlreadySent[locationIdFromName]++; if (locationsAlreadySent[locationIdFromName] == 10) { Plugin.Log("Silencing: " + location); } else if (locationsAlreadySent[locationIdFromName] < 10) { Plugin.Log("Location already checked: " + location); } } else { locationsAlreadySent[locationIdFromName] = 1; Plugin.Log("Location already checked: " + location); } return false; } Plugin.Log($"Sending AP Location: {location} (id: {locationIdFromName})"); session.Locations.CompleteLocationChecksAsync(new long[1] { locationIdFromName }); if (ShowLocationAlerts) { UnityLambdaQueue.Add(delegate { GameMB.NewsService.PublishNews("Location Checked", "You just sent an item from the location: " + location, (AlertSeverity)1, (Sprite)null, (IBroadcaster)null); }); } return true; } public static bool HasReceivedGuardianPart(string part) { if (!RequiredGuardianParts) { return true; } return session.Items.AllItemsReceived.Any((ItemInfo itemInfo) => itemInfo.ItemDisplayName == part); } public static int TotalGroveExpeditionLocationsCount() { if (session == null) { return 0; } int num = 0; foreach (long allLocation in session.Locations.AllLocations) { string locationNameFromId = session.Locations.GetLocationNameFromId(allLocation, (string)null); Match match = new Regex("Coastal Grove - \\d\\d?\\w\\w Expedition").Match(locationNameFromId); if (match.Success) { num++; } } return num; } public static int CheckedGroveExpeditionLocationsCount() { if (session == null) { return 0; } int num = 0; foreach (long item in session.Locations.AllLocationsChecked) { string locationNameFromId = session.Locations.GetLocationNameFromId(item, (string)null); Match match = new Regex("Coastal Grove - \\d\\d?\\w\\w Expedition").Match(locationNameFromId); if (match.Success) { num++; } } return num; } public static int GetNextUncheckedGroveExpedition() { int num = -1; foreach (long allMissingLocation in session.Locations.AllMissingLocations) { string locationNameFromId = session.Locations.GetLocationNameFromId(allMissingLocation, (string)null); Match match = new Regex("Coastal Grove - (\\d\\d?)\\w\\w Expedition").Match(locationNameFromId); if (match.Success) { int num2 = int.Parse(match.Groups[1].ToString()); num = ((num >= 0) ? Math.Min(num, num2) : num2); } } return num; } public static int GetNextUncheckedCornerstoneForge() { int num = -1; foreach (long allMissingLocation in session.Locations.AllMissingLocations) { string locationNameFromId = session.Locations.GetLocationNameFromId(allMissingLocation, (string)null); Match match = new Regex("Ashen Thicket - Forge (\\d)\\w\\w Cornerstone").Match(locationNameFromId); if (match.Success) { int num2 = int.Parse(match.Groups[1].ToString()); num = ((num >= 0) ? Math.Min(num, num2) : num2); } } return num; } 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 bool HasReceivedBiome(string biome) { if (!EnableBiomeKeys || session == null) { return true; } return session.Items.AllItemsReceived.Any((ItemInfo itemInfo) => itemInfo.ItemDisplayName == biome); } private static void HandleItemReceived(string itemName) { //IL_0184: 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_0134: Unknown result type (might be due to invalid IL or missing references) if (!GameMB.IsGameActive || itemName.StartsWith("Guardian ")) { return; } if (itemName == "Survivor Bonding") { UnityLambdaQueue.Add(delegate { MB.Settings.GetEffect("AncientGate_Hardships").Apply((EffectContextType)0, (string)null, 0); }); return; } Match match = new Regex("(\\d+) Starting (.+)").Match(itemName); if (match.Success) { int fillerQty = int.Parse(match.Groups[1].ToString()); string fillerType = match.Groups[2].ToString(); if (fillerType == "Villagers") { VillagersToSpawn += fillerQty; return; } GameMB.StorageService.Store(new Good(GoodsTypesExtensions.ToName(Constants.ITEM_DICT[fillerType]), fillerQty), (StorageOperationType)32); UnityLambdaQueue.Add(delegate { //IL_004d: Unknown result type (might be due to invalid IL or missing references) GameMB.NewsService.PublishNews($"{fillerQty} {fillerType} received from AP!", $"{fillerQty} {fillerType} received. You will also receive this bonus in all future settlements.", (AlertSeverity)1, MB.Settings.GetGoodIcon(GoodsTypesExtensions.ToName(Constants.ITEM_DICT[fillerType])), (IBroadcaster)null); }); return; } if (Constants.ITEM_DICT.ContainsKey(itemName)) { string text = GoodsTypesExtensions.ToName(Constants.ITEM_DICT[itemName]); if (HasReceivedItem(text)) { return; } if (OriginalGoodIcons.Keys.Contains(text)) { MB.Settings.GetGood(text).icon = OriginalGoodIcons[text]; if (Constants.SERVICE_MAPPING.ContainsKey(itemName)) { ((IEnumerable<NeedModel>)MB.Settings.Needs).FirstOrDefault((Func<NeedModel, bool>)((NeedModel need) => ((SO)need).Name == Constants.SERVICE_MAPPING[itemName])).presentation.overrideIcon = OriginalGoodIcons[text]; } } SO.EffectsService.GrantRawGoodProduction(text, 999999); UnityLambdaQueue.Add(delegate { //IL_0042: Unknown result type (might be due to invalid IL or missing references) GameMB.NewsService.PublishNews(itemName + " unlocked!", itemName + " received from AP. You can now produce, gather, and obtain " + itemName + ".", (AlertSeverity)1, MB.Settings.GetGoodIcon(GoodsTypesExtensions.ToName(Constants.ITEM_DICT[itemName])), (IBroadcaster)null); }); return; } string iDFromWorkshopName = Utils.GetIDFromWorkshopName(itemName); if (MB.Settings.ContainsBuilding(iDFromWorkshopName)) { GameMB.GameContentService.Unlock(MB.Settings.GetBuilding(iDFromWorkshopName)); UnityLambdaQueue.Add(delegate { GameMB.NewsService.PublishNews(itemName + " BP received from AP!", itemName + " received. You can now build this blueprint in this and all future settlements.", (AlertSeverity)1, (Sprite)null, (IBroadcaster)null); }); return; } if (ArrayExtension.Contains<string>(new string[10] { "Royal Woodlands", "Coral Forest", "The Marshlands", "Scarlet Orchard", "Cursed Royal Woodlands", "Coastal Grove", "Ashen Thicket", "Bamboo Flats", "Rocky Ravine", "Sealed Forest" }, itemName) && GameMB.IsGameActive) { UnityLambdaQueue.Add(delegate { GameMB.NewsService.PublishNews(itemName + " unlocked from AP!", "You received the key for " + itemName + " from AP. You can now check locations in that biome!", (AlertSeverity)1, (Sprite)null, (IBroadcaster)null); }); return; } Plugin.Log("Warning: Received unknown item " + itemName + " from AP!"); BuildingModel[] buildings = MB.Settings.Buildings; foreach (BuildingModel val in buildings) { Plugin.Log(((Object)val).name); } } private static void HandleOrderRewards(OrderState order) { int number = GameMB.StateService.Orders.currentOrders.FindIndex((OrderState o) => o.id == order.id) + 1; CheckLocation("Completed Order - " + number + Utils.GetOrdinalSuffix(number) + " Pack"); } private static void HandleGameResult(bool gameWon) { PlayerPrefs.SetInt("ap.previouslyProcessedLength", 0); if (!gameWon) { return; } string biomeNameFromID = Utils.GetBiomeNameFromID(MB.MetaStateService.GameConditions.biome); if (biomeNameFromID == "Coastal Grove" || biomeNameFromID == "Ashen Thicket") { if (EnabledKeepersDLC) { CheckLocation("Victory - " + biomeNameFromID); } } else if (biomeNameFromID == "Bamboo Flats" || biomeNameFromID == "Rocky Ravine") { if (EnabledNightwatchersDLC) { CheckLocation("Victory - " + biomeNameFromID); } } else { CheckLocation("Victory - " + biomeNameFromID); } if (GameMB.GameSealService.IsSealedBiome() && GameMB.GameSealService.IsSealCompleted()) { session.SetGoalAchieved(); } } private static void HandleHubLevelUp(Hearth hearth) { int number = hearth.state.hubIndex + 1; CheckLocation("Upgraded Hearth - " + number + Utils.GetOrdinalSuffix(number) + " Tier"); } public 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"); } string text = relic.GetCurrentDecision()?.decisionTag?.displayName?.key; if (text == "DecisionTag_Corruption") { CheckLocation("Complete a Glade Event with a Corruption tag"); } if (text == "DecisionTag_Empathy") { CheckLocation("Complete a Glade Event with an Empathy tag"); } if (text == "DecisionTag_Loyalty") { CheckLocation("Complete a Glade Event with a Loyalty tag"); } if (((Object)relic.model).name.StartsWith("Angry Ghost")) { CheckLocation("Cursed Royal Woodlands - Appease an Angry Ghost"); } if (((Object)relic.model).name.StartsWith("Calm Ghost")) { CheckLocation("Cursed Royal Woodlands - Appease a Calm Ghost"); } if (((Object)relic.model).name == "Spider 3") { CheckLocation("Scarlet Orchard - Reconstruct the Sealed Spider"); } if (((Object)relic.model).name == "Snake 3") { CheckLocation("Scarlet Orchard - Reconstruct the Sea Snake"); } if (((Object)relic.model).name == "Scorpion 3") { CheckLocation("Scarlet Orchard - Reconstruct the Smoldering Scorpion"); } } private static void HandleSlowUpdate() { if (!GameMB.IsGameActive) { return; } try { GameMB.RacesService.HasRace("Human"); } catch (NullReferenceException) { Plugin.Log("Caught HasRace NRE, skipping SlowUpdate"); return; } 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 - Lizards"); } if (GameMB.RacesService.HasRace("Beaver") && GameMB.ResolveService.GetReputationGainFor("Beaver") >= 1f) { CheckLocation("First Reputation through Resolve - Beavers"); } 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"); } if (EnabledKeepersDLC && GameMB.RacesService.HasRace("Frog") && GameMB.ResolveService.GetReputationGainFor("Frog") >= 1f) { CheckLocation("First Reputation through Resolve - Frogs"); } if (EnabledNightwatchersDLC && GameMB.RacesService.HasRace("Bat") && GameMB.ResolveService.GetReputationGainFor("Bat") >= 1f) { CheckLocation("First Reputation through Resolve - Bats"); } float num = GameMB.ReputationService.GetReputationGainedFrom((ReputationChangeSource)1) + GameMB.ReputationService.GetReputationGainedFrom((ReputationChangeSource)0) + GameMB.ReputationService.GetReputationGainedFrom((ReputationChangeSource)3) + GameMB.ReputationService.GetReputationGainedFrom((ReputationChangeSource)2); string biomeNameFromID = Utils.GetBiomeNameFromID(MB.MetaStateService.GameConditions.biome); if (biomeNameFromID == "Coastal Grove" || biomeNameFromID == "Ashen Thicket") { if (EnabledKeepersDLC) { foreach (int reputationLocationIndex in ReputationLocationIndices) { if (num >= (float)reputationLocationIndex) { CheckLocation($"{reputationLocationIndex}{Utils.GetOrdinalSuffix(reputationLocationIndex)} Reputation - {biomeNameFromID}"); } } } } else if (biomeNameFromID == "Bamboo Flats" || biomeNameFromID == "Rocky Ravine") { if (EnabledNightwatchersDLC) { foreach (int reputationLocationIndex2 in ReputationLocationIndices) { if (num >= (float)reputationLocationIndex2) { CheckLocation($"{reputationLocationIndex2}{Utils.GetOrdinalSuffix(reputationLocationIndex2)} Reputation - {biomeNameFromID}"); } } } } else { foreach (int reputationLocationIndex3 in ReputationLocationIndices) { if (num >= (float)reputationLocationIndex3) { CheckLocation($"{reputationLocationIndex3}{Utils.GetOrdinalSuffix(reputationLocationIndex3)} Reputation - {biomeNameFromID}"); } } } if (GameMB.RacesService.HasRace("Human") && (double)GameMB.ResolveService.GetResolveFor("Human") >= 49.5) { CheckLocation("50 Resolve - Humans"); } if (GameMB.RacesService.HasRace("Lizard") && (double)GameMB.ResolveService.GetResolveFor("Lizard") >= 49.5) { CheckLocation("50 Resolve - Lizards"); } if (GameMB.RacesService.HasRace("Beaver") && (double)GameMB.ResolveService.GetResolveFor("Beaver") >= 49.5) { CheckLocation("50 Resolve - Beavers"); } if (GameMB.RacesService.HasRace("Harpy") && (double)GameMB.ResolveService.GetResolveFor("Harpy") >= 49.5) { CheckLocation("50 Resolve - Harpies"); } if (GameMB.RacesService.HasRace("Foxes") && (double)GameMB.ResolveService.GetResolveFor("Foxes") >= 49.5) { CheckLocation("50 Resolve - Foxes"); } if (EnabledKeepersDLC && GameMB.RacesService.HasRace("Frog") && (double)GameMB.ResolveService.GetResolveFor("Frog") >= 49.5) { CheckLocation("50 Resolve - Frogs"); } if (EnabledNightwatchersDLC && GameMB.RacesService.HasRace("Bat") && (double)GameMB.ResolveService.GetResolveFor("Bat") >= 49.5) { CheckLocation("50 Resolve - Bats"); } if (GameMB.RacesService.HasRace("Human") && Utils.GetFullyUpgradedHousedAmount("Human") >= 20) { CheckLocation("Have 20 Villagers in fully upgraded Housing - Humans"); } if (GameMB.RacesService.HasRace("Beaver") && Utils.GetFullyUpgradedHousedAmount("Beaver") >= 20) { CheckLocation("Have 20 Villagers in fully upgraded Housing - Beavers"); } if (GameMB.RacesService.HasRace("Lizard") && Utils.GetFullyUpgradedHousedAmount("Lizard") >= 20) { CheckLocation("Have 20 Villagers in fully upgraded Housing - Lizards"); } if (GameMB.RacesService.HasRace("Harpy") && Utils.GetFullyUpgradedHousedAmount("Harpy") >= 20) { CheckLocation("Have 20 Villagers in fully upgraded Housing - Harpies"); } if (GameMB.RacesService.HasRace("Foxes") && Utils.GetFullyUpgradedHousedAmount("Foxes") >= 20) { CheckLocation("Have 20 Villagers in fully upgraded Housing - Foxes"); } if (EnabledKeepersDLC && GameMB.RacesService.HasRace("Frog") && Utils.GetFullyUpgradedHousedAmount("Frog") >= 20) { CheckLocation("Have 20 Villagers in fully upgraded Housing - Frogs"); } if (EnabledNightwatchersDLC && GameMB.RacesService.HasRace("Bat") && Utils.GetFullyUpgradedHousedAmount("Bat") >= 20) { CheckLocation("Have 20 Villagers in fully upgraded Housing - Bats"); } if (VillagersToSpawn > 0) { GameMB.NewsService.PublishNews($"{VillagersToSpawn} Villagers arrived!", $"{VillagersToSpawn} extra Villagers received from AP!", (AlertSeverity)1, (Sprite)null, (IBroadcaster)null); while (VillagersToSpawn > 0) { GameMB.VillagersService.SpawnNewVillager(SO.RacesService.GetRandom()); VillagersToSpawn--; } } while (UnityLambdaQueue.Any()) { UnityLambdaQueue[0](); UnityLambdaQueue.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 HandleStandingLevelUp(TradeTownState tradeTownState) { for (int num = tradeTownState.standingLevel; num > 0; num--) { CheckLocation($"Reach level {num} standing with a neighbor"); } List<int> list = GameMB.StateService.Trade.tradeTowns.ConvertAll((TradeTownState tradeTown) => tradeTown.standingLevel); if (list.Count >= 4) { list.Sort(); int num2 = list[list.Count - 4]; for (int num3 = num2; num3 > 0; num3--) { CheckLocation($"Reach level {num3} standing with ALL 4 neighbors"); } } } private static void HandleTradeRouteCollect(RouteState route) { if (route.townId == 9999) { string arg = (route.good.name.Contains("[Water]") ? route.good.name.Replace("[Water] ", "") : Constants.ITEM_DICT.FirstOrDefault((KeyValuePair<string, GoodsTypes> pair) => GoodsTypesExtensions.ToName(pair.Value) == route.good.name).Key); CheckLocation($"Trade - {route.good.amount} {arg}"); } } } internal class ATSGiftingService { private static ICloseTraitParser<string> giftTraitParser = (ICloseTraitParser<string>)(object)new BKTreeCloseTraitParser<string>((DistanceDelegate<string>)null); private static GiftingService giftingService = null; private static Dictionary<string, GiftTrait[]> giftTagDictionary = new Dictionary<string, GiftTrait[]> { ["Wood"] = (GiftTrait[])(object)new GiftTrait[3] { new GiftTrait("Wood"), new GiftTrait("Material"), new GiftTrait("Fuel") }, ["Berry"] = (GiftTrait[])(object)new GiftTrait[3] { new GiftTrait("Berry"), new GiftTrait("Fruit"), new GiftTrait("Food") }, ["Egg"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Egg"), new GiftTrait("Food") }, ["Insect"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Insect"), new GiftTrait("Food") }, ["Meat"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Meat"), new GiftTrait("Food") }, ["Mushroom"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Mushroom"), new GiftTrait("Food") }, ["Root"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Root"), new GiftTrait("Food") }, ["Vegetable"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Vegetable"), new GiftTrait("Food") }, ["Fish"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Fish"), new GiftTrait("Food") }, ["Biscuit"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Cooking"), new GiftTrait("Food", 2.0) }, ["Jerky"] = (GiftTrait[])(object)new GiftTrait[3] { new GiftTrait("Meat"), new GiftTrait("Cooking"), new GiftTrait("Food", 2.0) }, ["Pickled Good"] = (GiftTrait[])(object)new GiftTrait[3] { new GiftTrait("Pickle"), new GiftTrait("Cooking"), new GiftTrait("Food", 2.0) }, ["Pie"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Cooking"), new GiftTrait("Food", 2.0) }, ["Porridge"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Cooking"), new GiftTrait("Food", 2.0) }, ["Skewer"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Cooking"), new GiftTrait("Food", 2.0) }, ["Paste"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Cooking"), new GiftTrait("Food", 2.0) }, ["Coat"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Cloth"), new GiftTrait("Clothing") }, ["Boot"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Leather"), new GiftTrait("Clothing") }, ["Brick"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Stone", 2.0), new GiftTrait("Material", 2.0) }, ["Fabric"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Cloth"), new GiftTrait("Material", 2.0) }, ["Plank"] = (GiftTrait[])(object)new GiftTrait[3] { new GiftTrait("Lumber"), new GiftTrait("Wood", 2.0), new GiftTrait("Material", 2.0) }, ["Pipe"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Pipe"), new GiftTrait("Metal") }, ["Part"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Gear", 3.0) }, ["Wildfire Essence"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Fire", 3.0) }, ["Ale"] = (GiftTrait[])(object)new GiftTrait[3] { new GiftTrait("Alcohol"), new GiftTrait("Root"), new GiftTrait("Luxury") }, ["Incense"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Luxury") }, ["Scroll"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Scroll"), new GiftTrait("Luxury") }, ["Tea"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Coffee"), new GiftTrait("Luxury") }, ["Training Gear"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Weapon"), new GiftTrait("Luxury") }, ["Wine"] = (GiftTrait[])(object)new GiftTrait[3] { new GiftTrait("Alcohol"), new GiftTrait("Fruit"), new GiftTrait("Luxury") }, ["Clay"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Clay"), new GiftTrait("Material") }, ["Copper Ore"] = (GiftTrait[])(object)new GiftTrait[4] { new GiftTrait("Ore"), new GiftTrait("Copper", 0.5), new GiftTrait("Metal", 0.5), new GiftTrait("Material") }, ["Scale"] = (GiftTrait[])(object)new GiftTrait[3] { new GiftTrait("Material"), new GiftTrait("Ore", 0.1), new GiftTrait("Copper", 0.1) }, ["Crystallized Dew"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Metal"), new GiftTrait("Material") }, ["Grain"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Grain") }, ["Herb"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Herb") }, ["Leather"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Leather"), new GiftTrait("Material") }, ["Plant Fiber"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Fiber"), new GiftTrait("Material") }, ["Algae"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Algae"), new GiftTrait("Material") }, ["Reed"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Material") }, ["Resin"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Material"), new GiftTrait("Amber", 0.1) }, ["Stone"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Stone"), new GiftTrait("Material") }, ["Salt"] = (GiftTrait[])(object)new GiftTrait[3] { new GiftTrait("Salted"), new GiftTrait("Mineral"), new GiftTrait("Material") }, ["Barrel"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Container") }, ["Copper Bar"] = (GiftTrait[])(object)new GiftTrait[3] { new GiftTrait("Metal"), new GiftTrait("Copper"), new GiftTrait("Material") }, ["Flour"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Flour") }, ["Dye"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Dye"), new GiftTrait("Material") }, ["Pottery"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Ceramic"), new GiftTrait("Container") }, ["Waterskin"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Container") }, ["Amber"] = (GiftTrait[])(object)new GiftTrait[3] { new GiftTrait("Amber"), new GiftTrait("Currency"), new GiftTrait("Gem") }, ["Pack of Building Materials"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Pack") }, ["Pack of Crops"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Pack") }, ["Pack of Luxury Goods"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Pack") }, ["Pack of Provisions"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Pack") }, ["Pack of Trade Goods"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Pack") }, ["Ancient Tablet"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Ancient"), new GiftTrait("Artifact") }, ["Coal"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Coal"), new GiftTrait("Fuel", 3.0) }, ["Oil"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Oil"), new GiftTrait("Fuel", 2.0) }, ["Purging Fire"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Fire") }, ["Sea Marrow"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Fossil"), new GiftTrait("Fuel", 3.0) }, ["Tool"] = (GiftTrait[])(object)new GiftTrait[1] { new GiftTrait("Tool") }, ["Drizzle Water"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Water"), new GiftTrait("Green", 0.1) }, ["Clearance Water"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Water"), new GiftTrait("Yellow", 0.1) }, ["Storm Water"] = (GiftTrait[])(object)new GiftTrait[2] { new GiftTrait("Water"), new GiftTrait("Blue", 0.1) } }; private static Dictionary<string, string> giftNames = new Dictionary<string, string> { [GoodsTypesExtensions.ToName((GoodsTypes)43)] = "Wood", [GoodsTypesExtensions.ToName((GoodsTypes)18)] = "Berry", [GoodsTypesExtensions.ToName((GoodsTypes)19)] = "Egg", [GoodsTypesExtensions.ToName((GoodsTypes)23)] = "Insect", [GoodsTypesExtensions.ToName((GoodsTypes)24)] = "Meat", [GoodsTypesExtensions.ToName((GoodsTypes)25)] = "Mushroom", [GoodsTypesExtensions.ToName((GoodsTypes)26)] = "Root", [GoodsTypesExtensions.ToName((GoodsTypes)27)] = "Vegetable", [GoodsTypesExtensions.ToName((GoodsTypes)20)] = "Fish", [GoodsTypesExtensions.ToName((GoodsTypes)11)] = "Biscuit", [GoodsTypesExtensions.ToName((GoodsTypes)12)] = "Jerky", [GoodsTypesExtensions.ToName((GoodsTypes)14)] = "Pickled Good", [GoodsTypesExtensions.ToName((GoodsTypes)15)] = "Pie", [GoodsTypesExtensions.ToName((GoodsTypes)16)] = "Porridge", [GoodsTypesExtensions.ToName((GoodsTypes)17)] = "Skewer", [GoodsTypesExtensions.ToName((GoodsTypes)13)] = "Paste", [GoodsTypesExtensions.ToName((GoodsTypes)5)] = "Coat", [GoodsTypesExtensions.ToName((GoodsTypes)48)] = "Boot", [GoodsTypesExtensions.ToName((GoodsTypes)29)] = "Brick", [GoodsTypesExtensions.ToName((GoodsTypes)30)] = "Fabric", [GoodsTypesExtensions.ToName((GoodsTypes)33)] = "Plank", [GoodsTypesExtensions.ToName((GoodsTypes)32)] = "Pipe", [GoodsTypesExtensions.ToName((GoodsTypes)31)] = "Part", [GoodsTypesExtensions.ToName((GoodsTypes)28)] = "Wildfire Essence", [GoodsTypesExtensions.ToName((GoodsTypes)47)] = "Ale", [GoodsTypesExtensions.ToName((GoodsTypes)50)] = "Incense", [GoodsTypesExtensions.ToName((GoodsTypes)51)] = "Scroll", [GoodsTypesExtensions.ToName((GoodsTypes)53)] = "Tea", [GoodsTypesExtensions.ToName((GoodsTypes)54)] = "Training Gear", [GoodsTypesExtensions.ToName((GoodsTypes)35)] = "Wine", [GoodsTypesExtensions.ToName((GoodsTypes)35)] = "Clay", [GoodsTypesExtensions.ToName((GoodsTypes)45)] = "Copper Ore", [GoodsTypesExtensions.ToName((GoodsTypes)40)] = "Scale", [GoodsTypesExtensions.ToName((GoodsTypes)46)] = "Crystallized Dew", [GoodsTypesExtensions.ToName((GoodsTypes)21)] = "Grain", [GoodsTypesExtensions.ToName((GoodsTypes)22)] = "Herb", [GoodsTypesExtensions.ToName((GoodsTypes)36)] = "Leather", [GoodsTypesExtensions.ToName((GoodsTypes)37)] = "Plant Fiber", [GoodsTypesExtensions.ToName((GoodsTypes)34)] = "Algae", [GoodsTypesExtensions.ToName((GoodsTypes)38)] = "Reed", [GoodsTypesExtensions.ToName((GoodsTypes)39)] = "Resin", [GoodsTypesExtensions.ToName((GoodsTypes)42)] = "Stone", [GoodsTypesExtensions.ToName((GoodsTypes)9)] = "Salt", [GoodsTypesExtensions.ToName((GoodsTypes)67)] = "Barrel", [GoodsTypesExtensions.ToName((GoodsTypes)44)] = "Copper Bar", [GoodsTypesExtensions.ToName((GoodsTypes)7)] = "Flour", [GoodsTypesExtensions.ToName((GoodsTypes)6)] = "Dye", [GoodsTypesExtensions.ToName((GoodsTypes)68)] = "Pottery", [GoodsTypesExtensions.ToName((GoodsTypes)69)] = "Waterskin", [GoodsTypesExtensions.ToName((GoodsTypes)64)] = "Amber", [GoodsTypesExtensions.ToName((GoodsTypes)57)] = "Pack of Building Materials", [GoodsTypesExtensions.ToName((GoodsTypes)58)] = "Pack of Crops", [GoodsTypesExtensions.ToName((GoodsTypes)59)] = "Pack of Luxury Goods", [GoodsTypesExtensions.ToName((GoodsTypes)60)] = "Pack of Provisions", [GoodsTypesExtensions.ToName((GoodsTypes)61)] = "Pack of Trade Goods", [GoodsTypesExtensions.ToName((GoodsTypes)65)] = "Ancient Tablet", [GoodsTypesExtensions.ToName((GoodsTypes)5)] = "Coal", [GoodsTypesExtensions.ToName((GoodsTypes)8)] = "Oil", [GoodsTypesExtensions.ToName((GoodsTypes)4)] = "Purging Fire", [GoodsTypesExtensions.ToName((GoodsTypes)10)] = "Sea Marrow", [GoodsTypesExtensions.ToName((GoodsTypes)63)] = "Tool", [GoodsTypesExtensions.ToName((GoodsTypes)71)] = "Drizzle Water", [GoodsTypesExtensions.ToName((GoodsTypes)70)] = "Clearance Water", [GoodsTypesExtensions.ToName((GoodsTypes)72)] = "Storm Water" }; public static void InitializeGifting(ArchipelagoSession session) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown giftingService = new GiftingService(session); giftingService.OpenGiftBox(); giftingService.OnNewGift += new GiftReceivedHandler(HandleGift); if (GameMB.IsGameActive) { HandleAllNewGifts(giftingService.GetAllGiftsAndEmptyGiftBox()); } foreach (KeyValuePair<string, GiftTrait[]> item in giftTagDictionary) { giftTraitParser.RegisterAvailableGift(item.Key, item.Value); } } public static void EnterGame() { if (giftingService != null) { HandleAllNewGifts(giftingService.GetAllGiftsAndEmptyGiftBox()); } } private static void HandleAllNewGifts(Dictionary<string, Gift> gifts) { if (!GameMB.IsGameActive) { return; } Dictionary<string, Gift> dictionary = new Dictionary<string, Gift>(); foreach (KeyValuePair<string, Gift> gift in gifts) { if (!dictionary.Keys.Contains(gift.Value.ItemName + gift.Value.SenderSlot)) { dictionary[gift.Value.ItemName + gift.Value.SenderSlot] = gift.Value; continue; } Gift obj = dictionary[gift.Value.ItemName + gift.Value.SenderSlot]; obj.Amount += gift.Value.Amount; } foreach (KeyValuePair<string, Gift> item in dictionary) { HandleGift(item.Value); } } private static void HandleGift(Gift gift) { Plugin.Log($"Incoming AP gift: \"{gift.ItemName}\" ({gift.Amount})."); string text = null; text = HandleSpecialCaseGift(gift); if (text != null) { ReceiveGoods(text, gift); return; } text = HandleStringContainsGift(gift); if (text != null) { ReceiveGoods(text, gift); return; } text = HandleGiftTagGift(gift); if (text != null) { ReceiveGoods(text, gift); return; } Plugin.Log(string.Format("Failed to process gift \"{0}\" ({1}) with tags [{2}]", gift.ItemName, gift.Amount, string.Join(", ", gift.Traits.Select((GiftTrait trait) => trait.Trait)))); giftingService.RefundGift(gift); } private static void ReceiveGoods(string goodsId, Gift gift) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) ArchipelagoService.UnityLambdaQueue.Add(delegate { GameMB.NewsService.PublishNews("You received a gift through AP!", $"{gift.Amount} {giftNames[goodsId]} received from {ArchipelagoService.session.Players.GetPlayerAlias(gift.SenderSlot)}!", (AlertSeverity)1, (Sprite)null, (IBroadcaster)null); }); GameMB.StorageService.Store(new Good(goodsId, gift.Amount), (StorageOperationType)32); giftingService.RemoveGiftFromGiftBox(gift.ID); } private static string HandleSpecialCaseGift(Gift gift) { return null; } private static string HandleStringContainsGift(Gift gift) { string text = " " + gift.ItemName + " "; foreach (KeyValuePair<string, string> giftName in giftNames) { if (text.Contains(giftName.Value + " ") || text.Contains(" " + giftName.Value)) { Plugin.Log("Matched gift on gift name: " + giftName.Value); return giftName.Key; } } return null; } private static string HandleGiftTagGift(Gift gift) { List<string> parsedGiftOptions = giftTraitParser.FindClosestAvailableGift(gift.Traits); if (parsedGiftOptions.Count > 0) { object[] traits = gift.Traits; Plugin.Log("Matched gift using gift tags: " + string.Join(", ", traits) + ". Result: " + string.Join(", ", parsedGiftOptions)); return giftNames.FirstOrDefault((KeyValuePair<string, string> pair) => pair.Value == parsedGiftOptions[0]).Key; } return null; } [Command(/*Could not decode attribute arguments.*/)] public static IEnumerator<ICommandAction> SendGift(string player, string resourceToGift, int quantity) { if (!GameMB.IsGameActive) { yield return (ICommandAction)new Value((object)"You must be currently in a settlement to send/receive gifts.", true); yield break; } if (giftingService == null) { yield return (ICommandAction)new Value((object)"Not connected to archipelago, cannot send gift.", true); yield break; } string resourceId; if (!giftNames.ContainsKey(resourceToGift)) { switch (resourceToGift) { case "Wood": resourceId = GoodsTypesExtensions.ToName((GoodsTypes)43); break; case "Drizzle Water": resourceId = GoodsTypesExtensions.ToName((GoodsTypes)71); break; case "Clearance Water": resourceId = GoodsTypesExtensions.ToName((GoodsTypes)70); break; case "Storm Water": resourceId = GoodsTypesExtensions.ToName((GoodsTypes)72); break; default: if (Constants.ITEM_DICT.ContainsKey(resourceToGift)) { resourceId = GoodsTypesExtensions.ToName(Constants.ITEM_DICT[resourceToGift]); break; } yield return (ICommandAction)new Value((object)("Could not understand resource \"" + resourceToGift + "\"."), true); yield break; } } else { resourceId = resourceToGift; } if (!giftingService.CanGiftToPlayer(player, giftTagDictionary[giftNames[resourceId]].Select((GiftTrait trait) => trait.Trait)).CanGift) { yield return (ICommandAction)new Value((object)("Cannot gift to " + player + ". Maybe their giftbox isn't open or their game can't accept " + resourceToGift + "."), true); } else if (GameMB.StorageService.GetAmount(resourceId) < quantity) { yield return (ICommandAction)new Value((object)$"Insufficient resources in warehouse to send {quantity} of \"{resourceToGift}\".", true); } else { giftingService.SendGift(new GiftItem(giftNames[resourceId], quantity, (BigInteger)0), giftTagDictionary[giftNames[resourceId]], player); GameMB.StorageService.Remove(new Good(resourceId, quantity), (StorageOperationType)32); yield return (ICommandAction)new Value((object)"Gift sent!", true); } } } internal class Constants { public enum CustomHookType { APItemReceived = 9999 } public static Dictionary<string, GoodsTypes> ITEM_DICT = new Dictionary<string, GoodsTypes> { ["Berries"] = (GoodsTypes)18, ["Eggs"] = (GoodsTypes)19, ["Insects"] = (GoodsTypes)23, ["Meat"] = (GoodsTypes)24, ["Mushrooms"] = (GoodsTypes)25, ["Roots"] = (GoodsTypes)26, ["Vegetables"] = (GoodsTypes)27, ["Fish"] = (GoodsTypes)20, ["Biscuits"] = (GoodsTypes)11, ["Jerky"] = (GoodsTypes)12, ["Pickled Goods"] = (GoodsTypes)14, ["Pie"] = (GoodsTypes)15, ["Porridge"] = (GoodsTypes)16, ["Skewers"] = (GoodsTypes)17, ["Paste"] = (GoodsTypes)13, ["Coats"] = (GoodsTypes)49, ["Boots"] = (GoodsTypes)48, ["Bricks"] = (GoodsTypes)29, ["Fabric"] = (GoodsTypes)30, ["Planks"] = (GoodsTypes)33, ["Pipes"] = (GoodsTypes)32, ["Parts"] = (GoodsTypes)31, ["Wildfire Essence"] = (GoodsTypes)28, ["Ale"] = (GoodsTypes)47, ["Incense"] = (GoodsTypes)50, ["Scrolls"] = (GoodsTypes)51, ["Tea"] = (GoodsTypes)53, ["Training Gear"] = (GoodsTypes)54, ["Wine"] = (GoodsTypes)55, ["Clay"] = (GoodsTypes)35, ["Copper Ore"] = (GoodsTypes)45, ["Scales"] = (GoodsTypes)40, ["Crystallized Dew"] = (GoodsTypes)46, ["Grain"] = (GoodsTypes)21, ["Herbs"] = (GoodsTypes)22, ["Leather"] = (GoodsTypes)36, ["Plant Fiber"] = (GoodsTypes)37, ["Algae"] = (GoodsTypes)34, ["Reeds"] = (GoodsTypes)38, ["Resin"] = (GoodsTypes)39, ["Stone"] = (GoodsTypes)42, ["Salt"] = (GoodsTypes)9, ["Barrels"] = (GoodsTypes)67, ["Copper Bars"] = (GoodsTypes)44, ["Flour"] = (GoodsTypes)7, ["Dye"] = (GoodsTypes)6, ["Pottery"] = (GoodsTypes)68, ["Waterskins"] = (GoodsTypes)69, ["Amber"] = (GoodsTypes)64, ["Pack of Building Materials"] = (GoodsTypes)57, ["Pack of Crops"] = (GoodsTypes)58, ["Pack of Luxury Goods"] = (GoodsTypes)59, ["Pack of Provisions"] = (GoodsTypes)60, ["Pack of Trade Goods"] = (GoodsTypes)61, ["Ancient Tablet"] = (GoodsTypes)65, ["Coal"] = (GoodsTypes)5, ["Oil"] = (GoodsTypes)8, ["Purging Fire"] = (GoodsTypes)4, ["Sea Marrow"] = (GoodsTypes)10, ["Tools"] = (GoodsTypes)63 }; public static Dictionary<string, string> SERVICE_MAPPING = new Dictionary<string, string> { ["Jerky"] = "Jerky", ["Porridge"] = "Porridge", ["Paste"] = "Paste", ["Skewer"] = "Skewer", ["Biscuits"] = "Biscuits", ["Pie"] = "Pie", ["Pickled Goods"] = "Pickled Goods", ["Boots"] = "Boots", ["Coats"] = "Clothes", ["Ale"] = "Leasiure", ["Training Gear"] = "Bloodthirst", ["Incense"] = "Religion", ["Scrolls"] = "Education", ["Wine"] = "Luxury", ["Tea"] = "Treatment" }; public const int PRODUCTIVITY_MODIFIER = 999999; public const int TRADE_TOWN_ID = 9999; public const string SCOUT_STRING_PREFIX = "[AP Scout]"; public const string AP_GAME_NAME = "Against the Storm"; public const long RECIPE_SHUFFLE_VANILLA = 0L; public const long RECIPE_SHUFFLE_EXCLUDE_CRUDE_WS_AND_MS_POST = 1L; public const long RECIPE_SHUFFLE_EXCLUDE_CRUDE_WS = 2L; public const long RECIPE_SHUFFLE_EXCLUDE_MS_POST = 3L; public const long RECIPE_SHUFFLE_FULL_SHUFFLE = 4L; 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"; } internal class GamePatches { private static BuildingCategoryModel apExpeditionCategoryModel = new BuildingCategoryModel { name = "AP Checks", icon = TextureHelper.GetImageAsSprite("ap-category-icon.png", (SpriteType)0, (FilterMode)0), isActive = true, isOnHUD = false, isDebugOnHUD = false, shortName = LocalizationManager.ToLocaText("ATSForAP_GameUI_APExpeditionCategory"), displayName = LocalizationManager.ToLocaText("ATSForAP_GameUI_APExpeditionCategory") }; private static CustomGamePopup customGamePopupInstance; [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(/*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 = ((RecipeState)__instance).active && ArchipelagoService.HasReceivedItem(model.producedGood.Name); } [HarmonyPostfix] [HarmonyPatch(typeof(GathererHut), "SetUp")] [HarmonyPatch(new Type[] { typeof(GathererHutModel), typeof(GathererHutState) })] private static void CampSetUpPostfix(GathererHut __instance, GathererHutModel model, GathererHutState state) { foreach (RecipeState recipe in __instance.state.recipes) { recipe.active = recipe.active && ArchipelagoService.HasReceivedItem(MB.Settings.GetRecipe(recipe.model).GetProducedGood()); } } [HarmonyPostfix] [HarmonyPatch(typeof(FishingHut), "SetUp")] [HarmonyPatch(new Type[] { typeof(FishingHutModel), typeof(FishingHutState) })] private static void FishingHutSetUpPostfix(GathererHut __instance, FishingHutModel model, FishingHutState state) { foreach (RecipeState recipe in state.recipes) { recipe.active = recipe.active && ArchipelagoService.HasReceivedItem(MB.Settings.GetRecipe(recipe.model).GetProducedGood()); } } [HarmonyPostfix] [HarmonyPatch(typeof(Farm), "SetUp")] [HarmonyPatch(new Type[] { typeof(FarmModel), typeof(FarmState) })] private static void FarmSetUpPostfix(Farm __instance, FarmModel model, FarmState state) { foreach (RecipeState recipe in __instance.state.recipes) { recipe.active = recipe.active && ArchipelagoService.HasReceivedItem(MB.Settings.GetRecipe(recipe.model).GetProducedGood()); } } [HarmonyPrefix] [HarmonyPatch(typeof(TradeRoutesGenerator), "RegenerateOffersFor")] [HarmonyPatch(new Type[] { typeof(TradeTownState) })] private static bool TradeRoutesRegenerateForPrefix(TradeTownState town) { if (town.id == 9999) { return false; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(ExtendOffersSlot), "CanBuy")] private static bool TradeTownExtendPrefix(ExtendOffersSlot __instance, ref bool __result) { if (__instance.state.id == 9999) { __result = false; return false; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(ReputationRewardButton), "UpdateCounter")] [HarmonyPatch(new Type[] { typeof(int) })] private static void ReputationBlueprintCounterPrefix(ref int amount) { if (ArchipelagoService.PreventNaturalBPSelection) { amount = GameMB.EffectsService.GetWildcardPicksLeft(); } } [HarmonyPrefix] [HarmonyPatch(typeof(CalendarDisplay), "SelfUpdate")] private static bool CalendarUpdatePrefix(CalendarDisplay __instance) { __instance.UpdateCalendar(GameMB.CalendarService.GetCurrentSeasonProgress()); __instance.CheckForStormSound(); return false; } [HarmonyPostfix] [HarmonyPatch(typeof(Relic), "AddAllRewards")] private static void RelicAllRewardsPostfix(Relic __instance) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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) foreach (Good item in ((GoodsCollection)__instance.state.rewards).ToList()) { if (!ArchipelagoService.HasReceivedItem(item.name)) { ((GoodsCollection)__instance.state.rewards).Remove(item); } } } [HarmonyPostfix] [HarmonyPatch(typeof(CustomGameTradeTownsPanel), "PrepareAll")] private static void CustomGameTradeTownSetUpPostfix(CustomGameTradeTownsPanel __instance) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < 4 && i < __instance.all.Count; i++) { __instance.picked.Add(__instance.all[i]); } } private static string GetRelevantPart() { SealState val = ((Serviceable.StateService.Buildings.seals.Count > 0) ? Serviceable.StateService.Buildings.seals.First() : null); int i; for (i = 0; i < val.kits.Length && val.kits[i].completedIndex >= 0; i++) { } return i switch { 0 => "Guardian Heart", 1 => "Guardian Blood", 2 => "Guardian Feathers", 3 => "Guardian Essence", _ => "", }; } private static bool HasCompletedEnoughOrders() { SealState val = ((Serviceable.StateService.Buildings.seals.Count > 0) ? Serviceable.StateService.Buildings.seals.First() : null); int i; for (i = 0; i < val.kits.Length && val.kits[i].completedIndex >= 0; i++) { } int num = 0; for (int j = 0; j < val.kits[i].orders.Length; j++) { if (GameMB.OrdersService.CanComplete(val.kits[i].orders[j], MB.Settings.GetOrder(val.kits[i].orders[j].model))) { num++; } } return num >= ArchipelagoService.RequiredSealTasks; } [HarmonyPostfix] [HarmonyPatch(typeof(PartSlot), "StartButtons")] private static void PartSlotButtonsPostfix(PartSlot __instance) { UniRxExtensions.Subscribe<Unit>((IObservable<Unit>)__instance.completeButton.OnFailedClick, (Action)delegate { if (!ArchipelagoService.HasReceivedGuardianPart(GetRelevantPart())) { GameMB.NewsService.PublishNews("Can't complete seal part!", "\"Seal Parts\" option is on for this AP slot, meaning you need to receive \"" + GetRelevantPart() + "\" before you can complete this phase of the Seal.", (AlertSeverity)2, (Sprite)null, (IBroadcaster)null); } else if (!HasCompletedEnoughOrders()) { GameMB.NewsService.PublishNews("Can't complete seal part!", $"\"Required Seal Tasks\" is set to {ArchipelagoService.RequiredSealTasks} for this AP slot, meaning you need to fulfill the requirements for that many tasks before you can complete this phase of the Seal.", (AlertSeverity)2, (Sprite)null, (IBroadcaster)null); } }); } [HarmonyPostfix] [HarmonyPatch(typeof(PartSlot), "UpdateButton")] private static void PartSlotUpdateButtonPostfix(PartSlot __instance) { __instance.completeButton.CanInteract = __instance.completeButton.CanInteract && HasCompletedEnoughOrders() && (!ArchipelagoService.RequiredGuardianParts || ArchipelagoService.HasReceivedGuardianPart(GetRelevantPart())); } [HarmonyPostfix] [HarmonyPatch(typeof(VersionText), "Start")] private static void VersionTextStartPostfix(VersionText __instance) { TMP_Text text = __instance.text; text.text += " + APv1.2.0"; } [HarmonyPrefix] [HarmonyPatch(typeof(TownOfferSlot), "SetUpSlots")] private static bool SetUpTownOfferSlotPostfix(TownOfferSlot __instance) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Invalid comparison between Unknown and I4 //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Invalid comparison between Unknown and I4 //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Invalid comparison between Unknown and I4 string text = ArchipelagoService.LocationScouts.Keys.FirstOrDefault(delegate(string scout) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) Match match = new Regex("Trade - (\\d+) (.+)").Match(scout); int num = int.Parse(match.Groups[1].ToString()); string text3 = match.Groups[2].ToString(); if (Constants.ITEM_DICT.ContainsKey(text3)) { text3 = GoodsTypesExtensions.ToName(Constants.ITEM_DICT[text3]); } else if (text3.Contains("Water")) { text3 = "[Water] " + text3; } return __instance.state.good.name == text3 && __instance.state.good.amount == num; }); if (__instance.state.townId == 9999 && text != null) { ScoutedItemInfo val = ArchipelagoService.LocationScouts[text]; string alias = val.Player.Alias; string itemDisplayName = ((ItemInfo)val).ItemDisplayName; string text2 = (((int)((ItemInfo)val).Flags == 4) ? "trap" : (((int)((ItemInfo)val).Flags == 2) ? "useful" : (((int)((ItemInfo)val).Flags == 1) ? "progression" : "filler"))); __instance.good.SetUp(GameMB.TradeRoutesService.GetFullGood(__instance.state)); __instance.price.SetUp(new Good("[AP Scout]||" + text2 + "||" + itemDisplayName + "||" + alias, 0)); __instance.fuel.SetUp(GameMB.TradeRoutesService.GetFullFuel(__instance.state)); return false; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(GoodSlot), "SetUpIcon")] private static bool SetUpSlotIconPrefix(GoodSlot __instance) { if (__instance.good.name.StartsWith("[AP Scout]")) { __instance.icon.sprite = TextureHelper.GetImageAsSprite("ap-icon.png", (SpriteType)0, (FilterMode)0); return false; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(GoodSlot), "SetUpCounter")] private static bool SetUpSlotCounterPrefix(GoodSlot __instance) { if (__instance.good.name.StartsWith("[AP Scout]")) { ComponentExtensions.SetActive((Component)(object)__instance.counter.transform.parent, false); return false; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(GoodTooltip), "Show")] [HarmonyPatch(new Type[] { typeof(RectTransform), typeof(TooltipSettings), typeof(Good), typeof(GoodTooltipMode), typeof(string) })] private static bool GoodTooltipShowPrefix(GoodTooltip __instance, RectTransform target, TooltipSettings settings, Good good, GoodTooltipMode mode, string footnote = null) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (good.name.StartsWith("[AP Scout]")) { Match match = new Regex("^\\|\\|(.*)\\|\\|(.*)\\|\\|(.*)$").Match(good.name.Substring("[AP Scout]".Length)); string text = match.Groups[3].ToString(); string text2 = match.Groups[2].ToString(); string text3 = match.Groups[1].ToString(); __instance.mode = mode; __instance.nameText.text = text + "'s " + text2; __instance.descText.text = "You can trade for " + text + "'s " + text2 + "! " + text3.Replace("trap", "They might not be too happy about it though...").Replace("filler", "It's uncertain how much they'll use it though.").Replace("useful", "They'll probably get some use out of this.") .Replace("progression", "It seems pretty important to them!"); __instance.labelText.text = "Archipelago Item"; __instance.storageParent.SetActive(false); if ((Object)(object)__instance.icon != (Object)null) { __instance.icon.sprite = TextureHelper.GetImageAsSprite("ap-icon.png", (SpriteType)0, (FilterMode)0); } __instance.footnoteParent.SetActive(false); ((Tooltip)__instance).AnimateShow(target, settings); return false; } return true; } [HarmonyPostfix] [HarmonyPatch(typeof(HarvestDeposit), "OnComplete")] private static void HarvestCompletePostfix(HarvestDeposit __instance) { string name = ((Object)__instance.deposit).name; if (name.Contains("Marshlands Infinite")) { if (name.Contains("Grain")) { ArchipelagoService.CheckLocation("The Marshlands - Harvest from an Ancient Proto Wheat"); } if (name.Contains("Meat")) { ArchipelagoService.CheckLocation("The Marshlands - Harvest from a Dead Leviathan"); } if (name.Contains("Mushroom")) { ArchipelagoService.CheckLocation("The Marshlands - Harvest from a Giant Proto Fungus"); } } } [HarmonyPostfix] [HarmonyPatch(typeof(PortRewardsPickPanel), "GetCategories")] private static void GetCategoriesPostfix(ref List<BuildingCategoryModel> __result) { if (ArchipelagoService.TotalGroveExpeditionLocationsCount() > 0) { __result.Add(apExpeditionCategoryModel); } } [HarmonyPrefix] [HarmonyPatch(typeof(BuildingCategoryRadialSlot), "CountOwned")] private static bool PortMenuSlotCountOwnedPrefix(BuildingCategoryRadialSlot __instance, ref int __result) { if (((LabelModel)__instance.model).displayName.Text == "AP Checks") { __result = ArchipelagoService.CheckedGroveExpeditionLocationsCount(); return false; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(BuildingCategoryRadialSlot), "CountMax")] private static bool PortMenuSlotCountMaxPrefix(BuildingCategoryRadialSlot __instance, ref int __result) { if (((LabelModel)__instance.model).displayName.Text == "AP Checks") { __result = ArchipelagoService.TotalGroveExpeditionLocationsCount(); return false; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(PortRewardsPickPanel), "GetCurrentCategory")] private static bool PortRewardPanelGetCategoryPrefix(PortRewardsPickPanel __instance, ref BuildingCategoryModel __result) { Plugin.Log(__instance.port.state.pickedCategory); if (__instance.port.state.pickedCategory == "AP Checks") { __result = apExpeditionCategoryModel; return false; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(PortRewardsGenerator), "SetMainReward")] private static bool PortRewardGeneratorMainRewardPrefix(PortRewardsGenerator __instance) { if (__instance.port.state.pickedCategory == "AP Checks") { int nextUncheckedGroveExpedition = ArchipelagoService.GetNextUncheckedGroveExpedition(); if (nextUncheckedGroveExpedition < 0) { return true; } ArchipelagoService.CheckLocation($"Coastal Grove - {nextUncheckedGroveExpedition}{Utils.GetOrdinalSuffix(nextUncheckedGroveExpedition)} Expedition"); return false; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(PortRewardsPanel), "HasBlueprintReward")] private static bool PortRewardsBPRewardPrefix(PortRewardsPanel __instance, ref bool __result) { if (__instance.port.state.pickedCategory == "AP Checks") { __result = false; return false; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(PerkCrafter), "CreateCurrentPerk")] private static void PerkCraftingCreatePrefix() { int nextUncheckedCornerstoneForge = ArchipelagoService.GetNextUncheckedCornerstoneForge(); if (nextUncheckedCornerstoneForge >= 0) { ArchipelagoService.CheckLocation($"Ashen Thicket - Forge {nextUncheckedCornerstoneForge}{Utils.GetOrdinalSuffix(nextUncheckedCornerstoneForge)} Cornerstone"); } } [HarmonyPrefix] [HarmonyPatch(typeof(AchivsService), "SendAchivs")] private static bool SendAchievementPrefix() { return false; } [HarmonyPostfix] [HarmonyPatch(typeof(Villager), "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(typeof(CustomGamePopup), "Show")] private static void CustomGameShowPostfix(CustomGamePopup __instance) { customGamePopupInstance = __instance; ((Selectable)__instance.embarkButton).interactable = ArchipelagoService.HasReceivedBiome(Utils.GetBiomeNameFromID(((SO)__instance.biomePanel.GetBiome()).Name)); ((UnityEvent<int>)(object)__instance.biomePanel.dropdown.onValueChanged).AddListener((UnityAction<int>)OnBiomeValueChanged); } private static void OnBiomeValueChanged(int value) { ((Selectable)customGamePopupInstance.embarkButton).interactable = ArchipelagoService.HasReceivedBiome(Utils.GetBiomeNameFromID(((SO)customGamePopupInstance.biomePanel.GetBiome()).Name)); } [HarmonyPrefix] [HarmonyPatch(typeof(CustomGameBiomePanel), "CanBiomeBePicked")] [HarmonyPatch(new Type[] { typeof(BiomeModel) })] private static bool CustomGameBiomeCanBePickedPrefix(CustomGameBiomePanel __instance, BiomeModel biome, ref bool __result) { if (!ArchipelagoService.HasReceivedBiome(Utils.GetBiomeNameFromID(((SO)biome).Name))) { __result = false; return false; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(BuildingsPickScreen), "UpdateConfirmButton")] private static bool EmbarkScreenShowPostfix(BuildingsPickScreen __instance) { if (!ArchipelagoService.HasReceivedBiome(Utils.GetBiomeNameFromID(((SO)__instance.field.Biome).Name))) { __instance.confirmButton.CanInteract = false; return false; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(Relic), "FireResolveEvents")] private static void RelicResolvePrefix(Relic __instance) { ArchipelagoService.HandleRelicResolve(__instance); } } [BepInPlugin("Ryguy9999.ATS.ATSForAP", "Ryguy9999.ATS.ATSForAP", "1.2.0")] public class Plugin : BaseUnityPlugin { public static Plugin Instance; private Harmony harmony; private void Awake() { Instance = this; harmony = Harmony.CreateAndPatchAll(typeof(Plugin), (string)null); harmony.PatchAll(typeof(GamePatches)); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin Ryguy9999.ATS.ATSForAP loaded."); } public static void Log(object o) { Log(o.ToString()); } public static void Log(string s) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)s); } public static void Logify(object o, int maxDepth = 10) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown Log(JsonConvert.SerializeObject(o, (Formatting)0, new JsonSerializerSettings { ReferenceLoopHandling = (ReferenceLoopHandling)1, MaxDepth = maxDepth })); } [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; LocalizationManager.AddString("ATSForAP_GameUI_APExpeditionCategory", "AP Checks", (SystemLanguage)10); MB.Settings.customGameConfig.reputationDefaultIndex = 2; MB.Settings.customGameConfig.stormDurationDefaultIndex = 3; MB.Settings.customGameConfig.initialPositiveEffectsAmount = 1; MB.Settings.customGameConfig.blightFootprintDefaultIndex = 2; GoodRef[] embarkGoods = MB.Settings.customGameConfig.embarkGoods; foreach (GoodRef val in embarkGoods) { if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)43)) { val.amount = 70; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)5)) { val.amount = 28; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)28)) { val.amount = 6; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)31)) { val.amount = 28; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)32)) { val.amount = 14; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)19)) { val.amount = 42; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)26)) { val.amount = 28; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)27)) { val.amount = 28; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)24)) { val.amount = 28; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)25)) { val.amount = 28; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)23)) { val.amount = 28; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)18)) { val.amount = 28; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)20)) { val.amount = 28; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)33)) { val.amount = 7; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)30)) { val.amount = 7; } else if (val.Name == GoodsTypesExtensions.ToName((GoodsTypes)29)) { val.amount = 7; } } } [HarmonyPatch(typeof(GameController), "StartGame")] [HarmonyPostfix] private static void HookEveryGameStart() { ArchipelagoService.EnterGame(); } } internal class Utils { public static int GetFullyUpgradedHouse