Decompiled source of ContentWarningArchipelago v1.0.3
ContentWarningArchipelago.dll
Decompiled a day 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.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading.Tasks; using Archipelago.MultiClient.Net; using Archipelago.MultiClient.Net.Enums; using Archipelago.MultiClient.Net.Helpers; using Archipelago.MultiClient.Net.Models; using Archipelago.MultiClient.Net.Packets; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ContentWarningArchipelago.Core; using ContentWarningArchipelago.Data; using ContentWarningArchipelago.Patches; using ContentWarningArchipelago.UI; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using MyceliumNetworking; using Newtonsoft.Json; using Photon.Pun; using Photon.Realtime; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using Zorro.Core; using pworld.Scripts.Extensions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ContentWarningArchipelago")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Archipelago Randomizer for Content Warning")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+7a8fe43c6a672dcf7da9254c9a58671e13d29218")] [assembly: AssemblyProduct("Content Warning Archipelago")] [assembly: AssemblyTitle("ContentWarningArchipelago")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ContentWarningArchipelago { [BepInPlugin("automagic.cw-archipelago", "Content Warning Archipelago", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Logger; public static ArchipelagoClient connection; internal const uint MyceliumModId = 2718281828u; public static APConfig APConfig { get; private set; } public static string apAddress => APConfig?.address.Value ?? "archipelago.gg"; public static int apPort => APConfig?.port.Value ?? 38281; public static string apPassword => APConfig?.password.Value ?? ""; public static string apSlot => APConfig?.slot.Value ?? ""; public static Plugin Instance { get; private set; } public static bool isConnecting { get; private set; } private void Awake() { //IL_006c: Unknown result type (might be due to invalid IL or missing references) Instance = this; Logger = ((BaseUnityPlugin)this).Logger; Logger.LogInfo((object)"[CWArch] automagic.cw-archipelago v0.1.0 loading…"); APConfig = new APConfig(((BaseUnityPlugin)this).Config); Logger.LogInfo((object)$"[CWArch] Config loaded. AP address: {apAddress}:{APConfig.port.Value}"); ItemData.Init(); LocationData.Init(); new Harmony("automagic.cw-archipelago").PatchAll(); MyceliumNetwork.RegisterNetworkObject((object)this, 2718281828u, 0); Logger.LogInfo((object)"[CWArch] Harmony patches applied."); } private void Start() { connection = new ArchipelagoClient(); Logger.LogInfo((object)"[CWArch] ArchipelagoClient created. Waiting for connection."); } private void OnDestroy() { MyceliumNetwork.DeregisterNetworkObject((object)this, 2718281828u, 0); } private void Update() { if (connection.connected) { connection.checkItemsReceived?.MoveNext(); connection.incomingItemHandler?.MoveNext(); connection.outgoingCheckHandler?.MoveNext(); } } [CustomRPC] internal void LocationFound(string locName, RPCInfo info) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (info.SenderSteamID != SteamUser.GetSteamID()) { Logger.LogDebug((object)("[CWArch] Received remote location-found notification: '" + locName + "'")); APNotificationUI.ShowLocationFound(locName); } } public static async void Connect() { if (isConnecting || connection.connected) { return; } isConnecting = true; try { await connection.TryConnect(apAddress, apPort, apPassword, apSlot); } finally { isConnecting = false; } } public static void Disconnect() { connection.TryDisconnect(); } public static void SendCheck(long locationId) { connection.ActivateCheck(locationId); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "automagic.cw-archipelago"; public const string PLUGIN_NAME = "Content Warning Archipelago"; public const string PLUGIN_VERSION = "0.1.0"; } } namespace ContentWarningArchipelago.UI { public class APConnectionPanelUI : MonoBehaviour { private TMP_InputField _addressField; private TMP_InputField _slotField; private TMP_InputField _passwordField; private Button _connectButton; private Button _disconnectButton; private TextMeshProUGUI _statusLabel; private static readonly Color ColDisconnected = new Color(0.9f, 0.22f, 0.22f); private static readonly Color ColConnecting = new Color(1f, 0.85f, 0.1f); private static readonly Color ColConnected = new Color(0.2f, 0.88f, 0.2f); private void Awake() { BuildUI(); PopulateFromConfig(); } private void Update() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (_statusLabel != null) { if (Plugin.isConnecting) { ((TMP_Text)_statusLabel).text = "○ Connecting…"; ((Graphic)_statusLabel).color = ColConnecting; } else if (Plugin.connection != null && Plugin.connection.connected) { ((TMP_Text)_statusLabel).text = "● Connected!"; ((Graphic)_statusLabel).color = ColConnected; } else { ((TMP_Text)_statusLabel).text = "● Disconnected"; ((Graphic)_statusLabel).color = ColDisconnected; } } } public void PopulateFromConfig() { if (Plugin.APConfig != null) { if (_addressField != null) { _addressField.text = $"{Plugin.APConfig.address.Value}:{Plugin.APConfig.port.Value}"; } if (_slotField != null) { _slotField.text = Plugin.APConfig.slot.Value; } if (_passwordField != null) { _passwordField.text = Plugin.APConfig.password.Value; } } } private void OnAddressChanged(string value) { if (Plugin.APConfig == null) { return; } int num = value.LastIndexOf(':'); if (num > 0 && num < value.Length - 1) { string value2 = value.Substring(0, num); string s = value.Substring(num + 1); Plugin.APConfig.address.Value = value2; if (int.TryParse(s, out var result) && result > 0 && result <= 65535) { Plugin.APConfig.port.Value = result; } } else if (num < 0) { Plugin.APConfig.address.Value = value.Trim(); } Plugin.Logger.LogDebug((object)$"[APPanel] Address updated: {Plugin.APConfig.address.Value}:{Plugin.APConfig.port.Value}"); } private void OnSlotChanged(string value) { if (Plugin.APConfig != null) { Plugin.APConfig.slot.Value = value; Plugin.Logger.LogDebug((object)("[APPanel] Slot updated: " + value)); } } private void OnPasswordChanged(string value) { if (Plugin.APConfig != null) { Plugin.APConfig.password.Value = value; Plugin.Logger.LogDebug((object)"[APPanel] Password updated."); } } private void OnConnectClicked() { if (!Plugin.isConnecting) { ArchipelagoClient connection = Plugin.connection; if (connection == null || !connection.connected) { Plugin.Logger.LogInfo((object)"[APPanel] Connect button clicked."); Plugin.Connect(); } } } private void OnDisconnectClicked() { Plugin.Logger.LogInfo((object)"[APPanel] Disconnect button clicked."); Plugin.Disconnect(); } private void BuildUI() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) if (((Component)this).gameObject.GetComponent<RectTransform>() == null) { ((Component)this).gameObject.AddComponent<RectTransform>(); } ContentSizeFitter obj = ((Component)this).gameObject.AddComponent<ContentSizeFitter>(); obj.verticalFit = (FitMode)2; obj.horizontalFit = (FitMode)0; VerticalLayoutGroup obj2 = ((Component)this).gameObject.AddComponent<VerticalLayoutGroup>(); ((LayoutGroup)obj2).padding = new RectOffset(10, 10, 8, 8); ((HorizontalOrVerticalLayoutGroup)obj2).spacing = 5f; ((LayoutGroup)obj2).childAlignment = (TextAnchor)0; ((HorizontalOrVerticalLayoutGroup)obj2).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj2).childControlHeight = false; ((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandHeight = false; ((Graphic)((Component)this).gameObject.AddComponent<Image>()).color = new Color(0.05f, 0.05f, 0.05f, 0.72f); CreateLabel(((Component)this).transform, "── Archipelago Connection ──", 16f, (FontStyles)1, (TextAlignmentOptions)514, 26f, new Color(1f, 0.92f, 0.016f)); _addressField = CreateLabeledField(((Component)this).transform, "Address : Port", $"{Plugin.apAddress}:{Plugin.apPort}", isPassword: false); _slotField = CreateLabeledField(((Component)this).transform, "Slot Name", Plugin.apSlot, isPassword: false); _passwordField = CreateLabeledField(((Component)this).transform, "Password", Plugin.apPassword, isPassword: true); ((UnityEvent<string>)(object)_addressField.onEndEdit).AddListener((UnityAction<string>)OnAddressChanged); ((UnityEvent<string>)(object)_slotField.onEndEdit).AddListener((UnityAction<string>)OnSlotChanged); ((UnityEvent<string>)(object)_passwordField.onEndEdit).AddListener((UnityAction<string>)OnPasswordChanged); CreateButtonRow(((Component)this).transform); } private static TMP_InputField CreateLabeledField(Transform parent, string labelText, string initialValue, bool isPassword) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("APRow_" + labelText); val.transform.SetParent(parent, false); val.AddComponent<LayoutElement>().preferredHeight = 28f; HorizontalLayoutGroup obj = val.AddComponent<HorizontalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)obj).spacing = 6f; ((LayoutGroup)obj).childAlignment = (TextAnchor)3; ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = false; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false; GameObject val2 = new GameObject("Label"); val2.transform.SetParent(val.transform, false); LayoutElement obj2 = val2.AddComponent<LayoutElement>(); obj2.preferredWidth = 110f; obj2.preferredHeight = 28f; TextMeshProUGUI obj3 = val2.AddComponent<TextMeshProUGUI>(); ((TMP_Text)obj3).text = labelText; ((TMP_Text)obj3).fontSize = 13f; ((TMP_Text)obj3).alignment = (TextAlignmentOptions)4100; ((Graphic)obj3).color = new Color(0.85f, 0.85f, 0.85f); TMP_InputField val3 = CreateInputField(val.transform, labelText, isPassword); LayoutElement obj4 = ((Component)val3).gameObject.GetComponent<LayoutElement>() ?? ((Component)val3).gameObject.AddComponent<LayoutElement>(); obj4.flexibleWidth = 1f; obj4.preferredHeight = 28f; val3.text = initialValue; return val3; } private void CreateButtonRow(Transform parent) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown //IL_016b: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("APButtonRow"); val.transform.SetParent(parent, false); val.AddComponent<LayoutElement>().preferredHeight = 32f; HorizontalLayoutGroup obj = val.AddComponent<HorizontalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)obj).spacing = 8f; ((LayoutGroup)obj).childAlignment = (TextAnchor)3; ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = false; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false; _connectButton = CreateButton(val.transform, "Connect", new Color(0.16f, 0.56f, 0.16f), 88f); _disconnectButton = CreateButton(val.transform, "Disconnect", new Color(0.5f, 0.13f, 0.13f), 100f); ((UnityEvent)_connectButton.onClick).AddListener(new UnityAction(OnConnectClicked)); ((UnityEvent)_disconnectButton.onClick).AddListener(new UnityAction(OnDisconnectClicked)); GameObject val2 = new GameObject("StatusLabel"); val2.transform.SetParent(val.transform, false); LayoutElement obj2 = val2.AddComponent<LayoutElement>(); obj2.flexibleWidth = 1f; obj2.preferredHeight = 32f; _statusLabel = val2.AddComponent<TextMeshProUGUI>(); ((TMP_Text)_statusLabel).fontSize = 13f; ((TMP_Text)_statusLabel).alignment = (TextAlignmentOptions)4097; ((TMP_Text)_statusLabel).text = "● Disconnected"; ((Graphic)_statusLabel).color = ColDisconnected; } private static TextMeshProUGUI CreateLabel(Transform parent, string text, float fontSize, FontStyles style, TextAlignmentOptions alignment, float height, Color color = default(Color)) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("APLabel"); val.transform.SetParent(parent, false); val.AddComponent<LayoutElement>().preferredHeight = height; TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)obj).text = text; ((TMP_Text)obj).fontSize = fontSize; ((TMP_Text)obj).fontStyle = style; ((TMP_Text)obj).alignment = alignment; ((Graphic)obj).color = ((color == default(Color)) ? Color.white : color); return obj; } private static TMP_InputField CreateInputField(Transform parent, string placeholder, bool isPassword) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_006f: 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) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("InputField"); val.transform.SetParent(parent, false); val.AddComponent<RectTransform>(); ((Graphic)val.AddComponent<Image>()).color = new Color(0.12f, 0.12f, 0.12f, 0.95f); TMP_InputField val2 = val.AddComponent<TMP_InputField>(); GameObject val3 = new GameObject("Text Area"); val3.transform.SetParent(val.transform, false); RectTransform val4 = val3.AddComponent<RectTransform>(); val4.anchorMin = Vector2.zero; val4.anchorMax = Vector2.one; val4.offsetMin = new Vector2(5f, 2f); val4.offsetMax = new Vector2(-5f, -2f); val3.AddComponent<RectMask2D>(); GameObject val5 = new GameObject("Placeholder"); val5.transform.SetParent(val3.transform, false); RectTransform obj = val5.AddComponent<RectTransform>(); obj.anchorMin = Vector2.zero; obj.anchorMax = Vector2.one; obj.offsetMin = Vector2.zero; obj.offsetMax = Vector2.zero; TextMeshProUGUI val6 = val5.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val6).text = placeholder; ((TMP_Text)val6).fontSize = 13f; ((Graphic)val6).color = new Color(0.65f, 0.65f, 0.65f, 0.65f); ((TMP_Text)val6).alignment = (TextAlignmentOptions)4097; ((TMP_Text)val6).fontStyle = (FontStyles)2; ((Graphic)val6).raycastTarget = false; GameObject val7 = new GameObject("Text"); val7.transform.SetParent(val3.transform, false); RectTransform obj2 = val7.AddComponent<RectTransform>(); obj2.anchorMin = Vector2.zero; obj2.anchorMax = Vector2.one; obj2.offsetMin = Vector2.zero; obj2.offsetMax = Vector2.zero; TextMeshProUGUI val8 = val7.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val8).fontSize = 13f; ((Graphic)val8).color = Color.white; ((TMP_Text)val8).alignment = (TextAlignmentOptions)4097; ((Graphic)val8).raycastTarget = false; val2.textViewport = val4; val2.textComponent = (TMP_Text)(object)val8; val2.placeholder = (Graphic)(object)val6; if (isPassword) { val2.contentType = (ContentType)7; val2.inputType = (InputType)2; } else { val2.contentType = (ContentType)0; } return val2; } private static Button CreateButton(Transform parent, string label, Color bgColor, float width) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Btn_" + label); val.transform.SetParent(parent, false); LayoutElement obj = val.AddComponent<LayoutElement>(); obj.preferredWidth = width; obj.preferredHeight = 28f; Image val2 = val.AddComponent<Image>(); ((Graphic)val2).color = bgColor; Button obj2 = val.AddComponent<Button>(); ColorBlock colors = ((Selectable)obj2).colors; ((ColorBlock)(ref colors)).normalColor = bgColor; ((ColorBlock)(ref colors)).highlightedColor = bgColor * 1.25f; ((ColorBlock)(ref colors)).pressedColor = bgColor * 0.75f; ((ColorBlock)(ref colors)).disabledColor = new Color(0.35f, 0.35f, 0.35f); ((Selectable)obj2).colors = colors; ((Selectable)obj2).targetGraphic = (Graphic)(object)val2; GameObject val3 = new GameObject("Text"); val3.transform.SetParent(val.transform, false); RectTransform obj3 = val3.AddComponent<RectTransform>(); obj3.anchorMin = Vector2.zero; obj3.anchorMax = Vector2.one; obj3.offsetMin = Vector2.zero; obj3.offsetMax = Vector2.zero; TextMeshProUGUI obj4 = val3.AddComponent<TextMeshProUGUI>(); ((TMP_Text)obj4).text = label; ((TMP_Text)obj4).fontSize = 13f; ((TMP_Text)obj4).fontStyle = (FontStyles)1; ((TMP_Text)obj4).alignment = (TextAlignmentOptions)514; ((Graphic)obj4).color = Color.white; ((Graphic)obj4).raycastTarget = false; return obj2; } } internal class APNotificationDriver : MonoBehaviour { [CompilerGenerated] private sealed class <AnimateRoutine>d__2 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public APNotificationDriver <>4__this; public string line2; public string line1; public float duration; private float <elapsed>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <AnimateRoutine>d__2(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Expected O, but got Unknown //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) int num = <>1__state; APNotificationDriver aPNotificationDriver = <>4__this; float num2; switch (num) { default: return false; case 0: <>1__state = -1; if (aPNotificationDriver.label == null) { return false; } ((TMP_Text)aPNotificationDriver.label).text = (string.IsNullOrEmpty(line2) ? line1 : (line1 + "\n<size=70%>" + line2 + "</size>")); ((Graphic)aPNotificationDriver.label).color = new Color(1f, 0.92f, 0.016f, 0f); ((Behaviour)aPNotificationDriver.label).enabled = true; <elapsed>5__2 = 0f; goto IL_0117; case 1: <>1__state = -1; goto IL_0117; case 2: <>1__state = -1; <elapsed>5__2 = 0f; break; case 3: { <>1__state = -1; break; } IL_0117: if (<elapsed>5__2 < 0.3f) { <elapsed>5__2 += Time.deltaTime; Color color = ((Graphic)aPNotificationDriver.label).color; ((Graphic)aPNotificationDriver.label).color = new Color(color.r, color.g, color.b, Mathf.Clamp01(<elapsed>5__2 / 0.3f)); <>2__current = null; <>1__state = 1; return true; } ((Graphic)aPNotificationDriver.label).color = new Color(((Graphic)aPNotificationDriver.label).color.r, ((Graphic)aPNotificationDriver.label).color.g, ((Graphic)aPNotificationDriver.label).color.b, 1f); num2 = Mathf.Max(0f, duration - 0.3f - 0.6f); <>2__current = (object)new WaitForSeconds(num2); <>1__state = 2; return true; } if (<elapsed>5__2 < 0.6f) { <elapsed>5__2 += Time.deltaTime; Color color2 = ((Graphic)aPNotificationDriver.label).color; ((Graphic)aPNotificationDriver.label).color = new Color(color2.r, color2.g, color2.b, Mathf.Clamp01(1f - <elapsed>5__2 / 0.6f)); <>2__current = null; <>1__state = 3; return true; } Object.Destroy((Object)(object)((Component)aPNotificationDriver).gameObject); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } internal TextMeshProUGUI label; internal void Show(string line1, string line2, float duration = 3.5f) { ((MonoBehaviour)this).StartCoroutine(AnimateRoutine(line1, line2, duration)); } [IteratorStateMachine(typeof(<AnimateRoutine>d__2))] private IEnumerator AnimateRoutine(string line1, string line2, float duration) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <AnimateRoutine>d__2(0) { <>4__this = this, line1 = line1, line2 = line2, duration = duration }; } } public static class APNotificationUI { private const float Duration = 3.5f; public static void ShowItemReceived(string itemName, string senderName = "") { string text = "Received: " + itemName; string text2 = (string.IsNullOrEmpty(senderName) ? "" : ("from " + senderName)); try { UserInterface.ShowMoneyNotification(text, text2, (MoneyCellType)2); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[APNotif] ShowMoneyNotification (MetaCoins) failed: " + ex.Message + " — using fallback label.")); SpawnHUDLabel(text, text2); } } public static void ShowLocationFound(string locationName) { try { UserInterface.ShowMoneyNotification("Location Found!", locationName, (MoneyCellType)1); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[APNotif] ShowMoneyNotification (Revenue) failed: " + ex.Message + " — using fallback label.")); SpawnHUDLabel("Location Found!", locationName); } } public static void ShowMoneyReceived(string displayLabel, int amount, MoneyCellType cellType) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) try { UserInterface.ShowMoneyNotification(displayLabel, amount.ToString(), cellType); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[APNotif] UserInterface.ShowMoneyNotification failed: " + ex.Message)); SpawnHUDLabel("[AP] Currency Received!", $"+{amount} {displayLabel}"); } } private static void SpawnHUDLabel(string line1, string line2) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) try { Canvas val = FindHUDCanvas(); if (val == null) { Plugin.Logger.LogDebug((object)("[APNotif] No HUD canvas — logging instead: " + line1 + " | " + line2)); return; } GameObject val2 = new GameObject("AP_Notification"); val2.transform.SetParent(((Component)val).transform, false); RectTransform obj = val2.AddComponent<RectTransform>(); obj.anchorMin = new Vector2(0.5f, 1f); obj.anchorMax = new Vector2(0.5f, 1f); obj.pivot = new Vector2(0.5f, 1f); obj.anchoredPosition = new Vector2(0f, -120f); obj.sizeDelta = new Vector2(700f, 80f); TextMeshProUGUI val3 = val2.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val3).alignment = (TextAlignmentOptions)514; ((TMP_Text)val3).fontSize = 26f; ((TMP_Text)val3).fontStyle = (FontStyles)1; ((Graphic)val3).raycastTarget = false; ((Behaviour)val3).enabled = false; APNotificationDriver aPNotificationDriver = val2.AddComponent<APNotificationDriver>(); aPNotificationDriver.label = val3; aPNotificationDriver.Show(line1, line2); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[APNotif] SpawnHUDLabel failed: " + ex.Message)); } } private static Canvas FindHUDCanvas() { //IL_00c0: Unknown result type (might be due to invalid IL or missing references) if (Player.localPlayer != null) { string[] array = new string[4] { "HUD", "PlayerHUD", "Canvas_HUD", "UI" }; foreach (string text in array) { Transform val = ((Component)Player.localPlayer).transform.Find(text); if (val != null) { Canvas component = ((Component)val).GetComponent<Canvas>(); if (component != null) { return component; } } } } Canvas[] array2 = Object.FindObjectsOfType<Canvas>(); Canvas[] array3 = array2; foreach (Canvas val2 in array3) { string text2 = ((Object)val2).name.ToLowerInvariant(); if (text2.Contains("hud") || text2.Contains("playerhud")) { return val2; } } array3 = array2; foreach (Canvas val3 in array3) { if ((int)val3.renderMode == 0) { return val3; } } return null; } } } namespace ContentWarningArchipelago.Patches { [HarmonyPatch] internal static class ChorbySpawnPatch { private static object? _chorbyItem; private static MethodBase? TargetMethod() { Type type = AccessTools.TypeByName("RoundArtifactSpawner"); if (type == null) { Plugin.Logger.LogWarning((object)"[ChorbySpawnPatch] RoundArtifactSpawner type not found — Chorby spawn-cap disabled."); return null; } MethodInfo methodInfo = AccessTools.Method(type, "GetArtifactsToSpawn", (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Logger.LogWarning((object)"[ChorbySpawnPatch] GetArtifactsToSpawn method not found — Chorby spawn-cap disabled."); return null; } Plugin.Logger.LogInfo((object)("[ChorbySpawnPatch] Patching " + type.Name + "." + methodInfo.Name)); return methodInfo; } [HarmonyPostfix] private static void Postfix(object __instance, object __result) { try { if (!(__result is IList list)) { Plugin.Logger.LogWarning((object)"[ChorbySpawnPatch] GetArtifactsToSpawn returned a non-IList — Chorby skipped this dive."); return; } if (_chorbyItem == null) { _chorbyItem = ResolveChorby(__instance); } if (_chorbyItem == null) { Plugin.Logger.LogWarning((object)"[ChorbySpawnPatch] Chorby Item not found in possibleSpawns — Chorby skipped this dive."); return; } int num = 0; for (int num2 = list.Count - 1; num2 >= 0; num2--) { if (list[num2] == _chorbyItem) { list.RemoveAt(num2); num++; } } list.Insert(0, _chorbyItem); Plugin.Logger.LogInfo((object)("[ChorbySpawnPatch] Forced 1 Chorby into the artifact list " + string.Format("(removed {0} vanilla pick{1}, ", num, (num == 1) ? "" : "s") + $"final count={list.Count}).")); } catch (Exception arg) { Plugin.Logger.LogError((object)$"[ChorbySpawnPatch] Postfix failed: {arg}"); } } private static object? ResolveChorby(object spawnerInstance) { FieldInfo fieldInfo = AccessTools.Field(spawnerInstance.GetType(), "possibleSpawns"); if (fieldInfo == null) { return null; } if (!(fieldInfo.GetValue(spawnerInstance) is IEnumerable enumerable)) { return null; } foreach (object item in enumerable) { Object val = (Object)((item is Object) ? item : null); if (val != null && string.Equals(val.name, "Chorby", StringComparison.Ordinal)) { return item; } } return null; } } [HarmonyPatch] internal static class ChorbyPickupPatch { private static MethodBase? TargetMethod() { Type type = AccessTools.TypeByName("Pickup"); if (type == null) { Plugin.Logger.LogWarning((object)"[ChorbyPickupPatch] Pickup type not found — Chorby pickup intercept disabled."); return null; } MethodInfo methodInfo = AccessTools.Method(type, "RPC_RequestPickup", (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Logger.LogWarning((object)"[ChorbyPickupPatch] Pickup.RPC_RequestPickup not found — Chorby pickup intercept disabled."); return null; } Plugin.Logger.LogInfo((object)("[ChorbyPickupPatch] Patching " + type.Name + "." + methodInfo.Name)); return methodInfo; } [HarmonyPrefix] private static bool Prefix(object __instance) { try { if (!IsChorby(__instance, out string itemName)) { return true; } if (!PhotonNetwork.IsMasterClient) { Plugin.Logger.LogDebug((object)"[ChorbyPickupPatch] Non-master client received RPC_RequestPickup — letting vanilla handle it."); return true; } Plugin.Logger.LogInfo((object)("[ChorbyPickupPatch] Chorby pickup intercepted (" + itemName + ").")); if (Plugin.connection.connected && APSave.saveData.chorbiesFound >= APSave.saveData.quotaCount) { if (!APSave.saveData.allChorbyChecksFoundNotified) { BroadcastAllChorbyChecksFound(); APSave.saveData.allChorbyChecksFoundNotified = true; APSave.Flush(); } DestroyPickup(__instance); return false; } if (Plugin.connection.connected) { APSave.saveData.chorbiesFound++; int chorbiesFound = APSave.saveData.chorbiesFound; string text = "Found Chorby " + chorbiesFound; long id = LocationData.GetId(text); if (id > 0) { Plugin.Logger.LogInfo((object)$"[ChorbyPickupPatch] Chorby #{chorbiesFound} picked up → {text}"); Plugin.SendCheck(id); } else { Plugin.Logger.LogDebug((object)$"[ChorbyPickupPatch] Chorby #{chorbiesFound} above the 21-check ceiling — no AP check sent."); } APSave.Flush(); } DestroyPickup(__instance); return false; } catch (Exception arg) { Plugin.Logger.LogError((object)$"[ChorbyPickupPatch] Prefix failed: {arg}"); return true; } } private static bool IsChorby(object pickupInstance, out string itemName) { itemName = string.Empty; FieldInfo fieldInfo = AccessTools.Field(pickupInstance.GetType(), "m_itemID"); if (fieldInfo == null) { return false; } if (!(fieldInfo.GetValue(pickupInstance) is byte b)) { return false; } Type type = AccessTools.TypeByName("ItemDatabase"); MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "TryGetItemFromID", (Type[])null, (Type[])null) : null); if (methodInfo == null) { return false; } object[] array = new object[2] { b, null }; if ((bool)(methodInfo.Invoke(null, array) ?? ((object)false))) { object obj = array[1]; Object val = (Object)((obj is Object) ? obj : null); if (val != null) { itemName = val.name ?? string.Empty; return string.Equals(itemName, "Chorby", StringComparison.Ordinal); } } return false; } private static void DestroyPickup(object pickupInstance) { try { object? obj = AccessTools.Field(pickupInstance.GetType(), "m_photonView")?.GetValue(pickupInstance); PhotonView val = (PhotonView)((obj is PhotonView) ? obj : null); if (val != null) { val.RPC("RPC_Remove", (RpcTarget)2, Array.Empty<object>()); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ChorbyPickupPatch] DestroyPickup failed: " + ex.Message)); } } private static void BroadcastAllChorbyChecksFound() { string text = "All Chorby checks found"; APNotificationUI.ShowLocationFound(text); Plugin.Logger.LogInfo((object)("[ChorbyPickupPatch] " + text + " (post-quota Chorby pickup).")); if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom.PlayerCount > 1) { try { MyceliumNetwork.RPC(2718281828u, "LocationFound", (ReliableType)1, new object[1] { text }); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ChorbyPickupPatch] Mycelium broadcast failed: " + ex.Message)); } } } } [HarmonyPatch(typeof(DivingBellSuitCellUI))] internal static class DivingBellAPStatusPatch { internal static string LastItemName = ""; [HarmonyPostfix] [HarmonyPatch("Set")] private static void Set(Player player, float dst, DivingBellSuitCellUI __instance) { if (!player.IsLocal) { return; } try { bool connected = Plugin.connection.connected; string text = (connected ? "<size=70%><color=#00FF88>[AP] Connected</color></size>" : "<size=70%><color=#FF4444>[AP] Disconnected</color></size>"); if (connected && !string.IsNullOrEmpty(LastItemName)) { text = text + "\n<size=60%>Last item: " + LastItemName + "</size>"; } if (connected) { APSaveData saveData = APSave.saveData; if (saveData.viewsChecks || saveData.viewsGoal || saveData.viralSensationGoal) { text += $"\n<size=60%>Lifetime views: {saveData.lifetimeViews:N0}"; if (saveData.viewsGoal && saveData.viewsGoalTarget > 0) { text += $" / {saveData.viewsGoalTarget:N0}"; } text += "</size>"; if (saveData.viralSensationGoal) { text += $"\n<size=60%>This quota: {saveData.currentQuotaViews:N0} / 1,000,000</size>"; } } } string text2 = ((TMP_Text)__instance.m_oxygenText).text; int num = text2.IndexOf("\n<size=70%><color=#"); if (num >= 0) { text2 = text2.Substring(0, num); } ((TMP_Text)__instance.m_oxygenText).text = text2 + "\n" + text; } catch (Exception ex) { Plugin.Logger.LogDebug((object)("[DiveBellAPStatus] Postfix exception: " + ex.Message)); } } } [HarmonyPatch(typeof(Player), "Update")] internal static class DivingBellRechargePatch { private const float ChargeRatePerSecond = 0.05f; [HarmonyPostfix] private static void Postfix(Player __instance) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) PlayerInventory val = default(PlayerInventory); if (!__instance.IsLocal || !Plugin.connection.connected || !APSave.saveData.diveBellChargerUnlocked || !__instance.data.isInDiveBell || !__instance.TryGetInventory(ref val)) { return; } float num = 0.05f * Time.deltaTime; InventorySlot[] slots = val.slots; BatteryEntry val3 = default(BatteryEntry); foreach (InventorySlot val2 in slots) { if (val2 != null && !((Object)(object)val2.ItemInSlot.item == (Object)null)) { ItemInstanceData data = val2.ItemInSlot.data; if (data != null && data.TryGetEntry<BatteryEntry>(ref val3) && !(val3.m_maxCharge <= val3.m_charge)) { val3.AddCharge(num * val3.m_maxCharge); Plugin.Logger.LogDebug((object)("[DiveBellCharger] Charged " + ((Object)val2.ItemInSlot.item).name + ": " + $"{val3.m_charge:F1}/{val3.m_maxCharge:F1} " + $"({val3.GetPercentage() * 100f:F0}%)")); } } } } } [HarmonyPatch(typeof(HatShop), "RPCA_StockShop")] internal static class HatShopFixedSeedPatch { [HarmonyPrefix] private static void Prefix(ref int seed) { if (Plugin.connection.connected) { ArchipelagoSession? session = Plugin.connection.session; string text = ((session != null) ? session.RoomState.Seed : null); if (!string.IsNullOrEmpty(text)) { int num = TryGetCurrentDay(); seed = $"{text}:{num}".GetHashCode(); Plugin.Logger.LogInfo((object)("[HatShopFixedSeedPatch] Hat shop seed overridden " + $"(apSeed + day {num}): {seed}")); } } } private static int TryGetCurrentDay() { Type type = AccessTools.TypeByName("GameAPI"); if (type != null) { PropertyInfo propertyInfo = AccessTools.Property(type, "CurrentDay"); if (propertyInfo != null && propertyInfo.GetValue(null) is int num && num > 0) { return num; } } Type type2 = AccessTools.TypeByName("SurfaceNetworkHandler"); if (type2 != null) { PropertyInfo propertyInfo2 = AccessTools.Property(type2, "RoomStats"); if (propertyInfo2 != null) { object value = propertyInfo2.GetValue(null); if (value != null) { PropertyInfo propertyInfo3 = AccessTools.Property(value.GetType(), "CurrentDay"); if (propertyInfo3 != null && propertyInfo3.GetValue(value) is int num2 && num2 > 0) { return num2; } } } } return 0; } } [HarmonyPatch(typeof(HatShop), "Restock")] internal static class HatShopFilterRestockPatch { [HarmonyPrefix] private static bool Prefix(HatShop __instance) { //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.connection.connected) { return true; } if ((Object)(object)HatDatabase.instance == (Object)null || HatDatabase.instance.hats == null) { return true; } FieldInfo fieldInfo = AccessTools.Field(typeof(HatShop), "savedSeed"); if (fieldInfo == null) { return true; } int num = (int)(fieldInfo.GetValue(__instance) ?? ((object)0)); State state = Random.state; Random.InitState(num); try { ArchipelagoSession? session = Plugin.connection.session; ReadOnlyCollection<long> checkedLocs = ((session != null) ? session.Locations.AllLocationsChecked : null); List<Hat> list = new List<Hat>(); Hat[] hats = HatDatabase.instance.hats; foreach (Hat val in hats) { if (!((Object)(object)val == (Object)null) && !IsHatLocationChecked(val, checkedLocs)) { list.Add(val); } } int count = __instance.hatBuyInteractables.Count; int num2 = Math.Min(count, list.Count); List<Hat> list2 = ((num2 > 0) ? ExtCollections.GetRandomNoDuplicates<Hat>((IEnumerable<Hat>)list, num2) : new List<Hat>()); for (int j = 0; j < count; j++) { HatBuyInteractable val2 = __instance.hatBuyInteractables[j]; if ((Object)(object)val2 == (Object)null) { continue; } if (j < list2.Count) { Hat val3 = list2[j]; int num3 = Mathf.RoundToInt((float)val3.GetBasePrice() * Random.Range(0.5f, 2f) / 10f) * 10; val2.LoadHat(((Component)val3).gameObject, num3); continue; } val2.ClearHat(); if ((Object)(object)val2.nameText != (Object)null) { ((TMP_Text)val2.nameText).text = string.Empty; } if ((Object)(object)val2.priceText != (Object)null) { ((TMP_Text)val2.priceText).text = string.Empty; } } Plugin.Logger.LogInfo((object)("[HatShopFilterRestockPatch] Filtered restock: " + $"{num2}/{count} slots filled, " + $"{list.Count} unbought hats in pool.")); } catch (Exception arg) { Plugin.Logger.LogError((object)$"[HatShopFilterRestockPatch] Exception: {arg}"); } finally { Random.state = state; } return false; } private static bool IsHatLocationChecked(Hat hat, IReadOnlyCollection<long>? checkedLocs) { if (checkedLocs == null || checkedLocs.Count == 0) { return false; } string text = null; if (!string.IsNullOrEmpty(hat.displayName) && HatShopAPLabelPatch.HatNameToLocation.TryGetValue(hat.displayName, out string value)) { text = value; } else { string name = hat.GetName(); if (!string.IsNullOrEmpty(name) && HatShopAPLabelPatch.HatNameToLocation.TryGetValue(name, out string value2)) { text = value2; } } if (string.IsNullOrEmpty(text)) { return false; } long id = LocationData.GetId(text); if (id < 0) { return false; } return checkedLocs.Contains(id); } } [HarmonyPatch(typeof(HatShop), "Restock")] internal static class HatShopAPLabelPatch { internal static readonly Dictionary<string, string> HatNameToLocation = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Beanie", "Bought Beanie" }, { "Bucket Hat", "Bought Bucket Hat" }, { "Floppy Hat", "Bought Floppy Hat" }, { "Homburg", "Bought Homburg" }, { "Bowler Hat", "Bought Bowler Hat" }, { "Cap", "Bought Cap" }, { "News Cap", "Bought News Cap" }, { "Newsboy Cap", "Bought News Cap" }, { "Sports Helmet", "Bought Sports Helmet" }, { "Hard Hat", "Bought Hard Hat" }, { "Chefs Hat", "Bought Chefs Hat" }, { "Chef's Hat", "Bought Chefs Hat" }, { "Propeller Hat", "Bought Propeller Hat" }, { "Cowboy Hat", "Bought Cowboy Hat" }, { "Horns", "Bought Horns" }, { "Hotdog Hat", "Bought Hotdog Hat" }, { "Hot Dog Hat", "Bought Hotdog Hat" }, { "Milk Hat", "Bought Milk Hat" }, { "Pirate Hat", "Bought Pirate Hat" }, { "Top Hat", "Bought Top Hat" }, { "Party Hat", "Bought Party Hat" }, { "Ushanka", "Bought Ushanka" }, { "Balaclava", "Bought Balaclava" }, { "Cat Ears", "Bought Cat Ears" }, { "Curly Hair", "Bought Curly Hair" }, { "Clown Hair", "Bought Clown Hair" }, { "Crown", "Bought Crown" }, { "Halo", "Bought Halo" }, { "Jesters Hat", "Bought Jesters Hat" }, { "Jester's Hat", "Bought Jesters Hat" }, { "Ghost Hat", "Bought Ghost Hat" }, { "Tooop Hat", "Bought Tooop Hat" }, { "Shroom Hat", "Bought Shroom Hat" }, { "Witch Hat", "Bought Witch Hat" }, { "Savannah Hair", "Bought Savannah Hair" } }; [HarmonyPostfix] private static void Postfix(HatShop __instance) { if (Plugin.connection.connected && PhotonNetwork.IsMasterClient) { (((Component)__instance).GetComponent<HatShopAPSyncBehaviour>() ?? ((Component)__instance).gameObject.AddComponent<HatShopAPSyncBehaviour>()).LaunchScout(__instance); } } } internal static class HatShopRestockLabelPatch { internal static readonly Dictionary<HatBuyInteractable, string> ScoutedNames = new Dictionary<HatBuyInteractable, string>(); internal static readonly Dictionary<HatBuyInteractable, string> ScoutedAPItemNames = new Dictionary<HatBuyInteractable, string>(); } internal class HatShopAPSyncBehaviour : MonoBehaviour { [CompilerGenerated] private sealed class <ScoutAndLabelCoroutine>d__7 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public HatShop hatShop; private Dictionary<HatBuyInteractable, long> <slotToLocId>5__2; private Task<Dictionary<long, string>> <scoutTask>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ScoutAndLabelCoroutine>d__7(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <slotToLocId>5__2 = null; <scoutTask>5__3 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: { <>1__state = -1; <slotToLocId>5__2 = new Dictionary<HatBuyInteractable, long>(); List<long> list = new List<long>(); foreach (HatBuyInteractable hatBuyInteractable in hatShop.hatBuyInteractables) { if ((Object)(object)hatBuyInteractable == (Object)null || hatBuyInteractable.IsEmpty || (Object)(object)hatBuyInteractable.ihat == (Object)null) { continue; } string text = ResolveLocationName(hatBuyInteractable.ihat); if (text != null) { long id = LocationData.GetId(text); if (id < 0) { Plugin.Logger.LogDebug((object)("[HatShopAPSyncBehaviour] '" + text + "' not in LocationData table.")); continue; } <slotToLocId>5__2[hatBuyInteractable] = id; list.Add(id); } } if (list.Count == 0) { Plugin.Logger.LogDebug((object)"[HatShopAPSyncBehaviour] No valid hat AP locations to scout."); return false; } Plugin.Logger.LogInfo((object)$"[HatShopAPSyncBehaviour] Scouting {list.Count} hat location(s)…"); <scoutTask>5__3 = Plugin.connection.ScoutLocationsAsync(list); break; } case 1: <>1__state = -1; break; } if (!<scoutTask>5__3.IsCompleted) { <>2__current = null; <>1__state = 1; return true; } Dictionary<long, string> dictionary; try { dictionary = <scoutTask>5__3.Result ?? new Dictionary<long, string>(); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[HatShopAPSyncBehaviour] Scout task threw: " + ex.Message)); return false; } HatShopRestockLabelPatch.ScoutedNames.Clear(); HatShopRestockLabelPatch.ScoutedAPItemNames.Clear(); string[] array = new string[hatShop.hatBuyInteractables.Count]; for (int i = 0; i < hatShop.hatBuyInteractables.Count; i++) { HatBuyInteractable val = hatShop.hatBuyInteractables[i]; array[i] = string.Empty; if (!((Object)(object)val == (Object)null) && !val.IsEmpty && !((Object)(object)val.ihat == (Object)null) && <slotToLocId>5__2.TryGetValue(val, out var value) && dictionary.TryGetValue(value, out var value2) && !string.IsNullOrEmpty(value2)) { string name = val.ihat.GetName(); string text2 = name + " [" + value2 + "]"; if ((Object)(object)val.nameText != (Object)null) { ((TMP_Text)val.nameText).text = text2; ApplyAutoSizing(val.nameText); } HatShopRestockLabelPatch.ScoutedNames[val] = text2; HatShopRestockLabelPatch.ScoutedAPItemNames[val] = value2; array[i] = text2; Plugin.Logger.LogInfo((object)$"[HatShopAPSyncBehaviour] Labelled slot {i}: '{name}' → '{value2}'"); } } Plugin.Logger.LogInfo((object)$"[HatShopAPSyncBehaviour] Applied {HatShopRestockLabelPatch.ScoutedNames.Count} label(s)."); if (PhotonNetwork.CurrentRoom != null && PhotonNetwork.CurrentRoom.PlayerCount > 1) { PhotonView component = ((Component)hatShop).GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null) { component.RPC("RPCA_SyncArchipelagoLabels", (RpcTarget)1, new object[1] { array }); Plugin.Logger.LogInfo((object)"[HatShopAPSyncBehaviour] Broadcast hat labels to other clients."); } else { Plugin.Logger.LogWarning((object)"[HatShopAPSyncBehaviour] HatShop has no PhotonView — cannot broadcast labels."); } } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } internal static HatShopAPSyncBehaviour? Instance { get; private set; } private void Awake() { Instance = this; } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } internal void LaunchScout(HatShop hatShop) { ((MonoBehaviour)this).StopAllCoroutines(); ((MonoBehaviour)this).StartCoroutine(ScoutAndLabelCoroutine(hatShop)); } [IteratorStateMachine(typeof(<ScoutAndLabelCoroutine>d__7))] private IEnumerator ScoutAndLabelCoroutine(HatShop hatShop) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <ScoutAndLabelCoroutine>d__7(0) { hatShop = hatShop }; } [PunRPC] public void RPCA_SyncArchipelagoLabels(string[] labels) { if ((Object)(object)HatShop.instance == (Object)null) { Plugin.Logger.LogWarning((object)"[HatShopAPSyncBehaviour] RPCA_SyncArchipelagoLabels: HatShop.instance is null."); return; } List<HatBuyInteractable> hatBuyInteractables = HatShop.instance.hatBuyInteractables; for (int i = 0; i < labels.Length && i < hatBuyInteractables.Count; i++) { string text = labels[i]; if (string.IsNullOrEmpty(text)) { continue; } HatBuyInteractable val = hatBuyInteractables[i]; if ((Object)(object)val == (Object)null || val.IsEmpty || (Object)(object)val.ihat == (Object)null) { continue; } if ((Object)(object)val.nameText != (Object)null) { ((TMP_Text)val.nameText).text = text; ApplyAutoSizing(val.nameText); } HatShopRestockLabelPatch.ScoutedNames[val] = text; int num = text.LastIndexOf('['); int num2 = text.LastIndexOf(']'); if (num >= 0 && num2 > num) { string value = text.Substring(num + 1, num2 - num - 1); if (!string.IsNullOrEmpty(value)) { HatShopRestockLabelPatch.ScoutedAPItemNames[val] = value; } } } Plugin.Logger.LogInfo((object)$"[HatShopAPSyncBehaviour] RPCA_SyncArchipelagoLabels: applied {labels.Length} label(s)."); } private static void ApplyAutoSizing(TextMeshPro tmp) { if (!((Object)(object)tmp == (Object)null)) { ((TMP_Text)tmp).enableWordWrapping = true; ((TMP_Text)tmp).enableAutoSizing = true; ((TMP_Text)tmp).fontSizeMax = Mathf.Max(((TMP_Text)tmp).fontSize, (((TMP_Text)tmp).fontSizeMax > 0f) ? ((TMP_Text)tmp).fontSizeMax : ((TMP_Text)tmp).fontSize); ((TMP_Text)tmp).fontSizeMin = 8f; ((TMP_Text)tmp).overflowMode = (TextOverflowModes)3; } } private static string? ResolveLocationName(Hat hat) { if (!string.IsNullOrEmpty(hat.displayName) && HatShopAPLabelPatch.HatNameToLocation.TryGetValue(hat.displayName, out string value)) { return value; } string name = hat.GetName(); if (!string.IsNullOrEmpty(name) && HatShopAPLabelPatch.HatNameToLocation.TryGetValue(name, out string value2)) { return value2; } Plugin.Logger.LogDebug((object)("[HatShopAPSyncBehaviour] No location mapping for hat displayName='" + hat.displayName + "' / GetName()='" + hat.GetName() + "'.")); return null; } } [HarmonyPatch(typeof(HatShop), "RPCA_BuyHat")] internal static class HatShopBuyAPCheckPatch { [HarmonyPrefix] private static void Prefix(HatShop __instance, int hatBuyIndex) { if (!Plugin.connection.connected || !PhotonNetwork.IsMasterClient) { return; } try { if (hatBuyIndex < 0 || hatBuyIndex >= __instance.hatBuyInteractables.Count) { return; } HatBuyInteractable val = __instance.hatBuyInteractables[hatBuyIndex]; if ((Object)(object)val == (Object)null || val.IsEmpty || (Object)(object)val.ihat == (Object)null) { return; } string text = null; if (!string.IsNullOrEmpty(val.ihat.displayName) && HatShopAPLabelPatch.HatNameToLocation.TryGetValue(val.ihat.displayName, out string value)) { text = value; } else { string name = val.ihat.GetName(); if (!string.IsNullOrEmpty(name) && HatShopAPLabelPatch.HatNameToLocation.TryGetValue(name, out string value2)) { text = value2; } } if (text == null) { string text2 = ((!string.IsNullOrEmpty(val.ihat.displayName)) ? val.ihat.displayName : val.ihat.GetName()); if (!string.IsNullOrEmpty(text2)) { text = "Bought " + text2; } } if (!string.IsNullOrEmpty(text)) { long id = LocationData.GetId(text); if (id < 0) { Plugin.Logger.LogDebug((object)("[HatShopBuyAPCheckPatch] '" + text + "' not in AP location table.")); return; } Plugin.Logger.LogInfo((object)("[HatShopBuyAPCheckPatch] Hat purchase → sending check: " + text)); Plugin.SendCheck(id); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[HatShopBuyAPCheckPatch] Exception: " + ex.Message)); } } } [HarmonyPatch(typeof(HatShop), "Restock")] internal static class HatShopRestockPatch { private static readonly Dictionary<string, int> HatStagePrices = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase) { { "Beanie", 500 }, { "Bucket Hat", 500 }, { "Floppy Hat", 500 }, { "Homburg", 500 }, { "Bowler Hat", 500 }, { "Cap", 500 }, { "News Cap", 500 }, { "Newsboy Cap", 500 }, { "Sports Helmet", 500 }, { "Hard Hat", 500 }, { "Chefs Hat", 1000 }, { "Chef's Hat", 1000 }, { "Propeller Hat", 1000 }, { "Cowboy Hat", 1000 }, { "Horns", 1000 }, { "Hotdog Hat", 1000 }, { "Hot Dog Hat", 1000 }, { "Milk Hat", 1000 }, { "Pirate Hat", 1000 }, { "Top Hat", 1000 }, { "Party Hat", 1000 }, { "Ushanka", 1000 }, { "Balaclava", 2000 }, { "Cat Ears", 2000 }, { "Curly Hair", 2000 }, { "Clown Hair", 2000 }, { "Crown", 2000 }, { "Halo", 2000 }, { "Jesters Hat", 2000 }, { "Jester's Hat", 2000 }, { "Ghost Hat", 2000 }, { "Tooop Hat", 2000 }, { "Shroom Hat", 2000 }, { "Witch Hat", 2000 }, { "Savannah Hair", 2000 } }; private static int RarityFallbackPrice(RARITY rarity) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Invalid comparison between Unknown and I4 //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 if ((int)rarity <= 10000) { if ((int)rarity == 100 || (int)rarity == 1000) { return 500; } if ((int)rarity == 10000) { goto IL_003f; } } else { if ((int)rarity == 100000) { goto IL_003f; } if ((int)rarity == 1000000 || (int)rarity == 10000000) { return 2000; } } return 500; IL_003f: return 1000; } [HarmonyPostfix] private static void Postfix(HatShop __instance) { //IL_0082: 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) foreach (HatBuyInteractable hatBuyInteractable in __instance.hatBuyInteractables) { if ((Object)(object)hatBuyInteractable == (Object)null || hatBuyInteractable.IsEmpty || (Object)(object)hatBuyInteractable.ihat == (Object)null) { continue; } bool flag = true; if (!HatStagePrices.TryGetValue(hatBuyInteractable.ihat.displayName, out var value)) { string name = hatBuyInteractable.ihat.GetName(); if (!HatStagePrices.TryGetValue(name, out value)) { value = RarityFallbackPrice(hatBuyInteractable.ihat.rarity); flag = false; Plugin.Logger.LogDebug((object)("[HatShopRestockPatch] No stage mapping for '" + hatBuyInteractable.ihat.displayName + "' / '" + name + "' " + $"(rarity: {hatBuyInteractable.ihat.rarity}) — fallback {value} MC.")); } } hatBuyInteractable.ihat.priceToday = value; ((TMP_Text)hatBuyInteractable.priceText).text = value + " MC"; Plugin.Logger.LogDebug((object)("[HatShopRestockPatch] '" + hatBuyInteractable.ihat.GetName() + "' → " + string.Format("{0} MC ({1})", value, flag ? "table" : "rarity fallback"))); } } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class HatBuyInteractableHoverTextPatch { [HarmonyPostfix] private static void Postfix(HatBuyInteractable __instance, ref string __result) { if (!string.IsNullOrEmpty(__result) && !((Object)(object)__instance == (Object)null) && HatShopRestockLabelPatch.ScoutedAPItemNames.TryGetValue(__instance, out string value) && !string.IsNullOrEmpty(value)) { __result = __result + " [" + value + "]"; } } } [HarmonyPatch] public static class ShopBuyPatch { private static readonly Dictionary<string, string> _shopNameOverrides = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "AncientGestures3", "Bought Ancient Gestures 3" }, { "Ancient Gestures 3", "Bought Ancient Gestures 3" }, { "AncientGestures2", "Bought Ancient Gestures 2" }, { "Ancient Gestures 2", "Bought Ancient Gestures 2" }, { "AncientGestures1", "Bought Ancient Gestures 1" }, { "Ancient Gestures 1", "Bought Ancient Gestures 1" }, { "Backflip", "Bought Backflip" }, { "Backflip_Emote", "Bought Backflip" }, { "HalfFlip", "Bought Backflip" }, { "Half_Flip", "Bought Backflip" }, { "Emote_HalfBackflip", "Bought Backflip" }, { "Emote_HalfBackflip_01", "Bought Backflip" }, { "Dance101", "Bought Dance 101" }, { "Dance 101", "Bought Dance 101" }, { "Dance102", "Bought Dance 102" }, { "Dance 102", "Bought Dance 102" }, { "Dance103", "Bought Dance 103" }, { "Dance 103", "Bought Dance 103" }, { "Workout1", "Bought Workout 1" }, { "Workout 1", "Bought Workout 1" }, { "Workout2", "Bought Workout 2" }, { "Workout 2", "Bought Workout 2" }, { "Thumbnail1", "Bought Thumbnail 1" }, { "Thumbnail 1", "Bought Thumbnail 1" }, { "Thumbnail2", "Bought Thumbnail 2" }, { "Thumbnail 2", "Bought Thumbnail 2" }, { "Gymnastics", "Bought Gymnastics" }, { "Caring", "Bought Caring" }, { "Yoga", "Bought Yoga" }, { "PartyPopper", "Bought Party Popper" } }; private static MethodBase? TargetMethod() { Type type = AccessTools.TypeByName("ShopHandler"); if (type == null) { Plugin.Logger.LogWarning((object)"[ShopBuyPatch] Could not find type 'ShopHandler'. Patch skipped."); return null; } MethodInfo methodInfo = AccessTools.Method(type, "RPCA_SpawnDrone", (Type[])null, (Type[])null); if (methodInfo != null) { Plugin.Logger.LogInfo((object)("[ShopBuyPatch] Patching " + type.Name + "." + methodInfo.Name)); return methodInfo; } Plugin.Logger.LogWarning((object)"[ShopBuyPatch] Could not find 'RPCA_SpawnDrone' on ShopHandler. Patch skipped."); return null; } [HarmonyPostfix] private static void Postfix(object __instance, byte[] itemIDs) { if (!Plugin.connection.connected || !PhotonNetwork.IsMasterClient) { return; } try { foreach (byte b in itemIDs) { TryGetItemDisplayName(b, out string displayName, out string assetName); string text = null; if (!string.IsNullOrEmpty(assetName) && _shopNameOverrides.TryGetValue(assetName, out string value)) { text = value; } if (text == null && !string.IsNullOrEmpty(displayName) && _shopNameOverrides.TryGetValue(displayName, out string value2)) { text = value2; } if (text == null && !string.IsNullOrEmpty(displayName)) { text = "Bought " + displayName; } if (text == null && !string.IsNullOrEmpty(assetName)) { text = "Bought " + assetName; } if (string.IsNullOrEmpty(text)) { Plugin.Logger.LogDebug((object)$"[ShopBuyPatch] Could not resolve any name for item ID {b}."); continue; } long id = LocationData.GetId(text); if (id < 0) { Plugin.Logger.LogDebug((object)("[ShopBuyPatch] '" + text + "' is not an AP location (non-AP item).")); continue; } Plugin.Logger.LogInfo((object)("[ShopBuyPatch] Purchase confirmed → sending check: " + text)); Plugin.SendCheck(id); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[ShopBuyPatch] Exception in postfix: {arg}"); } } private static void TryGetItemDisplayName(byte itemId, out string? displayName, out string? assetName) { displayName = null; assetName = null; Type type = AccessTools.TypeByName("ItemDatabase"); if (type == null) { return; } MethodInfo methodInfo = AccessTools.Method(type, "TryGetItemFromID", (Type[])null, (Type[])null); if (methodInfo == null) { return; } object[] array = new object[2] { itemId, null }; if (!(bool)(methodInfo.Invoke(null, array) ?? ((object)false)) || array[1] == null) { return; } object obj = array[1]; Object val = (Object)((obj is Object) ? obj : null); if (val != null && !string.IsNullOrWhiteSpace(val.name)) { assetName = val.name; } FieldInfo fieldInfo = AccessTools.Field(obj.GetType(), "displayName"); if (fieldInfo != null) { string text = fieldInfo.GetValue(obj)?.ToString(); if (!string.IsNullOrWhiteSpace(text)) { displayName = text; } } } } [HarmonyPatch] public static class PickupPatch { private static MethodBase? TargetMethod() { Type type = AccessTools.TypeByName("Pickup"); if (type == null) { Plugin.Logger.LogWarning((object)"[PickupPatch] Could not find type 'Pickup'. Patch skipped."); return null; } MethodInfo methodInfo = AccessTools.Method(type, "RPC_RequestPickup", (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Logger.LogWarning((object)"[PickupPatch] Could not find 'RPC_RequestPickup' on Pickup. Patch skipped."); return null; } Plugin.Logger.LogInfo((object)("[PickupPatch] Patching " + type.Name + "." + methodInfo.Name)); return methodInfo; } [HarmonyPostfix] private static void Postfix(object __instance) { if (!Plugin.connection.connected) { return; } try { string text = TryGetPickupName(__instance); Plugin.Logger.LogDebug((object)("[PickupPatch] Item picked up: " + (text ?? "(unknown)"))); } catch (Exception arg) { Plugin.Logger.LogError((object)$"[PickupPatch] Exception in postfix: {arg}"); } } private static string? TryGetPickupName(object instance) { FieldInfo fieldInfo = AccessTools.Field(instance.GetType(), "itemInstance"); if (fieldInfo != null) { object value = fieldInfo.GetValue(instance); if (value != null) { FieldInfo fieldInfo2 = AccessTools.Field(value.GetType(), "item"); if (fieldInfo2 != null) { object value2 = fieldInfo2.GetValue(value); if (value2 != null) { FieldInfo fieldInfo3 = AccessTools.Field(value2.GetType(), "displayName"); if (fieldInfo3 != null) { string text = fieldInfo3.GetValue(value2)?.ToString(); if (!string.IsNullOrEmpty(text)) { return text; } } Object val = (Object)((value2 is Object) ? value2 : null); if (val != null) { return val.name; } } } } } Component val2 = (Component)((instance is Component) ? instance : null); if (val2 != null) { return ((Object)val2.gameObject).name; } return null; } } [HarmonyPatch] public static class ContentEvaluatorPatch { private static readonly HashSet<string> _monstersFilmedThisDay = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static MethodBase? TargetMethod() { string[] array = new string[1] { "ContentEvaluator" }; for (int i = 0; i < array.Length; i++) { Type type = AccessTools.TypeByName(array[i]); if (!(type == null)) { MethodInfo methodInfo = AccessTools.Method(type, "EvaluateRecording", (Type[])null, (Type[])null); if (methodInfo != null) { Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] Patching " + type.Name + "." + methodInfo.Name)); return methodInfo; } } } Plugin.Logger.LogWarning((object)"[ContentEvaluatorPatch] Could not find ContentEvaluator.EvaluateRecording. Extraction and filming checks will not fire. Verify the class name in Assembly-CSharp."); return null; } [HarmonyPostfix] private static void Postfix(bool __result) { if (!__result || !Plugin.connection.connected || !PhotonNetwork.IsMasterClient) { return; } try { long id = LocationData.GetId("Any Extraction"); if (id >= 0) { Plugin.Logger.LogInfo((object)"[ContentEvaluatorPatch] Sending check: Any Extraction"); Plugin.SendCheck(id); BroadcastAPCheckNotification("Any Extraction"); } int num = TryGetCurrentDay(); if (num > 0 && num <= 63) { string text = "Extracted Footage on Day " + num; long id2 = LocationData.GetId(text); if (id2 >= 0) { Plugin.Logger.LogInfo((object)$"[ContentEvaluatorPatch] Day {num} extraction → sending check: {text}"); Plugin.SendCheck(id2); BroadcastAPCheckNotification(text); } } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[ContentEvaluatorPatch] Exception in extraction postfix: {arg}"); } } [HarmonyPostfix] private static void FilmingPostfix(bool __result, object buffer) { if (!__result || !Plugin.connection.connected || buffer == null) { return; } try { int viewsMultiplierLevel = APSave.saveData.viewsMultiplierLevel; if (viewsMultiplierLevel > 0) { float num = 1f + (float)viewsMultiplierLevel * 0.1f; if (AccessTools.Field(buffer.GetType(), "buffer")?.GetValue(buffer) is IList list) { foreach (object item in list) { if (item != null) { FieldInfo fieldInfo = AccessTools.Field(item.GetType(), "score"); if (!(fieldInfo == null)) { float num2 = (float)(fieldInfo.GetValue(item) ?? ((object)0f)); fieldInfo.SetValue(item, num2 * num); } } } Plugin.Logger.LogInfo((object)($"[ContentEvaluatorPatch] Progressive Views: applied {num:F1}× " + $"score multiplier (level {viewsMultiplierLevel}) to {list.Count} buffer entries.")); } } if (PhotonNetwork.IsMasterClient) { FireFilmingChecks(buffer); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[ContentEvaluatorPatch] Exception in filming postfix: {arg}"); } } public static void ResetDailyFilmingState() { int count = _monstersFilmedThisDay.Count; _monstersFilmedThisDay.Clear(); Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] Daily filming state reset " + string.Format("({0} entr{1} cleared).", count, (count == 1) ? "y" : "ies"))); } private static void FireFilmingChecks(object contentBuffer) { HashSet<string> fired = new HashSet<string>(StringComparer.OrdinalIgnoreCase); FieldInfo fieldInfo = AccessTools.Field(contentBuffer.GetType(), "buffer"); if (fieldInfo == null) { Plugin.Logger.LogWarning((object)"[ContentEvaluatorPatch] Could not find 'buffer' field on ContentBuffer. Filming checks skipped."); } else { if (!(fieldInfo.GetValue(contentBuffer) is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { if (item == null) { continue; } FieldInfo fieldInfo2 = AccessTools.Field(item.GetType(), "frame"); if (fieldInfo2 == null) { continue; } object value = fieldInfo2.GetValue(item); if (value == null) { continue; } FieldInfo fieldInfo3 = AccessTools.Field(value.GetType(), "contentEvent"); if (fieldInfo3 == null) { continue; } object value2 = fieldInfo3.GetValue(value); if (value2 == null) { continue; } Type type = value2.GetType(); ushort num = 0; MethodInfo method = type.GetMethod("GetID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { object obj = method.Invoke(value2, null); if (obj != null) { try { num = Convert.ToUInt16(obj); } catch { } } } string arg = string.Empty; MethodInfo method2 = type.GetMethod("GetName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method2 != null) { arg = method2.Invoke(value2, null)?.ToString() ?? string.Empty; } string name = type.Name; Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] Buffer scan — type='" + name + "', " + $"GetName()='{arg}', GetID()={num}")); if (name.IndexOf("artifact", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("prop", StringComparison.OrdinalIgnoreCase) >= 0) { Plugin.Logger.LogInfo((object)"[ContentEvaluatorPatch] >> Artifact/prop event — dumping all fields:"); Type type2 = type; while (type2 != null && type2 != typeof(object)) { FieldInfo[] fields = type2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo4 in fields) { try { object value3 = fieldInfo4.GetValue(value2); Object val = (Object)((value3 is Object) ? value3 : null); string text = ((val != null) ? ("UnityObject(name='" + val.name + "', type='" + ((object)val).GetType().Name + "')") : (value3?.ToString() ?? "null")); Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] [" + type2.Name + "]." + fieldInfo4.Name + " = " + text)); } catch (Exception ex) { Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] [" + type2.Name + "]." + fieldInfo4.Name + " = <error: " + ex.Message + ">")); } } type2 = type2.BaseType; } } string text2 = null; if (name.Equals("ArtifactContentEvent", StringComparison.Ordinal)) { FieldInfo fieldInfo5 = null; string text3 = null; string[] array = new string[3] { "artifact", "content", "item" }; foreach (string text4 in array) { fieldInfo5 = AccessTools.Field(type, text4); if (fieldInfo5 != null) { text3 = text4; break; } } if (fieldInfo5 != null) { Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] ArtifactContentEvent: resolved field name = '" + text3 + "'")); object value4 = fieldInfo5.GetValue(value2); Object val2 = (Object)((value4 is Object) ? value4 : null); if (val2 != null) { text2 = val2.name; Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] ArtifactContentEvent: " + $"assetName='{text2}', ID={num}")); } else { Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] ArtifactContentEvent: field '" + text3 + "' value is not UnityEngine.Object (actual type: '" + (value4?.GetType().Name ?? "null") + "')")); } } else { FieldInfo[] fields2 = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); string text5 = string.Join(", ", Array.ConvertAll(fields2, (FieldInfo f) => f.Name)); Plugin.Logger.LogWarning((object)("[ContentEvaluatorPatch] ArtifactContentEvent: could not find 'content'/'item'/'artifact' field. Available fields: [" + text5 + "]")); } } TryFireEntityCheck(name, num, text2, fired); } } } private static void TryFireEntityCheck(string rawName, ushort id, string? artifactDisplayName, HashSet<string> fired) { string text = null; if (id != 0) { text = FilmingLocationData.TryGetLocationById(id); } if (text == null && !string.IsNullOrEmpty(artifactDisplayName)) { text = FilmingLocationData.TryGetLocationByTypeName(artifactDisplayName); } if (text == null) { text = FilmingLocationData.TryGetLocationByTypeName(rawName); } if (text == null && rawName.EndsWith("ContentEvent", StringComparison.OrdinalIgnoreCase)) { text = FilmingLocationData.TryGetLocationByTypeName(rawName.Substring(0, rawName.Length - "ContentEvent".Length)); } if (text == null) { Plugin.Logger.LogDebug((object)($"[ContentEvaluatorPatch] No AP location for: class='{rawName}', ID={id}, " + "artifactName='" + (artifactDisplayName ?? "n/a") + "'. Add to FilmingLocationData if this is a new monster/artifact.")); } else { if (fired.Contains(text)) { return; } fired.Add(text); string text2 = text; if (_monstersFilmedThisDay.Contains(text2)) { Plugin.Logger.LogDebug((object)("[ContentEvaluatorPatch] '" + text2 + "' already filmed this dive — skipping tier advancement.")); return; } _monstersFilmedThisDay.Add(text2); bool monsterTiersEnabled = APSave.saveData.monsterTiersEnabled; long id2 = LocationData.GetId(text2); long num = (monsterTiersEnabled ? LocationData.GetId(text2 + " 2") : (-1)); long num2 = (monsterTiersEnabled ? LocationData.GetId(text2 + " 3") : (-1)); string text3 = null; if (id2 >= 0 && !APSave.IsLocationChecked(id2)) { text3 = text2; } else if (num >= 0 && !APSave.IsLocationChecked(num)) { text3 = text2 + " 2"; } else if (num2 >= 0 && !APSave.IsLocationChecked(num2)) { text3 = text2 + " 3"; } if (text3 == null) { Plugin.Logger.LogDebug((object)("[ContentEvaluatorPatch] All tiers for '" + text2 + "' already checked.")); return; } long id3 = LocationData.GetId(text3); if (id3 < 0) { Plugin.Logger.LogDebug((object)("[ContentEvaluatorPatch] '" + text3 + "' not in AP location table.")); return; } Plugin.Logger.LogInfo((object)("[ContentEvaluatorPatch] Filming check → " + text3)); Plugin.SendCheck(id3); BroadcastAPCheckNotification(text3); } } private static void BroadcastAPCheckNotification(string locationName) { if (string.IsNullOrEmpty(locationName)) { return; } try { SurfaceNetworkHandler instance = SurfaceNetworkHandler.Instance; if (!((Object)(object)instance == (Object)null)) { if ((Object)(object)((Component)instance).GetComponent<SurfaceAPNotificationBroadcaster>() == (Object)null) { ((Component)instance).gameObject.AddComponent<SurfaceAPNotificationBroadcaster>(); } ((MonoBehaviourPun)instance).photonView.RPC("RPCA_BroadcastAPCheckNotification", (RpcTarget)0, new object[1] { locationName }); Plugin.Logger.LogDebug((object)("[ContentEvaluatorPatch] Broadcast AP notification RPC: '" + locationName + "'")); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[ContentEvaluatorPatch] BroadcastAPCheckNotification failed: " + ex.Message)); } } private static int TryGetCurrentDay() { Type type = AccessTools.TypeByName("GameAPI"); if (type != null) { PropertyInfo propertyInfo = AccessTools.Property(type, "CurrentDay"); if (propertyInfo != null && propertyInfo.GetValue(null) is int num && num > 0) { return num; } } Type type2 = AccessTools.TypeByName("SurfaceNetworkHandler"); if (type2 != null) { PropertyInfo propertyInfo2 = AccessTools.Property(type2, "RoomStats"); if (propertyInfo2 != null) { object value = propertyInfo2.GetValue(null); if (value != null) { PropertyInfo propertyInfo3 = AccessTools.Property(value.GetType(), "CurrentDay"); if (propertyInfo3 != null && propertyInfo3.GetValue(value) is int num2 && num2 > 0) { return num2; } } } } return 0; } } internal class SurfaceAPNotificationBroadcaster : MonoBehaviour { [PunRPC] public void RPCA_BroadcastAPCheckNotification(string locationName) { if (!string.IsNullOrEmpty(locationName) && !PhotonNetwork.IsMasterClient) { Plugin.Logger.LogInfo((object)("[SurfaceAPNotificationBroadcaster] Showing AP check notification: '" + locationName + "'")); APNotificationUI.ShowLocationFound(locationName); } } } [HarmonyPatch(typeof(PlayerHandler))] [HarmonyPriority(400)] internal static class LateJoinSyncPatch { internal const string KeyDiveBellCharger = "AP_DBC"; internal const string KeyDiveBellO2 = "AP_O2"; internal const string KeyPendingMoney = "AP_PM"; [HarmonyPostfix] [HarmonyPatch("AddPlayer")] private static void AddPlayerPostfix(Player player) { if (PhotonNetwork.IsMasterClient && !player.IsLocal && Plugin.connection.connected) { Player controller = player.refs.view.Controller; Plugin.Logger.LogInfo((object)("[LateJoinSync] New player '" + controller.NickName + "' joined — broadcasting AP world state.")); BroadcastWorldState(); SyncHatLabelsToPlayer(controller); } } private static void SyncHatLabelsToPlayer(Player photonPlayer) { try { if ((Object)(object)HatShopAPSyncBehaviour.Instance == (Object)null) { Plugin.Logger.LogDebug((object)"[LateJoinSync] SyncHatLabelsToPlayer: HatShopAPSyncBehaviour not ready — skipping targeted sync."); return; } Type type = AccessTools.TypeByName("HatShop"); object obj = ((type != null) ? AccessTools.Field(type, "instance") : null)?.GetValue(null); if (obj == null) { Plugin.Logger.LogDebug((object)"[LateJoinSync] SyncHatLabelsToPlayer: HatShop.instance is null — skipping targeted sync."); return; } if (!(AccessTools.Field(obj.GetType(), "hatBuyInteractables")?.GetValue(obj) is List<HatBuyInteractable> list) || list.Count == 0) { Plugin.Logger.LogDebug((object)"[LateJoinSync] SyncHatLabelsToPlayer: hatBuyInteractables empty — skipping targeted sync."); return; } string[] array = new string[list.Count]; for (int i = 0; i < list.Count; i++) { HatBuyInteractable val = list[i]; string value = string.Empty; if ((Object)(object)val != (Object)null) { HatShopRestockLabelPatch.ScoutedNames.TryGetValue(val, out value); } array[i] = value; } object? obj2 = AccessTools.Field(obj.GetType(), "view")?.GetValue(obj); PhotonView val2 = (PhotonView)((obj2 is PhotonView) ? obj2 : null); if ((Object)(object)val2 == (Object)null) { Plugin.Logger.LogWarning((object)"[LateJoinSync] SyncHatLabelsToPlayer: HatShop PhotonView is null — cannot send targeted label sync."); return; } val2.RPC("RPCA_SyncArchipelagoLabels", photonPlayer, new object[1] { array }); Plugin.Logger.LogInfo((object)$"[LateJoinSync] Sent {array.Length} hat label(s) directly to '{photonPlayer.NickName}'."); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[LateJoinSync] SyncHatLabelsToPlayer exception: " + ex.Message)); } } internal static void BroadcastWorldState() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown if (PhotonNetwork.IsMasterClient) { Hashtable val = new Hashtable { [(object)"AP_DBC"] = APSave.saveData.diveBellChargerUnlocked, [(object)"AP_O2"] = APSave.saveData.diveBellO2Unlocked, [(object)"AP_PM"] = APSave.saveData.pendingMoney }; Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom != null) { currentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); } Plugin.Logger.LogDebug((object)"[LateJoinSync] Room properties updated with AP world state."); } } internal static void ApplyRoomProperties() { Room currentRoom = PhotonNetwork.CurrentRoom; Hashtable val = ((currentRoom != null) ? ((RoomInfo)currentRoom).CustomProperties : null); if (val != null) { if (((Dictionary<object, object>)(object)val).TryGetValue((object)"AP_DBC", out object value) && value is bool && (bool)value && !APSave.saveData.diveBellChargerUnlocked) { APSave.saveData.diveBellChargerUnlocked = true; Plugin.Logger.LogInfo((object)"[LateJoinSync] Applied DiveBellCharger from room properties."); } if (((Dictionary<object, object>)(object)val).TryGetValue((object)"AP_O2", out object value2) && value2 is bool && (bool)value2 && !APSave.saveData.diveBellO2Unlocked) { APSave.saveData.diveBellO2Unlocked = true; Plugin.Logger.LogInfo((object)"[LateJoinSync] Applied DiveBellO2 from room properties."); } if (((Dictionary<object, object>)(object)val).TryGetValue((object)"AP_PM", out object value3) && value3 is int num && num > 0) { Plugin.Logger.LogDebug((object)$"[LateJoinSync] Room has {num} pending AP money (master will apply)."); } APSave.Flush(); } } } [HarmonyPatch(typeof(SurfaceNetworkHandler))] [HarmonyPriority(400)] internal static class SurfaceApplyRoomPropsPatch { [HarmonyPostfix] [HarmonyPatch("RPCM_StartGame")] private static void RPCM_StartGamePostfix() { ContentEvaluatorPatch.ResetDailyFilmingState(); if (Plugin.connection.connected) { LateJoinSyncPatch.ApplyRoomProperties(); if (PhotonNetwork.IsMasterClient) { LateJoinSyncPatch.BroadcastWorldState(); } } } } [HarmonyPatch(typeof(SurfaceNetworkHandler), "RPCA_Sleep")] [HarmonyPriority(400)] internal static class SurfaceSleepResetPatch { [HarmonyPostfix] private static void Postfix() { ContentEvaluatorPatch.ResetDailyFilmingState(); } } [HarmonyPatch(typeof(MainMenuHandler), "Start")] internal static class MainMenuAPPatch { [CompilerGenerated] private sealed class <InjectPanelDelayed>d__1 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public MainMenuHandler menu; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <InjectPanelDelayed>d__1(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(3f); <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)GameObject.Find("AP_ConnectionPanel") != (Object)null) { return false; } InjectPanel(menu); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static void Postfix(MainMenuHandler __instance) { if (!((Object)(object)GameObject.Find("AP_ConnectionPanel") != (Object)null)) { Plugin.Logger.LogDebug((object)"[MainMenuAPPatch] MainMenuHandler.Start() completed — starting 3-second coroutine before injecting AP panel."); ((MonoBehaviour)__instance).StartCoroutine(InjectPanelDelayed(__instance)); } } [IteratorStateMachine(typeof(<InjectPanelDelayed>d__1))] private static IEnumerator InjectPanelDelayed(MainMenuHandler menu) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <InjectPanelDelayed>d__1(0) { menu = menu }; } private static void InjectPanel(MainMenuHandler menu) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) try { Plugin.Logger.LogInfo((object)"[MainMenuAPPatch] Injecting AP panel into main-menu UI."); Transform val = (((Object)(object)menu.UIHandler != (Object)null) ? ((Component)menu.UIHandler).transform : ((Component)menu).transform); Plugin.Logger.LogDebug((object)("[MainMenuAPPatch] Using parent: " + ((Object)val).name + " (full path: " + GetPath(val) + ")")); GameObject val2 = new GameObject("AP_ConnectionPanel"); val2.transform.SetParent(val, false); val2.transform.localScale = Vector3.one; RectTransform obj = val2.GetComponent<RectTransform>() ?? val2.AddComponent<RectTransform>(); obj.anchorMin = new Vector2(1f, 1f); obj.anchorMax = new Vector2(1f, 1f); obj.pivot = new Vector2(1f, 1f); obj.anchoredPosition = new Vector2(-10f, -10f); obj.sizeDelta = new Vector2(350f, 0f); val2.AddComponent<APConnectionPanelUI>(); val2.SetActive(true); Transform val3 = val; while ((Object)(object)val3 != (Object)null) { if (!((Component)val3).gameObject.activeSelf) { Plugin.Logger.LogDebug((object)("[MainMenuAPPatch] Activating inactive ancestor: " + GetPath(val3))); ((Component)val3).gameObject.SetActive(true); } val3 = val3.parent; } val2.transform.SetAsLastSibling(); Plugin.Logger.LogInfo((object)"[MainMenuAPPatch] AP connection panel successfully injected into main-menu UI."); } catch (Exception arg) { Plugin.Logger.LogError((object)$"[MainMenuAPPatch] Exception while injecting AP panel: {arg}"); } } private static string GetPath(Transform t) { if ((Object)(object)t.parent == (Object)null) { return ((Object)t).name; } return GetPath(t.parent) + "/" + ((Object)t).name; } } [HarmonyPatch(typeof(MetaProgressionHandler), "GetUnlockedHats")] internal static class GetUnlockedHatsPatch { [HarmonyPostfix] private static void Postfix(ref int[] __result) { if (Plugin.connection.connected) { int[] array = new int[APSave.saveData.sessionUnlockedHats.Count]; APSave.saveData.sessionUnlockedHats.CopyTo(array); __result = array; Plugin.Logger.LogDebug((object)$"[GetUnlockedHatsPatch] AP active — returning {array.Length} session hat(s)."); } } } [HarmonyPatch(typeof(MetaProgressionHandler), "UnlockHat")] internal static class UnlockHatPatch { [HarmonyPrefix] private static bool Prefix(int hat) { if (!Plugin.connection.connected) { return true; } if (APSave.saveData.sessionUnlockedHats.Add(hat)) { Plugin.Logger.LogInfo((object)$"[UnlockHatPatch] AP active — hat {hat} added to session (no disk write)."); } return false; } } [HarmonyPatch(typeof(MetaProgressionHandler), "AddMetaCoins")] internal static class AddMetaCoinsPatch { [HarmonyPrefix] private static bool Prefix(int amount) { if (!Plugin.connection.connected) { return true; } Plugin.Logger.LogDebug((object)$"[AddMetaCoinsPatch] AP active — vanilla AddMetaCoins({amount}) suppressed."); return false; } } [HarmonyPatch(typeof(MetaProgressionHandler), "RemoveMetaCoins")] internal static class RemoveMetaCoinsPatch { [HarmonyPrefix] private static bool Prefix(int amount) { if (!Plugin.connection.connected) { return true; } if (PhotonNetwork.IsMasterClient) { Plugin.connection.AddMetaCoinsDelta(-amount); Plugin.Logger.LogInfo((object)$"[RemoveMetaCoinsPatch] AP active — sent -{amount} MC to DataStorage."); } else { Plugin.Logger.LogDebug((object)("[RemoveMetaCoinsPatch] AP active — non-master client " + $"swallowing local RemoveMetaCoins({amount}); listener will sync.")); } return false; } } [HarmonyPatch(typeof(MetaProgressionHandler), "UpdateAndSave")] internal static class UpdateAndSavePatch { [HarmonyPrefix] private static bool Prefix() { return !Plugin.connection.connected; } } internal static class ModManagerAPPatch { internal static void TryApplyPatch(Harmony harmony) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown Type type = AccessTools.TypeByName("ModManagerUI"); if (type == null) { Plugin.Logger.LogWarning((object)"[ModManagerAPPatch] ModManagerUI type not found in any loaded assembly. The AP connection panel will not be injected into the Mod Manager. This is expected if the game's built-in Mod Manager is absent."); return; } MethodInfo methodInfo = AccessTools.Method(type, "OnEnable", (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Logger.LogWarning((object)"[ModManagerAPPatch] Could not find ModManagerUI.OnEnable() — skipping patch."); return; } HarmonyMethod val = new HarmonyMethod(typeof(ModManagerAPPatch), "OnEnable_Postfix", (Type[])null); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Logger.LogInfo((object)"[ModManagerAPPatch] Successfully patched ModManagerUI.OnEnable() with AP panel injector."); } private static void OnEnable_Postfix(MonoBehaviour __instance) { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) try { Plugin.Logger.LogInfo((object)"[ModManagerAPPatch] OnEnable_Postfix fired — checking for existing AP panel."); if ((Object)(object)GameObject.Find("AP_ConnectionPanel") != (Object)null) { Plugin.Logger.LogDebug((object)"[ModManagerAPPatch] AP_ConnectionPanel already exists in scene — skipping."); return; } Transform transform = ((Component)__instance).transform; FieldInfo fieldInfo = AccessTools.Field(((object)__instance).GetType(), "modlist"); if (fieldInfo != null) { object? value = fieldInfo.GetValue(__instance); Object val = (Object)((value is Object) ? value : null); Component val2 = (Component)(object)((val is Component) ? val : null); if (val2 != null) { transform = val2.transform; } else { GameObject val3 = (GameObject)(object)((val is GameObject) ? val : null); if (val3 != null) { transform = val3.transform; } else { Plugin.Logger.LogWarning((object)"[ModManagerAPPatch] 'modlist' field found but could not be cast to Component or GameObject — falling back to root transform."); } } } else { Plugin.Logger.LogWarning((object)"[ModManagerAPPatch] Could not find 'modlist' field on ModManagerUI — falling back to root transform."); } GameObject val4 = new GameObject("AP_ConnectionPanel"); val4.transform.SetParent(transform, false); LayoutElement obj = val4.AddComponent<LayoutElement>(); obj.preferredWidth = -1f; obj.preferredHeight = 155f; val4.AddComponent<APConnectionPanelUI>(); val4.transform.SetAsLastSibling(); Plugin.Logger.LogInfo((object)"[ModManagerAPPatch] AP connection panel successfully injected into ModManagerUI."); } catch (Exception arg) { Plugin.Logger.LogError((object)$"[ModManagerAPPatch] Exception while injecting AP panel: {arg}"); } } } [HarmonyPatch(typeof(ShopHandler))] internal static class MoneyPatch { [HarmonyPatch("InitShop")] [HarmonyPostfix] private static void InitShopPostfix(ShopHandler __instance) { if (PhotonNetwork.IsMasterClient) { int pendingMoney = APSave.saveData.pendingMoney; if (pendingMoney > 0) { Plugin.Logger.LogInfo((object)$"[MoneyPatch] Draining {pendingMoney} pending AP money
Archipelago.MultiClient.Net.dll
Decompiled a day 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 © 2026")] [assembly: AssemblyDescription("A client library for use with .NET based prog-langs for interfacing with Archipelago hosts.")] [assembly: AssemblyFileVersion("6.7.1.0")] [assembly: AssemblyInformationalVersion("6.7.1+0c57591db30f2497b0b4fef87164aa2bbe2e51b2")] [assembly: AssemblyProduct("Archipelago.MultiClient.Net")] [assembly: AssemblyTitle("Archipelago.MultiClient.Net")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/ArchipelagoMW/Archipelago.MultiClient.Net")] [assembly: AssemblyVersion("6.7.1.0")] internal interface IConcurrentHashSet<T> { bool TryAdd(T item); bool Contains(T item); void UnionWith(T[] otherSet); T[] ToArray(); ReadOnlyCollection<T> AsToReadOnlyCollection(); ReadOnlyCollection<T> AsToReadOnlyCollectionExcept(IConcurrentHashSet<T> otherSet); } public class AttemptingStringEnumConverter : StringEnumConverter { public AttemptingStringEnumConverter() { } public AttemptingStringEnumConverter(Type namingStrategyType) : base(namingStrategyType) { } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { try { return ((StringEnumConverter)this).ReadJson(reader, objectType, existingValue, serializer); } catch (JsonSerializationException) { return objectType.IsValueType ? Activator.CreateInstance(objectType) : null; } } } namespace Archipelago.MultiClient.Net { [Serializable] public abstract class ArchipelagoPacketBase { [JsonIgnore] internal JObject jobject; [JsonProperty("cmd")] [JsonConverter(typeof(StringEnumConverter))] public abstract ArchipelagoPacketType PacketType { get; } public JObject ToJObject() { return jobject; } } public interface IArchipelagoSession : IArchipelagoSessionActions { IArchipelagoSocketHelper Socket { get; } IReceivedItemsHelper Items { get; } ILocationCheckHelper Locations { get; } IPlayerHelper Players { get; } IDataStorageHelper DataStorage { get; } IConnectionInfoProvider ConnectionInfo { get; } IRoomStateHelper RoomState { get; } IMessageLogHelper MessageLog { get; } IHintsHelper Hints { get; } Task<RoomInfoPacket> ConnectAsync(); Task<LoginResult> LoginAsync(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true); LoginResult TryConnectAndLogin(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true); } public class ArchipelagoSession : IArchipelagoSession, IArchipelagoSessionActions { private const int ArchipelagoConnectionTimeoutInSeconds = 4; private ConnectionInfoHelper connectionInfo; private TaskCompletionSource<LoginResult> loginResultTask = new TaskCompletionSource<LoginResult>(); private TaskCompletionSource<RoomInfoPacket> roomInfoPacketTask = new TaskCompletionSource<RoomInfoPacket>(); public IArchipelagoSocketHelper Socket { get; } public IReceivedItemsHelper Items { get; } public ILocationCheckHelper Locations { get; } public IPlayerHelper Players { get; } public IDataStorageHelper DataStorage { get; } public IConnectionInfoProvider ConnectionInfo => connectionInfo; public IRoomStateHelper RoomState { get; } public IMessageLogHelper MessageLog { get; } public IHintsHelper Hints { get; } internal ArchipelagoSession(IArchipelagoSocketHelper socket, IReceivedItemsHelper items, ILocationCheckHelper locations, IPlayerHelper players, IRoomStateHelper roomState, ConnectionInfoHelper connectionInfoHelper, IDataStorageHelper dataStorage, IMessageLogHelper messageLog, IHintsHelper createHints) { Socket = socket; Items = items; Locations = locations; Players = players; RoomState = roomState; connectionInfo = connectionInfoHelper; DataStorage = dataStorage; MessageLog = messageLog; Hints = createHints; socket.PacketReceived += Socket_PacketReceived; } private void Socket_PacketReceived(ArchipelagoPacketBase packet) { if (!(packet is ConnectedPacket) && !(packet is ConnectionRefusedPacket)) { if (packet is RoomInfoPacket result) { roomInfoPacketTask.TrySetResult(result); } } else { loginResultTask.TrySetResult(LoginResult.FromPacket(packet)); } } public Task<RoomInfoPacket> ConnectAsync() { roomInfoPacketTask = new TaskCompletionSource<RoomInfoPacket>(); Task.Factory.StartNew(delegate { try { Task task = Socket.ConnectAsync(); task.Wait(TimeSpan.FromSeconds(4.0)); if (!task.IsCompleted) { roomInfoPacketTask.TrySetCanceled(); } } catch (AggregateException) { roomInfoPacketTask.TrySetCanceled(); } }); return roomInfoPacketTask.Task; } public Task<LoginResult> LoginAsync(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true) { loginResultTask = new TaskCompletionSource<LoginResult>(); if (!roomInfoPacketTask.Task.IsCompleted) { loginResultTask.TrySetResult(new LoginFailure("You are not connected, run ConnectAsync() first")); return loginResultTask.Task; } connectionInfo.SetConnectionParameters(game, tags, itemsHandlingFlags, uuid); try { Socket.SendPacket(BuildConnectPacket(name, password, version, requestSlotData)); } catch (ArchipelagoSocketClosedException) { loginResultTask.TrySetResult(new LoginFailure("You are not connected, run ConnectAsync() first")); return loginResultTask.Task; } SetResultAfterTimeout(loginResultTask, 4, new LoginFailure("Connection timed out.")); return loginResultTask.Task; } private static void SetResultAfterTimeout<T>(TaskCompletionSource<T> task, int timeoutInSeconds, T result) { new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)).Token.Register(delegate { task.TrySetResult(result); }); } public LoginResult TryConnectAndLogin(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true) { Task<RoomInfoPacket> task = ConnectAsync(); try { task.Wait(TimeSpan.FromSeconds(4.0)); } catch (AggregateException ex) { if (ex.GetBaseException() is OperationCanceledException) { return new LoginFailure("Connection timed out."); } return new LoginFailure(ex.GetBaseException().Message); } if (!task.IsCompleted) { return new LoginFailure("Connection timed out."); } return LoginAsync(game, name, itemsHandlingFlags, version, tags, uuid, password, requestSlotData).Result; } private ConnectPacket BuildConnectPacket(string name, string password, Version version, bool requestSlotData) { return new ConnectPacket { Game = ConnectionInfo.Game, Name = name, Password = password, Tags = ConnectionInfo.Tags, Uuid = ConnectionInfo.Uuid, Version = ((version != null) ? new NetworkVersion(version) : new NetworkVersion(0, 6, 0)), ItemsHandling = ConnectionInfo.ItemsHandlingFlags, RequestSlotData = requestSlotData }; } public void Say(string message) { Socket.SendPacket(new SayPacket { Text = message }); } public void SetClientState(ArchipelagoClientState state) { Socket.SendPacket(new StatusUpdatePacket { Status = state }); } public void SetGoalAchieved() { SetClientState(ArchipelagoClientState.ClientGoal); } } public interface IArchipelagoSessionActions { void Say(string message); void SetClientState(ArchipelagoClientState state); void SetGoalAchieved(); } public static class ArchipelagoSessionFactory { public static ArchipelagoSession CreateSession(Uri uri) { ArchipelagoSocketHelper socket = new ArchipelagoSocketHelper(uri); DataPackageCache cache = new DataPackageCache(socket); ConnectionInfoHelper connectionInfoHelper = new ConnectionInfoHelper(socket); PlayerHelper playerHelper = new PlayerHelper(socket, connectionInfoHelper); ItemInfoResolver itemInfoResolver = new ItemInfoResolver(cache, connectionInfoHelper); LocationCheckHelper locationCheckHelper = new LocationCheckHelper(socket, itemInfoResolver, connectionInfoHelper, playerHelper); ReceivedItemsHelper items = new ReceivedItemsHelper(socket, locationCheckHelper, itemInfoResolver, connectionInfoHelper, playerHelper); RoomStateHelper roomStateHelper = new RoomStateHelper(socket, locationCheckHelper); DataStorageHelper dataStorageHelper = new DataStorageHelper(socket, connectionInfoHelper); MessageLogHelper messageLog = new MessageLogHelper(socket, itemInfoResolver, playerHelper, connectionInfoHelper); HintsHelper createHints = new HintsHelper(socket, playerHelper, locationCheckHelper, roomStateHelper, dataStorageHelper); return new ArchipelagoSession(socket, items, locationCheckHelper, playerHelper, roomStateHelper, connectionInfoHelper, dataStorageHelper, messageLog, createHints); } public static ArchipelagoSession CreateSession(string hostname, int port = 38281) { return CreateSession(ParseUri(hostname, port)); } internal static Uri ParseUri(string hostname, int port) { string text = hostname; if (!text.StartsWith("ws://") && !text.StartsWith("wss://")) { text = "unspecified://" + text; } if (!text.Substring(text.IndexOf("://", StringComparison.Ordinal) + 3).Contains(":")) { text += $":{port}"; } if (text.EndsWith(":")) { text += port; } return new Uri(text); } } public abstract class LoginResult { public abstract bool Successful { get; } public static LoginResult FromPacket(ArchipelagoPacketBase packet) { if (!(packet is ConnectedPacket connectedPacket)) { if (packet is ConnectionRefusedPacket connectionRefusedPacket) { return new LoginFailure(connectionRefusedPacket); } throw new ArgumentOutOfRangeException("packet", "packet is not a connection result packet"); } return new LoginSuccessful(connectedPacket); } } public class LoginSuccessful : LoginResult { public override bool Successful => true; public int Team { get; } public int Slot { get; } public Dictionary<string, object> SlotData { get; } public LoginSuccessful(ConnectedPacket connectedPacket) { Team = connectedPacket.Team; Slot = connectedPacket.Slot; SlotData = connectedPacket.SlotData; } } public class LoginFailure : LoginResult { public override bool Successful => false; public ConnectionRefusedError[] ErrorCodes { get; } public string[] Errors { get; } public LoginFailure(ConnectionRefusedPacket connectionRefusedPacket) { if (connectionRefusedPacket.Errors != null) { ErrorCodes = connectionRefusedPacket.Errors.ToArray(); Errors = ErrorCodes.Select(GetErrorMessage).ToArray(); } else { ErrorCodes = new ConnectionRefusedError[0]; Errors = new string[0]; } } public LoginFailure(string message) { ErrorCodes = new ConnectionRefusedError[0]; Errors = new string[1] { message }; } private static string GetErrorMessage(ConnectionRefusedError errorCode) { return errorCode switch { ConnectionRefusedError.InvalidSlot => "The slot name did not match any slot on the server.", ConnectionRefusedError.InvalidGame => "The slot is set to a different game on the server.", ConnectionRefusedError.SlotAlreadyTaken => "The slot already has a connection with a different uuid established.", ConnectionRefusedError.IncompatibleVersion => "The client and server version mismatch.", ConnectionRefusedError.InvalidPassword => "The password is invalid.", ConnectionRefusedError.InvalidItemsHandling => "The item handling flags provided are invalid.", _ => $"Unknown error: {errorCode}.", }; } } internal class TwoWayLookup<TA, TB> : IEnumerable<KeyValuePair<TB, TA>>, IEnumerable { private readonly Dictionary<TA, TB> aToB = new Dictionary<TA, TB>(); private readonly Dictionary<TB, TA> bToA = new Dictionary<TB, TA>(); public TA this[TB b] => bToA[b]; public TB this[TA a] => aToB[a]; public void Add(TA a, TB b) { aToB[a] = b; bToA[b] = a; } public void Add(TB b, TA a) { Add(a, b); } public bool TryGetValue(TA a, out TB b) { return aToB.TryGetValue(a, out b); } public bool TryGetValue(TB b, out TA a) { return bToA.TryGetValue(b, out a); } public IEnumerator<KeyValuePair<TB, TA>> GetEnumerator() { return bToA.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } namespace Archipelago.MultiClient.Net.Packets { public class BouncedPacket : BouncePacket { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Bounced; } public class BouncePacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Bounce; [JsonProperty("games")] public List<string> Games { get; set; } = new List<string>(); [JsonProperty("slots")] public List<int> Slots { get; set; } = new List<int>(); [JsonProperty("tags")] public List<string> Tags { get; set; } = new List<string>(); [JsonProperty("data")] public Dictionary<string, JToken> Data { get; set; } } public class ConnectedPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Connected; [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } [JsonProperty("players")] public NetworkPlayer[] Players { get; set; } [JsonProperty("missing_locations")] public long[] MissingChecks { get; set; } [JsonProperty("checked_locations")] public long[] LocationsChecked { get; set; } [JsonProperty("slot_data")] public Dictionary<string, object> SlotData { get; set; } [JsonProperty("slot_info")] public Dictionary<int, NetworkSlot> SlotInfo { get; set; } [JsonProperty("hint_points")] public int? HintPoints { get; set; } } public class ConnectionRefusedPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.ConnectionRefused; [JsonProperty("errors", ItemConverterType = typeof(AttemptingStringEnumConverter))] public ConnectionRefusedError[] Errors { get; set; } } public class ConnectPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Connect; [JsonProperty("password")] public string Password { get; set; } [JsonProperty("game")] public string Game { get; set; } [JsonProperty("name")] public string Name { get; set; } [JsonProperty("uuid")] public string Uuid { get; set; } [JsonProperty("version")] public NetworkVersion Version { get; set; } [JsonProperty("tags")] public string[] Tags { get; set; } [JsonProperty("items_handling")] public ItemsHandlingFlags ItemsHandling { get; set; } [JsonProperty("slot_data")] public bool RequestSlotData { get; set; } } public class ConnectUpdatePacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.ConnectUpdate; [JsonProperty("tags")] public string[] Tags { get; set; } [JsonProperty("items_handling")] public ItemsHandlingFlags? ItemsHandling { get; set; } } public class CreateHintsPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.CreateHints; [JsonProperty("locations")] public long[] Locations { get; set; } [JsonProperty("player")] public int Player { get; set; } [JsonProperty("status")] public HintStatus Status { get; set; } } public class DataPackagePacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.DataPackage; [JsonProperty("data")] public Archipelago.MultiClient.Net.Models.DataPackage DataPackage { get; set; } } public class GetDataPackagePacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.GetDataPackage; [JsonProperty("games")] public string[] Games { get; set; } } public class GetPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Get; [JsonProperty("keys")] public string[] Keys { get; set; } } public class InvalidPacketPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.InvalidPacket; [JsonProperty("type")] public InvalidPacketErrorType ErrorType { get; set; } [JsonProperty("text")] public string ErrorText { get; set; } [JsonProperty("original_cmd")] public ArchipelagoPacketType OriginalCmd { get; set; } } public class LocationChecksPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.LocationChecks; [JsonProperty("locations")] public long[] Locations { get; set; } } public class LocationInfoPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.LocationInfo; [JsonProperty("locations")] public NetworkItem[] Locations { get; set; } } public class LocationScoutsPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.LocationScouts; [JsonProperty("locations")] public long[] Locations { get; set; } [JsonProperty("create_as_hint")] public int CreateAsHint { get; set; } } public class PrintJsonPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.PrintJSON; [JsonProperty("data")] public JsonMessagePart[] Data { get; set; } [JsonProperty("type")] [JsonConverter(typeof(AttemptingStringEnumConverter))] public JsonMessageType? MessageType { get; set; } } public class ItemPrintJsonPacket : PrintJsonPacket { [JsonProperty("receiving")] public int ReceivingPlayer { get; set; } [JsonProperty("item")] public NetworkItem Item { get; set; } } public class ItemCheatPrintJsonPacket : PrintJsonPacket { [JsonProperty("receiving")] public int ReceivingPlayer { get; set; } [JsonProperty("item")] public NetworkItem Item { get; set; } [JsonProperty("team")] public int Team { get; set; } } public class HintPrintJsonPacket : PrintJsonPacket { [JsonProperty("receiving")] public int ReceivingPlayer { get; set; } [JsonProperty("item")] public NetworkItem Item { get; set; } [JsonProperty("found")] public bool? Found { get; set; } } public class JoinPrintJsonPacket : PrintJsonPacket { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } [JsonProperty("tags")] public string[] Tags { get; set; } } public class LeavePrintJsonPacket : PrintJsonPacket { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } } public class ChatPrintJsonPacket : PrintJsonPacket { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } [JsonProperty("message")] public string Message { get; set; } } public class ServerChatPrintJsonPacket : PrintJsonPacket { [JsonProperty("message")] public string Message { get; set; } } public class TutorialPrintJsonPacket : PrintJsonPacket { } public class TagsChangedPrintJsonPacket : PrintJsonPacket { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } [JsonProperty("tags")] public string[] Tags { get; set; } } public class CommandResultPrintJsonPacket : PrintJsonPacket { } public class AdminCommandResultPrintJsonPacket : PrintJsonPacket { } public class GoalPrintJsonPacket : PrintJsonPacket { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } } public class ReleasePrintJsonPacket : PrintJsonPacket { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } } public class CollectPrintJsonPacket : PrintJsonPacket { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } } public class CountdownPrintJsonPacket : PrintJsonPacket { [JsonProperty("countdown")] public int RemainingSeconds { get; set; } } public class ReceivedItemsPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.ReceivedItems; [JsonProperty("index")] public int Index { get; set; } [JsonProperty("items")] public NetworkItem[] Items { get; set; } } public class RetrievedPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Retrieved; [JsonProperty("keys")] public Dictionary<string, JToken> Data { get; set; } } public class RoomInfoPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.RoomInfo; [JsonProperty("version")] public NetworkVersion Version { get; set; } [JsonProperty("generator_version")] public NetworkVersion GeneratorVersion { get; set; } [JsonProperty("tags")] public string[] Tags { get; set; } [JsonProperty("password")] public bool Password { get; set; } [JsonProperty("permissions")] public Dictionary<string, Permissions> Permissions { get; set; } [JsonProperty("hint_cost")] public int HintCostPercentage { get; set; } [JsonProperty("location_check_points")] public int LocationCheckPoints { get; set; } [JsonProperty("players")] public NetworkPlayer[] Players { get; set; } [JsonProperty("games")] public string[] Games { get; set; } [JsonProperty("datapackage_checksums")] public Dictionary<string, string> DataPackageChecksums { get; set; } [JsonProperty("seed_name")] public string SeedName { get; set; } [JsonProperty("time")] public double Timestamp { get; set; } } public class RoomUpdatePacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.RoomUpdate; [JsonProperty("tags")] public string[] Tags { get; set; } [JsonProperty("password")] public bool? Password { get; set; } [JsonProperty("permissions")] public Dictionary<string, Permissions> Permissions { get; set; } = new Dictionary<string, Permissions>(); [JsonProperty("hint_cost")] public int? HintCostPercentage { get; set; } [JsonProperty("location_check_points")] public int? LocationCheckPoints { get; set; } [JsonProperty("players")] public NetworkPlayer[] Players { get; set; } [JsonProperty("hint_points")] public int? HintPoints { get; set; } [JsonProperty("checked_locations")] public long[] CheckedLocations { get; set; } } public class SayPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Say; [JsonProperty("text")] public string Text { get; set; } } public class SetNotifyPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.SetNotify; [JsonProperty("keys")] public string[] Keys { get; set; } } public class SetPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Set; [JsonProperty("key")] public string Key { get; set; } [JsonProperty("default")] public JToken DefaultValue { get; set; } [JsonProperty("operations")] public OperationSpecification[] Operations { get; set; } [JsonProperty("want_reply")] public bool WantReply { get; set; } [JsonExtensionData] public Dictionary<string, JToken> AdditionalArguments { get; set; } [OnDeserialized] internal void OnDeserializedMethod(StreamingContext context) { AdditionalArguments?.Remove("cmd"); } } public class SetReplyPacket : SetPacket { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.SetReply; [JsonProperty("value")] public JToken Value { get; set; } [JsonProperty("original_value")] public JToken OriginalValue { get; set; } } public class StatusUpdatePacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.StatusUpdate; [JsonProperty("status")] public ArchipelagoClientState Status { get; set; } } public class SyncPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Sync; } internal class UnknownPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Unknown; } public class UpdateHintPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.UpdateHint; [JsonProperty("player")] public int Player { get; set; } [JsonProperty("location")] public long Location { get; set; } [JsonProperty("status")] public HintStatus Status { get; set; } } } namespace Archipelago.MultiClient.Net.Models { public struct Color : IEquatable<Color> { public static Color Red = new Color(byte.MaxValue, 0, 0); public static Color Green = new Color(0, 128, 0); public static Color Yellow = new Color(byte.MaxValue, byte.MaxValue, 0); public static Color Blue = new Color(0, 0, byte.MaxValue); public static Color Magenta = new Color(byte.MaxValue, 0, byte.MaxValue); public static Color Cyan = new Color(0, byte.MaxValue, byte.MaxValue); public static Color Black = new Color(0, 0, 0); public static Color White = new Color(byte.MaxValue, byte.MaxValue, byte.MaxValue); public static Color SlateBlue = new Color(106, 90, 205); public static Color Salmon = new Color(250, 128, 114); public static Color Plum = new Color(221, 160, 221); public byte R { get; set; } public byte G { get; set; } public byte B { get; set; } public Color(byte r, byte g, byte b) { R = r; G = g; B = b; } public override bool Equals(object obj) { if (obj is Color color && R == color.R && G == color.G) { return B == color.B; } return false; } public bool Equals(Color other) { if (R == other.R && G == other.G) { return B == other.B; } return false; } public override int GetHashCode() { return ((-1520100960 * -1521134295 + R.GetHashCode()) * -1521134295 + G.GetHashCode()) * -1521134295 + B.GetHashCode(); } public static bool operator ==(Color left, Color right) { return left.Equals(right); } public static bool operator !=(Color left, Color right) { return !(left == right); } } public class DataPackage { [JsonProperty("games")] public Dictionary<string, GameData> Games { get; set; } = new Dictionary<string, GameData>(); } public class DataStorageElement { internal DataStorageElementContext Context; internal List<OperationSpecification> Operations = new List<OperationSpecification>(0); internal DataStorageHelper.DataStorageUpdatedHandler Callbacks; internal Dictionary<string, JToken> AdditionalArguments = new Dictionary<string, JToken>(0); private JToken cachedValue; public event DataStorageHelper.DataStorageUpdatedHandler OnValueChanged { add { Context.AddHandler(Context.Key, value); } remove { Context.RemoveHandler(Context.Key, value); } } internal DataStorageElement(DataStorageElementContext context) { Context = context; } internal DataStorageElement(OperationType operationType, JToken value) { Operations = new List<OperationSpecification>(1) { new OperationSpecification { OperationType = operationType, Value = value } }; } internal DataStorageElement(DataStorageElement source, OperationType operationType, JToken value) : this(source.Context) { Operations = source.Operations.ToList(); Callbacks = source.Callbacks; AdditionalArguments = source.AdditionalArguments; Operations.Add(new OperationSpecification { OperationType = operationType, Value = value }); } internal DataStorageElement(DataStorageElement source, Callback callback) : this(source.Context) { Operations = source.Operations.ToList(); Callbacks = source.Callbacks; AdditionalArguments = source.AdditionalArguments; Callbacks = (DataStorageHelper.DataStorageUpdatedHandler)Delegate.Combine(Callbacks, callback.Method); } internal DataStorageElement(DataStorageElement source, AdditionalArgument additionalArgument) : this(source.Context) { Operations = source.Operations.ToList(); Callbacks = source.Callbacks; AdditionalArguments = source.AdditionalArguments; AdditionalArguments[additionalArgument.Key] = additionalArgument.Value; } public static DataStorageElement operator ++(DataStorageElement a) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(1)); } public static DataStorageElement operator --(DataStorageElement a) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(-1)); } public static DataStorageElement operator +(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, string b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, JToken b) { return new DataStorageElement(a, OperationType.Add, b); } public static DataStorageElement operator +(DataStorageElement a, IEnumerable b) { return new DataStorageElement(a, OperationType.Add, (JToken)(object)JArray.FromObject((object)b)); } public static DataStorageElement operator +(DataStorageElement a, OperationSpecification s) { return new DataStorageElement(a, s.OperationType, s.Value); } public static DataStorageElement operator +(DataStorageElement a, Callback c) { return new DataStorageElement(a, c); } public static DataStorageElement operator +(DataStorageElement a, AdditionalArgument arg) { return new DataStorageElement(a, arg); } public static DataStorageElement operator *(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator *(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator *(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator *(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator *(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator -(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b))); } public static DataStorageElement operator -(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b))); } public static DataStorageElement operator -(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(0f - b))); } public static DataStorageElement operator -(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(0.0 - b))); } public static DataStorageElement operator -(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b))); } public static DataStorageElement operator /(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / (decimal)b))); } public static DataStorageElement operator /(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / (decimal)b))); } public static DataStorageElement operator /(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1.0 / (double)b))); } public static DataStorageElement operator /(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1.0 / b))); } public static DataStorageElement operator /(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / b))); } public static implicit operator DataStorageElement(bool b) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(b)); } public static implicit operator DataStorageElement(int i) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(i)); } public static implicit operator DataStorageElement(long l) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(l)); } public static implicit operator DataStorageElement(decimal m) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(m)); } public static implicit operator DataStorageElement(double d) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(d)); } public static implicit operator DataStorageElement(float f) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(f)); } public static implicit operator DataStorageElement(string s) { if (s != null) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(s)); } return new DataStorageElement(OperationType.Replace, (JToken)(object)JValue.CreateNull()); } public static implicit operator DataStorageElement(JToken o) { return new DataStorageElement(OperationType.Replace, o); } public static implicit operator DataStorageElement(Array a) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)a)); } public static implicit operator DataStorageElement(List<bool> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<int> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<long> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<decimal> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<double> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<float> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<string> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<object> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator bool(DataStorageElement e) { return RetrieveAndReturnBoolValue<bool>(e); } public static implicit operator bool?(DataStorageElement e) { return RetrieveAndReturnBoolValue<bool?>(e); } public static implicit operator int(DataStorageElement e) { return RetrieveAndReturnDecimalValue<int>(e); } public static implicit operator int?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<int?>(e); } public static implicit operator long(DataStorageElement e) { return RetrieveAndReturnDecimalValue<long>(e); } public static implicit operator long?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<long?>(e); } public static implicit operator float(DataStorageElement e) { return RetrieveAndReturnDecimalValue<float>(e); } public static implicit operator float?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<float?>(e); } public static implicit operator double(DataStorageElement e) { return RetrieveAndReturnDecimalValue<double>(e); } public static implicit operator double?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<double?>(e); } public static implicit operator decimal(DataStorageElement e) { return RetrieveAndReturnDecimalValue<decimal>(e); } public static implicit operator decimal?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<decimal?>(e); } public static implicit operator string(DataStorageElement e) { return RetrieveAndReturnStringValue(e); } public static implicit operator bool[](DataStorageElement e) { return RetrieveAndReturnArrayValue<bool[]>(e); } public static implicit operator int[](DataStorageElement e) { return RetrieveAndReturnArrayValue<int[]>(e); } public static implicit operator long[](DataStorageElement e) { return RetrieveAndReturnArrayValue<long[]>(e); } public static implicit operator decimal[](DataStorageElement e) { return RetrieveAndReturnArrayValue<decimal[]>(e); } public static implicit operator double[](DataStorageElement e) { return RetrieveAndReturnArrayValue<double[]>(e); } public static implicit operator float[](DataStorageElement e) { return RetrieveAndReturnArrayValue<float[]>(e); } public static implicit operator string[](DataStorageElement e) { return RetrieveAndReturnArrayValue<string[]>(e); } public static implicit operator object[](DataStorageElement e) { return RetrieveAndReturnArrayValue<object[]>(e); } public static implicit operator List<bool>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<bool>>(e); } public static implicit operator List<int>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<int>>(e); } public static implicit operator List<long>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<long>>(e); } public static implicit operator List<decimal>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<decimal>>(e); } public static implicit operator List<double>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<double>>(e); } public static implicit operator List<float>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<float>>(e); } public static implicit operator List<string>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<string>>(e); } public static implicit operator List<object>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<object>>(e); } public static implicit operator Array(DataStorageElement e) { return RetrieveAndReturnArrayValue<Array>(e); } public static implicit operator JArray(DataStorageElement e) { return RetrieveAndReturnArrayValue<JArray>(e); } public static implicit operator JToken(DataStorageElement e) { return e.Context.GetData(e.Context.Key); } public static DataStorageElement operator +(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Add, JToken.Parse(b.ToString())); } public static DataStorageElement operator *(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Mul, JToken.Parse(b.ToString())); } public static DataStorageElement operator %(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Mod, JToken.Parse(b.ToString())); } public static DataStorageElement operator ^(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Pow, JToken.Parse(b.ToString())); } public static DataStorageElement operator -(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Add, JToken.Parse((-b).ToString())); } public static DataStorageElement operator /(DataStorageElement a, BigInteger b) { throw new InvalidOperationException("DataStorage[Key] / BigInterger is not supported, due to loss of precision when using integer division"); } public static implicit operator DataStorageElement(BigInteger bi) { return new DataStorageElement(OperationType.Replace, JToken.Parse(bi.ToString())); } public static implicit operator BigInteger(DataStorageElement e) { return RetrieveAndReturnBigIntegerValue<BigInteger>(e); } public static implicit operator BigInteger?(DataStorageElement e) { return RetrieveAndReturnBigIntegerValue<BigInteger?>(e); } private static T RetrieveAndReturnBigIntegerValue<T>(DataStorageElement e) { if (e.cachedValue != null) { if (!BigInteger.TryParse(((object)e.cachedValue).ToString(), out var result)) { return default(T); } return (T)Convert.ChangeType(result, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T)); } BigInteger result2; BigInteger? bigInteger = (BigInteger.TryParse(((object)e.Context.GetData(e.Context.Key)).ToString(), out result2) ? new BigInteger?(result2) : null); if (!bigInteger.HasValue && !IsNullable<T>()) { bigInteger = Activator.CreateInstance<BigInteger>(); } foreach (OperationSpecification operation in e.Operations) { if (operation.OperationType == OperationType.Floor || operation.OperationType == OperationType.Ceil) { continue; } if (!BigInteger.TryParse(((object)operation.Value).ToString(), NumberStyles.AllowLeadingSign, null, out var result3)) { throw new InvalidOperationException($"DataStorage[Key] cannot be converted to BigInterger as its value its not an integer number, value: {operation.Value}"); } switch (operation.OperationType) { case OperationType.Replace: bigInteger = result3; break; case OperationType.Add: bigInteger += result3; break; case OperationType.Mul: bigInteger *= result3; break; case OperationType.Mod: bigInteger %= result3; break; case OperationType.Pow: bigInteger = BigInteger.Pow(bigInteger.Value, (int)operation.Value); break; case OperationType.Max: { BigInteger value = result3; BigInteger? bigInteger2 = bigInteger; if (value > bigInteger2) { bigInteger = result3; } break; } case OperationType.Min: { BigInteger value = result3; BigInteger? bigInteger2 = bigInteger; if (value < bigInteger2) { bigInteger = result3; } break; } case OperationType.Xor: bigInteger ^= result3; break; case OperationType.Or: bigInteger |= result3; break; case OperationType.And: bigInteger &= result3; break; case OperationType.LeftShift: bigInteger <<= (int)operation.Value; break; case OperationType.RightShift: bigInteger >>= (int)operation.Value; break; } } e.cachedValue = JToken.Parse(bigInteger.ToString()); if (!bigInteger.HasValue) { return default(T); } return (T)Convert.ChangeType(bigInteger.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T)); } public void Initialize(JToken value) { Context.Initialize(Context.Key, value); } public void Initialize(IEnumerable value) { Context.Initialize(Context.Key, (JToken)(object)JArray.FromObject((object)value)); } public Task<T> GetAsync<T>() { return GetAsync().ContinueWith((Task<JToken> r) => r.Result.ToObject<T>()); } public Task<JToken> GetAsync() { return Context.GetAsync(Context.Key); } private static T RetrieveAndReturnArrayValue<T>(DataStorageElement e) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Invalid comparison between Unknown and I4 //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Invalid comparison between Unknown and I4 //IL_00d8: Unknown result type (might be due to invalid IL or missing references) if (e.cachedValue != null) { return ((JToken)(JArray)e.cachedValue).ToObject<T>(); } JArray val = (JArray)(((object)e.Context.GetData(e.Context.Key).ToObject<JArray>()) ?? ((object)new JArray())); foreach (OperationSpecification operation in e.Operations) { switch (operation.OperationType) { case OperationType.Add: if ((int)operation.Value.Type != 2) { throw new InvalidOperationException($"Cannot perform operation {OperationType.Add} on Array value, with a non Array value: {operation.Value}"); } ((JContainer)val).Merge((object)operation.Value); break; case OperationType.Replace: if ((int)operation.Value.Type != 2) { throw new InvalidOperationException($"Cannot replace Array value, with a non Array value: {operation.Value}"); } val = (JArray)(((object)operation.Value.ToObject<JArray>()) ?? ((object)new JArray())); break; default: throw new InvalidOperationException($"Cannot perform operation {operation.OperationType} on Array value"); } } e.cachedValue = (JToken)(object)val; return ((JToken)val).ToObject<T>(); } private static string RetrieveAndReturnStringValue(DataStorageElement e) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Invalid comparison between Unknown and I4 if (e.cachedValue != null) { return (string)e.cachedValue; } JToken val = e.Context.GetData(e.Context.Key); string text = (((int)val.Type == 10) ? null : ((object)val).ToString()); foreach (OperationSpecification operation in e.Operations) { switch (operation.OperationType) { case OperationType.Add: text += (string)operation.Value; break; case OperationType.Mul: if ((int)operation.Value.Type != 6) { throw new InvalidOperationException($"Cannot perform operation {OperationType.Mul} on string value, with a non interger value: {operation.Value}"); } text = string.Concat(Enumerable.Repeat(text, (int)operation.Value)); break; case OperationType.Replace: text = (string)operation.Value; break; default: throw new InvalidOperationException($"Cannot perform operation {operation.OperationType} on string value"); } } if (text == null) { e.cachedValue = (JToken)(object)JValue.CreateNull(); } else { e.cachedValue = JToken.op_Implicit(text); } return (string)e.cachedValue; } private static T RetrieveAndReturnBoolValue<T>(DataStorageElement e) { if (e.cachedValue != null) { return e.cachedValue.ToObject<T>(); } bool? flag = e.Context.GetData(e.Context.Key).ToObject<bool?>() ?? ((bool?)Activator.CreateInstance(typeof(T))); foreach (OperationSpecification operation in e.Operations) { if (operation.OperationType == OperationType.Replace) { flag = (bool?)operation.Value; continue; } throw new InvalidOperationException($"Cannot perform operation {operation.OperationType} on boolean value"); } e.cachedValue = JToken.op_Implicit(flag); if (!flag.HasValue) { return default(T); } return (T)Convert.ChangeType(flag.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T)); } private static T RetrieveAndReturnDecimalValue<T>(DataStorageElement e) { if (e.cachedValue != null) { return e.cachedValue.ToObject<T>(); } decimal? num = e.Context.GetData(e.Context.Key).ToObject<decimal?>(); if (!num.HasValue && !IsNullable<T>()) { num = Activator.CreateInstance<decimal>(); } foreach (OperationSpecification operation in e.Operations) { switch (operation.OperationType) { case OperationType.Replace: num = (decimal)operation.Value; break; case OperationType.Add: num += (decimal?)(decimal)operation.Value; break; case OperationType.Mul: num *= (decimal?)(decimal)operation.Value; break; case OperationType.Mod: num %= (decimal?)(decimal)operation.Value; break; case OperationType.Pow: num = (decimal)Math.Pow((double)num.Value, (double)operation.Value); break; case OperationType.Max: num = Math.Max(num.Value, (decimal)operation.Value); break; case OperationType.Min: num = Math.Min(num.Value, (decimal)operation.Value); break; case OperationType.Xor: num = (long)num.Value ^ (long)operation.Value; break; case OperationType.Or: num = (long)num.Value | (long)operation.Value; break; case OperationType.And: num = (long)num.Value & (long)operation.Value; break; case OperationType.LeftShift: num = (long)num.Value << (int)operation.Value; break; case OperationType.RightShift: num = (long)num.Value >> (int)operation.Value; break; case OperationType.Floor: num = Math.Floor(num.Value); break; case OperationType.Ceil: num = Math.Ceiling(num.Value); break; } } e.cachedValue = JToken.op_Implicit(num); if (!num.HasValue) { return default(T); } return (T)Convert.ChangeType(num.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T)); } private static bool IsNullable<T>() { if (typeof(T).IsGenericType) { return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition(); } return false; } public T To<T>() { if (Operations.Count != 0) { throw new InvalidOperationException("DataStorageElement.To<T>() cannot be used together with other operations on the DataStorageElement"); } return Context.GetData(Context.Key).ToObject<T>(); } public override string ToString() { return (Context?.ToString() ?? "(null)") + ", (" + ListOperations() + ")"; } private string ListOperations() { if (Operations != null) { return string.Join(", ", Operations.Select((OperationSpecification o) => o.ToString()).ToArray()); } return "none"; } } internal class DataStorageElementContext { internal string Key { get; set; } internal Action<string, DataStorageHelper.DataStorageUpdatedHandler> AddHandler { get; set; } internal Action<string, DataStorageHelper.DataStorageUpdatedHandler> RemoveHandler { get; set; } internal Func<string, JToken> GetData { get; set; } internal Action<string, JToken> Initialize { get; set; } internal Func<string, Task<JToken>> GetAsync { get; set; } public override string ToString() { return "Key: " + Key; } } public class GameData { [JsonProperty("location_name_to_id")] public Dictionary<string, long> LocationLookup { get; set; } = new Dictionary<string, long>(); [JsonProperty("item_name_to_id")] public Dictionary<string, long> ItemLookup { get; set; } = new Dictionary<string, long>(); [Obsolete("use Checksum instead")] [JsonProperty("version")] public int Version { get; set; } [JsonProperty("checksum")] public string Checksum { get; set; } } public class Hint { [JsonProperty("receiving_player")] public int ReceivingPlayer { get; set; } [JsonProperty("finding_player")] public int FindingPlayer { get; set; } [JsonProperty("item")] public long ItemId { get; set; } [JsonProperty("location")] public long LocationId { get; set; } [JsonProperty("item_flags")] public ItemFlags ItemFlags { get; set; } [JsonProperty("found")] public bool Found { get; set; } [JsonProperty("entrance")] public string Entrance { get; set; } [JsonProperty("status")] public HintStatus Status { get; set; } } public class ItemInfo { private readonly IItemInfoResolver itemInfoResolver; public long ItemId { get; } public long LocationId { get; } public PlayerInfo Player { get; } public ItemFlags Flags { get; } public string ItemName => itemInfoResolver.GetItemName(ItemId, ItemGame); public string ItemDisplayName => ItemName ?? $"Item: {ItemId}"; public string LocationName => itemInfoResolver.GetLocationName(LocationId, LocationGame); public string LocationDisplayName => LocationName ?? $"Location: {LocationId}"; public string ItemGame { get; } public string LocationGame { get; } public ItemInfo(NetworkItem item, string receiverGame, string senderGame, IItemInfoResolver itemInfoResolver, PlayerInfo player) { this.itemInfoResolver = itemInfoResolver; ItemGame = receiverGame; LocationGame = senderGame; ItemId = item.Item; LocationId = item.Location; Flags = item.Flags; Player = player; } public SerializableItemInfo ToSerializable() { return new SerializableItemInfo { IsScout = (GetType() == typeof(ScoutedItemInfo)), ItemId = ItemId, LocationId = LocationId, PlayerSlot = Player, Player = Player, Flags = Flags, ItemGame = ItemGame, ItemName = ItemName, LocationGame = LocationGame, LocationName = LocationName }; } } public class ScoutedItemInfo : ItemInfo { public new PlayerInfo Player => base.Player; public bool IsReceiverRelatedToActivePlayer { get; } public ScoutedItemInfo(NetworkItem item, string receiverGame, string senderGame, IItemInfoResolver itemInfoResolver, IPlayerHelper players, PlayerInfo player) : base(item, receiverGame, senderGame, itemInfoResolver, player) { IsReceiverRelatedToActivePlayer = (players.ActivePlayer ?? new PlayerInfo()).IsRelatedTo(player); } } public class JsonMessagePart { [JsonProperty("type")] [JsonConverter(typeof(AttemptingStringEnumConverter), new object[] { typeof(SnakeCaseNamingStrategy) })] public JsonMessagePartType? Type { get; set; } [JsonProperty("color")] [JsonConverter(typeof(AttemptingStringEnumConverter), new object[] { typeof(SnakeCaseNamingStrategy) })] public JsonMessagePartColor? Color { get; set; } [JsonProperty("text")] public string Text { get; set; } [JsonProperty("player")] public int? Player { get; set; } [JsonProperty("flags")] public ItemFlags? Flags { get; set; } [JsonProperty("hint_status")] public HintStatus? HintStatus { get; set; } } public struct NetworkItem { [JsonProperty("item")] public long Item { get; set; } [JsonProperty("location")] public long Location { get; set; } [JsonProperty("player")] public int Player { get; set; } [JsonProperty("flags")] public ItemFlags Flags { get; set; } } public struct NetworkPlayer { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } [JsonProperty("alias")] public string Alias { get; set; } [JsonProperty("name")] public string Name { get; set; } } public struct NetworkSlot { [JsonProperty("name")] public string Name { get; set; } [JsonProperty("game")] public string Game { get; set; } [JsonProperty("type")] public SlotType Type { get; set; } [JsonProperty("group_members")] public int[] GroupMembers { get; set; } } public class NetworkVersion { [JsonProperty("major")] public int Major { get; set; } [JsonProperty("minor")] public int Minor { get; set; } [JsonProperty("build")] public int Build { get; set; } [JsonProperty("class")] public string Class => "Version"; public NetworkVersion() { } public NetworkVersion(int major, int minor, int build) { Major = major; Minor = minor; Build = build; } public NetworkVersion(Version version) { Major = version.Major; Minor = version.Minor; Build = version.Build; } public Version ToVersion() { return new Version(Major, Minor, Build); } } public class OperationSpecification { [JsonProperty("operation")] [JsonConverter(typeof(AttemptingStringEnumConverter), new object[] { typeof(SnakeCaseNamingStrategy) })] public OperationType OperationType; [JsonProperty("value")] public JToken Value { get; set; } public override string ToString() { return $"{OperationType}: {Value}"; } } public static class Operation { public static OperationSpecification Min(int i) { return new OperationSpecification { OperationType = OperationType.Min, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Min(long i) { return new OperationSpecification { OperationType = OperationType.Min, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Min(float i) { return new OperationSpecification { OperationType = OperationType.Min, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Min(double i) { return new OperationSpecification { OperationType = OperationType.Min, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Min(decimal i) { return new OperationSpecification { OperationType = OperationType.Min, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Min(JToken i) { return new OperationSpecification { OperationType = OperationType.Min, Value = i }; } public static OperationSpecification Min(BigInteger i) { return new OperationSpecification { OperationType = OperationType.Min, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification Max(int i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(long i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(float i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(double i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(decimal i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(JToken i) { return new OperationSpecification { OperationType = OperationType.Max, Value = i }; } public static OperationSpecification Max(BigInteger i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification Remove(JToken value) { return new OperationSpecification { OperationType = OperationType.Remove, Value = value }; } public static OperationSpecification Pop(int value) { return new OperationSpecification { OperationType = OperationType.Pop, Value = JToken.op_Implicit(value) }; } public static OperationSpecification Pop(JToken value) { return new OperationSpecification { OperationType = OperationType.Pop, Value = value }; } public static OperationSpecification Update(IDictionary dictionary) { return new OperationSpecification { OperationType = OperationType.Update, Value = (JToken)(object)JObject.FromObject((object)dictionary) }; } public static OperationSpecification Floor() { return new OperationSpecification { OperationType = OperationType.Floor, Value = null }; } public static OperationSpecification Ceiling() { return new OperationSpecification { OperationType = OperationType.Ceil, Value = null }; } } public static class Bitwise { public static OperationSpecification Xor(long i) { return new OperationSpecification { OperationType = OperationType.Xor, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Xor(BigInteger i) { return new OperationSpecification { OperationType = OperationType.Xor, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification Or(long i) { return new OperationSpecification { OperationType = OperationType.Or, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Or(BigInteger i) { return new OperationSpecification { OperationType = OperationType.Or, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification And(long i) { return new OperationSpecification { OperationType = OperationType.And, Value = JToken.op_Implicit(i) }; } public static OperationSpecification And(BigInteger i) { return new OperationSpecification { OperationType = OperationType.And, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification LeftShift(long i) { return new OperationSpecification { OperationType = OperationType.LeftShift, Value = JToken.op_Implicit(i) }; } public static OperationSpecification RightShift(long i) { return new OperationSpecification { OperationType = OperationType.RightShift, Value = JToken.op_Implicit(i) }; } } public class Callback { internal DataStorageHelper.DataStorageUpdatedHandler Method { get; set; } private Callback() { } public static Callback Add(DataStorageHelper.DataStorageUpdatedHandler callback) { return new Callback { Method = callback }; } } public class AdditionalArgument { internal string Key { get; set; } internal JToken Value { get; set; } private AdditionalArgument() { } public static AdditionalArgument Add(string name, JToken value) { return new AdditionalArgument { Key = name, Value = value }; } } public class MinimalSerializableItemInfo { public long ItemId { get; set; } public long LocationId { get; set; } public int PlayerSlot { get; set; } public ItemFlags Flags { get; set; } public string ItemGame { get; set; } public string LocationGame { get; set; } } public class SerializableItemInfo : MinimalSerializableItemInfo { public bool IsScout { get; set; } public PlayerInfo Player { get; set; } public string ItemName { get; set; } public string LocationName { get; set; } [JsonIgnore] public string ItemDisplayName => ItemName ?? $"Item: {base.ItemId}"; [JsonIgnore] public string LocationDisplayName => LocationName ?? $"Location: {base.LocationId}"; public string ToJson(bool full = false) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown MinimalSerializableItemInfo minimalSerializableItemInfo = this; if (!full) { minimalSerializableItemInfo = new MinimalSerializableItemInfo { ItemId = base.ItemId, LocationId = base.LocationId, PlayerSlot = base.PlayerSlot, Flags = base.Flags }; if (IsScout) { minimalSerializableItemInfo.ItemGame = base.ItemGame; } else { minimalSerializableItemInfo.LocationGame = base.LocationGame; } } JsonSerializerSettings val = new JsonSerializerSettings { NullValueHandling = (NullValueHandling)1, Formatting = (Formatting)0 }; return JsonConvert.SerializeObject((object)minimalSerializableItemInfo, val); } public static SerializableItemInfo FromJson(string json, IArchipelagoSession session = null) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown ItemInfoStreamingContext additional = ((session != null) ? new ItemInfoStreamingContext { Items = session.Items, Locations = session.Locations, PlayerHelper = session.Players, ConnectionInfo = session.ConnectionInfo } : null); JsonSerializerSettings val = new JsonSerializerSettings { Context = new StreamingContext(StreamingContextStates.Other, additional) }; return JsonConvert.DeserializeObject<SerializableItemInfo>(json, val); } [OnDeserialized] internal void OnDeserializedMethod(StreamingContext streamingContext) { if (base.ItemGame == null && base.LocationGame != null) { IsScout = false; } else if (base.ItemGame != null && base.LocationGame == null) { IsScout = true; } if (streamingContext.Context is ItemInfoStreamingContext itemInfoStreamingContext) { if (IsScout && base.LocationGame == null) { base.LocationGame = itemInfoStreamingContext.ConnectionInfo.Game; } else if (!IsScout && base.ItemGame == null) { base.ItemGame = itemInfoStreamingContext.ConnectionInfo.Game; } if (ItemName == null) { ItemName = itemInfoStreamingContext.Items.GetItemName(base.ItemId, base.ItemGame); } if (LocationName == null) { LocationName = itemInfoStreamingContext.Locations.GetLocationNameFromId(base.LocationId, base.LocationGame); } if (Player == null) { Player = itemInfoStreamingContext.PlayerHelper.GetPlayerInfo(base.PlayerSlot); } } } } internal class ItemInfoStreamingContext { public IReceivedItemsHelper Items { get; set; } public ILocationCheckHelper Locations { get; set; } public IPlayerHelper PlayerHelper { get; set; } public IConnectionInfoProvider ConnectionInfo { get; set; } } } namespace Archipelago.MultiClient.Net.MessageLog.Parts { public class EntranceMessagePart : MessagePart { internal EntranceMessagePart(JsonMessagePart messagePart) : base(MessagePartType.Entrance, messagePart, Archipelago.MultiClient.Net.Colors.PaletteColor.Blue) { base.Text = messagePart.Text; } } public class HintStatusMessagePart : MessagePart { internal HintStatusMessagePart(JsonMessagePart messagePart) : base(MessagePartType.HintStatus, messagePart) { base.Text = messagePart.Text; if (messagePart.HintStatus.HasValue) { base.PaletteColor = ColorUtils.GetColor(messagePart.HintStatus.Value); } } } public class ItemMessagePart : MessagePart { public ItemFlags Flags { get; } public long ItemId { get; } public int Player { get; } internal ItemMessagePart(IPlayerHelper players, IItemInfoResolver items, JsonMessagePart part) : base(MessagePartType.Item, part) { Flags = part.Flags.GetValueOrDefault(); base.PaletteColor = ColorUtils.GetColor(Flags); Player = part.Player.GetValueOrDefault(); string game = (players.GetPlayerInfo(Player) ?? new PlayerInfo()).Game; JsonMessagePartType? type = part.Type; if (type.HasValue) { switch (type.GetValueOrDefault()) { case JsonMessagePartType.ItemId: ItemId = long.Parse(part.Text); base.Text = items.GetItemName(ItemId, game) ?? $"Item: {ItemId}"; break; case JsonMessagePartType.ItemName: ItemId = 0L; base.Text = part.Text; break; } } } } public class LocationMessagePart : MessagePart { public long LocationId { get; } public int Player { get; } internal LocationMessagePart(IPlayerHelper players, IItemInfoResolver itemInfoResolver, JsonMessagePart part) : base(MessagePartType.Location, part, Archipelago.MultiClient.Net.Colors.PaletteColor.Green) { Player = part.Player.GetValueOrDefault(); string game = (players.GetPlayerInfo(Player) ?? new PlayerInfo()).Game; JsonMessagePartType? type = part.Type; if (type.HasValue) { switch (type.GetValueOrDefault()) { case JsonMessagePartType.LocationId: LocationId = long.Parse(part.Text); base.Text = itemInfoResolver.GetLocationName(LocationId, game) ?? $"Location: {LocationId}"; break; case JsonMessagePartType.LocationName: LocationId = itemInfoResolver.GetLocationId(part.Text, game); base.Text = part.Text; break; } } } } public class MessagePart { public string Text { get; internal set; } public MessagePartType Type { get; internal set; } public Color Color => GetColor(BuiltInPalettes.Dark); public PaletteColor? PaletteColor { get; protected set; } public bool IsBackgroundColor { get; internal set; } internal MessagePart(MessagePartType type, JsonMessagePart messagePart, PaletteColor? color = null) { Type = type; Text = messagePart.Text; if (color.HasValue) { PaletteColor = color.Value; } else if (messagePart.Color.HasValue) { PaletteColor = ColorUtils.GetColor(messagePart.Color.Value); IsBackgroundColor = messagePart.Color.Value >= JsonMessagePartColor.BlackBg; } else { PaletteColor = null; } } public T GetColor<T>(Palette<T> palette) { return palette[PaletteColor]; } public override string ToString() { return Text; } } public enum MessagePartType { Text, Player, Item, Location, Entrance, HintStatus } public class PlayerMessagePart : MessagePart { public bool IsActivePlayer { get; } public int SlotId { get; } internal PlayerMessagePart(IPlayerHelper players, IConnectionInfoProvider connectionInfo, JsonMessagePart part) : base(MessagePartType.Player, part) { switch (part.Type) { case JsonMessagePartType.PlayerId: SlotId = int.Parse(part.Text); IsActivePlayer = SlotId == connectionInfo.Slot; base.Text = players.GetPlayerAlias(SlotId) ?? $"Player {SlotId}"; break; case JsonMessagePartType.PlayerName: SlotId = 0; IsActivePlayer = false; base.Text = part.Text; break; } base.PaletteColor = (IsActivePlayer ? Archipelago.MultiClient.Net.Colors.PaletteColor.Magenta : Archipelago.MultiClient.Net.Colors.PaletteColor.Yellow); } } } namespace Archipelago.MultiClient.Net.MessageLog.Messages { public class AdminCommandResultLogMessage : LogMessage { internal AdminCommandResultLogMessage(MessagePart[] parts) : base(parts) { } } public class ChatLogMessage : PlayerSpecificLogMessage { public string Message { get; } internal ChatLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string message) : base(parts, players, team, slot) { Message = message; } } public class CollectLogMessage : PlayerSpecificLogMessage { internal CollectLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts, players, team, slot) { } } public class CommandResultLogMessage : LogMessage { internal CommandResultLogMessage(MessagePart[] parts) : base(parts) { } } public class CountdownLogMessage : LogMessage { public int RemainingSeconds { get; } internal CountdownLogMessage(MessagePart[] parts, int remainingSeconds) : base(parts) { RemainingSeconds = remainingSeconds; } } public class GoalLogMessage : PlayerSpecificLogMessage { internal GoalLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts, players, team, slot) { } } public class HintItemSendLogMessage : ItemSendLogMessage { public bool IsFound { get; } internal HintItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, bool found, IItemInfoResolver itemInfoResolver) : base(parts, players, receiver, sender, item, itemInfoResolver) { IsFound = found; } } public class ItemCheatLogMessage : ItemSendLogMessage { internal ItemCheatLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, NetworkItem item, IItemInfoResolver itemInfoResolver) : base(parts, players, slot, 0, item, team, itemInfoResolver) { } } public class ItemSendLogMessage : LogMessage { private PlayerInfo ActivePlayer { get; } public PlayerInfo Receiver { get; } public PlayerInfo Sender { get; } public bool IsReceiverTheActivePlayer => Receiver == ActivePlayer; public bool IsSenderTheActivePlayer => Sender == ActivePlayer; public bool IsRelatedToActivePlayer { get { if (!ActivePlayer.IsRelatedTo(Receiver)) { return ActivePlayer.IsRelatedTo(Sender); } return true; } } public ItemInfo Item { get; } internal ItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, IItemInfoResolver itemInfoResolver) : this(parts, players, receiver, sender, item, players.ActivePlayer.Team, itemInfoResolver) { } internal ItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, int team, IItemInfoResolver itemInfoResolver) : base(parts) { ActivePlayer = players.ActivePlayer ?? new PlayerInfo(); Receiver = players.GetPlayerInfo(team, receiver) ?? new PlayerInfo(); Sender = players.GetPlayerInfo(team, sender) ?? new PlayerInfo(); PlayerInfo player = players.GetPlayerInfo(team, item.Player) ?? new PlayerInfo(); Item = new ItemInfo(item, Receiver.Game, Sender.Game, itemInfoResolver, player); } } public class JoinLogMessage : PlayerSpecificLogMessage { public string[] Tags { get; } internal JoinLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string[] tags) : base(parts, players, team, slot) { Tags = tags; } } public class LeaveLogMessage : PlayerSpecificLogMessage { internal LeaveLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts, players, team, slot) { } } public class LogMessage { public MessagePart[] Parts { get; } internal LogMessage(MessagePart[] parts) { Parts = parts; } public override string ToString() { if (Parts.Length == 1) { return Parts[0].Text; } StringBuilder stringBuilder = new StringBuilder(); MessagePart[] parts = Parts; foreach (MessagePart messagePart in parts) { stringBuilder.Append(messagePart.Text); } return stringBuilder.ToString(); } } public abstract class PlayerSpecificLogMessage : LogMessage { private PlayerInfo ActivePlayer { get; } public PlayerInfo Player { get; } public bool IsActivePlayer => Player == ActivePlayer; public bool IsRelatedToActivePlayer => ActivePlayer.IsRelatedTo(Player); internal PlayerSpecificLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts) { ActivePlayer = players.ActivePlayer ?? new PlayerInfo(); Player = players.GetPlayerInfo(team, slot) ?? new PlayerInfo(); } } public class ReleaseLogMessage : PlayerSpecificLogMessage { internal ReleaseLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts, players, team, slot) { } } public class ServerChatLogMessage : LogMessage { public string Message { get; } internal ServerChatLogMessage(MessagePart[] parts, string message) : base(parts) { Message = message; } } public class TagsChangedLogMessage : PlayerSpecificLogMessage { public string[] Tags { get; } internal TagsChangedLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string[] tags) : base(parts, players, team, slot) { Tags = tags; } } public class TutorialLogMessage : LogMessage { internal TutorialLogMessage(MessagePart[] parts) : base(parts) { } } } namespace Archipelago.MultiClient.Net.Helpers { public class ArchipelagoSocketHelper : BaseArchipelagoSocketHelper<ClientWebSocket>, IArchipelagoSocketHelper { public Uri Uri { get; } internal ArchipelagoSocketHelper(Uri hostUri) : base(CreateWebSocket(), 1024) { Uri = hostUri; } private static ClientWebSocket CreateWebSocket() { return new ClientWebSocket(); } public async Task ConnectAsync() { await ConnectToProvidedUri(Uri); StartPolling(); } private async Task ConnectToProvidedUri(Uri uri) { if (uri.Scheme != "unspecified") { try { await Socket.ConnectAsync(uri, CancellationToken.None); return; } catch (Exception e) { OnError(e); throw; } } List<Exception> errors = new List<Exception>(0); try { await Socket.ConnectAsync(uri.AsWss(), CancellationToken.None); if (Socket.State == WebSocketState.Open) { return; } } catch (Exception item) { errors.Add(item); Socket = CreateWebSocket(); } try { await Socket.ConnectAsync(uri.AsWs(), CancellationToken.None); } catch (Exception item2) { errors.Add(item2); OnError(new AggregateException(errors)); throw; } } } public class BaseArchipelagoSocketHelper<T> where T : WebSocket { private static readonly ArchipelagoPacketConverter Converter = new ArchipelagoPacketConverter(); private readonly BlockingCollection<Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>> sendQueue = new BlockingCollection<Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>>(); internal T Socket; private readonly int bufferSize; public bool Connected { get { if (Socket.State != WebSocketState.Open) { return Socket.State == WebSocketState.CloseReceived; } return true; } } public event ArchipelagoSocketHelperDelagates.PacketReceivedHandler PacketReceived; public event ArchipelagoSocketHelperDelagates.PacketsSentHandler PacketsSent; public event ArchipelagoSocketHelperDelagates.ErrorReceivedHandler ErrorReceived; public event ArchipelagoSocketHelperDelagates.SocketClosedHandler SocketClosed; public event ArchipelagoSocketHelperDelagates.SocketOpenedHandler SocketOpened; internal BaseArchipelagoSocketHelper(T socket, int bufferSize = 1024) { Socket = socket; this.bufferSize = bufferSize; } internal void StartPolling() { if (this.SocketOpened != null) { this.SocketOpened(); } Task.Run((Func<Task?>)PollingLoop); Task.Run((Func<Task?>)SendLoop); } private async Task PollingLoop() { byte[] buffer = new byte[bufferSize]; while (Socket.State == WebSocketState.Open) { string message = null; try { message = await ReadMessageAsync(buffer); } catch (Exception e) { OnError(e); } OnMessageReceived(message); } } private async Task SendLoop() { while (Socket.State == WebSocketState.Open) { try { await HandleSendBuffer(); } catch (Exception e) { OnError(e); } await Task.Delay(20); } } private async Task<string> ReadMessageAsync(byte[] buffer) { using MemoryStream readStream = new MemoryStream(buffer.Length); WebSocketReceiveResult result; do { result = await Socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); if (result.MessageType == WebSocketMessageType.Close) { try { await Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); } catch { } OnSocketClosed(); } else { readStream.Write(buffer, 0, result.Count); } } while (!result.EndOfMessage); return Encoding.UTF8.GetString(readStream.ToArray()); } public async Task DisconnectAsync() { await Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closure requested by client", CancellationToken.None); OnSocketClosed(); } public void SendPacket(ArchipelagoPacketBase packet) { SendMultiplePackets(new List<ArchipelagoPacketBase> { packet }); } public void SendMultiplePackets(List<ArchipelagoPacketBase> packets) { SendMultiplePackets(packets.ToArray()); } public void SendMultiplePackets(params ArchipelagoPacketBase[] packets) { SendMultiplePacketsAsync(packets).Wait(); } public Task SendPacketAsync(ArchipelagoPacketBase packet) { return SendMultiplePacketsAsync(new List<ArchipelagoPacketBase> { packet }); } public Task SendMultiplePacketsAsync(List<ArchipelagoPacketBase> packets) { return SendMultiplePacketsAsync(packets.ToArray()); } public Task SendMultiplePacketsAsync(params ArchipelagoPacketBase[] packets) { TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>(); foreach (ArchipelagoPacketBase item in packets) { sendQueue.Add(new Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>(item, taskCompletionSource)); } return taskCompletionSource.Task; } private async Task HandleSendBuffer() { List<ArchipelagoPacketBase> list = new List<ArchipelagoPacketBase>(); List<TaskCompletionSource<bool>> tasks = new List<TaskCompletionSource<bool>>(); Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>> tuple = sendQueue.Take(); list.Add(tuple.Item1); tasks.Add(tuple.Item2); Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>> item; while (sendQueue.TryTake(out item)) { list.Add(item.Item1); tasks.Add(item.Item2); } if (!list.Any()) { return; } if (Socket.State != WebSocketState.Open) { throw new ArchipelagoSocketClosedException(); } ArchipelagoPacketBase[] packets = list.ToArray(); string s = JsonConvert.SerializeObject((object)packets); byte[] messageBuffer = Encoding.UTF8.GetBytes(s); int messagesCount = (int)Math.Ceiling((double)messageBuffer.Length / (double)bufferSize); for (int i = 0; i < messagesCount; i++) { int num = bufferSize * i; int num2 = bufferSize; bool endOfMessage = i + 1 == messagesCount; if (num2 * (i + 1) > messageBuffer.Length) { num2 = messageBuffer.Length - num; } await Socket.SendAsync(new ArraySegment<byte>(messageBuffer, num, num2), WebSocketMessageType.Text, endOfMessage, CancellationToken.None); } foreach (TaskCompletionSource<bool> item2 in tasks) { item2.TrySetResult(result: true); } OnPacketSend(packets); } private void OnPacketSend(ArchipelagoPacketBase[] packets) { try { if (this.PacketsSent != null) { this.PacketsSent(packets); } } catch (Exception e) { OnError(e); } } private void OnSocketClosed() { try { if (this.SocketClosed != null) { this.SocketClosed(""); } } catch (Exception e) { OnError(e); } } private void OnMessageReceived(string message) { try { if (string.IsNullOrEmpty(message) || this.PacketReceived == null) { return; } List<ArchipelagoPacketBase> list = null; try { list = JsonConvert.DeserializeObject<List<ArchipelagoPacketBase>>(message, (JsonConverter[])(object)new JsonConverter[1] { Converter }); } catch (Exception e) { OnError(e); } if (list == null) { return; } foreach (ArchipelagoPacketBase item in list) { this.PacketReceived(item); } } catch (Exception e2) { OnError(e2); } } protected void OnError(Exception e) { try { if (this.ErrorReceived != null) { this.ErrorReceived(e, e.Message); } } catch (Exception ex) { Console.Out.WriteLine("Error occured during reporting of errorOuter Errror: " + e.Message + " " + e.StackTrace + "Inner Errror: " + ex.Message + " " + ex.StackTrace); } } } public interface IConnectionInfoProvider { string Game { get; } int Team { get; } int Slot { get; } string[] Tags { get; } ItemsHandlingFlags ItemsHandlingFlags { get; } string Uuid { get; } void UpdateConnectionOptions(string[] tags); void UpdateConnectionOptions(ItemsHandlingFlags itemsHandlingFlags); void UpdateConnectionOptions(string[] tags, ItemsHandlingFlags itemsHandlingFlags); } public class ConnectionInfoHelper : IConnectionInfoProvider { private readonly IArchipelagoSocketHelper socket; public string Game { get; private set; } public int Team { get; private set; } public int Slot { get; private set; } public string[] Tags { get; internal set; } public ItemsHandlingFlags ItemsHandlingFlags { get; internal set; } public string Uuid { get; private set; } internal ConnectionInfoHelper(IArchipelagoSocketHelper socket) { this.socket = socket; Reset(); socket.PacketReceived += PacketReceived; } private void PacketReceived(ArchipelagoPacketBase packet) { if (!(packet is ConnectedPacket connectedPacket)) { if (packet is ConnectionRefusedPacket) { Reset(); } return; } Team = connectedPacket.Team; Slot = connectedPacket.Slot; if (connectedPacket.SlotInfo != null && connectedPacket.SlotInfo.ContainsKey(Slot)) { Game = connectedPacket.SlotInfo[Slot].Game; } } internal void SetConnectionParameters(string game, string[] tags, ItemsHandlingFlags itemsHandlingFlags, string uuid) { Game = game; Tags = tags ?? new string[0]; ItemsHandlingFlags = itemsHandlingFlags; Uuid = uuid ?? Guid.NewGuid().ToString(); } private void Reset() { Game = null; Team = -1; Slot = -1; Tags = new string[0]; ItemsHandlingFlags = ItemsHandlingFlags.NoItems; Uuid = null; } public void UpdateConnectionOptions(string[] tags) { UpdateConnectionOptions(tags, ItemsHandlingFlags); } public void UpdateConnectionOptions(ItemsHandlingFlags itemsHandlingFlags) { UpdateConnectionOptions(Tags, itemsHandlingFlags); } public void UpdateConnectionOptions(string[] tags, ItemsHandlingFlags itemsHandlingFlags) { SetConnectionParameters(Game, tags, itemsHandlingFlags, Uuid); socket.SendPacket(new ConnectUpdatePacket { Tags = Tags, ItemsHandling = ItemsHandlingFlags }); } } public interface IDataStorageHelper : IDataStorageWrapper { DataStorageElement this[Scope scope, string key] { get; set; } DataStorageElement this[string key] { get; set; } } public class DataStorageHelper : IDataStorageHelper, IDataStorageWrapper { public delegate void DataStorageUpdatedHandler(JToken originalValue, JToken newValue, Dictionary<string, JToken> additionalArguments); private readonly Dictionary<string, DataStorageUpdatedHandler> onValueChangedEventHandlers = new Dictionary<string, DataStorageUpdatedHandler>(); private readonly Dictionary<Guid, DataStorageUpdatedHandler> operationSpecificCallbacks = new Dictionary<Guid, DataStorageUpdatedHandler>(); private readonly Dictionary<string, TaskCompletionSource<JToken>> asyncRetrievalTasks = new Dictionary<string, TaskCompletionSource<JToken>>(); private readonly IArchipelagoSocketHelper socket; private readonly IConnectionInfoProvider connectionInfoProvider; public DataStorageElement this[Scope scope, string key] { get { return this[AddScope(scope, key)]; } set { this[AddScope(scope, key)] = value; } } public DataStorageElement this[string key] { get { return new DataStorageElement(GetContextForKey(key)); } set { SetValue(key, value); } } internal DataStorageHelper(IArchipelagoSocketHelper socket, IConnectionInfoProvider connectionInfoProvider) { this.socket = socket; this.connectionInfoProvider = connectionInfoProvider; socket.PacketReceived += OnPacketReceived; } private void OnPacketReceived(ArchipelagoPacketBase packet) { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Invalid comparison between Unknown and I4 if (!(packet is RetrievedPacket retrievedPacket)) { if (packet is SetReplyPacket setReplyPacket) { if (setReplyPacket.AdditionalArguments != null && setReplyPacket.AdditionalArguments.ContainsKey("Reference") && (int)setReplyPacket.AdditionalArguments["Reference"].Type == 8 && ((string)setReplyPacket.AdditionalArguments["Reference"]).TryParseNGuid(out var g) && operationSpecificCallbacks.TryGetValue(g, out var value)) { value(setReplyPacket.OriginalValue, setReplyPacket.Value, setReplyPacket.AdditionalArguments); operationSpecificCallbacks.Remove(g); } if (onValueChangedEventHandlers.TryGetValue(setReplyPacket.Key, out var value2)) { value2(setReplyPacket.OriginalValue, setReplyPacket.Value, setReplyPacket.AdditionalArguments); } } return; } foreach (KeyValuePair<string, JToken> datum in retrievedPacket.Data) { if (asyncRetrievalTasks.TryGetValue(datum.Key, out var value3)) { value3.TrySetResult(datum.Value); asyncRetrievalTasks.Remove(datum.Key); } } } private Task<JToken> GetAsync(string key) { if (asyncRetrievalTasks.TryGetValue(key, out var value)) { return value.Task; } TaskCompletionSource<JToken> taskCompletionSource = new TaskCompletionSource<JToken>(); asyncRetrievalTasks[key] = taskCompletionSource; socket.SendPacketAsync(new GetPacket { Keys = new string[1] { key } }); return taskCompletionSource.Task; } private void Initialize(string key, JToken value) { socket.SendPacketAsync(new SetPacket { Key = key, DefaultValue = value, Operations = new OperationSpecification[1] { new OperationSpecification { OperationType = OperationType.Default } } }); } private JToken GetValue(string key) { Task<JToken> async = GetAsync(key); if (!async.Wait(TimeSpan.FromSeconds(2.0))) { throw new TimeoutException("Timed out retrieving data for key `" + key + "`. This may be due to an attempt to retrieve a value from the DataStorageHelper in a synchronous fashion from within a PacketReceived handler. When using the DataStorageHelper from within code which runs on the websocket thread then use the asynchronous getters. Ex: `DataStorageHelper[\"" + key + "\"].GetAsync().ContinueWith(x => {});`Be aware that DataStorageHelper calls tend to cause packet responses, so making a call from within a PacketReceived handler may cause an infinite loop."); } return async.Result; } private void SetValue(string key, DataStorageElement e) { if (key.StartsWith("_read_")) { throw new InvalidOperationException("DataStorage write operation on readonly key '" + key + "' is not allowed"); } if (e == null) { e = new DataStorageElement(OperationType.Replace, (JToken)(object)JValue.CreateNull()); } if (e.Context == null) { e.Context = GetContextForKey(key); } else if (e.Context.Key != key) { e.Operations.Insert(0, new OperationSpecification { OperationType = OperationType.Replace, Value = GetValue(e.Context.Key) }); } Dictionary<string, JToken> dictionary = e.AdditionalArguments ?? new Dictionary<string, JToken>(0); if (e.Callbacks != null) { Guid key2 = Guid.NewGuid(); operationSpecificCallbacks[key2] = e.Callbacks; dictionary["Reference"] = JToken.op_Implicit(key2.ToString("N")); socket.SendPacketAsync(new SetPacket { Key = key, Operations = e.Operations.ToArray(), WantReply = true, AdditionalArguments = dictionary }); } else { socket.SendPacketAsync(new SetPacket { Key = key, Operations = e.Operations.ToArray(), AdditionalArguments = dictionary }); } } private DataStorageElementContext GetContextForKey(string key) { return new DataStorageElementContext { Key = key, GetData = GetValue, GetAsync = GetAsync, Initialize = Initialize, AddHandler = AddHandler, RemoveHandler = RemoveHandler }; } private void AddHandler(string key, DataStorageUpdatedHandler handler) { if (onValueChangedEventHandlers.ContainsKey(key)) { Dictionary<string, DataStorageUpdatedHandler> dictionary = onValueChangedEventHandlers; dictionary[key] = (DataStorageUpdatedHandler)Delegate.Combine(dictionary[key], handler); } else { onValueChangedEventHandlers[key] = handler; } socket.SendPacketAsync(new SetNotifyPacket { Keys = new string[1] { key } }); } private void RemoveHandler(string key, DataStorageUpdatedHandler handler) { if (onValueChangedEventHandlers.ContainsKey(key)) { Dictionary<string, DataStorageUpdatedHandler> dictionary = onValueChangedEventHandlers; dictionary[key] = (DataStorageUpdatedHandler)Delegate.Remove(dictionary[key], handler); if (onValueChangedEventHandlers[key] == null) { onValueChangedEventHandlers.Remove(key); } } } private string AddScope(Scope scope, string key) { return scope switch { Scope.Global => key, Scope.Game => $"{scope}:{connectionInfoProvider.Game}:{key}", Scope.Team => $"{scope}:{connectionInfoProvider.Team}:{key}", Scope.Slot => $"{scope}:{connectionInfoProvider.Slot}:{key}", Scope.ReadOnly => "_read_" + key, _ => throw new ArgumentOutOfRangeException("scope", scope, "Invalid scope for key " + key), }; } private DataStorageElement GetHintsElement(int? slot = null, int? team = null) { return this[Scope.ReadOnly, $"hints_{team ?? connectionInfoProvider.Team}_{slot ?? connectionInfoProvider.Slot}"]; } private DataStorageElement GetSlotDataElement(int? slot = null) { return this[Scope.ReadOnly, $"slot_data_{slot ?? connectionInfoProvider.Slot}"]; } private DataStorageElement GetItemNameGroupsElement(string game = null) { return this[Scope.ReadOnly, "item_name_groups_" + (game ?? connectionInfoProvider.Game)]; } private DataStorageElement GetLocationNameGroupsElement(string game = null) { return this[Scope.ReadOnly, "location_name_groups_" + (game ?? connectionInfoProvider.Game)]; } private DataStorageElement GetClientStatusElement(int? slot = null, int? team = null) { return this[Scope.ReadOnly, $"client_status_{team ?? connectionInfoProvider.Team}_{slot ?? connectionInfoProvider.Slot}"]; } private DataStorageElement GetRaceModeElement() { return this[Scope.ReadOnly, "race_mode"]; } public Hint[] GetHints(int? slot = null, int? team = null) { return GetHintsElement(slot, team).To<Hint[]>(); } public Task<Hint[]> GetHintsAsync(int? slot = null, int? team = null) { return GetHintsElement(slot, team).GetAsync<Hint[]>(); } public void TrackHints(Action<Hint[]> onHintsUpdated, bool retrieveCurrentlyUnlockedHints = true, int? slot = null, int? team = null) { GetHintsElement(slot, team).OnValueChanged += delegate(JToken _, JToken newValue, Dictionary<string, JToken> x) { onHintsUpdated(newValue.ToObject<Hint[]>()); }; if (retrieveCurrentlyUnlockedHints) { GetHintsAsync(slot, team).ContinueWith(delegate(Task<Hint[]> t) { onHintsUpdated(t.Result); }); } } public Dictionary<string, object> GetSlotData(int? slot = null) { return GetSlotData<Dictionary<string, object>>(slot); } public T GetSlotData<T>(int? slot = null) where T : class { return GetSlotDataElement(slot).To<T>(); } public Task<Dictionary<string, object>> GetSlotDataAsync(int? slot = null) { return GetSlotDataAsync<Dictionary<string, object>>(slot); } public Task<T> GetSlotDataAsync<T>(int? slot = null) where T : class { return GetSlotDataElement(slot).GetAsync<T>(); } public Dictionary<string, string[]> GetItemNameGroups(string game = null) { return GetItemNameGroupsElement(game).To<Dictionary<string, string[]>>(); } public Task<Dictionary<string, string[]>> GetItemNameGroupsAsync(string game = null) { return GetItemNameGroupsElement(game).GetAsync<Dictionary<string, string[]>>(); } public Dictionary<string, string[]> GetLocationNameGroups(string game = null) { return GetLocationNameGroupsElement(game).To<Dictionary<string, string[]>>(); } public Task<Dictionary<string, string[]>> GetLocationNameGroupsAsync(string game = null) { return GetLocationNameGroupsElement(game).GetAsync<Dictionary<string, string[]>>(); } public ArchipelagoClientState GetClientStatus(int? slot = null, int? team = null) { return GetClientStatusElement(slot, team).To<ArchipelagoClientState?>().GetValueOrDefault(); } public Task<ArchipelagoClientState> GetClientStatusAsync(int? slot = null, int? team = null) { return GetClientStatusElement(slot, team).GetAsync<ArchipelagoClientState?>().ContinueWith((Task<ArchipelagoClientState?> r) => r.Result.GetValueOrDefault()); } public void TrackClientStatus(Action<ArchipelagoClientState> onStatusUpdated, bool retrieveCurrentClientStatus = true, int? slot = null, int? team = null) { GetClientStatusElement(slot, team).OnValueChanged += delegate(JToken _, JToken newValue, Dictionary<string, JToken> x) { onStatusUpdated(newValue.ToObject<ArchipelagoClientState>());