Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of AutoReorder v1.0.1
AutoReorder.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using AutoReorder; using HarmonyLib; using MelonLoader; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(Core), "AutoReorder", "1.0.1", "HazDS", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("AutoReorder")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+5bb5186c319c06a3dc0d8474ec24c49e8b7987a8")] [assembly: AssemblyProduct("AutoReorder")] [assembly: AssemblyTitle("AutoReorder")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace AutoReorder { public class Core : MelonMod { [CompilerGenerated] private sealed class <DoAutoReorder>d__28 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public AutoReorderEntry entry; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DoAutoReorder>d__28(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(1f); <>1__state = 1; return true; case 1: <>1__state = -1; ExecuteAutoReorder(entry); entry.ReorderPending = false; 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 bool _initialized; private static Type _deliveryManagerType; private static Type _deliveryAppType; private static Type _deliveryReceiptDisplayType; private static Type _loadManagerType; private static Type _saveManagerType; private static MethodInfo _getActiveShopDelivery; private static bool _saveHooksWired; private const string PersistFileName = "AutoReorder.json"; public static readonly Dictionary<string, AutoReorderEntry> ActiveAutoReorders = new Dictionary<string, AutoReorderEntry>(); private static readonly Dictionary<object, GameObject> _autoReorderButtons = new Dictionary<object, GameObject>(); public static Core Instance { get; private set; } public override void OnInitializeMelon() { Instance = this; CacheTypes(); ApplyPatches(); ((MelonBase)this).LoggerInstance.Msg("AutoReorder initialized!"); } private void CacheTypes() { _deliveryManagerType = CrossCompat.FindType("ScheduleOne.Delivery.DeliveryManager"); _deliveryAppType = CrossCompat.FindType("ScheduleOne.UI.Phone.Delivery.DeliveryApp"); _deliveryReceiptDisplayType = CrossCompat.FindType("ScheduleOne.UI.Phone.Delivery.DeliveryReceiptDisplay"); _loadManagerType = CrossCompat.FindType("ScheduleOne.Persistence.LoadManager"); _saveManagerType = CrossCompat.FindType("ScheduleOne.Persistence.SaveManager"); if (_deliveryManagerType == null) { ((MelonBase)this).LoggerInstance.Error("Could not find DeliveryManager type"); } if (_deliveryAppType == null) { ((MelonBase)this).LoggerInstance.Error("Could not find DeliveryApp type"); } if (_deliveryReceiptDisplayType == null) { ((MelonBase)this).LoggerInstance.Error("Could not find DeliveryReceiptDisplay type"); } if (_loadManagerType == null) { ((MelonBase)this).LoggerInstance.Error("Could not find LoadManager type"); } if (_saveManagerType == null) { ((MelonBase)this).LoggerInstance.Error("Could not find SaveManager type"); } } private void ApplyPatches() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown if (_deliveryReceiptDisplayType != null) { MethodInfo methodInfo = CrossCompat.FindMethod(_deliveryReceiptDisplayType, "Set", 3); if (methodInfo != null) { ((MelonBase)this).HarmonyInstance.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Core), "ReceiptDisplay_Set_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((MelonBase)this).LoggerInstance.Msg("Patched DeliveryReceiptDisplay.Set"); } else { ((MelonBase)this).LoggerInstance.Error("Could not find DeliveryReceiptDisplay.Set method"); } } if (_deliveryManagerType != null) { _getActiveShopDelivery = CrossCompat.FindMethod(_deliveryManagerType, "GetActiveShopDelivery", 1); if (_getActiveShopDelivery == null) { ((MelonBase)this).LoggerInstance.Warning("Could not find GetActiveShopDelivery method"); } } } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { if (sceneName == "Menu") { _initialized = false; ActiveAutoReorders.Clear(); _autoReorderButtons.Clear(); } } public override void OnSceneWasInitialized(int buildIndex, string sceneName) { if (sceneName == "Main" && !_initialized) { _initialized = true; WireSaveHooks(); } } private static void WireSaveHooks() { if (_saveHooksWired) { return; } try { object singletonInstance = GetSingletonInstance(_loadManagerType); object singletonInstance2 = GetSingletonInstance(_saveManagerType); if (singletonInstance != null) { object memberValue = CrossCompat.GetMemberValue(singletonInstance, "onLoadComplete"); if (memberValue != null) { AddUnityEventListener(memberValue, RestoreState); Melon<Core>.Logger.Msg("Hooked LoadManager.onLoadComplete"); } else { Melon<Core>.Logger.Warning("LoadManager.onLoadComplete not found"); } } if (singletonInstance2 != null) { object memberValue2 = CrossCompat.GetMemberValue(singletonInstance2, "onSaveComplete"); if (memberValue2 != null) { AddUnityEventListener(memberValue2, SaveState); Melon<Core>.Logger.Msg("Hooked SaveManager.onSaveComplete"); } else { Melon<Core>.Logger.Warning("SaveManager.onSaveComplete not found"); } } _saveHooksWired = true; } catch (Exception ex) { Melon<Core>.Logger.Error("WireSaveHooks failed: " + ex.Message); Melon<Core>.Logger.Error("Stack trace: " + ex.StackTrace); } } private static string GetLoadedGameFolderPath() { object singletonInstance = GetSingletonInstance(_loadManagerType); if (singletonInstance == null) { return null; } return CrossCompat.GetMemberValue(singletonInstance, "LoadedGameFolderPath") as string; } private static void SaveState() { try { string loadedGameFolderPath = GetLoadedGameFolderPath(); if (string.IsNullOrEmpty(loadedGameFolderPath)) { return; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("{\"v\":1,\"ids\":["); bool flag = true; foreach (KeyValuePair<string, AutoReorderEntry> activeAutoReorder in ActiveAutoReorders) { if (!flag) { stringBuilder.Append(','); } flag = false; stringBuilder.Append('"').Append(JsonEscape(activeAutoReorder.Key)).Append('"'); } stringBuilder.Append("]}"); string text = Path.Combine(loadedGameFolderPath, "AutoReorder.json"); File.WriteAllText(text, stringBuilder.ToString()); Melon<Core>.Logger.Msg($"Saved {ActiveAutoReorders.Count} auto-reorder(s) to {text}"); } catch (Exception ex) { Melon<Core>.Logger.Error("SaveState failed: " + ex.Message); Melon<Core>.Logger.Error("Stack trace: " + ex.StackTrace); } } private static void RestoreState() { try { string loadedGameFolderPath = GetLoadedGameFolderPath(); if (string.IsNullOrEmpty(loadedGameFolderPath)) { return; } string text = Path.Combine(loadedGameFolderPath, "AutoReorder.json"); if (!File.Exists(text)) { return; } string json = File.ReadAllText(text); List<string> list = ParseIdList(json); if (list.Count == 0) { return; } object networkSingletonInstance = GetNetworkSingletonInstance(_deliveryManagerType); if (networkSingletonInstance == null) { Melon<Core>.Logger.Warning("DeliveryManager unavailable — cannot restore auto-reorders"); return; } object memberValue = CrossCompat.GetMemberValue(networkSingletonInstance, "DisplayedDeliveryHistory"); if (!(memberValue is IEnumerable enumerable)) { Melon<Core>.Logger.Warning("DisplayedDeliveryHistory is not enumerable"); return; } Dictionary<string, object> dictionary = new Dictionary<string, object>(); foreach (object item in enumerable) { if (item != null) { string text2 = CrossCompat.GetMemberValue(item, "DeliveryID") as string; if (!string.IsNullOrEmpty(text2)) { dictionary[text2] = item; } } } object playerSingletonInstance = GetPlayerSingletonInstance(_deliveryAppType); int num = 0; foreach (string item2 in list) { if (dictionary.TryGetValue(item2, out var value)) { string text3 = CrossCompat.GetMemberValue(value, "StoreName") as string; bool hadActiveDelivery = playerSingletonInstance != null && !string.IsNullOrEmpty(text3) && ShopHasActiveDelivery(playerSingletonInstance, text3); ActiveAutoReorders[item2] = new AutoReorderEntry { Receipt = value, ShopName = text3, DeliveryId = item2, HadActiveDelivery = hadActiveDelivery, ReorderPending = false }; num++; } } Melon<Core>.Logger.Msg($"Restored {num}/{list.Count} auto-reorder(s) from {text}"); } catch (Exception ex) { Melon<Core>.Logger.Error("RestoreState failed: " + ex.Message); Melon<Core>.Logger.Error("Stack trace: " + ex.StackTrace); } } private static string JsonEscape(string s) { if (string.IsNullOrEmpty(s)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(s.Length); foreach (char c in s) { switch (c) { case '"': stringBuilder.Append("\\\""); continue; case '\\': stringBuilder.Append("\\\\"); continue; case '\n': stringBuilder.Append("\\n"); continue; case '\r': stringBuilder.Append("\\r"); continue; case '\t': stringBuilder.Append("\\t"); continue; } if (c < ' ') { stringBuilder.AppendFormat("\\u{0:x4}", (int)c); } else { stringBuilder.Append(c); } } return stringBuilder.ToString(); } private static List<string> ParseIdList(string json) { List<string> list = new List<string>(); if (string.IsNullOrEmpty(json)) { return list; } int num = json.IndexOf("\"ids\"", StringComparison.Ordinal); if (num < 0) { return list; } int num2 = json.IndexOf('[', num); if (num2 < 0) { return list; } int num3 = json.IndexOf(']', num2); if (num3 < 0) { return list; } int num4 = num2 + 1; while (num4 < num3) { if (json[num4] == '"') { int num5 = num4 + 1; StringBuilder stringBuilder = new StringBuilder(); num4 = num5; while (num4 < num3 && json[num4] != '"') { if (json[num4] == '\\' && num4 + 1 < num3) { char c = json[num4 + 1]; switch (c) { case '"': stringBuilder.Append('"'); break; case '\\': stringBuilder.Append('\\'); break; case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; case 't': stringBuilder.Append('\t'); break; default: stringBuilder.Append(c); break; } num4 += 2; } else { stringBuilder.Append(json[num4]); num4++; } } list.Add(stringBuilder.ToString()); num4++; } else { num4++; } } return list; } public override void OnUpdate() { if (!_initialized || ActiveAutoReorders.Count == 0 || Time.frameCount % 60 != 0) { return; } try { object networkSingletonInstance = GetNetworkSingletonInstance(_deliveryManagerType); if (networkSingletonInstance == null) { return; } object playerSingletonInstance = GetPlayerSingletonInstance(_deliveryAppType); if (playerSingletonInstance == null) { return; } List<KeyValuePair<string, AutoReorderEntry>> list = new List<KeyValuePair<string, AutoReorderEntry>>(ActiveAutoReorders); foreach (KeyValuePair<string, AutoReorderEntry> item in list) { AutoReorderEntry value = item.Value; if (ShopHasActiveDelivery(playerSingletonInstance, value.ShopName)) { value.HadActiveDelivery = true; } else if (value.HadActiveDelivery && !value.ReorderPending) { value.HadActiveDelivery = false; value.ReorderPending = true; ((MelonBase)this).LoggerInstance.Msg("Detected delivery completion for " + value.ShopName + ", triggering auto-reorder..."); MelonCoroutines.Start(DoAutoReorder(value)); } } } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Error("OnUpdate poll failed: " + ex.Message); } } private static bool ShopHasActiveDelivery(object deliveryApp, string shopName) { MethodInfo methodInfo = CrossCompat.FindMethod(_deliveryAppType, "GetShop", 1); if (methodInfo == null) { return false; } object obj = methodInfo.Invoke(deliveryApp, new object[1] { shopName }); if (obj == null) { return false; } object networkSingletonInstance = GetNetworkSingletonInstance(_deliveryManagerType); if (networkSingletonInstance == null || _getActiveShopDelivery == null) { return false; } object obj2 = _getActiveShopDelivery.Invoke(networkSingletonInstance, new object[1] { obj }); return obj2 != null; } [IteratorStateMachine(typeof(<DoAutoReorder>d__28))] private static IEnumerator DoAutoReorder(AutoReorderEntry entry) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DoAutoReorder>d__28(0) { entry = entry }; } private static void ExecuteAutoReorder(AutoReorderEntry entry) { try { object playerSingletonInstance = GetPlayerSingletonInstance(_deliveryAppType); if (playerSingletonInstance == null) { Melon<Core>.Logger.Warning("DeliveryApp not found, cannot auto-reorder."); return; } MethodInfo methodInfo = CrossCompat.FindMethod(_deliveryAppType, "Reorder", 1); if (methodInfo != null) { methodInfo.Invoke(playerSingletonInstance, new object[1] { entry.Receipt }); Melon<Core>.Logger.Msg("Auto-reorder placed for " + entry.ShopName + "!"); } } catch (Exception ex) { Melon<Core>.Logger.Error("ExecuteAutoReorder failed: " + ex.Message); Melon<Core>.Logger.Error("Stack trace: " + ex.StackTrace); } } public static void ReceiptDisplay_Set_Postfix(object __instance) { try { object obj = CrossCompat.GetMemberValue(__instance, "Receipt") ?? CrossCompat.GetMemberValue(__instance, "_receipt"); if (obj == null) { return; } string text = CrossCompat.GetMemberValue(obj, "DeliveryID") as string; if (string.IsNullOrEmpty(text)) { return; } if (!_autoReorderButtons.TryGetValue(__instance, out var value) || (Object)(object)value == (Object)null) { value = CreateAutoReorderButton(__instance); if ((Object)(object)value == (Object)null) { return; } _autoReorderButtons[__instance] = value; } bool isEnabled = ActiveAutoReorders.ContainsKey(text); UpdateButtonVisual(value, isEnabled); Button comp = CrossCompat.GetComp<Button>(value); if (!((Object)(object)comp == (Object)null)) { ((UnityEventBase)comp.onClick).RemoveAllListeners(); object capturedReceipt = obj; string capturedId = text; GameObject capturedBtnObj = value; AddButtonListener(comp, delegate { bool isEnabled2 = ToggleAutoReorder(capturedReceipt, capturedId); UpdateButtonVisual(capturedBtnObj, isEnabled2); RefreshAllButtons(); }); } } catch (Exception ex) { Melon<Core>.Logger.Error("ReceiptDisplay_Set_Postfix failed: " + ex.Message); Melon<Core>.Logger.Error("Stack trace: " + ex.StackTrace); } } private static GameObject CreateAutoReorderButton(object displayInstance) { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: 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_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) object obj = CrossCompat.GetMemberValue(displayInstance, "ReorderButton") ?? CrossCompat.GetMemberValue(displayInstance, "_ReorderButton"); if (obj == null) { return null; } object memberValue = CrossCompat.GetMemberValue(obj, "gameObject"); GameObject val = (GameObject)((memberValue is GameObject) ? memberValue : null); if ((Object)(object)val == (Object)null) { return null; } Transform parent = val.transform.parent; GameObject val2 = Object.Instantiate<GameObject>(val, parent); ((Object)val2).name = "AutoReorderButton"; RectTransform comp = CrossCompat.GetComp<RectTransform>(val); RectTransform comp2 = CrossCompat.GetComp<RectTransform>(val2); if ((Object)(object)comp != (Object)null && (Object)(object)comp2 != (Object)null) { comp2.anchoredPosition = comp.anchoredPosition + new Vector2(0f - comp.sizeDelta.x - 8f, 0f); comp2.sizeDelta = new Vector2(comp.sizeDelta.x * 0.85f, comp.sizeDelta.y); } bool flag = false; for (int i = 0; i < val2.transform.childCount; i++) { Transform child = val2.transform.GetChild(i); Text comp3 = CrossCompat.GetComp<Text>(((Component)child).gameObject); if ((Object)(object)comp3 != (Object)null) { if (!flag) { comp3.text = "Auto"; comp3.fontSize = Mathf.Max(comp3.fontSize - 2, 10); flag = true; } else { ((Component)child).gameObject.SetActive(false); } } } try { Type type = CrossCompat.FindType("ScheduleOne.UI.Tooltips.Tooltip"); if (type != null) { if (CrossCompat._goGetComponentGeneric == null) { MethodInfo[] methods = typeof(GameObject).GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "GetComponent" && methodInfo.IsGenericMethod && methodInfo.GetParameters().Length == 0) { CrossCompat._goGetComponentGeneric = methodInfo; break; } } } if (CrossCompat._goGetComponentGeneric != null) { MethodInfo methodInfo2 = CrossCompat._goGetComponentGeneric.MakeGenericMethod(type); object obj2 = methodInfo2.Invoke(val2, null); Component val3 = (Component)((obj2 is Component) ? obj2 : null); if (val3 != null) { Object.Destroy((Object)(object)val3); } } } } catch { } Button comp4 = CrossCompat.GetComp<Button>(val2); if ((Object)(object)comp4 == (Object)null) { return null; } ((UnityEventBase)comp4.onClick).RemoveAllListeners(); ((Selectable)comp4).interactable = true; return val2; } private static void AddButtonListener(Button button, Action callback) { if (!((Object)(object)button == (Object)null)) { AddUnityEventListener(button.onClick, callback); } } private static void AddUnityEventListener(object unityEvent, Action callback) { if (unityEvent == null) { return; } MethodInfo methodInfo = null; MethodInfo[] methods = unityEvent.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo2 in methods) { if (methodInfo2.Name == "AddListener" && !methodInfo2.IsGenericMethod && methodInfo2.GetParameters().Length == 1) { methodInfo = methodInfo2; break; } } if (methodInfo == null) { return; } Type parameterType = methodInfo.GetParameters()[0].ParameterType; ConstructorInfo[] constructors = parameterType.GetConstructors(); foreach (ConstructorInfo constructorInfo in constructors) { ParameterInfo[] parameters = constructorInfo.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType == typeof(Action)) { object obj = constructorInfo.Invoke(new object[1] { callback }); methodInfo.Invoke(unityEvent, new object[1] { obj }); return; } } MethodInfo[] methods2 = parameterType.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo3 in methods2) { if ((methodInfo3.Name == "op_Implicit" || methodInfo3.Name == "op_Explicit") && methodInfo3.GetParameters().Length == 1 && methodInfo3.GetParameters()[0].ParameterType == typeof(Action)) { object obj2 = methodInfo3.Invoke(null, new object[1] { callback }); methodInfo.Invoke(unityEvent, new object[1] { obj2 }); return; } } if (typeof(Delegate).IsAssignableFrom(parameterType)) { Delegate @delegate = Delegate.CreateDelegate(parameterType, callback.Target, callback.Method); methodInfo.Invoke(unityEvent, new object[1] { @delegate }); } else { Melon<Core>.Logger.Error("AddUnityEventListener: Could not create delegate for type " + parameterType.FullName); } } private static void UpdateButtonVisual(GameObject btnObj, bool isEnabled) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)btnObj == (Object)null)) { Button comp = CrossCompat.GetComp<Button>(btnObj); if ((Object)(object)comp != (Object)null) { ColorBlock colors = ((Selectable)comp).colors; ((ColorBlock)(ref colors)).normalColor = (isEnabled ? new Color(0.2f, 0.75f, 0.2f, 1f) : new Color(0.6f, 0.6f, 0.6f, 1f)); ((ColorBlock)(ref colors)).highlightedColor = (isEnabled ? new Color(0.15f, 0.65f, 0.15f, 1f) : new Color(0.5f, 0.5f, 0.5f, 1f)); ((ColorBlock)(ref colors)).pressedColor = (isEnabled ? new Color(0.1f, 0.55f, 0.1f, 1f) : new Color(0.4f, 0.4f, 0.4f, 1f)); ((Selectable)comp).colors = colors; } Text compInChildren = CrossCompat.GetCompInChildren<Text>(btnObj); if ((Object)(object)compInChildren != (Object)null) { compInChildren.text = (isEnabled ? "Auto ON" : "Auto"); } } } private static void RefreshAllButtons() { foreach (KeyValuePair<object, GameObject> autoReorderButton in _autoReorderButtons) { if (autoReorderButton.Key != null && !((Object)(object)autoReorderButton.Value == (Object)null)) { object obj = CrossCompat.GetMemberValue(autoReorderButton.Key, "Receipt") ?? CrossCompat.GetMemberValue(autoReorderButton.Key, "_receipt"); if (obj != null) { bool isEnabled = CrossCompat.GetMemberValue(obj, "DeliveryID") is string key && ActiveAutoReorders.ContainsKey(key); UpdateButtonVisual(autoReorderButton.Value, isEnabled); } } } } private static bool ToggleAutoReorder(object receipt, string deliveryId) { if (ActiveAutoReorders.ContainsKey(deliveryId)) { RemoveAutoReorder(deliveryId); return false; } string text = CrossCompat.GetMemberValue(receipt, "StoreName") as string; string text2 = null; foreach (KeyValuePair<string, AutoReorderEntry> activeAutoReorder in ActiveAutoReorders) { if (activeAutoReorder.Value.ShopName == text) { text2 = activeAutoReorder.Key; break; } } if (text2 != null) { RemoveAutoReorder(text2); } object playerSingletonInstance = GetPlayerSingletonInstance(_deliveryAppType); bool flag = playerSingletonInstance != null && ShopHasActiveDelivery(playerSingletonInstance, text); ActiveAutoReorders[deliveryId] = new AutoReorderEntry { Receipt = receipt, ShopName = text, DeliveryId = deliveryId, HadActiveDelivery = flag, ReorderPending = false }; Melon<Core>.Logger.Msg("Auto-reorder enabled for " + text + " (ID: " + deliveryId + ")"); if (!flag) { AutoReorderEntry autoReorderEntry = ActiveAutoReorders[deliveryId]; autoReorderEntry.ReorderPending = true; MelonCoroutines.Start(DoAutoReorder(autoReorderEntry)); } return true; } private static void RemoveAutoReorder(string deliveryId) { if (ActiveAutoReorders.TryGetValue(deliveryId, out var value)) { ActiveAutoReorders.Remove(deliveryId); Melon<Core>.Logger.Msg("Auto-reorder disabled for " + value.ShopName + " (ID: " + deliveryId + ")"); } } private static object GetNetworkSingletonInstance(Type gameType) { if (gameType == null) { return null; } Type type = CrossCompat.FindType("ScheduleOne.DevUtilities.NetworkSingleton`1"); if (type == null) { return null; } return CrossCompat.GetStaticMemberValue(type.MakeGenericType(gameType), "Instance"); } private static object GetPlayerSingletonInstance(Type gameType) { if (gameType == null) { return null; } Type type = CrossCompat.FindType("ScheduleOne.DevUtilities.PlayerSingleton`1"); if (type == null) { return null; } return CrossCompat.GetStaticMemberValue(type.MakeGenericType(gameType), "Instance"); } private static object GetSingletonInstance(Type gameType) { if (gameType == null) { return null; } Type type = CrossCompat.FindType("ScheduleOne.DevUtilities.Singleton`1"); if (type == null) { return null; } return CrossCompat.GetStaticMemberValue(type.MakeGenericType(gameType), "Instance"); } } public class AutoReorderEntry { public object Receipt; public string ShopName; public string DeliveryId; public bool HadActiveDelivery; public bool ReorderPending; } public static class CrossCompat { internal static MethodInfo _goGetComponentGeneric; private static MethodInfo _goGetComponentInChildrenGeneric; private const BindingFlags AllInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private const BindingFlags AllStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; public static Type FindType(string fullName) { string name = (fullName.StartsWith("ScheduleOne") ? ("Il2Cpp" + fullName) : fullName); Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type type = assembly.GetType(fullName) ?? assembly.GetType(name); if (type != null) { return type; } } return null; } public static object GetMemberValue(object obj, string name) { if (obj == null) { return null; } Type type = obj.GetType(); PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property.GetValue(obj); } FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(obj); } return null; } public static void SetMemberValue(object obj, string name, object value) { if (obj == null) { return; } Type type = obj.GetType(); PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { property.SetValue(obj, value); return; } FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(obj, value); } } public static object GetStaticMemberValue(Type type, string name) { if (type == null) { return null; } PropertyInfo property = type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property.GetValue(null); } FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(null); } return null; } public static object InvokeMethod(object obj, string methodName, params object[] args) { if (obj == null) { return null; } Type type = obj.GetType(); MethodInfo method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { return method.Invoke(obj, args); } return null; } public static object InvokeStaticMethod(Type type, string methodName, params object[] args) { if (type == null) { return null; } MethodInfo method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { return method.Invoke(null, args); } return null; } public static MethodInfo FindMethod(Type type, string name, int paramCount, BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) { if (type == null) { return null; } MethodInfo[] methods = type.GetMethods(flags); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == name && !methodInfo.IsGenericMethod && methodInfo.GetParameters().Length == paramCount) { return methodInfo; } } return null; } public static T GetComp<T>(GameObject go) where T : class { if ((Object)(object)go == (Object)null) { return null; } if (_goGetComponentGeneric == null) { MethodInfo[] methods = typeof(GameObject).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "GetComponent" && methodInfo.IsGenericMethod && methodInfo.GetParameters().Length == 0) { _goGetComponentGeneric = methodInfo; break; } } } if (_goGetComponentGeneric != null) { try { MethodInfo methodInfo2 = _goGetComponentGeneric.MakeGenericMethod(typeof(T)); return methodInfo2.Invoke(go, null) as T; } catch { } } return null; } public static T GetCompInChildren<T>(GameObject go) where T : class { if ((Object)(object)go == (Object)null) { return null; } if (_goGetComponentInChildrenGeneric == null) { MethodInfo[] methods = typeof(GameObject).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "GetComponentInChildren" && methodInfo.IsGenericMethod && methodInfo.GetParameters().Length == 0) { _goGetComponentInChildrenGeneric = methodInfo; break; } } } if (_goGetComponentInChildrenGeneric != null) { try { MethodInfo methodInfo2 = _goGetComponentInChildrenGeneric.MakeGenericMethod(typeof(T)); return methodInfo2.Invoke(go, null) as T; } catch { } } return null; } } }