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 REPOForge v1.5.9
REPOForge.dll
Decompiled 12 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using REPOForge.Compat; using REPOForge.Network; using REPOForge.Shop; using UnityEngine; using UnityEngine.SceneManagement; [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("REPOForge")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("REPOForge")] [assembly: AssemblyTitle("REPOForge")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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] [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 REPOForge { public static class Forge { public static class Bus { public static void Subscribe<T>(Action<T> handler) { PluginBus.Subscribe(handler); } public static void Unsubscribe<T>(Action<T> handler) { PluginBus.Unsubscribe(handler); } public static void Publish<T>(T evt) { PluginBus.Publish(evt); } } public static class Library { public static int Count => ((IReadOnlyCollection<ModEntry>)ModLibrary.Entries).Count; public static bool ShopSkip(string itemName) { return ModLibrary.ShopSkip(itemName); } public static bool SelfSpawns(string itemName) { return ModLibrary.SelfSpawns(itemName); } } public static class Shop { public static string? LastManifestHash => ShopCoordinator.LastHash; public static void Register(ShopIntent intent) { ShopCoordinator.Register(intent); } public static void Unregister(string itemName) { ShopCoordinator.Unregister(itemName); } } public static class Compat { public static string Fingerprint => DiscrepancyGuard.LocalFingerprint; public static void Declare(CompatManifest manifest) { DiscrepancyGuard.Declare(manifest); } } public static class Network { public static bool RegisterPrefab(string path, GameObject prefab) { return PrefabRegistry.Register(path, prefab); } public static GameObject? SpawnItem(object item, Vector3 position, Quaternion rotation) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) return PrefabRegistry.SpawnItem(item, position, rotation); } } public static class Harmony { public static void Claim(string method, HarmonyLane lane, string owner) { HarmonyArbiter.Claim(method, lane, owner); } } } public static class PluginBus { private static readonly Dictionary<Type, List<Delegate>> _subs = new Dictionary<Type, List<Delegate>>(); public static void Subscribe<T>(Action<T> handler) { Type typeFromHandle = typeof(T); List<Delegate> value = null; if (!_subs.TryGetValue(typeFromHandle, out value)) { value = new List<Delegate>(); _subs[typeFromHandle] = value; } value.Add(handler); } public static void Unsubscribe<T>(Action<T> handler) { List<Delegate> value = null; if (_subs.TryGetValue(typeof(T), out value)) { value.Remove(handler); } } public static void Publish<T>(T evt) { List<Delegate> value = null; if (evt == null || !_subs.TryGetValue(typeof(T), out value)) { return; } Delegate[] array = value.ToArray(); foreach (Delegate obj in array) { try { ((Action<T>)obj)(evt); } catch (Exception arg) { Plugin.Log.LogError((object)$"Bus handler for {typeof(T).Name} threw: {arg}"); } } } } public sealed class ShopReadyEvent { public string ManifestHash { get; set; } = ""; public int Count { get; set; } public int Seed { get; set; } } public sealed class DriftReportEvent { public bool HardMismatch { get; set; } public string HostFingerprint { get; set; } = ""; public string ClientFingerprint { get; set; } = ""; public List<string> Findings { get; set; } = new List<string>(); } public enum HarmonyLane { Core = 0, Forge = 100, Content = 400, Override = 800 } public enum ItemVolumeKind { Small, Medium, Large, Large_wide, Power_crystal, Large_high, Rubber_duck, Health_pack, Large_plus } public enum ShopCategoryKind { Items, Consumables, Upgrades, Health, Secret } public sealed class ShopIntent { public string ItemName { get; set; } = ""; public string PrefabPath { get; set; } = ""; public float Weight { get; set; } = 10f; public int MaxInShop { get; set; } = 1; public ItemVolumeKind Volume { get; set; } = ItemVolumeKind.Medium; public ShopCategoryKind Category { get; set; } public int PriceMin { get; set; } = 4000; public int PriceMax { get; set; } = 8000; public object? Item { get; set; } } public sealed class CompatManifest { public string Guid { get; set; } = ""; public string Version { get; set; } = ""; public string DisplayName { get; set; } = ""; public bool HostRequired { get; set; } = true; public List<string> PrefabPaths { get; } = new List<string>(); public List<string> PatchClaims { get; } = new List<string>(); } internal static class GameAccess { private const BindingFlags Flags = BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static readonly Dictionary<(Type, string), MemberInfo?> Cache = new Dictionary<(Type, string), MemberInfo>(); private static readonly HashSet<string> Dumped = new HashSet<string>(); private static bool _roleLogged; internal static Type? Type(string name) { return AccessTools.TypeByName(name) ?? AccessTools.TypeByName(name + ", Assembly-CSharp"); } internal static object? Instance(string typeName) { Type type = Type(typeName); if (type == null) { return null; } return GetStatic(type, "instance") ?? GetStatic(type, "Instance") ?? GetStatic(type, "Singleton"); } internal static bool IsMaster() { bool? flag = PhotonIsMaster(); if (flag.HasValue) { LogRoleOnce(flag.Value); return flag.Value; } try { Type type = Type("SemiFunc"); MethodInfo methodInfo = ((type != null) ? type.GetMethod("IsMasterClientOrSingleplayer", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : null) ?? ((type != null) ? type.GetMethod("IsMasterClient", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : null); if (methodInfo != null) { bool num = (bool)methodInfo.Invoke(null, null); LogRoleOnce(num); return num; } } catch (Exception) { } return !IsMultiplayer(); } private static bool? PhotonIsMaster() { try { Type type = Type("Photon.Pun.PhotonNetwork") ?? Type("PhotonNetwork"); if (type == null) { return null; } bool? flag = StaticBool(type, "IsMasterClient") ?? StaticBool(type, "isMasterClient"); bool? flag2 = StaticBool(type, "InRoom") ?? StaticBool(type, "inRoom"); if (flag2 == true && flag.HasValue) { return flag.Value; } if ((StaticBool(type, "OfflineMode") ?? StaticBool(type, "offlineMode")) == true) { return true; } return (flag2 == false) | flag; } catch (Exception) { return null; } } private static void LogRoleOnce(bool master) { if (_roleLogged) { return; } _roleLogged = true; try { Plugin.Log.LogInfo((object)(master ? "Co-op: this machine is the host. Shop items spawn here; others receive them." : "Co-op: this machine is a client. Shop spawn is skipped here so it does not desync.")); } catch (Exception) { } } internal static void ResetRoleLog() { _roleLogged = false; } internal static bool IsMultiplayer() { try { Type type = Type("Photon.Pun.PhotonNetwork") ?? Type("PhotonNetwork"); if (type != null) { bool? flag = StaticBool(type, "InRoom") ?? StaticBool(type, "inRoom"); bool? flag2 = StaticBool(type, "OfflineMode") ?? StaticBool(type, "offlineMode"); if (flag == true && flag2 != true) { return true; } object obj = GetStatic(type, "CountOfPlayersInRooms") ?? GetStatic(type, "CountOfPlayers"); if (obj is int && (int)obj > 1) { return true; } } } catch (Exception) { } try { Type type2 = Type("SemiFunc") ?? Type("GameManager"); MethodInfo methodInfo = ((type2 != null) ? type2.GetMethod("IsMultiplayer", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : null) ?? ((type2 != null) ? type2.GetMethod("Multiplayer", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : null); if (methodInfo != null) { return (bool)methodInfo.Invoke(null, null); } } catch (Exception) { } return false; } private static bool? StaticBool(Type t, string name) { object obj = GetStatic(t, name); if (obj is bool) { return (bool)obj; } return null; } internal static object? PhotonLocalPlayer() { Type type = Type("Photon.Pun.PhotonNetwork") ?? Type("PhotonNetwork"); object obj; if (!(type == null)) { obj = GetStatic(type, "LocalPlayer"); if (obj == null) { return GetStatic(type, "localPlayer"); } } else { obj = null; } return obj; } internal static IEnumerable<object> PhotonPlayers() { Type type = Type("Photon.Pun.PhotonNetwork") ?? Type("PhotonNetwork"); object obj = ((type == null) ? null : (GetStatic(type, "PlayerList") ?? GetStatic(type, "playerList"))); IEnumerable enumerable = (IEnumerable)((obj is IEnumerable) ? obj : null); if (enumerable == null) { yield break; } IEnumerator enumerator = enumerable.GetEnumerator(); try { while (enumerator.MoveNext()) { object current = enumerator.Current; if (current != null) { yield return current; } } } finally { ((IDisposable)((enumerator is IDisposable) ? enumerator : null))?.Dispose(); } } internal static bool IsNotMaster() { return !IsMaster(); } internal static bool PhotonInRoom() { Type type = Type("Photon.Pun.PhotonNetwork") ?? Type("PhotonNetwork"); if (type == null) { return false; } try { if (!((GetStatic(type, "InRoom") ?? GetStatic(type, "inRoom")) is bool result)) { object obj = GetStatic(type, "IsConnectedAndReady"); bool flag = false; int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)(num & (flag ? 1 : 0)) != 0; } return result; } catch (Exception) { return false; } } internal static IList? ListField(object obj, string name) { object obj2 = Read(obj, name); return (IList)((obj2 is IList) ? obj2 : null); } internal static IEnumerable<object> AllStatItems() { object stats = Instance("StatsManager"); if (stats == null) { yield break; } DumpType(stats.GetType()); Type itemType = Type("Item"); HashSet<int> seen = new HashSet<int>(); FieldInfo[] fields = stats.GetType().GetFields(BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { object value; try { value = fieldInfo.GetValue(stats); } catch (Exception) { continue; } IDictionary dictionary = (IDictionary)((value is IDictionary) ? value : null); if (dictionary != null) { IDictionaryEnumerator enumerator = dictionary.GetEnumerator(); try { while (enumerator.MoveNext()) { object value2 = ((DictionaryEntry)enumerator.Current).Value; if (value2 != null && (!(itemType != null) || itemType.IsInstanceOfType(value2)) && seen.Add(value2.GetHashCode())) { yield return value2; } } } finally { ((IDisposable)((enumerator is IDisposable) ? enumerator : null))?.Dispose(); } continue; } IList list = (IList)((value is IList) ? value : null); if (list == null) { continue; } IEnumerator enumerator2 = list.GetEnumerator(); try { while (enumerator2.MoveNext()) { object current = enumerator2.Current; if (current != null && (!(itemType != null) || itemType.IsInstanceOfType(current)) && seen.Add(current.GetHashCode())) { yield return current; } } } finally { ((IDisposable)((enumerator2 is IDisposable) ? enumerator2 : null))?.Dispose(); } } } internal static GameObject? PrefabObject(object? item) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown if (item == null) { return null; } object member = GetMember(item, "prefab"); return (GameObject)(((member is GameObject) ? member : null) ?? null); } internal static void SetEnum(object obj, string name, string value) { if (obj == null || string.IsNullOrEmpty(value)) { return; } FieldInfo field = obj.GetType().GetField(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field == null || !field.FieldType.IsEnum) { return; } try { field.SetValue(obj, Enum.Parse(field.FieldType, value, ignoreCase: true)); } catch (Exception) { } } internal static IList? NewItemList(object item) { Type type = Type("Item"); if (type == null || item == null) { return null; } try { IList obj = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(type)); obj.Add(item); return obj; } catch (Exception) { return null; } } internal static void SetField(object obj, string name, object? value) { FieldInfo field = obj.GetType().GetField(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(obj, value); } } internal static void DumpType(Type t) { DumpOnce(t); } internal static int ReadInt(object obj, string name, int fallback = 0) { object obj2 = Read(obj, name); if (obj2 is int) { return (int)obj2; } if (obj2 is float) { return (int)(float)obj2; } return fallback; } internal static void RaiseInt(object obj, string name, int atLeast) { if (obj != null && atLeast > 0) { int num = ReadInt(obj, name); if (atLeast > num) { SetField(obj, name, atLeast); } } } internal static object[] FindAll(string typeName, bool includeInactive = false) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) Type type = Type(typeName); if (type == null) { return Array.Empty<object>(); } try { MethodInfo methodInfo = AccessTools.Method(typeof(Resources), "FindObjectsOfTypeAll", new Type[1] { typeof(Type) }, (Type[])null); if (methodInfo != null && methodInfo.Invoke(null, new object[1] { type }) is Array { Length: >0 } array) { List<object> list = new List<object>(array.Length); for (int i = 0; i < array.Length; i++) { object value = array.GetValue(i); if (value == null || (Object)((value is Object) ? value : null) == (Object)null) { continue; } try { Component val = (Component)((value is Component) ? value : null); if ((Object)(object)val != (Object)null) { if ((int)((Object)val).hideFlags != 0 || ((Object)(object)val.transform != (Object)null && val.transform.position.y < -500f)) { continue; } Scene scene = val.gameObject.scene; if (!((Scene)(ref scene)).IsValid() || (((Scene)(ref scene)).name ?? "").IndexOf("DontDestroyOnLoad", StringComparison.OrdinalIgnoreCase) >= 0) { continue; } } } catch (Exception) { } list.Add(value); } if (list.Count > 0) { return list.ToArray(); } } } catch (Exception) { } try { object[] array2 = InvokeFind(typeof(Object), "FindObjectsOfType", type, includeInactive: true); if (array2 != null && array2.Length != 0) { return array2; } return InvokeFind(typeof(Object), "FindObjectsOfType", type, includeInactive); } catch (Exception) { return Array.Empty<object>(); } } private static object[] InvokeFind(Type host, string method, Type target, bool includeInactive = false) { MethodInfo[] methods = host.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name != method) { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); object[] array = null; if (parameters.Length == 2 && parameters[0].ParameterType == typeof(Type) && parameters[1].ParameterType == typeof(bool)) { array = new object[2] { target, includeInactive }; } else if (!includeInactive && parameters.Length == 1 && parameters[0].ParameterType == typeof(Type)) { array = new object[1] { target }; } if (array != null) { object obj = methodInfo.Invoke(null, array); Array array2 = (Array)((obj is Array) ? obj : null); if (array2 != null && array2.Length > 0) { object[] array3 = new object[array2.Length]; array2.CopyTo(array3, 0); return array3; } } } return Array.Empty<object>(); } internal static object? GetMember(object obj, string name) { return Read(obj, name); } internal static string ItemName(object item) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) object obj = Read(item, "itemName"); object obj2 = ((obj is string) ? obj : null); if (obj2 == null) { object obj3 = Read(item, "name"); obj2 = ((obj3 is string) ? obj3 : null); if (obj2 == null) { object obj4 = ((item is Object) ? item : null); obj2 = ((obj4 != null) ? ((Object)obj4).name : null) ?? item.ToString(); } } return (string)obj2; } internal static string PrefabPath(object item) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown DumpOnce(item.GetType()); object obj = Read(item, "prefab"); if (obj != null) { DumpOnce(obj.GetType()); object obj2 = Read(obj, "resourcePath"); string text = (string)(((obj2 is string) ? obj2 : null) ?? null); if (!string.IsNullOrEmpty(text)) { return text; } GameObject val = (GameObject)(((obj is GameObject) ? obj : null) ?? null); if ((Object)val != (Object)null) { return "Items/" + ((Object)val).name; } } return "Items/" + ItemName(item); } internal static string ItemVolume(object item) { return (Read(item, "itemVolume") ?? Read(item, "volume") ?? Read(item, "itemType"))?.ToString() ?? "Medium"; } internal static int MaxInShop(object item) { if ((Read(item, "maxAmountInShop") ?? Read(item, "maxAmount") ?? Read(item, "maxPurchaseAmount")) is int val) { return Math.Max(1, val); } return 1; } internal static int AveragePrice(object item) { object obj = Read(item, "value") ?? Read(item, "valuePreset") ?? Read(item, "ValuePreset"); if (obj != null) { DumpOnce(obj.GetType()); } object obj2 = obj ?? item; int num = CoerceInt(Read(obj2, "valueMin") ?? Read(obj2, "min") ?? Read(obj2, "from") ?? Read(obj2, "x")); int num2 = CoerceInt(Read(obj2, "valueMax") ?? Read(obj2, "max") ?? Read(obj2, "to") ?? Read(obj2, "y")); if (num <= 0 && num2 <= 0) { num = CoerceInt(Read(obj2, "value")); } if (num <= 0) { num = 8000; } if (num2 <= 0) { num2 = num; } return Math.Max(1, (num + num2) / 2); } private static int CoerceInt(object? v) { if (v is int) { return (int)v; } if (v is float) { return (int)(float)v; } if (v is double) { return (int)(double)v; } if (v is short) { return (short)v; } return 0; } private static object? GetStatic(Type t, string name) { MemberInfo memberInfo = Resolve(t, name); try { FieldInfo fieldInfo = (FieldInfo)((memberInfo is FieldInfo) ? memberInfo : null); if (fieldInfo != null) { return fieldInfo.GetValue(null); } PropertyInfo propertyInfo = (PropertyInfo)((memberInfo is PropertyInfo) ? memberInfo : null); if (propertyInfo != null && propertyInfo.CanRead) { return propertyInfo.GetValue(null); } } catch (Exception) { } return null; } private static object? Read(object obj, string name) { if (obj == null) { return null; } MemberInfo memberInfo = Resolve(obj.GetType(), name); try { FieldInfo fieldInfo = (FieldInfo)((memberInfo is FieldInfo) ? memberInfo : null); if (fieldInfo != null) { return fieldInfo.GetValue(obj); } PropertyInfo propertyInfo = (PropertyInfo)((memberInfo is PropertyInfo) ? memberInfo : null); if (propertyInfo != null && propertyInfo.CanRead) { return propertyInfo.GetValue(obj); } } catch (Exception) { } return null; } private static MemberInfo? Resolve(Type t, string name) { (Type, string) tuple = default((Type, string)); tuple = (t, name); MemberInfo value = null; if (Cache.TryGetValue(tuple, out value)) { return value; } MemberInfo memberInfo = t.GetField(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (memberInfo == null) { memberInfo = t.GetProperty(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } Cache[tuple] = memberInfo; return memberInfo; } private static void DumpOnce(Type t) { if (t == null || !Dumped.Add(t.FullName ?? t.Name)) { return; } try { List<string> list = new List<string>(); FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { list.Add(fieldInfo.FieldType.Name + " " + fieldInfo.Name); } PropertyInfo[] properties = t.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.GetIndexParameters().Length == 0) { list.Add(propertyInfo.PropertyType.Name + " " + propertyInfo.Name + "{get}"); } } Plugin.Log.LogInfo((object)("[reflect] " + t.Name + ": " + ((list.Count == 0) ? "(no instance members)" : string.Join(", ", list)))); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[reflect] " + t.Name + " dump failed: " + ex.Message)); } } } [BepInPlugin("com.repoforge.core", "REPOForge", "1.5.9")] public sealed class Plugin : BaseUnityPlugin { [Serializable] [CompilerGenerated] private sealed class __c { public static readonly __c __9 = new __c(); public static Action __9__28_0; public static Action __9__28_1; public static Action __9__28_2; public static Action __9__28_3; internal void Awake_b__28_0() { HarmonyArbiter.Install(Harmony); } internal void Awake_b__28_1() { PrefabRegistry.Install(Harmony); } internal void Awake_b__28_2() { ShopCoordinator.Install(Harmony); } internal void Awake_b__28_3() { DiscrepancyGuard.Install(Harmony); } } internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> Handshake; internal static ConfigEntry<bool> KickOnHardMismatch; internal static ConfigEntry<bool> PlaceholderMissingPrefabs; internal static ConfigEntry<bool> VerboseLogging; internal static ConfigEntry<bool> ShopPipeline; internal static ConfigEntry<bool> PriceWeight; internal static ConfigEntry<int> ExtraShelves; internal static ConfigEntry<int> ItemSpawnTargetAmount; internal static ConfigEntry<bool> FillEmptyVolumes; internal static ConfigEntry<bool> DiversifyPool; internal static ConfigEntry<bool> PreferModItems; internal static ConfigEntry<int> MaxCopiesPerItem; internal static ConfigEntry<bool> RemapSmallToMedium; internal static ConfigEntry<bool> AdoptUnlistedMods; internal static ConfigEntry<bool> RefreshLibrary; private static bool _deferredBootDone; private static bool _deferredBootScheduled; private static bool _deferredBootHooked; private static bool _deferredUpdateLogged; private static long _deferredBootStartTicks; private const double DeferredMinWaitSec = 1.0; private const double DeferredMaxWaitSec = 12.0; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static Harmony Harmony { get; private set; } private void Awake() { //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Expected O, but got Unknown //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Expected O, but got Unknown //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Expected O, but got Unknown //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"REPOForge 1.5.9 waking."); Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch."); Handshake = ((BaseUnityPlugin)this).Config.Bind<bool>("Compat", "Handshake", false, "In co-op, compare loaded mods with other players and log a mismatch. Default OFF — avoids Photon hang when not connected."); KickOnHardMismatch = ((BaseUnityPlugin)this).Config.Bind<bool>("Compat", "KickOnHardMismatch", false, "Kick clients with hard mismatches. Default is warn-only."); PlaceholderMissingPrefabs = ((BaseUnityPlugin)this).Config.Bind<bool>("Compat", "PlaceholderMissingPrefabs", true, "Spawn a placeholder instead of crashing when a prefab is missing on a client."); VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Compat", "VerboseLogging", false, "Extra log lines for patches and shop rolls."); ShopPipeline = ((BaseUnityPlugin)this).Config.Bind<bool>("Shop", "Pipeline", true, "Intercept shop item lists and spawn through the host manifest."); PriceWeight = ((BaseUnityPlugin)this).Config.Bind<bool>("Shop", "UseShopPriceForItemSelection", false, "Reorder the pool so cheaper items spawn more often. Off by default — vanilla/MSI already weight by duplicate entries."); ExtraShelves = ((BaseUnityPlugin)this).Config.Bind<int>("Shop", "ExtraShelves", 0, new ConfigDescription("Unused. Kept so old configs still load.", (AcceptableValueBase)new AcceptableValueRange<int>(0, 6), Array.Empty<object>())); ItemSpawnTargetAmount = ((BaseUnityPlugin)this).Config.Bind<int>("Shop", "ItemSpawnTargetAmount", 0, new ConfigDescription("Hard cap. Ignored while FillEmptyVolumes is on. 0 = do not override.", (AcceptableValueBase)new AcceptableValueRange<int>(0, 256), Array.Empty<object>())); FillEmptyVolumes = ((BaseUnityPlugin)this).Config.Bind<bool>("Shop", "FillEmptyVolumes", true, "Raise spawn budgets to match empty shelf slots. Never lowers More Shop Items."); DiversifyPool = ((BaseUnityPlugin)this).Config.Bind<bool>("Shop", "DiversifyPool", true, "Round-robin unique items so one type cannot fill every slot (e.g. only medium health packs)."); PreferModItems = ((BaseUnityPlugin)this).Config.Bind<bool>("Shop", "PreferModItems", true, "Give non-vanilla (REPOLib / other mods) items a guaranteed shelf slot before vanilla repeats."); MaxCopiesPerItem = ((BaseUnityPlugin)this).Config.Bind<int>("Shop", "MaxCopiesPerItem", 2, new ConfigDescription("Max copies of one prefab on the shelves. 1 = all unique, 2 = at most a pair.", (AcceptableValueBase)new AcceptableValueRange<int>(1, 16), Array.Empty<object>())); RemapSmallToMedium = ((BaseUnityPlugin)this).Config.Bind<bool>("Shop", "RemapSmallItemsOntoMediumVolumes", true, "If no unused item matches the shelf volume, place a different unused item instead of a duplicate."); AdoptUnlistedMods = ((BaseUnityPlugin)this).Config.Bind<bool>("Shop", "AdoptUnlistedMods", false, "Off: only items already in the shop roll (REPOLib RegisterItem / vanilla GetAll). On: also pull extra StatsManager items that look like shop gear (never valuables or secret attic loot)."); RefreshLibrary = ((BaseUnityPlugin)this).Config.Bind<bool>("Compat", "RefreshLibrary", false, "On Start, scan BepInEx/plugins manifests and optionally fetch the Thunderstore R.E.P.O. package index. Default OFF — full catalog (+31585) froze lobby join."); if (AdoptUnlistedMods.Value) { Log.LogWarning((object)"Shop.AdoptUnlistedMods was on (1.2.0 stuffed level loot into the shop). Turning off. Future shop mods still appear via REPOLib RegisterItem."); AdoptUnlistedMods.Value = false; } if (FillEmptyVolumes.Value && ItemSpawnTargetAmount.Value > 0 && ItemSpawnTargetAmount.Value <= 28) { Log.LogWarning((object)$"Shop.ItemSpawnTargetAmount was {ItemSpawnTargetAmount.Value} (old cap). Ignored — FillEmptyVolumes fills every shelf. Set FillEmptyVolumes=false if you want a hard cap."); ItemSpawnTargetAmount.Value = 0; } if (MaxCopiesPerItem.Value >= 6) { Log.LogWarning((object)$"Shop.MaxCopiesPerItem was {MaxCopiesPerItem.Value} (old default). Reset to 2 so shelves are not walls of the same crystal/pan."); MaxCopiesPerItem.Value = 2; } Harmony = new Harmony("com.repoforge.core"); if (!Enabled.Value) { Log.LogWarning((object)"REPOForge is disabled in config."); return; } Log.LogInfo((object)"REPOForge 1.5.9 config bound; Harmony patches deferred until after Steam/Photon auth (Lobby / ConnectedToMaster)."); try { ScheduleDeferredBoot(); } catch (Exception ex) { Log.LogWarning((object)("Deferred boot schedule from Awake failed: " + ex.Message)); } } private void Start() { if (!Enabled.Value) { return; } try { try { string path = Path.Combine(Paths.ConfigPath, "com.repoforge.library.cache.json"); if (File.Exists(path) && !RefreshLibrary.Value) { File.Delete(path); Log.LogInfo((object)"Deleted com.repoforge.library.cache.json (RefreshLibrary=false)."); } } catch (Exception ex) { Log.LogDebug((object)("Cache cleanup: " + ex.Message)); } } catch (Exception ex2) { Log.LogWarning((object)("Cache cleanup skipped: " + ex2.Message)); } ScheduleDeferredBoot(); } private static bool IsPastNetworkAuth() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) try { if (GameAccess.PhotonInRoom()) { return true; } } catch (Exception) { } try { Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (!string.IsNullOrEmpty(name) && (name.IndexOf("Lobby", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Menu", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Shop", StringComparison.OrdinalIgnoreCase) >= 0 || name.Equals("Main", StringComparison.OrdinalIgnoreCase))) { return true; } } catch (Exception) { } try { Type type = GameAccess.Type("Photon.Pun.PhotonNetwork") ?? GameAccess.Type("PhotonNetwork"); if (type == null) { return false; } PropertyInfo propertyInfo = AccessTools.Property(type, "InRoom"); if (propertyInfo != null) { object value = propertyInfo.GetValue(null, null); bool flag = default(bool); int num; if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return true; } } PropertyInfo propertyInfo2 = AccessTools.Property(type, "CloudRegion"); if (!string.IsNullOrEmpty(((propertyInfo2 != null) ? propertyInfo2.GetValue(null, null) : null) as string)) { return true; } PropertyInfo propertyInfo3 = AccessTools.Property(type, "NetworkClientState"); if (propertyInfo3 != null) { object value2 = propertyInfo3.GetValue(null, null); string text = ((value2 != null) ? value2.ToString() : ""); if (!string.IsNullOrEmpty(text) && (text.IndexOf("ConnectedToMaster", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("JoinedLobby", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Joined", StringComparison.OrdinalIgnoreCase) >= 0)) { return true; } } } catch (Exception) { } return false; } private static double DeferredElapsedSec() { return (double)(DateTime.UtcNow.Ticks - _deferredBootStartTicks) / 10000000.0; } private void ScheduleDeferredBoot() { if (!_deferredBootDone && Enabled.Value) { if (!_deferredBootScheduled) { _deferredBootScheduled = true; _deferredBootStartTicks = DateTime.UtcNow.Ticks; Log.LogInfo((object)"Deferred boot armed (Update + sceneLoaded; DateTime clock, no Unity Time)."); } HookDeferredSceneLoaded(); TryFinishDeferredBoot("schedule"); } } private void HookDeferredSceneLoaded() { if (_deferredBootHooked) { return; } _deferredBootHooked = true; try { SceneManager.sceneLoaded += OnDeferredSceneLoaded; } catch (Exception ex) { Log.LogWarning((object)("sceneLoaded hook failed: " + ex.Message)); } } private void OnDeferredSceneLoaded(Scene scene, LoadSceneMode mode) { try { string text = ((Scene)(ref scene)).name ?? ""; Log.LogInfo((object)("Deferred boot saw scene: " + text)); TryFinishDeferredBoot("scene:" + text); } catch (Exception ex) { Log.LogWarning((object)("OnDeferredSceneLoaded: " + ex.Message)); } } private void Update() { if (!_deferredBootScheduled || _deferredBootDone) { return; } if (!_deferredUpdateLogged) { _deferredUpdateLogged = true; try { Log.LogInfo((object)"Deferred boot Update() is running."); } catch (Exception) { } } TryFinishDeferredBoot("update"); } private void TryFinishDeferredBoot(string reason) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) if (_deferredBootDone || !_deferredBootScheduled || !Enabled.Value) { return; } double num = DeferredElapsedSec(); if (num < 0.0) { num = 0.0; } bool flag = false; bool flag2 = false; try { flag = num >= 1.0 && IsPastNetworkAuth(); } catch (Exception ex) { Log.LogWarning((object)("IsPastNetworkAuth threw: " + ex.Message)); } if (!flag && num >= 3.0) { try { Scene activeScene = SceneManager.GetActiveScene(); string text = ((Scene)(ref activeScene)).name ?? ""; if (text.IndexOf("Main", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Lobby", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Shop", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Menu", StringComparison.OrdinalIgnoreCase) >= 0) { flag = true; } } catch (Exception) { } } if (!flag && num >= 12.0) { flag2 = true; flag = true; } if (!flag) { return; } _deferredBootDone = true; try { if (_deferredBootHooked) { SceneManager.sceneLoaded -= OnDeferredSceneLoaded; _deferredBootHooked = false; } } catch (Exception) { } if (flag2) { Log.LogWarning((object)$"Deferred boot timed out after {num:0.0}s ({reason}) — applying patches anyway."); } else { Log.LogInfo((object)$"Past network auth after {num:0.0}s ({reason}) — applying deferred Harmony installs."); } try { ApplyDeferredInstalls(); } catch (Exception ex4) { Log.LogError((object)("ApplyDeferredInstalls failed: " + ex4.ToString())); } } private void ApplyDeferredInstalls() { //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Expected O, but got Unknown object obj = (Action)delegate { HarmonyArbiter.Install(Harmony); }; SafeInstall("HarmonyArbiter", (Action)obj); object obj2 = (Action)delegate { PrefabRegistry.Install(Harmony); }; SafeInstall("PrefabRegistry", (Action)obj2); object obj3 = (Action)delegate { ShopCoordinator.Install(Harmony); }; SafeInstall("ShopCoordinator", (Action)obj3); object obj4 = (Action)delegate { DiscrepancyGuard.Install(Harmony); }; SafeInstall("DiscrepancyGuard", (Action)obj4); Log.LogInfo((object)"REPOForge 1.5.9 loaded (deferred). Duplicate copies of this GUID are skipped by BepInEx — keep one folder."); try { ModLibrary.Boot(); } catch (Exception ex) { Log.LogWarning((object)("Library boot skipped: " + ex.Message)); } try { DiscrepancyGuard.DiscoverLoadedPlugins(); } catch (Exception ex2) { Log.LogWarning((object)("Auto-discover skipped: " + ex2.Message)); } try { DiscrepancyGuard.BeginWatch((MonoBehaviour)this); } catch (Exception ex3) { Log.LogWarning((object)("Handshake watch skipped: " + ex3.Message)); } } private void OnDestroy() { Harmony harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private static void SafeInstall(string name, Action fn) { try { fn(); } catch (Exception ex) { Log.LogError((object)(name + " install failed (continuing): " + ex.GetBaseException().Message)); } } } public static class MyPluginInfo { public const string GUID = "com.repoforge.core"; public const string Name = "REPOForge"; public const string Version = "1.5.9"; } } namespace REPOForge.Compat { internal static class DiscrepancyGuard { [CompilerGenerated] private sealed class __c__DisplayClass10_0 { public CompatManifest manifest; internal bool Declare_b__0(CompatManifest m) { return m.Guid == manifest.Guid; } } private static readonly List<CompatManifest> _manifests = new List<CompatManifest>(); private static readonly HashSet<string> _seenPeers = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static string _cached = ""; private static bool _dirty = true; internal static string LocalFingerprint { get { if (_dirty) { _cached = Compute(); } return _cached; } } internal static void Install(Harmony _harmony) { Plugin.Log.LogInfo((object)"Handshake is poll-only. Photon join is not patched (that hung co-op loading in 1.5.0)."); } internal static void BeginWatch(MonoBehaviour host) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)host == (Object)null)) { if (!Plugin.Handshake.Value) { Plugin.Log.LogInfo((object)"Handshake disabled — WatchRoom not started (co-op stability)."); } else { host.StartCoroutine(WatchRoom()); } } } private static IEnumerator WatchRoom() { bool wasIn = false; WaitForSecondsRealtime wait = new WaitForSecondsRealtime(0.75f); while (true) { yield return wait; bool flag; try { flag = GameAccess.PhotonInRoom(); } catch (Exception) { flag = false; } if (flag && !wasIn) { try { GameAccess.ResetRoleLog(); if (Plugin.Enabled.Value && Plugin.Handshake.Value) { Plugin.Log.LogInfo((object)$"[OnJoinedRoom] fingerprint {LocalFingerprint} ({_manifests.Count} mods) host={GameAccess.IsMaster()} coop={GameAccess.IsMultiplayer()}. Compare this line with other players."); ScanRoom("join"); } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Handshake failed: " + ex2.Message)); } } if (!flag && wasIn) { _seenPeers.Clear(); GameAccess.ResetRoleLog(); } wasIn = flag; } } internal static void DiscoverLoadedPlugins() { try { foreach (PluginInfo value in Chainloader.PluginInfos.Values) { BepInPlugin metadata = value.Metadata; if (metadata != null) { CompatManifest obj = new CompatManifest { Guid = metadata.GUID }; Version version = metadata.Version; obj.Version = ((version != null) ? version.ToString() : null) ?? "0"; obj.DisplayName = metadata.Name ?? metadata.GUID; obj.HostRequired = metadata.GUID != "com.repoforge.core"; Declare(obj); } } Plugin.Log.LogInfo((object)$"Auto-discovered {_manifests.Count} plugins. Fingerprint {LocalFingerprint}."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Plugin auto-discover skipped: " + ex.Message)); } } internal static void Declare(CompatManifest manifest) { __c__DisplayClass10_0 CS__8__locals7 = new __c__DisplayClass10_0(); CS__8__locals7.manifest = manifest; if (CS__8__locals7.manifest != null && !string.IsNullOrEmpty(CS__8__locals7.manifest.Guid)) { _manifests.RemoveAll((CompatManifest m) => m.Guid == CS__8__locals7.manifest.Guid); _manifests.Add(CS__8__locals7.manifest); _dirty = true; if (Plugin.VerboseLogging.Value) { Plugin.Log.LogInfo((object)("Compat declared " + CS__8__locals7.manifest.Guid + "@" + CS__8__locals7.manifest.Version)); } } } private static void ScanRoom(string reason) { try { foreach (object item in GameAccess.PhotonPlayers()) { ReadPlayer(item, reason); } } catch (Exception) { } } private static void ReadPlayer(object? player, string reason) { if (player == null) { return; } try { object member = GameAccess.GetMember(player, "NickName"); string text = (string)(((member is string) ? member : null) ?? "player"); object obj = GameAccess.PhotonLocalPlayer(); if (obj == null || obj != player) { object obj2 = GameAccess.GetMember(player, "CustomProperties") ?? GameAccess.GetMember(player, "customProperties"); string text2 = null; IDictionary dictionary = (IDictionary)((obj2 is IDictionary) ? obj2 : null); if (dictionary != null && dictionary.Contains("rf")) { text2 = dictionary["rf"]?.ToString(); } if (!string.IsNullOrEmpty(text2)) { ParsePayload(text2, text + "/" + reason); } } } catch (Exception) { } } private static void ParsePayload(string payload, string nick) { if (!string.IsNullOrEmpty(payload)) { string[] array = payload.Split('|'); string remote = array[0]; string version = ((array.Length > 1) ? array[1] : "?"); OnRemoteFingerprint(remote, version, nick); } } internal static void OnRemoteFingerprint(string remote, string version, string nick) { if (string.IsNullOrEmpty(remote)) { return; } string item = nick + "|" + remote; if (_seenPeers.Add(item)) { string localFingerprint = LocalFingerprint; List<string> list = new List<string>(); bool flag = localFingerprint != remote; if (flag) { list.Add("fingerprint " + localFingerprint + " vs " + remote); Plugin.Log.LogWarning((object)("Co-op mismatch with " + nick + ": this machine " + localFingerprint + ", theirs " + remote + " (Forge " + version + "). Use the same mods or the shop will desync.")); } else { Plugin.Log.LogInfo((object)("Co-op aligned with " + nick + " (" + remote + ")")); } PluginBus.Publish(new DriftReportEvent { HardMismatch = flag, HostFingerprint = (GameAccess.IsMaster() ? localFingerprint : remote), ClientFingerprint = (GameAccess.IsMaster() ? remote : localFingerprint), Findings = list }); if (flag && Plugin.KickOnHardMismatch.Value && GameAccess.IsMaster()) { Plugin.Log.LogWarning((object)("KickOnHardMismatch is on — host should kick " + nick + ".")); } } } private static string Compute() { _dirty = false; IOrderedEnumerable<string> orderedEnumerable = Chainloader.PluginInfos.Values.Select((PluginInfo p) => $"{p.Metadata.GUID}@{p.Metadata.Version}").OrderBy<string, string>((string result) => result, StringComparer.OrdinalIgnoreCase); IOrderedEnumerable<string> orderedEnumerable2 = _manifests.SelectMany((CompatManifest m) => m.PrefabPaths).Distinct<string>(StringComparer.OrdinalIgnoreCase).OrderBy<string, string>((string result) => result, StringComparer.OrdinalIgnoreCase); string s = string.Join(";", orderedEnumerable) + "|" + string.Join(";", orderedEnumerable2); using SHA256 sHA = SHA256.Create(); return BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(s))).Replace("-", "").Substring(0, 12) .ToLowerInvariant(); } } internal static class HarmonyArbiter { [CompilerGenerated] private sealed class __c__DisplayClass2_0 { public string owner; internal bool Claim_b__0((string, HarmonyLane) c) { return c.Item1 == owner; } } private static readonly Dictionary<string, List<(string, HarmonyLane)>> _claims = new Dictionary<string, List<(string, HarmonyLane)>>(StringComparer.OrdinalIgnoreCase); private static bool _dumped; internal static void Install(Harmony harmony) { SceneManager.sceneLoaded += delegate { DumpIfNeeded(); }; } internal static void Claim(string method, HarmonyLane lane, string owner) { __c__DisplayClass2_0 CS__8__locals4 = new __c__DisplayClass2_0(); CS__8__locals4.owner = owner; if (string.IsNullOrEmpty(method) || string.IsNullOrEmpty(CS__8__locals4.owner)) { return; } List<(string, HarmonyLane)> value = null; if (!_claims.TryGetValue(method, out value)) { value = new List<(string, HarmonyLane)>(); _claims[method] = value; } value.RemoveAll(((string, HarmonyLane) c) => c.Item1 == CS__8__locals4.owner); value.Add((CS__8__locals4.owner, lane)); if (value.Count > 1) { string text = string.Join(", ", value.ConvertAll(((string, HarmonyLane) c) => $"{c.Item1}[{c.Item2}]")); bool flag = value.Exists(((string, HarmonyLane) c) => c.Item2 == HarmonyLane.Override) && value.Exists(((string, HarmonyLane) c) => c.Item2 != HarmonyLane.Override); Plugin.Log.LogWarning((object)("Harmony claim on " + method + ": " + text + (flag ? " — Override vs others, last skip-original Prefix will win unless they use Forge.Shop." : ""))); } } private static void DumpIfNeeded() { if (_dumped) { return; } _dumped = true; foreach (KeyValuePair<string, List<(string, HarmonyLane)>> claim in _claims) { if (claim.Value.Count >= 2) { Plugin.Log.LogInfo((object)$"[arbiter] {claim.Key} owned by {claim.Value.Count} mods"); } } } } internal sealed class ModEntry { public string Guid = ""; public string Name = ""; public string Thunderstore = ""; public string Kind = "content"; public bool HostRequired = true; public readonly HashSet<string> Policy = new HashSet<string>(StringComparer.OrdinalIgnoreCase); public readonly List<string> Markers = new List<string>(); public readonly List<string> Patches = new List<string>(); public string Version = ""; public bool Installed; public bool Baked; } internal static class ModLibrary { [CompilerGenerated] private static class __O { public static ThreadStart _0__RefreshThunderstore; } [CompilerGenerated] private sealed class __c__DisplayClass12_0 { public string m; internal bool Upsert_b__0(string x) { return string.Equals(x, m, StringComparison.OrdinalIgnoreCase); } } [CompilerGenerated] private sealed class __c__DisplayClass12_1 { public string p; internal bool Upsert_b__1(string x) { return string.Equals(x, p, StringComparison.OrdinalIgnoreCase); } } private static readonly object Gate = new object(); private static readonly Dictionary<string, ModEntry> ByGuid = new Dictionary<string, ModEntry>(StringComparer.OrdinalIgnoreCase); private static readonly List<ModEntry> All = new List<ModEntry>(); private static bool _booted; private static readonly string[][] Baked = new string[25][] { new string[7] { "com.repoforge.core", "REPOForge", "REPOForge-REPOForge", "library", "core", "", "ShopManager.GetAllItemsFromStatsManager|PunManager.SpawnShopItem" }, new string[7] { "Zehs.REPOLib", "REPOLib", "Zehs-REPOLib", "library", "content_api", "", "StatsManager.AddItem" }, new string[7] { "Jettcodey.MoreShopItems", "More Shop Items", "Jettcodey-MoreShopItems", "shop", "raise_budgets|keep_weights", "", "ShopManager.GetAllItemsFromStatsManager" }, new string[7] { "HeroHanex.NoItemSpawnLimit", "NoItemSpawnLimit", "HeroHanex-NoItemSpawnLimit", "shop", "raise_caps", "", "" }, new string[7] { "MEDVAC.HealerDrone", "MEDVAC Healer Drone", "cherdak-MEDVAC_Healer_Drone", "content", "self_spawns|shop_skip", "MEDVAC", "" }, new string[7] { "uz.cherdak.repo.cargobackpack", "C.A.R.G.O. Backpack", "cherdak-CARGO_Backpack_Mod", "content", "shop_pin", "C.A.R.G.O|CARGO Backpack", "" }, new string[7] { "uz.cherdak.repo.featherguarddrone", "Featherguard Drone", "cherdak-FeatherguardDrone", "content", "shop_pin", "Featherguard|Feather Guard", "" }, new string[7] { "uz.cherdak.repo.echomannequin", "Echo Mannequin", "cherdak-EchoMannequin", "content", "shop_skip", "Echo Mannequin|Item Echo", "" }, new string[7] { "com.github.zehsteam.LethalCompanyValuables", "LethalCompanyValuables", "Zehs-LethalCompanyValuables", "valuable", "valuables", "Valuable", "" }, new string[7] { "Rangerbb275.REPOing_Valuables", "REPOing Valuables", "Rangerbb275-REPOing_Valuables", "valuable", "valuables", "Valuable", "" }, new string[7] { "Roemi.Blackbox", "Blackbox", "Roemi-Blackbox", "qol", "", "", "" }, new string[7] { "MinecraftStrongholdLevel", "Minecraft Stronghold Level", "AriIcedT-MinecraftStrongholdLevel", "level", "level_loot", "MC Item", "" }, new string[7] { "DirtyGames.REPOGambling", "REPOGambling", "DirtyGames-REPOGambling", "shop", "shop_module", "", "" }, new string[7] { "Tolga.ShoppingCart", "ShoppingCart", "Tolga-ShoppingCart", "qol", "shop_module", "", "" }, new string[7] { "HVG.ShopSpawnByType", "ShopSpawnByType", "HVG_Solutions-ShopSpawnByType", "shop", "shop_list", "", "PunManager.SpawnShopItem|ShopManager.GetAllItemsFromStatsManager" }, new string[7] { "Zichen.ShopPlus", "ShopPlus", "Zichen-ShopPlus", "shop", "shop_list", "", "ShopManager.ShopInitialize|PunManager.SpawnShopItem" }, new string[7] { "SeroRonin.ItemBundles", "ItemBundles", "SeroRonin-ItemBundles", "shop", "shop_pin", "Bundle", "ShopManager.GetAllItemsFromStatsManager" }, new string[7] { "itsUndefined.Shop_Items_Spawn_in_Level", "Shop Items Spawn in Level", "itsUndefined-Shop_Items_Spawn_in_Level", "shop", "level_loot", "", "ValuableDirector.Spawn" }, new string[7] { "papucsevo.All_Shop_Items_In_Level", "All Shop Items In Level", "papucsevo-All_Shop_Items_In_Level", "shop", "level_loot", "", "ValuableDirector.Spawn" }, new string[7] { "BULLETBOT.MoreUpgrades", "MoreUpgrades", "BULLETBOT-MoreUpgrades", "content", "shop_pin", "", "" }, new string[7] { "nickklmao.repoconfig", "REPOConfig", "nickklmao-REPOConfig", "qol", "client_ok", "", "" }, new string[7] { "flipf17.DeadTTS", "DeadTTS", "flipf17-DeadTTS", "qol", "client_ok", "", "" }, new string[7] { "com.empress.blackboxfixer", "Empress Blackbox Fixer", "Empress-BlackboxFixer", "fix", "client_ok", "", "" }, new string[7] { "WesleysEnemies", "WesleysEnemies", "Wesley-WesleysEnemies", "content", "enemies", "", "" }, new string[7] { "WesleysLevels", "WesleysLevels", "Wesley-WesleysLevels", "level", "level_loot", "", "" } }; internal static IReadOnlyList<ModEntry> Entries { get { lock (Gate) { return All.ToArray(); } } } internal static void Boot() { if (_booted) { return; } _booted = true; LoadBaked(); LoadJsonBesideDll(); ScanInstalledManifests(); BindChainloader(); ApplyHarmonyClaims(); int num = 0; int num2 = 0; foreach (ModEntry item in All) { if (item.Installed) { num++; } if (item.Baked) { num2++; } } Plugin.Log.LogInfo((object)$"Library: {num2} known REPO mods, {num} installed, {All.Count} total. Unknown packages are treated as shop-pin if they register via REPOLib."); if (Plugin.RefreshLibrary.Value) { object obj = new ThreadStart(RefreshThunderstore); Thread thread = new Thread((ThreadStart)obj); thread.IsBackground = true; thread.Name = "REPOForge.Library"; thread.Start(); } } internal static bool ShopSkip(string? name) { if (string.IsNullOrEmpty(name)) { return false; } if (name.IndexOf("Echo Mannequin", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Item Echo", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } if (!MarkerHits(name, "shop_skip") && !MarkerHits(name, "self_spawns")) { return MarkerHits(name, "valuables"); } return true; } internal static bool SelfSpawns(string? name) { if (!string.IsNullOrEmpty(name)) { if (name.IndexOf("MEDVAC", StringComparison.OrdinalIgnoreCase) < 0) { return MarkerHits(name, "self_spawns"); } return true; } return false; } internal static bool LooksLikeValuable(string? blob) { if (!string.IsNullOrEmpty(blob)) { if (blob.IndexOf("valuable", StringComparison.OrdinalIgnoreCase) < 0) { return MarkerHits(blob, "valuables"); } return true; } return false; } internal static bool ShopPin(string? name) { if (!string.IsNullOrEmpty(name)) { return MarkerHits(name, "shop_pin"); } return false; } private static bool MarkerHits(string text, string policy) { lock (Gate) { foreach (ModEntry item in All) { if (!item.Policy.Contains(policy)) { continue; } foreach (string marker in item.Markers) { if (marker.Length > 0 && text.IndexOf(marker, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } } } return false; } private static void Upsert(ModEntry incoming) { if (string.IsNullOrEmpty(incoming.Guid) && string.IsNullOrEmpty(incoming.Thunderstore)) { return; } string text = ((!string.IsNullOrEmpty(incoming.Guid)) ? incoming.Guid : incoming.Thunderstore); ModEntry value = null; if (ByGuid.TryGetValue(text, out value)) { if (!string.IsNullOrEmpty(incoming.Name)) { value.Name = incoming.Name; } if (!string.IsNullOrEmpty(incoming.Version)) { value.Version = incoming.Version; } if (!string.IsNullOrEmpty(incoming.Thunderstore)) { value.Thunderstore = incoming.Thunderstore; } if (incoming.Installed) { value.Installed = true; } if (incoming.Baked) { value.Baked = true; } foreach (string item in incoming.Policy) { value.Policy.Add(item); } using (List<string>.Enumerator enumerator2 = incoming.Markers.GetEnumerator()) { while (enumerator2.MoveNext()) { __c__DisplayClass12_0 CS__8__locals6 = new __c__DisplayClass12_0(); CS__8__locals6.m = enumerator2.Current; if (!value.Markers.Exists((string x) => string.Equals(x, CS__8__locals6.m, StringComparison.OrdinalIgnoreCase))) { value.Markers.Add(CS__8__locals6.m); } } } using (List<string>.Enumerator enumerator2 = incoming.Patches.GetEnumerator()) { while (enumerator2.MoveNext()) { __c__DisplayClass12_1 CS__8__locals7 = new __c__DisplayClass12_1(); CS__8__locals7.p = enumerator2.Current; if (!value.Patches.Exists((string x) => string.Equals(x, CS__8__locals7.p, StringComparison.OrdinalIgnoreCase))) { value.Patches.Add(CS__8__locals7.p); } } } if (!string.IsNullOrEmpty(incoming.Guid) && !ByGuid.ContainsKey(incoming.Guid)) { ByGuid[incoming.Guid] = value; } } else { ByGuid[text] = incoming; if (!string.IsNullOrEmpty(incoming.Guid) && incoming.Guid != text) { ByGuid[incoming.Guid] = incoming; } All.Add(incoming); } } private static void LoadBaked() { lock (Gate) { string[][] baked = Baked; for (int i = 0; i < baked.Length; i++) { ModEntry modEntry = ParseRow(baked[i]); modEntry.Baked = true; Upsert(modEntry); } } } private static void LoadJsonBesideDll() { try { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (!string.IsNullOrEmpty(directoryName)) { string text = Path.Combine(directoryName, "library.json"); if (File.Exists(text)) { MergeJson(File.ReadAllText(text), baked: true); Plugin.Log.LogInfo((object)("Library loaded " + text)); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Library json skipped: " + ex.Message)); } } private static void ScanInstalledManifests() { try { string pluginPath = Paths.PluginPath; if (string.IsNullOrEmpty(pluginPath) || !Directory.Exists(pluginPath)) { return; } string[] files = Directory.GetFiles(pluginPath, "manifest.json", SearchOption.AllDirectories); foreach (string path in files) { try { string json = File.ReadAllText(path); string text = JsonStr(json, "name"); string version = JsonStr(json, "version_number"); string fileName = Path.GetFileName(Path.GetDirectoryName(path) ?? ""); string text2 = fileName; int num = fileName.IndexOf('-'); if (num > 0) { string text3 = fileName.Substring(num + 1); int num2 = text3.LastIndexOf('-'); if (num2 > 0 && char.IsDigit(text3[num2 + 1])) { text2 = fileName.Substring(0, num + 1 + num2); } } Upsert(new ModEntry { Name = (string.IsNullOrEmpty(text) ? fileName : text), Version = version, Thunderstore = text2, Guid = text2, Installed = true, Kind = "content" }); } catch (Exception) { } } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Library scan skipped: " + ex2.Message)); } } private static void BindChainloader() { try { foreach (PluginInfo value in Chainloader.PluginInfos.Values) { BepInPlugin metadata = value.Metadata; if (metadata != null) { ModEntry obj = new ModEntry { Guid = metadata.GUID, Name = (metadata.Name ?? metadata.GUID) }; Version version = metadata.Version; obj.Version = ((version != null) ? version.ToString() : null) ?? ""; obj.Installed = true; ModEntry modEntry = obj; lock (Gate) { Upsert(modEntry); } DiscrepancyGuard.Declare(new CompatManifest { Guid = metadata.GUID, Version = modEntry.Version, DisplayName = modEntry.Name, HostRequired = true }); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Library chainloader bind skipped: " + ex.Message)); } } private static void ApplyHarmonyClaims() { lock (Gate) { foreach (ModEntry item in All) { if (!item.Installed) { continue; } foreach (string patch in item.Patches) { HarmonyArbiter.Claim(patch, HarmonyLane.Content, item.Guid); } } } } private static void RefreshThunderstore() { try { ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; using WebClient webClient = new WebClient { Encoding = Encoding.UTF8 }; webClient.Headers[HttpRequestHeader.UserAgent] = "REPOForge/1.5.4"; string[] obj = new string[2] { "https://thunderstore.io/c/repo/api/v1/package/", "https://thunderstore.io/api/experimental/community/repo/packages/" }; string text = null; string[] array = obj; foreach (string address in array) { try { text = webClient.DownloadString(address); if (!string.IsNullOrEmpty(text)) { break; } } catch (Exception) { } } if (string.IsNullOrEmpty(text)) { Plugin.Log.LogWarning((object)"Library Thunderstore refresh skipped: no catalog endpoint answered."); return; } int num = MergeThunderstorePage(text); if (num > 500) { Plugin.Log.LogWarning((object)$"Library Thunderstore refresh aborted: +{num} packages would bloat cache (co-op hang). Keep RefreshLibrary=false."); return; } Plugin.Log.LogInfo((object)$"Library Thunderstore refresh: +{num} packages."); try { File.WriteAllText(Path.Combine(Paths.ConfigPath, "com.repoforge.library.cache.json"), text); } catch (Exception) { } } catch (Exception ex3) { Plugin.Log.LogWarning((object)("Library Thunderstore refresh skipped: " + ex3.Message)); } } private static int MergeThunderstorePage(string json) { int num = 0; int i = 0; while (true) { string text = ExtractAfter(json, "\"full_name\":", ref i); if (text == null) { break; } string name = ExtractAfter(json, "\"name\":", ref i) ?? text; ModEntry incoming = new ModEntry { Guid = text, Name = name, Thunderstore = text, Kind = "content" }; lock (Gate) { int count = All.Count; Upsert(incoming); if (All.Count > count) { num++; } } } return num; } private static void MergeJson(string json, bool baked) { int num = json.IndexOf("\"mods\"", StringComparison.Ordinal); if (num < 0) { return; } int i = json.IndexOf('[', num); if (i < 0) { return; } int num2 = 0; int num3 = -1; for (; i < json.Length; i++) { switch (json[i]) { case '{': if (num2 == 0) { num3 = i; } num2++; break; case '}': num2--; if (num2 == 0 && num3 >= 0) { ModEntry modEntry = FromJsonObject(json.Substring(num3, i - num3 + 1)); modEntry.Baked = baked; lock (Gate) { Upsert(modEntry); } num3 = -1; } break; } } } private static ModEntry FromJsonObject(string obj) { ModEntry modEntry = new ModEntry { Guid = JsonStr(obj, "guid"), Name = JsonStr(obj, "name"), Thunderstore = JsonStr(obj, "thunderstore"), Kind = JsonStr(obj, "kind") }; if (string.IsNullOrEmpty(modEntry.Kind)) { modEntry.Kind = "content"; } string text = JsonStr(obj, "hostRequired"); modEntry.HostRequired = text != "false"; foreach (string item in JsonArr(obj, "policy")) { modEntry.Policy.Add(item); } modEntry.Markers.AddRange(JsonArr(obj, "markers")); modEntry.Patches.AddRange(JsonArr(obj, "patches")); return modEntry; } private static string JsonStr(string json, string key) { string value = "\"" + key + "\""; int num = json.IndexOf(value, StringComparison.OrdinalIgnoreCase); if (num < 0) { return ""; } num = json.IndexOf(':', num); if (num < 0) { return ""; } for (num++; num < json.Length && char.IsWhiteSpace(json[num]); num++) { } if (num >= json.Length) { return ""; } if (json[num] == '"') { num++; int num2 = json.IndexOf('"', num); if (num2 >= 0) { return json.Substring(num, num2 - num); } return ""; } int i; for (i = num; i < json.Length && json[i] != ',' && json[i] != '}' && json[i] != ']'; i++) { } return json.Substring(num, i - num).Trim(); } private static List<string> JsonArr(string json, string key) { List<string> list = new List<string>(); string value = "\"" + key + "\""; int num = json.IndexOf(value, StringComparison.OrdinalIgnoreCase); if (num < 0) { return list; } num = json.IndexOf('[', num); if (num < 0) { return list; } int num2 = json.IndexOf(']', num); if (num2 < 0) { return list; } string text = json.Substring(num, num2 - num); int startIndex = 0; while (true) { int num3 = text.IndexOf('"', startIndex); if (num3 < 0) { break; } int num4 = text.IndexOf('"', num3 + 1); if (num4 < 0) { break; } list.Add(text.Substring(num3 + 1, num4 - num3 - 1)); startIndex = num4 + 1; } return list; } private static string? ExtractAfter(string json, string key, ref int i) { int num = json.IndexOf(key, i, StringComparison.Ordinal); if (num < 0) { return null; } int num2 = json.IndexOf('"', num + key.Length); if (num2 < 0) { return null; } int num3 = json.IndexOf('"', num2 + 1); if (num3 < 0) { return null; } i = num3 + 1; return json.Substring(num2 + 1, num3 - num2 - 1); } private static ModEntry ParseRow(string[] r) { ModEntry modEntry = new ModEntry { Guid = r[0], Name = r[1], Thunderstore = r[2], Kind = r[3] }; string[] array = r[4].Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries); foreach (string item in array) { modEntry.Policy.Add(item); } array = r[5].Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries); foreach (string item2 in array) { modEntry.Markers.Add(item2); } array = r[6].Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries); foreach (string item3 in array) { modEntry.Patches.Add(item3); } return modEntry; } } } namespace REPOForge.Network { internal static class PrefabRegistry { private static readonly Dictionary<string, GameObject> _local = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, string> _owners = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); internal static void Install(Harmony harmony) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown Type type = GameAccess.Type("Photon.Pun.PhotonNetwork") ?? GameAccess.Type("PhotonNetwork"); if (!(type == null)) { MethodInfo methodInfo = AccessTools.Method(type, "InstantiateRoomObject", new Type[5] { typeof(string), typeof(Vector3), typeof(Quaternion), typeof(byte), typeof(object[]) }, (Type[])null) ?? AccessTools.Method(type, "InstantiateRoomObject", (Type[])null, (Type[])null); if (!(methodInfo == null)) { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(PrefabRegistry), "InstantiateFinalizer", (Type[])null), (HarmonyMethod)null); } } } internal static bool Register(string path, GameObject prefab) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown if (string.IsNullOrEmpty(path) || (Object)prefab == (Object)null) { return false; } if (_local.ContainsKey(path)) { Plugin.Log.LogWarning((object)("Prefab '" + path + "' already registered. First registrant (" + _owners[path] + ") wins.")); return false; } _local[path] = prefab; _owners[path] = "com.repoforge.core"; if (Plugin.VerboseLogging.Value) { Plugin.Log.LogInfo((object)("Prefab registered " + path)); } return true; } internal static GameObject? SpawnItem(object item, Vector3 position, Quaternion rotation) { //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_03c4: Unknown result type (might be due to invalid IL or missing references) //IL_03a6: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Expected O, but got Unknown //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_03b5: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Expected O, but got Unknown //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Expected O, but got Unknown //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Expected O, but got Unknown //IL_017a: 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_031b: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Expected O, but got Unknown //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Expected O, but got Unknown //IL_036b: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Expected O, but got Unknown if (!GameAccess.IsMaster()) { Plugin.Log.LogError((object)"SpawnItem is host-only."); return null; } string text = GameAccess.PrefabPath(item); string text2 = GameAccess.ItemName(item); object member = GameAccess.GetMember(item, "prefab"); GameObject val = (GameObject)(((member is GameObject) ? member : null) ?? null); if ((Object)val == (Object)null && member != null) { object obj = GameAccess.GetMember(member, "Prefab") ?? GameAccess.GetMember(member, "prefab"); val = (GameObject)(((obj is GameObject) ? obj : null) ?? null); } if ((Object)val == (Object)null && !string.IsNullOrEmpty(text) && _local.TryGetValue(text, out var value)) { val = value; } object obj2 = GameAccess.GetMember(member, "PrefabName") ?? GameAccess.GetMember(member, "prefabName"); string text3 = (string)(((obj2 is string) ? obj2 : null) ?? null); Plugin.Log.LogInfo((object)string.Format("[spawn] PrefabRegistry {0} path={1} prefabName={2} prefab={3} mp={4} pos={5}", text2, text, text3, ((Object)val != (Object)null) ? ((Object)val).name : "NULL", GameAccess.IsMultiplayer(), ((Vector3)(ref position)).ToString("F2"))); try { if (GameAccess.IsMultiplayer()) { EnsurePhotonCache(text, val); EnsurePhotonCache(text3, val); GameObject val2 = PhotonInstantiate(text, position, rotation) ?? PhotonInstantiate(text3, position, rotation); if ((Object)val2 == (Object)null) { string[] array = new string[6] { "Items/Item Drone Heal", "Items/Item Drone Feather", "Items/Item Drone Battery", "Items/Item Drone Indestructible", "Item Drone Heal", "Item Drone Feather" }; foreach (string text4 in array) { val2 = PhotonInstantiate(text4, position, rotation); if ((Object)val2 != (Object)null) { Plugin.Log.LogInfo((object)("[spawn] PrefabRegistry photon-fallback " + text2 + " via " + text4 + " → " + ((Object)val2).name)); return val2; } } } if ((Object)val2 != (Object)null) { Plugin.Log.LogInfo((object)("[spawn] PrefabRegistry photon " + text2 + " → " + ((Object)val2).name)); return val2; } Plugin.Log.LogWarning((object)("[spawn] Co-op: '" + text2 + "' did not replicate. Not spawning it only on the host — guests would not see it.")); return null; } if ((Object)val != (Object)null) { GameObject val3 = Object.Instantiate<GameObject>(val, position, rotation); Plugin.Log.LogInfo((object)("[spawn] PrefabRegistry Instantiate " + text2 + " → " + (((Object)val3 != (Object)null) ? ((Object)val3).name : "NULL"))); return val3; } GameObject val4 = PhotonInstantiate(text, position, rotation) ?? PhotonInstantiate(text3, position, rotation); if ((Object)val4 != (Object)null) { Plugin.Log.LogInfo((object)("[spawn] PrefabRegistry photon-sp " + text2 + " → " + ((Object)val4).name)); return val4; } Plugin.Log.LogWarning((object)("[spawn] PrefabRegistry FAIL " + text2 + " no Prefab GameObject, photon null. path=" + text)); return Placeholder(text, position, rotation); } catch (Exception ex) { Plugin.Log.LogError((object)("SpawnItem(" + text + ") failed: " + ex.Message)); if (GameAccess.IsMultiplayer()) { return null; } if ((Object)val != (Object)null) { try { return Object.Instantiate<GameObject>(val, position, rotation); } catch (Exception) { } } return Placeholder(text, position, rotation); } } private static void EnsurePhotonCache(string path, GameObject prefab) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown if (string.IsNullOrEmpty(path) || (Object)prefab == (Object)null) { return; } try { if (!_local.ContainsKey(path)) { _local[path] = prefab; } Type type = GameAccess.Type("Photon.Pun.PhotonNetwork") ?? GameAccess.Type("PhotonNetwork"); if (type == null) { return; } PropertyInfo propertyInfo = AccessTools.Property(type, "PrefabPool"); object obj = ((propertyInfo != null) ? propertyInfo.GetValue(null, null) : null); if (obj != null) { FieldInfo fieldInfo = AccessTools.Field(obj.GetType(), "ResourceCache") ?? AccessTools.Field(obj.GetType(), "resourceCache"); if (!(fieldInfo == null) && fieldInfo.GetValue(obj) is IDictionary dictionary) { dictionary[path] = prefab; Plugin.Log.LogInfo((object)("[spawn] seeded Photon ResourceCache " + path)); } } } catch (Exception ex) { Plugin.Log.LogDebug((object)("EnsurePhotonCache: " + ex.Message)); } } private static GameObject? PhotonInstantiate(string? path, Vector3 position, Quaternion rotation) { //IL_00a6: 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_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown if (string.IsNullOrEmpty(path)) { return null; } Type type = GameAccess.Type("Photon.Pun.PhotonNetwork") ?? GameAccess.Type("PhotonNetwork"); MethodInfo methodInfo = AccessTools.Method(type, "InstantiateRoomObject", new Type[5] { typeof(string), typeof(Vector3), typeof(Quaternion), typeof(byte), typeof(object[]) }, (Type[])null) ?? AccessTools.Method(type, "InstantiateRoomObject", (Type[])null, (Type[])null); if (methodInfo == null) { return null; } object obj = methodInfo.Invoke(null, new object[5] { path, position, rotation, (byte)0, null }); return (GameObject)((obj is GameObject) ? obj : null); } private static Exception? InstantiateFinalizer(Exception? __exception, string __0, Vector3 __1, Quaternion __2, ref GameObject? __result) { //IL_008b: 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) if (__exception == null) { return null; } if (!Plugin.Enabled.Value || !Plugin.PlaceholderMissingPrefabs.Value) { return __exception; } string text = __0 ?? ""; if (text.IndexOf("Item", StringComparison.OrdinalIgnoreCase) < 0 && !text.StartsWith("Items/", StringComparison.OrdinalIgnoreCase)) { return __exception; } Plugin.Log.LogWarning((object)("Photon instantiate failed for '" + text + "': " + __exception.Message + ". Spawning placeholder.")); __result = Placeholder(text, __1, __2); return null; } private static GameObject? Placeholder(string path, Vector3 position, Quaternion rotation) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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) try { GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj).name = "REPOForge_Placeholder_" + (path ?? "unknown"); obj.transform.position = position; obj.transform.rotation = rotation; obj.transform.localScale = Vector3.one * 0.35f; return obj; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Placeholder spawn failed: " + ex.Message)); return null; } } } } namespace REPOForge.Shop { internal static class ShopCoordinator { private sealed class Group { internal string Path = ""; internal string Name = ""; internal object Item; internal int Count; internal bool Mod; } [CompilerGenerated] private sealed class __c__DisplayClass14_0 { public ShopIntent intent; internal bool Register_b__0(ShopIntent i) { return i.ItemName == intent.ItemName; } } [CompilerGenerated] private sealed class __c__DisplayClass15_0 { public string itemName; internal bool Unregister_b__0(ShopIntent i) { return i.ItemName == itemName; } } private static readonly List<ShopIntent> _intents = new List<ShopIntent>(); private static readonly List<object> _reservedMods = new List<object>(); private static readonly HashSet<string> _placedThisVisit = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly object _gate = new object(); private static readonly Dictionary<string, int> _spawned = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, object> _homeVol = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> _usedSlots = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static string? _lastAttempt; internal static string? LastHash { get; private set; } private static void SpawnLog(string msg) { Plugin.Log.LogInfo((object)("[spawn] " + msg)); } internal static void Install(Harmony harmony) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Expected O, but got Unknown //IL_0124: Expected O, but got Unknown //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Expected O, but got Unknown //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Expected O, but got Unknown //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Expected O, but got Unknown Type? type = GameAccess.Type("ShopManager"); MethodInfo methodInfo = AccessTools.Method(type, "GetAllItemsFromStatsManager", (Type[])null, (Type[])null); if (methodInfo != null) { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(ShopCoordinator), "GetAllPostfix", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"Patched ShopManager.GetAllItemsFromStatsManager (postfix last)."); } else { Plugin.Log.LogWarning((object)"ShopManager.GetAllItemsFromStatsManager not found."); } Type? type2 = GameAccess.Type("PunManager"); MethodInfo methodInfo2 = AccessTools.Method(type2, "SpawnShopItem", (Type[])null, (Type[])null); if (methodInfo2 != null) { ParameterInfo[] parameters = methodInfo2.GetParameters(); Plugin.Log.LogInfo((object)("Patched PunManager.SpawnShopItem (" + string.Join(", ", Array.ConvertAll(parameters, (ParameterInfo p) => p.ParameterType.Name + " " + p.Name)) + ").")); harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(ShopCoordinator), "SpawnPrefix", (Type[])null) { priority = 100 }, new HarmonyMethod(typeof(ShopCoordinator), "SpawnPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { Plugin.Log.LogWarning((object)"PunManager.SpawnShopItem not found — volume matching still runs via GetAll postfix."); } MethodInfo methodInfo3 = AccessTools.Method(type2, "ShopPopulateItemVolumes", (Type[])null, (Type[])null); if (methodInfo3 != null) { harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(ShopCoordinator), "PopulatePrefix", (Type[])null) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(typeof(ShopCoordinator), "PopulatePostfix", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo4 = AccessTools.Method(type, "GetAllItemVolumesInScene", (Type[])null, (Type[])null); if (methodInfo4 != null) { harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(typeof(ShopCoordinator), "VolumesPostfix", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } internal static void Register(ShopIntent intent) { __c__DisplayClass14_0 CS__8__locals10 = new __c__DisplayClass14_0(); CS__8__locals10.intent = intent; if (CS__8__locals10.intent == null || string.IsNullOrEmpty(CS__8__locals10.intent.ItemName)) { return; } lock (_gate) { _intents.RemoveAll((ShopIntent i) => i.ItemName == CS__8__locals10.intent.ItemName); _intents.Add(CS__8__locals10.intent); if (CS__8__locals10.intent.Item != null) { GameAccess.SetEnum(CS__8__locals10.intent.Item, "itemVolume", CS__8__locals10.intent.Volume.ToString()); } } if (Plugin.VerboseLogging.Value) { Plugin.Log.LogInfo((object)("Shop intent registered: " + CS__8__locals10.intent.ItemName + " (" + CS__8__locals10.intent.PrefabPath + ")")); } } internal static void Unregister(string itemName) { __c__DisplayClass15_0 CS__8__locals2 = new __c__DisplayClass15_0(); CS__8__locals2.itemName = itemName; lock (_gate) { _intents.RemoveAll((ShopIntent i) => i.ItemName == CS__8__locals2.itemName); } } private static void GetAllPostfix(object __instance) { if (!Plugin.Enabled.Value || !Plugin.ShopPipeline.Value) { return; } if (GameAccess.IsNotMaster()) { Plugin.Log.LogInfo((object)"Shop mix skipped — client. Host fills the shelves; you receive them."); return; } try { _spawned.Clear(); _reservedMods.Clear(); _placedThisVisit.Clear(); _homeVol.Clear(); _usedSlots.Clear(); ReconcileLists(__instance); string text = (LastHash = ComputeHash(__instance)); int count = CountItems(__instance); PluginBus.Publish(new ShopReadyEvent { ManifestHash = text, Count = count, Seed = Environment.TickCount }); int num = GameAccess.ListField(__instance, "potentialItems")?.Count ?? 0; int num2 = GameAccess.ListField(__instance, "potentialItemConsumables")?.Count ?? 0; int num3 = GameAccess.ListField(__instance, "potentialItemUpgrades")?.Count ?? 0; int num4 = GameAccess.ListField(__instance, "potentialItemHealthPacks")?.Count ?? 0; object member = GameAccess.GetMember(__instance, "itemSpawnTargetAmount"); Plugin.Log.LogInfo((object)$"Shop pool items={num} consumables={num2} upgrades={num3} health={num4} target={member} unique={text}"); } catch (Exception arg) { Plugin.Log.LogError((object)$"Shop reconcile failed: {arg}"); } } private static void PopulatePrefix() { if (Plugin.Enabled.Value && Plugin.ShopPipeline.Value && !GameAccess.IsNotMaster()) { _spawned.Clear(); _placedThisVisit.Clear(); _homeVol.Clear(); _usedSlots.Clear(); object obj = GameAccess.Instance("ShopManager"); if (obj != null) { ApplyBudgets(obj); } } } private static void VolumesPostfix(object __instance) { if (Plugin.Enabled.Value && Plugin.ShopPipeline.Value && !GameAccess.IsNotMaster()) { ApplyBudgets(__instance); } } private static void PopulatePostfix() { if (!Plugin.Enabled.Value || !Plugin.ShopPipeline.Value || GameAccess.IsNotMaster()) { return; } try { ForcePlaceMods(); } catch (Exception arg) { Plugin.Log.LogError((object)$"Force-place mods failed: {arg}"); } } private static bool SpawnPrefix(object itemVolume, IList itemList, ref int spawnCount, bool isSecret, ref bool __result) { _lastAttempt = null; if (!Plugin.Enabled.Value || !Plugin.ShopPipeline.Value) { return true; } if (GameAccess.IsNotMaster()) { return true; } if (itemList == null || itemList.Count == 0) { SpawnLog($"skip empty-list vol={VolKind(itemVolume)} secret={isSecret} count={spawnCount}"); return true; } try { string text = DescribeTail(itemList); if (itemVolume != null) { PreferMatchingVolume(itemVolume, itemList); } int num = 0; while (itemList.Count > 0) { object obj = itemList[itemList.Count - 1]; if (obj == null) { SpawnLog($"skip null-tail vol={VolKind(itemVolume)} secret={isSecret}"); return true; } string text2 = GameAccess.ItemName(obj); string text3 = GameAccess.ItemVolume(obj); if (!IsModItem(obj)) { _lastAttempt = text2; SpawnLog($"vanilla-path {text2}[{text3}] onto {VolName(itemVolume)} secret={isSecret} count={spawnCount} list={itemList.Count} (was {text})"); return true; } if (IsMedvac(text2) || IsShopExcluded(text2) || AlreadyPlaced(text2)) { SpawnLog($"strip {text2} medvac={IsMedvac(text2)} excluded={IsShopExcluded(text2)} already={AlreadyPlaced(text2)} vol={VolName(itemVolume)}"); itemList.RemoveAt(itemList.Count - 1); if (itemVolume != null && itemList.Count > 0) { PreferMatchingVolume(itemVolume, itemList); } continue; } if (!VolCompatible(itemVolume, obj)) { SpawnLog("defer " + text2 + "[" + text3 + "] off " + VolName(itemVolume)); itemList.RemoveAt(itemList.Count - 1); itemList.Insert(0, obj); if (itemVolume != null && itemList.Count > 0) { PreferMatchingVolume(itemVolume, itemList); } if (++num <= itemList.Count) { continue; } return true; } _lastAttempt = text2; SpawnLog($"mod-path {text2}[{text3}] onto {VolName(itemVolume)} secret={isSecret} count={spawnCount} list={itemList.Count} — leaving for vanilla SpawnShopItem"); return true; } SpawnLog($"list exhausted after strip vol={VolName(itemVolume)} secret={isSecret} count={spawnCount}"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Spawn remap skipped: " + ex.Message)); } return true; } private static void SpawnPostfix(object itemVolume, IList itemList, ref int spawnCount, bool isSecret, bool __result) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_00f3: Expected O, but got Unknown if (Plugin.Enabled.Value && Plugin.ShopPipeline.Value) { string lastAttempt = _lastAttempt; GameObject val = ((!string.IsNullOrEmpty(lastAttempt)) ? FindClone(lastAttempt) : null); if (__result && (Object)val != (Object)null && itemVolume != null && !string.IsNullOrEmpty(lastAttempt)) { _homeVol[lastAttempt] = itemVolume; RememberSlot(itemVolume); } object obj; if (!((Object)val != (Object)null)) { obj = "-"; } else { Vector3 position = val.transform.position; obj = ((Vector3)(ref position)).ToString("F2"); } string text = (string)obj; SpawnLog(string.Format("result ok={0} item={1} secret={2} count={3} remaining={4} clone={5} pos={6} vol={7}", __result, lastAttempt ?? "(none)", isSecret, spawnCount, itemList?.Count ?? 0, ((Object)val != (Object)null) ? ((Object)val).name : "MISSING", text, VolName(itemVolume))); } } private static bool VolCompatible(object? volume, object item) { string text = EffectiveVolume(item); string text2 = VolKind(volume); if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(text2)) { return true; } if (string.Equals(text, text2, StringComparison.OrdinalIgnoreCase)) { return true; } if (Plugin.RemapSmallToMedium.Value && Contains(text, "small") && Contains(text2, "medium")) { return true; } if (Contains(text, "large") && (Contains(text2, "large") || Contains(text2, "wide") || Contains(text2, "plus"))) { return true; } return false; } private static string EffectiveVolume(object item) { return GameAccess.ItemVolume(item); } private static bool IsSecretItem(object item) { string text = GameAccess.GetMember(item, "itemSecretShopType")?.ToString() ?? ""; if (string.IsNullOrEmpty(text)) { return false; } if (!Contains(text, "none")) { return text != "0"; } return false; } private static bool IsValuable(object item) { string text = GameAccess.ItemName(item); string text2 = GameAccess.PrefabPath(item); string a = GameAccess.GetMember(item, "itemType")?.ToString() ?? ""; if (!Contains(text, "valuable") && !Contains(text2, "valuable") && !Contains(a, "valuable") && !ModLibrary.LooksLikeValuable(text)) { return ModLibrary.LooksLikeValuable(text2); } return true; } private static bool IsShopEligible(object item) { if (item == null) { return false; } string text = GameAccess.ItemName(item); if (IsMedvac(text) || IsShopExcluded(text)) { return false; } if (!IsModItem(item)) { return false; } if (IsValuable(item) || IsSecretItem(item)) { return false; } if (GameAccess.ReadInt(item, "maxAmountInShop") <= 0) { return false; } if (Contains(GameAccess.PrefabPath(item), "MC Item") && !Contains(text, "Firework")) { return false; } return true; } private static void AdoptMods(object shop) { if (!Plugin.AdoptUnlistedMods.Value) { return; } HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); Collect(GameAccess.ListField(shop, "potentialItems"), hashSet); Collect(GameAccess.ListField(shop, "potentialItemConsumables"), hashSet); Collect(GameAccess.ListField(shop, "potentialItemUpgrades"), hashSet); Collect(GameAccess.ListField(shop, "potentialItemHealthPacks"), hashSet); int num = 0; foreach (object item in GameAccess.AllStatItems()) { if (!IsShopEligible(item)) { continue; } string text = GameAccess.PrefabPath(item); if (!string.IsNullOrEmpty(text) && hashSet.Add(text)) { IList list = ListFor(shop, ShopCategoryKind.Items); string text2 = GameAccess.ItemVolume(item); if (Contains(text2, "health")) { list = GameAccess.ListField(shop, "potentialItemHealthPacks"); } else if (Contains(text2, "upgrade")) { list = GameAccess.ListField(shop, "potentialItemUpgrades"); } list?.Add(item); RememberMod(item); num++; Plugin.Log.LogInfo((object)("Shop adopt " + GameAccess.ItemName(item) + " [" + text2 + "] " + text)); } } if (num > 0) { Plugin.Log.LogInfo((object)$"Shop adopted {num} unlisted shop item(s)."); } } private static string DescribeTail(IList itemList) { if (itemList == null || itemList.Count == 0) { return "(empty)"; } object obj = itemList[itemList.Count - 1]; if (obj != null) { return GameAccess.ItemName(obj) + "[" + GameAccess.ItemVolume(obj) + "]"; } return "(null)"; } private static void PreferMatchingVolume(object itemVolume, IList itemList) { if (itemList.Count < 2) { return; } string text = GameAccess.GetMember(itemVolume, "itemVolume")?.ToString() ?? ""; int num = Math.Max(1, Plugin.MaxCopiesPerItem.Value); int num2 = -1; int num3 = -1; int value = 0; for (int num4 = itemList.Count - 1; num4 >= 0; num4--) { object obj = itemList[num4]; if (obj != null && (string.Equals(EffectiveVolume(obj), text, StringComparison.OrdinalIgnoreCase) || (Plugin.RemapSmallToMedium.Value && Contains(EffectiveVolume(obj), "small") && Contains(text, "medium")))) { if (num3 < 0) { num3 = num4; } string key = GameAccess.PrefabPath(obj); if ((_spawned.TryGetValue(key, out value) ? value : 0) < num) { num2 = num4; break; } } } int num5 = ((num2 >= 0) ? num2 : num3); if (num5 >= 0) { object obj2 = itemList[num5]; if (num5 != itemList.Count - 1) { itemList.RemoveAt(num5); itemList.Add(obj2); } string key2 = GameAccess.PrefabPath(obj2); int value2 = 0; _spawned[key2] = (_spawned.TryGetValue(key2, out value2) ? value2 : 0) + 1; } } private static void ReconcileLists(object shop) { HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); Collect(GameAccess.ListField(shop, "potentialItems"), hashSet); Collect(GameAccess.ListField(shop, "potentialItemConsumables"), hashSet); Collect(GameAccess.ListField(shop, "potentialItemUpgrades"), hashSet); Collect(GameAccess.ListField(shop, "potentialItemHealthPacks"), hashSet); lock (_gate) { foreach (ShopIntent intent in _intents) { if (intent.Item == null) { continue; } string text = (string.IsNullOrEmpty(intent.PrefabPath) ? GameAccess.PrefabPath(intent.Item) : intent.PrefabPath); if (!hashSet.Add(text)) { if (Plugin.VerboseLogging.Value) { Plugin.Log.LogInfo((object)("Shop intent '" + intent.ItemName + "' already in pool (" + text + ").")); } } else { ListFor(shop, intent.Category)?.Add(intent.Item); } } } EnsureCoverage(shop); if (Plugin.AdoptUnlistedMods.Value) { AdoptMods(shop); } if (Plugin.DiversifyPool.Value) { Mix(GameAccess.ListField(shop, "potentialItems"), "items", reserve: true); Mix(GameAccess.ListField(shop, "potentialItemConsumables"), "consumables", reserve: true); Mix(GameAccess.ListField(shop, "potentialItemUpgrades"), "upgrades", reserve: true); Mix(GameAccess.ListField(shop, "potentialItemHealthPacks"), "health", reserve: true); MixSecrets(shop); } else if (Plugin.PriceWeight.Value) { WeightByPrice(GameAccess.ListField(shop, "potentialItems")); WeightByPrice(GameAccess.ListField(shop, "potentialItemConsumables")); WeightByPrice(GameAccess.ListField(shop, "potentialItemUpgrades")); WeightByPrice(GameAccess.ListField(shop, "potentialItemHealthPacks")); } ApplyBudgets(shop); } private static void Mix(IList? list, string label, bool reserve) { if (list == null || list.Count == 0) { return; } List<Group> list2 = new List<Group>(); Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); IEnumerator e