Decompiled source of REPOForge v1.5.9

REPOForge.dll

Decompiled 12 hours ago
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