Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ProperSave CommandQueueExtended v1.0.0
ProperSave_CommandQueueExtended.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Logging; using ProperSave; using RoR2; using SimpleJSON; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially or fully created with the assistance of Generative AI (e.g., Code Suggestions, Refactoring, Documentation Generation).")] [assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic")] [assembly: AssemblyVersion("0.0.0.0")] namespace ProperSaveCommandQueueExtended; internal static class Log { private static ManualLogSource _source; internal static void Init(ManualLogSource source) { _source = source; } internal static void Info(object message) { ManualLogSource source = _source; if (source != null) { source.LogInfo(message); } } internal static void Warning(object message) { ManualLogSource source = _source; if (source != null) { source.LogWarning(message); } } internal static void Error(object message) { ManualLogSource source = _source; if (source != null) { source.LogError(message); } } } [BepInPlugin("ProperSave_CommandQueueExtended", "ProperSave_CommandQueueExtended", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class ProperSaveCommandQueueExtendedPlugin : BaseUnityPlugin { public const string Guid = "ProperSave_CommandQueueExtended"; public const string Name = "ProperSave_CommandQueueExtended"; public const string Version = "1.0.0"; private const string ProperSaveGuid = "com.KingEnderBrine.ProperSave"; internal const string SaveKey = "local.ProperSave_CommandQueueExtended.queue"; private const string ChatPrefix = "<color=#9ad3ff>[CommandQueue]</color> "; private const float DelayAfterSpawn = 0.75f; private const float GiveUpAfter = 30f; private QueueBridge _bridge; private string _bridgeFailure; private bool _bridgeLogged; private List<QueuedPickup> _pending; private float _pendingSince; private float _applyAt = -1f; private QueueBridge Bridge { get { if (_bridge == null && _bridgeFailure == null) { _bridge = QueueBridge.Discover(out _bridgeFailure); if (!_bridgeLogged) { _bridgeLogged = true; if (_bridge != null) { Log.Info("Using " + _bridge.Description); } else { Log.Error("CommandQueueExtended's queue could not be found: " + _bridgeFailure); } } } return _bridge; } } private void Awake() { Log.Init(((BaseUnityPlugin)this).Logger); SaveFile.OnGatherSaveData += OnGatherSaveData; Loading.OnLoadingEnded += OnLoadingEnded; CharacterBody.onBodyStartGlobal += OnBodyStart; Run.onRunDestroyGlobal += OnRunDestroy; Log.Info("ProperSave_CommandQueueExtended 1.0.0 ready."); } private void OnDestroy() { SaveFile.OnGatherSaveData -= OnGatherSaveData; Loading.OnLoadingEnded -= OnLoadingEnded; CharacterBody.onBodyStartGlobal -= OnBodyStart; Run.onRunDestroyGlobal -= OnRunDestroy; } private void OnGatherSaveData(Dictionary<string, object> data) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) QueueBridge bridge = Bridge; if (bridge == null || bridge.Queues == null) { return; } List<QueuedPickup> queue = new List<QueuedPickup>(); foreach (KeyValuePair<PickupIndex, int> item in bridge.Snapshot()) { PickupDef pickupDef = PickupCatalog.GetPickupDef(item.Key); if (pickupDef != null && !string.IsNullOrEmpty(pickupDef.internalName)) { QueueCodec.Append(queue, pickupDef.internalName, item.Value); } } data["local.ProperSave_CommandQueueExtended.queue"] = QueueCodec.Encode(queue); Log.Info($"Saved {QueueCodec.Total(queue)} queued item(s) with the run."); } private void OnLoadingEnded(SaveFile save) { try { string text = ((save != null) ? save.TryGetModdedData<string>("local.ProperSave_CommandQueueExtended.queue") : null); if (string.IsNullOrEmpty(text)) { Log.Info("This save has no stored queue (it was made before this mod was installed)."); return; } _pending = QueueCodec.Decode(text); _pendingSince = Time.unscaledTime; _applyAt = -1f; Log.Info($"Loaded save holds {QueueCodec.Total(_pending)} queued item(s); restoring once you spawn."); } catch (Exception arg) { _pending = null; Log.Error($"Could not read the saved queue: {arg}"); } } private void OnBodyStart(CharacterBody body) { if (_pending != null && !((Object)(object)body == (Object)null) && !(_applyAt >= 0f)) { LocalUser firstLocalUser = LocalUserManager.GetFirstLocalUser(); CharacterMaster master = body.master; if ((Object)(object)master != (Object)null && firstLocalUser != null && ((Object)(object)firstLocalUser.cachedMaster == (Object)(object)master || ((Object)(object)master.playerCharacterMasterController != (Object)null && (Object)(object)master.playerCharacterMasterController.networkUser != (Object)null && master.playerCharacterMasterController.networkUser.localUser == firstLocalUser))) { _applyAt = Time.unscaledTime + 0.75f; } } } private void OnRunDestroy(Run run) { _pending = null; _applyAt = -1f; } private void Update() { if (_pending == null) { return; } if ((Object)(object)Run.instance == (Object)null) { if (Time.unscaledTime - _pendingSince > 30f) { _pending = null; } return; } bool num = _applyAt >= 0f && Time.unscaledTime >= _applyAt; bool flag = Time.unscaledTime - _pendingSince > 30f; if (num || flag) { Restore(); } } private void Restore() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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) List<QueuedPickup> pending = _pending; _pending = null; _applyAt = -1f; QueueBridge bridge = Bridge; if (bridge == null) { Chat.AddMessage("<color=#9ad3ff>[CommandQueue]</color> Could not restore the queue: " + _bridgeFailure + "."); return; } try { List<KeyValuePair<PickupIndex, int>> list = new List<KeyValuePair<PickupIndex, int>>(); int num = 0; foreach (QueuedPickup item in pending) { PickupIndex key = PickupCatalog.FindPickupIndex(item.Pickup); if (((PickupIndex)(ref key)).isValid) { list.Add(new KeyValuePair<PickupIndex, int>(key, item.Count)); } else { num += item.Count; } } int num2 = bridge.Replace(list, TierOf); string text = ((num > 0) ? $" ({num} skipped: item no longer installed)" : string.Empty); Chat.AddMessage("<color=#9ad3ff>[CommandQueue]</color> " + string.Format("Restored {0} queued item{1} from your save{2}.", num2, (num2 == 1) ? string.Empty : "s", text)); Log.Info($"Restored {num2} queued item(s){text}."); } catch (Exception arg) { Log.Error($"Restoring the queue failed: {arg}"); Chat.AddMessage("<color=#9ad3ff>[CommandQueue]</color> Restoring the queue failed; see the BepInEx log."); } } private static object TierOf(PickupIndex pickup) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) PickupDef pickupDef = PickupCatalog.GetPickupDef(pickup); if (pickupDef == null) { return null; } return pickupDef.itemTier; } } internal sealed class QueueBridge { private const BindingFlags Static = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private const BindingFlags Instance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private FieldInfo _queuesField; private PropertyInfo _queuesProperty; private Type _entryType; private MemberInfo _entryPickup; private MemberInfo _entryCount; private MethodInfo _enqueue; private MethodInfo _remove; private bool _removeTakesCount; internal string Description { get; private set; } internal IDictionary Queues => ((_queuesField != null) ? _queuesField.GetValue(null) : _queuesProperty.GetValue(null)) as IDictionary; private QueueBridge() { } internal static QueueBridge Discover(out string failure) { failure = null; List<Assembly> list = CandidateAssemblies(); if (list.Count == 0) { failure = "no CommandQueue plugin is loaded (is CommandQueueExtended installed and enabled?)"; return null; } foreach (Assembly item in list) { foreach (Type item2 in SafeTypes(item)) { QueueBridge queueBridge = TryType(item2); if (queueBridge != null) { return queueBridge; } } } failure = "found " + string.Join(", ", list.Select((Assembly a) => a.GetName().Name)) + " but no queue storage inside it (a dictionary of ItemTier to queue entries)"; return null; } private static List<Assembly> CandidateAssemblies() { List<Assembly> list = new List<Assembly>(); Assembly assembly = typeof(QueueBridge).Assembly; try { foreach (PluginInfo value in Chainloader.PluginInfos.Values) { if ((value.Metadata.GUID + " " + value.Metadata.Name).ToLowerInvariant().Contains("commandqueue") && !((Object)(object)value.Instance == (Object)null)) { Assembly assembly2 = ((object)value.Instance).GetType().Assembly; if (assembly2 != assembly && !list.Contains(assembly2)) { list.Add(assembly2); } } } } catch (Exception) { } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly3 in assemblies) { string text = assembly3.GetName().Name ?? string.Empty; if (assembly3 != assembly && text.IndexOf("CommandQueue", StringComparison.OrdinalIgnoreCase) >= 0 && !list.Contains(assembly3)) { list.Add(assembly3); } } return list.OrderByDescending((Assembly a) => (a.GetName().Name ?? string.Empty).IndexOf("Extended", StringComparison.OrdinalIgnoreCase) >= 0).ToList(); } private static IEnumerable<Type> SafeTypes(Assembly assembly) { try { return assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { return ex.Types.Where((Type t) => t != null); } catch (Exception) { return Enumerable.Empty<Type>(); } } private static QueueBridge TryType(Type type) { foreach (MemberInfo item in from m in type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Cast<MemberInfo>().Concat(from p in type.GetProperties(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where p.GetIndexParameters().Length == 0 select p) orderby string.Equals(m.Name, "mainQueues", StringComparison.OrdinalIgnoreCase) descending select m) { if (!TryQueueShape((item is FieldInfo fieldInfo) ? fieldInfo.FieldType : ((PropertyInfo)item).PropertyType, out var entryType, out var pickup, out var count)) { continue; } QueueBridge queueBridge = new QueueBridge { _queuesField = (item as FieldInfo), _queuesProperty = (item as PropertyInfo), _entryType = entryType, _entryPickup = pickup, _entryCount = count }; queueBridge._enqueue = (from m in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where m.Name.IndexOf("Enqueue", StringComparison.OrdinalIgnoreCase) >= 0 && Matches(m, typeof(PickupIndex)) orderby (!(m.Name == "Enqueue")) ? 1 : 0, m.GetParameters().Length select m).FirstOrDefault(); queueBridge._remove = (from m in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where m.Name == "Remove" && Matches(m, typeof(ItemTier), typeof(int), typeof(int)) orderby m.GetParameters().Length select m).FirstOrDefault(); queueBridge._removeTakesCount = queueBridge._remove != null; if (queueBridge._remove == null) { queueBridge._remove = (from m in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where m.Name == "Remove" && Matches(m, typeof(ItemTier), typeof(int)) orderby m.GetParameters().Length select m).FirstOrDefault(); } queueBridge.Description = type.FullName + "." + item.Name + " in " + type.Assembly.GetName().Name + " (entries " + entryType.Name + ": " + pickup.Name + ", " + count.Name + "; enqueue: " + ((queueBridge._enqueue != null) ? queueBridge._enqueue.Name : "direct add") + "; remove: " + ((queueBridge._remove != null) ? queueBridge._remove.Name : "direct clear") + ")"; return queueBridge; } return null; } private static bool TryQueueShape(Type type, out Type entryType, out MemberInfo pickup, out MemberInfo count) { entryType = null; pickup = null; count = null; if (type == null || !typeof(IDictionary).IsAssignableFrom(type) || !type.IsGenericType) { return false; } Type[] genericArguments = type.GetGenericArguments(); if (genericArguments.Length != 2 || genericArguments[0] != typeof(ItemTier) || !typeof(IList).IsAssignableFrom(genericArguments[1]) || !genericArguments[1].IsGenericType) { return false; } entryType = genericArguments[1].GetGenericArguments().FirstOrDefault(); if (entryType == null) { return false; } List<MemberInfo> source = entryType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Cast<MemberInfo>().Concat(from p in entryType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where p.GetIndexParameters().Length == 0 && p.CanRead select p) .ToList(); pickup = source.FirstOrDefault((MemberInfo m) => TypeOf(m) == typeof(PickupIndex) && !m.Name.Contains("k__BackingField")); count = (from m in source where TypeOf(m) == typeof(int) && !m.Name.Contains("k__BackingField") orderby string.Equals(m.Name, "count", StringComparison.OrdinalIgnoreCase) descending select m).FirstOrDefault(); if (pickup != null) { return count != null; } return false; } private static Type TypeOf(MemberInfo member) { if (!(member is FieldInfo fieldInfo)) { return (member as PropertyInfo)?.PropertyType; } return fieldInfo.FieldType; } private static bool Matches(MethodInfo method, params Type[] leading) { ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length < leading.Length) { return false; } for (int i = 0; i < leading.Length; i++) { if (parameters[i].ParameterType != leading[i]) { return false; } } return parameters.Skip(leading.Length).All((ParameterInfo p) => p.IsOptional); } private static object[] Arguments(MethodInfo method, params object[] leading) { ParameterInfo[] parameters = method.GetParameters(); object[] array = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { array[i] = ((i < leading.Length) ? leading[i] : (parameters[i].HasDefaultValue ? parameters[i].DefaultValue : Type.Missing)); } return array; } internal List<KeyValuePair<PickupIndex, int>> Snapshot() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) List<KeyValuePair<PickupIndex, int>> list = new List<KeyValuePair<PickupIndex, int>>(); IDictionary queues = Queues; if (queues == null) { return list; } foreach (DictionaryEntry item in queues) { if (!(item.Value is IList list2)) { continue; } foreach (object item2 in list2) { if (item2 != null && Read(item2, _entryPickup) is PickupIndex key && Read(item2, _entryCount) is int num && num > 0) { list.Add(new KeyValuePair<PickupIndex, int>(key, num)); } } } return list; } internal int Replace(IEnumerable<KeyValuePair<PickupIndex, int>> pickups, Func<PickupIndex, object> tierOf) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0101: 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) //IL_010c: Unknown result type (might be due to invalid IL or missing references) IDictionary queues = Queues; if (queues == null) { throw new InvalidOperationException("CommandQueueExtended's queues are not set up yet"); } Clear(queues); int num = 0; foreach (KeyValuePair<PickupIndex, int> pickup in pickups) { if (_enqueue != null) { for (int i = 0; i < pickup.Value; i++) { _enqueue.Invoke(null, Arguments(_enqueue, pickup.Key)); num++; } continue; } object obj = tierOf(pickup.Key); if (obj != null && queues.Contains(obj) && queues[obj] is IList list) { object obj2 = ((list.Count > 0) ? list[list.Count - 1] : null); if (obj2 != null && Read(obj2, _entryPickup) is PickupIndex val && ((PickupIndex)(ref val)).Equals(pickup.Key) && Read(obj2, _entryCount) is int num2 && !_entryType.IsValueType) { Write(obj2, _entryCount, num2 + pickup.Value); } else { object obj3 = Activator.CreateInstance(_entryType); Write(obj3, _entryPickup, pickup.Key); Write(obj3, _entryCount, pickup.Value); list.Add(obj3); } num += pickup.Value; } } return num; } private void Clear(IDictionary queues) { object[] array = queues.Keys.Cast<object>().ToArray(); foreach (object obj in array) { if (!(queues[obj] is IList list)) { continue; } if (_remove == null) { list.Clear(); continue; } int num = 10000; while (list.Count > 0 && num-- > 0) { int count = list.Count; int num2 = ((!(Read(list[0], _entryCount) is int num3)) ? 1 : num3); _remove.Invoke(null, _removeTakesCount ? Arguments(_remove, obj, 0, num2) : Arguments(_remove, obj, 0)); int num4 = ((list.Count > 0 && Read(list[0], _entryCount) is int num5) ? num5 : 0); if (list.Count == count && num4 == num2) { list.Clear(); break; } } } } private static object Read(object instance, MemberInfo member) { if (!(member is FieldInfo fieldInfo)) { return ((PropertyInfo)member).GetValue(instance); } return fieldInfo.GetValue(instance); } private static void Write(object instance, MemberInfo member, object value) { if (member is FieldInfo fieldInfo) { fieldInfo.SetValue(instance, value); } else if (member is PropertyInfo { CanWrite: not false } propertyInfo) { propertyInfo.SetValue(instance, value); } } } public sealed class QueuedPickup { public string Pickup { get; } public int Count { get; set; } public QueuedPickup(string pickup, int count) { Pickup = pickup; Count = count; } public override string ToString() { return Pickup + "*" + Count; } } public static class QueueCodec { public const int Version = 1; public const int MaxCount = 999; public static void Append(List<QueuedPickup> queue, string pickup, int count) { if (!string.IsNullOrEmpty(pickup) && count > 0) { if (queue.Count > 0 && string.Equals(queue[queue.Count - 1].Pickup, pickup, StringComparison.Ordinal)) { queue[queue.Count - 1].Count = Math.Min(999, queue[queue.Count - 1].Count + count); } else { queue.Add(new QueuedPickup(pickup, Math.Min(999, count))); } } } public static string Encode(List<QueuedPickup> queue) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown JSONArray val = new JSONArray(); foreach (QueuedPickup item in queue) { JSONObject val2 = new JSONObject(); ((JSONNode)val2)["pickup"] = JSONNode.op_Implicit(item.Pickup); ((JSONNode)val2)["count"] = JSONNode.op_Implicit(item.Count); ((JSONNode)val).Add((JSONNode)(object)val2); } return ((object)new JSONObject { ["version"] = JSONNode.op_Implicit(1), ["queue"] = (JSONNode)(object)val }).ToString(); } public static List<QueuedPickup> Decode(string text) { List<QueuedPickup> list = new List<QueuedPickup>(); if (string.IsNullOrEmpty(text)) { return list; } JSONNode val = JSON.Parse(text); JSONNode val2 = ((val != (object)null && val.IsObject) ? val["queue"] : null); if (val2 == (object)null || !val2.IsArray) { throw new FormatException("saved queue has no queue array"); } foreach (JSONNode child in val2.Children) { if (!(child == (object)null) && child.IsObject) { JSONNode val3 = child["pickup"]; JSONNode val4 = child["count"]; if (val3 != (object)null && val3.IsString) { Append(list, val3.Value, (val4 != (object)null) ? val4.AsInt : 0); } } } return list; } public static int Total(List<QueuedPickup> queue) { int num = 0; foreach (QueuedPickup item in queue) { num += item.Count; } return num; } }